tuple encoding now has fewer copies ; various java 8 vs java 6 inconsistency fixes
This commit is contained in:
parent
1855f876db
commit
957243a88e
|
@ -45,7 +45,7 @@ import com.apple.foundationdb.Range;
|
|||
* <h3>Types</h3>
|
||||
* A {@code Tuple} can
|
||||
* contain byte arrays ({@code byte[]}), {@link String}s, {@link Number}s, {@link UUID}s,
|
||||
* {@code boolean}s, {@link List}s, other {@code Tuple}s, and {@code null}.
|
||||
* {@code boolean}s, {@link List}s, {@link Versionstamp}s, other {@code Tuple}s, and {@code null}.
|
||||
* {@link Float} and {@link Double} instances will be serialized as single- and double-precision
|
||||
* numbers respectively, and {@link BigInteger}s within the range [{@code -2^2040+1},
|
||||
* {@code 2^2040-1}] are serialized without loss of precision (those outside the range
|
||||
|
@ -167,8 +167,8 @@ public class Tuple implements Comparable<Tuple>, Iterable<Object> {
|
|||
|
||||
/**
|
||||
* Creates a copy of this {@code Tuple} with a {@link BigInteger} appended as the last element.
|
||||
* As {@link Tuple}s cannot contain {@code null} numeric types, a {@link NullPointerException}
|
||||
* is raised if a {@code null} argument is passed.
|
||||
* As {@link Tuple}s cannot contain {@code null} numeric types, a {@link NullPointerException}
|
||||
* is raised if a {@code null} argument is passed.
|
||||
*
|
||||
* @param bi the {@link BigInteger} to append
|
||||
*
|
||||
|
@ -217,8 +217,8 @@ public class Tuple implements Comparable<Tuple>, Iterable<Object> {
|
|||
|
||||
/**
|
||||
* Creates a copy of this {@code Tuple} with an {@link List} appended as the last element.
|
||||
* This does not add the elements individually (for that, use {@link Tuple#addAll(List) Tuple.addAll}).
|
||||
* This adds the list as a single elemented nested within the outer {@code Tuple}.
|
||||
* This does not add the elements individually (for that, use {@link Tuple#addAll(List) Tuple.addAll}).
|
||||
* This adds the list as a single element nested within the outer {@code Tuple}.
|
||||
*
|
||||
* @param l the {@link List} to append
|
||||
*
|
||||
|
@ -230,8 +230,8 @@ public class Tuple implements Comparable<Tuple>, Iterable<Object> {
|
|||
|
||||
/**
|
||||
* Creates a copy of this {@code Tuple} with a {@code Tuple} appended as the last element.
|
||||
* This does not add the elements individually (for that, use {@link Tuple#addAll(Tuple) Tuple.addAll}).
|
||||
* This adds the list as a single elemented nested within the outer {@code Tuple}.
|
||||
* This does not add the elements individually (for that, use {@link Tuple#addAll(Tuple) Tuple.addAll}).
|
||||
* This adds the list as a single element nested within the outer {@code Tuple}.
|
||||
*
|
||||
* @param t the {@code Tuple} to append
|
||||
*
|
||||
|
@ -302,11 +302,7 @@ public class Tuple implements Comparable<Tuple>, Iterable<Object> {
|
|||
* @return a serialized representation of this {@code Tuple} prepended by the {@code prefix}.
|
||||
*/
|
||||
public byte[] pack(byte[] prefix) {
|
||||
TupleUtil.EncodeResult encoded = TupleUtil.pack(elements, prefix);
|
||||
if(encoded.versionPos > 0) {
|
||||
throw new IllegalArgumentException("Incomplete Versionstamp included in vanilla tuple pack");
|
||||
}
|
||||
return encoded.data;
|
||||
return TupleUtil.pack(elements, prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -323,7 +319,7 @@ public class Tuple implements Comparable<Tuple>, Iterable<Object> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get an encoded representation of this {@code Tuple} for use with.
|
||||
* Get an encoded representation of this {@code Tuple} for use with
|
||||
* {@link com.apple.foundationdb.MutationType#SET_VERSIONSTAMPED_KEY MutationType.SET_VERSIONSTAMPED_KEY}.
|
||||
* There must be exactly one incomplete {@link Versionstamp} instance within this
|
||||
* {@code Tuple} or this will throw an {@link IllegalArgumentException}.
|
||||
|
@ -340,14 +336,7 @@ public class Tuple implements Comparable<Tuple>, Iterable<Object> {
|
|||
* @throws IllegalArgumentException if there is not exactly one incomplete {@link Versionstamp} included in this {@code Tuple}
|
||||
*/
|
||||
public byte[] packWithVersionstamp(byte[] prefix) {
|
||||
TupleUtil.EncodeResult encoded = TupleUtil.pack(elements, prefix);
|
||||
if(encoded.versionPos < 0) {
|
||||
throw new IllegalArgumentException("No incomplete Versionstamp included in tuple pack with versionstamp");
|
||||
}
|
||||
return ByteBuffer.allocate(encoded.data.length + 2).order(ByteOrder.LITTLE_ENDIAN)
|
||||
.put(encoded.data)
|
||||
.putShort((short)encoded.versionPos)
|
||||
.array();
|
||||
return TupleUtil.packWithVersionstamp(elements, prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -567,7 +556,7 @@ public class Tuple implements Comparable<Tuple>, Iterable<Object> {
|
|||
/**
|
||||
* Gets an indexed item as a {@link UUID}. This function will not do type conversion
|
||||
* and so will throw a {@code ClassCastException} if the element is not a {@code UUID}.
|
||||
* The element at the index may not be {@code null}.
|
||||
* The element at the index may be {@code null}.
|
||||
*
|
||||
* @param index the location of the item to return
|
||||
*
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.nio.charset.Charset;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
@ -34,7 +35,6 @@ import java.util.stream.Stream;
|
|||
|
||||
class TupleUtil {
|
||||
private static final byte nil = 0x00;
|
||||
private static final byte[] nil_rep = new byte[] {nil, (byte)0xFF};
|
||||
private static final BigInteger[] size_limits;
|
||||
private static final Charset UTF8;
|
||||
private static final IterableComparator iterableComparator;
|
||||
|
@ -52,6 +52,15 @@ class TupleUtil {
|
|||
private static final byte UUID_CODE = 0x30;
|
||||
private static final byte VERSIONSTAMP_CODE = 0x33;
|
||||
|
||||
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[] BYTES_ARR = new byte[]{0x01};
|
||||
private static final byte[] STRING_ARR = new byte[]{0x02};
|
||||
private static final byte[] NESTED_ARR = new byte[]{0x05};
|
||||
private static final byte[] FALSE_ARR = new byte[]{0x26};
|
||||
private static final byte[] TRUE_ARR = new byte[]{0x27};
|
||||
private static final byte[] VERSIONSTAMP_ARR = new byte[]{0x33};
|
||||
|
||||
static {
|
||||
size_limits = new BigInteger[9];
|
||||
for(int i = 0; i < 9; i++) {
|
||||
|
@ -72,12 +81,12 @@ class TupleUtil {
|
|||
}
|
||||
|
||||
static class EncodeResult {
|
||||
final int totalLength;
|
||||
final int versionPos;
|
||||
final byte[] data;
|
||||
|
||||
EncodeResult(int versionPos, byte[] data) {
|
||||
EncodeResult(int totalLength, int versionPos) {
|
||||
this.totalLength = totalLength;
|
||||
this.versionPos = versionPos;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -148,65 +157,70 @@ class TupleUtil {
|
|||
throw new IllegalArgumentException("Unsupported data type: " + o.getClass().getName());
|
||||
}
|
||||
|
||||
static EncodeResult encode(Object t, boolean nested) {
|
||||
static EncodeResult encode(Object t, boolean nested, List<byte[]> encoded) {
|
||||
if(t == null)
|
||||
if (nested)
|
||||
return new EncodeResult(-1, new byte[]{nil, (byte) 0xff});
|
||||
else
|
||||
return new EncodeResult(-1, new byte[]{nil});
|
||||
if (nested) {
|
||||
encoded.add(NULL_ESCAPED_ARR);
|
||||
return new EncodeResult(NULL_ESCAPED_ARR.length,-1);
|
||||
}
|
||||
else {
|
||||
encoded.add(NULL_ARR);
|
||||
return new EncodeResult(NULL_ARR.length, -1);
|
||||
}
|
||||
if(t instanceof byte[])
|
||||
return encode((byte[])t);
|
||||
return encode((byte[]) t, encoded);
|
||||
if(t instanceof String)
|
||||
return encode((String)t);
|
||||
return encode((String)t, encoded);
|
||||
if(t instanceof BigInteger)
|
||||
return encode((BigInteger)t);
|
||||
return encode((BigInteger)t, encoded);
|
||||
if(t instanceof Float)
|
||||
return encode((Float)t);
|
||||
return encode((Float)t, encoded);
|
||||
if(t instanceof Double)
|
||||
return encode((Double)t);
|
||||
return encode((Double)t, encoded);
|
||||
if(t instanceof Boolean)
|
||||
return encode((Boolean)t);
|
||||
return encode((Boolean)t, encoded);
|
||||
if(t instanceof UUID)
|
||||
return encode((UUID)t);
|
||||
return encode((UUID)t, encoded);
|
||||
if(t instanceof Number)
|
||||
return encode(((Number)t).longValue());
|
||||
return encode(((Number)t).longValue(), encoded);
|
||||
if(t instanceof Versionstamp)
|
||||
return encode((Versionstamp)t);
|
||||
return encode((Versionstamp)t, encoded);
|
||||
if(t instanceof List<?>)
|
||||
return encode((List<?>)t);
|
||||
return encode((List<?>)t, encoded);
|
||||
if(t instanceof Tuple)
|
||||
return encode(((Tuple)t).getItems());
|
||||
return encode(((Tuple)t).getItems(), encoded);
|
||||
throw new IllegalArgumentException("Unsupported data type: " + t.getClass().getName());
|
||||
}
|
||||
|
||||
static EncodeResult encode(Object t) {
|
||||
return encode(t, false);
|
||||
static EncodeResult encode(Object t, List<byte[]> encoded) {
|
||||
return encode(t, false, encoded);
|
||||
}
|
||||
|
||||
static EncodeResult encode(byte[] bytes) {
|
||||
List<byte[]> list = new ArrayList<byte[]>(3);
|
||||
list.add(new byte[] {BYTES_CODE});
|
||||
list.add(ByteArrayUtil.replace(bytes, new byte[] {nil}, nil_rep));
|
||||
list.add(new byte[] {nil});
|
||||
static EncodeResult encode(byte[] bytes, List<byte[]> encoded) {
|
||||
encoded.add(BYTES_ARR);
|
||||
byte[] escaped = ByteArrayUtil.replace(bytes, NULL_ARR, NULL_ESCAPED_ARR);
|
||||
encoded.add(escaped);
|
||||
encoded.add(new byte[] {nil});
|
||||
|
||||
//System.out.println("Joining bytes...");
|
||||
return new EncodeResult(-1, ByteArrayUtil.join(null, list));
|
||||
return new EncodeResult(2 + escaped.length,-1);
|
||||
}
|
||||
|
||||
static EncodeResult encode(String s) {
|
||||
List<byte[]> list = new ArrayList<byte[]>(3);
|
||||
list.add(new byte[] {STRING_CODE});
|
||||
list.add(ByteArrayUtil.replace(s.getBytes(UTF8), new byte[] {nil}, nil_rep));
|
||||
list.add(new byte[] {nil});
|
||||
static EncodeResult encode(String s, List<byte[]> encoded) {
|
||||
encoded.add(STRING_ARR);
|
||||
byte[] escaped = ByteArrayUtil.replace(s.getBytes(UTF8), NULL_ARR, NULL_ESCAPED_ARR);
|
||||
encoded.add(escaped);
|
||||
encoded.add(NULL_ARR);
|
||||
|
||||
//System.out.println("Joining string...");
|
||||
return new EncodeResult(-1, ByteArrayUtil.join(null, list));
|
||||
return new EncodeResult(2 + escaped.length, -1);
|
||||
}
|
||||
|
||||
static EncodeResult encode(BigInteger i) {
|
||||
static EncodeResult encode(BigInteger i, List<byte[]> encoded) {
|
||||
//System.out.println("Encoding integral " + i);
|
||||
if(i.equals(BigInteger.ZERO)) {
|
||||
return new EncodeResult(-1, new byte[] { INT_ZERO_CODE });
|
||||
encoded.add(new byte[]{INT_ZERO_CODE});
|
||||
return new EncodeResult(1,-1);
|
||||
}
|
||||
byte[] bytes = i.toByteArray();
|
||||
if(i.compareTo(BigInteger.ZERO) > 0) {
|
||||
|
@ -219,7 +233,8 @@ class TupleUtil {
|
|||
result[0] = POS_INT_END;
|
||||
result[1] = (byte)(length);
|
||||
System.arraycopy(bytes, bytes.length - length, result, 2, length);
|
||||
return new EncodeResult(-1, result);
|
||||
encoded.add(result);
|
||||
return new EncodeResult(result.length, -1);
|
||||
}
|
||||
int n = ByteArrayUtil.bisectLeft(size_limits, i);
|
||||
assert n <= size_limits.length;
|
||||
|
@ -228,7 +243,8 @@ class TupleUtil {
|
|||
byte[] result = new byte[n+1];
|
||||
result[0] = (byte)(INT_ZERO_CODE + n);
|
||||
System.arraycopy(bytes, bytes.length - n, result, 1, n);
|
||||
return new EncodeResult(-1, result);
|
||||
encoded.add(result);
|
||||
return new EncodeResult(result.length, -1);
|
||||
}
|
||||
if(i.negate().compareTo(size_limits[size_limits.length-1]) > 0) {
|
||||
int length = byteLength(i.negate().toByteArray());
|
||||
|
@ -246,7 +262,8 @@ class TupleUtil {
|
|||
Arrays.fill(result, 2, result.length - adjusted.length, (byte)0x00);
|
||||
System.arraycopy(adjusted, 0, result, result.length - adjusted.length, adjusted.length);
|
||||
}
|
||||
return new EncodeResult(-1, result);
|
||||
encoded.add(result);
|
||||
return new EncodeResult(result.length, -1);
|
||||
}
|
||||
int n = ByteArrayUtil.bisectLeft(size_limits, i.negate());
|
||||
|
||||
|
@ -257,70 +274,71 @@ class TupleUtil {
|
|||
byte[] result = new byte[n+1];
|
||||
result[0] = (byte)(20 - n);
|
||||
System.arraycopy(adjustedBytes, adjustedBytes.length - n, result, 1, n);
|
||||
return new EncodeResult(-1, result);
|
||||
encoded.add(result);
|
||||
return new EncodeResult(result.length, -1);
|
||||
}
|
||||
|
||||
static EncodeResult encode(Integer i) {
|
||||
return encode(i.longValue());
|
||||
static EncodeResult encode(Integer i, List<byte[]> encoded) {
|
||||
return encode(i.longValue(), encoded);
|
||||
}
|
||||
|
||||
static EncodeResult encode(long i) {
|
||||
return encode(BigInteger.valueOf(i));
|
||||
static EncodeResult encode(long i, List<byte[]> encoded) {
|
||||
return encode(BigInteger.valueOf(i), encoded);
|
||||
}
|
||||
|
||||
static EncodeResult encode(Float f) {
|
||||
static EncodeResult encode(Float f, List<byte[]> encoded) {
|
||||
byte[] result = ByteBuffer.allocate(5).order(ByteOrder.BIG_ENDIAN).put(FLOAT_CODE).putFloat(f).array();
|
||||
floatingPointCoding(result, 1, true);
|
||||
return new EncodeResult(-1, result);
|
||||
encoded.add(result);
|
||||
return new EncodeResult(result.length, -1);
|
||||
}
|
||||
|
||||
static EncodeResult encode(Double d) {
|
||||
static EncodeResult encode(Double d, List<byte[]> encoded) {
|
||||
byte[] result = ByteBuffer.allocate(9).order(ByteOrder.BIG_ENDIAN).put(DOUBLE_CODE).putDouble(d).array();
|
||||
floatingPointCoding(result, 1, true);
|
||||
return new EncodeResult(-1, result);
|
||||
encoded.add(result);
|
||||
return new EncodeResult(result.length, -1);
|
||||
}
|
||||
|
||||
static EncodeResult encode(Boolean b) {
|
||||
static EncodeResult encode(Boolean b, List<byte[]> encoded) {
|
||||
if (b) {
|
||||
return new EncodeResult(-1, new byte[] {TRUE_CODE});
|
||||
encoded.add(TRUE_ARR);
|
||||
} else {
|
||||
return new EncodeResult(-1, new byte[] {FALSE_CODE});
|
||||
encoded.add(FALSE_ARR);
|
||||
}
|
||||
return new EncodeResult(1, -1);
|
||||
}
|
||||
|
||||
static EncodeResult encode(UUID uuid) {
|
||||
static EncodeResult encode(UUID uuid, List<byte[]> encoded) {
|
||||
byte[] result = ByteBuffer.allocate(17).put(UUID_CODE).order(ByteOrder.BIG_ENDIAN)
|
||||
.putLong(uuid.getMostSignificantBits()).putLong(uuid.getLeastSignificantBits())
|
||||
.array();
|
||||
return new EncodeResult(-1, result);
|
||||
encoded.add(result);
|
||||
return new EncodeResult(result.length, -1);
|
||||
}
|
||||
|
||||
static EncodeResult encode(Versionstamp v) {
|
||||
byte[] result = ByteBuffer.allocate(Versionstamp.LENGTH + 1)
|
||||
.put(VERSIONSTAMP_CODE)
|
||||
.put(v.getBytes())
|
||||
.array();
|
||||
return new EncodeResult((v.isComplete() ? -1 : 1), result);
|
||||
static EncodeResult encode(Versionstamp v, List<byte[]> encoded) {
|
||||
encoded.add(VERSIONSTAMP_ARR);
|
||||
encoded.add(v.getBytes());
|
||||
return new EncodeResult(1 + Versionstamp.LENGTH, (v.isComplete() ? -1 : 1));
|
||||
}
|
||||
|
||||
static EncodeResult encode(List<?> value) {
|
||||
List<byte[]> parts = new LinkedList<byte[]>();
|
||||
static EncodeResult encode(List<?> value, List<byte[]> encoded) {
|
||||
int lenSoFar = 0;
|
||||
int versionPos = -1;
|
||||
parts.add(new byte[]{NESTED_CODE});
|
||||
encoded.add(NESTED_ARR);
|
||||
for(Object t : value) {
|
||||
EncodeResult childResult = encode(t, true);
|
||||
EncodeResult childResult = encode(t, true, encoded);
|
||||
if(childResult.versionPos > 0) {
|
||||
if(versionPos > 0) {
|
||||
throw new IllegalArgumentException("Multiple incomplete Versionstamps included in Tuple");
|
||||
}
|
||||
versionPos = lenSoFar + childResult.versionPos;
|
||||
}
|
||||
lenSoFar += childResult.data.length;
|
||||
parts.add(childResult.data);
|
||||
lenSoFar += childResult.totalLength;
|
||||
}
|
||||
parts.add(new byte[]{0x00});
|
||||
return new EncodeResult((versionPos < 0 ? -1 : versionPos + 1), ByteArrayUtil.join(null, parts));
|
||||
encoded.add(NULL_ARR);
|
||||
return new EncodeResult(lenSoFar + 2, (versionPos < 0 ? -1 : versionPos + 1));
|
||||
}
|
||||
|
||||
static DecodeResult decode(byte[] rep, int pos, int last) {
|
||||
|
@ -335,14 +353,14 @@ class TupleUtil {
|
|||
if(code == BYTES_CODE) {
|
||||
int end = ByteArrayUtil.findTerminator(rep, (byte)0x0, (byte)0xff, start, last);
|
||||
//System.out.println("End of byte string: " + end);
|
||||
byte[] range = ByteArrayUtil.replace(rep, start, end - start, nil_rep, 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) + "'");
|
||||
return new DecodeResult(end + 1, range);
|
||||
}
|
||||
if(code == STRING_CODE) {
|
||||
int end = ByteArrayUtil.findTerminator(rep, (byte)0x0, (byte)0xff, start, last);
|
||||
//System.out.println("End of UTF8 string: " + end);
|
||||
byte[] stringBytes = ByteArrayUtil.replace(rep, start, end - start, nil_rep, new byte[] { nil });
|
||||
byte[] stringBytes = ByteArrayUtil.replace(rep, start, end - start, NULL_ESCAPED_ARR, new byte[] { nil });
|
||||
String str = new String(stringBytes, UTF8);
|
||||
//System.out.println(" -> UTF8 string contents: '" + str + "'");
|
||||
return new DecodeResult(end + 1, str);
|
||||
|
@ -437,6 +455,18 @@ class TupleUtil {
|
|||
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) {
|
||||
int code1 = TupleUtil.getCodeFor(item1);
|
||||
int code2 = TupleUtil.getCodeFor(item2);
|
||||
|
@ -473,25 +503,28 @@ class TupleUtil {
|
|||
if(code1 == DOUBLE_CODE) {
|
||||
// This is done over vanilla double comparison basically to handle NaN
|
||||
// sorting correctly.
|
||||
byte[] encoded1 = encode((Double)item1).data;
|
||||
byte[] encoded2 = encode((Double)item2).data;
|
||||
return ByteArrayUtil.compareUnsigned(encoded1, encoded2);
|
||||
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) {
|
||||
// This is done for the same reason that double comparison is done
|
||||
// that way.
|
||||
byte[] encoded1 = encode((Float)item1).data;
|
||||
byte[] encoded2 = encode((Float)item2).data;
|
||||
return ByteArrayUtil.compareUnsigned(encoded1, encoded2);
|
||||
byte[] fBytes1 = ByteBuffer.allocate(4).putFloat((Float)item1).array();
|
||||
byte[] fBytes2 = ByteBuffer.allocate(4).putFloat((Float)item2).array();
|
||||
return compareSignedBigEndian(fBytes1, fBytes2);
|
||||
}
|
||||
if(code1 == FALSE_CODE) {
|
||||
return Boolean.compare((Boolean)item1, (Boolean)item2);
|
||||
}
|
||||
if(code1 == UUID_CODE) {
|
||||
// Java UUID.compareTo is signed.
|
||||
byte[] encoded1 = encode((UUID)item1).data;
|
||||
byte[] encoded2 = encode((UUID)item2).data;
|
||||
return ByteArrayUtil.compareUnsigned(encoded1, encoded2);
|
||||
// Java UUID.compareTo is signed, so we have to used the unsigned methods.
|
||||
UUID uuid1 = (UUID)item1;
|
||||
UUID uuid2 = (UUID)item2;
|
||||
int cmp1 = Long.compareUnsigned(uuid1.getMostSignificantBits(), uuid2.getMostSignificantBits());
|
||||
if(cmp1 != 0)
|
||||
return cmp1;
|
||||
return Long.compareUnsigned(uuid1.getLeastSignificantBits(), uuid2.getLeastSignificantBits());
|
||||
}
|
||||
if(code1 == VERSIONSTAMP_CODE) {
|
||||
return ((Versionstamp)item1).compareTo((Versionstamp)item2);
|
||||
|
@ -514,29 +547,48 @@ class TupleUtil {
|
|||
return items;
|
||||
}
|
||||
|
||||
static EncodeResult pack(List<Object> items, byte[] prefix) {
|
||||
if(items.size() == 0)
|
||||
return new EncodeResult(-1, new byte[0]);
|
||||
|
||||
List<byte[]> parts = new ArrayList<>(items.size() + (prefix == null ? 0 : 1));
|
||||
static EncodeResult encodeAll(List<Object> items, byte[] prefix, List<byte[]> encoded) {
|
||||
if(prefix != null) {
|
||||
parts.add(prefix);
|
||||
encoded.add(prefix);
|
||||
}
|
||||
int lenSoFar = (prefix == null) ? 0 : prefix.length;
|
||||
int versionPos = -1;
|
||||
for(Object t : items) {
|
||||
EncodeResult result = encode(t);
|
||||
EncodeResult result = encode(t, encoded);
|
||||
if(result.versionPos > 0) {
|
||||
if(versionPos > 0) {
|
||||
throw new IllegalArgumentException("Multiple incomplete Versionstamps included in Tuple");
|
||||
}
|
||||
versionPos = result.versionPos + lenSoFar;
|
||||
}
|
||||
lenSoFar += result.data.length;
|
||||
parts.add(result.data);
|
||||
lenSoFar += result.totalLength;
|
||||
}
|
||||
//System.out.println("Joining whole tuple...");
|
||||
return new EncodeResult(versionPos, ByteArrayUtil.join(null, parts));
|
||||
return new EncodeResult(lenSoFar, versionPos);
|
||||
}
|
||||
|
||||
static byte[] pack(List<Object> items, byte[] prefix) {
|
||||
List<byte[]> encoded = new ArrayList<byte[]>(2 * items.size() + (prefix == null ? 0 : 1));
|
||||
EncodeResult result = encodeAll(items, prefix, encoded);
|
||||
if(result.versionPos > 0) {
|
||||
throw new IllegalArgumentException("Incomplete Versionstamp included in vanilla tuple pack");
|
||||
} else {
|
||||
return ByteArrayUtil.join(null, encoded);
|
||||
}
|
||||
}
|
||||
|
||||
static byte[] packWithVersionstamp(List<Object> items, byte[] prefix) {
|
||||
List<byte[]> encoded = new ArrayList<byte[]>(2 * items.size() + (prefix == null ? 1 : 2));
|
||||
EncodeResult result = encodeAll(items, prefix, encoded);
|
||||
if(result.versionPos < 0) {
|
||||
throw new IllegalArgumentException("No incomplete Versionstamp included in tuple pack with versionstamp");
|
||||
} else {
|
||||
if(result.versionPos > 0xffff) {
|
||||
throw new IllegalArgumentException("Tuple has incomplete version at position " + result.versionPos + " which is greater than the maximum " + 0xffff);
|
||||
}
|
||||
encoded.add(ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN).putShort((short)result.versionPos).array());
|
||||
return ByteArrayUtil.join(null, encoded);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean hasIncompleteVersionstamp(Stream<?> items) {
|
||||
|
@ -557,7 +609,7 @@ class TupleUtil {
|
|||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
byte[] bytes = encode( 4 ).data;
|
||||
byte[] bytes = pack(Collections.singletonList(4), null );
|
||||
assert 4 == (Integer)(decode( bytes, 0, bytes.length ).o);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
@ -565,7 +617,7 @@ class TupleUtil {
|
|||
}
|
||||
|
||||
try {
|
||||
byte[] bytes = encode( "\u021Aest \u0218tring" ).data;
|
||||
byte[] bytes = pack( Collections.singletonList("\u021Aest \u0218tring"), null );
|
||||
String string = (String)(decode( bytes, 0, bytes.length ).o);
|
||||
System.out.println("contents -> " + string);
|
||||
assert "\u021Aest \u0218tring" == string;
|
||||
|
@ -576,10 +628,10 @@ class TupleUtil {
|
|||
|
||||
/*Object[] a = new Object[] { "\u0000a", -2, "b\u0001", 12345, ""};
|
||||
List<Object> o = Arrays.asList(a);
|
||||
byte[] packed = pack( o );
|
||||
byte[] packed = pack( o, null );
|
||||
System.out.println("packed length: " + packed.length);
|
||||
o = unpack( packed );
|
||||
System.out.println("unpacked elements: " + packed);
|
||||
o = unpack( packed, 0, packed.length );
|
||||
System.out.println("unpacked elements: " + o);
|
||||
for(Object obj : o)
|
||||
System.out.println(" -> type: " + obj.getClass().getName());*/
|
||||
}
|
||||
|
|
|
@ -299,11 +299,7 @@ public class Tuple implements Comparable<Tuple>, Iterable<Object> {
|
|||
* @return a serialized representation of this {@code Tuple} prepended by the {@code prefix}.
|
||||
*/
|
||||
public byte[] pack(byte[] prefix) {
|
||||
TupleUtil.EncodeResult encoded = TupleUtil.pack(elements, prefix);
|
||||
if(encoded.versionPos > 0) {
|
||||
throw new IllegalArgumentException("Incomplete Versionstamp included in vanilla tuple pack");
|
||||
}
|
||||
return encoded.data;
|
||||
return TupleUtil.pack(elements, prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -337,14 +333,7 @@ public class Tuple implements Comparable<Tuple>, Iterable<Object> {
|
|||
* @throws IllegalArgumentException if there is not exactly one incomplete {@link Versionstamp} included in this {@code Tuple}
|
||||
*/
|
||||
public byte[] packWithVersionstamp(byte[] prefix) {
|
||||
TupleUtil.EncodeResult encoded = TupleUtil.pack(elements, prefix);
|
||||
if(encoded.versionPos < 0) {
|
||||
throw new IllegalArgumentException("No incomplete Versionstamp included in tuple pack with versionstamp");
|
||||
}
|
||||
return ByteBuffer.allocate(encoded.data.length + 2).order(ByteOrder.LITTLE_ENDIAN)
|
||||
.put(encoded.data)
|
||||
.putShort((short)encoded.versionPos)
|
||||
.array();
|
||||
return TupleUtil.packWithVersionstamp(elements, prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,13 +26,13 @@ import java.nio.ByteOrder;
|
|||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
class TupleUtil {
|
||||
private static final byte nil = 0x00;
|
||||
private static final byte[] nil_rep = new byte[] {nil, (byte)0xFF};
|
||||
private static final BigInteger[] size_limits;
|
||||
private static final Charset UTF8;
|
||||
private static final IterableComparator iterableComparator;
|
||||
|
@ -50,6 +50,15 @@ class TupleUtil {
|
|||
private static final byte UUID_CODE = 0x30;
|
||||
private static final byte VERSIONSTAMP_CODE = 0x33;
|
||||
|
||||
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[] BYTES_ARR = new byte[]{0x01};
|
||||
private static final byte[] STRING_ARR = new byte[]{0x02};
|
||||
private static final byte[] NESTED_ARR = new byte[]{0x05};
|
||||
private static final byte[] FALSE_ARR = new byte[]{0x26};
|
||||
private static final byte[] TRUE_ARR = new byte[]{0x27};
|
||||
private static final byte[] VERSIONSTAMP_ARR = new byte[]{0x33};
|
||||
|
||||
static {
|
||||
size_limits = new BigInteger[9];
|
||||
for(int i = 0; i < 9; i++) {
|
||||
|
@ -70,12 +79,12 @@ class TupleUtil {
|
|||
}
|
||||
|
||||
static class EncodeResult {
|
||||
final int totalLength;
|
||||
final int versionPos;
|
||||
final byte[] data;
|
||||
|
||||
EncodeResult(int versionPos, byte[] data) {
|
||||
EncodeResult(int totalLength, int versionPos) {
|
||||
this.totalLength = totalLength;
|
||||
this.versionPos = versionPos;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,65 +161,70 @@ class TupleUtil {
|
|||
throw new IllegalArgumentException("Unsupported data type: " + o.getClass().getName());
|
||||
}
|
||||
|
||||
static EncodeResult encode(Object t, boolean nested) {
|
||||
static EncodeResult encode(Object t, boolean nested, List<byte[]> encoded) {
|
||||
if(t == null)
|
||||
if (nested)
|
||||
return new EncodeResult(-1, new byte[]{nil, (byte) 0xff});
|
||||
else
|
||||
return new EncodeResult(-1, new byte[]{nil});
|
||||
if (nested) {
|
||||
encoded.add(NULL_ESCAPED_ARR);
|
||||
return new EncodeResult(NULL_ESCAPED_ARR.length,-1);
|
||||
}
|
||||
else {
|
||||
encoded.add(NULL_ARR);
|
||||
return new EncodeResult(NULL_ARR.length, -1);
|
||||
}
|
||||
if(t instanceof byte[])
|
||||
return encode((byte[])t);
|
||||
return encode((byte[]) t, encoded);
|
||||
if(t instanceof String)
|
||||
return encode((String)t);
|
||||
return encode((String)t, encoded);
|
||||
if(t instanceof BigInteger)
|
||||
return encode((BigInteger)t);
|
||||
return encode((BigInteger)t, encoded);
|
||||
if(t instanceof Float)
|
||||
return encode((Float)t);
|
||||
return encode((Float)t, encoded);
|
||||
if(t instanceof Double)
|
||||
return encode((Double)t);
|
||||
return encode((Double)t, encoded);
|
||||
if(t instanceof Boolean)
|
||||
return encode((Boolean)t);
|
||||
return encode((Boolean)t, encoded);
|
||||
if(t instanceof UUID)
|
||||
return encode((UUID)t);
|
||||
return encode((UUID)t, encoded);
|
||||
if(t instanceof Number)
|
||||
return encode(((Number)t).longValue());
|
||||
return encode(((Number)t).longValue(), encoded);
|
||||
if(t instanceof Versionstamp)
|
||||
return encode((Versionstamp)t);
|
||||
return encode((Versionstamp)t, encoded);
|
||||
if(t instanceof List<?>)
|
||||
return encode((List<?>)t);
|
||||
return encode((List<?>)t, encoded);
|
||||
if(t instanceof Tuple)
|
||||
return encode(((Tuple)t).getItems());
|
||||
return encode(((Tuple)t).getItems(), encoded);
|
||||
throw new IllegalArgumentException("Unsupported data type: " + t.getClass().getName());
|
||||
}
|
||||
|
||||
static EncodeResult encode(Object t) {
|
||||
return encode(t, false);
|
||||
static EncodeResult encode(Object t, List<byte[]> encoded) {
|
||||
return encode(t, false, encoded);
|
||||
}
|
||||
|
||||
static EncodeResult encode(byte[] bytes) {
|
||||
List<byte[]> list = new ArrayList<byte[]>(3);
|
||||
list.add(new byte[] {BYTES_CODE});
|
||||
list.add(ByteArrayUtil.replace(bytes, new byte[] {nil}, nil_rep));
|
||||
list.add(new byte[] {nil});
|
||||
static EncodeResult encode(byte[] bytes, List<byte[]> encoded) {
|
||||
encoded.add(BYTES_ARR);
|
||||
byte[] escaped = ByteArrayUtil.replace(bytes, NULL_ARR, NULL_ESCAPED_ARR);
|
||||
encoded.add(escaped);
|
||||
encoded.add(new byte[] {nil});
|
||||
|
||||
//System.out.println("Joining bytes...");
|
||||
return new EncodeResult(-1, ByteArrayUtil.join(null, list));
|
||||
return new EncodeResult(2 + escaped.length,-1);
|
||||
}
|
||||
|
||||
static EncodeResult encode(String s) {
|
||||
List<byte[]> list = new ArrayList<byte[]>(3);
|
||||
list.add(new byte[] {STRING_CODE});
|
||||
list.add(ByteArrayUtil.replace(s.getBytes(UTF8), new byte[] {nil}, nil_rep));
|
||||
list.add(new byte[] {nil});
|
||||
static EncodeResult encode(String s, List<byte[]> encoded) {
|
||||
encoded.add(STRING_ARR);
|
||||
byte[] escaped = ByteArrayUtil.replace(s.getBytes(UTF8), NULL_ARR, NULL_ESCAPED_ARR);
|
||||
encoded.add(escaped);
|
||||
encoded.add(NULL_ARR);
|
||||
|
||||
//System.out.println("Joining string...");
|
||||
return new EncodeResult(-1, ByteArrayUtil.join(null, list));
|
||||
return new EncodeResult(2 + escaped.length, -1);
|
||||
}
|
||||
|
||||
static EncodeResult encode(BigInteger i) {
|
||||
static EncodeResult encode(BigInteger i, List<byte[]> encoded) {
|
||||
//System.out.println("Encoding integral " + i);
|
||||
if(i.equals(BigInteger.ZERO)) {
|
||||
return new EncodeResult(-1, new byte[] { INT_ZERO_CODE });
|
||||
encoded.add(new byte[]{INT_ZERO_CODE});
|
||||
return new EncodeResult(1,-1);
|
||||
}
|
||||
byte[] bytes = i.toByteArray();
|
||||
if(i.compareTo(BigInteger.ZERO) > 0) {
|
||||
|
@ -223,7 +237,8 @@ class TupleUtil {
|
|||
result[0] = POS_INT_END;
|
||||
result[1] = (byte)(length);
|
||||
System.arraycopy(bytes, bytes.length - length, result, 2, length);
|
||||
return new EncodeResult(-1, result);
|
||||
encoded.add(result);
|
||||
return new EncodeResult(result.length, -1);
|
||||
}
|
||||
int n = ByteArrayUtil.bisectLeft(size_limits, i);
|
||||
assert n <= size_limits.length;
|
||||
|
@ -232,7 +247,8 @@ class TupleUtil {
|
|||
byte[] result = new byte[n+1];
|
||||
result[0] = (byte)(INT_ZERO_CODE + n);
|
||||
System.arraycopy(bytes, bytes.length - n, result, 1, n);
|
||||
return new EncodeResult(-1, result);
|
||||
encoded.add(result);
|
||||
return new EncodeResult(result.length, -1);
|
||||
}
|
||||
if(i.negate().compareTo(size_limits[size_limits.length-1]) > 0) {
|
||||
int length = byteLength(i.negate().toByteArray());
|
||||
|
@ -250,7 +266,8 @@ class TupleUtil {
|
|||
Arrays.fill(result, 2, result.length - adjusted.length, (byte)0x00);
|
||||
System.arraycopy(adjusted, 0, result, result.length - adjusted.length, adjusted.length);
|
||||
}
|
||||
return new EncodeResult(-1, result);
|
||||
encoded.add(result);
|
||||
return new EncodeResult(result.length, -1);
|
||||
}
|
||||
int n = ByteArrayUtil.bisectLeft(size_limits, i.negate());
|
||||
|
||||
|
@ -261,70 +278,71 @@ class TupleUtil {
|
|||
byte[] result = new byte[n+1];
|
||||
result[0] = (byte)(20 - n);
|
||||
System.arraycopy(adjustedBytes, adjustedBytes.length - n, result, 1, n);
|
||||
return new EncodeResult(-1, result);
|
||||
encoded.add(result);
|
||||
return new EncodeResult(result.length, -1);
|
||||
}
|
||||
|
||||
static EncodeResult encode(Integer i) {
|
||||
return encode(i.longValue());
|
||||
static EncodeResult encode(Integer i, List<byte[]> encoded) {
|
||||
return encode(i.longValue(), encoded);
|
||||
}
|
||||
|
||||
static EncodeResult encode(long i) {
|
||||
return encode(BigInteger.valueOf(i));
|
||||
static EncodeResult encode(long i, List<byte[]> encoded) {
|
||||
return encode(BigInteger.valueOf(i), encoded);
|
||||
}
|
||||
|
||||
static EncodeResult encode(Float f) {
|
||||
static EncodeResult encode(Float f, List<byte[]> encoded) {
|
||||
byte[] result = ByteBuffer.allocate(5).order(ByteOrder.BIG_ENDIAN).put(FLOAT_CODE).putFloat(f).array();
|
||||
floatingPointCoding(result, 1, true);
|
||||
return new EncodeResult(-1, result);
|
||||
encoded.add(result);
|
||||
return new EncodeResult(result.length, -1);
|
||||
}
|
||||
|
||||
static EncodeResult encode(Double d) {
|
||||
static EncodeResult encode(Double d, List<byte[]> encoded) {
|
||||
byte[] result = ByteBuffer.allocate(9).order(ByteOrder.BIG_ENDIAN).put(DOUBLE_CODE).putDouble(d).array();
|
||||
floatingPointCoding(result, 1, true);
|
||||
return new EncodeResult(-1, result);
|
||||
encoded.add(result);
|
||||
return new EncodeResult(result.length, -1);
|
||||
}
|
||||
|
||||
static EncodeResult encode(Boolean b) {
|
||||
static EncodeResult encode(Boolean b, List<byte[]> encoded) {
|
||||
if (b) {
|
||||
return new EncodeResult(-1, new byte[] {TRUE_CODE});
|
||||
encoded.add(TRUE_ARR);
|
||||
} else {
|
||||
return new EncodeResult(-1, new byte[] {FALSE_CODE});
|
||||
encoded.add(FALSE_ARR);
|
||||
}
|
||||
return new EncodeResult(1, -1);
|
||||
}
|
||||
|
||||
static EncodeResult encode(UUID uuid) {
|
||||
static EncodeResult encode(UUID uuid, List<byte[]> encoded) {
|
||||
byte[] result = ByteBuffer.allocate(17).put(UUID_CODE).order(ByteOrder.BIG_ENDIAN)
|
||||
.putLong(uuid.getMostSignificantBits()).putLong(uuid.getLeastSignificantBits())
|
||||
.array();
|
||||
return new EncodeResult(-1, result);
|
||||
encoded.add(result);
|
||||
return new EncodeResult(result.length, -1);
|
||||
}
|
||||
|
||||
static EncodeResult encode(Versionstamp v) {
|
||||
byte[] result = ByteBuffer.allocate(Versionstamp.LENGTH + 1)
|
||||
.put(VERSIONSTAMP_CODE)
|
||||
.put(v.getBytes())
|
||||
.array();
|
||||
return new EncodeResult((v.isComplete() ? -1 : 1), result);
|
||||
static EncodeResult encode(Versionstamp v, List<byte[]> encoded) {
|
||||
encoded.add(VERSIONSTAMP_ARR);
|
||||
encoded.add(v.getBytes());
|
||||
return new EncodeResult(1 + Versionstamp.LENGTH, (v.isComplete() ? -1 : 1));
|
||||
}
|
||||
|
||||
static EncodeResult encode(List<?> value) {
|
||||
List<byte[]> parts = new LinkedList<byte[]>();
|
||||
static EncodeResult encode(List<?> value, List<byte[]> encoded) {
|
||||
int lenSoFar = 0;
|
||||
int versionPos = -1;
|
||||
parts.add(new byte[]{NESTED_CODE});
|
||||
encoded.add(NESTED_ARR);
|
||||
for(Object t : value) {
|
||||
EncodeResult childResult = encode(t, true);
|
||||
EncodeResult childResult = encode(t, true, encoded);
|
||||
if(childResult.versionPos > 0) {
|
||||
if(versionPos > 0) {
|
||||
throw new IllegalArgumentException("Multiple incomplete Versionstamps included in Tuple");
|
||||
}
|
||||
versionPos = lenSoFar + childResult.versionPos;
|
||||
}
|
||||
lenSoFar += childResult.data.length;
|
||||
parts.add(childResult.data);
|
||||
lenSoFar += childResult.totalLength;
|
||||
}
|
||||
parts.add(new byte[]{0x00});
|
||||
return new EncodeResult((versionPos < 0 ? -1 : versionPos + 1), ByteArrayUtil.join(null, parts));
|
||||
encoded.add(NULL_ARR);
|
||||
return new EncodeResult(lenSoFar + 2, (versionPos < 0 ? -1 : versionPos + 1));
|
||||
}
|
||||
|
||||
static DecodeResult decode(byte[] rep, int pos, int last) {
|
||||
|
@ -339,14 +357,14 @@ class TupleUtil {
|
|||
if(code == BYTES_CODE) {
|
||||
int end = ByteArrayUtil.findTerminator(rep, (byte)0x0, (byte)0xff, start, last);
|
||||
//System.out.println("End of byte string: " + end);
|
||||
byte[] range = ByteArrayUtil.replace(rep, start, end - start, nil_rep, 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) + "'");
|
||||
return new DecodeResult(end + 1, range);
|
||||
}
|
||||
if(code == STRING_CODE) {
|
||||
int end = ByteArrayUtil.findTerminator(rep, (byte)0x0, (byte)0xff, start, last);
|
||||
//System.out.println("End of UTF8 string: " + end);
|
||||
byte[] stringBytes = ByteArrayUtil.replace(rep, start, end - start, nil_rep, new byte[] { nil });
|
||||
byte[] stringBytes = ByteArrayUtil.replace(rep, start, end - start, NULL_ESCAPED_ARR, new byte[] { nil });
|
||||
String str = new String(stringBytes, UTF8);
|
||||
//System.out.println(" -> UTF8 string contents: '" + str + "'");
|
||||
return new DecodeResult(end + 1, str);
|
||||
|
@ -441,6 +459,18 @@ class TupleUtil {
|
|||
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) {
|
||||
int code1 = TupleUtil.getCodeFor(item1);
|
||||
int code2 = TupleUtil.getCodeFor(item2);
|
||||
|
@ -477,16 +507,16 @@ class TupleUtil {
|
|||
if(code1 == DOUBLE_CODE) {
|
||||
// This is done over vanilla double comparison basically to handle NaN
|
||||
// sorting correctly.
|
||||
byte[] encoded1 = encode((Double)item1).data;
|
||||
byte[] encoded2 = encode((Double)item2).data;
|
||||
return ByteArrayUtil.compareUnsigned(encoded1, encoded2);
|
||||
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) {
|
||||
// This is done for the same reason that double comparison is done
|
||||
// that way.
|
||||
byte[] encoded1 = encode((Float)item1).data;
|
||||
byte[] encoded2 = encode((Float)item2).data;
|
||||
return ByteArrayUtil.compareUnsigned(encoded1, encoded2);
|
||||
byte[] fBytes1 = ByteBuffer.allocate(4).putFloat((Float)item1).array();
|
||||
byte[] fBytes2 = ByteBuffer.allocate(4).putFloat((Float)item2).array();
|
||||
return compareSignedBigEndian(fBytes1, fBytes2);
|
||||
}
|
||||
if(code1 == FALSE_CODE) {
|
||||
boolean b1 = (Boolean)item1;
|
||||
|
@ -495,8 +525,16 @@ class TupleUtil {
|
|||
}
|
||||
if(code1 == UUID_CODE) {
|
||||
// Java UUID.compareTo is signed.
|
||||
byte[] encoded1 = encode((UUID)item1).data;
|
||||
byte[] encoded2 = encode((UUID)item2).data;
|
||||
UUID uuid1 = (UUID)item1;
|
||||
UUID uuid2 = (UUID)item2;
|
||||
byte[] encoded1 = ByteBuffer.allocate(16).order(ByteOrder.BIG_ENDIAN)
|
||||
.putLong(uuid1.getMostSignificantBits())
|
||||
.putLong(uuid1.getLeastSignificantBits())
|
||||
.array();
|
||||
byte[] encoded2 = ByteBuffer.allocate(16).order(ByteOrder.BIG_ENDIAN)
|
||||
.putLong(uuid2.getMostSignificantBits())
|
||||
.putLong(uuid2.getLeastSignificantBits())
|
||||
.array();
|
||||
return ByteArrayUtil.compareUnsigned(encoded1, encoded2);
|
||||
}
|
||||
if(code1 == VERSIONSTAMP_CODE) {
|
||||
|
@ -520,29 +558,48 @@ class TupleUtil {
|
|||
return items;
|
||||
}
|
||||
|
||||
static EncodeResult pack(List<Object> items, byte[] prefix) {
|
||||
if(items.size() == 0)
|
||||
return new EncodeResult(-1, new byte[0]);
|
||||
|
||||
List<byte[]> parts = new ArrayList<byte[]>(items.size() + (prefix == null ? 0 : 1));
|
||||
static EncodeResult encodeAll(List<Object> items, byte[] prefix, List<byte[]> encoded) {
|
||||
if(prefix != null) {
|
||||
parts.add(prefix);
|
||||
encoded.add(prefix);
|
||||
}
|
||||
int lenSoFar = (prefix == null) ? 0 : prefix.length;
|
||||
int versionPos = -1;
|
||||
for(Object t : items) {
|
||||
EncodeResult result = encode(t);
|
||||
EncodeResult result = encode(t, encoded);
|
||||
if(result.versionPos > 0) {
|
||||
if(versionPos > 0) {
|
||||
throw new IllegalArgumentException("Multiple incomplete Versionstamps included in Tuple");
|
||||
}
|
||||
versionPos = result.versionPos + lenSoFar;
|
||||
}
|
||||
lenSoFar += result.data.length;
|
||||
parts.add(result.data);
|
||||
lenSoFar += result.totalLength;
|
||||
}
|
||||
//System.out.println("Joining whole tuple...");
|
||||
return new EncodeResult(versionPos, ByteArrayUtil.join(null, parts));
|
||||
return new EncodeResult(lenSoFar, versionPos);
|
||||
}
|
||||
|
||||
static byte[] pack(List<Object> items, byte[] prefix) {
|
||||
List<byte[]> encoded = new ArrayList<byte[]>(2 * items.size() + (prefix == null ? 0 : 1));
|
||||
EncodeResult result = encodeAll(items, prefix, encoded);
|
||||
if(result.versionPos > 0) {
|
||||
throw new IllegalArgumentException("Incomplete Versionstamp included in vanilla tuple pack");
|
||||
} else {
|
||||
return ByteArrayUtil.join(null, encoded);
|
||||
}
|
||||
}
|
||||
|
||||
static byte[] packWithVersionstamp(List<Object> items, byte[] prefix) {
|
||||
List<byte[]> encoded = new ArrayList<byte[]>(2 * items.size() + (prefix == null ? 1 : 2));
|
||||
EncodeResult result = encodeAll(items, prefix, encoded);
|
||||
if(result.versionPos < 0) {
|
||||
throw new IllegalArgumentException("No incomplete Versionstamp included in tuple pack with versionstamp");
|
||||
} else {
|
||||
if(result.versionPos > 0xffff) {
|
||||
throw new IllegalArgumentException("Tuple has incomplete version at position " + result.versionPos + " which is greater than the maximum " + 0xffff);
|
||||
}
|
||||
encoded.add(ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN).putShort((short)result.versionPos).array());
|
||||
return ByteArrayUtil.join(null, encoded);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean hasIncompleteVersionstamp(Iterable<?> items) {
|
||||
|
@ -567,7 +624,7 @@ class TupleUtil {
|
|||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
byte[] bytes = encode( 4 ).data;
|
||||
byte[] bytes = pack( Collections.singletonList((Object)4), null );
|
||||
assert 4 == (Integer)(decode( bytes, 0, bytes.length ).o);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
@ -575,7 +632,7 @@ class TupleUtil {
|
|||
}
|
||||
|
||||
try {
|
||||
byte[] bytes = encode( "\u021Aest \u0218tring" ).data;
|
||||
byte[] bytes = pack( Collections.singletonList((Object)"\u021Aest \u0218tring"), null );
|
||||
String string = (String)(decode( bytes, 0, bytes.length ).o);
|
||||
System.out.println("contents -> " + string);
|
||||
assert "\u021Aest \u0218tring" == string;
|
||||
|
@ -586,10 +643,10 @@ class TupleUtil {
|
|||
|
||||
/*Object[] a = new Object[] { "\u0000a", -2, "b\u0001", 12345, ""};
|
||||
List<Object> o = Arrays.asList(a);
|
||||
byte[] packed = pack( o ).data;
|
||||
byte[] packed = pack( o, null );
|
||||
System.out.println("packed length: " + packed.length);
|
||||
o = unpack( packed );
|
||||
System.out.println("unpacked elements: " + packed);
|
||||
o = unpack( packed, 0, packed.length );
|
||||
System.out.println("unpacked elements: " + o);
|
||||
for(Object obj : o)
|
||||
System.out.println(" -> type: " + obj.getClass().getName());*/
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue