详解varint,zigzag编码, 以及在Go标准库中的实现

文章目录

  • 为啥需要varint编码
  • 为啥需要zigzag编码
  • varint
    • 编码
    • 解码
  • zigzag
    • 编码
    • 解码
  • 局限性

为啥需要varint编码

当我们用定长数字类型int32来表示整数时,为了传输一个整数1,我们需要传输00000000 00000000 00000000 00000001 32 个 bits,而有价值的数据只有 1 位。这就导致了大量的空间浪费,因为大部分字节并没有实际存储有效的信息

varint编码通过使用可变长度的字节序列来表示整数,根据数字的大小灵活占用的空间。这样使得小的整数可以用更少的字节表示,提高空间效率

下面是varint编码中,正数的大小和需要的字节数的关系

数字大小uvarint编码需要的字节数
<=1271
<=163832
<=20971513
<=2684354554

我们业务中大部分数据的size都 <= 16383,也就只用1或2个字节。相比于定长的int32,int64能节省不少空间

其设计原理为:

  • 每7bit 为一组:将整数的二进制按照每7个bit划分到一个byte中
  • 最高位表示是否还有下个字节:划分好的byte中,如果最高位为1,表示还有下个byte,否则当前byte是最后一个。最后一个字节的最高位为1,其他字节的最高位为0

在这里插入图片描述

例如:对于一个整数 500,它的二进制表示是 111110100。将其分为2组,即 111110100。然后在每组前面添加标志位,得到两个字节 1000001101110100,这两个字节就是 500 的 varint 编码。相比于用 int32 的 4 字节表示,节省了 50% 的存储空间


为啥需要zigzag编码

但如果是负数,那么继续采用Varint编码就没有任何压缩效果,甚至占用更多字节。因为负数的符号位最高位为1,也就是一定会用满最大的字节

ZigZag编码解决了varint对负数编码效率低的问题,其原理为:

  • 对于正数 n,会将其映射为 2 * n。例如整数 2,经过 zigzag 编码之后变成 4
  • 对于负数 -n 来说,会将其映射为 2 * n-1。例如负数 -3,经过 zigzag 编码之后变成了 2 * 3 - 1 = 5
nzigzag编码后
00
-11
12
-23
24
-35
36

例如:举个极端的例子-1,如果不用zigzag编码,直接用varint,那么会用10个字节。如果先zigzag变成1,再varint,只会用1个字节

接下来阅读golang标准库中如何对varint和zagzig进行编码和解码的


varint

编码

将x以varint的形式写入buf中,返回写了多少个字节

由于是每7位用一个字节存储,那么只要大于等于10000000,也就是需要超过7位,就需要先把低7位存到buf[i]中

for循环中:buf[i] = byte(x) | 10000000,这行是保留低7位,并且把buf[i]的第8位强制置为1

最后一个字节的最高位为0

func PutUvarint(buf []byte, x uint64) int {i := 0// Ox80 = 10000000for x >= 0x80 {// buf[i] = x的低8位 | 10000000buf[i] = byte(x) | 0x80// 移除低7位x >>= 7// 需要用到的字节数 + 1i++}// 最后一个字节buf[i] = byte(x)return i + 1
}

解码

传入需要解码的字节序列,返回解码后的数字,以及其占用了字节序列中前多少字节

func Uvarint(buf []byte) (uint64, int) {var x uint64var s uintfor i, b := range buf {if i == MaxVarintLen64 {return 0, -(i + 1) // overflow}// b < 10000000,也最高位为0,代表是就是最后一个字节if b < 0x80 {if i == MaxVarintLen64-1 && b > 1 {return 0, -(i + 1) // overflow}// x | 最高的7位,返回return x | uint64(b)<<s, i + 1}// 最高位为1,表示后面还有字节// 提取这个字节的前7位:b & 01111111x |= uint64(b&0x7f) << ss += 7}return 0, 0
}

注意:如果要解码成uint64,一共64位,按7位一组,最多10组且第10组最大为1(第64位)

对应到源码中判断,如果超过10组或第10组大于1了,就返回溢出


zigzag

编码

我们知道在zigzag编码中:

  • 如果x是正数,按照2 * x的varint编码

  • 如果x是负数,假设其值为-n,就按照2 * n - 1的varint编码

对照源码看看:

func PutVarint(buf []byte, x int64) int {ux := uint64(x) << 1if x < 0 {ux = ^ux}return PutUvarint(buf, ux)
}

如果x是正数,等于x << 1的varint编码,没问题

如果x是负数,这里的操作是 x << 1,再按位取反

go中x = ^x代表对x按位取反

这就有些难以理解了,为啥 ^(x << 1)等于 2 * n - 1

我们先看看负数的二进制怎么表示

要计算-n的二进制表示,先计算n-1,再按位取反

那么反过来,给定一个负数的二进制x,怎么得到n是多少(也就是负多少)?就是把上面的操作反过来,先按位取反,再+1,也就是n = ^x + 1

我们目的是要得到 n * 2 - 1 ,把上面的式子带进去,得到 2 * (^x + 1) - 1 = 2 * ^x + 1

而对一个数先取反,再乘2,再加1,等于对这个数先乘2,再取反

2 * ^x + 1 = ^(2 * x)

举个例子,假设x = 10010:
在这里插入图片描述

为啥这个等式成立呢?因右边2 * x后,最低位变成0了,此时再取反的到的值,相比于先对x取反再*2得到的值来说,最低位多了个1。也就是后取反的话,比先取反多把末尾的0翻转成1了

于是得到n * 2 - 1 = ^(2 * x),对应了源码中对负数的编码


解码

如果varint中存的是偶数,那么原始值就是正数,值为ux / 2

如果varint中存的是奇数,那么原始值就是负数,那么值是多少呢?

ux / 2得到的值是n-1,最终要得到-n

我们先看n怎么得到-n?n-1再按位取反

而现在已经有n-1了,直接按位取反即可

func Varint(buf []byte) (int64, int) {ux, n := Uvarint(buf) x := int64(ux >> 1)// 负数 x = n-1,要得到-n,按位取反即可if ux&1 != 0 {x = ^x}return x, n
}

局限性

注意不是所有场景都适合用varint编码:

  1. 当数值比较大时:例如用满了int64的64位,那么在varint中会用到10位,反而比定长编码多了用了20%的空间
  2. 需要随机访问时:例如一个varint数组,要随机访问下标i的值。此时就不适合用任何变长编码的数据了。因为要随机访问的前提是每个元素的长度是定长的,这样才能根据公式 i * 定长,随机访问到特定的内存空间

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/58095.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SQLite3库增删改查实现数据管理

1. SQLite3简介 SQLite3是一个轻量级的、嵌入式的关系型数据库管理系统&#xff0c;在保存测序数据或结果等时可使用&#xff0c;简单高效&#xff0c;并且有无需服务器、单文件存储数据、支持标准SQL、支持跨平台等优势。 本文以Sqlite3数据库为基础&#xff0c;创建代码示例…

tomcat基本配置

目录 1.java容器简介介绍 2.部署tomcat 2.1上传jdk 2.2创建一个软连接 2.3配置环境变量 2.4读取环境文件并且查看java版本 2.5检查jdk tomcat信息 2.6启动tomcat 2.7检测 3.tomcat 目录结构 3.1总体目录 3.2 bin目录 3.3conf 3.4 logs日志 4.运行代码 4.…

如何确保电子商务网站服务器的正常运行时间

对于电商网站而言&#xff0c;服务器的正常运行时间至关重要。网站宕机会直接影响销售额、客户体验以及品牌声誉。本文将详细探讨如何监控并保障服务器的正常运行时间&#xff0c;确保您的电商网站始终保持在线状态&#xff0c; 为什么监控正常运行时间很重要&#xff1f; 减…

【Oracle实验】字段为空的,无法通过排除判断

Oracle相关文档&#xff0c;希望互相学习&#xff0c;共同进步 风123456789&#xff5e;-CSDN博客 1.场景描述 需求&#xff1a;查询不是某个机构的数据。 同事SQL&#xff1a;where substr(bank_code,1,9) not in(014009001)&#xff1b; 看SQL似乎没什么问题&#xff0c;分析…

【modbus协议】libmodbus库移植基于linux平台

文章目录 下载库函数源码编译路径添加libmodbus 源码分析核心数据结构常用接口函数 开发 TCP Server 端开发TCP Client 端 下载库函数源码 编译路径添加 libmodbus 源码分析 核心数据结构 modbus_t结构体&#xff1a; 这是 libmodbus 的核心数据结构&#xff0c;代表一个 Mod…

OSPF特殊区域及其他特性

不用的链路这状态信息没必要一直保存&#xff0c;要不路由器承受不了。用OSPF 特殊区域解决 1. Stub区域和Totally Stub区域 R1作为ASBR引入多个外部网段&#xff0c;如果Area 2是普通区域&#xff0c;则R3将向该区域注入5类和4类LSA。 当把Area 2配置为Stub区域后&#xff1a…

node升级package.json中的版本

由于项目使用时间过老&#xff0c;升级对应包版本&#xff0c;可以使用新功能 1.使用npm-check-updates这个工具&#xff0c;先全局安装 npm install -g npm-check-updates2.检查package.json中dependencies的最新版本 ncu3.更新dependencies到新版本 ncu -u也是一样的 npx…

探索Python安全字符串处理的奥秘:MarkupSafe库揭秘

文章目录 探索Python安全字符串处理的奥秘&#xff1a;MarkupSafe库揭秘第一部分&#xff1a;背景介绍第二部分&#xff1a;MarkupSafe是什么&#xff1f;第三部分&#xff1a;如何安装MarkupSafe&#xff1f;第四部分&#xff1a;MarkupSafe的简单使用方法1. 使用escape函数2.…

机器视觉运动控制一体机在DELTA并联机械手视觉上下料应用

市场应用背景 DELTA并联机械手是由三个相同的支链所组成&#xff0c;每个支链包含一个转动关节和一个移动关节&#xff0c;具有结构紧凑、占地面积小、高速高灵活性等特点&#xff0c;可在有限的空间内进行高效的作业&#xff0c;广泛应用于柔性上下料、包装、分拣、装配等需要…

【C++】类和对象(二):this指针

大家好&#xff0c;我是苏貝&#xff0c;本篇博客带大家了解C的this指针&#xff0c;如果你觉得我写的还不错的话&#xff0c;可以给我一个赞&#x1f44d;吗&#xff0c;感谢❤️ 目录 1 this指针的引出2 this指针的特性 1 this指针的引出 我们先来定义一个日期类Date 问&am…

华为原生鸿蒙操作系统的发布有何重大意义和影响:

#1024程序员节 | 征文# 一、华为原生鸿蒙操作系统的发布对中国的意义可以从多个层面进行分析&#xff1a; 1. 技术自主创新 鸿蒙操作系统的推出标志着中国在操作系统领域的自主创新能力的提升。过去&#xff0c;中国在高端操作系统方面依赖于外国技术&#xff0c;鸿蒙的发布…

开发涉及的安全规范整理

文章目录 前言安全场景与措施API调用方式鉴权参数校验日志打印数据保存加密 总结 前言 这篇文章我们来整理下写代码和方案设计中的安全规范问题&#xff0c;内容偏服务端&#xff0c;即使是入门的新人&#xff0c;如果你对安全有所了解会让成熟规范的团队对你高看一眼。安全经常…

用HTML构建酷炫的文件上传下载界面

1. 基础HTML结构 首先&#xff0c;我们构建一个基本的HTML结构&#xff0c;包括一个表单用于文件上传&#xff0c;以及一个列表用于展示已上传文件&#xff1a; HTML <!DOCTYPE html> <html> <head><title>酷炫文件上传下载</title><link …

健康养生的重要性

养生之道&#xff0c;健康相随 在快节奏的现代生活中&#xff0c;养生健康已成为我们不可忽视的话题。随着生活水平的提高&#xff0c;人们越来越注重身体的保养与健康的维护。那么&#xff0c;如何才能做到养生健康&#xff0c;让身体与心灵都得到滋养呢&#xff1f; 首先&a…

鱼跃医疗助力退役军人事务部“高原情暖老兵项目”

10月17日-22日&#xff0c;在退役军人事务部指导下&#xff0c;中国老龄事业发展基金会联合腾讯SSV时光实验室、腾讯天籁实验室等机构发起的“情暖老兵&#xff0c;守望相助—老兵听力关怀计划”项目走进西藏&#xff0c;为退伍老兵提供听力健康筛查服务。西藏鱼跃医疗投资有限…

fastGpt

参考本地部署FastGPT使用在线大语言模型 1 rockylinx 1 ollama安装 在rockylinux中安装的&#xff0c;ollama由1.5G&#xff0c;还是比较大&#xff0c;所有采用在windows下下载&#xff0c;然后安装的方式&#xff0c;linux安装 tar -C /usr -xzf ollama-linux-amd64.tgz #…

U-net医学分割网络——学习笔记

《U-Net: Convolutional Networks for Biomedical Image Segmentation》 一、提出背景 U-Net 的提出是为了解决生物医学图像分割的几个关键问题&#xff1a;需要像素级的精确分割、标注数据稀缺、滑动窗口方法效率低以及多尺度特征融合的需求。U-Net 通过对称的 U 型全卷积结…

Redis+Lua限流的四种算法

1. 固定窗口&#xff08;Fixed Window&#xff09; 原理&#xff1a; 固定窗口算法将时间划分为固定的时间段&#xff08;窗口&#xff09;&#xff0c;比如 1 秒、1 分钟等。在每个时间段内&#xff0c;允许最多一定数量的请求。如果请求超出配额&#xff0c;则拒绝。 优点…

【linux网络编程】| 网络套接字socket | 初识网络开发

前言&#xff1a;本篇内容将要正式进入网络的编程当中。 本篇的目的是为了能够看完就可以上手写一些网络代码了。 但是本篇也并不会单纯的只讲接口&#xff0c; 前面还是会铺垫一些理论知识更好的认识网络传输。下面&#xff0c; 开始我们的学习吧! ps&#xff1a;本篇内容的某…

摄像头点击器常见问题——摄像头视窗打开慢

【嵌入式开发】可编程4k蓝牙摄像头点击器_能编程的摄像头-CSDN博客 拥有上述文章产品的朋友出现标题所述问题&#xff0c;可继续往下阅读 出现以上问题&#xff0c;摄像头画面打开较慢&#xff0c;可以按以下操作进行设置 在环境变量里设置一下这个参数&#xff0c;值设置为1&…