Skip to content

Commit

Permalink
Merge pull request #29 from dtolnay/nopanic
Browse files Browse the repository at this point in the history
Add no-panic feature to confirm no panicking codepaths
  • Loading branch information
dtolnay authored Oct 7, 2022
2 parents 5be78ac + 546e2c9 commit 8ad9f09
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 4 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
19 changes: 18 additions & 1 deletion src/diyfp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<F, E> {
Expand All @@ -35,6 +37,7 @@ pub struct DiyFp<F, E> {
}

impl<F, E> DiyFp<F, E> {
#[cfg_attr(feature = "no-panic", no_panic)]
pub fn new(f: F, e: E) -> Self {
DiyFp { f, e }
}
Expand All @@ -45,6 +48,8 @@ where
F: Sub<F, Output = F>,
{
type Output = Self;

#[cfg_attr(feature = "no-panic", no_panic)]
fn sub(self, rhs: Self) -> Self {
DiyFp {
f: self.f - rhs.f,
Expand All @@ -55,6 +60,8 @@ where

impl Mul for DiyFp<u32, i32> {
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
Expand All @@ -67,6 +74,8 @@ impl Mul for DiyFp<u32, i32> {

impl Mul for DiyFp<u64, isize> {
type Output = Self;

#[cfg_attr(feature = "no-panic", no_panic)]
fn mul(self, rhs: Self) -> Self {
let m32 = 0xFFFFFFFFu64;
let a = self.f >> 32;
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
Expand All @@ -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,
)
}
Expand Down
26 changes: 23 additions & 3 deletions src/dtoa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
// the License.

use core::ptr;
#[cfg(feature = "no-panic")]
use no_panic::no_panic;

/*
inline unsigned CountDecimalDigit32(uint32_t n) {
Expand All @@ -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
Expand Down Expand Up @@ -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'-';
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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);
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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);
}
}
Expand All @@ -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();
Expand Down Expand Up @@ -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() {
Expand Down
9 changes: 9 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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::<u8>::uninit(); 25];
Buffer { bytes }
Expand All @@ -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<F: Float>(&mut self, value: F) -> &str {
if value.is_nonfinite() {
value.format_nonfinite()
Expand All @@ -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<F: Float>(&mut self, value: F) -> &str {
value.write(self)
}
Expand All @@ -158,13 +163,15 @@ 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();
bits & EXP_MASK == EXP_MASK
}

#[cold]
#[cfg_attr(feature = "no-panic", no_panic)]
fn format_nonfinite(self) -> &'static str {
const MANTISSA_MASK: u32 = 0x007fffff;
const SIGN_MASK: u32 = 0x80000000;
Expand Down Expand Up @@ -202,13 +209,15 @@ 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();
bits & EXP_MASK == EXP_MASK
}

#[cold]
#[cfg_attr(feature = "no-panic", no_panic)]
fn format_nonfinite(self) -> &'static str {
const MANTISSA_MASK: u64 = 0x000fffffffffffff;
const SIGN_MASK: u64 = 0x8000000000000000;
Expand Down

0 comments on commit 8ad9f09

Please sign in to comment.