diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/ryu/src/buffer/mod.rs | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/ryu/src/buffer/mod.rs')
-rw-r--r-- | third_party/rust/ryu/src/buffer/mod.rs | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/third_party/rust/ryu/src/buffer/mod.rs b/third_party/rust/ryu/src/buffer/mod.rs new file mode 100644 index 0000000000..881858ce71 --- /dev/null +++ b/third_party/rust/ryu/src/buffer/mod.rs @@ -0,0 +1,199 @@ +use core::{mem, slice, str}; + +#[cfg(maybe_uninit)] +use core::mem::MaybeUninit; + +use raw; + +#[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 +/// +/// ```edition2018 +/// let mut buffer = ryu::Buffer::new(); +/// let printed = buffer.format_finite(1.234); +/// assert_eq!(printed, "1.234"); +/// ``` +#[derive(Copy, Clone)] +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 = unsafe { MaybeUninit::uninit().assume_init() }; + #[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.first_byte_pointer_mut()); + debug_assert!(n <= self.bytes.len()); + 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 { + #[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) + } +} |