use crate::mqtt::Arc;
use alloc::{string::String, vec::Vec};
use serde::{Serialize, Serializer};
#[cfg(feature = "sso-lv20")]
const SSO_BUFFER_SIZE: usize = 255; #[cfg(all(not(feature = "sso-lv20"), feature = "sso-lv10"))]
const SSO_BUFFER_SIZE: usize = 127; #[cfg(all(
not(any(feature = "sso-lv20", feature = "sso-lv10")),
feature = "sso-min-64bit"
))]
const SSO_BUFFER_SIZE: usize = 31; #[cfg(all(
not(any(feature = "sso-lv20", feature = "sso-lv10", feature = "sso-min-64bit")),
feature = "sso-min-32bit"
))]
const SSO_BUFFER_SIZE: usize = 15; #[cfg(not(any(
feature = "sso-min-32bit",
feature = "sso-min-64bit",
feature = "sso-lv10",
feature = "sso-lv20"
)))]
#[allow(dead_code)]
const SSO_BUFFER_SIZE: usize = 0;
#[cfg(any(
feature = "sso-min-32bit",
feature = "sso-min-64bit",
feature = "sso-lv10",
feature = "sso-lv20"
))]
type LengthType = u8;
#[cfg(not(any(
feature = "sso-min-32bit",
feature = "sso-min-64bit",
feature = "sso-lv10",
feature = "sso-lv20"
)))]
#[allow(dead_code)]
type LengthType = u8;
#[derive(Clone)]
#[allow(clippy::large_enum_variant)]
pub enum ArcPayload {
#[cfg(any(
feature = "sso-min-32bit",
feature = "sso-min-64bit",
feature = "sso-lv10",
feature = "sso-lv20"
))]
Small([u8; SSO_BUFFER_SIZE], LengthType), Large {
data: Arc<[u8]>,
start: usize,
length: usize,
},
}
impl PartialEq for ArcPayload {
fn eq(&self, other: &Self) -> bool {
self.as_slice() == other.as_slice()
}
}
impl Eq for ArcPayload {}
impl Serialize for ArcPayload {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.as_slice().serialize(serializer)
}
}
impl ArcPayload {
pub fn new(data: Arc<[u8]>, start: usize, length: usize) -> Self {
debug_assert!(start + length <= data.len(), "payload out of bounds",);
#[cfg(any(
feature = "sso-min-32bit",
feature = "sso-min-64bit",
feature = "sso-lv10",
feature = "sso-lv20"
))]
if length <= SSO_BUFFER_SIZE {
let mut buffer = [0u8; SSO_BUFFER_SIZE];
buffer[..length].copy_from_slice(&data[start..start + length]);
return Self::Small(buffer, length as LengthType);
}
Self::Large {
data,
start,
length,
}
}
pub fn as_slice(&self) -> &[u8] {
match self {
ArcPayload::Large {
data,
start,
length,
} => &data[*start..*start + *length],
#[cfg(any(
feature = "sso-min-32bit",
feature = "sso-min-64bit",
feature = "sso-lv10",
feature = "sso-lv20"
))]
ArcPayload::Small(buffer, length) => &buffer[..*length as usize],
}
}
pub fn len(&self) -> usize {
match self {
ArcPayload::Large { length, .. } => *length,
#[cfg(any(
feature = "sso-min-32bit",
feature = "sso-min-64bit",
feature = "sso-lv10",
feature = "sso-lv20"
))]
ArcPayload::Small(_, length) => *length as usize,
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn arc_data(&self) -> Option<&Arc<[u8]>> {
match self {
ArcPayload::Large { data, .. } => Some(data),
#[cfg(any(
feature = "sso-min-32bit",
feature = "sso-min-64bit",
feature = "sso-lv10",
feature = "sso-lv20"
))]
ArcPayload::Small(_, _) => None, }
}
}
impl Default for ArcPayload {
fn default() -> Self {
#[cfg(any(
feature = "sso-min-32bit",
feature = "sso-min-64bit",
feature = "sso-lv10",
feature = "sso-lv20"
))]
return ArcPayload::Small([0u8; SSO_BUFFER_SIZE], 0 as LengthType);
#[cfg(not(any(
feature = "sso-min-32bit",
feature = "sso-min-64bit",
feature = "sso-lv10",
feature = "sso-lv20"
)))]
return ArcPayload::Large {
data: Arc::from(&[] as &[u8]),
start: 0,
length: 0,
};
}
}
impl core::fmt::Debug for ArcPayload {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ArcPayload")
.field("data", &self.as_slice())
.field("len", &self.len())
.finish()
}
}
pub trait IntoPayload {
fn into_payload(self) -> ArcPayload;
}
impl IntoPayload for &str {
fn into_payload(self) -> ArcPayload {
let bytes = self.as_bytes();
#[cfg(any(
feature = "sso-min-32bit",
feature = "sso-min-64bit",
feature = "sso-lv10",
feature = "sso-lv20"
))]
if bytes.len() <= SSO_BUFFER_SIZE {
let mut buffer = [0u8; SSO_BUFFER_SIZE];
buffer[..bytes.len()].copy_from_slice(bytes);
return ArcPayload::Small(buffer, bytes.len() as LengthType);
}
ArcPayload::Large {
data: Arc::from(bytes),
start: 0,
length: bytes.len(),
}
}
}
impl IntoPayload for String {
fn into_payload(self) -> ArcPayload {
let bytes = self.as_bytes();
#[cfg(any(
feature = "sso-min-32bit",
feature = "sso-min-64bit",
feature = "sso-lv10",
feature = "sso-lv20"
))]
if bytes.len() <= SSO_BUFFER_SIZE {
let mut buffer = [0u8; SSO_BUFFER_SIZE];
buffer[..bytes.len()].copy_from_slice(bytes);
return ArcPayload::Small(buffer, bytes.len() as LengthType);
}
ArcPayload::Large {
data: Arc::from(bytes),
start: 0,
length: bytes.len(),
}
}
}
impl IntoPayload for &[u8] {
fn into_payload(self) -> ArcPayload {
#[cfg(any(
feature = "sso-min-32bit",
feature = "sso-min-64bit",
feature = "sso-lv10",
feature = "sso-lv20"
))]
if self.len() <= SSO_BUFFER_SIZE {
let mut buffer = [0u8; SSO_BUFFER_SIZE];
buffer[..self.len()].copy_from_slice(self);
return ArcPayload::Small(buffer, self.len() as LengthType);
}
ArcPayload::Large {
data: Arc::from(self),
start: 0,
length: self.len(),
}
}
}
impl IntoPayload for Vec<u8> {
fn into_payload(self) -> ArcPayload {
#[cfg(any(
feature = "sso-min-32bit",
feature = "sso-min-64bit",
feature = "sso-lv10",
feature = "sso-lv20"
))]
if self.len() <= SSO_BUFFER_SIZE {
let mut buffer = [0u8; SSO_BUFFER_SIZE];
buffer[..self.len()].copy_from_slice(&self);
return ArcPayload::Small(buffer, self.len() as LengthType);
}
let len = self.len();
ArcPayload::Large {
data: Arc::from(self),
start: 0,
length: len,
}
}
}
impl IntoPayload for &Vec<u8> {
fn into_payload(self) -> ArcPayload {
let slice: &[u8] = self.as_slice();
#[cfg(any(
feature = "sso-min-32bit",
feature = "sso-min-64bit",
feature = "sso-lv10",
feature = "sso-lv20"
))]
if slice.len() <= SSO_BUFFER_SIZE {
let mut buffer = [0u8; SSO_BUFFER_SIZE];
buffer[..slice.len()].copy_from_slice(slice);
return ArcPayload::Small(buffer, slice.len() as LengthType);
}
ArcPayload::Large {
data: Arc::from(slice),
start: 0,
length: slice.len(),
}
}
}
impl<const N: usize> IntoPayload for &[u8; N] {
fn into_payload(self) -> ArcPayload {
let slice: &[u8] = self.as_slice();
#[cfg(any(
feature = "sso-min-32bit",
feature = "sso-min-64bit",
feature = "sso-lv10",
feature = "sso-lv20"
))]
if slice.len() <= SSO_BUFFER_SIZE {
let mut buffer = [0u8; SSO_BUFFER_SIZE];
buffer[..slice.len()].copy_from_slice(slice);
return ArcPayload::Small(buffer, slice.len() as LengthType);
}
ArcPayload::Large {
data: Arc::from(slice),
start: 0,
length: slice.len(),
}
}
}
impl IntoPayload for Arc<[u8]> {
fn into_payload(self) -> ArcPayload {
let len = self.len();
#[cfg(any(
feature = "sso-min-32bit",
feature = "sso-min-64bit",
feature = "sso-lv10",
feature = "sso-lv20"
))]
if len <= SSO_BUFFER_SIZE {
let mut buffer = [0u8; SSO_BUFFER_SIZE];
buffer[..len].copy_from_slice(&self);
return ArcPayload::Small(buffer, len as LengthType);
}
ArcPayload::Large {
data: self,
start: 0,
length: len,
}
}
}
impl IntoPayload for () {
fn into_payload(self) -> ArcPayload {
ArcPayload::default() }
}
impl IntoPayload for ArcPayload {
fn into_payload(self) -> ArcPayload {
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_payload() {
let payload = ArcPayload::default();
assert_eq!(payload.len(), 0);
assert!(payload.is_empty());
assert_eq!(payload.as_slice(), &[] as &[u8]);
}
#[test]
fn test_small_payload() {
let data = b"hello";
let payload = data.into_payload();
assert_eq!(payload.len(), 5);
assert!(!payload.is_empty());
assert_eq!(payload.as_slice(), b"hello");
}
#[test]
fn test_payload_variants() {
let small_data = b"small";
let payload = small_data.into_payload();
#[cfg(any(
feature = "sso-min-32bit",
feature = "sso-min-64bit",
feature = "sso-lv10",
feature = "sso-lv20"
))]
assert!(matches!(payload, ArcPayload::Small(_, _)));
#[cfg(not(any(
feature = "sso-min-32bit",
feature = "sso-min-64bit",
feature = "sso-lv10",
feature = "sso-lv20"
)))]
assert!(matches!(payload, ArcPayload::Large { .. }));
let medium_data = vec![0u8; 200]; let _payload = medium_data.into_payload();
#[cfg(feature = "sso-lv20")]
assert!(matches!(_payload, ArcPayload::Small(_, _)));
#[cfg(all(
any(
feature = "sso-min-32bit",
feature = "sso-min-64bit",
feature = "sso-lv10"
),
not(feature = "sso-lv20")
))]
assert!(matches!(_payload, ArcPayload::Large { .. }));
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.";
let payload = very_large_data.into_payload();
assert!(matches!(payload, ArcPayload::Large { .. }));
}
#[test]
fn test_arc_data_access() {
let small_data = b"test";
let small_payload = small_data.into_payload();
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.";
let large_payload = very_large_data.into_payload();
#[cfg(any(
feature = "sso-min-32bit",
feature = "sso-min-64bit",
feature = "sso-lv10",
feature = "sso-lv20"
))]
if let ArcPayload::Small(_, _) = small_payload {
assert!(small_payload.arc_data().is_none());
}
#[cfg(not(any(
feature = "sso-min-32bit",
feature = "sso-min-64bit",
feature = "sso-lv10",
feature = "sso-lv20"
)))]
assert!(small_payload.arc_data().is_some());
assert!(large_payload.arc_data().is_some());
}
#[test]
fn test_into_payload_implementations() {
let str_payload = "hello".into_payload();
assert_eq!(str_payload.as_slice(), b"hello");
let string_payload = String::from("world").into_payload();
assert_eq!(string_payload.as_slice(), b"world");
let vec_payload = vec![1, 2, 3, 4].into_payload();
assert_eq!(vec_payload.as_slice(), &[1, 2, 3, 4]);
let arr_payload = (&[5, 6, 7, 8]).into_payload();
assert_eq!(arr_payload.as_slice(), &[5, 6, 7, 8]);
let unit_payload = ().into_payload();
assert!(unit_payload.is_empty());
}
}