boa_engine/builtins/map/
ordered_map.rs1use crate::{JsData, JsValue, object::JsObject};
4use boa_gc::{Finalize, Trace, custom_trace};
5use indexmap::{Equivalent, IndexMap};
6use std::{
7 fmt::Debug,
8 hash::{Hash, Hasher},
9};
10
11#[derive(PartialEq, Eq, Clone, Debug)]
12pub(crate) enum MapKey {
13 Key(JsValue),
14 Empty(usize), }
16
17impl Hash for MapKey {
19 fn hash<H: Hasher>(&self, state: &mut H) {
20 match self {
21 Self::Key(v) => v.hash(state),
22 Self::Empty(e) => e.hash(state),
23 }
24 }
25}
26
27impl Equivalent<MapKey> for JsValue {
28 fn equivalent(&self, key: &MapKey) -> bool {
29 match key {
30 MapKey::Key(v) => v == self,
31 MapKey::Empty(_) => false,
32 }
33 }
34}
35
36#[derive(Clone, Finalize, JsData)]
38pub struct OrderedMap<V> {
39 map: IndexMap<MapKey, Option<V>>,
40 lock: u32,
41 empty_count: usize,
42}
43
44unsafe impl<V: Trace> Trace for OrderedMap<V> {
45 custom_trace!(this, mark, {
46 for (k, v) in &this.map {
47 if let MapKey::Key(key) = k {
48 mark(key);
49 }
50 mark(v);
51 }
52 });
53}
54
55impl<V: Debug> Debug for OrderedMap<V> {
56 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
57 self.map.fmt(formatter)
58 }
59}
60
61impl<V> Default for OrderedMap<V> {
62 fn default() -> Self {
63 Self::new()
64 }
65}
66
67impl<V> OrderedMap<V> {
68 #[must_use]
70 pub fn new() -> Self {
71 Self {
72 map: IndexMap::new(),
73 lock: 0,
74 empty_count: 0,
75 }
76 }
77
78 #[must_use]
80 pub fn with_capacity(capacity: usize) -> Self {
81 Self {
82 map: IndexMap::with_capacity(capacity),
83 lock: 0,
84 empty_count: 0,
85 }
86 }
87
88 #[must_use]
92 pub fn full_len(&self) -> usize {
93 self.map.len()
94 }
95
96 #[must_use]
100 pub fn len(&self) -> usize {
101 self.map.len() - self.empty_count
102 }
103
104 #[must_use]
108 pub fn is_empty(&self) -> bool {
109 self.len() == 0
110 }
111
112 pub fn insert(&mut self, key: JsValue, value: V) -> Option<V> {
123 self.map.insert(MapKey::Key(key), Some(value)).flatten()
124 }
125
126 pub fn remove(&mut self, key: &JsValue) -> Option<V> {
137 if self.lock == 0 {
138 self.map.shift_remove(key).flatten()
139 } else if self.map.contains_key(key) {
140 self.map.insert(MapKey::Empty(self.empty_count), None);
141 self.empty_count += 1;
142 self.map.swap_remove(key).flatten()
143 } else {
144 None
145 }
146 }
147
148 pub fn clear(&mut self) {
151 self.map.clear();
152 self.map.shrink_to_fit();
153 self.empty_count = 0;
154 }
155
156 pub fn get(&self, key: &JsValue) -> Option<&V> {
161 self.map.get(key).and_then(Option::as_ref)
162 }
163
164 #[must_use]
170 pub fn get_index(&self, index: usize) -> Option<(&JsValue, &V)> {
171 if let (MapKey::Key(key), Some(value)) = self.map.get_index(index)? {
172 Some((key, value))
173 } else {
174 None
175 }
176 }
177
178 pub fn iter(&self) -> impl Iterator<Item = (&JsValue, &V)> {
180 self.map.iter().filter_map(|o| {
181 if let (MapKey::Key(key), Some(value)) = o {
182 Some((key, value))
183 } else {
184 None
185 }
186 })
187 }
188
189 #[must_use]
193 pub fn contains_key(&self, key: &JsValue) -> bool {
194 self.map.contains_key(key)
195 }
196
197 pub(crate) fn lock(&mut self) {
201 self.lock += 1;
202 }
203
204 pub(crate) fn unlock(&mut self) {
206 self.lock -= 1;
207 if self.lock == 0 {
208 self.map.retain(|k, _| matches!(k, MapKey::Key(_)));
209 self.empty_count = 0;
210 }
211 }
212}
213
214pub(crate) struct MapLock<'a, V: Trace + 'static>(&'a JsObject<OrderedMap<V>>);
215
216impl<'a, V: Trace + 'static> MapLock<'a, V> {
217 pub(crate) fn new(js_object: &'a JsObject<OrderedMap<V>>) -> Self {
218 js_object.borrow_mut().data_mut().lock();
219 Self(js_object)
220 }
221}
222
223impl<V: Trace + 'static> Drop for MapLock<'_, V> {
224 fn drop(&mut self) {
225 self.0.borrow_mut().data_mut().unlock();
226 }
227}