diff --git a/build.rs b/build.rs index b1ea2f1..7b639e7 100644 --- a/build.rs +++ b/build.rs @@ -28,6 +28,12 @@ fn main() { if minor >= 27 { println!("cargo:rustc-cfg=must_use_return"); } + + // MaybeUninit stabilized in Rust 1.36: + // https://blog.rust-lang.org/2019/07/04/Rust-1.36.0.html + if minor >= 36 { + println!("cargo:rustc-cfg=maybe_uninit"); + } } fn rustc_minor_version() -> Option { diff --git a/src/buffer/mod.rs b/src/buffer/mod.rs index 5731aea..fee80be 100644 --- a/src/buffer/mod.rs +++ b/src/buffer/mod.rs @@ -1,5 +1,8 @@ use core::{mem, slice, str}; +#[cfg(maybe_uninit)] +use core::mem::MaybeUninit; + use raw; #[cfg(feature = "no-panic")] @@ -20,6 +23,9 @@ const NEG_INFINITY: &'static str = "-inf"; /// ``` #[derive(Copy, Clone)] pub struct Buffer { + #[cfg(maybe_uninit)] + bytes: [MaybeUninit; 24], + #[cfg(not(maybe_uninit))] bytes: [u8; 24], } @@ -29,8 +35,15 @@ impl Buffer { #[inline] #[cfg_attr(feature = "no-panic", no_panic)] pub fn new() -> Self { + // assume_init is safe here, since this is an array of MaybeUninit, which does not need + // to be initialized. + #[cfg(maybe_uninit)] + let bytes = unsafe { MaybeUninit::uninit().assume_init() }; + #[cfg(not(maybe_uninit))] + let bytes = unsafe { mem::uninitialized() }; + Buffer { - bytes: unsafe { mem::uninitialized() }, + bytes: bytes, } } @@ -74,12 +87,36 @@ impl Buffer { #[cfg_attr(feature = "no-panic", no_panic)] pub fn format_finite(&mut self, f: F) -> &str { unsafe { - let n = f.write_to_ryu_buffer(&mut self.bytes[0]); + let n = f.write_to_ryu_buffer(self.first_byte_pointer_mut()); debug_assert!(n <= self.bytes.len()); - let slice = slice::from_raw_parts(&self.bytes[0], n); + let slice = slice::from_raw_parts(self.first_byte_pointer(), n); str::from_utf8_unchecked(slice) } } + + #[inline] + #[cfg(maybe_uninit)] + fn first_byte_pointer(&self) -> *const u8 { + self.bytes[0].as_ptr() + } + + #[inline] + #[cfg(not(maybe_uninit))] + fn first_byte_pointer(&self) -> *const u8 { + &self.bytes[0] as *const u8 + } + + #[inline] + #[cfg(maybe_uninit)] + fn first_byte_pointer_mut(&mut self) -> *mut u8 { + self.bytes[0].as_mut_ptr() + } + + #[inline] + #[cfg(not(maybe_uninit))] + fn first_byte_pointer_mut(&mut self) -> *mut u8 { + &mut self.bytes[0] as *mut u8 + } } impl Default for Buffer { diff --git a/src/d2s.rs b/src/d2s.rs index 46fe54b..375e3cd 100644 --- a/src/d2s.rs +++ b/src/d2s.rs @@ -18,6 +18,12 @@ // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. +use core::ptr; + +#[cfg(maybe_uninit)] +use core::mem::MaybeUninit; + +#[cfg(not(maybe_uninit))] use core::mem; use common::*; @@ -48,12 +54,14 @@ fn mul_shift_all( m: u64, mul: &(u64, u64), j: u32, - vp: &mut u64, - vm: &mut u64, + vp: *mut u64, + vm: *mut u64, mm_shift: u32, ) -> u64 { - *vp = mul_shift(4 * m + 2, mul, j); - *vm = mul_shift(4 * m - 1 - mm_shift as u64, mul, j); + unsafe { + ptr::write(vp, mul_shift(4 * m + 2, mul, j)); + ptr::write(vm, mul_shift(4 * m - 1 - mm_shift as u64, mul, j)); + } mul_shift(4 * m, mul, j) } @@ -177,8 +185,17 @@ pub fn d2d(ieee_mantissa: u64, ieee_exponent: u32) -> FloatingDecimal64 { // Step 3: Convert to a decimal power base using 128-bit arithmetic. let mut vr: u64; - let mut vp: u64 = unsafe { mem::uninitialized() }; - let mut vm: u64 = unsafe { mem::uninitialized() }; + let mut vp: u64; + let mut vm: u64; + #[cfg(not(maybe_uninit))] + { + vp = unsafe { mem::uninitialized() }; + vm = unsafe { mem::uninitialized() }; + } + #[cfg(maybe_uninit)] + let mut vp_uninit: MaybeUninit = MaybeUninit::uninit(); + #[cfg(maybe_uninit)] + let mut vm_uninit: MaybeUninit = MaybeUninit::uninit(); let e10: i32; let mut vm_is_trailing_zeros = false; let mut vr_is_trailing_zeros = false; @@ -201,10 +218,21 @@ pub fn d2d(ieee_mantissa: u64, ieee_exponent: u32) -> FloatingDecimal64 { DOUBLE_POW5_INV_SPLIT.get_unchecked(q as usize) }, i as u32, - &mut vp, - &mut vm, + #[cfg(maybe_uninit)] + { vp_uninit.as_mut_ptr() }, + #[cfg(not(maybe_uninit))] + { &mut vp }, + #[cfg(maybe_uninit)] + { vm_uninit.as_mut_ptr() }, + #[cfg(not(maybe_uninit))] + { &mut vm }, mm_shift, ); + #[cfg(maybe_uninit)] + { + vp = unsafe { vp_uninit.assume_init() }; + vm = unsafe { vm_uninit.assume_init() }; + } if q <= 21 { // This should use q <= 22, but I think 21 is also safe. Smaller values // may still be safe, but it's more difficult to reason about them. @@ -241,10 +269,21 @@ pub fn d2d(ieee_mantissa: u64, ieee_exponent: u32) -> FloatingDecimal64 { DOUBLE_POW5_SPLIT.get_unchecked(i as usize) }, j as u32, - &mut vp, - &mut vm, + #[cfg(maybe_uninit)] + { vp_uninit.as_mut_ptr() }, + #[cfg(not(maybe_uninit))] + { &mut vp }, + #[cfg(maybe_uninit)] + { vm_uninit.as_mut_ptr() }, + #[cfg(not(maybe_uninit))] + { &mut vm }, mm_shift, ); + #[cfg(maybe_uninit)] + { + vp = unsafe { vp_uninit.assume_init() }; + vm = unsafe { vm_uninit.assume_init() }; + } if q <= 1 { // {vr,vp,vm} is trailing zeros if {mv,mp,mm} has at least q trailing 0 bits. // mv = 4 * m2, so it always has at least two trailing 0 bits. diff --git a/src/pretty/mod.rs b/src/pretty/mod.rs index 9184893..3dead1b 100644 --- a/src/pretty/mod.rs +++ b/src/pretty/mod.rs @@ -38,12 +38,14 @@ use no_panic::no_panic; /// ## Example /// /// ```edition2018 +/// use std::mem::MaybeUninit; +/// /// let f = 1.234f64; /// /// unsafe { -/// let mut buffer: [u8; 24] = std::mem::uninitialized(); -/// let len = ryu::raw::format64(f, buffer.as_mut_ptr()); -/// let slice = std::slice::from_raw_parts(buffer.as_ptr(), len); +/// let mut buffer: [MaybeUninit; 24] = MaybeUninit::uninit().assume_init(); +/// let len = ryu::raw::format64(f, buffer.as_mut_ptr() as *mut u8); +/// let slice = std::slice::from_raw_parts(buffer.as_ptr() as *const u8, len); /// let print = std::str::from_utf8_unchecked(slice); /// assert_eq!(print, "1.234"); /// } @@ -143,12 +145,14 @@ pub unsafe fn format64(f: f64, result: *mut u8) -> usize { /// ## Example /// /// ```edition2018 +/// use std::mem::MaybeUninit; +/// /// let f = 1.234f32; /// /// unsafe { -/// let mut buffer: [u8; 16] = std::mem::uninitialized(); -/// let len = ryu::raw::format32(f, buffer.as_mut_ptr()); -/// let slice = std::slice::from_raw_parts(buffer.as_ptr(), len); +/// let mut buffer: [MaybeUninit; 16] = MaybeUninit::uninit().assume_init(); +/// let len = ryu::raw::format32(f, buffer.as_mut_ptr() as *mut u8); +/// let slice = std::slice::from_raw_parts(buffer.as_ptr() as *const u8, len); /// let print = std::str::from_utf8_unchecked(slice); /// assert_eq!(print, "1.234"); /// }