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)] |
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 | |
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 |
87 | 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)] |
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. |
105 | pub trait Float: Sealed {} |
106 | impl Float for f32 {} |
107 | impl Float for f64 {} |
108 | |
109 | pub 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 | |
115 | impl 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 | |
144 | impl 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 | |