Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Run header decoder (ULEB128 Decoder)

A run header is an integer encoded using ULEB-128 encoding, Wikipedia has a very well-explained encoding example for 624485.

MSB ------------------ LSB
      10011000011101100101  In raw binary
     010011000011101100101  Padded to a multiple of 7 bits
 0100110  0001110  1100101  Split into 7-bit groups
00100110 10001110 11100101  Add high 1 bits on all but last (most significant) group to form bytes
    0x26     0x8E     0xE5  In hexadecimal

→ 0xE5 0x8E 0x26            Output stream (LSB to MSB)

One important note is that the last byte has its MSB set to 0 instead of 1; this signals the decoder to stop fetching bytes when decoding data.

Decode

Decoding is a reverse operation: it strips the MSB (Most Significant Bit) in each byte, and packs them together. Let’s visualize it.

ULEB128 decoder start

Take the first byte and strip its MSB.

The first byte is taken from encoded data and put to decoded data

Take the second byte and strip its MSB.

The second byte is taken from encoded data and put to decoded data

Take the third byte and strip its MSB. This is also the last byte because its MSB is 0.

The last byte is taken from encoded data and put to decoded data

The decoded value is 010011000011101100101, which is 624485.

Task

Implement the uleb128_decode function in src/decoder/uleb128.rs. It takes the encoded Bytes and returns a decoded integer with the remaining bytes.

pub fn uleb128_decode(encoded_data: Bytes) -> Result<(u64, Bytes)> {
    todo!("step10-01: implement uleb128 decoder")
}

Test

Test case for this step is step10_01_uleb128_decoder.

Hints and Solution

Hint

There is no hint for this task.

Solution
pub fn uleb128_decode(encoded_data: Bytes) -> Result<(u64, Bytes)> {
    let mut encoded_data = encoded_data;
    let mut result = 0u64;

    let total_bytes = encoded_data.len();
    for i in 0..total_bytes {
        let byte = encoded_data.get_u8() as u64;
        result |= (byte & 0x7F) << (i * 7);
        // MSB = 0, stop
        if byte & 0x80 == 0 {
            return Ok((result, encoded_data));
        }
    }
    bail!("uleb128_decode: no byte with leading 0")
}