1 /* 2 * Copyright 2018 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #ifndef SkSpan_DEFINED 9 #define SkSpan_DEFINED 10 11 #include "include/private/base/SkAssert.h" 12 #include "include/private/base/SkDebug.h" 13 #include "include/private/base/SkTo.h" 14 15 #include <cstddef> 16 #include <initializer_list> 17 #include <iterator> 18 #include <limits> 19 #include <utility> 20 21 // Having this be an export works around IWYU churn related to 22 // https://github.com/include-what-you-use/include-what-you-use/issues/1121 23 #include <type_traits> // IWYU pragma: export 24 25 // Add macro to check the lifetime of initializer_list arguments. initializer_list has a very 26 // short life span, and can only be used as a parameter, and not as a variable. 27 #if defined(__clang__) && defined(__has_cpp_attribute) && __has_cpp_attribute(clang::lifetimebound) 28 #define SK_CHECK_IL_LIFETIME [[clang::lifetimebound]] 29 #else 30 #define SK_CHECK_IL_LIFETIME 31 #endif 32 33 /** 34 * SkSpan holds a reference to contiguous data of type T along with a count. SkSpan does not own 35 * the data itself but is merely a reference, therefore you must take care with the lifetime of 36 * the underlying data. 37 * 38 * SkSpan is a count and a pointer into existing array or data type that stores its data in 39 * contiguous memory like std::vector. Any container that works with std::size() and std::data() 40 * can be used. 41 * 42 * SkSpan makes a convenient parameter for a routine to accept array like things. This allows you to 43 * write the routine without overloads for all different container types. 44 * 45 * Example: 46 * void routine(SkSpan<const int> a) { ... } 47 * 48 * std::vector v = {1, 2, 3, 4, 5}; 49 * 50 * routine(a); 51 * 52 * A word of caution when working with initializer_list, initializer_lists have a lifetime that is 53 * limited to the current statement. The following is correct and safe: 54 * 55 * Example: 56 * routine({1,2,3,4,5}); 57 * 58 * The following is undefined, and will result in erratic execution: 59 * 60 * Bad Example: 61 * initializer_list l = {1, 2, 3, 4, 5}; // The data behind l dies at the ;. 62 * routine(l); 63 */ 64 template <typename T> 65 class SkSpan { 66 public: SkSpan()67 constexpr SkSpan() : fPtr{nullptr}, fSize{0} {} 68 69 template <typename Integer, std::enable_if_t<std::is_integral_v<Integer>, bool> = true> SkSpan(T * ptr,Integer size)70 constexpr SkSpan(T* ptr, Integer size) : fPtr{ptr}, fSize{SkToSizeT(size)} { 71 SkASSERT(ptr || fSize == 0); // disallow nullptr + a nonzero size 72 SkASSERT(fSize < (std::numeric_limits<size_t>::max() / sizeof(T))); 73 } 74 template <typename U, typename = std::enable_if_t<std::is_same_v<const U, T>>> SkSpan(const SkSpan<U> & that)75 constexpr SkSpan(const SkSpan<U>& that) : fPtr(std::data(that)), fSize(std::size(that)) {} 76 constexpr SkSpan(const SkSpan& o) = default; SkSpan(T (& a)[N])77 template<size_t N> constexpr SkSpan(T(&a)[N]) : SkSpan(a, N) { } 78 template<typename Container> SkSpan(Container && c)79 constexpr SkSpan(Container&& c) : SkSpan(std::data(c), std::size(c)) { } SkSpan(std::initializer_list<T> il SK_CHECK_IL_LIFETIME)80 SkSpan(std::initializer_list<T> il SK_CHECK_IL_LIFETIME) 81 : SkSpan(std::data(il), std::size(il)) {} 82 83 constexpr SkSpan& operator=(const SkSpan& that) = default; 84 85 constexpr T& operator [] (size_t i) const { 86 return fPtr[sk_collection_check_bounds(i, this->size())]; 87 } front()88 constexpr T& front() const { sk_collection_not_empty(this->empty()); return fPtr[0]; } back()89 constexpr T& back() const { sk_collection_not_empty(this->empty()); return fPtr[fSize - 1]; } begin()90 constexpr T* begin() const { return fPtr; } end()91 constexpr T* end() const { return fPtr + fSize; } rbegin()92 constexpr auto rbegin() const { return std::make_reverse_iterator(this->end()); } rend()93 constexpr auto rend() const { return std::make_reverse_iterator(this->begin()); } data()94 constexpr T* data() const { return this->begin(); } size()95 constexpr size_t size() const { return fSize; } empty()96 constexpr bool empty() const { return fSize == 0; } size_bytes()97 constexpr size_t size_bytes() const { return fSize * sizeof(T); } first(size_t prefixLen)98 constexpr SkSpan<T> first(size_t prefixLen) const { 99 return SkSpan{fPtr, sk_collection_check_length(prefixLen, fSize)}; 100 } last(size_t postfixLen)101 constexpr SkSpan<T> last(size_t postfixLen) const { 102 return SkSpan{fPtr + (this->size() - postfixLen), 103 sk_collection_check_length(postfixLen, fSize)}; 104 } subspan(size_t offset)105 constexpr SkSpan<T> subspan(size_t offset) const { 106 return this->subspan(offset, this->size() - offset); 107 } subspan(size_t offset,size_t count)108 constexpr SkSpan<T> subspan(size_t offset, size_t count) const { 109 const size_t safeOffset = sk_collection_check_length(offset, fSize); 110 111 // Should read offset + count > size(), but that could overflow. We know that safeOffset 112 // is <= size, therefore the subtraction will not overflow. 113 if (count > this->size() - safeOffset) SK_UNLIKELY { 114 // The count is too large. 115 SkUNREACHABLE; 116 } 117 return SkSpan{fPtr + safeOffset, count}; 118 } 119 120 private: 121 T* fPtr; 122 size_t fSize; 123 }; 124 125 template <typename Container> 126 SkSpan(Container&&) -> 127 SkSpan<std::remove_pointer_t<decltype(std::data(std::declval<Container>()))>>; 128 129 #endif // SkSpan_DEFINED 130