Skip to content
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
15e85b7
tests: improve coverage throughout
noamteyssier Jul 7, 2026
90dfea6
fix: bug in bq stream reader with incorrect id for small buffers
noamteyssier Jul 7, 2026
ba5814d
tests: improve coverage
noamteyssier Jul 7, 2026
ee9f663
style: fmt
noamteyssier Jul 7, 2026
6b2848c
ci: skip unnecessary runs
noamteyssier Jul 7, 2026
601e58a
style: clippy fix suggestions
noamteyssier Jul 7, 2026
dab2eea
ci: add clippy check
noamteyssier Jul 7, 2026
7dbe81e
ci: update checkout version
noamteyssier Jul 7, 2026
e7ada38
ci: added additional lint allowances
noamteyssier Jul 7, 2026
ade8aef
style: clippy fix suggestions
noamteyssier Jul 7, 2026
826126f
ci: added clippy warnings
noamteyssier Jul 7, 2026
ee703e9
style: clippy fix suggestions
noamteyssier Jul 7, 2026
ec3b43a
Merge pull request #97 from ArcInstitute/test/coverage
noamteyssier Jul 7, 2026
8ff162a
dep: update dependencies
noamteyssier Jul 16, 2026
19e2de5
Merge pull request #98 from ArcInstitute/dep/upgrade-dependencies
noamteyssier Jul 16, 2026
f877ba1
docs: keep a changelog
noamteyssier Jul 16, 2026
fb21b39
Merge branch 'dev-0.9.4' of github.com:arcinstitute/binseq into dev-0…
noamteyssier Jul 16, 2026
0a18840
chore(semver): bump
noamteyssier Jul 16, 2026
a191e1f
style: clippy fix suggestions
noamteyssier Jul 16, 2026
3270b5e
refactor: expose magic through crate
noamteyssier Jul 16, 2026
2dd17a5
feat: added format detection error type
noamteyssier Jul 16, 2026
fce6fa7
feat: determine format from magic bytes
noamteyssier Jul 16, 2026
e65f4b3
docs: update changelog
noamteyssier Jul 16, 2026
b018101
refactor: allocate on stack instead of heap
noamteyssier Jul 16, 2026
329e562
Merge pull request #100 from ArcInstitute/feat/reader-from-bytes
noamteyssier Jul 16, 2026
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
19 changes: 11 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
name: CI

on: [push, pull_request]
on:
push:
branches: [main]
pull_request:

env:
CARGO_TERM_COLOR: always
Expand All @@ -9,7 +12,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v7
- name: Build
run: cargo build --verbose
- name: Run tests
Expand All @@ -20,19 +23,19 @@ jobs:
fmt_lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v7
- name: Formatting
run: cargo fmt --check
- name: Linting
run: cargo clippy --verbose
run: cargo clippy --all-features --all-targets -- -D warnings

example_grep:
runs-on: ubuntu-latest
strategy:
matrix:
ext: [bq, vbq, cbq]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v7
- name: run example ${{ matrix.ext }}
run: cargo run --release --example grep -- ./data/subset.${{ matrix.ext }} "ACGTACGT"

Expand All @@ -42,7 +45,7 @@ jobs:
matrix:
ext: [bq, vbq, cbq]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v7
- name: run example ${{ matrix.ext }}
run: cargo run --release --example parallel_range -- ./data/subset.${{ matrix.ext }} 4 30 200

Expand All @@ -52,7 +55,7 @@ jobs:
matrix:
ext: [bq, vbq, cbq]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v7
- name: run example (single) ${{ matrix.ext }}
run: cargo run --release --example write -- ./data/subset_R1.fastq.gz -o ./output.${{ matrix.ext }}
- name: run example (paired) ${{ matrix.ext }}
Expand All @@ -64,6 +67,6 @@ jobs:
matrix:
ext: [bq, vbq, cbq]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v7
- name: run example ${{ matrix.ext }}
run: cargo run --release --example read -- ./data/subset.${{ matrix.ext }}
167 changes: 167 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.9.4] - 2026-07-15

### Fixed

- Bug in the `bq` stream reader that used an incorrect record id for small buffers.

### Changed

- Added clippy checks and additional lint allowances to CI, plus general style fixes.
- Improved test coverage throughout the library.

## [0.9.3] - 2026-07-01

### Changed

- `Binseq` input is now accepted as a path rather than a pre-opened handle.

### Fixed

- Use `reserve` instead of `resize` to avoid unnecessary zeroing/copies (issue #94).
- Ensure `len_nef` is cleared correctly.

### Tests

- Added coverage for the sequential reader and N-containing sequences.

## [0.9.1] - 2026-06-25

### Added

- Support for writing only completed blocks, enabling on-thread compression and
reducing lock contention for parallel writers.
- `on_thread_complete` callback support.
- `ingest_completed` is now passed through, with behavior changes scoped to `cbq`.

### Tests

- Added coverage for `cbq` ingestion.

## [0.9.0] - 2026-01-23

### Added

- Integrated `cbq` (compressed BINSEQ) into the core library as a first-class format.
- Builder-based construction of a file from an existing header.
- Auto-encode a FASTX file (or a pair of FASTX files) directly into a BINSEQ format via
a builder method.
- A generic writer abstraction over BINSEQ files.
- Default quality score handling across all readers.
- Elias-Fano encoding for N-positions instead of raw `u64` indices, reducing index size.
- An iterator over block headers, and convenience accessors for index metadata.

### Changed

- **Breaking:** renamed `BINSEQ`/`VBINSEQ` naming throughout to `BQ`/`VBQ`
(e.g. `BinseqHeader` → `bq::FileHeader`), and renamed several `vbq`/`bq`-specific
headers and writer structs for clarity.
- Reworked the write API around a sequence record struct.
- Reworked the internal index to track record count and sequence count separately.
- Replaced `anyhow`-based errors throughout the crate with crate-native error types.
- Removed the external `vbq` index file in favor of writing the index into the file itself.

### Fixed

- Several small hotfixes to the write path.
- Incorrect binary file used in a test fixture.

## [0.8.1] - 2025-12-10

### Fixed

- Incorrect decoding of the extended buffer on batch decoding in `bq` when a full
chunk of primary data was present (issue #75).

## [0.8.0] - 2025-12-10

### Added

- `sseq`/`xseq` functions on the record trait for use during batch decoding, implemented
for both `bq` and `vbq`.
- Additional usage examples.

### Changed

- **Breaking:** improved `bq` decoding via batch decoding, keeping data in SIMD form longer.
- Improved header access logic for zero-copy reads.

### Fixed

- Correct index returned from decoding.
- Correct slicing when per-record flags are set in `bq`.

## [0.7.8] - 2025-12-09

### Changed

- `vbq` reader is now zero-copy or single-copy depending on compression status.
- Simplified the encoding API.
- Reusable decompression context for improved throughput.
- Updated all dependencies.

### Fixed

- Validate the size of bytes provided to the non-compressed reader.

## [0.7.7] - 2025-12-08

### Changed

- Split the sequencing context into multiple traits with distinct variants
(renamed `FullCtx` to `Ctx`).
- Removed unsafe code from the context implementation in favor of a resize-copy pattern.

### Fixed

- Logical error in quality-score padding (`ensure_quality`).

### Docs

- Documented the different context variants.

## [0.7.6] - 2025-12-08

### Added

- A basic sequencing context struct, with mutable references to its internals.
- Ability to fill in missing quality scores when required.
- A generic error-conversion trait, made public.

## [0.7.5] - 2025-10-02

### Added

- BINSEQ v2 format.
- Optional headers in `vbq` files, with the index moved into the file itself
(no more auxiliary index file).
- Support for 4-bit encodings, with a `bitsize` re-export so downstream users don't
need to depend on `bitnuc` directly.
- Header builder pattern for constructing `bq` and `vbq` files.

### Changed

- Per-record flags are now optional across `bq`, `vbq`, and the record API.
- `vbq` index now uses `u64` for cumulative record counts.

## [0.6.5] - 2025-07-01

Baseline release for this changelog. Earlier history predates version tagging.

[Unreleased]: https://github.com/arcinstitute/binseq/compare/v0.9.3...HEAD
[0.9.3]: https://github.com/arcinstitute/binseq/compare/v0.9.1...v0.9.3
[0.9.1]: https://github.com/arcinstitute/binseq/compare/v0.9.0...v0.9.1
[0.9.0]: https://github.com/arcinstitute/binseq/compare/v0.8.1...v0.9.0
[0.8.1]: https://github.com/arcinstitute/binseq/compare/v0.8.0...v0.8.1
[0.8.0]: https://github.com/arcinstitute/binseq/compare/v0.7.8...v0.8.0
[0.7.8]: https://github.com/arcinstitute/binseq/compare/v0.7.7...v0.7.8
[0.7.7]: https://github.com/arcinstitute/binseq/compare/v0.7.6...v0.7.7
[0.7.6]: https://github.com/arcinstitute/binseq/compare/v0.7.5...v0.7.6
[0.7.5]: https://github.com/arcinstitute/binseq/compare/v0.6.5...v0.7.5
[0.6.5]: https://github.com/arcinstitute/binseq/releases/tag/v0.6.5
29 changes: 16 additions & 13 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "binseq"
version = "0.9.3"
version = "0.9.4"
edition = "2024"
description = "A high efficiency binary format for sequencing data"
license = "MIT"
Expand All @@ -11,27 +11,27 @@ categories = ["science::bioinformatics", "encoding", "data-structures"]
keywords = ["binary", "nucleotide", "sequencing", "genomics", "fastq"]

[dependencies]
anyhow = {version = "1.0.100", optional = true}
anyhow = {version = "1.0.103", optional = true}
auto_impl = "1.3.0"
bitnuc = "0.4.0"
bytemuck = { version = "1.24.0", features = ["derive", "extern_crate_alloc"] }
bitnuc = "0.4.1"
bytemuck = { version = "1.25.1", features = ["derive", "extern_crate_alloc"] }
byteorder = "1.5.0"
itoa = "1.0.17"
memchr = "2.7.6"
memmap2 = "0.9.9"
itoa = "1.0.18"
memchr = "2.8.3"
memmap2 = "0.9.11"
num_cpus = "1.17.0"
paraseq = { version = "0.4.8", optional = true }
paraseq = { version = "0.4.14", optional = true }
parking_lot = {version = "0.12.5", optional = true }
rand = { version = "0.9.2", features = ["small_rng"] }
rand = { version = "0.9.5", features = ["small_rng"] }
sucds = "0.8.3"
thiserror = "2.0.17"
thiserror = "2.0.18"
zstd = { version = "0.13.3", features = ["zstdmt"] }

[dev-dependencies]
anyhow = "1.0.100"
anyhow = "1.0.103"
parking_lot = "0.12.5"
clap = { version = "4.5.54", features = ["derive"] }
paraseq = "0.4.8"
clap = { version = "4.6.2", features = ["derive"] }
paraseq = "0.4.14"

[features]
default = ["paraseq", "anyhow"]
Expand All @@ -41,4 +41,7 @@ paraseq = ["dep:paraseq", "dep:parking_lot"]
[lints.clippy]
pedantic = { level = "warn", priority = -1 }
cast_possible_truncation = "allow"
cast_precision_loss = "allow"
missing_errors_doc = "allow"
struct_excessive_bools = "allow"
fn_params_excessive_bools = "allow"
20 changes: 9 additions & 11 deletions examples/auto-write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,15 @@ impl Args {
fn format(&self) -> Format {
if let Some(format) = self.format {
format
} else {
if let Some(output) = &self.output {
match output.split(".").last() {
Some("bq") => Format::Bq,
Some("vbq") => Format::Vbq,
Some("cbq") => Format::Cbq,
_ => Format::default(),
}
} else {
Format::default()
} else if let Some(output) = &self.output {
match output.split('.').next_back() {
Some("bq") => Format::Bq,
Some("vbq") => Format::Vbq,
Some("cbq") => Format::Cbq,
_ => Format::default(),
}
Comment thread
noamteyssier marked this conversation as resolved.
} else {
Format::default()
}
}
fn bitsize(&self) -> BitSize {
Expand All @@ -87,7 +85,7 @@ impl Args {
/// Creates an output file handle
fn ohandle(&self) -> Result<BoxedWriter> {
let path = if let Some(output) = &self.output {
output.to_string()
output.clone()
} else {
format!("{}{}", &self.prefix, self.format().extension())
};
Expand Down
2 changes: 1 addition & 1 deletion examples/grep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ impl GrepCounter {
}
impl ParallelProcessor for GrepCounter {
fn process_record<R: binseq::BinseqRecord>(&mut self, record: R) -> binseq::Result<()> {
if self.match_sequence(&record.sseq()) || self.match_sequence(&record.xseq()) {
if self.match_sequence(record.sseq()) || self.match_sequence(record.xseq()) {
self.local_count += 1;
}

Expand Down
24 changes: 12 additions & 12 deletions examples/parallel_range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,18 @@ impl ParallelProcessor for RangeProcessor {
let count = self.counter.fetch_add(1, Ordering::Relaxed);

// Print progress every 10,000 records
if count % 10_000 == 0 {
if let Some(tid) = self.tid {
println!(
"Thread {}: Processed {} records (Range: {}-{}, Index: {}, Len: {})",
tid,
count + 1,
self.range_start,
self.range_end,
record.index(),
record.sseq().len(),
);
}
if count.is_multiple_of(10_000)
&& let Some(tid) = self.tid
{
println!(
"Thread {}: Processed {} records (Range: {}-{}, Index: {}, Len: {})",
tid,
count + 1,
self.range_start,
self.range_end,
record.index(),
record.sseq().len(),
);
}

Ok(())
Expand Down
Loading
Loading