1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2019 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#ifndef SkZip_DEFINED
9cb93a386Sopenharmony_ci#define SkZip_DEFINED
10cb93a386Sopenharmony_ci
11cb93a386Sopenharmony_ci#include <iterator>
12cb93a386Sopenharmony_ci#include <tuple>
13cb93a386Sopenharmony_ci#include <type_traits>
14cb93a386Sopenharmony_ci#include <utility>
15cb93a386Sopenharmony_ci
16cb93a386Sopenharmony_ci#include "include/core/SkSpan.h"
17cb93a386Sopenharmony_ci#include "include/core/SkTypes.h"
18cb93a386Sopenharmony_ci#include "include/private/SkTemplates.h"
19cb93a386Sopenharmony_ci#include "include/private/SkTo.h"
20cb93a386Sopenharmony_ci
21cb93a386Sopenharmony_ci// Take a list of things that can be pointers, and use them all in parallel. The iterators and
22cb93a386Sopenharmony_ci// accessor operator[] for the class produce a tuple of the items.
23cb93a386Sopenharmony_citemplate<typename... Ts>
24cb93a386Sopenharmony_ciclass SkZip {
25cb93a386Sopenharmony_ci    using ReturnTuple = std::tuple<Ts&...>;
26cb93a386Sopenharmony_ci
27cb93a386Sopenharmony_ci    class Iterator {
28cb93a386Sopenharmony_ci    public:
29cb93a386Sopenharmony_ci        using value_type = ReturnTuple;
30cb93a386Sopenharmony_ci        using difference_type = ptrdiff_t;
31cb93a386Sopenharmony_ci        using pointer = value_type*;
32cb93a386Sopenharmony_ci        using reference = value_type;
33cb93a386Sopenharmony_ci        using iterator_category = std::input_iterator_tag;
34cb93a386Sopenharmony_ci        constexpr Iterator(const SkZip* zip, size_t index) : fZip{zip}, fIndex{index} { }
35cb93a386Sopenharmony_ci        constexpr Iterator(const Iterator& that) : Iterator{ that.fZip, that.fIndex } { }
36cb93a386Sopenharmony_ci        constexpr Iterator& operator++() { ++fIndex; return *this; }
37cb93a386Sopenharmony_ci        constexpr Iterator operator++(int) { Iterator tmp(*this); operator++(); return tmp; }
38cb93a386Sopenharmony_ci        constexpr bool operator==(const Iterator& rhs) const { return fIndex == rhs.fIndex; }
39cb93a386Sopenharmony_ci        constexpr bool operator!=(const Iterator& rhs) const { return fIndex != rhs.fIndex; }
40cb93a386Sopenharmony_ci        constexpr reference operator*() { return (*fZip)[fIndex]; }
41cb93a386Sopenharmony_ci        friend constexpr difference_type operator-(Iterator lhs, Iterator rhs) {
42cb93a386Sopenharmony_ci            return lhs.fIndex - rhs.fIndex;
43cb93a386Sopenharmony_ci        }
44cb93a386Sopenharmony_ci
45cb93a386Sopenharmony_ci    private:
46cb93a386Sopenharmony_ci        const SkZip* const fZip = nullptr;
47cb93a386Sopenharmony_ci        size_t fIndex = 0;
48cb93a386Sopenharmony_ci    };
49cb93a386Sopenharmony_ci
50cb93a386Sopenharmony_ci    template<typename T>
51cb93a386Sopenharmony_ci    inline static constexpr T* nullify = nullptr;
52cb93a386Sopenharmony_ci
53cb93a386Sopenharmony_cipublic:
54cb93a386Sopenharmony_ci    constexpr SkZip() : fPointers{nullify<Ts>...}, fSize{0} {}
55cb93a386Sopenharmony_ci    constexpr SkZip(size_t) = delete;
56cb93a386Sopenharmony_ci    constexpr SkZip(size_t size, Ts*... ts)
57cb93a386Sopenharmony_ci            : fPointers{ts...}
58cb93a386Sopenharmony_ci            , fSize{size} {}
59cb93a386Sopenharmony_ci    constexpr SkZip(const SkZip& that) = default;
60cb93a386Sopenharmony_ci    constexpr SkZip& operator=(const SkZip &that) = default;
61cb93a386Sopenharmony_ci
62cb93a386Sopenharmony_ci    // Check to see if U can be used for const T or is the same as T
63cb93a386Sopenharmony_ci    template <typename U, typename T>
64cb93a386Sopenharmony_ci    using CanConvertToConst = typename std::integral_constant<bool,
65cb93a386Sopenharmony_ci                    std::is_convertible<U*, T*>::value && sizeof(U) == sizeof(T)>::type;
66cb93a386Sopenharmony_ci
67cb93a386Sopenharmony_ci    // Allow SkZip<const T> to be constructed from SkZip<T>.
68cb93a386Sopenharmony_ci    template<typename... Us,
69cb93a386Sopenharmony_ci            typename = std::enable_if<skstd::conjunction<CanConvertToConst<Us, Ts>...>::value>>
70cb93a386Sopenharmony_ci    constexpr SkZip(const SkZip<Us...>& that)
71cb93a386Sopenharmony_ci        : fPointers(that.data())
72cb93a386Sopenharmony_ci        , fSize{that.size()} { }
73cb93a386Sopenharmony_ci
74cb93a386Sopenharmony_ci    constexpr ReturnTuple operator[](size_t i) const { return this->index(i);}
75cb93a386Sopenharmony_ci    constexpr size_t size() const { return fSize; }
76cb93a386Sopenharmony_ci    constexpr bool empty() const { return this->size() == 0; }
77cb93a386Sopenharmony_ci    constexpr ReturnTuple front() const { return this->index(0); }
78cb93a386Sopenharmony_ci    constexpr ReturnTuple back() const { return this->index(this->size() - 1); }
79cb93a386Sopenharmony_ci    constexpr Iterator begin() const { return Iterator{this, 0}; }
80cb93a386Sopenharmony_ci    constexpr Iterator end() const { return Iterator{this, this->size()}; }
81cb93a386Sopenharmony_ci    template<size_t I> constexpr auto get() const {
82cb93a386Sopenharmony_ci        return SkMakeSpan(std::get<I>(fPointers), fSize);
83cb93a386Sopenharmony_ci    }
84cb93a386Sopenharmony_ci    constexpr std::tuple<Ts*...> data() const { return fPointers; }
85cb93a386Sopenharmony_ci    constexpr SkZip first(size_t n) const {
86cb93a386Sopenharmony_ci        SkASSERT(n <= this->size());
87cb93a386Sopenharmony_ci        if (n == 0) { return SkZip(); }
88cb93a386Sopenharmony_ci        return SkZip{n, fPointers};
89cb93a386Sopenharmony_ci    }
90cb93a386Sopenharmony_ci    constexpr SkZip last(size_t n) const {
91cb93a386Sopenharmony_ci        SkASSERT(n <= this->size());
92cb93a386Sopenharmony_ci        if (n == 0) { return SkZip(); }
93cb93a386Sopenharmony_ci        return SkZip{n, this->pointersAt(fSize - n)};
94cb93a386Sopenharmony_ci    }
95cb93a386Sopenharmony_ci    constexpr SkZip subspan(size_t offset, size_t count) const {
96cb93a386Sopenharmony_ci        SkASSERT(offset < this->size());
97cb93a386Sopenharmony_ci        SkASSERT(count <= this->size() - offset);
98cb93a386Sopenharmony_ci        if (count == 0) { return SkZip(); }
99cb93a386Sopenharmony_ci        return SkZip(count, pointersAt(offset));
100cb93a386Sopenharmony_ci    }
101cb93a386Sopenharmony_ci
102cb93a386Sopenharmony_ciprivate:
103cb93a386Sopenharmony_ci    constexpr SkZip(size_t n, const std::tuple<Ts*...>& pointers)
104cb93a386Sopenharmony_ci        : fPointers{pointers}
105cb93a386Sopenharmony_ci        , fSize{n} {}
106cb93a386Sopenharmony_ci
107cb93a386Sopenharmony_ci    constexpr ReturnTuple index(size_t i) const {
108cb93a386Sopenharmony_ci        SkASSERT(this->size() > 0);
109cb93a386Sopenharmony_ci        SkASSERT(i < this->size());
110cb93a386Sopenharmony_ci        return indexDetail(i, std::make_index_sequence<sizeof...(Ts)>{});
111cb93a386Sopenharmony_ci    }
112cb93a386Sopenharmony_ci
113cb93a386Sopenharmony_ci    template<std::size_t... Is>
114cb93a386Sopenharmony_ci    constexpr ReturnTuple indexDetail(size_t i, std::index_sequence<Is...>) const {
115cb93a386Sopenharmony_ci        return ReturnTuple((std::get<Is>(fPointers))[i]...);
116cb93a386Sopenharmony_ci    }
117cb93a386Sopenharmony_ci
118cb93a386Sopenharmony_ci    std::tuple<Ts*...> pointersAt(size_t i) const {
119cb93a386Sopenharmony_ci        SkASSERT(this->size() > 0);
120cb93a386Sopenharmony_ci        SkASSERT(i < this->size());
121cb93a386Sopenharmony_ci        return pointersAtDetail(i, std::make_index_sequence<sizeof...(Ts)>{});
122cb93a386Sopenharmony_ci    }
123cb93a386Sopenharmony_ci
124cb93a386Sopenharmony_ci    template<std::size_t... Is>
125cb93a386Sopenharmony_ci    constexpr std::tuple<Ts*...> pointersAtDetail(size_t i, std::index_sequence<Is...>) const {
126cb93a386Sopenharmony_ci        return std::tuple<Ts*...>{&(std::get<Is>(fPointers))[i]...};
127cb93a386Sopenharmony_ci    }
128cb93a386Sopenharmony_ci
129cb93a386Sopenharmony_ci    std::tuple<Ts*...> fPointers;
130cb93a386Sopenharmony_ci    size_t fSize;
131cb93a386Sopenharmony_ci};
132cb93a386Sopenharmony_ci
133cb93a386Sopenharmony_ciclass SkMakeZipDetail {
134cb93a386Sopenharmony_ci    template<typename T> struct DecayPointer{
135cb93a386Sopenharmony_ci        using U = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
136cb93a386Sopenharmony_ci        using type = typename std::conditional<std::is_pointer<U>::value, U, T>::type;
137cb93a386Sopenharmony_ci    };
138cb93a386Sopenharmony_ci    template<typename T> using DecayPointerT = typename DecayPointer<T>::type;
139cb93a386Sopenharmony_ci
140cb93a386Sopenharmony_ci    template<typename C> struct ContiguousMemory { };
141cb93a386Sopenharmony_ci    template<typename T> struct ContiguousMemory<T*> {
142cb93a386Sopenharmony_ci        using value_type = T;
143cb93a386Sopenharmony_ci        static constexpr value_type* Data(T* t) { return t; }
144cb93a386Sopenharmony_ci        static constexpr size_t Size(T* s) { return SIZE_MAX; }
145cb93a386Sopenharmony_ci    };
146cb93a386Sopenharmony_ci    template<typename T, size_t N> struct ContiguousMemory<T(&)[N]> {
147cb93a386Sopenharmony_ci        using value_type = T;
148cb93a386Sopenharmony_ci        static constexpr value_type* Data(T(&t)[N]) { return t; }
149cb93a386Sopenharmony_ci        static constexpr size_t Size(T(&)[N]) { return N; }
150cb93a386Sopenharmony_ci    };
151cb93a386Sopenharmony_ci    // In general, we don't want r-value collections, but SkSpans are ok, because they are a view
152cb93a386Sopenharmony_ci    // onto an actual container.
153cb93a386Sopenharmony_ci    template<typename T> struct ContiguousMemory<SkSpan<T>> {
154cb93a386Sopenharmony_ci        using value_type = T;
155cb93a386Sopenharmony_ci        static constexpr value_type* Data(SkSpan<T> s) { return s.data(); }
156cb93a386Sopenharmony_ci        static constexpr size_t Size(SkSpan<T> s) { return s.size(); }
157cb93a386Sopenharmony_ci    };
158cb93a386Sopenharmony_ci    // Only accept l-value references to collections.
159cb93a386Sopenharmony_ci    template<typename C> struct ContiguousMemory<C&> {
160cb93a386Sopenharmony_ci        using value_type = typename std::remove_pointer<decltype(std::declval<C>().data())>::type;
161cb93a386Sopenharmony_ci        static constexpr value_type* Data(C& c) { return c.data(); }
162cb93a386Sopenharmony_ci        static constexpr size_t Size(C& c) { return c.size(); }
163cb93a386Sopenharmony_ci    };
164cb93a386Sopenharmony_ci    template<typename C> using Span = ContiguousMemory<DecayPointerT<C>>;
165cb93a386Sopenharmony_ci    template<typename C> using ValueType = typename Span<C>::value_type;
166cb93a386Sopenharmony_ci
167cb93a386Sopenharmony_ci    template<typename C, typename... Ts> struct PickOneSize { };
168cb93a386Sopenharmony_ci    template <typename T, typename... Ts> struct PickOneSize<T*, Ts...> {
169cb93a386Sopenharmony_ci        static constexpr size_t Size(T* t, Ts... ts) {
170cb93a386Sopenharmony_ci            return PickOneSize<Ts...>::Size(std::forward<Ts>(ts)...);
171cb93a386Sopenharmony_ci        }
172cb93a386Sopenharmony_ci    };
173cb93a386Sopenharmony_ci    template <typename T, typename... Ts, size_t N> struct PickOneSize<T(&)[N], Ts...> {
174cb93a386Sopenharmony_ci        static constexpr size_t Size(T(&)[N], Ts...) { return N; }
175cb93a386Sopenharmony_ci    };
176cb93a386Sopenharmony_ci    template<typename T, typename... Ts> struct PickOneSize<SkSpan<T>, Ts...> {
177cb93a386Sopenharmony_ci        static constexpr size_t Size(SkSpan<T> s, Ts...) { return s.size(); }
178cb93a386Sopenharmony_ci    };
179cb93a386Sopenharmony_ci    template<typename C, typename... Ts> struct PickOneSize<C&, Ts...> {
180cb93a386Sopenharmony_ci        static constexpr size_t Size(C& c, Ts...) { return c.size(); }
181cb93a386Sopenharmony_ci    };
182cb93a386Sopenharmony_ci
183cb93a386Sopenharmony_cipublic:
184cb93a386Sopenharmony_ci    template<typename... Ts>
185cb93a386Sopenharmony_ci    static constexpr auto MakeZip(Ts&& ... ts) {
186cb93a386Sopenharmony_ci
187cb93a386Sopenharmony_ci        // Pick the first collection that has a size, and use that for the size.
188cb93a386Sopenharmony_ci        size_t size = PickOneSize<DecayPointerT<Ts>...>::Size(std::forward<Ts>(ts)...);
189cb93a386Sopenharmony_ci
190cb93a386Sopenharmony_ci#ifdef SK_DEBUG
191cb93a386Sopenharmony_ci        // Check that all sizes are the same.
192cb93a386Sopenharmony_ci        size_t minSize = SIZE_MAX;
193cb93a386Sopenharmony_ci        size_t maxSize = 0;
194cb93a386Sopenharmony_ci        for (size_t s : {Span<Ts>::Size(std::forward<Ts>(ts))...}) {
195cb93a386Sopenharmony_ci            if (s != SIZE_MAX) {
196cb93a386Sopenharmony_ci                minSize = std::min(minSize, s);
197cb93a386Sopenharmony_ci                maxSize = std::max(maxSize, s);
198cb93a386Sopenharmony_ci            }
199cb93a386Sopenharmony_ci        }
200cb93a386Sopenharmony_ci        SkASSERT(minSize == maxSize);
201cb93a386Sopenharmony_ci#endif
202cb93a386Sopenharmony_ci
203cb93a386Sopenharmony_ci        return SkZip<ValueType<Ts>...>{size, Span<Ts>::Data(std::forward<Ts>(ts))...};
204cb93a386Sopenharmony_ci    }
205cb93a386Sopenharmony_ci};
206cb93a386Sopenharmony_ci
207cb93a386Sopenharmony_citemplate<typename... Ts>
208cb93a386Sopenharmony_ciinline constexpr auto SkMakeZip(Ts&& ... ts) {
209cb93a386Sopenharmony_ci    return SkMakeZipDetail::MakeZip(std::forward<Ts>(ts)...);
210cb93a386Sopenharmony_ci}
211cb93a386Sopenharmony_ci#endif //SkZip_DEFINED
212