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(i32),
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 allocate(&mut self, max_length: usize, name: &[u8]) -> EntityId {
57 let index = self.dicts.len() as u32;
58 self.dicts.push(DictEntry {
59 max_length,
60 entries: HashMap::with_capacity_and_hasher(max_length.min(64), Default::default()),
61 access: ObjFlags::ACCESS_UNLIMITED,
62 name: name.to_vec(),
63 });
64 self.entities.allocate(index, 0, 0, false, 0)
66 }
67
68 pub fn allocate_with(
70 &mut self,
71 max_length: usize,
72 name: &[u8],
73 save_level: u16,
74 global: bool,
75 created_after_save: u32,
76 ) -> EntityId {
77 let index = self.dicts.len() as u32;
78 self.dicts.push(DictEntry {
79 max_length,
80 entries: HashMap::with_capacity_and_hasher(max_length.min(64), Default::default()),
81 access: ObjFlags::ACCESS_UNLIMITED,
82 name: name.to_vec(),
83 });
84 self.entities
85 .allocate(index, 0, save_level, global, created_after_save)
86 }
87
88 fn dict_index(&self, entity: EntityId) -> usize {
90 self.entities.get(entity).offset as usize
91 }
92
93 #[inline]
95 pub fn get(&self, entity: EntityId, key: &DictKey) -> Option<PsObject> {
96 self.dicts[self.dict_index(entity)]
97 .entries
98 .get(key)
99 .copied()
100 }
101
102 pub fn put(&mut self, entity: EntityId, key: DictKey, value: PsObject) {
104 let idx = self.dict_index(entity);
105 self.dicts[idx].entries.insert(key, value);
106 }
107
108 pub fn known(&self, entity: EntityId, key: &DictKey) -> bool {
110 self.dicts[self.dict_index(entity)]
111 .entries
112 .contains_key(key)
113 }
114
115 pub fn get_name(&self, entity: EntityId) -> &[u8] {
117 &self.dicts[self.dict_index(entity)].name
118 }
119
120 pub fn set_name(&mut self, entity: EntityId, name: &[u8]) {
122 let idx = self.dict_index(entity);
123 self.dicts[idx].name.clear();
124 self.dicts[idx].name.extend_from_slice(name);
125 }
126
127 pub fn length(&self, entity: EntityId) -> usize {
129 self.dicts[self.dict_index(entity)].entries.len()
130 }
131
132 pub fn max_length(&self, entity: EntityId) -> usize {
134 self.dicts[self.dict_index(entity)].max_length
135 }
136
137 pub fn remove(&mut self, entity: EntityId, key: &DictKey) {
139 let idx = self.dict_index(entity);
140 self.dicts[idx].entries.remove(key);
141 }
142
143 pub fn entry(&self, entity: EntityId) -> &DictEntry {
145 &self.dicts[self.dict_index(entity)]
146 }
147
148 pub fn entry_mut(&mut self, entity: EntityId) -> &mut DictEntry {
150 let idx = self.dict_index(entity);
151 &mut self.dicts[idx]
152 }
153
154 pub fn keys(&self, entity: EntityId) -> impl Iterator<Item = &DictKey> {
156 self.dicts[self.dict_index(entity)].entries.keys()
157 }
158
159 pub fn access(&self, entity: EntityId) -> u8 {
161 self.dicts[self.dict_index(entity)].access
162 }
163
164 #[inline]
166 pub fn require_read(&self, entity: EntityId) -> Result<(), crate::error::PsError> {
167 if self.access(entity) >= ObjFlags::ACCESS_READ_ONLY {
168 Ok(())
169 } else {
170 Err(crate::error::PsError::InvalidAccess)
171 }
172 }
173
174 #[inline]
176 pub fn require_write(&self, entity: EntityId) -> Result<(), crate::error::PsError> {
177 if self.access(entity) >= ObjFlags::ACCESS_UNLIMITED {
178 Ok(())
179 } else {
180 Err(crate::error::PsError::InvalidAccess)
181 }
182 }
183
184 pub fn set_access(&mut self, entity: EntityId, access: u8) {
186 let idx = self.dict_index(entity);
187 self.dicts[idx].access = access;
188 }
189
190 pub fn cow_copy(&mut self, entity: EntityId) -> EntityId {
194 let idx = self.dict_index(entity);
195 let meta = self.entities.get(entity);
196 let save_level = meta.save_level;
197 let is_global = meta.is_global();
198 let created_after_save = meta.created_after_save;
199
200 let orig = &self.dicts[idx];
202 let copy = DictEntry {
203 max_length: orig.max_length,
204 entries: orig.entries.clone(),
205 access: orig.access,
206 name: orig.name.clone(),
207 };
208
209 let new_index = self.dicts.len() as u32;
210 self.dicts.push(copy);
211
212 let backup_id = self.entities.allocate(
214 idx as u32, 0,
216 save_level,
217 is_global,
218 created_after_save,
219 );
220
221 self.entities.get_mut(entity).offset = new_index;
223
224 backup_id
225 }
226
227 pub fn swap_offsets(&mut self, a: EntityId, b: EntityId) {
229 let off_a = self.entities.get(a).offset;
230 let off_b = self.entities.get(b).offset;
231 self.entities.get_mut(a).offset = off_b;
232 self.entities.get_mut(b).offset = off_a;
233 }
234}
235
236impl Default for DictStore {
237 fn default() -> Self {
238 Self::new()
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245
246 #[test]
247 fn test_dict_basic() {
248 let mut store = DictStore::new();
249 let d = store.allocate(10, b"testdict");
250 let key = DictKey::Name(NameId(0));
251 assert!(!store.known(d, &key));
252
253 store.put(d, key.clone(), PsObject::int(42));
254 assert!(store.known(d, &key));
255 assert_eq!(store.get(d, &key).unwrap().as_i32(), Some(42));
256 assert_eq!(store.length(d), 1);
257 assert_eq!(store.max_length(d), 10);
258
259 store.remove(d, &key);
260 assert!(!store.known(d, &key));
261 }
262
263 #[test]
264 fn test_multiple_dicts() {
265 let mut store = DictStore::new();
266 let d1 = store.allocate(10, b"dict1");
267 let d2 = store.allocate(10, b"dict2");
268 let key = DictKey::Int(1);
269
270 store.put(d1, key.clone(), PsObject::int(100));
271 store.put(d2, key.clone(), PsObject::int(200));
272
273 assert_eq!(store.get(d1, &key).unwrap().as_i32(), Some(100));
274 assert_eq!(store.get(d2, &key).unwrap().as_i32(), Some(200));
275 }
276
277 #[test]
278 fn test_cow_copy() {
279 let mut store = DictStore::new();
280 let d = store.allocate(10, b"test");
281 let key = DictKey::Name(NameId(0));
282 store.put(d, key.clone(), PsObject::int(42));
283
284 let backup = store.cow_copy(d);
285
286 store.put(d, key.clone(), PsObject::int(99));
288 assert_eq!(store.get(d, &key).unwrap().as_i32(), Some(99));
289 assert_eq!(store.get(backup, &key).unwrap().as_i32(), Some(42));
290 }
291
292 #[test]
293 fn test_swap_offsets() {
294 let mut store = DictStore::new();
295 let d1 = store.allocate(10, b"a");
296 let d2 = store.allocate(10, b"b");
297 let key = DictKey::Int(0);
298 store.put(d1, key.clone(), PsObject::int(1));
299 store.put(d2, key.clone(), PsObject::int(2));
300
301 store.swap_offsets(d1, d2);
302 assert_eq!(store.get(d1, &key).unwrap().as_i32(), Some(2));
303 assert_eq!(store.get(d2, &key).unwrap().as_i32(), Some(1));
304 }
305}