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 truncate_to(&mut self, data_len: usize, entity_len: usize) {
39 self.data.truncate(data_len);
40 self.entities.truncate(entity_len);
41 }
42
43 pub fn allocate(&mut self, len: usize) -> EntityId {
44 let offset = self.data.len() as u32;
45 self.data.resize(self.data.len() + len, 0);
46 self.entities.allocate(offset, len as u32, 0, false, 0)
47 }
48
49 pub fn allocate_from(&mut self, bytes: &[u8]) -> EntityId {
51 let offset = self.data.len() as u32;
52 self.data.extend_from_slice(bytes);
53 self.entities
54 .allocate(offset, bytes.len() as u32, 0, false, 0)
55 }
56
57 pub fn allocate_from_with(
59 &mut self,
60 bytes: &[u8],
61 save_level: u16,
62 global: bool,
63 created_after_save: u32,
64 ) -> EntityId {
65 let offset = self.data.len() as u32;
66 self.data.extend_from_slice(bytes);
67 self.entities.allocate(
68 offset,
69 bytes.len() as u32,
70 save_level,
71 global,
72 created_after_save,
73 )
74 }
75
76 pub fn allocate_with(
78 &mut self,
79 len: usize,
80 save_level: u16,
81 global: bool,
82 created_after_save: u32,
83 ) -> EntityId {
84 let offset = self.data.len() as u32;
85 self.data.resize(self.data.len() + len, 0);
86 self.entities
87 .allocate(offset, len as u32, save_level, global, created_after_save)
88 }
89
90 pub fn get(&self, entity: EntityId, start: u32, len: u32) -> &[u8] {
93 let base = self.entities.get(entity).offset as usize + start as usize;
94 &self.data[base..base + len as usize]
95 }
96
97 pub fn get_mut(&mut self, entity: EntityId, start: u32, len: u32) -> &mut [u8] {
100 let base = self.entities.get(entity).offset as usize + start as usize;
101 &mut self.data[base..base + len as usize]
102 }
103
104 pub fn put_byte(&mut self, entity: EntityId, offset: u32, byte: u8) {
106 let base = self.entities.get(entity).offset as usize;
107 self.data[base + offset as usize] = byte;
108 }
109
110 pub fn get_byte(&self, entity: EntityId, offset: u32) -> u8 {
112 let base = self.entities.get(entity).offset as usize;
113 self.data[base + offset as usize]
114 }
115
116 pub fn cow_copy(&mut self, entity: EntityId) -> EntityId {
120 let meta = self.entities.get(entity);
121 let old_offset = meta.offset as usize;
122 let len = meta.len;
123 let save_level = meta.save_level;
124 let is_global = meta.is_global();
125 let created_after_save = meta.created_after_save;
126
127 let temp: Vec<u8> = self.data[old_offset..old_offset + len as usize].to_vec();
129 let new_offset = self.data.len() as u32;
130 self.data.extend_from_slice(&temp);
131
132 let copy_id = self.entities.allocate(
134 meta.offset, len,
136 save_level,
137 is_global,
138 created_after_save,
139 );
140
141 self.entities.get_mut(copy_id).set_cow_backup();
142
143 self.entities.get_mut(entity).offset = new_offset;
145
146 copy_id
147 }
148
149 pub fn swap_offsets(&mut self, a: EntityId, b: EntityId) {
151 let off_a = self.entities.get(a).offset;
152 let off_b = self.entities.get(b).offset;
153 self.entities.get_mut(a).offset = off_b;
154 self.entities.get_mut(b).offset = off_a;
155 }
156
157 pub fn data(&self) -> &[u8] {
159 &self.data
160 }
161}
162
163impl Default for StringStore {
164 fn default() -> Self {
165 Self::new()
166 }
167}
168
169#[cfg(test)]
170mod tests {
171 use super::*;
172
173 #[test]
174 fn test_allocate_from() {
175 let mut store = StringStore::new();
176 let id = store.allocate_from(b"hello");
177 assert_eq!(store.get(id, 0, 5), b"hello");
178 }
179
180 #[test]
181 fn test_allocate_zeroed() {
182 let mut store = StringStore::new();
183 let id = store.allocate(3);
184 assert_eq!(store.get(id, 0, 3), &[0, 0, 0]);
185 }
186
187 #[test]
188 fn test_put_get_byte() {
189 let mut store = StringStore::new();
190 let id = store.allocate(3);
191 store.put_byte(id, 1, 42);
192 assert_eq!(store.get_byte(id, 0), 0);
193 assert_eq!(store.get_byte(id, 1), 42);
194 }
195
196 #[test]
197 fn test_multiple_strings() {
198 let mut store = StringStore::new();
199 let id1 = store.allocate_from(b"abc");
200 let id2 = store.allocate_from(b"xyz");
201 assert_eq!(store.get(id1, 0, 3), b"abc");
202 assert_eq!(store.get(id2, 0, 3), b"xyz");
203 }
204
205 #[test]
206 fn test_entity_indirection() {
207 let mut store = StringStore::new();
208 let id = store.allocate_from(b"test");
209 let meta = store.entities.get(id);
210 assert_eq!(meta.len, 4);
211 assert_eq!(meta.save_level, 0);
212 assert!(!meta.is_global());
213 }
214
215 #[test]
216 fn test_cow_copy() {
217 let mut store = StringStore::new();
218 let id = store.allocate_from(b"hello");
219
220 store.put_byte(id, 0, b'H');
222 assert_eq!(store.get(id, 0, 5), b"Hello");
223
224 let backup = store.cow_copy(id);
226
227 store.put_byte(id, 1, b'a');
229 assert_eq!(store.get(id, 0, 5), b"Hallo");
230 assert_eq!(store.get(backup, 0, 5), b"Hello");
231 }
232
233 #[test]
234 fn test_swap_offsets() {
235 let mut store = StringStore::new();
236 let id1 = store.allocate_from(b"aaa");
237 let id2 = store.allocate_from(b"bbb");
238
239 store.swap_offsets(id1, id2);
240 assert_eq!(store.get(id1, 0, 3), b"bbb");
241 assert_eq!(store.get(id2, 0, 3), b"aaa");
242 }
243
244 #[test]
245 fn test_allocate_with_save_level() {
246 let mut store = StringStore::new();
247 let id = store.allocate_with(5, 2, true, 0);
248 let meta = store.entities.get(id);
249 assert_eq!(meta.save_level, 2);
250 assert!(meta.is_global());
251 }
252}