| use crate::raw; |
| #[cfg(maybe_uninit)] |
| use core::mem::MaybeUninit; |
| use core::{mem, slice, str}; |
| #[cfg(feature = "no-panic")] |
| use no_panic::no_panic; |
| |
| const NAN: &'static str = "NaN"; |
| const INFINITY: &'static str = "inf"; |
| const NEG_INFINITY: &'static 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 { |
| #[cfg(maybe_uninit)] |
| bytes: [MaybeUninit<u8>; 24], |
| #[cfg(not(maybe_uninit))] |
| bytes: [u8; 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 { |
| // assume_init is safe here, since this is an array of MaybeUninit, which does not need |
| // to be initialized. |
| #[cfg(maybe_uninit)] |
| let bytes = [MaybeUninit::<u8>::uninit(); 24]; |
| #[cfg(not(maybe_uninit))] |
| let bytes = unsafe { mem::uninitialized() }; |
| |
| Buffer { bytes: 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<F: Float>(&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<F: Float>(&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] |
| 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 = unsafe { mem::transmute::<f32, u32>(self) }; |
| 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 = unsafe { mem::transmute::<f32, u32>(self) }; |
| 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 = unsafe { mem::transmute::<f64, u64>(self) }; |
| 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 = unsafe { mem::transmute::<f64, u64>(self) }; |
| 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) |
| } |
| } |