Skip to main content

ryu/buffer/
mod.rs

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