diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aef3ec9..51a101e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,12 @@ on: pull_request: schedule: [cron: "40 1 * * *"] +permissions: + contents: read + +env: + RUSTFLAGS: -Dwarnings + jobs: test: name: Rust ${{matrix.rust}} @@ -37,12 +43,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@nightly - with: - components: miri + - uses: dtolnay/rust-toolchain@miri - run: cargo miri test env: - MIRIFLAGS: "-Zmiri-tag-raw-pointers" + MIRIFLAGS: -Zmiri-strict-provenance clippy: name: Clippy @@ -60,4 +64,14 @@ jobs: steps: - uses: actions/checkout@v3 - uses: dtolnay/install@cargo-outdated - - run: cargo outdated --exit-code 1 + - run: cargo outdated --workspace --exit-code 1 + - run: cargo outdated --manifest-path fuzz/Cargo.toml --exit-code 1 + + fuzz: + name: Fuzz + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@nightly + - uses: dtolnay/install@cargo-fuzz + - run: cargo fuzz build -O diff --git a/Cargo.toml b/Cargo.toml index 2fc8c7e..73c00e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,14 +2,15 @@ name = "ryu-js" version = "0.2.2" # don't forget to update html_root_url authors = ["David Tolnay ", "boa-dev"] -license = "Apache-2.0 OR BSL-1.0" +categories = ["value-formatting", "no-std"] description = "Fast floating point to string conversion, ECMAScript compliant." -repository = "https://github.com/boa-dev/ryu-js" documentation = "https://docs.rs/ryu-js" -categories = ["value-formatting"] -readme = "README.md" -exclude = ["performance.png", "chart/**"] edition = "2018" +exclude = ["performance.png", "chart/**"] +keywords = ["float"] +license = "Apache-2.0 OR BSL-1.0" +repository = "https://github.com/boa-dev/ryu-js" +readme = "README.md" rust-version = "1.36" [features] diff --git a/fuzz/.gitignore b/fuzz/.gitignore new file mode 100644 index 0000000..a092511 --- /dev/null +++ b/fuzz/.gitignore @@ -0,0 +1,3 @@ +target +corpus +artifacts diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml new file mode 100644 index 0000000..07d953d --- /dev/null +++ b/fuzz/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "ryu-fuzz" +version = "0.0.0" +authors = ["David Tolnay "] +edition = "2018" +publish = false + +[package.metadata] +cargo-fuzz = true + +[dependencies] +arbitrary = { version = "1", features = ["derive"] } +libfuzzer-sys = "0.4" +ryu-js = { path = ".." } + +[[bin]] +name = "fuzz_ryu_js" +path = "fuzz_targets/fuzz_ryu_js.rs" +test = false +doc = false + +[workspace] diff --git a/fuzz/fuzz_targets/fuzz_ryu_js.rs b/fuzz/fuzz_targets/fuzz_ryu_js.rs new file mode 100644 index 0000000..c6a24ae --- /dev/null +++ b/fuzz/fuzz_targets/fuzz_ryu_js.rs @@ -0,0 +1,36 @@ +#![no_main] + +use arbitrary::Arbitrary; +use libfuzzer_sys::fuzz_target; +use std::mem; + +#[derive(Arbitrary, Debug)] +enum FloatInput { + F32(f32), + F64(f64), +} + +macro_rules! ryu_js_test { + ($val:expr, $method:ident) => { + match $val { + val => { + let mut buffer = ryu_js::Buffer::new(); + let string = buffer.$method(val); + assert!(string.len() <= mem::size_of::()); + if val.is_finite() { + assert_eq!(val, string.parse().unwrap()); + } + } + } + }; +} + +fuzz_target!(|inputs: (FloatInput, bool)| { + let (input, finite) = inputs; + match (input, finite) { + (FloatInput::F32(val), false) => ryu_js_test!(val, format), + (FloatInput::F32(val), true) => ryu_js_test!(val, format_finite), + (FloatInput::F64(val), false) => ryu_js_test!(val, format), + (FloatInput::F64(val), true) => ryu_js_test!(val, format_finite), + } +}); diff --git a/src/lib.rs b/src/lib.rs index 1964296..2611ba2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,7 +21,7 @@ //! } //! ``` //! -//! ## Performance +//! ## Performance (lower is better) //! //! The benchmarks measure the average time to print a 32-bit float and average //! time to print a 64-bit float, where the inputs are distributed as uniform random diff --git a/src/pretty/mantissa.rs b/src/pretty/mantissa.rs index 150c79c..0149f5c 100644 --- a/src/pretty/mantissa.rs +++ b/src/pretty/mantissa.rs @@ -43,7 +43,7 @@ pub unsafe fn write_mantissa_long(mut output: u64, mut result: *mut u8) { #[cfg_attr(feature = "no-panic", inline)] pub unsafe fn write_mantissa(mut output: u32, mut result: *mut u8) { while output >= 10_000 { - let c = (output - 10_000 * (output / 10_000)) as u32; + let c = output - 10_000 * (output / 10_000); output /= 10_000; let c0 = (c % 100) << 1; let c1 = (c / 100) << 1; @@ -60,7 +60,7 @@ pub unsafe fn write_mantissa(mut output: u32, mut result: *mut u8) { result = result.offset(-4); } if output >= 100 { - let c = ((output % 100) << 1) as u32; + let c = (output % 100) << 1; output /= 100; ptr::copy_nonoverlapping( DIGIT_TABLE.as_ptr().offset(c as isize), @@ -70,7 +70,7 @@ pub unsafe fn write_mantissa(mut output: u32, mut result: *mut u8) { result = result.offset(-2); } if output >= 10 { - let c = (output << 1) as u32; + let c = output << 1; ptr::copy_nonoverlapping( DIGIT_TABLE.as_ptr().offset(c as isize), result.offset(-2), diff --git a/src/pretty/mod.rs b/src/pretty/mod.rs index 163eac3..6977339 100644 --- a/src/pretty/mod.rs +++ b/src/pretty/mod.rs @@ -160,8 +160,7 @@ pub unsafe fn format32(f: f32, result: *mut u8) -> usize { let bits = f.to_bits(); let sign = ((bits >> (FLOAT_MANTISSA_BITS + FLOAT_EXPONENT_BITS)) & 1) != 0; let ieee_mantissa = bits & ((1u32 << FLOAT_MANTISSA_BITS) - 1); - let ieee_exponent = - ((bits >> FLOAT_MANTISSA_BITS) & ((1u32 << FLOAT_EXPONENT_BITS) - 1)) as u32; + let ieee_exponent = (bits >> FLOAT_MANTISSA_BITS) & ((1u32 << FLOAT_EXPONENT_BITS) - 1); if ieee_exponent == 0 && ieee_mantissa == 0 { *result = b'0'; diff --git a/src/s2f.rs b/src/s2f.rs index 5d945fd..3c34c5a 100644 --- a/src/s2f.rs +++ b/src/s2f.rs @@ -217,7 +217,7 @@ pub fn s2f(buffer: &[u8]) -> Result { // for overflow here. ieee_e2 += 1; } - let ieee = ((((signed_m as u32) << f2s::FLOAT_EXPONENT_BITS) | ieee_e2 as u32) + let ieee = ((((signed_m as u32) << f2s::FLOAT_EXPONENT_BITS) | ieee_e2) << f2s::FLOAT_MANTISSA_BITS) | ieee_m2; Ok(f32::from_bits(ieee)) diff --git a/tests/exhaustive.rs b/tests/exhaustive.rs index e97045e..569bcff 100644 --- a/tests/exhaustive.rs +++ b/tests/exhaustive.rs @@ -16,7 +16,7 @@ fn test_exhaustive() { let counter = counter.clone(); let finished = finished.clone(); workers.push(thread::spawn(move || loop { - let batch = counter.fetch_add(1, Ordering::SeqCst) as u32; + let batch = counter.fetch_add(1, Ordering::Relaxed) as u32; if batch > u32::max_value() / BATCH_SIZE { return; } @@ -41,7 +41,7 @@ fn test_exhaustive() { } let increment = (max - min + 1) as usize; - let update = finished.fetch_add(increment, Ordering::SeqCst); + let update = finished.fetch_add(increment, Ordering::Relaxed); println!("{}", update + increment); })); }