Skip to content

Commit 40923a7

Browse files
committed
Merge remote-tracking branch 'origin/develop' into ji/slots-for-arrays
Signed-off-by: Joe Isaacs <joe.isaacs@live.co.uk>
2 parents 15573bb + 124c698 commit 40923a7

65 files changed

Lines changed: 2709 additions & 332 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/fuzz.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,42 @@ jobs:
9999
gh_token: ${{ secrets.GITHUB_TOKEN }}
100100
incident_io_alert_token: ${{ secrets.INCIDENT_IO_ALERT_TOKEN }}
101101

102+
# ============================================================================
103+
# FSST LIKE Fuzzer
104+
# ============================================================================
105+
fsst_like_fuzz:
106+
name: "FSST LIKE Fuzz"
107+
uses: ./.github/workflows/run-fuzzer.yml
108+
with:
109+
fuzz_target: fsst_like
110+
jobs: 16
111+
secrets:
112+
R2_FUZZ_ACCESS_KEY_ID: ${{ secrets.R2_FUZZ_ACCESS_KEY_ID }}
113+
R2_FUZZ_SECRET_ACCESS_KEY: ${{ secrets.R2_FUZZ_SECRET_ACCESS_KEY }}
114+
115+
report-fsst-like-fuzz-failures:
116+
name: "Report FSST LIKE Fuzz Failures"
117+
needs: fsst_like_fuzz
118+
if: always() && needs.fsst_like_fuzz.outputs.crashes_found == 'true'
119+
permissions:
120+
issues: write
121+
contents: read
122+
id-token: write
123+
pull-requests: read
124+
uses: ./.github/workflows/report-fuzz-crash.yml
125+
with:
126+
fuzz_target: fsst_like
127+
crash_file: ${{ needs.fsst_like_fuzz.outputs.first_crash_name }}
128+
artifact_url: ${{ needs.fsst_like_fuzz.outputs.artifact_url }}
129+
artifact_name: fsst_like-crash-artifacts
130+
logs_artifact_name: fsst_like-logs
131+
branch: ${{ github.ref_name }}
132+
commit: ${{ github.sha }}
133+
secrets:
134+
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
135+
gh_token: ${{ secrets.GITHUB_TOKEN }}
136+
incident_io_alert_token: ${{ secrets.INCIDENT_IO_ALERT_TOKEN }}
137+
102138
# ============================================================================
103139
# Compress Roundtrip Fuzzer
104140
# ============================================================================

CLAUDE.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
time you reach a stopping point or think you've finished work.
99
* run `cargo +nightly fmt --all` to format Rust source files. Please do this every time you reach a stopping point or
1010
think you've finished work.
11+
* run `cargo xtask public-api` to re-generate the public API lock files. Please do this every time you reach a stopping
12+
point or think you've finished work.
1113
* you can try running
1214
`cargo fix --lib --allow-dirty --allow-staged && cargo clippy --fix --lib --allow-dirty --allow-staged` to
1315
automatically many fix minor errors.
@@ -47,8 +49,11 @@
4749
* Prefer having test return VortexResult<()> and use ? over unwrap.
4850
* All imports must be at the top of the module, never inside functions. The only exception is `#[cfg(test)]` blocks,
4951
where imports should be at the top of the test module. Function-scoped imports are only acceptable when (a) required,
50-
or (b) it would be exceptionally verbose otherwise, such as a match statement where left and right sides have similar names.
51-
* Only write comments that explain non-obvious logic or important context. Avoid commenting simple or self-explanatory code.
52+
or (b) it would be exceptionally verbose otherwise, such as a match statement where left and right sides have similar
53+
names.
54+
* Imports should be preferred over qualified identifiers.
55+
* Only write comments that explain non-obvious logic or important context. Avoid commenting simple or self-explanatory
56+
code.
5257
* Use `assert_arrays_eq!` macro for comparing arrays in tests instead of element-by-element comparison.
5358
* Keep tests concise and to the point - avoid unnecessary setup or verbose assertions.
5459
* Run tests for a specific crate with `cargo test -p <crate-name>` (e.g., `cargo test -p vortex-array`).

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

_typos.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
extend-ignore-identifiers-re = ["ffor", "FFOR", "FoR", "typ", "ratatui"]
33
# We support a few common special comments to tell the checker to ignore sections of code
44
extend-ignore-re = [
5-
"(#|//)\\s*spellchecker:ignore-next-line\\n.*", # Ignore the next line
6-
"(?Rm)^.*(#|//)\\s*spellchecker:disable-line$", # Ignore line that ends with this hint
5+
"(#|//)\\s*spellchecker:ignore-next-line\\n.*", # Ignore the next line
6+
"(?Rm)^.*(#|//)\\s*spellchecker:disable-line$", # Ignore line that ends with this hint
77
"(?s)(#|//)\\s*spellchecker:off.*?\\n\\s*(#|//)\\s*spellchecker:on", # Ignore block between hints
88
]
99

1010
[files]
11-
extend-exclude = ["/vortex-bench/**", "/docs/references.bib", "benchmarks/**", "vortex-sqllogictest/slt/**"]
11+
extend-exclude = ["/vortex-bench/**", "/docs/references.bib", "benchmarks/**", "vortex-sqllogictest/slt/**", "encodings/fsst/src/dfa/tests.rs", "encodings/fsst/src/dfa/flat_contains.rs"]
1212

1313
[type.py]
1414
extend-ignore-identifiers-re = [

benchmarks/compress-bench/src/main.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ use indicatif::ProgressBar;
1313
use itertools::Itertools;
1414
use regex::Regex;
1515
use vortex::utils::aliases::hash_map::HashMap;
16-
use vortex_bench::BenchmarkOutput;
1716
use vortex_bench::Engine;
1817
use vortex_bench::Format;
1918
use vortex_bench::Target;
@@ -23,6 +22,7 @@ use vortex_bench::compress::Compressor;
2322
use vortex_bench::compress::benchmark_compress;
2423
use vortex_bench::compress::benchmark_decompress;
2524
use vortex_bench::compress::calculate_ratios;
25+
use vortex_bench::create_output_writer;
2626
use vortex_bench::datasets::Dataset;
2727
use vortex_bench::datasets::struct_list_of_ints::StructListOfInts;
2828
use vortex_bench::datasets::taxi_data::TaxiData;
@@ -169,8 +169,7 @@ async fn run_compress(
169169

170170
progress.finish();
171171

172-
let output = BenchmarkOutput::with_path(BENCHMARK_ID, output_path);
173-
let mut writer = output.create_writer()?;
172+
let mut writer = create_output_writer(&display_format, output_path, BENCHMARK_ID)?;
174173

175174
match display_format {
176175
DisplayFormat::Table => {

benchmarks/random-access-bench/src/main.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ use rand::SeedableRng;
1414
use rand::rngs::StdRng;
1515
use rand_distr::Distribution;
1616
use rand_distr::Exp;
17-
use vortex_bench::BenchmarkOutput;
1817
use vortex_bench::Engine;
1918
use vortex_bench::Format;
2019
use vortex_bench::Target;
20+
use vortex_bench::create_output_writer;
2121
use vortex_bench::datasets::feature_vectors::FeatureVectorsData;
2222
use vortex_bench::datasets::nested_lists::NestedListsData;
2323
use vortex_bench::datasets::nested_structs::NestedStructsData;
@@ -416,8 +416,7 @@ async fn run_random_access(
416416

417417
progress.finish();
418418

419-
let output = BenchmarkOutput::with_path(BENCHMARK_ID, output_path);
420-
let mut writer = output.create_writer()?;
419+
let mut writer = create_output_writer(&display_format, output_path, BENCHMARK_ID)?;
421420

422421
match display_format {
423422
DisplayFormat::Table => {

encodings/fastlanes/public-api.lock

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,18 @@ pub mod vortex_fastlanes
22

33
pub mod vortex_fastlanes::bit_transpose
44

5+
pub fn vortex_fastlanes::bit_transpose::transpose_bitbuffer(bits: vortex_buffer::bit::buf::BitBuffer) -> vortex_buffer::bit::buf::BitBuffer
6+
57
pub fn vortex_fastlanes::bit_transpose::transpose_bits(input: &[u8; 128], output: &mut [u8; 128])
68

9+
pub fn vortex_fastlanes::bit_transpose::transpose_validity(validity: &vortex_array::validity::Validity, ctx: &mut vortex_array::executor::ExecutionCtx) -> vortex_error::VortexResult<vortex_array::validity::Validity>
10+
11+
pub fn vortex_fastlanes::bit_transpose::untranspose_bitbuffer(bits: vortex_buffer::bit::buf::BitBuffer) -> vortex_buffer::bit::buf::BitBuffer
12+
713
pub fn vortex_fastlanes::bit_transpose::untranspose_bits(input: &[u8; 128], output: &mut [u8; 128])
814

15+
pub fn vortex_fastlanes::bit_transpose::untranspose_validity(validity: &vortex_array::validity::Validity, ctx: &mut vortex_array::executor::ExecutionCtx) -> vortex_error::VortexResult<vortex_array::validity::Validity>
16+
917
pub mod vortex_fastlanes::bitpack_compress
1018

1119
pub fn vortex_fastlanes::bitpack_compress::bit_width_histogram(array: &vortex_array::arrays::primitive::array::PrimitiveArray) -> vortex_error::VortexResult<alloc::vec::Vec<usize>>
@@ -294,7 +302,7 @@ pub type vortex_fastlanes::Delta::Metadata = vortex_array::metadata::ProstMetada
294302

295303
pub type vortex_fastlanes::Delta::OperationsVTable = vortex_fastlanes::Delta
296304

297-
pub type vortex_fastlanes::Delta::ValidityVTable = vortex_array::vtable::validity::ValidityVTableFromChildSliceHelper
305+
pub type vortex_fastlanes::Delta::ValidityVTable = vortex_fastlanes::Delta
298306

299307
pub fn vortex_fastlanes::Delta::array_eq(array: &vortex_fastlanes::DeltaArray, other: &vortex_fastlanes::DeltaArray, precision: vortex_array::hash::Precision) -> bool
300308

@@ -336,6 +344,10 @@ impl vortex_array::vtable::operations::OperationsVTable<vortex_fastlanes::Delta>
336344

337345
pub fn vortex_fastlanes::Delta::scalar_at(array: &vortex_fastlanes::DeltaArray, index: usize) -> vortex_error::VortexResult<vortex_array::scalar::Scalar>
338346

347+
impl vortex_array::vtable::validity::ValidityVTable<vortex_fastlanes::Delta> for vortex_fastlanes::Delta
348+
349+
pub fn vortex_fastlanes::Delta::validity(array: &vortex_fastlanes::DeltaArray) -> vortex_error::VortexResult<vortex_array::validity::Validity>
350+
339351
pub struct vortex_fastlanes::DeltaArray
340352

341353
impl vortex_fastlanes::DeltaArray
@@ -354,9 +366,7 @@ pub fn vortex_fastlanes::DeltaArray::offset(&self) -> usize
354366

355367
pub fn vortex_fastlanes::DeltaArray::try_from_delta_compress_parts(bases: vortex_array::array::ArrayRef, deltas: vortex_array::array::ArrayRef) -> vortex_error::VortexResult<Self>
356368

357-
pub fn vortex_fastlanes::DeltaArray::try_from_primitive_array(array: &vortex_array::arrays::primitive::array::PrimitiveArray) -> vortex_error::VortexResult<Self>
358-
359-
pub fn vortex_fastlanes::DeltaArray::try_from_vec<T: vortex_array::dtype::ptype::NativePType>(vec: alloc::vec::Vec<T>) -> vortex_error::VortexResult<Self>
369+
pub fn vortex_fastlanes::DeltaArray::try_from_primitive_array(array: &vortex_array::arrays::primitive::array::PrimitiveArray, ctx: &mut vortex_array::executor::ExecutionCtx) -> vortex_error::VortexResult<Self>
360370

361371
pub fn vortex_fastlanes::DeltaArray::try_new(bases: vortex_array::array::ArrayRef, deltas: vortex_array::array::ArrayRef, offset: usize, logical_len: usize) -> vortex_error::VortexResult<Self>
362372

@@ -390,10 +400,6 @@ impl vortex_array::array::IntoArray for vortex_fastlanes::DeltaArray
390400

391401
pub fn vortex_fastlanes::DeltaArray::into_array(self) -> vortex_array::array::ArrayRef
392402

393-
impl vortex_array::vtable::validity::ValidityChildSliceHelper for vortex_fastlanes::DeltaArray
394-
395-
pub fn vortex_fastlanes::DeltaArray::unsliced_child_and_slice(&self) -> (&vortex_array::array::ArrayRef, usize, usize)
396-
397403
pub struct vortex_fastlanes::FoR
398404

399405
impl vortex_fastlanes::FoR
@@ -660,6 +666,6 @@ impl vortex_array::vtable::validity::ValidityChildSliceHelper for vortex_fastlan
660666

661667
pub fn vortex_fastlanes::RLEArray::unsliced_child_and_slice(&self) -> (&vortex_array::array::ArrayRef, usize, usize)
662668

663-
pub fn vortex_fastlanes::delta_compress(array: &vortex_array::arrays::primitive::array::PrimitiveArray) -> vortex_error::VortexResult<(vortex_array::arrays::primitive::array::PrimitiveArray, vortex_array::arrays::primitive::array::PrimitiveArray)>
669+
pub fn vortex_fastlanes::delta_compress(array: &vortex_array::arrays::primitive::array::PrimitiveArray, ctx: &mut vortex_array::executor::ExecutionCtx) -> vortex_error::VortexResult<(vortex_array::arrays::primitive::array::PrimitiveArray, vortex_array::arrays::primitive::array::PrimitiveArray)>
664670

665671
pub fn vortex_fastlanes::initialize(session: &mut vortex_session::VortexSession)

encodings/fastlanes/src/bit_transpose/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ mod scalar;
2525
#[cfg(not(feature = "_test-harness"))]
2626
mod x86;
2727

28+
mod validity;
29+
30+
pub use validity::*;
31+
2832
/// Base indices for the first 64 output bytes (lanes 0-7).
2933
/// Each entry indicates the starting input byte index for that output byte group.
3034
/// Pattern: [0*2, 4*2, 2*2, 6*2, 1*2, 5*2, 3*2, 7*2] = [0, 8, 4, 12, 2, 10, 6, 14]
@@ -39,6 +43,8 @@ const TRANSPOSE_2X2: u64 = 0x00AA_00AA_00AA_00AA;
3943
const TRANSPOSE_4X4: u64 = 0x0000_CCCC_0000_CCCC;
4044
const TRANSPOSE_8X8: u64 = 0x0000_0000_F0F0_F0F0;
4145

46+
/// Transpose 1024-bits into FastLanes layout.
47+
///
4248
/// Dispatch to the best available implementation at runtime.
4349
#[inline]
4450
pub fn transpose_bits(input: &[u8; 128], output: &mut [u8; 128]) {
@@ -64,6 +70,8 @@ pub fn transpose_bits(input: &[u8; 128], output: &mut [u8; 128]) {
6470
scalar::transpose_bits_scalar(input, output);
6571
}
6672

73+
/// Untranspose 1024-bits from FastLanes layout.
74+
///
6775
/// Dispatch untranspose to the best available implementation at runtime.
6876
#[inline]
6977
pub fn untranspose_bits(input: &[u8; 128], output: &mut [u8; 128]) {
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
use std::mem;
5+
use std::mem::MaybeUninit;
6+
7+
use vortex_array::Canonical;
8+
use vortex_array::ExecutionCtx;
9+
use vortex_array::IntoArray;
10+
use vortex_array::arrays::BoolArray;
11+
use vortex_array::validity::Validity;
12+
use vortex_buffer::BitBuffer;
13+
use vortex_buffer::ByteBuffer;
14+
use vortex_buffer::ByteBufferMut;
15+
use vortex_error::VortexExpect;
16+
use vortex_error::VortexResult;
17+
18+
use crate::bit_transpose::transpose_bits;
19+
use crate::bit_transpose::untranspose_bits;
20+
21+
pub fn transpose_validity(validity: &Validity, ctx: &mut ExecutionCtx) -> VortexResult<Validity> {
22+
match validity {
23+
Validity::Array(mask) => {
24+
let bools = mask
25+
.clone()
26+
.execute::<Canonical>(ctx)?
27+
.into_bool()
28+
.into_bit_buffer();
29+
30+
Ok(Validity::Array(
31+
BoolArray::new(transpose_bitbuffer(bools), Validity::NonNullable).into_array(),
32+
))
33+
}
34+
v @ Validity::AllValid | v @ Validity::AllInvalid | v @ Validity::NonNullable => {
35+
Ok(v.clone())
36+
}
37+
}
38+
}
39+
40+
#[inline]
41+
pub fn transpose_bitbuffer(bits: BitBuffer) -> BitBuffer {
42+
let (offset, len, bytes) = bits.into_inner();
43+
44+
if bytes.len().is_multiple_of(128) {
45+
match bytes.try_into_mut() {
46+
Ok(mut bytes_mut) => {
47+
// We can ignore the spare trailer capacity that can be an artifact of allocator as we requested 128 multiple chunks
48+
let (chunks, _) = bytes_mut.as_chunks_mut::<128>();
49+
let mut tmp = [0u8; 128];
50+
for chunk in chunks {
51+
transpose_bits(chunk, &mut tmp);
52+
chunk.copy_from_slice(&tmp);
53+
}
54+
BitBuffer::new_with_offset(bytes_mut.freeze().into_byte_buffer(), len, offset)
55+
}
56+
Err(bytes) => bits_op_with_copy(bytes, len, offset, transpose_bits),
57+
}
58+
} else {
59+
bits_op_with_copy(bytes, len, offset, transpose_bits)
60+
}
61+
}
62+
63+
pub fn untranspose_validity(validity: &Validity, ctx: &mut ExecutionCtx) -> VortexResult<Validity> {
64+
match validity {
65+
Validity::Array(mask) => {
66+
let bools = mask
67+
.clone()
68+
.execute::<Canonical>(ctx)?
69+
.into_bool()
70+
.into_bit_buffer();
71+
72+
Ok(Validity::Array(
73+
BoolArray::new(untranspose_bitbuffer(bools), Validity::NonNullable).into_array(),
74+
))
75+
}
76+
v @ Validity::AllValid | v @ Validity::AllInvalid | v @ Validity::NonNullable => {
77+
Ok(v.clone())
78+
}
79+
}
80+
}
81+
82+
#[inline]
83+
pub fn untranspose_bitbuffer(bits: BitBuffer) -> BitBuffer {
84+
assert!(
85+
bits.inner().len().is_multiple_of(128),
86+
"Transpose BitBuffer must be 128-byte aligned"
87+
);
88+
let (offset, len, bytes) = bits.into_inner();
89+
match bytes.try_into_mut() {
90+
Ok(mut bytes_mut) => {
91+
let (chunks, _) = bytes_mut.as_chunks_mut::<128>();
92+
let mut tmp = [0u8; 128];
93+
for chunk in chunks {
94+
untranspose_bits(chunk, &mut tmp);
95+
chunk.copy_from_slice(&tmp);
96+
}
97+
BitBuffer::new_with_offset(bytes_mut.freeze().into_byte_buffer(), len, offset)
98+
}
99+
Err(bytes) => bits_op_with_copy(bytes, len, offset, untranspose_bits),
100+
}
101+
}
102+
103+
fn bits_op_with_copy<F: Fn(&[u8; 128], &mut [u8; 128])>(
104+
bytes: ByteBuffer,
105+
len: usize,
106+
offset: usize,
107+
op: F,
108+
) -> BitBuffer {
109+
let output_len = bytes.len().next_multiple_of(128);
110+
let mut output = ByteBufferMut::with_capacity(output_len);
111+
let (input_chunks, input_trailer) = bytes.as_chunks::<128>();
112+
// We can ignore the spare trailer capacity that can be an artifact of allocator as we requested 128 multiple chunks
113+
let (output_chunks, _) = output.spare_capacity_mut().as_chunks_mut::<128>();
114+
115+
for (input, output) in input_chunks.iter().zip(output_chunks.iter_mut()) {
116+
op(input, unsafe {
117+
mem::transmute::<&mut [MaybeUninit<u8>; 128], &mut [u8; 128]>(output)
118+
});
119+
}
120+
121+
if !input_trailer.is_empty() {
122+
let mut padded_input = [0u8; 128];
123+
padded_input[0..input_trailer.len()].clone_from_slice(input_trailer);
124+
op(&padded_input, unsafe {
125+
mem::transmute::<&mut [MaybeUninit<u8>; 128], &mut [u8; 128]>(
126+
output_chunks
127+
.last_mut()
128+
.vortex_expect("Output wasn't a multiple of 128 bytes"),
129+
)
130+
});
131+
}
132+
133+
unsafe { output.set_len(output_len) };
134+
BitBuffer::new_with_offset(
135+
output.freeze().into_byte_buffer(),
136+
len.next_multiple_of(1024),
137+
offset,
138+
)
139+
}

0 commit comments

Comments
 (0)