]>
Commit | Line | Data |
---|---|---|
f035d41b XL |
1 | use alloc::vec::Vec; |
2 | use indexmap::IndexSet; | |
3 | use std::ops::{Deref, DerefMut}; | |
4 | ||
5 | use crate::common::{DebugLineStrOffset, DebugStrOffset, SectionId}; | |
6 | use 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`. | |
25 | macro_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 | ||
88 | define_id!(StringId, "An identifier for a string in a `StringTable`."); | |
89 | ||
90 | define_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 | ||
98 | define_section!(DebugStr, DebugStrOffset, "A writable `.debug_str` section."); | |
99 | ||
100 | define_offsets!( | |
101 | DebugStrOffsets: StringId => DebugStrOffset, | |
102 | "The section offsets of all strings within a `.debug_str` section." | |
103 | ); | |
104 | ||
105 | define_id!( | |
106 | LineStringId, | |
107 | "An identifier for a string in a `LineStringTable`." | |
108 | ); | |
109 | ||
110 | define_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 | ||
118 | define_section!( | |
119 | DebugLineStr, | |
120 | DebugLineStrOffset, | |
121 | "A writable `.debug_line_str` section." | |
122 | ); | |
123 | ||
124 | define_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")] | |
131 | mod 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 | } |