1use core::fmt;
5use core::ops::Index;
6use core::str;
7use scroll::{ctx, Pread};
8if_alloc! {
9 use crate::error;
10 use alloc::vec::Vec;
11}
12
13pub struct Strtab<'a> {
17 delim: ctx::StrCtx,
18 bytes: &'a [u8],
19 #[cfg(feature = "alloc")]
20 strings: Vec<(usize, &'a str)>,
21}
22
23#[inline(always)]
24fn get_str(offset: usize, bytes: &[u8], delim: ctx::StrCtx) -> scroll::Result<&str> {
25 bytes.pread_with::<&str>(offset, delim)
26}
27
28impl<'a> Strtab<'a> {
29 pub fn new(bytes: &'a [u8], delim: u8) -> Self {
34 Self::from_slice_unparsed(bytes, 0, bytes.len(), delim)
35 }
36
37 pub fn len(&self) -> usize {
39 self.bytes.len()
40 }
41
42 pub fn from_slice_unparsed(bytes: &'a [u8], offset: usize, len: usize, delim: u8) -> Self {
46 Self {
47 delim: ctx::StrCtx::Delimiter(delim),
48 bytes: &bytes[offset..offset + len],
49 #[cfg(feature = "alloc")]
50 strings: Vec::new(),
51 }
52 }
53 pub fn get_unsafe(&self, offset: usize) -> Option<&'a str> {
58 if offset >= self.bytes.len() {
59 None
60 } else {
61 Some(get_str(offset, self.bytes, self.delim).unwrap())
62 }
63 }
64 #[cfg(feature = "alloc")]
65 pub fn parse(bytes: &'a [u8], offset: usize, len: usize, delim: u8) -> error::Result<Self> {
70 let (end, overflow) = offset.overflowing_add(len);
71 if overflow || end > bytes.len() {
72 return Err(error::Error::Malformed(format!(
73 "Strtable size ({}) + offset ({}) is out of bounds for {} #bytes. Overflowed: {}",
74 len,
75 offset,
76 bytes.len(),
77 overflow
78 )));
79 }
80 let mut result = Self::from_slice_unparsed(bytes, offset, len, delim);
81 let mut i = 0;
82 while i < result.bytes.len() {
83 let string = get_str(i, result.bytes, result.delim)?;
84 result.strings.push((i, string));
85 i += string.len() + 1;
86 }
87 Ok(result)
88 }
89 #[cfg(feature = "alloc")]
90 pub fn new_preparsed(bytes: &'a [u8], delim: u8) -> error::Result<Self> {
94 Self::parse(bytes, 0, bytes.len(), delim)
95 }
96 #[cfg(feature = "alloc")]
97 pub fn to_vec(&self) -> error::Result<Vec<&'a str>> {
120 if self.strings.is_empty() {
122 let mut result = Vec::new();
123 let mut i = 0;
124 while i < self.bytes.len() {
125 let string = get_str(i, self.bytes, self.delim)?;
126 result.push(string);
127 i += string.len() + 1;
128 }
129 return Ok(result);
130 }
131 Ok(self.strings.iter().map(|&(_key, value)| value).collect())
132 }
133 #[cfg(feature = "alloc")]
134 pub fn get_at(&self, offset: usize) -> Option<&'a str> {
139 match self
140 .strings
141 .binary_search_by_key(&offset, |&(key, _value)| key)
142 {
143 Ok(index) => Some(self.strings[index].1),
144 Err(index) => {
145 if index == 0 {
146 return None;
147 }
148 let (string_begin_offset, entire_string) = self.strings[index - 1];
149 entire_string.get(offset - string_begin_offset..)
150 }
151 }
152 }
153 #[deprecated(since = "0.4.2", note = "Use from_slice_unparsed() instead")]
154 pub unsafe fn from_raw(ptr: *const u8, len: usize, delim: u8) -> Strtab<'a> {
159 Self::from_slice_unparsed(core::slice::from_raw_parts(ptr, len), 0, len, delim)
160 }
161 #[deprecated(since = "0.4.2", note = "Bad performance, use get_at() instead")]
162 #[cfg(feature = "alloc")]
163 pub fn get(&self, offset: usize) -> Option<error::Result<&'a str>> {
168 if offset >= self.bytes.len() {
169 None
170 } else {
171 Some(get_str(offset, self.bytes, self.delim).map_err(core::convert::Into::into))
172 }
173 }
174}
175
176impl<'a> fmt::Debug for Strtab<'a> {
177 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
178 f.debug_struct("Strtab")
179 .field("delim", &self.delim)
180 .field("bytes", &str::from_utf8(self.bytes))
181 .finish()
182 }
183}
184
185impl<'a> Default for Strtab<'a> {
186 fn default() -> Self {
187 Self {
188 delim: ctx::StrCtx::default(),
189 bytes: &[],
190 #[cfg(feature = "alloc")]
191 strings: Vec::new(),
192 }
193 }
194}
195
196impl<'a> Index<usize> for Strtab<'a> {
197 type Output = str;
198 #[inline(always)]
201 fn index(&self, offset: usize) -> &Self::Output {
202 get_str(offset, self.bytes, self.delim).unwrap()
206 }
207}
208
209#[test]
210fn as_vec_no_final_null() {
211 let strtab = Strtab::new_preparsed(b"\0printf\0memmove\0busta", 0x0).unwrap();
212 let vec = strtab.to_vec().unwrap();
213 assert_eq!(vec.len(), 4);
214 assert_eq!(vec, vec!["", "printf", "memmove", "busta"]);
215}
216
217#[test]
218fn as_vec_no_first_null_no_final_null() {
219 let strtab = Strtab::new_preparsed(b"printf\0memmove\0busta", 0x0).unwrap();
220 let vec = strtab.to_vec().unwrap();
221 assert_eq!(vec.len(), 3);
222 assert_eq!(vec, vec!["printf", "memmove", "busta"]);
223}
224
225#[test]
226fn to_vec_final_null() {
227 let strtab = Strtab::new_preparsed(b"\0printf\0memmove\0busta\0", 0x0).unwrap();
228 let vec = strtab.to_vec().unwrap();
229 assert_eq!(vec.len(), 4);
230 assert_eq!(vec, vec!["", "printf", "memmove", "busta"]);
231}
232
233#[test]
234fn to_vec_newline_delim() {
235 let strtab = Strtab::new_preparsed(b"\nprintf\nmemmove\nbusta\n", b'\n').unwrap();
236 let vec = strtab.to_vec().unwrap();
237 assert_eq!(vec.len(), 4);
238 assert_eq!(vec, vec!["", "printf", "memmove", "busta"]);
239}
240
241#[test]
242fn parse_utf8() {
243 assert!(match Strtab::new_preparsed(&[0x80, 0x80], b'\n') {
244 Err(error::Error::Scroll(scroll::Error::BadInput {
245 size: 2,
246 msg: "invalid utf8",
247 })) => true,
248 _ => false,
249 });
250 assert!(
251 match Strtab::new_preparsed(&[0xC6, 0x92, 0x6F, 0x6F], b'\n') {
252 Ok(_) => true,
253 _ => false,
254 }
255 );
256}
257
258#[test]
259fn get_at_utf8() {
260 let strtab = Strtab::new_preparsed("\nƒoo\nmemmove\n🅱️usta\n".as_bytes(), b'\n').unwrap();
261 assert_eq!(strtab.get_at(0), Some(""));
262 assert_eq!(strtab.get_at(5), Some(""));
263 assert_eq!(strtab.get_at(6), Some("memmove"));
264 assert_eq!(strtab.get_at(14), Some("\u{1f171}\u{fe0f}usta"));
265 assert_eq!(strtab.get_at(16), None);
266 assert_eq!(strtab.get_at(18), Some("\u{fe0f}usta"));
267 assert_eq!(strtab.get_at(21), Some("usta"));
268 assert_eq!(strtab.get_at(25), Some(""));
269 assert_eq!(strtab.get_at(26), None);
270}