blob: df21fe0df2dc91129c3515d45dfaec248acf6af1 [file] [log] [blame]
Yi Kongce81bb62020-08-31 01:21:33 +08001use crate::raw;
2#[cfg(maybe_uninit)]
3use core::mem::MaybeUninit;
4use core::{mem, slice, str};
5#[cfg(feature = "no-panic")]
6use no_panic::no_panic;
7
8const NAN: &'static str = "NaN";
9const INFINITY: &'static str = "inf";
10const NEG_INFINITY: &'static str = "-inf";
11
12/// Safe API for formatting floating point numbers to text.
13///
14/// ## Example
15///
16/// ```
17/// let mut buffer = ryu::Buffer::new();
18/// let printed = buffer.format_finite(1.234);
19/// assert_eq!(printed, "1.234");
20/// ```
21pub struct Buffer {
22 #[cfg(maybe_uninit)]
23 bytes: [MaybeUninit<u8>; 24],
24 #[cfg(not(maybe_uninit))]
25 bytes: [u8; 24],
26}
27
28impl Buffer {
29 /// This is a cheap operation; you don't need to worry about reusing buffers
30 /// for efficiency.
31 #[inline]
32 #[cfg_attr(feature = "no-panic", no_panic)]
33 pub fn new() -> Self {
34 // assume_init is safe here, since this is an array of MaybeUninit, which does not need
35 // to be initialized.
36 #[cfg(maybe_uninit)]
37 let bytes = [MaybeUninit::<u8>::uninit(); 24];
38 #[cfg(not(maybe_uninit))]
39 let bytes = unsafe { mem::uninitialized() };
40
41 Buffer { bytes: bytes }
42 }
43
44 /// Print a floating point number into this buffer and return a reference to
45 /// its string representation within the buffer.
46 ///
47 /// # Special cases
48 ///
49 /// This function formats NaN as the string "NaN", positive infinity as
50 /// "inf", and negative infinity as "-inf" to match std::fmt.
51 ///
52 /// If your input is known to be finite, you may get better performance by
53 /// calling the `format_finite` method instead of `format` to avoid the
54 /// checks for special cases.
55 #[cfg_attr(feature = "no-panic", inline)]
56 #[cfg_attr(feature = "no-panic", no_panic)]
57 pub fn format<F: Float>(&mut self, f: F) -> &str {
58 if f.is_nonfinite() {
59 f.format_nonfinite()
60 } else {
61 self.format_finite(f)
62 }
63 }
64
65 /// Print a floating point number into this buffer and return a reference to
66 /// its string representation within the buffer.
67 ///
68 /// # Special cases
69 ///
70 /// This function **does not** check for NaN or infinity. If the input
71 /// number is not a finite float, the printed representation will be some
72 /// correctly formatted but unspecified numerical value.
73 ///
74 /// Please check [`is_finite`] yourself before calling this function, or
75 /// check [`is_nan`] and [`is_infinite`] and handle those cases yourself.
76 ///
77 /// [`is_finite`]: https://doc.rust-lang.org/std/primitive.f64.html#method.is_finite
78 /// [`is_nan`]: https://doc.rust-lang.org/std/primitive.f64.html#method.is_nan
79 /// [`is_infinite`]: https://doc.rust-lang.org/std/primitive.f64.html#method.is_infinite
80 #[inline]
81 #[cfg_attr(feature = "no-panic", no_panic)]
82 pub fn format_finite<F: Float>(&mut self, f: F) -> &str {
83 unsafe {
84 let n = f.write_to_ryu_buffer(self.bytes.as_mut_ptr() as *mut u8);
85 debug_assert!(n <= self.bytes.len());
86 let slice = slice::from_raw_parts(self.bytes.as_ptr() as *const u8, n);
87 str::from_utf8_unchecked(slice)
88 }
89 }
90}
91
92impl Copy for Buffer {}
93
94impl Clone for Buffer {
95 #[inline]
96 fn clone(&self) -> Self {
97 Buffer::new()
98 }
99}
100
101impl Default for Buffer {
102 #[inline]
103 #[cfg_attr(feature = "no-panic", no_panic)]
104 fn default() -> Self {
105 Buffer::new()
106 }
107}
108
109/// A floating point number, f32 or f64, that can be written into a
110/// [`ryu::Buffer`][Buffer].
111///
112/// This trait is sealed and cannot be implemented for types outside of the
113/// `ryu` crate.
114pub trait Float: Sealed {}
115impl Float for f32 {}
116impl Float for f64 {}
117
118pub trait Sealed: Copy {
119 fn is_nonfinite(self) -> bool;
120 fn format_nonfinite(self) -> &'static str;
121 unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize;
122}
123
124impl Sealed for f32 {
125 #[inline]
126 fn is_nonfinite(self) -> bool {
127 const EXP_MASK: u32 = 0x7f800000;
128 let bits = unsafe { mem::transmute::<f32, u32>(self) };
129 bits & EXP_MASK == EXP_MASK
130 }
131
132 #[cold]
133 #[cfg_attr(feature = "no-panic", inline)]
134 fn format_nonfinite(self) -> &'static str {
135 const MANTISSA_MASK: u32 = 0x007fffff;
136 const SIGN_MASK: u32 = 0x80000000;
137 let bits = unsafe { mem::transmute::<f32, u32>(self) };
138 if bits & MANTISSA_MASK != 0 {
139 NAN
140 } else if bits & SIGN_MASK != 0 {
141 NEG_INFINITY
142 } else {
143 INFINITY
144 }
145 }
146
147 #[inline]
148 unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize {
149 raw::format32(self, result)
150 }
151}
152
153impl Sealed for f64 {
154 #[inline]
155 fn is_nonfinite(self) -> bool {
156 const EXP_MASK: u64 = 0x7ff0000000000000;
157 let bits = unsafe { mem::transmute::<f64, u64>(self) };
158 bits & EXP_MASK == EXP_MASK
159 }
160
161 #[cold]
162 #[cfg_attr(feature = "no-panic", inline)]
163 fn format_nonfinite(self) -> &'static str {
164 const MANTISSA_MASK: u64 = 0x000fffffffffffff;
165 const SIGN_MASK: u64 = 0x8000000000000000;
166 let bits = unsafe { mem::transmute::<f64, u64>(self) };
167 if bits & MANTISSA_MASK != 0 {
168 NAN
169 } else if bits & SIGN_MASK != 0 {
170 NEG_INFINITY
171 } else {
172 INFINITY
173 }
174 }
175
176 #[inline]
177 unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize {
178 raw::format64(self, result)
179 }
180}