1use crate::entity_table::EntityTable;
11use crate::object::EntityId;
12
13pub struct StringStore {
15 data: Vec<u8>,
16 pub entities: EntityTable,
17}
18
19impl StringStore {
20 pub fn new() -> Self {
21 Self {
22 data: Vec::new(),
23 entities: EntityTable::new(),
24 }
25 }
26
27 pub fn data_len(&self) -> usize {
30 self.data.len()
31 }
32
33 pub fn data_capacity(&self) -> usize {
41 self.data.capacity()
42 }
43
44 pub fn truncate_to(&mut self, data_len: usize, entity_len: usize) {
50 self.data.truncate(data_len);
51 self.entities.truncate(entity_len);
52 }
53
54 pub fn allocate(&mut self, len: usize) -> EntityId {
55 let offset = self.data.len() as u32;
56 self.data.resize(self.data.len() + len, 0);
57 self.entities.allocate(offset, len as u32, 0, false, 0)
58 }
59
60 pub fn allocate_from(&mut self, bytes: &[u8]) -> EntityId {
62 let offset = self.data.len() as u32;
63 self.data.extend_from_slice(bytes);
64 self.entities
65 .allocate(offset, bytes.len() as u32, 0, false, 0)
66 }
67
68 pub fn allocate_from_with(
70 &mut self,
71 bytes: &[u8],
72 save_level: u16,
73 global: bool,
74 created_after_save: u32,
75 ) -> EntityId {
76 let offset = self.data.len() as u32;
77 self.data.extend_from_slice(bytes);
78 self.entities.allocate(
79 offset,
80 bytes.len() as u32,
81 save_level,
82 global,
83 created_after_save,
84 )
85 }
86
87 pub fn allocate_with(
89 &mut self,
90 len: usize,
91 save_level: u16,
92 global: bool,
93 created_after_save: u32,
94 ) -> EntityId {
95 let offset = self.data.len() as u32;
96 self.data.resize(self.data.len() + len, 0);
97 self.entities
98 .allocate(offset, len as u32, save_level, global, created_after_save)
99 }
100
101 pub fn get(&self, entity: EntityId, start: u32, len: u32) -> &[u8] {
104 let base = self.entities.get(entity).offset as usize + start as usize;
105 &self.data[base..base + len as usize]
106 }
107
108 pub fn get_mut(&mut self, entity: EntityId, start: u32, len: u32) -> &mut [u8] {
111 let base = self.entities.get(entity).offset as usize + start as usize;
112 &mut self.data[base..base + len as usize]
113 }
114
115 pub fn put_byte(&mut self, entity: EntityId, offset: u32, byte: u8) {
117 let base = self.entities.get(entity).offset as usize;
118 self.data[base + offset as usize] = byte;
119 }
120
121 pub fn get_byte(&self, entity: EntityId, offset: u32) -> u8 {
123 let base = self.entities.get(entity).offset as usize;
124 self.data[base + offset as usize]
125 }
126
127 pub fn cow_copy(&mut self, entity: EntityId) -> EntityId {
131 let meta = self.entities.get(entity);
132 let old_offset = meta.offset as usize;
133 let len = meta.len;
134 let save_level = meta.save_level;
135 let is_global = meta.is_global();
136 let created_after_save = meta.created_after_save;
137
138 let temp: Vec<u8> = self.data[old_offset..old_offset + len as usize].to_vec();
140 let new_offset = self.data.len() as u32;
141 self.data.extend_from_slice(&temp);
142
143 let copy_id = self.entities.allocate(
145 meta.offset, len,
147 save_level,
148 is_global,
149 created_after_save,
150 );
151
152 self.entities.get_mut(copy_id).set_cow_backup();
153
154 self.entities.get_mut(entity).offset = new_offset;
156
157 copy_id
158 }
159
160 pub fn swap_offsets(&mut self, a: EntityId, b: EntityId) {
162 let off_a = self.entities.get(a).offset;
163 let off_b = self.entities.get(b).offset;
164 self.entities.get_mut(a).offset = off_b;
165 self.entities.get_mut(b).offset = off_a;
166 }
167
168 pub fn data(&self) -> &[u8] {
170 &self.data
171 }
172}
173
174impl Default for StringStore {
175 fn default() -> Self {
176 Self::new()
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183
184 #[test]
185 fn test_allocate_from() {
186 let mut store = StringStore::new();
187 let id = store.allocate_from(b"hello");
188 assert_eq!(store.get(id, 0, 5), b"hello");
189 }
190
191 #[test]
192 fn test_allocate_zeroed() {
193 let mut store = StringStore::new();
194 let id = store.allocate(3);
195 assert_eq!(store.get(id, 0, 3), &[0, 0, 0]);
196 }
197
198 #[test]
199 fn test_put_get_byte() {
200 let mut store = StringStore::new();
201 let id = store.allocate(3);
202 store.put_byte(id, 1, 42);
203 assert_eq!(store.get_byte(id, 0), 0);
204 assert_eq!(store.get_byte(id, 1), 42);
205 }
206
207 #[test]
208 fn test_multiple_strings() {
209 let mut store = StringStore::new();
210 let id1 = store.allocate_from(b"abc");
211 let id2 = store.allocate_from(b"xyz");
212 assert_eq!(store.get(id1, 0, 3), b"abc");
213 assert_eq!(store.get(id2, 0, 3), b"xyz");
214 }
215
216 #[test]
217 fn test_entity_indirection() {
218 let mut store = StringStore::new();
219 let id = store.allocate_from(b"test");
220 let meta = store.entities.get(id);
221 assert_eq!(meta.len, 4);
222 assert_eq!(meta.save_level, 0);
223 assert!(!meta.is_global());
224 }
225
226 #[test]
227 fn test_cow_copy() {
228 let mut store = StringStore::new();
229 let id = store.allocate_from(b"hello");
230
231 store.put_byte(id, 0, b'H');
233 assert_eq!(store.get(id, 0, 5), b"Hello");
234
235 let backup = store.cow_copy(id);
237
238 store.put_byte(id, 1, b'a');
240 assert_eq!(store.get(id, 0, 5), b"Hallo");
241 assert_eq!(store.get(backup, 0, 5), b"Hello");
242 }
243
244 #[test]
245 fn test_swap_offsets() {
246 let mut store = StringStore::new();
247 let id1 = store.allocate_from(b"aaa");
248 let id2 = store.allocate_from(b"bbb");
249
250 store.swap_offsets(id1, id2);
251 assert_eq!(store.get(id1, 0, 3), b"bbb");
252 assert_eq!(store.get(id2, 0, 3), b"aaa");
253 }
254
255 #[test]
256 fn test_allocate_with_save_level() {
257 let mut store = StringStore::new();
258 let id = store.allocate_with(5, 2, true, 0);
259 let meta = store.entities.get(id);
260 assert_eq!(meta.save_level, 2);
261 assert!(meta.is_global());
262 }
263}