#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;
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...>{});
}
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) {}
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()));
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_;
};
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_;
};
}
template <typename... Ranges>
constexpr internal::Zipper<Ranges...> zip(Ranges&... ranges LIFETIME_BOUND) {
return internal::Zipper<Ranges...>(ranges...);
}
}
#endif