1#[cfg(not(feature = "std"))]
8use alloc::{format, string::String, vec::Vec};
9
10use crate::convert::{TryToUsize, is_undefined_addr};
11use crate::datatype::{CharacterSet, Datatype};
12use crate::error::FormatError;
13use crate::global_heap::GlobalHeapIndex;
14#[cfg(test)]
15use crate::source::BytesSource;
16use crate::source::FileSource;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
24pub struct VlenStringReadOptions {
25 max_elements: Option<usize>,
26 max_payload_bytes: Option<usize>,
27}
28
29impl VlenStringReadOptions {
30 pub const fn new() -> Self {
32 Self {
33 max_elements: None,
34 max_payload_bytes: None,
35 }
36 }
37
38 pub const fn with_max_elements(mut self, max_elements: usize) -> Self {
40 self.max_elements = Some(max_elements);
41 self
42 }
43
44 pub const fn with_max_payload_bytes(mut self, max_payload_bytes: usize) -> Self {
46 self.max_payload_bytes = Some(max_payload_bytes);
47 self
48 }
49
50 pub const fn max_elements(&self) -> Option<usize> {
52 self.max_elements
53 }
54
55 pub const fn max_payload_bytes(&self) -> Option<usize> {
57 self.max_payload_bytes
58 }
59}
60
61#[derive(Debug, Clone)]
63pub struct VlElement {
64 pub length: u32,
66 pub collection_address: u64,
68 pub object_index: u32,
70}
71
72fn ensure_len(data: &[u8], offset: usize, needed: usize) -> Result<(), FormatError> {
73 match offset.checked_add(needed) {
74 Some(end) if end <= data.len() => Ok(()),
75 _ => Err(FormatError::UnexpectedEof {
76 expected: offset.saturating_add(needed),
77 available: data.len(),
78 }),
79 }
80}
81
82fn read_offset(data: &[u8], pos: usize, offset_size: u8) -> Result<u64, FormatError> {
83 let s = offset_size as usize;
84 ensure_len(data, pos, s)?;
85 let slice = &data[pos..pos + s];
86 Ok(match offset_size {
87 2 => u16::from_le_bytes([slice[0], slice[1]]) as u64,
88 4 => u32::from_le_bytes([slice[0], slice[1], slice[2], slice[3]]) as u64,
89 8 => u64::from_le_bytes([
90 slice[0], slice[1], slice[2], slice[3], slice[4], slice[5], slice[6], slice[7],
91 ]),
92 _ => return Err(FormatError::InvalidOffsetSize(offset_size)),
93 })
94}
95
96pub fn parse_vl_references(
98 raw_data: &[u8],
99 num_elements: u64,
100 offset_size: u8,
101) -> Result<Vec<VlElement>, FormatError> {
102 let elem_size = 4 + offset_size as u64 + 4; let total = num_elements
104 .checked_mul(elem_size)
105 .ok_or(FormatError::OffsetOverflow {
106 offset: num_elements,
107 length: elem_size,
108 })?
109 .to_usize()?;
110 if raw_data.len() < total {
111 return Err(FormatError::UnexpectedEof {
112 expected: total,
113 available: raw_data.len(),
114 });
115 }
116
117 let mut elements = Vec::with_capacity(num_elements.to_usize()?);
118 let mut pos = 0;
119
120 for _ in 0..num_elements {
121 let length = u32::from_le_bytes([
122 raw_data[pos],
123 raw_data[pos + 1],
124 raw_data[pos + 2],
125 raw_data[pos + 3],
126 ]);
127 pos += 4;
128
129 let collection_address = read_offset(raw_data, pos, offset_size)?;
130 pos += offset_size as usize;
131
132 let object_index = u32::from_le_bytes([
133 raw_data[pos],
134 raw_data[pos + 1],
135 raw_data[pos + 2],
136 raw_data[pos + 3],
137 ]);
138 pos += 4;
139
140 elements.push(VlElement {
141 length,
142 collection_address,
143 object_index,
144 });
145 }
146
147 Ok(elements)
148}
149
150pub(crate) fn is_vlen_string_datatype(datatype: &Datatype) -> bool {
153 match datatype {
154 Datatype::VariableLength {
155 is_string: true, ..
156 } => true,
157 Datatype::VariableLength {
158 is_string: false,
159 base_type,
160 ..
161 } => matches!(
162 base_type.as_ref(),
163 Datatype::String {
164 size: 1,
165 charset: CharacterSet::Ascii,
166 ..
167 }
168 ),
169 _ => false,
170 }
171}
172
173fn check_element_limit(
174 num_elements: u64,
175 options: VlenStringReadOptions,
176) -> Result<(), FormatError> {
177 if let Some(limit) = options.max_elements
178 && num_elements > limit as u64
179 {
180 return Err(FormatError::VariableLengthElementLimitExceeded {
181 limit,
182 actual: num_elements,
183 });
184 }
185 Ok(())
186}
187
188fn payload_size(refs: &[VlElement], options: VlenStringReadOptions) -> Result<u64, FormatError> {
189 let mut required = 0u64;
190 for element in refs {
191 required =
192 required
193 .checked_add(u64::from(element.length))
194 .ok_or(FormatError::OffsetOverflow {
195 offset: required,
196 length: u64::from(element.length),
197 })?;
198 }
199 if let Some(limit) = options.max_payload_bytes
200 && required > limit as u64
201 {
202 return Err(FormatError::VariableLengthByteLimitExceeded { limit, required });
203 }
204 Ok(required)
205}
206
207pub fn vlen_string_payload_size(
209 raw_data: &[u8],
210 num_elements: u64,
211 offset_size: u8,
212) -> Result<u64, FormatError> {
213 check_element_limit(num_elements, VlenStringReadOptions::default())?;
214 let refs = parse_vl_references(raw_data, num_elements, offset_size)?;
215 payload_size(&refs, VlenStringReadOptions::default())
216}
217
218pub fn visit_vl_strings_from_source<S, F>(
221 source: &S,
222 raw_data: &[u8],
223 num_elements: u64,
224 offset_size: u8,
225 length_size: u8,
226 base_address: u64,
227 options: VlenStringReadOptions,
228 mut visitor: F,
229) -> Result<(), FormatError>
230where
231 S: FileSource + ?Sized,
232 F: FnMut(&str),
233{
234 check_element_limit(num_elements, options)?;
235 let refs = parse_vl_references(raw_data, num_elements, offset_size)?;
236 payload_size(&refs, options)?;
237
238 let mut collections: Vec<(u64, GlobalHeapIndex)> = Vec::new();
239 for element in &refs {
240 if element.length == 0
241 && (is_undefined_addr(element.collection_address, offset_size)
242 || element.collection_address == 0)
243 {
244 visitor("");
245 continue;
246 }
247 if is_undefined_addr(element.collection_address, offset_size) {
248 return Err(FormatError::VlDataError(
249 "non-empty VL element has an undefined heap address".into(),
250 ));
251 }
252
253 let collection_address = element.collection_address.checked_add(base_address).ok_or(
254 FormatError::OffsetOverflow {
255 offset: element.collection_address,
256 length: base_address,
257 },
258 )?;
259 let collection_pos = match collections
260 .iter()
261 .position(|(address, _)| *address == collection_address)
262 {
263 Some(pos) => pos,
264 None => {
265 let collection = GlobalHeapIndex::parse(source, collection_address, length_size)?;
266 collections.push((collection_address, collection));
267 collections.len() - 1
268 }
269 };
270
271 let index = u16::try_from(element.object_index).map_err(|_| {
272 FormatError::VlDataError(format!(
273 "global heap object index {} does not fit u16",
274 element.object_index
275 ))
276 })?;
277 let object = collections[collection_pos].1.get_object(index).ok_or(
278 FormatError::GlobalHeapObjectNotFound {
279 collection_address,
280 index,
281 },
282 )?;
283 if u64::from(element.length) > object.size {
284 return Err(FormatError::VlDataError(format!(
285 "VL element length {} exceeds global heap object size {}",
286 element.length, object.size
287 )));
288 }
289
290 let bytes = source.read_exact_at(object.data_address, element.length as usize)?;
291 let string = String::from_utf8_lossy(&bytes);
292 visitor(&string);
293 }
294
295 Ok(())
296}
297
298pub fn read_vl_strings_from_source<S: FileSource + ?Sized>(
300 source: &S,
301 raw_data: &[u8],
302 num_elements: u64,
303 offset_size: u8,
304 length_size: u8,
305 base_address: u64,
306 options: VlenStringReadOptions,
307) -> Result<Vec<String>, FormatError> {
308 let mut strings = Vec::new();
309 visit_vl_strings_from_source(
310 source,
311 raw_data,
312 num_elements,
313 offset_size,
314 length_size,
315 base_address,
316 options,
317 |string| strings.push(String::from(string)),
318 )?;
319 Ok(strings)
320}
321
322#[derive(Debug, Clone, PartialEq, Eq)]
331pub(crate) enum VlByteObject {
332 Null,
334 Bytes(Vec<u8>),
336}
337
338pub(crate) fn read_vl_byte_objects_from_source<S: FileSource + ?Sized>(
352 source: &S,
353 raw_data: &[u8],
354 num_elements: u64,
355 offset_size: u8,
356 length_size: u8,
357 base_address: u64,
358 element_size: usize,
359 options: VlenStringReadOptions,
360) -> Result<Vec<VlByteObject>, FormatError> {
361 check_element_limit(num_elements, options)?;
362 let refs = parse_vl_references(raw_data, num_elements, offset_size)?;
363 payload_size(&refs, options)?;
364
365 let mut objects = Vec::with_capacity(refs.len());
366 let mut collections: Vec<(u64, GlobalHeapIndex)> = Vec::new();
367 for element in &refs {
368 if element.length == 0
369 && (is_undefined_addr(element.collection_address, offset_size)
370 || element.collection_address == 0)
371 {
372 objects.push(VlByteObject::Null);
373 continue;
374 }
375 if is_undefined_addr(element.collection_address, offset_size) {
376 return Err(FormatError::VlDataError(
377 "non-empty VL element has an undefined heap address".into(),
378 ));
379 }
380
381 let collection_address = element.collection_address.checked_add(base_address).ok_or(
382 FormatError::OffsetOverflow {
383 offset: element.collection_address,
384 length: base_address,
385 },
386 )?;
387 let collection_pos = match collections
388 .iter()
389 .position(|(address, _)| *address == collection_address)
390 {
391 Some(pos) => pos,
392 None => {
393 let collection = GlobalHeapIndex::parse(source, collection_address, length_size)?;
394 collections.push((collection_address, collection));
395 collections.len() - 1
396 }
397 };
398
399 let index = u16::try_from(element.object_index).map_err(|_| {
400 FormatError::VlDataError(format!(
401 "global heap object index {} does not fit u16",
402 element.object_index
403 ))
404 })?;
405 let object = collections[collection_pos].1.get_object(index).ok_or(
406 FormatError::GlobalHeapObjectNotFound {
407 collection_address,
408 index,
409 },
410 )?;
411 let byte_len = (element.length as u64)
415 .checked_mul(element_size as u64)
416 .ok_or(FormatError::OffsetOverflow {
417 offset: u64::from(element.length),
418 length: element_size as u64,
419 })?;
420 if byte_len > object.size {
421 return Err(FormatError::VlDataError(format!(
422 "VL element length {} ({} bytes) exceeds global heap object size {}",
423 element.length, byte_len, object.size
424 )));
425 }
426
427 let bytes = source.read_exact_at(object.data_address, byte_len.to_usize()?)?;
428 objects.push(VlByteObject::Bytes(bytes));
429 }
430
431 Ok(objects)
432}
433
434#[cfg(test)]
439pub fn read_vl_strings(
440 file_data: &[u8],
441 raw_data: &[u8],
442 num_elements: u64,
443 offset_size: u8,
444 length_size: u8,
445) -> Result<Vec<String>, FormatError> {
446 read_vl_strings_from_source(
447 &BytesSource::new(file_data),
448 raw_data,
449 num_elements,
450 offset_size,
451 length_size,
452 0,
453 VlenStringReadOptions::default(),
454 )
455}
456
457#[cfg(test)]
458mod tests {
459 use super::*;
460
461 fn build_gcol_at(
463 file_data: &mut Vec<u8>,
464 offset: usize,
465 objects: &[(u16, &[u8])], ) {
467 let length_size = 8usize;
468
469 let header_size = 8 + length_size;
471 let mut obj_total = 0usize;
472 for (_, data) in objects {
473 let padded = (data.len() + 7) & !7;
474 obj_total += 8 + length_size + padded;
475 }
476 obj_total += 2; let collection_size = header_size + obj_total;
478 let needed = offset + collection_size;
479 if file_data.len() < needed {
480 file_data.resize(needed, 0);
481 }
482
483 let mut pos = offset;
484 file_data[pos..pos + 4].copy_from_slice(b"GCOL");
486 file_data[pos + 4] = 1; pos += 8;
489 file_data[pos..pos + 8].copy_from_slice(&(collection_size as u64).to_le_bytes());
490 pos += 8;
491
492 for (index, data) in objects {
493 file_data[pos..pos + 2].copy_from_slice(&index.to_le_bytes());
494 file_data[pos + 2..pos + 4].copy_from_slice(&1u16.to_le_bytes()); pos += 8;
497 file_data[pos..pos + 8].copy_from_slice(&(data.len() as u64).to_le_bytes());
498 pos += 8;
499 file_data[pos..pos + data.len()].copy_from_slice(data);
500 let padded = (data.len() + 7) & !7;
501 pos += padded;
502 }
503 file_data[pos..pos + 2].copy_from_slice(&0u16.to_le_bytes());
505 }
506
507 fn build_vl_refs(
509 strings: &[&str],
510 collection_address: u64,
511 start_index: u16,
512 offset_size: u8,
513 ) -> Vec<u8> {
514 let mut raw = Vec::new();
515 for (i, s) in strings.iter().enumerate() {
516 raw.extend_from_slice(&(s.len() as u32).to_le_bytes());
517 match offset_size {
518 4 => raw.extend_from_slice(&(collection_address as u32).to_le_bytes()),
519 8 => raw.extend_from_slice(&collection_address.to_le_bytes()),
520 _ => panic!("unsupported"),
521 }
522 raw.extend_from_slice(&(start_index as u32 + i as u32).to_le_bytes());
523 }
524 raw
525 }
526
527 #[test]
528 fn parse_vl_references_two_elements() {
529 let raw = build_vl_refs(&["hello", "world"], 0x1000, 1, 8);
530 let refs = parse_vl_references(&raw, 2, 8).unwrap();
531 assert_eq!(refs.len(), 2);
532 assert_eq!(refs[0].length, 5);
533 assert_eq!(refs[0].collection_address, 0x1000);
534 assert_eq!(refs[0].object_index, 1);
535 assert_eq!(refs[1].length, 5);
536 assert_eq!(refs[1].object_index, 2);
537 }
538
539 #[test]
540 fn read_vl_strings_from_heap() {
541 let gcol_offset = 256usize;
542 let mut file_data = vec![0u8; 512];
543 build_gcol_at(&mut file_data, gcol_offset, &[(1, b"Alice"), (2, b"Bob")]);
544
545 let raw = build_vl_refs(&["Alice", "Bob"], gcol_offset as u64, 1, 8);
546 let strings = read_vl_strings(&file_data, &raw, 2, 8, 8).unwrap();
547 assert_eq!(strings, vec!["Alice", "Bob"]);
548 }
549
550 #[cfg(feature = "std")]
551 #[test]
552 fn read_vl_strings_from_seekable_source() {
553 use std::io::Cursor;
554
555 use crate::source::ReadSeekSource;
556
557 let gcol_offset = 256usize;
558 let mut file_data = vec![0u8; 512];
559 build_gcol_at(&mut file_data, gcol_offset, &[(1, b"Alice"), (2, b"Bob")]);
560 let raw = build_vl_refs(&["Alice", "Bob"], gcol_offset as u64, 1, 8);
561 let source = ReadSeekSource::new(Cursor::new(file_data)).unwrap();
562
563 let strings = read_vl_strings_from_source(
564 &source,
565 &raw,
566 2,
567 8,
568 8,
569 0,
570 VlenStringReadOptions::default(),
571 )
572 .unwrap();
573 assert_eq!(strings, vec!["Alice", "Bob"]);
574 }
575
576 #[test]
577 fn null_vl_element_empty_string() {
578 let mut raw = Vec::new();
580 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];
585 let strings = read_vl_strings(&file_data, &raw, 1, 8, 8).unwrap();
586 assert_eq!(strings, vec![""]);
587 }
588
589 #[test]
590 fn null_vl_element_zero_address() {
591 let mut raw = Vec::new();
592 raw.extend_from_slice(&0u32.to_le_bytes());
593 raw.extend_from_slice(&0u64.to_le_bytes());
594 raw.extend_from_slice(&0u32.to_le_bytes());
595
596 let file_data = vec![0u8; 16];
597 let strings = read_vl_strings(&file_data, &raw, 1, 8, 8).unwrap();
598 assert_eq!(strings, vec![""]);
599 }
600
601 #[test]
602 fn parse_vl_references_truncated_error() {
603 let raw = vec![0u8; 10]; let err = parse_vl_references(&raw, 1, 8).unwrap_err();
605 assert!(matches!(err, FormatError::UnexpectedEof { .. }));
606 }
607}