rlp编码
具体规则定义可参考该文章
以太坊源码学习—RLP编码
这里摘录其关键部分
编码数据类型
(1)byte数组
(2)byte数组的数组,称之为列表
编码规则
(1)对于值在[0, 127]之间的单个字节,其编码是其本身
(2)如果byte数组长度l <= 55,编码的结果是数组本身,再加上128+l作为前缀
空字符的编码是128
(3) 如果数组长度大于55, 编码结果第一个是183加数组长度的编码的长度,然后是数组长度的本身的编码,最后是byte数组的编码。
(4)如果列表长度小于55,编码结果第一位是192加列表长度的编码的长度,然后依次连接各子列表的编码。
(5) 如果列表长度超过55,编码结果第一位是247加列表长度的编码长度,然后是列表长度本身的编码,最后依次连接各子列表的编码。
解码规则
解码时,首先根据编码结果第一个字节f的大小,执行以下的规则判断:
(1)如果f∈ [0,128), 那么它是一个字节本身。
(2)如果f∈[128,184),那么它是一个长度不超过55的byte数组,数组的长度为 l=f-128
(3)如果f∈[184,192),那么它是一个长度超过55的数组,长度本身的编码长度ll=f-183,然后从第二个字节开始读取长度为ll的bytes,按照BigEndian编码成整数l,l即为数组的长度。
(4)如果f∈(192,247],那么它是一个编码后总长度不超过55的列表,列表长度为l=f-192。递归使用规则1~4进行解码。
(5)如果f∈(247,256],那么它是编码后长度大于55的列表,其长度本身的编码长度ll=f-247,然后从第二个字节读取长度为ll的bytes,按BigEndian编码成整数l,l即为子列表长度。然后递归根据解码规则进行解码。
编解码的C语言实现
//将整形转为大端字节序,然后进行rlp编码
static int rlp_pack_varint_be(void **dest, size_t *left, uint64_t num)
{
int space = 0;
if (num <= 0xff) {
space = 1;
if (*left < space)
return -1;
*(uint8_t *)(*dest) = num;
} else if (num <= 0xffff) {
space = 2;
if (*left < space)
return -1;
uint16_t tmp = htobe16((uint16_t)num);
memcpy(*dest, &tmp, space);
} else if (num <= 0xffffffff) {
space = 4;
if (num <= 0xffffff)
space = 3;
if (*left < space)
return -1;
uint32_t tmp = htobe32((uint32_t)num);
memcpy(*dest, &tmp + 4 - space, space);
} else {
space = 8;
if (num <= 0xffffffffff)
space = 5;
else if (num <= 0xffffffffffff)
space = 6;
else if (num <= 0xffffffffffffff)
space = 7;
if (*left < space)
return -1;
uint64_t tmp = htobe64(num);
memcpy(*dest, &tmp + 8 - space, space);
}
*dest += space;
*left -= space;
return space;
}
//对应的解码过程
static int rlp_unpack_varint_be(void **dest, size_t *left, uint64_t *num)
{
uint8_t prefix = ((uint8_t *)*dest)[0];
if (prefix < 184 || prefix > 191)
return - 1;
*dest += 1;
*left -= 1;
int space = prefix - 183;
uint64_t tmp = 0;
memcpy(&tmp, *dest, space);
*dest += space;
*left -= space;
*num = be64toh(tmp);
return space;
}
//对字符数组进行编码
static int rlp_pack_buf(void **dest, size_t *left, const void *data, size_t len)
{
if (*left < len)
return -1;
if (len == 1) {
uint8_t val = ((uint8_t *)data)[0];
if (val == 0)
*(uint8_t *)(*dest) = 128;
else
memcpy(*dest, data, len);
*dest += len;
*left -= len;
} else if (len <= 55) {
if (*left < len + 1)
return - 1;
*(uint8_t *)(*dest) = 128 + len;
*dest += 1;
*left -= 1;
memcpy(*dest, data, len);
*dest += len;
*left -= len;
} else {
char buf[8] = {0};
void *buf_p = buf;
size_t buf_left = 8;
int space = rlp_pack_varint_be(&buf_p, &buf_left, len);
if (*left < len + space + 1)
return - 1;
*(uint8_t *)(*dest) = 183 + space;
*dest += 1;
*left -= 1;
memcpy(*dest, buf, space);
*dest += space;
*left -= space;
memcpy(*dest, data, len);
*dest += len;
*left -= len;
}
return len;
}
//对字符数组进行解码
static int rlp_unpack_buf(void **src, size_t *left, void *dest, size_t len)
{
if (*left > len)
return -1;
uint8_t prefix = ((uint8_t *)*src)[0];
size_t dest_len = -1;
if (prefix == 128) {
*(uint8_t *)dest = 0;
dest_len = 0;
*src += 1;
*left -= 1;
} else if (prefix < 128) {
*(uint8_t *)dest = prefix;
dest_len = 1;
*src += 1;
*left -= 1;
} else if (prefix <= 183) {
dest_len = prefix - 128;
if (len < dest_len)
return -1;
*src += 1;
*left -= 1;
memcpy(dest, *src, dest_len);
*src += dest_len;
*left -= dest_len;
} else if (prefix <= 191) {
uint64_t tmp = 0;
rlp_unpack_varint_be(src, left, &tmp);
dest_len = tmp;
if (len < dest_len)
return -1;
memcpy(dest, *src, dest_len);
*src += dest_len;
*left -= dest_len;
}
return dest_len;
}
以上只进行了rlp的字符数组编解码,也就是实现了编码,解码规则的前三条,至于列表的编码会在下一篇以太坊区块hash的计算中实现