• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::raw;
2 #[cfg(maybe_uninit)]
3 use core::mem::MaybeUninit;
4 use core::{mem, slice, str};
5 #[cfg(feature = "no-panic")]
6 use no_panic::no_panic;
7 
8 const NAN: &'static str = "NaN";
9 const INFINITY: &'static str = "inf";
10 const 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 /// ```
21 pub struct Buffer {
22     #[cfg(maybe_uninit)]
23     bytes: [MaybeUninit<u8>; 24],
24     #[cfg(not(maybe_uninit))]
25     bytes: [u8; 24],
26 }
27 
28 impl 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)]
new() -> Self33     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)]
format<F: Float>(&mut self, f: F) -> &str57     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)]
format_finite<F: Float>(&mut self, f: F) -> &str82     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 
92 impl Copy for Buffer {}
93 
94 impl Clone for Buffer {
95     #[inline]
clone(&self) -> Self96     fn clone(&self) -> Self {
97         Buffer::new()
98     }
99 }
100 
101 impl Default for Buffer {
102     #[inline]
103     #[cfg_attr(feature = "no-panic", no_panic)]
default() -> Self104     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.
114 pub trait Float: Sealed {}
115 impl Float for f32 {}
116 impl Float for f64 {}
117 
118 pub trait Sealed: Copy {
is_nonfinite(self) -> bool119     fn is_nonfinite(self) -> bool;
format_nonfinite(self) -> &'static str120     fn format_nonfinite(self) -> &'static str;
write_to_ryu_buffer(self, result: *mut u8) -> usize121     unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize;
122 }
123 
124 impl Sealed for f32 {
125     #[inline]
is_nonfinite(self) -> bool126     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)]
format_nonfinite(self) -> &'static str134     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]
write_to_ryu_buffer(self, result: *mut u8) -> usize148     unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize {
149         raw::format32(self, result)
150     }
151 }
152 
153 impl Sealed for f64 {
154     #[inline]
is_nonfinite(self) -> bool155     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)]
format_nonfinite(self) -> &'static str163     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]
write_to_ryu_buffer(self, result: *mut u8) -> usize177     unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize {
178         raw::format64(self, result)
179     }
180 }
181