1use alloc::vec::Vec;
2use core::ops::{Deref, DerefMut};
3
4use crate::common::{DebugLineStrOffset, DebugStrOffset, SectionId};
5use crate::write::{BaseId, FnvIndexSet, Result, Section, Writer};
6
7macro_rules! define_string_table {
20 ($name:ident, $id:ident, $section:ident, $offset:ident, $docs:expr) => {
21 #[doc=$docs]
22 #[derive(Debug, Default)]
23 pub struct $name {
24 base_id: BaseId,
25 strings: FnvIndexSet<Vec<u8>>,
26 offsets: Vec<$offset>,
27 len: usize,
28 }
29
30 impl $name {
31 pub fn add<T>(&mut self, bytes: T) -> $id
39 where
40 T: Into<Vec<u8>>,
41 {
42 let bytes = bytes.into();
43 assert!(!bytes.contains(&0));
44 let len = bytes.len();
45 let (index, inserted) = self.strings.insert_full(bytes);
46 if inserted {
47 self.offsets.push($offset(self.len));
48 self.len += len + 1;
49 }
50 $id::new(self.base_id, index)
51 }
52
53 #[inline]
55 pub fn count(&self) -> usize {
56 self.strings.len()
57 }
58
59 pub fn get(&self, id: $id) -> &[u8] {
65 debug_assert_eq!(self.base_id, id.base_id);
66 self.strings.get_index(id.index).map(Vec::as_slice).unwrap()
67 }
68
69 pub fn offset(&self, id: $id) -> $offset {
75 debug_assert_eq!(self.base_id, id.base_id);
76 self.offsets[id.index]
77 }
78
79 pub fn write<W: Writer>(&self, w: &mut $section<W>) -> Result<()> {
81 for bytes in self.strings.iter() {
82 w.write(bytes)?;
83 w.write_u8(0)?;
84 }
85 Ok(())
86 }
87 }
88 };
89}
90
91define_id!(StringId, "An identifier for a string in a `StringTable`.");
92
93define_string_table!(
94 StringTable,
95 StringId,
96 DebugStr,
97 DebugStrOffset,
98 "A table of strings that will be stored in a `.debug_str` section."
99);
100
101define_section!(DebugStr, DebugStrOffset, "A writable `.debug_str` section.");
102
103define_id!(
104 LineStringId,
105 "An identifier for a string in a `LineStringTable`."
106);
107
108define_string_table!(
109 LineStringTable,
110 LineStringId,
111 DebugLineStr,
112 DebugLineStrOffset,
113 "A table of strings that will be stored in a `.debug_line_str` section."
114);
115
116define_section!(
117 DebugLineStr,
118 DebugLineStrOffset,
119 "A writable `.debug_line_str` section."
120);
121
122#[cfg(test)]
123#[cfg(feature = "read")]
124mod tests {
125 use super::*;
126 use crate::LittleEndian;
127 use crate::read;
128 use crate::write::EndianVec;
129
130 #[test]
131 fn test_string_table() {
132 let mut strings = StringTable::default();
133 assert_eq!(strings.count(), 0);
134 let id1 = strings.add(&b"one"[..]);
135 let id2 = strings.add(&b"two"[..]);
136 let id3 = strings.add(&[]);
137 assert_eq!(strings.add(&b"one"[..]), id1);
138 assert_eq!(strings.add(&b"two"[..]), id2);
139 assert_eq!(strings.add(&[]), id3);
140 assert_eq!(strings.get(id1), &b"one"[..]);
141 assert_eq!(strings.get(id2), &b"two"[..]);
142 assert_eq!(strings.get(id3), &[]);
143 assert_eq!(strings.count(), 3);
144 assert_eq!(strings.offset(id1), DebugStrOffset(0));
145 assert_eq!(strings.offset(id2), DebugStrOffset(4));
146 assert_eq!(strings.offset(id3), DebugStrOffset(8));
147
148 let mut debug_str = DebugStr::from(EndianVec::new(LittleEndian));
149 strings.write(&mut debug_str).unwrap();
150 assert_eq!(debug_str.slice(), b"one\0two\0\0");
151
152 let read_debug_str = read::DebugStr::new(debug_str.slice(), LittleEndian);
153 let str1 = read_debug_str.get_str(strings.offset(id1)).unwrap();
154 let str2 = read_debug_str.get_str(strings.offset(id2)).unwrap();
155 let str3 = read_debug_str.get_str(strings.offset(id3)).unwrap();
156 assert_eq!(str1.slice(), &b"one"[..]);
157 assert_eq!(str2.slice(), &b"two"[..]);
158 assert_eq!(str3.slice(), b"");
159 }
160}