Skip to content

Commit

Permalink
Rollup merge of rust-lang#58272 - fitzgen:num-format-code-size, r=Mar…
Browse files Browse the repository at this point in the history
…k-Simulacrum

Cut down on number formating code size

r? @alexcrichton
  • Loading branch information
Centril committed Feb 13, 2019
2 parents 193c377 + f00f0e6 commit c0d507d
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 74 deletions.
101 changes: 62 additions & 39 deletions src/libcore/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1036,6 +1036,27 @@ pub fn write(output: &mut dyn Write, args: Arguments) -> Result {
Ok(())
}

/// Padding after the end of something. Returned by `Formatter::padding`.
#[must_use = "don't forget to write the post padding"]
struct PostPadding {
fill: char,
padding: usize,
}

impl PostPadding {
fn new(fill: char, padding: usize) -> PostPadding {
PostPadding { fill, padding }
}

/// Write this post padding.
fn write(self, buf: &mut dyn Write) -> Result {
for _ in 0..self.padding {
buf.write_char(self.fill)?;
}
Ok(())
}
}

impl<'a> Formatter<'a> {
fn wrap_buf<'b, 'c, F>(&'b mut self, wrap: F) -> Formatter<'c>
where 'b: 'c, F: FnOnce(&'b mut (dyn Write+'b)) -> &'c mut (dyn Write+'c)
Expand Down Expand Up @@ -1153,47 +1174,56 @@ impl<'a> Formatter<'a> {
sign = Some('+'); width += 1;
}

let prefixed = self.alternate();
if prefixed {
let prefix = if self.alternate() {
width += prefix.chars().count();
}
Some(prefix)
} else {
None
};

// Writes the sign if it exists, and then the prefix if it was requested
let write_prefix = |f: &mut Formatter| {
#[inline(never)]
fn write_prefix(f: &mut Formatter, sign: Option<char>, prefix: Option<&str>) -> Result {
if let Some(c) = sign {
f.buf.write_char(c)?;
}
if prefixed { f.buf.write_str(prefix) }
else { Ok(()) }
};
if let Some(prefix) = prefix {
f.buf.write_str(prefix)
} else {
Ok(())
}
}

// The `width` field is more of a `min-width` parameter at this point.
match self.width {
// If there's no minimum length requirements then we can just
// write the bytes.
None => {
write_prefix(self)?; self.buf.write_str(buf)
write_prefix(self, sign, prefix)?;
self.buf.write_str(buf)
}
// Check if we're over the minimum width, if so then we can also
// just write the bytes.
Some(min) if width >= min => {
write_prefix(self)?; self.buf.write_str(buf)
write_prefix(self, sign, prefix)?;
self.buf.write_str(buf)
}
// The sign and prefix goes before the padding if the fill character
// is zero
Some(min) if self.sign_aware_zero_pad() => {
self.fill = '0';
self.align = rt::v1::Alignment::Right;
write_prefix(self)?;
self.with_padding(min - width, rt::v1::Alignment::Right, |f| {
f.buf.write_str(buf)
})
write_prefix(self, sign, prefix)?;
let post_padding = self.padding(min - width, rt::v1::Alignment::Right)?;
self.buf.write_str(buf)?;
post_padding.write(self.buf)
}
// Otherwise, the sign and prefix goes after the padding
Some(min) => {
self.with_padding(min - width, rt::v1::Alignment::Right, |f| {
write_prefix(f)?; f.buf.write_str(buf)
})
let post_padding = self.padding(min - width, rt::v1::Alignment::Right)?;
write_prefix(self, sign, prefix)?;
self.buf.write_str(buf)?;
post_padding.write(self.buf)
}
}
}
Expand Down Expand Up @@ -1264,19 +1294,21 @@ impl<'a> Formatter<'a> {
// up the minimum width with the specified string + some alignment.
Some(width) => {
let align = rt::v1::Alignment::Left;
self.with_padding(width - s.chars().count(), align, |me| {
me.buf.write_str(s)
})
let post_padding = self.padding(width - s.chars().count(), align)?;
self.buf.write_str(s)?;
post_padding.write(self.buf)
}
}
}

/// Runs a callback, emitting the correct padding either before or
/// afterwards depending on whether right or left alignment is requested.
fn with_padding<F>(&mut self, padding: usize, default: rt::v1::Alignment,
f: F) -> Result
where F: FnOnce(&mut Formatter) -> Result,
{
/// Write the pre-padding and return the unwritten post-padding. Callers are
/// responsible for ensuring post-padding is written after the thing that is
/// being padded.
fn padding(
&mut self,
padding: usize,
default: rt::v1::Alignment
) -> result::Result<PostPadding, Error> {
let align = match self.align {
rt::v1::Alignment::Unknown => default,
_ => self.align
Expand All @@ -1289,20 +1321,11 @@ impl<'a> Formatter<'a> {
rt::v1::Alignment::Center => (padding / 2, (padding + 1) / 2),
};

let mut fill = [0; 4];
let fill = self.fill.encode_utf8(&mut fill);

for _ in 0..pre_pad {
self.buf.write_str(fill)?;
}

f(self)?;

for _ in 0..post_pad {
self.buf.write_str(fill)?;
self.buf.write_char(self.fill)?;
}

Ok(())
Ok(PostPadding::new(self.fill, post_pad))
}

/// Takes the formatted parts and applies the padding.
Expand Down Expand Up @@ -1334,9 +1357,9 @@ impl<'a> Formatter<'a> {
let ret = if width <= len { // no padding
self.write_formatted_parts(&formatted)
} else {
self.with_padding(width - len, align, |f| {
f.write_formatted_parts(&formatted)
})
let post_padding = self.padding(width - len, align)?;
self.write_formatted_parts(&formatted)?;
post_padding.write(self.buf)
};
self.fill = old_fill;
self.align = old_align;
Expand Down
87 changes: 52 additions & 35 deletions src/libcore/fmt/num.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,45 +178,36 @@ integer! { i32, u32 }
integer! { i64, u64 }
integer! { i128, u128 }

const DEC_DIGITS_LUT: &'static[u8] =

static DEC_DIGITS_LUT: &[u8; 200] =
b"0001020304050607080910111213141516171819\
2021222324252627282930313233343536373839\
4041424344454647484950515253545556575859\
6061626364656667686970717273747576777879\
8081828384858687888990919293949596979899";

macro_rules! impl_Display {
($($t:ident),*: $conv_fn:ident) => ($(
#[stable(feature = "rust1", since = "1.0.0")]
impl fmt::Display for $t {
#[allow(unused_comparisons)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let is_nonnegative = *self >= 0;
let mut n = if is_nonnegative {
self.$conv_fn()
} else {
// convert the negative num to positive by summing 1 to it's 2 complement
(!self.$conv_fn()).wrapping_add(1)
};
($($t:ident),* as $u:ident via $conv_fn:ident named $name:ident) => {
fn $name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter) -> fmt::Result {
let mut buf = uninitialized_array![u8; 39];
let mut curr = buf.len() as isize;
let buf_ptr = MaybeUninit::first_ptr_mut(&mut buf);
let lut_ptr = DEC_DIGITS_LUT.as_ptr();

unsafe {
// need at least 16 bits for the 4-characters-at-a-time to work.
if ::mem::size_of::<$t>() >= 2 {
// eagerly decode 4 characters at a time
while n >= 10000 {
let rem = (n % 10000) as isize;
n /= 10000;

let d1 = (rem / 100) << 1;
let d2 = (rem % 100) << 1;
curr -= 4;
ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
ptr::copy_nonoverlapping(lut_ptr.offset(d2), buf_ptr.offset(curr + 2), 2);
}
assert!(::mem::size_of::<$u>() >= 2);

// eagerly decode 4 characters at a time
while n >= 10000 {
let rem = (n % 10000) as isize;
n /= 10000;

let d1 = (rem / 100) << 1;
let d2 = (rem % 100) << 1;
curr -= 4;
ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
ptr::copy_nonoverlapping(lut_ptr.offset(d2), buf_ptr.offset(curr + 2), 2);
}

// if we reach here numbers are <= 9999, so at most 4 chars long
Expand Down Expand Up @@ -247,15 +238,41 @@ macro_rules! impl_Display {
};
f.pad_integral(is_nonnegative, "", buf_slice)
}
})*);

$(
#[stable(feature = "rust1", since = "1.0.0")]
impl fmt::Display for $t {
#[allow(unused_comparisons)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let is_nonnegative = *self >= 0;
let n = if is_nonnegative {
self.$conv_fn()
} else {
// convert the negative num to positive by summing 1 to it's 2 complement
(!self.$conv_fn()).wrapping_add(1)
};
$name(n, is_nonnegative, f)
}
})*
};
}

// Include wasm32 in here since it doesn't reflect the native pointer size, and
// often cares strongly about getting a smaller code size.
#[cfg(any(target_pointer_width = "64", target_arch = "wasm32"))]
mod imp {
use super::*;
impl_Display!(
i8, u8, i16, u16, i32, u32, i64, u64, usize, isize
as u64 via to_u64 named fmt_u64
);
}

#[cfg(not(any(target_pointer_width = "64", target_arch = "wasm32")))]
mod imp {
use super::*;
impl_Display!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named fmt_u32);
impl_Display!(i64, u64 as u64 via to_u64 named fmt_u64);
}

impl_Display!(i8, u8, i16, u16, i32, u32: to_u32);
impl_Display!(i64, u64: to_u64);
impl_Display!(i128, u128: to_u128);
#[cfg(target_pointer_width = "16")]
impl_Display!(isize, usize: to_u16);
#[cfg(target_pointer_width = "32")]
impl_Display!(isize, usize: to_u32);
#[cfg(target_pointer_width = "64")]
impl_Display!(isize, usize: to_u64);
impl_Display!(i128, u128 as u128 via to_u128 named fmt_u128);
10 changes: 10 additions & 0 deletions src/test/run-make/wasm-stringify-ints-small/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-include ../../run-make-fulldeps/tools.mk

ifeq ($(TARGET),wasm32-unknown-unknown)
all:
$(RUSTC) foo.rs -C lto -O --target wasm32-unknown-unknown
wc -c < $(TMPDIR)/foo.wasm
[ "`wc -c < $(TMPDIR)/foo.wasm`" -lt "20500" ]
else
all:
endif
33 changes: 33 additions & 0 deletions src/test/run-make/wasm-stringify-ints-small/foo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#![crate_type = "cdylib"]

extern "C" {
fn observe(ptr: *const u8, len: usize);
}

macro_rules! s {
( $( $f:ident -> $t:ty );* $(;)* ) => {
$(
extern "C" {
fn $f() -> $t;
}
let s = $f().to_string();
observe(s.as_ptr(), s.len());
)*
};
}

#[no_mangle]
pub unsafe extern "C" fn foo() {
s! {
get_u8 -> u8;
get_i8 -> i8;
get_u16 -> u16;
get_i16 -> i16;
get_u32 -> u32;
get_i32 -> i32;
get_u64 -> u64;
get_i64 -> i64;
get_usize -> usize;
get_isize -> isize;
}
}

0 comments on commit c0d507d

Please sign in to comment.