Skip to main content

seyd_wire/
v1.rs

1// Copyright 2026 Anton Gravestam
2// SPDX-License-Identifier: Apache-2.0
3//! Legacy v1 header (prototype, `packages/agent/fec.py`). Decode only.
4//!
5//! Kept so the Rust coder can be checked against the Python-generated interop
6//! vectors, and so a v2 receiver can *recognise* v1 traffic in its
7//! `bad_header` counter. Not used on the wire by the product.
8
9pub const VERSION: u8 = 1;
10pub const HEADER_LEN: usize = 10;
11pub const MAX_CHUNK_PAYLOAD: usize = 1000;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub struct Header {
15    pub keyframe: bool,
16    pub fec_type: u8,
17    pub frame_id: u16,
18    pub chunk_idx: u16,
19    pub n: u16,
20    pub k: u8,
21    pub last_len: u16,
22}
23
24/// Parse a v1 chunk. `None` if too short or not version 1.
25pub fn parse(buf: &[u8]) -> Option<(Header, &[u8])> {
26    if buf.len() < HEADER_LEN {
27        return None;
28    }
29    let flags = buf[0];
30    if flags & 0x0f != VERSION {
31        return None;
32    }
33    let be16 = |i: usize| u16::from_be_bytes([buf[i], buf[i + 1]]);
34    Some((
35        Header {
36            keyframe: flags & 0x80 != 0,
37            fec_type: (flags >> 4) & 0x07,
38            frame_id: be16(1),
39            chunk_idx: be16(3),
40            n: be16(5),
41            k: buf[7],
42            last_len: be16(8),
43        },
44        &buf[HEADER_LEN..],
45    ))
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51
52    #[test]
53    fn parses_reference_layout() {
54        // flags: keyframe | RS | v1 ; frame 0x1234 ; idx 3 ; n 5 ; k 2 ; last_len 999
55        let buf = [
56            0xa1, 0x12, 0x34, 0x00, 0x03, 0x00, 0x05, 0x02, 0x03, 0xe7, 0xaa,
57        ];
58        let (h, payload) = parse(&buf).unwrap();
59        assert_eq!(
60            h,
61            Header {
62                keyframe: true,
63                fec_type: 2,
64                frame_id: 0x1234,
65                chunk_idx: 3,
66                n: 5,
67                k: 2,
68                last_len: 999
69            }
70        );
71        assert_eq!(payload, &[0xaa]);
72        assert!(parse(&[0xa2; 10]).is_none(), "wrong version rejected");
73        assert!(parse(&[0xa1; 9]).is_none(), "short buffer rejected");
74    }
75}