1use crate::mqtt::Arc;
24use alloc::{string::String, vec::Vec};
25use serde::{Serialize, Serializer};
26
27#[cfg(feature = "sso-lv20")]
29const SSO_BUFFER_SIZE: usize = 255; #[cfg(all(not(feature = "sso-lv20"), feature = "sso-lv10"))]
31const SSO_BUFFER_SIZE: usize = 127; #[cfg(all(
33 not(any(feature = "sso-lv20", feature = "sso-lv10")),
34 feature = "sso-min-64bit"
35))]
36const SSO_BUFFER_SIZE: usize = 31; #[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 = 15; #[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))]
58type LengthType = u8;
59
60#[cfg(not(any(
61 feature = "sso-min-32bit",
62 feature = "sso-min-64bit",
63 feature = "sso-lv10",
64 feature = "sso-lv20"
65)))]
66#[allow(dead_code)]
67type LengthType = u8;
68
69#[derive(Clone)]
76#[allow(clippy::large_enum_variant)]
77pub enum ArcPayload {
78 #[cfg(any(
79 feature = "sso-min-32bit",
80 feature = "sso-min-64bit",
81 feature = "sso-lv10",
82 feature = "sso-lv20"
83 ))]
84 Small([u8; SSO_BUFFER_SIZE], LengthType), Large {
86 data: Arc<[u8]>,
87 start: usize,
88 length: usize,
89 },
90}
91
92impl PartialEq for ArcPayload {
93 fn eq(&self, other: &Self) -> bool {
94 self.as_slice() == other.as_slice()
95 }
96}
97
98impl Eq for ArcPayload {}
99
100impl Serialize for ArcPayload {
101 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
102 where
103 S: Serializer,
104 {
105 self.as_slice().serialize(serializer)
106 }
107}
108
109impl ArcPayload {
110 pub fn new(data: Arc<[u8]>, start: usize, length: usize) -> Self {
135 debug_assert!(start + length <= data.len(), "payload out of bounds",);
136
137 #[cfg(any(
138 feature = "sso-min-32bit",
139 feature = "sso-min-64bit",
140 feature = "sso-lv10",
141 feature = "sso-lv20"
142 ))]
143 if length <= SSO_BUFFER_SIZE {
144 let mut buffer = [0u8; SSO_BUFFER_SIZE];
145 buffer[..length].copy_from_slice(&data[start..start + length]);
146 return Self::Small(buffer, length as LengthType);
147 }
148
149 Self::Large {
150 data,
151 start,
152 length,
153 }
154 }
155
156 pub fn as_slice(&self) -> &[u8] {
164 match self {
165 ArcPayload::Large {
166 data,
167 start,
168 length,
169 } => &data[*start..*start + *length],
170 #[cfg(any(
171 feature = "sso-min-32bit",
172 feature = "sso-min-64bit",
173 feature = "sso-lv10",
174 feature = "sso-lv20"
175 ))]
176 ArcPayload::Small(buffer, length) => &buffer[..*length as usize],
177 }
178 }
179
180 pub fn len(&self) -> usize {
188 match self {
189 ArcPayload::Large { length, .. } => *length,
190 #[cfg(any(
191 feature = "sso-min-32bit",
192 feature = "sso-min-64bit",
193 feature = "sso-lv10",
194 feature = "sso-lv20"
195 ))]
196 ArcPayload::Small(_, length) => *length as usize,
197 }
198 }
199
200 pub fn is_empty(&self) -> bool {
208 self.len() == 0
209 }
210
211 pub fn arc_data(&self) -> Option<&Arc<[u8]>> {
221 match self {
222 ArcPayload::Large { data, .. } => Some(data),
223 #[cfg(any(
224 feature = "sso-min-32bit",
225 feature = "sso-min-64bit",
226 feature = "sso-lv10",
227 feature = "sso-lv20"
228 ))]
229 ArcPayload::Small(_, _) => None, }
231 }
232}
233
234impl Default for ArcPayload {
235 fn default() -> Self {
236 #[cfg(any(
237 feature = "sso-min-32bit",
238 feature = "sso-min-64bit",
239 feature = "sso-lv10",
240 feature = "sso-lv20"
241 ))]
242 return ArcPayload::Small([0u8; SSO_BUFFER_SIZE], 0 as LengthType);
243
244 #[cfg(not(any(
245 feature = "sso-min-32bit",
246 feature = "sso-min-64bit",
247 feature = "sso-lv10",
248 feature = "sso-lv20"
249 )))]
250 return ArcPayload::Large {
251 data: Arc::from(&[] as &[u8]),
252 start: 0,
253 length: 0,
254 };
255 }
256}
257
258impl core::fmt::Debug for ArcPayload {
259 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
260 f.debug_struct("ArcPayload")
261 .field("data", &self.as_slice())
262 .field("len", &self.len())
263 .finish()
264 }
265}
266
267pub trait IntoPayload {
273 fn into_payload(self) -> ArcPayload;
279}
280
281impl IntoPayload for &str {
283 fn into_payload(self) -> ArcPayload {
284 let bytes = self.as_bytes();
285
286 #[cfg(any(
287 feature = "sso-min-32bit",
288 feature = "sso-min-64bit",
289 feature = "sso-lv10",
290 feature = "sso-lv20"
291 ))]
292 if bytes.len() <= SSO_BUFFER_SIZE {
293 let mut buffer = [0u8; SSO_BUFFER_SIZE];
294 buffer[..bytes.len()].copy_from_slice(bytes);
295 return ArcPayload::Small(buffer, bytes.len() as LengthType);
296 }
297
298 ArcPayload::Large {
299 data: Arc::from(bytes),
300 start: 0,
301 length: bytes.len(),
302 }
303 }
304}
305
306impl IntoPayload for String {
308 fn into_payload(self) -> ArcPayload {
309 let bytes = self.as_bytes();
310
311 #[cfg(any(
312 feature = "sso-min-32bit",
313 feature = "sso-min-64bit",
314 feature = "sso-lv10",
315 feature = "sso-lv20"
316 ))]
317 if bytes.len() <= SSO_BUFFER_SIZE {
318 let mut buffer = [0u8; SSO_BUFFER_SIZE];
319 buffer[..bytes.len()].copy_from_slice(bytes);
320 return ArcPayload::Small(buffer, bytes.len() as LengthType);
321 }
322
323 ArcPayload::Large {
324 data: Arc::from(bytes),
325 start: 0,
326 length: bytes.len(),
327 }
328 }
329}
330
331impl IntoPayload for &[u8] {
333 fn into_payload(self) -> ArcPayload {
334 #[cfg(any(
335 feature = "sso-min-32bit",
336 feature = "sso-min-64bit",
337 feature = "sso-lv10",
338 feature = "sso-lv20"
339 ))]
340 if self.len() <= SSO_BUFFER_SIZE {
341 let mut buffer = [0u8; SSO_BUFFER_SIZE];
342 buffer[..self.len()].copy_from_slice(self);
343 return ArcPayload::Small(buffer, self.len() as LengthType);
344 }
345
346 ArcPayload::Large {
347 data: Arc::from(self),
348 start: 0,
349 length: self.len(),
350 }
351 }
352}
353
354impl IntoPayload for Vec<u8> {
356 fn into_payload(self) -> ArcPayload {
357 #[cfg(any(
358 feature = "sso-min-32bit",
359 feature = "sso-min-64bit",
360 feature = "sso-lv10",
361 feature = "sso-lv20"
362 ))]
363 if self.len() <= SSO_BUFFER_SIZE {
364 let mut buffer = [0u8; SSO_BUFFER_SIZE];
365 buffer[..self.len()].copy_from_slice(&self);
366 return ArcPayload::Small(buffer, self.len() as LengthType);
367 }
368
369 let len = self.len();
370 ArcPayload::Large {
371 data: Arc::from(self),
372 start: 0,
373 length: len,
374 }
375 }
376}
377
378impl IntoPayload for &Vec<u8> {
380 fn into_payload(self) -> ArcPayload {
381 let slice: &[u8] = self.as_slice();
382
383 #[cfg(any(
384 feature = "sso-min-32bit",
385 feature = "sso-min-64bit",
386 feature = "sso-lv10",
387 feature = "sso-lv20"
388 ))]
389 if slice.len() <= SSO_BUFFER_SIZE {
390 let mut buffer = [0u8; SSO_BUFFER_SIZE];
391 buffer[..slice.len()].copy_from_slice(slice);
392 return ArcPayload::Small(buffer, slice.len() as LengthType);
393 }
394
395 ArcPayload::Large {
396 data: Arc::from(slice),
397 start: 0,
398 length: slice.len(),
399 }
400 }
401}
402
403impl<const N: usize> IntoPayload for &[u8; N] {
405 fn into_payload(self) -> ArcPayload {
406 let slice: &[u8] = self.as_slice();
407
408 #[cfg(any(
409 feature = "sso-min-32bit",
410 feature = "sso-min-64bit",
411 feature = "sso-lv10",
412 feature = "sso-lv20"
413 ))]
414 if slice.len() <= SSO_BUFFER_SIZE {
415 let mut buffer = [0u8; SSO_BUFFER_SIZE];
416 buffer[..slice.len()].copy_from_slice(slice);
417 return ArcPayload::Small(buffer, slice.len() as LengthType);
418 }
419
420 ArcPayload::Large {
421 data: Arc::from(slice),
422 start: 0,
423 length: slice.len(),
424 }
425 }
426}
427
428impl IntoPayload for Arc<[u8]> {
430 fn into_payload(self) -> ArcPayload {
431 let len = self.len();
432
433 #[cfg(any(
434 feature = "sso-min-32bit",
435 feature = "sso-min-64bit",
436 feature = "sso-lv10",
437 feature = "sso-lv20"
438 ))]
439 if len <= SSO_BUFFER_SIZE {
440 let mut buffer = [0u8; SSO_BUFFER_SIZE];
441 buffer[..len].copy_from_slice(&self);
442 return ArcPayload::Small(buffer, len as LengthType);
443 }
444
445 ArcPayload::Large {
446 data: self,
447 start: 0,
448 length: len,
449 }
450 }
451}
452
453impl IntoPayload for () {
455 fn into_payload(self) -> ArcPayload {
456 ArcPayload::default() }
458}
459
460impl IntoPayload for ArcPayload {
462 fn into_payload(self) -> ArcPayload {
463 self
464 }
465}
466
467#[cfg(test)]
468mod tests {
469 use super::*;
470
471 #[test]
472 fn test_empty_payload() {
473 let payload = ArcPayload::default();
474 assert_eq!(payload.len(), 0);
475 assert!(payload.is_empty());
476 assert_eq!(payload.as_slice(), &[] as &[u8]);
477 }
478
479 #[test]
480 fn test_small_payload() {
481 let data = b"hello";
482 let payload = data.into_payload();
483 assert_eq!(payload.len(), 5);
484 assert!(!payload.is_empty());
485 assert_eq!(payload.as_slice(), b"hello");
486 }
487
488 #[test]
489 fn test_payload_variants() {
490 let small_data = b"small";
492 let payload = small_data.into_payload();
493
494 #[cfg(any(
495 feature = "sso-min-32bit",
496 feature = "sso-min-64bit",
497 feature = "sso-lv10",
498 feature = "sso-lv20"
499 ))]
500 assert!(matches!(payload, ArcPayload::Small(_, _)));
501
502 #[cfg(not(any(
503 feature = "sso-min-32bit",
504 feature = "sso-min-64bit",
505 feature = "sso-lv10",
506 feature = "sso-lv20"
507 )))]
508 assert!(matches!(payload, ArcPayload::Large { .. }));
509
510 let medium_data = vec![0u8; 200]; let _payload = medium_data.into_payload();
513
514 #[cfg(feature = "sso-lv20")]
516 assert!(matches!(_payload, ArcPayload::Small(_, _)));
517
518 #[cfg(all(
520 any(
521 feature = "sso-min-32bit",
522 feature = "sso-min-64bit",
523 feature = "sso-lv10"
524 ),
525 not(feature = "sso-lv20")
526 ))]
527 assert!(matches!(_payload, ArcPayload::Large { .. }));
528
529 let very_large_data = b"This is a very long payload that exceeds even the largest SSO buffer size of 255 bytes. It should definitely be stored in the Large variant regardless of which SSO feature flags are enabled. This ensures consistent behavior across all configurations and provides a reliable test case.";
531 let payload = very_large_data.into_payload();
532 assert!(matches!(payload, ArcPayload::Large { .. }));
533 }
534
535 #[test]
536 fn test_arc_data_access() {
537 let small_data = b"test";
538 let small_payload = small_data.into_payload();
539
540 let very_large_data = b"This is a very long payload that exceeds even the largest SSO buffer size of 255 bytes. It should definitely be stored in the Large variant regardless of which SSO feature flags are enabled. This ensures consistent behavior across all configurations and provides a reliable test case for arc_data access.";
542 let large_payload = very_large_data.into_payload();
543
544 #[cfg(any(
546 feature = "sso-min-32bit",
547 feature = "sso-min-64bit",
548 feature = "sso-lv10",
549 feature = "sso-lv20"
550 ))]
551 if let ArcPayload::Small(_, _) = small_payload {
552 assert!(small_payload.arc_data().is_none());
553 }
554
555 #[cfg(not(any(
557 feature = "sso-min-32bit",
558 feature = "sso-min-64bit",
559 feature = "sso-lv10",
560 feature = "sso-lv20"
561 )))]
562 assert!(small_payload.arc_data().is_some());
563
564 assert!(large_payload.arc_data().is_some());
566 }
567
568 #[test]
569 fn test_into_payload_implementations() {
570 let str_payload = "hello".into_payload();
572 assert_eq!(str_payload.as_slice(), b"hello");
573
574 let string_payload = String::from("world").into_payload();
575 assert_eq!(string_payload.as_slice(), b"world");
576
577 let vec_payload = vec![1, 2, 3, 4].into_payload();
578 assert_eq!(vec_payload.as_slice(), &[1, 2, 3, 4]);
579
580 let arr_payload = (&[5, 6, 7, 8]).into_payload();
581 assert_eq!(arr_payload.as_slice(), &[5, 6, 7, 8]);
582
583 let unit_payload = ().into_payload();
584 assert!(unit_payload.is_empty());
585 }
586}