1use std::sync::atomic::{fence, AtomicPtr, AtomicU64, AtomicUsize, Ordering};
23use std::sync::{LazyLock, Once};
24
25use catallaxy_gc::{RootSource, Tracer};
26use parking_lot::Mutex;
27use rustc_hash::FxHashMap;
28
29use crate::buf::{release, WordBuf};
30use crate::object::{mark_value, str_of};
31use crate::ownership::Access;
32use crate::value::Value;
33
34pub const MAX_SHAPE_KEYS: usize = 32;
36const MAX_SHAPES: usize = 1 << 16;
39
40pub struct Shape {
43 pub id: u32,
44 keys: Vec<(u64, Value)>,
45 transitions: Mutex<FxHashMap<Box<str>, &'static Shape>>,
46}
47
48struct ShapeRegistry {
49 count: usize,
50 keys: Vec<Value>,
52}
53
54static REGISTRY: Mutex<ShapeRegistry> = Mutex::new(ShapeRegistry { count: 1, keys: Vec::new() });
55static ROOT_SHAPE: LazyLock<&'static Shape> =
56 LazyLock::new(|| Box::leak(Box::new(Shape { id: 0, keys: Vec::new(), transitions: Mutex::default() })));
57static SHAPE_ROOTS: Once = Once::new();
58
59struct ShapeKeyRoots;
60impl RootSource for ShapeKeyRoots {
61 fn trace_roots(&self, t: &mut Tracer) {
62 for &k in REGISTRY.lock().keys.iter() {
64 mark_value(t, k);
65 }
66 }
67}
68
69impl Shape {
70 pub fn len(&self) -> usize {
71 self.keys.len()
72 }
73 pub fn is_empty(&self) -> bool {
74 self.keys.is_empty()
75 }
76
77 fn add(&'static self, hash: u64, key: Value) -> Option<&'static Shape> {
80 let k = str_of(key)?;
81 let mut tr = self.transitions.lock();
82 if let Some(&next) = tr.get(k) {
83 return Some(next);
84 }
85 let mut reg = REGISTRY.lock();
86 if reg.count >= MAX_SHAPES {
87 return None;
88 }
89 SHAPE_ROOTS.call_once(|| catallaxy_gc::add_global_roots(Box::new(ShapeKeyRoots)));
90 if let Some(h) = key.header() {
92 h.tenure();
93 }
94 let id = reg.count as u32;
95 reg.count += 1;
96 reg.keys.push(key);
97 let mut keys = self.keys.clone();
98 keys.push((hash, key));
99 let next: &'static Shape = Box::leak(Box::new(Shape { id, keys, transitions: Mutex::default() }));
100 tr.insert(k.into(), next);
101 Some(next)
102 }
103}
104
105#[derive(Copy, Clone)]
106pub struct Entry {
107 pub hash: u64,
108 pub key: Value,
109 pub value: Value,
110}
111
112const TOMB: Entry = Entry { hash: 0, key: Value::TOMBSTONE, value: Value::NIL };
113
114const EMPTY_IX: u64 = u64::MAX;
116const W: usize = 3;
118
119pub struct DictTable {
120 seq: AtomicU64,
123 live: AtomicUsize,
124 used: AtomicUsize,
126 entries: AtomicPtr<WordBuf>,
128 indices: AtomicPtr<WordBuf>,
130 shape: AtomicPtr<Shape>,
132}
133
134pub enum Probe {
136 Found(usize),
138 Absent,
140 Check(Vec<usize>),
142}
143
144impl Default for DictTable {
145 fn default() -> Self {
146 DictTable::new()
147 }
148}
149
150fn index_cap(n: usize) -> usize {
151 (n * 3 / 2 + 1).next_power_of_two().max(8)
152}
153
154impl DictTable {
155 pub fn new() -> Self {
156 DictTable {
157 seq: AtomicU64::new(0),
158 live: AtomicUsize::new(0),
159 used: AtomicUsize::new(0),
160 entries: AtomicPtr::new(WordBuf::empty()),
161 indices: AtomicPtr::new(WordBuf::empty()),
162 shape: AtomicPtr::new(std::ptr::null_mut()),
163 }
164 }
165
166 pub fn with_capacity(n: usize) -> Self {
167 let t = DictTable::new();
168 if n > 0 {
169 t.entries.store(WordBuf::alloc(n * W, Value::NIL.0), Ordering::Relaxed);
170 t.indices.store(WordBuf::alloc(index_cap(n), EMPTY_IX), Ordering::Relaxed);
171 }
172 t
173 }
174
175 pub fn record(n: usize) -> Self {
177 let t = DictTable::new();
178 t.entries.store(WordBuf::alloc(n, Value::NIL.0), Ordering::Relaxed);
179 t.shape.store(*ROOT_SHAPE as *const Shape as *mut Shape, Ordering::Relaxed);
180 t
181 }
182
183 #[inline]
189 fn ents(&self) -> &[AtomicU64] {
190 unsafe { WordBuf::slots(self.entries.load(Ordering::Acquire)) }
191 }
192
193 #[inline]
195 pub fn shape(&self) -> Option<&'static Shape> {
196 let p = self.shape.load(Ordering::Acquire);
197 (!p.is_null()).then(|| unsafe { &*p })
198 }
199
200 #[inline]
202 pub fn slot_value(&self, slot: usize) -> Option<Value> {
203 if slot >= self.used.load(Ordering::Relaxed) {
204 return None;
205 }
206 self.ents().get(slot).map(|w| Value(w.load(Ordering::Relaxed)))
207 }
208
209 pub fn len(&self) -> usize {
210 self.live.load(Ordering::Relaxed)
211 }
212 pub fn is_empty(&self) -> bool {
213 self.len() == 0
214 }
215 pub fn version(&self) -> u64 {
216 self.seq.load(Ordering::Acquire)
217 }
218
219 pub fn heap_bytes(&self) -> usize {
220 unsafe {
221 8 * (WordBuf::cap(self.entries.load(Ordering::Relaxed))
222 + WordBuf::cap(self.indices.load(Ordering::Relaxed)))
223 }
224 }
225
226 pub fn trace(&self, t: &mut Tracer) {
228 for e in self.iter() {
230 mark_value(t, e.key);
231 mark_value(t, e.value);
232 }
233 }
234
235 pub fn iter(&self) -> impl Iterator<Item = Entry> + '_ {
237 (0..self.raw_len()).filter_map(|i| self.raw_entry(i))
238 }
239
240 pub fn entry(&self, idx: usize) -> Entry {
242 let ents = self.ents();
243 match self.shape() {
244 Some(shape) => match (shape.keys.get(idx), ents.get(idx)) {
245 (Some(&(hash, key)), Some(v)) => Entry { hash, key, value: Value(v.load(Ordering::Relaxed)) },
246 _ => TOMB,
247 },
248 None => match ents.get(idx * W..idx * W + W) {
249 Some(w) => Entry {
250 hash: w[0].load(Ordering::Relaxed),
251 key: Value(w[1].load(Ordering::Relaxed)),
252 value: Value(w[2].load(Ordering::Relaxed)),
253 },
254 None => TOMB,
255 },
256 }
257 }
258
259 pub fn raw_len(&self) -> usize {
262 self.used.load(Ordering::Relaxed)
263 }
264 pub fn raw_entry(&self, i: usize) -> Option<Entry> {
265 if i >= self.raw_len() {
266 return None;
267 }
268 Some(self.entry(i)).filter(|e| e.key != Value::TOMBSTONE)
269 }
270
271 pub fn first_live(&self) -> Option<usize> {
272 (0..self.raw_len()).find(|&i| self.raw_entry(i).is_some())
273 }
274
275 pub fn probe(&self, hash: u64, key: Value) -> Probe {
276 let mut check = Vec::new();
277 if let Some(shape) = self.shape() {
278 let n = self.raw_len().min(shape.keys.len());
279 for (i, &(h, k)) in shape.keys[..n].iter().enumerate() {
280 if h == hash {
281 if k == key {
282 return Probe::Found(i);
283 }
284 check.push(i);
285 }
286 }
287 return if check.is_empty() { Probe::Absent } else { Probe::Check(check) };
288 }
289 let idx = unsafe { WordBuf::slots(self.indices.load(Ordering::Acquire)) };
290 if idx.is_empty() {
291 return Probe::Absent;
292 }
293 let mask = idx.len() - 1;
294 let mut i = (hash as usize) & mask;
295 let mut perturb = hash;
296 for _ in 0..idx.len() {
298 let ix = idx[i].load(Ordering::Relaxed);
299 if ix == EMPTY_IX {
300 break;
301 }
302 let e = self.entry(ix as usize);
303 if e.key != Value::TOMBSTONE && e.hash == hash {
304 if e.key == key {
305 return Probe::Found(ix as usize);
306 }
307 check.push(ix as usize);
308 }
309 perturb >>= 5;
310 i = (i.wrapping_mul(5).wrapping_add(1).wrapping_add(perturb as usize)) & mask;
311 }
312 if check.is_empty() {
313 Probe::Absent
314 } else {
315 Probe::Check(check)
316 }
317 }
318
319 pub fn read_optimistic<R>(&self, f: &impl Fn(&DictTable) -> R) -> Option<R> {
322 for _ in 0..4 {
323 let s1 = self.seq.load(Ordering::Acquire);
324 if s1 & 1 == 1 {
325 std::hint::spin_loop();
326 continue;
327 }
328 let r = f(self);
329 fence(Ordering::Acquire);
330 if self.seq.load(Ordering::Relaxed) == s1 {
331 return Some(r);
332 }
333 }
334 None
335 }
336
337 fn begin(&self, access: Access) -> u64 {
342 let s = self.seq.load(Ordering::Relaxed);
343 self.seq.store(s + 1, Ordering::Relaxed);
344 if access == Access::Shared {
345 fence(Ordering::Release);
346 }
347 s
348 }
349
350 fn end(&self, s: u64) {
351 self.seq.store(s + 2, Ordering::Release);
352 }
353
354 pub unsafe fn set_value_at(&self, idx: usize, v: Value, access: Access) {
357 let s = self.begin(access);
358 let slot = if self.shape().is_some() { idx } else { idx * W + 2 };
359 if let Some(w) = self.ents().get(slot) {
360 w.store(v.0, Ordering::Relaxed);
361 }
362 self.end(s);
363 }
364
365 pub unsafe fn insert_new(&self, hash: u64, key: Value, value: Value, access: Access) {
370 let s = self.begin(access);
371 if let Some(shape) = self.shape() {
372 if shape.len() < MAX_SHAPE_KEYS {
373 if let Some(next) = shape.add(hash, key) {
374 let n = self.raw_len();
375 let cap = WordBuf::cap(self.entries.load(Ordering::Relaxed));
376 if n == cap {
377 let buf = WordBuf::alloc((cap * 2).max(4), Value::NIL.0);
378 for (d, w) in WordBuf::slots(buf).iter().zip(self.ents()) {
379 d.store(w.load(Ordering::Relaxed), Ordering::Relaxed);
380 }
381 release(self.entries.swap(buf, Ordering::AcqRel), access);
382 }
383 self.ents()[n].store(value.0, Ordering::Relaxed);
384 self.shape.store(next as *const Shape as *mut Shape, Ordering::Release);
385 self.used.store(n + 1, Ordering::Relaxed);
386 self.live.fetch_add(1, Ordering::Relaxed);
387 self.end(s);
388 return;
389 }
390 }
391 self.leave_shape_mode(access);
392 }
393 let n = self.raw_len();
394 let icap = WordBuf::cap(self.indices.load(Ordering::Relaxed));
395 let ecap = WordBuf::cap(self.entries.load(Ordering::Relaxed)) / W;
396 if (n + 1) * 3 >= icap * 2 || n == ecap {
397 self.rebuild(self.len() + 1, access);
398 }
399 let n = self.raw_len();
400 let w = &self.ents()[n * W..n * W + W];
401 w[0].store(hash, Ordering::Relaxed);
402 w[1].store(key.0, Ordering::Relaxed);
403 w[2].store(value.0, Ordering::Relaxed);
404 Self::place(WordBuf::slots(self.indices.load(Ordering::Relaxed)), hash, n);
405 self.used.store(n + 1, Ordering::Relaxed);
406 self.live.fetch_add(1, Ordering::Relaxed);
407 self.end(s);
408 }
409
410 fn place(idx: &[AtomicU64], hash: u64, at: usize) {
411 let mask = idx.len() - 1;
412 let mut i = (hash as usize) & mask;
413 let mut perturb = hash;
414 while idx[i].load(Ordering::Relaxed) != EMPTY_IX {
415 perturb >>= 5;
416 i = (i.wrapping_mul(5).wrapping_add(1).wrapping_add(perturb as usize)) & mask;
417 }
418 idx[i].store(at as u64, Ordering::Relaxed);
419 }
420
421 unsafe fn rebuild(&self, want: usize, access: Access) {
424 let live: Vec<Entry> = self.iter().collect();
425 let ents = WordBuf::alloc((want.max(live.len()) * 2).max(4) * W, Value::NIL.0);
426 let idx = WordBuf::alloc(index_cap(want.max(live.len()) * 2), EMPTY_IX);
427 let (ew, iw) = (WordBuf::slots(ents), WordBuf::slots(idx));
428 for (i, e) in live.iter().enumerate() {
429 ew[i * W].store(e.hash, Ordering::Relaxed);
430 ew[i * W + 1].store(e.key.0, Ordering::Relaxed);
431 ew[i * W + 2].store(e.value.0, Ordering::Relaxed);
432 Self::place(iw, e.hash, i);
433 }
434 release(self.entries.swap(ents, Ordering::AcqRel), access);
435 release(self.indices.swap(idx, Ordering::AcqRel), access);
436 self.used.store(live.len(), Ordering::Relaxed);
437 }
438
439 unsafe fn leave_shape_mode(&self, access: Access) {
441 if self.shape().is_none() {
442 return;
443 }
444 let n = self.raw_len();
445 let ents = WordBuf::alloc((n + 1).max(4) * W, Value::NIL.0);
446 let idx = WordBuf::alloc(index_cap(n + 1), EMPTY_IX);
447 let (ew, iw) = (WordBuf::slots(ents), WordBuf::slots(idx));
448 for i in 0..n {
449 let e = self.entry(i);
450 ew[i * W].store(e.hash, Ordering::Relaxed);
451 ew[i * W + 1].store(e.key.0, Ordering::Relaxed);
452 ew[i * W + 2].store(e.value.0, Ordering::Relaxed);
453 Self::place(iw, e.hash, i);
454 }
455 release(self.indices.swap(idx, Ordering::AcqRel), access);
456 let old = self.entries.swap(ents, Ordering::AcqRel);
457 self.shape.store(std::ptr::null_mut(), Ordering::Release);
458 release(old, access);
459 }
460
461 pub unsafe fn remove_at(&self, idx: usize, access: Access) -> Value {
466 let s = self.begin(access);
467 self.leave_shape_mode(access);
468 let mut v = Value::NIL;
469 if let Some(w) = self.ents().get(idx * W..idx * W + W) {
470 v = Value(w[2].load(Ordering::Relaxed));
471 w[1].store(Value::TOMBSTONE.0, Ordering::Relaxed);
473 w[2].store(Value::NIL.0, Ordering::Relaxed);
474 self.live.fetch_sub(1, Ordering::Relaxed);
475 }
476 self.end(s);
477 v
478 }
479
480 pub unsafe fn clear(&self, access: Access) {
483 let s = self.begin(access);
484 if self.shape().is_some() {
485 self.shape.store(*ROOT_SHAPE as *const Shape as *mut Shape, Ordering::Release);
486 }
487 self.used.store(0, Ordering::Relaxed);
488 self.live.store(0, Ordering::Relaxed);
489 release(self.entries.swap(WordBuf::empty(), Ordering::AcqRel), access);
490 release(self.indices.swap(WordBuf::empty(), Ordering::AcqRel), access);
491 self.end(s);
492 }
493}
494
495impl Drop for DictTable {
496 fn drop(&mut self) {
497 unsafe {
498 WordBuf::free(*self.entries.get_mut() as *mut u8);
499 WordBuf::free(*self.indices.get_mut() as *mut u8);
500 }
501 }
502}
503
504impl Clone for DictTable {
505 fn clone(&self) -> Self {
506 let n = self.raw_len();
507 if let Some(shape) = self.shape() {
508 let t = DictTable::record(n);
509 for (d, w) in t.ents().iter().zip(&self.ents()[..n.min(self.ents().len())]) {
510 d.store(w.load(Ordering::Relaxed), Ordering::Relaxed);
511 }
512 t.shape.store(shape as *const Shape as *mut Shape, Ordering::Relaxed);
513 t.used.store(n, Ordering::Relaxed);
514 t.live.store(self.len(), Ordering::Relaxed);
515 return t;
516 }
517 let t = DictTable::with_capacity(self.len());
518 for e in self.iter() {
519 unsafe { t.insert_new(e.hash, e.key, e.value, Access::Local) };
521 }
522 t
523 }
524}