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
569 CSVField operator[](size_t n) const;
571 inline std::string to_json(const std::vector<std::string>& subset = {}) const {
572 const auto* converter = this->get_json_converter();
573 return converter == nullptr ? "{}"
574 : converter->row_to_json(this->size(), [this](size_t i) { return this->get_field(i); }, subset);
575 }
576 inline std::string to_json_array(const std::vector<std::string>& subset = {}) const {
577 const auto* converter = this->get_json_converter();
578 return converter == nullptr ? "[]"
579 : converter->row_to_json_array(this->size(), [this](size_t i) { return this->get_field(i); }, subset);
580 }
581
583 const std::vector<std::string>& get_col_names() const {
584 return this->data->col_names->get_col_names();
585 }
586
588 internals::ConstColNamesPtr col_names_ptr() const noexcept {
589 return this->data->col_names;
590 }
591
595 std::unordered_map<std::string, std::string> to_unordered_map() const;
596
598 std::unordered_map<std::string, std::string> to_unordered_map(
599 const std::vector<std::string>& subset
600 ) const;
601
602 #ifdef CSV_HAS_CXX20
607 auto to_sv_range() const {
608 return std::views::iota(size_t{0}, this->size())
609 | std::views::transform([this](size_t i) { return this->get_field(i); });
610 }
611 #endif
612
621 operator std::vector<std::string>() const;
622
630 csv::string_view raw_str() const noexcept;
632
636 class iterator {
637 public:
638#ifndef DOXYGEN_SHOULD_SKIP_THIS
639 using value_type = CSVField;
640 using difference_type = int;
641 using pointer = std::shared_ptr<CSVField>;
642 using reference = CSVField & ;
643 using iterator_category = std::random_access_iterator_tag;
644#endif
645 iterator(const CSVRow*, int i);
646
647 reference operator*() const;
648 pointer operator->() const;
649
650 iterator operator++(int);
651 iterator& operator++();
652 iterator operator--(int);
653 iterator& operator--();
654 iterator operator+(difference_type n) const;
655 iterator operator-(difference_type n) const;
656 iterator& operator+=(difference_type n);
657 iterator& operator-=(difference_type n);
658 difference_type operator-(const iterator& other) const noexcept;
659
661 CONSTEXPR bool operator==(const iterator& other) const noexcept {
662 return this->i == other.i;
663 };
664
665 CONSTEXPR bool operator!=(const iterator& other) const noexcept { return !operator==(other); }
666
667#ifndef NDEBUG
668 friend CSVRow;
669#endif
670
671 private:
672 const CSVRow * daddy = nullptr; // Pointer to parent
673 internals::RawCSVDataPtr data = nullptr; // Keep data alive for lifetime of iterator
674 std::shared_ptr<CSVField> field = nullptr; // Current field pointed at
675 int i = 0; // Index of current field
676 };
677
679 using reverse_iterator = std::reverse_iterator<iterator>;
680
685 iterator begin() const;
686 iterator end() const noexcept;
687 reverse_iterator rbegin() const noexcept;
688 reverse_iterator rend() const;
690
691 private:
693 inline csv::string_view get_field_impl(size_t index, const internals::RawCSVDataPtr& _data) const {
694 if (index >= this->size())
695 throw std::runtime_error(internals::CSV_ERROR_INDEX_OUT_OF_BOUNDS);
696
697 const size_t field_index = this->fields_start + index;
698 const auto field = _data->fields[field_index];
699 csv::string_view field_str;
700 if (field.has_realized_storage()) {
701 field_str = _data->quote_arena.view(field.start, field.length);
702 }
703 else {
704 field_str = csv::string_view(_data->data).substr(this->data_start + field.start, field.length);
705 }
706
707 if (_data->has_ws_trimming) {
708 field_str = internals::get_trimmed(field_str, _data->ws_flags);
709 }
710
711 return field_str;
712 }
713
714 CSVField make_field(size_t index, const internals::RawCSVDataPtr& _data) const;
715
717 csv::string_view get_field(size_t index) const;
718
722 csv::string_view get_field_safe(size_t index, internals::RawCSVDataPtr _data) const;
723
724 const internals::JsonConverter* get_json_converter() const {
725 if (this->data.get() == nullptr) {
726 return nullptr;
727 }
728
729 return &this->data->json_converter.get_or_create([this]() {
730 const std::vector<std::string> columns = this->data->col_names
731 ? this->data->col_names->get_col_names()
732 : std::vector<std::string>();
733 return std::make_shared<internals::JsonConverter>(columns);
734 });
735 }
736
737 internals::RawCSVDataPtr data;
738
740 size_t data_start = 0;
741
743 size_t fields_start = 0;
744
746 size_t row_length = 0;
747
749 size_t data_end = (std::numeric_limits<size_t>::max)();
750 };
751
752#ifdef _MSC_VER
753#pragma region CSVField::get Specializations
754#endif
756 template<>
757 inline std::string CSVField::get<std::string>() {
758 return std::string(this->sv);
759 }
760
766 template<>
767 CONSTEXPR_14 csv::string_view CSVField::get<csv::string_view>() {
768 return this->sv;
769 }
770
772 template<>
773 inline long double CSVField::get<long double>() {
774 if (!is_num())
775 throw std::runtime_error(internals::ERROR_NAN);
776
777 return this->numeric_value_as_long_double();
778 }
779
781 template<>
782 inline bool CSVField::try_get<std::string>(std::string& out) noexcept {
783 out = std::string(this->sv);
784 return true;
785 }
786
788 template<>
789 CONSTEXPR_14 bool CSVField::try_get<csv::string_view>(csv::string_view& out) noexcept {
790 out = this->sv;
791 return true;
792 }
793
795 template<>
796 inline bool CSVField::try_get<long double>(long double& out) noexcept {
797 if (!is_num())
798 return false;
799
800 out = this->numeric_value_as_long_double();
801 return true;
802 }
803#ifdef _MSC_VER
804#pragma endregion CSVField::get Specializations
805#endif
806
808 template<>
809 CONSTEXPR bool CSVField::operator==(const char * other) const noexcept
810 {
811 return this->sv == other;
812 }
813
815 template<>
817 {
818 return this->sv == other;
819 }
820}
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:636
CONSTEXPR bool operator==(const iterator &other) const noexcept
Two iterators are equal if they point to the same field.
Definition csv_row.hpp:661
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:679
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:583
auto to_sv_range() const
Convert this CSVRow into a std::ranges::input_range of string_views.
Definition csv_row.hpp:607
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:588
iterator begin() const
Return an iterator pointing to the first field.
Definition csv_row.cpp:186
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:183
#define CONSTEXPR
Expands to constexpr in decent compilers and inline otherwise.
Definition common.hpp:251
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:174
Internal data structures for CSV parsing.
Cached scalar classification and parsed value for one CSV field.
Definition data_type.hpp:35