0

I want to call the following method in C (defined here):

heif_image_handle* handle;
heif_context_get_primary_image_handle(ctx, &handle);

The problem I have is that I have no access via the C-API to the struct heif_image_handle. It is defined as a struct without a definition:

struct heif_image_handle;

What I have tried:

try (var scope = ResourceScope.newSharedScope()) {
    MemoryAddress heif_context_alloc = heif_context_alloc();
    // ...
    MemoryAddress primary_image_handle = MemorySegment.allocateNative(C_POINTER, scope).address();
    heif_context_get_primary_image_handle(scope, primary_image_handle.address(), heif_context_alloc);
    // ...
}

Can someone help me how can I use this method with Panama API. My actual workaround was to extend the C-API but original author does not want to do that.

My actual code is under: https://github.com/lanthale/LibHeifFX/blob/main/LibHeifFX/src/main/java/org/libheiffx/LibheifImage.java

Jorn Vernee
  • 31,735
  • 4
  • 76
  • 93
lanthale
  • 23
  • 3
  • This seems to be the correct approach - if I read it correctly that `handle` is an "out-parameter". Read the address (pointer) from the segment after you make the call. – Johannes Kuhn Jan 22 '22 at 19:34
  • When you say you have no access to struct heif_image_handle, does that mean you don’t have any way to see its definition? Or did you mean that your Java program cannot access it? If you know how that struct is laid out, you probably want to pass the result of `MemoryLayout.structLayout` to the allocateNative method. – VGR Jan 23 '22 at 02:28
  • The definition is not in the header - so you have to work with pointers to it. – Johannes Kuhn Jan 23 '22 at 14:06
  • `allocateNative(C_POINTER)` will not allocate enough space for a struct, only enough space for a pointer. Is that your intent? – VGR Jan 23 '22 at 14:13

1 Answers1

2

Your code looks almost correct to me. You just need to keep hold of the allocated segment (which represents the heif_image_handle**), and then after calling heif_context_get_primary_image_handle, retrieve the MemoryAddress from that segment after the library sets the primary image handle into it (example using the JDK 17 API):

// allocate blob of memory the size of a pointer
MemorSegment primary_image_handle_seg = MemorySegment.allocateNative(C_POINTER);
// call library to set the handle into the allocated memory
heif_context_get_primary_image_handle(ctx, primary_image_handle_seg.address());
// retrieve pointer from allocated memory
MemoryAddress primary_image_handle = MemoryAccess.getAddress(primary_image_handle_seg);

In general, doing stack allocations and taking the address of the allocated value like in C, as in the snippet that you show, is not possible directly in Java. So, as far as the panama foreign API goes, whenever you see something like this in C code:

some_type* val;

You will need to allocate a MemorySegment for it:

// some_type** val_ptr;
MemorySegment val_ptr = MemerySegment.allocateNative(C_POINTER, scope);
// some_type** val_ptr_as_ma; (as a bare MemoryAddress)
MemoryAddress val_ptr_as_ma = val.address();
// some_type* val; (dereference/copy val, `*val_ptr`)
MemoryAddress val = MemoryAccess.getAddress(val);

Note that we have to go through the MemorySegment route in this case. Since it is not possible to take the address of a MemoryAddress.

In general, the Java API does not have an equivalent of the & ampersand operator. The .address() method is there to convert address-like things into MemoryAddress instances, not to mimic &. And for MemoryAddress itself it just return this (so your primary_image_handle.address() call has no effect).

Essentially, the C equivalent of what we do in Java, without stack allocation and &, is this:

some_type** val_ptr = malloc(sizeof *val_ptr);
func(val_ptr); // void func(some_type** v) { ... }
some_type* val = *val_ptr;
Jorn Vernee
  • 31,735
  • 4
  • 76
  • 93
  • I would wrap the entire thing into it's own `ResourceScope` for just this call. – Johannes Kuhn Jan 23 '22 at 17:00
  • I am using already a Ressource scope. Thank you so much. It is working as expected. – lanthale Jan 24 '22 at 11:52
  • 1
    As an update to anyone who finds this later on, as of 09/21/22 it appears you can call .address() on MemoryAddress now. The class "MemoryAccess" no longer seems to exist. I have no idea if calling .address() on MA is what you want to do. – Gavin Ray Sep 21 '22 at 23:19