1use crate::entity_table::EntityTable;
13use crate::object::{EntityId, PsObject};
14
15pub struct ArrayStore {
17 data: Vec<PsObject>,
18 pub entities: EntityTable,
19}
20
21impl ArrayStore {
22 pub fn new() -> Self {
23 Self {
24 data: Vec::new(),
25 entities: EntityTable::new(),
26 }
27 }
28
29 pub fn allocated_objects(&self) -> usize {
36 self.data.len()
37 }
38
39 pub fn data_len(&self) -> usize {
48 self.data.len()
49 }
50
51 pub fn data_capacity(&self) -> usize {
59 self.data.capacity()
60 }
61
62 pub fn truncate_to(&mut self, data_len: usize, entity_len: usize) {
63 self.data.truncate(data_len);
64 self.entities.truncate(entity_len);
65 }
66
67 pub fn allocate(&mut self, len: usize) -> EntityId {
69 let offset = self.data.len() as u32;
70 self.data.resize(self.data.len() + len, PsObject::null());
71 self.entities.allocate(offset, len as u32, 0, false, 0)
72 }
73
74 pub fn allocate_from(&mut self, items: &[PsObject]) -> EntityId {
76 let offset = self.data.len() as u32;
77 self.data.extend_from_slice(items);
78 self.entities
79 .allocate(offset, items.len() as u32, 0, false, 0)
80 }
81
82 pub fn allocate_from_with(
84 &mut self,
85 items: &[PsObject],
86 save_level: u16,
87 global: bool,
88 created_after_save: u32,
89 ) -> EntityId {
90 let offset = self.data.len() as u32;
91 self.data.extend_from_slice(items);
92 self.entities.allocate(
93 offset,
94 items.len() as u32,
95 save_level,
96 global,
97 created_after_save,
98 )
99 }
100
101 pub fn allocate_with(
103 &mut self,
104 len: usize,
105 save_level: u16,
106 global: bool,
107 created_after_save: u32,
108 ) -> EntityId {
109 let offset = self.data.len() as u32;
110 self.data.resize(self.data.len() + len, PsObject::null());
111 self.entities
112 .allocate(offset, len as u32, save_level, global, created_after_save)
113 }
114
115 pub fn iter_entities(&self) -> impl Iterator<Item = (EntityId, &[PsObject])> {
122 (0..self.entities.len()).map(|i| {
123 let meta = self.entities.get_by_index(i);
124 let id = if meta.is_global() {
125 EntityId::global(i as u32)
126 } else {
127 EntityId::local(i as u32)
128 };
129 let base = meta.offset as usize;
130 (id, &self.data[base..base + meta.len as usize])
131 })
132 }
133
134 pub fn get(&self, entity: EntityId, start: u32, len: u32) -> &[PsObject] {
136 let base = self.entities.get(entity).offset as usize + start as usize;
137 &self.data[base..base + len as usize]
138 }
139
140 pub fn get_mut(&mut self, entity: EntityId, start: u32, len: u32) -> &mut [PsObject] {
142 let base = self.entities.get(entity).offset as usize + start as usize;
143 &mut self.data[base..base + len as usize]
144 }
145
146 #[inline]
148 pub fn get_element(&self, entity: EntityId, index: u32) -> PsObject {
149 let base = self.entities.get(entity).offset as usize;
150 self.data[base + index as usize]
151 }
152
153 pub fn set_element(&mut self, entity: EntityId, index: u32, obj: PsObject) {
155 let base = self.entities.get(entity).offset as usize;
156 self.data[base + index as usize] = obj;
157 }
158
159 pub fn cow_copy(&mut self, entity: EntityId) -> EntityId {
163 let meta = self.entities.get(entity);
164 let old_offset = meta.offset as usize;
165 let len = meta.len;
166 let save_level = meta.save_level;
167 let is_global = meta.is_global();
168 let created_after_save = meta.created_after_save;
169
170 let temp: Vec<PsObject> = self.data[old_offset..old_offset + len as usize].to_vec();
171 let new_offset = self.data.len() as u32;
172 self.data.extend_from_slice(&temp);
173
174 let copy_id = self.entities.allocate(
176 meta.offset, len,
178 save_level,
179 is_global,
180 created_after_save,
181 );
182
183 self.entities.get_mut(copy_id).set_cow_backup();
184
185 self.entities.get_mut(entity).offset = new_offset;
187
188 copy_id
189 }
190
191 pub fn swap_offsets(&mut self, a: EntityId, b: EntityId) {
193 let off_a = self.entities.get(a).offset;
194 let off_b = self.entities.get(b).offset;
195 self.entities.get_mut(a).offset = off_b;
196 self.entities.get_mut(b).offset = off_a;
197 }
198}
199
200impl Default for ArrayStore {
201 fn default() -> Self {
202 Self::new()
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209 use crate::object::PsValue;
210
211 #[test]
212 fn test_allocate_from() {
213 let mut store = ArrayStore::new();
214 let items = [PsObject::int(1), PsObject::int(2), PsObject::int(3)];
215 let id = store.allocate_from(&items);
216 assert_eq!(store.get_element(id, 0).as_i32(), Some(1));
217 assert_eq!(store.get_element(id, 1).as_i32(), Some(2));
218 assert_eq!(store.get_element(id, 2).as_i32(), Some(3));
219 }
220
221 #[test]
222 fn test_allocate_null_filled() {
223 let mut store = ArrayStore::new();
224 let id = store.allocate(3);
225 for i in 0..3 {
226 assert!(matches!(store.get_element(id, i).value, PsValue::Null));
227 }
228 }
229
230 #[test]
231 fn test_set_element() {
232 let mut store = ArrayStore::new();
233 let id = store.allocate(2);
234 store.set_element(id, 0, PsObject::int(42));
235 assert_eq!(store.get_element(id, 0).as_i32(), Some(42));
236 }
237
238 #[test]
239 fn test_subarray_view() {
240 let mut store = ArrayStore::new();
241 let items = [
242 PsObject::int(10),
243 PsObject::int(20),
244 PsObject::int(30),
245 PsObject::int(40),
246 ];
247 let id = store.allocate_from(&items);
248 let sub = store.get(id, 1, 2);
250 assert_eq!(sub[0].as_i32(), Some(20));
251 assert_eq!(sub[1].as_i32(), Some(30));
252 }
253
254 #[test]
255 fn test_cow_copy() {
256 let mut store = ArrayStore::new();
257 let items = [PsObject::int(1), PsObject::int(2), PsObject::int(3)];
258 let id = store.allocate_from(&items);
259
260 let backup = store.cow_copy(id);
261
262 store.set_element(id, 0, PsObject::int(99));
264 assert_eq!(store.get_element(id, 0).as_i32(), Some(99));
265 assert_eq!(store.get_element(backup, 0).as_i32(), Some(1));
266 }
267
268 #[test]
269 fn test_swap_offsets() {
270 let mut store = ArrayStore::new();
271 let id1 = store.allocate_from(&[PsObject::int(1)]);
272 let id2 = store.allocate_from(&[PsObject::int(2)]);
273
274 store.swap_offsets(id1, id2);
275 assert_eq!(store.get_element(id1, 0).as_i32(), Some(2));
276 assert_eq!(store.get_element(id2, 0).as_i32(), Some(1));
277 }
278}