Skip to main content

hdds_micro/transport/mesh/
header.rs

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2// Copyright (c) 2025-2026 naskel.com
3
4//! Mesh packet header
5
6use crate::error::{Error, Result};
7
8/// Mesh header size in bytes
9pub const MESH_HEADER_SIZE: usize = 7;
10
11/// Mesh header magic byte
12const MESH_MAGIC: u8 = 0x4D; // 'M' for Mesh
13
14/// Mesh packet flags
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
16pub struct MeshFlags(u8);
17
18impl MeshFlags {
19    /// No flags set
20    pub const fn empty() -> Self {
21        Self(0)
22    }
23
24    /// Message requires acknowledgment
25    pub const ACK_REQUIRED: Self = Self(0x01);
26
27    /// Message is an acknowledgment
28    pub const IS_ACK: Self = Self(0x02);
29
30    /// Message is a route request
31    pub const ROUTE_REQUEST: Self = Self(0x04);
32
33    /// Message is a route reply
34    pub const ROUTE_REPLY: Self = Self(0x08);
35
36    /// Message is a beacon
37    pub const BEACON: Self = Self(0x10);
38
39    /// Check if flag is set
40    pub fn contains(&self, flag: Self) -> bool {
41        (self.0 & flag.0) != 0
42    }
43
44    /// Set a flag
45    pub fn set(&mut self, flag: Self) {
46        self.0 |= flag.0;
47    }
48
49    /// Clear a flag
50    pub fn clear(&mut self, flag: Self) {
51        self.0 &= !flag.0;
52    }
53
54    /// Get raw value
55    pub fn bits(&self) -> u8 {
56        self.0
57    }
58
59    /// Create from raw value
60    pub fn from_bits(bits: u8) -> Self {
61        Self(bits)
62    }
63}
64
65/// Mesh packet header
66///
67/// ```text
68/// +-------+-------+-------+-------+-------+---------------+
69/// | Magic |  Src  |  Dst  | SeqHi | SeqLo | TTL | Flags   |
70/// |  1B   |  1B   |  1B   |  1B   |  1B   | 4b  |   4b    |
71/// +-------+-------+-------+-------+-------+---------------+
72/// Total: 7 bytes
73/// ```
74#[derive(Debug, Clone, Copy, PartialEq, Eq)]
75pub struct MeshHeader {
76    /// Source node ID
77    pub src: u8,
78    /// Destination node ID (0xFF = broadcast)
79    pub dst: u8,
80    /// Sequence number (for duplicate detection)
81    pub seq: u16,
82    /// Time-to-live (hops remaining)
83    pub ttl: u8,
84    /// Flags
85    pub flags: MeshFlags,
86    /// Hop count (how many hops traversed)
87    pub hop_count: u8,
88}
89
90impl MeshHeader {
91    /// Create a new mesh header
92    pub fn new(src: u8, dst: u8, seq: u16, ttl: u8) -> Self {
93        Self {
94            src,
95            dst,
96            seq,
97            ttl,
98            flags: MeshFlags::empty(),
99            hop_count: 0,
100        }
101    }
102
103    /// Create a broadcast header
104    pub fn broadcast(src: u8, seq: u16, ttl: u8) -> Self {
105        Self::new(src, 0xFF, seq, ttl)
106    }
107
108    /// Check if this is a broadcast message
109    pub fn is_broadcast(&self) -> bool {
110        self.dst == 0xFF
111    }
112
113    /// Create header for relay (decrements TTL, increments hop)
114    pub fn for_relay(&self) -> Option<Self> {
115        if self.ttl == 0 {
116            return None;
117        }
118
119        Some(Self {
120            src: self.src,
121            dst: self.dst,
122            seq: self.seq,
123            ttl: self.ttl - 1,
124            flags: self.flags,
125            hop_count: self.hop_count.saturating_add(1),
126        })
127    }
128
129    /// Encode header to buffer
130    pub fn encode(&self, buf: &mut [u8]) -> Result<usize> {
131        if buf.len() < MESH_HEADER_SIZE {
132            return Err(Error::BufferTooSmall);
133        }
134
135        buf[0] = MESH_MAGIC;
136        buf[1] = self.src;
137        buf[2] = self.dst;
138        buf[3] = (self.seq >> 8) as u8;
139        buf[4] = (self.seq & 0xFF) as u8;
140        // Pack TTL (4 bits) and hop_count (4 bits) together
141        buf[5] = (self.ttl & 0x0F) | ((self.hop_count & 0x0F) << 4);
142        buf[6] = self.flags.bits();
143
144        Ok(MESH_HEADER_SIZE)
145    }
146
147    /// Decode header from buffer
148    pub fn decode(buf: &[u8]) -> Result<Self> {
149        if buf.len() < MESH_HEADER_SIZE {
150            return Err(Error::BufferTooSmall);
151        }
152
153        if buf[0] != MESH_MAGIC {
154            return Err(Error::InvalidData);
155        }
156
157        let src = buf[1];
158        let dst = buf[2];
159        let seq = ((buf[3] as u16) << 8) | (buf[4] as u16);
160        let ttl = buf[5] & 0x0F;
161        let hop_count = (buf[5] >> 4) & 0x0F;
162        let flags = MeshFlags::from_bits(buf[6]);
163
164        Ok(Self {
165            src,
166            dst,
167            seq,
168            ttl,
169            flags,
170            hop_count,
171        })
172    }
173
174    /// Get unique message ID for duplicate detection
175    pub fn message_id(&self) -> u32 {
176        ((self.src as u32) << 16) | (self.seq as u32)
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use super::*;
183
184    #[test]
185    fn test_header_encode_decode() {
186        let header = MeshHeader {
187            src: 1,
188            dst: 2,
189            seq: 0x1234,
190            ttl: 5,
191            flags: MeshFlags::empty(),
192            hop_count: 2,
193        };
194
195        let mut buf = [0u8; 16];
196        let len = header.encode(&mut buf).unwrap();
197        assert_eq!(len, MESH_HEADER_SIZE);
198
199        let decoded = MeshHeader::decode(&buf).unwrap();
200        assert_eq!(decoded.src, 1);
201        assert_eq!(decoded.dst, 2);
202        assert_eq!(decoded.seq, 0x1234);
203        assert_eq!(decoded.ttl, 5);
204        assert_eq!(decoded.hop_count, 2);
205    }
206
207    #[test]
208    fn test_header_broadcast() {
209        let header = MeshHeader::broadcast(5, 100, 3);
210        assert!(header.is_broadcast());
211        assert_eq!(header.dst, 0xFF);
212        assert_eq!(header.src, 5);
213        assert_eq!(header.seq, 100);
214        assert_eq!(header.ttl, 3);
215    }
216
217    #[test]
218    fn test_header_for_relay() {
219        let header = MeshHeader::new(1, 2, 100, 3);
220        assert_eq!(header.hop_count, 0);
221
222        let relayed = header.for_relay().unwrap();
223        assert_eq!(relayed.ttl, 2);
224        assert_eq!(relayed.hop_count, 1);
225
226        // TTL 0 should not relay
227        let last_hop = MeshHeader::new(1, 2, 100, 0);
228        assert!(last_hop.for_relay().is_none());
229    }
230
231    #[test]
232    fn test_header_buffer_too_small() {
233        let header = MeshHeader::new(1, 2, 100, 3);
234        let mut buf = [0u8; 4];
235        assert!(header.encode(&mut buf).is_err());
236    }
237
238    #[test]
239    fn test_header_invalid_magic() {
240        let buf = [0x00, 1, 2, 0, 100, 3, 0];
241        assert!(MeshHeader::decode(&buf).is_err());
242    }
243
244    #[test]
245    fn test_flags() {
246        let mut flags = MeshFlags::empty();
247        assert!(!flags.contains(MeshFlags::ACK_REQUIRED));
248
249        flags.set(MeshFlags::ACK_REQUIRED);
250        assert!(flags.contains(MeshFlags::ACK_REQUIRED));
251
252        flags.set(MeshFlags::BEACON);
253        assert!(flags.contains(MeshFlags::ACK_REQUIRED));
254        assert!(flags.contains(MeshFlags::BEACON));
255
256        flags.clear(MeshFlags::ACK_REQUIRED);
257        assert!(!flags.contains(MeshFlags::ACK_REQUIRED));
258        assert!(flags.contains(MeshFlags::BEACON));
259    }
260
261    #[test]
262    fn test_message_id() {
263        let h1 = MeshHeader::new(1, 2, 100, 3);
264        let h2 = MeshHeader::new(1, 2, 100, 3);
265        let h3 = MeshHeader::new(1, 2, 101, 3);
266        let h4 = MeshHeader::new(2, 2, 100, 3);
267
268        assert_eq!(h1.message_id(), h2.message_id());
269        assert_ne!(h1.message_id(), h3.message_id());
270        assert_ne!(h1.message_id(), h4.message_id());
271    }
272}