【问题标题】:Encoding a long to a VLQ byte array and writing it to System.IO.BinaryWriter将 long 编码为 VLQ 字节数组并将其写入 System.IO.BinaryWriter
【发布时间】:2018-12-23 08:22:51
【问题描述】:

这个问题是对我上一个问题的跟进: Link

在那个问题中,我询问了如何阅读特定的 VLQ 格式,我不会再次描述,但您可以从我之前的问题中阅读。

harold 的结果基本上是这样的:

static int ReadVLQInt64(this BinaryReader r)
{
    sbyte b0 = r.ReadSByte();
    // the first byte has 6 bits of the raw value
    int shift = 6;
    long raw = b0 & 0x3FL;
    // 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;
}

(这只是它的 int64 版本)

这很好地读取这样的值,但现在我需要能够写入这样的值。 简而言之,我需要该功能的反面。获取一个 int64 值并将其分解为上一个问题中描述的格式的可变长度字节数组。

任何帮助将不胜感激。

感谢阅读。

编辑:应 cmets 的要求,我需要提供一些我自己的代码来构建。

public static void WriteVLQInt64(this BinaryWriter bw, long value)
{
    var bytes = new List<byte>();
    var i = 6;
    var j = 0;
    var shift = 0;

    while (true)
    {
        var andvalue = Convert.ToInt64(Math.Pow(2, i) - Math.Pow(2, j));
        j = i;

        var b = Convert.ToByte((value & andvalue) >> shift);

        if (b <= 0) break;

        bytes.Add(b);

        shift = i;
        i += 7;
    }

    for (int k = 0; k < bytes.Count; k++)
    {
        if (bytes[k] == bytes.First())
        {
            if (value < 0)
            {
                bytes[k] |= 128;
            }
            if (bytes[k] != bytes.Last())
            {
                bytes[k] |= 64;
            }
            continue;
        }

        if (bytes[k] != bytes.Last())
        {
            bytes[k] |= 128;
        }
    }

    bw.Write(bytes.ToArray());

    /* - Just for debug
    foreach (var item in bytes)
    {
        Console.Write(Convert.ToString(item, 2).PadLeft(8, '0'));
    }
    Console.WriteLine();
    */
}

我之前没有发布这个只是因为它是一个非常混乱的解决方案,并且有太多的事情要做。 所以我将重新提出我的问题......是否有人可以帮助我压缩该功能并从中删除很多不必要的东西?例如 Math.Pow 中的大量 if 语句和 Convert.To 的大量使用...

再次感谢您的阅读。

【问题讨论】:

  • 与往常一样,SO 不是“为我编写代码”...您编写一些代码,我们帮助您。你写0代码,我们不用帮你。
  • 好的,我用我自己的一些乱码更新了帖子。
  • 提醒一下,64 位版本需要b &amp; 0x7FL,但不需要b0 &amp; 0x3FL(对于第一个字节,位永远不会移出int 的范围,它们甚至不是转移)。

标签: c# binary bit-manipulation .net-4.7.1


【解决方案1】:

增加了对long.MinValue的支持(稍微复杂一点,因为abs(long.MinValue) &gt; long.MaxValue,所以需要特殊处理)。添加了对int 的单独支持。添加了对“非法”值的检查(否则您可能会构建对于 intlong 来说太大的非法序列)。

public static long ReadVlqInt64(this BinaryReader r)
{
    byte b = r.ReadByte();

    // the first byte has 6 bits of the raw value
    ulong raw = (ulong)(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 == 62)
            {
                if (negative)
                {
                    // minumum value abs(long.MinValue)
                    if (b > 0x2 || (b == 0x2 && raw != 0))
                    {
                        throw new Exception();
                    }
                }
                else
                {
                    // maximum value long.MaxValue
                    if (b > 0x1)
                    {
                        throw new Exception();
                    }
                }
            }

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

            if (!cont)
            {
                break;
            }

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

            shift += 7;
        }
    }

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

public static void WriteVlqInt64(this BinaryWriter r, long n)
{
    bool negative = n < 0;

    // We use unchecked here to handle long.MinValue
    ulong raw = negative ? unchecked((ulong)-n) : (ulong)n;

    byte b = (byte)(raw & 0x3F);

    if (negative)
    {
        b |= 0x80;
    }

    raw >>= 6;
    bool cont = raw != 0;

    if (cont)
    {
        b |= 0x40;
    }

    r.Write(b);

    while (cont)
    {
        b = (byte)(raw & 0x7F);

        raw >>= 7;
        cont = raw != 0;

        if (cont)
        {
            b |= 0x80;
        }

        r.Write(b);
    }
}

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;
}

public static void WriteVlqInt32(this BinaryWriter r, int n)
{
    bool negative = n < 0;

    // We use unchecked here to handle int.MinValue
    uint raw = negative ? unchecked((uint)-n) : (uint)n;

    byte b = (byte)(raw & 0x3F);

    if (negative)
    {
        b |= 0x80;
    }

    raw >>= 6;
    bool cont = raw != 0;

    if (cont)
    {
        b |= 0x40;
    }

    r.Write(b);

    while (cont)
    {
        b = (byte)(raw & 0x7F);

        raw >>= 7;
        cont = raw != 0;

        if (cont)
        {
            b |= 0x80;
        }

        r.Write(b);
    }
}

【讨论】:

  • 非常感谢!效果很好。抱歉之前缺少代码,但现在我明白我需要事先展示一些关于这个问题的工作,即使我觉得这很糟糕。
  • @plmjk 添加了对long.MinValue 的支持以及非法值检查以及int 变体。
猜你喜欢
  • 1970-01-01
  • 2015-08-01
  • 2011-05-16
  • 1970-01-01
  • 1970-01-01
  • 2014-07-31
  • 1970-01-01
  • 2015-08-23
  • 1970-01-01
相关资源
最近更新 更多