diff --git a/.github/workflows/flow-deploy-release-artifact.yaml b/.github/workflows/flow-deploy-release-artifact.yaml index c6ba9228..6229fbc1 100644 --- a/.github/workflows/flow-deploy-release-artifact.yaml +++ b/.github/workflows/flow-deploy-release-artifact.yaml @@ -6,7 +6,7 @@ on: description: "Java JDK Version:" type: string required: false - default: "21.0.6" + default: "25.0.2" java-distribution: description: "Java JDK Distribution:" type: string @@ -103,7 +103,7 @@ jobs: uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 with: distribution: ${{ github.event.inputs.java-distribution || 'temurin' }} - java-version: ${{ github.event.inputs.java-version || '21.0.6' }} + java-version: ${{ github.event.inputs.java-version || '25.0.2' }} - name: Setup Gradle uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0 diff --git a/.github/workflows/zxc-compile-pbj-code.yaml b/.github/workflows/zxc-compile-pbj-code.yaml index 30eb25d8..ed10d244 100644 --- a/.github/workflows/zxc-compile-pbj-code.yaml +++ b/.github/workflows/zxc-compile-pbj-code.yaml @@ -26,7 +26,7 @@ on: description: "Java JDK Version:" type: string required: false - default: "21.0.6" + default: "25.0.2" gradle-version: description: "Gradle Version:" type: string diff --git a/pbj-core/gradle/toolchain-versions.properties b/pbj-core/gradle/toolchain-versions.properties index 75a5c2a8..6d973e8f 100644 --- a/pbj-core/gradle/toolchain-versions.properties +++ b/pbj-core/gradle/toolchain-versions.properties @@ -1,3 +1,2 @@ # SPDX-License-Identifier: Apache-2.0 - -jdk=21.0.6 +jdk=25.0.2 diff --git a/pbj-core/pbj-grpc-client-helidon/src/main/java/com/hedera/pbj/grpc/client/helidon/PbjGrpcClientConfig.java b/pbj-core/pbj-grpc-client-helidon/src/main/java/com/hedera/pbj/grpc/client/helidon/PbjGrpcClientConfig.java index ceecbf18..087d58f4 100644 --- a/pbj-core/pbj-grpc-client-helidon/src/main/java/com/hedera/pbj/grpc/client/helidon/PbjGrpcClientConfig.java +++ b/pbj-core/pbj-grpc-client-helidon/src/main/java/com/hedera/pbj/grpc/client/helidon/PbjGrpcClientConfig.java @@ -10,6 +10,19 @@ /** * Configuration for PBJ GRPC client. + * + * @param readTimeout A read timeout. Duration.ofSeconds(10) is a good default. + * @param tls TLS configuration. + * @param authority An optional authority string. + * @param contentType A content type, such as "application/grpc+proto" or "application/grpc+json". + * @param encoding Default GRPC encoding for sending data to remote peers, e.g. "identity", "gzip", etc. + * Note that the encoding must be registered as a `Compressor` with `GrpcCompression` to actually be supported, + * otherwise "identity" is used by default. + * If the remote peer doesn't support this encoding, then the sender is free to choose another one supported + * by both the remote peer and the `GrpcCompression`. + * @param acceptEncodings Accepted GRPC encodings for receiving data from remote peers which typically refer + * to compression algorithms, e.g. "identity", "gzip", etc. + * Note that the encoding must be registered as a `Decompressor` with `GrpcCompression` to actually be supported. * @param maxSize the maximum size of messages that the client is able to receive, defaults to Codec.DEFAULT_MAX_SIZE. * @param maxIncomingBufferSize the max size of an incoming buffer for receiving messages. Must be larger than * the `maxSize` to account for protobuf metadata as well as support high rate of ingress @@ -17,29 +30,11 @@ * Defaults to Codec.DEFAULT_MAX_SIZE * 5. */ public record PbjGrpcClientConfig( - /** A read timeout. Duration.ofSeconds(10) is a good default. */ Duration readTimeout, - /** TLS configuration. */ Tls tls, - /** An optional authority string. */ Optional authority, - /** A content type, such as "application/grpc+proto" or "application/grpc+json". */ String contentType, - - /** - * Default GRPC encoding for sending data to remote peers, e.g. "identity", "gzip", etc. - * Note that the encoding must be registered as a `Compressor` with `GrpcCompression` to actually be supported, - * otherwise "identity" is used by default. - * If the remote peer doesn't support this encoding, then the sender is free to choose another one supported - * by both the remote peer and the `GrpcCompression`. - */ String encoding, - - /** - * Accepted GRPC encodings for receiving data from remote peers which typically refer to compression algorithms, - * e.g. "identity", "gzip", etc. - * Note that the encoding must be registered as a `Decompressor` with `GrpcCompression` to actually be supported. - */ Set acceptEncodings, int maxSize, int maxIncomingBufferSize) { diff --git a/pbj-core/pbj-grpc-helidon/src/test/java/com/hedera/pbj/grpc/helidon/PbjTest.java b/pbj-core/pbj-grpc-helidon/src/test/java/com/hedera/pbj/grpc/helidon/PbjTest.java index 21598bd6..491db8c3 100644 --- a/pbj-core/pbj-grpc-helidon/src/test/java/com/hedera/pbj/grpc/helidon/PbjTest.java +++ b/pbj-core/pbj-grpc-helidon/src/test/java/com/hedera/pbj/grpc/helidon/PbjTest.java @@ -104,11 +104,9 @@ void setupEachRun() { PROXY.svc = new GreeterServiceImpl(); } - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // HTTP2 Tests // - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Nested class Http2Tests { @@ -284,7 +282,6 @@ void acceptEncodingExcludesAllSupportedEncodings() { } } - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // METADATA // // SPEC: @@ -316,11 +313,9 @@ void acceptEncodingExcludesAllSupportedEncodings() { // // TESTS: // - Not implemented - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Nested class MetadataTests {} - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Request-Headers // // SPEC: @@ -330,7 +325,6 @@ class MetadataTests {} // SETTINGS_MAX_HEADER_LIST_SIZE: the sum of all header fields, for each field the // sum of the uncompressed field name and value lengths plus 32, with binary values' // lengths being post-Base64. - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Nested class CompressionTests { @@ -688,9 +682,7 @@ public void run() { } } - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Utility methods - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private GrpcStatus grpcStatus(Http2ClientResponse response) { try { diff --git a/pbj-core/pbj-runtime/src/main/java/com/hedera/pbj/runtime/io/UnsafeUtils.java b/pbj-core/pbj-runtime/src/main/java/com/hedera/pbj/runtime/io/UnsafeUtils.java index 2f4b5505..ca745d5d 100644 --- a/pbj-core/pbj-runtime/src/main/java/com/hedera/pbj/runtime/io/UnsafeUtils.java +++ b/pbj-core/pbj-runtime/src/main/java/com/hedera/pbj/runtime/io/UnsafeUtils.java @@ -1,56 +1,32 @@ // SPDX-License-Identifier: Apache-2.0 package com.hedera.pbj.runtime.io; -import java.lang.reflect.Field; -import java.nio.Buffer; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import sun.misc.Unsafe; /** - * A set of utility methods on top of sun.misc.Unsafe + * A set of utility methods for faster access to memory of arrays and buffers. + * This class used to rely on sun.misc.Unsafe, but has since been migrated to use modern Java 22+ APIs. */ public class UnsafeUtils { - private static final Unsafe UNSAFE; - - /** - * Field offset of the byte[] class - */ - private static final int BYTE_ARRAY_BASE_OFFSET; - - /** - * Direct byte buffer "address" field offset. This is not the address of the buffer, - * but the offset of the field, which contains the address of the buffer - */ - private static final long DIRECT_BYTEBUFFER_ADDRESS_OFFSET; - - static { - try { - final Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe"); - theUnsafeField.setAccessible(true); - UNSAFE = (Unsafe) theUnsafeField.get(null); - BYTE_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(byte[].class); - final Field addressField = Buffer.class.getDeclaredField("address"); - DIRECT_BYTEBUFFER_ADDRESS_OFFSET = UNSAFE.objectFieldOffset(addressField); - } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { - throw new InternalError(e); - } - } + private static final VarHandle BYTE_ARRAY_HANDLE = MethodHandles.arrayElementVarHandle(byte[].class); + private static final VarHandle INT_LITTLE_ENDIAN_BYTE_ARRAY_VIEW_HANDLE = + MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.LITTLE_ENDIAN); + private static final VarHandle INT_BIG_ENDIAN_BYTE_ARRAY_VIEW_HANDLE = + MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.BIG_ENDIAN); + private static final VarHandle LONG_LITTLE_ENDIAN_BYTE_ARRAY_VIEW_HANDLE = + MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.LITTLE_ENDIAN); + private static final VarHandle LONG_BIG_ENDIAN_BYTE_ARRAY_VIEW_HANDLE = + MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.BIG_ENDIAN); private UnsafeUtils() {} - /** - * Get byte array element at a given offset. Identical to arr[offset]. - */ - public static byte getArrayByte(final byte[] arr, final int offset) { - if (arr.length <= offset) { - throw new IndexOutOfBoundsException(); - } - return getArrayByteNoChecks(arr, offset); - } - /** * Get byte array element at a given offset. Identical to arr[offset], but faster, * because no array bounds checks are performed. @@ -58,7 +34,7 @@ public static byte getArrayByte(final byte[] arr, final int offset) { *

Use with caution! */ public static byte getArrayByteNoChecks(final byte[] arr, final int offset) { - return UNSAFE.getByte(arr, BYTE_ARRAY_BASE_OFFSET + offset); + return (byte) BYTE_ARRAY_HANDLE.get(arr, offset); } /** @@ -79,7 +55,7 @@ public static byte getHeapBufferByte(final ByteBuffer buf, final int offset) { *

Use with caution! */ public static byte getHeapBufferByteNoChecks(final ByteBuffer buf, final int offset) { - return UNSAFE.getByte(buf.array(), BYTE_ARRAY_BASE_OFFSET + offset); + return (byte) BYTE_ARRAY_HANDLE.get(buf.array(), offset); } /** @@ -98,8 +74,7 @@ public static byte getDirectBufferByte(final ByteBuffer buf, final int offset) { * because no buffer range checks are performed. May only be called for direct byte buffers. */ public static byte getDirectBufferByteNoChecks(final ByteBuffer buf, final int offset) { - final long address = UNSAFE.getLong(buf, DIRECT_BYTEBUFFER_ADDRESS_OFFSET); - return UNSAFE.getByte(null, address + offset); + return MemorySegment.ofBuffer(buf.duplicate().clear()).get(ValueLayout.JAVA_BYTE, offset); } /** @@ -116,8 +91,9 @@ public static int getInt(final byte[] arr, final int offset, final ByteOrder byt if (arr.length < offset + Integer.BYTES) { throw new BufferUnderflowException(); } - final int value = UNSAFE.getInt(arr, BYTE_ARRAY_BASE_OFFSET + offset); - return byteOrder != ByteOrder.nativeOrder() ? Integer.reverseBytes(value) : value; + return byteOrder == ByteOrder.BIG_ENDIAN + ? (int) INT_BIG_ENDIAN_BYTE_ARRAY_VIEW_HANDLE.get(arr, offset) + : (int) INT_LITTLE_ENDIAN_BYTE_ARRAY_VIEW_HANDLE.get(arr, offset); } /** @@ -134,8 +110,9 @@ public static long getLong(final byte[] arr, final int offset, final ByteOrder b if (arr.length < offset + Long.BYTES) { throw new BufferUnderflowException(); } - final long value = UNSAFE.getLong(arr, BYTE_ARRAY_BASE_OFFSET + offset); - return byteOrder != ByteOrder.nativeOrder() ? Long.reverseBytes(value) : value; + return byteOrder == ByteOrder.BIG_ENDIAN + ? (long) LONG_BIG_ENDIAN_BYTE_ARRAY_VIEW_HANDLE.get(arr, offset) + : (long) LONG_LITTLE_ENDIAN_BYTE_ARRAY_VIEW_HANDLE.get(arr, offset); } /** @@ -144,8 +121,12 @@ public static long getLong(final byte[] arr, final int offset, final ByteOrder b */ public static void getHeapBufferToArray( final ByteBuffer buffer, final long offset, final byte[] dst, final int dstOffset, final int length) { - UNSAFE.copyMemory( - buffer.array(), BYTE_ARRAY_BASE_OFFSET + offset, dst, BYTE_ARRAY_BASE_OFFSET + dstOffset, length); + MemorySegment.copy( + MemorySegment.ofBuffer(buffer.duplicate().clear()), + offset, + MemorySegment.ofArray(dst), + dstOffset, + length); } /** @@ -154,8 +135,12 @@ public static void getHeapBufferToArray( */ public static void getDirectBufferToArray( final ByteBuffer buffer, final long offset, final byte[] dst, final int dstOffset, final int length) { - final long address = UNSAFE.getLong(buffer, DIRECT_BYTEBUFFER_ADDRESS_OFFSET); - UNSAFE.copyMemory(null, address + offset, dst, BYTE_ARRAY_BASE_OFFSET + dstOffset, length); + MemorySegment.copy( + MemorySegment.ofBuffer(buffer.duplicate().clear()), + offset, + MemorySegment.ofArray(dst), + dstOffset, + length); } /** @@ -164,9 +149,12 @@ public static void getDirectBufferToArray( */ public static void getDirectBufferToDirectBuffer( final ByteBuffer buffer, final long offset, final ByteBuffer dst, final int dstOffset, final int length) { - final long address = UNSAFE.getLong(buffer, DIRECT_BYTEBUFFER_ADDRESS_OFFSET); - final long dstAddress = UNSAFE.getLong(dst, DIRECT_BYTEBUFFER_ADDRESS_OFFSET); - UNSAFE.copyMemory(null, address + offset, null, dstAddress, length); + MemorySegment.copy( + MemorySegment.ofBuffer(buffer.duplicate().clear()), + offset, + MemorySegment.ofBuffer(dst.duplicate().clear()), + dstOffset, + length); } /** @@ -174,7 +162,11 @@ public static void getDirectBufferToDirectBuffer( */ public static void putByteArrayToDirectBuffer( final ByteBuffer buffer, final long offset, final byte[] src, final int srcOffset, final int length) { - final long address = UNSAFE.getLong(buffer, DIRECT_BYTEBUFFER_ADDRESS_OFFSET); - UNSAFE.copyMemory(src, BYTE_ARRAY_BASE_OFFSET + srcOffset, null, address + offset, length); + MemorySegment.copy( + MemorySegment.ofArray(src), + srcOffset, + MemorySegment.ofBuffer(buffer.duplicate().clear()), + offset, + length); } } diff --git a/pbj-core/version.txt b/pbj-core/version.txt index 52bba554..4f3da74f 100644 --- a/pbj-core/version.txt +++ b/pbj-core/version.txt @@ -1 +1 @@ -0.12.0-SNAPSHOT +0.15.0-SNAPSHOT diff --git a/pbj-integration-tests/gradle/toolchain-versions.properties b/pbj-integration-tests/gradle/toolchain-versions.properties index 75a5c2a8..6d973e8f 100644 --- a/pbj-integration-tests/gradle/toolchain-versions.properties +++ b/pbj-integration-tests/gradle/toolchain-versions.properties @@ -1,3 +1,2 @@ # SPDX-License-Identifier: Apache-2.0 - -jdk=21.0.6 +jdk=25.0.2