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 | fn clone(&self) -> Self { |
87 | Buffer::new() |
88 | } |
89 | } |
90 | |
91 | impl Default for Buffer { |
92 | #[inline ] |
93 | #[cfg_attr (feature = "no-panic" , no_panic)] |
94 | fn default() -> Self { |
95 | Buffer::new() |
96 | } |
97 | } |
98 | |
99 | /// A floating point number, f32 or f64, that can be written into a |
100 | /// [`ryu::Buffer`][Buffer]. |
101 | /// |
102 | /// This trait is sealed and cannot be implemented for types outside of the |
103 | /// `ryu` crate. |
104 | pub trait Float: Sealed {} |
105 | impl Float for f32 {} |
106 | impl Float for f64 {} |
107 | |
108 | pub trait Sealed: Copy { |
109 | fn is_nonfinite(self) -> bool; |
110 | fn format_nonfinite(self) -> &'static str; |
111 | unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize; |
112 | } |
113 | |
114 | impl Sealed for f32 { |
115 | #[inline ] |
116 | fn is_nonfinite(self) -> bool { |
117 | const EXP_MASK: u32 = 0x7f800000; |
118 | let bits = self.to_bits(); |
119 | bits & EXP_MASK == EXP_MASK |
120 | } |
121 | |
122 | #[cold ] |
123 | #[cfg_attr (feature = "no-panic" , inline)] |
124 | fn format_nonfinite(self) -> &'static str { |
125 | const MANTISSA_MASK: u32 = 0x007fffff; |
126 | const SIGN_MASK: u32 = 0x80000000; |
127 | let bits = self.to_bits(); |
128 | if bits & MANTISSA_MASK != 0 { |
129 | NAN |
130 | } else if bits & SIGN_MASK != 0 { |
131 | NEG_INFINITY |
132 | } else { |
133 | INFINITY |
134 | } |
135 | } |
136 | |
137 | #[inline ] |
138 | unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize { |
139 | raw::format32(self, result) |
140 | } |
141 | } |
142 | |
143 | impl Sealed for f64 { |
144 | #[inline ] |
145 | fn is_nonfinite(self) -> bool { |
146 | const EXP_MASK: u64 = 0x7ff0000000000000; |
147 | let bits = self.to_bits(); |
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: u64 = 0x000fffffffffffff; |
155 | const SIGN_MASK: u64 = 0x8000000000000000; |
156 | let bits = self.to_bits(); |
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::format64(self, result) |
169 | } |
170 | } |
171 | |