use crate::raw; use core::mem::MaybeUninit; use core::{slice, str}; #[cfg(feature = "no-panic")] use no_panic::no_panic; const NAN: &str = "NaN"; const INFINITY: &str = "inf"; const NEG_INFINITY: &str = "-inf"; /// Safe API for formatting floating point numbers to text. /// /// ## Example /// /// ``` /// let mut buffer = ryu::Buffer::new(); /// let printed = buffer.format_finite(1.234); /// assert_eq!(printed, "1.234"); /// ``` pub struct Buffer { bytes: [MaybeUninit; 24], } impl Buffer { /// This is a cheap operation; you don't need to worry about reusing buffers /// for efficiency. #[inline] #[cfg_attr(feature = "no-panic", no_panic)] pub fn new() -> Self { let bytes = [MaybeUninit::::uninit(); 24]; Buffer { bytes } } /// Print a floating point number into this buffer and return a reference to /// its string representation within the buffer. /// /// # Special cases /// /// This function formats NaN as the string "NaN", positive infinity as /// "inf", and negative infinity as "-inf" to match std::fmt. /// /// If your input is known to be finite, you may get better performance by /// calling the `format_finite` method instead of `format` to avoid the /// checks for special cases. #[cfg_attr(feature = "no-panic", inline)] #[cfg_attr(feature = "no-panic", no_panic)] pub fn format(&mut self, f: F) -> &str { if f.is_nonfinite() { f.format_nonfinite() } else { self.format_finite(f) } } /// Print a floating point number into this buffer and return a reference to /// its string representation within the buffer. /// /// # Special cases /// /// This function **does not** check for NaN or infinity. If the input /// number is not a finite float, the printed representation will be some /// correctly formatted but unspecified numerical value. /// /// Please check [`is_finite`] yourself before calling this function, or /// check [`is_nan`] and [`is_infinite`] and handle those cases yourself. /// /// [`is_finite`]: https://doc.rust-lang.org/std/primitive.f64.html#method.is_finite /// [`is_nan`]: https://doc.rust-lang.org/std/primitive.f64.html#method.is_nan /// [`is_infinite`]: https://doc.rust-lang.org/std/primitive.f64.html#method.is_infinite #[inline] #[cfg_attr(feature = "no-panic", no_panic)] pub fn format_finite(&mut self, f: F) -> &str { unsafe { let n = f.write_to_ryu_buffer(self.bytes.as_mut_ptr() as *mut u8); debug_assert!(n <= self.bytes.len()); let slice = slice::from_raw_parts(self.bytes.as_ptr() as *const u8, n); str::from_utf8_unchecked(slice) } } } impl Copy for Buffer {} impl Clone for Buffer { #[inline] #[allow(clippy::non_canonical_clone_impl)] // false positive https://github.com/rust-lang/rust-clippy/issues/11072 fn clone(&self) -> Self { Buffer::new() } } impl Default for Buffer { #[inline] #[cfg_attr(feature = "no-panic", no_panic)] fn default() -> Self { Buffer::new() } } /// A floating point number, f32 or f64, that can be written into a /// [`ryu::Buffer`][Buffer]. /// /// This trait is sealed and cannot be implemented for types outside of the /// `ryu` crate. pub trait Float: Sealed {} impl Float for f32 {} impl Float for f64 {} pub trait Sealed: Copy { fn is_nonfinite(self) -> bool; fn format_nonfinite(self) -> &'static str; unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize; } impl Sealed for f32 { #[inline] fn is_nonfinite(self) -> bool { const EXP_MASK: u32 = 0x7f800000; let bits = self.to_bits(); bits & EXP_MASK == EXP_MASK } #[cold] #[cfg_attr(feature = "no-panic", inline)] fn format_nonfinite(self) -> &'static str { const MANTISSA_MASK: u32 = 0x007fffff; const SIGN_MASK: u32 = 0x80000000; let bits = self.to_bits(); if bits & MANTISSA_MASK != 0 { NAN } else if bits & SIGN_MASK != 0 { NEG_INFINITY } else { INFINITY } } #[inline] unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize { raw::format32(self, result) } } impl Sealed for f64 { #[inline] fn is_nonfinite(self) -> bool { const EXP_MASK: u64 = 0x7ff0000000000000; let bits = self.to_bits(); bits & EXP_MASK == EXP_MASK } #[cold] #[cfg_attr(feature = "no-panic", inline)] fn format_nonfinite(self) -> &'static str { const MANTISSA_MASK: u64 = 0x000fffffffffffff; const SIGN_MASK: u64 = 0x8000000000000000; let bits = self.to_bits(); if bits & MANTISSA_MASK != 0 { NAN } else if bits & SIGN_MASK != 0 { NEG_INFINITY } else { INFINITY } } #[inline] unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize { raw::format64(self, result) } }