mqtt_protocol_core/mqtt/packet/
mqtt_binary.rs1use crate::mqtt::result_code::MqttError;
23use alloc::vec::Vec;
24use core::convert::TryFrom;
25use serde::{Serialize, Serializer};
26#[cfg(feature = "std")]
27use std::io::IoSlice;
28
29#[cfg(feature = "sso-lv20")]
31const SSO_BUFFER_SIZE: usize = 48; #[cfg(all(
33 not(feature = "sso-lv20"),
34 any(feature = "sso-lv10", feature = "sso-min-64bit")
35))]
36const SSO_BUFFER_SIZE: usize = 24; #[cfg(all(
38 not(any(feature = "sso-lv20", feature = "sso-lv10", feature = "sso-min-64bit")),
39 feature = "sso-min-32bit"
40))]
41const SSO_BUFFER_SIZE: usize = 12; #[cfg(not(any(
43 feature = "sso-min-32bit",
44 feature = "sso-min-64bit",
45 feature = "sso-lv10",
46 feature = "sso-lv20"
47)))]
48#[allow(dead_code)]
49const SSO_BUFFER_SIZE: usize = 0; #[cfg(any(
53 feature = "sso-min-32bit",
54 feature = "sso-min-64bit",
55 feature = "sso-lv10",
56 feature = "sso-lv20"
57))]
58const SSO_DATA_THRESHOLD: usize = SSO_BUFFER_SIZE - 2;
59
60#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
81#[allow(clippy::large_enum_variant)]
82pub enum MqttBinary {
83 #[cfg(any(
84 feature = "sso-min-32bit",
85 feature = "sso-min-64bit",
86 feature = "sso-lv10",
87 feature = "sso-lv20"
88 ))]
89 Small([u8; SSO_BUFFER_SIZE]), Large(Vec<u8>),
91}
92
93impl MqttBinary {
94 pub fn new(data: impl AsRef<[u8]>) -> Result<Self, MqttError> {
96 let data_ref = data.as_ref();
97 if data_ref.len() > 65535 {
98 return Err(MqttError::MalformedPacket);
99 }
100
101 #[cfg(any(
103 feature = "sso-min-32bit",
104 feature = "sso-min-64bit",
105 feature = "sso-lv10",
106 feature = "sso-lv20"
107 ))]
108 if data_ref.len() <= SSO_DATA_THRESHOLD {
109 let mut buffer = [0u8; SSO_BUFFER_SIZE];
110 let len = data_ref.len() as u16;
111 buffer[0] = (len >> 8) as u8;
112 buffer[1] = len as u8;
113 buffer[2..2 + data_ref.len()].copy_from_slice(data_ref);
114 return Ok(MqttBinary::Small(buffer));
115 }
116
117 let len = data_ref.len() as u16;
119 let mut encoded = Vec::with_capacity(2 + data_ref.len());
120 encoded.push((len >> 8) as u8);
121 encoded.push(len as u8);
122 encoded.extend_from_slice(data_ref);
123 Ok(MqttBinary::Large(encoded))
124 }
125
126 pub fn as_bytes(&self) -> &[u8] {
128 match self {
129 #[cfg(any(
130 feature = "sso-min-32bit",
131 feature = "sso-min-64bit",
132 feature = "sso-lv10",
133 feature = "sso-lv20"
134 ))]
135 MqttBinary::Small(buffer) => {
136 let len = ((buffer[0] as usize) << 8) | (buffer[1] as usize);
137 &buffer[..2 + len]
138 }
139 MqttBinary::Large(encoded) => encoded,
140 }
141 }
142
143 pub fn as_slice(&self) -> &[u8] {
145 match self {
146 #[cfg(any(
147 feature = "sso-min-32bit",
148 feature = "sso-min-64bit",
149 feature = "sso-lv10",
150 feature = "sso-lv20"
151 ))]
152 MqttBinary::Small(buffer) => {
153 let len = ((buffer[0] as usize) << 8) | (buffer[1] as usize);
154 &buffer[2..2 + len]
155 }
156 MqttBinary::Large(encoded) => &encoded[2..],
157 }
158 }
159
160 pub fn len(&self) -> usize {
162 match self {
163 #[cfg(any(
164 feature = "sso-min-32bit",
165 feature = "sso-min-64bit",
166 feature = "sso-lv10",
167 feature = "sso-lv20"
168 ))]
169 MqttBinary::Small(buffer) => ((buffer[0] as usize) << 8) | (buffer[1] as usize),
170 MqttBinary::Large(encoded) => encoded.len() - 2,
171 }
172 }
173
174 pub fn is_empty(&self) -> bool {
176 self.len() == 0
177 }
178
179 pub fn size(&self) -> usize {
181 match self {
182 #[cfg(any(
183 feature = "sso-min-32bit",
184 feature = "sso-min-64bit",
185 feature = "sso-lv10",
186 feature = "sso-lv20"
187 ))]
188 MqttBinary::Small(buffer) => {
189 let len = ((buffer[0] as usize) << 8) | (buffer[1] as usize);
190 2 + len
191 }
192 MqttBinary::Large(encoded) => encoded.len(),
193 }
194 }
195
196 #[cfg(feature = "std")]
198 pub fn to_buffers(&self) -> Vec<IoSlice<'_>> {
199 match self {
200 MqttBinary::Large(encoded) => vec![IoSlice::new(encoded)],
201 #[cfg(any(
202 feature = "sso-min-32bit",
203 feature = "sso-min-64bit",
204 feature = "sso-lv10",
205 feature = "sso-lv20"
206 ))]
207 MqttBinary::Small(buffer) => {
208 let len = ((buffer[0] as usize) << 8) | (buffer[1] as usize);
209 vec![IoSlice::new(&buffer[..2 + len])]
210 }
211 }
212 }
213
214 pub fn to_continuous_buffer(&self) -> Vec<u8> {
216 self.as_bytes().to_vec()
217 }
218
219 pub fn decode(data: &[u8]) -> Result<(Self, usize), MqttError> {
221 if data.len() < 2 {
222 return Err(MqttError::MalformedPacket);
223 }
224
225 let data_len = ((data[0] as usize) << 8) | (data[1] as usize);
226 if data.len() < 2 + data_len {
227 return Err(MqttError::MalformedPacket);
228 }
229
230 #[cfg(any(
232 feature = "sso-min-32bit",
233 feature = "sso-min-64bit",
234 feature = "sso-lv10",
235 feature = "sso-lv20"
236 ))]
237 if data_len <= SSO_DATA_THRESHOLD {
238 let payload = &data[2..2 + data_len];
239 let mut buffer = [0u8; SSO_BUFFER_SIZE];
240 buffer[0] = data[0];
241 buffer[1] = data[1];
242 buffer[2..2 + data_len].copy_from_slice(payload);
243 return Ok((MqttBinary::Small(buffer), 2 + data_len));
244 }
245
246 let mut encoded = Vec::with_capacity(2 + data_len);
248 encoded.extend_from_slice(&data[0..2 + data_len]);
249 Ok((MqttBinary::Large(encoded), 2 + data_len))
250 }
251}
252
253impl AsRef<[u8]> for MqttBinary {
255 fn as_ref(&self) -> &[u8] {
256 self.as_slice()
257 }
258}
259
260impl core::ops::Deref for MqttBinary {
262 type Target = [u8];
263
264 fn deref(&self) -> &Self::Target {
265 self.as_slice()
266 }
267}
268
269impl Serialize for MqttBinary {
271 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
272 where
273 S: Serializer,
274 {
275 serializer.serialize_bytes(self.as_slice())
276 }
277}
278
279impl TryFrom<&str> for MqttBinary {
281 type Error = MqttError;
282
283 fn try_from(s: &str) -> Result<Self, Self::Error> {
284 Self::new(s.as_bytes())
285 }
286}
287
288impl Default for MqttBinary {
290 fn default() -> Self {
291 Self::new(b"").unwrap()
292 }
293}
294
295impl core::fmt::Debug for MqttBinary {
296 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
297 f.debug_struct("MqttBinary")
298 .field("data", &self.as_slice())
299 .finish()
300 }
301}
302
303#[cfg(test)]
304mod tests {
305 use super::*;
306
307 #[test]
308 fn test_empty_binary() {
309 let binary = MqttBinary::new(b"").unwrap();
310 assert_eq!(binary.len(), 0);
311 assert!(binary.is_empty());
312 assert_eq!(binary.size(), 2);
313 assert_eq!(binary.as_slice(), b"");
314 assert_eq!(binary.as_bytes(), vec![0x00, 0x00]);
315 }
316
317 #[test]
318 fn test_small_binary() {
319 let data = b"hello";
320 let binary = MqttBinary::new(data).unwrap();
321 assert_eq!(binary.len(), 5);
322 assert!(!binary.is_empty());
323 assert_eq!(binary.size(), 7);
324 assert_eq!(binary.as_slice(), data);
325 assert_eq!(
326 binary.as_bytes(),
327 vec![0x00, 0x05, b'h', b'e', b'l', b'l', b'o']
328 );
329 }
330
331 #[test]
332 fn test_decode_roundtrip() {
333 let original_data = b"test data";
334 let binary = MqttBinary::new(original_data).unwrap();
335 let encoded = binary.as_bytes();
336
337 let (decoded_binary, consumed) = MqttBinary::decode(&encoded).unwrap();
338 assert_eq!(consumed, encoded.len());
339 assert_eq!(decoded_binary.as_slice(), original_data);
340 assert_eq!(decoded_binary.len(), original_data.len());
341 }
342
343 #[test]
344 fn test_max_size() {
345 let data = vec![0u8; 65535];
346 let binary = MqttBinary::new(&data).unwrap();
347 assert_eq!(binary.len(), 65535);
348 assert_eq!(binary.size(), 65537);
349 }
350
351 #[test]
352 fn test_oversized_data() {
353 let data = vec![0u8; 65536];
354 let result = MqttBinary::new(&data);
355 assert!(result.is_err());
356 assert_eq!(result.unwrap_err(), MqttError::MalformedPacket);
357 }
358
359 #[test]
360 fn test_decode_malformed() {
361 assert!(MqttBinary::decode(&[0x00]).is_err());
363
364 assert!(MqttBinary::decode(&[0x00, 0x05, b'h', b'i']).is_err());
366 }
367
368 #[test]
369 fn test_continuous_buffer() {
370 let data = b"continuous";
371 let binary = MqttBinary::new(data).unwrap();
372 let buffer = binary.to_continuous_buffer();
373 assert_eq!(buffer[0], 0x00);
374 assert_eq!(buffer[1], 0x0A); assert_eq!(&buffer[2..], data);
376 }
377
378 #[cfg(feature = "std")]
379 #[test]
380 fn test_to_buffers() {
381 let data = b"buffer test";
382 let binary = MqttBinary::new(data).unwrap();
383 let buffers = binary.to_buffers();
384
385 assert_eq!(buffers.len(), 1);
387
388 let buffer_data: &[u8] = &buffers[0];
390 assert_eq!(buffer_data, binary.as_bytes());
391 }
392
393 #[test]
394 fn test_trait_implementations() {
395 let binary = MqttBinary::new(b"trait test").unwrap();
396
397 let slice: &[u8] = binary.as_ref();
399 assert_eq!(slice, b"trait test");
400
401 assert_eq!(&*binary, b"trait test");
403
404 let binary2 = MqttBinary::new(b"trait test").unwrap();
406 assert_eq!(binary, binary2);
407
408 let cloned = binary.clone();
410 assert_eq!(binary, cloned);
411
412 let default = MqttBinary::default();
414 assert!(default.is_empty());
415 }
416
417 #[test]
418 fn test_from_conversions() {
419 let binary = MqttBinary::try_from("string test").unwrap();
421 assert_eq!(binary.as_slice(), b"string test");
422
423 let long_str = "x".repeat(65536);
425 assert!(MqttBinary::try_from(long_str.as_str()).is_err());
426 }
427
428 #[cfg(any(
430 feature = "sso-min-32bit",
431 feature = "sso-min-64bit",
432 feature = "sso-lv10",
433 feature = "sso-lv20"
434 ))]
435 #[test]
436 fn test_sso_features() {
437 let small_data = b"small";
439 let binary = MqttBinary::new(small_data).unwrap();
440
441 #[cfg(any(
442 feature = "sso-min-32bit",
443 feature = "sso-min-64bit",
444 feature = "sso-lv10",
445 feature = "sso-lv20"
446 ))]
447 {
448 if let MqttBinary::Small(buffer) = binary {
449 let len = ((buffer[0] as usize) << 8) | (buffer[1] as usize);
450 assert_eq!(len, 5); } else {
452 panic!("Expected Small variant for small data with SSO enabled");
453 }
454 }
455
456 #[cfg(not(any(
457 feature = "sso-min-32bit",
458 feature = "sso-min-64bit",
459 feature = "sso-lv10",
460 feature = "sso-lv20"
461 )))]
462 assert!(matches!(binary, MqttBinary::Large(_)));
463
464 let very_large_data = b"This is a very long binary data that exceeds even the largest SSO buffer size to ensure it's always stored in the Large variant for consistent testing";
466 let binary = MqttBinary::new(very_large_data).unwrap();
467 assert!(matches!(binary, MqttBinary::Large(_)));
468 }
469}