use crate::{builtins::map::ordered_map::MapKey, object::JsObject, JsValue};
use boa_gc::{custom_trace, Finalize, Trace};
use indexmap::IndexSet;
use std::{collections::hash_map::RandomState, fmt::Debug, hash::BuildHasher};
#[derive(Clone, Finalize)]
pub struct OrderedSet<S = RandomState> {
inner: IndexSet<MapKey, S>,
lock: u32,
empty_count: usize,
}
unsafe impl<S: BuildHasher> Trace for OrderedSet<S> {
custom_trace!(this, {
for v in this.inner.iter() {
if let MapKey::Key(v) = v {
mark(v);
}
}
});
}
impl Debug for OrderedSet {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
self.inner.fmt(formatter)
}
}
impl Default for OrderedSet {
fn default() -> Self {
Self::new()
}
}
impl OrderedSet {
#[must_use]
pub fn new() -> Self {
Self {
inner: IndexSet::new(),
lock: 0,
empty_count: 0,
}
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
inner: IndexSet::with_capacity(capacity),
lock: 0,
empty_count: 0,
}
}
#[must_use]
pub fn full_len(&self) -> usize {
self.inner.len()
}
#[must_use]
pub fn len(&self) -> usize {
self.inner.len() - self.empty_count
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.inner.len() == 0
}
pub fn add(&mut self, value: JsValue) -> bool {
self.inner.insert(MapKey::Key(value))
}
pub fn delete(&mut self, value: &JsValue) -> bool {
if self.lock == 0 {
self.inner.shift_remove(value)
} else if self.inner.contains(value) {
self.inner.insert(MapKey::Empty(self.empty_count));
self.empty_count += 1;
self.inner.swap_remove(value)
} else {
false
}
}
#[inline]
pub fn clear(&mut self) {
self.inner.clear();
self.inner.shrink_to_fit();
self.empty_count = 0;
}
pub fn contains(&self, value: &JsValue) -> bool {
self.inner.contains(value)
}
#[must_use]
pub fn get_index(&self, index: usize) -> Option<&JsValue> {
if let MapKey::Key(value) = self.inner.get_index(index)? {
Some(value)
} else {
None
}
}
pub fn iter(&self) -> impl Iterator<Item = &JsValue> {
self.inner.iter().filter_map(|v| {
if let MapKey::Key(v) = v {
Some(v)
} else {
None
}
})
}
pub(crate) fn lock(&mut self, set: JsObject) -> SetLock {
self.lock += 1;
SetLock(set)
}
fn unlock(&mut self) {
self.lock -= 1;
if self.lock == 0 {
self.inner.retain(|k| matches!(k, MapKey::Key(_)));
self.empty_count = 0;
}
}
}
#[derive(Debug, Trace)]
pub(crate) struct SetLock(JsObject);
impl Clone for SetLock {
fn clone(&self) -> Self {
let mut set = self.0.borrow_mut();
let set = set.as_set_mut().expect("SetLock does not point to a set");
set.lock(self.0.clone())
}
}
impl Finalize for SetLock {
fn finalize(&self) {
let mut set = self.0.borrow_mut();
let set = set.as_set_mut().expect("SetLock does not point to a set");
set.unlock();
}
}