diff --git a/java/fory-core/src/main/java/org/apache/fory/builder/CodecBuilder.java b/java/fory-core/src/main/java/org/apache/fory/builder/CodecBuilder.java index 3846cf1f1b..7544fc2353 100644 --- a/java/fory-core/src/main/java/org/apache/fory/builder/CodecBuilder.java +++ b/java/fory-core/src/main/java/org/apache/fory/builder/CodecBuilder.java @@ -20,6 +20,7 @@ package org.apache.fory.builder; import static org.apache.fory.codegen.Expression.Invoke.inlineInvoke; +import static org.apache.fory.platform.JdkVersion.JDK8_ARM; import static org.apache.fory.type.TypeUtils.CLASS_TYPE; import static org.apache.fory.type.TypeUtils.OBJECT_ARRAY_TYPE; import static org.apache.fory.type.TypeUtils.OBJECT_TYPE; @@ -59,6 +60,7 @@ import org.apache.fory.memory.NativeByteOrder; import org.apache.fory.platform.GraalvmSupport; import org.apache.fory.platform.JdkVersion; +import org.apache.fory.platform.internal._UnsafeUtils; import org.apache.fory.reflect.ObjectInstantiator; import org.apache.fory.reflect.ObjectInstantiators; import org.apache.fory.reflect.ReflectionUtils; @@ -787,27 +789,45 @@ protected Expression unsafePutBoolean(Expression base, Expression pos, Expressio } protected Expression unsafePutChar(Expression base, Expression pos, Expression value) { + if (JDK8_ARM) { + return new StaticInvoke(_UnsafeUtils.class, "putChar", base, pos, value); + } return unsafeInvoke("putChar", base, pos, value); } protected Expression unsafePutShort(Expression base, Expression pos, Expression value) { + if (JDK8_ARM) { + return new StaticInvoke(_UnsafeUtils.class, "putShort", base, pos, value); + } return unsafeInvoke("putShort", base, pos, value); } protected Expression unsafePutInt(Expression base, Expression pos, Expression value) { + if (JDK8_ARM) { + return new StaticInvoke(_UnsafeUtils.class, "putInt", base, pos, value); + } return unsafeInvoke("putInt", base, pos, value); } protected Expression unsafePutLong(Expression base, Expression pos, Expression value) { + if (JDK8_ARM) { + return new StaticInvoke(_UnsafeUtils.class, "putLong", base, pos, value); + } return unsafeInvoke("putLong", base, pos, value); } protected Expression unsafePutFloat(Expression base, Expression pos, Expression value) { + if (JDK8_ARM) { + return new StaticInvoke(_UnsafeUtils.class, "putFloat", base, pos, value); + } return unsafeInvoke("putFloat", base, pos, value); } /** Build unsafePutDouble operation. */ protected Expression unsafePutDouble(Expression base, Expression pos, Expression value) { + if (JDK8_ARM) { + return new StaticInvoke(_UnsafeUtils.class, "putDouble", base, pos, value); + } return unsafeInvoke("putDouble", base, pos, value); } @@ -821,7 +841,10 @@ protected Expression unsafeGetBoolean(Expression base, Expression pos) { } protected Expression unsafeGetChar(Expression base, Expression pos) { - Inlineable expr = unsafeInvoke("getChar", PRIMITIVE_CHAR_TYPE, base, pos); + Inlineable expr = + JDK8_ARM + ? new StaticInvoke(_UnsafeUtils.class, "getChar", PRIMITIVE_CHAR_TYPE, base, pos) + : unsafeInvoke("getChar", PRIMITIVE_CHAR_TYPE, base, pos); if (!NativeByteOrder.IS_LITTLE_ENDIAN) { expr = new StaticInvoke(Character.class, "reverseBytes", PRIMITIVE_CHAR_TYPE, expr.inline()); } @@ -829,7 +852,10 @@ protected Expression unsafeGetChar(Expression base, Expression pos) { } protected Expression unsafeGetShort(Expression base, Expression pos) { - Inlineable expr = unsafeInvoke("getShort", PRIMITIVE_SHORT_TYPE, base, pos); + Inlineable expr = + JDK8_ARM + ? new StaticInvoke(_UnsafeUtils.class, "getShort", PRIMITIVE_SHORT_TYPE, base, pos) + : unsafeInvoke("getShort", PRIMITIVE_SHORT_TYPE, base, pos); if (!NativeByteOrder.IS_LITTLE_ENDIAN) { expr = new StaticInvoke(Short.class, "reverseBytes", PRIMITIVE_SHORT_TYPE, expr.inline()); } @@ -837,7 +863,10 @@ protected Expression unsafeGetShort(Expression base, Expression pos) { } protected Expression unsafeGetInt(Expression base, Expression pos) { - Inlineable expr = unsafeInvoke("getInt", PRIMITIVE_INT_TYPE, base, pos); + Inlineable expr = + JDK8_ARM + ? new StaticInvoke(_UnsafeUtils.class, "getInt", PRIMITIVE_INT_TYPE, base, pos) + : unsafeInvoke("getInt", PRIMITIVE_INT_TYPE, base, pos); if (!NativeByteOrder.IS_LITTLE_ENDIAN) { expr = new StaticInvoke(Integer.class, "reverseBytes", PRIMITIVE_INT_TYPE, expr.inline()); } @@ -845,7 +874,10 @@ protected Expression unsafeGetInt(Expression base, Expression pos) { } protected Expression unsafeGetLong(Expression base, Expression pos) { - Inlineable expr = unsafeInvoke("getLong", PRIMITIVE_LONG_TYPE, base, pos); + Inlineable expr = + JDK8_ARM + ? new StaticInvoke(_UnsafeUtils.class, "getLong", PRIMITIVE_LONG_TYPE, base, pos) + : unsafeInvoke("getLong", PRIMITIVE_LONG_TYPE, base, pos); if (!NativeByteOrder.IS_LITTLE_ENDIAN) { expr = new StaticInvoke(Long.class, "reverseBytes", PRIMITIVE_LONG_TYPE, expr.inline()); } @@ -853,7 +885,10 @@ protected Expression unsafeGetLong(Expression base, Expression pos) { } protected Expression unsafeGetFloat(Expression base, Expression pos) { - Inlineable expr = unsafeInvoke("getInt", PRIMITIVE_INT_TYPE, base, pos); + Inlineable expr = + JDK8_ARM + ? new StaticInvoke(_UnsafeUtils.class, "getInt", PRIMITIVE_INT_TYPE, base, pos) + : unsafeInvoke("getInt", PRIMITIVE_INT_TYPE, base, pos); if (!NativeByteOrder.IS_LITTLE_ENDIAN) { expr = new StaticInvoke(Integer.class, "reverseBytes", PRIMITIVE_INT_TYPE, expr.inline()); } @@ -861,7 +896,10 @@ protected Expression unsafeGetFloat(Expression base, Expression pos) { } protected Expression unsafeGetDouble(Expression base, Expression pos) { - Inlineable expr = unsafeInvoke("getLong", PRIMITIVE_LONG_TYPE, base, pos); + Inlineable expr = + JDK8_ARM + ? new StaticInvoke(_UnsafeUtils.class, "getLong", PRIMITIVE_LONG_TYPE, base, pos) + : unsafeInvoke("getLong", PRIMITIVE_LONG_TYPE, base, pos); if (!NativeByteOrder.IS_LITTLE_ENDIAN) { expr = new StaticInvoke(Long.class, "reverseBytes", PRIMITIVE_LONG_TYPE, expr.inline()); } diff --git a/java/fory-core/src/main/java/org/apache/fory/memory/LittleEndian.java b/java/fory-core/src/main/java/org/apache/fory/memory/LittleEndian.java index fccbe3ab87..035c381a9e 100644 --- a/java/fory-core/src/main/java/org/apache/fory/memory/LittleEndian.java +++ b/java/fory-core/src/main/java/org/apache/fory/memory/LittleEndian.java @@ -1,5 +1,7 @@ package org.apache.fory.memory; +import static org.apache.fory.platform.JdkVersion.JDK8_ARM; + import org.apache.fory.platform.AndroidSupport; import org.apache.fory.platform.internal._UnsafeUtils; import sun.misc.Unsafe; @@ -80,9 +82,13 @@ public static long getInt64(byte[] o, int index) { if (AndroidSupport.IS_ANDROID) { return MemoryOps.getInt64(o, index); } - // Unsafe object offsets are long. Keep the cast so JDK8-compiled bytecode calls - // getLong(Object, long) when the artifact runs on JDK9+. - long v = UNSAFE.getLong(o, (long) BYTE_ARRAY_OFFSET + index); + // Compute the absolute array offset in long so large indices cannot overflow the addition. + long v; + if (JDK8_ARM) { + v = _UnsafeUtils.getLongFromInts(o, (long) BYTE_ARRAY_OFFSET + index); + } else { + v = UNSAFE.getLong(o, (long) BYTE_ARRAY_OFFSET + index); + } return NativeByteOrder.IS_LITTLE_ENDIAN ? v : Long.reverseBytes(v); } @@ -90,7 +96,12 @@ public static int getInt32(byte[] o, int index) { if (AndroidSupport.IS_ANDROID) { return MemoryOps.getInt32(o, index); } - int v = UNSAFE.getInt(o, (long) BYTE_ARRAY_OFFSET + index); + int v; + if (JDK8_ARM) { + v = _UnsafeUtils.getIntFromShorts(o, (long) BYTE_ARRAY_OFFSET + index); + } else { + v = UNSAFE.getInt(o, (long) BYTE_ARRAY_OFFSET + index); + } return NativeByteOrder.IS_LITTLE_ENDIAN ? v : Integer.reverseBytes(v); } @@ -102,7 +113,11 @@ public static void putInt32(byte[] o, int index, int value) { if (!NativeByteOrder.IS_LITTLE_ENDIAN) { value = Integer.reverseBytes(value); } - UNSAFE.putInt(o, (long) BYTE_ARRAY_OFFSET + index, value); + if (JDK8_ARM) { + _UnsafeUtils.putIntAsShorts(o, (long) BYTE_ARRAY_OFFSET + index, value); + } else { + UNSAFE.putInt(o, (long) BYTE_ARRAY_OFFSET + index, value); + } } public static void putInt64(byte[] o, int index, long value) { @@ -113,7 +128,11 @@ public static void putInt64(byte[] o, int index, long value) { if (!NativeByteOrder.IS_LITTLE_ENDIAN) { value = Long.reverseBytes(value); } - // See getInt64: the cast controls the Unsafe method descriptor in bytecode. - UNSAFE.putLong(o, (long) BYTE_ARRAY_OFFSET + index, value); + // As in getInt64, widen before adding the array base offset. + if (JDK8_ARM) { + _UnsafeUtils.putLongAsInts(o, (long) BYTE_ARRAY_OFFSET + index, value); + } else { + UNSAFE.putLong(o, (long) BYTE_ARRAY_OFFSET + index, value); + } } } diff --git a/java/fory-core/src/main/java/org/apache/fory/memory/MemoryBuffer.java b/java/fory-core/src/main/java/org/apache/fory/memory/MemoryBuffer.java index 8738beab9d..5260009309 100644 --- a/java/fory-core/src/main/java/org/apache/fory/memory/MemoryBuffer.java +++ b/java/fory-core/src/main/java/org/apache/fory/memory/MemoryBuffer.java @@ -18,6 +18,7 @@ package org.apache.fory.memory; +import static org.apache.fory.platform.JdkVersion.JDK8_ARM; import static org.apache.fory.util.Preconditions.checkArgument; import static org.apache.fory.util.Preconditions.checkNotNull; @@ -642,7 +643,12 @@ public char getChar(int index) { } final long pos = address + index; checkPosition(index, pos, 2); - char c = UNSAFE.getChar(heapMemory, pos); + char c; + if (JDK8_ARM) { + c = (char) _UnsafeUtils.getShortBytes(heapMemory, pos); + } else { + c = UNSAFE.getChar(heapMemory, pos); + } return LITTLE_ENDIAN ? c : Character.reverseBytes(c); } @@ -652,7 +658,12 @@ public char _unsafeGetChar(int index) { if (AndroidSupport.IS_ANDROID) { return MemoryOps.unsafeGetChar(this, index); } - char c = UNSAFE.getChar(heapMemory, address + index); + char c; + if (JDK8_ARM) { + c = (char) _UnsafeUtils.getShortBytes(heapMemory, address + index); + } else { + c = UNSAFE.getChar(heapMemory, address + index); + } return LITTLE_ENDIAN ? c : Character.reverseBytes(c); } @@ -665,7 +676,11 @@ public void putChar(int index, char value) { if (!LITTLE_ENDIAN) { value = Character.reverseBytes(value); } - UNSAFE.putChar(heapMemory, pos, value); + if (JDK8_ARM) { + _UnsafeUtils.putShortBytes(heapMemory, pos, (short) value); + } else { + UNSAFE.putChar(heapMemory, pos, value); + } } } @@ -678,7 +693,11 @@ public void _unsafePutChar(int index, char value) { if (!LITTLE_ENDIAN) { value = Character.reverseBytes(value); } - UNSAFE.putChar(heapMemory, address + index, value); + if (JDK8_ARM) { + _UnsafeUtils.putShortBytes(heapMemory, address + index, (short) value); + } else { + UNSAFE.putChar(heapMemory, address + index, value); + } } } @@ -688,7 +707,12 @@ public short getInt16(int index) { } final long pos = address + index; checkPosition(index, pos, 2); - short v = UNSAFE.getShort(heapMemory, pos); + short v; + if (JDK8_ARM) { + v = _UnsafeUtils.getShortBytes(heapMemory, pos); + } else { + v = UNSAFE.getShort(heapMemory, pos); + } return LITTLE_ENDIAN ? v : Short.reverseBytes(v); } @@ -701,7 +725,11 @@ public void putInt16(int index, short value) { if (!LITTLE_ENDIAN) { value = Short.reverseBytes(value); } - UNSAFE.putShort(heapMemory, pos, value); + if (JDK8_ARM) { + _UnsafeUtils.putShortBytes(heapMemory, pos, value); + } else { + UNSAFE.putShort(heapMemory, pos, value); + } } } @@ -711,7 +739,12 @@ public short _unsafeGetInt16(int index) { if (AndroidSupport.IS_ANDROID) { return MemoryOps.unsafeGetInt16(this, index); } - short v = UNSAFE.getShort(heapMemory, address + index); + short v; + if (JDK8_ARM) { + v = _UnsafeUtils.getShortBytes(heapMemory, address + index); + } else { + v = UNSAFE.getShort(heapMemory, address + index); + } return LITTLE_ENDIAN ? v : Short.reverseBytes(v); } @@ -724,7 +757,11 @@ public void _unsafePutInt16(int index, short value) { if (!LITTLE_ENDIAN) { value = Short.reverseBytes(value); } - UNSAFE.putShort(heapMemory, address + index, value); + if (JDK8_ARM) { + _UnsafeUtils.putShortBytes(heapMemory, address + index, value); + } else { + UNSAFE.putShort(heapMemory, address + index, value); + } } } @@ -734,7 +771,12 @@ public int getInt32(int index) { } final long pos = address + index; checkPosition(index, pos, 4); - int v = UNSAFE.getInt(heapMemory, pos); + int v; + if (JDK8_ARM) { + v = _UnsafeUtils.getIntFromShorts(heapMemory, pos); + } else { + v = UNSAFE.getInt(heapMemory, pos); + } return LITTLE_ENDIAN ? v : Integer.reverseBytes(v); } @@ -747,7 +789,11 @@ public void putInt32(int index, int value) { if (!LITTLE_ENDIAN) { value = Integer.reverseBytes(value); } - UNSAFE.putInt(heapMemory, pos, value); + if (JDK8_ARM) { + _UnsafeUtils.putIntAsShorts(heapMemory, pos, value); + } else { + UNSAFE.putInt(heapMemory, pos, value); + } } } @@ -757,7 +803,12 @@ public int _unsafeGetInt32(int index) { if (AndroidSupport.IS_ANDROID) { return MemoryOps.unsafeGetInt32(this, index); } - int v = UNSAFE.getInt(heapMemory, address + index); + int v; + if (JDK8_ARM) { + v = _UnsafeUtils.getIntFromShorts(heapMemory, address + index); + } else { + v = UNSAFE.getInt(heapMemory, address + index); + } return LITTLE_ENDIAN ? v : Integer.reverseBytes(v); } @@ -770,7 +821,11 @@ public void _unsafePutInt32(int index, int value) { if (!LITTLE_ENDIAN) { value = Integer.reverseBytes(value); } - UNSAFE.putInt(heapMemory, address + index, value); + if (JDK8_ARM) { + _UnsafeUtils.putIntAsShorts(heapMemory, address + index, value); + } else { + UNSAFE.putInt(heapMemory, address + index, value); + } } } @@ -780,7 +835,12 @@ public long getInt64(int index) { } final long pos = address + index; checkPosition(index, pos, 8); - long v = UNSAFE.getLong(heapMemory, pos); + long v; + if (JDK8_ARM) { + v = _UnsafeUtils.getLongFromInts(heapMemory, pos); + } else { + v = UNSAFE.getLong(heapMemory, pos); + } return LITTLE_ENDIAN ? v : Long.reverseBytes(v); } @@ -793,7 +853,11 @@ public void putInt64(int index, long value) { if (!LITTLE_ENDIAN) { value = Long.reverseBytes(value); } - UNSAFE.putLong(heapMemory, pos, value); + if (JDK8_ARM) { + _UnsafeUtils.putLongAsInts(heapMemory, pos, value); + } else { + UNSAFE.putLong(heapMemory, pos, value); + } } } @@ -803,7 +867,12 @@ public long _unsafeGetInt64(int index) { if (AndroidSupport.IS_ANDROID) { return MemoryOps.unsafeGetInt64(this, index); } - long v = UNSAFE.getLong(heapMemory, address + index); + long v; + if (JDK8_ARM) { + v = _UnsafeUtils.getLongFromInts(heapMemory, address + index); + } else { + v = UNSAFE.getLong(heapMemory, address + index); + } return LITTLE_ENDIAN ? v : Long.reverseBytes(v); } @@ -816,7 +885,11 @@ public void _unsafePutInt64(int index, long value) { if (!LITTLE_ENDIAN) { value = Long.reverseBytes(value); } - UNSAFE.putLong(heapMemory, address + index, value); + if (JDK8_ARM) { + _UnsafeUtils.putLongAsInts(heapMemory, address + index, value); + } else { + UNSAFE.putLong(heapMemory, address + index, value); + } } } @@ -826,7 +899,12 @@ public float getFloat32(int index) { } final long pos = address + index; checkPosition(index, pos, 4); - int v = UNSAFE.getInt(heapMemory, pos); + int v; + if (JDK8_ARM) { + v = _UnsafeUtils.getIntFromShorts(heapMemory, pos); + } else { + v = UNSAFE.getInt(heapMemory, pos); + } if (!LITTLE_ENDIAN) { v = Integer.reverseBytes(v); } @@ -843,7 +921,11 @@ public void putFloat32(int index, float value) { if (!LITTLE_ENDIAN) { v = Integer.reverseBytes(v); } - UNSAFE.putInt(heapMemory, pos, v); + if (JDK8_ARM) { + _UnsafeUtils.putIntAsShorts(heapMemory, pos, v); + } else { + UNSAFE.putInt(heapMemory, pos, v); + } } } @@ -853,7 +935,12 @@ public double getFloat64(int index) { } final long pos = address + index; checkPosition(index, pos, 8); - long v = UNSAFE.getLong(heapMemory, pos); + long v; + if (JDK8_ARM) { + v = _UnsafeUtils.getLongFromInts(heapMemory, pos); + } else { + v = UNSAFE.getLong(heapMemory, pos); + } if (!LITTLE_ENDIAN) { v = Long.reverseBytes(v); } @@ -870,7 +957,11 @@ public void putFloat64(int index, double value) { if (!LITTLE_ENDIAN) { v = Long.reverseBytes(v); } - UNSAFE.putLong(heapMemory, pos, v); + if (JDK8_ARM) { + _UnsafeUtils.putLongAsInts(heapMemory, pos, v); + } else { + UNSAFE.putLong(heapMemory, pos, v); + } } } @@ -1004,7 +1095,11 @@ public void writeChar(char value) { if (!LITTLE_ENDIAN) { value = Character.reverseBytes(value); } - UNSAFE.putChar(heapMemory, pos, value); + if (JDK8_ARM) { + _UnsafeUtils.putShortBytes(heapMemory, pos, (short) value); + } else { + UNSAFE.putChar(heapMemory, pos, value); + } writerIndex = newIdx; } } @@ -1019,7 +1114,11 @@ public void writeInt16(short value) { if (!LITTLE_ENDIAN) { value = Short.reverseBytes(value); } - UNSAFE.putShort(heapMemory, address + writerIdx, value); + if (JDK8_ARM) { + _UnsafeUtils.putShortBytes(heapMemory, address + writerIdx, value); + } else { + UNSAFE.putShort(heapMemory, address + writerIdx, value); + } writerIndex = newIdx; } } @@ -1034,7 +1133,11 @@ public void writeInt32(int value) { if (!LITTLE_ENDIAN) { value = Integer.reverseBytes(value); } - UNSAFE.putInt(heapMemory, address + writerIdx, value); + if (JDK8_ARM) { + _UnsafeUtils.putIntAsShorts(heapMemory, address + writerIdx, value); + } else { + UNSAFE.putInt(heapMemory, address + writerIdx, value); + } writerIndex = newIdx; } } @@ -1049,7 +1152,11 @@ public void writeInt64(long value) { if (!LITTLE_ENDIAN) { value = Long.reverseBytes(value); } - UNSAFE.putLong(heapMemory, address + writerIdx, value); + if (JDK8_ARM) { + _UnsafeUtils.putLongAsInts(heapMemory, address + writerIdx, value); + } else { + UNSAFE.putLong(heapMemory, address + writerIdx, value); + } writerIndex = newIdx; } } @@ -1065,7 +1172,11 @@ public void writeFloat32(float value) { if (!LITTLE_ENDIAN) { v = Integer.reverseBytes(v); } - UNSAFE.putInt(heapMemory, address + writerIdx, v); + if (JDK8_ARM) { + _UnsafeUtils.putIntAsShorts(heapMemory, address + writerIdx, v); + } else { + UNSAFE.putInt(heapMemory, address + writerIdx, v); + } writerIndex = newIdx; } } @@ -1081,7 +1192,11 @@ public void writeFloat64(double value) { if (!LITTLE_ENDIAN) { v = Long.reverseBytes(v); } - UNSAFE.putLong(heapMemory, address + writerIdx, v); + if (JDK8_ARM) { + _UnsafeUtils.putLongAsInts(heapMemory, address + writerIdx, v); + } else { + UNSAFE.putLong(heapMemory, address + writerIdx, v); + } writerIndex = newIdx; } } @@ -1181,7 +1296,11 @@ private int continueWriteVarUInt32Small7(int value) { return diff; } if (value >>> 14 == 0) { - UNSAFE.putInt(heapMemory, address + writerIdx, encoded); + if (JDK8_ARM) { + _UnsafeUtils.putIntAsShorts(heapMemory, address + writerIdx, encoded); + } else { + UNSAFE.putInt(heapMemory, address + writerIdx, encoded); + } writerIndex += 2; return 2; } @@ -1217,7 +1336,11 @@ public int _unsafePutVarUInt32(int index, int value) { return putVarUInt32BigEndian(index, encoded, value); } if (value >>> 14 == 0) { - UNSAFE.putInt(heapMemory, address + index, encoded); + if (JDK8_ARM) { + _UnsafeUtils.putIntAsShorts(heapMemory, address + index, encoded); + } else { + UNSAFE.putInt(heapMemory, address + index, encoded); + } return 2; } return continuePutVarUInt32(index, encoded, value); @@ -1227,26 +1350,42 @@ private int continuePutVarUInt32(int index, int encoded, int value) { // 0x1fc000: 0b1111111 << 14 encoded |= (((value & 0x1fc000) << 2) | 0x8000); if (value >>> 21 == 0) { - UNSAFE.putInt(heapMemory, address + index, encoded); + if (JDK8_ARM) { + _UnsafeUtils.putIntAsShorts(heapMemory, address + index, encoded); + } else { + UNSAFE.putInt(heapMemory, address + index, encoded); + } return 3; } // 0xfe00000: 0b1111111 << 21 encoded |= ((value & 0xfe00000) << 3) | 0x800000; if (value >>> 28 == 0) { - UNSAFE.putInt(heapMemory, address + index, encoded); + if (JDK8_ARM) { + _UnsafeUtils.putIntAsShorts(heapMemory, address + index, encoded); + } else { + UNSAFE.putInt(heapMemory, address + index, encoded); + } return 4; } // 5-byte case: bits 28-31 go to the 5th byte // Need long for the final write to include the 5th byte long encodedLong = Integer.toUnsignedLong(encoded) | 0x80000000L; encodedLong |= (long) (value >>> 28) << 32; - UNSAFE.putLong(heapMemory, address + index, encodedLong); + if (JDK8_ARM) { + _UnsafeUtils.putLongAsInts(heapMemory, address + index, encodedLong); + } else { + UNSAFE.putLong(heapMemory, address + index, encodedLong); + } return 5; } private int putVarUInt32BigEndian(int index, int encoded, int value) { if (value >>> 14 == 0) { - UNSAFE.putInt(heapMemory, address + index, Integer.reverseBytes(encoded)); + if (JDK8_ARM) { + _UnsafeUtils.putIntAsShorts(heapMemory, address + index, Integer.reverseBytes(encoded)); + } else { + UNSAFE.putInt(heapMemory, address + index, Integer.reverseBytes(encoded)); + } return 2; } return continuePutVarUInt32BigEndian(index, encoded, value); @@ -1256,20 +1395,32 @@ private int continuePutVarUInt32BigEndian(int index, int encoded, int value) { // 0x1fc000: 0b1111111 << 14 encoded |= (((value & 0x1fc000) << 2) | 0x8000); if (value >>> 21 == 0) { - UNSAFE.putInt(heapMemory, address + index, Integer.reverseBytes(encoded)); + if (JDK8_ARM) { + _UnsafeUtils.putIntAsShorts(heapMemory, address + index, Integer.reverseBytes(encoded)); + } else { + UNSAFE.putInt(heapMemory, address + index, Integer.reverseBytes(encoded)); + } return 3; } // 0xfe00000: 0b1111111 << 21 encoded |= ((value & 0xfe00000) << 3) | 0x800000; if (value >>> 28 == 0) { - UNSAFE.putInt(heapMemory, address + index, Integer.reverseBytes(encoded)); + if (JDK8_ARM) { + _UnsafeUtils.putIntAsShorts(heapMemory, address + index, Integer.reverseBytes(encoded)); + } else { + UNSAFE.putInt(heapMemory, address + index, Integer.reverseBytes(encoded)); + } return 4; } // 5-byte case: bits 28-31 go to the 5th byte // Need long for the final write to include the 5th byte long encodedLong = Integer.toUnsignedLong(encoded) | 0x80000000L; encodedLong |= (long) (value >>> 28) << 32; - UNSAFE.putLong(heapMemory, address + index, Long.reverseBytes(encodedLong)); + if (JDK8_ARM) { + _UnsafeUtils.putLongAsInts(heapMemory, address + index, Long.reverseBytes(encodedLong)); + } else { + UNSAFE.putLong(heapMemory, address + index, Long.reverseBytes(encodedLong)); + } return 5; } @@ -1295,7 +1446,11 @@ public int _unsafePutVarUint36Small(int index, long value) { return putVarUint36SmallBigEndian(index, encoded, value); } if (value >>> 14 == 0) { - UNSAFE.putInt(heapMemory, address + index, (int) encoded); + if (JDK8_ARM) { + _UnsafeUtils.putIntAsShorts(heapMemory, address + index, (int) encoded); + } else { + UNSAFE.putInt(heapMemory, address + index, (int) encoded); + } return 2; } return continuePutVarUint36Small(index, encoded, value); @@ -1305,30 +1460,51 @@ private int continuePutVarUint36Small(int index, long encoded, long value) { // 0x1fc000: 0b1111111 << 14 encoded |= (((value & 0x1fc000) << 2) | 0x8000); if (value >>> 21 == 0) { - UNSAFE.putInt(heapMemory, address + index, (int) encoded); + if (JDK8_ARM) { + _UnsafeUtils.putIntAsShorts(heapMemory, address + index, (int) encoded); + } else { + UNSAFE.putInt(heapMemory, address + index, (int) encoded); + } return 3; } // 0xfe00000: 0b1111111 << 21 encoded |= ((value & 0xfe00000) << 3) | 0x800000; if (value >>> 28 == 0) { - UNSAFE.putInt(heapMemory, address + index, (int) encoded); + if (JDK8_ARM) { + _UnsafeUtils.putIntAsShorts(heapMemory, address + index, (int) encoded); + } else { + UNSAFE.putInt(heapMemory, address + index, (int) encoded); + } return 4; } // The fifth byte carries seven data bits. Bit 35 uses a sixth byte so this // specialized string-header encoding stays standard VarUint64 on the wire. encoded |= ((value & 0x7f0000000L) << 4) | 0x80000000L; if (value >>> 35 == 0) { - UNSAFE.putLong(heapMemory, address + index, encoded); + if (JDK8_ARM) { + _UnsafeUtils.putLongAsInts(heapMemory, address + index, encoded); + } else { + UNSAFE.putLong(heapMemory, address + index, encoded); + } return 5; } encoded |= 0x8000000000L | ((value >>> 35) << 40); - UNSAFE.putLong(heapMemory, address + index, encoded); + if (JDK8_ARM) { + _UnsafeUtils.putLongAsInts(heapMemory, address + index, encoded); + } else { + UNSAFE.putLong(heapMemory, address + index, encoded); + } return 6; } private int putVarUint36SmallBigEndian(int index, long encoded, long value) { if (value >>> 14 == 0) { - UNSAFE.putInt(heapMemory, address + index, Integer.reverseBytes((int) encoded)); + if (JDK8_ARM) { + _UnsafeUtils.putIntAsShorts( + heapMemory, address + index, Integer.reverseBytes((int) encoded)); + } else { + UNSAFE.putInt(heapMemory, address + index, Integer.reverseBytes((int) encoded)); + } return 2; } return continuePutVarUint36SmallBigEndian(index, encoded, value); @@ -1338,22 +1514,40 @@ private int continuePutVarUint36SmallBigEndian(int index, long encoded, long val // 0x1fc000: 0b1111111 << 14 encoded |= (((value & 0x1fc000) << 2) | 0x8000); if (value >>> 21 == 0) { - UNSAFE.putInt(heapMemory, address + index, Integer.reverseBytes((int) encoded)); + if (JDK8_ARM) { + _UnsafeUtils.putIntAsShorts( + heapMemory, address + index, Integer.reverseBytes((int) encoded)); + } else { + UNSAFE.putInt(heapMemory, address + index, Integer.reverseBytes((int) encoded)); + } return 3; } // 0xfe00000: 0b1111111 << 21 encoded |= ((value & 0xfe00000) << 3) | 0x800000; if (value >>> 28 == 0) { - UNSAFE.putInt(heapMemory, address + index, Integer.reverseBytes((int) encoded)); + if (JDK8_ARM) { + _UnsafeUtils.putIntAsShorts( + heapMemory, address + index, Integer.reverseBytes((int) encoded)); + } else { + UNSAFE.putInt(heapMemory, address + index, Integer.reverseBytes((int) encoded)); + } return 4; } encoded |= ((value & 0x7f0000000L) << 4) | 0x80000000L; if (value >>> 35 == 0) { - UNSAFE.putLong(heapMemory, address + index, Long.reverseBytes(encoded)); + if (JDK8_ARM) { + _UnsafeUtils.putLongAsInts(heapMemory, address + index, Long.reverseBytes(encoded)); + } else { + UNSAFE.putLong(heapMemory, address + index, Long.reverseBytes(encoded)); + } return 5; } encoded |= 0x8000000000L | ((value >>> 35) << 40); - UNSAFE.putLong(heapMemory, address + index, Long.reverseBytes(encoded)); + if (JDK8_ARM) { + _UnsafeUtils.putLongAsInts(heapMemory, address + index, Long.reverseBytes(encoded)); + } else { + UNSAFE.putLong(heapMemory, address + index, Long.reverseBytes(encoded)); + } return 6; } @@ -1403,7 +1597,11 @@ private int writeVarUInt32Aligned1(int value) { } else { UNSAFE.putByte(heapMemory, pos, (byte) first); // zero out 4 bytes, so that `bit 7` value can be trusted. - UNSAFE.putInt(heapMemory, pos + 1, 0); + if (JDK8_ARM) { + _UnsafeUtils.putIntAsShorts(heapMemory, pos + 1, 0); + } else { + UNSAFE.putInt(heapMemory, pos + 1, 0); + } UNSAFE.putByte(heapMemory, pos + numPaddingBytes - 1, (byte) (0x40)); writerIndex = writerIdx + numPaddingBytes; return numPaddingBytes; @@ -1426,7 +1624,11 @@ private int writeVarUInt32Aligned2(int value) { } else { UNSAFE.putByte(heapMemory, pos + 1, (byte) (value >>> 6)); // zero out 4 bytes, so that `bit 7` value can be trusted. - UNSAFE.putInt(heapMemory, pos + 2, 0); + if (JDK8_ARM) { + _UnsafeUtils.putIntAsShorts(heapMemory, pos + 2, 0); + } else { + UNSAFE.putInt(heapMemory, pos + 2, 0); + } if (numPaddingBytes > 2) { UNSAFE.putByte(heapMemory, pos + numPaddingBytes - 1, (byte) (0x40)); writerIndex = writerIdx + numPaddingBytes; @@ -1456,7 +1658,11 @@ private int writeVarUInt32Aligned3(int value) { } else { UNSAFE.putByte(heapMemory, pos + 2, (byte) (value >>> 12)); // zero out 4 bytes, so that `bit 7` value can be trusted. - UNSAFE.putInt(heapMemory, pos + 3, 0); + if (JDK8_ARM) { + _UnsafeUtils.putIntAsShorts(heapMemory, pos + 3, 0); + } else { + UNSAFE.putInt(heapMemory, pos + 3, 0); + } if (numPaddingBytes == 4) { UNSAFE.putByte(heapMemory, pos + numPaddingBytes - 1, (byte) (0x40)); writerIndex = writerIdx + numPaddingBytes; @@ -1487,7 +1693,11 @@ private int writeVarUInt32Aligned4(int value) { } else { UNSAFE.putByte(heapMemory, pos + 3, (byte) (value >>> 18)); // zero out 4 bytes, so that `bit 7` value can be trusted. - UNSAFE.putInt(heapMemory, pos + 4, 0); + if (JDK8_ARM) { + _UnsafeUtils.putIntAsShorts(heapMemory, pos + 4, 0); + } else { + UNSAFE.putInt(heapMemory, pos + 4, 0); + } UNSAFE.putByte(heapMemory, pos + numPaddingBytes + 3, (byte) (0x40)); writerIndex = writerIdx + numPaddingBytes + 4; return numPaddingBytes + 4; @@ -1513,7 +1723,11 @@ private int writeVarUInt32Aligned5(int value) { } else { UNSAFE.putByte(heapMemory, pos + 4, (byte) (value >>> 24)); // zero out 4 bytes, so that `bit 7` value can be trusted. - UNSAFE.putInt(heapMemory, pos + 5, 0); + if (JDK8_ARM) { + _UnsafeUtils.putIntAsShorts(heapMemory, pos + 5, 0); + } else { + UNSAFE.putInt(heapMemory, pos + 5, 0); + } UNSAFE.putByte(heapMemory, pos + numPaddingBytes + 3, (byte) (0x40)); writerIndex = writerIdx + numPaddingBytes + 4; return numPaddingBytes + 4; @@ -1540,7 +1754,11 @@ private int writeVarUInt32Aligned6(int value) { } else { UNSAFE.putByte(heapMemory, pos + 5, (byte) (value >>> 30)); // zero out 4 bytes, so that `bit 7` value can be trusted. - UNSAFE.putInt(heapMemory, pos + 6, 0); + if (JDK8_ARM) { + _UnsafeUtils.putIntAsShorts(heapMemory, pos + 6, 0); + } else { + UNSAFE.putInt(heapMemory, pos + 6, 0); + } if (numPaddingBytes == 1) { UNSAFE.putByte(heapMemory, pos + 8, (byte) (0x40)); writerIndex = writerIdx + 9; @@ -1685,7 +1903,11 @@ public int _unsafeWriteTaggedUInt64(long value) { if (!LITTLE_ENDIAN) { v = Integer.reverseBytes(v); } - UNSAFE.putInt(heapMemory, pos, v); + if (JDK8_ARM) { + _UnsafeUtils.putIntAsShorts(heapMemory, pos, v); + } else { + UNSAFE.putInt(heapMemory, pos, v); + } this.writerIndex = writerIndex + 4; return 4; } else { @@ -1693,7 +1915,11 @@ public int _unsafeWriteTaggedUInt64(long value) { if (!LITTLE_ENDIAN) { value = Long.reverseBytes(value); } - UNSAFE.putLong(heapMemory, pos + 1, value); + if (JDK8_ARM) { + _UnsafeUtils.putLongAsInts(heapMemory, pos + 1, value); + } else { + UNSAFE.putLong(heapMemory, pos + 1, value); + } this.writerIndex = writerIndex + 9; return 9; } @@ -1724,7 +1950,11 @@ public int _unsafeWriteTaggedInt64(long value) { if (!LITTLE_ENDIAN) { v = Integer.reverseBytes(v); } - UNSAFE.putInt(heapMemory, pos, v); + if (JDK8_ARM) { + _UnsafeUtils.putIntAsShorts(heapMemory, pos, v); + } else { + UNSAFE.putInt(heapMemory, pos, v); + } this.writerIndex = writerIndex + 4; return 4; } else { @@ -1732,7 +1962,11 @@ public int _unsafeWriteTaggedInt64(long value) { if (!LITTLE_ENDIAN) { value = Long.reverseBytes(value); } - UNSAFE.putLong(heapMemory, pos + 1, value); + if (JDK8_ARM) { + _UnsafeUtils.putLongAsInts(heapMemory, pos + 1, value); + } else { + UNSAFE.putLong(heapMemory, pos + 1, value); + } this.writerIndex = writerIndex + 9; return 9; } @@ -2322,7 +2556,12 @@ public char readChar() { streamReader.fillBuffer(2 - remaining); } readerIndex = readerIdx + 2; - char c = UNSAFE.getChar(heapMemory, address + readerIdx); + char c; + if (JDK8_ARM) { + c = (char) _UnsafeUtils.getShortBytes(heapMemory, address + readerIdx); + } else { + c = UNSAFE.getChar(heapMemory, address + readerIdx); + } return LITTLE_ENDIAN ? c : Character.reverseBytes(c); } @@ -2337,7 +2576,12 @@ public short readInt16() { streamReader.fillBuffer(2 - remaining); } readerIndex = readerIdx + 2; - short v = UNSAFE.getShort(heapMemory, address + readerIdx); + short v; + if (JDK8_ARM) { + v = _UnsafeUtils.getShortBytes(heapMemory, address + readerIdx); + } else { + v = UNSAFE.getShort(heapMemory, address + readerIdx); + } return LITTLE_ENDIAN ? v : Short.reverseBytes(v); } @@ -2356,7 +2600,11 @@ public short _readInt16OnLE() { streamReader.fillBuffer(2 - remaining); } readerIndex = readerIdx + 2; - return UNSAFE.getShort(heapMemory, address + readerIdx); + if (JDK8_ARM) { + return _UnsafeUtils.getShortBytes(heapMemory, address + readerIdx); + } else { + return UNSAFE.getShort(heapMemory, address + readerIdx); + } } // Reduce method body for better inline in the caller. @@ -2374,7 +2622,11 @@ public short _readInt16OnBE() { streamReader.fillBuffer(2 - remaining); } readerIndex = readerIdx + 2; - return Short.reverseBytes(UNSAFE.getShort(heapMemory, address + readerIdx)); + if (JDK8_ARM) { + return Short.reverseBytes(_UnsafeUtils.getShortBytes(heapMemory, address + readerIdx)); + } else { + return Short.reverseBytes(UNSAFE.getShort(heapMemory, address + readerIdx)); + } } public int readInt32() { @@ -2388,7 +2640,12 @@ public int readInt32() { streamReader.fillBuffer(4 - remaining); } readerIndex = readerIdx + 4; - int v = UNSAFE.getInt(heapMemory, address + readerIdx); + int v; + if (JDK8_ARM) { + v = _UnsafeUtils.getIntFromShorts(heapMemory, address + readerIdx); + } else { + v = UNSAFE.getInt(heapMemory, address + readerIdx); + } return LITTLE_ENDIAN ? v : Integer.reverseBytes(v); } @@ -2407,7 +2664,11 @@ public int _readInt32OnLE() { streamReader.fillBuffer(4 - remaining); } readerIndex = readerIdx + 4; - return UNSAFE.getInt(heapMemory, address + readerIdx); + if (JDK8_ARM) { + return _UnsafeUtils.getIntFromShorts(heapMemory, address + readerIdx); + } else { + return UNSAFE.getInt(heapMemory, address + readerIdx); + } } // Reduce method body for better inline in the caller. @@ -2425,7 +2686,11 @@ public int _readInt32OnBE() { streamReader.fillBuffer(4 - remaining); } readerIndex = readerIdx + 4; - return Integer.reverseBytes(UNSAFE.getInt(heapMemory, address + readerIdx)); + if (JDK8_ARM) { + return Integer.reverseBytes(_UnsafeUtils.getIntFromShorts(heapMemory, address + readerIdx)); + } else { + return Integer.reverseBytes(UNSAFE.getInt(heapMemory, address + readerIdx)); + } } public long readInt64() { @@ -2439,7 +2704,12 @@ public long readInt64() { streamReader.fillBuffer(8 - remaining); } readerIndex = readerIdx + 8; - long v = UNSAFE.getLong(heapMemory, address + readerIdx); + long v; + if (JDK8_ARM) { + v = _UnsafeUtils.getLongFromInts(heapMemory, address + readerIdx); + } else { + v = UNSAFE.getLong(heapMemory, address + readerIdx); + } return LITTLE_ENDIAN ? v : Long.reverseBytes(v); } @@ -2458,7 +2728,11 @@ public long _readInt64OnLE() { streamReader.fillBuffer(8 - remaining); } readerIndex = readerIdx + 8; - return UNSAFE.getLong(heapMemory, address + readerIdx); + if (JDK8_ARM) { + return _UnsafeUtils.getLongFromInts(heapMemory, address + readerIdx); + } else { + return UNSAFE.getLong(heapMemory, address + readerIdx); + } } // Reduce method body for better inline in the caller. @@ -2476,7 +2750,11 @@ public long _readInt64OnBE() { streamReader.fillBuffer(8 - remaining); } readerIndex = readerIdx + 8; - return Long.reverseBytes(UNSAFE.getLong(heapMemory, address + readerIdx)); + if (JDK8_ARM) { + return Long.reverseBytes(_UnsafeUtils.getLongFromInts(heapMemory, address + readerIdx)); + } else { + return Long.reverseBytes(UNSAFE.getLong(heapMemory, address + readerIdx)); + } } /** Read signed fory Tagged(Small Long as Int) encoded long. */ @@ -2509,7 +2787,12 @@ public long _readTaggedUInt64OnLE() { if (diff < 4) { streamReader.fillBuffer(4 - diff); } - int i = UNSAFE.getInt(heapMemory, address + readIdx); + int i; + if (JDK8_ARM) { + i = _UnsafeUtils.getIntFromShorts(heapMemory, address + readIdx); + } else { + i = UNSAFE.getInt(heapMemory, address + readIdx); + } if ((i & 0b1) != 0b1) { readerIndex = readIdx + 4; return i >>> 1; // unsigned right shift @@ -2519,7 +2802,11 @@ public long _readTaggedUInt64OnLE() { streamReader.fillBuffer(9 - diff); } readerIndex = readIdx + 9; - return UNSAFE.getLong(heapMemory, address + readIdx + 1); + if (JDK8_ARM) { + return _UnsafeUtils.getLongFromInts(heapMemory, address + readIdx + 1); + } else { + return UNSAFE.getLong(heapMemory, address + readIdx + 1); + } } @CodegenInvoke @@ -2534,7 +2821,12 @@ public long _readTaggedUInt64OnBE() { if (diff < 4) { streamReader.fillBuffer(4 - diff); } - int i = Integer.reverseBytes(UNSAFE.getInt(heapMemory, address + readIdx)); + int i; + if (JDK8_ARM) { + i = Integer.reverseBytes(_UnsafeUtils.getIntFromShorts(heapMemory, address + readIdx)); + } else { + i = Integer.reverseBytes(UNSAFE.getInt(heapMemory, address + readIdx)); + } if ((i & 0b1) != 0b1) { readerIndex = readIdx + 4; return i >>> 1; // unsigned right shift @@ -2544,7 +2836,11 @@ public long _readTaggedUInt64OnBE() { streamReader.fillBuffer(9 - diff); } readerIndex = readIdx + 9; - return Long.reverseBytes(UNSAFE.getLong(heapMemory, address + readIdx + 1)); + if (JDK8_ARM) { + return Long.reverseBytes(_UnsafeUtils.getLongFromInts(heapMemory, address + readIdx + 1)); + } else { + return Long.reverseBytes(UNSAFE.getLong(heapMemory, address + readIdx + 1)); + } } @CodegenInvoke @@ -2561,7 +2857,12 @@ public long _readTaggedInt64OnLE() { if (diff < 4) { streamReader.fillBuffer(4 - diff); } - int i = UNSAFE.getInt(heapMemory, address + readIdx); + int i; + if (JDK8_ARM) { + i = _UnsafeUtils.getIntFromShorts(heapMemory, address + readIdx); + } else { + i = UNSAFE.getInt(heapMemory, address + readIdx); + } if ((i & 0b1) != 0b1) { readerIndex = readIdx + 4; return i >> 1; @@ -2571,7 +2872,11 @@ public long _readTaggedInt64OnLE() { streamReader.fillBuffer(9 - diff); } readerIndex = readIdx + 9; - return UNSAFE.getLong(heapMemory, address + readIdx + 1); + if (JDK8_ARM) { + return _UnsafeUtils.getLongFromInts(heapMemory, address + readIdx + 1); + } else { + return UNSAFE.getLong(heapMemory, address + readIdx + 1); + } } @CodegenInvoke @@ -2587,7 +2892,12 @@ public long _readTaggedInt64OnBE() { if (diff < 4) { streamReader.fillBuffer(4 - diff); } - int i = Integer.reverseBytes(UNSAFE.getInt(heapMemory, address + readIdx)); + int i; + if (JDK8_ARM) { + i = Integer.reverseBytes(_UnsafeUtils.getIntFromShorts(heapMemory, address + readIdx)); + } else { + i = Integer.reverseBytes(UNSAFE.getInt(heapMemory, address + readIdx)); + } if ((i & 0b1) != 0b1) { readerIndex = readIdx + 4; return i >> 1; @@ -2597,7 +2907,11 @@ public long _readTaggedInt64OnBE() { streamReader.fillBuffer(9 - diff); } readerIndex = readIdx + 9; - return Long.reverseBytes(UNSAFE.getLong(heapMemory, address + readIdx + 1)); + if (JDK8_ARM) { + return Long.reverseBytes(_UnsafeUtils.getLongFromInts(heapMemory, address + readIdx + 1)); + } else { + return Long.reverseBytes(UNSAFE.getLong(heapMemory, address + readIdx + 1)); + } } public float readFloat32() { @@ -2612,7 +2926,12 @@ public float readFloat32() { streamReader.fillBuffer(4 - remaining); } readerIndex = readerIdx + 4; - int v = UNSAFE.getInt(heapMemory, address + readerIdx); + int v; + if (JDK8_ARM) { + v = _UnsafeUtils.getIntFromShorts(heapMemory, address + readerIdx); + } else { + v = UNSAFE.getInt(heapMemory, address + readerIdx); + } if (!LITTLE_ENDIAN) { v = Integer.reverseBytes(v); } @@ -2634,7 +2953,11 @@ public float _readFloat32OnLE() { streamReader.fillBuffer(4 - remaining); } readerIndex = readerIdx + 4; - return Float.intBitsToFloat(UNSAFE.getInt(heapMemory, address + readerIdx)); + if (JDK8_ARM) { + return Float.intBitsToFloat(_UnsafeUtils.getIntFromShorts(heapMemory, address + readerIdx)); + } else { + return Float.intBitsToFloat(UNSAFE.getInt(heapMemory, address + readerIdx)); + } } // Reduce method body for better inline in the caller. @@ -2652,8 +2975,13 @@ public float _readFloat32OnBE() { streamReader.fillBuffer(4 - remaining); } readerIndex = readerIdx + 4; - return Float.intBitsToFloat( - Integer.reverseBytes(UNSAFE.getInt(heapMemory, address + readerIdx))); + if (JDK8_ARM) { + return Float.intBitsToFloat( + Integer.reverseBytes(_UnsafeUtils.getIntFromShorts(heapMemory, address + readerIdx))); + } else { + return Float.intBitsToFloat( + Integer.reverseBytes(UNSAFE.getInt(heapMemory, address + readerIdx))); + } } public double readFloat64() { @@ -2668,7 +2996,12 @@ public double readFloat64() { streamReader.fillBuffer(8 - remaining); } readerIndex = readerIdx + 8; - long v = UNSAFE.getLong(heapMemory, address + readerIdx); + long v; + if (JDK8_ARM) { + v = _UnsafeUtils.getLongFromInts(heapMemory, address + readerIdx); + } else { + v = UNSAFE.getLong(heapMemory, address + readerIdx); + } if (!LITTLE_ENDIAN) { v = Long.reverseBytes(v); } @@ -2690,7 +3023,11 @@ public double _readFloat64OnLE() { streamReader.fillBuffer(8 - remaining); } readerIndex = readerIdx + 8; - return Double.longBitsToDouble(UNSAFE.getLong(heapMemory, address + readerIdx)); + if (JDK8_ARM) { + return Double.longBitsToDouble(_UnsafeUtils.getLongFromInts(heapMemory, address + readerIdx)); + } else { + return Double.longBitsToDouble(UNSAFE.getLong(heapMemory, address + readerIdx)); + } } // Reduce method body for better inline in the caller. @@ -2708,8 +3045,13 @@ public double _readFloat64OnBE() { streamReader.fillBuffer(8 - remaining); } readerIndex = readerIdx + 8; - return Double.longBitsToDouble( - Long.reverseBytes(UNSAFE.getLong(heapMemory, address + readerIdx))); + if (JDK8_ARM) { + return Double.longBitsToDouble( + Long.reverseBytes(_UnsafeUtils.getLongFromInts(heapMemory, address + readerIdx))); + } else { + return Double.longBitsToDouble( + Long.reverseBytes(UNSAFE.getLong(heapMemory, address + readerIdx))); + } } /** Reads the 1-5 byte int part of a varint. */ @@ -2741,7 +3083,12 @@ public int _readVarInt32OnLE() { } else { long address = this.address; // | 1bit + 7bits | 1bit + 7bits | 1bit + 7bits | 1bit + 7bits | - int fourByteValue = UNSAFE.getInt(heapMemory, address + readIdx); + int fourByteValue; + if (JDK8_ARM) { + fourByteValue = _UnsafeUtils.getIntFromShorts(heapMemory, address + readIdx); + } else { + fourByteValue = UNSAFE.getInt(heapMemory, address + readIdx); + } // Duplicate and manual inline for performance. // noinspection Duplicates readIdx++; @@ -2790,7 +3137,13 @@ public int _readVarInt32OnBE() { result = readVarUInt32Slow(); } else { long address = this.address; - int fourByteValue = Integer.reverseBytes(UNSAFE.getInt(heapMemory, address + readIdx)); + int fourByteValue; + if (JDK8_ARM) { + fourByteValue = + Integer.reverseBytes(_UnsafeUtils.getIntFromShorts(heapMemory, address + readIdx)); + } else { + fourByteValue = Integer.reverseBytes(UNSAFE.getInt(heapMemory, address + readIdx)); + } // Duplicate and manual inline for performance. // noinspection Duplicates readIdx++; @@ -2828,13 +3181,18 @@ public long readVarUint36Small() { if (AndroidSupport.IS_ANDROID) { return MemoryOps.readVarUint36Small(this); } - // Android exits above. Keep JVM small-varint bulk reads as raw Unsafe loads instead of calling - // `_unsafeGet*` helpers; those helpers carry Android/endian branches and can break inlining. + // Android exits above. Use the narrow Unsafe access owner instead of `_unsafeGet*` helpers; + // those buffer helpers carry Android/endian branches and can break inlining. // Duplicate and manual inline for performance. // noinspection Duplicates int readIdx = readerIndex; if (size - readIdx >= 9) { - long bulkValue = UNSAFE.getLong(heapMemory, address + readIdx++); + long bulkValue; + if (JDK8_ARM) { + bulkValue = _UnsafeUtils.getLongFromInts(heapMemory, address + readIdx++); + } else { + bulkValue = UNSAFE.getLong(heapMemory, address + readIdx++); + } if (!LITTLE_ENDIAN) { bulkValue = Long.reverseBytes(bulkValue); } @@ -2952,7 +3310,12 @@ public int readVarUInt32() { return readVarUInt32Slow(); } // | 1bit + 7bits | 1bit + 7bits | 1bit + 7bits | 1bit + 7bits | - int fourByteValue = UNSAFE.getInt(heapMemory, address + readIdx); + int fourByteValue; + if (JDK8_ARM) { + fourByteValue = _UnsafeUtils.getIntFromShorts(heapMemory, address + readIdx); + } else { + fourByteValue = UNSAFE.getInt(heapMemory, address + readIdx); + } if (!LITTLE_ENDIAN) { fourByteValue = Integer.reverseBytes(fourByteValue); } @@ -3017,7 +3380,12 @@ public int readVarUInt32Small14() { } int readIdx = readerIndex; if (size - readIdx >= 5) { - int fourByteValue = UNSAFE.getInt(heapMemory, address + readIdx++); + int fourByteValue; + if (JDK8_ARM) { + fourByteValue = _UnsafeUtils.getIntFromShorts(heapMemory, address + readIdx++); + } else { + fourByteValue = UNSAFE.getInt(heapMemory, address + readIdx++); + } if (!LITTLE_ENDIAN) { fourByteValue = Integer.reverseBytes(fourByteValue); } @@ -3083,7 +3451,12 @@ public long _readVarInt64OnLE() { result = readVarUInt64Slow(); } else { long address = this.address; - long bulkValue = UNSAFE.getLong(heapMemory, address + readIdx); + long bulkValue; + if (JDK8_ARM) { + bulkValue = _UnsafeUtils.getLongFromInts(heapMemory, address + readIdx); + } else { + bulkValue = UNSAFE.getLong(heapMemory, address + readIdx); + } // Duplicate and manual inline for performance. // noinspection Duplicates readIdx++; @@ -3116,7 +3489,12 @@ public long _readVarInt64OnBE() { result = readVarUInt64Slow(); } else { long address = this.address; - long bulkValue = Long.reverseBytes(UNSAFE.getLong(heapMemory, address + readIdx)); + long bulkValue; + if (JDK8_ARM) { + bulkValue = Long.reverseBytes(_UnsafeUtils.getLongFromInts(heapMemory, address + readIdx)); + } else { + bulkValue = Long.reverseBytes(UNSAFE.getLong(heapMemory, address + readIdx)); + } // Duplicate and manual inline for performance. // noinspection Duplicates readIdx++; @@ -3146,7 +3524,12 @@ public long readVarUInt64() { return readVarUInt64Slow(); } // varint are written using little endian byte order, so read by little endian byte order. - long bulkValue = UNSAFE.getLong(heapMemory, address + readIdx); + long bulkValue; + if (JDK8_ARM) { + bulkValue = _UnsafeUtils.getLongFromInts(heapMemory, address + readIdx); + } else { + bulkValue = UNSAFE.getLong(heapMemory, address + readIdx); + } if (!LITTLE_ENDIAN) { bulkValue = Long.reverseBytes(bulkValue); } @@ -3394,7 +3777,12 @@ public long readBytesAsInt64(int len) { int remaining = size - readerIdx; if (remaining >= 8) { readerIndex = readerIdx + len; - long v = UNSAFE.getLong(heapMemory, address + readerIdx); + long v; + if (JDK8_ARM) { + v = _UnsafeUtils.getLongFromInts(heapMemory, address + readerIdx); + } else { + v = UNSAFE.getLong(heapMemory, address + readerIdx); + } v = (LITTLE_ENDIAN ? v : Long.reverseBytes(v)) & (0xffffffffffffffffL >>> ((8 - len) * 8)); return v; } @@ -3466,7 +3854,12 @@ public int readBinarySize() { if (size - readIdx >= 5) { // Android exits above. Keep this small-size fast path as a raw JVM load; `_unsafeGetInt32` // carries Android/endian branches and can grow the method enough to disturb inlining. - int fourByteValue = UNSAFE.getInt(heapMemory, address + readIdx++); + int fourByteValue; + if (JDK8_ARM) { + fourByteValue = _UnsafeUtils.getIntFromShorts(heapMemory, address + readIdx++); + } else { + fourByteValue = UNSAFE.getInt(heapMemory, address + readIdx++); + } if (!LITTLE_ENDIAN) { fourByteValue = Integer.reverseBytes(fourByteValue); } @@ -4387,8 +4780,16 @@ private static boolean unsafeEqualTo( } if (UNALIGNED || (((leftOffset + i) % 8 == 0) && ((rightOffset + i) % 8 == 0))) { while (i <= length - 8) { - if (UNSAFE.getLong(leftBase, leftOffset + i) - != UNSAFE.getLong(rightBase, rightOffset + i)) { + long left; + long right; + if (JDK8_ARM) { + left = _UnsafeUtils.getLongFromInts(leftBase, leftOffset + i); + right = _UnsafeUtils.getLongFromInts(rightBase, rightOffset + i); + } else { + left = UNSAFE.getLong(leftBase, leftOffset + i); + right = UNSAFE.getLong(rightBase, rightOffset + i); + } + if (left != right) { return false; } i += 8; diff --git a/java/fory-core/src/main/java/org/apache/fory/platform/JdkVersion.java b/java/fory-core/src/main/java/org/apache/fory/platform/JdkVersion.java index defb613391..4d3f8430fe 100644 --- a/java/fory-core/src/main/java/org/apache/fory/platform/JdkVersion.java +++ b/java/fory-core/src/main/java/org/apache/fory/platform/JdkVersion.java @@ -19,12 +19,23 @@ package org.apache.fory.platform; -/** JDK version facts which are safe to load without initializing Unsafe-backed code. */ +import java.util.Locale; + +/** JDK runtime facts which are safe to load without initializing Unsafe-backed code. */ public final class JdkVersion { public static final int MAJOR_VERSION = parseMajorVersion(); + /** Whether indexed Unsafe accesses need the JDK 8 ARM compiler compatibility path. */ + public static final boolean JDK8_ARM = + MAJOR_VERSION == 8 && isArm(System.getProperty("os.arch", "")); + private JdkVersion() {} + private static boolean isArm(String arch) { + arch = arch.toLowerCase(Locale.ROOT); + return arch.startsWith("arm") || arch.startsWith("aarch64"); + } + private static int parseMajorVersion() { String version = System.getProperty("java.specification.version"); if (version == null || version.isEmpty()) { diff --git a/java/fory-core/src/main/java/org/apache/fory/platform/internal/_UnsafeUtils.java b/java/fory-core/src/main/java/org/apache/fory/platform/internal/_UnsafeUtils.java index 32555f842e..015430de72 100644 --- a/java/fory-core/src/main/java/org/apache/fory/platform/internal/_UnsafeUtils.java +++ b/java/fory-core/src/main/java/org/apache/fory/platform/internal/_UnsafeUtils.java @@ -19,7 +19,10 @@ package org.apache.fory.platform.internal; +import static org.apache.fory.platform.JdkVersion.JDK8_ARM; + import java.lang.reflect.Field; +import org.apache.fory.memory.NativeByteOrder; import sun.misc.Unsafe; /** Root-runtime owner for {@link Unsafe}. Java25+ code must use overlay classes instead. */ @@ -39,4 +42,148 @@ public final class _UnsafeUtils { } private _UnsafeUtils() {} + + // Oracle JDK 8 ARM C2 can replace an address scale with the load/store width, e.g. turn + // charIndex * 2 into charIndex * 8 for getLong(char[], offset). A wrapper around the same + // wide Unsafe operation is insufficient: inlining recreates the faulty address expression. + // The separate methods below share each computed address between two narrower accesses so C2 + // materializes the address instead of folding its scale into a single mismatched instruction. + // Hot callers branch on JDK8_ARM directly to avoid an extra wrapper consuming JIT inlining depth. + + /** Reads a native-order char at an unchecked object or native-memory byte offset. */ + public static char getChar(Object base, long offset) { + if (JDK8_ARM) { + return (char) getShortBytes(base, offset); + } + return UNSAFE.getChar(base, offset); + } + + /** Reads a native-order short at an unchecked object or native-memory byte offset. */ + public static short getShort(Object base, long offset) { + if (JDK8_ARM) { + return getShortBytes(base, offset); + } + return UNSAFE.getShort(base, offset); + } + + /** Reads a native-order int at an unchecked object or native-memory byte offset. */ + public static int getInt(Object base, long offset) { + if (JDK8_ARM) { + return getIntFromShorts(base, offset); + } + return UNSAFE.getInt(base, offset); + } + + /** Reads a native-order long at an unchecked object or native-memory byte offset. */ + public static long getLong(Object base, long offset) { + if (JDK8_ARM) { + return getLongFromInts(base, offset); + } + return UNSAFE.getLong(base, offset); + } + + /** Writes a native-order char at an unchecked object or native-memory byte offset. */ + public static void putChar(Object base, long offset, char value) { + if (JDK8_ARM) { + putShortBytes(base, offset, (short) value); + } else { + UNSAFE.putChar(base, offset, value); + } + } + + /** Writes a native-order short at an unchecked object or native-memory byte offset. */ + public static void putShort(Object base, long offset, short value) { + if (JDK8_ARM) { + putShortBytes(base, offset, value); + } else { + UNSAFE.putShort(base, offset, value); + } + } + + /** Writes a native-order int at an unchecked object or native-memory byte offset. */ + public static void putInt(Object base, long offset, int value) { + if (JDK8_ARM) { + putIntAsShorts(base, offset, value); + } else { + UNSAFE.putInt(base, offset, value); + } + } + + /** Writes a native-order long at an unchecked object or native-memory byte offset. */ + public static void putLong(Object base, long offset, long value) { + if (JDK8_ARM) { + putLongAsInts(base, offset, value); + } else { + UNSAFE.putLong(base, offset, value); + } + } + + /** Writes a native-order float at an unchecked object or native-memory byte offset. */ + public static void putFloat(Object base, long offset, float value) { + if (JDK8_ARM) { + putIntAsShorts(base, offset, Float.floatToRawIntBits(value)); + } else { + UNSAFE.putFloat(base, offset, value); + } + } + + /** Writes a native-order double at an unchecked object or native-memory byte offset. */ + public static void putDouble(Object base, long offset, double value) { + if (JDK8_ARM) { + putLongAsInts(base, offset, Double.doubleToRawLongBits(value)); + } else { + UNSAFE.putDouble(base, offset, value); + } + } + + /** Reads a native-order short using two byte loads at an unchecked byte offset. */ + public static short getShortBytes(Object base, long offset) { + int value = (UNSAFE.getByte(base, offset) & 0xff) | (UNSAFE.getByte(base, offset + 1) << 8); + return NativeByteOrder.IS_LITTLE_ENDIAN ? (short) value : Short.reverseBytes((short) value); + } + + /** Reads a native-order int using two short loads at an unchecked byte offset. */ + public static int getIntFromShorts(Object base, long offset) { + int first = UNSAFE.getShort(base, offset) & 0xffff; + int second = UNSAFE.getShort(base, offset + 2) & 0xffff; + return NativeByteOrder.IS_LITTLE_ENDIAN ? first | (second << 16) : (first << 16) | second; + } + + /** Reads a native-order long using two int loads at an unchecked byte offset. */ + public static long getLongFromInts(Object base, long offset) { + long first = UNSAFE.getInt(base, offset) & 0xffffffffL; + long second = UNSAFE.getInt(base, offset + 4) & 0xffffffffL; + return NativeByteOrder.IS_LITTLE_ENDIAN ? first | (second << 32) : (first << 32) | second; + } + + /** Writes a native-order short using two byte stores at an unchecked byte offset. */ + public static void putShortBytes(Object base, long offset, short value) { + if (!NativeByteOrder.IS_LITTLE_ENDIAN) { + value = Short.reverseBytes(value); + } + UNSAFE.putByte(base, offset, (byte) value); + UNSAFE.putByte(base, offset + 1, (byte) (value >>> 8)); + } + + /** Writes a native-order int using two short stores at an unchecked byte offset. */ + public static void putIntAsShorts(Object base, long offset, int value) { + if (NativeByteOrder.IS_LITTLE_ENDIAN) { + UNSAFE.putShort(base, offset, (short) value); + UNSAFE.putShort(base, offset + 2, (short) (value >>> 16)); + } else { + UNSAFE.putShort(base, offset, (short) (value >>> 16)); + UNSAFE.putShort(base, offset + 2, (short) value); + } + } + + /** Writes a native-order long using two int stores at an unchecked byte offset. */ + public static void putLongAsInts(Object base, long offset, long value) { + if (NativeByteOrder.IS_LITTLE_ENDIAN) { + UNSAFE.putInt(base, offset, (int) value); + UNSAFE.putInt(base, offset + 4, (int) (value >>> 32)); + } else { + UNSAFE.putInt(base, offset, (int) (value >>> 32)); + UNSAFE.putInt(base, offset + 4, (int) value); + } + } } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/PlatformStringUtils.java b/java/fory-core/src/main/java/org/apache/fory/serializer/PlatformStringUtils.java index 978d119550..e44baf1c7f 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/PlatformStringUtils.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/PlatformStringUtils.java @@ -19,6 +19,8 @@ package org.apache.fory.serializer; +import static org.apache.fory.platform.JdkVersion.JDK8_ARM; + import java.lang.reflect.Field; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.memory.NativeByteOrder; @@ -143,7 +145,11 @@ static long getCharsLong(char[] chars, int charIndex) { return (c0 << 48) | (c1 << 32) | (c2 << 16) | c3; } } - return UNSAFE.getLong(chars, CHAR_ARRAY_OFFSET + ((long) charIndex << 1)); + if (JDK8_ARM) { + return _UnsafeUtils.getLongFromInts(chars, CHAR_ARRAY_OFFSET + ((long) charIndex << 1)); + } else { + return UNSAFE.getLong(chars, CHAR_ARRAY_OFFSET + ((long) charIndex << 1)); + } } static long getBytesLong(byte[] bytes, int byteIndex) { @@ -168,9 +174,12 @@ static long getBytesLong(byte[] bytes, int byteIndex) { | ((long) bytes[byteIndex + 7] & 0xff); } } - // Unsafe object offsets are long. Keep the cast so JDK8-compiled bytecode calls - // getLong(Object, long) when the artifact runs on JDK9+. - return UNSAFE.getLong(bytes, (long) BYTE_ARRAY_OFFSET + byteIndex); + // Compute the absolute array offset in long so large indices cannot overflow the addition. + if (JDK8_ARM) { + return _UnsafeUtils.getLongFromInts(bytes, (long) BYTE_ARRAY_OFFSET + byteIndex); + } else { + return UNSAFE.getLong(bytes, (long) BYTE_ARRAY_OFFSET + byteIndex); + } } static char getBytesChar(byte[] bytes, int byteIndex) { @@ -181,7 +190,11 @@ static char getBytesChar(byte[] bytes, int byteIndex) { return (char) (((bytes[byteIndex] & 0xff) << 8) | (bytes[byteIndex + 1] & 0xff)); } } - return UNSAFE.getChar(bytes, (long) BYTE_ARRAY_OFFSET + byteIndex); + if (JDK8_ARM) { + return (char) _UnsafeUtils.getShortBytes(bytes, (long) BYTE_ARRAY_OFFSET + byteIndex); + } else { + return UNSAFE.getChar(bytes, (long) BYTE_ARRAY_OFFSET + byteIndex); + } } static void copyCharsToBytes( diff --git a/java/fory-core/src/test/java/org/apache/fory/builder/ObjectCodecBuilderTest.java b/java/fory-core/src/test/java/org/apache/fory/builder/ObjectCodecBuilderTest.java index 2404099366..f5c661379a 100644 --- a/java/fory-core/src/test/java/org/apache/fory/builder/ObjectCodecBuilderTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/builder/ObjectCodecBuilderTest.java @@ -65,7 +65,11 @@ public void genCode(boolean compressNumber) { .build(); new ObjectCodecBuilder(Foo.class, fory).genCode(); // System.out.println(code); - new ObjectCodecBuilder(BeanA.class, fory).genCode(); + String code = new ObjectCodecBuilder(BeanA.class, fory).genCode(); + if (JdkVersion.JDK8_ARM && !compressNumber) { + Assert.assertTrue(code.contains("_UnsafeUtils.getLong(")); + Assert.assertTrue(code.contains("_UnsafeUtils.putLong(")); + } new ObjectCodecBuilder(BeanB.class, fory).genCode(); new ObjectCodecBuilder(Struct.createStructClass("ObjectCodecBuilderTestStruct", 1), fory) .genCode(); diff --git a/java/fory-core/src/test/java/org/apache/fory/platform/internal/UnsafeUtilsTest.java b/java/fory-core/src/test/java/org/apache/fory/platform/internal/UnsafeUtilsTest.java new file mode 100644 index 0000000000..5f68269146 --- /dev/null +++ b/java/fory-core/src/test/java/org/apache/fory/platform/internal/UnsafeUtilsTest.java @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fory.platform.internal; + +import static org.testng.Assert.assertEquals; + +import org.apache.fory.memory.NativeByteOrder; +import org.apache.fory.platform.AndroidSupport; +import org.apache.fory.platform.JdkVersion; +import org.testng.SkipException; +import org.testng.annotations.Test; + +public class UnsafeUtilsTest { + private static final long BYTE_ARRAY_OFFSET = + JdkVersion.MAJOR_VERSION < 25 && !AndroidSupport.IS_ANDROID + ? _UnsafeUtils.UNSAFE.arrayBaseOffset(byte[].class) + : 0; + + private static void requireUnsafe() { + if (JdkVersion.MAJOR_VERSION >= 25 || AndroidSupport.IS_ANDROID) { + throw new SkipException("Indexed Unsafe access is only used by the JDK 8-24 runtime"); + } + } + + @Test + public void testScaledReads() { + requireUnsafe(); + byte[] bytes = new byte[1024]; + for (int i = 0; i < bytes.length; i++) { + bytes[i] = (byte) (i * 37 + 137); + } + long[][] expected = new long[3][32]; + for (int i = 0; i < 32; i++) { + expected[0][i] = word(bytes, (i << 2), 2); + expected[1][i] = word(bytes, (i << 1), 4); + expected[2][i] = word(bytes, (i << 1), 8); + } + // Cross the C2 compilation threshold and vary nonzero indices. Index zero hides the bug. + for (int call = 0; call < 100_000; call++) { + int index = call & 31; + assertEquals(readChar(bytes, index), (char) expected[0][index]); + assertEquals(readShort(bytes, index), (short) expected[0][index]); + assertEquals(readInt(bytes, index), (int) expected[1][index]); + assertEquals(readLong(bytes, index), expected[2][index]); + assertEquals((int) readLong(bytes, index), (int) expected[2][index]); + } + } + + @Test + public void testScaledWrites() { + requireUnsafe(); + byte[] bytes = new byte[1024]; + for (int call = 0; call < 100_000; call++) { + int index = call & 31; + long value = 0xfedcba9876543210L ^ ((long) call << 29); + writeChar(bytes, index, (char) value); + assertEquals(word(bytes, (index << 2), 2), value & 0xffff); + writeShort(bytes, index, (short) value); + assertEquals(word(bytes, (index << 2), 2), value & 0xffff); + writeInt(bytes, index, (int) value); + assertEquals(word(bytes, (index << 1), 4), value & 0xffffffffL); + writeLong(bytes, index, value); + assertEquals(word(bytes, (index << 1), 8), value); + } + } + + private static long word(byte[] bytes, int index, int size) { + long value = 0; + for (int i = 0; i < size; i++) { + int shift = NativeByteOrder.IS_LITTLE_ENDIAN ? i * 8 : (size - i - 1) * 8; + value |= (bytes[index + i] & 0xffL) << shift; + } + return value; + } + + private static char readChar(byte[] bytes, int index) { + return _UnsafeUtils.getChar(bytes, BYTE_ARRAY_OFFSET + ((long) index << 2)); + } + + private static short readShort(byte[] bytes, int index) { + return _UnsafeUtils.getShort(bytes, BYTE_ARRAY_OFFSET + ((long) index << 2)); + } + + private static int readInt(byte[] bytes, int index) { + return _UnsafeUtils.getInt(bytes, BYTE_ARRAY_OFFSET + ((long) index << 1)); + } + + private static long readLong(byte[] bytes, int index) { + return _UnsafeUtils.getLong(bytes, BYTE_ARRAY_OFFSET + ((long) index << 1)); + } + + private static void writeChar(byte[] bytes, int index, char value) { + _UnsafeUtils.putChar(bytes, BYTE_ARRAY_OFFSET + ((long) index << 2), value); + } + + private static void writeShort(byte[] bytes, int index, short value) { + _UnsafeUtils.putShort(bytes, BYTE_ARRAY_OFFSET + ((long) index << 2), value); + } + + private static void writeInt(byte[] bytes, int index, int value) { + _UnsafeUtils.putInt(bytes, BYTE_ARRAY_OFFSET + ((long) index << 1), value); + } + + private static void writeLong(byte[] bytes, int index, long value) { + _UnsafeUtils.putLong(bytes, BYTE_ARRAY_OFFSET + ((long) index << 1), value); + } +} diff --git a/java/fory-core/src/test/java/org/apache/fory/serializer/StringEncodingUtilsTest.java b/java/fory-core/src/test/java/org/apache/fory/serializer/StringEncodingUtilsTest.java index 7df39320e1..9ae368ce50 100644 --- a/java/fory-core/src/test/java/org/apache/fory/serializer/StringEncodingUtilsTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/serializer/StringEncodingUtilsTest.java @@ -23,9 +23,30 @@ import java.nio.charset.StandardCharsets; import org.apache.fory.ForyTestBase; +import org.apache.fory.memory.NativeByteOrder; import org.testng.annotations.Test; public class StringEncodingUtilsTest extends ForyTestBase { + @Test + public void testCharWords() { + char[] chars = new char[512]; + for (int i = 0; i < chars.length; i++) { + chars[i] = (char) (i * 127 + 1); + } + long[] expected = new long[32]; + for (int i = 0; i < expected.length; i++) { + for (int j = 0; j < 4; j++) { + int shift = NativeByteOrder.IS_LITTLE_ENDIAN ? j * 16 : (3 - j) * 16; + expected[i] |= (long) chars[i + j] << shift; + } + } + // Exercise the actual string access owner before and after C2 compilation. + for (int call = 0; call < 100_000; call++) { + int index = call & 31; + assertEquals(PlatformStringUtils.getCharsLong(chars, index), expected[index]); + } + } + @Test public void testUTF8ToUTF16() { String input = "jbmbmner8 jhk hj \n \t üäßß@µ你好";