49 if (reserve_bytes != 0) {
50 sequence_.reserve(
static_cast<size_t>(reserve_bytes));
56 return external_sequence_.empty() ? std::string_view{sequence_} : external_sequence_;
63 std::span<const RecordRange>{ranges_.data(), ranges_.size()},
69 assert(external_sequence_.empty() &&
"cannot appendRecord when external_sequence_ is set");
72 static_cast<uint64_t
>(sequence_.size()),
73 static_cast<uint64_t
>(record_sequence.size()),
76 sequence_.append(record_sequence);
81 ranges_.push_back(range);
89 return external_sequence_;
92 [[nodiscard]]
bool empty() const noexcept {
93 return ranges_.empty();
97 return static_cast<uint64_t
>(ranges_.size());
102 if (!external_sequence_.empty()) {
105 total += range.sequenceBytes;
109 return static_cast<uint64_t
>(sequence_.size());
112 [[nodiscard]]
const std::vector<RecordRange>&
ranges() const noexcept {
119 external_sequence_ = {};
124 std::string{}.swap(sequence_);
125 std::vector<RecordRange>{}.swap(ranges_);
129 std::string sequence_;
130 std::string_view external_sequence_{};
131 std::vector<RecordRange> ranges_;
147 std::span<const uint8_t>
hits{};
187 std::span<const uint8_t>
hits{};
213 std::vector<FastxDetailedQueryRecord>
records;
223template <
typename Functor>
225 { f(view) } -> std::same_as<void>;
235template <
typename Functor>
237 { f(record) } -> std::same_as<void>;
262 if (!line.empty() && line.back() ==
'\r') {
279 std::string_view line
288 return static_cast<uint32_t>(line.empty() ? 1 : line.size() + 1);
318 const auto header = readHeaderLine();
320 return Err(header.error());
322 if (header->empty()) {
335 parseError(
"expected FASTA or FASTQ header",
fastx_column_at(*header, 0))
342 return Err(parseError(
343 "mixed FASTA and FASTQ records are not supported",
fastx_column_at(*header, 0)
347 record.header.assign(header->substr(1));
357 std::istream& input_;
358 std::string source_name_;
359 std::string pendingHeader_;
360 std::string lineBuffer_;
364 [[
nodiscard]] Error parseError(std::string_view message,
uint32_t column)
const {
371 if (!pendingHeader_.empty()) {
372 lineBuffer_ = std::move(pendingHeader_);
373 pendingHeader_.clear();
374 return std::string_view{lineBuffer_};
377 while (std::getline(input_, lineBuffer_)) {
380 if (!lineBuffer_.empty()) {
381 return std::string_view{lineBuffer_};
387 Error::io(std::format(
"Failed to read FASTA/FASTQ input from {}", source_name_))
390 return std::string_view{};
393 [[
nodiscard]] Result<void> readFastaSequence(std::string& sequence) {
394 sequence.reserve(4096);
395 while (std::getline(input_, lineBuffer_)) {
398 if (!lineBuffer_.empty() && lineBuffer_.front() ==
'>') {
399 pendingHeader_ = std::move(lineBuffer_);
402 sequence += lineBuffer_;
407 Error::io(std::format(
"Failed to read FASTA/FASTQ input from {}", source_name_))
413 [[
nodiscard]] Result<void> readFastqSequence(std::string& sequence) {
414 sequence.reserve(4096);
415 while (std::getline(input_, lineBuffer_)) {
418 if (!lineBuffer_.empty() && lineBuffer_.front() ==
'+') {
419 CUSBF_TRY(readFastqQualities(sequence.size()));
422 sequence += lineBuffer_;
427 Error::io(std::format(
"Failed to read FASTA/FASTQ input from {}", source_name_))
430 return Err(parseError(
431 "unterminated FASTQ record: missing '+' separator",
432 fastx_column_at(lineBuffer_, lineBuffer_.size() > 0 ? lineBuffer_.size() - 1 : 0)
445 return Err(parseError(
446 "FASTQ quality length exceeds sequence length",
457 Error::io(std::format(
"Failed to read FASTA/FASTQ input from {}", source_name_))
460 return Err(parseError(
"FASTQ quality length does not match sequence length",
short_column));
471 const std::filesystem::path&
path
476 auto input = std::make_unique<std::ifstream>(
path);
477 if (!
input->is_open()) {
478 return Err(
Error::io(std::format(
"Failed to open FASTA/FASTQ file: {}",
path.string())));
Accumulates parsed FASTX records into a dense RecordBatchView.
bool empty() const noexcept
uint64_t raw_sequence_bytes() const noexcept
Sum of record payload bytes (ignores mmap storage outside ranges).
std::string_view & external_sequence_slot() noexcept
RecordBatchView view() const noexcept
Non-owning RecordBatchView over the accumulated records.
void push_range(RecordRange range)
Records a range already present in sequence_view.
DenseRecordBatchBuilder(uint64_t reserve_bytes=0)
uint64_t recordCount() const noexcept
void appendRecord(std::string_view record_sequence)
Appends one record payload and records its byte range.
const std::vector< RecordRange > & ranges() const noexcept
std::string_view sequence_view() const noexcept
Dense sequence buffer backing view (owned or mmap-external).
std::string & sequence_buffer() noexcept
Streaming FASTA/FASTQ parser.
FastxReader(std::istream &input, std::string_view source_name="<stream>")
Constructs a reader over an open input stream.
Result< bool > nextRecord(FastxRecord &record)
Reads the next FASTA/FASTQ record into record.
static Result< std::unique_ptr< GzIstream > > open(const std::filesystem::path &path)
Opens a gzip file as an input stream.
Callable invoked once per record by Filter::query_fastx_records and related FASTX streaming query API...
Callable invoked once per record by Filter::query_record_batch overloads that take a callback.
#define CUSBF_TRY(expr)
Propagates a cusbf::Result failure from the enclosing function (GNU statement expression).
bool isGzipFile(const std::filesystem::path &path)
True when path begins with the gzip magic bytes (0x1F, 0x8B).
void trimTrailingCarriageReturn(std::string &line)
Removes a trailing carriage return from line if present (Windows line endings).
Result< std::unique_ptr< std::istream > > openFastxFile(const std::filesystem::path &path)
Opens a FASTA/FASTQ file for reading.
uint32_t fastx_column_at(std::string_view line, size_t byte_index)
1-based column at byte_index within line (clamped to the line end).
uint32_t fastx_quality_short_column(std::string_view line)
1-based column where a quality run ends too short (position after the last byte).
uint32_t fastx_quality_excess_column(uint64_t quality_length, uint64_t expected_length, std::string_view line)
1-based column of the first quality byte that exceeds expected_length.
FastxFormat
Detected file format for a FASTA/FASTQ stream.
@ fasta
FASTA (> headers).
@ fastq
FASTQ (@ headers).
@ unknown
Format not yet determined from the first header.
consteval bool separatorPositionAlwaysEncodesInvalid(char *input, uint64_t separatorPosition, uint64_t index)
Recursively tests whether placing the separator byte at any position in an input of valid bytes alway...
cuda::std::unexpected< Error > Err(Error error)
Failure return; converts to any Result<T> via cuda::std::unexpected.
static Error io(std::string message)
static Error fastx_parse(SourceLocation site, std::string_view detail)
Detailed per-record query results returned by Filter FASTX detail APIs.
std::vector< uint8_t > hits
Per-k-mer hits retained after the API returns.
std::string sequence
Owning copy of the record sequence.
std::string header
Owning copy of the record header.
uint64_t queriedBases
Bases included in the query window.
uint64_t positive_kmers
K-mers reported present.
uint64_t queriedKmers
K-mer windows evaluated.
uint64_t record_index
Index in the input stream or file.
Aggregate and per-record results returned by Filter FASTX detail APIs.
FastxQueryReport summary
Totals across all records.
std::vector< FastxDetailedQueryRecord > records
One entry per record in source order.
Summary statistics returned by Filter insert operations on FASTX and record-batch input.
uint64_t indexedBases
Total sequence bytes indexed.
uint64_t recordsIndexed
Records parsed and indexed.
uint64_t insertedKmers
K-mer windows inserted (valid symbols only).
Summary statistics returned by Filter query operations on FASTX and record-batch input.
uint64_t queriedBases
Total sequence bytes queried.
uint64_t recordsQueried
Records parsed and queried.
uint64_t queriedKmers
K-mer windows evaluated.
uint64_t positive_kmers
K-mers reported present in the filter.
Per-record query payload emitted by FASTX streaming query APIs.
uint64_t queriedKmers
K-mer windows evaluated.
std::string_view header
FASTA/FASTQ header (without leading > or @).
uint64_t positive_kmers
K-mers reported present.
std::string_view sequence
Record sequence bytes.
uint64_t queriedBases
Bases included in the query window.
uint64_t record_index
Index in the input stream or file.
std::span< const uint8_t > hits
Per-k-mer hits (1 = present), valid only for the callback duration.
Dense host-resident sequence batch plus explicit record boundaries.
std::span< const RecordRange > records
Ordered, non-overlapping record ranges.
std::string_view sequence
Raw record payloads concatenated.
Per-record query payload emitted by query_record_batch().
uint64_t queriedKmers
K-mer windows evaluated (valid symbols only).
std::span< const uint8_t > hits
Per-k-mer hits (1 = present), valid only in callback.
uint64_t positive_kmers
K-mers reported present in the filter.
std::string_view sequence
Record sequence slice (normalized layout).
uint64_t queriedBases
Bases included in the query window.
uint64_t record_index
Index in the source batch.
Ordered non-overlapping byte range for one record inside a dense sequence batch.
uint64_t sequenceOffset
Byte offset into RecordBatchView::sequence.
uint64_t sequenceBytes
Payload length in bytes (symbol-aligned).
Fallible API result: cuda::std::expected<T, Error> with cuSBF factories.
static SourceLocation fastx(std::string_view file, uint32_t line, uint32_t column)
A single sequence record extracted from a FASTA/FASTQ stream.
std::string header
Header line without the leading > or .
std::string sequence
Concatenated sequence lines (qualities skipped for FASTQ).