1use crate::error::XrceError;
26use crate::serial_number::SerialNumber16;
27
28pub const CLIENT_KEY_LEN: usize = 4;
30
31pub const SESSION_ID_NONE_WITH_CLIENT_KEY: u8 = 0x00;
34
35pub const SESSION_ID_NONE_WITHOUT_CLIENT_KEY: u8 = 0x80;
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
42pub struct SessionId(pub u8);
43
44impl SessionId {
45 #[must_use]
47 pub fn carries_client_key(self) -> bool {
48 self.0 <= 127
49 }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
58pub struct StreamId(pub u8);
59
60impl StreamId {
61 pub const NONE: Self = Self(0);
63 pub const BUILTIN_BEST_EFFORT: Self = Self(0x01);
65 pub const BUILTIN_RELIABLE: Self = Self(0x80);
67
68 #[must_use]
70 pub fn is_reliable(self) -> bool {
71 self.0 >= 128
72 }
73
74 #[must_use]
76 pub fn is_best_effort(self) -> bool {
77 self.0 >= 1 && self.0 <= 127
78 }
79
80 #[must_use]
82 pub fn is_none(self) -> bool {
83 self.0 == 0
84 }
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
89pub struct ClientKey(pub [u8; CLIENT_KEY_LEN]);
90
91impl ClientKey {
92 pub const INVALID: Self = Self([0; CLIENT_KEY_LEN]);
94
95 #[must_use]
97 pub fn as_bytes(&self) -> &[u8; CLIENT_KEY_LEN] {
98 &self.0
99 }
100}
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
104pub struct MessageHeader {
105 pub session_id: SessionId,
107 pub stream_id: StreamId,
109 pub sequence_nr: SerialNumber16,
111 pub client_key: Option<ClientKey>,
113}
114
115impl MessageHeader {
116 pub const WIRE_SIZE_NO_KEY: usize = 4;
118 pub const WIRE_SIZE_WITH_KEY: usize = 4 + CLIENT_KEY_LEN;
120
121 pub fn without_client_key(
127 session_id: SessionId,
128 stream_id: StreamId,
129 sequence_nr: SerialNumber16,
130 ) -> Result<Self, XrceError> {
131 if session_id.carries_client_key() {
132 return Err(XrceError::ValueOutOfRange {
133 message: "session_id <=127 requires client_key",
134 });
135 }
136 Ok(Self {
137 session_id,
138 stream_id,
139 sequence_nr,
140 client_key: None,
141 })
142 }
143
144 pub fn with_client_key(
149 session_id: SessionId,
150 stream_id: StreamId,
151 sequence_nr: SerialNumber16,
152 client_key: ClientKey,
153 ) -> Result<Self, XrceError> {
154 if !session_id.carries_client_key() {
155 return Err(XrceError::ValueOutOfRange {
156 message: "session_id >=128 must not carry client_key",
157 });
158 }
159 Ok(Self {
160 session_id,
161 stream_id,
162 sequence_nr,
163 client_key: Some(client_key),
164 })
165 }
166
167 #[must_use]
169 pub fn wire_size(&self) -> usize {
170 if self.client_key.is_some() {
171 Self::WIRE_SIZE_WITH_KEY
172 } else {
173 Self::WIRE_SIZE_NO_KEY
174 }
175 }
176
177 pub fn write_to(&self, out: &mut [u8]) -> Result<usize, XrceError> {
183 let needed = self.wire_size();
184 if out.len() < needed {
185 return Err(XrceError::WriteOverflow {
186 needed,
187 available: out.len(),
188 });
189 }
190 out[0] = self.session_id.0;
191 out[1] = self.stream_id.0;
192 let seq = self.sequence_nr.raw().to_le_bytes();
193 out[2] = seq[0];
194 out[3] = seq[1];
195 if let Some(ck) = self.client_key {
196 out[4..8].copy_from_slice(&ck.0);
197 }
198 Ok(needed)
199 }
200
201 pub fn read_from(bytes: &[u8]) -> Result<(Self, usize), XrceError> {
208 if bytes.len() < Self::WIRE_SIZE_NO_KEY {
209 return Err(XrceError::UnexpectedEof {
210 needed: Self::WIRE_SIZE_NO_KEY,
211 offset: 0,
212 });
213 }
214 let session_id = SessionId(bytes[0]);
215 let stream_id = StreamId(bytes[1]);
216 let mut seq_buf = [0u8; 2];
217 seq_buf.copy_from_slice(&bytes[2..4]);
218 let sequence_nr = SerialNumber16::new(u16::from_le_bytes(seq_buf));
219
220 if session_id.carries_client_key() {
221 if bytes.len() < Self::WIRE_SIZE_WITH_KEY {
222 return Err(XrceError::UnexpectedEof {
223 needed: Self::WIRE_SIZE_WITH_KEY,
224 offset: bytes.len(),
225 });
226 }
227 let mut ck = [0u8; CLIENT_KEY_LEN];
228 ck.copy_from_slice(&bytes[4..8]);
229 Ok((
230 Self {
231 session_id,
232 stream_id,
233 sequence_nr,
234 client_key: Some(ClientKey(ck)),
235 },
236 Self::WIRE_SIZE_WITH_KEY,
237 ))
238 } else {
239 Ok((
240 Self {
241 session_id,
242 stream_id,
243 sequence_nr,
244 client_key: None,
245 },
246 Self::WIRE_SIZE_NO_KEY,
247 ))
248 }
249 }
250}
251
252#[cfg(test)]
253mod tests {
254 #![allow(clippy::expect_used, clippy::unwrap_used)]
255 use super::*;
256
257 #[test]
258 fn session_id_127_carries_client_key() {
259 assert!(SessionId(0).carries_client_key());
260 assert!(SessionId(127).carries_client_key());
261 assert!(!SessionId(128).carries_client_key());
262 assert!(!SessionId(255).carries_client_key());
263 }
264
265 #[test]
266 fn stream_id_classification() {
267 assert!(StreamId::NONE.is_none());
268 assert!(StreamId::BUILTIN_BEST_EFFORT.is_best_effort());
269 assert!(StreamId::BUILTIN_RELIABLE.is_reliable());
270 assert!(StreamId(127).is_best_effort());
271 assert!(StreamId(128).is_reliable());
272 assert!(StreamId(255).is_reliable());
273 }
274
275 #[test]
276 fn header_no_client_key_wire_size_4() {
277 let h = MessageHeader::without_client_key(
278 SessionId(SESSION_ID_NONE_WITHOUT_CLIENT_KEY),
279 StreamId::NONE,
280 SerialNumber16::new(0),
281 )
282 .unwrap();
283 assert_eq!(h.wire_size(), 4);
284 }
285
286 #[test]
287 fn header_with_client_key_wire_size_8() {
288 let h = MessageHeader::with_client_key(
289 SessionId(0),
290 StreamId::BUILTIN_RELIABLE,
291 SerialNumber16::new(0),
292 ClientKey([1, 2, 3, 4]),
293 )
294 .unwrap();
295 assert_eq!(h.wire_size(), 8);
296 }
297
298 #[test]
299 fn header_constructor_rejects_inconsistent_no_key() {
300 let res = MessageHeader::without_client_key(
302 SessionId(10),
303 StreamId::NONE,
304 SerialNumber16::new(0),
305 );
306 assert!(res.is_err());
307 }
308
309 #[test]
310 fn header_constructor_rejects_inconsistent_with_key() {
311 let res = MessageHeader::with_client_key(
313 SessionId(200),
314 StreamId::NONE,
315 SerialNumber16::new(0),
316 ClientKey::INVALID,
317 );
318 assert!(res.is_err());
319 }
320
321 #[test]
322 fn header_layout_bytes_no_key() {
323 let h = MessageHeader::without_client_key(
324 SessionId(0x80),
325 StreamId(0x42),
326 SerialNumber16::new(0x1234),
327 )
328 .unwrap();
329 let mut buf = [0u8; 4];
330 let n = h.write_to(&mut buf).unwrap();
331 assert_eq!(n, 4);
332 assert_eq!(buf[0], 0x80);
333 assert_eq!(buf[1], 0x42);
334 assert_eq!(buf[2], 0x34);
336 assert_eq!(buf[3], 0x12);
337 }
338
339 #[test]
340 fn header_layout_bytes_with_key() {
341 let h = MessageHeader::with_client_key(
342 SessionId(0x10),
343 StreamId(0x80),
344 SerialNumber16::new(0xABCD),
345 ClientKey([0xAA, 0xBB, 0xCC, 0xDD]),
346 )
347 .unwrap();
348 let mut buf = [0u8; 8];
349 h.write_to(&mut buf).unwrap();
350 assert_eq!(buf[0], 0x10);
351 assert_eq!(buf[1], 0x80);
352 assert_eq!(buf[2], 0xCD);
353 assert_eq!(buf[3], 0xAB);
354 assert_eq!(&buf[4..8], &[0xAA, 0xBB, 0xCC, 0xDD]);
355 }
356
357 #[test]
358 fn header_roundtrip_no_key() {
359 let h = MessageHeader::without_client_key(
360 SessionId(SESSION_ID_NONE_WITHOUT_CLIENT_KEY),
361 StreamId::BUILTIN_BEST_EFFORT,
362 SerialNumber16::new(7),
363 )
364 .unwrap();
365 let mut buf = [0u8; 4];
366 h.write_to(&mut buf).unwrap();
367 let (decoded, n) = MessageHeader::read_from(&buf).unwrap();
368 assert_eq!(n, 4);
369 assert_eq!(decoded, h);
370 }
371
372 #[test]
373 fn header_roundtrip_with_key() {
374 let h = MessageHeader::with_client_key(
375 SessionId(0x55),
376 StreamId::BUILTIN_RELIABLE,
377 SerialNumber16::new(0xFFFE),
378 ClientKey([9, 8, 7, 6]),
379 )
380 .unwrap();
381 let mut buf = [0u8; 8];
382 h.write_to(&mut buf).unwrap();
383 let (decoded, n) = MessageHeader::read_from(&buf).unwrap();
384 assert_eq!(n, 8);
385 assert_eq!(decoded, h);
386 }
387
388 #[test]
389 fn header_decode_truncated_no_key() {
390 let buf = [0x80u8, 0x01, 0x00]; let res = MessageHeader::read_from(&buf);
392 assert!(matches!(
393 res,
394 Err(XrceError::UnexpectedEof { needed: 4, .. })
395 ));
396 }
397
398 #[test]
399 fn header_decode_truncated_with_key() {
400 let buf = [0x00u8, 0x01, 0x00, 0x00, 0xAA]; let res = MessageHeader::read_from(&buf);
403 assert!(matches!(
404 res,
405 Err(XrceError::UnexpectedEof { needed: 8, .. })
406 ));
407 }
408
409 #[test]
410 fn header_write_overflow_when_buffer_too_small() {
411 let h = MessageHeader::with_client_key(
412 SessionId(0),
413 StreamId::NONE,
414 SerialNumber16::new(0),
415 ClientKey::INVALID,
416 )
417 .unwrap();
418 let mut buf = [0u8; 4]; let res = h.write_to(&mut buf);
420 assert!(matches!(
421 res,
422 Err(XrceError::WriteOverflow {
423 needed: 8,
424 available: 4
425 })
426 ));
427 }
428
429 #[test]
430 fn header_extra_trailing_bytes_are_ignored() {
431 let h = MessageHeader::without_client_key(
432 SessionId(0xFF),
433 StreamId(2),
434 SerialNumber16::new(42),
435 )
436 .unwrap();
437 let mut buf = [0u8; 16];
438 h.write_to(&mut buf).unwrap();
439 let (decoded, n) = MessageHeader::read_from(&buf).unwrap();
440 assert_eq!(decoded, h);
441 assert_eq!(n, 4);
442 }
443}