At this time, this can be tested with: openjdk:22-slim-bookworm
$ docker pull openjdk:22-slim-bookworm
More images: https://hub.docker.com/_/openjdk/
Linker LINKER = Linker.nativeLinker();
SymbolLookup SYMBOL_LOOKUP = LINKER.defaultLookup();
var getpid = LINKER.downcallHandle(
SYMBOL_LOOKUP.find("getpid").orElseThrow(),
FunctionDescriptor.of(ValueLayout.JAVA_LONG) // (1)
);
-
getpid
returns the pid of the process
var printf = LINKER.downcallHandle(
SYMBOL_LOOKUP.find("printf").orElseThrow(),
FunctionDescriptor.of(
ValueLayout.JAVA_LONG,
ValueLayout.ADDRESS // (1)
)
);
-
The printf function takes a pointer to a memory address, note the carrier type of the
ADDRESS
layout is aMemorySegment
.
try (var arena = Arena.ofConfined()) { // (1)
var memorySegment = arena.allocateFrom(str); // (2)
return (long) printf.invoke(memorySegment); // (3)
}
try (var scope = ResourceScope.newConfinedScope()) {
var allocator = SegmentAllocator.nativeAllocator(scope);
var memorySegment = allocator.allocateFrom(str); // (2)
return (long) printf.invoke(memorySegment.address()); // (3)
}
-
Create an arena confimed to this try-with-resources block
-
Copy the Java String to an off-heap memory segment
-
Invoke the
printf
function
Tip
|
If an The message gives hints about misuse.
In this case, the difference is in the type of the argument, a `MemorySegment` is expected because it is the carrier type for a |
Caution
|
Mind the invoke method |
Credits to Carl Dea.
nm touchid-swift-lib/build/lib/main/release/shared/macos/libTouchIdDemoLib.dylib
Show JAVA_LIBRARY_PATH
System.loadLibrary("TouchIdDemoLib");
var authenticate_user = LINKER.downcallHandle(
SYMBOL_LOOKUP.lookup("authenticate_user_touchid").get(),
FunctionDescriptor.ofVoid()
);
// no arena needed here
authenticate_user.invoke();
cd c
# building the intrinsics-based implementations
gcc -c -fPIC -O3 -msse2 blake3_sse2.c -o blake3_sse2.o
gcc -c -fPIC -O3 -msse4.1 blake3_sse41.c -o blake3_sse41.o
gcc -c -fPIC -O3 -mavx2 blake3_avx2.c -o blake3_avx2.o
gcc -c -fPIC -O3 -mavx512f -mavx512vl blake3_avx512.c -o blake3_avx512.o
gcc -shared -O3 -o libblake3.so blake3.c blake3_dispatch.c blake3_portable.c \
blake3_avx2.o blake3_avx512.o blake3_sse41.o blake3_sse2.o
The Gradle project is set up to invoke jextract
automatically, just update the jextract_home
property.
Note
|
Download Or, for unreleased JDKs, build form source https://github.com/openjdk/jextract. Choose available branch, e.g. sh ./gradlew \
-Pjdk22_home=$HOME/.asdf/installs/java/openjdk-22-ea+26 \
-Pllvm_home=$(brew --prefix llvm) \
clean verify Then set the path to the |
This is still possible to run jextract
manually.
jextract
manuallyalias jextract=$HOME/opensource/jextract/build/jextract/bin/jextract
jextract \
-d ffm-blake3/build/generated/sources/jextract-blake3/java \
--source \
--target-package blake3 \
-I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include \
~/opensource/BLAKE3/c/blake3.h
--include-typedef blake3_chunk_state
--include-typedef blake3_hasher
--include-constant BLAKE3_BLOCK_LEN
--include-constant BLAKE3_CHUNK_LEN
--include-constant BLAKE3_KEY_LEN
--include-constant BLAKE3_MAX_DEPTH
--include-constant BLAKE3_OUT_LEN
--include-constant BLAKE3_VERSION_STRING
--include-function blake3_hasher_finalize
--include-function blake3_hasher_finalize_seek
--include-function blake3_hasher_init
--include-function blake3_hasher_init_derive_key
--include-function blake3_hasher_init_derive_key_raw
--include-function blake3_hasher_init_keyed
--include-function blake3_hasher_reset
--include-function blake3_hasher_update
--include-function blake3_version
System.load("/Users/brice.dutheil/opensource/BLAKE3/c/libblake3.so");
try (var arena = Arena.ofConfined()) {
}
var hasher = blake3_hasher.allocate(scope); // (1)
blake3_h.blake3_hasher_init(hasher); // (2)
-
blake3_hasher
is a specific data structure -
blake3_hasher_init
is a function that initializes the hasher
var content = arena.allocateFrom("Hello panama!\n", StandardCharsets.US_ASCII);
blake3_h.blake3_hasher_update(hasher, content, content.byteSize() - 1);
var out = arena.allocate(
MemoryLayout.sequenceLayout(
blake3_h.BLAKE3_OUT_LEN(),
ValueLayout.JAVA_BYTE
)
);
blake3_h.blake3_hasher_finalize(hasher, out, blake3_h.BLAKE3_OUT_LEN());
A memory segment for a file can only be obtained from a FileChannel
try (Arena arena = Arena.ofConfined();
FileChannel channel = FileChannel.open(path)) {
// ...
}
Re-using the blake3 hasher from above, the byte array is reused.
var content = MemorySegment.mapFile(
path,
0,
Files.size(path),
MapMode.READ_ONLY,
scope
);
blake3_h.blake3_hasher_update(hasher, content, content.byteSize());