ryu/buffer/
mod.rs

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