Skip to content

Commit

Permalink
Auto merge of #86048 - nbdd0121:no_floating_point, r=Amanieu
Browse files Browse the repository at this point in the history
core: add unstable no_fp_fmt_parse to disable float formatting code

In some projects (e.g. kernel), floating point is forbidden. They can disable
hardware floating point support and use `+soft-float` to avoid fp instructions
from being generated, but as libcore contains the formatting code for `f32`
and `f64`, some fp intrinsics are depended. One could define stubs for these
intrinsics that just panic [1], but it means that if any formatting functions
are accidentally used, mistake can only be caught during the runtime rather
than during compile-time or link-time, and they consume a lot of space without
LTO.

This patch provides an unstable cfg `no_fp_fmt_parse` to disable these.
A panicking stub is still provided for the `Debug` implementation (unfortunately)
because there are some SIMD types that use `#[derive(Debug)]`.

[1]: https://lkml.org/lkml/2021/4/14/1028
  • Loading branch information
bors committed Jul 4, 2021
2 parents 308fc23 + ec7292a commit 9044245
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 121 deletions.
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 @@ -1423,7 +1426,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 @@ -1463,14 +1466,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 @@ -1481,7 +1484,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 @@ -1492,7 +1495,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 @@ -1501,7 +1504,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

0 comments on commit 9044245

Please sign in to comment.