在C#中设计BitStream

Ela*_*ich 5 c# binary byte bits

我正在查看名为BitStream的C#库,它允许您向标准C#Stream对象写入和读取任意数量的位.我注意到在我看来一个奇怪的设计决定:

将位添加到空字节时,这些位将添加到字节的MSB中.例如:

var s = new BitStream();
s.Write(true);
Debug.Assert(s.ToByteArray()[0] == 0x80);  // and not 0x01

var s = new BitStream();
s.Write(0x7,0,4);
s.Write(0x3,0,4);
Debug.Assert(s.ToByteArray()[0] == 0x73); // and not 0x37
Run Code Online (Sandbox Code Playgroud)

但是,当引用数字中的位作为输入时,输入数字的第一位是LSB.例如

//s.Write(int input,int bit_offset, int count_bits)
//when referencing the LSB and the next bit we'll write
s.Write(data,0,2); //and not s.Write(data,data_bits_number,data_bits_number-2)
Run Code Online (Sandbox Code Playgroud)

这对我来说似乎不一致.由于在这种情况下,当"逐渐"复制像前一个例子中的字节(前四位,然后是最后四位)时,我们将不会得到原始字节.我们需要"向后"复制它(首先是最后四位,然后是前四位).

这种设计是否有理由让我失踪?具有此行为的位流的任何其他实现?有什么设计考虑因素?

似乎ffmpeg比特流的行为方式与我认为一致.查看它在使用函数中OR的src指针进行字节移位之前的量.put_bits

作为旁注:

第一字节添加,是字节数组中的第一个字节.例如

var s = new BitStream();
s.Write(0x1,0,4);
s.Write(0x2,0,4);
s.Write(0x3,0,4);
Debug.Assert(s.ToByteArray()[0] == 0x12); // and not s.ToByteArray()[1] == 0x12
Run Code Online (Sandbox Code Playgroud)

小智 3

以下是一些额外的注意事项:

对于布尔值 - 只需一位即可表示 true 或 false。当该位被添加到流的开头时,位流为“1”。当您将该流扩展到字节长度时,它会强制将零位填充到流的末尾,即使这些位一开始就不存在于流中。流中的位置是重要的信息,就像位的值一样,“1000000”或 0x80 的位流可以保证流的后续读取器可能认为他们读取的第一个位是添加的第一个位。

其次,其他数据类型(例如整数)需要更多位来表示,因此它们将比布尔值在流中占用更多空间。当不同大小的数据类型未在字节边界上对齐时,在同一流中混合不同大小的数据类型可能会非常棘手。

最后,如果您使用的是 Intel x86,您的 CPU 架构是“little-endian”,这意味着 LSB 首先,就像您所描述的那样。如果您需要将流中的值存储为大端字节序,则需要在代码中添加一个转换层 - 类似于上面显示的内容,一次按您想要的顺序将一个字节推入流中。这很烦人,但如果您需要与大端 Unix 机器进行互操作或协议规范可能需要,则通常需要这样做。

希望有帮助!