Vince's CSV Parser
Loading...
Searching...
No Matches
common.hpp
Go to the documentation of this file.
1
5#pragma once
6#include <algorithm>
7#include <array>
8#include <cassert>
9#include <cmath>
10#include <cstdint>
11#include <cstdlib>
12#include <deque>
13#include <limits>
14#include <memory>
15#if !defined(CSV_ENABLE_THREADS) || CSV_ENABLE_THREADS
16#include <mutex>
17#endif
18
19#if defined(_WIN32)
20# ifndef WIN32_LEAN_AND_MEAN
21# define WIN32_LEAN_AND_MEAN
22# endif
23# include <windows.h>
24# undef max
25# undef min
26#elif defined(__linux__)
27# include <unistd.h>
28#endif
29
30#include <type_traits>
31
32#if defined(__EMSCRIPTEN__)
33#undef CSV_ENABLE_THREADS
34#define CSV_ENABLE_THREADS 0
35#elif !defined(CSV_ENABLE_THREADS)
36#define CSV_ENABLE_THREADS 1
37#endif
38
39// Minimal portability macros (Hedley subset) with CSV_ prefix.
40#if defined(CSV_CODE_COVERAGE)
41 #define CSV_CONST
42 #define CSV_PURE
43 #define CSV_FORCE_INLINE inline
44 #define CSV_PRIVATE
45 #define CSV_NON_NULL(...)
46#elif defined(__clang__) || defined(__GNUC__)
47 #define CSV_CONST __attribute__((__const__))
48 #define CSV_PURE __attribute__((__pure__))
49 #define CSV_FORCE_INLINE inline __attribute__((__always_inline__))
50 #if defined(_WIN32)
51 #define CSV_PRIVATE
52 #else
53 #define CSV_PRIVATE __attribute__((__visibility__("hidden")))
54 #endif
55 #define CSV_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__)))
56#elif defined(_MSC_VER)
57 #define CSV_CONST
58 #define CSV_PURE
59 #define CSV_FORCE_INLINE __forceinline
60 #define CSV_PRIVATE
61 #define CSV_NON_NULL(...)
62#else
63 #define CSV_CONST
64 #define CSV_PURE
65 #define CSV_FORCE_INLINE inline
66 #define CSV_PRIVATE
67 #define CSV_NON_NULL(...)
68#endif
69
70// MSVC-specific warning suppression helpers. Use __pragma so the macros
71// work inside other macro bodies (where #pragma is not allowed).
72#ifdef _MSC_VER
73# define CSV_MSVC_PUSH_DISABLE(w) __pragma(warning(push)) __pragma(warning(disable: w))
74# define CSV_MSVC_POP __pragma(warning(pop))
75#else
76# define CSV_MSVC_PUSH_DISABLE(w)
77# define CSV_MSVC_POP
78#endif
79
80// Detect compiler exception support to select the optional throwing APIs.
81#if defined(__cpp_exceptions) || defined(_CPPUNWIND) || defined(__EXCEPTIONS)
82#define CSV_EXCEPTIONS_ENABLED 1
83#else
84#define CSV_EXCEPTIONS_ENABLED 0
85#endif
86
87// Keep complete handlers out of compiler-disabled builds: unreachable branches
88// would still need named catch variables and exception-only declarations.
89// Usage: CSV_TRY { ... } CSV_CATCH((const Error& error), { ... })
90// Parenthesize the catch declaration so template commas remain one argument.
91// Keep preprocessing directives outside the catch-body macro argument.
92#if CSV_EXCEPTIONS_ENABLED
93#define CSV_TRY try
94#define CSV_CATCH(exception_declaration, ...) catch exception_declaration __VA_ARGS__
95#else
96#define CSV_TRY
97#define CSV_CATCH(exception_declaration, ...)
98#endif
99
100
101// Detect C++ standard version BEFORE namespace to properly include string_view
102// MSVC: __cplusplus == 199711L unless /Zc:__cplusplus is set; use _MSVC_LANG instead.
103#if defined(_MSVC_LANG) && _MSVC_LANG > __cplusplus
104# define CSV_CPLUSPLUS _MSVC_LANG
105#else
106# define CSV_CPLUSPLUS __cplusplus
107#endif
108
109#if CSV_CPLUSPLUS >= 202302L
110#define CSV_HAS_CXX23
111#endif
112
113#if CSV_CPLUSPLUS >= 202002L
114#define CSV_HAS_CXX20
115#endif
116
117#if CSV_CPLUSPLUS >= 201703L
118#define CSV_HAS_CXX17
119#endif
120
121#if CSV_CPLUSPLUS >= 201402L
122#define CSV_HAS_CXX14
123#endif
124
125// Annotate intentional switch fallthroughs in parser hot loops without
126// reshaping the control flow just to appease compiler diagnostics.
127#if defined(CSV_HAS_CXX17)
128#define CSV_FALLTHROUGH [[fallthrough]]
129#elif defined(__clang__) && defined(__has_cpp_attribute)
130#if __has_cpp_attribute(clang::fallthrough)
131#define CSV_FALLTHROUGH [[clang::fallthrough]]
132#else
133#define CSV_FALLTHROUGH ((void)0)
134#endif
135#elif defined(__GNUC__) && __GNUC__ >= 7
136#define CSV_FALLTHROUGH __attribute__((fallthrough))
137#else
138#define CSV_FALLTHROUGH ((void)0)
139#endif
140
141// Include string_view BEFORE csv namespace to avoid namespace pollution issues
142#ifdef CSV_HAS_CXX17
143#include <string_view>
144#else
145#include "../external/string_view.hpp"
146#endif
147
148#ifdef CSV_HAS_CXX20
149#include <ranges>
150#endif
151
152namespace csv {
153#ifdef _MSC_VER
154#pragma region Compatibility Macros
155#endif
169// Allows static assertions without specifying a message
170#define STATIC_ASSERT(x) static_assert(x, "Assertion failed")
171
172#ifdef NDEBUG
173 #define CSV_DEBUG_ASSERT(x) ((void)sizeof(x), (void)0)
174#else
175 #define CSV_DEBUG_ASSERT(x) assert(x)
176#endif
177
178#ifdef CSV_HAS_CXX17
182 using string_view = std::string_view;
183#else
187 using string_view = nonstd::string_view;
188#endif
189
190#ifdef CSV_HAS_CXX17
191 #define IF_CONSTEXPR if constexpr
192 #define CONSTEXPR_VALUE constexpr
193
194 #define CONSTEXPR_17 constexpr
195#else
196 #define IF_CONSTEXPR if
197 #define CONSTEXPR_VALUE const
198
199 #define CONSTEXPR_17 inline
200#endif
201
202#ifdef CSV_HAS_CXX14
203 template<bool B, class T = void>
204 using enable_if_t = std::enable_if_t<B, T>;
205
206 #define CONSTEXPR_14 constexpr
207 #define CONSTEXPR_VALUE_14 constexpr
208#else
209 template<bool B, class T = void>
210 using enable_if_t = typename std::enable_if<B, T>::type;
211
212 #define CONSTEXPR_14 inline
213 #define CONSTEXPR_VALUE_14 const
214#endif
215
216 namespace internals {
217 template<bool B, class T = void>
218 using enable_if_t = csv::enable_if_t<B, T>;
219 }
220
221#ifdef CSV_HAS_CXX17
222 template<typename F, typename... Args>
223 using invoke_result_t = typename std::invoke_result<F, Args...>::type;
224#else
225 template<typename F, typename... Args>
226 using invoke_result_t = typename std::result_of<F(Args...)>::type;
227#endif
228
229 template<typename... Ts>
230 using void_t = void;
231
232 template<typename F, typename ReturnType, typename Enable, typename... Args>
233 struct is_invocable_returning_impl : std::false_type {};
234
235 template<typename F, typename ReturnType, typename... Args>
237 F,
238 ReturnType,
239 void_t<invoke_result_t<F, Args...>>,
240 Args...
241 > : std::integral_constant<
242 bool,
243 std::is_convertible<invoke_result_t<F, Args...>, ReturnType>::value
244 > {};
245
246 template<typename F, typename ReturnType, typename... Args>
247 struct is_invocable_returning : is_invocable_returning_impl<F, ReturnType, void, Args...> {};
248
249 // Resolves g++ bug with regard to constexpr methods.
250 // Keep this gated to C++17+, since C++11/14 pedantic mode rejects constexpr
251 // non-static members when the enclosing class is non-literal.
252 // See: https://stackoverflow.com/questions/36489369/constexpr-non-static-member-function-with-non-constexpr-constructor-gcc-clang-d
253#if defined(__GNUC__) && !defined(__clang__)
254 #if defined(CSV_HAS_CXX17) && (((__GNUC__ == 7) && (__GNUC_MINOR__ >= 2)) || (__GNUC__ >= 8))
255 #define CONSTEXPR constexpr
256 #endif
257#else
258 #ifdef CSV_HAS_CXX17
259 #define CONSTEXPR constexpr
260 #endif
261#endif
262
263#ifndef CONSTEXPR
264#define CONSTEXPR inline
265#endif
266
267#ifdef _MSC_VER
268#pragma endregion
269#endif
270
271 namespace internals {
272 template<typename T>
274 private:
275 template<typename U>
276 static auto test(int) -> decltype(
277 std::hash<U>{}(std::declval<const U&>()),
278 std::true_type{}
279 );
280
281 template<typename>
282 static std::false_type test(...);
283
284 public:
285 static constexpr bool value = decltype(test<T>(0))::value;
286 };
287
288 template<typename T>
290 private:
291 template<typename U>
292 static auto test(int) -> decltype(
293 std::declval<const U&>() == std::declval<const U&>(),
294 std::true_type{}
295 );
296
297 template<typename>
298 static std::false_type test(...);
299
300 public:
301 static constexpr bool value = decltype(test<T>(0))::value;
302 };
303
304 template<typename T>
306 public:
307 lazy_shared_ptr() = default;
308 lazy_shared_ptr(const lazy_shared_ptr&) = delete;
309 lazy_shared_ptr& operator=(const lazy_shared_ptr&) = delete;
310
311 lazy_shared_ptr(lazy_shared_ptr&& other) noexcept : value_(std::move(other.value_)) {}
312
313 lazy_shared_ptr& operator=(lazy_shared_ptr&& other) noexcept {
314 if (this != &other) {
315 value_ = std::move(other.value_);
316 }
317
318 return *this;
319 }
320
321 template<typename Factory>
322 T& get_or_create(Factory&& factory) const {
323#if CSV_ENABLE_THREADS
324 std::call_once(init_once_, [this, &factory]() {
325 value_ = factory();
326 });
327#else
328 if (!value_) {
329 value_ = factory();
330 }
331#endif
332 return *value_;
333 }
334
335 T* get() const noexcept {
336 return value_.get();
337 }
338
339 private:
340 mutable std::shared_ptr<T> value_ = nullptr;
341#if CSV_ENABLE_THREADS
342 mutable std::once_flag init_once_;
343#endif
344 };
345
346 #ifdef CSV_HAS_CXX20
347 #ifdef _MSC_VER
348 #pragma region CXX20 Concepts
349 #endif
350
351 template<typename T>
353 std::ranges::input_range<std::remove_reference_t<T>>
354 && std::convertible_to<
355 std::ranges::range_reference_t<std::remove_reference_t<T>>,
357 >;
358
359 template<typename T>
360 concept has_to_sv_range = requires(const std::remove_reference_t<T>& value) {
361 { value.to_sv_range() } -> std::ranges::input_range;
362 requires std::convertible_to<
363 std::ranges::range_reference_t<decltype(value.to_sv_range())>,
365 >;
366 };
367
368 template<typename T>
370
371 template<typename T>
373 std::ranges::input_range<std::remove_reference_t<T>>
374 && csv_row_like<
375 std::ranges::range_reference_t<std::remove_reference_t<T>>
376 >;
377
378 #ifdef _MSC_VER
379 #pragma endregion
380 #endif
381 #endif
382
383 // PAGE_SIZE macro could be already defined by the host system.
384#if defined(PAGE_SIZE)
385#undef PAGE_SIZE
386#endif
387
388// Get operating system specific details
389#if defined(_WIN32)
390 inline int getpagesize() {
391 _SYSTEM_INFO sys_info = {};
392 GetSystemInfo(&sys_info);
393 return std::max(sys_info.dwPageSize, sys_info.dwAllocationGranularity);
394 }
395
396 const int PAGE_SIZE = getpagesize();
397#elif defined(__linux__)
398 const int PAGE_SIZE = getpagesize();
399#else
403 const int PAGE_SIZE = 4096;
404#endif
405
416 constexpr size_t CSV_CHUNK_SIZE_DEFAULT = 10000000; // 10MB
417
419 typedef std::uint32_t CSVChunkIndex;
420
421 CONSTEXPR_VALUE_14 CSVChunkIndex CSV_CHUNK_INDEX_MAX = (std::numeric_limits<CSVChunkIndex>::max)();
422
423 CONSTEXPR_VALUE_14 size_t CSV_CHUNK_SIZE_MAX = CSV_CHUNK_INDEX_MAX;
424
430 constexpr size_t CSV_CHUNK_SIZE_FLOOR = 500 * 1024; // 500KB
431
433 constexpr size_t CSV_SPECULATIVE_PARALLEL_MIN_BYTES = 50ull * 1024ull * 1024ull; // 50MB
434
435 template<typename T>
436 inline bool is_equal(T a, T b, T epsilon = 0.001) {
438 static_assert(std::is_floating_point<T>::value, "T must be a floating point type.");
439 return std::abs(a - b) < epsilon;
440 }
441
448 enum class ParseFlags {
450 QUOTE = 2 | 1,
451 NOT_SPECIAL = 4,
452 DELIMITER = 4 | 1,
453 CARRIAGE_RETURN = 4 | 2,
454 NEWLINE = 4 | 2 | 1
455 };
456
459 constexpr ParseFlags quote_escape_flag(ParseFlags flag, bool quote_escape) noexcept {
460 return (ParseFlags)((int)flag & ~((int)ParseFlags::QUOTE * quote_escape));
461 }
462
463 // Assumed to be true by parsing functions: allows for testing
464 // if an item is DELIMITER or NEWLINE with a >= statement
465 STATIC_ASSERT(ParseFlags::DELIMITER < ParseFlags::CARRIAGE_RETURN);
466 STATIC_ASSERT(ParseFlags::DELIMITER < ParseFlags::NEWLINE);
467 STATIC_ASSERT(ParseFlags::CARRIAGE_RETURN < ParseFlags::NEWLINE);
468
474 STATIC_ASSERT(quote_escape_flag(ParseFlags::NOT_SPECIAL, false) == ParseFlags::NOT_SPECIAL);
475 STATIC_ASSERT(quote_escape_flag(ParseFlags::QUOTE, false) == ParseFlags::QUOTE);
476 STATIC_ASSERT(quote_escape_flag(ParseFlags::DELIMITER, false) == ParseFlags::DELIMITER);
477 STATIC_ASSERT(quote_escape_flag(ParseFlags::CARRIAGE_RETURN, false) == ParseFlags::CARRIAGE_RETURN);
478 STATIC_ASSERT(quote_escape_flag(ParseFlags::NEWLINE, false) == ParseFlags::NEWLINE);
479
480 STATIC_ASSERT(quote_escape_flag(ParseFlags::NOT_SPECIAL, true) == ParseFlags::NOT_SPECIAL);
481 STATIC_ASSERT(quote_escape_flag(ParseFlags::QUOTE, true) == ParseFlags::QUOTE_ESCAPE_QUOTE);
482 STATIC_ASSERT(quote_escape_flag(ParseFlags::DELIMITER, true) == ParseFlags::NOT_SPECIAL);
483 STATIC_ASSERT(quote_escape_flag(ParseFlags::CARRIAGE_RETURN, true) == ParseFlags::NOT_SPECIAL);
484 STATIC_ASSERT(quote_escape_flag(ParseFlags::NEWLINE, true) == ParseFlags::NOT_SPECIAL);
485
487 using ParseFlagMap = std::array<ParseFlags, 256>;
488
490 using WhitespaceMap = std::array<bool, 256>;
491 }
492
494 constexpr int CSV_NOT_FOUND = -1;
495
497 constexpr unsigned CHAR_OFFSET = std::numeric_limits<char>::is_signed ? 128 : 0;
498}
std::array< ParseFlags, 256 > ParseFlagMap
An array which maps ASCII chars to a parsing flag.
Definition common.hpp:487
std::array< bool, 256 > WhitespaceMap
An array which maps ASCII chars to a flag indicating if it is whitespace.
Definition common.hpp:490
bool is_equal(T a, T b, T epsilon=0.001)
Definition common.hpp:436
const int PAGE_SIZE
Size of a memory page in bytes.
Definition common.hpp:403
ParseFlags
An enum used for describing the significance of each character with respect to CSV parsing.
Definition common.hpp:448
@ QUOTE_ESCAPE_QUOTE
A quote inside or terminating a quote_escaped field.
@ NOT_SPECIAL
Characters with no special meaning or escaped delimiters and newlines.
@ NEWLINE
Characters which signify a new row.
@ CARRIAGE_RETURN
Characters which signify a carriage return.
@ QUOTE
Characters which may signify a quote escape.
@ DELIMITER
Characters which signify a new field.
constexpr size_t CSV_CHUNK_SIZE_FLOOR
Minimum supported custom chunk size for CSVFormat::chunk_size().
Definition common.hpp:430
constexpr ParseFlags quote_escape_flag(ParseFlags flag, bool quote_escape) noexcept
Transform the ParseFlags given the context of whether or not the current field is quote escaped.
Definition common.hpp:459
std::uint32_t CSVChunkIndex
Type used to represent the location of a CSV byte within a larger chunk.
Definition common.hpp:419
constexpr size_t CSV_SPECULATIVE_PARALLEL_MIN_BYTES
Default minimum source size before speculative parallel parsing is considered.
Definition common.hpp:433
constexpr size_t CSV_CHUNK_SIZE_DEFAULT
Default chunk size for lazy-loading large CSV files.
Definition common.hpp:416
The all encompassing namespace.
constexpr int CSV_NOT_FOUND
Integer indicating a requested column wasn't found.
Definition common.hpp:494
constexpr unsigned CHAR_OFFSET
Offset to convert char into array index.
Definition common.hpp:497
std::string_view string_view
The string_view class used by this library.
Definition common.hpp:182