Vince's CSV Parser
Loading...
Searching...
No Matches
csv_row.hpp
Go to the documentation of this file.
1
5#pragma once
6#include <chrono>
7#include <cstdint>
8#include <cmath>
9#include <iterator>
10#include <memory> // For CSVField
11#include <limits> // For CSVField
12#include <unordered_set>
13#include <string>
14#include <sstream>
15#include <vector>
16
17#include "common.hpp"
18#ifdef CSV_HAS_CXX17
19#include <optional>
20#endif
21#ifdef CSV_HAS_CXX23
22#if defined(__has_include)
23#if __has_include(<expected>)
24#include <expected>
25#ifdef __cpp_lib_expected
26#define CSV_HAS_STD_EXPECTED
27#endif
28#endif
29#endif
30#endif
31#include "csv_exceptions.hpp"
32#ifdef CSV_HAS_CXX20
33#include <ranges>
34#endif
35#include "data_type.hpp"
36#include "../external/classify_scalar.hpp"
37#include "json_converter.hpp"
38#include "raw_csv_data.hpp"
39
40namespace csv {
41 namespace internals {
42 template<typename RowSink, typename ParsePolicy, typename FieldPolicy, typename RowPolicy>
43 class CSVParserCore;
44 struct CSVRowRowPolicy;
45 namespace parser {
46 class CSVParserDriverBase;
47 }
48 namespace speculative {
49 struct CSVRowFragment;
50 }
51
52 static const std::string ERROR_NAN = "Not a number.";
53 static const std::string ERROR_OVERFLOW = "Overflow error.";
54 static const std::string ERROR_FLOAT_TO_INT =
55 "Attempted to convert a floating point value to an integral type.";
56 static const std::string ERROR_NEG_TO_UNSIGNED = "Negative numbers cannot be converted to unsigned types.";
57
58 // Inside CSVField::get() or wherever you materialize the value
59 csv::string_view get_trimmed(csv::string_view sv, const WhitespaceMap& ws_flags) noexcept;
60 }
61
72 enum class CSVConversionError {
74 None = 0,
75
78
81
84
87 };
88
89 namespace internals {
90 typedef const char* csv_error_message;
91
92 static CONSTEXPR_VALUE_14 csv_error_message CSV_CONVERSION_ERROR_MESSAGES[] = {
93 "",
94 "Not a number.",
95 "Overflow error.",
96 "Attempted to convert a floating point value to an integral type.",
97 "Negative numbers cannot be converted to unsigned types."
98 };
99 }
100
102 inline const char* csv_conversion_error_message(CSVConversionError error) noexcept {
103 const size_t index = static_cast<size_t>(error);
104 return index < (sizeof(internals::CSV_CONVERSION_ERROR_MESSAGES) / sizeof(internals::CSV_CONVERSION_ERROR_MESSAGES[0]))
105 ? internals::CSV_CONVERSION_ERROR_MESSAGES[index]
106 : internals::CSV_CONVERSION_ERROR_MESSAGES[static_cast<size_t>(CSVConversionError::NotANumber)];
107 }
108
114 class CSVField {
115 public:
117 constexpr explicit CSVField(csv::string_view _sv) noexcept
118 : sv(_sv.data() ? _sv : csv::string_view("")) {}
119
120 CSVField(csv::string_view _sv, const internals::CSVFieldScalar& scalar) noexcept
121 : sv(_sv.data() ? _sv : csv::string_view("")),
122 type_(scalar.type) {
123 switch (scalar.type) {
128 this->value_.integer = scalar.integer;
129 break;
132 this->value_.floating = scalar.floating;
133 break;
135 this->value_.timestamp = scalar.timestamp;
136 break;
138 this->value_.boolean = scalar.boolean;
139 break;
140 default:
141 break;
142 }
143 }
144
145 operator csv::string_view() const noexcept {
146 return this->sv;
147 }
148
149 operator std::string() const {
150 return std::string(this->sv);
151 }
152
180 template<typename T = std::string> T get() {
181 T out{};
182 const CSVConversionError err = check_convert(out);
183 if (err != CSVConversionError::None) throw std::runtime_error(csv_conversion_error_message(err));
184 return out;
185 }
186
187#ifdef CSV_HAS_STD_EXPECTED
199 template<typename T = std::string>
200 std::expected<T, CSVConversionError> as() {
201 T out{};
202 const CSVConversionError err = check_convert(out);
203 return (err != CSVConversionError::None)
204 ? std::expected<T, CSVConversionError>(std::unexpected(err))
205 : std::expected<T, CSVConversionError>(out);
206 }
207#endif
208
224 template<typename T = std::string>
225 bool try_get(T& out) noexcept {
226 return check_convert(out) == CSVConversionError::None;
227 }
228
229#ifdef CSV_HAS_CXX17
238 template<typename T>
239 operator std::optional<T>() {
240 T out{};
241 return try_get(out) ? std::optional<T>(out) : std::nullopt;
242 }
243#endif
244
248 template<typename T = long long>
249 bool try_parse_hex(T& parsedValue) {
250 static_assert(std::is_integral<T>::value,
251 "try_parse_hex only works with integral types (int, long, long long, etc.)");
252
253 return classify_scalar::parse_hex(this->sv.data(), this->sv.data() + this->sv.size(), parsedValue);
254 }
255
262 bool try_parse_decimal(long double& dVal, const char decimalSymbol = '.');
263
269 bool try_parse_timestamp(std::uint64_t& out) noexcept;
270
272#ifdef DOXYGEN_SHOULD_SKIP_THIS
273 template<typename T>
274 bool try_parse_timestamp(T& out) noexcept;
275#else
276 template<typename T>
277 internals::enable_if_t<
278 std::is_integral<T>::value && std::is_unsigned<T>::value && !std::is_same<T, bool>::value
279 && (sizeof(T) >= sizeof(std::uint64_t)),
280 bool
281 >
282 try_parse_timestamp(T& out) noexcept {
283 std::uint64_t milliseconds = 0;
284 if (!this->try_parse_timestamp(milliseconds))
285 return false;
286
287 out = static_cast<T>(milliseconds);
288 return true;
289 }
290#endif
291
293 template<typename Rep, typename Period>
294 bool try_parse_timestamp(std::chrono::duration<Rep, Period>& out) noexcept {
295 std::uint64_t milliseconds = 0;
296 if (!this->try_parse_timestamp(milliseconds))
297 return false;
298
299 out = std::chrono::duration_cast<std::chrono::duration<Rep, Period>>(
300 std::chrono::milliseconds(milliseconds));
301 return true;
302 }
303
305 template<typename Duration>
306 bool try_parse_timestamp(std::chrono::time_point<std::chrono::system_clock, Duration>& out) noexcept {
307 std::uint64_t milliseconds = 0;
308 if (!this->try_parse_timestamp(milliseconds))
309 return false;
310
311 out = std::chrono::time_point<std::chrono::system_clock, Duration>(
312 std::chrono::duration_cast<Duration>(std::chrono::milliseconds(milliseconds)));
313 return true;
314 }
315
329 template<typename T>
330 inline bool operator==(T other) const noexcept
331 {
332 static_assert(std::is_arithmetic<T>::value,
333 "T should be a numeric value.");
334
335 const_cast<CSVField*>(this)->get_value();
336 if (this->type_ < DataType::CSV_INT8 || this->type_ > DataType::CSV_DOUBLE || this->type_ == DataType::CSV_BIGINT) {
337 return false;
338 }
339
340 return internals::is_equal(this->numeric_value_as_long_double(), static_cast<long double>(other), 0.000001L);
341 }
342
344 CONSTEXPR csv::string_view get_sv() const noexcept { return this->sv; }
345
347 inline bool is_null() noexcept { return type() == DataType::CSV_NULL; }
348
350 inline bool is_str() noexcept { return type() == DataType::CSV_STRING; }
351
353 inline bool is_num() noexcept {
355 }
356
358 inline bool is_int() noexcept {
359 return (type() >= DataType::CSV_INT8) && (type() <= DataType::CSV_INT64);
360 }
361
363 inline bool is_float() noexcept { return type() == DataType::CSV_DOUBLE; }
364
366 inline bool is_bool() noexcept { return type() == DataType::CSV_BOOL; }
367
369 inline bool is_timestamp() noexcept { return type() == DataType::CSV_TIMESTAMP; }
370
372 inline DataType type() noexcept {
373 this->get_value();
374 return type_;
375 }
376
377 private:
378 // GCC emits a psABI note for by-value APIs involving unions with long double.
379 // This is a workaround to keep the logs clean without sacrificing the benefits of a union on other compilers.
380 // Give only GCC users the struct tax so normal builds and strict CI logs stay quiet.
381#if defined(__GNUC__) && !defined(__clang__)
382 struct FieldValue {
383 constexpr FieldValue() noexcept
384 : integer(0), floating(0), timestamp(0), boolean(false) {}
385
386 std::int64_t integer;
387 long double floating;
388 std::uint64_t timestamp;
389 bool boolean;
390 };
391#else
392 union FieldValue {
393 constexpr FieldValue() noexcept : floating(0) {}
394
395 std::int64_t integer;
396 long double floating;
397 std::uint64_t timestamp;
398 bool boolean;
399 };
400#endif
401
402 struct FieldValueOutput {
403 FieldValue& value;
404
405 template<classify_scalar::ScalarKind Kind>
406 typename std::enable_if<
407 Kind == classify_scalar::scalar_int8
408 || Kind == classify_scalar::scalar_int16
409 || Kind == classify_scalar::scalar_int32
410 || Kind == classify_scalar::scalar_int64,
411 void>::type set(std::int64_t parsed) const noexcept {
412 value.integer = parsed;
413 }
414
415 template<classify_scalar::ScalarKind Kind>
416 typename std::enable_if<Kind == classify_scalar::scalar_float, void>::type set(long double parsed) const noexcept {
417 value.floating = parsed;
418 }
419
420 template<classify_scalar::ScalarKind Kind>
421 typename std::enable_if<Kind == classify_scalar::scalar_bool, void>::type set(bool parsed) const noexcept {
422 value.boolean = parsed;
423 }
424
425 template<classify_scalar::ScalarKind Kind>
426 typename std::enable_if<Kind == classify_scalar::scalar_timestamp, void>::type set(std::uint64_t parsed) const noexcept {
427 value.timestamp = parsed;
428 }
429 };
430
431 FieldValue value_;
432 csv::string_view sv = "";
433 DataType type_ = DataType::UNKNOWN;
435 CONSTEXPR_14 bool stores_integral() const noexcept {
436 return type_ >= DataType::CSV_INT8 && type_ <= DataType::CSV_INT64;
437 }
438
439 CONSTEXPR_14 long double numeric_value_as_long_double() const noexcept {
440 return stores_integral()
441 ? static_cast<long double>(value_.integer)
442 : value_.floating;
443 }
444
445 CONSTEXPR_14 void cache_parsed_value(DataType parsed_type, long double parsed_value) noexcept {
446 type_ = parsed_type;
447
448 if (parsed_type >= DataType::CSV_INT8 && parsed_type <= DataType::CSV_INT64) {
449 value_.integer = static_cast<std::int64_t>(parsed_value);
450 }
451 else if (parsed_type == DataType::CSV_DOUBLE || parsed_type == DataType::CSV_BIGINT) {
452 value_.floating = parsed_value;
453 }
454 }
455
459 CSVConversionError check_convert(bool& out) noexcept {
460 if (this->type() != DataType::CSV_BOOL)
462
463 out = this->value_.boolean;
465 }
466
467 template<typename Rep, typename Period>
468 CSVConversionError check_convert(std::chrono::duration<Rep, Period>& out) noexcept {
469 if (this->type() != DataType::CSV_TIMESTAMP)
471
472 out = std::chrono::duration_cast<std::chrono::duration<Rep, Period>>(
473 std::chrono::milliseconds(this->value_.timestamp));
475 }
476
477 template<typename Duration>
478 CSVConversionError check_convert(std::chrono::time_point<std::chrono::system_clock, Duration>& out) noexcept {
479 if (this->type() != DataType::CSV_TIMESTAMP)
481
482 out = std::chrono::time_point<std::chrono::system_clock, Duration>(
483 std::chrono::duration_cast<Duration>(std::chrono::milliseconds(this->value_.timestamp)));
485 }
486
487 template<typename T>
488 CSVConversionError check_convert(T& out) noexcept {
489 IF_CONSTEXPR(std::is_arithmetic<T>::value) {
490 if (!this->is_num())
492 if (this->type_ == DataType::CSV_BIGINT)
494 }
495
496 IF_CONSTEXPR(std::is_integral<T>::value) {
497 if (this->is_float())
499
500 IF_CONSTEXPR(std::is_unsigned<T>::value) {
501 if (this->numeric_value_as_long_double() < 0)
503 }
504 }
505
506 IF_CONSTEXPR(!std::is_floating_point<T>::value) {
507 const long double value = this->numeric_value_as_long_double();
508 if (value < static_cast<long double>(std::numeric_limits<T>::min())
509 || value > static_cast<long double>(std::numeric_limits<T>::max())) {
511 }
512 }
513
514 out = this->stores_integral()
515 ? static_cast<T>(this->value_.integer)
516 : static_cast<T>(this->value_.floating);
518 }
519
521 inline void get_value() noexcept {
522 if ((int)type_ < 0) {
523 if (this->sv.empty()) {
524 type_ = DataType::CSV_NULL;
525 return;
526 }
527
528 const char* first = this->sv.data();
529 const char* last = first + this->sv.size();
530 typedef classify_scalar::policy_pack<
531 classify_scalar::builtin_numeric_policy<'.', false>,
532 classify_scalar::builtin_timestamp_policy,
533 classify_scalar::builtin_bool_policy
534 > csv_field_policy_pack;
535
536 type_ = classify_scalar::classify_scalar<
537 DataType,
538 true>(first, last, FieldValueOutput{ this->value_ }, csv_field_policy_pack());
539 }
540 }
541 };
542
544 class CSVRow {
545 public:
546 template<typename RowSink, typename ParsePolicy, typename FieldPolicy, typename RowPolicy>
547 friend class internals::CSVParserCore;
548 friend struct internals::CSVRowRowPolicy;
549 friend internals::parser::CSVParserDriverBase;
550 friend struct internals::speculative::CSVRowFragment;
551
552 CSVRow() = default;
553
555 CSVRow(internals::RawCSVDataPtr _data) : data(_data) {}
556 CSVRow(internals::RawCSVDataPtr _data, size_t _data_start, size_t _field_bounds)
557 : data(_data), data_start(_data_start), fields_start(_field_bounds) {}
558 CSVRow(internals::RawCSVDataPtr _data, size_t _data_start, size_t _field_bounds, size_t _row_length)
559 : data(_data), data_start(_data_start), fields_start(_field_bounds), row_length(_row_length) {}
560
562 CONSTEXPR bool empty() const noexcept { return this->size() == 0; }
563
565 CONSTEXPR size_t size() const noexcept { return row_length; }
566
568 size_t byte_offset() const noexcept {
569 return this->data ? this->data->source_start + this->data_start : 0;
570 }
571
574 CSVField operator[](size_t n) const;
576 inline std::string to_json(const std::vector<std::string>& subset = {}) const {
577 const auto* converter = this->get_json_converter();
578 return converter == nullptr ? "{}"
579 : converter->row_to_json(this->size(), [this](size_t i) { return this->get_field(i); }, subset);
580 }
581 inline std::string to_json_array(const std::vector<std::string>& subset = {}) const {
582 const auto* converter = this->get_json_converter();
583 return converter == nullptr ? "[]"
584 : converter->row_to_json_array(this->size(), [this](size_t i) { return this->get_field(i); }, subset);
585 }
586
588 const std::vector<std::string>& get_col_names() const {
589 return this->data->col_names->get_col_names();
590 }
591
593 internals::ConstColNamesPtr col_names_ptr() const noexcept {
594 return this->data->col_names;
595 }
596
600 std::unordered_map<std::string, std::string> to_unordered_map() const;
601
603 std::unordered_map<std::string, std::string> to_unordered_map(
604 const std::vector<std::string>& subset
605 ) const;
606
607 #ifdef CSV_HAS_CXX20
612 auto to_sv_range() const {
613 return std::views::iota(size_t{0}, this->size())
614 | std::views::transform([this](size_t i) { return this->get_field(i); });
615 }
616 #endif
617
626 operator std::vector<std::string>() const;
627
635 csv::string_view raw_str() const noexcept;
637
641 class iterator {
642 public:
643#ifndef DOXYGEN_SHOULD_SKIP_THIS
644 using value_type = CSVField;
645 using difference_type = int;
646 using pointer = std::shared_ptr<CSVField>;
647 using reference = CSVField & ;
648 using iterator_category = std::random_access_iterator_tag;
649#endif
650 iterator(const CSVRow*, int i);
651
652 reference operator*() const;
653 pointer operator->() const;
654
655 iterator operator++(int);
656 iterator& operator++();
657 iterator operator--(int);
658 iterator& operator--();
659 iterator operator+(difference_type n) const;
660 iterator operator-(difference_type n) const;
661 iterator& operator+=(difference_type n);
662 iterator& operator-=(difference_type n);
663 difference_type operator-(const iterator& other) const noexcept;
664
666 CONSTEXPR bool operator==(const iterator& other) const noexcept {
667 return this->i == other.i;
668 };
669
670 CONSTEXPR bool operator!=(const iterator& other) const noexcept { return !operator==(other); }
671
672#ifndef NDEBUG
673 friend CSVRow;
674#endif
675
676 private:
677 const CSVRow * daddy = nullptr; // Pointer to parent
678 internals::RawCSVDataPtr data = nullptr; // Keep data alive for lifetime of iterator
679 std::shared_ptr<CSVField> field = nullptr; // Current field pointed at
680 int i = 0; // Index of current field
681 };
682
684 using reverse_iterator = std::reverse_iterator<iterator>;
685
690 iterator begin() const;
691 iterator end() const noexcept;
692 reverse_iterator rbegin() const noexcept;
693 reverse_iterator rend() const;
695
696 private:
698 inline csv::string_view get_field_impl(size_t index, const internals::RawCSVDataPtr& _data) const {
699 if (index >= this->size())
700 throw std::runtime_error(internals::CSV_ERROR_INDEX_OUT_OF_BOUNDS);
701
702 const size_t field_index = this->fields_start + index;
703 const auto field = _data->fields[field_index];
704 csv::string_view field_str;
705 if (field.has_realized_storage()) {
706 field_str = _data->quote_arena.view(field.start, field.length);
707 }
708 else {
709 field_str = csv::string_view(_data->data).substr(this->data_start + field.start, field.length);
710 }
711
712 if (_data->has_ws_trimming) {
713 field_str = internals::get_trimmed(field_str, _data->ws_flags);
714 }
715
716 return field_str;
717 }
718
719 CSVField make_field(size_t index, const internals::RawCSVDataPtr& _data) const;
720
722 csv::string_view get_field(size_t index) const;
723
727 csv::string_view get_field_safe(size_t index, internals::RawCSVDataPtr _data) const;
728
729 const internals::JsonConverter* get_json_converter() const {
730 if (this->data.get() == nullptr) {
731 return nullptr;
732 }
733
734 return &this->data->json_converter.get_or_create([this]() {
735 const std::vector<std::string> columns = this->data->col_names
736 ? this->data->col_names->get_col_names()
737 : std::vector<std::string>();
738 return std::make_shared<internals::JsonConverter>(columns);
739 });
740 }
741
742 internals::RawCSVDataPtr data;
743
745 size_t data_start = 0;
746
748 size_t fields_start = 0;
749
751 size_t row_length = 0;
752
754 size_t data_end = (std::numeric_limits<size_t>::max)();
755 };
756
757#ifdef _MSC_VER
758#pragma region CSVField::get Specializations
759#endif
761 template<>
762 inline std::string CSVField::get<std::string>() {
763 return std::string(this->sv);
764 }
765
771 template<>
772 CONSTEXPR_14 csv::string_view CSVField::get<csv::string_view>() {
773 return this->sv;
774 }
775
777 template<>
778 inline long double CSVField::get<long double>() {
779 if (!is_num())
780 throw std::runtime_error(internals::ERROR_NAN);
781
782 return this->numeric_value_as_long_double();
783 }
784
786 template<>
787 inline bool CSVField::try_get<std::string>(std::string& out) noexcept {
788 out = std::string(this->sv);
789 return true;
790 }
791
793 template<>
794 CONSTEXPR_14 bool CSVField::try_get<csv::string_view>(csv::string_view& out) noexcept {
795 out = this->sv;
796 return true;
797 }
798
800 template<>
801 inline bool CSVField::try_get<long double>(long double& out) noexcept {
802 if (!is_num())
803 return false;
804
805 out = this->numeric_value_as_long_double();
806 return true;
807 }
808#ifdef _MSC_VER
809#pragma endregion CSVField::get Specializations
810#endif
811
813 template<>
814 CONSTEXPR bool CSVField::operator==(const char * other) const noexcept
815 {
816 return this->sv == other;
817 }
818
820 template<>
822 {
823 return this->sv == other;
824 }
825}
Data type representing individual CSV values.
Definition csv_row.hpp:114
bool try_parse_timestamp(std::uint64_t &out) noexcept
Parse this field as Unix milliseconds.
Definition csv_row.cpp:165
bool try_parse_decimal(long double &dVal, const char decimalSymbol='.')
Attempts to parse a decimal (or integer) value using the given symbol, returning true if the value is...
Definition csv_row.cpp:131
DataType type() noexcept
Return the type of the underlying CSV data.
Definition csv_row.hpp:372
bool try_parse_timestamp(std::chrono::duration< Rep, Period > &out) noexcept
Parse this field as a timestamp duration since the Unix epoch.
Definition csv_row.hpp:294
bool operator==(T other) const noexcept
Compares the contents of this field to a numeric value.
Definition csv_row.hpp:330
std::expected< T, CSVConversionError > as()
Return this field as T, preserving conversion failure as CSVConversionError.
Definition csv_row.hpp:200
bool is_bool() noexcept
Returns true if field is a boolean value.
Definition csv_row.hpp:366
bool is_str() noexcept
Returns true if field is a non-numeric, non-empty string.
Definition csv_row.hpp:350
constexpr CSVField(csv::string_view _sv) noexcept
Constructs a CSVField from a string_view.
Definition csv_row.hpp:117
T get()
Returns the value casted to the requested type, performing type checking before.
Definition csv_row.hpp:180
bool try_parse_timestamp(T &out) noexcept
Parse this field as Unix milliseconds in a 64-bit unsigned integer.
bool try_get(T &out) noexcept
Non-throwing equivalent of get().
Definition csv_row.hpp:225
bool is_null() noexcept
Returns true if field is an empty string or string of whitespace characters.
Definition csv_row.hpp:347
CONSTEXPR csv::string_view get_sv() const noexcept
Return a string view over the field's contents.
Definition csv_row.hpp:344
bool is_float() noexcept
Returns true if field is a floating point value.
Definition csv_row.hpp:363
bool try_parse_timestamp(std::chrono::time_point< std::chrono::system_clock, Duration > &out) noexcept
Parse this field as a std::chrono::system_clock time point.
Definition csv_row.hpp:306
bool is_num() noexcept
Returns true if field is an integer or float.
Definition csv_row.hpp:353
bool is_int() noexcept
Returns true if field is an integer.
Definition csv_row.hpp:358
bool is_timestamp() noexcept
Returns true if field is a timestamp value.
Definition csv_row.hpp:369
bool try_parse_hex(T &parsedValue)
Parse a hexadecimal value, returning false if the value is not hex.
Definition csv_row.hpp:249
A random access iterator over the contents of a CSV row.
Definition csv_row.hpp:641
CONSTEXPR bool operator==(const iterator &other) const noexcept
Two iterators are equal if they point to the same field.
Definition csv_row.hpp:666
Data structure for representing CSV rows.
Definition csv_row.hpp:544
iterator end() const noexcept
Return an iterator pointing to just after the end of the CSVRow.
Definition csv_row.cpp:195
std::reverse_iterator< iterator > reverse_iterator
A reverse iterator over the contents of a CSVRow.
Definition csv_row.hpp:684
CONSTEXPR bool empty() const noexcept
Indicates whether row is empty or not.
Definition csv_row.hpp:562
csv::string_view raw_str() const noexcept
Return a string_view of the raw bytes of this row as they appear in the underlying parse buffer,...
Definition csv_row.cpp:66
const std::vector< std::string > & get_col_names() const
Retrieve this row's associated column names.
Definition csv_row.hpp:588
auto to_sv_range() const
Convert this CSVRow into a std::ranges::input_range of string_views.
Definition csv_row.hpp:612
std::unordered_map< std::string, std::string > to_unordered_map() const
Convert this CSVRow into an unordered map.
Definition csv_row.cpp:85
CONSTEXPR size_t size() const noexcept
Return the number of fields in this row.
Definition csv_row.hpp:565
CSVField operator[](size_t n) const
Return a CSVField object corrsponding to the nth value in the row.
Definition csv_row.cpp:38
internals::ConstColNamesPtr col_names_ptr() const noexcept
Internal accessor for preserving resolved column-name lookup policy across helper types.
Definition csv_row.hpp:593
iterator begin() const
Return an iterator pointing to the first field.
Definition csv_row.cpp:186
size_t byte_offset() const noexcept
Return the absolute byte offset where this row starts in the source.
Definition csv_row.hpp:568
CSVRow(internals::RawCSVDataPtr _data)
Construct a CSVRow view over parsed row storage.
Definition csv_row.hpp:555
A standalone header file containing shared code.
#define IF_CONSTEXPR
Expands to if constexpr in C++17 and if otherwise.
Definition common.hpp:185
#define CONSTEXPR
Expands to constexpr in decent compilers and inline otherwise.
Definition common.hpp:253
Shared exception message templates and throw helpers.
CSV scalar type classification adapter.
Internal JSON serialization helpers for row-like CSV data.
The all encompassing namespace.
DataType
Enumerates the different CSV field types recognized by this library.
Definition data_type.hpp:14
@ CSV_TIMESTAMP
Timestamp value.
@ CSV_INT64
64-bit integer
@ CSV_DOUBLE
Floating point value.
@ CSV_BOOL
Boolean value.
@ CSV_NULL
Empty string.
@ CSV_BIGINT
Integer too large to fit in 64 bits.
@ CSV_INT16
16-bit integer
@ CSV_INT32
32-bit integer
@ CSV_INT8
8-bit integer
@ CSV_STRING
Non-scalar string.
CSVConversionError
Non-throwing CSVField conversion result.
Definition csv_row.hpp:72
@ FloatToInt
A floating point field was requested as an integral type.
@ Overflow
The parsed value does not fit in the requested target type.
@ NegativeToUnsigned
A negative value was requested as an unsigned type.
@ None
Conversion succeeded.
@ NotANumber
The field is not compatible with the requested target type.
const char * csv_conversion_error_message(CSVConversionError error) noexcept
Return a stable human-readable description for a CSVConversionError.
Definition csv_row.hpp:102
std::string_view string_view
The string_view class used by this library.
Definition common.hpp:176
Internal data structures for CSV parsing.
Cached scalar classification and parsed value for one CSV field.
Definition data_type.hpp:35