various Java tuple performance tweaks
These include: * Memoizing packed representations within Tuples * Using longs instead of BigIntegers if possible * As much as possible sticking to manipulating primitive types when using floats/doubles
This commit is contained in:
parent
e6ce0ebd27
commit
e9771364d7
|
@ -229,8 +229,7 @@ public class ByteArrayUtil {
|
||||||
int n = Arrays.binarySearch(arr, i);
|
int n = Arrays.binarySearch(arr, i);
|
||||||
if(n >= 0)
|
if(n >= 0)
|
||||||
return n;
|
return n;
|
||||||
int ip = (n + 1) * -1;
|
return (n + 1) * -1;
|
||||||
return ip;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -824,9 +824,12 @@ public class Tuple implements Comparable<Tuple>, Iterable<Object> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the number of bytes in the packed representation of this {@code Tuple}.
|
* Get the number of bytes in the packed representation of this {@code Tuple}. Note that at the
|
||||||
|
* moment, this number is calculated by packing the {@code Tuple} and looking at its size. This method
|
||||||
|
* will memoize the result, however, so asking the same {@code Tuple} for its size multiple times
|
||||||
|
* is a fast operation.
|
||||||
*
|
*
|
||||||
* @return
|
* @return the number of bytes in the packed representation of this {@code Tuple}
|
||||||
*/
|
*/
|
||||||
public int getPackedSize() {
|
public int getPackedSize() {
|
||||||
byte[] p = packMaybeVersionstamp(null);
|
byte[] p = packMaybeVersionstamp(null);
|
||||||
|
@ -847,8 +850,13 @@ public class Tuple implements Comparable<Tuple>, Iterable<Object> {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(Tuple t) {
|
public int compareTo(Tuple t) {
|
||||||
|
if(packed != null && t.packed != null) {
|
||||||
|
return ByteArrayUtil.compareUnsigned(packed, t.packed);
|
||||||
|
}
|
||||||
|
else {
|
||||||
return comparator.compare(elements, t.elements);
|
return comparator.compare(elements, t.elements);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a hash code value for this {@code Tuple}. Computing the hash code is fairly expensive
|
* Returns a hash code value for this {@code Tuple}. Computing the hash code is fairly expensive
|
||||||
|
|
|
@ -36,8 +36,10 @@ import com.apple.foundationdb.FDB;
|
||||||
|
|
||||||
class TupleUtil {
|
class TupleUtil {
|
||||||
private static final byte nil = 0x00;
|
private static final byte nil = 0x00;
|
||||||
private static final BigInteger[] size_limits;
|
private static final BigInteger[] BIG_INT_SIZE_LIMITS;
|
||||||
private static final Charset UTF8;
|
private static final Charset UTF8;
|
||||||
|
private static final BigInteger LONG_MIN_VALUE = BigInteger.valueOf(Long.MIN_VALUE);
|
||||||
|
private static final BigInteger LONG_MAX_VALUE = BigInteger.valueOf(Long.MAX_VALUE);
|
||||||
private static final IterableComparator iterableComparator;
|
private static final IterableComparator iterableComparator;
|
||||||
|
|
||||||
private static final byte BYTES_CODE = 0x01;
|
private static final byte BYTES_CODE = 0x01;
|
||||||
|
@ -55,27 +57,28 @@ class TupleUtil {
|
||||||
|
|
||||||
private static final byte[] NULL_ARR = new byte[] {nil};
|
private static final byte[] NULL_ARR = new byte[] {nil};
|
||||||
private static final byte[] NULL_ESCAPED_ARR = new byte[] {nil, (byte)0xFF};
|
private static final byte[] NULL_ESCAPED_ARR = new byte[] {nil, (byte)0xFF};
|
||||||
private static final byte[] BYTES_ARR = new byte[]{0x01};
|
private static final byte[] BYTES_ARR = new byte[]{BYTES_CODE};
|
||||||
private static final byte[] STRING_ARR = new byte[]{0x02};
|
private static final byte[] STRING_ARR = new byte[]{STRING_CODE};
|
||||||
private static final byte[] NESTED_ARR = new byte[]{0x05};
|
private static final byte[] NESTED_ARR = new byte[]{NESTED_CODE};
|
||||||
private static final byte[] FALSE_ARR = new byte[]{0x26};
|
private static final byte[] INT_ZERO_ARR = new byte[]{INT_ZERO_CODE};
|
||||||
private static final byte[] TRUE_ARR = new byte[]{0x27};
|
private static final byte[] FALSE_ARR = new byte[]{FALSE_CODE};
|
||||||
private static final byte[] VERSIONSTAMP_ARR = new byte[]{0x33};
|
private static final byte[] TRUE_ARR = new byte[]{TRUE_CODE};
|
||||||
|
private static final byte[] VERSIONSTAMP_ARR = new byte[]{VERSIONSTAMP_CODE};
|
||||||
|
|
||||||
static {
|
static {
|
||||||
size_limits = new BigInteger[9];
|
BIG_INT_SIZE_LIMITS = new BigInteger[9];
|
||||||
for(int i = 0; i < 9; i++) {
|
for(int i = 0; i < BIG_INT_SIZE_LIMITS.length; i++) {
|
||||||
size_limits[i] = (BigInteger.ONE).shiftLeft(i * 8).subtract(BigInteger.ONE);
|
BIG_INT_SIZE_LIMITS[i] = (BigInteger.ONE).shiftLeft(i * 8).subtract(BigInteger.ONE);
|
||||||
}
|
}
|
||||||
UTF8 = Charset.forName("UTF-8");
|
UTF8 = Charset.forName("UTF-8");
|
||||||
iterableComparator = new IterableComparator();
|
iterableComparator = new IterableComparator();
|
||||||
}
|
}
|
||||||
|
|
||||||
static class DecodeResult {
|
static class DecodeState {
|
||||||
final List<Object> values;
|
final List<Object> values;
|
||||||
int end;
|
int end;
|
||||||
|
|
||||||
DecodeResult() {
|
DecodeState() {
|
||||||
values = new ArrayList<>();
|
values = new ArrayList<>();
|
||||||
end = 0;
|
end = 0;
|
||||||
}
|
}
|
||||||
|
@ -86,18 +89,18 @@ class TupleUtil {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class EncodeResult {
|
static class EncodeState {
|
||||||
final List<byte[]> encodedValues;
|
final List<byte[]> encodedValues;
|
||||||
int totalLength;
|
int totalLength;
|
||||||
int versionPos;
|
int versionPos;
|
||||||
|
|
||||||
EncodeResult(int capacity) {
|
EncodeState(int capacity) {
|
||||||
this.encodedValues = new ArrayList<>(capacity);
|
this.encodedValues = new ArrayList<>(capacity);
|
||||||
totalLength = 0;
|
totalLength = 0;
|
||||||
versionPos = -1;
|
versionPos = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
EncodeResult add(byte[] encoded, int versionPos) {
|
EncodeState add(byte[] encoded, int versionPos) {
|
||||||
if(versionPos >= 0 && this.versionPos >= 0) {
|
if(versionPos >= 0 && this.versionPos >= 0) {
|
||||||
throw new IllegalArgumentException("Multiple incomplete Versionstamps included in Tuple");
|
throw new IllegalArgumentException("Multiple incomplete Versionstamps included in Tuple");
|
||||||
}
|
}
|
||||||
|
@ -107,7 +110,7 @@ class TupleUtil {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
EncodeResult add(byte[] encoded) {
|
EncodeState add(byte[] encoded) {
|
||||||
encodedValues.add(encoded);
|
encodedValues.add(encoded);
|
||||||
totalLength += encoded.length;
|
totalLength += encoded.length;
|
||||||
return this;
|
return this;
|
||||||
|
@ -122,37 +125,37 @@ class TupleUtil {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// These four functions are for adjusting the encoding of floating point numbers so
|
||||||
* Takes the Big-Endian byte representation of a floating point number and adjusts
|
// that when their byte representation is written out in big-endian order, unsigned
|
||||||
* it so that it sorts correctly. For encoding, if the sign bit is 1 (the number
|
// lexicographic byte comparison orders the values in the same way as the semantic
|
||||||
* is negative), then we need to flip all of the bits; otherwise, just flip the
|
// ordering of the values. This means flipping all bits for negative values and flipping
|
||||||
* sign bit. For decoding, if the sign bit is 0 (the number is negative), then
|
// only the most-significant bit (i.e., the sign bit as all values in Java are signed)
|
||||||
* we also need to flip all of the bits; otherwise, just flip the sign bit.
|
// in the case that the number is positive. For these purposes, 0.0 is positive and -0.0
|
||||||
* This will mutate in place the given array.
|
// is negative.
|
||||||
*
|
|
||||||
* @param bytes Big-Endian IEEE encoding of a floating point number
|
static int encodeFloatBits(float f) {
|
||||||
* @param start the (zero-indexed) first byte in the array to mutate
|
int intBits = Float.floatToRawIntBits(f);
|
||||||
* @param encode <code>true</code> if we encoding the float and <code>false</code> if we are decoding
|
return (intBits < 0) ? (~intBits) : (intBits ^ Integer.MIN_VALUE);
|
||||||
* @return the encoded {@code byte[]}
|
|
||||||
*/
|
|
||||||
static byte[] floatingPointCoding(byte[] bytes, int start, boolean encode) {
|
|
||||||
if(encode && (bytes[start] & (byte)0x80) != (byte)0x00) {
|
|
||||||
for(int i = start; i < bytes.length; i++) {
|
|
||||||
bytes[i] = (byte) (bytes[i] ^ 0xff);
|
|
||||||
}
|
|
||||||
} else if(!encode && (bytes[start] & (byte)0x80) != (byte)0x80) {
|
|
||||||
for(int i = start; i < bytes.length; i++) {
|
|
||||||
bytes[i] = (byte) (bytes[i] ^ 0xff);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
bytes[start] = (byte) (0x80 ^ bytes[start]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return bytes;
|
static long encodeDoubleBits(double d) {
|
||||||
|
long longBits = Double.doubleToRawLongBits(d);
|
||||||
|
return (longBits < 0L) ? (~longBits) : (longBits ^ Long.MIN_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static byte[] join(List<byte[]> items) {
|
static float decodeFloatBits(int i) {
|
||||||
return ByteArrayUtil.join(null, items);
|
int origBits = (i >= 0) ? (~i) : (i ^ Integer.MIN_VALUE);
|
||||||
|
return Float.intBitsToFloat(origBits);
|
||||||
|
}
|
||||||
|
|
||||||
|
static double decodeDoubleBits(long l) {
|
||||||
|
long origBits = (l >= 0) ? (~l) : (l ^ Long.MIN_VALUE);
|
||||||
|
return Double.longBitsToDouble(origBits);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the number of bytes in the representation of a long.
|
||||||
|
static int byteCount(long i) {
|
||||||
|
return (Long.SIZE + 7 - Long.numberOfLeadingZeros(i >= 0 ? i : -i)) / 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void adjustVersionPosition300(byte[] packed, int delta) {
|
private static void adjustVersionPosition300(byte[] packed, int delta) {
|
||||||
|
@ -215,64 +218,64 @@ class TupleUtil {
|
||||||
throw new IllegalArgumentException("Unsupported data type: " + o.getClass().getName());
|
throw new IllegalArgumentException("Unsupported data type: " + o.getClass().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void encode(EncodeResult result, Object t, boolean nested) {
|
static void encode(EncodeState state, Object t, boolean nested) {
|
||||||
if(t == null) {
|
if(t == null) {
|
||||||
if(nested) {
|
if(nested) {
|
||||||
result.add(NULL_ESCAPED_ARR);
|
state.add(NULL_ESCAPED_ARR);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
result.add(NULL_ARR);
|
state.add(NULL_ARR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(t instanceof byte[])
|
else if(t instanceof byte[])
|
||||||
encode(result, (byte[]) t);
|
encode(state, (byte[]) t);
|
||||||
else if(t instanceof String)
|
else if(t instanceof String)
|
||||||
encode(result, (String)t);
|
encode(state, (String)t);
|
||||||
else if(t instanceof BigInteger)
|
|
||||||
encode(result, (BigInteger)t);
|
|
||||||
else if(t instanceof Float)
|
else if(t instanceof Float)
|
||||||
encode(result, (Float)t);
|
encode(state, (Float)t);
|
||||||
else if(t instanceof Double)
|
else if(t instanceof Double)
|
||||||
encode(result, (Double)t);
|
encode(state, (Double)t);
|
||||||
else if(t instanceof Boolean)
|
else if(t instanceof Boolean)
|
||||||
encode(result, (Boolean)t);
|
encode(state, (Boolean)t);
|
||||||
else if(t instanceof UUID)
|
else if(t instanceof UUID)
|
||||||
encode(result, (UUID)t);
|
encode(state, (UUID)t);
|
||||||
|
else if(t instanceof BigInteger)
|
||||||
|
encode(state, (BigInteger)t);
|
||||||
else if(t instanceof Number)
|
else if(t instanceof Number)
|
||||||
encode(result, ((Number)t).longValue());
|
encode(state, ((Number)t).longValue());
|
||||||
else if(t instanceof Versionstamp)
|
else if(t instanceof Versionstamp)
|
||||||
encode(result, (Versionstamp)t);
|
encode(state, (Versionstamp)t);
|
||||||
else if(t instanceof List<?>)
|
else if(t instanceof List<?>)
|
||||||
encode(result, (List<?>)t);
|
encode(state, (List<?>)t);
|
||||||
else if(t instanceof Tuple)
|
else if(t instanceof Tuple)
|
||||||
encode(result, ((Tuple)t).getItems());
|
encode(state, ((Tuple)t).getItems());
|
||||||
else
|
else
|
||||||
throw new IllegalArgumentException("Unsupported data type: " + t.getClass().getName());
|
throw new IllegalArgumentException("Unsupported data type: " + t.getClass().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void encode(EncodeResult result, Object t) {
|
static void encode(EncodeState state, Object t) {
|
||||||
encode(result, t, false);
|
encode(state, t, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void encode(EncodeResult result, byte[] bytes) {
|
static void encode(EncodeState state, byte[] bytes) {
|
||||||
byte[] escaped = ByteArrayUtil.replace(bytes, NULL_ARR, NULL_ESCAPED_ARR);
|
byte[] escaped = ByteArrayUtil.replace(bytes, NULL_ARR, NULL_ESCAPED_ARR);
|
||||||
result.add(BYTES_ARR).add(escaped).add(NULL_ARR);
|
state.add(BYTES_ARR).add(escaped).add(NULL_ARR);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void encode(EncodeResult result, String s) {
|
static void encode(EncodeState state, String s) {
|
||||||
byte[] escaped = ByteArrayUtil.replace(s.getBytes(UTF8), NULL_ARR, NULL_ESCAPED_ARR);
|
byte[] escaped = ByteArrayUtil.replace(s.getBytes(UTF8), NULL_ARR, NULL_ESCAPED_ARR);
|
||||||
result.add(STRING_ARR).add(escaped).add(NULL_ARR);
|
state.add(STRING_ARR).add(escaped).add(NULL_ARR);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void encode(EncodeResult result, BigInteger i) {
|
static void encode(EncodeState state, BigInteger i) {
|
||||||
//System.out.println("Encoding integral " + i);
|
//System.out.println("Encoding integral " + i);
|
||||||
if(i.equals(BigInteger.ZERO)) {
|
if(i.equals(BigInteger.ZERO)) {
|
||||||
result.add(new byte[]{INT_ZERO_CODE});
|
state.add(INT_ZERO_ARR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
byte[] bytes = i.toByteArray();
|
byte[] bytes = i.toByteArray();
|
||||||
if(i.compareTo(BigInteger.ZERO) > 0) {
|
if(i.compareTo(BigInteger.ZERO) > 0) {
|
||||||
if(i.compareTo(size_limits[size_limits.length-1]) > 0) {
|
if(i.compareTo(BIG_INT_SIZE_LIMITS[BIG_INT_SIZE_LIMITS.length-1]) > 0) {
|
||||||
int length = byteLength(bytes);
|
int length = byteLength(bytes);
|
||||||
if(length > 0xff) {
|
if(length > 0xff) {
|
||||||
throw new IllegalArgumentException("BigInteger magnitude is too large (more than 255 bytes)");
|
throw new IllegalArgumentException("BigInteger magnitude is too large (more than 255 bytes)");
|
||||||
|
@ -281,21 +284,20 @@ class TupleUtil {
|
||||||
intBytes[0] = POS_INT_END;
|
intBytes[0] = POS_INT_END;
|
||||||
intBytes[1] = (byte)(length);
|
intBytes[1] = (byte)(length);
|
||||||
System.arraycopy(bytes, bytes.length - length, intBytes, 2, length);
|
System.arraycopy(bytes, bytes.length - length, intBytes, 2, length);
|
||||||
result.add(intBytes);
|
state.add(intBytes);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int n = ByteArrayUtil.bisectLeft(size_limits, i);
|
int n = ByteArrayUtil.bisectLeft(BIG_INT_SIZE_LIMITS, i);
|
||||||
assert n <= size_limits.length;
|
assert n <= BIG_INT_SIZE_LIMITS.length;
|
||||||
//byte[] bytes = ByteBuffer.allocate(8).order(ByteOrder.BIG_ENDIAN).putLong(i).array();
|
|
||||||
//System.out.println(" -- integral has 'n' of " + n + " and output bytes of " + bytes.length);
|
//System.out.println(" -- integral has 'n' of " + n + " and output bytes of " + bytes.length);
|
||||||
byte[] intBytes = new byte[n + 1];
|
byte[] intBytes = new byte[n + 1];
|
||||||
intBytes[0] = (byte) (INT_ZERO_CODE + n);
|
intBytes[0] = (byte) (INT_ZERO_CODE + n);
|
||||||
System.arraycopy(bytes, bytes.length - n, intBytes, 1, n);
|
System.arraycopy(bytes, bytes.length - n, intBytes, 1, n);
|
||||||
result.add(intBytes);
|
state.add(intBytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(i.negate().compareTo(size_limits[size_limits.length - 1]) > 0) {
|
if(i.negate().compareTo(BIG_INT_SIZE_LIMITS[BIG_INT_SIZE_LIMITS.length - 1]) > 0) {
|
||||||
int length = byteLength(i.negate().toByteArray());
|
int length = byteLength(i.negate().toByteArray());
|
||||||
if (length > 0xff) {
|
if (length > 0xff) {
|
||||||
throw new IllegalArgumentException("BigInteger magnitude is too large (more than 255 bytes)");
|
throw new IllegalArgumentException("BigInteger magnitude is too large (more than 255 bytes)");
|
||||||
|
@ -311,92 +313,109 @@ class TupleUtil {
|
||||||
Arrays.fill(intBytes, 2, intBytes.length - adjusted.length, (byte) 0x00);
|
Arrays.fill(intBytes, 2, intBytes.length - adjusted.length, (byte) 0x00);
|
||||||
System.arraycopy(adjusted, 0, intBytes, intBytes.length - adjusted.length, adjusted.length);
|
System.arraycopy(adjusted, 0, intBytes, intBytes.length - adjusted.length, adjusted.length);
|
||||||
}
|
}
|
||||||
result.add(intBytes);
|
state.add(intBytes);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int n = ByteArrayUtil.bisectLeft(size_limits, i.negate());
|
int n = ByteArrayUtil.bisectLeft(BIG_INT_SIZE_LIMITS, i.negate());
|
||||||
|
|
||||||
assert n >= 0 && n < size_limits.length; // can we do this? it seems to be required for the following statement
|
assert n >= 0 && n < BIG_INT_SIZE_LIMITS.length; // can we do this? it seems to be required for the following statement
|
||||||
|
|
||||||
long maxv = size_limits[n].add(i).longValue();
|
long maxv = BIG_INT_SIZE_LIMITS[n].add(i).longValue();
|
||||||
byte[] adjustedBytes = ByteBuffer.allocate(8).order(ByteOrder.BIG_ENDIAN).putLong(maxv).array();
|
byte[] adjustedBytes = ByteBuffer.allocate(8).order(ByteOrder.BIG_ENDIAN).putLong(maxv).array();
|
||||||
byte[] intBytes = new byte[n + 1];
|
byte[] intBytes = new byte[n + 1];
|
||||||
intBytes[0] = (byte) (20 - n);
|
intBytes[0] = (byte) (INT_ZERO_CODE - n);
|
||||||
System.arraycopy(adjustedBytes, adjustedBytes.length - n, intBytes, 1, n);
|
System.arraycopy(adjustedBytes, adjustedBytes.length - n, intBytes, 1, n);
|
||||||
result.add(intBytes);
|
state.add(intBytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void encode(EncodeResult result, Integer i) {
|
static void encode(EncodeState state, long i) {
|
||||||
encode(result, i.longValue());
|
if(i == 0L) {
|
||||||
|
state.add(INT_ZERO_ARR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int n = byteCount(i);
|
||||||
|
byte[] intBytes = new byte[n + 1];
|
||||||
|
// First byte encodes number of bytes (as difference from INT_ZERO_CODE)
|
||||||
|
intBytes[0] = (byte)(INT_ZERO_CODE + (i >= 0 ? n : -n));
|
||||||
|
// For positive integers, copy the bytes in big-endian order excluding leading 0x00 bytes.
|
||||||
|
// For negative integers, copy the bytes of the one's complement representation excluding
|
||||||
|
// the leading 0xff bytes. As Java stores negative values in two's complement, we subtract 1
|
||||||
|
// from negative values.
|
||||||
|
long val = Long.reverseBytes((i >= 0) ? i : (i - 1)) >> (Long.SIZE - 8 * n);
|
||||||
|
for(int x = 1; x < intBytes.length; x++) {
|
||||||
|
intBytes[x] = (byte)(val & 0xff);
|
||||||
|
val >>= 8;
|
||||||
|
}
|
||||||
|
state.add(intBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void encode(EncodeResult result, long i) {
|
static void encode(EncodeState state, Float f) {
|
||||||
encode(result, BigInteger.valueOf(i));
|
byte[] floatBytes = ByteBuffer.allocate(1 + Float.BYTES).order(ByteOrder.BIG_ENDIAN)
|
||||||
|
.put(FLOAT_CODE)
|
||||||
|
.putInt(encodeFloatBits(f))
|
||||||
|
.array();
|
||||||
|
state.add(floatBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void encode(EncodeResult result, Float f) {
|
static void encode(EncodeState state, Double d) {
|
||||||
byte[] floatBytes = ByteBuffer.allocate(5).order(ByteOrder.BIG_ENDIAN).put(FLOAT_CODE).putFloat(f).array();
|
byte[] doubleBytes = ByteBuffer.allocate(1 + Double.BYTES).order(ByteOrder.BIG_ENDIAN)
|
||||||
floatingPointCoding(floatBytes, 1, true);
|
.put(DOUBLE_CODE)
|
||||||
result.add(floatBytes);
|
.putLong(encodeDoubleBits(d))
|
||||||
|
.array();
|
||||||
|
state.add(doubleBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void encode(EncodeResult result, Double d) {
|
static void encode(EncodeState state, Boolean b) {
|
||||||
byte[] doubleBytes = ByteBuffer.allocate(9).order(ByteOrder.BIG_ENDIAN).put(DOUBLE_CODE).putDouble(d).array();
|
|
||||||
floatingPointCoding(doubleBytes, 1, true);
|
|
||||||
result.add(doubleBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void encode(EncodeResult result, Boolean b) {
|
|
||||||
if(b) {
|
if(b) {
|
||||||
result.add(TRUE_ARR);
|
state.add(TRUE_ARR);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
result.add(FALSE_ARR);
|
state.add(FALSE_ARR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void encode(EncodeResult result, UUID uuid) {
|
static void encode(EncodeState state, UUID uuid) {
|
||||||
byte[] uuidBytes = ByteBuffer.allocate(17).put(UUID_CODE).order(ByteOrder.BIG_ENDIAN)
|
byte[] uuidBytes = ByteBuffer.allocate(17).put(UUID_CODE).order(ByteOrder.BIG_ENDIAN)
|
||||||
.putLong(uuid.getMostSignificantBits()).putLong(uuid.getLeastSignificantBits())
|
.putLong(uuid.getMostSignificantBits()).putLong(uuid.getLeastSignificantBits())
|
||||||
.array();
|
.array();
|
||||||
result.add(uuidBytes);
|
state.add(uuidBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void encode(EncodeResult result, Versionstamp v) {
|
static void encode(EncodeState state, Versionstamp v) {
|
||||||
result.add(VERSIONSTAMP_ARR);
|
state.add(VERSIONSTAMP_ARR);
|
||||||
if(v.isComplete()) {
|
if(v.isComplete()) {
|
||||||
result.add(v.getBytes());
|
state.add(v.getBytes());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
result.add(v.getBytes(), result.totalLength);
|
state.add(v.getBytes(), state.totalLength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void encode(EncodeResult result, List<?> value) {
|
static void encode(EncodeState state, List<?> value) {
|
||||||
result.add(NESTED_ARR);
|
state.add(NESTED_ARR);
|
||||||
for(Object t : value) {
|
for(Object t : value) {
|
||||||
encode(result, t, true);
|
encode(state, t, true);
|
||||||
}
|
}
|
||||||
result.add(NULL_ARR);
|
state.add(NULL_ARR);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void decode(DecodeResult result, byte[] rep, int pos, int last) {
|
static void decode(DecodeState state, byte[] rep, int pos, int last) {
|
||||||
//System.out.println("Decoding '" + ArrayUtils.printable(rep) + "' at " + pos);
|
//System.out.println("Decoding '" + ArrayUtils.printable(rep) + "' at " + pos);
|
||||||
|
|
||||||
// SOMEDAY: codes over 127 will be a problem with the signed Java byte mess
|
// SOMEDAY: codes over 127 will be a problem with the signed Java byte mess
|
||||||
int code = rep[pos];
|
int code = rep[pos];
|
||||||
int start = pos + 1;
|
int start = pos + 1;
|
||||||
if(code == nil) {
|
if(code == nil) {
|
||||||
result.add(null, start);
|
state.add(null, start);
|
||||||
}
|
}
|
||||||
else if(code == BYTES_CODE) {
|
else if(code == BYTES_CODE) {
|
||||||
int end = ByteArrayUtil.findTerminator(rep, (byte)0x0, (byte)0xff, start, last);
|
int end = ByteArrayUtil.findTerminator(rep, (byte)0x0, (byte)0xff, start, last);
|
||||||
//System.out.println("End of byte string: " + end);
|
//System.out.println("End of byte string: " + end);
|
||||||
byte[] range = ByteArrayUtil.replace(rep, start, end - start, NULL_ESCAPED_ARR, new byte[] { nil });
|
byte[] range = ByteArrayUtil.replace(rep, start, end - start, NULL_ESCAPED_ARR, new byte[] { nil });
|
||||||
//System.out.println(" -> byte string contents: '" + ArrayUtils.printable(range) + "'");
|
//System.out.println(" -> byte string contents: '" + ArrayUtils.printable(range) + "'");
|
||||||
result.add(range, end + 1);
|
state.add(range, end + 1);
|
||||||
}
|
}
|
||||||
else if(code == STRING_CODE) {
|
else if(code == STRING_CODE) {
|
||||||
int end = ByteArrayUtil.findTerminator(rep, (byte)0x0, (byte)0xff, start, last);
|
int end = ByteArrayUtil.findTerminator(rep, (byte)0x0, (byte)0xff, start, last);
|
||||||
|
@ -404,78 +423,91 @@ class TupleUtil {
|
||||||
byte[] stringBytes = ByteArrayUtil.replace(rep, start, end - start, NULL_ESCAPED_ARR, new byte[] { nil });
|
byte[] stringBytes = ByteArrayUtil.replace(rep, start, end - start, NULL_ESCAPED_ARR, new byte[] { nil });
|
||||||
String str = new String(stringBytes, UTF8);
|
String str = new String(stringBytes, UTF8);
|
||||||
//System.out.println(" -> UTF8 string contents: '" + str + "'");
|
//System.out.println(" -> UTF8 string contents: '" + str + "'");
|
||||||
result.add(str, end + 1);
|
state.add(str, end + 1);
|
||||||
}
|
}
|
||||||
else if(code == FLOAT_CODE) {
|
else if(code == FLOAT_CODE) {
|
||||||
byte[] resBytes = Arrays.copyOfRange(rep, start, start+4);
|
int rawFloatBits = ByteBuffer.wrap(rep, start, Float.BYTES).getInt();
|
||||||
floatingPointCoding(resBytes, 0, false);
|
float res = decodeFloatBits(rawFloatBits);
|
||||||
float res = ByteBuffer.wrap(resBytes).order(ByteOrder.BIG_ENDIAN).getFloat();
|
state.add(res, start + Float.BYTES);
|
||||||
result.add(res, start + Float.BYTES);
|
|
||||||
}
|
}
|
||||||
else if(code == DOUBLE_CODE) {
|
else if(code == DOUBLE_CODE) {
|
||||||
byte[] resBytes = Arrays.copyOfRange(rep, start, start+8);
|
long rawDoubleBits = ByteBuffer.wrap(rep, start, Double.BYTES).getLong();
|
||||||
floatingPointCoding(resBytes, 0, false);
|
double res = decodeDoubleBits(rawDoubleBits);
|
||||||
double res = ByteBuffer.wrap(resBytes).order(ByteOrder.BIG_ENDIAN).getDouble();
|
state.add(res, start + Double.BYTES);
|
||||||
result.add(res, start + Double.BYTES);
|
|
||||||
}
|
}
|
||||||
else if(code == FALSE_CODE) {
|
else if(code == FALSE_CODE) {
|
||||||
result.add(false, start);
|
state.add(false, start);
|
||||||
}
|
}
|
||||||
else if(code == TRUE_CODE) {
|
else if(code == TRUE_CODE) {
|
||||||
result.add(true, start);
|
state.add(true, start);
|
||||||
}
|
}
|
||||||
else if(code == UUID_CODE) {
|
else if(code == UUID_CODE) {
|
||||||
ByteBuffer bb = ByteBuffer.wrap(rep, start, 16).order(ByteOrder.BIG_ENDIAN);
|
ByteBuffer bb = ByteBuffer.wrap(rep, start, 16).order(ByteOrder.BIG_ENDIAN);
|
||||||
long msb = bb.getLong();
|
long msb = bb.getLong();
|
||||||
long lsb = bb.getLong();
|
long lsb = bb.getLong();
|
||||||
result.add(new UUID(msb, lsb), start + 16);
|
state.add(new UUID(msb, lsb), start + 16);
|
||||||
}
|
}
|
||||||
else if(code == POS_INT_END) {
|
else if(code == POS_INT_END) {
|
||||||
int n = rep[start] & 0xff;
|
int n = rep[start] & 0xff;
|
||||||
BigInteger res = new BigInteger(ByteArrayUtil.join(new byte[]{0x00}, Arrays.copyOfRange(rep, start+1, start+n+1)));
|
BigInteger res = new BigInteger(ByteArrayUtil.join(new byte[]{0x00}, Arrays.copyOfRange(rep, start+1, start+n+1)));
|
||||||
result.add(res, start + n + 1);
|
state.add(res, start + n + 1);
|
||||||
}
|
}
|
||||||
else if(code == NEG_INT_START) {
|
else if(code == NEG_INT_START) {
|
||||||
int n = (rep[start] ^ 0xff) & 0xff;
|
int n = (rep[start] ^ 0xff) & 0xff;
|
||||||
BigInteger origValue = new BigInteger(ByteArrayUtil.join(new byte[]{0x00}, Arrays.copyOfRange(rep, start+1, start+n+1)));
|
BigInteger origValue = new BigInteger(ByteArrayUtil.join(new byte[]{0x00}, Arrays.copyOfRange(rep, start+1, start+n+1)));
|
||||||
BigInteger offset = BigInteger.ONE.shiftLeft(n*8).subtract(BigInteger.ONE);
|
BigInteger offset = BigInteger.ONE.shiftLeft(n*8).subtract(BigInteger.ONE);
|
||||||
result.add(origValue.subtract(offset), start + n + 1);
|
state.add(origValue.subtract(offset), start + n + 1);
|
||||||
}
|
}
|
||||||
else if(code > NEG_INT_START && code < POS_INT_END) {
|
else if(code > NEG_INT_START && code < POS_INT_END) {
|
||||||
// decode a long
|
// decode a long
|
||||||
byte[] longBytes = new byte[9];
|
boolean positive = code >= INT_ZERO_CODE;
|
||||||
boolean upper = code >= INT_ZERO_CODE;
|
int n = positive ? code - INT_ZERO_CODE : INT_ZERO_CODE - code;
|
||||||
int n = upper ? code - 20 : 20 - code;
|
|
||||||
int end = start + n;
|
int end = start + n;
|
||||||
|
|
||||||
if(rep.length < end) {
|
if(rep.length < end) {
|
||||||
throw new RuntimeException("Invalid tuple (possible truncation)");
|
throw new RuntimeException("Invalid tuple (possible truncation)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(positive && (n < Long.BYTES || rep[start] > 0)) {
|
||||||
|
long res = 0L;
|
||||||
|
for(int i = start; i < end; i++) {
|
||||||
|
res = (res << 8) + (rep[i] & 0xff);
|
||||||
|
}
|
||||||
|
state.add(res, end);
|
||||||
|
}
|
||||||
|
else if(!positive && (n < Long.BYTES || rep[start] < 0)) {
|
||||||
|
long res = ~0L;
|
||||||
|
for(int i = start; i < end; i++) {
|
||||||
|
res = (res << 8) + (rep[i] & 0xff);
|
||||||
|
}
|
||||||
|
state.add(res + 1, end);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
byte[] longBytes = new byte[9];
|
||||||
System.arraycopy(rep, start, longBytes, longBytes.length-n, n);
|
System.arraycopy(rep, start, longBytes, longBytes.length-n, n);
|
||||||
if (!upper)
|
if (!positive)
|
||||||
for(int i=longBytes.length-n; i<longBytes.length; i++)
|
for(int i=longBytes.length-n; i<longBytes.length; i++)
|
||||||
longBytes[i] = (byte)(longBytes[i] ^ 0xff);
|
longBytes[i] = (byte)(longBytes[i] ^ 0xff);
|
||||||
|
|
||||||
BigInteger val = new BigInteger(longBytes);
|
BigInteger val = new BigInteger(longBytes);
|
||||||
if (!upper) val = val.negate();
|
if (!positive) val = val.negate();
|
||||||
|
|
||||||
// Convert to long if in range -- otherwise, leave as BigInteger.
|
// Convert to long if in range -- otherwise, leave as BigInteger.
|
||||||
if (val.compareTo(BigInteger.valueOf(Long.MIN_VALUE))<0||
|
if (val.compareTo(LONG_MIN_VALUE) >= 0 && val.compareTo(LONG_MAX_VALUE) <= 0) {
|
||||||
val.compareTo(BigInteger.valueOf(Long.MAX_VALUE))>0) {
|
state.add(val.longValue(), end);
|
||||||
|
} else {
|
||||||
// This can occur if the thing can be represented with 8 bytes but not
|
// This can occur if the thing can be represented with 8 bytes but not
|
||||||
// the right sign information.
|
// the right sign information.
|
||||||
result.add(val, end);
|
state.add(val, end);
|
||||||
} else {
|
}
|
||||||
result.add(val.longValue(), end);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(code == VERSIONSTAMP_CODE) {
|
else if(code == VERSIONSTAMP_CODE) {
|
||||||
Versionstamp val = Versionstamp.fromBytes(Arrays.copyOfRange(rep, start, start + Versionstamp.LENGTH));
|
Versionstamp val = Versionstamp.fromBytes(Arrays.copyOfRange(rep, start, start + Versionstamp.LENGTH));
|
||||||
result.add(val, start + Versionstamp.LENGTH);
|
state.add(val, start + Versionstamp.LENGTH);
|
||||||
}
|
}
|
||||||
else if(code == NESTED_CODE) {
|
else if(code == NESTED_CODE) {
|
||||||
DecodeResult subResult = new DecodeResult();
|
DecodeState subResult = new DecodeState();
|
||||||
int endPos = start;
|
int endPos = start;
|
||||||
while(endPos < rep.length) {
|
while(endPos < rep.length) {
|
||||||
if(rep[endPos] == nil) {
|
if(rep[endPos] == nil) {
|
||||||
|
@ -491,25 +523,13 @@ class TupleUtil {
|
||||||
endPos = subResult.end;
|
endPos = subResult.end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result.add(subResult.values, endPos);
|
state.add(subResult.values, endPos);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new IllegalArgumentException("Unknown tuple data type " + code + " at index " + pos);
|
throw new IllegalArgumentException("Unknown tuple data type " + code + " at index " + pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int compareSignedBigEndian(byte[] arr1, byte[] arr2) {
|
|
||||||
if(arr1[0] < 0 && arr2[0] < 0) {
|
|
||||||
return -1 * ByteArrayUtil.compareUnsigned(arr1, arr2);
|
|
||||||
} else if(arr1[0] < 0) {
|
|
||||||
return -1;
|
|
||||||
} else if(arr2[0] < 0) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return ByteArrayUtil.compareUnsigned(arr1, arr2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int compareItems(Object item1, Object item2) {
|
static int compareItems(Object item1, Object item2) {
|
||||||
int code1 = TupleUtil.getCodeFor(item1);
|
int code1 = TupleUtil.getCodeFor(item1);
|
||||||
int code2 = TupleUtil.getCodeFor(item2);
|
int code2 = TupleUtil.getCodeFor(item2);
|
||||||
|
@ -529,33 +549,39 @@ class TupleUtil {
|
||||||
return ByteArrayUtil.compareUnsigned(((String)item1).getBytes(UTF8), ((String)item2).getBytes(UTF8));
|
return ByteArrayUtil.compareUnsigned(((String)item1).getBytes(UTF8), ((String)item2).getBytes(UTF8));
|
||||||
}
|
}
|
||||||
if(code1 == INT_ZERO_CODE) {
|
if(code1 == INT_ZERO_CODE) {
|
||||||
|
if(item1 instanceof Long && item2 instanceof Long) {
|
||||||
|
// This should be the common case, so it's probably worth including as a way out.
|
||||||
|
return Long.compare((Long)item1, (Long)item2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
BigInteger bi1;
|
BigInteger bi1;
|
||||||
if(item1 instanceof BigInteger) {
|
if (item1 instanceof BigInteger) {
|
||||||
bi1 = (BigInteger)item1;
|
bi1 = (BigInteger) item1;
|
||||||
} else {
|
} else {
|
||||||
bi1 = BigInteger.valueOf(((Number)item1).longValue());
|
bi1 = BigInteger.valueOf(((Number) item1).longValue());
|
||||||
}
|
}
|
||||||
BigInteger bi2;
|
BigInteger bi2;
|
||||||
if(item2 instanceof BigInteger) {
|
if (item2 instanceof BigInteger) {
|
||||||
bi2 = (BigInteger)item2;
|
bi2 = (BigInteger) item2;
|
||||||
} else {
|
} else {
|
||||||
bi2 = BigInteger.valueOf(((Number)item2).longValue());
|
bi2 = BigInteger.valueOf(((Number) item2).longValue());
|
||||||
}
|
}
|
||||||
return bi1.compareTo(bi2);
|
return bi1.compareTo(bi2);
|
||||||
}
|
}
|
||||||
if(code1 == DOUBLE_CODE) {
|
|
||||||
// This is done over vanilla double comparison basically to handle NaN
|
|
||||||
// sorting correctly.
|
|
||||||
byte[] dBytes1 = ByteBuffer.allocate(8).putDouble((Double)item1).array();
|
|
||||||
byte[] dBytes2 = ByteBuffer.allocate(8).putDouble((Double)item2).array();
|
|
||||||
return compareSignedBigEndian(dBytes1, dBytes2);
|
|
||||||
}
|
}
|
||||||
if(code1 == FLOAT_CODE) {
|
if(code1 == FLOAT_CODE) {
|
||||||
// This is done for the same reason that double comparison is done
|
// This is done for the same reason that double comparison is done
|
||||||
// that way.
|
// that way.
|
||||||
byte[] fBytes1 = ByteBuffer.allocate(4).putFloat((Float)item1).array();
|
int fbits1 = encodeFloatBits((Float)item1);
|
||||||
byte[] fBytes2 = ByteBuffer.allocate(4).putFloat((Float)item2).array();
|
int fbits2 = encodeFloatBits((Float)item2);
|
||||||
return compareSignedBigEndian(fBytes1, fBytes2);
|
return Integer.compareUnsigned(fbits1, fbits2);
|
||||||
|
}
|
||||||
|
if(code1 == DOUBLE_CODE) {
|
||||||
|
// This is done over vanilla double comparison basically to handle NaN
|
||||||
|
// sorting correctly.
|
||||||
|
long dbits1 = encodeDoubleBits((Double)item1);
|
||||||
|
long dbits2 = encodeDoubleBits((Double)item2);
|
||||||
|
return Long.compareUnsigned(dbits1, dbits2);
|
||||||
}
|
}
|
||||||
if(code1 == FALSE_CODE) {
|
if(code1 == FALSE_CODE) {
|
||||||
return Boolean.compare((Boolean)item1, (Boolean)item2);
|
return Boolean.compare((Boolean)item1, (Boolean)item2);
|
||||||
|
@ -579,51 +605,53 @@ class TupleUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<Object> unpack(byte[] bytes, int start, int length) {
|
static List<Object> unpack(byte[] bytes, int start, int length) {
|
||||||
DecodeResult decodeResult = new DecodeResult();
|
DecodeState decodeState = new DecodeState();
|
||||||
int pos = start;
|
int pos = start;
|
||||||
int end = start + length;
|
int end = start + length;
|
||||||
while(pos < end) {
|
while(pos < end) {
|
||||||
decode(decodeResult, bytes, pos, end);
|
decode(decodeState, bytes, pos, end);
|
||||||
pos = decodeResult.end;
|
pos = decodeState.end;
|
||||||
}
|
}
|
||||||
return decodeResult.values;
|
return decodeState.values;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void encodeAll(EncodeResult result, List<Object> items, byte[] prefix) {
|
static void encodeAll(EncodeState state, List<Object> items, byte[] prefix) {
|
||||||
if(prefix != null) {
|
if(prefix != null) {
|
||||||
result.add(prefix);
|
state.add(prefix);
|
||||||
}
|
}
|
||||||
for(Object t : items) {
|
for(Object t : items) {
|
||||||
encode(result, t);
|
encode(state, t);
|
||||||
}
|
}
|
||||||
//System.out.println("Joining whole tuple...");
|
//System.out.println("Joining whole tuple...");
|
||||||
}
|
}
|
||||||
|
|
||||||
static byte[] pack(List<Object> items, byte[] prefix) {
|
static byte[] pack(List<Object> items, byte[] prefix) {
|
||||||
EncodeResult result = new EncodeResult(2 * items.size() + (prefix == null ? 0 : 1));
|
EncodeState state = new EncodeState(2 * items.size() + (prefix == null ? 0 : 1));
|
||||||
encodeAll(result, items, prefix);
|
encodeAll(state, items, prefix);
|
||||||
if(result.versionPos >= 0) {
|
if(state.versionPos >= 0) {
|
||||||
throw new IllegalArgumentException("Incomplete Versionstamp included in vanilla tuple packInternal");
|
throw new IllegalArgumentException("Incomplete Versionstamp included in vanilla tuple packInternal");
|
||||||
} else {
|
}
|
||||||
return ByteArrayUtil.join(null, result.encodedValues);
|
else {
|
||||||
|
return ByteArrayUtil.join(null, state.encodedValues);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static byte[] packWithVersionstamp(List<Object> items, byte[] prefix) {
|
static byte[] packWithVersionstamp(List<Object> items, byte[] prefix) {
|
||||||
EncodeResult result = new EncodeResult(2 * items.size() + (prefix == null ? 1 : 2));
|
EncodeState state = new EncodeState(2 * items.size() + (prefix == null ? 1 : 2));
|
||||||
encodeAll(result, items, prefix);
|
encodeAll(state, items, prefix);
|
||||||
if(result.versionPos < 0) {
|
if(state.versionPos < 0) {
|
||||||
throw new IllegalArgumentException("No incomplete Versionstamp included in tuple packInternal with versionstamp");
|
throw new IllegalArgumentException("No incomplete Versionstamp included in tuple packInternal with versionstamp");
|
||||||
} else {
|
}
|
||||||
if(result.versionPos > 0xffff) {
|
else {
|
||||||
throw new IllegalArgumentException("Tuple has incomplete version at position " + result.versionPos + " which is greater than the maximum " + 0xffff);
|
if(state.versionPos > 0xffff) {
|
||||||
|
throw new IllegalArgumentException("Tuple has incomplete version at position " + state.versionPos + " which is greater than the maximum " + 0xffff);
|
||||||
}
|
}
|
||||||
if (FDB.instance().getAPIVersion() < 520) {
|
if (FDB.instance().getAPIVersion() < 520) {
|
||||||
result.add(ByteBuffer.allocate(Short.BYTES).order(ByteOrder.LITTLE_ENDIAN).putShort((short)result.versionPos).array());
|
state.add(ByteBuffer.allocate(Short.BYTES).order(ByteOrder.LITTLE_ENDIAN).putShort((short)state.versionPos).array());
|
||||||
} else {
|
} else {
|
||||||
result.add(ByteBuffer.allocate(Integer.BYTES).order(ByteOrder.LITTLE_ENDIAN).putInt(result.versionPos).array());
|
state.add(ByteBuffer.allocate(Integer.BYTES).order(ByteOrder.LITTLE_ENDIAN).putInt(state.versionPos).array());
|
||||||
}
|
}
|
||||||
return ByteArrayUtil.join(null, result.encodedValues);
|
return ByteArrayUtil.join(null, state.encodedValues);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -631,13 +659,17 @@ class TupleUtil {
|
||||||
return items.anyMatch(item -> {
|
return items.anyMatch(item -> {
|
||||||
if(item == null) {
|
if(item == null) {
|
||||||
return false;
|
return false;
|
||||||
} else if(item instanceof Versionstamp) {
|
}
|
||||||
|
else if(item instanceof Versionstamp) {
|
||||||
return !((Versionstamp) item).isComplete();
|
return !((Versionstamp) item).isComplete();
|
||||||
} else if(item instanceof Tuple) {
|
}
|
||||||
|
else if(item instanceof Tuple) {
|
||||||
return hasIncompleteVersionstamp(((Tuple) item).stream());
|
return hasIncompleteVersionstamp(((Tuple) item).stream());
|
||||||
} else if(item instanceof Collection<?>) {
|
}
|
||||||
|
else if(item instanceof Collection<?>) {
|
||||||
return hasIncompleteVersionstamp(((Collection) item).stream());
|
return hasIncompleteVersionstamp(((Collection) item).stream());
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -646,23 +678,25 @@ class TupleUtil {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
try {
|
try {
|
||||||
byte[] bytes = pack(Collections.singletonList(4), null);
|
byte[] bytes = pack(Collections.singletonList(4), null);
|
||||||
DecodeResult result = new DecodeResult();
|
DecodeState result = new DecodeState();
|
||||||
decode(result, bytes, 0, bytes.length);
|
decode(result, bytes, 0, bytes.length);
|
||||||
int val = (int)result.values.get(0);
|
int val = (int)result.values.get(0);
|
||||||
assert 4 == val;
|
assert 4 == val;
|
||||||
} catch (Exception e) {
|
}
|
||||||
|
catch(Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
System.out.println("Error " + e.getMessage());
|
System.out.println("Error " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
byte[] bytes = pack(Collections.singletonList("\u021Aest \u0218tring"), null);
|
byte[] bytes = pack(Collections.singletonList("\u021Aest \u0218tring"), null);
|
||||||
DecodeResult result = new DecodeResult();
|
DecodeState result = new DecodeState();
|
||||||
decode(result, bytes, 0, bytes.length);
|
decode(result, bytes, 0, bytes.length);
|
||||||
String string = (String)result.values.get(0);
|
String string = (String)result.values.get(0);
|
||||||
System.out.println("contents -> " + string);
|
System.out.println("contents -> " + string);
|
||||||
assert "\u021Aest \u0218tring".equals(string);
|
assert "\u021Aest \u0218tring".equals(string);
|
||||||
} catch (Exception e) {
|
}
|
||||||
|
catch(Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
System.out.println("Error " + e.getMessage());
|
System.out.println("Error " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue