diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index 048efce..66e4c38 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -26,12 +26,15 @@ jobs: runs-on: ubuntu-24.04 permissions: contents: read - # rustsec/audit-check opens an issue when it finds an advisory on a - # scheduled run, which is the only way a weekly job can reach anyone. - issues: write - checks: write steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: rustsec/audit-check@69366f33c96575abad1ee0dba8212993eecbe998 # v2.0.0 + # The project itself stays on Rust 1.83, while cargo-audit needs a newer + # compiler. The audit tool never compiles Basalt, so run it explicitly + # under stable instead of making the project toolchain drift. + - uses: dtolnay/rust-toolchain@stable with: - token: ${{ secrets.GITHUB_TOKEN }} + toolchain: stable + - name: Install cargo-audit + run: cargo +stable install cargo-audit --version 0.22.2 --locked + - name: Audit Cargo.lock + run: cargo +stable audit diff --git a/Cargo.lock b/Cargo.lock index 9832b53..49a157d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,15 +18,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] -name = "atty" -version = "0.2.14" +name = "anstyle" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "autocfg" @@ -104,12 +99,6 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.13.1" @@ -157,44 +146,45 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.25" +version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" dependencies = [ - "bitflags 1.3.2", - "clap_lex", - "indexmap", - "textwrap", + "clap_builder", ] [[package]] -name = "clap_lex" -version = "0.2.4" +name = "clap_builder" +version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" dependencies = [ - "os_str_bytes", + "anstyle", + "clap_lex", ] +[[package]] +name = "clap_lex" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" + [[package]] name = "criterion" -version = "0.4.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" +checksum = "e1c047a62b0cc3e145fa84415a3191f628e980b194c2755aa12300a4e6cbd928" dependencies = [ "anes", - "atty", "cast", "ciborium", "clap", "criterion-plot", "itertools", - "lazy_static", "num-traits", "oorandom", "regex", "serde", - "serde_derive", "serde_json", "tinytemplate", "walkdir", @@ -202,9 +192,9 @@ dependencies = [ [[package]] name = "criterion-plot" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +checksum = "9b1bcc0dc7dfae599d84ad0b1a55f80cde8af3725da8313b528da95ef783e338" dependencies = [ "cast", "itertools", @@ -266,36 +256,11 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown", -] - [[package]] name = "itertools" -version = "0.10.5" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] @@ -358,12 +323,6 @@ version = "11.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" -[[package]] -name = "os_str_bytes" -version = "6.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" - [[package]] name = "ppv-lite86" version = "0.2.21" @@ -390,7 +349,7 @@ checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.13.1", + "bitflags", "lazy_static", "num-traits", "rand", @@ -491,7 +450,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.13.1", + "bitflags", "errno", "libc", "linux-raw-sys", @@ -597,12 +556,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "textwrap" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" - [[package]] name = "thiserror" version = "2.0.20" @@ -670,22 +623,6 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - [[package]] name = "winapi-util" version = "0.1.11" @@ -695,12 +632,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows-link" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index 6761ce7..7648641 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,7 +66,7 @@ basalt-plan = { path = "crates/basalt-plan", version = "0.1.0" } # Exact pins: these are the newest releases that still build on the pinned # 1.83 toolchain (later versions pull in edition-2024 transitive dependencies). proptest = "=1.5.0" -criterion = { version = "=0.4.0", default-features = false, features = ["cargo_bench_support"] } +criterion = { version = "=0.7.0", default-features = false, features = ["cargo_bench_support"] } tempfile = "=3.13.0" # --------------------------------------------------------------------------- diff --git a/crates/basalt-encoding/benches/codecs.rs b/crates/basalt-encoding/benches/codecs.rs index 80da1e4..ef1a246 100644 --- a/crates/basalt-encoding/benches/codecs.rs +++ b/crates/basalt-encoding/benches/codecs.rs @@ -7,7 +7,7 @@ #![allow(clippy::unwrap_used)] -use std::time::Duration; +use std::{hint::black_box, time::Duration}; use basalt_common::CompressionLevel; use basalt_encoding::{ @@ -15,7 +15,7 @@ use basalt_encoding::{ encode_integers, Codec, Encoding, PlainBytesCodec, }; use basalt_types::{ColumnVector, PrimitiveArray, StringArray}; -use criterion::{black_box, BenchmarkId, Criterion, Throughput}; +use criterion::{BenchmarkId, Criterion, Throughput}; const N: usize = 65_536; diff --git a/crates/basalt-encoding/src/bytes.rs b/crates/basalt-encoding/src/bytes.rs index 2818b12..1ff4c1b 100644 --- a/crates/basalt-encoding/src/bytes.rs +++ b/crates/basalt-encoding/src/bytes.rs @@ -419,7 +419,7 @@ mod tests { ), ("long match", { let mut v = vec![0u8; 300]; - v.extend(std::iter::repeat(0u8).take(3000)); + v.extend(std::iter::repeat_n(0u8, 3000)); v }), ] diff --git a/crates/basalt-encoding/src/codec.rs b/crates/basalt-encoding/src/codec.rs index c98e6d6..1477ae5 100644 --- a/crates/basalt-encoding/src/codec.rs +++ b/crates/basalt-encoding/src/codec.rs @@ -252,7 +252,11 @@ pub(crate) fn capacity_hint(count: usize, available: usize, min_bytes_per_value: if min_bytes_per_value == 0 { count.min(NO_BYTE_BOUND_CAP) } else { - count.min(available / min_bytes_per_value + 1) + let byte_bound = available + .checked_div(min_bytes_per_value) + .unwrap_or(0) + .saturating_add(1); + count.min(byte_bound) } } @@ -324,6 +328,9 @@ mod tests { assert_eq!(capacity_hint(10, 1000, 8), 10); assert_eq!(capacity_hint(1_000_000, 16, 8), 3); assert_eq!(capacity_hint(usize::MAX, 0, 0), 1 << 20); + // A corrupted header can claim `usize::MAX` elements. Capacity + // calculation must clamp safely rather than overflowing `+ 1`. + assert_eq!(capacity_hint(usize::MAX, usize::MAX, 1), usize::MAX); } #[test] diff --git a/crates/basalt-encoding/src/column.rs b/crates/basalt-encoding/src/column.rs index c413122..ce090e0 100644 --- a/crates/basalt-encoding/src/column.rs +++ b/crates/basalt-encoding/src/column.rs @@ -399,11 +399,9 @@ pub fn decode_column(bytes: &[u8], data_type: &DataType) -> Result DataType::Float32 => { let values: Vec = decode_integers(body)? .into_iter() - .map(|v| { - u32::try_from(v as u64 & 0xFFFF_FFFF) - .map(f32::from_bits) - .unwrap_or(f32::NAN) - }) + // Integer codecs transport the IEEE representation. A cast to + // u32 preserves exactly the low 32 bits, including NaN payloads. + .map(|v| f32::from_bits(v as u32)) .collect(); ColumnVector::Float32(PrimitiveArray::try_new(Buffer::from_vec(values), validity)?) } diff --git a/crates/basalt-encoding/src/varint.rs b/crates/basalt-encoding/src/varint.rs index 081107c..4c60b0c 100644 --- a/crates/basalt-encoding/src/varint.rs +++ b/crates/basalt-encoding/src/varint.rs @@ -28,7 +28,7 @@ pub const fn uvarint_len(value: u64) -> usize { if bits == 0 { 1 } else { - ((bits + 6) / 7) as usize + bits.div_ceil(7) as usize } } diff --git a/crates/basalt-sql/src/error.rs b/crates/basalt-sql/src/error.rs index 299f9b9..3090c6c 100644 --- a/crates/basalt-sql/src/error.rs +++ b/crates/basalt-sql/src/error.rs @@ -9,7 +9,7 @@ //! problem. Hints are only attached where the fix is unambiguous — a wrong //! guess is worse than no guess. -use std::fmt; +use std::fmt::{self, Write as _}; use basalt_common::BasaltError; @@ -108,16 +108,20 @@ impl SqlError { let gutter = format!("{line}"); let pad = " ".repeat(gutter.len()); let mut out = format!("error: {}\n", self.message); - out.push_str(&format!("{pad}--> {line}:{column}\n")); - out.push_str(&format!("{pad} |\n")); - out.push_str(&format!("{gutter} | {text}\n")); - out.push_str(&format!( - "{pad} | {}{}\n", + // `String` implements `fmt::Write` infallibly; using it avoids a + // temporary allocation for every diagnostic line. + writeln!(&mut out, "{pad}--> {line}:{column}").expect("writing to a String is infallible"); + writeln!(&mut out, "{pad} |").expect("writing to a String is infallible"); + writeln!(&mut out, "{gutter} | {text}").expect("writing to a String is infallible"); + writeln!( + &mut out, + "{pad} | {}{}", " ".repeat(column - 1), "^".repeat(width) - )); + ) + .expect("writing to a String is infallible"); if let Some(hint) = &self.hint { - out.push_str(&format!("{pad} = hint: {hint}\n")); + writeln!(&mut out, "{pad} = hint: {hint}").expect("writing to a String is infallible"); } out } diff --git a/crates/basalt-sql/tests/roundtrip.rs b/crates/basalt-sql/tests/roundtrip.rs index 209b08d..7f88baf 100644 --- a/crates/basalt-sql/tests/roundtrip.rs +++ b/crates/basalt-sql/tests/roundtrip.rs @@ -7,6 +7,8 @@ #![allow(clippy::unwrap_used)] +use std::fmt::Write as _; + use basalt_sql::{parse, parse_expression}; use proptest::prelude::*; @@ -116,7 +118,7 @@ fn arbitrary_query() -> impl Strategy { sql.push_str(direction); } if let Some(limit) = limit { - sql.push_str(&format!(" LIMIT {limit}")); + write!(&mut sql, " LIMIT {limit}").expect("writing to a String is infallible"); } sql }, diff --git a/crates/basalt-storage/tests/segment.rs b/crates/basalt-storage/tests/segment.rs index 661b257..9e7b329 100644 --- a/crates/basalt-storage/tests/segment.rs +++ b/crates/basalt-storage/tests/segment.rs @@ -156,7 +156,8 @@ fn round_trip_over_many_shapes() { .with_row_group_rows(row_group_rows) .with_compression_level(level); let batch = wide_batch(&schema, rows, (rows * 31 + row_group_rows) as u64); - let (bytes, metadata) = write_segment(&schema, &[batch.clone()], config); + let (bytes, metadata) = + write_segment(&schema, std::slice::from_ref(&batch), config); let reader = SegmentReader::open(bytes).unwrap(); assert_eq!(reader.schema().as_ref(), schema.as_ref()); @@ -419,7 +420,7 @@ fn zone_maps_match_a_reference_scan() { .with_batch_size(64) .with_row_group_rows(500); let batch = wide_batch(&schema, 2000, 1234); - let (_, metadata) = write_segment(&schema, &[batch.clone()], config); + let (_, metadata) = write_segment(&schema, std::slice::from_ref(&batch), config); for (group_index, group) in metadata.row_groups.iter().enumerate() { let offset = group_index * 500; diff --git a/crates/basalt-types/src/array.rs b/crates/basalt-types/src/array.rs index d392ed9..20a9c27 100644 --- a/crates/basalt-types/src/array.rs +++ b/crates/basalt-types/src/array.rs @@ -1489,7 +1489,7 @@ mod tests { let joined = concat(&[a.clone(), b]).unwrap(); assert_eq!(joined.len(), 3); assert_eq!(joined.value(2), Value::Utf8("three".into())); - assert_eq!(concat(&[a.clone()]).unwrap(), a); + assert_eq!(concat(std::slice::from_ref(&a)).unwrap(), a); let with_nulls = ColumnVector::Int64(PrimitiveArray::from_opt_vec(vec![Some(1), None])); let dense = ColumnVector::Int64(PrimitiveArray::from_vec(vec![3]));