diff --git a/engine/src/main/java/com/arcadedb/serializer/BinaryComparator.java b/engine/src/main/java/com/arcadedb/serializer/BinaryComparator.java index 2aabc5da27..36046d737a 100644 --- a/engine/src/main/java/com/arcadedb/serializer/BinaryComparator.java +++ b/engine/src/main/java/com/arcadedb/serializer/BinaryComparator.java @@ -25,9 +25,11 @@ import com.arcadedb.utility.CollectionUtils; import com.arcadedb.utility.DateUtils; -import java.math.*; -import java.time.temporal.*; -import java.util.*; +import java.math.BigDecimal; +import java.time.temporal.ChronoUnit; +import java.util.Arrays; +import java.util.List; +import java.util.Map; public class BinaryComparator { public int compare(final Object value1, final byte type1, final Object value2, final byte type2) { @@ -329,8 +331,7 @@ public int compare(final Object value1, final byte type1, final Object value2, f } case BinaryTypes.TYPE_LIST: { - switch (type2) { - case BinaryTypes.TYPE_LIST: + if (type2 == BinaryTypes.TYPE_LIST) { final List v1 = value1.getClass().isArray() ? Arrays.asList((Object[]) value1) : (List) value1; final List v2 = value2.getClass().isArray() ? Arrays.asList((Object[]) value2) : (List) value2; @@ -340,8 +341,7 @@ public int compare(final Object value1, final byte type1, final Object value2, f } case BinaryTypes.TYPE_MAP: { - switch (type2) { - case BinaryTypes.TYPE_MAP: + if (type2 == BinaryTypes.TYPE_MAP) { return CollectionUtils.compare((Map) value1, (Map) value2); } break; diff --git a/engine/src/main/java/com/arcadedb/serializer/BinarySerializer.java b/engine/src/main/java/com/arcadedb/serializer/BinarySerializer.java index 104be15c57..d232861e04 100644 --- a/engine/src/main/java/com/arcadedb/serializer/BinarySerializer.java +++ b/engine/src/main/java/com/arcadedb/serializer/BinarySerializer.java @@ -49,12 +49,22 @@ import com.arcadedb.serializer.json.JSONObject; import com.arcadedb.utility.DateUtils; -import java.lang.reflect.*; -import java.math.*; -import java.time.*; -import java.time.temporal.*; -import java.util.*; -import java.util.logging.*; +import java.lang.reflect.Array; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.logging.Level; /** * Default serializer implementation. diff --git a/engine/src/main/java/com/arcadedb/serializer/UnsignedBytesComparator.java b/engine/src/main/java/com/arcadedb/serializer/UnsignedBytesComparator.java index 52dc2a25dd..659511233c 100644 --- a/engine/src/main/java/com/arcadedb/serializer/UnsignedBytesComparator.java +++ b/engine/src/main/java/com/arcadedb/serializer/UnsignedBytesComparator.java @@ -7,8 +7,7 @@ * * 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, + * 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. @@ -18,111 +17,73 @@ */ package com.arcadedb.serializer; -import com.arcadedb.exception.ArcadeDBException; -import sun.misc.Unsafe; - -import java.lang.reflect.*; -import java.nio.*; -import java.security.*; +import java.lang.foreign.MemorySegment; /** * This class was inspired by Guava's UnsignedBytes, under Apache 2 license. + * Updated to use java.lang.foreign.MemorySegment instead of sun.misc.Unsafe + * according to JEP 471 recommendations. * * @author Louis Wasserman * @author Brian Milch * @author Colin Evans * @author Luca Garulli (l.garulli@arcadedata.com) */ + public final class UnsignedBytesComparator { private static final int UNSIGNED_MASK = 0xFF; - private static final Unsafe theUnsafe; - private static final int BYTE_ARRAY_BASE_OFFSET; public static final PureJavaComparator PURE_JAVA_COMPARATOR = new PureJavaComparator(); public static final ByteArrayComparator BEST_COMPARATOR; static { - theUnsafe = getUnsafe(); - BYTE_ARRAY_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class); - // fall back to the safer pure java implementation unless we're in - // a 64-bit JVM with an 8-byte aligned field offset. - if (!("64".equals(System.getProperty("sun.arch.data.model")) && (BYTE_ARRAY_BASE_OFFSET % 8) == 0 - // sanity check - this should never fail - && theUnsafe.arrayIndexScale(byte[].class) == 1)) { + // Fall back to the pure Java implementation unless we're in a 64-bit JVM + // Note: We don't need to check the offset alignment as with MemorySegment API + if (!"64".equals(System.getProperty("sun.arch.data.model"))) { BEST_COMPARATOR = PURE_JAVA_COMPARATOR; // force fallback to PureJavaComparator } else { - BEST_COMPARATOR = new UnsafeComparator(); + BEST_COMPARATOR = new ModernComparator(); } } private UnsignedBytesComparator() { } - public static class UnsafeComparator implements ByteArrayComparator { - static final boolean BIG_ENDIAN = ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN); - + public static class ModernComparator implements ByteArrayComparator { @Override public int compare(final byte[] left, final byte[] right) { - final int stride = 8; - final int minLength = Math.min(left.length, right.length); - final int strideLimit = minLength & -stride; - int i; - - /* - * Compare 8 bytes at a time. Benchmarking on x86 shows a stride of 8 bytes is no slower - * than 4 bytes even on 32-bit. On the other hand, it is substantially faster on 64-bit. - */ - for (i = 0; i < strideLimit; i += stride) { - final long lw = theUnsafe.getLong(left, BYTE_ARRAY_BASE_OFFSET + (long) i); - final long rw = theUnsafe.getLong(right, BYTE_ARRAY_BASE_OFFSET + (long) i); - if (lw != rw) { - if (BIG_ENDIAN) { - return unsignedLongsCompare(lw, rw); - } - final int n = Long.numberOfTrailingZeros(lw ^ rw) & ~0x7; - return ((int) ((lw >>> n) & UNSIGNED_MASK)) - ((int) ((rw >>> n) & UNSIGNED_MASK)); - } + // assumes left and right are non-null + final MemorySegment leftSegment = MemorySegment.ofArray(left); + final MemorySegment rightSegment = MemorySegment.ofArray(right); + + final long index = leftSegment.mismatch(rightSegment); + if (index == -1) { + return Integer.compare(left.length, right.length); } - // The epilogue to cover the last (minLength % stride) elements. - for (; i < minLength; i++) { - final int result = UnsignedBytesComparator.compare(left[i], right[i]); - if (result != 0) - return result; + // index is either the byte offset which differs or the length of the shorter array + if (index >= left.length || index >= right.length) { + return Integer.compare(left.length, right.length); } - return left.length - right.length; + return Integer.compare(Byte.toUnsignedInt(left[(int) index]), Byte.toUnsignedInt(right[(int) index])); } @Override public boolean equals(final byte[] left, final byte[] right, final int length) { - final int stride = 8; - final int strideLimit = length & -stride; - int i; - - /* - * Compare 8 bytes at a time. Benchmarking on x86 shows a stride of 8 bytes is no slower - * than 4 bytes even on 32-bit. On the other hand, it is substantially faster on 64-bit. - */ - for (i = 0; i < strideLimit; i += stride) { - final long lw = theUnsafe.getLong(left, BYTE_ARRAY_BASE_OFFSET + (long) i); - final long rw = theUnsafe.getLong(right, BYTE_ARRAY_BASE_OFFSET + (long) i); - if (lw != rw) - return false; - } + // assumes left and right are non-null and length is non-zero + if (left.length < length || right.length < length) + return false; - // The epilogue to cover the last (minLength % stride) elements. - for (; i < length; i++) { - final int result = UnsignedBytesComparator.compare(left[i], right[i]); - if (result != 0) - return false; - } + final MemorySegment leftSegment = MemorySegment.ofArray(left).asSlice(0, length); + final MemorySegment rightSegment = MemorySegment.ofArray(right).asSlice(0, length); - return true; + // mismatch is optimized and should be faster than a for loop + return leftSegment.mismatch(rightSegment) == -1; } @Override public String toString() { - return "UnsignedBytes.lexicographicalComparator() (sun.misc.Unsafe version)"; + return "UnsignedBytes.lexicographicalComparator() (java.lang.foreign.MemorySegment version)"; } } @@ -169,34 +130,4 @@ public static int unsignedLongsCompare(final long a, final long b) { final long b2 = b ^ Long.MIN_VALUE; return Long.compare(a2, b2); } - - /** - * Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package. Replace with a simple - * call to Unsafe.getUnsafe when integrating into a jdk. - * - * @return a sun.misc.Unsafe - */ - private static Unsafe getUnsafe() { - try { - return Unsafe.getUnsafe(); - } catch (final SecurityException e) { - // that's okay; try reflection instead - } - try { - return AccessController.doPrivileged((PrivilegedExceptionAction) () -> { - final Class k = Unsafe.class; - for (final Field f : k.getDeclaredFields()) { - f.setAccessible(true); - final Object x = f.get(null); - if (k.isInstance(x)) { - return k.cast(x); - } - } - throw new NoSuchFieldError("the Unsafe"); - }); - } catch (final PrivilegedActionException e) { - throw new ArcadeDBException("Could not initialize intrinsics", e.getCause()); - } - } - }