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
11pub struct Buffer {
21 bytes: [MaybeUninit<u8>; 24],
22}
23
24impl Buffer {
25 #[inline]
28 #[cfg_attr(feature = "no-panic", no_panic)]
29 pub const fn new() -> Self {
30 let bytes = [MaybeUninit::<u8>::uninit(); 24];
31 Buffer { bytes }
32 }
33
34 #[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 #[inline]
71 #[cfg_attr(feature = "no-panic", no_panic)]
72 pub fn format_finite<F: Float>(&mut self, f: F) -> &str {
73 #[expect(clippy::ptr_as_ptr)]
74 unsafe {
75 let n = f.write_to_ryu_buffer(self.bytes.as_mut_ptr() as *mut u8);
76 if true {
if !(n <= self.bytes.len()) {
::core::panicking::panic("assertion failed: n <= self.bytes.len()")
};
};debug_assert!(n <= self.bytes.len());
77 let slice = slice::from_raw_parts(self.bytes.as_ptr() as *const u8, n);
78 str::from_utf8_unchecked(slice)
79 }
80 }
81}
82
83impl Copy for Buffer {}
84
85#[allow(clippy::non_canonical_clone_impl)]
86impl Clone for Buffer {
87 #[inline]
88 fn clone(&self) -> Self {
89 Buffer::new()
90 }
91}
92
93impl Default for Buffer {
94 #[inline]
95 #[cfg_attr(feature = "no-panic", no_panic)]
96 fn default() -> Self {
97 Buffer::new()
98 }
99}
100
101pub trait Float: Sealed {}
107impl Float for f32 {}
108impl Float for f64 {}
109
110pub trait Sealed: Copy {
111 fn is_nonfinite(self) -> bool;
112 fn format_nonfinite(self) -> &'static str;
113 unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize;
114}
115
116#[derive(#[automatically_derived]
impl<T: ::core::fmt::Debug> ::core::fmt::Debug for ConstFloat<T> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_tuple_field1_finish(f, "ConstFloat",
&&self.0)
}
}Debug, #[automatically_derived]
impl<T: ::core::clone::Clone> ::core::clone::Clone for ConstFloat<T> {
#[inline]
fn clone(&self) -> ConstFloat<T> {
ConstFloat(::core::clone::Clone::clone(&self.0))
}
}Clone, #[automatically_derived]
impl<T: ::core::marker::Copy> ::core::marker::Copy for ConstFloat<T> { }Copy)]
117struct ConstFloat<T>(T);
118
119impl ConstFloat<f32> {
120 const fn to_bits(self) -> u32 {
121 self.0.to_bits()
122 }
123}
124impl ConstFloat<f64> {
125 const fn to_bits(self) -> u64 {
126 self.0.to_bits()
127 }
128}
129
130macro_rules! Float {
131 ($(
132 impl $Trait:ident for $Ty:ty {$(
133 $(#$attr:tt)*
134 $f1:ident $f2:ident $($f3:ident)? ($this:ident $(, $rest_arg:ident: $RestArgType:ty)* $(,)?) -> $ReturnType:ty
135 $f_body:block
136 )*}
137 )*) => {$(
138 impl ConstFloat<$Ty> {$(
139 #[inline] const $f1 $f2 $($f3)? ($this $(, $rest_arg: $RestArgType)*) -> $ReturnType
141 $f_body
142 )*}
143 impl $Trait for $Ty {$(
144 $(#$attr)* $f1 $f2 $($f3)? ($this $(, $rest_arg: $RestArgType)*) -> $ReturnType {
146 Float!(@call_fn (ConstFloat::<$Ty>($this)) ($f2 $($f3)?) ($($rest_arg),*))
147 }
148 )*}
149 )*};
150 (@call_fn ($this:expr) ($f:ident) $args:tt) => {
151 $this.$f $args
152 };
153 (@call_fn ($this:expr) (fn $f:ident) $args:tt) => {
154 $this.$f $args
155 };
156}
157
158impl ConstFloat<f64> {
#[inline]
const fn is_nonfinite(self) -> bool {
const EXP_MASK: u64 = 0x7ff0000000000000;
let bits = self.to_bits();
bits & EXP_MASK == EXP_MASK
}
#[inline]
const fn format_nonfinite(self) -> &'static str {
const MANTISSA_MASK: u64 = 0x000fffffffffffff;
const SIGN_MASK: u64 = 0x8000000000000000;
let bits = self.to_bits();
if bits & MANTISSA_MASK != 0 {
NAN
} else if bits & SIGN_MASK != 0 { NEG_INFINITY } else { INFINITY }
}
#[inline]
const unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize {
raw::format64(self.0, result)
}
}
impl Sealed for f64 {
#[inline]
fn is_nonfinite(self) -> bool { ConstFloat::<f64>(self).is_nonfinite() }
#[cold]
fn format_nonfinite(self) -> &'static str {
ConstFloat::<f64>(self).format_nonfinite()
}
#[inline]
unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize {
ConstFloat::<f64>(self).write_to_ryu_buffer(result)
}
}Float! {
159impl Sealed for f32 {
160 #[inline]
161 fn is_nonfinite(self) -> bool {
162 const EXP_MASK: u32 = 0x7f800000;
163 let bits = self.to_bits();
164 bits & EXP_MASK == EXP_MASK
165 }
166
167 #[cold]
168 #[cfg_attr(feature = "no-panic", inline)]
169 fn format_nonfinite(self) -> &'static str {
170 const MANTISSA_MASK: u32 = 0x007fffff;
171 const SIGN_MASK: u32 = 0x80000000;
172 let bits = self.to_bits();
173 if bits & MANTISSA_MASK != 0 {
174 NAN
175 } else if bits & SIGN_MASK != 0 {
176 NEG_INFINITY
177 } else {
178 INFINITY
179 }
180 }
181
182 #[inline]
183 unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize {
184 raw::format32(self.0, result)
185 }
186}
187
188impl Sealed for f64 {
189 #[inline]
190 fn is_nonfinite(self) -> bool {
191 const EXP_MASK: u64 = 0x7ff0000000000000;
192 let bits = self.to_bits();
193 bits & EXP_MASK == EXP_MASK
194 }
195
196 #[cold]
197 #[cfg_attr(feature = "no-panic", inline)]
198 fn format_nonfinite(self) -> &'static str {
199 const MANTISSA_MASK: u64 = 0x000fffffffffffff;
200 const SIGN_MASK: u64 = 0x8000000000000000;
201 let bits = self.to_bits();
202 if bits & MANTISSA_MASK != 0 {
203 NAN
204 } else if bits & SIGN_MASK != 0 {
205 NEG_INFINITY
206 } else {
207 INFINITY
208 }
209 }
210
211 #[inline]
212 unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize {
213 raw::format64(self.0, result)
214 }
215}
216}
217
218pub struct Format<'a, T>(pub &'a mut Buffer, pub T);
237
238pub struct FormatFinite<'a, T>(pub &'a mut Buffer, pub T);
254
255macro_rules! impl_fns {
256 (
257 type $T:ident = each_of!$each_of:tt;
258
259 $($rest:tt)*
260 ) => {
261 impl_fns! { @impl $T $each_of {$($rest)*} }
262 };
263 (@impl $T:ident [$($Ty:ty),* $(,)?] $impl_body:tt) => {$(
264 const _: () = {
265 type $T = $Ty;
266 const _: () = $impl_body;
267 };
268 )*};
269}
270
271const _: () =
{
type F = f64;
const _: () =
{
impl<'a> Format<'a, F> {
pub const fn call_once(self) -> &'a str {
let Self(this, f) = self;
let f = ConstFloat(f);
if f.is_nonfinite() {
f.format_nonfinite()
} else { FormatFinite(this, f.0).call_once() }
}
}
impl<'a> FormatFinite<'a, F> {
pub const fn call_once(self) -> &'a str {
let Self(this, f) = self;
let f = ConstFloat(f);
#[expect(clippy::ptr_as_ptr)]
unsafe {
let n =
f.write_to_ryu_buffer(this.bytes.as_mut_ptr() as *mut u8);
if true {
if !(n <= this.bytes.len()) {
::core::panicking::panic("assertion failed: n <= this.bytes.len()")
};
};
let slice =
slice::from_raw_parts(this.bytes.as_ptr() as *const u8, n);
str::from_utf8_unchecked(slice)
}
}
}
};
};impl_fns!(
272 type F = each_of![f32, f64];
273
274 impl<'a> Format<'a, F> {
275 pub const fn call_once(self) -> &'a str {
276 let Self(this, f) = self;
277 let f = ConstFloat(f);
278
279 if f.is_nonfinite() {
280 f.format_nonfinite()
281 } else {
282 FormatFinite(this, f.0).call_once()
283 }
284 }
285 }
286
287 impl<'a> FormatFinite<'a, F> {
288 pub const fn call_once(self) -> &'a str {
289 let Self(this, f) = self;
290 let f = ConstFloat(f);
291
292 #[expect(clippy::ptr_as_ptr)]
293 unsafe {
294 let n = f.write_to_ryu_buffer(this.bytes.as_mut_ptr() as *mut u8);
295 debug_assert!(n <= this.bytes.len());
296 let slice = slice::from_raw_parts(this.bytes.as_ptr() as *const u8, n);
297 str::from_utf8_unchecked(slice)
298 }
299 }
300 }
301);