diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ab3623e..6649e73 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,6 +26,8 @@ jobs: toolchain: ${{matrix.rust}} - run: cargo build - run: cargo test + - run: cargo build --tests --features no-panic --release + if: matrix.rust == 'nightly' miri: name: Miri diff --git a/Cargo.toml b/Cargo.toml index 563da41..a23825f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,5 +12,8 @@ license = "MIT OR Apache-2.0" repository = "https://github.com/dtolnay/dtoa" rust-version = "1.36" +[dependencies] +no-panic = { version = "0.1", optional = true } + [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/src/diyfp.rs b/src/diyfp.rs index 7aeb552..ee2e50a 100644 --- a/src/diyfp.rs +++ b/src/diyfp.rs @@ -27,6 +27,8 @@ // the License. use core::ops::{Mul, Sub}; +#[cfg(feature = "no-panic")] +use no_panic::no_panic; #[derive(Copy, Clone, Debug)] pub struct DiyFp { @@ -35,6 +37,7 @@ pub struct DiyFp { } impl DiyFp { + #[cfg_attr(feature = "no-panic", no_panic)] pub fn new(f: F, e: E) -> Self { DiyFp { f, e } } @@ -45,6 +48,8 @@ where F: Sub, { type Output = Self; + + #[cfg_attr(feature = "no-panic", no_panic)] fn sub(self, rhs: Self) -> Self { DiyFp { f: self.f - rhs.f, @@ -55,6 +60,8 @@ where impl Mul for DiyFp { type Output = Self; + + #[cfg_attr(feature = "no-panic", no_panic)] fn mul(self, rhs: Self) -> Self { let mut tmp = self.f as u64 * rhs.f as u64; tmp += 1u64 << 31; // mult_round @@ -67,6 +74,8 @@ impl Mul for DiyFp { impl Mul for DiyFp { type Output = Self; + + #[cfg_attr(feature = "no-panic", no_panic)] fn mul(self, rhs: Self) -> Self { let m32 = 0xFFFFFFFFu64; let a = self.f >> 32; @@ -127,6 +136,7 @@ macro_rules! diyfp { } } */ + #[cfg_attr(feature = "no-panic", no_panic)] unsafe fn from(d: $fty) -> Self { let u: $mask_type = mem::transmute(d); @@ -156,6 +166,7 @@ macro_rules! diyfp { return res; } */ + #[cfg_attr(feature = "no-panic", no_panic)] fn normalize(self) -> DiyFp { let mut res = self; while (res.f & (1 << ($diy_significand_size - 1))) == 0 { @@ -181,6 +192,7 @@ macro_rules! diyfp { return res; } */ + #[cfg_attr(feature = "no-panic", no_panic)] fn normalize_boundary(self) -> DiyFp { let mut res = self; while (res.f & $hidden_bit << 1) == 0 { @@ -210,6 +222,7 @@ macro_rules! diyfp { *minus = mi; } */ + #[cfg_attr(feature = "no-panic", no_panic)] fn normalized_boundaries(self) -> (DiyFp, DiyFp) { let pl = DiyFp::new((self.f << 1) + 1, self.e - 1).normalize_boundary(); let mut mi = if self.f == $hidden_bit { @@ -238,6 +251,7 @@ macro_rules! diyfp { } */ #[inline] + #[cfg_attr(feature = "no-panic", no_panic)] fn get_cached_power(e: $expty) -> (DiyFp, isize) { let dk = (3 - $diy_significand_size - e) as f64 * 0.30102999566398114f64 - ($min_power + 1) as f64; @@ -250,7 +264,10 @@ macro_rules! diyfp { let k = -($min_power + (index << 3) as isize); ( - DiyFp::new($cached_powers_f[index], $cached_powers_e[index] as $expty), + DiyFp::new(*unsafe { $cached_powers_f.get_unchecked(index) }, *unsafe { + $cached_powers_e.get_unchecked(index) + } + as $expty), k, ) } diff --git a/src/dtoa.rs b/src/dtoa.rs index 54fb66a..9d0dbfb 100644 --- a/src/dtoa.rs +++ b/src/dtoa.rs @@ -27,6 +27,8 @@ // the License. use core::ptr; +#[cfg(feature = "no-panic")] +use no_panic::no_panic; /* inline unsigned CountDecimalDigit32(uint32_t n) { @@ -47,6 +49,7 @@ inline unsigned CountDecimalDigit32(uint32_t n) { */ #[inline] +#[cfg_attr(feature = "no-panic", no_panic)] pub fn count_decimal_digit32(n: u32) -> usize { if n < 10 { 1 @@ -98,6 +101,7 @@ inline char* WriteExponent(int K, char* buffer) { */ #[inline] +#[cfg_attr(feature = "no-panic", no_panic)] unsafe fn write_exponent(mut k: isize, mut buffer: *mut u8) -> *mut u8 { if k < 0 { *buffer = b'-'; @@ -127,6 +131,7 @@ inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) { */ #[inline] +#[cfg_attr(feature = "no-panic", no_panic)] pub unsafe fn prettify(buffer: *mut u8, length: isize, k: isize) -> *mut u8 { let kk = length + k; // 10^(kk-1) <= v < 10^kk @@ -301,6 +306,7 @@ macro_rules! dtoa { */ #[inline] + #[cfg_attr(feature = "no-panic", no_panic)] unsafe fn grisu_round(buffer: *mut u8, len: isize, delta: $sigty, mut rest: $sigty, ten_kappa: $sigty, wp_w: $sigty) { while rest < wp_w && delta - rest >= ten_kappa && (rest + ten_kappa < wp_w || // closer @@ -323,6 +329,7 @@ macro_rules! dtoa { // Returns length and k. #[inline] + #[cfg_attr(feature = "no-panic", no_panic)] unsafe fn digit_gen(w: DiyFp, mp: DiyFp, mut delta: $sigty, buffer: *mut u8, mut k: isize) -> (isize, isize) { static POW10: [$sigty; 10] = [ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 ]; let one = DiyFp::new(1 << -mp.e, mp.e); @@ -377,10 +384,10 @@ macro_rules! dtoa { len += 1; } kappa -= 1; - let tmp = (p1 as $sigty << -one.e) + p2; + let tmp = ((p1 as $sigty) << -one.e) + p2; if tmp <= delta { k += kappa as isize; - grisu_round(buffer, len, delta, tmp, POW10[kappa] << -one.e, wp_w.f); + grisu_round(buffer, len, delta, tmp, *POW10.get_unchecked(kappa) << -one.e, wp_w.f); return (len, k); } } @@ -416,7 +423,18 @@ macro_rules! dtoa { if p2 < delta { k += kappa as isize; let index = -(kappa as isize); - grisu_round(buffer, len, delta, p2, one.f, wp_w.f * if index < 9 { POW10[-(kappa as isize) as usize] } else { 0 }); + grisu_round( + buffer, + len, + delta, + p2, + one.f, + wp_w.f * if index < 9 { + *POW10.get_unchecked(-(kappa as isize) as usize) + } else { + 0 + }, + ); return (len, k); } } @@ -440,6 +458,7 @@ macro_rules! dtoa { // Returns length and k. #[inline] + #[cfg_attr(feature = "no-panic", no_panic)] unsafe fn grisu2(value: $fty, buffer: *mut u8) -> (isize, isize) { let v = DiyFp::from(value); let (w_m, w_p) = v.normalized_boundaries(); @@ -478,6 +497,7 @@ macro_rules! dtoa { */ #[inline] + #[cfg_attr(feature = "no-panic", no_panic)] unsafe fn dtoa(buf: &mut Buffer, mut value: $fty) -> &str { if value == 0.0 { if value.is_sign_negative() { diff --git a/src/lib.rs b/src/lib.rs index aeaf48b..2303c40 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,6 +58,8 @@ mod dtoa; use core::mem::{self, MaybeUninit}; use core::slice; use core::str; +#[cfg(feature = "no-panic")] +use no_panic::no_panic; const NAN: &str = "NaN"; const INFINITY: &str = "inf"; @@ -95,6 +97,7 @@ 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() -> Buffer { let bytes = [MaybeUninit::::uninit(); 25]; Buffer { bytes } @@ -111,6 +114,7 @@ impl Buffer { /// 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", no_panic)] pub fn format(&mut self, value: F) -> &str { if value.is_nonfinite() { value.format_nonfinite() @@ -134,6 +138,7 @@ impl Buffer { /// [`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 + #[cfg_attr(feature = "no-panic", no_panic)] pub fn format_finite(&mut self, value: F) -> &str { value.write(self) } @@ -158,6 +163,7 @@ mod private { impl private::Sealed for f32 { #[inline] + #[cfg_attr(feature = "no-panic", no_panic)] fn is_nonfinite(self) -> bool { const EXP_MASK: u32 = 0x7f800000; let bits = self.to_bits(); @@ -165,6 +171,7 @@ impl private::Sealed for f32 { } #[cold] + #[cfg_attr(feature = "no-panic", no_panic)] fn format_nonfinite(self) -> &'static str { const MANTISSA_MASK: u32 = 0x007fffff; const SIGN_MASK: u32 = 0x80000000; @@ -202,6 +209,7 @@ impl private::Sealed for f32 { impl private::Sealed for f64 { #[inline] + #[cfg_attr(feature = "no-panic", no_panic)] fn is_nonfinite(self) -> bool { const EXP_MASK: u64 = 0x7ff0000000000000; let bits = self.to_bits(); @@ -209,6 +217,7 @@ impl private::Sealed for f64 { } #[cold] + #[cfg_attr(feature = "no-panic", no_panic)] fn format_nonfinite(self) -> &'static str { const MANTISSA_MASK: u64 = 0x000fffffffffffff; const SIGN_MASK: u64 = 0x8000000000000000;