From 56129d39c0d6f0e15e2f4fc40b8079b8b22e34fe Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 2 Sep 2020 12:18:07 +0200 Subject: [PATCH] flt2dec: properly handle uninitialized memory --- .../benches/num/flt2dec/strategy/dragon.rs | 49 ++-- .../benches/num/flt2dec/strategy/grisu.rs | 49 ++-- library/core/src/fmt/float.rs | 116 ++++----- library/core/src/num/flt2dec/mod.rs | 226 ++++++++++-------- .../core/src/num/flt2dec/strategy/dragon.rs | 41 +++- .../core/src/num/flt2dec/strategy/grisu.rs | 97 +++++--- library/core/tests/num/flt2dec/mod.rs | 71 +++--- library/core/tests/num/flt2dec/random.rs | 31 +-- 8 files changed, 387 insertions(+), 293 deletions(-) diff --git a/library/core/benches/num/flt2dec/strategy/dragon.rs b/library/core/benches/num/flt2dec/strategy/dragon.rs index 4e1fd8bf753ca..319b9773e49fe 100644 --- a/library/core/benches/num/flt2dec/strategy/dragon.rs +++ b/library/core/benches/num/flt2dec/strategy/dragon.rs @@ -1,59 +1,76 @@ use super::super::*; use core::num::flt2dec::strategy::dragon::*; +use std::mem::MaybeUninit; use test::Bencher; #[bench] fn bench_small_shortest(b: &mut Bencher) { let decoded = decode_finite(3.141592f64); - let mut buf = [0; MAX_SIG_DIGITS]; - b.iter(|| format_shortest(&decoded, &mut buf)); + let mut buf = [MaybeUninit::new(0); MAX_SIG_DIGITS]; + b.iter(|| { + format_shortest(&decoded, &mut buf); + }); } #[bench] fn bench_big_shortest(b: &mut Bencher) { let decoded = decode_finite(f64::MAX); - let mut buf = [0; MAX_SIG_DIGITS]; - b.iter(|| format_shortest(&decoded, &mut buf)); + let mut buf = [MaybeUninit::new(0); MAX_SIG_DIGITS]; + b.iter(|| { + format_shortest(&decoded, &mut buf); + }); } #[bench] fn bench_small_exact_3(b: &mut Bencher) { let decoded = decode_finite(3.141592f64); - let mut buf = [0; 3]; - b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); + let mut buf = [MaybeUninit::new(0); 3]; + b.iter(|| { + format_exact(&decoded, &mut buf, i16::MIN); + }); } #[bench] fn bench_big_exact_3(b: &mut Bencher) { let decoded = decode_finite(f64::MAX); - let mut buf = [0; 3]; - b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); + let mut buf = [MaybeUninit::new(0); 3]; + b.iter(|| { + format_exact(&decoded, &mut buf, i16::MIN); + }); } #[bench] fn bench_small_exact_12(b: &mut Bencher) { let decoded = decode_finite(3.141592f64); - let mut buf = [0; 12]; - b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); + let mut buf = [MaybeUninit::new(0); 12]; + b.iter(|| { + format_exact(&decoded, &mut buf, i16::MIN); + }); } #[bench] fn bench_big_exact_12(b: &mut Bencher) { let decoded = decode_finite(f64::MAX); - let mut buf = [0; 12]; - b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); + let mut buf = [MaybeUninit::new(0); 12]; + b.iter(|| { + format_exact(&decoded, &mut buf, i16::MIN); + }); } #[bench] fn bench_small_exact_inf(b: &mut Bencher) { let decoded = decode_finite(3.141592f64); - let mut buf = [0; 1024]; - b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); + let mut buf = [MaybeUninit::new(0); 1024]; + b.iter(|| { + format_exact(&decoded, &mut buf, i16::MIN); + }); } #[bench] fn bench_big_exact_inf(b: &mut Bencher) { let decoded = decode_finite(f64::MAX); - let mut buf = [0; 1024]; - b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); + let mut buf = [MaybeUninit::new(0); 1024]; + b.iter(|| { + format_exact(&decoded, &mut buf, i16::MIN); + }); } diff --git a/library/core/benches/num/flt2dec/strategy/grisu.rs b/library/core/benches/num/flt2dec/strategy/grisu.rs index 77ca901a90af3..76425731e1ddd 100644 --- a/library/core/benches/num/flt2dec/strategy/grisu.rs +++ b/library/core/benches/num/flt2dec/strategy/grisu.rs @@ -1,5 +1,6 @@ use super::super::*; use core::num::flt2dec::strategy::grisu::*; +use std::mem::MaybeUninit; use test::Bencher; pub fn decode_finite(v: T) -> Decoded { @@ -12,55 +13,71 @@ pub fn decode_finite(v: T) -> Decoded { #[bench] fn bench_small_shortest(b: &mut Bencher) { let decoded = decode_finite(3.141592f64); - let mut buf = [0; MAX_SIG_DIGITS]; - b.iter(|| format_shortest(&decoded, &mut buf)); + let mut buf = [MaybeUninit::new(0); MAX_SIG_DIGITS]; + b.iter(|| { + format_shortest(&decoded, &mut buf); + }); } #[bench] fn bench_big_shortest(b: &mut Bencher) { let decoded = decode_finite(f64::MAX); - let mut buf = [0; MAX_SIG_DIGITS]; - b.iter(|| format_shortest(&decoded, &mut buf)); + let mut buf = [MaybeUninit::new(0); MAX_SIG_DIGITS]; + b.iter(|| { + format_shortest(&decoded, &mut buf); + }); } #[bench] fn bench_small_exact_3(b: &mut Bencher) { let decoded = decode_finite(3.141592f64); - let mut buf = [0; 3]; - b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); + let mut buf = [MaybeUninit::new(0); 3]; + b.iter(|| { + format_exact(&decoded, &mut buf, i16::MIN); + }); } #[bench] fn bench_big_exact_3(b: &mut Bencher) { let decoded = decode_finite(f64::MAX); - let mut buf = [0; 3]; - b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); + let mut buf = [MaybeUninit::new(0); 3]; + b.iter(|| { + format_exact(&decoded, &mut buf, i16::MIN); + }); } #[bench] fn bench_small_exact_12(b: &mut Bencher) { let decoded = decode_finite(3.141592f64); - let mut buf = [0; 12]; - b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); + let mut buf = [MaybeUninit::new(0); 12]; + b.iter(|| { + format_exact(&decoded, &mut buf, i16::MIN); + }); } #[bench] fn bench_big_exact_12(b: &mut Bencher) { let decoded = decode_finite(f64::MAX); - let mut buf = [0; 12]; - b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); + let mut buf = [MaybeUninit::new(0); 12]; + b.iter(|| { + format_exact(&decoded, &mut buf, i16::MIN); + }); } #[bench] fn bench_small_exact_inf(b: &mut Bencher) { let decoded = decode_finite(3.141592f64); - let mut buf = [0; 1024]; - b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); + let mut buf = [MaybeUninit::new(0); 1024]; + b.iter(|| { + format_exact(&decoded, &mut buf, i16::MIN); + }); } #[bench] fn bench_big_exact_inf(b: &mut Bencher) { let decoded = decode_finite(f64::MAX); - let mut buf = [0; 1024]; - b.iter(|| format_exact(&decoded, &mut buf, i16::MIN)); + let mut buf = [MaybeUninit::new(0); 1024]; + b.iter(|| { + format_exact(&decoded, &mut buf, i16::MIN); + }); } diff --git a/library/core/src/fmt/float.rs b/library/core/src/fmt/float.rs index 7f9ab5357d697..5908da477e11e 100644 --- a/library/core/src/fmt/float.rs +++ b/library/core/src/fmt/float.rs @@ -14,25 +14,17 @@ fn float_to_decimal_common_exact( where T: flt2dec::DecodableFloat, { - // SAFETY: Possible undefined behavior, see FIXME(#76092) - unsafe { - let mut buf = MaybeUninit::<[u8; 1024]>::uninit(); // enough for f32 and f64 - let mut parts = MaybeUninit::<[flt2dec::Part<'_>; 4]>::uninit(); - // FIXME(#76092): This is calling `assume_init_mut` on an uninitialized - // `MaybeUninit` (here and elsewhere in this file). Revisit this once - // we decided whether that is valid or not. - // We can do this only because we are libstd and coupled to the compiler. - // (FWIW, using `freeze` would not be enough; `flt2dec::Part` is an enum!) - let formatted = flt2dec::to_exact_fixed_str( - flt2dec::strategy::grisu::format_exact, - *num, - sign, - precision, - buf.assume_init_mut(), - parts.assume_init_mut(), - ); - fmt.pad_formatted_parts(&formatted) - } + let mut buf: [MaybeUninit; 1024] = MaybeUninit::uninit_array(); // enough for f32 and f64 + let mut parts: [MaybeUninit>; 4] = MaybeUninit::uninit_array(); + let formatted = flt2dec::to_exact_fixed_str( + flt2dec::strategy::grisu::format_exact, + *num, + sign, + precision, + &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 @@ -47,22 +39,18 @@ fn float_to_decimal_common_shortest( where T: flt2dec::DecodableFloat, { - // SAFETY: Possible undefined behavior, see FIXME(#76092) - unsafe { - // enough for f32 and f64 - let mut buf = MaybeUninit::<[u8; flt2dec::MAX_SIG_DIGITS]>::uninit(); - let mut parts = MaybeUninit::<[flt2dec::Part<'_>; 4]>::uninit(); - // FIXME(#76092) - let formatted = flt2dec::to_shortest_str( - flt2dec::strategy::grisu::format_shortest, - *num, - sign, - precision, - buf.assume_init_mut(), - parts.assume_init_mut(), - ); - fmt.pad_formatted_parts(&formatted) - } + // enough for f32 and f64 + let mut buf: [MaybeUninit; flt2dec::MAX_SIG_DIGITS] = MaybeUninit::uninit_array(); + let mut parts: [MaybeUninit>; 4] = MaybeUninit::uninit_array(); + let formatted = flt2dec::to_shortest_str( + flt2dec::strategy::grisu::format_shortest, + *num, + sign, + precision, + &mut buf, + &mut parts, + ); + fmt.pad_formatted_parts(&formatted) } // Common code of floating point Debug and Display. @@ -103,22 +91,18 @@ fn float_to_exponential_common_exact( where T: flt2dec::DecodableFloat, { - // SAFETY: Possible undefined behavior, see FIXME(#76092) - unsafe { - let mut buf = MaybeUninit::<[u8; 1024]>::uninit(); // enough for f32 and f64 - let mut parts = MaybeUninit::<[flt2dec::Part<'_>; 6]>::uninit(); - // FIXME(#76092) - let formatted = flt2dec::to_exact_exp_str( - flt2dec::strategy::grisu::format_exact, - *num, - sign, - precision, - upper, - buf.assume_init_mut(), - parts.assume_init_mut(), - ); - fmt.pad_formatted_parts(&formatted) - } + let mut buf: [MaybeUninit; 1024] = MaybeUninit::uninit_array(); // enough for f32 and f64 + let mut parts: [MaybeUninit>; 6] = MaybeUninit::uninit_array(); + 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 @@ -133,23 +117,19 @@ fn float_to_exponential_common_shortest( where T: flt2dec::DecodableFloat, { - // SAFETY: Possible undefined behavior, see FIXME(#76092) - unsafe { - // enough for f32 and f64 - let mut buf = MaybeUninit::<[u8; flt2dec::MAX_SIG_DIGITS]>::uninit(); - let mut parts = MaybeUninit::<[flt2dec::Part<'_>; 6]>::uninit(); - // FIXME(#76092) - let formatted = flt2dec::to_shortest_exp_str( - flt2dec::strategy::grisu::format_shortest, - *num, - sign, - (0, 0), - upper, - buf.assume_init_mut(), - parts.assume_init_mut(), - ); - fmt.pad_formatted_parts(&formatted) - } + // enough for f32 and f64 + let mut buf: [MaybeUninit; flt2dec::MAX_SIG_DIGITS] = MaybeUninit::uninit_array(); + let mut parts: [MaybeUninit>; 6] = MaybeUninit::uninit_array(); + 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) } // Common code of floating point LowerExp and UpperExp. diff --git a/library/core/src/num/flt2dec/mod.rs b/library/core/src/num/flt2dec/mod.rs index 9bf56e93d896f..1f28edb412827 100644 --- a/library/core/src/num/flt2dec/mod.rs +++ b/library/core/src/num/flt2dec/mod.rs @@ -124,6 +124,8 @@ functions. pub use self::decoder::{decode, DecodableFloat, Decoded, FullDecoded}; +use crate::mem::MaybeUninit; + pub mod decoder; pub mod estimator; @@ -140,23 +142,23 @@ pub mod strategy { /// The exact formula is `ceil(# bits in mantissa * log_10 2 + 1)`. pub const MAX_SIG_DIGITS: usize = 17; -/// When `d[..n]` contains decimal digits, increase the last digit and propagate carry. -/// Returns a next digit when it causes the length change. +/// When `d` contains decimal digits, increase the last digit and propagate carry. +/// Returns a next digit when it causes the length to change. #[doc(hidden)] -pub fn round_up(d: &mut [u8], n: usize) -> Option { - match d[..n].iter().rposition(|&c| c != b'9') { +pub fn round_up(d: &mut [u8]) -> Option { + match d.iter().rposition(|&c| c != b'9') { Some(i) => { // d[i+1..n] is all nines d[i] += 1; - for j in i + 1..n { + for j in i + 1..d.len() { d[j] = b'0'; } None } - None if n > 0 => { + None if d.len() > 0 => { // 999..999 rounds to 1000..000 with an increased exponent d[0] = b'1'; - for j in 1..n { + for j in 1..d.len() { d[j] = b'0'; } Some(b'0') @@ -281,7 +283,7 @@ fn digits_to_dec_str<'a>( buf: &'a [u8], exp: i16, frac_digits: usize, - parts: &'a mut [Part<'a>], + parts: &'a mut [MaybeUninit>], ) -> &'a [Part<'a>] { assert!(!buf.is_empty()); assert!(buf[0] > b'0'); @@ -303,38 +305,44 @@ fn digits_to_dec_str<'a>( if exp <= 0 { // the decimal point is before rendered digits: [0.][000...000][1234][____] let minus_exp = -(exp as i32) as usize; - parts[0] = Part::Copy(b"0."); - parts[1] = Part::Zero(minus_exp); - parts[2] = Part::Copy(buf); + parts[0] = MaybeUninit::new(Part::Copy(b"0.")); + parts[1] = MaybeUninit::new(Part::Zero(minus_exp)); + parts[2] = MaybeUninit::new(Part::Copy(buf)); if frac_digits > buf.len() && frac_digits - buf.len() > minus_exp { - parts[3] = Part::Zero((frac_digits - buf.len()) - minus_exp); - &parts[..4] + parts[3] = MaybeUninit::new(Part::Zero((frac_digits - buf.len()) - minus_exp)); + // SAFETY: we just initialized the elements `..4`. + unsafe { MaybeUninit::slice_get_ref(&parts[..4]) } } else { - &parts[..3] + // SAFETY: we just initialized the elements `..3`. + unsafe { MaybeUninit::slice_get_ref(&parts[..3]) } } } else { let exp = exp as usize; if exp < buf.len() { // the decimal point is inside rendered digits: [12][.][34][____] - parts[0] = Part::Copy(&buf[..exp]); - parts[1] = Part::Copy(b"."); - parts[2] = Part::Copy(&buf[exp..]); + parts[0] = MaybeUninit::new(Part::Copy(&buf[..exp])); + parts[1] = MaybeUninit::new(Part::Copy(b".")); + parts[2] = MaybeUninit::new(Part::Copy(&buf[exp..])); if frac_digits > buf.len() - exp { - parts[3] = Part::Zero(frac_digits - (buf.len() - exp)); - &parts[..4] + parts[3] = MaybeUninit::new(Part::Zero(frac_digits - (buf.len() - exp))); + // SAFETY: we just initialized the elements `..4`. + unsafe { MaybeUninit::slice_get_ref(&parts[..4]) } } else { - &parts[..3] + // SAFETY: we just initialized the elements `..3`. + unsafe { MaybeUninit::slice_get_ref(&parts[..3]) } } } else { // the decimal point is after rendered digits: [1234][____0000] or [1234][__][.][__]. - parts[0] = Part::Copy(buf); - parts[1] = Part::Zero(exp - buf.len()); + parts[0] = MaybeUninit::new(Part::Copy(buf)); + parts[1] = MaybeUninit::new(Part::Zero(exp - buf.len())); if frac_digits > 0 { - parts[2] = Part::Copy(b"."); - parts[3] = Part::Zero(frac_digits); - &parts[..4] + parts[2] = MaybeUninit::new(Part::Copy(b".")); + parts[3] = MaybeUninit::new(Part::Zero(frac_digits)); + // SAFETY: we just initialized the elements `..4`. + unsafe { MaybeUninit::slice_get_ref(&parts[..4]) } } else { - &parts[..2] + // SAFETY: we just initialized the elements `..2`. + unsafe { MaybeUninit::slice_get_ref(&parts[..2]) } } } } @@ -354,7 +362,7 @@ fn digits_to_exp_str<'a>( exp: i16, min_ndigits: usize, upper: bool, - parts: &'a mut [Part<'a>], + parts: &'a mut [MaybeUninit>], ) -> &'a [Part<'a>] { assert!(!buf.is_empty()); assert!(buf[0] > b'0'); @@ -362,15 +370,15 @@ fn digits_to_exp_str<'a>( let mut n = 0; - parts[n] = Part::Copy(&buf[..1]); + parts[n] = MaybeUninit::new(Part::Copy(&buf[..1])); n += 1; if buf.len() > 1 || min_ndigits > 1 { - parts[n] = Part::Copy(b"."); - parts[n + 1] = Part::Copy(&buf[1..]); + parts[n] = MaybeUninit::new(Part::Copy(b".")); + parts[n + 1] = MaybeUninit::new(Part::Copy(&buf[1..])); n += 2; if min_ndigits > buf.len() { - parts[n] = Part::Zero(min_ndigits - buf.len()); + parts[n] = MaybeUninit::new(Part::Zero(min_ndigits - buf.len())); n += 1; } } @@ -378,13 +386,14 @@ fn digits_to_exp_str<'a>( // 0.1234 x 10^exp = 1.234 x 10^(exp-1) let exp = exp as i32 - 1; // avoid underflow when exp is i16::MIN if exp < 0 { - parts[n] = Part::Copy(if upper { b"E-" } else { b"e-" }); - parts[n + 1] = Part::Num(-exp as u16); + parts[n] = MaybeUninit::new(Part::Copy(if upper { b"E-" } else { b"e-" })); + parts[n + 1] = MaybeUninit::new(Part::Num(-exp as u16)); } else { - parts[n] = Part::Copy(if upper { b"E" } else { b"e" }); - parts[n + 1] = Part::Num(exp as u16); + parts[n] = MaybeUninit::new(Part::Copy(if upper { b"E" } else { b"e" })); + parts[n + 1] = MaybeUninit::new(Part::Num(exp as u16)); } - &parts[..n + 2] + // SAFETY: we just initialized the elements `..n + 2`. + unsafe { MaybeUninit::slice_get_ref(&parts[..n + 2]) } } /// Sign formatting options. @@ -446,6 +455,7 @@ fn determine_sign(sign: Sign, decoded: &FullDecoded, negative: bool) -> &'static /// (which can be an empty string if no sign is rendered). /// /// `format_shortest` should be the underlying digit-generation function. +/// It should return the part of the buffer that it initialized. /// You probably would want `strategy::grisu::format_shortest` for this. /// /// `frac_digits` can be less than the number of actual fractional digits in `v`; @@ -461,12 +471,12 @@ pub fn to_shortest_str<'a, T, F>( v: T, sign: Sign, frac_digits: usize, - buf: &'a mut [u8], - parts: &'a mut [Part<'a>], + buf: &'a mut [MaybeUninit], + parts: &'a mut [MaybeUninit>], ) -> Formatted<'a> where T: DecodableFloat, - F: FnMut(&Decoded, &mut [u8]) -> (usize, i16), + F: FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), { assert!(parts.len() >= 4); assert!(buf.len() >= MAX_SIG_DIGITS); @@ -475,27 +485,31 @@ where let sign = determine_sign(sign, &full_decoded, negative); match full_decoded { FullDecoded::Nan => { - parts[0] = Part::Copy(b"NaN"); - Formatted { sign, parts: &parts[..1] } + parts[0] = MaybeUninit::new(Part::Copy(b"NaN")); + // SAFETY: we just initialized the elements `..1`. + Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..1]) } } } FullDecoded::Infinite => { - parts[0] = Part::Copy(b"inf"); - Formatted { sign, parts: &parts[..1] } + parts[0] = MaybeUninit::new(Part::Copy(b"inf")); + // SAFETY: we just initialized the elements `..1`. + Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..1]) } } } FullDecoded::Zero => { if frac_digits > 0 { // [0.][0000] - parts[0] = Part::Copy(b"0."); - parts[1] = Part::Zero(frac_digits); - Formatted { sign, parts: &parts[..2] } + parts[0] = MaybeUninit::new(Part::Copy(b"0.")); + parts[1] = MaybeUninit::new(Part::Zero(frac_digits)); + // SAFETY: we just initialized the elements `..2`. + Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..2]) } } } else { - parts[0] = Part::Copy(b"0"); - Formatted { sign, parts: &parts[..1] } + parts[0] = MaybeUninit::new(Part::Copy(b"0")); + // SAFETY: we just initialized the elements `..1`. + Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..1]) } } } } FullDecoded::Finite(ref decoded) => { - let (len, exp) = format_shortest(decoded, buf); - Formatted { sign, parts: digits_to_dec_str(&buf[..len], exp, frac_digits, parts) } + let (buf, exp) = format_shortest(decoded, buf); + Formatted { sign, parts: digits_to_dec_str(buf, exp, frac_digits, parts) } } } } @@ -509,6 +523,7 @@ where /// an empty string if no sign is rendered). /// /// `format_shortest` should be the underlying digit-generation function. +/// It should return the part of the buffer that it initialized. /// You probably would want `strategy::grisu::format_shortest` for this. /// /// The `dec_bounds` is a tuple `(lo, hi)` such that the number is formatted @@ -525,12 +540,12 @@ pub fn to_shortest_exp_str<'a, T, F>( sign: Sign, dec_bounds: (i16, i16), upper: bool, - buf: &'a mut [u8], - parts: &'a mut [Part<'a>], + buf: &'a mut [MaybeUninit], + parts: &'a mut [MaybeUninit>], ) -> Formatted<'a> where T: DecodableFloat, - F: FnMut(&Decoded, &mut [u8]) -> (usize, i16), + F: FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), { assert!(parts.len() >= 6); assert!(buf.len() >= MAX_SIG_DIGITS); @@ -540,28 +555,31 @@ where let sign = determine_sign(sign, &full_decoded, negative); match full_decoded { FullDecoded::Nan => { - parts[0] = Part::Copy(b"NaN"); - Formatted { sign, parts: &parts[..1] } + parts[0] = MaybeUninit::new(Part::Copy(b"NaN")); + // SAFETY: we just initialized the elements `..1`. + Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..1]) } } } FullDecoded::Infinite => { - parts[0] = Part::Copy(b"inf"); - Formatted { sign, parts: &parts[..1] } + parts[0] = MaybeUninit::new(Part::Copy(b"inf")); + // SAFETY: we just initialized the elements `..1`. + Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..1]) } } } FullDecoded::Zero => { parts[0] = if dec_bounds.0 <= 0 && 0 < dec_bounds.1 { - Part::Copy(b"0") + MaybeUninit::new(Part::Copy(b"0")) } else { - Part::Copy(if upper { b"0E0" } else { b"0e0" }) + MaybeUninit::new(Part::Copy(if upper { b"0E0" } else { b"0e0" })) }; - Formatted { sign, parts: &parts[..1] } + // SAFETY: we just initialized the elements `..1`. + Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..1]) } } } FullDecoded::Finite(ref decoded) => { - let (len, exp) = format_shortest(decoded, buf); + let (buf, exp) = format_shortest(decoded, buf); let vis_exp = exp as i32 - 1; let parts = if dec_bounds.0 as i32 <= vis_exp && vis_exp < dec_bounds.1 as i32 { - digits_to_dec_str(&buf[..len], exp, 0, parts) + digits_to_dec_str(buf, exp, 0, parts) } else { - digits_to_exp_str(&buf[..len], exp, 0, upper, parts) + digits_to_exp_str(buf, exp, 0, upper, parts) }; Formatted { sign, parts } } @@ -600,6 +618,7 @@ fn estimate_max_buf_len(exp: i16) -> usize { /// an empty string if no sign is rendered). /// /// `format_exact` should be the underlying digit-generation function. +/// It should return the part of the buffer that it initialized. /// You probably would want `strategy::grisu::format_exact` for this. /// /// The byte buffer should be at least `ndigits` bytes long unless `ndigits` is @@ -613,12 +632,12 @@ pub fn to_exact_exp_str<'a, T, F>( sign: Sign, ndigits: usize, upper: bool, - buf: &'a mut [u8], - parts: &'a mut [Part<'a>], + buf: &'a mut [MaybeUninit], + parts: &'a mut [MaybeUninit>], ) -> Formatted<'a> where T: DecodableFloat, - F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16), + F: FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), { assert!(parts.len() >= 6); assert!(ndigits > 0); @@ -627,23 +646,27 @@ where let sign = determine_sign(sign, &full_decoded, negative); match full_decoded { FullDecoded::Nan => { - parts[0] = Part::Copy(b"NaN"); - Formatted { sign, parts: &parts[..1] } + parts[0] = MaybeUninit::new(Part::Copy(b"NaN")); + // SAFETY: we just initialized the elements `..1`. + Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..1]) } } } FullDecoded::Infinite => { - parts[0] = Part::Copy(b"inf"); - Formatted { sign, parts: &parts[..1] } + parts[0] = MaybeUninit::new(Part::Copy(b"inf")); + // SAFETY: we just initialized the elements `..1`. + Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..1]) } } } FullDecoded::Zero => { if ndigits > 1 { // [0.][0000][e0] - parts[0] = Part::Copy(b"0."); - parts[1] = Part::Zero(ndigits - 1); - parts[2] = Part::Copy(if upper { b"E0" } else { b"e0" }); - Formatted { sign, parts: &parts[..3] } + parts[0] = MaybeUninit::new(Part::Copy(b"0.")); + parts[1] = MaybeUninit::new(Part::Zero(ndigits - 1)); + parts[2] = MaybeUninit::new(Part::Copy(if upper { b"E0" } else { b"e0" })); + // SAFETY: we just initialized the elements `..3`. + Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..3]) } } } else { - parts[0] = Part::Copy(if upper { b"0E0" } else { b"0e0" }); - Formatted { sign, parts: &parts[..1] } + parts[0] = MaybeUninit::new(Part::Copy(if upper { b"0E0" } else { b"0e0" })); + // SAFETY: we just initialized the elements `..1`. + Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..1]) } } } } FullDecoded::Finite(ref decoded) => { @@ -651,8 +674,8 @@ where assert!(buf.len() >= ndigits || buf.len() >= maxlen); let trunc = if ndigits < maxlen { ndigits } else { maxlen }; - let (len, exp) = format_exact(decoded, &mut buf[..trunc], i16::MIN); - Formatted { sign, parts: digits_to_exp_str(&buf[..len], exp, ndigits, upper, parts) } + let (buf, exp) = format_exact(decoded, &mut buf[..trunc], i16::MIN); + Formatted { sign, parts: digits_to_exp_str(buf, exp, ndigits, upper, parts) } } } } @@ -665,6 +688,7 @@ where /// (which can be an empty string if no sign is rendered). /// /// `format_exact` should be the underlying digit-generation function. +/// It should return the part of the buffer that it initialized. /// You probably would want `strategy::grisu::format_exact` for this. /// /// The byte buffer should be enough for the output unless `frac_digits` is @@ -677,12 +701,12 @@ pub fn to_exact_fixed_str<'a, T, F>( v: T, sign: Sign, frac_digits: usize, - buf: &'a mut [u8], - parts: &'a mut [Part<'a>], + buf: &'a mut [MaybeUninit], + parts: &'a mut [MaybeUninit>], ) -> Formatted<'a> where T: DecodableFloat, - F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16), + F: FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), { assert!(parts.len() >= 4); @@ -690,22 +714,26 @@ where let sign = determine_sign(sign, &full_decoded, negative); match full_decoded { FullDecoded::Nan => { - parts[0] = Part::Copy(b"NaN"); - Formatted { sign, parts: &parts[..1] } + parts[0] = MaybeUninit::new(Part::Copy(b"NaN")); + // SAFETY: we just initialized the elements `..1`. + Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..1]) } } } FullDecoded::Infinite => { - parts[0] = Part::Copy(b"inf"); - Formatted { sign, parts: &parts[..1] } + parts[0] = MaybeUninit::new(Part::Copy(b"inf")); + // SAFETY: we just initialized the elements `..1`. + Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..1]) } } } FullDecoded::Zero => { if frac_digits > 0 { // [0.][0000] - parts[0] = Part::Copy(b"0."); - parts[1] = Part::Zero(frac_digits); - Formatted { sign, parts: &parts[..2] } + parts[0] = MaybeUninit::new(Part::Copy(b"0.")); + parts[1] = MaybeUninit::new(Part::Zero(frac_digits)); + // SAFETY: we just initialized the elements `..2`. + Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..2]) } } } else { - parts[0] = Part::Copy(b"0"); - Formatted { sign, parts: &parts[..1] } + parts[0] = MaybeUninit::new(Part::Copy(b"0")); + // SAFETY: we just initialized the elements `..1`. + Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..1]) } } } } FullDecoded::Finite(ref decoded) => { @@ -716,23 +744,25 @@ where // `format_exact` will end rendering digits much earlier in this case, // because we are strictly limited by `maxlen`. let limit = if frac_digits < 0x8000 { -(frac_digits as i16) } else { i16::MIN }; - let (len, exp) = format_exact(decoded, &mut buf[..maxlen], limit); + let (buf, exp) = format_exact(decoded, &mut buf[..maxlen], limit); if exp <= limit { // the restriction couldn't been met, so this should render like zero no matter // `exp` was. this does not include the case that the restriction has been met // only after the final rounding-up; it's a regular case with `exp = limit + 1`. - debug_assert_eq!(len, 0); + debug_assert_eq!(buf.len(), 0); if frac_digits > 0 { // [0.][0000] - parts[0] = Part::Copy(b"0."); - parts[1] = Part::Zero(frac_digits); - Formatted { sign, parts: &parts[..2] } + parts[0] = MaybeUninit::new(Part::Copy(b"0.")); + parts[1] = MaybeUninit::new(Part::Zero(frac_digits)); + // SAFETY: we just initialized the elements `..2`. + Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..2]) } } } else { - parts[0] = Part::Copy(b"0"); - Formatted { sign, parts: &parts[..1] } + parts[0] = MaybeUninit::new(Part::Copy(b"0")); + // SAFETY: we just initialized the elements `..1`. + Formatted { sign, parts: unsafe { MaybeUninit::slice_get_ref(&parts[..1]) } } } } else { - Formatted { sign, parts: digits_to_dec_str(&buf[..len], exp, frac_digits, parts) } + Formatted { sign, parts: digits_to_dec_str(buf, exp, frac_digits, parts) } } } } diff --git a/library/core/src/num/flt2dec/strategy/dragon.rs b/library/core/src/num/flt2dec/strategy/dragon.rs index c8de0004352ef..8cb6763cdbe45 100644 --- a/library/core/src/num/flt2dec/strategy/dragon.rs +++ b/library/core/src/num/flt2dec/strategy/dragon.rs @@ -5,6 +5,7 @@ //! quickly and accurately. SIGPLAN Not. 31, 5 (May. 1996), 108-116. use crate::cmp::Ordering; +use crate::mem::MaybeUninit; use crate::num::bignum::Big32x40 as Big; use crate::num::bignum::Digit32 as Digit; @@ -97,7 +98,10 @@ fn div_rem_upto_16<'a>( } /// The shortest mode implementation for Dragon. -pub fn format_shortest(d: &Decoded, buf: &mut [u8]) -> (/*#digits*/ usize, /*exp*/ i16) { +pub fn format_shortest<'a>( + d: &Decoded, + buf: &'a mut [MaybeUninit], +) -> (/*digits*/ &'a [u8], /*exp*/ i16) { // the number `v` to format is known to be: // - equal to `mant * 2^exp`; // - preceded by `(mant - 2 * minus) * 2^exp` in the original type; and @@ -186,7 +190,7 @@ pub fn format_shortest(d: &Decoded, buf: &mut [u8]) -> (/*#digits*/ usize, /*exp // generate one digit: `d[n] = floor(mant / scale) < 10`. let (d, _) = div_rem_upto_16(&mut mant, &scale, &scale2, &scale4, &scale8); debug_assert!(d < 10); - buf[i] = b'0' + d; + buf[i] = MaybeUninit::new(b'0' + d); i += 1; // this is a simplified description of the modified Dragon algorithm. @@ -241,18 +245,24 @@ pub fn format_shortest(d: &Decoded, buf: &mut [u8]) -> (/*#digits*/ usize, /*exp // if rounding up changes the length, the exponent should also change. // it seems that this condition is very hard to satisfy (possibly impossible), // but we are just being safe and consistent here. - if let Some(c) = round_up(buf, i) { - buf[i] = c; + // SAFETY: we initialized that memory above. + if let Some(c) = round_up(unsafe { MaybeUninit::slice_get_mut(&mut buf[..i]) }) { + buf[i] = MaybeUninit::new(c); i += 1; k += 1; } } - (i, k) + // SAFETY: we initialized that memory above. + (unsafe { MaybeUninit::slice_get_ref(&buf[..i]) }, k) } /// The exact and fixed mode implementation for Dragon. -pub fn format_exact(d: &Decoded, buf: &mut [u8], limit: i16) -> (/*#digits*/ usize, /*exp*/ i16) { +pub fn format_exact<'a>( + d: &Decoded, + buf: &'a mut [MaybeUninit], + limit: i16, +) -> (/*digits*/ &'a [u8], /*exp*/ i16) { assert!(d.mant > 0); assert!(d.minus > 0); assert!(d.plus > 0); @@ -319,9 +329,10 @@ pub fn format_exact(d: &Decoded, buf: &mut [u8], limit: i16) -> (/*#digits*/ usi // following digits are all zeroes, we stop here // do *not* try to perform rounding! rather, fill remaining digits. for c in &mut buf[i..len] { - *c = b'0'; + *c = MaybeUninit::new(b'0'); } - return (len, k); + // SAFETY: we initialized that memory above. + return (unsafe { MaybeUninit::slice_get_ref(&buf[..len]) }, k); } let mut d = 0; @@ -343,7 +354,7 @@ pub fn format_exact(d: &Decoded, buf: &mut [u8], limit: i16) -> (/*#digits*/ usi } debug_assert!(mant < scale); debug_assert!(d < 10); - buf[i] = b'0' + d; + buf[i] = MaybeUninit::new(b'0' + d); mant.mul_small(10); } } @@ -353,21 +364,25 @@ pub fn format_exact(d: &Decoded, buf: &mut [u8], limit: i16) -> (/*#digits*/ usi // round to even (i.e., avoid rounding up when the prior digit is even). let order = mant.cmp(scale.mul_small(5)); if order == Ordering::Greater - || (order == Ordering::Equal && (len == 0 || buf[len - 1] & 1 == 1)) + || (order == Ordering::Equal + // SAFETY: `buf[len-1]` is initialized. + && (len == 0 || unsafe { buf[len - 1].assume_init() } & 1 == 1)) { // if rounding up changes the length, the exponent should also change. // but we've been requested a fixed number of digits, so do not alter the buffer... - if let Some(c) = round_up(buf, len) { + // SAFETY: we initialized that memory above. + if let Some(c) = round_up(unsafe { MaybeUninit::slice_get_mut(&mut buf[..len]) }) { // ...unless we've been requested the fixed precision instead. // we also need to check that, if the original buffer was empty, // the additional digit can only be added when `k == limit` (edge case). k += 1; if k > limit && len < buf.len() { - buf[len] = c; + buf[len] = MaybeUninit::new(c); len += 1; } } } - (len, k) + // SAFETY: we initialized that memory above. + (unsafe { MaybeUninit::slice_get_ref(&buf[..len]) }, k) } diff --git a/library/core/src/num/flt2dec/strategy/grisu.rs b/library/core/src/num/flt2dec/strategy/grisu.rs index 1e2db212dd0de..ed1dd654a3add 100644 --- a/library/core/src/num/flt2dec/strategy/grisu.rs +++ b/library/core/src/num/flt2dec/strategy/grisu.rs @@ -5,6 +5,7 @@ //! [^1]: Florian Loitsch. 2010. Printing floating-point numbers quickly and //! accurately with integers. SIGPLAN Not. 45, 6 (June 2010), 233-243. +use crate::mem::MaybeUninit; use crate::num::diy_float::Fp; use crate::num::flt2dec::{round_up, Decoded, MAX_SIG_DIGITS}; @@ -161,10 +162,10 @@ pub fn max_pow10_no_more_than(x: u32) -> (u8, u32) { /// The shortest mode implementation for Grisu. /// /// It returns `None` when it would return an inexact representation otherwise. -pub fn format_shortest_opt( +pub fn format_shortest_opt<'a>( d: &Decoded, - buf: &mut [u8], -) -> Option<(/*#digits*/ usize, /*exp*/ i16)> { + buf: &'a mut [MaybeUninit], +) -> Option<(/*digits*/ &'a [u8], /*exp*/ i16)> { assert!(d.mant > 0); assert!(d.minus > 0); assert!(d.plus > 0); @@ -266,14 +267,23 @@ pub fn format_shortest_opt( let q = remainder / ten_kappa; let r = remainder % ten_kappa; debug_assert!(q < 10); - buf[i] = b'0' + q as u8; + buf[i] = MaybeUninit::new(b'0' + q as u8); i += 1; let plus1rem = ((r as u64) << e) + plus1frac; // == (plus1 % 10^kappa) * 2^e if plus1rem < delta1 { // `plus1 % 10^kappa < delta1 = plus1 - minus1`; we've found the correct `kappa`. let ten_kappa = (ten_kappa as u64) << e; // scale 10^kappa back to the shared exponent - return round_and_weed(&mut buf[..i], exp, plus1rem, delta1, plus1 - v.f, ten_kappa, 1); + return round_and_weed( + // SAFETY: we initialized that memory above. + unsafe { MaybeUninit::slice_get_mut(&mut buf[..i]) }, + exp, + plus1rem, + delta1, + plus1 - v.f, + ten_kappa, + 1, + ); } // break the loop when we have rendered all integral digits. @@ -310,13 +320,14 @@ pub fn format_shortest_opt( let q = remainder >> e; let r = remainder & ((1 << e) - 1); debug_assert!(q < 10); - buf[i] = b'0' + q as u8; + buf[i] = MaybeUninit::new(b'0' + q as u8); i += 1; if r < threshold { let ten_kappa = 1 << e; // implicit divisor return round_and_weed( - &mut buf[..i], + // SAFETY: we initialized that memory above. + unsafe { MaybeUninit::slice_get_mut(&mut buf[..i]) }, exp, r, threshold, @@ -355,7 +366,7 @@ pub fn format_shortest_opt( plus1v: u64, ten_kappa: u64, ulp: u64, - ) -> Option<(usize, i16)> { + ) -> Option<(&[u8], i16)> { assert!(!buf.is_empty()); // produce two approximations to `v` (actually `plus1 - v`) within 1.5 ulps. @@ -437,20 +448,22 @@ pub fn format_shortest_opt( // this is too liberal, though, so we reject any `w(n)` not between `plus0` and `minus0`, // i.e., `plus1 - plus1w(n) <= minus0` or `plus1 - plus1w(n) >= plus0`. we utilize the facts // that `threshold = plus1 - minus1` and `plus1 - plus0 = minus0 - minus1 = 2 ulp`. - if 2 * ulp <= plus1w && plus1w <= threshold - 4 * ulp { - Some((buf.len(), exp)) - } else { - None - } + if 2 * ulp <= plus1w && plus1w <= threshold - 4 * ulp { Some((buf, exp)) } else { None } } } /// The shortest mode implementation for Grisu with Dragon fallback. /// /// This should be used for most cases. -pub fn format_shortest(d: &Decoded, buf: &mut [u8]) -> (/*#digits*/ usize, /*exp*/ i16) { +pub fn format_shortest<'a>( + d: &Decoded, + buf: &'a mut [MaybeUninit], +) -> (/*digits*/ &'a [u8], /*exp*/ i16) { use crate::num::flt2dec::strategy::dragon::format_shortest as fallback; - match format_shortest_opt(d, buf) { + // SAFETY: The borrow checker is not smart enough to let us use `buf` + // in the second branch, so we launder the lifetime here. But we only re-use + // `buf` if `format_shortest_opt` returned `None` so this is okay. + match format_shortest_opt(d, unsafe { &mut *(buf as *mut _) }) { Some(ret) => ret, None => fallback(d, buf), } @@ -459,11 +472,11 @@ pub fn format_shortest(d: &Decoded, buf: &mut [u8]) -> (/*#digits*/ usize, /*exp /// The exact and fixed mode implementation for Grisu. /// /// It returns `None` when it would return an inexact representation otherwise. -pub fn format_exact_opt( +pub fn format_exact_opt<'a>( d: &Decoded, - buf: &mut [u8], + buf: &'a mut [MaybeUninit], limit: i16, -) -> Option<(/*#digits*/ usize, /*exp*/ i16)> { +) -> Option<(/*digits*/ &'a [u8], /*exp*/ i16)> { assert!(d.mant > 0); assert!(d.mant < (1 << 61)); // we need at least three bits of additional precision assert!(!buf.is_empty()); @@ -510,7 +523,11 @@ pub fn format_exact_opt( // thus we are being sloppy here and widen the error range by a factor of 10. // this will increase the false negative rate, but only very, *very* slightly; // it can only matter noticeably when the mantissa is bigger than 60 bits. - return possibly_round(buf, 0, exp, limit, v.f / 10, (max_ten_kappa as u64) << e, err << e); + // + // SAFETY: `len=0`, so the obligation of having initialized this memory is trivial. + return unsafe { + possibly_round(buf, 0, exp, limit, v.f / 10, (max_ten_kappa as u64) << e, err << e) + }; } else if ((exp as i32 - limit as i32) as usize) < buf.len() { (exp - limit) as usize } else { @@ -534,13 +551,16 @@ pub fn format_exact_opt( let q = remainder / ten_kappa; let r = remainder % ten_kappa; debug_assert!(q < 10); - buf[i] = b'0' + q as u8; + buf[i] = MaybeUninit::new(b'0' + q as u8); i += 1; // is the buffer full? run the rounding pass with the remainder. if i == len { let vrem = ((r as u64) << e) + vfrac; // == (v % 10^kappa) * 2^e - return possibly_round(buf, len, exp, limit, vrem, (ten_kappa as u64) << e, err << e); + // SAFETY: we have initialized `len` many bytes. + return unsafe { + possibly_round(buf, len, exp, limit, vrem, (ten_kappa as u64) << e, err << e) + }; } // break the loop when we have rendered all integral digits. @@ -585,12 +605,13 @@ pub fn format_exact_opt( let q = remainder >> e; let r = remainder & ((1 << e) - 1); debug_assert!(q < 10); - buf[i] = b'0' + q as u8; + buf[i] = MaybeUninit::new(b'0' + q as u8); i += 1; // is the buffer full? run the rounding pass with the remainder. if i == len { - return possibly_round(buf, len, exp, limit, r, 1 << e, err); + // SAFETY: we have initialized `len` many bytes. + return unsafe { possibly_round(buf, len, exp, limit, r, 1 << e, err) }; } // restore invariants @@ -610,15 +631,17 @@ pub fn format_exact_opt( // - `remainder = (v % 10^kappa) * k` // - `ten_kappa = 10^kappa * k` // - `ulp = 2^-e * k` - fn possibly_round( - buf: &mut [u8], + // + // SAFETY: the first `len` bytes of `buf` must be initialized. + unsafe fn possibly_round( + buf: &mut [MaybeUninit], mut len: usize, mut exp: i16, limit: i16, remainder: u64, ten_kappa: u64, ulp: u64, - ) -> Option<(usize, i16)> { + ) -> Option<(&[u8], i16)> { debug_assert!(remainder < ten_kappa); // 10^kappa @@ -677,7 +700,8 @@ pub fn format_exact_opt( // we've already verified that `ulp < 10^kappa / 2`, so as long as // `10^kappa` did not overflow after all, the second check is fine. if ten_kappa - remainder > remainder && ten_kappa - 2 * remainder >= 2 * ulp { - return Some((len, exp)); + // SAFETY: our caller initialized that memory. + return Some((unsafe { MaybeUninit::slice_get_ref(&buf[..len]) }, exp)); } // :<------- remainder ------>| : @@ -698,17 +722,19 @@ pub fn format_exact_opt( // as `10^kappa` is never zero). also note that `remainder - ulp <= 10^kappa`, // so the second check does not overflow. if remainder > ulp && ten_kappa - (remainder - ulp) <= remainder - ulp { - if let Some(c) = round_up(buf, len) { + // SAFETY: our caller must have initialized that memory. + if let Some(c) = round_up(unsafe { MaybeUninit::slice_get_mut(&mut buf[..len]) }) { // only add an additional digit when we've been requested the fixed precision. // we also need to check that, if the original buffer was empty, // the additional digit can only be added when `exp == limit` (edge case). exp += 1; if exp > limit && len < buf.len() { - buf[len] = c; + buf[len] = MaybeUninit::new(c); len += 1; } } - return Some((len, exp)); + // SAFETY: we and our caller initialized that memory. + return Some((unsafe { MaybeUninit::slice_get_ref(&buf[..len]) }, exp)); } // otherwise we are doomed (i.e., some values between `v - 1 ulp` and `v + 1 ulp` are @@ -720,9 +746,16 @@ pub fn format_exact_opt( /// The exact and fixed mode implementation for Grisu with Dragon fallback. /// /// This should be used for most cases. -pub fn format_exact(d: &Decoded, buf: &mut [u8], limit: i16) -> (/*#digits*/ usize, /*exp*/ i16) { +pub fn format_exact<'a>( + d: &Decoded, + buf: &'a mut [MaybeUninit], + limit: i16, +) -> (/*digits*/ &'a [u8], /*exp*/ i16) { use crate::num::flt2dec::strategy::dragon::format_exact as fallback; - match format_exact_opt(d, buf, limit) { + // SAFETY: The borrow checker is not smart enough to let us use `buf` + // in the second branch, so we launder the lifetime here. But we only re-use + // `buf` if `format_exact_opt` returned `None` so this is okay. + match format_exact_opt(d, unsafe { &mut *(buf as *mut _) }, limit) { Some(ret) => ret, None => fallback(d, buf, limit), } diff --git a/library/core/tests/num/flt2dec/mod.rs b/library/core/tests/num/flt2dec/mod.rs index ae892e3b0bfbf..8e95249a79d19 100644 --- a/library/core/tests/num/flt2dec/mod.rs +++ b/library/core/tests/num/flt2dec/mod.rs @@ -1,3 +1,4 @@ +use std::mem::MaybeUninit; use std::{fmt, str}; use core::num::flt2dec::{decode, DecodableFloat, Decoded, FullDecoded}; @@ -36,20 +37,20 @@ macro_rules! check_shortest { ); ($f:ident($v:expr) => $buf:expr, $exp:expr; $fmt:expr, $($key:ident = $val:expr),*) => ({ - let mut buf = [b'_'; MAX_SIG_DIGITS]; - let (len, k) = $f(&decode_finite($v), &mut buf); - assert!((&buf[..len], k) == ($buf, $exp), - $fmt, actual = (str::from_utf8(&buf[..len]).unwrap(), k), + let mut buf = [MaybeUninit::new(b'_'); MAX_SIG_DIGITS]; + let (buf, k) = $f(&decode_finite($v), &mut buf); + assert!((buf, k) == ($buf, $exp), + $fmt, actual = (str::from_utf8(buf).unwrap(), k), expected = (str::from_utf8($buf).unwrap(), $exp), $($key = $val),*); }); ($f:ident{$($k:ident: $v:expr),+} => $buf:expr, $exp:expr; $fmt:expr, $($key:ident = $val:expr),*) => ({ - let mut buf = [b'_'; MAX_SIG_DIGITS]; - let (len, k) = $f(&Decoded { $($k: $v),+ }, &mut buf); - assert!((&buf[..len], k) == ($buf, $exp), - $fmt, actual = (str::from_utf8(&buf[..len]).unwrap(), k), + let mut buf = [MaybeUninit::new(b'_'); MAX_SIG_DIGITS]; + let (buf, k) = $f(&Decoded { $($k: $v),+ }, &mut buf); + assert!((buf, k) == ($buf, $exp), + $fmt, actual = (str::from_utf8(buf).unwrap(), k), expected = (str::from_utf8($buf).unwrap(), $exp), $($key = $val),*); }) @@ -58,9 +59,9 @@ macro_rules! check_shortest { macro_rules! try_exact { ($f:ident($decoded:expr) => $buf:expr, $expected:expr, $expectedk:expr; $fmt:expr, $($key:ident = $val:expr),*) => ({ - let (len, k) = $f($decoded, &mut $buf[..$expected.len()], i16::MIN); - assert!((&$buf[..len], k) == ($expected, $expectedk), - $fmt, actual = (str::from_utf8(&$buf[..len]).unwrap(), k), + let (buf, k) = $f($decoded, &mut $buf[..$expected.len()], i16::MIN); + assert!((buf, k) == ($expected, $expectedk), + $fmt, actual = (str::from_utf8(buf).unwrap(), k), expected = (str::from_utf8($expected).unwrap(), $expectedk), $($key = $val),*); }) @@ -69,9 +70,9 @@ macro_rules! try_exact { macro_rules! try_fixed { ($f:ident($decoded:expr) => $buf:expr, $request:expr, $expected:expr, $expectedk:expr; $fmt:expr, $($key:ident = $val:expr),*) => ({ - let (len, k) = $f($decoded, &mut $buf[..], $request); - assert!((&$buf[..len], k) == ($expected, $expectedk), - $fmt, actual = (str::from_utf8(&$buf[..len]).unwrap(), k), + let (buf, k) = $f($decoded, &mut $buf[..], $request); + assert!((buf, k) == ($expected, $expectedk), + $fmt, actual = (str::from_utf8(buf).unwrap(), k), expected = (str::from_utf8($expected).unwrap(), $expectedk), $($key = $val),*); }) @@ -93,10 +94,10 @@ fn ldexp_f64(a: f64, b: i32) -> f64 { fn check_exact(mut f: F, v: T, vstr: &str, expected: &[u8], expectedk: i16) where T: DecodableFloat, - F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16), + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), { // use a large enough buffer - let mut buf = [b'_'; 1024]; + let mut buf = [MaybeUninit::new(b'_'); 1024]; let mut expected_ = [b'_'; 1024]; let decoded = decode_finite(v); @@ -118,7 +119,7 @@ where // we should always return `100..00` (`i` digits) instead, since that's // what we can came up with `i` digits anyway. `round_up` assumes that // the adjustment to the length is done by caller, which we simply ignore. - if let Some(_) = round_up(&mut expected_, i) { + if let Some(_) = round_up(&mut expected_[..i]) { expectedk_ += 1; } } @@ -193,10 +194,10 @@ impl TestableFloat for f64 { fn check_exact_one(mut f: F, x: i64, e: isize, tstr: &str, expected: &[u8], expectedk: i16) where T: TestableFloat, - F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16), + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), { // use a large enough buffer - let mut buf = [b'_'; 1024]; + let mut buf = [MaybeUninit::new(b'_'); 1024]; let v: T = TestableFloat::ldexpi(x, e); let decoded = decode_finite(v); @@ -230,7 +231,7 @@ macro_rules! check_exact_one { pub fn f32_shortest_sanity_test(mut f: F) where - F: FnMut(&Decoded, &mut [u8]) -> (usize, i16), + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), { // 0.0999999940395355224609375 // 0.100000001490116119384765625 @@ -277,7 +278,7 @@ where pub fn f32_exact_sanity_test(mut f: F) where - F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16), + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), { let minf32 = ldexp_f32(1.0, -149); @@ -321,7 +322,7 @@ where pub fn f64_shortest_sanity_test(mut f: F) where - F: FnMut(&Decoded, &mut [u8]) -> (usize, i16), + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), { // 0.0999999999999999777955395074968691915273... // 0.1000000000000000055511151231257827021181... @@ -387,7 +388,7 @@ where pub fn f64_exact_sanity_test(mut f: F) where - F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16), + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), { let minf64 = ldexp_f64(1.0, -1074); @@ -474,7 +475,7 @@ where pub fn more_shortest_sanity_test(mut f: F) where - F: FnMut(&Decoded, &mut [u8]) -> (usize, i16), + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), { check_shortest!(f{mant: 99_999_999_999_999_999, minus: 1, plus: 1, exp: 0, inclusive: true} => b"1", 18); @@ -484,10 +485,10 @@ where fn to_string_with_parts(mut f: F) -> String where - F: for<'a> FnMut(&'a mut [u8], &'a mut [Part<'a>]) -> Formatted<'a>, + F: for<'a> FnMut(&'a mut [MaybeUninit], &'a mut [MaybeUninit>]) -> Formatted<'a>, { - let mut buf = [0; 1024]; - let mut parts = [Part::Zero(0); 16]; + let mut buf = [MaybeUninit::new(0); 1024]; + let mut parts = [MaybeUninit::new(Part::Zero(0)); 16]; let formatted = f(&mut buf, &mut parts); let mut ret = vec![0; formatted.len()]; assert_eq!(formatted.write(&mut ret), Some(ret.len())); @@ -496,14 +497,14 @@ where pub fn to_shortest_str_test(mut f_: F) where - F: FnMut(&Decoded, &mut [u8]) -> (usize, i16), + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), { use core::num::flt2dec::Sign::*; fn to_string(f: &mut F, v: T, sign: Sign, frac_digits: usize) -> String where T: DecodableFloat, - F: FnMut(&Decoded, &mut [u8]) -> (usize, i16), + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), { to_string_with_parts(|buf, parts| { to_shortest_str(|d, b| f(d, b), v, sign, frac_digits, buf, parts) @@ -597,14 +598,14 @@ where pub fn to_shortest_exp_str_test(mut f_: F) where - F: FnMut(&Decoded, &mut [u8]) -> (usize, i16), + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), { use core::num::flt2dec::Sign::*; fn to_string(f: &mut F, v: T, sign: Sign, exp_bounds: (i16, i16), upper: bool) -> String where T: DecodableFloat, - F: FnMut(&Decoded, &mut [u8]) -> (usize, i16), + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), { to_string_with_parts(|buf, parts| { to_shortest_exp_str(|d, b| f(d, b), v, sign, exp_bounds, upper, buf, parts) @@ -716,14 +717,14 @@ where pub fn to_exact_exp_str_test(mut f_: F) where - F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16), + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), { use core::num::flt2dec::Sign::*; fn to_string(f: &mut F, v: T, sign: Sign, ndigits: usize, upper: bool) -> String where T: DecodableFloat, - F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16), + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), { to_string_with_parts(|buf, parts| { to_exact_exp_str(|d, b, l| f(d, b, l), v, sign, ndigits, upper, buf, parts) @@ -989,14 +990,14 @@ where pub fn to_exact_fixed_str_test(mut f_: F) where - F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16), + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), { use core::num::flt2dec::Sign::*; fn to_string(f: &mut F, v: T, sign: Sign, frac_digits: usize) -> String where T: DecodableFloat, - F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16), + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), { to_string_with_parts(|buf, parts| { to_exact_fixed_str(|d, b, l| f(d, b, l), v, sign, frac_digits, buf, parts) diff --git a/library/core/tests/num/flt2dec/random.rs b/library/core/tests/num/flt2dec/random.rs index e5656eb204c73..57b3dcf8e1e04 100644 --- a/library/core/tests/num/flt2dec/random.rs +++ b/library/core/tests/num/flt2dec/random.rs @@ -1,5 +1,6 @@ #![cfg(not(target_arch = "wasm32"))] +use std::mem::MaybeUninit; use std::str; use core::num::flt2dec::strategy::grisu::format_exact_opt; @@ -20,8 +21,8 @@ pub fn decode_finite(v: T) -> Decoded { fn iterate(func: &str, k: usize, n: usize, mut f: F, mut g: G, mut v: V) -> (usize, usize) where - F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>, - G: FnMut(&Decoded, &mut [u8]) -> (usize, i16), + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, + G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), V: FnMut(usize) -> Decoded, { assert!(k <= 1024); @@ -42,11 +43,11 @@ where } let decoded = v(i); - let mut buf1 = [0; 1024]; - if let Some((len1, e1)) = f(&decoded, &mut buf1[..k]) { - let mut buf2 = [0; 1024]; - let (len2, e2) = g(&decoded, &mut buf2[..k]); - if e1 == e2 && &buf1[..len1] == &buf2[..len2] { + let mut buf1 = [MaybeUninit::new(0); 1024]; + if let Some((buf1, e1)) = f(&decoded, &mut buf1[..k]) { + let mut buf2 = [MaybeUninit::new(0); 1024]; + let (buf2, e2) = g(&decoded, &mut buf2[..k]); + if e1 == e2 && buf1 == buf2 { npassed += 1; } else { println!( @@ -54,9 +55,9 @@ where i, n, decoded, - str::from_utf8(&buf1[..len1]).unwrap(), + str::from_utf8(buf1).unwrap(), e1, - str::from_utf8(&buf2[..len2]).unwrap(), + str::from_utf8(buf2).unwrap(), e2 ); } @@ -85,8 +86,8 @@ where pub fn f32_random_equivalence_test(f: F, g: G, k: usize, n: usize) where - F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>, - G: FnMut(&Decoded, &mut [u8]) -> (usize, i16), + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, + G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), { if cfg!(target_os = "emscripten") { return; // using rng pulls in i128 support, which doesn't work @@ -101,8 +102,8 @@ where pub fn f64_random_equivalence_test(f: F, g: G, k: usize, n: usize) where - F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>, - G: FnMut(&Decoded, &mut [u8]) -> (usize, i16), + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, + G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), { if cfg!(target_os = "emscripten") { return; // using rng pulls in i128 support, which doesn't work @@ -117,8 +118,8 @@ where pub fn f32_exhaustive_equivalence_test(f: F, g: G, k: usize) where - F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>, - G: FnMut(&Decoded, &mut [u8]) -> (usize, i16), + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, + G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), { // we have only 2^23 * (2^8 - 1) - 1 = 2,139,095,039 positive finite f32 values, // so why not simply testing all of them?