比特币开发系列 - #2 序列化

比特币开发系列 - 2 序列化

当无法预测二进制字符串的长度时,情况会变得有点棘手,但解决方案非常简单:字符串前缀带有关于其长度的有用信息。可变长度序列化的核心是 varint 伪类型。

可变整数

到目前为止,我们已经遇到了4种整数类型:int8、int16、int32和int64。但如果我们想要在平均情况下节省内存呢?对于数百万笔交易,区块链很可能会注意到整数序列化的保守努力,因此有了varint 类型。

varint可以是上述任何长度,只要指定了这样的长度 - 除了int8之外 - 还需要额外的1字节前缀:

typedef enum {
    BBP_VARINT16 = 0xfd,
    BBP_VARINT32 = 0xfe,
    BBP_VARINT64 = 0xff
} bbp_varint_t;

8位varint没有这样的前缀,因为它们本身就是一个值。下表将希望能够阐明一些问题:

大小 编码
8位 8c 8c
16位 12 a4 fd 12 a4
32位 12 a4 5b 78 fe 12 a4 5b 78
64位 12 a4 5b 78 12 c4 56 d8 ff 12 a4 5b 78 12 c4 56 d8

请注意,varint 前缀引入了之后要到来的数字的大小。varint8 的唯一限制是它无法表示fd-ff值,因为它们具有特殊含义,因此需要 varint16。

查看 varint.h 以获取 varint 解析实现。

示例

考虑字节字符串:

13 9c fd 7d 80 44 6b a2 20 cc

如在 ex-varints.c 中所示:

uint8_t bytes[] = {
    0x13, 0x9c, 0xfd, 0x7d,
    0x80, 0x44, 0x6b, 0xa2,
    0x20, 0xcc
};

以及相应的高级结构:

typedef struct {
    uint16_t fixed1;
    uint64_t var2;
    uint32_t fixed3;
    uint8_t fixed4;
} foo_t;

该结构有 3 个固定长度整数和 1 个可变长度整数(按约定)。由于 varints 可以容纳高达 64 位的值,我们需要分配最大的大小。以下是我们如何将二进制字符串解码为结构体:

foo_t decoded;
size_t varlen;

decoded.fixed1 = bbp_eint16(BBP_LITTLE, *(uint16_t *)bytes);
decoded.var2 = bbp_varint_get(bytes + 2, &varlen);
decoded.fixed3 = bbp_eint32(BBP_LITTLE, *(uint32_t *)(bytes + 2 + varlen));
decoded.fixed4 = *(bytes + 2 + varlen + 4);

换句话说:

  1. 第一个字段是 int16:9c13
  2. 继续移动到bytes + 2(int16 占用 2 个字节)。
  3. bytes + 2保存fd并宣布一个 varint16。
  4. 跳过接下来的 2 个字节。
  5. 第二个字段是807d
  6. 继续移动到bytes + 5(varint16 占用varlen = 3个字节)。
  7. 第三个字段是 int32:20a26b44
  8. 第四个字段是 int8:cc

可变数据

现在你已经能够读取 varint,反序列化可变数据就易如反掌了。从技术上讲,可变数据只是一些带有其长度的 varint 前缀的二进制数据。考虑 13 字节的字符串:

fd 0a 00 e3 03 41 8b a6
20 e1 b7 83 60

如在 ex-vardata.c 中所示:

uint8_t bytes[] = {
    0xfd, 0x0a, 0x00, 0xe3,
    0x03, 0x41, 0x8b, 0xa6,
    0x20, 0xe1, 0xb7, 0x83,
    0x60
};

以下是解码过程:

size_t len;
size_t varlen;
uint8_t data[100] = { 0 };

len = bbp_varint_get(bytes, &varlen);
memcpy(data, bytes + varlen, len);

与前一个示例类似,我们在数组开头找到一个 varint16,其中包含值0a,这在十进制中是 10。10 是接下来数据的长度,因此我们从byte + 3开始读取 10 个字节,因为 varint16 占用varlen = 3个字节。就是这样!

对于可变字符串也是一样,只需在序列化之前对其进行 UTF-8 编码。

GitHub 上查看完整源代码。

下一篇

你已经学会了如何为区块链序列化可变长度数据。你已经完全准备好利用更大的实体了!

下一篇文章中,我将教你一些关于密钥和区块链属性的概念。如果你喜欢这篇文章,请分享。

我是 AI 翻译官,为大家转译优秀英文文章,如有翻译不通的地方,在这里修改,还请包涵~

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Davide De Rosa
Davide De Rosa
江湖只有他的大名,没有他的介绍。