@@ -117,6 +117,9 @@ namespace glz
117
117
}
118
118
}
119
119
};
120
+
121
+ // CSV spec: https://www.ietf.org/rfc/rfc4180.txt
122
+ // Quotes are escaped via double quotes
120
123
121
124
template <string_t T>
122
125
struct from <CSV, T>
@@ -129,35 +132,51 @@ namespace glz
129
132
}
130
133
131
134
value.clear ();
132
- auto start = it;
133
- while (it < end) {
134
- switch (*it) {
135
- case ' ,' :
136
- case ' \n ' : {
137
- value.append (start, static_cast <size_t >(it - start));
138
- return ;
135
+
136
+ if (it == end) {
137
+ return ;
138
+ }
139
+
140
+ if (*it == ' "' ) {
141
+ // Quoted field
142
+ ++it; // Skip the opening quote
143
+ while (it != end) {
144
+ if (*it == ' "' ) {
145
+ ++it; // Skip the quote
146
+ if (it == end) {
147
+ // End of input after closing quote
148
+ break ;
149
+ }
150
+ if (*it == ' "' ) {
151
+ // Escaped quote
152
+ value.push_back (' "' );
153
+ ++it;
154
+ } else {
155
+ // Closing quote
156
+ break ;
157
+ }
158
+ } else {
159
+ value.push_back (*it);
160
+ ++it;
161
+ }
139
162
}
140
- case ' \\ ' :
141
- case ' \b ' :
142
- case ' \f ' :
143
- case ' \r ' :
144
- case ' \t ' : {
163
+ // After closing quote, expect comma, newline, or end of input
164
+ if (it != end && *it != ' ,' && *it == ' \n ' ) {
165
+ // Invalid character after closing quote
145
166
ctx.error = error_code::syntax_error;
146
167
return ;
147
168
}
148
- case ' \0 ' : {
149
- ctx.error = error_code::unexpected_end;
150
- return ;
151
- }
152
- default :
169
+ } else {
170
+ // Unquoted field
171
+ while (it != end && *it != ' ,' && *it != ' \n ' ) {
172
+ value.push_back (*it);
153
173
++it;
154
174
}
155
175
}
156
-
157
- value.append (start, static_cast <size_t >(it - start));
158
176
}
159
177
};
160
178
179
+
161
180
template <bool_t T>
162
181
struct from <CSV, T>
163
182
{
@@ -439,89 +458,87 @@ namespace glz
439
458
decode_hash_with_size<CSV, T, HashInfo, HashInfo.type >::op (key.data (), end, key.size ());
440
459
441
460
if (index < N) [[likely]] {
442
- jump_table<N>(
443
- [&]<size_t I>() {
444
- decltype (auto ) member = [&]() -> decltype (auto ) {
445
- if constexpr (reflectable<T>) {
446
- return get_member (value, get<I>(to_tuple (value)));
461
+ visit<N>([&]<size_t I>() {
462
+ decltype (auto ) member = [&]() -> decltype (auto ) {
463
+ if constexpr (reflectable<T>) {
464
+ return get_member (value, get<I>(to_tuple (value)));
465
+ }
466
+ else {
467
+ return get_member (value, get<I>(reflect<T>::values));
468
+ }
469
+ }();
470
+
471
+ using M = std::decay_t <decltype (member)>;
472
+ if constexpr (fixed_array_value_t <M> && emplace_backable<M>) {
473
+ size_t col = 0 ;
474
+ while (it != end) {
475
+ if (col < member.size ()) [[likely]] {
476
+ read <CSV>::op<Opts>(member[col][csv_index], ctx, it, end);
447
477
}
448
- else {
449
- return get_member (value, get<I>(reflect<T>::values) );
478
+ else [[unlikely]] {
479
+ read <CSV>::op<Opts>(member. emplace_back ()[csv_index], ctx, it, end );
450
480
}
451
- }();
452
-
453
- using M = std::decay_t <decltype (member)>;
454
- if constexpr (fixed_array_value_t <M> && emplace_backable<M>) {
455
- size_t col = 0 ;
456
- while (it != end) {
457
- if (col < member.size ()) [[likely]] {
458
- read <CSV>::op<Opts>(member[col][csv_index], ctx, it, end);
459
- }
460
- else [[unlikely]] {
461
- read <CSV>::op<Opts>(member.emplace_back ()[csv_index], ctx, it, end);
462
- }
463
481
464
- if (*it == ' \r ' ) {
465
- ++it;
466
- if (*it == ' \n ' ) {
467
- ++it;
468
- break ;
469
- }
470
- else [[unlikely]] {
471
- ctx.error = error_code::syntax_error;
472
- return ;
473
- }
474
- }
475
- else if (*it == ' \n ' ) {
482
+ if (*it == ' \r ' ) {
483
+ ++it;
484
+ if (*it == ' \n ' ) {
476
485
++it;
477
486
break ;
478
487
}
479
- else if (it == end) {
480
- return ;
481
- }
482
-
483
- if (*it == ' ,' ) [[likely]] {
484
- ++it;
485
- }
486
488
else [[unlikely]] {
487
489
ctx.error = error_code::syntax_error;
488
490
return ;
489
491
}
492
+ }
493
+ else if (*it == ' \n ' ) {
494
+ ++it;
495
+ break ;
496
+ }
497
+ else if (it == end) {
498
+ return ;
499
+ }
490
500
491
- ++col;
501
+ if (*it == ' ,' ) [[likely]] {
502
+ ++it;
492
503
}
504
+ else [[unlikely]] {
505
+ ctx.error = error_code::syntax_error;
506
+ return ;
507
+ }
508
+
509
+ ++col;
493
510
}
494
- else {
495
- while (it != end) {
496
- read <CSV>::op<Opts>(member, ctx, it, end);
511
+ }
512
+ else {
513
+ while (it != end) {
514
+ read <CSV>::op<Opts>(member, ctx, it, end);
497
515
498
- if (*it == ' \r ' ) {
499
- ++it;
500
- if (*it == ' \n ' ) {
501
- ++it;
502
- break ;
503
- }
504
- else [[unlikely]] {
505
- ctx.error = error_code::syntax_error;
506
- return ;
507
- }
508
- }
509
- else if (*it == ' \n ' ) {
516
+ if (*it == ' \r ' ) {
517
+ ++it;
518
+ if (*it == ' \n ' ) {
510
519
++it;
511
520
break ;
512
521
}
513
-
514
- if (*it == ' ,' ) [[likely]] {
515
- ++it;
516
- }
517
522
else [[unlikely]] {
518
523
ctx.error = error_code::syntax_error;
519
524
return ;
520
525
}
521
526
}
527
+ else if (*it == ' \n ' ) {
528
+ ++it;
529
+ break ;
530
+ }
531
+
532
+ if (*it == ' ,' ) [[likely]] {
533
+ ++it;
534
+ }
535
+ else [[unlikely]] {
536
+ ctx.error = error_code::syntax_error;
537
+ return ;
538
+ }
522
539
}
523
- },
524
- index );
540
+ }
541
+ }, index );
525
542
526
543
if (bool (ctx.error )) [[unlikely]] {
527
544
return ;
@@ -556,7 +573,7 @@ namespace glz
556
573
key.data (), key.data () + key.size (), key.size ());
557
574
558
575
if (index < N) [[likely]] {
559
- jump_table <N>(
576
+ visit <N>(
560
577
[&]<size_t I>() {
561
578
decltype (auto ) member = [&]() -> decltype (auto ) {
562
579
if constexpr (reflectable<T>) {
@@ -629,13 +646,12 @@ namespace glz
629
646
return value;
630
647
}
631
648
632
- template <uint32_t layout = rowwise, read_csv_supported T>
633
- [[nodiscard]] inline error_ctx read_file_csv (T& value, const sv file_name)
649
+ template <uint32_t layout = rowwise, read_csv_supported T, is_buffer Buffer >
650
+ [[nodiscard]] inline error_ctx read_file_csv (T& value, const sv file_name, Buffer&& buffer )
634
651
{
635
652
context ctx{};
636
653
ctx.current_file = file_name;
637
654
638
- std::string buffer;
639
655
const auto ec = file_to_buffer (buffer, ctx.current_file );
640
656
641
657
if (bool (ec)) {
0 commit comments