在 JAVA 中写入位 reader (32 位小尾数法最高到最低有效位打包)

Writing a bit reader in JAVA (32-bit little-endian most-to-least-significant bit packing)

无论我如何折腾字节和位,我就是无法让它工作。我理解字节顺序非常好,并且在某种程度上理解 MSB(最高有效位)。但我似乎无法将两者放在一起。

目前我的数据在一个普通的 JAVA byte[] 数组中。比特流中的符号使用 32 位小尾数法从最高有效位到最低有效位打包进行编码。我需要以一种 reader N 位的方式阅读它,例如:

public int read(int bits, boolean advanceReader)

当然要跟踪

private int byteOffset;
private int bitOffset;

问题是获取整数值,我只是无法理解如何正确实现:(

编辑:我正在尝试使用此 Apache 许可的 BitReader class 添加小端(我添加到 readInt())支持(原始代码:https://raw.githubusercontent.com/jcodec/jcodec/master/src/main/java/org/jcodec/common/io/BitReader.java):

public class BitReader {

protected int deficit;
protected int curInt;
private ByteBuffer bb;
private int initPos;

public BitReader(ByteBuffer bb) {
    this.bb = bb;
    initPos = bb.position();
    curInt = readInt();
    deficit = 0;
}

public final int readInt() {
    if (bb.remaining() >= 4) {
        deficit -= 32;
        final int b1 = bb.get() & 0xff;
        final int b2 = bb.get() & 0xff;
        final int b3 = bb.get() & 0xff;
        final int b4 = bb.get() & 0xff;
        if (bb.order() == ByteOrder.BIG_ENDIAN) {
            return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4;
        } else {
            return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1;
        }
    } else {
        return readIntSafe();
    }
}

public int readNBit(int n) {
    if (n > 32) {
        throw new IllegalArgumentException("Can not read more then 32 bit");
    }

    int nn = n;

    int ret = 0;
    if (n + deficit > 31) {
        ret |= (curInt >>> deficit);
        n -= 32 - deficit;
        ret <<= n;
        deficit = 32;
        curInt = readInt();
    }

    if (n != 0) {
        ret |= curInt >>> (32 - n);
        curInt <<= n;
        deficit += n;
    }

    // for(--nn; nn >=0; nn--)
    // System.out.print((ret >> nn) & 1);
    // System.out.println();

    return ret;
}

public int position() {
    return ((bb.position() - initPos - 4) << 3) + deficit;
}

}

也许我在 readNBit 中遗漏了一些东西,它仍然像大端一样处理?结果接近那里,但不太正确。

一些数据:

The array:
[ 0] byte 64
[ 1] byte 1
[ 2] byte -32
[ 3] byte 1
[ 4] byte 100
[ 5] byte 20
[ 6] byte 30
[ 7] byte 3
[ 8] byte 47
[ 9] byte -91
[10] byte 52
[11] byte -12
[12] byte 2
[13] byte -6
[14] byte 11
[15] byte -24
[16] byte 41
[17] byte 98
[18] byte -124
[19] byte 52
Deficit = 21
Position (byte array) = 12

读取 32 位得到我:-1510145665

显然应该是:-1510145696

byte[] array = {
  64, 1, -32, 1,
  100, 20, 30, 3,
  47, -91, 52, -12,
  2, -6, 11, -24,
  41, 98, -124, 52
};
ByteBuffer bArray = ByteBuffer.wrap(array);
bArray.order(ByteOrder.LITTLE_ENDIAN);
BitReader bitReader = new BitReader(bArray);
bitReader.readNBit(32);
bitReader.readNBit(32);
bitReader.readNBit(21);
int luku = bitReader.readNBit(32);

Luku == -1510145665,我觉得应该是-1510145696。

47, -91, 52, -12 to little endian int => 11110100001101001010010100101111
2, -6, 11, -24 to little endian int => 11101000000010111111101000000010

消耗完21位后,接下来的32位10100101111111010000000101111111,十六进制为0xA5FD017F,十进制为2784821631

System.out.println(0xA5FD017F);
long l = 2784821631L;
System.out.println((int)l);

都会给你-1510145665。所以原码是正确的。

当您一次读取少于 32 位时,将输入字节数组作为小端字节序进行的更改可能不是您想要的。您可能必须一次读取一个字节,而不是使用现有的 readInt()