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
8// Requirements:
9// - values are `[u8]`, null bytes are not allowed
10// - insertion returns a fixed id
11// - inserting a duplicate returns the id of the existing value
12// - able to convert an id to a section offset
13// Optional?
14// - able to get an existing value given an id
15//
16// Limitations of current implementation (using IndexSet):
17// - inserting requires either an allocation for duplicates,
18// or a double lookup for non-duplicates
19// - doesn't preserve offsets when updating an existing `.debug_str` section
20//
21// Possible changes:
22// - calculate offsets as we add values, and use that as the id.
23// This would avoid the need for DebugStrOffsets but would make it
24// hard to implement `get`.
25macro_rules! define_string_table {
26 ($name:ident, $id:ident, $section: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 /// Add a string to the string table and return its id.
36 ///
37 /// If the string already exists, then return the id of the existing string.
38 ///
39 /// # Panics
40 ///
41 /// Panics if `bytes` contains a null byte.
42 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 /// Return the number of strings in the table.
53 #[inline]
54 pub fn count(&self) -> usize {
55 self.strings.len()
56 }
57
58 /// Get a reference to a string in the table.
59 ///
60 /// # Panics
61 ///
62 /// Panics if `id` is invalid.
63 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 /// Write the string table to the `.debug_str` section.
69 ///
70 /// Returns the offsets at which the strings are written.
71 pub fn write<W: Writer>(&self, w: &mut $section<W>) -> Result<$offsets> {
72 let mut offsets = Vec::new();
73 for bytes in self.strings.iter() {
74 offsets.push(w.offset());
75 w.write(bytes)?;
76 w.write_u8(0)?;
77 }
78
79 Ok($offsets {
80 base_id: self.base_id,
81 offsets,
82 })
83 }
84 }
85 };
86}
87
88define_id!(StringId, "An identifier for a string in a `StringTable`.");
89
90define_string_table!(
91 StringTable,
92 StringId,
93 DebugStr,
94 DebugStrOffsets,
95 "A table of strings that will be stored in a `.debug_str` section."
96);
97
98define_section!(DebugStr, DebugStrOffset, "A writable `.debug_str` section.");
99
100define_offsets!(
101 DebugStrOffsets: StringId => DebugStrOffset,
102 "The section offsets of all strings within a `.debug_str` section."
103);
104
105define_id!(
106 LineStringId,
107 "An identifier for a string in a `LineStringTable`."
108);
109
110define_string_table!(
111 LineStringTable,
112 LineStringId,
113 DebugLineStr,
114 DebugLineStrOffsets,
115 "A table of strings that will be stored in a `.debug_line_str` section."
116);
117
118define_section!(
119 DebugLineStr,
120 DebugLineStrOffset,
121 "A writable `.debug_line_str` section."
122);
123
124define_offsets!(
125 DebugLineStrOffsets: LineStringId => DebugLineStrOffset,
126 "The section offsets of all strings within a `.debug_line_str` section."
127);
128
129#[cfg(test)]
130#[cfg(feature = "read")]
131mod tests {
132 use super::*;
133 use crate::read;
134 use crate::write::EndianVec;
135 use crate::LittleEndian;
136
137 #[test]
138 fn test_string_table() {
139 let mut strings = StringTable::default();
140 assert_eq!(strings.count(), 0);
141 let id1 = strings.add(&b"one"[..]);
142 let id2 = strings.add(&b"two"[..]);
143 assert_eq!(strings.add(&b"one"[..]), id1);
144 assert_eq!(strings.add(&b"two"[..]), id2);
145 assert_eq!(strings.get(id1), &b"one"[..]);
146 assert_eq!(strings.get(id2), &b"two"[..]);
147 assert_eq!(strings.count(), 2);
148
149 let mut debug_str = DebugStr::from(EndianVec::new(LittleEndian));
150 let offsets = strings.write(&mut debug_str).unwrap();
151 assert_eq!(debug_str.slice(), b"one\0two\0");
152 assert_eq!(offsets.get(id1), DebugStrOffset(0));
153 assert_eq!(offsets.get(id2), DebugStrOffset(4));
154 assert_eq!(offsets.count(), 2);
155 }
156
157 #[test]
158 fn test_string_table_read() {
159 let mut strings = StringTable::default();
160 let id1 = strings.add(&b"one"[..]);
161 let id2 = strings.add(&b"two"[..]);
162
163 let mut debug_str = DebugStr::from(EndianVec::new(LittleEndian));
164 let offsets = strings.write(&mut debug_str).unwrap();
165
166 let read_debug_str = read::DebugStr::new(debug_str.slice(), LittleEndian);
167 let str1 = read_debug_str.get_str(offsets.get(id1)).unwrap();
168 let str2 = read_debug_str.get_str(offsets.get(id2)).unwrap();
169 assert_eq!(str1.slice(), &b"one"[..]);
170 assert_eq!(str2.slice(), &b"two"[..]);
171 }
172}
173