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