Created
December 15, 2020 13:27
-
-
Save Mr00Anderson/16c0169d2d9d31f0d718dbdf6f3da9f9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package app.virtualhex.math; | |
import java.nio.ByteBuffer; | |
import java.nio.charset.Charset; | |
import java.nio.charset.StandardCharsets; | |
import java.util.ArrayList; | |
import java.util.List; | |
public class VLQUtilsNative { | |
/** Returns the encoding size in bytes of its input value. | |
* @param i the integer to be measured | |
* @return the encoding size in bytes of its input value | |
*/ | |
public static int varIntSize(int i) { | |
int result = 0; | |
do { | |
result++; | |
i >>>= 7; | |
} while (i != 0); | |
return result; | |
} | |
/** | |
* Reads a varint from src, places its values into the first element of | |
* dst and returns the offset in to src of the first byte after the varint. | |
* | |
* @param src source buffer to retrieve from | |
* @param offset offset within src | |
* @param dst the resulting int value | |
* @return the updated offset after reading the varint | |
*/ | |
public static int getVarInt(byte[] src, int offset, int[] dst) { | |
int result = 0; | |
int shift = 0; | |
int b; | |
do { | |
if (shift >= 32) { | |
// Out of range | |
throw new IndexOutOfBoundsException("varint too long"); | |
} | |
// Get 7 bits from next byte | |
b = src[offset++]; | |
result |= (b & 0x7F) << shift; | |
shift += 7; | |
} while ((b & 0x80) != 0); | |
dst[0] = result; | |
return offset; | |
} | |
/** | |
* Encodes an integer in a variable-length encoding, 7 bits per byte, into a | |
* destination byte[], following the protocol buffer convention. | |
* | |
* @param v the int value to write to sink | |
* @param out the sink buffer to write to | |
* @param offset the offset within sink to begin writing | |
* @return the updated offset after writing the varint | |
*/ | |
public static int putVarInt(int v, byte[] out, int offset) { | |
do { | |
// Encode next 7 bits + terminator bit | |
int bits = v & 0x7F; | |
v >>>= 7; | |
byte b = (byte) (bits + ((v != 0) ? 0x80 : 0)); | |
out[offset++] = b; | |
} while (v != 0); | |
return offset; | |
} | |
public static byte[] encodeToByteArray(long v) { | |
int numBytes = computeUInt64Size(v); | |
byte[] output = new byte[numBytes]; | |
for (int i = numBytes - 1; i >= 0; i--) { | |
int curByte = (int) (v & 0x7F); | |
if (i != (numBytes - 1)) | |
curByte |= 0x80; | |
output[i] = (byte) curByte; | |
v >>>= 7; | |
} | |
return output; | |
} | |
public static int computeInt32Size(String[] strings){ | |
int length = strings.length; | |
int i = computeUInt32Size(length); | |
int totalSize = 0; | |
for (int j = 0; j < strings.length; j++) { | |
String string = strings[i]; | |
totalSize += VLQUtilsNative.computeUInt32Size(string.length()) + string.length(); | |
} | |
return totalSize + i; | |
} | |
public static int computeInt32Size(List<String> strings){ | |
int size = strings.size(); | |
int i = computeUInt32Size(size); | |
int totalSize = 0; | |
for (int j = 0; j < strings.size(); j++) { | |
String string = strings.get(i); | |
totalSize += VLQUtilsNative.computeUInt32Size(string.length()) + string.length(); | |
} | |
return totalSize + i; | |
} | |
/** | |
* Compute the number of bytes that would be needed to encode a | |
* {@code uint32} field. | |
*/ | |
public static int computeUInt32Size(int v) { | |
if ((v & (~0 << 7)) == 0) { | |
return 1; | |
} | |
if ((v & (~0 << 14)) == 0) { | |
return 2; | |
} | |
if ((v & (~0 << 21)) == 0) { | |
return 3; | |
} | |
if ((v & (~0 << 28)) == 0) { | |
return 4; | |
} | |
return 5; | |
} | |
/** | |
* Compute the number of bytes that would be needed to encode a | |
* {@code uint64} field, including tag. | |
*/ | |
public static int computeUInt64Size(long v) { | |
// handle two popular special cases up front ... | |
if ((v & (~0L << 7)) == 0L) { | |
return 1; | |
} | |
if (v < 0L) { | |
return 10; | |
} | |
// ... leaving us with 8 remaining, which we can divide and conquer | |
int n = 2; | |
if ((v & (~0L << 35)) != 0L) { | |
n += 4; v >>>= 28; | |
} | |
if ((v & (~0L << 21)) != 0L) { | |
n += 2; v >>>= 14; | |
} | |
if ((v & (~0L << 14)) != 0L) { | |
n += 1; | |
} | |
return n; | |
} | |
public static void putVarInt(ByteBuffer out, int v) { | |
while (true) { | |
int bits = v & 0x7f; | |
v >>>= 7; | |
if (v == 0) { | |
out.put((byte) bits); | |
return; | |
} | |
out.put((byte) (bits | 0x80)); | |
} | |
} | |
/** | |
* Reads a varint from the current position of the given ByteBuffer and | |
* returns the decoded value as 32 bit integer. | |
* | |
* <p>The position of the buffer is advanced to the first byte after the | |
* decoded varint. | |
* | |
* @param src the ByteBuffer to get the var int from | |
* @return The integer value of the decoded varint | |
*/ | |
public static int getVarInt(ByteBuffer src) { | |
int tmp; | |
if ((tmp = src.get()) >= 0) { | |
return tmp; | |
} | |
int result = tmp & 0x7f; | |
if ((tmp = src.get()) >= 0) { | |
result |= tmp << 7; | |
} else { | |
result |= (tmp & 0x7f) << 7; | |
if ((tmp = src.get()) >= 0) { | |
result |= tmp << 14; | |
} else { | |
result |= (tmp & 0x7f) << 14; | |
if ((tmp = src.get()) >= 0) { | |
result |= tmp << 21; | |
} else { | |
result |= (tmp & 0x7f) << 21; | |
result |= (tmp = src.get()) << 28; | |
while (tmp < 0) { | |
// We get into this loop only in the case of overflow. | |
// By doing this, we can call getVarInt() instead of | |
// getVarLong() when we only need an int. | |
tmp = src.get(); | |
} | |
} | |
} | |
} | |
return result; | |
} | |
/** | |
* Returns the encoding size in bytes of its input value. | |
* | |
* @param v the long to be measured | |
* @return the encoding size in bytes of a given long value. | |
*/ | |
public static int varLongSize(long v) { | |
int result = 0; | |
do { | |
result++; | |
v >>>= 7; | |
} while (v != 0); | |
return result; | |
} | |
/** | |
* Reads an up to 64 bit long varint from the current position of the | |
* given ByteBuffer and returns the decoded value as long. | |
* | |
* <p>The position of the buffer is advanced to the first byte after the | |
* decoded varint. | |
* | |
* @param src the ByteBuffer to get the var int from | |
* @return The integer value of the decoded long varint | |
*/ | |
public static long getVarLong(ByteBuffer src) { | |
long tmp; | |
if ((tmp = src.get()) >= 0) { | |
return tmp; | |
} | |
long result = tmp & 0x7f; | |
if ((tmp = src.get()) >= 0) { | |
result |= tmp << 7; | |
} else { | |
result |= (tmp & 0x7f) << 7; | |
if ((tmp = src.get()) >= 0) { | |
result |= tmp << 14; | |
} else { | |
result |= (tmp & 0x7f) << 14; | |
if ((tmp = src.get()) >= 0) { | |
result |= tmp << 21; | |
} else { | |
result |= (tmp & 0x7f) << 21; | |
if ((tmp = src.get()) >= 0) { | |
result |= tmp << 28; | |
} else { | |
result |= (tmp & 0x7f) << 28; | |
if ((tmp = src.get()) >= 0) { | |
result |= tmp << 35; | |
} else { | |
result |= (tmp & 0x7f) << 35; | |
if ((tmp = src.get()) >= 0) { | |
result |= tmp << 42; | |
} else { | |
result |= (tmp & 0x7f) << 42; | |
if ((tmp = src.get()) >= 0) { | |
result |= tmp << 49; | |
} else { | |
result |= (tmp & 0x7f) << 49; | |
if ((tmp = src.get()) >= 0) { | |
result |= tmp << 56; | |
} else { | |
result |= (tmp & 0x7f) << 56; | |
result |= ((long) src.get()) << 63; | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
return result; | |
} | |
/** | |
* Encodes a long integer in a variable-length encoding, 7 bits per byte, to a | |
* ByteBuffer sink. | |
* @param v the value to encode | |
* @param out the ByteBuffer to add the encoded value | |
*/ | |
public static void putVarLong(ByteBuffer out, long v) { | |
while (true) { | |
int bits = ((int) v) & 0x7f; | |
v >>>= 7; | |
if (v == 0) { | |
out.put((byte) bits); | |
return; | |
} | |
out.put((byte) (bits | 0x80)); | |
} | |
} | |
private static void encodeLong(ByteBuffer out, long number, int numBytes) { | |
int originalIndex = out.position(); | |
int adjustedI = originalIndex + numBytes; | |
final int capacity = out.capacity(); | |
if (adjustedI > capacity) { | |
out.limit(adjustedI); | |
} | |
out.position(out.position() + numBytes); | |
for (int i = adjustedI - 1; i >= originalIndex; i--) { | |
int curByte = (int) (number & 0x7F); | |
if (i != (adjustedI - 1)) { | |
curByte |= 0x80; | |
} | |
out.put(i, (byte) curByte); | |
number >>>= 7; | |
} | |
} | |
public static ByteBuffer encodeSignedInt(ByteBuffer out, int number) { | |
long result; | |
if (number < 0) { | |
result = ((-(number + 1)) << 1) | 1; | |
} else { | |
result = number << 1; | |
} | |
return encodeLong(out, result); | |
} | |
public static ByteBuffer encodeSignedLong(ByteBuffer out, long number) { | |
long result; | |
if (number < 0) { | |
result = ((-(number + 1)) << 1) | 1; | |
} else { | |
result = number << 1; | |
} | |
return encodeLong(out, result); | |
} | |
public static ByteBuffer encodeLong(ByteBuffer out, long number) { | |
return encodeLong(out, number); | |
} | |
public static ByteBuffer encodeSignedLong(ByteBuffer out, long number, int numBytes) { | |
long result; | |
if (number < 0) { | |
result = ((-(number + 1)) << 1) | 1; | |
} else { | |
result = number << 1; | |
} | |
return encodeLong(out, result); | |
} | |
//////////////// VLQ Decoding //////////////// | |
public static long decodeSigned(byte[] in) { | |
long value = decode(in); | |
if ((value & 1) == 0x00) | |
value = value >> 1; | |
else | |
value = -((value >> 1) + 1); | |
return value; | |
} | |
public static long decode(byte[] in) { | |
long n = 0; | |
for (int i = 0; i < in.length; i++) { | |
int curByte = in[i] & 0xFF; | |
n = (n << 7) | (curByte & 0x7F); | |
if ((curByte & 0x80) == 0) { | |
break; | |
} | |
} | |
return n; | |
} | |
public static long decodeSigned(ByteBuffer in) { | |
long value = decode(in); | |
if ((value & 1) == 0x00) | |
value = value >> 1; | |
else | |
value = -((value >> 1) + 1); | |
return value; | |
} | |
public static long decode(ByteBuffer in) { | |
long n = 0; | |
for (int i = 0; i <= 8; i++) { | |
if (in.remaining() == 0) { | |
return n; | |
} | |
byte curByte = in.get(); | |
n = (n << 7) | (curByte & 0x7f); | |
if ((curByte & 0x80) == 0) { | |
break; | |
} | |
} | |
return n; | |
} | |
public static long decodeSignedIntegerOrLess(byte[] in) { | |
long value = decodeIntegerOrLess(in); | |
if ((value & 1) == 0x00) | |
value = value >> 1; | |
else | |
value = -((value >> 1) + 1); | |
return value; | |
} | |
public static int decodeIntegerOrLess(byte[] b) { | |
int n = 0; | |
for (int i = 0; i < b.length; i++) { | |
int curByte = b[i] & 0xFF; | |
n = (n << 7) | (curByte & 0x7F); | |
if ((curByte & 0x80) == 0) { | |
break; | |
} | |
} | |
return n; | |
} | |
public static long decodeSignedIntegerOrLess(ByteBuffer in) { | |
long value = decodeIntegerOrLess(in); | |
if ((value & 1) == 0x00) | |
value = value >> 1; | |
else | |
value = -((value >> 1) + 1); | |
return value; | |
} | |
public static int decodeIntegerOrLess(ByteBuffer in) { | |
int n = 0; | |
for (int i = 0; i <= 8; i++) { | |
if (in.remaining() == 0) { | |
return n; | |
} | |
byte curByte = in.get(); | |
n = (n << 7) | (curByte & 0x7f); | |
if ((curByte & 0x80) == 0) { | |
break; | |
} | |
} | |
return n; | |
} | |
/** | |
* This will internalRead a VLQ and then a byte arrays to form a String getType from a {@link ByteBuffer} and advanced the b reader index by the length getByHashCode the VLQ and internalRead bytes | |
* | |
* @param in ByteBuffer representing the array to elementSize internalRead | |
* @return String the String that was internalRead | |
*/ | |
public static String decodeString(ByteBuffer in) { | |
byte[] bytes = decodeArray(in); | |
return new String(bytes, Charset.forName("UTF-8")); | |
} | |
/** | |
* This will internalRead a VLQ and then a byte arrays to form a String getType from a {@link ByteBuffer} and advanced the b reader index by the length getByHashCode the VLQ and internalRead bytes | |
* | |
* @param in ByteBuffer representing the array to elementSize internalRead | |
* @return String the String that was internalRead | |
*/ | |
public static String decodeMaybeString(ByteBuffer in) { | |
boolean string = in.get() == 1; | |
if(string){ | |
byte[] bytes = decodeArray(in); | |
return new String(bytes, Charset.forName("UTF-8")); | |
} else { | |
return null; | |
} | |
} | |
/** | |
* This will internalRead a VLQ and then a variant length getByHashCode bytes from a {@link ByteBuffer} and advanced the b reader index by the length getByHashCode the VLQ and internalRead bytes | |
* | |
* @param in ByteBuffer representing the array to elementSize internalRead | |
* @return byte[] the byte[] that was internalRead | |
*/ | |
public static byte[] decodeArray(ByteBuffer in) { | |
int len = getVarInt(in); | |
byte[] bytes = new byte[len]; | |
in.get(bytes, 0, len); | |
return bytes; | |
} | |
/** | |
* This will internalWrite a String getType to a {@link ByteBuffer} and advanced the b writer index by a variant length and the number getByHashCode bytes | |
* | |
* @param out ByteBuffer representing the b to elementSize written to | |
* @param value String getType to elementSize written to the b | |
*/ | |
public static ByteBuffer encode(ByteBuffer out, String value) { | |
byte[] bytes = value.getBytes(Charset.forName("UTF-8")); | |
encode(out, bytes); | |
return out; | |
} | |
public static String[] decodeArrayList(ByteBuffer in){ | |
int listSize = VLQUtilsNative.decodeIntegerOrLess(in); | |
String[] strings = new String[listSize]; | |
for (int i = 0; i < listSize; i++) { | |
strings[i] = VLQUtilsNative.decodeString(in); | |
} | |
return strings; | |
} | |
public static List<String> decodeStringList(ByteBuffer in){ | |
int listSize = VLQUtilsNative.decodeIntegerOrLess(in); | |
ArrayList<String> strings = new ArrayList<>(listSize); | |
for (int i = 0; i < listSize; i++) { | |
strings.add(i, VLQUtilsNative.decodeString(in)); | |
} | |
return strings; | |
} | |
public static ByteBuffer encode(ByteBuffer out, String[] values){ | |
putVarInt(out, values.length); | |
for (int i = 0; i < values.length; i++) { | |
String value = values[i]; | |
encode(out, value); | |
} | |
return out; | |
} | |
public static int computeSmallStringsList(List<String> values){ | |
int listCount = computeUInt32Size(values.size()); | |
int allStringLen = 0; | |
for (int i = 0; i < values.size(); i++) { | |
allStringLen += 1 + values.get(i).length(); // Length field = 1 for smalls | |
} | |
return listCount + allStringLen; | |
} | |
public static ByteBuffer encodeSmallStringListUnsafe(ByteBuffer out, List<String> values){ | |
putVarInt(out, values.size()); | |
for (int i = 0; i < values.size(); i++) { | |
String value = values.get(i); | |
encodeSmallStringUnsafe(out, value); | |
} | |
return out; | |
} | |
public static ByteBuffer encodeSmallStringUnsafe(ByteBuffer out, String string){ | |
out.put((byte) string.length()); | |
out.put(string.getBytes(Charset.forName(StandardCharsets.UTF_8.name()))); | |
return out; | |
} | |
/** | |
* This will internalWrite a variant length byte[] getType to a {@link ByteBuffer} and advanced the b writer index by a variant length and the number getByHashCode bytesvariants | |
* | |
* @param out ByteBuffer representing the b to elementSize written to | |
* @param bytes bytes[] getType to elementSize written to the b | |
*/ | |
public static ByteBuffer encode(ByteBuffer out, byte[] bytes) { | |
putVarInt(out, bytes.length); | |
out.put(bytes); | |
return out; | |
} | |
/** | |
* This will internalWrite a String getType to a {@link ByteBuffer} and advanced the b writer index by a variant length and the number getByHashCode bytes | |
* | |
* @param out ByteBuffer representing the b to elementSize written to | |
* @param value String getType to elementSize written to the b | |
*/ | |
public static ByteBuffer encodeMaybeString(ByteBuffer out, String value) { | |
if(value != null && value.length() > 0){ | |
out.put((byte) 1); | |
byte[] bytes = value.getBytes(Charset.forName("UTF-8")); | |
encode(out, bytes); | |
} else { | |
out.put((byte) 0); | |
} | |
return out; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment