Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve speed of fmt::Debug for str and char #28662

Merged
merged 4 commits into from
Oct 3, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 27 additions & 4 deletions src/libcore/char.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,9 +185,7 @@ impl CharExt for char {
'\t' => EscapeDefaultState::Backslash('t'),
'\r' => EscapeDefaultState::Backslash('r'),
'\n' => EscapeDefaultState::Backslash('n'),
'\\' => EscapeDefaultState::Backslash('\\'),
'\'' => EscapeDefaultState::Backslash('\''),
'"' => EscapeDefaultState::Backslash('"'),
'\\' | '\'' | '"' => EscapeDefaultState::Backslash(self),
'\x20' ... '\x7e' => EscapeDefaultState::Char(self),
_ => EscapeDefaultState::Unicode(self.escape_unicode())
};
Expand Down Expand Up @@ -344,6 +342,22 @@ impl Iterator for EscapeUnicode {
EscapeUnicodeState::Done => None,
}
}

fn size_hint(&self) -> (usize, Option<usize>) {
let mut n = 0;
while (self.c as usize) >> (4 * (n + 1)) != 0 {
n += 1;
}
let n = match self.state {
EscapeUnicodeState::Backslash => n + 5,
EscapeUnicodeState::Type => n + 4,
EscapeUnicodeState::LeftBrace => n + 3,
EscapeUnicodeState::Value(offset) => offset + 2,
EscapeUnicodeState::RightBrace => 1,
EscapeUnicodeState::Done => 0,
};
(n, Some(n))
}
}

/// An iterator over the characters that represent a `char`, escaped
Expand Down Expand Up @@ -377,7 +391,16 @@ impl Iterator for EscapeDefault {
Some(c)
}
EscapeDefaultState::Done => None,
EscapeDefaultState::Unicode(ref mut iter) => iter.next()
EscapeDefaultState::Unicode(ref mut iter) => iter.next(),
}
}

fn size_hint(&self) -> (usize, Option<usize>) {
match self.state {
EscapeDefaultState::Char(_) => (1, Some(1)),
EscapeDefaultState::Backslash(_) => (2, Some(2)),
EscapeDefaultState::Unicode(ref iter) => iter.size_hint(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now if you want to retain the same speed as the original implementation you had this can possibly just be (0, None) (e.g. the default return value) and that way you don't have to bother with the calculations about the width of a character perhaps?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually tested this with (0, Some(10)) as return value here, before adding size_hint to EscapeUnicode and using it here. Didn't noticed any significant difference, so I guess slowdown comes from iterator construction. I'll double check that though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And it is iterator construction.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ok, thanks for checking!

EscapeDefaultState::Done => (0, Some(0)),
}
}
}
23 changes: 16 additions & 7 deletions src/libcore/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1310,11 +1310,21 @@ impl Display for bool {
#[stable(feature = "rust1", since = "1.0.0")]
impl Debug for str {
fn fmt(&self, f: &mut Formatter) -> Result {
try!(write!(f, "\""));
for c in self.chars().flat_map(|c| c.escape_default()) {
try!(f.write_char(c))
try!(f.write_char('"'));
let mut from = 0;
for (i, c) in self.char_indices() {
let esc = c.escape_default();
// If char needs escaping, flush backlog so far and write, else skip
if esc.size_hint() != (1, Some(1)) {
try!(f.write_str(&self[from..i]));
for c in esc {
try!(f.write_char(c));
}
from = i + c.len_utf8();
}
}
write!(f, "\"")
try!(f.write_str(&self[from..]));
f.write_char('"')
}
}

Expand All @@ -1328,12 +1338,11 @@ impl Display for str {
#[stable(feature = "rust1", since = "1.0.0")]
impl Debug for char {
fn fmt(&self, f: &mut Formatter) -> Result {
use char::CharExt;
try!(write!(f, "'"));
try!(f.write_char('\''));
for c in self.escape_default() {
try!(f.write_char(c))
}
write!(f, "'")
f.write_char('\'')
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/test/run-pass/ifmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ pub fn main() {
t!(format!("{:?}", 10_usize), "10");
t!(format!("{:?}", "true"), "\"true\"");
t!(format!("{:?}", "foo\nbar"), "\"foo\\nbar\"");
t!(format!("{:?}", "foo\n\"bar\"\r\n\'baz\'\t\\qux\\"),
r#""foo\n\"bar\"\r\n\'baz\'\t\\qux\\""#);
t!(format!("{:?}", "foo\0bar\x01baz\u{3b1}q\u{75}x"),
r#""foo\u{0}bar\u{1}baz\u{3b1}qux""#);
t!(format!("{:o}", 10_usize), "12");
t!(format!("{:x}", 10_usize), "a");
t!(format!("{:X}", 10_usize), "A");
Expand Down