All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.yamcs.utils.VarIntUtil Maven / Gradle / Ivy

There is a newer version: 5.10.9
Show newest version
package org.yamcs.utils;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

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
     */
    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 value
     * @return
     */
    public static int getEncodedSize(int value) {
        if (value < 128)
            return 1;
        if (value < 16384)
            return 2;
        if (value < 2097152)
            return 3;
        if (value < 268435456)
            return 4;
        return 5;
    }

    /**
     * Encode an int array as a sequence of varints representing the deltas between the subsequent elements of the input
     * array.
     * 
     * It is best if the array is sorted in ascending order (because encoding negative numbers in varint is not
     * efficient).
     * 
     * @param s
     * @return
     */
    public static byte[] encodeDeltaIntArray(IntArray s) {
        int[] a = s.array();
        int length = s.size();
        byte[] buf = new byte[length * 5];

        if (length == 0) {
            return buf;
        }

        int pos = VarIntUtil.writeVarInt32(buf, 0, a[0]);

        for (int i = 1; i < length; i++) {
            pos = VarIntUtil.writeVarInt32(buf, pos, (a[i] - a[i - 1]));
        }
        if (pos == buf.length) {
            return buf;
        } else {
            return Arrays.copyOf(buf, pos);
        }
    }

    /**
     * The reverse of the {@link #encodeDeltaIntArray(IntArray)} method.
     * 
     * @param buf
     * @return
     */
    public static IntArray decodeDeltaIntArray(byte[] buf) {
        if (buf.length == 0) {
            return new IntArray(0);
        }
        IntArray sia = new IntArray();

        ArrayDecoder ad = newArrayDecoder(buf);
        int s = 0;
        while (ad.hasNext()) {
            s += ad.next();
            sia.add(s);
        }
        return sia;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy