Endian-Aware Binary Reader

Parse and build big-endian length-prefixed frames with a version byte, flags, checksum, and explicit truncation errors.

RustData Integrity

Code

snippet
#[derive(Debug, PartialEq)]
struct Frame {
    version: u8,
    flags: u16,
    length: u32,
    payload: Vec<u8>,
}

const HEADER_LEN: usize = 8;

fn simple_sum(payload: &[u8]) -> u8 {
    payload
        .iter()
        .fold(0u8, |accumulator, byte| accumulator.wrapping_add(*byte))
}

fn parse_frame(bytes: &[u8]) -> Result<Frame, String> {
    if bytes.len() < HEADER_LEN {
        return Err(format!(
            "need {HEADER_LEN} header bytes, got {}",
            bytes.len()
        ));
    }
    let version = bytes[0];
    let flags = u16::from_be_bytes([bytes[1], bytes[2]]);
    let length = u32::from_be_bytes([bytes[3], bytes[4], bytes[5], bytes[6]]);
    let checksum = bytes[7];
    if version != 1 {
        return Err(format!("unsupported version {version}"));
    }
    let end = HEADER_LEN + length as usize;
    if bytes.len() < end {
        return Err(format!("truncated payload: need {end} bytes"));
    }
    let payload = bytes[HEADER_LEN..end].to_vec();
    if simple_sum(&payload) != checksum {
        return Err("checksum mismatch".to_string());
    }
    Ok(Frame {
        version,
        flags,
        length,
        payload,
    })
}

fn encode_frame(frame: &Frame) -> Vec<u8> {
    let mut bytes = Vec::with_capacity(HEADER_LEN + frame.payload.len());
    bytes.push(frame.version);
    bytes.extend_from_slice(&frame.flags.to_be_bytes());
    bytes.extend_from_slice(&frame.length.to_be_bytes());
    bytes.push(simple_sum(&frame.payload));
    bytes.extend_from_slice(&frame.payload);
    bytes
}

fn main() {
    let frame = Frame {
        version: 1,
        flags: 0b1010,
        length: 5,
        payload: b"hello".to_vec(),
    };
    let encoded = encode_frame(&frame);
    println!("encoded: {:?}", encoded);
    println!("decoded: {:?}", parse_frame(&encoded));
    println!(
        "truncated: {}",
        parse_frame(&encoded[..encoded.len() - 2]).is_err()
    );

    let mut corrupted = encoded.clone();
    corrupted[HEADER_LEN] ^= 0xff;
    println!("checksum error: {:?}", parse_frame(&corrupted).unwrap_err());
}