catallaxy_runtime/
dict.rs1use catallaxy_gc::Tracer;
10
11use crate::object::mark_value;
12use crate::value::Value;
13
14#[derive(Copy, Clone)]
15pub struct Entry {
16 pub hash: u64,
17 pub key: Value,
18 pub value: Value,
19}
20
21const EMPTY: u32 = u32::MAX;
22
23#[derive(Default)]
24pub struct DictTable {
25 entries: Vec<Entry>,
26 indices: Vec<u32>,
27 live: usize,
28 version: u64,
29}
30
31pub enum Probe {
33 Found(usize),
35 Absent,
37 Check(Vec<usize>),
39}
40
41impl DictTable {
42 pub fn new() -> Self {
43 DictTable::default()
44 }
45
46 pub fn with_capacity(n: usize) -> Self {
47 let mut t = DictTable::default();
48 if n > 0 {
49 t.entries.reserve(n);
50 t.indices = vec![EMPTY; (n * 3 / 2).next_power_of_two().max(8)];
51 }
52 t
53 }
54
55 pub fn len(&self) -> usize {
56 self.live
57 }
58 pub fn is_empty(&self) -> bool {
59 self.live == 0
60 }
61 pub fn version(&self) -> u64 {
62 self.version
63 }
64
65 pub fn heap_bytes(&self) -> usize {
66 self.entries.capacity() * std::mem::size_of::<Entry>() + self.indices.capacity() * 4
67 }
68
69 pub fn trace(&self, t: &mut Tracer) {
70 for e in &self.entries {
71 if e.key != Value::TOMBSTONE {
72 mark_value(t, e.key);
73 mark_value(t, e.value);
74 }
75 }
76 }
77
78 pub fn iter(&self) -> impl Iterator<Item = &Entry> {
80 self.entries.iter().filter(|e| e.key != Value::TOMBSTONE)
81 }
82
83 pub fn entry(&self, idx: usize) -> &Entry {
84 &self.entries[idx]
85 }
86
87 pub fn set_value_at(&mut self, idx: usize, v: Value) {
88 self.entries[idx].value = v;
89 }
90
91 pub fn raw_len(&self) -> usize {
93 self.entries.len()
94 }
95 pub fn raw_entry(&self, i: usize) -> Option<&Entry> {
96 self.entries.get(i).filter(|e| e.key != Value::TOMBSTONE)
97 }
98
99 pub fn probe(&self, hash: u64, key: Value) -> Probe {
100 if self.indices.is_empty() {
101 return Probe::Absent;
102 }
103 let mask = self.indices.len() - 1;
104 let mut i = (hash as usize) & mask;
105 let mut perturb = hash;
106 let mut check = Vec::new();
107 loop {
108 let ix = self.indices[i];
109 if ix == EMPTY {
110 return if check.is_empty() { Probe::Absent } else { Probe::Check(check) };
111 }
112 let e = &self.entries[ix as usize];
113 if e.key != Value::TOMBSTONE && e.hash == hash {
114 if e.key == key {
115 return Probe::Found(ix as usize);
116 }
117 check.push(ix as usize);
118 }
119 perturb >>= 5;
120 i = (i.wrapping_mul(5).wrapping_add(1).wrapping_add(perturb as usize)) & mask;
121 }
122 }
123
124 pub fn insert_new(&mut self, hash: u64, key: Value, value: Value) {
126 if (self.entries.len() + 1) * 3 >= self.indices.len() * 2 {
127 self.grow();
128 }
129 let idx = self.entries.len() as u32;
130 self.entries.push(Entry { hash, key, value });
131 self.place(hash, idx);
132 self.live += 1;
133 self.version += 1;
134 }
135
136 fn place(&mut self, hash: u64, idx: u32) {
137 let mask = self.indices.len() - 1;
138 let mut i = (hash as usize) & mask;
139 let mut perturb = hash;
140 while self.indices[i] != EMPTY {
141 perturb >>= 5;
142 i = (i.wrapping_mul(5).wrapping_add(1).wrapping_add(perturb as usize)) & mask;
143 }
144 self.indices[i] = idx;
145 }
146
147 fn grow(&mut self) {
148 if self.live < self.entries.len() {
150 self.entries.retain(|e| e.key != Value::TOMBSTONE);
151 }
152 let want = ((self.live + 1) * 3).next_power_of_two().max(8);
153 self.indices = vec![EMPTY; want];
154 for i in 0..self.entries.len() {
155 let h = self.entries[i].hash;
156 self.place(h, i as u32);
157 }
158 crate::note_external(want * 4);
159 }
160
161 pub fn remove_at(&mut self, idx: usize) -> Value {
163 let e = &mut self.entries[idx];
164 let v = e.value;
165 e.key = Value::TOMBSTONE;
166 e.value = Value::NIL;
167 self.live -= 1;
168 self.version += 1;
169 v
171 }
172
173 pub fn clear(&mut self) {
174 self.entries.clear();
175 self.indices.clear();
176 self.live = 0;
177 self.version += 1;
178 }
179
180 pub fn first_live(&self) -> Option<usize> {
181 self.entries.iter().position(|e| e.key != Value::TOMBSTONE)
182 }
183}
184
185impl Clone for DictTable {
186 fn clone(&self) -> Self {
187 let mut t = DictTable::with_capacity(self.live);
188 for e in self.iter() {
189 t.insert_new(e.hash, e.key, e.value);
190 }
191 t
192 }
193}