From 99636c6230f9a3f6a818db5a301137114bf79617 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Wed, 6 Dec 2017 17:18:44 +0800 Subject: [PATCH] Remove Vector returning split functions, use range adaptor Do not allocate temporary vectors to store splitted data, use the 'split' range adaptor along with transform(unescape) to provide the same feature with less allocations. --- src/commands.cc | 6 ++-- src/constexpr_utils.hh | 5 +++ src/normal.cc | 10 ++++-- src/option_types.hh | 44 +++++++++++++++----------- src/ranges.hh | 71 +++++++++++++++++++++++++++++++----------- src/string_utils.cc | 70 ----------------------------------------- src/string_utils.hh | 6 ++-- 7 files changed, 97 insertions(+), 115 deletions(-) diff --git a/src/commands.cc b/src/commands.cc index 8ae74c7c..8ee19c49 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -932,8 +932,8 @@ void define_command(const ParametersParser& parser, Context& context, const Shel ShellManager::Flags::WaitForStdout, shell_context).first; CandidateList candidates; - for (auto& str : split(output, '\n', 0)) - candidates.push_back(std::move(str)); + for (auto&& candidate : output | split('\n')) + candidates.push_back(candidate.str()); return Completions{ 0_byte, pos_in_token, std::move(candidates) }; }; @@ -1579,7 +1579,7 @@ void context_wrap(const ParametersParser& parser, Context& context, Func func) context_wrap_for_buffer(*buffer); } else - for (auto& name : split(*bufnames, ',')) + for (auto&& name : *bufnames | split(',')) context_wrap_for_buffer(BufferManager::instance().get_buffer(name)); return; } diff --git a/src/constexpr_utils.hh b/src/constexpr_utils.hh index f1d3ce81..b0d5643f 100644 --- a/src/constexpr_utils.hh +++ b/src/constexpr_utils.hh @@ -5,6 +5,8 @@ #include #include +#include "array_view.hh" + namespace Kakoune { @@ -16,6 +18,9 @@ struct Array constexpr const T* begin() const { return m_data; } constexpr const T* end() const { return m_data+N; } + constexpr operator ArrayView() { return {m_data, N}; } + constexpr operator ConstArrayView() const { return {m_data, N}; } + T m_data[N]; }; diff --git a/src/normal.cc b/src/normal.cc index a92eb257..0b3d8645 100644 --- a/src/normal.cc +++ b/src/normal.cc @@ -1185,9 +1185,13 @@ void select_object(Context& context, NormalParams params) if (event != PromptEvent::Validate) return; - Vector params = split(cmdline, ',', '\\'); - if (params.size() != 2 or params[0].empty() or params[1].empty()) - throw runtime_error{"desc parsing failed, expected ,"}; + struct error : runtime_error { error(size_t) : runtime_error{"desc parsing failed, expected ,"} {} }; + + auto params = cmdline | split(',', '\\') | + transform(unescape<',', '\\'>) | static_gather(); + + if (params[0].empty() or params[1].empty()) + throw error{0}; select_and_set_last( context, std::bind(select_surrounding, _1, _2, diff --git a/src/option_types.hh b/src/option_types.hh index 219fcc69..0c3fc3e3 100644 --- a/src/option_types.hh +++ b/src/option_types.hh @@ -84,7 +84,8 @@ template void option_from_string(StringView str, Vector& opt) { opt.clear(); - for (auto& elem : split(str, list_separator, '\\')) + for (auto&& elem : str | split(list_separator, '\\') + | transform(unescape)) { T opt_elem; option_from_string(elem, opt_elem); @@ -130,16 +131,18 @@ template bool option_add(HashMap& opt, StringView str) { bool changed = false; - for (auto& elem : split(str, list_separator, '\\')) + for (auto&& elem : str | split(list_separator, '\\') + | transform(unescape)) { - Vector pair_str = split(elem, '=', '\\'); - if (pair_str.size() != 2) - throw runtime_error("map option expects key=value"); - Key key; - Value value; - option_from_string(pair_str[0], key); - option_from_string(pair_str[1], value); - opt.insert({ std::move(key), std::move(value) }); + struct error : runtime_error { error(size_t) : runtime_error{"map option expects key=value"} {} }; + auto key_value = elem | split('=', '\\') + | transform(unescape<'=', '\\'>) + | static_gather(); + + HashItem item; + option_from_string(key_value[0], item.key); + option_from_string(key_value[1], item.value); + opt.insert(std::move(item)); changed = true; } return changed; @@ -200,11 +203,16 @@ String option_to_string(const std::tuple& opt) template void option_from_string(StringView str, std::tuple& opt) { - auto elems = split(str, tuple_separator, '\\'); - if (elems.size() != sizeof...(Types)) - throw runtime_error(elems.size() < sizeof...(Types) ? - "not enough elements in tuple" - : "too many elements in tuple"); + struct error : runtime_error + { + error(size_t i) : runtime_error{i < sizeof...(Types) ? + "not enough elements in tuple" + : "too many elements in tuple"} {} + }; + auto elems = str | split(tuple_separator, '\\') + | transform(unescape) + | static_gather(); + TupleOptionDetail::from_string(elems, opt); } @@ -246,9 +254,9 @@ inline void option_update(WorstMatch, const Context&) template inline void option_from_string(StringView str, LineAndColumn& opt) { - auto vals = split(str, ','); - if (vals.size() != 2) - throw runtime_error("expected ,"); + struct error : runtime_error { error(size_t) : runtime_error{"expected ,"} {} }; + auto vals = str | split(',') + | static_gather(); opt.line = str_to_int(vals[0]); opt.column = str_to_int(vals[1]); } diff --git a/src/ranges.hh b/src/ranges.hh index 599396dc..9a4b1c8e 100644 --- a/src/ranges.hh +++ b/src/ranges.hh @@ -161,7 +161,8 @@ inline auto transform(Transform t) }); } -template, +template, typename ValueTypeParam = void> struct SplitView { @@ -172,11 +173,15 @@ struct SplitView struct Iterator : std::iterator { - Iterator(RangeIt pos, RangeIt end, char separator) - : pos(pos), sep(pos), end(end), separator(separator) + Iterator(RangeIt pos, RangeIt end, Element separator, Element escaper) + : pos(pos), sep(pos), end(end), separator(separator), escaper(escaper) { - while (sep != end and *sep != separator) + bool escaped = false; + while (sep != end and (escaped or *sep != separator)) + { + escaped = escape and *sep == escaper; ++sep; + } } Iterator& operator++() { advance(); return *this; } @@ -197,32 +202,45 @@ struct SplitView } pos = sep+1; + bool escaped = escape and *sep == escaper; for (sep = pos; sep != end; ++sep) { - if (*sep == separator) + if (not escaped and *sep == separator) break; + escaped = escape and not escaped and *sep == escaper; } } RangeIt pos; RangeIt sep; RangeIt end; - Separator separator; + Element separator; + Element escaper; }; - Iterator begin() const { return {std::begin(m_range), std::end(m_range), m_separator}; } - Iterator end() const { return {std::end(m_range), std::end(m_range), m_separator}; } + Iterator begin() const { return {std::begin(m_range), std::end(m_range), m_separator, m_escaper}; } + Iterator end() const { return {std::end(m_range), std::end(m_range), m_separator, m_escaper}; } Range m_range; - Separator m_separator; + Element m_separator; + Element m_escaper; }; -template -auto split(Separator separator) +template +auto split(Element separator) { return make_view_factory([s = std::move(separator)](auto&& range) { using Range = decltype(range); - return SplitView, Separator, ValueType>{std::forward(range), std::move(s)}; + return SplitView, false, Element, ValueType>{std::forward(range), std::move(s), {}}; + }); +} + +template +auto split(Element separator, Element escaper) +{ + return make_view_factory([s = std::move(separator), e = std::move(escaper)](auto&& range) { + using Range = decltype(range); + return SplitView, true, Element, ValueType>{std::forward(range), std::move(s), std::move(e)}; }); } @@ -340,20 +358,37 @@ auto gather() } template -auto elements() +auto elements(bool exact_size = false) { - return make_view_factory([] (auto&& range) { + return make_view_factory([=] (auto&& range) { using std::begin; using std::end; - auto elem = [it = begin(range), end = end(range), i = 0u](size_t index) mutable { - for (; i < index; ++i, ++it) - if (it == end) throw ExceptionType{i}; + auto it = begin(range), end_it = end(range); + size_t i = 0; + auto elem = [&](size_t index) { + for (; i < index; ++i) + if (++it == end_it) throw ExceptionType{i}; return *it; }; // Note that initializer lists elements are guaranteed to be sequenced - return Array, sizeof...(Indexes)>{{elem(Indexes)...}}; + Array, sizeof...(Indexes)> res{{elem(Indexes)...}}; + if (exact_size and ++it != end_it) + throw ExceptionType{++i}; + return res; }); } +template +auto static_gather_impl(std::index_sequence) +{ + return elements(true); +} + +template +auto static_gather() +{ + return static_gather_impl(std::make_index_sequence()); +} + } #endif // ranges_hh_INCLUDED diff --git a/src/string_utils.cc b/src/string_utils.cc index 06e0eb7c..55c7562e 100644 --- a/src/string_utils.cc +++ b/src/string_utils.cc @@ -7,60 +7,6 @@ namespace Kakoune { -Vector split(StringView str, char separator, char escape) -{ - Vector res; - auto it = str.begin(); - auto start = it; - while (it != str.end()) - { - res.emplace_back(); - String& element = res.back(); - while (it != str.end()) - { - auto c = *it; - if (c == escape and it + 1 != str.end() and *(it+1) == separator) - { - element += StringView{start, it+1}; - element.back() = separator; - it += 2; - start = it; - } - else if (c == separator) - { - element += StringView{start, it}; - ++it; - start = it; - break; - } - else - ++it; - } - } - if (start != str.end()) - res.back() += StringView{start, str.end()}; - return res; -} - -Vector split(StringView str, char separator) -{ - Vector res; - if (str.empty()) - return res; - - auto beg = str.begin(); - for (auto it = beg; it != str.end(); ++it) - { - if (*it == separator) - { - res.emplace_back(beg, it); - beg = it + 1; - } - } - res.emplace_back(beg, str.end()); - return res; -} - StringView trim_whitespaces(StringView str) { auto beg = str.begin(), end = str.end(); @@ -390,22 +336,6 @@ UnitTest test_string{[]() { kak_assert(String("youpi ") + "matin" == "youpi matin"); - Vector splited = split("youpi:matin::tchou\\:kanaky:hihi\\:", ':', '\\'); - kak_assert(splited[0] == "youpi"); - kak_assert(splited[1] == "matin"); - kak_assert(splited[2] == ""); - kak_assert(splited[3] == "tchou:kanaky"); - kak_assert(splited[4] == "hihi:"); - - Vector splitedview = split("youpi:matin::tchou\\:kanaky:hihi\\:", ':'); - kak_assert(splitedview[0] == "youpi"); - kak_assert(splitedview[1] == "matin"); - kak_assert(splitedview[2] == ""); - kak_assert(splitedview[3] == "tchou\\"); - kak_assert(splitedview[4] == "kanaky"); - kak_assert(splitedview[5] == "hihi\\"); - kak_assert(splitedview[6] == ""); - Vector wrapped = wrap_lines("wrap this paragraph\n respecting whitespaces and much_too_long_words", 16); kak_assert(wrapped.size() == 6); kak_assert(wrapped[0] == "wrap this"); diff --git a/src/string_utils.hh b/src/string_utils.hh index 7263c6f6..f1796f7c 100644 --- a/src/string_utils.hh +++ b/src/string_utils.hh @@ -8,14 +8,14 @@ namespace Kakoune { -Vector split(StringView str, char separator, char escape); -Vector split(StringView str, char separator); - StringView trim_whitespaces(StringView str); String escape(StringView str, StringView characters, char escape); String unescape(StringView str, StringView characters, char escape); +template +String unescape(StringView str) { return unescape(str, character, escape); } + String indent(StringView str, StringView indent = " "); String replace(StringView str, StringView substr, StringView replacement);