use hashbrown::raw::Bucket;
use crate::{
builtin_traits::iterator, corelib::pair::Pair, ll::value::RawValue, Engine, Error, TypeBuilder,
UserData,
};
type InnerIter = hashbrown::raw::RawIter<(RawValue, RawValue)>;
pub(crate) struct DictIter {
dict: RawValue,
iter: InnerIter,
len: usize,
}
impl DictIter {
pub(crate) unsafe fn new(value: RawValue) -> Self {
let dict = value.get_raw_dict_unchecked().get();
Self { dict: value, iter: dict.raw_iter(), len: dict.len() }
}
fn checked_next(
dict: RawValue,
iter: &mut InnerIter,
len: usize,
) -> Result<Option<Bucket<(RawValue, RawValue)>>, LenChangedDuringIteration> {
let current_len = unsafe { dict.get_raw_dict_unchecked().get().len() };
if len != current_len {
return Err(LenChangedDuringIteration { was: len, became: current_len });
}
Ok(iter.next())
}
fn has_next(&self) -> Result<bool, LenChangedDuringIteration> {
Ok(Self::checked_next(self.dict, &mut self.iter.clone(), self.len)?.is_some())
}
fn next(&mut self) -> Result<Option<Pair>, LenChangedDuringIteration> {
if let Some(bucket) = Self::checked_next(self.dict, &mut self.iter, self.len)? {
let (key, value) = unsafe { bucket.read() };
Ok(Some(Pair { a: key, b: value }))
} else {
Ok(None)
}
}
}
impl UserData for DictIter {
fn visit_references(&self, visit: &mut dyn FnMut(RawValue)) {
visit(self.dict);
}
}
#[derive(Debug)]
struct LenChangedDuringIteration {
was: usize,
became: usize,
}
impl std::fmt::Display for LenChangedDuringIteration {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "dict length changed during iteration (was {}, became {})", self.was, self.became)
}
}
impl std::error::Error for LenChangedDuringIteration {}
pub(crate) fn load_dict_iter(engine: &mut Engine) -> Result<(), Error> {
engine.add_type(
TypeBuilder::<DictIter>::new("DictIter")
.add_builtin_trait_function(iterator::HasNext, DictIter::has_next)
.add_builtin_trait_function(iterator::Next, DictIter::next),
)?;
Ok(())
}