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 | } |
173 | |