lashlang/runtime/
record.rs1use super::Value;
2use rustc_hash::FxHashMap;
3use serde::{Deserialize, Serialize};
4use smallvec::SmallVec;
5use std::ops::Index;
6use std::sync::{Arc, OnceLock, RwLock};
7
8const RECORD_INDEX_THRESHOLD: usize = 8;
9const RECORD_INLINE_CAPACITY: usize = 4;
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
12pub(crate) struct Symbol(u32);
13
14#[derive(Default)]
15struct SymbolTable {
16 lookup: FxHashMap<Arc<str>, Symbol>,
17 names: Vec<Arc<str>>,
18}
19
20fn symbol_table() -> &'static RwLock<SymbolTable> {
21 static TABLE: OnceLock<RwLock<SymbolTable>> = OnceLock::new();
22 TABLE.get_or_init(|| RwLock::new(SymbolTable::default()))
23}
24
25pub(crate) fn lookup_symbol(name: &str) -> Option<Symbol> {
26 symbol_table()
27 .read()
28 .expect("symbol table read lock poisoned")
29 .lookup
30 .get(name)
31 .copied()
32}
33
34pub(crate) fn intern_symbol(name: &str) -> Symbol {
35 intern_symbol_with_name(name).0
36}
37
38pub(crate) fn intern_symbol_with_name(name: &str) -> (Symbol, Arc<str>) {
39 {
40 let table = symbol_table()
41 .read()
42 .expect("symbol table read lock poisoned");
43 if let Some(symbol) = table.lookup.get(name) {
44 return (*symbol, table.names[symbol.0 as usize].clone());
45 }
46 }
47
48 let mut table = symbol_table()
49 .write()
50 .expect("symbol table write lock poisoned");
51 if let Some(symbol) = table.lookup.get(name) {
52 return (*symbol, table.names[symbol.0 as usize].clone());
53 }
54
55 let symbol = Symbol(table.names.len() as u32);
56 let text: Arc<str> = Arc::<str>::from(name);
57 table.names.push(text.clone());
58 table.lookup.insert(text.clone(), symbol);
59 (symbol, text)
60}
61
62pub(crate) fn symbol_name(symbol: Symbol) -> Arc<str> {
63 symbol_table()
64 .read()
65 .expect("symbol table read lock poisoned")
66 .names[symbol.0 as usize]
67 .clone()
68}
69
70#[derive(Clone, Debug, PartialEq)]
71pub(super) struct RecordEntry {
72 pub(super) symbol: Symbol,
73 pub(super) name: Arc<str>,
74 pub(super) value: Value,
75}
76
77#[derive(Clone, Debug, Default)]
78pub struct Record {
79 pub(super) entries: SmallVec<[RecordEntry; RECORD_INLINE_CAPACITY]>,
80 index: Option<FxHashMap<Symbol, usize>>,
81}
82
83impl Record {
84 pub fn new() -> Self {
85 Self::default()
86 }
87
88 pub fn with_capacity(capacity: usize) -> Self {
89 Self {
90 entries: SmallVec::with_capacity(capacity),
91 index: (capacity > RECORD_INDEX_THRESHOLD)
92 .then(|| FxHashMap::with_capacity_and_hasher(capacity, Default::default())),
93 }
94 }
95
96 pub fn len(&self) -> usize {
97 self.entries.len()
98 }
99
100 pub fn is_empty(&self) -> bool {
101 self.entries.is_empty()
102 }
103
104 pub fn get(&self, name: &str) -> Option<&Value> {
105 self.get_symbol(lookup_symbol(name)?)
106 }
107
108 pub fn get_mut(&mut self, name: &str) -> Option<&mut Value> {
109 let symbol = lookup_symbol(name)?;
110 let index = self.position_for(symbol)?;
111 Some(&mut self.entries[index].value)
112 }
113
114 pub fn remove(&mut self, name: &str) -> Option<Value> {
115 let symbol = lookup_symbol(name)?;
116 self.remove_symbol(symbol)
117 }
118
119 pub fn insert(&mut self, name: String, value: Value) -> Option<Value> {
120 let (symbol, name) = intern_symbol_with_name(&name);
121 self.insert_symbolized(symbol, name, value)
122 }
123
124 pub fn insert_str(&mut self, name: &str, value: Value) -> Option<Value> {
125 let (symbol, name) = intern_symbol_with_name(name);
126 self.insert_symbolized(symbol, name, value)
127 }
128
129 pub fn iter(&self) -> impl Iterator<Item = (&str, &Value)> {
130 self.entries
131 .iter()
132 .map(|entry| (entry.name.as_ref(), &entry.value))
133 }
134
135 pub fn keys(&self) -> impl Iterator<Item = &str> {
136 self.entries.iter().map(|entry| entry.name.as_ref())
137 }
138
139 pub fn values(&self) -> impl Iterator<Item = &Value> {
140 self.entries.iter().map(|entry| &entry.value)
141 }
142
143 pub(crate) fn get_symbol(&self, symbol: Symbol) -> Option<&Value> {
144 let index = self.position_for(symbol)?;
145 Some(&self.entries[index].value)
146 }
147
148 pub(crate) fn get_symbol_mut(&mut self, symbol: Symbol) -> Option<&mut Value> {
149 let index = self.position_for(symbol)?;
150 Some(&mut self.entries[index].value)
151 }
152
153 pub(crate) fn insert_symbolized(
154 &mut self,
155 symbol: Symbol,
156 name: Arc<str>,
157 value: Value,
158 ) -> Option<Value> {
159 if let Some(index) = self.position_for(symbol) {
160 return Some(std::mem::replace(&mut self.entries[index].value, value));
161 }
162
163 let index = self.entries.len();
164 self.entries.push(RecordEntry {
165 symbol,
166 name,
167 value,
168 });
169 self.reindex_after_insert(index);
170 None
171 }
172
173 pub(super) fn remove_symbol(&mut self, symbol: Symbol) -> Option<Value> {
174 let index = self.position_for(symbol)?;
175 let removed = self.entries.swap_remove(index);
176 self.reindex_after_remove(symbol, index);
177 Some(removed.value)
178 }
179
180 fn position_for(&self, symbol: Symbol) -> Option<usize> {
181 if let Some(index) = &self.index {
182 return index.get(&symbol).copied();
183 }
184 self.entries.iter().position(|entry| entry.symbol == symbol)
185 }
186
187 fn rebuild_index(&mut self) {
188 self.index = (self.entries.len() > RECORD_INDEX_THRESHOLD).then(|| {
189 let mut index =
190 FxHashMap::with_capacity_and_hasher(self.entries.len(), Default::default());
191 for (slot, entry) in self.entries.iter().enumerate() {
192 index.insert(entry.symbol, slot);
193 }
194 index
195 });
196 }
197
198 fn reindex_after_insert(&mut self, index: usize) {
199 if let Some(map) = &mut self.index {
200 map.insert(self.entries[index].symbol, index);
201 return;
202 }
203 if self.entries.len() > RECORD_INDEX_THRESHOLD {
204 self.rebuild_index();
205 }
206 }
207
208 fn reindex_after_remove(&mut self, removed: Symbol, index: usize) {
209 if self.entries.len() <= RECORD_INDEX_THRESHOLD {
210 self.index = None;
211 return;
212 }
213
214 let Some(map) = &mut self.index else {
215 self.rebuild_index();
216 return;
217 };
218 map.remove(&removed);
219 if let Some(moved) = self.entries.get(index) {
220 map.insert(moved.symbol, index);
221 }
222 }
223}
224
225impl Index<&str> for Record {
226 type Output = Value;
227
228 fn index(&self, name: &str) -> &Self::Output {
229 self.get(name)
230 .unwrap_or_else(|| panic!("missing record key `{name}`"))
231 }
232}
233
234impl PartialEq for Record {
235 fn eq(&self, other: &Self) -> bool {
236 if self.len() != other.len() {
237 return false;
238 }
239 self.entries.iter().all(|entry| {
240 other
241 .get_symbol(entry.symbol)
242 .is_some_and(|value| value == &entry.value)
243 })
244 }
245}
246
247impl FromIterator<(String, Value)> for Record {
248 fn from_iter<T: IntoIterator<Item = (String, Value)>>(iter: T) -> Self {
249 let iter = iter.into_iter();
250 let (lower, _) = iter.size_hint();
251 let mut record = Record::with_capacity(lower);
252 for (name, value) in iter {
253 record.insert(name, value);
254 }
255 record
256 }
257}
258
259impl Serialize for Record {
260 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
261 where
262 S: serde::Serializer,
263 {
264 use serde::ser::SerializeMap;
265
266 let mut map = serializer.serialize_map(Some(self.entries.len()))?;
267 for entry in &self.entries {
268 map.serialize_entry(entry.name.as_ref(), &entry.value)?;
269 }
270 map.end()
271 }
272}
273
274impl<'de> Deserialize<'de> for Record {
275 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
276 where
277 D: serde::Deserializer<'de>,
278 {
279 let map = FxHashMap::<String, Value>::deserialize(deserializer)?;
280 Ok(map.into_iter().collect())
281 }
282}
283
284pub(crate) fn record_with_capacity(capacity: usize) -> Record {
285 Record::with_capacity(capacity)
286}