1#[cfg(not(feature = "std"))]
8use alloc::{string::String, vec::Vec};
9
10use crate::error::FormatError;
11use crate::global_heap::GlobalHeapCollection;
12
13#[derive(Debug, Clone)]
15pub struct VlElement {
16 pub length: u32,
18 pub collection_address: u64,
20 pub object_index: u32,
22}
23
24fn ensure_len(data: &[u8], offset: usize, needed: usize) -> Result<(), FormatError> {
25 match offset.checked_add(needed) {
26 Some(end) if end <= data.len() => Ok(()),
27 _ => Err(FormatError::UnexpectedEof {
28 expected: offset.saturating_add(needed),
29 available: data.len(),
30 }),
31 }
32}
33
34fn read_offset(data: &[u8], pos: usize, offset_size: u8) -> Result<u64, FormatError> {
35 let s = offset_size as usize;
36 ensure_len(data, pos, s)?;
37 let slice = &data[pos..pos + s];
38 Ok(match offset_size {
39 2 => u16::from_le_bytes([slice[0], slice[1]]) as u64,
40 4 => u32::from_le_bytes([slice[0], slice[1], slice[2], slice[3]]) as u64,
41 8 => u64::from_le_bytes([
42 slice[0], slice[1], slice[2], slice[3], slice[4], slice[5], slice[6], slice[7],
43 ]),
44 _ => return Err(FormatError::InvalidOffsetSize(offset_size)),
45 })
46}
47
48pub fn parse_vl_references(
50 raw_data: &[u8],
51 num_elements: u64,
52 offset_size: u8,
53) -> Result<Vec<VlElement>, FormatError> {
54 let elem_size = 4 + offset_size as usize + 4; let total = num_elements as usize * elem_size;
56 if raw_data.len() < total {
57 return Err(FormatError::UnexpectedEof {
58 expected: total,
59 available: raw_data.len(),
60 });
61 }
62
63 let mut elements = Vec::with_capacity(num_elements as usize);
64 let mut pos = 0;
65
66 for _ in 0..num_elements {
67 let length = u32::from_le_bytes([
68 raw_data[pos],
69 raw_data[pos + 1],
70 raw_data[pos + 2],
71 raw_data[pos + 3],
72 ]);
73 pos += 4;
74
75 let collection_address = read_offset(raw_data, pos, offset_size)?;
76 pos += offset_size as usize;
77
78 let object_index = u32::from_le_bytes([
79 raw_data[pos],
80 raw_data[pos + 1],
81 raw_data[pos + 2],
82 raw_data[pos + 3],
83 ]);
84 pos += 4;
85
86 elements.push(VlElement {
87 length,
88 collection_address,
89 object_index,
90 });
91 }
92
93 Ok(elements)
94}
95
96fn is_undefined_address(addr: u64, offset_size: u8) -> bool {
98 match offset_size {
99 2 => addr == 0xFFFF,
100 4 => addr == 0xFFFF_FFFF,
101 8 => addr == 0xFFFF_FFFF_FFFF_FFFF,
102 _ => false,
103 }
104}
105
106pub fn read_vl_strings(
108 file_data: &[u8],
109 raw_data: &[u8],
110 num_elements: u64,
111 offset_size: u8,
112 length_size: u8,
113) -> Result<Vec<String>, FormatError> {
114 let refs = parse_vl_references(raw_data, num_elements, offset_size)?;
115 let mut result = Vec::with_capacity(refs.len());
116
117 for vl in &refs {
118 if vl.length == 0 && is_undefined_address(vl.collection_address, offset_size) {
119 result.push(String::new());
120 continue;
121 }
122 if vl.length == 0 && vl.collection_address == 0 {
123 result.push(String::new());
124 continue;
125 }
126
127 let coll = GlobalHeapCollection::parse(
128 file_data,
129 vl.collection_address as usize,
130 length_size,
131 )?;
132 let obj = coll
133 .get_object(vl.object_index as u16)
134 .ok_or(FormatError::GlobalHeapObjectNotFound {
135 collection_address: vl.collection_address,
136 index: vl.object_index as u16,
137 })?;
138
139 let len = (vl.length as usize).min(obj.data.len());
141 let s = String::from_utf8_lossy(&obj.data[..len]).into_owned();
142 result.push(s);
143 }
144
145 Ok(result)
146}
147
148pub fn read_vl_bytes(
150 file_data: &[u8],
151 raw_data: &[u8],
152 num_elements: u64,
153 offset_size: u8,
154 length_size: u8,
155) -> Result<Vec<Vec<u8>>, FormatError> {
156 let refs = parse_vl_references(raw_data, num_elements, offset_size)?;
157 let mut result = Vec::with_capacity(refs.len());
158
159 for vl in &refs {
160 if vl.length == 0 && (is_undefined_address(vl.collection_address, offset_size) || vl.collection_address == 0) {
161 result.push(Vec::new());
162 continue;
163 }
164
165 let coll = GlobalHeapCollection::parse(
166 file_data,
167 vl.collection_address as usize,
168 length_size,
169 )?;
170 let obj = coll
171 .get_object(vl.object_index as u16)
172 .ok_or(FormatError::GlobalHeapObjectNotFound {
173 collection_address: vl.collection_address,
174 index: vl.object_index as u16,
175 })?;
176
177 let len = (vl.length as usize).min(obj.data.len());
178 result.push(obj.data[..len].to_vec());
179 }
180
181 Ok(result)
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187
188 fn build_gcol_at(
190 file_data: &mut Vec<u8>,
191 offset: usize,
192 objects: &[(u16, &[u8])], ) {
194 let length_size = 8usize;
195
196 let header_size = 8 + length_size;
198 let mut obj_total = 0usize;
199 for (_, data) in objects {
200 let padded = (data.len() + 7) & !7;
201 obj_total += 8 + length_size + padded;
202 }
203 obj_total += 2; let collection_size = header_size + obj_total;
205 let needed = offset + collection_size;
206 if file_data.len() < needed {
207 file_data.resize(needed, 0);
208 }
209
210 let mut pos = offset;
211 file_data[pos..pos + 4].copy_from_slice(b"GCOL");
213 file_data[pos + 4] = 1; pos += 8;
216 file_data[pos..pos + 8].copy_from_slice(&(collection_size as u64).to_le_bytes());
217 pos += 8;
218
219 for (index, data) in objects {
220 file_data[pos..pos + 2].copy_from_slice(&index.to_le_bytes());
221 file_data[pos + 2..pos + 4].copy_from_slice(&1u16.to_le_bytes()); pos += 8;
224 file_data[pos..pos + 8].copy_from_slice(&(data.len() as u64).to_le_bytes());
225 pos += 8;
226 file_data[pos..pos + data.len()].copy_from_slice(data);
227 let padded = (data.len() + 7) & !7;
228 pos += padded;
229 }
230 file_data[pos..pos + 2].copy_from_slice(&0u16.to_le_bytes());
232 }
233
234 fn build_vl_refs(
236 strings: &[&str],
237 collection_address: u64,
238 start_index: u16,
239 offset_size: u8,
240 ) -> Vec<u8> {
241 let mut raw = Vec::new();
242 for (i, s) in strings.iter().enumerate() {
243 raw.extend_from_slice(&(s.len() as u32).to_le_bytes());
244 match offset_size {
245 4 => raw.extend_from_slice(&(collection_address as u32).to_le_bytes()),
246 8 => raw.extend_from_slice(&collection_address.to_le_bytes()),
247 _ => panic!("unsupported"),
248 }
249 raw.extend_from_slice(&(start_index as u32 + i as u32).to_le_bytes());
250 }
251 raw
252 }
253
254 #[test]
255 fn parse_vl_references_two_elements() {
256 let raw = build_vl_refs(&["hello", "world"], 0x1000, 1, 8);
257 let refs = parse_vl_references(&raw, 2, 8).unwrap();
258 assert_eq!(refs.len(), 2);
259 assert_eq!(refs[0].length, 5);
260 assert_eq!(refs[0].collection_address, 0x1000);
261 assert_eq!(refs[0].object_index, 1);
262 assert_eq!(refs[1].length, 5);
263 assert_eq!(refs[1].object_index, 2);
264 }
265
266 #[test]
267 fn read_vl_strings_from_heap() {
268 let gcol_offset = 256usize;
269 let mut file_data = vec![0u8; 512];
270 build_gcol_at(&mut file_data, gcol_offset, &[
271 (1, b"Alice"),
272 (2, b"Bob"),
273 ]);
274
275 let raw = build_vl_refs(&["Alice", "Bob"], gcol_offset as u64, 1, 8);
276 let strings = read_vl_strings(&file_data, &raw, 2, 8, 8).unwrap();
277 assert_eq!(strings, vec!["Alice", "Bob"]);
278 }
279
280 #[test]
281 fn null_vl_element_empty_string() {
282 let mut raw = Vec::new();
284 raw.extend_from_slice(&0u32.to_le_bytes()); raw.extend_from_slice(&u64::MAX.to_le_bytes()); raw.extend_from_slice(&0u32.to_le_bytes()); let file_data = vec![0u8; 16];
289 let strings = read_vl_strings(&file_data, &raw, 1, 8, 8).unwrap();
290 assert_eq!(strings, vec![""]);
291 }
292
293 #[test]
294 fn null_vl_element_zero_address() {
295 let mut raw = Vec::new();
296 raw.extend_from_slice(&0u32.to_le_bytes());
297 raw.extend_from_slice(&0u64.to_le_bytes());
298 raw.extend_from_slice(&0u32.to_le_bytes());
299
300 let file_data = vec![0u8; 16];
301 let strings = read_vl_strings(&file_data, &raw, 1, 8, 8).unwrap();
302 assert_eq!(strings, vec![""]);
303 }
304
305 #[test]
306 fn read_vl_bytes_from_heap() {
307 let gcol_offset = 128usize;
308 let mut file_data = vec![0u8; 512];
309 build_gcol_at(&mut file_data, gcol_offset, &[
310 (1, &[0xDE, 0xAD]),
311 (2, &[0xBE, 0xEF, 0xCA]),
312 ]);
313
314 let raw = build_vl_refs(&["ab", "abc"], gcol_offset as u64, 1, 8);
315 let mut raw_fixed = Vec::new();
317 raw_fixed.extend_from_slice(&2u32.to_le_bytes());
318 raw_fixed.extend_from_slice(&(gcol_offset as u64).to_le_bytes());
319 raw_fixed.extend_from_slice(&1u32.to_le_bytes());
320 raw_fixed.extend_from_slice(&3u32.to_le_bytes());
321 raw_fixed.extend_from_slice(&(gcol_offset as u64).to_le_bytes());
322 raw_fixed.extend_from_slice(&2u32.to_le_bytes());
323
324 let bytes = read_vl_bytes(&file_data, &raw_fixed, 2, 8, 8).unwrap();
325 assert_eq!(bytes, vec![vec![0xDE, 0xAD], vec![0xBE, 0xEF, 0xCA]]);
326 }
327
328 #[test]
329 fn parse_vl_references_truncated_error() {
330 let raw = vec![0u8; 10]; let err = parse_vl_references(&raw, 1, 8).unwrap_err();
332 assert!(matches!(err, FormatError::UnexpectedEof { .. }));
333 }
334}