1use crate::pretty::to_fixed::MAX_BUFFER_SIZE;
2
3use crate::raw;
4use core::mem::MaybeUninit;
5use core::{slice, str};
6#[cfg(feature = "no-panic")]
7use no_panic::no_panic;
8
9const NAN: &str = "NaN";
10const INFINITY: &str = "Infinity";
11const NEG_INFINITY: &str = "-Infinity";
12
13const BUFFER_SIZE: usize = MAX_BUFFER_SIZE;
14
15#[derive(Copy)]
25pub struct Buffer {
26 bytes: [MaybeUninit<u8>; BUFFER_SIZE],
27}
28
29impl Buffer {
30 #[inline]
33 #[cfg_attr(feature = "no-panic", no_panic)]
34 pub fn new() -> Self {
35 let bytes = [MaybeUninit::<u8>::uninit(); BUFFER_SIZE];
36
37 Buffer { bytes }
38 }
39
40 #[inline]
54 #[cfg_attr(feature = "no-panic", no_panic)]
55 pub fn format<F: Float>(&mut self, f: F) -> &str {
56 if f.is_nonfinite() {
57 f.format_nonfinite()
58 } else {
59 self.format_finite(f)
60 }
61 }
62
63 #[inline]
79 #[cfg_attr(feature = "no-panic", no_panic)]
80 pub fn format_finite<F: Float>(&mut self, f: F) -> &str {
81 unsafe {
82 let n = f.write_to_ryu_buffer(self.bytes.as_mut_ptr().cast::<u8>());
83 debug_assert!(n <= self.bytes.len());
84 let slice = slice::from_raw_parts(self.bytes.as_ptr().cast::<u8>(), n);
85 str::from_utf8_unchecked(slice)
86 }
87 }
88
89 #[inline]
102 #[cfg_attr(feature = "no-panic", no_panic)]
103 pub fn format_to_fixed<F: FloatToFixed>(&mut self, f: F, fraction_digits: u8) -> &str {
104 let fraction_digits = fraction_digits.min(100);
105
106 if f.is_nonfinite() {
107 return f.format_nonfinite();
108 }
109
110 unsafe {
111 let n = f.write_to_ryu_buffer_to_fixed(
112 fraction_digits,
113 self.bytes.as_mut_ptr().cast::<u8>(),
114 );
115 debug_assert!(n <= self.bytes.len());
116 let slice = slice::from_raw_parts(self.bytes.as_ptr().cast::<u8>(), n);
117 str::from_utf8_unchecked(slice)
118 }
119 }
120}
121
122impl Clone for Buffer {
123 #[inline]
124 #[allow(clippy::non_canonical_clone_impl)] fn clone(&self) -> Self {
126 Buffer::new()
127 }
128}
129
130impl Default for Buffer {
131 #[inline]
132 #[cfg_attr(feature = "no-panic", no_panic)]
133 fn default() -> Self {
134 Buffer::new()
135 }
136}
137
138pub trait Float: Sealed {}
144impl Float for f32 {}
145impl Float for f64 {}
146
147pub trait FloatToFixed: Sealed {}
156impl FloatToFixed for f64 {}
157
158pub trait Sealed: Copy {
159 fn is_nonfinite(self) -> bool;
160 fn format_nonfinite(self) -> &'static str;
161 unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize;
162 unsafe fn write_to_ryu_buffer_to_fixed(self, fraction_digits: u8, result: *mut u8) -> usize;
163}
164
165impl Sealed for f32 {
166 #[inline]
167 fn is_nonfinite(self) -> bool {
168 const EXP_MASK: u32 = 0x7f800000;
169 let bits = self.to_bits();
170 bits & EXP_MASK == EXP_MASK
171 }
172
173 #[cold]
174 #[cfg_attr(feature = "no-panic", inline)]
175 fn format_nonfinite(self) -> &'static str {
176 const MANTISSA_MASK: u32 = 0x007fffff;
177 const SIGN_MASK: u32 = 0x80000000;
178 let bits = self.to_bits();
179 if bits & MANTISSA_MASK != 0 {
180 NAN
181 } else if bits & SIGN_MASK != 0 {
182 NEG_INFINITY
183 } else {
184 INFINITY
185 }
186 }
187
188 #[inline]
189 unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize {
190 raw::format32(self, result)
191 }
192
193 #[inline]
194 unsafe fn write_to_ryu_buffer_to_fixed(self, _fraction_digits: u8, _result: *mut u8) -> usize {
195 panic!("toFixed for f32 type is not implemented yet!")
196 }
197}
198
199impl Sealed for f64 {
200 #[inline]
201 fn is_nonfinite(self) -> bool {
202 const EXP_MASK: u64 = 0x7ff0000000000000;
203 let bits = self.to_bits();
204 bits & EXP_MASK == EXP_MASK
205 }
206
207 #[cold]
208 #[cfg_attr(feature = "no-panic", inline)]
209 fn format_nonfinite(self) -> &'static str {
210 const MANTISSA_MASK: u64 = 0x000fffffffffffff;
211 const SIGN_MASK: u64 = 0x8000000000000000;
212 let bits = self.to_bits();
213 if bits & MANTISSA_MASK != 0 {
214 NAN
215 } else if bits & SIGN_MASK != 0 {
216 NEG_INFINITY
217 } else {
218 INFINITY
219 }
220 }
221
222 #[inline]
223 unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize {
224 raw::format64(self, result)
225 }
226
227 #[inline]
228 unsafe fn write_to_ryu_buffer_to_fixed(self, fraction_digits: u8, result: *mut u8) -> usize {
229 raw::format64_to_fixed(self, fraction_digits, result)
230 }
231}