miltr_common/optneg/
mod.rs1mod capability;
4mod macros;
5mod protocol;
6
7use bytes::{Buf, BytesMut};
8use thiserror::Error;
9
10use crate::decoding::Parsable;
11use crate::encoding::Writable;
12use crate::error::STAGE_DECODING;
13use crate::{NotEnoughData, ProtocolError};
14
15pub use capability::Capability;
16pub use macros::{MacroStage, MacroStages};
17pub use protocol::Protocol;
18
19#[derive(Clone, PartialEq, Debug)]
21pub struct OptNeg {
22 pub version: u32,
24 pub capabilities: Capability,
26 pub protocol: Protocol,
28 pub macro_stages: MacroStages,
30}
31
32impl Default for OptNeg {
33 fn default() -> Self {
34 Self {
35 version: Self::VERSION,
36 capabilities: Capability::default(),
37 protocol: Protocol::default(),
38 macro_stages: MacroStages::default(),
39 }
40 }
41}
42
43#[derive(Debug, Error)]
46pub enum CompatibilityError {
47 #[error("Received version {received} which is not compatible with {supported}")]
49 UnsupportedVersion {
50 received: u32,
52 supported: u32,
54 },
55}
56
57impl OptNeg {
58 const VERSION: u32 = 6;
80
81 const DATA_SIZE: usize = 4 + 4 + 4;
82 const CODE: u8 = b'O';
83
84 pub fn merge_compatible(mut self, other: &Self) -> Result<Self, CompatibilityError> {
91 if self.version < other.version {
92 return Err(CompatibilityError::UnsupportedVersion {
93 received: other.version,
94 supported: self.version,
95 });
96 }
97
98 self.protocol = self
99 .protocol
100 .merge_regarding_version(self.version, other.protocol);
101
102 self.capabilities = self
103 .capabilities
104 .merge_regarding_version(self.version, other.capabilities);
105
106 Ok(self)
107 }
108
109 }
114
115impl Parsable for OptNeg {
116 const CODE: u8 = Self::CODE;
117
118 fn parse(mut buffer: BytesMut) -> Result<Self, ProtocolError> {
119 if buffer.len() != Self::DATA_SIZE {
120 return Err(NotEnoughData::new(
121 STAGE_DECODING,
122 "Option negotiation",
123 "not enough bits",
124 Self::DATA_SIZE,
125 buffer.len(),
126 buffer,
127 )
128 .into());
129 }
130
131 let mut version: [u8; 4] = [0; 4];
132 version.copy_from_slice(&buffer[0..4]);
133 let version = u32::from_be_bytes(version);
134
135 let mut capabilities: [u8; 4] = [0; 4];
136 capabilities.copy_from_slice(&buffer[4..8]);
137 let capabilities: Capability =
138 Capability::from_bits_retain(u32::from_be_bytes(capabilities));
139
140 let mut protocol: [u8; 4] = [0; 4];
141 protocol.copy_from_slice(&buffer[8..12]);
142 let protocol: Protocol = Protocol::from_bits_retain(u32::from_be_bytes(protocol));
143
144 buffer.advance(12);
145 Ok(Self {
146 version,
147 capabilities,
148 protocol,
149 macro_stages: MacroStages::default(),
151 })
152 }
153}
154
155impl Writable for OptNeg {
158 fn write(&self, buffer: &mut BytesMut) {
159 buffer.extend_from_slice(&self.version.to_be_bytes());
160 buffer.extend_from_slice(&self.capabilities.bits().to_be_bytes());
161 buffer.extend_from_slice(&self.protocol.bits().to_be_bytes());
162
163 self.macro_stages.write(buffer);
164 }
165
166 fn len(&self) -> usize {
167 Self::DATA_SIZE + self.macro_stages.len()
168 }
169
170 fn code(&self) -> u8 {
171 Self::CODE
172 }
173
174 fn is_empty(&self) -> bool {
175 self.len() == 0
176 }
177}
178
179#[cfg(test)]
180mod tests {
181
182 use super::*;
183 use pretty_assertions::assert_eq;
184
185 fn ver_caps_prot() -> ([u8; 4], [u8; 4], [u8; 4]) {
186 let version = [0u8, 0u8, 0u8, 6u8];
187 let capabilities = [0u8, 0u8, 0u8, 255u8];
188 let protocol = [0u8, 0u8, 0u8, 0u8];
189
190 (version, capabilities, protocol)
191 }
192
193 #[cfg(feature = "count-allocations")]
194 #[allow(clippy::type_complexity)] fn create_optneg_from_bytes() -> (BytesMut, ([u8; 4], [u8; 4], [u8; 4])) {
196 let mut buffer = BytesMut::new();
197
198 let (version, capabilities, protocol) = ver_caps_prot();
199
200 buffer.extend_from_slice(&version);
201 buffer.extend_from_slice(&capabilities);
202 buffer.extend_from_slice(&protocol);
203
204 (buffer, (version, capabilities, protocol))
205 }
206
207 #[cfg(feature = "count-allocations")]
208 #[test]
209 fn test_parse_optneg() {
210 use super::OptNeg;
211
212 let (buffer, _) = create_optneg_from_bytes();
213
214 let info = allocation_counter::measure(|| {
215 let res = OptNeg::parse(buffer);
216 allocation_counter::opt_out(|| {
217 println!("{res:?}");
218 assert!(res.is_ok());
219 });
220 });
221 println!("{}", &info.count_total);
222 assert_eq!(info.count_total, 0);
223 }
224
225 #[test]
226 fn test_write_optneg() {
227 let (version, capabilities, protocol) = ver_caps_prot();
229 let mut expected = Vec::new();
230 expected.extend_from_slice(&version);
231 expected.extend_from_slice(&capabilities);
232 expected.extend_from_slice(&protocol);
233
234 let mut buffer = BytesMut::new();
236 let optneg = OptNeg::default();
237 optneg.write(&mut buffer);
238
239 assert_eq!(optneg.len(), buffer.len());
241 assert_eq!(optneg.code(), b'O');
242 assert_eq!(expected, buffer.to_vec());
243 }
244}