fmt: reduce the stack space required by float formatting

For the two major entry points for float formatting, we split the exact
case and the shortest cases into separate functions.  We mark the
separate functions as #[inline(never) so the exact cases won't bloat
stack space in their callers unnecessarily.  The shortest cases are
marked so for similar reasons.

Fixes #41234.
This commit is contained in:
Nathan Froyd 2017-04-13 15:49:37 -04:00
parent 5a0078520e
commit a21f61685d
1 changed files with 66 additions and 16 deletions

View File

@ -11,6 +11,35 @@
use fmt::{Formatter, Result, LowerExp, UpperExp, Display, Debug};
use num::flt2dec;
// Don't inline this so callers don't use the stack space this function
// requires unless they have to.
#[inline(never)]
fn float_to_decimal_common_exact<T>(fmt: &mut Formatter, num: &T,
sign: flt2dec::Sign, precision: usize) -> Result
where T: flt2dec::DecodableFloat
{
let mut buf = [0; 1024]; // enough for f32 and f64
let mut parts = [flt2dec::Part::Zero(0); 16];
let formatted = flt2dec::to_exact_fixed_str(flt2dec::strategy::grisu::format_exact,
*num, sign, precision,
false, &mut buf, &mut parts);
fmt.pad_formatted_parts(&formatted)
}
// Don't inline this so callers that call both this and the above won't wind
// up using the combined stack space of both functions in some cases.
#[inline(never)]
fn float_to_decimal_common_shortest<T>(fmt: &mut Formatter,
num: &T, sign: flt2dec::Sign) -> Result
where T: flt2dec::DecodableFloat
{
let mut buf = [0; flt2dec::MAX_SIG_DIGITS]; // enough for f32 and f64
let mut parts = [flt2dec::Part::Zero(0); 16];
let formatted = flt2dec::to_shortest_str(flt2dec::strategy::grisu::format_shortest,
*num, sign, 0, false, &mut buf, &mut parts);
fmt.pad_formatted_parts(&formatted)
}
// Common code of floating point Debug and Display.
fn float_to_decimal_common<T>(fmt: &mut Formatter, num: &T, negative_zero: bool) -> Result
where T: flt2dec::DecodableFloat
@ -23,15 +52,41 @@ fn float_to_decimal_common<T>(fmt: &mut Formatter, num: &T, negative_zero: bool)
(true, true) => flt2dec::Sign::MinusPlusRaw,
};
if let Some(precision) = fmt.precision {
float_to_decimal_common_exact(fmt, num, sign, precision)
} else {
float_to_decimal_common_shortest(fmt, num, sign)
}
}
// Don't inline this so callers don't use the stack space this function
// requires unless they have to.
#[inline(never)]
fn float_to_exponential_common_exact<T>(fmt: &mut Formatter, num: &T,
sign: flt2dec::Sign, precision: usize,
upper: bool) -> Result
where T: flt2dec::DecodableFloat
{
let mut buf = [0; 1024]; // enough for f32 and f64
let mut parts = [flt2dec::Part::Zero(0); 16];
let formatted = if let Some(precision) = fmt.precision {
flt2dec::to_exact_fixed_str(flt2dec::strategy::grisu::format_exact, *num, sign,
precision, false, &mut buf, &mut parts)
} else {
flt2dec::to_shortest_str(flt2dec::strategy::grisu::format_shortest, *num, sign,
0, false, &mut buf, &mut parts)
};
let formatted = flt2dec::to_exact_exp_str(flt2dec::strategy::grisu::format_exact,
*num, sign, precision,
upper, &mut buf, &mut parts);
fmt.pad_formatted_parts(&formatted)
}
// Don't inline this so callers that call both this and the above won't wind
// up using the combined stack space of both functions in some cases.
#[inline(never)]
fn float_to_exponential_common_shortest<T>(fmt: &mut Formatter,
num: &T, sign: flt2dec::Sign,
upper: bool) -> Result
where T: flt2dec::DecodableFloat
{
let mut buf = [0; flt2dec::MAX_SIG_DIGITS]; // enough for f32 and f64
let mut parts = [flt2dec::Part::Zero(0); 16];
let formatted = flt2dec::to_shortest_exp_str(flt2dec::strategy::grisu::format_shortest, *num,
sign, (0, 0), upper, &mut buf, &mut parts);
fmt.pad_formatted_parts(&formatted)
}
@ -45,17 +100,12 @@ fn float_to_exponential_common<T>(fmt: &mut Formatter, num: &T, upper: bool) ->
true => flt2dec::Sign::MinusPlus,
};
let mut buf = [0; 1024]; // enough for f32 and f64
let mut parts = [flt2dec::Part::Zero(0); 16];
let formatted = if let Some(precision) = fmt.precision {
if let Some(precision) = fmt.precision {
// 1 integral digit + `precision` fractional digits = `precision + 1` total digits
flt2dec::to_exact_exp_str(flt2dec::strategy::grisu::format_exact, *num, sign,
precision + 1, upper, &mut buf, &mut parts)
float_to_exponential_common_exact(fmt, num, sign, precision + 1, upper)
} else {
flt2dec::to_shortest_exp_str(flt2dec::strategy::grisu::format_shortest, *num, sign,
(0, 0), upper, &mut buf, &mut parts)
};
fmt.pad_formatted_parts(&formatted)
float_to_exponential_common_shortest(fmt, num, sign, upper)
}
}
macro_rules! floating {