如何压缩 4 字节的长度以仅适合 1 字节的 Tag-Length-Value 流?

How to compress Length of 4 Bytes to fit into 1 Byte only of Tag-Length-Value stream?

提问人:Momo 提问时间:5/22/2022 更新时间:5/22/2022 访问量:458

问:

我看到这个家伙对 Tag-Length-Value 做了一些不同的事情,他允许存储 255 >的大长度,正如我们在 TLV 示例中看到的那样,它的工作原理是这样的:

我需要存储我的 ID + 姓名

标记 1、长度 5、值 (ID) = 12345,依此类推

再次回到我需要的,这家伙/TlvEncoding 如何压缩长度以允许存储超过 4 个字节。例如,带有 AND 或二进制逻辑运算符的 AND 或二进制逻辑运算符:

假设我有长度为 1000 的字符串,超过 1 个字节。他做以下

注意:0xffff = 65,535 或的最大值。我认为长度 1000 将在那条线上相遇时的条件。stream 将写入 2 个字节的长度。但是当他读到这个值时,他会做更多的逻辑操作。这已经泄漏了。:(UInt16ushort

    /// <summary>
    /// Write TLV length to stream
    /// </summary>
    /// <param name="stream">stream to write to</param>
    /// <param name="length">length to write or null to write indefinite length</param>
    public static void WriteLength(Stream stream, int? length)
    {
        if (length == null)
        {
            stream.WriteByte(0x80); // indefinite form
            return;
        }

        if (length < 0 || length > 0xffffffff)
            throw new TlvException(string.Format("Invalid length value: {0}", length));

        if (length <= 0x7f) // use short form if possible
        {
            stream.WriteByte(checked((byte)length));
            return;
        }

        byte lengthBytes;

        // use minimum number of octets
        if (length <= 0xff)
            lengthBytes = 1;
        else if (length <= 0xffff)
            lengthBytes = 2;
        else if (length <= 0xffffff)
            lengthBytes = 3;
        else if (length <= 0xffffffff)
            lengthBytes = 4;
        else
            throw new TlvException(string.Format("Length value too big: {0}", length));

        stream.WriteByte((byte)(lengthBytes | 0x80));

        // shift out the bytes
        for (var i = lengthBytes - 1; i >= 0; i--)
        {
            var data = (byte)(length >> (8 * i));
            stream.WriteByte(data);
        }
    }

在读取操作中,他做:(我认为条件会在这里满足) 我不知道他为什么选择等于 127 0x7f,然后0x80等于 128 中删除。0x7f; // remove 0x80 bit

    /// <summary>
    /// Read TLV length from stream
    /// </summary>
    /// <param name="stream">Stream to read</param>
    /// <returns>length or null to indicate indefinite length</returns>
    public static int? ReadLength(Stream stream)
    {
        var readByte = stream.ReadByte();
        if (readByte == -1)
            throw new TlvException("Unexpected end of stream while reading length");

        if ((readByte & 0x80) == 0)
            return (int)readByte; // length is in first byte

        int length = 0;
        var lengthBytes = readByte & 0x7f; // remove 0x80 bit

        if (lengthBytes == 0)
            return null; // indefinite form

        if (lengthBytes > 4)
            throw new TlvException($"Unsupported length: {lengthBytes} bytes");

        for (var i = 0; i < lengthBytes; i++)
        {
            readByte = stream.ReadByte();
            if (readByte == -1)
                throw new TlvException("Unexpected end of stream while reading length");

            length <<= 8;
            length |= (int)readByte;
        }

        return length;
    }

请我只需要了解那里的情况,因为我需要在字节数组中应用相同的方法。我需要存储 i.e:长度为 1500 的字符串、日期时间、一些整数,如 (TLV)。但我所知道的是如何只应用 1 个字节长度 (255)。

所以我只能读取 1 个字节的长度,因为我不知道如何判断数组我需要寻找 3 个字节的长度?还是 2 个字节?然后,我必须存储所有 2 或 3 个字节的 TLV。这是浪费空间。

  1. 整数存储为 4 个字节(OK),所以我可以写 Length 是(注意字节强制转换)(byte) 4
  2. 字符串只能存储在 255 中,但是如何像上面一样做到这一点呢?1500 个字节的长度?

简单地说,我解释一下,他允许存储 1 个字节作为长度,有时存储 3 个字节作为长度。我什至不知道他如何告诉编译器/流从接下来的 3 个字节读取此标签长度。或接下来的 2 个字节。或 1 字节。

C# 数组 .NET 字节 TLV

评论

0赞 Enigmativity 5/24/2022
你为什么删除 stackoverflow.com/questions/72356153/...
0赞 Momo 5/24/2022
因为它已经回答了,人们说不可能:)
0赞 Enigmativity 5/24/2022
然后发布该答案并保留问题 - 这个网站就是为未来的读者创建问题和答案。
0赞 Momo 5/24/2022
是的,当然,亲爱的。

答:

1赞 canton7 5/22/2022 #1

从 来看,它看起来相当简单。WriteLength

介于 0 和 0x7F 之间的值仅由具有该值的单个字节表示。因此,如果你想写例如值,你写一个带有值的字节。55

对于> 0x7F值:

  1. 第一个字节是表示值所需的字节数,设置了最高位(以便您可以分辨它与值介于 0-127 之间的简单字节之间的区别)
  2. 接下来的字节数为实际值。
价值 序列 化
0 0x00
1 0x01
127 0x7F
128 0x81 0x80
129 0x81 0x81
255 0x81 0xFF
256 0x82 0x01 0x00
257 0x82 0x01 0x01
65535 0x82 0xFF 0xFF
65536 0x83 0x01 0x00 0x00 0x00

等等......

评论

0赞 Momo 5/22/2022
非常感谢您的努力和描述性信息,但我希望您是否能提供一种积分方法来在没有十六进制的情况下实现相同的结果,我一生中从未处理过移位、按位运算符,您有没有办法实现 WriteLength() 使用正常数字比较而不是十六进制,并且没有按位和移位?:(
0赞 Momo 5/22/2022
如果它可以在没有移位和按位运算符的情况下实现,我会很高兴:(看到这里 prnt.sc/KKHQwO7Nczd9,也许如果..else 条件与整数?
0赞 canton7 5/22/2022
如果需要,您可以将每个十六进制数转换为十进制 - 但是选择十六进制是有原因的,这个原因是当您进行按位操作时,它更容易使用。您在这里执行的是位级操作,而按位操作是正确的工具。如果你对它们感到不舒服,那么现在是时候让自己舒服了,而不是试图避免它们
0赞 Momo 5/22/2022
我可以理解 ReadLength function(),但是 WriteLength(),我不知道他为什么在反向数组中循环......恩迪亚尼斯?
0赞 canton7 5/22/2022
是的。他们决定先写入最高有效字节。如果循环正在计数,它将首先写入最低有效字节