CsvStreamReader
Constant-memory chunked CSV reader with configurable delimiter, quote, and null string.
// include/zedda/stream_reader.hpp
namespace zedda {
struct ChunkResult {
int64_t rows_processed = 0;
int64_t total_rows = 0;
bool done = false;
};
struct StreamReaderConfig {
int64_t chunk_size = 65536; // rows per chunk
char delimiter = ',';
char quote_char = '"';
bool has_header = true;
std::string null_string = "";
};
class CsvStreamReader {
public:
explicit CsvStreamReader(const std::string& path, StreamReaderConfig config = {});
~CsvStreamReader() noexcept;
bool open();
void close();
std::vector<ColumnAccumulator> make_accumulators() const;
ChunkResult read_chunk(std::vector<ColumnAccumulator>& accumulators);
bool done() const;
int64_t rows_read() const;
const std::vector<std::string>& column_names() const;
size_t num_columns() const;
const std::string& path() const;
};
} // namespace zedda
Purpose
Constant-memory chunked CSV reader. Uses MmapFile for file access and the active SIMD scanner for field boundary detection. Feeds parsed values into per-column ColumnAccumulator instances.
StreamReaderConfig
| Field | Default | Meaning |
|---|---|---|
chunk_size |
65536 |
Rows per chunk. Memory usage scales with this × number of columns. |
delimiter |
',' |
Field delimiter |
quote_char |
'"' |
Quote character for escaped fields |
has_header |
true |
Treat the first row as a header |
null_string |
"" |
String to interpret as null (in addition to the built-in set) |
The built-in null detection (via fast_is_null) recognises: empty string, ?, NA, NaN, N/A, null, none, #N/A (all case-insensitive).
Methods
open()
Opens the underlying MmapFile. Returns false if the file cannot be opened (missing, permission denied, etc.). Never throws.
close()
Idempotent. Closes the underlying mmap.
make_accumulators()
Returns a vector of ColumnAccumulator — one per column — populated with the column names from the header (if has_header is true) or col_0, col_1, … (if false).
read_chunk(accumulators)
Reads up to chunk_size rows and feeds the values into accumulators. Returns a ChunkResult with the number of rows processed in this call and the total rows read so far. Sets done = true when the file is exhausted.
done(), rows_read(), column_names(), num_columns(), path()
Accessors. done() is true after the final read_chunk() call.
Header detection
If has_header=true (default), the first row is consumed as the header and used to name the accumulators. If has_header=false, columns are named col_0, col_1, …, and the first row is treated as data.
BOM and embedded newlines
The reader handles:
- UTF-8 BOM (
\xEF\xBB\xBF) at the start of the file — stripped silently (fix C-H11). - Embedded newlines in quoted fields — a newline inside a quoted field is treated as part of the field, not a row terminator (fix C-H10).
- Escaped quotes —
""inside a quoted field is treated as a literal".
Destructor
Marked noexcept (fix C-L12). Safe to destroy during stack unwinding.
See also
- C++ API: ProfileBuilder — orchestrates
CsvStreamReader. - C++ API: ColumnAccumulator — what
read_chunkfeeds. - C++ API: MmapFile — how the file is read.
- C++ API: SimdScanner — how field boundaries are found.