diff --git a/library/core/src/fmt/float.rs b/library/core/src/fmt/float.rs index ece3cde001580..ba65f0fadbd9d 100644 --- a/library/core/src/fmt/float.rs +++ b/library/core/src/fmt/float.rs @@ -1,6 +1,7 @@ use crate::fmt::{Debug, Display, Formatter, LowerExp, Result, UpperExp}; use crate::mem::MaybeUninit; use crate::num::flt2dec; +use crate::num::fmt as numfmt; // Don't inline this so callers don't use the stack space this function // requires unless they have to. @@ -15,7 +16,7 @@ where T: flt2dec::DecodableFloat, { let mut buf: [MaybeUninit; 1024] = MaybeUninit::uninit_array(); // enough for f32 and f64 - let mut parts: [MaybeUninit>; 4] = MaybeUninit::uninit_array(); + let mut parts: [MaybeUninit>; 4] = MaybeUninit::uninit_array(); let formatted = flt2dec::to_exact_fixed_str( flt2dec::strategy::grisu::format_exact, *num, @@ -41,7 +42,7 @@ where { // 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 mut parts: [MaybeUninit>; 4] = MaybeUninit::uninit_array(); let formatted = flt2dec::to_shortest_str( flt2dec::strategy::grisu::format_shortest, *num, @@ -85,7 +86,7 @@ where T: flt2dec::DecodableFloat, { let mut buf: [MaybeUninit; 1024] = MaybeUninit::uninit_array(); // enough for f32 and f64 - let mut parts: [MaybeUninit>; 6] = MaybeUninit::uninit_array(); + let mut parts: [MaybeUninit>; 6] = MaybeUninit::uninit_array(); let formatted = flt2dec::to_exact_exp_str( flt2dec::strategy::grisu::format_exact, *num, @@ -112,7 +113,7 @@ where { // 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 mut parts: [MaybeUninit>; 6] = MaybeUninit::uninit_array(); let formatted = flt2dec::to_shortest_exp_str( flt2dec::strategy::grisu::format_shortest, *num, diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 02ac4fb800655..269963400b624 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -7,13 +7,16 @@ use crate::char::EscapeDebugExtArgs; use crate::iter; use crate::marker::PhantomData; use crate::mem; -use crate::num::flt2dec; +use crate::num::fmt as numfmt; use crate::ops::Deref; use crate::result; use crate::str; mod builders; +#[cfg(not(no_fp_fmt_parse))] mod float; +#[cfg(no_fp_fmt_parse)] +mod nofloat; mod num; #[stable(feature = "fmt_flags_align", since = "1.28.0")] @@ -1421,7 +1424,7 @@ impl<'a> Formatter<'a> { /// Takes the formatted parts and applies the padding. /// Assumes that the caller already has rendered the parts with required precision, /// so that `self.precision` can be ignored. - fn pad_formatted_parts(&mut self, formatted: &flt2dec::Formatted<'_>) -> Result { + fn pad_formatted_parts(&mut self, formatted: &numfmt::Formatted<'_>) -> Result { if let Some(mut width) = self.width { // for the sign-aware zero padding, we render the sign first and // behave as if we had no sign from the beginning. @@ -1461,14 +1464,14 @@ impl<'a> Formatter<'a> { } } - fn write_formatted_parts(&mut self, formatted: &flt2dec::Formatted<'_>) -> Result { + fn write_formatted_parts(&mut self, formatted: &numfmt::Formatted<'_>) -> Result { fn write_bytes(buf: &mut dyn Write, s: &[u8]) -> Result { - // SAFETY: This is used for `flt2dec::Part::Num` and `flt2dec::Part::Copy`. - // It's safe to use for `flt2dec::Part::Num` since every char `c` is between + // SAFETY: This is used for `numfmt::Part::Num` and `numfmt::Part::Copy`. + // It's safe to use for `numfmt::Part::Num` since every char `c` is between // `b'0'` and `b'9'`, which means `s` is valid UTF-8. - // It's also probably safe in practice to use for `flt2dec::Part::Copy(buf)` + // It's also probably safe in practice to use for `numfmt::Part::Copy(buf)` // since `buf` should be plain ASCII, but it's possible for someone to pass - // in a bad value for `buf` into `flt2dec::to_shortest_str` since it is a + // in a bad value for `buf` into `numfmt::to_shortest_str` since it is a // public function. // FIXME: Determine whether this could result in UB. buf.write_str(unsafe { str::from_utf8_unchecked(s) }) @@ -1479,7 +1482,7 @@ impl<'a> Formatter<'a> { } for part in formatted.parts { match *part { - flt2dec::Part::Zero(mut nzeroes) => { + numfmt::Part::Zero(mut nzeroes) => { const ZEROES: &str = // 64 zeroes "0000000000000000000000000000000000000000000000000000000000000000"; while nzeroes > ZEROES.len() { @@ -1490,7 +1493,7 @@ impl<'a> Formatter<'a> { self.buf.write_str(&ZEROES[..nzeroes])?; } } - flt2dec::Part::Num(mut v) => { + numfmt::Part::Num(mut v) => { let mut s = [0; 5]; let len = part.len(); for c in s[..len].iter_mut().rev() { @@ -1499,7 +1502,7 @@ impl<'a> Formatter<'a> { } write_bytes(self.buf, &s[..len])?; } - flt2dec::Part::Copy(buf) => { + numfmt::Part::Copy(buf) => { write_bytes(self.buf, buf)?; } } diff --git a/library/core/src/fmt/nofloat.rs b/library/core/src/fmt/nofloat.rs new file mode 100644 index 0000000000000..cfb94cd9de530 --- /dev/null +++ b/library/core/src/fmt/nofloat.rs @@ -0,0 +1,15 @@ +use crate::fmt::{Debug, Formatter, Result}; + +macro_rules! floating { + ($ty:ident) => { + #[stable(feature = "rust1", since = "1.0.0")] + impl Debug for $ty { + fn fmt(&self, _fmt: &mut Formatter<'_>) -> Result { + panic!("floating point support is turned off"); + } + } + }; +} + +floating! { f32 } +floating! { f64 } diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index cdd731fdd4d4e..db45640df48d6 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -2,7 +2,7 @@ use crate::fmt; use crate::mem::MaybeUninit; -use crate::num::flt2dec; +use crate::num::fmt as numfmt; use crate::ops::{Div, Rem, Sub}; use crate::ptr; use crate::slice; @@ -406,9 +406,9 @@ macro_rules! impl_Exp { }; let parts = &[ - flt2dec::Part::Copy(buf_slice), - flt2dec::Part::Zero(added_precision), - flt2dec::Part::Copy(exp_slice) + numfmt::Part::Copy(buf_slice), + numfmt::Part::Zero(added_precision), + numfmt::Part::Copy(exp_slice) ]; let sign = if !is_nonnegative { "-" @@ -417,7 +417,7 @@ macro_rules! impl_Exp { } else { "" }; - let formatted = flt2dec::Formatted{sign, parts}; + let formatted = numfmt::Formatted{sign, parts}; f.pad_formatted_parts(&formatted) } diff --git a/library/core/src/num/flt2dec/mod.rs b/library/core/src/num/flt2dec/mod.rs index 93bdf5040e08b..62ae30e9ad386 100644 --- a/library/core/src/num/flt2dec/mod.rs +++ b/library/core/src/num/flt2dec/mod.rs @@ -124,6 +124,7 @@ functions. pub use self::decoder::{decode, DecodableFloat, Decoded, FullDecoded}; +use super::fmt::{Formatted, Part}; use crate::mem::MaybeUninit; pub mod decoder; @@ -170,107 +171,6 @@ pub fn round_up(d: &mut [u8]) -> Option { } } -/// Formatted parts. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum Part<'a> { - /// Given number of zero digits. - Zero(usize), - /// A literal number up to 5 digits. - Num(u16), - /// A verbatim copy of given bytes. - Copy(&'a [u8]), -} - -impl<'a> Part<'a> { - /// Returns the exact byte length of given part. - pub fn len(&self) -> usize { - match *self { - Part::Zero(nzeroes) => nzeroes, - Part::Num(v) => { - if v < 1_000 { - if v < 10 { - 1 - } else if v < 100 { - 2 - } else { - 3 - } - } else { - if v < 10_000 { 4 } else { 5 } - } - } - Part::Copy(buf) => buf.len(), - } - } - - /// Writes a part into the supplied buffer. - /// Returns the number of written bytes, or `None` if the buffer is not enough. - /// (It may still leave partially written bytes in the buffer; do not rely on that.) - pub fn write(&self, out: &mut [u8]) -> Option { - let len = self.len(); - if out.len() >= len { - match *self { - Part::Zero(nzeroes) => { - for c in &mut out[..nzeroes] { - *c = b'0'; - } - } - Part::Num(mut v) => { - for c in out[..len].iter_mut().rev() { - *c = b'0' + (v % 10) as u8; - v /= 10; - } - } - Part::Copy(buf) => { - out[..buf.len()].copy_from_slice(buf); - } - } - Some(len) - } else { - None - } - } -} - -/// Formatted result containing one or more parts. -/// This can be written to the byte buffer or converted to the allocated string. -#[allow(missing_debug_implementations)] -#[derive(Clone)] -pub struct Formatted<'a> { - /// A byte slice representing a sign, either `""`, `"-"` or `"+"`. - pub sign: &'static str, - /// Formatted parts to be rendered after a sign and optional zero padding. - pub parts: &'a [Part<'a>], -} - -impl<'a> Formatted<'a> { - /// Returns the exact byte length of combined formatted result. - pub fn len(&self) -> usize { - let mut len = self.sign.len(); - for part in self.parts { - len += part.len(); - } - len - } - - /// Writes all formatted parts into the supplied buffer. - /// Returns the number of written bytes, or `None` if the buffer is not enough. - /// (It may still leave partially written bytes in the buffer; do not rely on that.) - pub fn write(&self, out: &mut [u8]) -> Option { - if out.len() < self.sign.len() { - return None; - } - out[..self.sign.len()].copy_from_slice(self.sign.as_bytes()); - - let mut written = self.sign.len(); - for part in self.parts { - let len = part.write(&mut out[written..])?; - written += len; - } - Some(written) - } -} - /// Formats given decimal digits `0.<...buf...> * 10^exp` into the decimal form /// with at least given number of fractional digits. The result is stored to /// the supplied parts array and a slice of written parts is returned. diff --git a/library/core/src/num/fmt.rs b/library/core/src/num/fmt.rs new file mode 100644 index 0000000000000..578288bda2595 --- /dev/null +++ b/library/core/src/num/fmt.rs @@ -0,0 +1,108 @@ +//! Shared utilties used by both float and integer formatting. +#![doc(hidden)] +#![unstable( + feature = "numfmt", + reason = "internal routines only exposed for testing", + issue = "none" +)] + +/// Formatted parts. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum Part<'a> { + /// Given number of zero digits. + Zero(usize), + /// A literal number up to 5 digits. + Num(u16), + /// A verbatim copy of given bytes. + Copy(&'a [u8]), +} + +impl<'a> Part<'a> { + /// Returns the exact byte length of given part. + pub fn len(&self) -> usize { + match *self { + Part::Zero(nzeroes) => nzeroes, + Part::Num(v) => { + if v < 1_000 { + if v < 10 { + 1 + } else if v < 100 { + 2 + } else { + 3 + } + } else { + if v < 10_000 { 4 } else { 5 } + } + } + Part::Copy(buf) => buf.len(), + } + } + + /// Writes a part into the supplied buffer. + /// Returns the number of written bytes, or `None` if the buffer is not enough. + /// (It may still leave partially written bytes in the buffer; do not rely on that.) + pub fn write(&self, out: &mut [u8]) -> Option { + let len = self.len(); + if out.len() >= len { + match *self { + Part::Zero(nzeroes) => { + for c in &mut out[..nzeroes] { + *c = b'0'; + } + } + Part::Num(mut v) => { + for c in out[..len].iter_mut().rev() { + *c = b'0' + (v % 10) as u8; + v /= 10; + } + } + Part::Copy(buf) => { + out[..buf.len()].copy_from_slice(buf); + } + } + Some(len) + } else { + None + } + } +} + +/// Formatted result containing one or more parts. +/// This can be written to the byte buffer or converted to the allocated string. +#[allow(missing_debug_implementations)] +#[derive(Clone)] +pub struct Formatted<'a> { + /// A byte slice representing a sign, either `""`, `"-"` or `"+"`. + pub sign: &'static str, + /// Formatted parts to be rendered after a sign and optional zero padding. + pub parts: &'a [Part<'a>], +} + +impl<'a> Formatted<'a> { + /// Returns the exact byte length of combined formatted result. + pub fn len(&self) -> usize { + let mut len = self.sign.len(); + for part in self.parts { + len += part.len(); + } + len + } + + /// Writes all formatted parts into the supplied buffer. + /// Returns the number of written bytes, or `None` if the buffer is not enough. + /// (It may still leave partially written bytes in the buffer; do not rely on that.) + pub fn write(&self, out: &mut [u8]) -> Option { + if out.len() < self.sign.len() { + return None; + } + out[..self.sign.len()].copy_from_slice(self.sign.as_bytes()); + + let mut written = self.sign.len(); + for part in self.parts { + let len = part.write(&mut out[written..])?; + written += len; + } + Some(written) + } +} diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 6032dc9a2d371..f60513a5d239a 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -25,10 +25,15 @@ macro_rules! unlikely { } // All these modules are technically private and only exposed for coretests: +#[cfg(not(no_fp_fmt_parse))] pub mod bignum; +#[cfg(not(no_fp_fmt_parse))] pub mod dec2flt; +#[cfg(not(no_fp_fmt_parse))] pub mod diy_float; +#[cfg(not(no_fp_fmt_parse))] pub mod flt2dec; +pub mod fmt; #[macro_use] mod int_macros; // import int_impl! @@ -43,6 +48,7 @@ mod wrapping; pub use wrapping::Wrapping; #[stable(feature = "rust1", since = "1.0.0")] +#[cfg(not(no_fp_fmt_parse))] pub use dec2flt::ParseFloatError; #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 16051b3bc36c7..d990c48af710a 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -39,6 +39,7 @@ #![feature(maybe_uninit_extra)] #![feature(maybe_uninit_write_slice)] #![feature(min_specialization)] +#![feature(numfmt)] #![feature(step_trait)] #![feature(str_internals)] #![feature(test)] diff --git a/library/core/tests/num/flt2dec/mod.rs b/library/core/tests/num/flt2dec/mod.rs index 960a7ca5ff508..4874e8ec09f8c 100644 --- a/library/core/tests/num/flt2dec/mod.rs +++ b/library/core/tests/num/flt2dec/mod.rs @@ -2,10 +2,11 @@ use std::mem::MaybeUninit; use std::{fmt, str}; use core::num::flt2dec::{decode, DecodableFloat, Decoded, FullDecoded}; -use core::num::flt2dec::{round_up, Formatted, Part, Sign, MAX_SIG_DIGITS}; +use core::num::flt2dec::{round_up, Sign, MAX_SIG_DIGITS}; use core::num::flt2dec::{ to_exact_exp_str, to_exact_fixed_str, to_shortest_exp_str, to_shortest_str, }; +use core::num::fmt::{Formatted, Part}; pub use test::Bencher; diff --git a/src/test/run-make-fulldeps/core-no-fp-fmt-parse/Makefile b/src/test/run-make-fulldeps/core-no-fp-fmt-parse/Makefile new file mode 100644 index 0000000000000..bc4562bef3a96 --- /dev/null +++ b/src/test/run-make-fulldeps/core-no-fp-fmt-parse/Makefile @@ -0,0 +1,4 @@ +-include ../tools.mk + +all: + $(RUSTC) --edition=2018 --crate-type=rlib ../../../../library/core/src/lib.rs --cfg no_fp_fmt_parse