1use bytes::Bytes;
2
3use crate::msg::{MsgId, Slot};
4
5const STD: u8 = 0;
6const NO_ACK: u8 = 1;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum Trailer {
11 Std {
12 slot: Slot,
14 id: MsgId,
16 },
17 NoAck {
18 slot: Slot,
20 },
21 Unknown,
22}
23
24impl Trailer {
25 pub const MAX_SIZE: usize = u8::MAX as usize;
26
27 pub(crate) fn from_bytes(bytes: &mut Bytes) -> Option<Self> {
28 if bytes.len() < 2 {
29 return None;
30 }
31 let len = bytes.len();
32 let trailer_len = bytes[len - 1];
33 let trailer_typ = bytes[len - 2];
34 match trailer_typ {
35 STD => {
36 if trailer_len != 16 {
37 return None;
38 }
39 let id = u64::from_be_bytes(bytes[len - 10..len - 2].try_into().ok()?);
40 let slot = u64::from_be_bytes(bytes[len - 18..len - 10].try_into().ok()?);
41 bytes.truncate(len - 18);
42 Some(Self::Std {
43 slot: Slot(slot),
44 id: MsgId(id),
45 })
46 },
47 NO_ACK => {
48 if trailer_len != 8 {
49 return None;
50 }
51 let slot = u64::from_be_bytes(bytes[len - 10..len - 2].try_into().ok()?);
52 bytes.truncate(len - 10);
53 Some(Self::NoAck { slot: Slot(slot) })
54 },
55 _ => {
56 let trailer_len = 2 + usize::from(trailer_len);
57 if trailer_len > len {
58 return None;
59 }
60 bytes.truncate(len - trailer_len);
61 Some(Self::Unknown)
62 },
63 }
64 }
65
66 pub(crate) fn to_bytes(self) -> TrailerBytes {
67 match self {
68 Self::Std { slot, id } => {
69 let mut buf = [0; 18];
70 buf[..8].copy_from_slice(&slot.0.to_be_bytes()[..]);
71 buf[8..16].copy_from_slice(&id.0.to_be_bytes()[..]);
72 buf[16] = STD;
73 buf[17] = 16;
74 TrailerBytes::Std(buf)
75 },
76 Self::NoAck { slot } => {
77 let mut buf = [0; 10];
78 buf[..8].copy_from_slice(&slot.0.to_be_bytes()[..]);
79 buf[8] = NO_ACK;
80 buf[9] = 8;
81 TrailerBytes::NoAck(buf)
82 },
83 Self::Unknown => {
84 unreachable!("nothing constructs an unknown trailer")
85 },
86 }
87 }
88}
89
90#[derive(Debug, Clone, Copy)]
91pub(crate) enum TrailerBytes {
92 Std([u8; 18]),
93 NoAck([u8; 10]),
94}
95
96impl AsRef<[u8]> for TrailerBytes {
97 fn as_ref(&self) -> &[u8] {
98 match self {
99 Self::Std(a) => &a[..],
100 Self::NoAck(a) => &a[..],
101 }
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use bytes::Bytes;
108 use quickcheck::{Arbitrary, Gen, quickcheck};
109
110 use super::Trailer;
111 use crate::msg::{MsgId, Slot};
112
113 impl Arbitrary for Trailer {
114 fn arbitrary(g: &mut Gen) -> Self {
115 match bool::arbitrary(g) {
116 true => Self::Std {
117 slot: Slot(u64::arbitrary(g)),
118 id: MsgId(u64::arbitrary(g)),
119 },
120 false => Self::NoAck {
121 slot: Slot(u64::arbitrary(g)),
122 },
123 }
124 }
125 }
126
127 quickcheck! {
128 fn prop_to_bytes_from_bytes_id(t1: Trailer) -> bool {
129 let mut b = Bytes::copy_from_slice(t1.to_bytes().as_ref());
130 let t2 = Trailer::from_bytes(&mut b);
131 Some(t1) == t2
132 }
133 }
134}