Four new exported functions compute common BAM summaries entirely inside the multithreaded C++ reader (per-thread accumulators folded in the OpenMP region; only the compact result crosses into R – no per-read R objects are ever materialised), each verified byte-identical to its standard Bioconductor equivalent:
fragment_sizes(): paired-end fragment-size (insert-size) distribution; identical to table(abs(scanBam(isize))). drop_mate_unmapped = TRUE (default) replicates scanBam’s isize = NA rule for mate-unmapped reads.
mapq_dist(): mapping-quality distribution; identical to table(scanBam(mapq)) (MAPQ 255 reported as 255, where scanBam uses NA).
bam_coverage(): per-base coverage as a SimpleRleList; identical() to GenomicAlignments::coverage(readGAlignments(...)), including the CIGAR conventions (M/=/X/D cover, N gaps, drop.D.ranges = FALSE) and the readGAlignments record filter (only unmapped reads dropped).
bam_coverage_bigwig(): single-pass BAM -> coverage bigWig written natively via the bundled libBigWig – no R-side coverage object, no rtracklayer::export.bw round-trip. Data and zoom blocks are compressed across the OpenMP threads (parallel = TRUE, default); the parallel output is bit-for-bit identical to the serial writer. A compress_level argument exposes the zlib deflate level, and verbose = TRUE prints a per-phase timing breakdown.
bam_read(as = "GAlignments") now uses a dedicated fast path that assembles the GAlignments slots in C++: seqnames/strand accumulated as run-length pairs inside the parallel region, the cigar column materialised through a CHARSXP cache that exploits CIGAR-string redundancy, and qname decoded only when actually requested. Output is identical() to GenomicAlignments::readGAlignments() (verified at 226M-read scale) and to the previous construction path. options(BamScale.ga_fastpath = FALSE) restores the old path.
Vendored libBigWig 0.4.8 (MIT, Devon Ryan) under src/libBigWig/ (built with -DNOCURL, local files only). Local modifications, each commented in bwWrite.c: a tunable zlib deflate level, deferred parallel block compression, and zoom-level construction from in-memory coverage runs instead of re-reading the just-written data section.
Performance
Fixed a quadratic reallocation pattern in the reader’s batch merge: per-batch exact-capacity reserve() calls copied all accumulated data on every batch. Buffers now grow geometrically. This affects every read path – e.g. on a 229M-read BAM at 48 threads, as = "GAlignments" dropped from ~300 s to ~56 s, and plain data-frame reads improved 2.8-4.4x.
CIGAR strings are formatted with an allocation-free digit writer instead of per-op std::to_string.
Representative large-file results (229M-read ATAC BAM, 48 threads, warm cache): bam_coverage() 7.2x vs coverage(readGAlignments()); bam_coverage_bigwig() 2.6x vs megadepth at equal compression; fragment_sizes() 5.4x vs scanBam + table(); as = "GAlignments" 4.8x vs readGAlignments() (and faster even single-threaded).
Bug fixes
bam_coverage_bigwig() now works on Windows: the output stream is opened in binary mode ("w+b"; text mode corrupted the bigWig), and zoom levels are always built from the in-memory coverage runs instead of re-reading the just-written stream (Windows’ C runtime is strict about write-to-read transitions on update streams; the in-memory path is byte-identical to the re-read path, verified at 226M-read scale). Vendored libBigWig diagnostics now go through REprintf(), clearing the compiled-code check NOTE.
Removed a long-standing ^src/Makevars$ entry from .Rbuildignore (dating to the pkgdown setup): built tarballs – including the Bioconductor builds – shipped without src/Makevars and therefore compiled WITHOUT OpenMP (and without the explicit -lz), silently running single-threaded when installed from a tarball. In-place R CMD INSTALL . was unaffected, which is why local installs always threaded. Tarball builds now carry the full build configuration (OpenMP, zlib, -DNOCURL, and the bundled libBigWig objects).
Benchmarks
run_workflow_benchmark.R gains first-class arms for the four new functions (--include-fastcov/-bigwig/-fragsize/-mapq), each behind an identical() correctness gate, with an optional megadepth context arm (--megadepth-bin).
run_server_benchmark.R: BiocParallel PSOCK clusters are now started and their namespaces pre-loaded outside the timed region in all multi-file arms (previously cluster spin-up was charged to every iteration, diluting comparator ratios at high worker counts); added --include-single so single and multi scenarios can run in separate stages.
New reproducible manuscript-benchmark tooling: select_encode_atac.R (ENCODE portal selection -> accession/md5 manifest), manifest-driven download_atac_data.R with md5 verification, samtools_reference.sh, merge_runs.R, and the staged run_manuscript_final.sh orchestrator.
Tests
New testthat gates: byte-identity of fragment_sizes() / mapq_dist() / bam_coverage() against their Rsamtools/GenomicAlignments equivalents; bam_coverage_bigwig() value-identity after re-import plus bit-for-bit parallel-vs-serial output; six GAlignments fast-path identity gates (threads 1/4, mcols order, use.names, flag/mapq filters, fast == slow, gctorture).
BamScale 0.99.13
New features
On the compatible read path, seq and qual are now built directly in C as DNAStringSet / PhredQuality objects from the packed BAM byte buffer (via the IRanges/XVector C-callable constructors), avoiding a character round-trip. Output remains byte-identical to Rsamtools::scanBam.
Added an end-to-end workflow benchmark harness under inst/benchmarks/ (coverage → bigWig and ATAC fragment-size QC) and a self-contained benchmark-results vignette.
Bug fixes
GAlignments output now carries the BAM-header sequence lengths in its Seqinfo. Previously the seqlengths were NA, so coverage() extended each seqlevel only to its maximum end and export.bw() wrote truncated chromosome sizes, diverging from GenomicAlignments::readGAlignments.
Force-load the C-callable providers (S4Vectors, IRanges, XVector, Biostrings) and GenomicAlignments in .onLoad, so the compatible seq/qual and GAlignments paths resolve their constructors and classes on fresh SnowParam SOCK workers.
Documentation
Refreshed the README with the latest benchmark results (read-pattern and end-to-end workflow speedups) and Bioconductor installation instructions.