commit a0987ff: [Feature] Add utility to split string like stuff for C++ code

Vsevolod Stakhov vsevolod at rspamd.com
Thu Aug 17 11:49:15 UTC 2023


Author: Vsevolod Stakhov
Date: 2023-08-17 11:06:44 +0100
URL: https://github.com/rspamd/rspamd/commit/a0987ff8ade47357987ec52fc884881ac97da72e

[Feature] Add utility to split string like stuff for C++ code
No ranges, as they are a bit ugly to use yet

---
 src/libutil/cxx/util.hxx       | 43 +++++++++++++++++++++++++++++++++++++-----
 src/libutil/cxx/util_tests.cxx | 39 ++++++++++++++++++++++++++++++++++++--
 2 files changed, 75 insertions(+), 7 deletions(-)

diff --git a/src/libutil/cxx/util.hxx b/src/libutil/cxx/util.hxx
index 9ef2f6295..4ec1d568c 100644
--- a/src/libutil/cxx/util.hxx
+++ b/src/libutil/cxx/util.hxx
@@ -1,11 +1,11 @@
-/*-
- * Copyright 2021 Vsevolod Stakhov
+/*
+ * Copyright 2023 Vsevolod Stakhov
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *   http://www.apache.org/licenses/LICENSE-2.0
+ *    http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -53,8 +53,8 @@ constexpr auto find_map(const C &c, const K &k) -> std::optional<std::reference_
 }
 
 
-template<typename _It>
-inline constexpr auto make_string_view_from_it(_It begin, _It end)
+template<typename It>
+inline constexpr auto make_string_view_from_it(It begin, It end)
 {
 	using result_type = std::string_view;
 
@@ -91,6 +91,39 @@ inline auto string_foreach_line(const S &input, const F &functor)
 	}
 }
 
+/**
+ * Iterate over elements in a string
+ * @tparam S
+ * @tparam F
+ * @param input
+ * @param delim
+ * @param functor
+ * @param ignore_empty
+ * @return
+ */
+template<class S, class D, class F,
+		 typename std::enable_if_t<std::is_invocable_v<F, std::string_view> && std::is_constructible_v<std::string_view, S> && std::is_constructible_v<std::string_view, D>, bool> = true>
+inline auto string_foreach_delim(const S &input, const D &delim, const F &functor, const bool ignore_empty = true)
+{
+	size_t first = 0;
+	auto sv_input = std::string_view{input};
+	auto sv_delim = std::string_view{delim};
+
+	while (first < sv_input.size()) {
+		const auto second = sv_input.find_first_of(sv_delim, first);
+
+		if (first != second || !ignore_empty) {
+			functor(sv_input.substr(first, second - first));
+		}
+
+		if (second == std::string_view::npos) {
+			break;
+		}
+
+		first = second + 1;
+	}
+}
+
 template<class S, typename std::enable_if_t<std::is_constructible_v<std::string_view, S>, bool> = true>
 inline auto string_split_on(const S &input, std::string_view::value_type chr) -> std::pair<std::string_view, std::string_view>
 {
diff --git a/src/libutil/cxx/util_tests.cxx b/src/libutil/cxx/util_tests.cxx
index 2b3092779..83b293ce5 100644
--- a/src/libutil/cxx/util_tests.cxx
+++ b/src/libutil/cxx/util_tests.cxx
@@ -1,11 +1,11 @@
-/*-
+/*
  * Copyright 2023 Vsevolod Stakhov
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *   http://www.apache.org/licenses/LICENSE-2.0
+ *    http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -43,4 +43,39 @@ TEST_SUITE("cxx utils")
 			CHECK(res.second == expected.second);
 		}
 	}
+
+	TEST_CASE("string_foreach_delim")
+	{
+		std::tuple<std::string_view, std::string_view, std::pair<std::vector<std::string_view>, std::vector<std::string_view>>> cases[] = {
+			{"test"sv, ","sv, {{"test"}, {"test"}}},
+			{"test,test"sv, ","sv, {{"test", "test"}, {"test", "test"}}},
+			{"test, test"sv, ", "sv, {{"test", "test"}, {"test", "", "test"}}},
+			{"test, test,,"sv, ", "sv, {{"test", "test"}, {"test", "", "test", ""}}},
+		};
+
+		for (const auto &c: cases) {
+			auto res = std::vector<std::string_view>();
+			string_foreach_delim(std::get<0>(c), std::get<1>(c), [&](const auto &v) {
+				res.push_back(v);
+			});
+
+			auto compare_vec = []<class T>(const std::vector<T> &v1, const std::vector<T> &v2) {
+				CHECK(v1.size() == v2.size());
+				for (size_t i = 0; i < v1.size(); ++i) {
+					CHECK(v1[i] == v2[i]);
+				}
+			};
+
+			compare_vec(res, std::get<2>(c).first);
+
+			res.clear();
+			// Perform the same test but with no skip empty
+			string_foreach_delim(
+				std::get<0>(c), std::get<1>(c), [&](const auto &v) {
+					res.push_back(v);
+				},
+				false);
+			compare_vec(res, std::get<2>(c).second);
+		}
+	}
 }
\ No newline at end of file


More information about the Commits mailing list