需要帮助为特定值格式编写二进制 reader 扩展方法:6 位然后 7 位结构

Need help writing a binary reader extension method for a specific value format: 6 bit then 7 bits structure

好的,就这样吧。

我目前需要为 System.IO.BinaryReader class 编写一个能够读取特定格式的扩展方法。 我不知道这种格式叫什么,但我确实知道它是如何工作的,所以我将在下面描述它。

组成该值的每个字节都被标记以指示 reader 接下来需要如何表现。 第一个字节有 2 个标志,值的任何后续字节只有 1 个标志。

First byte:
 01000111
 ^^^^^^^^
 |||____|_ 6 bit value
 ||_______ flag: next byte required
 |________ flag: signed value

Next bytes:
 00000011
 ^^^^^^^^
 ||_____|_ 7 bit value
 |________ flag: next byte required

值的第一个字节有两个标志位,第一位表示值是正数还是负数。 第二位是是否需要读取另一个字节。 剩下的 6 位是目前的值,需要保留以备后用。

如果不需要读取更多字节,那么您只需 return 具有第一个位标志指示的正确符号的 6 位值。

如果需要读取另一个字节,则读取该字节的第一位,这将指示是否需要读取另一个字节。 剩下的 7 位就是这里的值。 该值需要与第一个字节的 6 位值相结合。

所以在上面的例子中:

第一个值是:01000111。 即为正,需要再读一个字节,目前的值为000111。

读取了另一个字节,它是:00000011 因此不需要读取新字节,这里的值是:0000011 到目前为止,它像这样连接到值的前面:0000011000111

因此这就是最终值:0000011000111 或 199

0100011100000011 变成这样:0000011000111

这是另一个例子:

 011001111000110100000001
 ^^^^^^^^^^^^^^^^^^^^^^^^
 |      ||      ||______|_____ Third Byte (1 flag)
 |      ||______|_____________ Second Byte (1 flag)
 |______|_____________________ First Byte (2 flags)

First Byte:
 0      - Positive
 1      - Next Needed
 100111 - Value

Second Byte:
 1       - Next Needed
 0001101 - Value

Third Byte:
 0       - Next Not Needed
 0000001 - Value

Value:
 00000010001101100111 = 9063

希望我的解释很清楚:)

现在我需要能够为 System.IO.BinaryReader 编写一个清晰、简单且最重要的快速扩展方法来从流中读取这样的值。 到目前为止,我的尝试有点糟糕,而且涉及布尔数组和位数组的不必要的复杂。

因此,我真的需要有人帮助我编写这样的方法,我将不胜感激!

感谢阅读。

根据评论中的描述,我想到了这个,异常读取有符号字节,因为它使继续标志更容易检查:(未测试)

static int ReadVLQInt32(this BinaryReader r)
{
    sbyte b0 = r.ReadSByte();
    // the first byte has 6 bits of the raw value
    int shift = 6;
    int raw = b0 & 0x3F;
    // first continue flag is the second bit from the top, shift it into the sign
    sbyte cont = (sbyte)(b0 << 1);
    while (cont < 0)
    {
        sbyte b = r.ReadSByte();
        // these bytes have 7 bits of the raw value
        raw |= (b & 0x7F) << shift;
        shift += 7;
        // continue flag is already in the sign
        cont = b;
    }
    return b0 < 0 ? -raw : raw;
}

它也可以很容易地扩展为读取 long,只需确保使用 b & 0x7FL 否则该值将被转换为 int 并且位会被丢弃。

检查非法值的版本(例如 0xFF、0xFF... 的超长序列,plus 与 C# 的 checked 数学一起工作(C# 编译器中有一个选项使用 cheched 数学来检查溢出)

public static int ReadVlqInt32(this BinaryReader r)
{
    byte b = r.ReadByte();

    // the first byte has 6 bits of the raw value
    uint raw = (uint)(b & 0x3F);

    bool negative = (b & 0x80) != 0;

    // first continue flag is the second bit from the top, shift it into the sign
    bool cont = (b & 0x40) != 0;

    if (cont)
    {
        int shift = 6;

        while (true)
        {
            b = r.ReadByte();
            cont = (b & 0x80) != 0;
            b &= 0x7F;

            if (shift == 27)
            {
                if (negative)
                {
                    // minumum value abs(int.MinValue)
                    if (b > 0x10 || (b == 0x10 && raw != 0))
                    {
                        throw new Exception();
                    }
                }
                else
                {
                    // maximum value int.MaxValue
                    if (b > 0xF)
                    {
                        throw new Exception();
                    }
                }
            }

            // these bytes have 7 bits of the raw value
            raw |= ((uint)b) << shift;

            if (!cont)
            {
                break;
            }

            if (shift == 27)
            {
                throw new Exception();
            }

            shift += 7;
        }
    }

    // We use unchecked here to handle int.MinValue
    return negative ? unchecked(-(int)raw) : (int)raw;
}