header_utils
Loading...
Searching...
No Matches
formats.h
1
4
5#pragma once
6
7#include "string_ops.h"
8
10{
13
14 namespace detail
15 {
16 constexpr int get_char(std::string_view& str) { if (str.empty()) { return -1; } const auto result = static_cast<unsigned char>(str[0]); str.remove_prefix(1); return result; }
17 constexpr int get_invalid_char(std::type_identity<std::string_view>) { return -1; }
18
19 inline int get_char(std::istream& strm) { return strm.get(); }
20 template <typename T>
21 requires std::derived_from<T, std::istream>
22 constexpr int get_invalid_char(std::type_identity<T>) { return -1; }
23 }
24
27
28 namespace csv
29 {
38 // TODO: Extend this to accept any char type
39 // TODO: Perhaps it would be best if we didn't allocate the row vector each time (we're moving it away currently)
40 // but reuse its storage (and the storage of its members)
41 // TODO: Extend this to use our own buffers API
42 template <typename BUFFER, typename ROW_CALLBACK>
43 intptr_t load(BUFFER&& buffer, ROW_CALLBACK&& row_callback)
44 {
45 using ghassanpl::formats::detail::get_char;
46 using ghassanpl::formats::detail::get_invalid_char;
47
48 intptr_t line = 0;
49 std::vector<std::string> row;
50
51 static_assert(std::invocable<ROW_CALLBACK, decltype(line), decltype(row)>, "must take arguments convertible to (`intptr row_num`, `std::vector<std::string> row_cells`)");
52
53 static constexpr auto invalid_value = get_invalid_char(std::type_identity<std::remove_cvref_t<decltype(buffer)>>{});
54 bool in_quote = false;
55
56 std::string current_cell;
57 int cp = 0;
58 while ((cp = get_char(buffer)) != invalid_value)
59 {
60 if (in_quote)
61 {
62 if (cp == '"')
63 {
64 if ((cp = get_char(buffer)) == invalid_value)
65 break;
66 if (cp != '"')
67 {
68 in_quote = false;
69 goto no_quote;
70 }
71 }
72 current_cell += (char)cp;
73 }
74 else
75 {
76 no_quote:
77
78 if (cp == '\r')
79 {
80 if ((cp = get_char(buffer)) == invalid_value)
81 break;
82 if (cp != '\n')
83 {
84 current_cell += '\r';
85 current_cell += (char)cp;
86 continue;
87 }
88 }
89
90 if (cp == '\n')
91 {
92 row.push_back(std::exchange(current_cell, {}));
93 if (!row_callback(line++, std::exchange(row, {})))
94 return line;
95 }
96 else if (cp == '"')
97 in_quote = true;
98 else if (cp == ',')
99 row.push_back(std::exchange(current_cell, {}));
100 else
101 current_cell += (char)cp;
102 }
103 }
104
105 if (!current_cell.empty())
106 row.push_back(std::move(current_cell));
107 if (!row.empty())
108 row_callback(line++, std::move(row));
109
110 return line;
111 }
112 }
113
114}
intptr_t load(BUFFER &&buffer, ROW_CALLBACK &&row_callback)
Loads a CSV text file from buffer, calling row_callback for each row.
Definition formats.h:43
The below code is based on Sun's libm library code, which is licensed under the following license: