Skip to main content

sim_table_hash/
hash.rs

1//! The [`HashTable`] object: an in-memory, hash-map-backed table that
2//! implements the kernel table and object-encoding contracts.
3
4use std::{
5    collections::HashMap,
6    sync::{PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard},
7};
8
9use sim_kernel::{
10    Cx, Error, Expr, Object, ObjectEncode, ObjectEncoding, Result, Symbol, Table, Value,
11    id::CORE_TABLE_CLASS_ID, object::ClassRef,
12};
13
14use crate::citizen::hash_table_class_symbol;
15
16/// In-memory table backed by a hash map of symbol keys to [`Value`]s.
17///
18/// Implements the kernel [`Table`] contract (`get`/`set`/`has`/`del`/`keys`/
19/// `entries`/`len`/`clear`) and the object-encoding contracts, so it can be
20/// stored as an opaque object and round-tripped through its
21/// [`HashTableDescriptor`](crate::HashTableDescriptor) citizen form. The map is
22/// guarded by an `RwLock`, so a `HashTable` is shareable and mutable through a
23/// shared reference.
24pub struct HashTable {
25    inner: RwLock<HashMap<Symbol, Value>>,
26}
27
28impl Clone for HashTable {
29    fn clone(&self) -> Self {
30        Self {
31            inner: RwLock::new(
32                self.inner
33                    .read()
34                    .unwrap_or_else(PoisonError::into_inner)
35                    .clone(),
36            ),
37        }
38    }
39}
40
41impl HashTable {
42    fn read(&self) -> Result<RwLockReadGuard<'_, HashMap<Symbol, Value>>> {
43        self.inner
44            .read()
45            .map_err(|_| Error::Eval("table/hash lock poisoned".into()))
46    }
47
48    fn write(&self) -> Result<RwLockWriteGuard<'_, HashMap<Symbol, Value>>> {
49        self.inner
50            .write()
51            .map_err(|_| Error::Eval("table/hash lock poisoned".into()))
52    }
53
54    /// Construct an empty hash table.
55    pub fn new() -> Self {
56        Self {
57            inner: RwLock::new(HashMap::new()),
58        }
59    }
60
61    /// Construct a hash table pre-populated with `entries`.
62    ///
63    /// Later entries with the same key overwrite earlier ones.
64    ///
65    /// # Examples
66    ///
67    /// ```
68    /// use std::sync::Arc;
69    /// use sim_kernel::{Cx, DefaultFactory, NoopEvalPolicy, Symbol, Table};
70    /// use sim_table_hash::HashTable;
71    ///
72    /// let mut cx = Cx::new(Arc::new(NoopEvalPolicy), Arc::new(DefaultFactory));
73    /// let value = cx.factory().bool(true).unwrap();
74    /// let table = HashTable::with_entries(vec![(Symbol::new("a"), value.clone())]);
75    ///
76    /// assert_eq!(table.len(&mut cx).unwrap(), 1);
77    /// assert!(table.has(&mut cx, Symbol::new("a")).unwrap());
78    /// assert_eq!(table.get(&mut cx, Symbol::new("a")).unwrap(), value);
79    /// ```
80    pub fn with_entries(entries: Vec<(Symbol, Value)>) -> Self {
81        Self {
82            inner: RwLock::new(entries.into_iter().collect()),
83        }
84    }
85
86    fn descriptor_entries(&self, cx: &mut Cx) -> Result<Vec<(Symbol, Expr)>> {
87        let mut entries = self
88            .read()?
89            .iter()
90            .map(|(key, value)| Ok((key.clone(), value.object().as_expr(cx)?)))
91            .collect::<Result<Vec<_>>>()?;
92        entries.sort_by(|left, right| left.0.cmp(&right.0));
93        Ok(entries)
94    }
95}
96
97impl Default for HashTable {
98    fn default() -> Self {
99        Self::new()
100    }
101}
102
103impl Object for HashTable {
104    fn display(&self, _cx: &mut Cx) -> Result<String> {
105        Ok(format!("table/hash[{}]", self.read()?.len()))
106    }
107
108    fn as_any(&self) -> &dyn std::any::Any {
109        self
110    }
111}
112
113impl sim_kernel::ObjectCompat for HashTable {
114    fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
115        let symbol = hash_table_class_symbol();
116        if let Some(value) = cx.registry().class_by_symbol(&symbol) {
117            return Ok(value.clone());
118        }
119        let symbol = Symbol::qualified("core", "Table");
120        if let Some(value) = cx.registry().class_by_symbol(&symbol) {
121            return Ok(value.clone());
122        }
123        cx.factory().class_stub(CORE_TABLE_CLASS_ID, symbol)
124    }
125    fn as_expr(&self, cx: &mut Cx) -> Result<Expr> {
126        self.as_table_expr(cx)
127    }
128    fn truth(&self, _cx: &mut Cx) -> Result<bool> {
129        Ok(!self.read()?.is_empty())
130    }
131    fn as_table_impl(&self) -> Option<&dyn Table> {
132        Some(self)
133    }
134    fn as_object_encoder(&self) -> Option<&dyn ObjectEncode> {
135        Some(self)
136    }
137}
138
139impl ObjectEncode for HashTable {
140    fn object_encoding(&self, cx: &mut Cx) -> Result<ObjectEncoding> {
141        Ok(ObjectEncoding::Constructor {
142            class: hash_table_class_symbol(),
143            args: vec![
144                Expr::Symbol(Symbol::new("v0")),
145                sim_table_core::citizen_fields::entries::encode(&self.descriptor_entries(cx)?),
146            ],
147        })
148    }
149}
150
151impl sim_citizen::Citizen for HashTable {
152    fn citizen_symbol() -> Symbol {
153        hash_table_class_symbol()
154    }
155
156    fn citizen_version() -> u32 {
157        0
158    }
159
160    fn citizen_arity() -> usize {
161        1
162    }
163
164    fn citizen_fields() -> &'static [&'static str] {
165        &["entries"]
166    }
167}
168
169impl Table for HashTable {
170    fn backend_symbol(&self) -> Symbol {
171        Symbol::qualified("table", "hash")
172    }
173
174    fn get(&self, cx: &mut Cx, key: Symbol) -> Result<Value> {
175        match self.read()?.get(&key) {
176            Some(value) => Ok(value.clone()),
177            None => cx.factory().nil(),
178        }
179    }
180
181    fn set(&self, _cx: &mut Cx, key: Symbol, value: Value) -> Result<()> {
182        self.write()?.insert(key, value);
183        Ok(())
184    }
185
186    fn has(&self, _cx: &mut Cx, key: Symbol) -> Result<bool> {
187        Ok(self.read()?.contains_key(&key))
188    }
189
190    fn del(&self, cx: &mut Cx, key: Symbol) -> Result<Value> {
191        match self.write()?.remove(&key) {
192            Some(value) => Ok(value),
193            None => cx.factory().nil(),
194        }
195    }
196
197    fn keys(&self, _cx: &mut Cx) -> Result<Vec<Symbol>> {
198        Ok(self.read()?.keys().cloned().collect())
199    }
200
201    fn entries(&self, _cx: &mut Cx) -> Result<Vec<(Symbol, Value)>> {
202        Ok(self
203            .read()?
204            .iter()
205            .map(|(key, value)| (key.clone(), value.clone()))
206            .collect())
207    }
208
209    fn len(&self, _cx: &mut Cx) -> Result<usize> {
210        Ok(self.read()?.len())
211    }
212
213    fn clear(&self, _cx: &mut Cx) -> Result<()> {
214        self.write()?.clear();
215        Ok(())
216    }
217}