Skip to main content

cobs_io_async/
protocol.rs

1use crate::{max_encoding_length, sync};
2
3pub(crate) const KIND_DATA: u8 = 1;
4pub(crate) const KIND_ACK: u8 = 2;
5
6const HEADER_LEN: usize = 1 + 8 + 4;
7const CRC_LEN: usize = 4;
8const OVERHEAD: usize = HEADER_LEN + CRC_LEN;
9
10pub(crate) const ACK_FRAME_LEN: usize = max_encoding_length(OVERHEAD) + 2;
11
12pub(crate) enum Packet<'a> {
13    Data {
14        session: u64,
15        seq: u32,
16        payload: &'a [u8],
17    },
18    Ack {
19        session: u64,
20        seq: u32,
21    },
22}
23
24/// CRC-32/ISO-HDLC: polynomial 0x04C11DB7 reflected, init and xorout 0xFFFFFFFF.
25const CRC_TABLE: [u32; 256] = {
26    let mut table = [0u32; 256];
27    let mut i = 0;
28    while i < 256 {
29        let mut crc = i as u32;
30        let mut bit = 0;
31        while bit < 8 {
32            crc = if crc & 1 != 0 {
33                (crc >> 1) ^ 0xEDB8_8320
34            } else {
35                crc >> 1
36            };
37            bit += 1;
38        }
39        table[i] = crc;
40        i += 1;
41    }
42    table
43};
44
45pub(crate) fn crc32(data: &[u8]) -> u32 {
46    !data.iter().fold(!0u32, |crc, &byte| {
47        CRC_TABLE[usize::from((crc as u8) ^ byte)] ^ (crc >> 8)
48    })
49}
50
51/// Largest payload whose delimited frame fits in `capacity` bytes, or zero if
52/// no payload does.
53pub(crate) const fn max_payload(capacity: usize) -> usize {
54    let Some(body) = capacity.checked_sub(2) else {
55        return 0;
56    };
57    let mut len = body;
58    while len > OVERHEAD && max_encoding_length(len) > body {
59        len -= 1;
60    }
61    len.saturating_sub(OVERHEAD)
62}
63
64fn seal(decoded: &mut [u8], len: usize, frame: &mut [u8]) -> usize {
65    let crc = crc32(&decoded[..len - CRC_LEN]);
66    decoded[len - CRC_LEN..len].copy_from_slice(&crc.to_be_bytes());
67    sync::encode_from_slice_including_sentinels(&decoded[..len], frame)
68        .expect("frame buffer holds the encoded packet")
69}
70
71fn header(decoded: &mut [u8], kind: u8, session: u64, seq: u32) {
72    decoded[0] = kind;
73    decoded[1..9].copy_from_slice(&session.to_be_bytes());
74    decoded[9..HEADER_LEN].copy_from_slice(&seq.to_be_bytes());
75}
76
77/// Encodes a delimited DATA frame into `frame`, using `scratch` for the
78/// unencoded packet. Returns the frame length.
79pub(crate) fn encode_data(
80    session: u64,
81    seq: u32,
82    payload: &[u8],
83    scratch: &mut [u8],
84    frame: &mut [u8],
85) -> usize {
86    header(scratch, KIND_DATA, session, seq);
87    scratch[HEADER_LEN..HEADER_LEN + payload.len()].copy_from_slice(payload);
88    seal(scratch, OVERHEAD + payload.len(), frame)
89}
90
91/// Encodes a delimited ACK frame into `frame`. Returns the frame length.
92pub(crate) fn encode_ack(session: u64, seq: u32, frame: &mut [u8; ACK_FRAME_LEN]) -> usize {
93    let mut decoded = [0u8; OVERHEAD];
94    header(&mut decoded, KIND_ACK, session, seq);
95    seal(&mut decoded, OVERHEAD, frame)
96}
97
98pub(crate) fn parse(decoded: &[u8]) -> Option<Packet<'_>> {
99    if decoded.len() < OVERHEAD {
100        return None;
101    }
102    let (body, crc) = decoded.split_at(decoded.len() - CRC_LEN);
103    if crc32(body) != u32::from_be_bytes(crc.try_into().ok()?) {
104        return None;
105    }
106    let session = u64::from_be_bytes(body[1..9].try_into().ok()?);
107    let seq = u32::from_be_bytes(body[9..HEADER_LEN].try_into().ok()?);
108    let payload = &body[HEADER_LEN..];
109    match body[0] {
110        KIND_DATA if !payload.is_empty() => Some(Packet::Data {
111            session,
112            seq,
113            payload,
114        }),
115        KIND_ACK if payload.is_empty() => Some(Packet::Ack { session, seq }),
116        _ => None,
117    }
118}