LEB128相關知識

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章