/* * Copyright 2019 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SkZip_DEFINED #define SkZip_DEFINED #include #include #include #include #include "include/core/SkSpan.h" #include "include/core/SkTypes.h" #include "include/private/SkTemplates.h" #include "include/private/SkTo.h" // Take a list of things that can be pointers, and use them all in parallel. The iterators and // accessor operator[] for the class produce a tuple of the items. template class SkZip { using ReturnTuple = std::tuple; class Iterator { public: using value_type = ReturnTuple; using difference_type = ptrdiff_t; using pointer = value_type*; using reference = value_type; using iterator_category = std::input_iterator_tag; constexpr Iterator(const SkZip* zip, size_t index) : fZip{zip}, fIndex{index} { } constexpr Iterator(const Iterator& that) : Iterator{ that.fZip, that.fIndex } { } constexpr Iterator& operator++() { ++fIndex; return *this; } constexpr Iterator operator++(int) { Iterator tmp(*this); operator++(); return tmp; } constexpr bool operator==(const Iterator& rhs) const { return fIndex == rhs.fIndex; } constexpr bool operator!=(const Iterator& rhs) const { return fIndex != rhs.fIndex; } constexpr reference operator*() { return (*fZip)[fIndex]; } friend constexpr difference_type operator-(Iterator lhs, Iterator rhs) { return lhs.fIndex - rhs.fIndex; } private: const SkZip* const fZip = nullptr; size_t fIndex = 0; }; template static constexpr T* nullify = nullptr; public: constexpr SkZip() : fPointers{nullify...}, fSize{0} {} constexpr SkZip(size_t) = delete; constexpr SkZip(size_t size, Ts*... ts) : fPointers{ts...} , fSize{size} {} constexpr SkZip(const SkZip& that) = default; // Check to see if U can be used for const T or is the same as T template using CanConvertToConst = typename std::integral_constant::value && sizeof(U) == sizeof(T)>::type; // Allow SkZip to be constructed from SkZip. template...>::value>> constexpr SkZip(const SkZip& that) : fPointers(that.data()) , fSize{that.size()} { } constexpr ReturnTuple operator[](size_t i) const { return this->index(i);} constexpr size_t size() const { return fSize; } constexpr bool empty() const { return this->size() == 0; } constexpr ReturnTuple front() const { return this->index(0); } constexpr ReturnTuple back() const { return this->index(this->size() - 1); } constexpr Iterator begin() const { return Iterator{this, 0}; } constexpr Iterator end() const { return Iterator{this, this->size()}; } template constexpr auto get() const { return SkMakeSpan(std::get(fPointers), fSize); } constexpr std::tuple data() const { return fPointers; } constexpr SkZip first(size_t n) const { SkASSERT(n <= this->size()); if (n == 0) { return SkZip(); } return SkZip{n, fPointers}; } constexpr SkZip last(size_t n) const { SkASSERT(n <= this->size()); if (n == 0) { return SkZip(); } return SkZip{n, this->pointersAt(fSize - n)}; } constexpr SkZip subspan(size_t offset, size_t count) const { SkASSERT(offset < this->size()); SkASSERT(count <= this->size() - offset); if (count == 0) { return SkZip(); } return SkZip(count, pointersAt(offset)); } private: constexpr SkZip(size_t n, const std::tuple& pointers) : fPointers{pointers} , fSize{n} {} constexpr ReturnTuple index(size_t i) const { SkASSERT(this->size() > 0); SkASSERT(i < this->size()); return indexDetail(i, std::make_index_sequence{}); } template constexpr ReturnTuple indexDetail(size_t i, std::index_sequence) const { return ReturnTuple((std::get(fPointers))[i]...); } std::tuple pointersAt(size_t i) const { SkASSERT(this->size() > 0); SkASSERT(i < this->size()); return pointersAtDetail(i, std::make_index_sequence{}); } template constexpr std::tuple pointersAtDetail(size_t i, std::index_sequence) const { return std::tuple{&(std::get(fPointers))[i]...}; } std::tuple fPointers; size_t fSize; }; class SkMakeZipDetail { template struct DecayPointer{ using U = typename std::remove_cv::type>::type; using type = typename std::conditional::value, U, T>::type; }; template using DecayPointerT = typename DecayPointer::type; template struct ContiguousMemory { }; template struct ContiguousMemory { using value_type = T; static constexpr value_type* Data(T* t) { return t; } static constexpr size_t Size(T* s) { return SIZE_MAX; } }; template struct ContiguousMemory { using value_type = T; static constexpr value_type* Data(T(&t)[N]) { return t; } static constexpr size_t Size(T(&)[N]) { return N; } }; // In general, we don't want r-value collections, but SkSpans are ok, because they are a view // onto an actual container. template struct ContiguousMemory> { using value_type = T; static constexpr value_type* Data(SkSpan s) { return s.data(); } static constexpr size_t Size(SkSpan s) { return s.size(); } }; // Only accept l-value references to collections. template struct ContiguousMemory { using value_type = typename std::remove_pointer().data())>::type; static constexpr value_type* Data(C& c) { return c.data(); } static constexpr size_t Size(C& c) { return c.size(); } }; template using Span = ContiguousMemory>; template using ValueType = typename Span::value_type; template struct PickOneSize { }; template struct PickOneSize { static constexpr size_t Size(T* t, Ts... ts) { return PickOneSize::Size(std::forward(ts)...); } }; template struct PickOneSize { static constexpr size_t Size(T(&)[N], Ts...) { return N; } }; template struct PickOneSize, Ts...> { static constexpr size_t Size(SkSpan s, Ts...) { return s.size(); } }; template struct PickOneSize { static constexpr size_t Size(C& c, Ts...) { return c.size(); } }; public: template static constexpr auto MakeZip(Ts&& ... ts) { // Pick the first collection that has a size, and use that for the size. size_t size = PickOneSize...>::Size(std::forward(ts)...); #ifdef SK_DEBUG // Check that all sizes are the same. size_t minSize = SIZE_MAX; size_t maxSize = 0; for (size_t s : {Span::Size(std::forward(ts))...}) { if (s != SIZE_MAX) { minSize = std::min(minSize, s); maxSize = std::max(maxSize, s); } } SkASSERT(minSize == maxSize); #endif return SkZip...>{size, Span::Data(std::forward(ts))...}; } }; template template constexpr T* SkZip::nullify; template inline constexpr auto SkMakeZip(Ts&& ... ts) { return SkMakeZipDetail::MakeZip(std::forward(ts)...); } #endif //SkZip_DEFINED