diff options
Diffstat (limited to 'src/lib.rs')
-rw-r--r-- | src/lib.rs | 89 |
1 files changed, 88 insertions, 1 deletions
@@ -25,8 +25,9 @@ #![no_std] #![deny(missing_docs)] +#![cfg_attr(docsrs, feature(doc_cfg))] -#[cfg(test)] +#[cfg(any(test, feature = "std"))] #[macro_use] extern crate std; @@ -144,6 +145,74 @@ pub fn demangle(mut s: &str) -> Demangle { } } +#[cfg(feature = "std")] +fn demangle_line(line: &str, include_hash: bool) -> std::borrow::Cow<str> { + let mut line = std::borrow::Cow::Borrowed(line); + let mut head = 0; + loop { + // Move to the next potential match + head = match (line[head..].find("_ZN"), line[head..].find("_R")) { + (Some(idx), None) | (None, Some(idx)) => head + idx, + (Some(idx1), Some(idx2)) => head + idx1.min(idx2), + (None, None) => { + // No more matches, we can return our line. + return line; + } + }; + // Find the non-matching character. + // + // If we do not find a character, then until the end of the line is the + // thing to demangle. + let match_end = line[head..] + .find(|ch: char| !(ch == '$' || ch == '.' || ch == '_' || ch.is_ascii_alphanumeric())) + .map(|idx| head + idx) + .unwrap_or(line.len()); + + let mangled = &line[head..match_end]; + if let Ok(demangled) = try_demangle(mangled) { + let demangled = if include_hash { + format!("{}", demangled) + } else { + format!("{:#}", demangled) + }; + line.to_mut().replace_range(head..match_end, &demangled); + // Start again after the replacement. + head = head + demangled.len(); + } else { + // Skip over the full symbol. We don't try to find a partial Rust symbol in the wider + // matched text today. + head = head + mangled.len(); + } + } +} + +/// Process a stream of data from `input` into the provided `output`, demangling any symbols found +/// within. +/// +/// This currently is implemented by buffering each line of input in memory, but that may be +/// changed in the future. Symbols never cross line boundaries so this is just an implementation +/// detail. +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +pub fn demangle_stream<R: std::io::BufRead, W: std::io::Write>( + input: &mut R, + output: &mut W, + include_hash: bool, +) -> std::io::Result<()> { + let mut buf = std::string::String::new(); + // We read in lines to reduce the memory usage at any time. + // + // demangle_line is also more efficient with relatively small buffers as it will copy around + // trailing data during demangling. In the future we might directly stream to the output but at + // least right now that seems to be less efficient. + while input.read_line(&mut buf)? > 0 { + let demangled_line = demangle_line(&buf, include_hash); + output.write_all(demangled_line.as_bytes())?; + buf.clear(); + } + Ok(()) +} + /// Error returned from the `try_demangle` function below when demangling fails. #[derive(Debug, Clone)] pub struct TryDemangleError { @@ -490,4 +559,22 @@ mod tests { "{size limit reached}" ); } + + #[test] + #[cfg(feature = "std")] + fn find_multiple() { + assert_eq!( + super::demangle_line("_ZN3fooE.llvm moocow _ZN3fooE.llvm", false), + "foo.llvm moocow foo.llvm" + ); + } + + #[test] + #[cfg(feature = "std")] + fn interleaved_new_legacy() { + assert_eq!( + super::demangle_line("_ZN3fooE.llvm moocow _RNvMNtNtNtNtCs8a2262Dv4r_3mio3sys4unix8selector5epollNtB2_8Selector6select _ZN3fooE.llvm", false), + "foo.llvm moocow <mio::sys::unix::selector::epoll::Selector>::select foo.llvm" + ); + } } |