1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
|
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_TYPES_ZIP_H_
#define BASE_TYPES_ZIP_H_
#include <algorithm>
#include <iterator>
#include <tuple>
#include <utility>
#include "base/check.h"
#include "base/compiler_specific.h"
namespace base {
namespace internal {
template <typename... Ranges>
class Zipper {
public:
constexpr explicit Zipper(Ranges&... ranges LIFETIME_BOUND) noexcept
: ranges_(ranges...) {}
struct ZipEnd;
class iterator {
public:
using value_type = std::tuple<
std::remove_cv_t<decltype(*std::begin(std::declval<Ranges&>()))>...>;
using reference = value_type;
using element_type = value_type;
using difference_type = std::ptrdiff_t;
using pointer = void;
// TODO(https://crbug.com/377940847): This could be improved going forward
// to select a better iterator category, based on the common denominator of
// the union of iterators, for instance output iterators, etc.
using iterator_category = std::input_iterator_tag;
using iterator_concept = std::input_iterator_tag;
constexpr iterator& operator++() noexcept LIFETIME_BOUND {
advance(std::index_sequence_for<Ranges...>{});
return *this;
}
constexpr iterator operator++(int) noexcept LIFETIME_BOUND {
auto it = *this;
advance(std::index_sequence_for<Ranges...>{});
return it;
}
constexpr auto operator*() const noexcept LIFETIME_BOUND {
return deref(std::index_sequence_for<Ranges...>{});
}
// Determines if the iterator has reached the end, so a for-range loop bails
// out.
constexpr bool operator!=(ZipEnd) const noexcept LIFETIME_BOUND {
return has_more(std::index_sequence_for<Ranges...>{});
}
private:
friend class Zipper;
constexpr explicit iterator(
std::tuple<decltype(std::begin(std::declval<Ranges&>()))...> begin
LIFETIME_BOUND,
std::tuple<decltype(std::end(std::declval<Ranges&>()))...> end
LIFETIME_BOUND) noexcept
: begin_(begin), end_(end) {}
// Checks if any range has reached the end.
template <std::size_t... Is>
constexpr bool has_more(std::index_sequence<Is...>) const {
return (... && (std::get<Is>(begin_) != std::get<Is>(end_)));
}
template <std::size_t... Is>
constexpr void advance(std::index_sequence<Is...>) {
CHECK(operator!=(ZipEnd()));
// SAFETY: The increment is safe as it has been just CHECKed so it is
// guaranteed to be inside [begin_, end_).
UNSAFE_BUFFERS((++std::get<Is>(begin_), ...));
}
template <size_t... Is>
constexpr value_type deref(std::index_sequence<Is...>) const
LIFETIME_BOUND {
return {*std::get<Is>(begin_)...};
}
std::tuple<decltype(std::begin(std::declval<Ranges&>()))...> begin_;
std::tuple<decltype(std::end(std::declval<Ranges&>()))...> end_;
};
// A sentinel used by the iterator to constrain the comparison to make sure it
// has the proper end of each range.
struct ZipEnd {
constexpr bool operator==(iterator it) const { return it == *this; }
};
constexpr iterator begin() noexcept LIFETIME_BOUND {
return begin_impl(std::index_sequence_for<Ranges...>{});
}
constexpr ZipEnd end() noexcept { return ZipEnd(); }
private:
template <size_t... Is>
constexpr iterator begin_impl(std::index_sequence<Is...>) LIFETIME_BOUND {
return iterator(std::make_tuple(std::begin(std::get<Is>(ranges_))...),
std::make_tuple(std::end(std::get<Is>(ranges_))...));
}
std::tuple<Ranges&...> ranges_;
};
} // namespace internal
// Zipping utility that allows iterating over multiple ranges in lockstep.
//
// Example:
//
// std::vector<int> a = {1, 2, 3};
// std::vector<double> b = {4.5, 5.5, 6.5};
// std::vector<std::string> c = {"x", "y", "z"};
// for (auto [x, y, z] : zip(a, b, c)) {
// LOG(INFO) << x << " " << y << " " << z;
// }
//
// Zipping will carry on until one of the ranges run out, at which the loop will
// bail.
template <typename... Ranges>
constexpr internal::Zipper<Ranges...> zip(Ranges&... ranges LIFETIME_BOUND) {
return internal::Zipper<Ranges...>(ranges...);
}
} // namespace base
#endif // BASE_TYPES_ZIP_H_
|