1use alloc::vec::Vec;
2use indexmap::IndexSet;
3use std::ops::{Deref, DerefMut};
4
5use crate::common::{DebugLineStrOffset, DebugStrOffset, SectionId};
6use crate::write::{BaseId, Result, Section, Writer};
7
8macro_rules! define_string_table {
26 ($name:ident, $id:ident, $section:ident, $offset:ident, $offsets:ident, $docs:expr) => {
27 #[doc=$docs]
28 #[derive(Debug, Default)]
29 pub struct $name {
30 base_id: BaseId,
31 strings: IndexSet<Vec<u8>>,
32 }
33
34 impl $name {
35 pub fn add<T>(&mut self, bytes: T) -> $id
43 where
44 T: Into<Vec<u8>>,
45 {
46 let bytes = bytes.into();
47 assert!(!bytes.contains(&0));
48 let (index, _) = self.strings.insert_full(bytes);
49 $id::new(self.base_id, index)
50 }
51
52 #[inline]
54 pub fn count(&self) -> usize {
55 self.strings.len()
56 }
57
58 pub fn get(&self, id: $id) -> &[u8] {
64 debug_assert_eq!(self.base_id, id.base_id);
65 self.strings.get_index(id.index).map(Vec::as_slice).unwrap()
66 }
67
68 pub fn write<W: Writer>(&self, w: &mut $section<W>) -> Result<$offsets> {
72 let mut offsets = Vec::new();
73 let mut empty = None;
74 for bytes in self.strings.iter() {
75 offsets.push(w.offset());
76 w.write(bytes)?;
77 if empty.is_none() {
78 empty = Some(w.offset());
79 }
80 w.write_u8(0)?;
81 }
82 if let Some(empty) = empty {
84 offsets.push(empty);
85 }
86
87 Ok($offsets {
88 base_id: self.base_id,
89 offsets,
90 })
91 }
92 }
93
94 impl $offsets {
95 pub(crate) fn get_empty(&self) -> Option<$id> {
96 if self.offsets.is_empty() {
97 None
98 } else {
99 Some($id::new(self.base_id, self.offsets.len() - 1))
101 }
102 }
103 }
104 };
105}
106
107define_id!(StringId, "An identifier for a string in a `StringTable`.");
108
109define_string_table!(
110 StringTable,
111 StringId,
112 DebugStr,
113 DebugStrOffset,
114 DebugStrOffsets,
115 "A table of strings that will be stored in a `.debug_str` section."
116);
117
118define_section!(DebugStr, DebugStrOffset, "A writable `.debug_str` section.");
119
120define_offsets!(
121 DebugStrOffsets: StringId => DebugStrOffset,
122 "The section offsets of all strings within a `.debug_str` section."
123);
124
125define_id!(
126 LineStringId,
127 "An identifier for a string in a `LineStringTable`."
128);
129
130define_string_table!(
131 LineStringTable,
132 LineStringId,
133 DebugLineStr,
134 DebugLineStrOffset,
135 DebugLineStrOffsets,
136 "A table of strings that will be stored in a `.debug_line_str` section."
137);
138
139define_section!(
140 DebugLineStr,
141 DebugLineStrOffset,
142 "A writable `.debug_line_str` section."
143);
144
145define_offsets!(
146 DebugLineStrOffsets: LineStringId => DebugLineStrOffset,
147 "The section offsets of all strings within a `.debug_line_str` section."
148);
149
150#[cfg(test)]
151#[cfg(feature = "read")]
152mod tests {
153 use super::*;
154 use crate::read;
155 use crate::write::EndianVec;
156 use crate::LittleEndian;
157
158 #[test]
159 fn test_string_table() {
160 let mut strings = StringTable::default();
161 assert_eq!(strings.count(), 0);
162 let id1 = strings.add(&b"one"[..]);
163 let id2 = strings.add(&b"two"[..]);
164 assert_eq!(strings.add(&b"one"[..]), id1);
165 assert_eq!(strings.add(&b"two"[..]), id2);
166 assert_eq!(strings.get(id1), &b"one"[..]);
167 assert_eq!(strings.get(id2), &b"two"[..]);
168 assert_eq!(strings.count(), 2);
169
170 let mut debug_str = DebugStr::from(EndianVec::new(LittleEndian));
171 let offsets = strings.write(&mut debug_str).unwrap();
172 assert_eq!(debug_str.slice(), b"one\0two\0");
173 assert_eq!(offsets.get(id1), DebugStrOffset(0));
174 assert_eq!(offsets.get(id2), DebugStrOffset(4));
175 assert_eq!(offsets.get(offsets.get_empty().unwrap()), DebugStrOffset(3));
176 assert_eq!(offsets.count(), 3);
177 }
178
179 #[test]
180 fn test_string_table_read() {
181 let mut strings = StringTable::default();
182 let id1 = strings.add(&b"one"[..]);
183 let id2 = strings.add(&b"two"[..]);
184
185 let mut debug_str = DebugStr::from(EndianVec::new(LittleEndian));
186 let offsets = strings.write(&mut debug_str).unwrap();
187
188 let read_debug_str = read::DebugStr::new(debug_str.slice(), LittleEndian);
189 let str1 = read_debug_str.get_str(offsets.get(id1)).unwrap();
190 let str2 = read_debug_str.get_str(offsets.get(id2)).unwrap();
191 let str3 = read_debug_str
192 .get_str(offsets.get(offsets.get_empty().unwrap()))
193 .unwrap();
194 assert_eq!(str1.slice(), &b"one"[..]);
195 assert_eq!(str2.slice(), &b"two"[..]);
196 assert_eq!(str3.slice(), b"");
197 }
198}