|
25 | 25 |
|
26 | 26 | #![no_std]
|
27 | 27 | #![deny(missing_docs)]
|
| 28 | +#![cfg_attr(docsrs, feature(doc_cfg))] |
28 | 29 |
|
29 |
| -#[cfg(test)] |
| 30 | +#[cfg(any(test, feature = "std"))] |
30 | 31 | #[macro_use]
|
31 | 32 | extern crate std;
|
32 | 33 |
|
@@ -144,6 +145,74 @@ pub fn demangle(mut s: &str) -> Demangle {
|
144 | 145 | }
|
145 | 146 | }
|
146 | 147 |
|
| 148 | +#[cfg(feature = "std")] |
| 149 | +fn demangle_line(line: &str, include_hash: bool) -> std::borrow::Cow<str> { |
| 150 | + let mut line = std::borrow::Cow::Borrowed(line); |
| 151 | + let mut head = 0; |
| 152 | + loop { |
| 153 | + // Move to the next potential match |
| 154 | + head = match (line[head..].find("_ZN"), line[head..].find("_R")) { |
| 155 | + (Some(idx), None) | (None, Some(idx)) => head + idx, |
| 156 | + (Some(idx1), Some(idx2)) => head + idx1.min(idx2), |
| 157 | + (None, None) => { |
| 158 | + // No more matches, we can return our line. |
| 159 | + return line; |
| 160 | + } |
| 161 | + }; |
| 162 | + // Find the non-matching character. |
| 163 | + // |
| 164 | + // If we do not find a character, then until the end of the line is the |
| 165 | + // thing to demangle. |
| 166 | + let match_end = line[head..] |
| 167 | + .find(|ch: char| !(ch == '$' || ch == '.' || ch == '_' || ch.is_ascii_alphanumeric())) |
| 168 | + .map(|idx| head + idx) |
| 169 | + .unwrap_or(line.len()); |
| 170 | + |
| 171 | + let mangled = &line[head..match_end]; |
| 172 | + if let Ok(demangled) = try_demangle(mangled) { |
| 173 | + let demangled = if include_hash { |
| 174 | + format!("{}", demangled) |
| 175 | + } else { |
| 176 | + format!("{:#}", demangled) |
| 177 | + }; |
| 178 | + line.to_mut().replace_range(head..match_end, &demangled); |
| 179 | + // Start again after the replacement. |
| 180 | + head = head + demangled.len(); |
| 181 | + } else { |
| 182 | + // Skip over the full symbol. We don't try to find a partial Rust symbol in the wider |
| 183 | + // matched text today. |
| 184 | + head = head + mangled.len(); |
| 185 | + } |
| 186 | + } |
| 187 | +} |
| 188 | + |
| 189 | +/// Process a stream of data from `input` into the provided `output`, demangling any symbols found |
| 190 | +/// within. |
| 191 | +/// |
| 192 | +/// This currently is implemented by buffering each line of input in memory, but that may be |
| 193 | +/// changed in the future. Symbols never cross line boundaries so this is just an implementation |
| 194 | +/// detail. |
| 195 | +#[cfg(feature = "std")] |
| 196 | +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] |
| 197 | +pub fn demangle_stream<R: std::io::BufRead, W: std::io::Write>( |
| 198 | + input: &mut R, |
| 199 | + output: &mut W, |
| 200 | + include_hash: bool, |
| 201 | +) -> std::io::Result<()> { |
| 202 | + let mut buf = std::string::String::new(); |
| 203 | + // We read in lines to reduce the memory usage at any time. |
| 204 | + // |
| 205 | + // demangle_line is also more efficient with relatively small buffers as it will copy around |
| 206 | + // trailing data during demangling. In the future we might directly stream to the output but at |
| 207 | + // least right now that seems to be less efficient. |
| 208 | + while input.read_line(&mut buf)? > 0 { |
| 209 | + let demangled_line = demangle_line(&buf, include_hash); |
| 210 | + output.write_all(demangled_line.as_bytes())?; |
| 211 | + buf.clear(); |
| 212 | + } |
| 213 | + Ok(()) |
| 214 | +} |
| 215 | + |
147 | 216 | /// Error returned from the `try_demangle` function below when demangling fails.
|
148 | 217 | #[derive(Debug, Clone)]
|
149 | 218 | pub struct TryDemangleError {
|
@@ -490,4 +559,22 @@ mod tests {
|
490 | 559 | "{size limit reached}"
|
491 | 560 | );
|
492 | 561 | }
|
| 562 | + |
| 563 | + #[test] |
| 564 | + #[cfg(feature = "std")] |
| 565 | + fn find_multiple() { |
| 566 | + assert_eq!( |
| 567 | + super::demangle_line("_ZN3fooE.llvm moocow _ZN3fooE.llvm", false), |
| 568 | + "foo.llvm moocow foo.llvm" |
| 569 | + ); |
| 570 | + } |
| 571 | + |
| 572 | + #[test] |
| 573 | + #[cfg(feature = "std")] |
| 574 | + fn interleaved_new_legacy() { |
| 575 | + assert_eq!( |
| 576 | + super::demangle_line("_ZN3fooE.llvm moocow _RNvMNtNtNtNtCs8a2262Dv4r_3mio3sys4unix8selector5epollNtB2_8Selector6select _ZN3fooE.llvm", false), |
| 577 | + "foo.llvm moocow <mio::sys::unix::selector::epoll::Selector>::select foo.llvm" |
| 578 | + ); |
| 579 | + } |
493 | 580 | }
|
0 commit comments