diff --git a/include/ss/common.hpp b/include/ss/common.hpp new file mode 100644 index 0000000..d2d34bd --- /dev/null +++ b/include/ss/common.hpp @@ -0,0 +1,19 @@ +#pragma once +#include + +namespace ss { + +struct none {}; + +using string_range = std::pair; +using split_data = std::vector; + +constexpr inline auto default_delimiter = ","; + +template +inline void assert_string_error_defined() { + static_assert(StringError, + "'string_error' needs to be enabled to use 'error_msg'"); +} + +} /* ss */ diff --git a/include/ss/converter.hpp b/include/ss/converter.hpp index 0a0b837..b66002b 100644 --- a/include/ss/converter.hpp +++ b/include/ss/converter.hpp @@ -40,19 +40,13 @@ template using no_validator_t = typename no_validator::type; template -struct no_validator_tup { - using type = typename apply_trait>::type; -}; +struct no_validator_tup : apply_trait> {}; template -struct no_validator_tup> { - using type = typename no_validator_tup::type; -}; +struct no_validator_tup> : no_validator_tup {}; template -struct no_validator_tup> { - using type = no_validator_t; -}; +struct no_validator_tup> : no_validator {}; template using no_validator_tup_t = typename no_validator_tup::type; @@ -62,10 +56,7 @@ using no_validator_tup_t = typename no_validator_tup::type; //////////////// template -struct no_void_tup { - using type = - typename filter_not>::type; -}; +struct no_void_tup : filter_not> {}; template using no_void_tup_t = filter_not_t; @@ -76,14 +67,11 @@ using no_void_tup_t = filter_not_t; // replace 'validators' and remove void from tuple template -struct no_void_validator_tup { - using type = no_validator_tup_t>; -}; +struct no_void_validator_tup : no_validator_tup> {}; template -struct no_void_validator_tup> { - using type = no_validator_tup_t>; -}; +struct no_void_validator_tup> + : no_validator_tup> {}; template using no_void_validator_tup_t = typename no_void_validator_tup::type; @@ -131,12 +119,12 @@ public: no_void_validator_tup_t convert( line_ptr_type line, const std::string& delim = default_delimiter) { split(line, delim); - return convert(splitter_.split_input_); + return convert(splitter_.split_data_); } // parses already split line, returns 'T' object with extracted values template - T convert_object(const split_input& elems) { + T convert_object(const split_data& elems) { return to_object(convert(elems)); } @@ -151,17 +139,12 @@ public: // one argument is given which is a class which has a tied // method which returns a tuple, returns that type template - no_void_validator_tup_t convert(const split_input& elems) { - if constexpr (sizeof...(Ts) == 0 && - is_instance_of::value) { + no_void_validator_tup_t convert(const split_data& elems) { + if constexpr (sizeof...(Ts) == 0 && is_instance_of_v) { return convert_impl(elems, static_cast(nullptr)); - } else if constexpr (tied_class_v) { - using arg_ref_tuple = - typename std::result_of_t; - - using arg_tuple = - typename apply_trait::type; + using arg_ref_tuple = std::result_of_t; + using arg_tuple = apply_trait_t; return to_object( convert_impl(elems, static_cast(nullptr))); @@ -173,7 +156,7 @@ public: // same as above, but uses cached split line template no_void_validator_tup_t convert() { - return convert(splitter_.split_input_); + return convert(splitter_.split_data_); } bool valid() const { @@ -185,8 +168,7 @@ public: } const std::string& error_msg() const { - static_assert(string_error, - "'string_error' needs to be enabled to use 'error_msg'"); + assert_string_error_defined(); return error_; } @@ -196,11 +178,11 @@ public: // 'splits' string by given delimiter, returns vector of pairs which // contain the beginnings and the ends of each column of the string - const split_input& split(line_ptr_type line, - const std::string& delim = default_delimiter) { - splitter_.split_input_.clear(); + const split_data& split(line_ptr_type line, + const std::string& delim = default_delimiter) { + splitter_.split_data_.clear(); if (line[0] == '\0') { - return splitter_.split_input_; + return splitter_.split_data_; } return splitter_.split(line, delim); @@ -211,11 +193,15 @@ private: // resplit //////////////// - const split_input& resplit(line_ptr_type new_line, ssize_t new_size, - const std::string& delim = default_delimiter) { + const split_data& resplit(line_ptr_type new_line, ssize_t new_size, + const std::string& delim = default_delimiter) { return splitter_.resplit(new_line, new_size, delim); } + size_t size_shifted() { + return splitter_.size_shifted(); + } + //////////////// // error //////////////// @@ -248,6 +234,15 @@ private: } } + void set_error_multiline_limit_reached() { + if constexpr (string_error) { + error_.clear(); + error_.append("multiline limit reached."); + } else { + error_ = true; + } + } + void set_error_invalid_conversion(const string_range msg, size_t pos) { if constexpr (string_error) { error_.clear(); @@ -285,7 +280,7 @@ private: //////////////// template - no_void_validator_tup_t convert_impl(const split_input& elems) { + no_void_validator_tup_t convert_impl(const split_data& elems) { clear_error(); if (!splitter_.valid()) { @@ -306,7 +301,7 @@ private: // do not know how to specialize by return type :( template no_void_validator_tup_t> convert_impl( - const split_input& elems, const std::tuple*) { + const split_data& elems, const std::tuple*) { return convert_impl(elems); } @@ -345,11 +340,11 @@ private: template void extract_multiple(no_void_validator_tup_t& tup, - const split_input& elems) { + const split_data& elems) { using elem_t = std::tuple_element_t>; constexpr bool not_void = !std::is_void_v; - constexpr bool one_element = count_not::size == 1; + constexpr bool one_element = count_not_v == 1; if constexpr (not_void) { if constexpr (one_element) { @@ -367,8 +362,8 @@ private: } template - no_void_validator_tup_t extract_tuple(const split_input& elems) { - static_assert(!all_of::value, + no_void_validator_tup_t extract_tuple(const split_data& elems) { + static_assert(!all_of_v, "at least one parameter must be non void"); no_void_validator_tup_t ret{}; extract_multiple<0, 0, Ts...>(ret, elems); @@ -379,7 +374,7 @@ private: // members //////////////// - error_type error_; + error_type error_{}; splitter splitter_; template diff --git a/include/ss/extract.hpp b/include/ss/extract.hpp index 60d3fdb..ebf7ef8 100644 --- a/include/ss/extract.hpp +++ b/include/ss/extract.hpp @@ -225,8 +225,8 @@ struct unsupported_type { template std::enable_if_t && !std::is_floating_point_v && - !is_instance_of::value && - !is_instance_of::value, + !is_instance_of_v && + !is_instance_of_v, bool> extract(const char*, const char*, T&) { static_assert(error::unsupported_type::value, @@ -246,7 +246,7 @@ extract(const char* begin, const char* end, T& value) { } template -std::enable_if_t::value, bool> extract( +std::enable_if_t, bool> extract( const char* begin, const char* end, T& value) { typename T::value_type raw_value; if (extract(begin, end, raw_value)) { @@ -271,7 +271,7 @@ bool extract_variant(const char* begin, const char* end, T& value) { } template -std::enable_if_t::value, bool> extract( +std::enable_if_t, bool> extract( const char* begin, const char* end, T& value) { return extract_variant(begin, end, value); } diff --git a/include/ss/parser.hpp b/include/ss/parser.hpp index af740b1..bf0fad6 100644 --- a/include/ss/parser.hpp +++ b/include/ss/parser.hpp @@ -13,13 +13,17 @@ namespace ss { template class parser { - struct none {}; - constexpr static auto string_error = setup::string_error; - constexpr static auto multiline = setup::multiline; + using multiline = typename setup::multiline; using error_type = ss::ternary_t; + constexpr static bool escaped_multiline_enabled = + multiline::enabled && setup::escape::enabled; + + constexpr static bool quoted_multiline_enabled = + multiline::enabled && setup::quote::enabled; + public: parser(const std::string& file_name, const std::string& delim = ss::default_delimiter) @@ -48,8 +52,7 @@ public: } const std::string& error_msg() const { - static_assert(string_error, - "'string_error' needs to be enabled to use 'error_msg'"); + assert_string_error_defined(); return error_; } @@ -157,11 +160,13 @@ public: if (!parser_.valid()) { return; } + if constexpr (!std::is_same_v) { value = to_object(std::move(tuple_output)); } else { value = std::move(tuple_output); } + parser_.try_invoke(*value, std::forward(fun)); } } @@ -177,6 +182,10 @@ public: return value; } + //////////////// + // members + //////////////// + std::tuple values_; parser& parser_; }; @@ -321,7 +330,7 @@ private: helper_buffer_{other.helper_buffer_}, converter_{std::move( other.converter_)}, next_line_converter_{std::move(other.next_line_converter_)}, - size_{other.size_}, + size_{other.size_}, next_line_size_{other.size_}, helper_size_{other.helper_size_}, delim_{std::move(other.delim_)}, file_{other.file_}, crlf_{other.crlf_} { other.buffer_ = nullptr; @@ -338,6 +347,7 @@ private: converter_ = std::move(other.converter_); next_line_converter_ = std::move(other.next_line_converter_); size_ = other.size_; + next_line_size_ = other.next_line_size_; helper_size_ = other.helper_size_; delim_ = std::move(other.delim_); file_ = other.file_; @@ -367,17 +377,23 @@ private: reader& operator=(const reader& other) = delete; bool read_next() { - ssize_t ssize = getline(&next_line_buffer_, &size_, file_); + memset(next_line_buffer_, '\0', next_line_size_); + ssize_t ssize = + getline(&next_line_buffer_, &next_line_size_, file_); if (ssize == -1) { return false; } size_t size = remove_eol(next_line_buffer_, ssize); + size_t limit = 0; - if constexpr (multiline && setup::escape::enabled) { + if constexpr (escaped_multiline_enabled) { while (escaped_eol(size)) { - if (!append_line(next_line_buffer_, size)) { + if (multiline_limit_reached(limit)) { + return true; + } + if (!append_next_line_to_buffer(next_line_buffer_, size)) { return false; } } @@ -385,11 +401,27 @@ private: next_line_converter_.split(next_line_buffer_, delim_); - if constexpr (multiline && setup::quote::enabled) { + if constexpr (quoted_multiline_enabled) { while (unterminated_quote()) { - if (!append_line(next_line_buffer_, size)) { + if (multiline_limit_reached(limit)) { + return true; + } + if (!append_next_line_to_buffer(next_line_buffer_, size)) { return false; } + + if constexpr (escaped_multiline_enabled) { + while (escaped_eol(size)) { + if (multiline_limit_reached(limit)) { + return true; + } + if (!append_next_line_to_buffer(next_line_buffer_, + size)) { + return false; + } + } + } + next_line_converter_.resplit(next_line_buffer_, size); } } @@ -399,9 +431,20 @@ private: void update() { std::swap(buffer_, next_line_buffer_); + std::swap(size_, next_line_size_); std::swap(converter_, next_line_converter_); } + bool multiline_limit_reached(size_t& limit) { + if constexpr (multiline::size > 0) { + if (limit++ >= multiline::size) { + next_line_converter_.set_error_multiline_limit_reached(); + return true; + } + } + return false; + } + bool escaped_eol(size_t size) { const char* curr; for (curr = next_line_buffer_ + size - 1; @@ -419,12 +462,15 @@ private: return false; } - void undo_remove_eol(size_t& string_end) { + void undo_remove_eol(char* buffer, size_t& string_end) { + if (next_line_converter_.unterminated_quote()) { + string_end -= next_line_converter_.size_shifted(); + } if (crlf_) { - std::copy_n("\r\n\0", 3, next_line_buffer_ + string_end); + std::copy_n("\r\n\0", 3, buffer + string_end); string_end += 2; } else { - std::copy_n("\n\0", 2, next_line_buffer_ + string_end); + std::copy_n("\n\0", 2, buffer + string_end); string_end += 1; } } @@ -444,23 +490,23 @@ private: void realloc_concat(char*& first, size_t& first_size, const char* const second, size_t second_size) { - first = static_cast(realloc(static_cast(first), - first_size + second_size + 2)); - + next_line_size_ = first_size + second_size + 2; + first = static_cast( + realloc(static_cast(first), next_line_size_)); std::copy_n(second, second_size + 1, first + first_size); first_size += second_size; } - bool append_line(char*& dst_buffer, size_t& dst_size) { - undo_remove_eol(dst_size); + bool append_next_line_to_buffer(char*& buffer, size_t& size) { + undo_remove_eol(buffer, size); - ssize_t ssize = getline(&helper_buffer_, &helper_size_, file_); - if (ssize == -1) { + ssize_t next_ssize = getline(&helper_buffer_, &helper_size_, file_); + if (next_ssize == -1) { return false; } - size_t size = remove_eol(helper_buffer_, ssize); - realloc_concat(dst_buffer, dst_size, helper_buffer_, size); + size_t next_size = remove_eol(helper_buffer_, next_ssize); + realloc_concat(buffer, size, helper_buffer_, next_size); return true; } @@ -475,6 +521,7 @@ private: converter next_line_converter_; size_t size_{0}; + size_t next_line_size_{0}; size_t helper_size_{0}; std::string delim_; @@ -488,7 +535,7 @@ private: //////////////// std::string file_name_; - error_type error_; + error_type error_{}; reader reader_; size_t line_number_{0}; bool eof_{false}; diff --git a/include/ss/setup.hpp b/include/ss/setup.hpp index c773fca..8f26eb8 100644 --- a/include/ss/setup.hpp +++ b/include/ss/setup.hpp @@ -4,6 +4,10 @@ namespace ss { +//////////////// +// matcher +//////////////// + template struct matcher { private: @@ -36,7 +40,7 @@ public: }; template -constexpr bool matches_intersect() { +inline constexpr bool matches_intersect() { for (const auto& first_match : FirstMatcher::matches) { for (const auto& second_match : SecondMatcher::matches) { if (first_match != '\0' && first_match == second_match) { @@ -47,6 +51,13 @@ constexpr bool matches_intersect() { return false; } +template +inline constexpr bool matches_intersect_union() { + return matches_intersect() || + matches_intersect(); +} + template <> class matcher<'\0'> { public: @@ -55,31 +66,52 @@ public: static bool match(char c) = delete; }; +//////////////// +// setup +//////////////// + +//////////////// +// matcher +//////////////// + template struct quote : matcher {}; template struct trim : matcher {}; +template +struct trim_left : matcher {}; + +template +struct trim_right : matcher {}; + template struct escape : matcher {}; template class Template> -struct is_instance_of_matcher { - constexpr static bool value = false; -}; +struct is_instance_of_matcher : std::false_type {}; template class Template> -struct is_instance_of_matcher, Template> { - constexpr static bool value = true; -}; +struct is_instance_of_matcher, Template> : std::true_type {}; + +template class Template> +using is_instance_of_matcher_t = + typename is_instance_of_matcher::type; template