use core::num::NonZero;
use crate::{EpeeError, Type, TypeOrEntry};
const EPEE_LIB_MAX_OBJECT_DEPTH: usize = 100;
const MAX_OBJECT_DEPTH: usize = EPEE_LIB_MAX_OBJECT_DEPTH + 2;
struct PackedTypes([u8; MAX_OBJECT_DEPTH.div_ceil(2)]);
impl PackedTypes {
fn get(&self, i: usize) -> TypeOrEntry {
let mut entry = self.0[i / 2];
entry >>= (i & 1) * 4;
entry &= 0b1111;
match entry {
0 => TypeOrEntry::Entry,
1 => TypeOrEntry::Type(Type::Int64),
2 => TypeOrEntry::Type(Type::Int32),
3 => TypeOrEntry::Type(Type::Int16),
4 => TypeOrEntry::Type(Type::Int8),
5 => TypeOrEntry::Type(Type::Uint64),
6 => TypeOrEntry::Type(Type::Uint32),
7 => TypeOrEntry::Type(Type::Uint16),
8 => TypeOrEntry::Type(Type::Uint8),
9 => TypeOrEntry::Type(Type::Double),
10 => TypeOrEntry::Type(Type::String),
11 => TypeOrEntry::Type(Type::Bool),
12 => TypeOrEntry::Type(Type::Object),
13 ..= 15 => panic!("`PackedTypes` was written to with a non-existent `TypeOrEntry`"),
_ => unreachable!("masked by 0b1111"),
}
}
fn set(&mut self, i: usize, kind: TypeOrEntry) {
let four_bits = match kind {
TypeOrEntry::Entry => 0,
#[expect(clippy::as_conversions)]
TypeOrEntry::Type(kind) => kind as u8,
};
let shift = (i & 1) * 4;
self.0[i / 2] &= 0b1111_0000 >> shift;
self.0[i / 2] |= four_bits << shift;
}
}
pub(crate) struct Stack {
types: PackedTypes,
amounts: [NonZero<usize>; MAX_OBJECT_DEPTH],
depth: u8,
}
#[cfg(test)]
const _ASSERT_KIBIBYTE_STACK: [(); 1024 - core::mem::size_of::<Stack>()] =
[(); 1024 - core::mem::size_of::<Stack>()];
impl Stack {
#[inline(always)]
pub(crate) fn root_object() -> Self {
let mut types = PackedTypes([0; MAX_OBJECT_DEPTH.div_ceil(2)]);
let mut amounts = [NonZero::<usize>::MIN; MAX_OBJECT_DEPTH];
types.set(0, TypeOrEntry::Type(Type::Object));
amounts[0] = NonZero::<usize>::MIN;
Self { types, amounts, depth: 1 }
}
#[inline(always)]
pub(crate) fn depth(&self) -> usize {
usize::from(self.depth)
}
#[inline(always)]
pub(crate) fn peek(&self) -> Option<(TypeOrEntry, NonZero<usize>)> {
let i = self.depth().checked_sub(1)?;
Some((self.types.get(i), self.amounts[i]))
}
pub(crate) fn pop(&mut self) -> Option<TypeOrEntry> {
let i = self.depth().checked_sub(1)?;
let kind = self.types.get(i);
let amount = self.amounts[i].get() - 1;
if let Some(amount) = NonZero::new(amount) {
self.amounts[i] = amount;
} else {
self.depth -= 1;
}
Some(kind)
}
pub(crate) fn push(&mut self, kind: TypeOrEntry, amount: usize) -> Result<(), EpeeError> {
if self.depth() == MAX_OBJECT_DEPTH {
Err(EpeeError::DepthLimitExceeded)?;
}
let Some(amount) = NonZero::new(amount) else {
return Ok(());
};
self.types.set(self.depth(), kind);
self.amounts[self.depth()] = amount;
self.depth += 1;
Ok(())
}
}