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)] new() -> Self29 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)] format<F: Float>(&mut self, f: F) -> &str47 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)] format_finite<F: Float>(&mut self, f: F) -> &str72 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 #[allow(clippy::non_canonical_clone_impl)] // false positive https://github.com/rust-lang/rust-clippy/issues/11072 clone(&self) -> Self87 fn clone(&self) -> Self { 88 Buffer::new() 89 } 90 } 91 92 impl Default for Buffer { 93 #[inline] 94 #[cfg_attr(feature = "no-panic", no_panic)] default() -> Self95 fn default() -> Self { 96 Buffer::new() 97 } 98 } 99 100 /// A floating point number, f32 or f64, that can be written into a 101 /// [`ryu::Buffer`][Buffer]. 102 /// 103 /// This trait is sealed and cannot be implemented for types outside of the 104 /// `ryu` crate. 105 pub trait Float: Sealed {} 106 impl Float for f32 {} 107 impl Float for f64 {} 108 109 pub trait Sealed: Copy { is_nonfinite(self) -> bool110 fn is_nonfinite(self) -> bool; format_nonfinite(self) -> &'static str111 fn format_nonfinite(self) -> &'static str; write_to_ryu_buffer(self, result: *mut u8) -> usize112 unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize; 113 } 114 115 impl Sealed for f32 { 116 #[inline] is_nonfinite(self) -> bool117 fn is_nonfinite(self) -> bool { 118 const EXP_MASK: u32 = 0x7f800000; 119 let bits = self.to_bits(); 120 bits & EXP_MASK == EXP_MASK 121 } 122 123 #[cold] 124 #[cfg_attr(feature = "no-panic", inline)] format_nonfinite(self) -> &'static str125 fn format_nonfinite(self) -> &'static str { 126 const MANTISSA_MASK: u32 = 0x007fffff; 127 const SIGN_MASK: u32 = 0x80000000; 128 let bits = self.to_bits(); 129 if bits & MANTISSA_MASK != 0 { 130 NAN 131 } else if bits & SIGN_MASK != 0 { 132 NEG_INFINITY 133 } else { 134 INFINITY 135 } 136 } 137 138 #[inline] write_to_ryu_buffer(self, result: *mut u8) -> usize139 unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize { 140 raw::format32(self, result) 141 } 142 } 143 144 impl Sealed for f64 { 145 #[inline] is_nonfinite(self) -> bool146 fn is_nonfinite(self) -> bool { 147 const EXP_MASK: u64 = 0x7ff0000000000000; 148 let bits = self.to_bits(); 149 bits & EXP_MASK == EXP_MASK 150 } 151 152 #[cold] 153 #[cfg_attr(feature = "no-panic", inline)] format_nonfinite(self) -> &'static str154 fn format_nonfinite(self) -> &'static str { 155 const MANTISSA_MASK: u64 = 0x000fffffffffffff; 156 const SIGN_MASK: u64 = 0x8000000000000000; 157 let bits = self.to_bits(); 158 if bits & MANTISSA_MASK != 0 { 159 NAN 160 } else if bits & SIGN_MASK != 0 { 161 NEG_INFINITY 162 } else { 163 INFINITY 164 } 165 } 166 167 #[inline] write_to_ryu_buffer(self, result: *mut u8) -> usize168 unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize { 169 raw::format64(self, result) 170 } 171 } 172