LEB128相关知识

介绍

LEB128(little endian base 128)是一种变长的整数压缩编码形式,它是出自于DWARF debug file format。在Android的Dalvik Executable format中使用该编码用于表示32位整数。由于32位整数占用固定的4个字节,可能大多数整数并不需要4个字节,最高几个字节可能为0(正数)或者为1(负数),该编码就是不保存最高位的这些字节。

原理

LEB128的表现形式都是一样的,如下面表格所示,由于是little endian,因此是从低字节到高字节。每个字节中的最高bit是标识信息,1表示还有后续字节,0表示结束,后面7bits是有效数据。将多个字节的该7bits从低到高组合起来就是所表示的整数。

LEB128分成有符号数和无符号数两种分别进行处理,不过,只是在编码和解码过程有些不同。

低地址 +1 +2 +3 +4
0 xxxxxxx
1 xxxxxxx 0 xxxxxxx
1 xxxxxxx 1 xxxxxxx 0 xxxxxxx
1 xxxxxxx 1 xxxxxxx 1 xxxxxxx 0 xxxxxxx
1 xxxxxxx 1 xxxxxxx 1 xxxxxxx 1 xxxxxxx 0 xxxxxxx

无符号整数

将无符号整数写成二进制形式,从低位到高位7个bits为一个整体组合成一个字节,在该字节最高位填入上述所说的标识信息。

下面以10000为例,编码过程:

二进制形式为 10 0111 0001 0000
以7bits为整体 1001110 0010000
添加标识组合成新的字节(从后往前,即低bits到高bits) 01001110(0x4E) 10010000(0x90) (最高位标识设置为0,表示没有后续字节)
LEB128 则为 0x90 0x4F (小端存放)

解码过程:

LEB128 0x90 0x4E
二进制形式 10010000 01001110
去掉标识信息 0010000(低7bits) 1001110(高7bits)
组合的结果为 10011100010000 (10000)

编码代码为:

    void EncodeULEB128(unsigned int value, unsigned char *leb128_buffer)
{
int pos = 0;
while (value != 0) {
leb128_buffer[pos++] = value & 0x7F | 0x80; //每个字节标识信息都设为1
value >>= 7;
}
if (pos > 0)
leb128_buffer[pos-1] &= 0x7F; //将最后一个字节的标识信息设为0
}

解码代码为:

    void DecodeULEB128(unsigned char *leb128_buffer, unsigned int *value)
{
int pos = 0;
int offset = 0;
while (buffer[pos] != 0) {
*value |= ( (buffer[pos] & 0x7F) << offset ); //从低到高将 bits 合并到一起
offset += 7;
if (buffer[pos] & 0x80 == 0)
break;
pos += 1;
}
}

有符号数

有符号数分成了正数和负数,在计算机的存储中都是以补码存储,正数和上述无符号数一样的处理,负数的处理会有些区别,以-10000为例说明,

编码过程:

二进制补码 11111111 11111111 11111100 00011000(可以看出最高两字节都是符号扩展的1)
以7bits为整体 1111 1111111 1111111 1111000 0011000
添加标识信息组合新的字节(从后往前,即低bits到高bits) 01111000 10011000(此处结束条件不像上面那么明显,若前面和该7bits的最高位都为1时停止)
LEB128则为 0x98 0x78

解码过程:

LEB128 0x98 0x78
二进制形式 10011000 01111000
去掉标识信息 0011000 1111000 (若最后一个字节中7bits的最高位为1,则前面需要符号扩展都添加1)
组合结果 11111111 11111111 1111100 00011000 (-10000)

编码代码为:

    void EncodeLEB128(int value, unsigned char *buffer)
{
int pos = 0;
int more = 1;
while (more) {
unsigned char byte = value & 0x7F;
value >>= 7;
if ( ((value == 0) && (byte & 0x40) == 0) || //正数
((value == -1) && (byte & 0x40) != 0) ) //负数
more = 0;
if (more != 0)
byte != 0x80;
buffer[pos++] = byte;
}
}

编码代码为:

    void DecodeLEB128(unsigned char *buffer, int *value)
{
int pos = 0;
int offset = 0;
unsigned char byte = buffer[pos++]; while (byte >= 0x80) {
*value |= (byte & 0x7f) << offset;
offset += 7;
byte = buffer[pos++];
}
if (byte & 0x40)
*value |= -(1 << offset);
}

总结

LEB128的理解难点是在有符号数上,编码结束条件不像无符号数那么明显(value等于0),分两种情况:

1. 若为正数,7bits中的最高位为0 并且 value == 0结束,value ==0 表示高字节没有数据,而7bits最高位为0用于表示是正数,用于解码;

2. 若为负数,7bits中的最高位为1 并且 value == -1结束, value == -1表示高字节都是符号扩展出来的1, 7bits最高位为1用于表示是负数,在解码时高位填充1。

参考

https://en.wikipedia.org/wiki/LEB128

http://llvm.org/docs/doxygen/html/LEB128_8h_source.html

LEB128相关知识的更多相关文章

  1. 【Python五篇慢慢弹(5)】类的继承案例解析,python相关知识延伸

    类的继承案例解析,python相关知识延伸 作者:白宁超 2016年10月10日22:36:57 摘要:继<快速上手学python>一文之后,笔者又将python官方文档认真学习下.官方给 ...

  2. 移动WEB像素相关知识

    了解移动web像素的知识,主要是为了切图时心中有数.本文主要围绕一个问题:怎样根据设备厂商提供的屏幕尺寸和物理像素得到我们切图需要的逻辑像素?围绕这个问题以iphone5为例讲解涉及到的web像素相关 ...

  3. listener监听器的相关知识

    从别人的博客上我学习了listener的相关知识现在分享给大家 1.概念: 监听器就是一个实现特定接口的普通java程序,这个程序专门用于监听另一个java对象的方法调用或属性改变,当被监听对象发生上 ...

  4. UIViewController相关知识

    title: UIViewController 相关知识date: 2015-12-13 11:50categories: IOS tags: UIViewController 小小程序猿我的博客:h ...

  5. 【转】java NIO 相关知识

    原文地址:http://www.iteye.com/magazines/132-Java-NIO Java NIO(New IO)是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的 ...

  6. NSString使用stringWithFormat拼接的相关知识

    NSString使用stringWithFormat拼接的相关知识 保留2位小数点 1 2 3 4 //.2代表小数点后面保留2位(2代表保留的数量) NSString *string = [NSSt ...

  7. iOS网络相关知识总结

    iOS网络相关知识总结 1.关于请求NSURLRequest? 我们经常讲的GET/POST/PUT等请求是指我们要向服务器发出的NSMutableURLRequest的类型; 我们可以设置Reque ...

  8. 电路相关知识--读<<继电器是如何成为CPU的>>

    电路相关知识–读<<继电器是如何成为CPU的>> */--> *///--> *///--> 电路相关知识–读<<继电器是如何成为CPU的> ...

  9. 地址标记,SpringMVC转发与调用相关知识存档

    1.mytest_mavenprj1中,index的 <a href="login/login.html">点击登录</a> 与 <a href=&q ...

随机推荐

  1. python中下划线的特殊用法

    python下划线用法总结: ① _XXX 不能用于“ from  model import * ”的导入: ②__XXX__ 系统定义名字: ③__XXX 类中的私有变量名. 总结:避免随意用下划线 ...

  2. AJAX 解决什么问题?如何使用AJAX?AJAX 有什么问题需要注意?项目中哪里用到了AJAX?

    ajax 解决的问题就是“无刷新更新页面”,用传统的HTML 表单方式进行页面的更新时,每次都要将请求提交到服务器,服务器返回后再重绘界面,这样界面就会经历:提交→变白→重新显示这样一个过程,用户体验 ...

  3. kaliLinux 安装 telnet

    一.安装xinetd telnetd root@helm:~# apt-get install xinetd telnetd 二.查看服务是否启动状态 说明自启动了 如果没有需要启动xinetd,启动 ...

  4. HTTP请求协议

    请求(Request)协议 * GET请求方式 * 请求行 * http协议的版本信息 1.1 * 请求地址 - URL?key=value&key=value * 请求方式 - GET * ...

  5. js中获取时间new Date()详细介绍

    var myDate = new Date();myDate.getYear(); //获取当前年份(2位)myDate.getFullYear(); //获取完整的年份(4位,1970-????)m ...

  6. linux下安装FTP详细

    一:安装ftp 1.检查是否安装了 ftp :(下图是已安装的效果,没安装没有展示) rpm -qa|grep vsftpd 2.安装指令:(找一个  -y  指令) yum install -y v ...

  7. 通过SSH去连接 github 和bitbucket

    github 和 bitbucket 都是项目托管服务器, 1 创建SSH private key and public key 首先需要安装git命令, 并且请检查是否有ssh 命令. 打开 Git ...

  8. snap7和plc的IP设置问题

    设备ip必须在同一个网段,才能ping到 相关ip的链接https://blog.csdn.net/bytxl/article/details/41897287 在调试plc与树莓派的过程中,犯了一个 ...

  9. JAVA字符串的常见处理和操作

    1.纯数字字符串补0为指定位,格式化输出(例如00482这样) 使用String.format处理: int mNumber = 1; // 0 代表前面补充0 // 4 代表长度为4 // d 代表 ...

  10. vue 用户停留页面超过30分钟未操作 强制退出到登录页面

    先说下主要实现思路,通过给你的根节点绑定mouseover事件,首先声明下当前时间,每次滑过时记录下滑过的时间,两个时间转化成毫秒数,进行对比,如果超过30分钟,则清除token,跳转到login.h ...