Skip to main content

lashlang/runtime/
record.rs

1use 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}