rune/core/object/
hashtable.rs1use super::{CloneIn, Gc, IntoObject, ObjCell, Object, WithLifetime};
6use crate::core::env::INTERNED_SYMBOLS;
7use crate::core::gc::{Block, GcHeap, GcState, Trace};
8use crate::derive_GcMoveable;
9use rune_core::hashmap::{HashSet, IndexMap};
10use rune_macros::Trace;
11use std::cell::RefCell;
12use std::fmt::{self, Debug, Display, Write};
13use std::ptr::NonNull;
14use std::sync::Mutex;
15
16pub(crate) type HashTable<'ob> = IndexMap<Object<'ob>, Object<'ob>>;
17
18#[derive(PartialEq, Trace)]
19pub(crate) struct LispHashTable(GcHeap<HashTableCore<'static>>);
20
21impl Eq for LispHashTable {}
22
23derive_GcMoveable!(LispHashTable);
24
25impl Debug for LispHashTable {
26 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
27 self.display_walk(f, &mut HashSet::default())
28 }
29}
30
31impl Display for LispHashTable {
32 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
33 self.display_walk(f, &mut HashSet::default())
34 }
35}
36
37impl LispHashTable {
38 pub(in crate::core) unsafe fn new(table: HashTable, constant: bool) -> Self {
39 Self(GcHeap::new(HashTableCore::new(table, constant), constant))
40 }
41
42 pub(in crate::core) fn forwarding_ptr(&self) -> Option<NonNull<u8>> {
43 use crate::core::gc::AllocState as A;
44 match self.0.allocation_state() {
45 A::Forwarded(f) => Some(f),
46 A::Global => panic!("global hashtable allocation found in local heap"),
47 A::Unmoved => None,
48 }
49 }
50}
51
52struct HashTableCore<'ob>(HashTableType<'ob>);
53
54enum HashTableType<'a> {
58 Local(RefCell<HashTableInner<'a>>),
59 Global(Mutex<HashTableInner<'a>>),
60}
61
62struct HashTableInner<'ob> {
63 iter_idx: usize,
66 inner: HashTable<'ob>,
67}
68
69impl LispHashTable {
70 pub(crate) fn len(&self) -> usize {
71 self.0.with(|x| x.len())
72 }
73
74 pub(crate) fn get(&self, key: Object) -> Option<Object<'_>> {
75 self.0.with(|x| x.get(&key).copied())
76 }
77
78 pub(crate) fn get_index(&self, index: usize) -> Option<(Object, Object)> {
79 self.0.with(|x| x.get_index(index).map(|(k, v)| (*k, *v)))
80 }
81
82 pub(crate) fn get_index_of(&self, key: Object) -> Option<usize> {
83 self.0.with(|x| x.get_index_of(&key))
84 }
85
86 pub(crate) fn insert(&self, key: Object, value: Object) {
87 match &self.0.0 {
88 HashTableType::Local(table) => {
89 let key = unsafe { key.with_lifetime() };
90 let value = unsafe { value.with_lifetime() };
91 table.borrow_mut().inner.insert(key, value)
92 }
93 HashTableType::Global(table) => {
94 let map = INTERNED_SYMBOLS.lock().unwrap();
95 let block = map.global_block();
96 let key = unsafe { key.clone_in(block).with_lifetime() };
99 let value = unsafe { value.clone_in(block).with_lifetime() };
100 table.lock().unwrap().inner.insert(key, value)
101 }
102 };
103 }
104
105 pub(crate) fn shift_remove(&self, key: Object) {
106 let key = unsafe { key.with_lifetime() };
107 self.0.with(|x| x.shift_remove(&key));
108 }
109
110 pub(crate) fn get_iter_index(&self) -> usize {
111 match &self.0.0 {
112 HashTableType::Local(table) => table.borrow().iter_idx,
113 HashTableType::Global(table) => table.lock().unwrap().iter_idx,
114 }
115 }
116
117 pub(crate) fn set_iter_index(&self, index: usize) {
118 match &self.0.0 {
119 HashTableType::Local(table) => table.borrow_mut().iter_idx = index,
120 HashTableType::Global(table) => table.lock().unwrap().iter_idx = index,
121 }
122 }
123}
124
125impl<'a> HashTableCore<'a> {
126 unsafe fn new(table: HashTable, constant: bool) -> Self {
127 let table = std::mem::transmute::<HashTable<'_>, HashTable<'a>>(table);
128 let inner = HashTableInner { iter_idx: 0, inner: table };
129 if constant {
130 HashTableCore(HashTableType::Global(Mutex::new(inner)))
131 } else {
132 HashTableCore(HashTableType::Local(RefCell::new(inner)))
133 }
134 }
135
136 fn with<F, T>(&self, mut f: F) -> T
137 where
138 F: FnMut(&mut HashTable<'a>) -> T,
139 {
140 match &self.0 {
141 HashTableType::Local(table) => f(&mut table.borrow_mut().inner),
142 HashTableType::Global(table) => f(&mut table.lock().unwrap().inner),
143 }
144 }
145}
146
147impl Trace for HashTableCore<'_> {
148 fn trace(&self, state: &mut GcState) {
149 let HashTableType::Local(table) = &self.0 else {
150 panic!("Global hash table should not be traced")
151 };
152 let table = &mut table.borrow_mut().inner;
153 let table = unsafe {
156 std::mem::transmute::<&mut IndexMap<Object, Object>, &mut IndexMap<ObjCell, ObjCell>>(
157 table,
158 )
159 };
160 table.rehash_keys(|key, val| {
161 key.trace(state);
162 val.trace(state);
163 });
164 }
165}
166
167impl PartialEq for HashTableCore<'_> {
168 fn eq(&self, other: &Self) -> bool {
169 std::ptr::eq(self, other)
170 }
171}
172
173impl<'new> CloneIn<'new, &'new Self> for LispHashTable {
174 fn clone_in<const C: bool>(&self, bk: &'new Block<C>) -> Gc<&'new Self> {
175 let mut table = HashTable::default();
176 self.0.with(|x| {
177 for (key, value) in x {
178 let new_key = key.clone_in(bk);
179 let new_value = value.clone_in(bk);
180 table.insert(new_key, new_value);
181 }
182 });
183 table.into_obj(bk)
184 }
185}
186
187impl LispHashTable {
188 pub(super) fn display_walk(
189 &self,
190 f: &mut fmt::Formatter,
191 seen: &mut HashSet<*const u8>,
192 ) -> fmt::Result {
193 let ptr = (&*self.0 as *const HashTableCore).cast();
194 if seen.contains(&ptr) {
195 return write!(f, "#0");
196 }
197 seen.insert(ptr);
198
199 write!(f, "#s(hash-table (")?;
200 self.0.with(|x| {
201 for (i, (k, v)) in x.iter().enumerate() {
202 if i != 0 {
203 f.write_char(' ')?;
204 }
205 k.untag().display_walk(f, seen)?;
206 f.write_char(' ')?;
207 v.untag().display_walk(f, seen)?;
208 }
209 Ok(())
210 })?;
211 write!(f, "))")
212 }
213}