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 fn clone(&self) -> Self {
87 Buffer::new()
88 }
89}
90
91impl 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.
104pub trait Float: Sealed {}
105impl Float for f32 {}
106impl Float for f64 {}
107
108pub 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
114impl 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
143impl 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