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 from_bytes(data: Vec<u8>) -> Self {
58 Self { data }
59 }
60
61 pub fn size(&self) -> usize {
63 self.data.len()
64 }
65
66 pub fn is_string_start(&self, offset: u32) -> bool {
71 let offset = offset as usize;
72 if offset >= self.data.len() {
73 return false;
74 }
75 offset == 0 || self.data[offset - 1] == 0
78 }
79}
80
81#[derive(Debug, Clone)]
83pub struct CachedStringBlock {
84 data: Arc<Vec<u8>>,
86 cache: HashMap<u32, (usize, usize)>,
88}
89
90impl CachedStringBlock {
91 pub fn from_string_block(string_block: &StringBlock) -> Self {
93 let data = Arc::new(string_block.data().to_vec());
94 let mut cache = HashMap::new();
95
96 let mut offset = 0;
97 while offset < data.len() {
98 let start_offset = offset;
99
100 while offset < data.len() && data[offset] != 0 {
102 offset += 1;
103 }
104
105 cache.insert(start_offset as u32, (start_offset, offset));
107
108 offset += 1;
110 }
111
112 Self { data, cache }
113 }
114
115 pub fn get_string(&self, string_ref: StringRef) -> Result<&str> {
117 let offset = string_ref.offset() as usize;
118
119 if let Some((start, end)) = self.cache.get(&string_ref.offset()) {
120 std::str::from_utf8(&self.data[*start..*end])
122 .map_err(|e| Error::TypeConversion(format!("Invalid UTF-8 string: {e}")))
123 } else {
124 if offset >= self.data.len() {
126 return Err(Error::OutOfBounds(format!(
127 "String reference offset out of bounds: {} (max: {})",
128 offset,
129 self.data.len()
130 )));
131 }
132
133 let mut end = offset;
134 while end < self.data.len() && self.data[end] != 0 {
135 end += 1;
136 }
137
138 std::str::from_utf8(&self.data[offset..end])
140 .map_err(|e| Error::TypeConversion(format!("Invalid UTF-8 string: {e}")))
141 }
142 }
143}