1use crate::{Error, Result, StringRef};
4use std::collections::HashMap;
5use std::io::{Read, Seek, SeekFrom};
6use std::sync::Arc;
7
8#[derive(Debug, Clone)]
10pub struct StringBlock {
11 data: Vec<u8>,
13}
14
15impl StringBlock {
16 pub fn parse<R: Read + Seek>(reader: &mut R, offset: u64, size: u32) -> Result<Self> {
18 reader.seek(SeekFrom::Start(offset))?;
19
20 let mut data = vec![0u8; size as usize];
21 reader.read_exact(&mut data)?;
22
23 Ok(Self { data })
24 }
25
26 pub fn get_string(&self, string_ref: StringRef) -> Result<&str> {
28 let offset = string_ref.offset() as usize;
29 if offset >= self.data.len() {
30 return Err(Error::OutOfBounds(format!(
31 "String reference offset out of bounds: {} (max: {})",
32 offset,
33 self.data.len()
34 )));
35 }
36
37 let mut end = offset;
39 while end < self.data.len() && self.data[end] != 0 {
40 end += 1;
41 }
42
43 std::str::from_utf8(&self.data[offset..end])
45 .map_err(|e| Error::TypeConversion(format!("Invalid UTF-8 string: {e}")))
46 }
47
48 pub fn data(&self) -> &[u8] {
50 &self.data
51 }
52
53 pub fn size(&self) -> usize {
55 self.data.len()
56 }
57
58 pub fn is_string_start(&self, offset: u32) -> bool {
63 let offset = offset as usize;
64 if offset >= self.data.len() {
65 return false;
66 }
67 offset == 0 || self.data[offset - 1] == 0
70 }
71}
72
73#[derive(Debug, Clone)]
75pub struct CachedStringBlock {
76 data: Arc<Vec<u8>>,
78 cache: HashMap<u32, (usize, usize)>,
80}
81
82impl CachedStringBlock {
83 pub fn from_string_block(string_block: &StringBlock) -> Self {
85 let data = Arc::new(string_block.data().to_vec());
86 let mut cache = HashMap::new();
87
88 let mut offset = 0;
89 while offset < data.len() {
90 let start_offset = offset;
91
92 while offset < data.len() && data[offset] != 0 {
94 offset += 1;
95 }
96
97 cache.insert(start_offset as u32, (start_offset, offset));
99
100 offset += 1;
102 }
103
104 Self { data, cache }
105 }
106
107 pub fn get_string(&self, string_ref: StringRef) -> Result<&str> {
109 let offset = string_ref.offset() as usize;
110
111 if let Some((start, end)) = self.cache.get(&string_ref.offset()) {
112 std::str::from_utf8(&self.data[*start..*end])
114 .map_err(|e| Error::TypeConversion(format!("Invalid UTF-8 string: {e}")))
115 } else {
116 if offset >= self.data.len() {
118 return Err(Error::OutOfBounds(format!(
119 "String reference offset out of bounds: {} (max: {})",
120 offset,
121 self.data.len()
122 )));
123 }
124
125 let mut end = offset;
126 while end < self.data.len() && self.data[end] != 0 {
127 end += 1;
128 }
129
130 std::str::from_utf8(&self.data[offset..end])
132 .map_err(|e| Error::TypeConversion(format!("Invalid UTF-8 string: {e}")))
133 }
134 }
135}