Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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;

Expand All @@ -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;
Expand Down
22 changes: 16 additions & 6 deletions engine/src/main/java/com/arcadedb/serializer/BinarySerializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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)";
}
}

Expand Down Expand Up @@ -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<Unsafe>) () -> {
final Class<Unsafe> 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());
}
}

}
Loading