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)) + WordBuf::cap(self.indices.load(Ordering::Relaxed)))
222 }
223 }
224
225 pub fn trace(&self, t: &mut Tracer) {
227 for e in self.iter() {
229 mark_value(t, e.key);
230 mark_value(t, e.value);
231 }
232 }
233
234 pub fn iter(&self) -> impl Iterator<Item = Entry> + '_ {
236 (0..self.raw_len()).filter_map(|i| self.raw_entry(i))
237 }
238
239 pub fn entry(&self, idx: usize) -> Entry {
241 let ents = self.ents();
242 match self.shape() {
243 Some(shape) => match (shape.keys.get(idx), ents.get(idx)) {
244 (Some(&(hash, key)), Some(v)) => Entry { hash, key, value: Value(v.load(Ordering::Relaxed)) },
245 _ => TOMB,
246 },
247 None => match ents.get(idx * W..idx * W + W) {
248 Some(w) => Entry {
249 hash: w[0].load(Ordering::Relaxed),
250 key: Value(w[1].load(Ordering::Relaxed)),
251 value: Value(w[2].load(Ordering::Relaxed)),
252 },
253 None => TOMB,
254 },
255 }
256 }
257
258 pub fn raw_len(&self) -> usize {
261 self.used.load(Ordering::Relaxed)
262 }
263 pub fn raw_entry(&self, i: usize) -> Option<Entry> {
264 if i >= self.raw_len() {
265 return None;
266 }
267 Some(self.entry(i)).filter(|e| e.key != Value::TOMBSTONE)
268 }
269
270 pub fn first_live(&self) -> Option<usize> {
271 (0..self.raw_len()).find(|&i| self.raw_entry(i).is_some())
272 }
273
274 pub fn probe(&self, hash: u64, key: Value) -> Probe {
275 let mut check = Vec::new();
276 if let Some(shape) = self.shape() {
277 let n = self.raw_len().min(shape.keys.len());
278 for (i, &(h, k)) in shape.keys[..n].iter().enumerate() {
279 if h == hash {
280 if k == key {
281 return Probe::Found(i);
282 }
283 check.push(i);
284 }
285 }
286 return if check.is_empty() { Probe::Absent } else { Probe::Check(check) };
287 }
288 let idx = unsafe { WordBuf::slots(self.indices.load(Ordering::Acquire)) };
289 if idx.is_empty() {
290 return Probe::Absent;
291 }
292 let mask = idx.len() - 1;
293 let mut i = (hash as usize) & mask;
294 let mut perturb = hash;
295 for _ in 0..idx.len() {
297 let ix = idx[i].load(Ordering::Relaxed);
298 if ix == EMPTY_IX {
299 break;
300 }
301 let e = self.entry(ix as usize);
302 if e.key != Value::TOMBSTONE && e.hash == hash {
303 if e.key == key {
304 return Probe::Found(ix as usize);
305 }
306 check.push(ix as usize);
307 }
308 perturb >>= 5;
309 i = (i.wrapping_mul(5).wrapping_add(1).wrapping_add(perturb as usize)) & mask;
310 }
311 if check.is_empty() {
312 Probe::Absent
313 } else {
314 Probe::Check(check)
315 }
316 }
317
318 pub fn read_optimistic<R>(&self, f: &impl Fn(&DictTable) -> R) -> Option<R> {
321 for _ in 0..4 {
322 let s1 = self.seq.load(Ordering::Acquire);
323 if s1 & 1 == 1 {
324 std::hint::spin_loop();
325 continue;
326 }
327 let r = f(self);
328 fence(Ordering::Acquire);
329 if self.seq.load(Ordering::Relaxed) == s1 {
330 return Some(r);
331 }
332 }
333 None
334 }
335
336 fn begin(&self, access: Access) -> u64 {
341 let s = self.seq.load(Ordering::Relaxed);
342 self.seq.store(s + 1, Ordering::Relaxed);
343 if access == Access::Shared {
344 fence(Ordering::Release);
345 }
346 s
347 }
348
349 fn end(&self, s: u64) {
350 self.seq.store(s + 2, Ordering::Release);
351 }
352
353 pub unsafe fn set_value_at(&self, idx: usize, v: Value, access: Access) {
356 let s = self.begin(access);
357 let slot = if self.shape().is_some() { idx } else { idx * W + 2 };
358 if let Some(w) = self.ents().get(slot) {
359 w.store(v.0, Ordering::Relaxed);
360 }
361 self.end(s);
362 }
363
364 pub unsafe fn insert_new(&self, hash: u64, key: Value, value: Value, access: Access) {
369 let s = self.begin(access);
370 if let Some(shape) = self.shape() {
371 if shape.len() < MAX_SHAPE_KEYS {
372 if let Some(next) = shape.add(hash, key) {
373 let n = self.raw_len();
374 let cap = WordBuf::cap(self.entries.load(Ordering::Relaxed));
375 if n == cap {
376 let buf = WordBuf::alloc((cap * 2).max(4), Value::NIL.0);
377 for (d, w) in WordBuf::slots(buf).iter().zip(self.ents()) {
378 d.store(w.load(Ordering::Relaxed), Ordering::Relaxed);
379 }
380 release(self.entries.swap(buf, Ordering::AcqRel), access);
381 }
382 self.ents()[n].store(value.0, Ordering::Relaxed);
383 self.shape.store(next as *const Shape as *mut Shape, Ordering::Release);
384 self.used.store(n + 1, Ordering::Relaxed);
385 self.live.fetch_add(1, Ordering::Relaxed);
386 self.end(s);
387 return;
388 }
389 }
390 self.leave_shape_mode(access);
391 }
392 let n = self.raw_len();
393 let icap = WordBuf::cap(self.indices.load(Ordering::Relaxed));
394 let ecap = WordBuf::cap(self.entries.load(Ordering::Relaxed)) / W;
395 if (n + 1) * 3 >= icap * 2 || n == ecap {
396 self.rebuild(self.len() + 1, access);
397 }
398 let n = self.raw_len();
399 let w = &self.ents()[n * W..n * W + W];
400 w[0].store(hash, Ordering::Relaxed);
401 w[1].store(key.0, Ordering::Relaxed);
402 w[2].store(value.0, Ordering::Relaxed);
403 Self::place(WordBuf::slots(self.indices.load(Ordering::Relaxed)), hash, n);
404 self.used.store(n + 1, Ordering::Relaxed);
405 self.live.fetch_add(1, Ordering::Relaxed);
406 self.end(s);
407 }
408
409 fn place(idx: &[AtomicU64], hash: u64, at: usize) {
410 let mask = idx.len() - 1;
411 let mut i = (hash as usize) & mask;
412 let mut perturb = hash;
413 while idx[i].load(Ordering::Relaxed) != EMPTY_IX {
414 perturb >>= 5;
415 i = (i.wrapping_mul(5).wrapping_add(1).wrapping_add(perturb as usize)) & mask;
416 }
417 idx[i].store(at as u64, Ordering::Relaxed);
418 }
419
420 unsafe fn rebuild(&self, want: usize, access: Access) {
423 let live: Vec<Entry> = self.iter().collect();
424 let ents = WordBuf::alloc((want.max(live.len()) * 2).max(4) * W, Value::NIL.0);
425 let idx = WordBuf::alloc(index_cap(want.max(live.len()) * 2), EMPTY_IX);
426 let (ew, iw) = (WordBuf::slots(ents), WordBuf::slots(idx));
427 for (i, e) in live.iter().enumerate() {
428 ew[i * W].store(e.hash, Ordering::Relaxed);
429 ew[i * W + 1].store(e.key.0, Ordering::Relaxed);
430 ew[i * W + 2].store(e.value.0, Ordering::Relaxed);
431 Self::place(iw, e.hash, i);
432 }
433 release(self.entries.swap(ents, Ordering::AcqRel), access);
434 release(self.indices.swap(idx, Ordering::AcqRel), access);
435 self.used.store(live.len(), Ordering::Relaxed);
436 }
437
438 unsafe fn leave_shape_mode(&self, access: Access) {
440 if self.shape().is_none() {
441 return;
442 }
443 let n = self.raw_len();
444 let ents = WordBuf::alloc((n + 1).max(4) * W, Value::NIL.0);
445 let idx = WordBuf::alloc(index_cap(n + 1), EMPTY_IX);
446 let (ew, iw) = (WordBuf::slots(ents), WordBuf::slots(idx));
447 for i in 0..n {
448 let e = self.entry(i);
449 ew[i * W].store(e.hash, Ordering::Relaxed);
450 ew[i * W + 1].store(e.key.0, Ordering::Relaxed);
451 ew[i * W + 2].store(e.value.0, Ordering::Relaxed);
452 Self::place(iw, e.hash, i);
453 }
454 release(self.indices.swap(idx, Ordering::AcqRel), access);
455 let old = self.entries.swap(ents, Ordering::AcqRel);
456 self.shape.store(std::ptr::null_mut(), Ordering::Release);
457 release(old, access);
458 }
459
460 pub unsafe fn remove_at(&self, idx: usize, access: Access) -> Value {
465 let s = self.begin(access);
466 self.leave_shape_mode(access);
467 let mut v = Value::NIL;
468 if let Some(w) = self.ents().get(idx * W..idx * W + W) {
469 v = Value(w[2].load(Ordering::Relaxed));
470 w[1].store(Value::TOMBSTONE.0, Ordering::Relaxed);
472 w[2].store(Value::NIL.0, Ordering::Relaxed);
473 self.live.fetch_sub(1, Ordering::Relaxed);
474 }
475 self.end(s);
476 v
477 }
478
479 pub unsafe fn clear(&self, access: Access) {
482 let s = self.begin(access);
483 if self.shape().is_some() {
484 self.shape.store(*ROOT_SHAPE as *const Shape as *mut Shape, Ordering::Release);
485 }
486 self.used.store(0, Ordering::Relaxed);
487 self.live.store(0, Ordering::Relaxed);
488 release(self.entries.swap(WordBuf::empty(), Ordering::AcqRel), access);
489 release(self.indices.swap(WordBuf::empty(), Ordering::AcqRel), access);
490 self.end(s);
491 }
492}
493
494impl Drop for DictTable {
495 fn drop(&mut self) {
496 unsafe {
497 WordBuf::free(*self.entries.get_mut() as *mut u8);
498 WordBuf::free(*self.indices.get_mut() as *mut u8);
499 }
500 }
501}
502
503impl Clone for DictTable {
504 fn clone(&self) -> Self {
505 let n = self.raw_len();
506 if let Some(shape) = self.shape() {
507 let t = DictTable::record(n);
508 for (d, w) in t.ents().iter().zip(&self.ents()[..n.min(self.ents().len())]) {
509 d.store(w.load(Ordering::Relaxed), Ordering::Relaxed);
510 }
511 t.shape.store(shape as *const Shape as *mut Shape, Ordering::Relaxed);
512 t.used.store(n, Ordering::Relaxed);
513 t.live.store(self.len(), Ordering::Relaxed);
514 return t;
515 }
516 let t = DictTable::with_capacity(self.len());
517 for e in self.iter() {
518 unsafe { t.insert_new(e.hash, e.key, e.value, Access::Local) };
520 }
521 t
522 }
523}