Skip to main content

cliquenet/msg/
frame.rs

1//! # Frame header
2//!
3//! The unit of data exchanged over the network is called a `Frame` and consists of
4//! a 4-byte header and a body of variable size. The header has the following
5//! structure:
6//!
7//! ```text
8//!  0                   1                   2                   3
9//!  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
10//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
11//! |       |       |P|             |                               |
12//! |Version|  Type |a|  Reserved   |        Payload length         |
13//! |       |       |r|             |                               |
14//! |       |       |t|             |                               |
15//! +-------+-------+-+-------------+-------------------------------+
16//! ```
17//!
18//! where
19//!
20//! - Version (4 bits)
21//! - Type (4 bits)
22//!    - Data (0)
23//!    - Ack  (1)
24//! - Partial (1 bit)
25//! - Reserved (7 bits)
26//! - Payload length (16 bits)
27//!
28//! If the partial bit is set, the frame is only a part of the message and the read task
29//! will assemble all frames to produce the final message.
30
31use std::fmt;
32
33/// The header of a frame.
34#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
35pub struct Header(u32);
36
37impl Header {
38    pub const SIZE: usize = 4;
39
40    pub fn new(ty: FrameType, len: u16) -> Self {
41        match ty {
42            FrameType::Data => Self::data(len),
43            FrameType::Ack => Self::ack(len),
44        }
45    }
46
47    /// Create a new, unvalidated header.
48    pub fn unvalidated(n: u32) -> Self {
49        Self(n)
50    }
51
52    /// Create a data header with the given payload length.
53    pub fn data(len: u16) -> Self {
54        Self(len as u32)
55    }
56
57    /// Create an ack header with the given payload length.
58    pub fn ack(len: u16) -> Self {
59        Self(0x1000000 | len as u32)
60    }
61
62    /// The type of the frame following this header.
63    pub fn frame_type(self) -> Result<FrameType, u8> {
64        match (self.0 & 0xF000000) >> 24 {
65            0 => Ok(FrameType::Data),
66            1 => Ok(FrameType::Ack),
67            t => Err(t as u8),
68        }
69    }
70
71    /// Set the partial flag to indicate that more frames follow.
72    pub fn partial(self) -> Self {
73        Self(self.0 | 0x800000)
74    }
75
76    /// Is this a data frame header?
77    pub fn is_data(self) -> bool {
78        self.0 & 0xF000000 == 0
79    }
80
81    /// Is this an ack frame header?
82    pub fn is_ack(self) -> bool {
83        self.0 & 0xF000000 == 0x1000000
84    }
85
86    /// Is this a partial frame?
87    pub fn is_partial(self) -> bool {
88        self.0 & 0x800000 == 0x800000
89    }
90
91    /// Get the payload length.
92    pub fn len(self) -> u16 {
93        (self.0 & 0xFFFF) as u16
94    }
95
96    /// Is the payload length 0?
97    pub fn is_empty(self) -> bool {
98        self.len() == 0
99    }
100
101    /// Convert this header into a byte array.
102    pub fn to_bytes(self) -> [u8; Self::SIZE] {
103        self.0.to_be_bytes()
104    }
105}
106
107/// The type of a frame.
108#[derive(Debug, Clone, Copy, PartialEq, Eq)]
109pub enum FrameType {
110    Data,
111    Ack,
112}
113
114impl From<Header> for [u8; Header::SIZE] {
115    fn from(val: Header) -> Self {
116        val.to_bytes()
117    }
118}
119
120impl fmt::Display for Header {
121    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122        f.debug_struct("Header")
123            .field("type", &self.frame_type())
124            .field("len", &self.len())
125            .field("partial", &self.is_partial())
126            .finish()
127    }
128}
129
130#[derive(Debug, thiserror::Error)]
131#[error("invalid header: {0}")]
132pub struct InvalidHeader(&'static str);
133
134#[cfg(test)]
135mod tests {
136    use quickcheck::quickcheck;
137
138    use super::{FrameType, Header};
139
140    quickcheck! {
141        fn data(len: u16) -> bool {
142            let hdr = Header::data(len);
143            hdr.is_data() && !hdr.is_partial() && hdr.frame_type() == Ok(FrameType::Data)
144        }
145
146        fn ack(len: u16) -> bool {
147            let hdr = Header::ack(len);
148            hdr.is_ack() && !hdr.is_partial() && hdr.frame_type() == Ok(FrameType::Ack)
149        }
150
151        fn partial_data(len: u16) -> bool {
152            Header::data(len).partial().is_partial()
153        }
154
155        fn partial_ack(len: u16) -> bool {
156            Header::ack(len).partial().is_partial()
157        }
158
159        fn data_len(len: u16) -> bool {
160            Header::data(len).len() == len
161        }
162
163        fn ack_len(len: u16) -> bool {
164            Header::ack(len).len() == len
165        }
166    }
167}