use crate::{
ecmascript::{OrdinaryObject, Value},
engine::bindable_handle,
heap::{CompactionLists, HeapMarkAndSweep, WorkQueues},
};
use ahash::AHasher;
use core::{
cell::RefCell,
hash::{Hash, Hasher},
sync::atomic::{AtomicBool, Ordering},
};
use hashbrown::HashTable;
use soavec_derive::SoAble;
#[derive(Debug, Default, SoAble)]
pub(crate) struct SetHeapData<'a> {
pub(crate) set_data: RefCell<HashTable<u32>>,
pub(crate) values: Vec<Option<Value<'a>>>,
pub(crate) object_index: Option<OrdinaryObject<'a>>,
pub(crate) needs_primitive_rehashing: AtomicBool,
}
bindable_handle!(SetHeapData);
impl HeapMarkAndSweep for SetHeapData<'static> {
fn mark_values(&self, queues: &mut WorkQueues) {
let Self {
object_index,
values,
..
} = self;
object_index.mark_values(queues);
values.iter().for_each(|value| value.mark_values(queues));
}
fn sweep_values(&mut self, compactions: &CompactionLists) {
let Self {
object_index,
values,
set_data,
needs_primitive_rehashing,
..
} = self;
let set_data = set_data.get_mut();
object_index.sweep_values(compactions);
let hasher = |value: Value| {
let mut hasher = AHasher::default();
if value.try_hash(&mut hasher).is_err() {
needs_primitive_rehashing.store(true, Ordering::Relaxed);
core::mem::discriminant(&value).hash(&mut hasher);
}
hasher.finish()
};
for index in 0..values.len() as u32 {
let value = &mut values[index as usize];
let Some(value) = value else {
continue;
};
let old_value = *value;
value.sweep_values(compactions);
let new_value = *value;
if old_value == new_value {
continue;
}
if !new_value.is_object() {
continue;
}
let old_hash = hasher(old_value);
let new_hash = hasher(new_value);
if let Ok(old_entry) =
set_data.find_entry(old_hash, |equal_hash_index| *equal_hash_index == index)
{
old_entry.remove();
}
let new_entry = set_data.entry(
new_hash,
|equal_hash_index| {
values[*equal_hash_index as usize].unwrap() == new_value
},
|index_to_hash| {
let value = values[*index_to_hash as usize].unwrap();
hasher(value)
},
);
match new_entry {
hashbrown::hash_table::Entry::Occupied(mut occupied) => {
*occupied.get_mut() = index;
}
hashbrown::hash_table::Entry::Vacant(vacant) => {
vacant.insert(index);
}
}
}
}
}