varint 背景
Varint 是一种紧凑的表示数字的方法。它用一个或多个字节来表示一个数字,值越小的数字使用越少的字节数。这能减少用来表示数字的字节数。
Varint 中的每个 byte 的最高位 bit 有特殊的含义,如果该位为 1,表示后续的 byte 也是该数字的一部分,如果该位为 0,则结束。其他的 7 个 bit 都用来表示数字。
如下图:(注意到最终计算前将两个 byte 的位置相互交换过一次,这是因为 Google Protocol Buffer 字节序采用 little-endian 的方式)
举例
例如:
1、整数 300 的表示,需要两个字节:
1010 1100 0000 0010
2、整数 1 的表示,仅需一个字节:
0000 0001
占用字节数
采用 Varint,对于小的 int32 类型数字,可以用 1 个 byte 来表示;但是也有不好的一面,对于大数字来说依旧采用 Varint 表示法时,大到会需要 5 个 byte 来表示。
所以用 Varint 表示 int32 数字,占 1~ 5 Byte。如下:
int32 MAX 0111 1111 1111 1111 1111 1111 1111 1111
Varint32 1111 1111 1111 1111 1111 1111 1111 1111 0000 0111
即使如此从统计的角度来说,一般不会所有的消息中的数字都是大数,因此大多数情况下,采用 Varint 后,可以用更少的字节数来表示数字信息。
使用注意:
1、写Varint时,可以不关注占用的字节长度范围
2、读取Varint时,必须以最大长度来读取解析。否则内部解析Varint数据时会越界 ( 比如:数据实际占用了3个字节,但是你只读2个字节来解析数据时,第二个字节并非数据末尾字节,proto 库函数会继续解析直到遇到末尾标志。但是在库函数继续解析时会发生越界)
举例 Varint 的读写
eg: cpp 写 Varint文章来源:https://www.toymoban.com/news/detail-816424.html
// cpp
std::ofstream output;
output.open("tmp.log");
/*
*** 此处忽略 n 行无关代码
*/
{
google::protobuf::io::OstreamOutputStream raw_output(&output);
google::protobuf::io::CodedOutputStream coded_output(&raw_output);
auto user_data = response->userdata();// 简略: response 为proto格式
int fid = (int)user_data;
coded_output.WriteVarint32(fid); // 文件中写入 varint32 类型 id 数据
coded_output.WriteVarint32(response->ByteSizeLong());// 文件中写入 varint32 类型 response 长度
response->SerializeToCodedStream(&coded_output);// 文件中写入 response 数据
}
eg:python 读 varint文章来源地址https://www.toymoban.com/news/detail-816424.html
# python
# 忽略 n 行无关代码
fid_buf = file.read(5) # 读取文件5个字节
if not fid_buf:
return -1, None
fid, new_pos = _DecodeVarint32(fid_buf, 0) #从5个字节中获取fid,并返回实际Varint占用长度
offset = len(fid_buf) - new_pos
if offset != 0:
file.seek(-offset, 1)
buf = file.read(5) # 再读取文件5个字节
if not buf:
return -1, None
msg_len, new_pos = _DecodeVarint32(buf, 0) #从5个字节中获取 response 长度,并返回实际Varint占用长度
msg_buf = file.read(msg_len)# 读取 response
到了这里,关于protobuf 之 Varint的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!