elb/
strings.rs

1use alloc::vec;
2use alloc::vec::Vec;
3use core::ffi::CStr;
4
5use crate::BlockRead;
6use crate::ByteOrder;
7use crate::Class;
8use crate::ElfRead;
9use crate::ElfWrite;
10use crate::Error;
11
12/// A table that stores NUL-terminated strings.
13///
14/// Always starts and ends with a NUL byte.
15#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
16pub struct StringTable(Vec<u8>);
17
18impl StringTable {
19    /// Create an empty table.
20    pub fn new() -> Self {
21        // String tables always start and end with a NUL byte.
22        Self(vec![0])
23    }
24
25    /// Insert new string into the table.
26    ///
27    /// Does nothing if the string is already in the table.
28    ///
29    /// Returns the offset at which you can find the string.
30    pub fn insert(&mut self, string: &CStr) -> usize {
31        if let Some(offset) = self.get_offset(string) {
32            return offset;
33        }
34        debug_assert!(!self.0.is_empty());
35        let offset = self.0.len();
36        self.0.extend_from_slice(string.to_bytes_with_nul());
37        offset
38    }
39
40    /// Get the offset of the string in the table.
41    ///
42    /// Returns `None` if the string isn't present in the table.
43    pub fn get_offset(&self, string: &CStr) -> Option<usize> {
44        debug_assert!(!self.0.is_empty());
45        let string = string.to_bytes_with_nul();
46        let mut j = 0;
47        let n = string.len();
48        for i in 0..self.0.len() {
49            if self.0[i] == string[j] {
50                j += 1;
51                if j == n {
52                    return Some(i + 1 - n);
53                }
54            } else {
55                j = 0;
56            }
57        }
58        None
59    }
60
61    /// Get a reference to a string at `offset`.
62    ///
63    /// Returns `None` if the offset is out-of-bounds.
64    pub fn get_string(&self, offset: usize) -> Option<&CStr> {
65        let c_str_bytes = self.0.get(offset..)?;
66        CStr::from_bytes_until_nul(c_str_bytes).ok()
67    }
68
69    /// Check that the table contains no strings.
70    pub fn is_empty(&self) -> bool {
71        self.0.iter().all(|b| *b == 0)
72    }
73
74    /// Get the underlying byte slice.
75    ///
76    /// The slice is never empty.
77    pub fn as_bytes(&self) -> &[u8] {
78        self.0.as_slice()
79    }
80
81    /// Get the underlying vector.
82    pub fn into_inner(self) -> Vec<u8> {
83        self.0
84    }
85
86    /// Read the table from the `reader`.
87    pub fn read<R: ElfRead>(reader: &mut R, len: u64) -> Result<Self, Error> {
88        let mut strings = vec![0_u8; len as usize];
89        reader.read_bytes(&mut strings[..])?;
90        Ok(Self(strings))
91    }
92
93    /// Write the table to the `writer`.
94    pub fn write<W: ElfWrite>(&self, writer: &mut W) -> Result<(), Error> {
95        writer.write_bytes(self.as_bytes())
96    }
97}
98
99impl From<Vec<u8>> for StringTable {
100    fn from(mut strings: Vec<u8>) -> Self {
101        if strings.is_empty() {
102            return Self::new();
103        }
104        if strings.first().copied() != Some(0) {
105            strings.insert(0, 0);
106        }
107        if strings.last().copied() != Some(0) {
108            strings.push(0);
109        }
110        Self(strings)
111    }
112}
113
114impl BlockRead for StringTable {
115    fn read<R: ElfRead>(
116        reader: &mut R,
117        _class: Class,
118        _byte_order: ByteOrder,
119        len: u64,
120    ) -> Result<Self, Error> {
121        StringTable::read(reader, len)
122    }
123}
124
125impl AsRef<[u8]> for StringTable {
126    fn as_ref(&self) -> &[u8] {
127        self.as_bytes()
128    }
129}
130
131impl Default for StringTable {
132    fn default() -> Self {
133        Self::new()
134    }
135}
136
137impl<T: AsRef<CStr>> FromIterator<T> for StringTable {
138    fn from_iter<I>(items: I) -> Self
139    where
140        I: IntoIterator<Item = T>,
141    {
142        let mut strings: Vec<u8> = Vec::new();
143        strings.push(0_u8);
144        for item in items.into_iter() {
145            strings.extend_from_slice(item.as_ref().to_bytes_with_nul());
146        }
147        Self(strings)
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use super::*;
154    use alloc::ffi::CString;
155    use arbitrary::Arbitrary;
156    use arbitrary::Unstructured;
157    use arbtest::arbtest;
158
159    use crate::test::test_block_io;
160
161    #[test]
162    fn test_get_offset() {
163        assert_eq!(
164            Some(1),
165            StringTable::from(b"hello\0".to_vec()).get_offset(c"hello")
166        );
167        assert_eq!(
168            Some(1),
169            StringTable::from(b"\0hello\0".to_vec()).get_offset(c"hello")
170        );
171        assert_eq!(
172            Some(7),
173            StringTable::from(b"\0first\0hello\0".to_vec()).get_offset(c"hello")
174        );
175        assert_eq!(None, StringTable::from(b"".to_vec()).get_offset(c"hello"));
176        assert_eq!(Some(0), StringTable::from(b"".to_vec()).get_offset(c""));
177        assert_eq!(Some(0), StringTable::from(b"abc".to_vec()).get_offset(c""));
178        assert_eq!(
179            Some(0),
180            StringTable::from(b"\0abc".to_vec()).get_offset(c"")
181        );
182    }
183
184    #[test]
185    fn test_symmetry() {
186        test_get_string(b"hello\0", c"hello");
187        test_get_string(b"\0abc", c"");
188    }
189
190    fn test_get_string(strings: &[u8], expected: &CStr) {
191        let table: StringTable = strings.to_vec().into();
192        let offset = table.get_offset(expected).unwrap();
193        let actual = table.get_string(offset).unwrap();
194        assert_eq!(expected, actual);
195    }
196
197    #[test]
198    fn test_get_offset_get_string_symmetry() {
199        arbtest(|u| {
200            let strings: Vec<CString> = u.arbitrary()?;
201            let mut table: StringTable = Default::default();
202            assert_eq!(Some(0), table.0.last().copied());
203            for s in strings.iter() {
204                table.insert(s);
205                assert_eq!(Some(0), table.0.last().copied());
206            }
207            for s in strings.iter() {
208                let offset = table.get_offset(s).unwrap();
209                let actual = table.get_string(offset).unwrap();
210                assert_eq!(s.as_ref(), actual);
211            }
212            Ok(())
213        });
214    }
215
216    #[test]
217    fn string_table_io() {
218        test_block_io::<StringTable>();
219    }
220
221    impl<'a> Arbitrary<'a> for StringTable {
222        fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
223            let strings: Vec<CString> = u.arbitrary()?;
224            Ok(strings.into_iter().collect())
225        }
226    }
227}