Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ jobs:
- run: cargo check
- run: cargo test
- run: cd extras/data-tests && cargo run --release
- run: cargo build --tests --features no-panic --release

msrv:
name: Rust ${{matrix.rust}}
Expand Down
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ rust-version = "1.37"
default = ["std"]
std = []

[dependencies]
no-panic = { version = "0.1", optional = true }

[dev-dependencies]
lexical-core = "1.0.2"
hexf-parse = "0.2.1"
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ on little-endian architectures.
Since [fast-float-rust](https://github.com/aldanor/fast-float-rust) is unmaintained, this is a fork
containing the patches and security updates.

## Features

`no-panic`: when this feature is enabled, the crate guarantees that it will not trigger a runtime panic.


## Testing

There are a few ways this crate is tested:
Expand Down
3 changes: 2 additions & 1 deletion src/binary.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::common::AdjustedMantissa;
use crate::float::Float;
use crate::table::{LARGEST_POWER_OF_FIVE, POWER_OF_FIVE_128, SMALLEST_POWER_OF_FIVE};
use crate::GetAt;

#[inline]
pub fn compute_float<F: Float>(q: i64, mut w: u64) -> AdjustedMantissa {
Expand Down Expand Up @@ -94,7 +95,7 @@ fn compute_product_approx(q: i64, w: u64, precision: usize) -> (u64, u64) {
// comes from a parsed result. Since this is unlikely to have any major
// performance implications, as is determined empirically, we keep the
// bounds check despite the performance hit.
let (lo5, hi5) = POWER_OF_FIVE_128[index];
let (lo5, hi5) = *POWER_OF_FIVE_128.at(index);
let (mut first_lo, mut first_hi) = full_multiplication(w, lo5);
if first_hi & mask == mask {
let (_, second_hi) = full_multiplication(w, hi5);
Expand Down
9 changes: 5 additions & 4 deletions src/common.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use core::marker::PhantomData;
use core::ptr;

use crate::GetAt;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AsciiStr<'a> {
ptr: *const u8,
Expand Down Expand Up @@ -172,13 +174,13 @@ pub trait ByteSlice: AsRef<[u8]> + AsMut<[u8]> {
if s.len() < u.len() {
return false;
}
let d = (0..u.len()).fold(0, |d, i| d | s[i] ^ u[i]);
let d = (0..u.len()).fold(0, |d, i| d | s.at(i) ^ u.at(i));
d == 0 || d == 32
}

#[inline]
fn advance(&self, n: usize) -> &[u8] {
&self.as_ref()[n..]
self.as_ref().at(n..)
}

#[inline]
Expand Down Expand Up @@ -215,8 +217,7 @@ pub trait ByteSlice: AsRef<[u8]> + AsMut<[u8]> {
}
}

impl ByteSlice for [u8] {
}
impl ByteSlice for [u8] {}

#[inline]
pub fn is_8digits(v: u64) -> bool {
Expand Down
56 changes: 30 additions & 26 deletions src/decimal.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use core::fmt::{self, Debug};

use crate::common::{is_8digits, parse_digits, ByteSlice};
use crate::GetAt;
use crate::GetAtMut;

#[derive(Clone)]
pub struct Decimal {
Expand All @@ -18,7 +20,7 @@ impl Debug for Decimal {
.field("decimal_point", &self.decimal_point)
.field("negative", &self.negative)
.field("truncated", &self.truncated)
.field("digits", &(&self.digits[..self.num_digits]))
.field("digits", &(&self.digits.at(..self.num_digits)))
.finish()
}
}
Expand All @@ -33,8 +35,7 @@ impl PartialEq for Decimal {
}
}

impl Eq for Decimal {
}
impl Eq for Decimal {}

impl Default for Decimal {
fn default() -> Self {
Expand All @@ -56,14 +57,14 @@ impl Decimal {
#[inline]
pub fn try_add_digit(&mut self, digit: u8) {
if self.num_digits < Self::MAX_DIGITS {
self.digits[self.num_digits] = digit;
*self.digits.at_mut(self.num_digits) = digit;
}
self.num_digits += 1;
}

#[inline]
pub fn trim(&mut self) {
while self.num_digits != 0 && self.digits[self.num_digits - 1] == 0 {
while self.num_digits != 0 && *self.digits.at(self.num_digits - 1) == 0 {
self.num_digits -= 1;
}
}
Expand All @@ -80,14 +81,14 @@ impl Decimal {
for i in 0..dp {
n *= 10;
if i < self.num_digits {
n += self.digits[i] as u64;
n += *self.digits.at(i) as u64;
}
}
let mut round_up = false;
if dp < self.num_digits {
round_up = self.digits[dp] >= 5;
if self.digits[dp] == 5 && dp + 1 == self.num_digits {
round_up = self.truncated || ((dp != 0) && (1 & self.digits[dp - 1] != 0));
round_up = *self.digits.at(dp) >= 5;
if *self.digits.at(dp) == 5 && dp + 1 == self.num_digits {
round_up = self.truncated || ((dp != 0) && (1 & self.digits.at(dp - 1) != 0));
}
}
if round_up {
Expand All @@ -108,11 +109,11 @@ impl Decimal {
while read_index != 0 {
read_index -= 1;
write_index -= 1;
n += (self.digits[read_index] as u64) << shift;
n += (*self.digits.at(read_index) as u64) << shift;
let quotient = n / 10;
let remainder = n - (10 * quotient);
if write_index < Self::MAX_DIGITS {
self.digits[write_index] = remainder as u8;
*self.digits.at_mut(write_index) = remainder as u8;
} else if remainder > 0 {
self.truncated = true;
}
Expand All @@ -123,7 +124,7 @@ impl Decimal {
let quotient = n / 10;
let remainder = n - (10 * quotient);
if write_index < Self::MAX_DIGITS {
self.digits[write_index] = remainder as u8;
*self.digits.at_mut(write_index) = remainder as u8;
} else if remainder > 0 {
self.truncated = true;
}
Expand All @@ -144,7 +145,7 @@ impl Decimal {
let mut n = 0_u64;
while (n >> shift) == 0 {
if read_index < self.num_digits {
n = (10 * n) + self.digits[read_index] as u64;
n = (10 * n) + *self.digits.at(read_index) as u64;
read_index += 1;
} else if n == 0 {
return;
Expand All @@ -167,16 +168,16 @@ impl Decimal {
let mask = (1_u64 << shift) - 1;
while read_index < self.num_digits {
let new_digit = (n >> shift) as u8;
n = (10 * (n & mask)) + self.digits[read_index] as u64;
n = (10 * (n & mask)) + *self.digits.at(read_index) as u64;
read_index += 1;
self.digits[write_index] = new_digit;
*self.digits.at_mut(write_index) = new_digit;
write_index += 1;
}
while n > 0 {
let new_digit = (n >> shift) as u8;
n = 10 * (n & mask);
if write_index < Self::MAX_DIGITS {
self.digits[write_index] = new_digit;
*self.digits.at_mut(write_index) = new_digit;
write_index += 1;
} else if new_digit > 0 {
self.truncated = true;
Expand All @@ -190,11 +191,14 @@ impl Decimal {
#[inline]
pub fn parse_decimal(mut s: &[u8]) -> Decimal {
// can't fail since it follows a call to parse_number
assert!(!s.is_empty(), "the buffer cannot be empty since it follows a call to parse_number");
debug_assert!(
!s.is_empty(),
"the buffer cannot be empty since it follows a call to parse_number"
);
let mut d = Decimal::default();
let start = s;

let c = s[0];
let c = *s.at(0);
d.negative = c == b'-';
if c == b'-' || c == b'+' {
s = s.advance(1);
Expand All @@ -214,7 +218,7 @@ pub fn parse_decimal(mut s: &[u8]) -> Decimal {
break;
}
// SAFETY: Safe since `num_digits + 8 < Decimal::MAX_DIGITS`
unsafe { d.digits[d.num_digits..].write_u64(v - 0x3030_3030_3030_3030) };
unsafe { d.digits.at_mut(d.num_digits..).write_u64(v - 0x3030_3030_3030_3030) };
d.num_digits += 8;
s = s.advance(8);
}
Expand All @@ -224,7 +228,7 @@ pub fn parse_decimal(mut s: &[u8]) -> Decimal {
if d.num_digits != 0 {
// Ignore the trailing zeros if there are any
let mut n_trailing_zeros = 0;
for &c in start[..(start.len() - s.len())].iter().rev() {
for &c in start.at(..(start.len() - s.len())).iter().rev() {
if c == b'0' {
n_trailing_zeros += 1;
} else if c != b'.' {
Expand Down Expand Up @@ -261,7 +265,7 @@ pub fn parse_decimal(mut s: &[u8]) -> Decimal {
};
}
for i in d.num_digits..Decimal::MAX_DIGITS_WITHOUT_OVERFLOW {
d.digits[i] = 0;
*d.digits.at_mut(i) = 0;
}
d
}
Expand Down Expand Up @@ -325,18 +329,18 @@ fn number_of_digits_decimal_left_shift(d: &Decimal, mut shift: usize) -> usize {
];

shift &= 63;
let x_a = TABLE[shift];
let x_b = TABLE[shift + 1];
let x_a = *TABLE.at(shift);
let x_b = *TABLE.at(shift + 1);
let num_new_digits = (x_a >> 11) as usize;
let pow5_a = (0x7FF & x_a) as usize;
let pow5_b = (0x7FF & x_b) as usize;
let pow5 = &TABLE_POW5[pow5_a..];
let pow5 = TABLE_POW5.at(pow5_a..);
for (i, &p5) in pow5.iter().enumerate().take(pow5_b - pow5_a) {
if i >= d.num_digits {
return num_new_digits - 1;
} else if d.digits[i] == p5 {
} else if *d.digits.at(i) == p5 {
continue;
} else if d.digits[i] < p5 {
} else if *d.digits.at(i) < p5 {
return num_new_digits - 1;
} else {
return num_new_digits;
Expand Down
12 changes: 6 additions & 6 deletions src/float.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use core::fmt::{Debug, Display};
use core::ops::{Add, Div, Mul, Neg};

use crate::GetAt;

mod private {
pub trait Sealed {}
}
Expand Down Expand Up @@ -45,8 +47,7 @@ pub trait Float:
fn pow10_fast_path(exponent: usize) -> Self;
}

impl private::Sealed for f32 {
}
impl private::Sealed for f32 {}

impl Float for f32 {
const INFINITY: Self = core::f32::INFINITY;
Expand Down Expand Up @@ -81,12 +82,11 @@ impl Float for f32 {
#[allow(clippy::use_self)]
const TABLE: [f32; 16] =
[1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 0., 0., 0., 0., 0.];
TABLE[exponent & 15]
*TABLE.at(exponent & 15)
}
}

impl private::Sealed for f64 {
}
impl private::Sealed for f64 {}

impl Float for f64 {
const INFINITY: Self = core::f64::INFINITY;
Expand Down Expand Up @@ -123,6 +123,6 @@ impl Float for f64 {
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22, 0., 0., 0., 0., 0., 0., 0., 0., 0.,
];
TABLE[exponent & 31]
*TABLE.at(exponent & 31)
}
}
Loading