mirror of
https://github.com/red0124/ssp.git
synced 2025-06-08 05:12:30 +02:00
Compare commits
15 Commits
dbaa8131e7
...
d4fc2ee561
Author | SHA1 | Date | |
---|---|---|---|
d4fc2ee561 | |||
d422667477 | |||
aaa22046a5 | |||
775b8c93e2 | |||
e4fba8a918 | |||
11d57bd073 | |||
45b840a30a | |||
8bb773625b | |||
0466c7234c | |||
d21c387a33 | |||
d019edb2bf | |||
a2666816de | |||
417a03a8a4 | |||
baf4317ffa | |||
63a618957b |
2
.gitignore
vendored
2
.gitignore
vendored
@ -6,5 +6,3 @@ build/
|
||||
hbuild/
|
||||
subprojects/*
|
||||
!subprojects/*.wrap
|
||||
ssp.cpp
|
||||
ssp.bin
|
||||
|
@ -12,6 +12,7 @@ using string_range = std::pair<const char*, const char*>;
|
||||
using split_data = std::vector<string_range>;
|
||||
|
||||
constexpr inline auto default_delimiter = ",";
|
||||
constexpr static auto get_line_initial_buffer_size = 128;
|
||||
|
||||
template <bool StringError>
|
||||
inline void assert_string_error_defined() {
|
||||
@ -46,7 +47,7 @@ inline ssize_t get_line_file(char** lineptr, size_t* n, FILE* stream) {
|
||||
}
|
||||
|
||||
if (*lineptr == nullptr) {
|
||||
*lineptr = static_cast<char*>(malloc(128));
|
||||
*lineptr = static_cast<char*>(malloc(get_line_initial_buffer_size));
|
||||
if (*lineptr == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
@ -57,8 +58,8 @@ inline ssize_t get_line_file(char** lineptr, size_t* n, FILE* stream) {
|
||||
while (c != EOF) {
|
||||
if (pos + 1 >= *n) {
|
||||
size_t new_size = *n + (*n >> 2);
|
||||
if (new_size < 128) {
|
||||
new_size = 128;
|
||||
if (new_size < get_line_initial_buffer_size) {
|
||||
new_size = get_line_initial_buffer_size;
|
||||
}
|
||||
char* new_ptr = static_cast<char*>(
|
||||
realloc(static_cast<void*>(*lineptr), new_size));
|
||||
|
@ -52,11 +52,16 @@ public:
|
||||
const std::string& delim = ss::default_delimiter)
|
||||
: file_name_{"buffer line"},
|
||||
reader_{csv_data_buffer, csv_data_size, delim} {
|
||||
read_line();
|
||||
if constexpr (ignore_header) {
|
||||
ignore_next();
|
||||
if (csv_data_buffer) {
|
||||
read_line();
|
||||
if constexpr (ignore_header) {
|
||||
ignore_next();
|
||||
} else {
|
||||
raw_header_ = reader_.get_buffer();
|
||||
}
|
||||
} else {
|
||||
raw_header_ = reader_.get_buffer();
|
||||
handle_error_null_buffer();
|
||||
eof_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -524,6 +529,19 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void handle_error_null_buffer() {
|
||||
constexpr static auto error_msg = " received null data buffer";
|
||||
|
||||
if constexpr (string_error) {
|
||||
error_.clear();
|
||||
error_.append(file_name_).append(error_msg);
|
||||
} else if constexpr (throw_on_error) {
|
||||
throw ss::exception{file_name_ + error_msg};
|
||||
} else {
|
||||
error_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void handle_error_file_not_open() {
|
||||
constexpr static auto error_msg = " could not be opened";
|
||||
|
||||
@ -728,19 +746,14 @@ private:
|
||||
size_t pos;
|
||||
int c;
|
||||
|
||||
// TODO remove check
|
||||
if (lineptr == nullptr || buffer == nullptr || n == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (curr_char >= csv_data_size) {
|
||||
return -1;
|
||||
}
|
||||
c = buffer[curr_char++];
|
||||
|
||||
// TODO maybe remove this too
|
||||
if (*lineptr == nullptr) {
|
||||
*lineptr = static_cast<char*>(malloc(128));
|
||||
*lineptr =
|
||||
static_cast<char*>(malloc(get_line_initial_buffer_size));
|
||||
if (*lineptr == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
@ -751,13 +764,11 @@ private:
|
||||
while (curr_char <= csv_data_size) {
|
||||
if (pos + 1 >= *n) {
|
||||
size_t new_size = *n + (*n >> 2);
|
||||
// TODO maybe remove this too
|
||||
if (new_size < 128) {
|
||||
new_size = 128;
|
||||
if (new_size < get_line_initial_buffer_size) {
|
||||
new_size = get_line_initial_buffer_size;
|
||||
}
|
||||
char* new_ptr = static_cast<char*>(
|
||||
realloc(static_cast<void*>(*lineptr), new_size));
|
||||
// TODO check for failed malloc in the callee
|
||||
if (new_ptr == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
@ -924,11 +935,11 @@ private:
|
||||
}
|
||||
|
||||
void realloc_concat(char*& first, size_t& first_size,
|
||||
const char* const second, size_t second_size) {
|
||||
// TODO make buffer_size an argument
|
||||
next_line_buffer_size_ = first_size + second_size + 3;
|
||||
size_t& buffer_size, const char* const second,
|
||||
size_t second_size) {
|
||||
buffer_size = first_size + second_size + 3;
|
||||
auto new_first = static_cast<char*>(
|
||||
realloc(static_cast<void*>(first), next_line_buffer_size_));
|
||||
realloc(static_cast<void*>(first), buffer_size));
|
||||
if (!first) {
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
@ -958,7 +969,8 @@ private:
|
||||
|
||||
++line_number_;
|
||||
size_t next_size = remove_eol(helper_buffer_, next_ssize);
|
||||
realloc_concat(buffer, size, helper_buffer_, next_size);
|
||||
realloc_concat(buffer, size, next_line_buffer_size_, helper_buffer_,
|
||||
next_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -109,7 +109,7 @@ struct get_matcher<Matcher, T, Ts...> {
|
||||
struct is_matcher : is_instance_of_matcher<U, Matcher> {};
|
||||
|
||||
static_assert(count_v<is_matcher, T, Ts...> <= 1,
|
||||
"the same matcher cannot"
|
||||
"the same matcher cannot "
|
||||
"be defined multiple times");
|
||||
using type = std::conditional_t<is_matcher<T>::value, T,
|
||||
typename get_matcher<Matcher, Ts...>::type>;
|
||||
|
61
ssp.hpp
61
ssp.hpp
@ -625,6 +625,7 @@ using string_range = std::pair<const char*, const char*>;
|
||||
using split_data = std::vector<string_range>;
|
||||
|
||||
constexpr inline auto default_delimiter = ",";
|
||||
constexpr static auto get_line_initial_buffer_size = 128;
|
||||
|
||||
template <bool StringError>
|
||||
inline void assert_string_error_defined() {
|
||||
@ -659,7 +660,7 @@ inline ssize_t get_line_file(char** lineptr, size_t* n, FILE* stream) {
|
||||
}
|
||||
|
||||
if (*lineptr == nullptr) {
|
||||
*lineptr = static_cast<char*>(malloc(128));
|
||||
*lineptr = static_cast<char*>(malloc(get_line_initial_buffer_size));
|
||||
if (*lineptr == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
@ -670,8 +671,8 @@ inline ssize_t get_line_file(char** lineptr, size_t* n, FILE* stream) {
|
||||
while (c != EOF) {
|
||||
if (pos + 1 >= *n) {
|
||||
size_t new_size = *n + (*n >> 2);
|
||||
if (new_size < 128) {
|
||||
new_size = 128;
|
||||
if (new_size < get_line_initial_buffer_size) {
|
||||
new_size = get_line_initial_buffer_size;
|
||||
}
|
||||
char* new_ptr = static_cast<char*>(
|
||||
realloc(static_cast<void*>(*lineptr), new_size));
|
||||
@ -803,7 +804,7 @@ struct get_matcher<Matcher, T, Ts...> {
|
||||
struct is_matcher : is_instance_of_matcher<U, Matcher> {};
|
||||
|
||||
static_assert(count_v<is_matcher, T, Ts...> <= 1,
|
||||
"the same matcher cannot"
|
||||
"the same matcher cannot "
|
||||
"be defined multiple times");
|
||||
using type = std::conditional_t<is_matcher<T>::value, T,
|
||||
typename get_matcher<Matcher, Ts...>::type>;
|
||||
@ -2178,11 +2179,16 @@ public:
|
||||
const std::string& delim = ss::default_delimiter)
|
||||
: file_name_{"buffer line"},
|
||||
reader_{csv_data_buffer, csv_data_size, delim} {
|
||||
read_line();
|
||||
if constexpr (ignore_header) {
|
||||
ignore_next();
|
||||
if (csv_data_buffer) {
|
||||
read_line();
|
||||
if constexpr (ignore_header) {
|
||||
ignore_next();
|
||||
} else {
|
||||
raw_header_ = reader_.get_buffer();
|
||||
}
|
||||
} else {
|
||||
raw_header_ = reader_.get_buffer();
|
||||
handle_error_null_buffer();
|
||||
eof_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2650,6 +2656,19 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void handle_error_null_buffer() {
|
||||
constexpr static auto error_msg = " received null data buffer";
|
||||
|
||||
if constexpr (string_error) {
|
||||
error_.clear();
|
||||
error_.append(file_name_).append(error_msg);
|
||||
} else if constexpr (throw_on_error) {
|
||||
throw ss::exception{file_name_ + error_msg};
|
||||
} else {
|
||||
error_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void handle_error_file_not_open() {
|
||||
constexpr static auto error_msg = " could not be opened";
|
||||
|
||||
@ -2854,19 +2873,14 @@ private:
|
||||
size_t pos;
|
||||
int c;
|
||||
|
||||
// TODO remove check
|
||||
if (lineptr == nullptr || buffer == nullptr || n == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (curr_char >= csv_data_size) {
|
||||
return -1;
|
||||
}
|
||||
c = buffer[curr_char++];
|
||||
|
||||
// TODO maybe remove this too
|
||||
if (*lineptr == nullptr) {
|
||||
*lineptr = static_cast<char*>(malloc(128));
|
||||
*lineptr =
|
||||
static_cast<char*>(malloc(get_line_initial_buffer_size));
|
||||
if (*lineptr == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
@ -2877,13 +2891,11 @@ private:
|
||||
while (curr_char <= csv_data_size) {
|
||||
if (pos + 1 >= *n) {
|
||||
size_t new_size = *n + (*n >> 2);
|
||||
// TODO maybe remove this too
|
||||
if (new_size < 128) {
|
||||
new_size = 128;
|
||||
if (new_size < get_line_initial_buffer_size) {
|
||||
new_size = get_line_initial_buffer_size;
|
||||
}
|
||||
char* new_ptr = static_cast<char*>(
|
||||
realloc(static_cast<void*>(*lineptr), new_size));
|
||||
// TODO check for failed malloc in the callee
|
||||
if (new_ptr == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
@ -3050,11 +3062,11 @@ private:
|
||||
}
|
||||
|
||||
void realloc_concat(char*& first, size_t& first_size,
|
||||
const char* const second, size_t second_size) {
|
||||
// TODO make buffer_size an argument
|
||||
next_line_buffer_size_ = first_size + second_size + 3;
|
||||
size_t& buffer_size, const char* const second,
|
||||
size_t second_size) {
|
||||
buffer_size = first_size + second_size + 3;
|
||||
auto new_first = static_cast<char*>(
|
||||
realloc(static_cast<void*>(first), next_line_buffer_size_));
|
||||
realloc(static_cast<void*>(first), buffer_size));
|
||||
if (!first) {
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
@ -3084,7 +3096,8 @@ private:
|
||||
|
||||
++line_number_;
|
||||
size_t next_size = remove_eol(helper_buffer_, next_ssize);
|
||||
realloc_concat(buffer, size, helper_buffer_, next_size);
|
||||
realloc_concat(buffer, size, next_line_buffer_size_, helper_buffer_,
|
||||
next_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -35,8 +35,8 @@ enable_testing()
|
||||
foreach(name IN ITEMS test_splitter test_parser1_1 test_parser1_2
|
||||
test_parser1_3 test_parser1_4 test_converter
|
||||
test_extractions test_parser2_1 test_parser2_2
|
||||
test_parser2_3 test_parser2_4
|
||||
test_extractions_without_fast_float)
|
||||
test_parser2_3 test_parser2_4 test_parser2_5
|
||||
test_parser2_6 test_extractions_without_fast_float)
|
||||
add_executable("${name}" "${name}.cpp")
|
||||
target_link_libraries("${name}" PRIVATE ssp::ssp fast_float
|
||||
doctest::doctest)
|
||||
|
@ -13,6 +13,8 @@ tests = [
|
||||
'parser2_2',
|
||||
'parser2_3',
|
||||
'parser2_4',
|
||||
'parser2_5',
|
||||
'parser2_6',
|
||||
'extractions_without_fast_float',
|
||||
]
|
||||
|
||||
|
@ -12,6 +12,11 @@
|
||||
#include <doctest.h>
|
||||
#endif
|
||||
|
||||
namespace ss {
|
||||
template <typename... Ts>
|
||||
class parser;
|
||||
} /* ss */
|
||||
|
||||
namespace {
|
||||
struct buffer {
|
||||
std::string data_;
|
||||
@ -34,7 +39,7 @@ struct buffer {
|
||||
|
||||
[[maybe_unused]] inline buffer buff;
|
||||
|
||||
std::string time_now_rand() {
|
||||
[[maybe_unused]] std::string time_now_rand() {
|
||||
srand(time(nullptr));
|
||||
std::stringstream ss;
|
||||
auto t = std::time(nullptr);
|
||||
@ -115,8 +120,8 @@ struct unique_file_name {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<std::vector<T>> vector_combinations(const std::vector<T>& v,
|
||||
size_t n) {
|
||||
[[maybe_unused]] std::vector<std::vector<T>> vector_combinations(
|
||||
const std::vector<T>& v, size_t n) {
|
||||
std::vector<std::vector<T>> ret;
|
||||
if (n <= 1) {
|
||||
for (const auto& i : v) {
|
||||
@ -134,4 +139,54 @@ std::vector<std::vector<T>> vector_combinations(const std::vector<T>& v,
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
[[maybe_unused]] std::string make_buffer(const std::string& file_name) {
|
||||
std::ifstream in{file_name, std::ios::binary};
|
||||
std::string tmp;
|
||||
std::string out;
|
||||
|
||||
auto copy_if_whitespaces = [&] {
|
||||
std::string matches = "\n\r\t ";
|
||||
while (std::any_of(matches.begin(), matches.end(),
|
||||
[&](auto c) { return in.peek() == c; })) {
|
||||
if (in.peek() == '\r') {
|
||||
out += "\r\n";
|
||||
in.ignore(2);
|
||||
} else {
|
||||
out += std::string{static_cast<char>(in.peek())};
|
||||
in.ignore(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
out.reserve(sizeof(out) + 1);
|
||||
|
||||
copy_if_whitespaces();
|
||||
while (in >> tmp) {
|
||||
out += tmp;
|
||||
copy_if_whitespaces();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
template <bool buffer_mode, typename... Ts>
|
||||
[[maybe_unused]] std::tuple<ss::parser<Ts...>, std::string> make_parser(
|
||||
const std::string& file_name, const std::string& delim = "") {
|
||||
if (buffer_mode) {
|
||||
auto buffer = make_buffer(file_name);
|
||||
if (delim.empty()) {
|
||||
return {ss::parser<Ts...>{buffer.data(), buffer.size()},
|
||||
std::move(buffer)};
|
||||
} else {
|
||||
return {ss::parser<Ts...>{buffer.data(), buffer.size(), delim},
|
||||
std::move(buffer)};
|
||||
}
|
||||
} else {
|
||||
if (delim.empty()) {
|
||||
return {ss::parser<Ts...>{file_name}, std::string{}};
|
||||
} else {
|
||||
return {ss::parser<Ts...>{file_name, delim}, std::string{}};
|
||||
}
|
||||
}
|
||||
}
|
||||
} /* namespace */
|
||||
|
@ -105,47 +105,4 @@ static void make_and_write(const std::string& file_name,
|
||||
}
|
||||
}
|
||||
|
||||
std::string make_buffer(const std::string& file_name) {
|
||||
std::ifstream in{file_name, std::ios::binary};
|
||||
std::string tmp;
|
||||
std::string out;
|
||||
out.reserve(sizeof(out) + 1);
|
||||
while (in >> tmp) {
|
||||
out += tmp;
|
||||
std::string matches = "\n\r\t ";
|
||||
while (std::any_of(matches.begin(), matches.end(),
|
||||
[&](auto c) { return in.peek() == c; })) {
|
||||
if (in.peek() == '\r') {
|
||||
out += "\r\n";
|
||||
in.ignore(2);
|
||||
} else {
|
||||
out += std::string{static_cast<char>(in.peek())};
|
||||
in.ignore(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
template <bool buffer_mode, typename... Ts>
|
||||
std::tuple<ss::parser<Ts...>, std::string> make_parser(
|
||||
const std::string& file_name, const std::string& delim = "") {
|
||||
if (buffer_mode) {
|
||||
auto buffer = make_buffer(file_name);
|
||||
if (delim.empty()) {
|
||||
return {ss::parser<Ts...>{buffer.data(), buffer.size()},
|
||||
std::move(buffer)};
|
||||
} else {
|
||||
return {ss::parser<Ts...>{buffer.data(), buffer.size(), delim},
|
||||
std::move(buffer)};
|
||||
}
|
||||
} else {
|
||||
if (delim.empty()) {
|
||||
return {ss::parser<Ts...>{file_name}, std::string{}};
|
||||
} else {
|
||||
return {ss::parser<Ts...>{file_name, delim}, std::string{}};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace */
|
||||
|
@ -21,6 +21,25 @@ TEST_CASE("test file not found") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("test null buffer") {
|
||||
{
|
||||
ss::parser p{nullptr, 10, ","};
|
||||
CHECK_FALSE(p.valid());
|
||||
}
|
||||
|
||||
{
|
||||
ss::parser<ss::string_error> p{nullptr, 10, ","};
|
||||
CHECK_FALSE(p.valid());
|
||||
}
|
||||
|
||||
try {
|
||||
ss::parser<ss::throw_on_error> p{nullptr, 10, ","};
|
||||
FAIL("Expected exception...");
|
||||
} catch (const std::exception& e) {
|
||||
CHECK_FALSE(std::string{e.what()}.empty());
|
||||
}
|
||||
}
|
||||
|
||||
template <bool buffer_mode, typename... Ts>
|
||||
void test_various_cases() {
|
||||
unique_file_name f{"test_parser"};
|
||||
|
@ -7,17 +7,13 @@ template <typename T, typename... Us>
|
||||
struct has_type<T, std::tuple<Us...>>
|
||||
: std::disjunction<std::is_same<T, Us>...> {};
|
||||
|
||||
static inline void check_size(size_t size1, size_t size2) {
|
||||
CHECK_EQ(size1, size2);
|
||||
}
|
||||
|
||||
template <typename Setup, typename... Ts>
|
||||
template <bool buffer_mode, typename Setup, typename... Ts>
|
||||
static void test_fields_impl(const std::string file_name,
|
||||
const std::vector<X>& data,
|
||||
const std::vector<std::string>& fields) {
|
||||
using CaseType = std::tuple<Ts...>;
|
||||
|
||||
ss::parser<Setup> p{file_name, ","};
|
||||
auto [p, _] = make_parser<buffer_mode, Setup>(file_name, ",");
|
||||
CHECK_FALSE(p.field_exists("Unknown"));
|
||||
p.use_fields(fields);
|
||||
std::vector<CaseType> i;
|
||||
@ -26,7 +22,7 @@ static void test_fields_impl(const std::string file_name,
|
||||
i.push_back(a);
|
||||
}
|
||||
|
||||
check_size(i.size(), data.size());
|
||||
CHECK_EQ(i.size(), data.size());
|
||||
for (size_t j = 0; j < i.size(); ++j) {
|
||||
if constexpr (has_type<int, CaseType>::value) {
|
||||
CHECK_EQ(std::get<int>(i[j]), data[j].i);
|
||||
@ -43,11 +39,16 @@ static void test_fields_impl(const std::string file_name,
|
||||
template <typename... Ts>
|
||||
static void test_fields(const std::string file_name, const std::vector<X>& data,
|
||||
const std::vector<std::string>& fields) {
|
||||
test_fields_impl<ss::setup<>, Ts...>(file_name, data, fields);
|
||||
test_fields_impl<ss::setup<ss::string_error>, Ts...>(file_name, data,
|
||||
fields);
|
||||
test_fields_impl<ss::setup<ss::throw_on_error>, Ts...>(file_name, data,
|
||||
fields);
|
||||
test_fields_impl<false, ss::setup<>, Ts...>(file_name, data, fields);
|
||||
test_fields_impl<false, ss::setup<ss::string_error>, Ts...>(file_name, data,
|
||||
fields);
|
||||
test_fields_impl<false, ss::setup<ss::throw_on_error>, Ts...>(file_name,
|
||||
data, fields);
|
||||
test_fields_impl<true, ss::setup<>, Ts...>(file_name, data, fields);
|
||||
test_fields_impl<true, ss::setup<ss::string_error>, Ts...>(file_name, data,
|
||||
fields);
|
||||
test_fields_impl<true, ss::setup<ss::throw_on_error>, Ts...>(file_name,
|
||||
data, fields);
|
||||
}
|
||||
|
||||
TEST_CASE("parser test various cases with header") {
|
||||
@ -196,34 +197,37 @@ TEST_CASE("parser test various cases with header") {
|
||||
test_fields<double, int, str>(o, d, {Dbl, Int, Str});
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
template <bool buffer_mode, typename... Ts>
|
||||
void test_invalid_fields_impl(const std::vector<std::string>& lines,
|
||||
const std::vector<std::string>& fields) {
|
||||
unique_file_name f{"test_parser"};
|
||||
std::ofstream out{f.name};
|
||||
for (const auto& line : lines) {
|
||||
out << line << std::endl;
|
||||
{
|
||||
std::ofstream out{f.name};
|
||||
for (const auto& line : lines) {
|
||||
out << line << std::endl;
|
||||
}
|
||||
}
|
||||
out.close();
|
||||
|
||||
{
|
||||
// No fields specified
|
||||
ss::parser<Ts...> p{f.name, ","};
|
||||
auto command = [&] { p.use_fields(); };
|
||||
auto [p, _] = make_parser<buffer_mode, Ts...>(f.name, ",");
|
||||
auto command = [&p = p] { p.use_fields(); };
|
||||
expect_error_on_command(p, command);
|
||||
}
|
||||
|
||||
{
|
||||
// Unknown field
|
||||
ss::parser<Ts...> p{f.name, ","};
|
||||
auto command = [&] { p.use_fields("Unknown"); };
|
||||
auto [p, _] = make_parser<buffer_mode, Ts...>(f.name, ",");
|
||||
auto command = [&p = p] { p.use_fields("Unknown"); };
|
||||
expect_error_on_command(p, command);
|
||||
}
|
||||
|
||||
{
|
||||
// Field used multiple times
|
||||
ss::parser<Ts...> p{f.name, ","};
|
||||
auto command = [&] { p.use_fields(fields.at(0), fields.at(0)); };
|
||||
auto [p, _] = make_parser<buffer_mode, Ts...>(f.name, ",");
|
||||
auto command = [&p = p, &fields = fields] {
|
||||
p.use_fields(fields.at(0), fields.at(0));
|
||||
};
|
||||
if (!fields.empty()) {
|
||||
expect_error_on_command(p, command);
|
||||
}
|
||||
@ -231,8 +235,8 @@ void test_invalid_fields_impl(const std::vector<std::string>& lines,
|
||||
|
||||
{
|
||||
// Mapping out of range
|
||||
ss::parser<Ts...> p{f.name, ","};
|
||||
auto command = [&] {
|
||||
auto [p, _] = make_parser<buffer_mode, Ts...>(f.name, ",");
|
||||
auto command = [&p = p, &fields = fields] {
|
||||
p.use_fields(fields.at(0));
|
||||
p.template get_next<std::string, std::string>();
|
||||
};
|
||||
@ -243,8 +247,8 @@ void test_invalid_fields_impl(const std::vector<std::string>& lines,
|
||||
|
||||
{
|
||||
// Invalid header
|
||||
ss::parser<Ts...> p{f.name, ","};
|
||||
auto command = [&] { p.use_fields(fields); };
|
||||
auto [p, _] = make_parser<buffer_mode, Ts...>(f.name, ",");
|
||||
auto command = [&p = p, &fields = fields] { p.use_fields(fields); };
|
||||
|
||||
if (!fields.empty()) {
|
||||
// Pass if there are no duplicates, fail otherwise
|
||||
@ -267,9 +271,12 @@ void test_invalid_fields_impl(const std::vector<std::string>& lines,
|
||||
template <typename... Ts>
|
||||
void test_invalid_fields(const std::vector<std::string>& lines,
|
||||
const std::vector<std::string>& fields) {
|
||||
test_invalid_fields_impl(lines, fields);
|
||||
test_invalid_fields_impl<ss::string_error>(lines, fields);
|
||||
test_invalid_fields_impl<ss::throw_on_error>(lines, fields);
|
||||
test_invalid_fields_impl<false>(lines, fields);
|
||||
test_invalid_fields_impl<false, ss::string_error>(lines, fields);
|
||||
test_invalid_fields_impl<false, ss::throw_on_error>(lines, fields);
|
||||
test_invalid_fields_impl<true>(lines, fields);
|
||||
test_invalid_fields_impl<true, ss::string_error>(lines, fields);
|
||||
test_invalid_fields_impl<true, ss::throw_on_error>(lines, fields);
|
||||
}
|
||||
|
||||
TEST_CASE("parser test invalid header fields usage") {
|
||||
@ -296,7 +303,7 @@ TEST_CASE("parser test invalid header fields usage") {
|
||||
test_invalid_fields({"Int,String,Int", "1,hi,3"}, {"Int", "String", "Int"});
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
template <bool buffer_mode, typename... Ts>
|
||||
void test_invalid_rows_with_header() {
|
||||
unique_file_name f{"test_parser"};
|
||||
{
|
||||
@ -311,7 +318,7 @@ void test_invalid_rows_with_header() {
|
||||
}
|
||||
|
||||
{
|
||||
ss::parser<Ts...> p{f.name};
|
||||
auto [p, _] = make_parser<buffer_mode, Ts...>(f.name);
|
||||
|
||||
p.use_fields("Int", "String", "Double");
|
||||
using data = std::tuple<int, std::string, double>;
|
||||
@ -337,7 +344,7 @@ void test_invalid_rows_with_header() {
|
||||
}
|
||||
|
||||
{
|
||||
ss::parser<Ts...> p{f.name};
|
||||
auto [p, _] = make_parser<buffer_mode, Ts...>(f.name);
|
||||
|
||||
p.use_fields("Double", "Int");
|
||||
using data = std::tuple<double, int>;
|
||||
@ -361,7 +368,7 @@ void test_invalid_rows_with_header() {
|
||||
}
|
||||
|
||||
{
|
||||
ss::parser<Ts...> p{f.name};
|
||||
auto [p, _] = make_parser<buffer_mode, Ts...>(f.name);
|
||||
|
||||
p.use_fields("String", "Double");
|
||||
using data = std::tuple<std::string, double>;
|
||||
@ -389,12 +396,15 @@ void test_invalid_rows_with_header() {
|
||||
}
|
||||
|
||||
TEST_CASE("parser test invalid rows with header") {
|
||||
test_invalid_rows_with_header();
|
||||
test_invalid_rows_with_header<ss::string_error>();
|
||||
test_invalid_rows_with_header<ss::throw_on_error>();
|
||||
test_invalid_rows_with_header<false>();
|
||||
test_invalid_rows_with_header<false, ss::string_error>();
|
||||
test_invalid_rows_with_header<false, ss::throw_on_error>();
|
||||
test_invalid_rows_with_header<true>();
|
||||
test_invalid_rows_with_header<true, ss::string_error>();
|
||||
test_invalid_rows_with_header<true, ss::throw_on_error>();
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
template <bool buffer_mode, typename... Ts>
|
||||
void test_ignore_empty_impl(const std::vector<X>& data) {
|
||||
unique_file_name f{"test_parser"};
|
||||
make_and_write(f.name, data);
|
||||
@ -407,7 +417,8 @@ void test_ignore_empty_impl(const std::vector<X>& data) {
|
||||
}
|
||||
|
||||
{
|
||||
ss::parser<ss::ignore_empty, Ts...> p{f.name, ","};
|
||||
auto [p, _] =
|
||||
make_parser<buffer_mode, ss::ignore_empty, Ts...>(f.name, ",");
|
||||
|
||||
std::vector<X> i;
|
||||
for (const auto& a : p.template iterate<X>()) {
|
||||
@ -418,7 +429,7 @@ void test_ignore_empty_impl(const std::vector<X>& data) {
|
||||
}
|
||||
|
||||
{
|
||||
ss::parser<Ts...> p{f.name, ","};
|
||||
auto [p, _] = make_parser<buffer_mode, Ts...>(f.name, ",");
|
||||
std::vector<X> i;
|
||||
size_t n = 0;
|
||||
while (!p.eof()) {
|
||||
@ -441,9 +452,12 @@ void test_ignore_empty_impl(const std::vector<X>& data) {
|
||||
|
||||
template <typename... Ts>
|
||||
void test_ignore_empty(const std::vector<X>& data) {
|
||||
test_ignore_empty_impl(data);
|
||||
test_ignore_empty_impl<ss::string_error>(data);
|
||||
test_ignore_empty_impl<ss::throw_on_error>(data);
|
||||
test_ignore_empty_impl<false>(data);
|
||||
test_ignore_empty_impl<false, ss::string_error>(data);
|
||||
test_ignore_empty_impl<false, ss::throw_on_error>(data);
|
||||
test_ignore_empty_impl<true>(data);
|
||||
test_ignore_empty_impl<true, ss::string_error>(data);
|
||||
test_ignore_empty_impl<true, ss::throw_on_error>(data);
|
||||
}
|
||||
|
||||
TEST_CASE("parser test various cases with empty lines") {
|
||||
|
@ -121,7 +121,7 @@ column make_column(const std::string& input_header,
|
||||
}
|
||||
|
||||
[[maybe_unused]] void replace_all2(std::string& s, const std::string& old_value,
|
||||
const std::string& new_value) {
|
||||
const std::string& new_value) {
|
||||
for (size_t i = 0; i < 999; ++i) {
|
||||
size_t pos = s.find(old_value);
|
||||
if (pos == std::string::npos) {
|
||||
@ -257,7 +257,8 @@ std::vector<std::string> generate_csv_data(const std::vector<field>& data,
|
||||
}
|
||||
|
||||
[[maybe_unused]] void write_to_file(const std::vector<std::string>& data,
|
||||
const std::string& delim, const std::string& file_name) {
|
||||
const std::string& delim,
|
||||
const std::string& file_name) {
|
||||
std::ofstream out{file_name, std::ios_base::app};
|
||||
std::string line;
|
||||
for (size_t i = 0; i < data.size(); ++i) {
|
||||
@ -299,7 +300,7 @@ std::vector<std::string> generate_csv_data(const std::vector<field>& data,
|
||||
CHECK(V1 == V2); \
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
template <bool buffer_mode, typename... Ts>
|
||||
void test_data_combinations(const std::vector<column>& input_data,
|
||||
const std::string& delim, bool include_header) {
|
||||
using setup = ss::setup<Ts...>;
|
||||
@ -388,7 +389,7 @@ void test_data_combinations(const std::vector<column>& input_data,
|
||||
}
|
||||
|
||||
for (const auto& layout : unique_layout_combinations) {
|
||||
ss::parser<setup> p{f.name, delim};
|
||||
auto [p, _] = make_parser<buffer_mode, setup>(f.name, delim);
|
||||
|
||||
if (include_header && !setup::ignore_header) {
|
||||
std::vector<std::string> fields;
|
||||
@ -409,7 +410,7 @@ void test_data_combinations(const std::vector<column>& input_data,
|
||||
REQUIRE(p.valid());
|
||||
}
|
||||
|
||||
auto check_error = [&p] {
|
||||
auto check_error = [&p = p] {
|
||||
CHECK(p.valid());
|
||||
if (!p.valid()) {
|
||||
if constexpr (setup::string_error) {
|
||||
@ -570,8 +571,14 @@ void test_option_combinations() {
|
||||
{columns0, columns1, columns2, columns3, columns4, columns5,
|
||||
columns6, columns7}) {
|
||||
try {
|
||||
test_data_combinations<Ts...>(columns, delimiter, false);
|
||||
test_data_combinations<Ts...>(columns, delimiter, true);
|
||||
test_data_combinations<false, Ts...>(columns, delimiter,
|
||||
false);
|
||||
test_data_combinations<false, Ts...>(columns, delimiter,
|
||||
true);
|
||||
test_data_combinations<true, Ts...>(columns, delimiter,
|
||||
false);
|
||||
test_data_combinations<true, Ts...>(columns, delimiter,
|
||||
true);
|
||||
} catch (std::exception& e) {
|
||||
std::cout << typeid(ss::parser<Ts...>).name() << std::endl;
|
||||
FAIL_CHECK(std::string{e.what()});
|
||||
@ -616,6 +623,7 @@ void test_option_combinations3() {
|
||||
|
||||
} /* namespace */
|
||||
|
||||
// Tests split into multiple compilation units
|
||||
#if 0
|
||||
|
||||
TEST_CASE("parser test various cases version 2 segment 1") {
|
||||
@ -627,25 +635,33 @@ TEST_CASE("parser test various cases version 2 segment 1") {
|
||||
using multiline_r = ss::multiline_restricted<10>;
|
||||
using trimr = ss::trim_right<' '>;
|
||||
using triml = ss::trim_left<' '>;
|
||||
using trim = ss::trim<' '>;
|
||||
|
||||
// segment 1
|
||||
test_option_combinations3<>();
|
||||
test_option_combinations3<escape>();
|
||||
test_option_combinations3<quote>();
|
||||
|
||||
// segment 2
|
||||
test_option_combinations3<quote>();
|
||||
test_option_combinations3<escape, quote>();
|
||||
|
||||
// segment 3
|
||||
test_option_combinations3<escape, multiline>();
|
||||
test_option_combinations3<quote, multiline>();
|
||||
|
||||
// segment 3
|
||||
// segment 4
|
||||
test_option_combinations3<escape, quote, multiline>();
|
||||
test_option_combinations3<escape, quote, multiline_r>();
|
||||
|
||||
// segment 4
|
||||
// segment 5
|
||||
test_option_combinations<escape, quote, multiline, triml>();
|
||||
test_option_combinations<escape, quote, multiline, trimr>();
|
||||
|
||||
// segment 6
|
||||
test_option_combinations3<escape, quote, multiline>();
|
||||
test_option_combinations3<escape, quote, multiline, trim>();
|
||||
#else
|
||||
|
||||
test_option_combinations3<escape, quote, multiline>();
|
||||
#endif
|
||||
}
|
||||
|
@ -3,12 +3,10 @@
|
||||
|
||||
TEST_CASE("parser test various cases version 2 segment 1") {
|
||||
#ifdef CMAKE_GITHUB_CI
|
||||
using quote = ss::quote<'"'>;
|
||||
using escape = ss::escape<'\\'>;
|
||||
|
||||
test_option_combinations3<>();
|
||||
test_option_combinations3<escape>();
|
||||
test_option_combinations3<quote>();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -5,11 +5,9 @@ TEST_CASE("parser test various cases version 2 segment 2") {
|
||||
#ifdef CMAKE_GITHUB_CI
|
||||
using quote = ss::quote<'"'>;
|
||||
using escape = ss::escape<'\\'>;
|
||||
using multiline = ss::multiline;
|
||||
|
||||
test_option_combinations3<quote>();
|
||||
test_option_combinations3<escape, quote>();
|
||||
test_option_combinations3<escape, multiline>();
|
||||
test_option_combinations3<quote, multiline>();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -6,10 +6,9 @@ TEST_CASE("parser test various cases version 2 segment 3") {
|
||||
using quote = ss::quote<'"'>;
|
||||
using escape = ss::escape<'\\'>;
|
||||
using multiline = ss::multiline;
|
||||
using multiline_r = ss::multiline_restricted<10>;
|
||||
|
||||
test_option_combinations3<escape, quote, multiline>();
|
||||
test_option_combinations3<escape, quote, multiline_r>();
|
||||
test_option_combinations3<escape, multiline>();
|
||||
test_option_combinations3<quote, multiline>();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -2,18 +2,14 @@
|
||||
#include "test_parser2.hpp"
|
||||
|
||||
TEST_CASE("parser test various cases version 2 segment 4") {
|
||||
#ifdef CMAKE_GITHUB_CI
|
||||
using quote = ss::quote<'"'>;
|
||||
using escape = ss::escape<'\\'>;
|
||||
using multiline = ss::multiline;
|
||||
using multiline_r = ss::multiline_restricted<10>;
|
||||
|
||||
#ifdef CMAKE_GITHUB_CI
|
||||
using trimr = ss::trim_right<' '>;
|
||||
using triml = ss::trim_left<' '>;
|
||||
|
||||
test_option_combinations<escape, quote, multiline, triml>();
|
||||
test_option_combinations<escape, quote, multiline, trimr>();
|
||||
#else
|
||||
test_option_combinations3<escape, quote, multiline>();
|
||||
test_option_combinations3<escape, quote, multiline_r>();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
16
test/test_parser2_5.cpp
Normal file
16
test/test_parser2_5.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
#define SEGMENT_NAME "segment5"
|
||||
#include "test_parser2.hpp"
|
||||
|
||||
TEST_CASE("parser test various cases version 2 segment 5") {
|
||||
#ifdef CMAKE_GITHUB_CI
|
||||
using quote = ss::quote<'"'>;
|
||||
using escape = ss::escape<'\\'>;
|
||||
using multiline = ss::multiline;
|
||||
using trimr = ss::trim_right<' '>;
|
||||
using triml = ss::trim_left<' '>;
|
||||
|
||||
test_option_combinations<escape, quote, multiline, triml>();
|
||||
test_option_combinations<escape, quote, multiline, trimr>();
|
||||
#endif
|
||||
}
|
||||
|
11
test/test_parser2_6.cpp
Normal file
11
test/test_parser2_6.cpp
Normal file
@ -0,0 +1,11 @@
|
||||
#define SEGMENT_NAME "segment6"
|
||||
#include "test_parser2.hpp"
|
||||
|
||||
TEST_CASE("parser test various cases version 2 segment 6") {
|
||||
using quote = ss::quote<'"'>;
|
||||
using escape = ss::escape<'\\'>;
|
||||
using multiline = ss::multiline;
|
||||
|
||||
test_option_combinations3<escape, quote, multiline>();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user