Skip to content

Commit ea547fd

Browse files
CSV Parser 2.1.3 Patch 7/28/21 (vincentlaucsb#179)
* Fixed more compilation errors * Added hex number parsing and a minor bug fix that could cause an assertion failure in C++11 * Fixed some clang issues * More fixes * Weird clang issues * Update test_write_csv.cpp
1 parent f7db370 commit ea547fd

File tree

15 files changed

+361
-93
lines changed

15 files changed

+361
-93
lines changed

.travis.yml

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ language:
33
matrix:
44
include:
55
- os: linux
6-
env: STD=c++11 CSV_CXX_STANDARD=11 MAIN_BUILD=true CXX_COMPILER=g++-9 C_COMPILER=gcc-9
6+
env: STD=c++11 CSV_CXX_STANDARD=11 CXX_COMPILER=g++-9 C_COMPILER=gcc-9
77
compiler: gcc
88
addons:
99
apt:
@@ -25,7 +25,7 @@ matrix:
2525
packages: ['g++-9', 'cmake', 'valgrind', 'doxygen']
2626
- os: linux
2727
dist: focal
28-
env: CSV_CXX_STANDARD=11 MAIN_BUILD=true CXX_COMPILER=clang++-11 C_COMPILER=clang-11
28+
env: CSV_CXX_STANDARD=11 CXX_COMPILER=clang++-11 C_COMPILER=clang-11
2929
compiler: clang
3030
addons:
3131
apt:
@@ -36,7 +36,7 @@ matrix:
3636
- clang-11
3737
- os: linux
3838
dist: focal
39-
env: CSV_CXX_STANDARD=14 MAIN_BUILD=true CXX_COMPILER=clang++-11 C_COMPILER=clang-11
39+
env: CSV_CXX_STANDARD=14 CXX_COMPILER=clang++-11 C_COMPILER=clang-11
4040
compiler: clang
4141
addons:
4242
apt:
@@ -47,7 +47,7 @@ matrix:
4747
- clang-11
4848
- os: linux
4949
dist: focal
50-
env: CSV_CXX_STANDARD=17 MAIN_BUILD=true CXX_COMPILER=clang++-11 C_COMPILER=clang-11
50+
env: CSV_CXX_STANDARD=17 CXX_COMPILER=clang++-11 C_COMPILER=clang-11
5151
compiler: clang
5252
addons:
5353
apt:
@@ -58,14 +58,11 @@ matrix:
5858
- clang-11
5959
dist: trusty
6060
sudo: required
61-
before_install:
62-
- pyenv install 3.6.0
63-
- pyenv global 3.6.0
64-
- pip3 install gcovr
6561
script:
6662
- export CSV_TEST_ROOT=$PWD/tests
6763
- cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=$CXX_COMPILER -DCMAKE_C_COMPILER=$C_COMPILER -DCSV_CXX_STANDARD=$CSV_CXX_STANDARD
68-
- make csv_coverage;
64+
- make csv_test
65+
- ./tests/csv_test
6966

7067
# Memory leak check
7168
- if [ "$MAIN_BUILD" == "true" ]; then

CMakeLists.txt

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ if (CSV_DEVELOPER)
5959
# More error messages.
6060
if (UNIX)
6161
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} \
62-
-Wall -Werror -Wextra -Wshadow -Wsign-compare \
63-
-Wshadow -Wwrite-strings -Wpointer-arith -Winit-self \
62+
-Wall -Werror -Wextra -Wsign-compare \
63+
-Wwrite-strings -Wpointer-arith -Winit-self \
6464
-Wconversion -Wno-sign-conversion")
6565
endif()
6666

@@ -98,16 +98,16 @@ if (CSV_DEVELOPER)
9898
add_subdirectory("tests")
9999

100100
# Code coverage
101-
find_program( GCOV_PATH gcov )
102-
if(GCOV_PATH)
103-
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/modules")
104-
include(CodeCoverage)
105-
append_coverage_compiler_flags()
106-
set(ENV{CSV_TEST_ROOT} ${CSV_TEST_DIR})
107-
setup_target_for_coverage_gcovr_html(
108-
NAME csv_coverage
109-
EXECUTABLE csv_test
110-
EXCLUDE "tests/*"
111-
)
112-
endif()
101+
#find_program( GCOV_PATH gcov )
102+
#if(GCOV_PATH)
103+
# set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/modules")
104+
# include(CodeCoverage)
105+
# append_coverage_compiler_flags()
106+
# set(ENV{CSV_TEST_ROOT} ${CSV_TEST_DIR})
107+
# setup_target_for_coverage_gcovr_html(
108+
# NAME csv_coverage
109+
# EXECUTABLE csv_test
110+
# EXCLUDE "tests/*"
111+
# )
112+
#endif()
113113
endif()

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,12 @@ for (auto& row: reader) {
194194
// numbers cannot be converted to unsigned types
195195
row["timestamp"].get<int>();
196196

197+
// You can also attempt to parse hex values
198+
int value;
199+
if (row["hexValue"].try_parse_hex(value)) {
200+
std::cout << "Hex value is " << value << std::endl;
201+
}
202+
197203
// ..
198204
}
199205
}

include/csv.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
CSV for C++, version 2.1.2
2+
CSV for C++, version 2.1.3
33
https://github.com/vincentlaucsb/csv-parser
44
55
MIT License

include/internal/basic_csv_parser.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ namespace csv {
182182
if (this->field_length == 0) {
183183
quote_escape = true;
184184
data_pos++;
185-
if (field_start == UNINITIALIZED_FIELD && !ws_flag(in[data_pos]))
185+
if (field_start == UNINITIALIZED_FIELD && data_pos < in.size() && !ws_flag(in[data_pos]))
186186
field_start = (int)(data_pos - current_row_start());
187187
break;
188188
}

include/internal/basic_csv_parser.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ namespace csv {
5858
* ASCII number for a character c and, v[i + 128] is true if
5959
* c is a whitespace character
6060
*/
61-
HEDLEY_CONST CONSTEXPR_14 WhitespaceMap make_ws_flags(const char* ws_chars, size_t n_chars) {
61+
HEDLEY_CONST CONSTEXPR_17 WhitespaceMap make_ws_flags(const char* ws_chars, size_t n_chars) {
6262
std::array<bool, 256> ret = {};
6363
for (int i = -128; i < 128; i++) {
6464
const int arr_idx = i + 128;

include/internal/common.hpp

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ namespace csv {
5252
* Intended for functions and methods.
5353
*/
5454

55+
#define STATIC_ASSERT(x) static_assert(x, "Assertion failed")
56+
5557
#if CMAKE_CXX_STANDARD == 17 || __cplusplus >= 201703L
5658
#define CSV_HAS_CXX17
5759
#endif
@@ -177,22 +179,22 @@ namespace csv {
177179

178180
// Assumed to be true by parsing functions: allows for testing
179181
// if an item is DELIMITER or NEWLINE with a >= statement
180-
static_assert(ParseFlags::DELIMITER < ParseFlags::NEWLINE);
182+
STATIC_ASSERT(ParseFlags::DELIMITER < ParseFlags::NEWLINE);
181183

182184
/** Optimizations for reducing branching in parsing loop
183185
*
184186
* Idea: The meaning of all non-quote characters changes depending
185187
* on whether or not the parser is in a quote-escaped mode (0 or 1)
186188
*/
187-
static_assert(quote_escape_flag(ParseFlags::NOT_SPECIAL, false) == ParseFlags::NOT_SPECIAL);
188-
static_assert(quote_escape_flag(ParseFlags::QUOTE, false) == ParseFlags::QUOTE);
189-
static_assert(quote_escape_flag(ParseFlags::DELIMITER, false) == ParseFlags::DELIMITER);
190-
static_assert(quote_escape_flag(ParseFlags::NEWLINE, false) == ParseFlags::NEWLINE);
191-
192-
static_assert(quote_escape_flag(ParseFlags::NOT_SPECIAL, true) == ParseFlags::NOT_SPECIAL);
193-
static_assert(quote_escape_flag(ParseFlags::QUOTE, true) == ParseFlags::QUOTE_ESCAPE_QUOTE);
194-
static_assert(quote_escape_flag(ParseFlags::DELIMITER, true) == ParseFlags::NOT_SPECIAL);
195-
static_assert(quote_escape_flag(ParseFlags::NEWLINE, true) == ParseFlags::NOT_SPECIAL);
189+
STATIC_ASSERT(quote_escape_flag(ParseFlags::NOT_SPECIAL, false) == ParseFlags::NOT_SPECIAL);
190+
STATIC_ASSERT(quote_escape_flag(ParseFlags::QUOTE, false) == ParseFlags::QUOTE);
191+
STATIC_ASSERT(quote_escape_flag(ParseFlags::DELIMITER, false) == ParseFlags::DELIMITER);
192+
STATIC_ASSERT(quote_escape_flag(ParseFlags::NEWLINE, false) == ParseFlags::NEWLINE);
193+
194+
STATIC_ASSERT(quote_escape_flag(ParseFlags::NOT_SPECIAL, true) == ParseFlags::NOT_SPECIAL);
195+
STATIC_ASSERT(quote_escape_flag(ParseFlags::QUOTE, true) == ParseFlags::QUOTE_ESCAPE_QUOTE);
196+
STATIC_ASSERT(quote_escape_flag(ParseFlags::DELIMITER, true) == ParseFlags::NOT_SPECIAL);
197+
STATIC_ASSERT(quote_escape_flag(ParseFlags::NEWLINE, true) == ParseFlags::NOT_SPECIAL);
196198

197199
/** An array which maps ASCII chars to a parsing flag */
198200
using ParseFlagMap = std::array<ParseFlags, 256>;

include/internal/csv_row.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,72 @@ namespace csv {
9898
return field_str.substr(0, field.length);
9999
}
100100

101+
CSV_INLINE bool CSVField::try_parse_hex(int& parsedValue) {
102+
size_t start = 0, end = 0;
103+
104+
// Trim out whitespace chars
105+
for (; start < this->sv.size() && this->sv[start] == ' '; start++);
106+
for (end = start; end < this->sv.size() && this->sv[end] != ' '; end++);
107+
108+
unsigned long long int value = 0;
109+
110+
size_t digits = (end - start);
111+
size_t base16_exponent = digits - 1;
112+
113+
if (digits == 0) return false;
114+
115+
for (const auto& ch : this->sv.substr(start, digits)) {
116+
int digit = 0;
117+
118+
switch (ch) {
119+
case '0':
120+
case '1':
121+
case '2':
122+
case '3':
123+
case '4':
124+
case '5':
125+
case '6':
126+
case '7':
127+
case '8':
128+
case '9':
129+
digit = static_cast<int>(ch - '0');
130+
break;
131+
case 'a':
132+
case 'A':
133+
digit = 10;
134+
break;
135+
case 'b':
136+
case 'B':
137+
digit = 11;
138+
break;
139+
case 'c':
140+
case 'C':
141+
digit = 12;
142+
break;
143+
case 'd':
144+
case 'D':
145+
digit = 13;
146+
break;
147+
case 'e':
148+
case 'E':
149+
digit = 14;
150+
break;
151+
case 'f':
152+
case 'F':
153+
digit = 15;
154+
break;
155+
default:
156+
return false;
157+
}
158+
159+
value += digit * pow(16, base16_exponent);
160+
base16_exponent--;
161+
}
162+
163+
parsedValue = value;
164+
return true;
165+
}
166+
101167
#ifdef _MSC_VER
102168
#pragma region CSVRow Iterator
103169
#endif

include/internal/csv_row.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,9 @@ namespace csv {
214214
return static_cast<T>(this->value);
215215
}
216216

217+
/** Parse a hexadecimal value, returning false if the value is not hex. */
218+
bool try_parse_hex(int& parsedValue);
219+
217220
/** Compares the contents of this field to a numeric value. If this
218221
* field does not contain a numeric value, then all comparisons return
219222
* false.
@@ -228,7 +231,7 @@ namespace csv {
228231
* @sa csv::CSVField::operator==(csv::string_view other)
229232
*/
230233
template<typename T>
231-
CONSTEXPR bool operator==(T other) const noexcept
234+
CONSTEXPR_14 bool operator==(T other) const noexcept
232235
{
233236
static_assert(std::is_arithmetic<T>::value,
234237
"T should be a numeric value.");

include/internal/csv_writer.hpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ namespace csv {
5151
csv::enable_if_t<std::is_floating_point<T>::value, int> = 0
5252
>
5353
inline std::string to_string(T value) {
54+
#ifdef __clang__
55+
return std::to_string(value);
56+
#else
57+
// TODO: Figure out why the below code doesn't work on clang
5458
std::string result;
5559

5660
T integral_part;
@@ -64,8 +68,9 @@ namespace csv {
6468
result = "0";
6569
}
6670
else {
67-
for (short n_digits = log(integral_part) / log(10); n_digits + 1 > 0; n_digits --) {
68-
short digit = std::fmod(integral_part, pow10(n_digits + 1)) / pow10(n_digits);
71+
for (int n_digits = (int)(std::log(integral_part) / std::log(10));
72+
n_digits + 1 > 0; n_digits --) {
73+
int digit = (int)(std::fmod(integral_part, pow10(n_digits + 1)) / pow10(n_digits));
6974
result += (char)('0' + digit);
7075
}
7176
}
@@ -74,9 +79,9 @@ namespace csv {
7479
result += ".";
7580

7681
if (fractional_part > 0) {
77-
fractional_part *= pow10(DECIMAL_PLACES);
78-
for (short n_digits = DECIMAL_PLACES; n_digits > 0; n_digits--) {
79-
short digit = std::fmod(fractional_part, pow10(n_digits)) / pow10(n_digits - 1);
82+
fractional_part *= (T)(pow10(DECIMAL_PLACES));
83+
for (int n_digits = DECIMAL_PLACES; n_digits > 0; n_digits--) {
84+
int digit = (int)(std::fmod(fractional_part, pow10(n_digits)) / pow10(n_digits - 1));
8085
result += (char)('0' + digit);
8186
}
8287
}
@@ -85,16 +90,19 @@ namespace csv {
8590
}
8691

8792
return result;
93+
#endif
8894
}
8995
}
9096

9197
/** Sets how many places after the decimal will be written for floating point numbers
9298
*
9399
* @param precision Number of decimal places
94100
*/
101+
#ifndef __clang___
95102
inline static void set_decimal_places(int precision) {
96103
internals::DECIMAL_PLACES = precision;
97104
}
105+
#endif
98106

99107
/** @name CSV Writing */
100108
///@{

0 commit comments

Comments
 (0)