当无法预测二进制字符串的长度时,事情会变得有些棘手,但解决方案非常简单:在字符串前面加上有关其长度的有用信息。可变长度序列化的核心是 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);
换句话说:
9c13
bytes + 2
(int16 占用 2 个字节)bytes + 2
包含 fd
并宣布一个 varint16807d
bytes + 5
(varint16 占用 varlen = 3
个字节)20a26b44
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 和区块链属性的概念。如果你喜欢这篇文章,请分享,并使用下面的表单提出问题和评论!
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!