Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core: add unstable no_fp_fmt_parse to disable float formatting code #86048

Merged
merged 2 commits into from
Jul 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions library/core/src/fmt/float.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -15,7 +16,7 @@ where
T: flt2dec::DecodableFloat,
{
let mut buf: [MaybeUninit<u8>; 1024] = MaybeUninit::uninit_array(); // enough for f32 and f64
let mut parts: [MaybeUninit<flt2dec::Part<'_>>; 4] = MaybeUninit::uninit_array();
let mut parts: [MaybeUninit<numfmt::Part<'_>>; 4] = MaybeUninit::uninit_array();
let formatted = flt2dec::to_exact_fixed_str(
flt2dec::strategy::grisu::format_exact,
*num,
Expand All @@ -41,7 +42,7 @@ where
{
// enough for f32 and f64
let mut buf: [MaybeUninit<u8>; flt2dec::MAX_SIG_DIGITS] = MaybeUninit::uninit_array();
let mut parts: [MaybeUninit<flt2dec::Part<'_>>; 4] = MaybeUninit::uninit_array();
let mut parts: [MaybeUninit<numfmt::Part<'_>>; 4] = MaybeUninit::uninit_array();
let formatted = flt2dec::to_shortest_str(
flt2dec::strategy::grisu::format_shortest,
*num,
Expand Down Expand Up @@ -85,7 +86,7 @@ where
T: flt2dec::DecodableFloat,
{
let mut buf: [MaybeUninit<u8>; 1024] = MaybeUninit::uninit_array(); // enough for f32 and f64
let mut parts: [MaybeUninit<flt2dec::Part<'_>>; 6] = MaybeUninit::uninit_array();
let mut parts: [MaybeUninit<numfmt::Part<'_>>; 6] = MaybeUninit::uninit_array();
let formatted = flt2dec::to_exact_exp_str(
flt2dec::strategy::grisu::format_exact,
*num,
Expand All @@ -112,7 +113,7 @@ where
{
// enough for f32 and f64
let mut buf: [MaybeUninit<u8>; flt2dec::MAX_SIG_DIGITS] = MaybeUninit::uninit_array();
let mut parts: [MaybeUninit<flt2dec::Part<'_>>; 6] = MaybeUninit::uninit_array();
let mut parts: [MaybeUninit<numfmt::Part<'_>>; 6] = MaybeUninit::uninit_array();
let formatted = flt2dec::to_shortest_exp_str(
flt2dec::strategy::grisu::format_shortest,
*num,
Expand Down
23 changes: 13 additions & 10 deletions library/core/src/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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) })
Expand All @@ -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() {
Expand All @@ -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() {
Expand All @@ -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)?;
}
}
Expand Down
15 changes: 15 additions & 0 deletions library/core/src/fmt/nofloat.rs
Original file line number Diff line number Diff line change
@@ -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 }
10 changes: 5 additions & 5 deletions library/core/src/fmt/num.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
"-"
Expand All @@ -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)
}

Expand Down
102 changes: 1 addition & 101 deletions library/core/src/num/flt2dec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -170,107 +171,6 @@ pub fn round_up(d: &mut [u8]) -> Option<u8> {
}
}

/// 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<usize> {
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<usize> {
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.
Expand Down
108 changes: 108 additions & 0 deletions library/core/src/num/fmt.rs
Original file line number Diff line number Diff line change
@@ -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<usize> {
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<usize> {
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)
}
}
Loading