1use rustc_hash::FxHashMap as HashMap;
11
12use crate::entity_table::EntityTable;
13use crate::object::{EntityId, NameId, ObjFlags, PsObject};
14
15#[derive(Clone, Debug, PartialEq, Eq, Hash)]
17pub enum DictKey {
18 Name(NameId),
19 Int(i64),
20 Real(u64), Bool(bool),
22 String(Vec<u8>), Operator(u16), Identity(u32, u32, u32),
27}
28
29pub struct DictEntry {
31 pub max_length: usize,
32 pub entries: HashMap<DictKey, PsObject>,
33 pub access: u8,
34 pub name: Vec<u8>,
35}
36
37pub struct DictStore {
43 dicts: Vec<DictEntry>,
44 pub entities: EntityTable,
45}
46
47impl DictStore {
48 pub fn new() -> Self {
49 Self {
50 dicts: Vec::new(),
51 entities: EntityTable::new(),
52 }
53 }
54
55 pub fn dict_slots(&self) -> usize {
58 self.dicts.len()
59 }
60
61 pub fn truncate_to(&mut self, dicts_len: usize, entity_len: usize) {
67 self.dicts.truncate(dicts_len);
68 self.entities.truncate(entity_len);
69 }
70
71 pub fn allocate(&mut self, max_length: usize, name: &[u8]) -> EntityId {
72 let index = self.dicts.len() as u32;
73 self.dicts.push(DictEntry {
74 max_length,
75 entries: HashMap::with_capacity_and_hasher(max_length.min(64), Default::default()),
76 access: ObjFlags::ACCESS_UNLIMITED,
77 name: name.to_vec(),
78 });
79 self.entities.allocate(index, 0, 0, false, 0)
81 }
82
83 pub fn allocate_with(
85 &mut self,
86 max_length: usize,
87 name: &[u8],
88 save_level: u16,
89 global: bool,
90 created_after_save: u32,
91 ) -> EntityId {
92 let index = self.dicts.len() as u32;
93 self.dicts.push(DictEntry {
94 max_length,
95 entries: HashMap::with_capacity_and_hasher(max_length.min(64), Default::default()),
96 access: ObjFlags::ACCESS_UNLIMITED,
97 name: name.to_vec(),
98 });
99 self.entities
100 .allocate(index, 0, save_level, global, created_after_save)
101 }
102
103 fn dict_index(&self, entity: EntityId) -> usize {
105 self.entities.get(entity).offset as usize
106 }
107
108 pub fn iter_entities(&self) -> impl Iterator<Item = (EntityId, &DictEntry)> {
117 (0..self.entities.len()).map(|i| {
118 let id = if self.entities.get_by_index(i).is_global() {
119 EntityId::global(i as u32)
120 } else {
121 EntityId::local(i as u32)
122 };
123 (id, &self.dicts[self.dict_index(id)])
124 })
125 }
126
127 #[inline]
129 pub fn get(&self, entity: EntityId, key: &DictKey) -> Option<PsObject> {
130 self.dicts[self.dict_index(entity)]
131 .entries
132 .get(key)
133 .copied()
134 }
135
136 pub fn put(&mut self, entity: EntityId, key: DictKey, value: PsObject) {
138 let idx = self.dict_index(entity);
139 self.dicts[idx].entries.insert(key, value);
140 }
141
142 pub fn known(&self, entity: EntityId, key: &DictKey) -> bool {
144 self.dicts[self.dict_index(entity)]
145 .entries
146 .contains_key(key)
147 }
148
149 pub fn get_name(&self, entity: EntityId) -> &[u8] {
151 &self.dicts[self.dict_index(entity)].name
152 }
153
154 pub fn set_name(&mut self, entity: EntityId, name: &[u8]) {
156 let idx = self.dict_index(entity);
157 self.dicts[idx].name.clear();
158 self.dicts[idx].name.extend_from_slice(name);
159 }
160
161 pub fn length(&self, entity: EntityId) -> usize {
163 self.dicts[self.dict_index(entity)].entries.len()
164 }
165
166 pub fn max_length(&self, entity: EntityId) -> usize {
168 self.dicts[self.dict_index(entity)].max_length
169 }
170
171 pub fn remove(&mut self, entity: EntityId, key: &DictKey) {
173 let idx = self.dict_index(entity);
174 self.dicts[idx].entries.remove(key);
175 }
176
177 pub fn entry(&self, entity: EntityId) -> &DictEntry {
179 &self.dicts[self.dict_index(entity)]
180 }
181
182 pub fn entry_mut(&mut self, entity: EntityId) -> &mut DictEntry {
184 let idx = self.dict_index(entity);
185 &mut self.dicts[idx]
186 }
187
188 pub fn keys(&self, entity: EntityId) -> impl Iterator<Item = &DictKey> {
190 self.dicts[self.dict_index(entity)].entries.keys()
191 }
192
193 pub fn access(&self, entity: EntityId) -> u8 {
195 self.dicts[self.dict_index(entity)].access
196 }
197
198 #[inline]
200 pub fn require_read(&self, entity: EntityId) -> Result<(), crate::error::PsError> {
201 if self.access(entity) >= ObjFlags::ACCESS_READ_ONLY {
202 Ok(())
203 } else {
204 Err(crate::error::PsError::InvalidAccess)
205 }
206 }
207
208 #[inline]
210 pub fn require_write(&self, entity: EntityId) -> Result<(), crate::error::PsError> {
211 if self.access(entity) >= ObjFlags::ACCESS_UNLIMITED {
212 Ok(())
213 } else {
214 Err(crate::error::PsError::InvalidAccess)
215 }
216 }
217
218 pub fn set_access(&mut self, entity: EntityId, access: u8) {
220 let idx = self.dict_index(entity);
221 self.dicts[idx].access = access;
222 }
223
224 pub fn cow_copy(&mut self, entity: EntityId) -> EntityId {
228 let idx = self.dict_index(entity);
229 let meta = self.entities.get(entity);
230 let save_level = meta.save_level;
231 let is_global = meta.is_global();
232 let created_after_save = meta.created_after_save;
233
234 let orig = &self.dicts[idx];
236 let copy = DictEntry {
237 max_length: orig.max_length,
238 entries: orig.entries.clone(),
239 access: orig.access,
240 name: orig.name.clone(),
241 };
242
243 let new_index = self.dicts.len() as u32;
244 self.dicts.push(copy);
245
246 let backup_id = self.entities.allocate(
248 idx as u32, 0,
250 save_level,
251 is_global,
252 created_after_save,
253 );
254
255 self.entities.get_mut(backup_id).set_cow_backup();
256
257 self.entities.get_mut(entity).offset = new_index;
259
260 backup_id
261 }
262
263 pub fn swap_offsets(&mut self, a: EntityId, b: EntityId) {
265 let off_a = self.entities.get(a).offset;
266 let off_b = self.entities.get(b).offset;
267 self.entities.get_mut(a).offset = off_b;
268 self.entities.get_mut(b).offset = off_a;
269 }
270}
271
272impl Default for DictStore {
273 fn default() -> Self {
274 Self::new()
275 }
276}
277
278#[cfg(test)]
279mod tests {
280 use super::*;
281
282 #[test]
283 fn test_dict_basic() {
284 let mut store = DictStore::new();
285 let d = store.allocate(10, b"testdict");
286 let key = DictKey::Name(NameId(0));
287 assert!(!store.known(d, &key));
288
289 store.put(d, key.clone(), PsObject::int(42));
290 assert!(store.known(d, &key));
291 assert_eq!(store.get(d, &key).unwrap().as_i32(), Some(42));
292 assert_eq!(store.length(d), 1);
293 assert_eq!(store.max_length(d), 10);
294
295 store.remove(d, &key);
296 assert!(!store.known(d, &key));
297 }
298
299 #[test]
300 fn test_multiple_dicts() {
301 let mut store = DictStore::new();
302 let d1 = store.allocate(10, b"dict1");
303 let d2 = store.allocate(10, b"dict2");
304 let key = DictKey::Int(1);
305
306 store.put(d1, key.clone(), PsObject::int(100));
307 store.put(d2, key.clone(), PsObject::int(200));
308
309 assert_eq!(store.get(d1, &key).unwrap().as_i32(), Some(100));
310 assert_eq!(store.get(d2, &key).unwrap().as_i32(), Some(200));
311 }
312
313 #[test]
314 fn test_cow_copy() {
315 let mut store = DictStore::new();
316 let d = store.allocate(10, b"test");
317 let key = DictKey::Name(NameId(0));
318 store.put(d, key.clone(), PsObject::int(42));
319
320 let backup = store.cow_copy(d);
321
322 store.put(d, key.clone(), PsObject::int(99));
324 assert_eq!(store.get(d, &key).unwrap().as_i32(), Some(99));
325 assert_eq!(store.get(backup, &key).unwrap().as_i32(), Some(42));
326 }
327
328 #[test]
329 fn test_swap_offsets() {
330 let mut store = DictStore::new();
331 let d1 = store.allocate(10, b"a");
332 let d2 = store.allocate(10, b"b");
333 let key = DictKey::Int(0);
334 store.put(d1, key.clone(), PsObject::int(1));
335 store.put(d2, key.clone(), PsObject::int(2));
336
337 store.swap_offsets(d1, d2);
338 assert_eq!(store.get(d1, &key).unwrap().as_i32(), Some(2));
339 assert_eq!(store.get(d2, &key).unwrap().as_i32(), Some(1));
340 }
341}