aboutsummaryrefslogtreecommitdiff
path: root/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs89
1 files changed, 88 insertions, 1 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 1ecb13f..7eb1c42 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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"
+ );
+ }
}