序列化 (第 2 部分)

  • AI 翻译
  • 更新于 2024-07-10 08:34
  • 阅读 25

在 GitHub 上查看

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

可变整数

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

varint 可以是上述任何一种长度,只要这种长度在额外的 1 字节前缀中指定——除了 int8:

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 个可变长度的整数(按合同)。由于 varint 可以容纳最多 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 的二进制数据,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

链中的下一个区块?

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

下一篇文章中,我将教你一些关于 keys 和区块链属性的概念。如果你喜欢这篇文章,请分享,并使用下面的表单提出问题和评论!

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

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

0 条评论

请先 登录 后评论
AI 翻译
AI 翻译
江湖只有他的大名,没有他的介绍。