|
1 | 1 | package io.github.dfa1.zstd; |
2 | 2 |
|
3 | 3 | import java.lang.foreign.Arena; |
| 4 | +import java.lang.foreign.MemoryLayout.PathElement; |
4 | 5 | import java.lang.foreign.MemorySegment; |
5 | 6 | import java.lang.foreign.SegmentAllocator; |
6 | 7 | import java.lang.invoke.MethodHandle; |
|
15 | 16 | /// @param upperBound the largest accepted value, inclusive |
16 | 17 | public record ZstdBounds(int lowerBound, int upperBound) { |
17 | 18 |
|
| 19 | + // Offsets into the returned ZSTD_bounds struct, derived from the named layout |
| 20 | + // rather than hand-counted, so they track the struct definition. |
| 21 | + private static final long ERROR_OFFSET = Bindings.BOUNDS_LAYOUT.byteOffset(PathElement.groupElement("error")); |
| 22 | + private static final long LOWER_OFFSET = Bindings.BOUNDS_LAYOUT.byteOffset(PathElement.groupElement("lowerBound")); |
| 23 | + private static final long UPPER_OFFSET = Bindings.BOUNDS_LAYOUT.byteOffset(PathElement.groupElement("upperBound")); |
| 24 | + |
18 | 25 | /// Calls a `*_getBounds` function (which returns a `ZSTD_bounds` struct by |
19 | 26 | /// value: `{ size_t error; int lowerBound; int upperBound; }`). |
20 | 27 | static ZstdBounds query(MethodHandle getBounds, int parameter) { |
21 | 28 | try (Arena arena = Arena.ofConfined()) { |
| 29 | + // getBounds returns a ZSTD_bounds struct by value. For a struct return, |
| 30 | + // the FFM linker prepends a SegmentAllocator parameter to the handle: |
| 31 | + // it allocates BOUNDS_LAYOUT.byteSize() bytes from that allocator, the |
| 32 | + // native call writes the struct there, and the handle returns a segment |
| 33 | + // viewing it. Passing the arena makes the struct arena-owned (freed on |
| 34 | + // close); the cast satisfies invokeExact's exact-type requirement. |
22 | 35 | MemorySegment bounds = (MemorySegment) getBounds.invokeExact((SegmentAllocator) arena, parameter); |
23 | | - long error = bounds.get(JAVA_LONG, 0); |
| 36 | + long error = bounds.get(JAVA_LONG, ERROR_OFFSET); |
24 | 37 | if (NativeCall.isError(error)) { |
25 | 38 | throw new ZstdException("parameter has no queryable bounds"); |
26 | 39 | } |
27 | | - return new ZstdBounds(bounds.get(JAVA_INT, 8), bounds.get(JAVA_INT, 12)); |
| 40 | + return new ZstdBounds(bounds.get(JAVA_INT, LOWER_OFFSET), bounds.get(JAVA_INT, UPPER_OFFSET)); |
28 | 41 | } catch (Throwable t) { |
29 | 42 | throw NativeCall.rethrow(t); |
30 | 43 | } |
|
0 commit comments