1 // Copyright 2018 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_CONTAINERS_CHECKED_ITERATORS_H_ 6 #define BASE_CONTAINERS_CHECKED_ITERATORS_H_ 7 8 #include <concepts> 9 #include <iterator> 10 #include <memory> 11 #include <type_traits> 12 13 #include "base/check_op.h" 14 #include "base/compiler_specific.h" 15 #include "base/containers/util.h" 16 #include "base/memory/raw_ptr_exclusion.h" 17 #include "build/build_config.h" 18 19 namespace base { 20 21 template <typename T> 22 class CheckedContiguousIterator { 23 public: 24 using difference_type = std::ptrdiff_t; 25 using value_type = std::remove_cv_t<T>; 26 using pointer = T*; 27 using reference = T&; 28 using iterator_category = std::contiguous_iterator_tag; 29 using iterator_concept = std::contiguous_iterator_tag; 30 31 // Required for converting constructor below. 32 template <typename U> 33 friend class CheckedContiguousIterator; 34 35 // Required to be able to get to the underlying pointer without triggering 36 // CHECK failures. 37 template <typename Ptr> 38 friend struct std::pointer_traits; 39 40 constexpr CheckedContiguousIterator() = default; 41 CheckedContiguousIterator(T * start,const T * end)42 UNSAFE_BUFFER_USAGE constexpr CheckedContiguousIterator(T* start, 43 const T* end) 44 : CheckedContiguousIterator(start, start, end) {} 45 CheckedContiguousIterator(const T * start,T * current,const T * end)46 UNSAFE_BUFFER_USAGE constexpr CheckedContiguousIterator(const T* start, 47 T* current, 48 const T* end) 49 : start_(start), current_(current), end_(end) { 50 CHECK_LE(start, current); 51 CHECK_LE(current, end); 52 } 53 54 constexpr CheckedContiguousIterator(const CheckedContiguousIterator& other) = 55 default; 56 57 // Converting constructor allowing conversions like CCI<T> to CCI<const T>, 58 // but disallowing CCI<const T> to CCI<T> or CCI<Derived> to CCI<Base>, which 59 // are unsafe. Furthermore, this is the same condition as used by the 60 // converting constructors of std::span<T> and std::unique_ptr<T[]>. 61 // See https://wg21.link/n4042 for details. 62 template <typename U> CheckedContiguousIterator(const CheckedContiguousIterator<U> & other)63 constexpr CheckedContiguousIterator(const CheckedContiguousIterator<U>& other) 64 requires(std::convertible_to<U (*)[], T (*)[]>) 65 : start_(other.start_), current_(other.current_), end_(other.end_) { 66 // We explicitly don't delegate to the 3-argument constructor here. Its 67 // CHECKs would be redundant, since we expect |other| to maintain its own 68 // invariant. However, DCHECKs never hurt anybody. Presumably. 69 DCHECK_LE(other.start_, other.current_); 70 DCHECK_LE(other.current_, other.end_); 71 } 72 73 ~CheckedContiguousIterator() = default; 74 75 constexpr CheckedContiguousIterator& operator=( 76 const CheckedContiguousIterator& other) = default; 77 78 friend constexpr bool operator==(const CheckedContiguousIterator& lhs, 79 const CheckedContiguousIterator& rhs) { 80 lhs.CheckComparable(rhs); 81 return lhs.current_ == rhs.current_; 82 } 83 84 friend constexpr auto operator<=>(const CheckedContiguousIterator& lhs, 85 const CheckedContiguousIterator& rhs) { 86 lhs.CheckComparable(rhs); 87 return lhs.current_ <=> rhs.current_; 88 } 89 90 constexpr CheckedContiguousIterator& operator++() { 91 CHECK_NE(current_, end_); 92 ++current_; 93 return *this; 94 } 95 96 constexpr CheckedContiguousIterator operator++(int) { 97 CheckedContiguousIterator old = *this; 98 ++*this; 99 return old; 100 } 101 102 constexpr CheckedContiguousIterator& operator--() { 103 CHECK_NE(current_, start_); 104 --current_; 105 return *this; 106 } 107 108 constexpr CheckedContiguousIterator operator--(int) { 109 CheckedContiguousIterator old = *this; 110 --*this; 111 return old; 112 } 113 114 constexpr CheckedContiguousIterator& operator+=(difference_type rhs) { 115 if (rhs > 0) { 116 CHECK_LE(rhs, end_ - current_); 117 } else { 118 CHECK_LE(-rhs, current_ - start_); 119 } 120 current_ += rhs; 121 return *this; 122 } 123 124 constexpr CheckedContiguousIterator operator+(difference_type rhs) const { 125 CheckedContiguousIterator it = *this; 126 it += rhs; 127 return it; 128 } 129 130 constexpr friend CheckedContiguousIterator operator+( 131 difference_type lhs, 132 const CheckedContiguousIterator& rhs) { 133 return rhs + lhs; 134 } 135 136 constexpr CheckedContiguousIterator& operator-=(difference_type rhs) { 137 if (rhs < 0) { 138 CHECK_LE(-rhs, end_ - current_); 139 } else { 140 CHECK_LE(rhs, current_ - start_); 141 } 142 current_ -= rhs; 143 return *this; 144 } 145 146 constexpr CheckedContiguousIterator operator-(difference_type rhs) const { 147 CheckedContiguousIterator it = *this; 148 it -= rhs; 149 return it; 150 } 151 152 constexpr friend difference_type operator-( 153 const CheckedContiguousIterator& lhs, 154 const CheckedContiguousIterator& rhs) { 155 lhs.CheckComparable(rhs); 156 return lhs.current_ - rhs.current_; 157 } 158 159 constexpr reference operator*() const { 160 CHECK_NE(current_, end_); 161 return *current_; 162 } 163 164 constexpr pointer operator->() const { 165 CHECK_NE(current_, end_); 166 return current_; 167 } 168 169 constexpr reference operator[](difference_type rhs) const { 170 CHECK_GE(rhs, 0); 171 CHECK_LT(rhs, end_ - current_); 172 return current_[rhs]; 173 } 174 IsRangeMoveSafe(const CheckedContiguousIterator & from_begin,const CheckedContiguousIterator & from_end,const CheckedContiguousIterator & to)175 [[nodiscard]] static bool IsRangeMoveSafe( 176 const CheckedContiguousIterator& from_begin, 177 const CheckedContiguousIterator& from_end, 178 const CheckedContiguousIterator& to) { 179 if (from_end < from_begin) 180 return false; 181 const auto from_begin_uintptr = get_uintptr(from_begin.current_); 182 const auto from_end_uintptr = get_uintptr(from_end.current_); 183 const auto to_begin_uintptr = get_uintptr(to.current_); 184 const auto to_end_uintptr = 185 get_uintptr((to + std::distance(from_begin, from_end)).current_); 186 187 return to_begin_uintptr >= from_end_uintptr || 188 to_end_uintptr <= from_begin_uintptr; 189 } 190 191 private: CheckComparable(const CheckedContiguousIterator & other)192 constexpr void CheckComparable(const CheckedContiguousIterator& other) const { 193 CHECK_EQ(start_, other.start_); 194 CHECK_EQ(end_, other.end_); 195 } 196 197 // RAW_PTR_EXCLUSION: The embedding class is stack-scoped. 198 RAW_PTR_EXCLUSION const T* start_ = nullptr; 199 RAW_PTR_EXCLUSION T* current_ = nullptr; 200 RAW_PTR_EXCLUSION const T* end_ = nullptr; 201 }; 202 203 template <typename T> 204 using CheckedContiguousConstIterator = CheckedContiguousIterator<const T>; 205 206 } // namespace base 207 208 // Specialize std::pointer_traits so that we can obtain the underlying raw 209 // pointer without resulting in CHECK failures. The important bit is the 210 // `to_address(pointer)` overload, which is the standard blessed way to 211 // customize `std::to_address(pointer)` in C++20 [1]. 212 // 213 // [1] https://wg21.link/pointer.traits.optmem 214 215 template <typename T> 216 struct std::pointer_traits<::base::CheckedContiguousIterator<T>> { 217 using pointer = ::base::CheckedContiguousIterator<T>; 218 using element_type = T; 219 using difference_type = ptrdiff_t; 220 221 template <typename U> 222 using rebind = ::base::CheckedContiguousIterator<U>; 223 224 static constexpr pointer pointer_to(element_type& r) noexcept { 225 return pointer(&r, &r); 226 } 227 228 static constexpr element_type* to_address(pointer p) noexcept { 229 return p.current_; 230 } 231 }; 232 233 #endif // BASE_CONTAINERS_CHECKED_ITERATORS_H_ 234