summaryrefslogtreecommitdiffstats
path: root/llvm
diff options
context:
space:
mode:
Diffstat (limited to 'llvm')
-rw-r--r--llvm/include/llvm/ADT/STLExtras.h126
-rw-r--r--llvm/unittests/ADT/IteratorTest.cpp30
2 files changed, 155 insertions, 1 deletions
diff --git a/llvm/include/llvm/ADT/STLExtras.h b/llvm/include/llvm/ADT/STLExtras.h
index ba31392f6e1..dba7279b204 100644
--- a/llvm/include/llvm/ADT/STLExtras.h
+++ b/llvm/include/llvm/ADT/STLExtras.h
@@ -508,9 +508,11 @@ make_early_inc_range(RangeT &&Range) {
EarlyIncIteratorT(std::end(std::forward<RangeT>(Range))));
}
-// forward declarations required by zip_shortest/zip_first
+// forward declarations required by zip_shortest/zip_first/zip_longest
template <typename R, typename UnaryPredicate>
bool all_of(R &&range, UnaryPredicate P);
+template <typename R, typename UnaryPredicate>
+bool any_of(R &&range, UnaryPredicate P);
template <size_t... I> struct index_sequence;
@@ -661,6 +663,128 @@ detail::zippy<detail::zip_first, T, U, Args...> zip_first(T &&t, U &&u,
std::forward<T>(t), std::forward<U>(u), std::forward<Args>(args)...);
}
+namespace detail {
+template <typename Iter>
+static Iter next_or_end(const Iter &I, const Iter &End) {
+ if (I == End)
+ return End;
+ return std::next(I);
+}
+
+template <typename Iter>
+static auto deref_or_none(const Iter &I, const Iter &End)
+ -> llvm::Optional<typename std::remove_const<
+ typename std::remove_reference<decltype(*I)>::type>::type> {
+ if (I == End)
+ return None;
+ return *I;
+}
+
+template <typename... Iters> struct ZipLongestValueType {
+ using type = std::tuple<
+ llvm::Optional<typename std::remove_const<typename std::remove_reference<
+ decltype(*std::declval<Iters>())>::type>::type>...>;
+};
+
+template <typename... Iters>
+class zip_longest_iterator
+ : public iterator_facade_base<
+ zip_longest_iterator<Iters...>,
+ typename std::common_type<
+ std::forward_iterator_tag,
+ typename std::iterator_traits<Iters>::iterator_category...>::type,
+ typename ZipLongestValueType<Iters...>::type,
+ typename std::iterator_traits<typename std::tuple_element<
+ 0, std::tuple<Iters...>>::type>::difference_type,
+ typename ZipLongestValueType<Iters...>::type *,
+ typename ZipLongestValueType<Iters...>::type> {
+public:
+ using value_type = typename ZipLongestValueType<Iters...>::type;
+
+private:
+ std::tuple<Iters...> iterators;
+ std::tuple<Iters...> end_iterators;
+
+ template <size_t... Ns>
+ bool test(const zip_longest_iterator<Iters...> &other,
+ index_sequence<Ns...>) const {
+ return llvm::any_of(
+ std::initializer_list<bool>{std::get<Ns>(this->iterators) !=
+ std::get<Ns>(other.iterators)...},
+ identity<bool>{});
+ }
+
+ template <size_t... Ns> value_type deref(index_sequence<Ns...>) const {
+ return value_type(
+ deref_or_none(std::get<Ns>(iterators), std::get<Ns>(end_iterators))...);
+ }
+
+ template <size_t... Ns>
+ decltype(iterators) tup_inc(index_sequence<Ns...>) const {
+ return std::tuple<Iters...>(
+ next_or_end(std::get<Ns>(iterators), std::get<Ns>(end_iterators))...);
+ }
+
+public:
+ zip_longest_iterator(std::pair<Iters &&, Iters &&>... ts)
+ : iterators(std::forward<Iters>(ts.first)...),
+ end_iterators(std::forward<Iters>(ts.second)...) {}
+
+ value_type operator*() { return deref(index_sequence_for<Iters...>{}); }
+
+ value_type operator*() const { return deref(index_sequence_for<Iters...>{}); }
+
+ zip_longest_iterator<Iters...> &operator++() {
+ iterators = tup_inc(index_sequence_for<Iters...>{});
+ return *this;
+ }
+
+ bool operator==(const zip_longest_iterator<Iters...> &other) const {
+ return !test(other, index_sequence_for<Iters...>{});
+ }
+};
+
+template <typename... Args> class zip_longest_range {
+public:
+ using iterator =
+ zip_longest_iterator<decltype(adl_begin(std::declval<Args>()))...>;
+ using iterator_category = typename iterator::iterator_category;
+ using value_type = typename iterator::value_type;
+ using difference_type = typename iterator::difference_type;
+ using pointer = typename iterator::pointer;
+ using reference = typename iterator::reference;
+
+private:
+ std::tuple<Args...> ts;
+
+ template <size_t... Ns> iterator begin_impl(index_sequence<Ns...>) const {
+ return iterator(std::make_pair(adl_begin(std::get<Ns>(ts)),
+ adl_end(std::get<Ns>(ts)))...);
+ }
+
+ template <size_t... Ns> iterator end_impl(index_sequence<Ns...>) const {
+ return iterator(std::make_pair(adl_end(std::get<Ns>(ts)),
+ adl_end(std::get<Ns>(ts)))...);
+ }
+
+public:
+ zip_longest_range(Args &&... ts_) : ts(std::forward<Args>(ts_)...) {}
+
+ iterator begin() const { return begin_impl(index_sequence_for<Args...>{}); }
+ iterator end() const { return end_impl(index_sequence_for<Args...>{}); }
+};
+} // namespace detail
+
+/// Iterate over two or more iterators at the same time. Iteration continues
+/// until all iterators reach the end. The llvm::Optional only contains a value
+/// if the iterator has not reached the end.
+template <typename T, typename U, typename... Args>
+detail::zip_longest_range<T, U, Args...> zip_longest(T &&t, U &&u,
+ Args &&... args) {
+ return detail::zip_longest_range<T, U, Args...>(
+ std::forward<T>(t), std::forward<U>(u), std::forward<Args>(args)...);
+}
+
/// Iterator wrapper that concatenates sequences together.
///
/// This can concatenate different iterators, even with different types, into
diff --git a/llvm/unittests/ADT/IteratorTest.cpp b/llvm/unittests/ADT/IteratorTest.cpp
index de0a6710789..f15ba8eac83 100644
--- a/llvm/unittests/ADT/IteratorTest.cpp
+++ b/llvm/unittests/ADT/IteratorTest.cpp
@@ -328,6 +328,36 @@ TEST(ZipIteratorTest, ZipFirstBasic) {
EXPECT_EQ(iters, 4u);
}
+TEST(ZipIteratorTest, ZipLongestBasic) {
+ using namespace std;
+ const vector<unsigned> pi{3, 1, 4, 1, 5, 9};
+ const vector<StringRef> e{"2", "7", "1", "8"};
+
+ {
+ // Check left range longer than right.
+ const vector<tuple<Optional<unsigned>, Optional<StringRef>>> expected{
+ {3, {"2"}}, {1, {"7"}}, {4, {"1"}}, {1, {"8"}}, {5, None}, {9, None}};
+ size_t iters = 0;
+ for (auto tup : zip_longest(pi, e)) {
+ EXPECT_EQ(tup, expected[iters]);
+ iters += 1;
+ }
+ EXPECT_EQ(iters, expected.size());
+ }
+
+ {
+ // Check right range longer than left.
+ const vector<tuple<Optional<StringRef>, Optional<unsigned>>> expected{
+ {{"2"}, 3}, {{"7"}, 1}, {{"1"}, 4}, {{"8"}, 1}, {None, 5}, {None, 9}};
+ size_t iters = 0;
+ for (auto tup : zip_longest(e, pi)) {
+ EXPECT_EQ(tup, expected[iters]);
+ iters += 1;
+ }
+ EXPECT_EQ(iters, expected.size());
+ }
+}
+
TEST(ZipIteratorTest, Mutability) {
using namespace std;
const SmallVector<unsigned, 4> pi{3, 1, 4, 1, 5, 9};
OpenPOWER on IntegriCloud