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
base / types / zip.h [blame]
// 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...) {}
// A sentinel used by the iterator to constrain the comparison to make sure it
// has the proper end of each range.
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 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_;
};
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_