1use std::sync::{Arc, LazyLock, Once};
16
17use catallaxy_gc::{RootSource, Tracer};
18use parking_lot::Mutex;
19use rustc_hash::FxHashMap;
20
21use crate::object::{mark_value, str_of};
22use crate::value::Value;
23
24pub const MAX_SHAPE_KEYS: usize = 32;
26const MAX_SHAPES: usize = 1 << 16;
29
30pub struct Shape {
33 pub id: u32,
34 keys: Vec<(u64, Value)>,
35 transitions: Mutex<FxHashMap<Box<str>, Arc<Shape>>>,
36}
37
38struct ShapeRegistry {
39 count: usize,
40 keys: Vec<Value>,
42}
43
44static REGISTRY: Mutex<ShapeRegistry> = Mutex::new(ShapeRegistry { count: 1, keys: Vec::new() });
45static ROOT_SHAPE: LazyLock<Arc<Shape>> =
46 LazyLock::new(|| Arc::new(Shape { id: 0, keys: Vec::new(), transitions: Mutex::new(FxHashMap::default()) }));
47static SHAPE_ROOTS: Once = Once::new();
48
49struct ShapeKeyRoots;
50impl RootSource for ShapeKeyRoots {
51 fn trace_roots(&self, t: &mut Tracer) {
52 for &k in REGISTRY.lock().keys.iter() {
54 mark_value(t, k);
55 }
56 }
57}
58
59impl Shape {
60 pub fn len(&self) -> usize {
61 self.keys.len()
62 }
63 pub fn is_empty(&self) -> bool {
64 self.keys.is_empty()
65 }
66
67 #[inline]
69 pub fn slot_of(&self, hash: u64, k: &str) -> Option<usize> {
70 self.keys.iter().position(|&(h, key)| h == hash && str_of(key) == Some(k))
71 }
72
73 fn add(self: &Arc<Shape>, hash: u64, key: Value) -> Option<Arc<Shape>> {
75 let k = str_of(key)?;
76 let mut tr = self.transitions.lock();
77 if let Some(next) = tr.get(k) {
78 return Some(next.clone());
79 }
80 let mut reg = REGISTRY.lock();
81 if reg.count >= MAX_SHAPES {
82 return None;
83 }
84 SHAPE_ROOTS.call_once(|| catallaxy_gc::add_global_roots(Box::new(ShapeKeyRoots)));
85 let id = reg.count as u32;
86 reg.count += 1;
87 reg.keys.push(key);
88 let mut keys = self.keys.clone();
89 keys.push((hash, key));
90 let next = Arc::new(Shape { id, keys, transitions: Mutex::new(FxHashMap::default()) });
91 tr.insert(k.into(), next.clone());
92 Some(next)
93 }
94}
95
96#[derive(Copy, Clone)]
97pub struct Entry {
98 pub hash: u64,
99 pub key: Value,
100 pub value: Value,
101}
102
103const EMPTY: u32 = u32::MAX;
104
105#[derive(Default)]
106pub struct DictTable {
107 entries: Vec<Entry>,
108 indices: Vec<u32>,
109 live: usize,
110 version: u64,
111 shape: Option<Arc<Shape>>,
113 values: Vec<Value>,
114}
115
116pub enum Probe {
118 Found(usize),
120 Absent,
122 Check(Vec<usize>),
124}
125
126impl DictTable {
127 pub fn new() -> Self {
128 DictTable::default()
129 }
130
131 pub fn with_capacity(n: usize) -> Self {
132 let mut t = DictTable::default();
133 if n > 0 {
134 t.entries.reserve(n);
135 t.indices = vec![EMPTY; (n * 3 / 2).next_power_of_two().max(8)];
136 }
137 t
138 }
139
140 pub fn record(n: usize) -> Self {
142 DictTable { shape: Some(ROOT_SHAPE.clone()), values: Vec::with_capacity(n), ..DictTable::default() }
143 }
144
145 #[inline]
147 pub fn shape(&self) -> Option<&Arc<Shape>> {
148 self.shape.as_ref()
149 }
150
151 #[inline]
153 pub fn slot_value(&self, slot: usize) -> Option<Value> {
154 self.values.get(slot).copied()
155 }
156
157 fn leave_shape_mode(&mut self) {
159 let Some(shape) = self.shape.take() else { return };
160 let values = std::mem::take(&mut self.values);
161 let n = values.len();
162 self.entries = Vec::with_capacity(n + 1);
163 self.indices = vec![EMPTY; ((n + 1) * 3).next_power_of_two().max(8)];
164 for (i, (&(hash, key), value)) in shape.keys.iter().zip(values).enumerate() {
165 self.entries.push(Entry { hash, key, value });
166 self.place(hash, i as u32);
167 }
168 crate::note_external(self.heap_bytes());
169 }
170
171 pub fn len(&self) -> usize {
172 self.live
173 }
174 pub fn is_empty(&self) -> bool {
175 self.live == 0
176 }
177 pub fn version(&self) -> u64 {
178 self.version
179 }
180
181 pub fn heap_bytes(&self) -> usize {
182 self.entries.capacity() * std::mem::size_of::<Entry>() + self.indices.capacity() * 4 + self.values.capacity() * 8
183 }
184
185 pub fn trace(&self, t: &mut Tracer) {
186 for &v in &self.values {
188 mark_value(t, v);
189 }
190 for e in &self.entries {
191 if e.key != Value::TOMBSTONE {
192 mark_value(t, e.key);
193 mark_value(t, e.value);
194 }
195 }
196 }
197
198 pub fn iter(&self) -> impl Iterator<Item = Entry> + '_ {
200 (0..self.raw_len()).filter_map(|i| self.raw_entry(i))
201 }
202
203 pub fn entry(&self, idx: usize) -> Entry {
204 match &self.shape {
205 Some(shape) => {
206 let (hash, key) = shape.keys[idx];
207 Entry { hash, key, value: self.values[idx] }
208 }
209 None => self.entries[idx],
210 }
211 }
212
213 pub fn set_value_at(&mut self, idx: usize, v: Value) {
214 match self.shape {
215 Some(_) => self.values[idx] = v,
216 None => self.entries[idx].value = v,
217 }
218 }
219
220 pub fn raw_len(&self) -> usize {
222 match self.shape {
223 Some(_) => self.values.len(),
224 None => self.entries.len(),
225 }
226 }
227 pub fn raw_entry(&self, i: usize) -> Option<Entry> {
228 match self.shape {
229 Some(_) => (i < self.values.len()).then(|| self.entry(i)),
230 None => self.entries.get(i).filter(|e| e.key != Value::TOMBSTONE).copied(),
231 }
232 }
233
234 pub fn probe(&self, hash: u64, key: Value) -> Probe {
235 if let Some(shape) = &self.shape {
236 return match str_of(key).and_then(|k| shape.slot_of(hash, k)) {
238 Some(i) => Probe::Found(i),
239 None => Probe::Absent,
240 };
241 }
242 if self.indices.is_empty() {
243 return Probe::Absent;
244 }
245 let mask = self.indices.len() - 1;
246 let mut i = (hash as usize) & mask;
247 let mut perturb = hash;
248 let mut check = Vec::new();
249 loop {
250 let ix = self.indices[i];
251 if ix == EMPTY {
252 return if check.is_empty() { Probe::Absent } else { Probe::Check(check) };
253 }
254 let e = &self.entries[ix as usize];
255 if e.key != Value::TOMBSTONE && e.hash == hash {
256 if e.key == key {
257 return Probe::Found(ix as usize);
258 }
259 check.push(ix as usize);
260 }
261 perturb >>= 5;
262 i = (i.wrapping_mul(5).wrapping_add(1).wrapping_add(perturb as usize)) & mask;
263 }
264 }
265
266 pub fn insert_new(&mut self, hash: u64, key: Value, value: Value) {
268 if let Some(shape) = &self.shape {
269 if shape.len() < MAX_SHAPE_KEYS {
270 if let Some(next) = shape.add(hash, key) {
271 self.shape = Some(next);
272 self.values.push(value);
273 self.live += 1;
274 self.version += 1;
275 return;
276 }
277 }
278 self.leave_shape_mode();
279 }
280 if (self.entries.len() + 1) * 3 >= self.indices.len() * 2 {
281 self.grow();
282 }
283 let idx = self.entries.len() as u32;
284 self.entries.push(Entry { hash, key, value });
285 self.place(hash, idx);
286 self.live += 1;
287 self.version += 1;
288 }
289
290 fn place(&mut self, hash: u64, idx: u32) {
291 let mask = self.indices.len() - 1;
292 let mut i = (hash as usize) & mask;
293 let mut perturb = hash;
294 while self.indices[i] != EMPTY {
295 perturb >>= 5;
296 i = (i.wrapping_mul(5).wrapping_add(1).wrapping_add(perturb as usize)) & mask;
297 }
298 self.indices[i] = idx;
299 }
300
301 fn grow(&mut self) {
302 if self.live < self.entries.len() {
304 self.entries.retain(|e| e.key != Value::TOMBSTONE);
305 }
306 let want = ((self.live + 1) * 3).next_power_of_two().max(8);
307 self.indices = vec![EMPTY; want];
308 for i in 0..self.entries.len() {
309 let h = self.entries[i].hash;
310 self.place(h, i as u32);
311 }
312 crate::note_external(want * 4);
313 }
314
315 pub fn remove_at(&mut self, idx: usize) -> Value {
317 self.leave_shape_mode();
318 let e = &mut self.entries[idx];
319 let v = e.value;
320 e.key = Value::TOMBSTONE;
321 e.value = Value::NIL;
322 self.live -= 1;
323 self.version += 1;
324 v
326 }
327
328 pub fn clear(&mut self) {
329 if self.shape.is_some() {
330 self.shape = Some(ROOT_SHAPE.clone());
331 }
332 self.values.clear();
333 self.entries.clear();
334 self.indices.clear();
335 self.live = 0;
336 self.version += 1;
337 }
338
339 pub fn first_live(&self) -> Option<usize> {
340 if self.shape.is_some() {
341 return (!self.values.is_empty()).then_some(0);
342 }
343 self.entries.iter().position(|e| e.key != Value::TOMBSTONE)
344 }
345}
346
347impl Clone for DictTable {
348 fn clone(&self) -> Self {
349 if let Some(shape) = &self.shape {
350 return DictTable {
351 shape: Some(shape.clone()),
352 values: self.values.clone(),
353 live: self.live,
354 ..DictTable::default()
355 };
356 }
357 let mut t = DictTable::with_capacity(self.live);
358 for e in self.iter() {
359 t.insert_new(e.hash, e.key, e.value);
360 }
361 t
362 }
363}