blob: 5731aea572aaec20e4619a43f4171ab3aa90c2a8 [file] [log] [blame]
use core::{mem, slice, str};
use raw;
#[cfg(feature = "no-panic")]
use no_panic::no_panic;
const NAN: &'static str = "NaN";
const INFINITY: &'static str = "inf";
const NEG_INFINITY: &'static str = "-inf";
/// Safe API for formatting floating point numbers to text.
///
/// ## Example
///
/// ```edition2018
/// let mut buffer = ryu::Buffer::new();
/// let printed = buffer.format_finite(1.234);
/// assert_eq!(printed, "1.234");
/// ```
#[derive(Copy, Clone)]
pub struct Buffer {
bytes: [u8; 24],
}
impl Buffer {
/// This is a cheap operation; you don't need to worry about reusing buffers
/// for efficiency.
#[inline]
#[cfg_attr(feature = "no-panic", no_panic)]
pub fn new() -> Self {
Buffer {
bytes: unsafe { mem::uninitialized() },
}
}
/// Print a floating point number into this buffer and return a reference to
/// its string representation within the buffer.
///
/// # Special cases
///
/// This function formats NaN as the string "NaN", positive infinity as
/// "inf", and negative infinity as "-inf" to match std::fmt.
///
/// If your input is known to be finite, you may get better performance by
/// calling the `format_finite` method instead of `format` to avoid the
/// checks for special cases.
#[cfg_attr(feature = "no-panic", inline)]
#[cfg_attr(feature = "no-panic", no_panic)]
pub fn format<F: Float>(&mut self, f: F) -> &str {
if f.is_nonfinite() {
f.format_nonfinite()
} else {
self.format_finite(f)
}
}
/// Print a floating point number into this buffer and return a reference to
/// its string representation within the buffer.
///
/// # Special cases
///
/// This function **does not** check for NaN or infinity. If the input
/// number is not a finite float, the printed representation will be some
/// correctly formatted but unspecified numerical value.
///
/// Please check [`is_finite`] yourself before calling this function, or
/// check [`is_nan`] and [`is_infinite`] and handle those cases yourself.
///
/// [`is_finite`]: https://doc.rust-lang.org/std/primitive.f64.html#method.is_finite
/// [`is_nan`]: https://doc.rust-lang.org/std/primitive.f64.html#method.is_nan
/// [`is_infinite`]: https://doc.rust-lang.org/std/primitive.f64.html#method.is_infinite
#[inline]
#[cfg_attr(feature = "no-panic", no_panic)]
pub fn format_finite<F: Float>(&mut self, f: F) -> &str {
unsafe {
let n = f.write_to_ryu_buffer(&mut self.bytes[0]);
debug_assert!(n <= self.bytes.len());
let slice = slice::from_raw_parts(&self.bytes[0], n);
str::from_utf8_unchecked(slice)
}
}
}
impl Default for Buffer {
#[inline]
#[cfg_attr(feature = "no-panic", no_panic)]
fn default() -> Self {
Buffer::new()
}
}
/// A floating point number, f32 or f64, that can be written into a
/// [`ryu::Buffer`][Buffer].
///
/// This trait is sealed and cannot be implemented for types outside of the
/// `ryu` crate.
pub trait Float: Sealed {}
impl Float for f32 {}
impl Float for f64 {}
pub trait Sealed: Copy {
fn is_nonfinite(self) -> bool;
fn format_nonfinite(self) -> &'static str;
unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize;
}
impl Sealed for f32 {
#[inline]
fn is_nonfinite(self) -> bool {
const EXP_MASK: u32 = 0x7f800000;
let bits = unsafe { mem::transmute::<f32, u32>(self) };
bits & EXP_MASK == EXP_MASK
}
#[cold]
#[cfg_attr(feature = "no-panic", inline)]
fn format_nonfinite(self) -> &'static str {
const MANTISSA_MASK: u32 = 0x007fffff;
const SIGN_MASK: u32 = 0x80000000;
let bits = unsafe { mem::transmute::<f32, u32>(self) };
if bits & MANTISSA_MASK != 0 {
NAN
} else if bits & SIGN_MASK != 0 {
NEG_INFINITY
} else {
INFINITY
}
}
#[inline]
unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize {
raw::format32(self, result)
}
}
impl Sealed for f64 {
#[inline]
fn is_nonfinite(self) -> bool {
const EXP_MASK: u64 = 0x7ff0000000000000;
let bits = unsafe { mem::transmute::<f64, u64>(self) };
bits & EXP_MASK == EXP_MASK
}
#[cold]
#[cfg_attr(feature = "no-panic", inline)]
fn format_nonfinite(self) -> &'static str {
const MANTISSA_MASK: u64 = 0x000fffffffffffff;
const SIGN_MASK: u64 = 0x8000000000000000;
let bits = unsafe { mem::transmute::<f64, u64>(self) };
if bits & MANTISSA_MASK != 0 {
NAN
} else if bits & SIGN_MASK != 0 {
NEG_INFINITY
} else {
INFINITY
}
}
#[inline]
unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize {
raw::format64(self, result)
}
}