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.
This commit is contained in:
Maxime Coste 2017-12-06 17:18:44 +08:00
parent 1b44056fce
commit 99636c6230
7 changed files with 97 additions and 115 deletions

View File

@ -932,8 +932,8 @@ void define_command(const ParametersParser& parser, Context& context, const Shel
ShellManager::Flags::WaitForStdout, ShellManager::Flags::WaitForStdout,
shell_context).first; shell_context).first;
CandidateList candidates; CandidateList candidates;
for (auto& str : split(output, '\n', 0)) for (auto&& candidate : output | split<StringView>('\n'))
candidates.push_back(std::move(str)); candidates.push_back(candidate.str());
return Completions{ 0_byte, pos_in_token, std::move(candidates) }; 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); context_wrap_for_buffer(*buffer);
} }
else else
for (auto& name : split(*bufnames, ',')) for (auto&& name : *bufnames | split<StringView>(','))
context_wrap_for_buffer(BufferManager::instance().get_buffer(name)); context_wrap_for_buffer(BufferManager::instance().get_buffer(name));
return; return;
} }

View File

@ -5,6 +5,8 @@
#include <initializer_list> #include <initializer_list>
#include <stddef.h> #include <stddef.h>
#include "array_view.hh"
namespace Kakoune namespace Kakoune
{ {
@ -16,6 +18,9 @@ struct Array
constexpr const T* begin() const { return m_data; } constexpr const T* begin() const { return m_data; }
constexpr const T* end() const { return m_data+N; } constexpr const T* end() const { return m_data+N; }
constexpr operator ArrayView<T>() { return {m_data, N}; }
constexpr operator ConstArrayView<T>() const { return {m_data, N}; }
T m_data[N]; T m_data[N];
}; };

View File

@ -1185,9 +1185,13 @@ void select_object(Context& context, NormalParams params)
if (event != PromptEvent::Validate) if (event != PromptEvent::Validate)
return; return;
Vector<String> params = split(cmdline, ',', '\\'); struct error : runtime_error { error(size_t) : runtime_error{"desc parsing failed, expected <open>,<close>"} {} };
if (params.size() != 2 or params[0].empty() or params[1].empty())
throw runtime_error{"desc parsing failed, expected <open>,<close>"}; auto params = cmdline | split<StringView>(',', '\\') |
transform(unescape<',', '\\'>) | static_gather<error, 2>();
if (params[0].empty() or params[1].empty())
throw error{0};
select_and_set_last<mode>( select_and_set_last<mode>(
context, std::bind(select_surrounding, _1, _2, context, std::bind(select_surrounding, _1, _2,

View File

@ -84,7 +84,8 @@ template<typename T, MemoryDomain domain>
void option_from_string(StringView str, Vector<T, domain>& opt) void option_from_string(StringView str, Vector<T, domain>& opt)
{ {
opt.clear(); opt.clear();
for (auto& elem : split(str, list_separator, '\\')) for (auto&& elem : str | split<StringView>(list_separator, '\\')
| transform(unescape<list_separator, '\\'>))
{ {
T opt_elem; T opt_elem;
option_from_string(elem, opt_elem); option_from_string(elem, opt_elem);
@ -130,16 +131,18 @@ template<typename Key, typename Value, MemoryDomain domain>
bool option_add(HashMap<Key, Value, domain>& opt, StringView str) bool option_add(HashMap<Key, Value, domain>& opt, StringView str)
{ {
bool changed = false; bool changed = false;
for (auto& elem : split(str, list_separator, '\\')) for (auto&& elem : str | split<StringView>(list_separator, '\\')
| transform(unescape<list_separator, '\\'>))
{ {
Vector<String> pair_str = split(elem, '=', '\\'); struct error : runtime_error { error(size_t) : runtime_error{"map option expects key=value"} {} };
if (pair_str.size() != 2) auto key_value = elem | split<StringView>('=', '\\')
throw runtime_error("map option expects key=value"); | transform(unescape<'=', '\\'>)
Key key; | static_gather<error, 2>();
Value value;
option_from_string(pair_str[0], key); HashItem<Key, Value> item;
option_from_string(pair_str[1], value); option_from_string(key_value[0], item.key);
opt.insert({ std::move(key), std::move(value) }); option_from_string(key_value[1], item.value);
opt.insert(std::move(item));
changed = true; changed = true;
} }
return changed; return changed;
@ -200,11 +203,16 @@ String option_to_string(const std::tuple<Types...>& opt)
template<typename... Types> template<typename... Types>
void option_from_string(StringView str, std::tuple<Types...>& opt) void option_from_string(StringView str, std::tuple<Types...>& opt)
{ {
auto elems = split(str, tuple_separator, '\\'); struct error : runtime_error
if (elems.size() != sizeof...(Types)) {
throw runtime_error(elems.size() < sizeof...(Types) ? error(size_t i) : runtime_error{i < sizeof...(Types) ?
"not enough elements in tuple" "not enough elements in tuple"
: "too many elements in tuple"); : "too many elements in tuple"} {}
};
auto elems = str | split<StringView>(tuple_separator, '\\')
| transform(unescape<tuple_separator, '\\'>)
| static_gather<error, sizeof...(Types)>();
TupleOptionDetail<sizeof...(Types)-1, Types...>::from_string(elems, opt); TupleOptionDetail<sizeof...(Types)-1, Types...>::from_string(elems, opt);
} }
@ -246,9 +254,9 @@ inline void option_update(WorstMatch, const Context&)
template<typename EffectiveType, typename LineType, typename ColumnType> template<typename EffectiveType, typename LineType, typename ColumnType>
inline void option_from_string(StringView str, LineAndColumn<EffectiveType, LineType, ColumnType>& opt) inline void option_from_string(StringView str, LineAndColumn<EffectiveType, LineType, ColumnType>& opt)
{ {
auto vals = split(str, ','); struct error : runtime_error { error(size_t) : runtime_error{"expected <line>,<column>"} {} };
if (vals.size() != 2) auto vals = str | split<StringView>(',')
throw runtime_error("expected <line>,<column>"); | static_gather<error, 2>();
opt.line = str_to_int(vals[0]); opt.line = str_to_int(vals[0]);
opt.column = str_to_int(vals[1]); opt.column = str_to_int(vals[1]);
} }

View File

@ -161,7 +161,8 @@ inline auto transform(Transform t)
}); });
} }
template<typename Range, typename Separator = ValueOf<Range>, template<typename Range, bool escape = false,
typename Element = ValueOf<Range>,
typename ValueTypeParam = void> typename ValueTypeParam = void>
struct SplitView struct SplitView
{ {
@ -172,12 +173,16 @@ struct SplitView
struct Iterator : std::iterator<std::forward_iterator_tag, ValueType> struct Iterator : std::iterator<std::forward_iterator_tag, ValueType>
{ {
Iterator(RangeIt pos, RangeIt end, char separator) Iterator(RangeIt pos, RangeIt end, Element separator, Element escaper)
: pos(pos), sep(pos), end(end), separator(separator) : 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; ++sep;
} }
}
Iterator& operator++() { advance(); return *this; } Iterator& operator++() { advance(); return *this; }
Iterator operator++(int) { auto copy = *this; advance(); return copy; } Iterator operator++(int) { auto copy = *this; advance(); return copy; }
@ -197,32 +202,45 @@ struct SplitView
} }
pos = sep+1; pos = sep+1;
bool escaped = escape and *sep == escaper;
for (sep = pos; sep != end; ++sep) for (sep = pos; sep != end; ++sep)
{ {
if (*sep == separator) if (not escaped and *sep == separator)
break; break;
escaped = escape and not escaped and *sep == escaper;
} }
} }
RangeIt pos; RangeIt pos;
RangeIt sep; RangeIt sep;
RangeIt end; RangeIt end;
Separator separator; Element separator;
Element escaper;
}; };
Iterator begin() const { return {std::begin(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}; } Iterator end() const { return {std::end(m_range), std::end(m_range), m_separator, m_escaper}; }
Range m_range; Range m_range;
Separator m_separator; Element m_separator;
Element m_escaper;
}; };
template<typename ValueType = void, typename Separator> template<typename ValueType = void, typename Element>
auto split(Separator separator) auto split(Element separator)
{ {
return make_view_factory([s = std::move(separator)](auto&& range) { return make_view_factory([s = std::move(separator)](auto&& range) {
using Range = decltype(range); using Range = decltype(range);
return SplitView<decay_range<Range>, Separator, ValueType>{std::forward<Range>(range), std::move(s)}; return SplitView<decay_range<Range>, false, Element, ValueType>{std::forward<Range>(range), std::move(s), {}};
});
}
template<typename ValueType = void, typename Element>
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<decay_range<Range>, true, Element, ValueType>{std::forward<Range>(range), std::move(s), std::move(e)};
}); });
} }
@ -340,20 +358,37 @@ auto gather()
} }
template<typename ExceptionType, size_t... Indexes> template<typename ExceptionType, size_t... Indexes>
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; using std::begin; using std::end;
auto elem = [it = begin(range), end = end(range), i = 0u](size_t index) mutable { auto it = begin(range), end_it = end(range);
for (; i < index; ++i, ++it) size_t i = 0;
if (it == end) throw ExceptionType{i}; auto elem = [&](size_t index) {
for (; i < index; ++i)
if (++it == end_it) throw ExceptionType{i};
return *it; return *it;
}; };
// Note that initializer lists elements are guaranteed to be sequenced // Note that initializer lists elements are guaranteed to be sequenced
return Array<std::decay_t<decltype(*begin(range))>, sizeof...(Indexes)>{{elem(Indexes)...}}; Array<std::decay_t<decltype(*begin(range))>, sizeof...(Indexes)> res{{elem(Indexes)...}};
if (exact_size and ++it != end_it)
throw ExceptionType{++i};
return res;
}); });
} }
template<typename ExceptionType, size_t... Indexes>
auto static_gather_impl(std::index_sequence<Indexes...>)
{
return elements<ExceptionType, Indexes...>(true);
}
template<typename ExceptionType, size_t size>
auto static_gather()
{
return static_gather_impl<ExceptionType>(std::make_index_sequence<size>());
}
} }
#endif // ranges_hh_INCLUDED #endif // ranges_hh_INCLUDED

View File

@ -7,60 +7,6 @@
namespace Kakoune namespace Kakoune
{ {
Vector<String> split(StringView str, char separator, char escape)
{
Vector<String> 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<StringView> split(StringView str, char separator)
{
Vector<StringView> 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) StringView trim_whitespaces(StringView str)
{ {
auto beg = str.begin(), end = str.end(); auto beg = str.begin(), end = str.end();
@ -390,22 +336,6 @@ UnitTest test_string{[]()
{ {
kak_assert(String("youpi ") + "matin" == "youpi matin"); kak_assert(String("youpi ") + "matin" == "youpi matin");
Vector<String> 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<StringView> 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<StringView> wrapped = wrap_lines("wrap this paragraph\n respecting whitespaces and much_too_long_words", 16); Vector<StringView> wrapped = wrap_lines("wrap this paragraph\n respecting whitespaces and much_too_long_words", 16);
kak_assert(wrapped.size() == 6); kak_assert(wrapped.size() == 6);
kak_assert(wrapped[0] == "wrap this"); kak_assert(wrapped[0] == "wrap this");

View File

@ -8,14 +8,14 @@
namespace Kakoune namespace Kakoune
{ {
Vector<String> split(StringView str, char separator, char escape);
Vector<StringView> split(StringView str, char separator);
StringView trim_whitespaces(StringView str); StringView trim_whitespaces(StringView str);
String escape(StringView str, StringView characters, char escape); String escape(StringView str, StringView characters, char escape);
String unescape(StringView str, StringView characters, char escape); String unescape(StringView str, StringView characters, char escape);
template<char character, char escape>
String unescape(StringView str) { return unescape(str, character, escape); }
String indent(StringView str, StringView indent = " "); String indent(StringView str, StringView indent = " ");
String replace(StringView str, StringView substr, StringView replacement); String replace(StringView str, StringView substr, StringView replacement);