如何压缩 4 字节的长度以仅适合标签长度值流的 1 字节?

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

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

我需要存储我的 ID + 姓名

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

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

假设我有一个长度为1000的字符串,超过了1个字节。他关注

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

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

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

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

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

所以我只能读取1个字节的长度,因为我不知道如何告诉数组我需要寻找3个字节的长度?还是 2 个字节?然后我必须用 2 或 3 个字节存储所有 TLV。太浪费了space.

  1. 整数存储为 4 个字节(OK),所以我可以写长度为 (byte) 4 (注意字节转换)
  2. String只能存储在255,但是怎么像上面那位那样呢? 3 字节 1500 长度?

我简单解释一下,他允许存储1个字节作为长度,有时3个字节作为长度。我什至不知道他如何告诉 compiler/stream 从接下来的 3 个字节开始读取这个标签长度。或接下来的 2 个字节。或 1 个字节。

WriteLength开始,看起来还算简单。

介于 0 和 0x7F 之间的值仅由具有该值的单个字节表示。所以如果你想写例如值 5,你写一个字节值 5.

对于 > 0x7F 的值:

  1. 第一个字节是表示值所需的字节数,设置了highest-bit(这样你就可以区分它和一个简单的字节,它的值在0-127之间)
  2. 接下来的 however-many 个字节包含实际值。
Value Serialized
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

等等...