36 const auto header = readHeaderLine();
38 return Err(header.error());
40 if (header->empty()) {
52 parseError(
"expected FASTA or FASTQ header",
fastx_column_at(*header, 0))
59 return Err(parseError(
60 "mixed FASTA and FASTQ records are not supported",
fastx_column_at(*header, 0)
64 record.header.assign(header->substr(1));
95 const auto header = readHeaderLine();
97 return Err(header.error());
99 if (header->empty()) {
111 parseError(
"expected FASTA or FASTQ header",
fastx_column_at(*header, 0))
118 return Err(parseError(
119 "mixed FASTA and FASTQ records are not supported",
fastx_column_at(*header, 0)
123 record.header.assign(header->substr(1));
126 const std::string_view line = readLine();
130 if (!line.empty() && line.front() ==
'>') {
131 pending_header_.assign(line);
135 if (position_ < data_.size() && data_[position_] !=
'>') {
137 sequence.append(line.data(), line.size());
145 if (position_ < data_.size() && data_[position_] ==
'>') {
146 pending_header_.assign(readLine());
164 std::string_view data_;
165 std::string_view source_name_;
167 std::string pending_header_;
168 std::string header_line_;
172 [[
nodiscard]] Error parseError(std::string_view message,
uint32_t column)
const {
179 [[
nodiscard]] std::string_view readLine() {
180 if (position_ >= data_.size()) {
184 size_t end = position_;
185 while (
end < data_.size() && data_[
end] !=
'\n' && data_[
end] !=
'\r') {
189 const std::string_view line = data_.substr(position_,
end - position_);
191 if (position_ < data_.size() && data_[position_] ==
'\r') {
194 if (position_ < data_.size() && data_[position_] ==
'\n') {
202 if (!pending_header_.empty()) {
203 header_line_ = std::move(pending_header_);
204 pending_header_.clear();
205 return std::string_view{header_line_};
208 while (position_ < data_.size()) {
209 const std::string_view line = readLine();
211 header_line_.assign(line.data(), line.size());
212 return std::string_view{header_line_};
215 return std::string_view{};
218 [[
nodiscard]] Result<void> readFastaSequence(std::string& sequence) {
219 while (position_ < data_.size()) {
220 const std::string_view line = readLine();
221 if (!line.empty() && line.front() ==
'>') {
222 pending_header_.assign(line);
225 sequence.append(line.data(), line.size());
230 [[
nodiscard]] Result<void> readFastqSequence(std::string& sequence) {
232 while (position_ < data_.size()) {
233 const std::string_view line = readLine();
235 if (!line.empty() && line.front() ==
'+') {
236 CUSBF_TRY(readFastqQualities(sequence.size()));
239 sequence.append(line.data(), line.size());
241 return Err(parseError(
242 "unterminated FASTQ record: missing '+' separator",
251 const std::string_view line = readLine();
255 return Err(parseError(
256 "FASTQ quality length exceeds sequence length",
262 return Err(parseError(
263 "FASTQ quality length does not match sequence length",
FASTA/FASTQ parser over a contiguous in-memory buffer.
FastxBufferReader(std::string_view data, std::string_view source_name="<buffer>")
Constructs a reader over a contiguous in-memory FASTA/FASTQ buffer.
Result< bool > nextRecord(FastxRecord &record)
Reads the next record into record.
Result< std::optional< RecordRange > > appendNextRecord(FastxRecord &record, std::string &sequence, std::string_view &buffer)
Parses one record with optional zero-copy sequence views for single-line FASTA.
std::string_view buffer() const noexcept
Entire mmap or owned buffer backing this reader.
#define CUSBF_TRY(expr)
Propagates a cusbf::Result failure from the enclosing function (GNU statement expression).
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 fastx_parse(SourceLocation site, std::string_view detail)
Ordered non-overlapping byte range for one record inside a dense sequence batch.
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.