Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/flow-deploy-release-artifact.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/zxc-compile-pbj-code.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 1 addition & 2 deletions pbj-core/gradle/toolchain-versions.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
# SPDX-License-Identifier: Apache-2.0

jdk=21.0.6
jdk=25.0.2
Original file line number Diff line number Diff line change
Expand Up @@ -10,36 +10,31 @@

/**
* 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
* of multiple messages, especially in case of server or bidi streaming.
* 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<String> 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<String> acceptEncodings,
int maxSize,
int maxIncomingBufferSize) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,9 @@ void setupEachRun() {
PROXY.svc = new GreeterServiceImpl();
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// HTTP2 Tests
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////

@Nested
class Http2Tests {
Expand Down Expand Up @@ -284,7 +282,6 @@ void acceptEncodingExcludesAllSupportedEncodings() {
}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// METADATA
//
// SPEC:
Expand Down Expand Up @@ -316,11 +313,9 @@ void acceptEncodingExcludesAllSupportedEncodings() {
//
// TESTS:
// - Not implemented
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@Nested
class MetadataTests {}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Request-Headers
//
// SPEC:
Expand All @@ -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 {
Expand Down Expand Up @@ -688,9 +682,7 @@ public void run() {
}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Utility methods
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////

private GrpcStatus grpcStatus(Http2ClientResponse response) {
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,64 +1,40 @@
// 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.
*
* <p><b>Use with caution!</b>
*/
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);
}

/**
Expand All @@ -79,7 +55,7 @@ public static byte getHeapBufferByte(final ByteBuffer buf, final int offset) {
* <p><b>Use with caution!</b>
*/
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);
}

/**
Expand All @@ -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);
}

/**
Expand All @@ -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);
}

/**
Expand All @@ -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);
}

/**
Expand All @@ -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);
}

/**
Expand All @@ -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);
}

/**
Expand All @@ -164,17 +149,24 @@ 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);
}

/**
* Copies a byte array to a direct byte buffer. May only be called for direct byte buffers
*/
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);
}
}
2 changes: 1 addition & 1 deletion pbj-core/version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.12.0-SNAPSHOT
0.15.0-SNAPSHOT
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
# SPDX-License-Identifier: Apache-2.0

jdk=21.0.6
jdk=25.0.2
Loading