Skip to content

Commit 69fb82b

Browse files
Merge pull request #62 from Mark-Simulacrum/stream-api
Support searching for and demangling symbols
2 parents 2811a1a + 9cb4703 commit 69fb82b

File tree

5 files changed

+110
-4
lines changed

5 files changed

+110
-4
lines changed

.github/workflows/main.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ jobs:
1414
run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }}
1515
- run: cargo build --all
1616
- run: cargo test --all
17+
- run: cargo build --features std
1718

1819
fuzz_targets:
1920
name: Fuzz Targets
@@ -23,7 +24,7 @@ jobs:
2324
# Note that building with fuzzers requires nightly since it uses unstable
2425
# flags to rustc.
2526
- run: rustup update nightly && rustup default nightly
26-
- run: cargo install cargo-fuzz --vers "^0.10"
27+
- run: cargo install cargo-fuzz --vers "^0.11"
2728
- run: cargo fuzz build --dev
2829

2930
rustfmt:

Cargo.toml

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rustc-demangle"
3-
version = "0.1.21"
3+
version = "0.1.22"
44
authors = ["Alex Crichton <[email protected]>"]
55
license = "MIT/Apache-2.0"
66
readme = "README.md"
@@ -20,6 +20,11 @@ compiler_builtins = { version = '0.1.2', optional = true }
2020

2121
[features]
2222
rustc-dep-of-std = ['core', 'compiler_builtins']
23+
std = []
2324

2425
[profile.release]
2526
lto = true
27+
28+
[package.metadata.docs.rs]
29+
features = ["std"]
30+
rustdoc-args = ["--cfg", "docsrs"]

fuzz/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ cargo-fuzz = true
1010

1111
[dependencies]
1212
libfuzzer-sys = "0.4"
13-
rustc-demangle = { path = '..' }
13+
rustc-demangle = { path = '..', features = ["std"] }
1414

1515
[[bin]]
1616
name = "demangle"

fuzz/fuzz_targets/demangle.rs

+13
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,17 @@ fuzz_target!(|data: &str| {
1111
if let Ok(sym) = rustc_demangle::try_demangle(data) {
1212
drop(write!(s, "{}", sym));
1313
}
14+
15+
let mut output = Vec::new();
16+
drop(rustc_demangle::demangle_stream(
17+
&mut s.as_bytes(),
18+
&mut output,
19+
true,
20+
));
21+
output.clear();
22+
drop(rustc_demangle::demangle_stream(
23+
&mut s.as_bytes(),
24+
&mut output,
25+
false,
26+
));
1427
});

src/lib.rs

+88-1
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@
2525
2626
#![no_std]
2727
#![deny(missing_docs)]
28+
#![cfg_attr(docsrs, feature(doc_cfg))]
2829

29-
#[cfg(test)]
30+
#[cfg(any(test, feature = "std"))]
3031
#[macro_use]
3132
extern crate std;
3233

@@ -144,6 +145,74 @@ pub fn demangle(mut s: &str) -> Demangle {
144145
}
145146
}
146147

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+
147216
/// Error returned from the `try_demangle` function below when demangling fails.
148217
#[derive(Debug, Clone)]
149218
pub struct TryDemangleError {
@@ -490,4 +559,22 @@ mod tests {
490559
"{size limit reached}"
491560
);
492561
}
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+
}
493580
}

0 commit comments

Comments
 (0)