From f6a687dc2b4c19dc1b3f6d9261e508bd6199daf1 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Wed, 10 Jul 2024 22:04:34 +0200
Subject: [PATCH] util/FilteredContainer: new class

---
 src/util/FilteredContainer.hxx | 116 +++++++++++++++++++++++++++++++++
 1 file changed, 116 insertions(+)
 create mode 100644 src/util/FilteredContainer.hxx

diff --git a/src/util/FilteredContainer.hxx b/src/util/FilteredContainer.hxx
new file mode 100644
index 000000000..6e2d8abfa
--- /dev/null
+++ b/src/util/FilteredContainer.hxx
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: BSD-2-Clause
+// author: Max Kellermann <max.kellermann@gmail.com>
+
+#pragma once
+
+#include <iterator> // for std::iterator_traits
+
+/**
+ * A container wrapper which returns only elements for which a second
+ * container returns true.
+ */
+template<std::input_iterator AB, typename AE,
+	 std::input_iterator BB>
+class FilteredContainer {
+	[[no_unique_address]]
+	AB a_begin;
+
+	[[no_unique_address]]
+	AE a_end;
+
+	[[no_unique_address]]
+	BB b_begin;
+
+	struct end_iterator {
+		using Traits = std::iterator_traits<AE>;
+
+		using iterator_category = typename Traits::iterator_category;
+		using difference_type = typename Traits::difference_type;
+		using value_type = typename Traits::value_type;
+		using pointer = typename Traits::pointer;
+		using reference = typename Traits::reference;
+
+		AE a;
+	};
+
+public:
+	explicit constexpr FilteredContainer(AB _a_begin, AE _a_end, BB _b_begin) noexcept
+		:a_begin(_a_begin), a_end(_a_end), b_begin(_b_begin) {}
+
+	class iterator {
+		using Traits = std::iterator_traits<AB>;
+
+		[[no_unique_address]]
+		AB a;
+
+		[[no_unique_address]]
+		AE a_end;
+
+		[[no_unique_address]]
+		BB b;
+
+	public:
+		using iterator_category = typename std::forward_iterator_tag;
+		using difference_type = typename Traits::difference_type;
+		using value_type = typename Traits::value_type;
+		using pointer = typename Traits::pointer;
+		using reference = typename Traits::reference;
+
+		explicit constexpr iterator(AB _a, AE _a_end, BB _b) noexcept
+			:a(_a), a_end(_a_end), b(_b) {}
+
+		constexpr void FindEnabled() noexcept {
+			while (a != a_end && !*b) {
+				++a;
+				++b;
+			}
+		}
+
+		constexpr bool operator==(const iterator &other) const noexcept {
+			return a == other.a;
+		}
+
+		constexpr bool operator==(const end_iterator &other) const noexcept {
+			return a == other.a;
+		}
+
+		constexpr auto &operator++() noexcept {
+			++a;
+			++b;
+			FindEnabled();
+			return *this;
+		}
+
+		constexpr auto operator++(int) noexcept {
+			auto old = *this;
+			++*this;
+			return old;
+		}
+
+		reference operator*() const noexcept {
+			return a.operator*();
+		}
+
+		pointer operator->() const noexcept {
+			return a.operator->();
+		}
+	};
+
+	constexpr iterator begin() const noexcept {
+		iterator it{a_begin, a_end, b_begin};
+		it.FindEnabled();
+		return it;
+	}
+
+	constexpr end_iterator end() const noexcept {
+		return {a_end};
+	}
+
+	constexpr iterator cbegin() const noexcept {
+		return begin();
+	}
+
+	constexpr end_iterator cend() const noexcept {
+		return end();
+	}
+};