1 use crate::raw;
2 use core::mem::MaybeUninit;
3 use core::{slice, str};
4 #[cfg(feature = "no-panic")]
5 use no_panic::no_panic;
6 
7 const NAN: &str = "NaN";
8 const INFINITY: &str = "inf";
9 const 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 /// ```
20 pub struct Buffer {
21     bytes: [MaybeUninit<u8>; 24],
22 }
23 
24 impl 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)]
new() -> Self29     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)]
format<F: Float>(&mut self, f: F) -> &str47     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`]: https://doc.rust-lang.org/std/primitive.f64.html#method.is_finite
68     /// [`is_nan`]: https://doc.rust-lang.org/std/primitive.f64.html#method.is_nan
69     /// [`is_infinite`]: https://doc.rust-lang.org/std/primitive.f64.html#method.is_infinite
70     #[inline]
71     #[cfg_attr(feature = "no-panic", no_panic)]
format_finite<F: Float>(&mut self, f: F) -> &str72     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 
82 impl Copy for Buffer {}
83 
84 impl Clone for Buffer {
85     #[inline]
86     #[allow(clippy::non_canonical_clone_impl)] // false positive https://github.com/rust-lang/rust-clippy/issues/11072
clone(&self) -> Self87     fn clone(&self) -> Self {
88         Buffer::new()
89     }
90 }
91 
92 impl Default for Buffer {
93     #[inline]
94     #[cfg_attr(feature = "no-panic", no_panic)]
default() -> Self95     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.
105 pub trait Float: Sealed {}
106 impl Float for f32 {}
107 impl Float for f64 {}
108 
109 pub trait Sealed: Copy {
is_nonfinite(self) -> bool110     fn is_nonfinite(self) -> bool;
format_nonfinite(self) -> &'static str111     fn format_nonfinite(self) -> &'static str;
write_to_ryu_buffer(self, result: *mut u8) -> usize112     unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize;
113 }
114 
115 impl Sealed for f32 {
116     #[inline]
is_nonfinite(self) -> bool117     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)]
format_nonfinite(self) -> &'static str125     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]
write_to_ryu_buffer(self, result: *mut u8) -> usize139     unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize {
140         raw::format32(self, result)
141     }
142 }
143 
144 impl Sealed for f64 {
145     #[inline]
is_nonfinite(self) -> bool146     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)]
format_nonfinite(self) -> &'static str154     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]
write_to_ryu_buffer(self, result: *mut u8) -> usize168     unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize {
169         raw::format64(self, result)
170     }
171 }
172