boa/builtins/map/
ordered_map.rs1use crate::{
2 gc::{custom_trace, Finalize, Trace},
3 object::JsObject,
4 JsValue,
5};
6use indexmap::{Equivalent, IndexMap};
7use std::{
8 collections::hash_map::RandomState,
9 fmt::Debug,
10 hash::{BuildHasher, Hash, Hasher},
11};
12
13#[derive(PartialEq, Eq, Clone, Debug)]
14enum MapKey {
15 Key(JsValue),
16 Empty(usize), }
18
19#[allow(clippy::derive_hash_xor_eq)]
21impl Hash for MapKey {
22 fn hash<H: Hasher>(&self, state: &mut H) {
23 match self {
24 MapKey::Key(v) => v.hash(state),
25 MapKey::Empty(e) => e.hash(state),
26 }
27 }
28}
29
30impl Equivalent<MapKey> for JsValue {
31 fn equivalent(&self, key: &MapKey) -> bool {
32 match key {
33 MapKey::Key(v) => v == self,
34 _ => false,
35 }
36 }
37}
38
39#[derive(Clone)]
41pub struct OrderedMap<V, S = RandomState> {
42 map: IndexMap<MapKey, Option<V>, S>,
43 lock: u32,
44 empty_count: usize,
45}
46
47impl<V: Trace, S: BuildHasher> Finalize for OrderedMap<V, S> {}
48unsafe impl<V: Trace, S: BuildHasher> Trace for OrderedMap<V, S> {
49 custom_trace!(this, {
50 for (k, v) in this.map.iter() {
51 if let MapKey::Key(key) = k {
52 mark(key);
53 }
54 mark(v);
55 }
56 });
57}
58
59impl<V: Debug> Debug for OrderedMap<V> {
60 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
61 self.map.fmt(formatter)
62 }
63}
64
65impl<V> Default for OrderedMap<V> {
66 fn default() -> Self {
67 Self::new()
68 }
69}
70
71impl<V> OrderedMap<V> {
72 pub fn new() -> Self {
73 OrderedMap {
74 map: IndexMap::new(),
75 lock: 0,
76 empty_count: 0,
77 }
78 }
79
80 pub fn with_capacity(capacity: usize) -> Self {
81 OrderedMap {
82 map: IndexMap::with_capacity(capacity),
83 lock: 0,
84 empty_count: 0,
85 }
86 }
87
88 pub fn full_len(&self) -> usize {
92 self.map.len()
93 }
94
95 pub fn len(&self) -> usize {
99 self.map.len() - self.empty_count
100 }
101
102 pub fn is_empty(&self) -> bool {
106 self.len() == 0
107 }
108
109 pub fn insert(&mut self, key: JsValue, value: V) -> Option<V> {
120 self.map.insert(MapKey::Key(key), Some(value)).flatten()
121 }
122
123 pub fn remove(&mut self, key: &JsValue) -> Option<V> {
134 if self.lock == 0 {
135 self.map.shift_remove(key).flatten()
136 } else if self.map.contains_key(key) {
137 self.map.insert(MapKey::Empty(self.empty_count), None);
138 self.empty_count += 1;
139 self.map.swap_remove(key).flatten()
140 } else {
141 None
142 }
143 }
144
145 pub fn clear(&mut self) {
148 self.map.clear();
149 self.map.shrink_to_fit();
150 self.empty_count = 0
151 }
152
153 pub fn get(&self, key: &JsValue) -> Option<&V> {
158 self.map.get(key).map(Option::as_ref).flatten()
159 }
160
161 pub fn get_index(&self, index: usize) -> Option<(&JsValue, &V)> {
167 if let (MapKey::Key(key), Some(value)) = self.map.get_index(index)? {
168 Some((key, value))
169 } else {
170 None
171 }
172 }
173
174 pub fn iter(&self) -> impl Iterator<Item = (&JsValue, &V)> {
176 self.map.iter().filter_map(|o| {
177 if let (MapKey::Key(key), Some(value)) = o {
178 Some((key, value))
179 } else {
180 None
181 }
182 })
183 }
184
185 pub fn contains_key(&self, key: &JsValue) -> bool {
189 self.map.contains_key(key)
190 }
191
192 pub(crate) fn lock(&mut self, map: JsObject) -> MapLock {
196 self.lock += 1;
197 MapLock(map)
198 }
199
200 fn unlock(&mut self) {
202 self.lock -= 1;
203 if self.lock == 0 {
204 self.map.retain(|k, _| matches!(k, MapKey::Key(_)));
205 self.empty_count = 0;
206 }
207 }
208}
209
210#[derive(Debug, Trace)]
212pub(crate) struct MapLock(JsObject);
213
214impl Clone for MapLock {
215 fn clone(&self) -> Self {
216 let mut map = self.0.borrow_mut();
217 let map = map.as_map_mut().expect("MapLock does not point to a map");
218 map.lock(self.0.clone())
219 }
220}
221
222impl Finalize for MapLock {
223 fn finalize(&self) {
224 let mut map = self.0.borrow_mut();
225 let map = map.as_map_mut().expect("MapLock does not point to a map");
226 map.unlock();
227 }
228}