BufferReader.java

package edu.jiangxin.apktoolbox.convert.protobuf.unsupervised;

import org.apache.commons.lang3.ArrayUtils;

import java.math.BigInteger;
import java.nio.ByteOrder;
import java.util.Map;

public class BufferReader {

    private byte[] buffer;
    private int offset;
    private int savedOffset;

    public BufferReader(byte[] buffer) {
        this.buffer = buffer;
        offset = 0;
    }

    public int getOffset() {
        return offset;
    }

    public BigInteger readVarInt() {
        Map<String, Object> result = VarintUtils.decodeVarint(buffer, offset);
        offset += (int) result.get("length");
        return (BigInteger) result.get("value");
    }

    public byte[] readBuffer(int length) {
        this.checkByte(length);
        byte[] result = ArrayUtils.subarray(buffer, offset, offset + length);
        offset += length;
        return result;
    }

    public void trySkipGrpcHeader() {
        int backupOffset = offset;
        if (buffer.length > 0 && buffer[offset] == 0 && leftBytes() >= 5) {
            offset++;
            int length = ByteUtil.bytesToInt(buffer, offset, ByteOrder.BIG_ENDIAN);
            offset += 4;

            if (length > leftBytes()) {
                offset = backupOffset;
            }
        }
    }

    public int leftBytes() {
        return buffer.length - offset;
    }

    public void checkByte(int length) {
        int bytesAvailable = leftBytes();
        if (length > bytesAvailable) {
            throw new RuntimeException("Not enough bytes left. Requested: " + length + " left: " + bytesAvailable);
        } else if (length < 0) {
            throw new RuntimeException("The length should be greater than 0");
        }
    }

    public void checkpoint() {
        this.savedOffset = offset;
    }

    public void resetToCheckpoint() {
        offset = this.savedOffset;
    }

}