use alloc::vec::Vec;
use potential_utf::PotentialUtf16;
use serde_core::de::*;
pub fn option_utf_16<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<Option<&'de PotentialUtf16>, D::Error> {
let Some(bytes) = <Option<&[u8]>>::deserialize(deserializer)? else {
return Ok(None);
};
unsafe { cast_bytes_to_slice(bytes) }
.map(PotentialUtf16::from_slice)
.map(Some)
}
pub fn vec_utf_16<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<Vec<&'de PotentialUtf16>, D::Error> {
struct Utf16Visitor;
impl<'de> Visitor<'de> for Utf16Visitor {
type Value = Vec<&'de PotentialUtf16>;
fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(formatter, "a sequence of UTF-16 slices")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut vec = Vec::with_capacity(seq.size_hint().unwrap_or_default());
while let Some(bytes) = seq.next_element::<&[u8]>()? {
vec.push(PotentialUtf16::from_slice(
unsafe { cast_bytes_to_slice(bytes) }?,
));
}
Ok(vec)
}
}
deserializer.deserialize_seq(Utf16Visitor)
}
pub fn option_i32<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<Option<&'de [i32]>, D::Error> {
let Some(bytes) = <Option<&[u8]>>::deserialize(deserializer)? else {
return Ok(None);
};
unsafe { cast_bytes_to_slice(bytes) }.map(Some)
}
pub fn option_u32<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<Option<&'de [u32]>, D::Error> {
let Some(bytes) = <Option<&[u8]>>::deserialize(deserializer)? else {
return Ok(None);
};
unsafe { cast_bytes_to_slice(bytes) }.map(Some)
}
pub fn i32_tuple<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<&'de [(i32, i32)], D::Error> {
let bytes = <&[u8]>::deserialize(deserializer)?;
unsafe { cast_bytes_to_slice(bytes) }
}
#[expect(clippy::type_complexity)] pub fn option_i32_tuple<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<Option<&'de [(i32, i32)]>, D::Error> {
let Some(bytes) = <Option<&[u8]>>::deserialize(deserializer)? else {
return Ok(None);
};
unsafe { cast_bytes_to_slice(bytes) }.map(Some)
}
pub unsafe fn cast_bytes_to_slice<T, E: Error>(bytes: &[u8]) -> Result<&[T], E> {
if bytes.as_ptr().align_offset(align_of::<T>()) != 0 || bytes.len() % size_of::<T>() != 0 {
return Err(E::custom("Wrong length or align"));
}
Ok(unsafe {
core::slice::from_raw_parts(bytes.as_ptr() as *const T, bytes.len() / size_of::<T>())
})
}
#[cfg(test)]
mod tests {
use super::*;
type E = value::Error;
#[test]
fn cast_aligned_correct_length() {
let data: [u32; 2] = [1, 2];
let bytes: &[u8] =
unsafe { core::slice::from_raw_parts(data.as_ptr() as *const u8, size_of_val(&data)) };
let result: Result<&[u32], E> = unsafe { cast_bytes_to_slice(bytes) };
assert!(result.is_ok());
assert_eq!(result.unwrap(), &[1u32, 2]);
}
#[test]
fn cast_wrong_length_only() {
let data: [u32; 2] = [1, 2];
let bytes: &[u8] = unsafe { core::slice::from_raw_parts(data.as_ptr() as *const u8, 5) };
let result: Result<&[u32], E> = unsafe { cast_bytes_to_slice(bytes) };
assert!(result.is_err(), "wrong length alone must be rejected");
}
#[test]
fn cast_wrong_alignment_only() {
let data: [u8; 8] = [0; 8];
let aligned_start = data.as_ptr().align_offset(align_of::<u32>());
let misaligned = &data[aligned_start + 1..aligned_start + 5];
assert_eq!(misaligned.len(), 4); assert_ne!(misaligned.as_ptr().align_offset(align_of::<u32>()), 0);
let result: Result<&[u32], E> = unsafe { cast_bytes_to_slice(misaligned) };
assert!(result.is_err(), "wrong alignment alone must be rejected");
}
}