diff --git a/zstd/src/main/java/io/github/dfa1/zstd/Bindings.java b/zstd/src/main/java/io/github/dfa1/zstd/Bindings.java index 9d1b45b..91baf28 100644 --- a/zstd/src/main/java/io/github/dfa1/zstd/Bindings.java +++ b/zstd/src/main/java/io/github/dfa1/zstd/Bindings.java @@ -158,8 +158,11 @@ final class Bindings { NativeLibrary.lookup("ZSTD_getDictID_fromDDict", FunctionDescriptor.of(JAVA_INT, ADDRESS)); // ZSTD_bounds { size_t error; int lowerBound; int upperBound; } — returned by value - private static final MemoryLayout BOUNDS_LAYOUT = - MemoryLayout.structLayout(JAVA_LONG, JAVA_INT, JAVA_INT); + static final MemoryLayout BOUNDS_LAYOUT = + MemoryLayout.structLayout( + JAVA_LONG.withName("error"), + JAVA_INT.withName("lowerBound"), + JAVA_INT.withName("upperBound")); // ZSTD_bounds ZSTD_cParam_getBounds(ZSTD_cParameter) / ZSTD_dParam_getBounds(ZSTD_dParameter) static final MethodHandle CPARAM_GET_BOUNDS = NativeLibrary.lookup("ZSTD_cParam_getBounds", FunctionDescriptor.of(BOUNDS_LAYOUT, JAVA_INT)); diff --git a/zstd/src/main/java/io/github/dfa1/zstd/NativeCall.java b/zstd/src/main/java/io/github/dfa1/zstd/NativeCall.java new file mode 100644 index 0000000..71181b8 --- /dev/null +++ b/zstd/src/main/java/io/github/dfa1/zstd/NativeCall.java @@ -0,0 +1,81 @@ +package io.github.dfa1.zstd; + +import java.lang.foreign.MemorySegment; +import java.nio.charset.StandardCharsets; + +/// Package-private helpers that adapt raw FFM downcalls to the zstd error +/// convention: run a native call, decode a zstd `size_t` error code into a +/// {@link ZstdException}, and guard native-segment arguments. Shared by the +/// binding classes so the conventions live in one place. +final class NativeCall { + + /// A native call returning a zstd `size_t` status that may encode an error. + @FunctionalInterface + interface ZstdCall { + long run() throws Throwable; + } + + /// Invokes a size-returning zstd call and converts a zstd error code into a + /// {@link ZstdException}. + static long checkReturnValue(ZstdCall c) { + long code; + try { + code = c.run(); + } catch (Throwable t) { + throw rethrow(t); + } + if (isError(code)) { + throw new ZstdException(errorName(code), ZstdErrorCode.of(errorCode(code))); + } + return code; + } + + static boolean isError(long code) { + try { + return ((int) Bindings.IS_ERROR.invokeExact(code)) != 0; + } catch (Throwable t) { + throw rethrow(t); + } + } + + private static int errorCode(long code) { + try { + return (int) Bindings.GET_ERROR_CODE.invokeExact(code); + } catch (Throwable t) { + throw rethrow(t); + } + } + + @SuppressWarnings("restricted") // reinterpret needed to read a C string of unknown length + private static String errorName(long code) { + try { + MemorySegment p = (MemorySegment) Bindings.GET_ERROR_NAME.invokeExact(code); + return p.reinterpret(Long.MAX_VALUE).getString(0, StandardCharsets.US_ASCII); + } catch (Throwable t) { + throw rethrow(t); + } + } + + /// Guards a zero-copy entry point: the segment handed to zstd must be backed + /// by native (off-heap) memory, since its address is dereferenced in C. Fails + /// fast with a clear message instead of the FFM linker's cryptic error. + static MemorySegment requireNative(MemorySegment seg, String name) { + if (!seg.isNative()) { + throw new IllegalArgumentException( + name + " must be a native (off-heap) MemorySegment; got a heap segment"); + } + return seg; + } + + /// Rethrows any `Throwable` as if unchecked, laundering the checked + /// `Throwable` that {@link java.lang.invoke.MethodHandle#invokeExact} declares. + /// The shared sink for every binding class's native-call catch blocks. + @SuppressWarnings("unchecked") + static RuntimeException rethrow(Throwable t) throws E { + throw (E) t; + } + + private NativeCall() { + // no instances + } +} diff --git a/zstd/src/main/java/io/github/dfa1/zstd/Zstd.java b/zstd/src/main/java/io/github/dfa1/zstd/Zstd.java index 98495f0..5020e6f 100644 --- a/zstd/src/main/java/io/github/dfa1/zstd/Zstd.java +++ b/zstd/src/main/java/io/github/dfa1/zstd/Zstd.java @@ -42,7 +42,7 @@ public static byte[] compress(byte[] src, int level) { MemorySegment in = copyIn(arena, src); long bound = compressBound(src.length); MemorySegment out = arena.allocate(bound); - long written = call(() -> (long) Bindings.COMPRESS.invokeExact( + long written = NativeCall.checkReturnValue(() -> (long) Bindings.COMPRESS.invokeExact( out, bound, in, (long) src.length, level)); return copyOut(out, written); } @@ -77,7 +77,7 @@ public static byte[] decompress(byte[] compressed, int maxSize) { try (Arena arena = Arena.ofConfined()) { MemorySegment in = copyIn(arena, compressed); MemorySegment out = arena.allocate(Math.max(maxSize, 1)); - long written = call(() -> (long) Bindings.DECOMPRESS.invokeExact( + long written = NativeCall.checkReturnValue(() -> (long) Bindings.DECOMPRESS.invokeExact( out, (long) maxSize, in, (long) compressed.length)); return copyOut(out, written); } @@ -91,12 +91,12 @@ public static byte[] decompress(byte[] compressed, int maxSize) { /// @return the decompressed length in bytes /// @throws ZstdException if the frame is invalid or does not store its size public static long decompressedSize(MemorySegment frame) { - requireNative(frame, "frame"); + NativeCall.requireNative(frame, "frame"); long size; try { size = (long) Bindings.GET_FRAME_CONTENT_SIZE.invokeExact(frame, frame.byteSize()); } catch (Throwable t) { - throw sneaky(t); + throw NativeCall.rethrow(t); } if (size == CONTENTSIZE_UNKNOWN) { throw new ZstdException("decompressed size not stored in frame"); @@ -116,7 +116,7 @@ public static long compressBound(long srcSize) { try { return (long) Bindings.COMPRESS_BOUND.invokeExact(srcSize); } catch (Throwable t) { - throw sneaky(t); + throw NativeCall.rethrow(t); } } @@ -126,7 +126,7 @@ private static long frameContentSize(byte[] compressed) { MemorySegment in = copyIn(arena, compressed); return (long) Bindings.GET_FRAME_CONTENT_SIZE.invokeExact(in, (long) compressed.length); } catch (Throwable t) { - throw sneaky(t); + throw NativeCall.rethrow(t); } } @@ -137,7 +137,7 @@ public static int maxCompressionLevel() { try { return (int) Bindings.MAX_C_LEVEL.invokeExact(); } catch (Throwable t) { - throw sneaky(t); + throw NativeCall.rethrow(t); } } @@ -148,7 +148,7 @@ public static int minCompressionLevel() { try { return (int) Bindings.MIN_C_LEVEL.invokeExact(); } catch (Throwable t) { - throw sneaky(t); + throw NativeCall.rethrow(t); } } @@ -159,7 +159,7 @@ public static int defaultCompressionLevel() { try { return (int) Bindings.DEFAULT_C_LEVEL.invokeExact(); } catch (Throwable t) { - throw sneaky(t); + throw NativeCall.rethrow(t); } } @@ -172,7 +172,7 @@ public static long estimateCompressContextSize(int level) { try { return (long) Bindings.ESTIMATE_CCTX_SIZE.invokeExact(level); } catch (Throwable t) { - throw sneaky(t); + throw NativeCall.rethrow(t); } } @@ -183,7 +183,7 @@ public static long estimateDecompressContextSize() { try { return (long) Bindings.ESTIMATE_DCTX_SIZE.invokeExact(); } catch (Throwable t) { - throw sneaky(t); + throw NativeCall.rethrow(t); } } @@ -197,7 +197,7 @@ public static long estimateCompressDictSize(long dictSize, int level) { try { return (long) Bindings.ESTIMATE_CDICT_SIZE.invokeExact(dictSize, level); } catch (Throwable t) { - throw sneaky(t); + throw NativeCall.rethrow(t); } } @@ -210,7 +210,7 @@ public static long estimateDecompressDictSize(long dictSize) { try { return (long) Bindings.ESTIMATE_DDICT_SIZE.invokeExact(dictSize, 0); // ZSTD_dlm_byCopy } catch (Throwable t) { - throw sneaky(t); + throw NativeCall.rethrow(t); } } @@ -223,69 +223,12 @@ public static String version() { MemorySegment p = (MemorySegment) Bindings.VERSION_STRING.invokeExact(); return p.reinterpret(Long.MAX_VALUE).getString(0, StandardCharsets.US_ASCII); } catch (Throwable t) { - throw sneaky(t); + throw NativeCall.rethrow(t); } } // --- package-private helpers shared with the context classes --- - - /// A native call returning a zstd `size_t` status that may encode an error. - @FunctionalInterface - interface SizeCall { - long run() throws Throwable; - } - - /// Invokes a size-returning zstd call and converts a zstd error code into a - /// {@link ZstdException}. - static long call(SizeCall c) { - long code; - try { - code = c.run(); - } catch (Throwable t) { - throw sneaky(t); - } - if (isError(code)) { - throw new ZstdException(errorName(code), ZstdErrorCode.of(errorCode(code))); - } - return code; - } - - static boolean isError(long code) { - try { - return ((int) Bindings.IS_ERROR.invokeExact(code)) != 0; - } catch (Throwable t) { - throw sneaky(t); - } - } - - private static int errorCode(long code) { - try { - return (int) Bindings.GET_ERROR_CODE.invokeExact(code); - } catch (Throwable t) { - throw sneaky(t); - } - } - - @SuppressWarnings("restricted") // reinterpret needed to read a C string of unknown length - private static String errorName(long code) { - try { - MemorySegment p = (MemorySegment) Bindings.GET_ERROR_NAME.invokeExact(code); - return p.reinterpret(Long.MAX_VALUE).getString(0, StandardCharsets.US_ASCII); - } catch (Throwable t) { - throw sneaky(t); - } - } - - /// Guards a zero-copy entry point: the segment handed to zstd must be backed - /// by native (off-heap) memory, since its address is dereferenced in C. Fails - /// fast with a clear message instead of the FFM linker's cryptic error. - static MemorySegment requireNative(MemorySegment seg, String name) { - if (!seg.isNative()) { - throw new IllegalArgumentException( - name + " must be a native (off-heap) MemorySegment; got a heap segment"); - } - return seg; - } + // Native-call status checking and segment guards live in NativeCall. static MemorySegment copyIn(Arena arena, byte[] src) { MemorySegment seg = arena.allocate(Math.max(src.length, 1)); @@ -299,11 +242,6 @@ static byte[] copyOut(MemorySegment seg, long len) { return out; } - @SuppressWarnings("unchecked") - private static RuntimeException sneaky(Throwable t) throws E { - throw (E) t; - } - private Zstd() { // no instances } diff --git a/zstd/src/main/java/io/github/dfa1/zstd/ZstdBounds.java b/zstd/src/main/java/io/github/dfa1/zstd/ZstdBounds.java index 8677dff..da97266 100644 --- a/zstd/src/main/java/io/github/dfa1/zstd/ZstdBounds.java +++ b/zstd/src/main/java/io/github/dfa1/zstd/ZstdBounds.java @@ -1,6 +1,7 @@ package io.github.dfa1.zstd; import java.lang.foreign.Arena; +import java.lang.foreign.MemoryLayout.PathElement; import java.lang.foreign.MemorySegment; import java.lang.foreign.SegmentAllocator; import java.lang.invoke.MethodHandle; @@ -15,23 +16,30 @@ /// @param upperBound the largest accepted value, inclusive public record ZstdBounds(int lowerBound, int upperBound) { + // Offsets into the returned ZSTD_bounds struct, derived from the named layout + // rather than hand-counted, so they track the struct definition. + private static final long ERROR_OFFSET = Bindings.BOUNDS_LAYOUT.byteOffset(PathElement.groupElement("error")); + private static final long LOWER_OFFSET = Bindings.BOUNDS_LAYOUT.byteOffset(PathElement.groupElement("lowerBound")); + private static final long UPPER_OFFSET = Bindings.BOUNDS_LAYOUT.byteOffset(PathElement.groupElement("upperBound")); + /// Calls a `*_getBounds` function (which returns a `ZSTD_bounds` struct by /// value: `{ size_t error; int lowerBound; int upperBound; }`). static ZstdBounds query(MethodHandle getBounds, int parameter) { try (Arena arena = Arena.ofConfined()) { + // getBounds returns a ZSTD_bounds struct by value. For a struct return, + // the FFM linker prepends a SegmentAllocator parameter to the handle: + // it allocates BOUNDS_LAYOUT.byteSize() bytes from that allocator, the + // native call writes the struct there, and the handle returns a segment + // viewing it. Passing the arena makes the struct arena-owned (freed on + // close); the cast satisfies invokeExact's exact-type requirement. MemorySegment bounds = (MemorySegment) getBounds.invokeExact((SegmentAllocator) arena, parameter); - long error = bounds.get(JAVA_LONG, 0); - if (Zstd.isError(error)) { + long error = bounds.get(JAVA_LONG, ERROR_OFFSET); + if (NativeCall.isError(error)) { throw new ZstdException("parameter has no queryable bounds"); } - return new ZstdBounds(bounds.get(JAVA_INT, 8), bounds.get(JAVA_INT, 12)); + return new ZstdBounds(bounds.get(JAVA_INT, LOWER_OFFSET), bounds.get(JAVA_INT, UPPER_OFFSET)); } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } } - - @SuppressWarnings("unchecked") - private static RuntimeException rethrow(Throwable t) throws E { - throw (E) t; - } } diff --git a/zstd/src/main/java/io/github/dfa1/zstd/ZstdCompressCtx.java b/zstd/src/main/java/io/github/dfa1/zstd/ZstdCompressCtx.java index 0df19a8..ded4987 100644 --- a/zstd/src/main/java/io/github/dfa1/zstd/ZstdCompressCtx.java +++ b/zstd/src/main/java/io/github/dfa1/zstd/ZstdCompressCtx.java @@ -33,7 +33,7 @@ private static MemorySegment create() { } return p; } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } } @@ -99,14 +99,14 @@ public byte[] compress(byte[] src) { MemorySegment in = Zstd.copyIn(arena, src); long bound = Zstd.compressBound(src.length); MemorySegment out = arena.allocate(bound); - long written = Zstd.call(() -> (long) Bindings.COMPRESS2.invokeExact( + long written = NativeCall.checkReturnValue(() -> (long) Bindings.COMPRESS2.invokeExact( ptr(), out, bound, in, (long) src.length)); return Zstd.copyOut(out, written); } } private void setParam(ZstdCompressParameter parameter, int value) { - Zstd.call(() -> (long) Bindings.CCTX_SET_PARAMETER.invokeExact(ptr(), parameter.value(), value)); + NativeCall.checkReturnValue(() -> (long) Bindings.CCTX_SET_PARAMETER.invokeExact(ptr(), parameter.value(), value)); } /// Compresses `src` against `dict` at this context's level. @@ -125,7 +125,7 @@ public byte[] compress(byte[] src, ZstdDictionary dict) { MemorySegment dseg = Zstd.copyIn(arena, d); long bound = Zstd.compressBound(src.length); MemorySegment out = arena.allocate(bound); - long written = Zstd.call(() -> (long) Bindings.COMPRESS_USING_DICT.invokeExact( + long written = NativeCall.checkReturnValue(() -> (long) Bindings.COMPRESS_USING_DICT.invokeExact( ptr(), out, bound, in, (long) src.length, dseg, (long) d.length, level)); return Zstd.copyOut(out, written); } @@ -143,7 +143,7 @@ public byte[] compress(byte[] src, ZstdCompressDict dict) { long bound = Zstd.compressBound(src.length); MemorySegment out = arena.allocate(bound); MemorySegment cdict = dict.ptr(); - long written = Zstd.call(() -> (long) Bindings.COMPRESS_USING_CDICT.invokeExact( + long written = NativeCall.checkReturnValue(() -> (long) Bindings.COMPRESS_USING_CDICT.invokeExact( ptr(), out, bound, in, (long) src.length, cdict)); return Zstd.copyOut(out, written); } @@ -162,9 +162,9 @@ public byte[] compress(byte[] src, ZstdCompressDict dict) { /// @return the number of bytes written into `dst` /// @throws ZstdException if `dst` is too small or compression fails public long compress(MemorySegment dst, MemorySegment src) { - Zstd.requireNative(dst, "dst"); - Zstd.requireNative(src, "src"); - return Zstd.call(() -> (long) Bindings.COMPRESS2.invokeExact( + NativeCall.requireNative(dst, "dst"); + NativeCall.requireNative(src, "src"); + return NativeCall.checkReturnValue(() -> (long) Bindings.COMPRESS2.invokeExact( ptr(), dst, dst.byteSize(), src, src.byteSize())); } @@ -175,10 +175,10 @@ public long compress(MemorySegment dst, MemorySegment src) { /// @param dict the pre-digested compression dictionary /// @return the number of bytes written into `dst` public long compress(MemorySegment dst, MemorySegment src, ZstdCompressDict dict) { - Zstd.requireNative(dst, "dst"); - Zstd.requireNative(src, "src"); + NativeCall.requireNative(dst, "dst"); + NativeCall.requireNative(src, "src"); MemorySegment cdict = dict.ptr(); - return Zstd.call(() -> (long) Bindings.COMPRESS_USING_CDICT.invokeExact( + return NativeCall.checkReturnValue(() -> (long) Bindings.COMPRESS_USING_CDICT.invokeExact( ptr(), dst, dst.byteSize(), src, src.byteSize(), cdict)); } @@ -216,7 +216,7 @@ public long sizeOf() { try { return (long) Bindings.SIZEOF_CCTX.invokeExact(ptr()); } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } } @@ -224,9 +224,4 @@ public long sizeOf() { protected void tryClose(MemorySegment ptr) throws Throwable { var _ = (long) Bindings.FREE_CCTX.invokeExact(ptr); } - - @SuppressWarnings("unchecked") - private static RuntimeException rethrow(Throwable t) throws E { - throw (E) t; - } } diff --git a/zstd/src/main/java/io/github/dfa1/zstd/ZstdCompressDict.java b/zstd/src/main/java/io/github/dfa1/zstd/ZstdCompressDict.java index 30b5ec5..c900a41 100644 --- a/zstd/src/main/java/io/github/dfa1/zstd/ZstdCompressDict.java +++ b/zstd/src/main/java/io/github/dfa1/zstd/ZstdCompressDict.java @@ -63,12 +63,12 @@ private static MemorySegment create(ZstdDictionary dict, int level) { } return p; } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } } private static MemorySegment create(MemorySegment dict, int level) { - Zstd.requireNative(dict, "dict"); + NativeCall.requireNative(dict, "dict"); try { MemorySegment p = (MemorySegment) Bindings.CREATE_CDICT.invokeExact(dict, dict.byteSize(), level); if (MemorySegment.NULL.equals(p)) { @@ -76,7 +76,7 @@ private static MemorySegment create(MemorySegment dict, int level) { } return p; } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } } @@ -94,7 +94,7 @@ public int id() { try { return (int) Bindings.GET_DICT_ID_FROM_CDICT.invokeExact(ptr()); } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } } @@ -105,7 +105,7 @@ public long sizeOf() { try { return (long) Bindings.SIZEOF_CDICT.invokeExact(ptr()); } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } } @@ -113,9 +113,4 @@ public long sizeOf() { protected void tryClose(MemorySegment ptr) throws Throwable { var _ = (long) Bindings.FREE_CDICT.invokeExact(ptr); } - - @SuppressWarnings("unchecked") - private static RuntimeException rethrow(Throwable t) throws E { - throw (E) t; - } } diff --git a/zstd/src/main/java/io/github/dfa1/zstd/ZstdCompressStream.java b/zstd/src/main/java/io/github/dfa1/zstd/ZstdCompressStream.java index 75fec0c..cbb19da 100644 --- a/zstd/src/main/java/io/github/dfa1/zstd/ZstdCompressStream.java +++ b/zstd/src/main/java/io/github/dfa1/zstd/ZstdCompressStream.java @@ -53,14 +53,14 @@ public ZstdCompressStream(int level, ZstdDictionary dictionary) { // close() — one release path, no leak on a half-built stream. super(createCctx()); try { - Zstd.call(() -> (long) Bindings.CCTX_SET_PARAMETER.invokeExact( + NativeCall.checkReturnValue(() -> (long) Bindings.CCTX_SET_PARAMETER.invokeExact( ptr(), ZstdCompressParameter.COMPRESSION_LEVEL.value(), level)); if (dictionary != null) { loadDictionary(dictionary); } } catch (Throwable t) { close(); - throw rethrow(t); + throw NativeCall.rethrow(t); } } @@ -72,7 +72,7 @@ private static MemorySegment createCctx() { } return cctx; } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } } @@ -80,7 +80,7 @@ private void loadDictionary(ZstdDictionary dictionary) { try (Arena staging = Arena.ofConfined()) { byte[] raw = dictionary.raw(); MemorySegment d = Zstd.copyIn(staging, raw); - Zstd.call(() -> (long) Bindings.CCTX_LOAD_DICTIONARY.invokeExact( + NativeCall.checkReturnValue(() -> (long) Bindings.CCTX_LOAD_DICTIONARY.invokeExact( ptr(), d, (long) raw.length)); } } @@ -97,11 +97,11 @@ private void loadDictionary(ZstdDictionary dictionary) { /// @return how much was consumed and produced, and the remaining hint /// @throws ZstdException if compression fails public ZstdStreamResult compress(MemorySegment dst, MemorySegment src, ZstdEndDirective directive) { - Zstd.requireNative(dst, "dst"); - Zstd.requireNative(src, "src"); + NativeCall.requireNative(dst, "dst"); + NativeCall.requireNative(src, "src"); in.set(src, src.byteSize(), 0); out.set(dst, dst.byteSize(), 0); - long remaining = Zstd.call(() -> (long) Bindings.COMPRESS_STREAM2.invokeExact( + long remaining = NativeCall.checkReturnValue(() -> (long) Bindings.COMPRESS_STREAM2.invokeExact( ptr(), out.segment(), in.segment(), directive.value())); return new ZstdStreamResult(in.pos(), out.pos(), remaining); } @@ -122,7 +122,7 @@ public ZstdFrameProgression progress() { p.get(JAVA_INT, 32), // currentJobID p.get(JAVA_INT, 36)); // nbActiveWorkers } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } } @@ -133,7 +133,7 @@ public long sizeOf() { try { return (long) Bindings.SIZEOF_CCTX.invokeExact(ptr()); } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } } @@ -145,9 +145,4 @@ protected void tryClose(MemorySegment ptr) throws Throwable { arena.close(); } } - - @SuppressWarnings("unchecked") - private static RuntimeException rethrow(Throwable t) throws E { - throw (E) t; - } } diff --git a/zstd/src/main/java/io/github/dfa1/zstd/ZstdDecompressCtx.java b/zstd/src/main/java/io/github/dfa1/zstd/ZstdDecompressCtx.java index e2c2b80..d709916 100644 --- a/zstd/src/main/java/io/github/dfa1/zstd/ZstdDecompressCtx.java +++ b/zstd/src/main/java/io/github/dfa1/zstd/ZstdDecompressCtx.java @@ -22,7 +22,7 @@ private static MemorySegment create() { } return p; } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } } @@ -33,7 +33,7 @@ private static MemorySegment create() { /// @return `this`, for chaining /// @throws ZstdException if the value is out of range for the parameter public ZstdDecompressCtx parameter(ZstdDecompressParameter parameter, int value) { - Zstd.call(() -> (long) Bindings.DCTX_SET_PARAMETER.invokeExact(ptr(), parameter.value(), value)); + NativeCall.checkReturnValue(() -> (long) Bindings.DCTX_SET_PARAMETER.invokeExact(ptr(), parameter.value(), value)); return this; } @@ -55,7 +55,7 @@ public byte[] decompress(byte[] compressed, int maxSize) { try (Arena arena = Arena.ofConfined()) { MemorySegment in = Zstd.copyIn(arena, compressed); MemorySegment out = arena.allocate(Math.max(maxSize, 1)); - long written = Zstd.call(() -> (long) Bindings.DECOMPRESS_DCTX.invokeExact( + long written = NativeCall.checkReturnValue(() -> (long) Bindings.DECOMPRESS_DCTX.invokeExact( ptr(), out, (long) maxSize, in, (long) compressed.length)); return Zstd.copyOut(out, written); } @@ -77,7 +77,7 @@ public byte[] decompress(byte[] compressed, int maxSize, ZstdDictionary dict) { byte[] d = dict.raw(); MemorySegment dseg = Zstd.copyIn(arena, d); MemorySegment out = arena.allocate(Math.max(maxSize, 1)); - long written = Zstd.call(() -> (long) Bindings.DECOMPRESS_USING_DICT.invokeExact( + long written = NativeCall.checkReturnValue(() -> (long) Bindings.DECOMPRESS_USING_DICT.invokeExact( ptr(), out, (long) maxSize, in, (long) compressed.length, dseg, (long) d.length)); return Zstd.copyOut(out, written); } @@ -94,7 +94,7 @@ public byte[] decompress(byte[] compressed, int maxSize, ZstdDecompressDict dict MemorySegment in = Zstd.copyIn(arena, compressed); MemorySegment out = arena.allocate(Math.max(maxSize, 1)); MemorySegment ddict = dict.ptr(); - long written = Zstd.call(() -> (long) Bindings.DECOMPRESS_USING_DDICT.invokeExact( + long written = NativeCall.checkReturnValue(() -> (long) Bindings.DECOMPRESS_USING_DDICT.invokeExact( ptr(), out, (long) maxSize, in, (long) compressed.length, ddict)); return Zstd.copyOut(out, written); } @@ -115,9 +115,9 @@ public byte[] decompress(byte[] compressed, int maxSize, ZstdDecompressDict dict /// @return the number of bytes written into `dst` /// @throws ZstdException if `dst` is too small or the frame is invalid public long decompress(MemorySegment dst, MemorySegment src) { - Zstd.requireNative(dst, "dst"); - Zstd.requireNative(src, "src"); - return Zstd.call(() -> (long) Bindings.DECOMPRESS_DCTX.invokeExact( + NativeCall.requireNative(dst, "dst"); + NativeCall.requireNative(src, "src"); + return NativeCall.checkReturnValue(() -> (long) Bindings.DECOMPRESS_DCTX.invokeExact( ptr(), dst, dst.byteSize(), src, src.byteSize())); } @@ -128,10 +128,10 @@ public long decompress(MemorySegment dst, MemorySegment src) { /// @param dict the pre-digested decompression dictionary /// @return the number of bytes written into `dst` public long decompress(MemorySegment dst, MemorySegment src, ZstdDecompressDict dict) { - Zstd.requireNative(dst, "dst"); - Zstd.requireNative(src, "src"); + NativeCall.requireNative(dst, "dst"); + NativeCall.requireNative(src, "src"); MemorySegment ddict = dict.ptr(); - return Zstd.call(() -> (long) Bindings.DECOMPRESS_USING_DDICT.invokeExact( + return NativeCall.checkReturnValue(() -> (long) Bindings.DECOMPRESS_USING_DDICT.invokeExact( ptr(), dst, dst.byteSize(), src, src.byteSize(), ddict)); } @@ -176,7 +176,7 @@ public long sizeOf() { try { return (long) Bindings.SIZEOF_DCTX.invokeExact(ptr()); } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } } @@ -184,9 +184,4 @@ public long sizeOf() { protected void tryClose(MemorySegment ptr) throws Throwable { var _ = (long) Bindings.FREE_DCTX.invokeExact(ptr); } - - @SuppressWarnings("unchecked") - private static RuntimeException rethrow(Throwable t) throws E { - throw (E) t; - } } diff --git a/zstd/src/main/java/io/github/dfa1/zstd/ZstdDecompressDict.java b/zstd/src/main/java/io/github/dfa1/zstd/ZstdDecompressDict.java index 159166c..aefe4ca 100644 --- a/zstd/src/main/java/io/github/dfa1/zstd/ZstdDecompressDict.java +++ b/zstd/src/main/java/io/github/dfa1/zstd/ZstdDecompressDict.java @@ -40,12 +40,12 @@ private static MemorySegment create(ZstdDictionary dict) { } return p; } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } } private static MemorySegment create(MemorySegment dict) { - Zstd.requireNative(dict, "dict"); + NativeCall.requireNative(dict, "dict"); try { MemorySegment p = (MemorySegment) Bindings.CREATE_DDICT.invokeExact(dict, dict.byteSize()); if (MemorySegment.NULL.equals(p)) { @@ -53,7 +53,7 @@ private static MemorySegment create(MemorySegment dict) { } return p; } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } } @@ -64,7 +64,7 @@ public int id() { try { return (int) Bindings.GET_DICT_ID_FROM_DDICT.invokeExact(ptr()); } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } } @@ -75,7 +75,7 @@ public long sizeOf() { try { return (long) Bindings.SIZEOF_DDICT.invokeExact(ptr()); } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } } @@ -83,9 +83,4 @@ public long sizeOf() { protected void tryClose(MemorySegment ptr) throws Throwable { var _ = (long) Bindings.FREE_DDICT.invokeExact(ptr); } - - @SuppressWarnings("unchecked") - private static RuntimeException rethrow(Throwable t) throws E { - throw (E) t; - } } diff --git a/zstd/src/main/java/io/github/dfa1/zstd/ZstdDecompressStream.java b/zstd/src/main/java/io/github/dfa1/zstd/ZstdDecompressStream.java index c42aa2f..cd44173 100644 --- a/zstd/src/main/java/io/github/dfa1/zstd/ZstdDecompressStream.java +++ b/zstd/src/main/java/io/github/dfa1/zstd/ZstdDecompressStream.java @@ -34,7 +34,7 @@ public ZstdDecompressStream(ZstdDictionary dictionary) { } } catch (Throwable t) { close(); - throw rethrow(t); + throw NativeCall.rethrow(t); } } @@ -46,7 +46,7 @@ private static MemorySegment createDctx() { } return dctx; } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } } @@ -54,7 +54,7 @@ private void loadDictionary(ZstdDictionary dictionary) { try (Arena staging = Arena.ofConfined()) { byte[] raw = dictionary.raw(); MemorySegment d = Zstd.copyIn(staging, raw); - Zstd.call(() -> (long) Bindings.DCTX_LOAD_DICTIONARY.invokeExact( + NativeCall.checkReturnValue(() -> (long) Bindings.DCTX_LOAD_DICTIONARY.invokeExact( ptr(), d, (long) raw.length)); } } @@ -72,7 +72,7 @@ private void loadDictionary(ZstdDictionary dictionary) { public ZstdStreamResult decompress(MemorySegment dst, MemorySegment src) { in.set(src, src.byteSize(), 0); out.set(dst, dst.byteSize(), 0); - long remaining = Zstd.call(() -> (long) Bindings.DECOMPRESS_STREAM.invokeExact( + long remaining = NativeCall.checkReturnValue(() -> (long) Bindings.DECOMPRESS_STREAM.invokeExact( ptr(), out.segment(), in.segment())); return new ZstdStreamResult(in.pos(), out.pos(), remaining); } @@ -84,7 +84,7 @@ public long sizeOf() { try { return (long) Bindings.SIZEOF_DCTX.invokeExact(ptr()); } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } } @@ -96,9 +96,4 @@ protected void tryClose(MemorySegment ptr) throws Throwable { arena.close(); } } - - @SuppressWarnings("unchecked") - private static RuntimeException rethrow(Throwable t) throws E { - throw (E) t; - } } diff --git a/zstd/src/main/java/io/github/dfa1/zstd/ZstdDictionary.java b/zstd/src/main/java/io/github/dfa1/zstd/ZstdDictionary.java index 2c6faa8..876b5e8 100644 --- a/zstd/src/main/java/io/github/dfa1/zstd/ZstdDictionary.java +++ b/zstd/src/main/java/io/github/dfa1/zstd/ZstdDictionary.java @@ -116,7 +116,7 @@ public static ZstdDictionary train(List samples, int maxDictBytes) { produced = (long) Bindings.ZDICT_TRAIN.invokeExact( dictBuf, (long) maxDictBytes, flat, sizes, samples.size()); } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } if (zdictIsError(produced)) { throw new ZstdException("dictionary training failed: " + zdictErrorName(produced)); @@ -204,7 +204,7 @@ private static ZstdDictionary optimize(List samples, int maxDictBytes, produced = (long) handle.invokeExact( dictBuf, (long) maxDictBytes, flat, sizes, samples.size(), params); } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } if (zdictIsError(produced)) { throw new ZstdException("dictionary training failed: " + zdictErrorName(produced)); @@ -255,7 +255,7 @@ public static ZstdDictionary finalizeFrom(byte[] content, List samples, dictBuf, (long) maxDictBytes, contentSeg, (long) content.length, flat, sizes, samples.size(), params); } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } if (zdictIsError(produced)) { throw new ZstdException("dictionary finalisation failed: " + zdictErrorName(produced)); @@ -275,7 +275,7 @@ public int id() { MemorySegment seg = Zstd.copyIn(arena, bytes); return (int) Bindings.ZDICT_GET_DICT_ID.invokeExact(seg, (long) bytes.length); } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } } @@ -293,7 +293,7 @@ public int headerSize() { } return Math.toIntExact(size); } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } } @@ -320,7 +320,7 @@ private static boolean zdictIsError(long code) { try { return ((int) Bindings.ZDICT_IS_ERROR.invokeExact(code)) != 0; } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } } @@ -330,12 +330,7 @@ private static String zdictErrorName(long code) { MemorySegment p = (MemorySegment) Bindings.ZDICT_GET_ERROR_NAME.invokeExact(code); return p.reinterpret(Long.MAX_VALUE).getString(0, StandardCharsets.US_ASCII); } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } } - - @SuppressWarnings("unchecked") - private static RuntimeException rethrow(Throwable t) throws E { - throw (E) t; - } } diff --git a/zstd/src/main/java/io/github/dfa1/zstd/ZstdErrorCode.java b/zstd/src/main/java/io/github/dfa1/zstd/ZstdErrorCode.java index b4d1f0f..37c8e10 100644 --- a/zstd/src/main/java/io/github/dfa1/zstd/ZstdErrorCode.java +++ b/zstd/src/main/java/io/github/dfa1/zstd/ZstdErrorCode.java @@ -109,15 +109,10 @@ public String description() { MemorySegment p = (MemorySegment) Bindings.GET_ERROR_STRING.invokeExact(value); return p.reinterpret(Long.MAX_VALUE).getString(0, StandardCharsets.US_ASCII); } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } } - @SuppressWarnings("unchecked") - private static RuntimeException rethrow(Throwable t) throws E { - throw (E) t; - } - /// Maps a native `ZSTD_ErrorCode` integer to its category. /// /// @param value the native error code diff --git a/zstd/src/main/java/io/github/dfa1/zstd/ZstdFrame.java b/zstd/src/main/java/io/github/dfa1/zstd/ZstdFrame.java index 7099ace..86c9070 100644 --- a/zstd/src/main/java/io/github/dfa1/zstd/ZstdFrame.java +++ b/zstd/src/main/java/io/github/dfa1/zstd/ZstdFrame.java @@ -145,7 +145,7 @@ public static byte[] writeSkippableFrame(byte[] content, int magicVariant) { MemorySegment src = Zstd.copyIn(arena, content); long cap = content.length + 8L; MemorySegment dst = arena.allocate(cap); - long written = Zstd.call(() -> (long) Bindings.WRITE_SKIPPABLE_FRAME.invokeExact( + long written = NativeCall.checkReturnValue(() -> (long) Bindings.WRITE_SKIPPABLE_FRAME.invokeExact( dst, cap, src, (long) content.length, magicVariant)); return Zstd.copyOut(dst, written); } @@ -162,7 +162,7 @@ public static ZstdSkippableContent readSkippableFrame(byte[] frame) { MemorySegment src = Zstd.copyIn(arena, frame); MemorySegment magic = arena.allocate(JAVA_INT); MemorySegment dst = arena.allocate(Math.max(frame.length, 1)); - long written = Zstd.call(() -> (long) Bindings.READ_SKIPPABLE_FRAME.invokeExact( + long written = NativeCall.checkReturnValue(() -> (long) Bindings.READ_SKIPPABLE_FRAME.invokeExact( dst, (long) frame.length, magic, src, (long) frame.length)); return new ZstdSkippableContent(Zstd.copyOut(dst, written), magic.get(JAVA_INT, 0)); } @@ -171,7 +171,7 @@ public static ZstdSkippableContent readSkippableFrame(byte[] frame) { private static ZstdFrameHeader header(MemorySegment data, long size) { try (Arena arena = Arena.ofConfined()) { MemorySegment zfh = arena.allocate(48); - long remaining = Zstd.call(() -> (long) Bindings.GET_FRAME_HEADER.invokeExact(zfh, data, size)); + long remaining = NativeCall.checkReturnValue(() -> (long) Bindings.GET_FRAME_HEADER.invokeExact(zfh, data, size)); if (remaining != 0) { throw new ZstdException("incomplete frame header: need " + remaining + " more bytes"); } @@ -190,7 +190,7 @@ private static boolean isSkippableFrame(MemorySegment data, long size) { try { return ((int) Bindings.IS_SKIPPABLE_FRAME.invokeExact(data, size)) != 0; } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } } @@ -198,12 +198,12 @@ private static boolean isZstdFrame(MemorySegment data, long size) { try { return ((int) Bindings.IS_FRAME.invokeExact(data, size)) != 0; } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } } private static long compressedSize(MemorySegment data, long size) { - return Zstd.call(() -> (long) Bindings.FIND_FRAME_COMPRESSED_SIZE.invokeExact(data, size)); + return NativeCall.checkReturnValue(() -> (long) Bindings.FIND_FRAME_COMPRESSED_SIZE.invokeExact(data, size)); } private static long decompressedBound(MemorySegment data, long size) { @@ -211,7 +211,7 @@ private static long decompressedBound(MemorySegment data, long size) { try { bound = (long) Bindings.DECOMPRESS_BOUND.invokeExact(data, size); } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } if (bound == CONTENTSIZE_ERROR) { throw new ZstdException("not valid zstd data"); @@ -223,15 +223,10 @@ private static int dictId(MemorySegment data, long size) { try { return (int) Bindings.GET_DICT_ID_FROM_FRAME.invokeExact(data, size); } catch (Throwable t) { - throw rethrow(t); + throw NativeCall.rethrow(t); } } - @SuppressWarnings("unchecked") - private static RuntimeException rethrow(Throwable t) throws E { - throw (E) t; - } - private ZstdFrame() { // no instances } diff --git a/zstd/src/main/java/io/github/dfa1/zstd/ZstdInputStream.java b/zstd/src/main/java/io/github/dfa1/zstd/ZstdInputStream.java index 1380875..99a3eff 100644 --- a/zstd/src/main/java/io/github/dfa1/zstd/ZstdInputStream.java +++ b/zstd/src/main/java/io/github/dfa1/zstd/ZstdInputStream.java @@ -80,7 +80,7 @@ public ZstdInputStream(InputStream in, ZstdDictionary dictionary) { freeDctx(d); } arena.close(); - throw rethrow(t); + throw NativeCall.rethrow(t); } } @@ -96,7 +96,7 @@ private void loadDictionary(ZstdDictionary dictionary) { try (Arena staging = Arena.ofConfined()) { byte[] raw = dictionary.raw(); MemorySegment dictSeg = Zstd.copyIn(staging, raw); - Zstd.call(() -> (long) Bindings.DCTX_LOAD_DICTIONARY.invokeExact( + NativeCall.checkReturnValue(() -> (long) Bindings.DCTX_LOAD_DICTIONARY.invokeExact( dctx, dictSeg, (long) raw.length)); } } @@ -143,7 +143,7 @@ private boolean produce() throws IOException { inBuf.set(inSeg, r, 0); } outBufView.set(outSeg, outCap, 0); - lastHint = Zstd.call(() -> (long) Bindings.DECOMPRESS_STREAM.invokeExact( + lastHint = NativeCall.checkReturnValue(() -> (long) Bindings.DECOMPRESS_STREAM.invokeExact( dctx, outBufView.segment(), inBuf.segment())); int produced = Math.toIntExact(outBufView.pos()); if (produced > 0) { @@ -183,9 +183,4 @@ private void ensureOpen() throws IOException { throw new IOException("stream closed"); } } - - @SuppressWarnings("unchecked") - private static RuntimeException rethrow(Throwable t) throws E { - throw (E) t; - } } diff --git a/zstd/src/main/java/io/github/dfa1/zstd/ZstdOutputStream.java b/zstd/src/main/java/io/github/dfa1/zstd/ZstdOutputStream.java index 2a42e30..2f1b89c 100644 --- a/zstd/src/main/java/io/github/dfa1/zstd/ZstdOutputStream.java +++ b/zstd/src/main/java/io/github/dfa1/zstd/ZstdOutputStream.java @@ -83,7 +83,7 @@ public static ZstdOutputStream withPledgedSize(OutputStream out, int level, long } private void setPledgedSrcSize(long pledgedSrcSize) { - Zstd.call(() -> (long) Bindings.CCTX_SET_PLEDGED_SRC_SIZE.invokeExact(cctx, pledgedSrcSize)); + NativeCall.checkReturnValue(() -> (long) Bindings.CCTX_SET_PLEDGED_SRC_SIZE.invokeExact(cctx, pledgedSrcSize)); } /// Wraps `out`, compressing against `dictionary` at `level`. @@ -100,7 +100,7 @@ public ZstdOutputStream(OutputStream out, int level, ZstdDictionary dictionary) throw new ZstdException("ZSTD_createCCtx returned NULL"); } this.cctx = c; - Zstd.call(() -> (long) Bindings.CCTX_SET_PARAMETER.invokeExact( + NativeCall.checkReturnValue(() -> (long) Bindings.CCTX_SET_PARAMETER.invokeExact( cctx, ZSTD_C_COMPRESSION_LEVEL, level)); if (dictionary != null) { loadDictionary(dictionary); @@ -117,7 +117,7 @@ public ZstdOutputStream(OutputStream out, int level, ZstdDictionary dictionary) freeCctx(c); } arena.close(); - throw rethrow(t); + throw NativeCall.rethrow(t); } } @@ -133,7 +133,7 @@ private void loadDictionary(ZstdDictionary dictionary) { try (Arena staging = Arena.ofConfined()) { byte[] raw = dictionary.raw(); MemorySegment dictSeg = Zstd.copyIn(staging, raw); - Zstd.call(() -> (long) Bindings.CCTX_LOAD_DICTIONARY.invokeExact( + NativeCall.checkReturnValue(() -> (long) Bindings.CCTX_LOAD_DICTIONARY.invokeExact( cctx, dictSeg, (long) raw.length)); } } @@ -197,7 +197,7 @@ public void close() throws IOException { /// Returns the zstd "remaining" hint (0 means the directive is fully flushed). private long drainOutput(int directive) throws IOException { outBuf.set(outSeg, outCap, 0); - long remainingHint = Zstd.call(() -> (long) Bindings.COMPRESS_STREAM2.invokeExact( + long remainingHint = NativeCall.checkReturnValue(() -> (long) Bindings.COMPRESS_STREAM2.invokeExact( cctx, outBuf.segment(), in.segment(), directive)); int produced = Math.toIntExact(outBuf.pos()); if (produced > 0) { @@ -212,9 +212,4 @@ private void ensureOpen() throws IOException { throw new IOException("stream closed"); } } - - @SuppressWarnings("unchecked") - private static RuntimeException rethrow(Throwable t) throws E { - throw (E) t; - } }