Skip to content

Commit

Permalink
rust-lang#66219 documented unsafe in core::fmt
Browse files Browse the repository at this point in the history
  • Loading branch information
foeb committed Nov 10, 2019
1 parent d78c056 commit d74823a
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 30 deletions.
6 changes: 4 additions & 2 deletions src/libcore/fmt/float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ use crate::fmt::{Formatter, Result, LowerExp, UpperExp, Display, Debug};
use crate::mem::MaybeUninit;
use crate::num::flt2dec;

// ignore-tidy-undocumented-unsafe

// Don't inline this so callers don't use the stack space this function
// requires unless they have to.
#[inline(never)]
fn float_to_decimal_common_exact<T>(fmt: &mut Formatter<'_>, num: &T,
sign: flt2dec::Sign, precision: usize) -> Result
where T: flt2dec::DecodableFloat
{
// SAFETY: possible undefined behavior, see comment
unsafe {
let mut buf = MaybeUninit::<[u8; 1024]>::uninit(); // enough for f32 and f64
let mut parts = MaybeUninit::<[flt2dec::Part<'_>; 4]>::uninit();
Expand All @@ -33,6 +32,7 @@ fn float_to_decimal_common_shortest<T>(fmt: &mut Formatter<'_>, num: &T,
sign: flt2dec::Sign, precision: usize) -> Result
where T: flt2dec::DecodableFloat
{
// SAFETY: possible undefined behavior, see comment
unsafe {
// enough for f32 and f64
let mut buf = MaybeUninit::<[u8; flt2dec::MAX_SIG_DIGITS]>::uninit();
Expand Down Expand Up @@ -73,6 +73,7 @@ fn float_to_exponential_common_exact<T>(fmt: &mut Formatter<'_>, num: &T,
upper: bool) -> Result
where T: flt2dec::DecodableFloat
{
// SAFETY: possible undefined behavior, see comment
unsafe {
let mut buf = MaybeUninit::<[u8; 1024]>::uninit(); // enough for f32 and f64
let mut parts = MaybeUninit::<[flt2dec::Part<'_>; 6]>::uninit();
Expand All @@ -92,6 +93,7 @@ fn float_to_exponential_common_shortest<T>(fmt: &mut Formatter<'_>,
upper: bool) -> Result
where T: flt2dec::DecodableFloat
{
// SAFETY: possible undefined behavior, see comment
unsafe {
// enough for f32 and f64
let mut buf = MaybeUninit::<[u8; flt2dec::MAX_SIG_DIGITS]>::uninit();
Expand Down
8 changes: 6 additions & 2 deletions src/libcore/fmt/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
//! Utilities for formatting and printing strings.
// ignore-tidy-undocumented-unsafe

#![stable(feature = "rust1", since = "1.0.0")]

use crate::cell::{UnsafeCell, Cell, RefCell, Ref, RefMut};
Expand Down Expand Up @@ -279,6 +277,7 @@ impl<'a> ArgumentV1<'a> {
issue = "0")]
pub fn new<'b, T>(x: &'b T,
f: fn(&T, &mut Formatter<'_>) -> Result) -> ArgumentV1<'b> {
// SAFETY: relies on T being sized to avoid undefined behavior
unsafe {
ArgumentV1 {
formatter: mem::transmute(f),
Expand All @@ -296,6 +295,7 @@ impl<'a> ArgumentV1<'a> {

fn as_usize(&self) -> Option<usize> {
if self.formatter as usize == ArgumentV1::show_usize as usize {
// SAFETY: if the formatter is show_usize, it means it came in as &usize
Some(unsafe { *(self.value as *const _ as *const usize) })
} else {
None
Expand Down Expand Up @@ -1355,6 +1355,8 @@ impl<'a> Formatter<'a> {
let mut align = old_align;
if self.sign_aware_zero_pad() {
// a sign always goes first
// SAFETY: formatted.sign is always generated from determine_sign which is
// valid utf8
let sign = unsafe { str::from_utf8_unchecked(formatted.sign) };
self.buf.write_str(sign)?;

Expand Down Expand Up @@ -1386,6 +1388,8 @@ impl<'a> Formatter<'a> {

fn write_formatted_parts(&mut self, formatted: &flt2dec::Formatted<'_>) -> Result {
fn write_bytes(buf: &mut dyn Write, s: &[u8]) -> Result {
// SAFETY: formatted.sign is always generated from determine_sign which is
// valid utf8
buf.write_str(unsafe { str::from_utf8_unchecked(s) })
}

Expand Down
63 changes: 37 additions & 26 deletions src/libcore/fmt/num.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
//! Integer and floating-point number formatting
// ignore-tidy-undocumented-unsafe


use crate::fmt;
use crate::ops::{Div, Rem, Sub};
use crate::str;
Expand Down Expand Up @@ -83,6 +80,7 @@ trait GenericRadix {
}
}
let buf = &buf[curr..];
// SAFETY: only chars in buf are created by Self::digit which are asuumed to be valid utf8
let buf = unsafe { str::from_utf8_unchecked(slice::from_raw_parts(
MaybeUninit::first_ptr(buf),
buf.len()
Expand Down Expand Up @@ -191,49 +189,62 @@ static DEC_DIGITS_LUT: &[u8; 200] =
macro_rules! impl_Display {
($($t:ident),* as $u:ident via $conv_fn:ident named $name:ident) => {
fn $name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// 2^128 is is about 3*10^38, so 39 gives an extra byte of space
let mut buf = [MaybeUninit::<u8>::uninit(); 39];
let mut curr = buf.len() as isize;
let buf_ptr = MaybeUninit::first_ptr_mut(&mut buf);
let lut_ptr = DEC_DIGITS_LUT.as_ptr();

unsafe {
// need at least 16 bits for the 4-characters-at-a-time to work.
assert!(crate::mem::size_of::<$u>() >= 2);
// need at least 16 bits for the 4-characters-at-a-time to work.
assert!(crate::mem::size_of::<$u>() >= 2);

// eagerly decode 4 characters at a time
while n >= 10000 {
let rem = (n % 10000) as isize;
n /= 10000;
// eagerly decode 4 characters at a time
while n >= 10000 {
let rem = (n % 10000) as isize;
n /= 10000;

let d1 = (rem / 100) << 1;
let d2 = (rem % 100) << 1;
curr -= 4;
let d1 = (rem / 100) << 1;
let d2 = (rem % 100) << 1;
curr -= 4;
// SAFETY: d1, d2 are each max 198, so buf_ptr[d1..d1 + 1] is safe to access
unsafe {
ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
ptr::copy_nonoverlapping(lut_ptr.offset(d2), buf_ptr.offset(curr + 2), 2);
}
}

// if we reach here numbers are <= 9999, so at most 4 chars long
let mut n = n as isize; // possibly reduce 64bit math
// if we reach here numbers are <= 9999, so at most 4 chars long
let mut n = n as isize; // possibly reduce 64bit math

// decode 2 more chars, if > 2 chars
if n >= 100 {
let d1 = (n % 100) << 1;
n /= 100;
curr -= 2;
// decode 2 more chars, if > 2 chars
if n >= 100 {
let d1 = (n % 100) << 1;
n /= 100;
curr -= 2;
// SAFETY: d1 is max 198, so buf_ptr[d1..d1 + 1] is safe to access
unsafe {
ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
}
}

// decode last 1 or 2 chars
if n < 10 {
curr -= 1;
// decode last 1 or 2 chars
if n < 10 {
curr -= 1;
// SAFETY: curr is still less than buf.len() and since n < 10, n + '0' is valid utf8
unsafe {
*buf_ptr.offset(curr) = (n as u8) + b'0';
} else {
let d1 = n << 1;
curr -= 2;
}
} else {
let d1 = n << 1;
curr -= 2;
// SAFETY: d1 is max 18, so buf_ptr[d1..d1 + 1] is safe to access
unsafe {
ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
}
}

// SAFETY: curr > 0 (since we made buf large enough), and all the chars are valid utf8
// since DEC_DIGITS_LUT is
let buf_slice = unsafe {
str::from_utf8_unchecked(
slice::from_raw_parts(buf_ptr.offset(curr), buf.len() - curr as usize))
Expand Down

0 comments on commit d74823a

Please sign in to comment.