| 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) | |
| } | |
| } |