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。