idalib/
strings.rs

1use std::marker::PhantomData;
2
3use crate::ffi::bytes::idalib_get_bytes;
4use crate::ffi::strings::{
5    build_strlist, clear_strlist, get_strlist_qty, idalib_get_strlist_item_addr,
6    idalib_get_strlist_item_length,
7};
8use crate::ffi::BADADDR;
9
10use crate::idb::IDB;
11use crate::Address;
12
13pub type StringIndex = usize;
14
15pub struct StringList<'a> {
16    _marker: PhantomData<&'a IDB>,
17}
18
19impl<'a> StringList<'a> {
20    pub(crate) fn new(_: &'a IDB) -> Self {
21        Self {
22            _marker: PhantomData,
23        }
24    }
25
26    pub fn rebuild(&self) {
27        unsafe { build_strlist() }
28    }
29
30    pub fn clear(&self) {
31        unsafe { clear_strlist() }
32    }
33
34    pub fn get_by_index(&self, index: StringIndex) -> Option<String> {
35        let addr = self.get_address_by_index(index)?;
36        let size = self.get_length_by_index(index);
37
38        // See also `IDB::get_bytes`
39        let mut buf = Vec::with_capacity(size);
40        let Ok(new_len) = (unsafe { idalib_get_bytes(addr.into(), &mut buf) }) else {
41            return None;
42        };
43        unsafe {
44            buf.set_len(new_len);
45        }
46
47        // TODO: switch to `String::from_utf8_lossy_owned` once it's stable
48        Some(String::from_utf8_lossy(&buf).into_owned())
49    }
50
51    pub fn get_address_by_index(&self, index: StringIndex) -> Option<Address> {
52        let addr = unsafe { idalib_get_strlist_item_addr(index) };
53        if addr == BADADDR {
54            None
55        } else {
56            Some(addr.into())
57        }
58    }
59
60    fn get_length_by_index(&self, index: StringIndex) -> usize {
61        unsafe { idalib_get_strlist_item_length(index) }
62    }
63
64    pub fn len(&self) -> usize {
65        unsafe { get_strlist_qty() }
66    }
67
68    pub fn is_empty(&self) -> bool {
69        self.len() == 0
70    }
71
72    pub fn iter(&self) -> StringListIter<'_, 'a> {
73        StringListIter {
74            string_list: self,
75            current_index: 0,
76        }
77    }
78}
79
80pub struct StringListIter<'s, 'a> {
81    string_list: &'s StringList<'a>,
82    current_index: StringIndex,
83}
84
85impl<'s, 'a> Iterator for StringListIter<'s, 'a> {
86    type Item = (Address, String);
87
88    fn next(&mut self) -> Option<Self::Item> {
89        while self.current_index < self.string_list.len() {
90            let addr = self.string_list.get_address_by_index(self.current_index);
91            let string = self.string_list.get_by_index(self.current_index);
92
93            self.current_index += 1;
94
95            if let (Some(addr), Some(string)) = (addr, string) {
96                return Some((addr, string));
97            };
98            // skip invalid strings, such as:
99            // - the index became invalid, such as if a string was undefined
100            // - the string failed to decode (today: not UTF-8)
101        }
102        None
103    }
104}