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