use crate::{
deserializer::{
constants::ARRAY, consumed::Consumed, number::read_unsigned_int, read::read_exact_bytes,
},
error::{Result, TypedStreamError},
};
use alloc::vec::Vec;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Type<'a> {
Utf8String,
EmbeddedData,
Object,
SignedInt,
UnsignedInt,
Float,
Double,
String(&'a str),
Array(usize),
Unknown(u8),
}
impl<'a> Type<'a> {
#[inline]
pub(crate) fn from_byte(byte: u8) -> Self {
match byte {
0x40 => Self::Object,
0x2B => Self::Utf8String,
0x2A => Self::EmbeddedData,
0x66 => Self::Float,
0x64 => Self::Double,
0x63 | 0x69 | 0x6c | 0x71 | 0x73 => Self::SignedInt,
0x43 | 0x49 | 0x4c | 0x51 | 0x53 => Self::UnsignedInt,
other => Self::Unknown(other),
}
}
#[inline]
pub(crate) fn new_string(str: &'a str) -> Self {
Self::String(str)
}
pub(crate) fn get_array_length(types: &'_ [u8]) -> Option<usize> {
if types.first() != Some(&ARRAY) {
return None;
}
let mut len = 0usize;
let mut saw_digit = false;
for &byte in &types[1..] {
if !byte.is_ascii_digit() {
break;
}
len = len * 10 + usize::from(byte - b'0');
saw_digit = true;
}
saw_digit.then_some(len)
}
pub(crate) fn read_new_type(data: &'_ [u8]) -> Result<Consumed<TypeEntry<'_>>> {
let type_length = read_unsigned_int(data)?;
let type_bytes = read_exact_bytes(
&data[type_length.bytes_consumed..],
type_length.value as usize,
)?;
let bytes_consumed = type_length.bytes_consumed + type_bytes.len();
if type_bytes.first() == Some(&ARRAY) {
let len =
Type::get_array_length(type_bytes).ok_or(TypedStreamError::InvalidArray(0))?;
return Ok(Consumed::new(
TypeEntry::One(Type::Array(len)),
bytes_consumed,
));
}
let entry = if let [byte] = type_bytes {
TypeEntry::One(Type::from_byte(*byte))
} else {
TypeEntry::Many(type_bytes.iter().copied().map(Type::from_byte).collect())
};
Ok(Consumed::new(entry, bytes_consumed))
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum TypeEntry<'a> {
One(Type<'a>),
Many(Vec<Type<'a>>),
}
impl<'a> TypeEntry<'a> {
#[must_use]
pub fn len(&self) -> usize {
match self {
TypeEntry::One(_) => 1,
TypeEntry::Many(types) => types.len(),
}
}
#[must_use]
pub fn is_empty(&self) -> bool {
matches!(self, TypeEntry::Many(types) if types.is_empty())
}
#[must_use]
pub fn first(&self) -> Option<&Type<'a>> {
match self {
TypeEntry::One(ty) => Some(ty),
TypeEntry::Many(types) => types.first(),
}
}
#[cfg(test)]
pub(crate) fn from_types(mut types: Vec<Type<'a>>) -> Self {
if types.len() == 1 {
TypeEntry::One(types.pop().unwrap())
} else {
TypeEntry::Many(types)
}
}
}
impl<'a> core::ops::Index<usize> for TypeEntry<'a> {
type Output = Type<'a>;
fn index(&self, index: usize) -> &Self::Output {
match self {
TypeEntry::One(ty) => {
assert_eq!(index, 0, "index out of bounds for single-type entry");
ty
}
TypeEntry::Many(types) => &types[index],
}
}
}