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] test_string_table()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] test_string_table_read()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