javakotlinproject-panamajava-ffm

Java Foreign API: How to get string from C_POINTER?


I want to use libimobiledevice by Java Foreign API, This is my code, written by Kotlin:

class Device {
    private val arena: Arena = Arena.openConfined()
    private val udidAddress: MemorySegment = arena.allocate(C_POINTER)
    private val deviceAddress: MemorySegment = arena.allocate(C_POINTER)

    init {
        if (idevice_new_with_options(deviceAddress, NULL(), IDEVICE_LOOKUP_USBMUX()) != IDEVICE_E_SUCCESS()) {
            throw Exception("ERROR: No device found!")
        }
        if (idevice_get_udid(deviceAddress, udidAddress) != IDEVICE_E_SUCCESS()) {
            idevice_free(deviceAddress)
            throw Exception("ERROR: Get UDID failed")
        } else {
            println("connected")
            println(MemorySegment.ofAddress(udidAddress.address()).getUtf8String(0))
        }
    }
}

fun main() {
    Device()
}

But it got error while getting udid:

Exception in thread "main" java.lang.IndexOutOfBoundsException: Out of bound access on segment MemorySegment{ array: Optional.empty address:140202431639472 limit: 0 }; new offset = 0; new length = 1
    at java.base/jdk.internal.foreign.AbstractMemorySegmentImpl.outOfBoundException(AbstractMemorySegmentImpl.java:371)
    at java.base/jdk.internal.foreign.AbstractMemorySegmentImpl.apply(AbstractMemorySegmentImpl.java:357)
    at java.base/jdk.internal.foreign.AbstractMemorySegmentImpl.apply(AbstractMemorySegmentImpl.java:70)
    at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:98)
    at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:124)
    at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:448)
    at java.base/jdk.internal.foreign.AbstractMemorySegmentImpl.checkBounds(AbstractMemorySegmentImpl.java:346)
    at java.base/jdk.internal.foreign.AbstractMemorySegmentImpl.checkAccess(AbstractMemorySegmentImpl.java:311)
    at java.base/java.lang.invoke.VarHandleSegmentAsBytes.checkAddress(VarHandleSegmentAsBytes.java:81)
    at java.base/java.lang.invoke.VarHandleSegmentAsBytes.get(VarHandleSegmentAsBytes.java:108)
    at java.base/java.lang.foreign.MemorySegment.get(MemorySegment.java:1388)
    at java.base/jdk.internal.foreign.abi.SharedUtils.strlen(SharedUtils.java:199)
    at java.base/jdk.internal.foreign.abi.SharedUtils.toJavaStringInternal(SharedUtils.java:190)
    at java.base/java.lang.foreign.MemorySegment.getUtf8String(MemorySegment.java:890)
    at Device.<init>(Device.kt:21)
    at DeviceKt.main(Device.kt:27)
    at DeviceKt.main(Device.kt)

The C language code i want:

static char *udid = NULL;

/* Device Handle */
idevice_t device = NULL;

/* Try to connect to first USB device */
if (idevice_new_with_options(&device, NULL, IDEVICE_LOOKUP_USBMUX) != IDEVICE_E_SUCCESS) {
  printf("ERROR: No device found!\n");
  return -1;
}

/* Retrieve the udid of the connected device */
if (idevice_get_udid(device, &udid) != IDEVICE_E_SUCCESS) {
  printf("ERROR: Unable to get the device UDID.\n");
  idevice_free(device);
  return -1;
}

/* Outputs device identifier */
printf("Connected with UDID: %s\n", udid);

how to solve this problem? (sorry, this is my first time using stackoverflow)

It should return a udid, but I got an error, the code I tried is above.


Solution

  • It worked when I write like this:

    class Device {
        private var device: MemorySegment
        private var udid: MemorySegment
    
        init {
            Arena.openConfined().use { arena ->
                val udidAddress: MemorySegment = arena.allocate(C_POINTER)
                val deviceAddress: MemorySegment = arena.allocate(C_POINTER)
    
                if (idevice_new_with_options(deviceAddress, NULL(), IDEVICE_LOOKUP_USBMUX()) != IDEVICE_E_SUCCESS()) {
                throw Exception("ERROR: No device found!")
                }
                device = deviceAddress.get(C_POINTER, 0)
    
                if (idevice_get_udid(deviceAddress, udidAddress) != IDEVICE_E_SUCCESS()) {
                    idevice_free(deviceAddress)
                    throw Exception("ERROR: Get UDID failed")
                }
                
                udid = udidAddress.get(C_POINTER, 0).get(C_POINTER, 0)
                println("connected")
                println(udid.getUtf8String(0))
            }
        }
    }