From e0096c3deeec18360033ac5fba0431eaf8a1bd34 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Sat, 19 Mar 2022 10:58:13 +0200 Subject: [PATCH] utils: improve split / join --- lib/utils/mu-utils.cc | 55 +++++++++++++++++++++++++++++++++++ lib/utils/mu-utils.hh | 26 +++++++++++++++++ lib/utils/tests/test-utils.cc | 17 ++++++++++- 3 files changed, 97 insertions(+), 1 deletion(-) diff --git a/lib/utils/mu-utils.cc b/lib/utils/mu-utils.cc index ee33518b..3c907714 100644 --- a/lib/utils/mu-utils.cc +++ b/lib/utils/mu-utils.cc @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -196,7 +197,61 @@ Mu::split(const std::string& str, const std::string& sepa) return vec; } +std::vector +Mu::split(const std::string& str, char sepa) +{ + std::vector vec; + size_t b = 0, e = 0; + + /* special case */ + if (str.empty()) + return vec; + + while (true) { + if (e = str.find(sepa, b); e != std::string::npos) { + vec.emplace_back(str.substr(b, e - b)); + b = e + sizeof(sepa); + } else { + vec.emplace_back(str.substr(b)); + break; + } + } + + return vec; +} + + +std::string +Mu::join(const std::vector& svec, const std::string& sepa) +{ + if (svec.empty()) + return {}; + + + /* calculate the overall size beforehand, to avoid re-allocations. */ + size_t value_len = + std::accumulate(svec.cbegin(), svec.cend(), 0, + [](size_t size, const std::string& s) { + return size + s.size(); + }) + (svec.size() - 1) * sepa.length(); + + std::string value; + value.reserve(value_len); + + std::accumulate(svec.cbegin(), svec.cend(), std::ref(value), + [&](std::string& s1, const std::string& s2)->std::string& { + if (s1.empty()) + s1 = s2; + else { + s1.append(sepa); + s1.append(s2); + } + return s1; + }); + + return value; +} std::string Mu::quote(const std::string& str) diff --git a/lib/utils/mu-utils.hh b/lib/utils/mu-utils.hh index 439db0dc..8f5c792f 100644 --- a/lib/utils/mu-utils.hh +++ b/lib/utils/mu-utils.hh @@ -79,6 +79,32 @@ std::string remove_ctrl(const std::string& str); */ std::vector split(const std::string& str, const std::string& sepa); + +/** + * Split a string in parts. As a special case, splitting an empty string + * yields an empty vector (not a vector with a single empty element) + * + * @param str a string + * @param sepa the separator + * + * @return the parts. + */ +std::vector split(const std::string& str, char sepa); + + +/** + * Join the strings in svec into a string, separated by sepa + * + * @param svec a string vector + * @param sepa separator + * + * @return string + */ +std::string join(const std::vector& svec, const std::string& sepa); +static inline std::string join(const std::vector& svec, char sepa) { + return join(svec, std::string(1, sepa)); +} + /** * Quote & escape a string for " and \ * diff --git a/lib/utils/tests/test-utils.cc b/lib/utils/tests/test-utils.cc index a774861a..7a2ab42a 100644 --- a/lib/utils/tests/test-utils.cc +++ b/lib/utils/tests/test-utils.cc @@ -161,7 +161,6 @@ test_format() } static void - test_split() { using svec = std::vector; @@ -178,8 +177,23 @@ test_split() assert_equal_svec(split("ayybyyc", "yy"), {"a", "b", "c"}); assert_equal_svec(split("abc", ""), {"a", "b", "c"}); assert_equal_svec(split("", "boo"), {}); + + + assert_equal_svec(split("axbxc", 'x'), {"a", "b", "c"}); + assert_equal_svec(split("axbxcx", 'x'), {"a", "b", "c", ""}); + assert_equal_svec(split("", "boo"), {}); } +static void +test_join() +{ + assert_equal(join({"a", "b", "c"}, "x"), "axbxc"); + assert_equal(join({"a", "b", "c"}, ""), "abc"); + assert_equal(join({},"foo"), ""); + assert_equal(join({"d", "e", "f"}, "foo"), "dfooefoof"); +} + + enum struct Bits { None = 0, Bit1 = 1 << 0, Bit2 = 1 << 1 }; MU_ENABLE_BITOPS(Bits); @@ -222,6 +236,7 @@ main(int argc, char* argv[]) g_test_add_func("/utils/clean", test_clean); g_test_add_func("/utils/format", test_format); g_test_add_func("/utils/split", test_split); + g_test_add_func("/utils/join", test_join); g_test_add_func("/utils/define-bitmap", test_define_bitmap); return g_test_run();