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