diff --git a/.clippy.toml b/.clippy.toml index 22fd4be..8f7dac5 100644 --- a/.clippy.toml +++ b/.clippy.toml @@ -1 +1 @@ -msrv = "1.64.0" +msrv = "1.71.0" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 38ef58c..a1b98d9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,21 +25,23 @@ jobs: strategy: fail-fast: false matrix: - rust: [nightly, beta, stable] + rust: [nightly, beta, stable, 1.86.0] timeout-minutes: 45 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{matrix.rust}} - name: Enable type layout randomization run: echo RUSTFLAGS=${RUSTFLAGS}\ -Zrandomize-layout >> $GITHUB_ENV if: matrix.rust == 'nightly' + - run: cargo build - run: cargo test + - run: cargo build --features small - run: cargo test --features small - run: cargo build --tests --features no-panic --release if: matrix.rust == 'nightly' - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v6 if: matrix.rust == 'nightly' && always() with: name: Cargo.lock @@ -47,12 +49,12 @@ jobs: continue-on-error: true msrv: - name: Rust 1.64.0 + name: Rust 1.71.0 runs-on: ubuntu-latest timeout-minutes: 45 steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@1.64.0 + - uses: actions/checkout@v6 + - uses: dtolnay/rust-toolchain@1.71.0 - run: cargo build - run: cargo build --features small @@ -63,20 +65,32 @@ jobs: env: RUSTDOCFLAGS: -Dwarnings steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@nightly - uses: dtolnay/install@cargo-docs-rs - run: cargo docs-rs miri: - name: Miri + name: Miri (${{matrix.name}}) runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - name: 64-bit little endian + target: x86_64-unknown-linux-gnu + - name: 64-bit big endian + target: powerpc64-unknown-linux-gnu + - name: 32-bit little endian + target: i686-unknown-linux-gnu + - name: 32-bit big endian + target: mips-unknown-linux-gnu timeout-minutes: 45 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@miri - run: cargo miri setup - - run: cargo miri test + - run: cargo miri test --target ${{matrix.target}} env: MIRIFLAGS: -Zmiri-strict-provenance @@ -85,7 +99,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 45 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@clippy - run: cargo clippy --tests --benches -- -Dclippy::all -Dclippy::pedantic @@ -94,10 +108,10 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 45 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable - uses: dtolnay/install@cargo-outdated - - run: cargo outdated --workspace --exit-code 1 + - run: cargo outdated --workspace --ignore rand,rand_core,rand_xorshift --exit-code 1 - run: cargo outdated --manifest-path fuzz/Cargo.toml --exit-code 1 fuzz: @@ -105,7 +119,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 45 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@nightly - uses: dtolnay/install@cargo-fuzz - run: cargo fuzz check diff --git a/Cargo.toml b/Cargo.toml index adf1c7f..4427554 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,12 +5,12 @@ authors = ["David Tolnay ", "boa-dev"] categories = ["value-formatting", "no-std", "no-std::no-alloc"] description = "Fast floating point to string conversion, ECMAScript compliant." documentation = "https://docs.rs/ryu-js" -edition = "2018" -exclude = ["build.rs", "performance.png", "chart/**"] +edition = "2021" +exclude = ["build.rs", "*.png"] keywords = ["float"] license = "Apache-2.0 OR BSL-1.0" repository = "https://github.com/boa-dev/ryu-js" -rust-version = "1.64" +rust-version = "1.71" [features] # Use smaller lookup tables. Instead of storing every required power of @@ -24,19 +24,28 @@ no-panic = { version = "0.1", optional = true } [dev-dependencies] num_cpus = "1.8" -rand = "0.9" -rand_xorshift = "0.4" -criterion = "0.5" +rand = "0.10" +rand_core = "0.10" +rand_xorshift = "0.5" + +[target.'cfg(not(miri))'.dev-dependencies] +criterion = { version = "0.8", default-features = false } [lib] bench = false +[[bench]] +name = "bench" +harness = false + [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] # See: https://github.com/rust-lang/rust/pull/84176 -rustdoc-args = ["--generate-link-to-definition"] - -[[bench]] -name = "bench" -harness = false +rustdoc-args = [ + "--generate-link-to-definition", + "--generate-macro-expansion", + "--extern-html-root-url=core=https://doc.rust-lang.org", + "--extern-html-root-url=alloc=https://doc.rust-lang.org", + "--extern-html-root-url=std=https://doc.rust-lang.org", +] diff --git a/README.md b/README.md index 89b6802..3f1d7c5 100644 --- a/README.md +++ b/README.md @@ -16,11 +16,8 @@ under the creative commons CC-BY-SA license. This Rust implementation is a line-by-line port of Ulf Adams' implementation in C, [https://github.com/ulfjack/ryu][upstream]. -*Requirements: This crate supports any compiler version back to rustc 1.64; it -uses nothing from the Rust standard library so is usable from no_std crates.* - [paper]: https://dl.acm.org/citation.cfm?id=3192369 -[upstream]: https://github.com/ulfjack/ryu/tree/abf76d252bc97300354857e64e80d4a2bf664291 +[upstream]: https://github.com/ulfjack/ryu/tree/e3e090c66efd258a0cee50ee93411ec99668d416 ```toml [dependencies] @@ -39,15 +36,7 @@ fn main() { } ``` -## Performance - - +## Performance You can run upstream's benchmarks with: diff --git a/benches/bench.rs b/benches/bench.rs index ffd24b8..41b912c 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -1,59 +1,42 @@ -// cargo bench - -#![allow( - clippy::approx_constant, - clippy::excessive_precision, - clippy::unreadable_literal -)] - -use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use criterion::{criterion_group, criterion_main, Criterion}; +use std::fmt::Display; +use std::hint; use std::io::Write; +use std::{f32, f64}; -macro_rules! benches { - ($($name:ident($value:expr),)*) => { - mod bench_ryu_js { - use super::*; - $( - pub fn $name(c: &mut Criterion) { - let mut buf = ryu_js::Buffer::new(); - - c.bench_function(concat!("ryu_js_", stringify!($name)), move |b| b.iter(move || { - let value = black_box($value); - let formatted = buf.format_finite(value); - black_box(formatted); - })); - } - )* - } - criterion_group!(bench_ryu_js, $( bench_ryu_js::$name, )*); +fn do_bench(c: &mut Criterion, group_name: &str, float: impl ryu_js::Float + Display) { + let mut group = c.benchmark_group(group_name); + group.bench_function("ryu_js", |b| { + let mut buf = ryu_js::Buffer::new(); + b.iter(move || { + let float = hint::black_box(float); + let formatted = buf.format_finite(float); + hint::black_box(formatted); + }); + }); + group.bench_function("std::fmt", |b| { + let mut buf = Vec::with_capacity(20); + b.iter(|| { + buf.clear(); + let float = hint::black_box(float); + write!(&mut buf, "{float}").unwrap(); + hint::black_box(buf.as_slice()); + }); + }); + group.finish(); +} - mod bench_std_fmt { - use super::*; - $( - pub fn $name(c: &mut Criterion) { - let mut buf = Vec::with_capacity(20); +fn bench(c: &mut Criterion) { + do_bench(c, "f64[0]", 0f64); + do_bench(c, "f64[short]", 0.1234f64); + do_bench(c, "f64[e]", f64::consts::E); + do_bench(c, "f64[max]", f64::MAX); - c.bench_function(concat!("std_fmt_", stringify!($name)), move |b| b.iter(|| { - buf.clear(); - let value = black_box($value); - write!(&mut buf, "{}", value).unwrap(); - black_box(buf.as_slice()); - })); - } - )* - } - criterion_group!(bench_std_fmt, $( bench_std_fmt::$name, )*); - criterion_main!(bench_ryu_js, bench_std_fmt); - }; + do_bench(c, "f32[0]", 0f32); + do_bench(c, "f32[short]", 0.1234f32); + do_bench(c, "f32[e]", f32::consts::E); + do_bench(c, "f32[max]", f32::MAX); } -benches! { - bench_0_f64(0_f64), - bench_short_f64(0.1234_f64), - bench_e_f64(2.718281828459045_f64), - bench_max_f64(f64::MAX), - bench_0_f32(0_f32), - bench_short_f32(0.1234_f32), - bench_e_f32(2.718281828459045_f32), - bench_max_f32(f32::MAX), -} +criterion_group!(benches, bench); +criterion_main!(benches); diff --git a/chart/.gitignore b/chart/.gitignore deleted file mode 100644 index 0017281..0000000 --- a/chart/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -/*.aux -/*.fdb_latexmk -/*.fls -/*.log -/*.pdf -/*.png -/*.svg diff --git a/chart/performance.tex b/chart/performance.tex deleted file mode 100644 index 124ffea..0000000 --- a/chart/performance.tex +++ /dev/null @@ -1,80 +0,0 @@ -\documentclass{standalone} -\usepackage{pgfplots} -\usepackage{sansmath} -\pgfplotsset{compat=1.16} -\definecolor{ryu}{HTML}{3366FF} -\definecolor{std}{HTML}{949494} -\definecolor{bg}{HTML}{CFCFCF} -\begin{document} -\pagecolor{white} -\begin{tikzpicture} -\edef\entries{ - "$0.0$", - "$0.1234$", - "$2.718281828459045$", - "$1.7976931348623157e308$", -} -\begin{axis}[ - width=5in, - height=3.5in, - ybar, - ymin=0, - bar width=24pt, - enlarge x limits={abs=39pt}, - ylabel={nanos for one call to write}, - legend style={ - anchor=north west, - at={(0.025,0.975)}, - legend columns=1, - draw=none, - fill=none, - }, - legend entries={ - ryu::Buffer::new().format\_finite(value)\\ - std::write!(\&mut buf, ``\{\}'', value)\\ - }, - legend cell align=left, - xtick={-0.5,0.5,1.5,2.5,3.5,4.5}, - xticklabels={}, - xtick pos=left, - visualization depends on={y \as \rawy}, - every node near coord/.append style={ - shift={(axis direction cs:0,-\rawy/2)}, - rotate=90, - anchor=center, - font=\sansmath\sffamily, - }, - axis background/.style={fill=bg}, - tick label style={font=\sansmath\sffamily}, - every axis label={font=\sansmath\sffamily}, - legend style={font=\sansmath\sffamily}, - label style={font=\sansmath\sffamily}, -] - \addplot[ - black, - fill=ryu, - area legend, - nodes near coords={}, - ] coordinates { - (0, 3) - (1, 40) - (2, 27) - (3, 28) - }; - \addplot[ - black, - fill=std, - area legend, - nodes near coords=\pgfmathsetmacro{\input}{{\entries}[\coordindex]}\input, - ] coordinates { - (0, 20) - (1, 66) - (2, 88) - (3, 114) - }; -\end{axis} -\pgfresetboundingbox\path - (current axis.south west) -- ++(-0.44in,-0.09in) - rectangle (current axis.north east) -- ++(0.05in,0.05in); -\end{tikzpicture} -\end{document} diff --git a/dtoa-benchmark.png b/dtoa-benchmark.png new file mode 100644 index 0000000..29c2550 Binary files /dev/null and b/dtoa-benchmark.png differ diff --git a/examples/upstream_benchmark.rs b/examples/upstream_benchmark.rs index 8e227de..16482fa 100644 --- a/examples/upstream_benchmark.rs +++ b/examples/upstream_benchmark.rs @@ -1,6 +1,6 @@ // cargo run --example upstream_benchmark --release -use rand::{Rng, SeedableRng}; +use rand::{RngExt as _, SeedableRng as _}; const SAMPLES: usize = 10000; const ITERATIONS: usize = 1000; diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index a5df692..971a1f7 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -2,14 +2,13 @@ name = "ryu-fuzz" version = "0.0.0" authors = ["David Tolnay ", "boa-dev"] -edition = "2018" +edition = "2021" publish = false [package.metadata] cargo-fuzz = true [dependencies] -arbitrary = { version = "1", features = ["derive"] } libfuzzer-sys = "0.4" ryu-js = { path = ".." } diff --git a/fuzz/fuzz_targets/fuzz_ryu_js.rs b/fuzz/fuzz_targets/fuzz_ryu_js.rs index c6a24ae..6e4cb5f 100644 --- a/fuzz/fuzz_targets/fuzz_ryu_js.rs +++ b/fuzz/fuzz_targets/fuzz_ryu_js.rs @@ -1,15 +1,8 @@ #![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 { @@ -25,12 +18,10 @@ macro_rules! ryu_js_test { }; } -fuzz_target!(|inputs: (FloatInput, bool)| { +fuzz_target!(|inputs: (f64, 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), + (val, false) => ryu_js_test!(val, format), + (val, true) => ryu_js_test!(val, format_finite), } }); diff --git a/src/buffer/mod.rs b/src/buffer/mod.rs index 1c3f593..6456380 100644 --- a/src/buffer/mod.rs +++ b/src/buffer/mod.rs @@ -72,9 +72,9 @@ impl Buffer { /// 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 + /// [`is_finite`]: f64::is_finite + /// [`is_nan`]: f64::is_nan + /// [`is_infinite`]: f64::is_infinite #[inline] #[cfg_attr(feature = "no-panic", no_panic)] pub fn format_finite(&mut self, f: F) -> &str { @@ -119,9 +119,9 @@ impl Buffer { } } +#[allow(clippy::non_canonical_clone_impl)] impl Clone for Buffer { #[inline] - #[allow(clippy::non_canonical_clone_impl)] // false positive https://github.com/rust-lang/rust-clippy/issues/11072 fn clone(&self) -> Self { Buffer::new() } diff --git a/src/f2s.rs b/src/f2s.rs index 987fefb..24ac81b 100644 --- a/src/f2s.rs +++ b/src/f2s.rs @@ -143,7 +143,6 @@ pub fn f2d(ieee_mantissa: u32, ieee_exponent: u32) -> FloatingDecimal32 { vr_is_trailing_zeros &= last_removed_digit == 0; last_removed_digit = (vr % 10) as u8; vr /= 10; - vp /= 10; vm /= 10; removed += 1; } diff --git a/src/lib.rs b/src/lib.rs index 305731c..31492ca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,7 +19,23 @@ //! assert_eq!(printed, "1.234"); //! ``` //! -//! ## Performance (lower is better) +//! ## Performance +//! +//! You can run upstream's benchmarks with: +//! +//! ```console +//! $ git clone https://github.com/ulfjack/ryu c-ryu +//! $ cd c-ryu +//! $ bazel run -c opt //ryu/benchmark +//! ``` +//! +//! And the same benchmark against our implementation with: +//! +//! ```console +//! $ git clone https://github.com/boa-dev/ryu-js rust-ryu +//! $ cd rust-ryu +//! $ cargo run --example upstream_benchmark --release +//! ``` //! //! 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 @@ -86,6 +102,8 @@ mod digit_table; mod f2s; mod f2s_intrinsics; mod pretty; +#[cfg(test)] +mod tests; pub use crate::buffer::{Buffer, Float, FloatToFixed}; diff --git a/src/pretty/exponent.rs b/src/pretty/exponent.rs index a77d0f9..f1d7aa7 100644 --- a/src/pretty/exponent.rs +++ b/src/pretty/exponent.rs @@ -6,11 +6,11 @@ pub unsafe fn write_exponent3(mut k: isize, mut result: *mut u8) -> usize { let sign = k < 0; if sign { *result = b'-'; - result = result.offset(1); + result = result.add(1); k = -k; } else { *result = b'+'; - result = result.offset(1); + result = result.add(1); } debug_assert!(k < 1000); @@ -18,7 +18,7 @@ pub unsafe fn write_exponent3(mut k: isize, mut result: *mut u8) -> usize { *result = b'0' + (k / 100) as u8; k %= 100; let d = DIGIT_TABLE.as_ptr().offset(k * 2); - ptr::copy_nonoverlapping(d, result.offset(1), 2); + ptr::copy_nonoverlapping(d, result.add(1), 2); 4 } else if k >= 10 { let d = DIGIT_TABLE.as_ptr().offset(k * 2); @@ -35,11 +35,11 @@ pub unsafe fn write_exponent2(mut k: isize, mut result: *mut u8) -> usize { let sign = k < 0; if sign { *result = b'-'; - result = result.offset(1); + result = result.add(1); k = -k; } else { *result = b'+'; - result = result.offset(1); + result = result.add(1); } debug_assert!(k < 100); diff --git a/src/pretty/mantissa.rs b/src/pretty/mantissa.rs index 552dfe3..d0f6acf 100644 --- a/src/pretty/mantissa.rs +++ b/src/pretty/mantissa.rs @@ -15,27 +15,11 @@ pub unsafe fn write_mantissa_long(mut output: u64, mut result: *mut u8) { let c1 = (c / 100) << 1; let d0 = (d % 100) << 1; let d1 = (d / 100) << 1; - ptr::copy_nonoverlapping( - DIGIT_TABLE.as_ptr().offset(c0 as isize), - result.offset(-2), - 2, - ); - ptr::copy_nonoverlapping( - DIGIT_TABLE.as_ptr().offset(c1 as isize), - result.offset(-4), - 2, - ); - ptr::copy_nonoverlapping( - DIGIT_TABLE.as_ptr().offset(d0 as isize), - result.offset(-6), - 2, - ); - ptr::copy_nonoverlapping( - DIGIT_TABLE.as_ptr().offset(d1 as isize), - result.offset(-8), - 2, - ); - result = result.offset(-8); + ptr::copy_nonoverlapping(DIGIT_TABLE.as_ptr().offset(c0 as isize), result.sub(2), 2); + ptr::copy_nonoverlapping(DIGIT_TABLE.as_ptr().offset(c1 as isize), result.sub(4), 2); + ptr::copy_nonoverlapping(DIGIT_TABLE.as_ptr().offset(d0 as isize), result.sub(6), 2); + ptr::copy_nonoverlapping(DIGIT_TABLE.as_ptr().offset(d1 as isize), result.sub(8), 2); + result = result.sub(8); } write_mantissa(output as u32, result); } @@ -47,36 +31,20 @@ pub unsafe fn write_mantissa(mut output: u32, mut result: *mut u8) { output /= 10_000; let c0 = (c % 100) << 1; let c1 = (c / 100) << 1; - ptr::copy_nonoverlapping( - DIGIT_TABLE.as_ptr().offset(c0 as isize), - result.offset(-2), - 2, - ); - ptr::copy_nonoverlapping( - DIGIT_TABLE.as_ptr().offset(c1 as isize), - result.offset(-4), - 2, - ); - result = result.offset(-4); + ptr::copy_nonoverlapping(DIGIT_TABLE.as_ptr().offset(c0 as isize), result.sub(2), 2); + ptr::copy_nonoverlapping(DIGIT_TABLE.as_ptr().offset(c1 as isize), result.sub(4), 2); + result = result.sub(4); } if output >= 100 { let c = (output % 100) << 1; output /= 100; - ptr::copy_nonoverlapping( - DIGIT_TABLE.as_ptr().offset(c as isize), - result.offset(-2), - 2, - ); - result = result.offset(-2); + ptr::copy_nonoverlapping(DIGIT_TABLE.as_ptr().offset(c as isize), result.sub(2), 2); + result = result.sub(2); } if output >= 10 { let c = output << 1; - ptr::copy_nonoverlapping( - DIGIT_TABLE.as_ptr().offset(c as isize), - result.offset(-2), - 2, - ); + ptr::copy_nonoverlapping(DIGIT_TABLE.as_ptr().offset(c as isize), result.sub(2), 2); } else { - *result.offset(-1) = b'0' + output as u8; + *result.sub(1) = b'0' + output as u8; } } diff --git a/src/pretty/mod.rs b/src/pretty/mod.rs index f69b932..82fd22b 100644 --- a/src/pretty/mod.rs +++ b/src/pretty/mod.rs @@ -26,9 +26,9 @@ pub use to_fixed::format64_to_fixed; /// 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 +/// [`is_finite`]: f64::is_finite +/// [`is_nan`]: f64::is_nan +/// [`is_infinite`]: f64::is_infinite /// /// ## Safety /// @@ -133,9 +133,9 @@ pub unsafe fn format64(f: f64, result: *mut u8) -> usize { /// 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.f32.html#method.is_finite -/// [`is_nan`]: https://doc.rust-lang.org/std/primitive.f32.html#method.is_nan -/// [`is_infinite`]: https://doc.rust-lang.org/std/primitive.f32.html#method.is_infinite +/// [`is_finite`]: f32::is_finite +/// [`is_nan`]: f32::is_nan +/// [`is_infinite`]: f32::is_infinite /// /// ## Safety /// diff --git a/src/pretty/to_fixed/mod.rs b/src/pretty/to_fixed/mod.rs index d371b68..3abc5f9 100644 --- a/src/pretty/to_fixed/mod.rs +++ b/src/pretty/to_fixed/mod.rs @@ -111,7 +111,7 @@ impl Cursor { .offset(5 - i as isize) .copy_from_nonoverlapping(DIGIT_TABLE.as_ptr().offset(c1 as isize), 2); } - *(result.offset(0)) = b'0' + digits as u8; + *result = b'0' + digits as u8; self.index += count; } diff --git a/src/tests.rs b/src/tests.rs new file mode 100644 index 0000000..9150570 --- /dev/null +++ b/src/tests.rs @@ -0,0 +1,29 @@ +use crate::digit_table; +use core::mem; + +const _: () = { + let mut static_data = mem::size_of_val(&digit_table::DIGIT_TABLE); + + #[cfg(feature = "small")] + { + use crate::d2s_small_table; + + static_data += mem::size_of_val(&d2s_small_table::DOUBLE_POW5_INV_SPLIT2) + + mem::size_of_val(&d2s_small_table::POW5_INV_OFFSETS) + + mem::size_of_val(&d2s_small_table::DOUBLE_POW5_SPLIT2) + + mem::size_of_val(&d2s_small_table::POW5_OFFSETS) + + mem::size_of_val(&d2s_small_table::DOUBLE_POW5_TABLE); + + assert!(static_data == 1016); + } + + #[cfg(not(feature = "small"))] + { + use crate::d2s_full_table; + + static_data += mem::size_of_val(&d2s_full_table::DOUBLE_POW5_INV_SPLIT) + + mem::size_of_val(&d2s_full_table::DOUBLE_POW5_SPLIT); + + assert!(static_data == 10888); // 10.6K + } +}; diff --git a/tests/d2s_table_test.rs b/tests/d2s_table_test.rs index b592b14..434eae8 100644 --- a/tests/d2s_table_test.rs +++ b/tests/d2s_table_test.rs @@ -24,6 +24,7 @@ clippy::cast_possible_truncation, clippy::cast_possible_wrap, clippy::cast_sign_loss, + clippy::uninlined_format_args, clippy::unreadable_literal, clippy::unseparated_literal_suffix, clippy::wildcard_imports diff --git a/tests/d2s_test.rs b/tests/d2s_test.rs index 2a4c620..898ffc1 100644 --- a/tests/d2s_test.rs +++ b/tests/d2s_test.rs @@ -73,7 +73,7 @@ fn test_random() { fn test_non_finite() { for i in 0u64..1 << 23 { let f = f64::from_bits((((1 << 11) - 1) << 52) + (i << 29)); - assert!(!f.is_finite(), "f={}", f); + assert!(!f.is_finite(), "f={f}"); ryu_js::Buffer::new().format_finite(f); } } diff --git a/tests/exhaustive.rs b/tests/exhaustive.rs index 1e05ff0..6716986 100644 --- a/tests/exhaustive.rs +++ b/tests/exhaustive.rs @@ -1,8 +1,7 @@ #![cfg_attr(not(check_cfg), allow(unexpected_cfgs))] -#![allow(clippy::cast_possible_truncation)] use std::str; -use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::Arc; use std::thread; @@ -10,15 +9,15 @@ use std::thread; #[cfg_attr(not(exhaustive), ignore = "requires cfg(exhaustive)")] fn test_exhaustive() { const BATCH_SIZE: u32 = 1_000_000; - let counter = Arc::new(AtomicUsize::new(0)); - let finished = Arc::new(AtomicUsize::new(0)); + let counter = Arc::new(AtomicU32::new(0)); + let finished = Arc::new(AtomicU32::new(0)); let mut workers = Vec::new(); for _ in 0..num_cpus::get() { let counter = counter.clone(); let finished = finished.clone(); workers.push(thread::spawn(move || loop { - let batch = counter.fetch_add(1, Ordering::Relaxed) as u32; + let batch = counter.fetch_add(1, Ordering::Relaxed); if batch > u32::MAX / BATCH_SIZE { return; } @@ -37,14 +36,14 @@ fn test_exhaustive() { if !f.is_finite() { continue; } - let n = unsafe { ryu_js::raw::format32(f, &mut bytes[0]) }; + let n = unsafe { ryu_js::raw::format32(f, bytes.as_mut_ptr()) }; assert_eq!(Ok(Ok(f)), str::from_utf8(&bytes[..n]).map(str::parse)); assert_eq!(Ok(f), buffer.format_finite(f).parse()); } - let increment = (max - min + 1) as usize; + let increment = max - min + 1; let update = finished.fetch_add(increment, Ordering::Relaxed); - println!("{}", update + increment); + println!("{}", u64::from(update) + u64::from(increment)); })); } diff --git a/tests/f2s_test.rs b/tests/f2s_test.rs index e6e41bd..f08e358 100644 --- a/tests/f2s_test.rs +++ b/tests/f2s_test.rs @@ -63,7 +63,7 @@ fn test_random() { fn test_non_finite() { for i in 0u32..1 << 23 { let f = f32::from_bits((((1 << 8) - 1) << 23) + i); - assert!(!f.is_finite(), "f={}", f); + assert!(!f.is_finite(), "f={f}"); ryu_js::Buffer::new().format_finite(f); } } diff --git a/tests/s2d_test.rs b/tests/s2d_test.rs index 5eec2a0..e3d1f78 100644 --- a/tests/s2d_test.rs +++ b/tests/s2d_test.rs @@ -18,7 +18,6 @@ // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. -#![allow(clippy::float_cmp)] #![cfg(not(feature = "small"))] #![allow(dead_code)] #![allow( diff --git a/tests/s2f_test.rs b/tests/s2f_test.rs index 5bae935..23da8c1 100644 --- a/tests/s2f_test.rs +++ b/tests/s2f_test.rs @@ -23,7 +23,6 @@ clippy::cast_lossless, clippy::cast_possible_truncation, clippy::cast_possible_wrap, - clippy::cast_possible_wrap, clippy::cast_sign_loss, clippy::checked_conversions, clippy::float_cmp,