Skip to content

Instantly share code, notes, and snippets.

@Mr00Anderson
Created December 15, 2020 13:27
Show Gist options
  • Save Mr00Anderson/16c0169d2d9d31f0d718dbdf6f3da9f9 to your computer and use it in GitHub Desktop.
Save Mr00Anderson/16c0169d2d9d31f0d718dbdf6f3da9f9 to your computer and use it in GitHub Desktop.
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