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

Handling simple blocks

Blocks are the basic components forming a frame. There exist three types of blocks:

  • A raw block contains some data, without any compression, that must be copied while decoding.
  • A RLE block is a block which contains a byte b and a length l. Decoding it duplicates the b byte l times.
  • A compressed block contains much more complex information and is the basis of Zstandard compression capabilities.

For a start, you will implement the decoding of the first two block types.

Structure

✅ In a block module, add an enumerated Block type with two variants (for now): RawBlock which encapsulates a slice, and RLEBlock which contains a byte and a repeat field.

✅ Implement Block::parse() which accepts a &mut ForwardByteParser and returns a result containing a (Block, bool) pair. The bool is true if the block is the last block of the frame.

Every block starts with a 3 byte header, read as a 24 bit little-endian integer: bit 0 is the last block flag, bits 1–2 are the block type (0: raw, 1: RLE, 2: compressed, 3: reserved), and the remaining 21 bits are the block size. For a raw block, the size is the number of bytes which follow; for a RLE block, a single byte follows and the size is the number of times it must be repeated.

Do not forget to add a dedicated Error type in the block module, as well as a Result type alias. If the block type is invalid (3 is reserved), an Error::ReservedBlockType should be returned. Also, you can call unimplemented!() if you encounter a block of type 2 (compressed block) as we do not implement it yet.

✅ Implement the Block::decode(self) method which decodes a block (and destroys the Block object by taking self).

Right now, decode() does not need any other parameter. This will no longer be the case when we implement compressed blocks, as decoding a compressed block requires accessing a decoding context containing information about earlier operations. Also, decode() needs to return a Result: even if decoding cannot fail right now, it will later when more complex blocks are implemented.

✅ Add tests.

Here are two simple tests, you must add others, including for checking that errors are correctly detected:

use net7212::block::Block;
use net7212::parsing::ForwardByteParser;

#[test]
fn decode_raw_block_last() {
    let mut parser = ForwardByteParser::new(&[
        // Raw block, last block, len 4, content 0x10, 0x20, 0x30, 0x40,
        // and an extra 0x50 at the end.
        0x21, 0x0, 0x0, 0x10, 0x20, 0x30, 0x40, 0x50
    ]);
    let (block, last) = Block::parse(&mut parser).unwrap();
    assert!(last);
    assert!(matches!(block, Block::RawBlock(&[0x10, 0x20, 0x30, 0x40])));
    assert_eq!(1, parser.len());
    let decoded = block.decode().unwrap();
    assert_eq!(vec![0x10, 0x20, 0x30, 0x40], decoded);
}

#[test]
fn decode_rle_block_not_last() {
    let mut parser = ForwardByteParser::new(&[
        // RLE block, not last, byte 0x42 and repeat 0x10004,
        // and an extra 0x50 at the end.
        0x22, 0x0, 0x08, 0x42, 0x50
    ]);
    let (block, last) = Block::parse(&mut parser).unwrap();
    assert!(!last);
    assert!(matches!(
        block,
        Block::RLEBlock {
            byte: 0x42,
            repeat: 65540
        }
    ));
    assert_eq!(1, parser.len());
    let decoded = block.decode().unwrap();
    assert_eq!(65540, decoded.len());
    assert!(decoded.into_iter().all(|b| b == 0x42));
}