package org.yamcs.utils; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; public class VarIntUtil { /** * Encodes x as varint in the buffer at position pos and returns the new position * * @param buf * @param x * @return the decoded integer */ static public int writeVarint32(byte[] buf, int pos, int x) { while ((x & ~0x7F) != 0) { buf[pos++] = ((byte)((x & 0x7F) | 0x80)); x >>>= 7; } buf[pos++] = (byte)(x & 0x7F); return pos; } public static void writeVarInt32(ByteBuffer bb, int x) { while ((x & ~0x7F) != 0) { bb.put((byte)((x & 0x7F) | 0x80)); x >>>= 7; } bb.put((byte)(x & 0x7F)); } public static void writeVarInt64(ByteBuffer bb, long x) { while ((x & ~0x7F) != 0) { bb.put((byte)((x & 0x7F) | 0x80)); x >>>= 7; } bb.put((byte)(x & 0x7F)); } public static int readVarInt32(ByteBuffer bb) throws DecodingException { byte b = bb.get(); int v = b &0x7F; for (int shift = 7; (b & 0x80) != 0; shift += 7) { if(shift>28) throw new DecodingException("Invalid VarInt32: more than 5 bytes!"); b = bb.get(); v |= (b & 0x7F) << shift; } return v; } public static long readVarInt64(ByteBuffer bb) { byte b = bb.get(); long v = b &0x7F; for (int shift = 7; (b & 0x80) != 0; shift += 7) { b = bb.get(); v |= (b & 0x7F) << shift; } return v; } public static void writeSignedVarint32(ByteBuffer bb, int x) { writeVarInt32(bb, encodeZigZag(x)); } public static int readSignedVarInt32(ByteBuffer bb) throws DecodingException { return decodeZigZag(readVarInt32(bb)); } //same as above but better for negative numbers static public int encodeSigned(byte[] buf, int pos, int x) { return writeVarint32(buf, pos, encodeZigZag(x)); } /** * decodes an array of varints * * @author nm * */ public static class ArrayDecoder { int pos=0; final byte[] buf; private ArrayDecoder(byte[] buf) { this.buf = buf; } /** * Returns true if the array contains another element. * If the array is corrupted, this will return true and next() will throw an BufferOverflow exception * @return */ public boolean hasNext() { return pos < buf.length; } public int next() { byte b = buf[pos++]; int v = b &0x7F; for (int shift = 7; (b & 0x80) != 0; shift += 7) { b = buf[pos++]; v |= (b & 0x7F) << shift; } return v; } } public static class SignedArrayDecoder extends ArrayDecoder{ private SignedArrayDecoder(byte[] buf) { super(buf); } public int next() { return decodeZigZag(super.next()); } } static public ArrayDecoder newArrayDecoder(byte[] buf) { return new ArrayDecoder(buf); } //used to transform small signed integers into unsigned (see protobuf docs) public static int decodeZigZag(int x) { return (x >>> 1) ^ -(x & 1); } public static int encodeZigZag(int x) { return (x << 1) ^ (x >> 31); } public static void writeSizeDelimitedString(ByteBuffer bb, String s) { byte[]b = s.getBytes(StandardCharsets.UTF_8); writeVarInt32(bb, b.length); bb.put(b); } public static String readSizeDelimitedString(ByteBuffer bb) throws DecodingException { int l = readVarInt32(bb); byte[] b = new byte[l]; bb.get(b); return new String(b, StandardCharsets.UTF_8); } //return a zigzag encoding of deltas of deltas // -> if the values in x are close to each-other or are increasing by a constant factor (think counters) // this result in an array of small numbers public static int[] encodeDeltaDeltaZigZag(int x[]) { int n = x.length; int[] ddz = new int[n]; if(n>0) { ddz[0] = encodeZigZag(x[0]); int d = 0; for(int i=1; i<n; i++) { int d1 = x[i]-x[i-1]; ddz[i] = encodeZigZag(d1-d); d=d1; } } return ddz; } // this is the reverse of the above public static int[] decodeDeltaDeltaZigZag(int ddz[]) { int n = ddz.length; int[] x = new int[n]; if(n>0) { x[0] = decodeZigZag(ddz[0]); int d = 0; for(int i=1; i<n; i++) { d = d + decodeZigZag(ddz[i]); x[i] = x[i-1] + d; } } return x; } //encoding of SortedIntArray in deltas of deltas public static int[] encodeDeltaDeltaZigZag(SortedIntArray a) { int n = a.size(); int[] ddz = new int[n]; if(n>0) { ddz[0] = encodeZigZag(a.get(0)); int d = 0; for(int i=1; i<n; i++) { int d1 = a.get(i)-a.get(i-1); ddz[i] = encodeZigZag(d1-d); d=d1; } } return ddz; } public static int[] encodeDeltaDeltaZigZag(IntArray a) { int n = a.size(); int[] ddz = new int[n]; if(n>0) { ddz[0] = encodeZigZag(a.get(0)); int d = 0; for(int i=1; i<n; i++) { int d1 = a.get(i)-a.get(i-1); ddz[i] = encodeZigZag(d1-d); d=d1; } } return ddz; } /** * get the number of bytes necessary to encode value * @param size * @return */ public static int getEncodedSize(int size) { if(size<128) return 1; if(size<16384) return 2; if(size<2097152) return 3; if(size<268435456) return 4; return 5; } }