Skip to content

Commit

Permalink
Avoid panic_bounds_check in fmt::write.
Browse files Browse the repository at this point in the history
Writing any fmt::Arguments would trigger the inclusion of usize
formatting and padding code in the resulting binary, because indexing
used in fmt::write would generate code using panic_bounds_check, which
prints the index and length.

These bounds checks are not necessary, as fmt::Arguments never contains
any out-of-bounds indexes.

This change replaces them with unsafe get_unchecked, to reduce the
amount of generated code, which is especially important for embedded
targets.
  • Loading branch information
m-ou-se committed Oct 19, 2020
1 parent ad268bd commit d80f127
Showing 1 changed file with 20 additions and 7 deletions.
27 changes: 20 additions & 7 deletions library/core/src/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1082,7 +1082,9 @@ pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result {
// a string piece.
for (arg, piece) in fmt.iter().zip(args.pieces.iter()) {
formatter.buf.write_str(*piece)?;
run(&mut formatter, arg, &args.args)?;
// SAFETY: arg and args.args come from the same Arguments,
// which guarantees the indexes are always within bounds.
unsafe { run(&mut formatter, arg, &args.args) }?;
idx += 1;
}
}
Expand All @@ -1096,25 +1098,36 @@ pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result {
Ok(())
}

fn run(fmt: &mut Formatter<'_>, arg: &rt::v1::Argument, args: &[ArgumentV1<'_>]) -> Result {
unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::v1::Argument, args: &[ArgumentV1<'_>]) -> Result {
fmt.fill = arg.format.fill;
fmt.align = arg.format.align;
fmt.flags = arg.format.flags;
fmt.width = getcount(args, &arg.format.width);
fmt.precision = getcount(args, &arg.format.precision);
// SAFETY: arg and args come from the same Arguments,
// which guarantees the indexes are always within bounds.
unsafe {
fmt.width = getcount(args, &arg.format.width);
fmt.precision = getcount(args, &arg.format.precision);
}

// Extract the correct argument
let value = args[arg.position];

// SAFETY: arg and args come from the same Arguments,
// which guarantees its index is always within bounds.
let value = unsafe { args.get_unchecked(arg.position) };

// Then actually do some printing
(value.formatter)(value.value, fmt)
}

fn getcount(args: &[ArgumentV1<'_>], cnt: &rt::v1::Count) -> Option<usize> {
unsafe fn getcount(args: &[ArgumentV1<'_>], cnt: &rt::v1::Count) -> Option<usize> {
match *cnt {
rt::v1::Count::Is(n) => Some(n),
rt::v1::Count::Implied => None,
rt::v1::Count::Param(i) => args[i].as_usize(),
rt::v1::Count::Param(i) => {
// SAFETY: cnt and args come from the same Arguments,
// which guarantees this index is always within bounds.
unsafe { args.get_unchecked(i).as_usize() }
}
}
}

Expand Down

0 comments on commit d80f127

Please sign in to comment.