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<const N: usize> TryFrom<&[u8; N]> for MqttBinary {
293 type Error = MqttError;
294
295 fn try_from(arr: &[u8; N]) -> Result<Self, Self::Error> {
296 Self::new(&arr[..])
297 }
298}
299
300impl TryFrom<Vec<u8>> for MqttBinary {
304 type Error = MqttError;
305
306 fn try_from(v: Vec<u8>) -> Result<Self, Self::Error> {
307 Self::new(&v)
308 }
309}
310
311impl TryFrom<&Vec<u8>> for MqttBinary {
315 type Error = MqttError;
316
317 fn try_from(v: &Vec<u8>) -> Result<Self, Self::Error> {
318 Self::new(v.as_slice())
319 }
320}
321
322impl Default for MqttBinary {
324 fn default() -> Self {
325 Self::new(b"").unwrap()
326 }
327}
328
329impl core::fmt::Debug for MqttBinary {
330 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
331 f.debug_struct("MqttBinary")
332 .field("data", &self.as_slice())
333 .finish()
334 }
335}
336
337#[cfg(test)]
338mod tests {
339 use super::*;
340
341 #[test]
342 fn test_empty_binary() {
343 let binary = MqttBinary::new(b"").unwrap();
344 assert_eq!(binary.len(), 0);
345 assert!(binary.is_empty());
346 assert_eq!(binary.size(), 2);
347 assert_eq!(binary.as_slice(), b"");
348 assert_eq!(binary.as_bytes(), vec![0x00, 0x00]);
349 }
350
351 #[test]
352 fn test_small_binary() {
353 let data = b"hello";
354 let binary = MqttBinary::new(data).unwrap();
355 assert_eq!(binary.len(), 5);
356 assert!(!binary.is_empty());
357 assert_eq!(binary.size(), 7);
358 assert_eq!(binary.as_slice(), data);
359 assert_eq!(
360 binary.as_bytes(),
361 vec![0x00, 0x05, b'h', b'e', b'l', b'l', b'o']
362 );
363 }
364
365 #[test]
366 fn test_decode_roundtrip() {
367 let original_data = b"test data";
368 let binary = MqttBinary::new(original_data).unwrap();
369 let encoded = binary.as_bytes();
370
371 let (decoded_binary, consumed) = MqttBinary::decode(&encoded).unwrap();
372 assert_eq!(consumed, encoded.len());
373 assert_eq!(decoded_binary.as_slice(), original_data);
374 assert_eq!(decoded_binary.len(), original_data.len());
375 }
376
377 #[test]
378 fn test_max_size() {
379 let data = vec![0u8; 65535];
380 let binary = MqttBinary::new(&data).unwrap();
381 assert_eq!(binary.len(), 65535);
382 assert_eq!(binary.size(), 65537);
383 }
384
385 #[test]
386 fn test_oversized_data() {
387 let data = vec![0u8; 65536];
388 let result = MqttBinary::new(&data);
389 assert!(result.is_err());
390 assert_eq!(result.unwrap_err(), MqttError::MalformedPacket);
391 }
392
393 #[test]
394 fn test_decode_malformed() {
395 assert!(MqttBinary::decode(&[0x00]).is_err());
397
398 assert!(MqttBinary::decode(&[0x00, 0x05, b'h', b'i']).is_err());
400 }
401
402 #[test]
403 fn test_continuous_buffer() {
404 let data = b"continuous";
405 let binary = MqttBinary::new(data).unwrap();
406 let buffer = binary.to_continuous_buffer();
407 assert_eq!(buffer[0], 0x00);
408 assert_eq!(buffer[1], 0x0A); assert_eq!(&buffer[2..], data);
410 }
411
412 #[cfg(feature = "std")]
413 #[test]
414 fn test_to_buffers() {
415 let data = b"buffer test";
416 let binary = MqttBinary::new(data).unwrap();
417 let buffers = binary.to_buffers();
418
419 assert_eq!(buffers.len(), 1);
421
422 let buffer_data: &[u8] = &buffers[0];
424 assert_eq!(buffer_data, binary.as_bytes());
425 }
426
427 #[test]
428 fn test_trait_implementations() {
429 let binary = MqttBinary::new(b"trait test").unwrap();
430
431 let slice: &[u8] = binary.as_ref();
433 assert_eq!(slice, b"trait test");
434
435 assert_eq!(&*binary, b"trait test");
437
438 let binary2 = MqttBinary::new(b"trait test").unwrap();
440 assert_eq!(binary, binary2);
441
442 let cloned = binary.clone();
444 assert_eq!(binary, cloned);
445
446 let default = MqttBinary::default();
448 assert!(default.is_empty());
449 }
450
451 #[test]
452 fn test_from_conversions() {
453 let binary = MqttBinary::try_from("string test").unwrap();
455 assert_eq!(binary.as_slice(), b"string test");
456
457 let long_str = "x".repeat(65536);
459 assert!(MqttBinary::try_from(long_str.as_str()).is_err());
460 }
461
462 #[cfg(any(
464 feature = "sso-min-32bit",
465 feature = "sso-min-64bit",
466 feature = "sso-lv10",
467 feature = "sso-lv20"
468 ))]
469 #[test]
470 fn test_sso_features() {
471 let small_data = b"small";
473 let binary = MqttBinary::new(small_data).unwrap();
474
475 #[cfg(any(
476 feature = "sso-min-32bit",
477 feature = "sso-min-64bit",
478 feature = "sso-lv10",
479 feature = "sso-lv20"
480 ))]
481 {
482 if let MqttBinary::Small(buffer) = binary {
483 let len = ((buffer[0] as usize) << 8) | (buffer[1] as usize);
484 assert_eq!(len, 5); } else {
486 panic!("Expected Small variant for small data with SSO enabled");
487 }
488 }
489
490 #[cfg(not(any(
491 feature = "sso-min-32bit",
492 feature = "sso-min-64bit",
493 feature = "sso-lv10",
494 feature = "sso-lv20"
495 )))]
496 assert!(matches!(binary, MqttBinary::Large(_)));
497
498 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";
500 let binary = MqttBinary::new(very_large_data).unwrap();
501 assert!(matches!(binary, MqttBinary::Large(_)));
502 }
503}