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`]: 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)]
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}
172