ryu/buffer/
mod.rs

1use crate::raw;
2use core::mem::MaybeUninit;
3use core::{slice, str};
4#[cfg(feature = "no-panic")]
5use no_panic::no_panic;
6
7const NAN: &str = "NaN";
8const INFINITY: &str = "inf";
9const NEG_INFINITY: &str = "-inf";
10
11/// Safe API for formatting floating point numbers to text.
12///
13/// ## Example
14///
15/// ```
16/// let mut buffer = ryu::Buffer::new();
17/// let printed = buffer.format_finite(1.234);
18/// assert_eq!(printed, "1.234");
19/// ```
20pub struct Buffer {
21    bytes: [MaybeUninit<u8>; 24],
22}
23
24impl Buffer {
25    /// This is a cheap operation; you don't need to worry about reusing buffers
26    /// for efficiency.
27    #[inline]
28    #[cfg_attr(feature = "no-panic", no_panic)]
29    pub fn new() -> Self {
30        let bytes = [MaybeUninit::<u8>::uninit(); 24];
31        Buffer { bytes }
32    }
33
34    /// Print a floating point number into this buffer and return a reference to
35    /// its string representation within the buffer.
36    ///
37    /// # Special cases
38    ///
39    /// This function formats NaN as the string "NaN", positive infinity as
40    /// "inf", and negative infinity as "-inf" to match std::fmt.
41    ///
42    /// If your input is known to be finite, you may get better performance by
43    /// calling the `format_finite` method instead of `format` to avoid the
44    /// checks for special cases.
45    #[cfg_attr(feature = "no-panic", inline)]
46    #[cfg_attr(feature = "no-panic", no_panic)]
47    pub fn format<F: Float>(&mut self, f: F) -> &str {
48        if f.is_nonfinite() {
49            f.format_nonfinite()
50        } else {
51            self.format_finite(f)
52        }
53    }
54
55    /// Print a floating point number into this buffer and return a reference to
56    /// its string representation within the buffer.
57    ///
58    /// # Special cases
59    ///
60    /// This function **does not** check for NaN or infinity. If the input
61    /// number is not a finite float, the printed representation will be some
62    /// correctly formatted but unspecified numerical value.
63    ///
64    /// Please check [`is_finite`] yourself before calling this function, or
65    /// check [`is_nan`] and [`is_infinite`] and handle those cases yourself.
66    ///
67    /// [`is_finite`]: f64::is_finite
68    /// [`is_nan`]: f64::is_nan
69    /// [`is_infinite`]: f64::is_infinite
70    #[inline]
71    #[cfg_attr(feature = "no-panic", no_panic)]
72    pub fn format_finite<F: Float>(&mut self, f: F) -> &str {
73        unsafe {
74            let n = f.write_to_ryu_buffer(self.bytes.as_mut_ptr() as *mut u8);
75            debug_assert!(n <= self.bytes.len());
76            let slice = slice::from_raw_parts(self.bytes.as_ptr() as *const u8, n);
77            str::from_utf8_unchecked(slice)
78        }
79    }
80}
81
82impl Copy for Buffer {}
83
84impl Clone for Buffer {
85    #[inline]
86    #[allow(clippy::non_canonical_clone_impl)] // false positive https://github.com/rust-lang/rust-clippy/issues/11072
87    fn clone(&self) -> Self {
88        Buffer::new()
89    }
90}
91
92impl Default for Buffer {
93    #[inline]
94    #[cfg_attr(feature = "no-panic", no_panic)]
95    fn default() -> Self {
96        Buffer::new()
97    }
98}
99
100/// A floating point number, f32 or f64, that can be written into a
101/// [`ryu::Buffer`][Buffer].
102///
103/// This trait is sealed and cannot be implemented for types outside of the
104/// `ryu` crate.
105pub trait Float: Sealed {}
106impl Float for f32 {}
107impl Float for f64 {}
108
109pub trait Sealed: Copy {
110    fn is_nonfinite(self) -> bool;
111    fn format_nonfinite(self) -> &'static str;
112    unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize;
113}
114
115impl Sealed for f32 {
116    #[inline]
117    fn is_nonfinite(self) -> bool {
118        const EXP_MASK: u32 = 0x7f800000;
119        let bits = self.to_bits();
120        bits & EXP_MASK == EXP_MASK
121    }
122
123    #[cold]
124    #[cfg_attr(feature = "no-panic", inline)]
125    fn format_nonfinite(self) -> &'static str {
126        const MANTISSA_MASK: u32 = 0x007fffff;
127        const SIGN_MASK: u32 = 0x80000000;
128        let bits = self.to_bits();
129        if bits & MANTISSA_MASK != 0 {
130            NAN
131        } else if bits & SIGN_MASK != 0 {
132            NEG_INFINITY
133        } else {
134            INFINITY
135        }
136    }
137
138    #[inline]
139    unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize {
140        raw::format32(self, result)
141    }
142}
143
144impl Sealed for f64 {
145    #[inline]
146    fn is_nonfinite(self) -> bool {
147        const EXP_MASK: u64 = 0x7ff0000000000000;
148        let bits = self.to_bits();
149        bits & EXP_MASK == EXP_MASK
150    }
151
152    #[cold]
153    #[cfg_attr(feature = "no-panic", inline)]
154    fn format_nonfinite(self) -> &'static str {
155        const MANTISSA_MASK: u64 = 0x000fffffffffffff;
156        const SIGN_MASK: u64 = 0x8000000000000000;
157        let bits = self.to_bits();
158        if bits & MANTISSA_MASK != 0 {
159            NAN
160        } else if bits & SIGN_MASK != 0 {
161            NEG_INFINITY
162        } else {
163            INFINITY
164        }
165    }
166
167    #[inline]
168    unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize {
169        raw::format64(self, result)
170    }
171}