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 42 // Constructs an iterator from `start` to `end`, starting at `start`. 43 // 44 // # Safety 45 // `start` and `end` must point to a single allocation. 46 // 47 // # Checks 48 // This function CHECKs that `start <= end` and will terminate otherwise. CheckedContiguousIterator(T * start,const T * end)49 UNSAFE_BUFFER_USAGE constexpr CheckedContiguousIterator(T* start, 50 const T* end) 51 : start_(start), current_(start), end_(end) { 52 CHECK_LE(start, end); 53 } 54 55 // Constructs an iterator from `start` to `end`, starting at `current`. 56 // 57 // # Safety 58 // `start`, `current` and `end` must point to a single allocation. 59 // 60 // # Checks 61 // This function CHECKs that `start <= current <= end` and will terminate 62 // otherwise. CheckedContiguousIterator(const T * start,T * current,const T * end)63 UNSAFE_BUFFER_USAGE constexpr CheckedContiguousIterator(const T* start, 64 T* current, 65 const T* end) 66 : start_(start), current_(current), end_(end) { 67 CHECK_LE(start, current); 68 CHECK_LE(current, end); 69 } 70 71 constexpr CheckedContiguousIterator(const CheckedContiguousIterator& other) = 72 default; 73 74 // Converting constructor allowing conversions like CCI<T> to CCI<const T>, 75 // but disallowing CCI<const T> to CCI<T> or CCI<Derived> to CCI<Base>, which 76 // are unsafe. Furthermore, this is the same condition as used by the 77 // converting constructors of std::span<T> and std::unique_ptr<T[]>. 78 // See https://wg21.link/n4042 for details. 79 template <typename U> CheckedContiguousIterator(const CheckedContiguousIterator<U> & other)80 constexpr CheckedContiguousIterator(const CheckedContiguousIterator<U>& other) 81 requires(std::convertible_to<U (*)[], T (*)[]>) 82 : start_(other.start_), current_(other.current_), end_(other.end_) { 83 // We explicitly don't delegate to the 3-argument constructor here. Its 84 // CHECKs would be redundant, since we expect |other| to maintain its own 85 // invariant. However, DCHECKs never hurt anybody. Presumably. 86 DCHECK_LE(other.start_, other.current_); 87 DCHECK_LE(other.current_, other.end_); 88 } 89 90 ~CheckedContiguousIterator() = default; 91 92 constexpr CheckedContiguousIterator& operator=( 93 const CheckedContiguousIterator& other) = default; 94 95 friend constexpr bool operator==(const CheckedContiguousIterator& lhs, 96 const CheckedContiguousIterator& rhs) { 97 lhs.CheckComparable(rhs); 98 return lhs.current_ == rhs.current_; 99 } 100 101 friend constexpr auto operator<=>(const CheckedContiguousIterator& lhs, 102 const CheckedContiguousIterator& rhs) { 103 lhs.CheckComparable(rhs); 104 return lhs.current_ <=> rhs.current_; 105 } 106 107 constexpr CheckedContiguousIterator& operator++() { 108 CHECK_NE(current_, end_); 109 // SAFETY: `current_ <= end_` is an invariant maintained internally, and the 110 // CHECK above ensures that we are not at the end yet, so incrementing stays 111 // in bounds of the allocation. 112 UNSAFE_BUFFERS(++current_); 113 return *this; 114 } 115 116 constexpr CheckedContiguousIterator operator++(int) { 117 CheckedContiguousIterator old = *this; 118 ++*this; 119 return old; 120 } 121 122 constexpr CheckedContiguousIterator& operator--() { 123 CHECK_NE(current_, start_); 124 // SAFETY: `current_ >= start_` is an invariant maintained internally, and 125 // the CHECK above ensures that we are not at the start yet, so decrementing 126 // stays in bounds of the allocation. 127 UNSAFE_BUFFERS(--current_); 128 return *this; 129 } 130 131 constexpr CheckedContiguousIterator operator--(int) { 132 CheckedContiguousIterator old = *this; 133 --*this; 134 return old; 135 } 136 137 constexpr CheckedContiguousIterator& operator+=(difference_type rhs) { 138 // NOTE: Since the max allocation size is PTRDIFF_MAX (in our compilers), 139 // subtracting two pointers from the same allocation can not underflow. 140 CHECK_LE(rhs, end_ - current_); 141 CHECK_GE(rhs, start_ - current_); 142 // SAFETY: `current_ <= end_` is an invariant maintained internally. The 143 // checks above ensure: 144 // `start_ - current_ <= rhs <= end_ - current_`. 145 // Which means: 146 // `start_ <= rhs + current <= end_`, so `current_` will remain in bounds of 147 // the allocation after adding `rhs`. 148 UNSAFE_BUFFERS(current_ += rhs); 149 return *this; 150 } 151 152 constexpr CheckedContiguousIterator operator+(difference_type rhs) const { 153 CheckedContiguousIterator it = *this; 154 it += rhs; 155 return it; 156 } 157 158 constexpr friend CheckedContiguousIterator operator+( 159 difference_type lhs, 160 const CheckedContiguousIterator& rhs) { 161 return rhs + lhs; 162 } 163 164 constexpr CheckedContiguousIterator& operator-=(difference_type rhs) { 165 // NOTE: Since the max allocation size is PTRDIFF_MAX (in our compilers), 166 // subtracting two pointers from the same allocation can not underflow. 167 CHECK_GE(rhs, current_ - end_); 168 CHECK_LE(rhs, current_ - start_); 169 // SAFETY: `start_ <= current_` is an invariant maintained internally. The 170 // checks above ensure: 171 // `current_ - end_ <= rhs <= current_ - start_`. 172 // Which means: 173 // `end_ >= current - rhs >= start_`, so `current_` will remain in bounds 174 // of the allocation after subtracting `rhs`. 175 UNSAFE_BUFFERS(current_ -= rhs); 176 return *this; 177 } 178 179 constexpr CheckedContiguousIterator operator-(difference_type rhs) const { 180 CheckedContiguousIterator it = *this; 181 it -= rhs; 182 return it; 183 } 184 185 constexpr friend difference_type operator-( 186 const CheckedContiguousIterator& lhs, 187 const CheckedContiguousIterator& rhs) { 188 lhs.CheckComparable(rhs); 189 return lhs.current_ - rhs.current_; 190 } 191 192 constexpr reference operator*() const { 193 CHECK_NE(current_, end_); 194 return *current_; 195 } 196 197 constexpr pointer operator->() const { 198 CHECK_NE(current_, end_); 199 return current_; 200 } 201 202 constexpr reference operator[](difference_type rhs) const { 203 // NOTE: Since the max allocation size is PTRDIFF_MAX (in our compilers), 204 // subtracting two pointers from the same allocation can not underflow. 205 CHECK_GE(rhs, start_ - current_); 206 CHECK_LT(rhs, end_ - current_); 207 // SAFETY: `start_ <= current_ <= end_` is an invariant maintained 208 // internally. The checks above ensure: 209 // `start_ - current_ <= rhs < end_ - current_`. 210 // Which means: 211 // `start_ <= current_ + rhs < end_`. 212 // So `current_[rhs]` will be a valid dereference of a pointer in the 213 // allocation (it is not the pointer toone-past-the-end). 214 return UNSAFE_BUFFERS(current_[rhs]); 215 } 216 IsRangeMoveSafe(const CheckedContiguousIterator & from_begin,const CheckedContiguousIterator & from_end,const CheckedContiguousIterator & to)217 [[nodiscard]] static bool IsRangeMoveSafe( 218 const CheckedContiguousIterator& from_begin, 219 const CheckedContiguousIterator& from_end, 220 const CheckedContiguousIterator& to) { 221 if (from_end < from_begin) { 222 return false; 223 } 224 const auto from_begin_uintptr = get_uintptr(from_begin.current_); 225 const auto from_end_uintptr = get_uintptr(from_end.current_); 226 const auto to_begin_uintptr = get_uintptr(to.current_); 227 const auto to_end_uintptr = 228 get_uintptr((to + std::distance(from_begin, from_end)).current_); 229 230 return to_begin_uintptr >= from_end_uintptr || 231 to_end_uintptr <= from_begin_uintptr; 232 } 233 234 private: CheckComparable(const CheckedContiguousIterator & other)235 constexpr void CheckComparable(const CheckedContiguousIterator& other) const { 236 CHECK_EQ(start_, other.start_); 237 CHECK_EQ(end_, other.end_); 238 } 239 240 // RAW_PTR_EXCLUSION: The embedding class is stack-scoped. 241 RAW_PTR_EXCLUSION const T* start_ = nullptr; 242 RAW_PTR_EXCLUSION T* current_ = nullptr; 243 RAW_PTR_EXCLUSION const T* end_ = nullptr; 244 }; 245 246 template <typename T> 247 using CheckedContiguousConstIterator = CheckedContiguousIterator<const T>; 248 249 } // namespace base 250 251 // Specialize std::pointer_traits so that we can obtain the underlying raw 252 // pointer without resulting in CHECK failures. The important bit is the 253 // `to_address(pointer)` overload, which is the standard blessed way to 254 // customize `std::to_address(pointer)` in C++20 [1]. 255 // 256 // [1] https://wg21.link/pointer.traits.optmem 257 258 template <typename T> 259 struct std::pointer_traits<::base::CheckedContiguousIterator<T>> { 260 using pointer = ::base::CheckedContiguousIterator<T>; 261 using element_type = T; 262 using difference_type = ptrdiff_t; 263 264 template <typename U> 265 using rebind = ::base::CheckedContiguousIterator<U>; 266 267 static constexpr pointer pointer_to(element_type& r) noexcept { 268 return pointer(&r, &r); 269 } 270 271 static constexpr element_type* to_address(pointer p) noexcept { 272 return p.current_; 273 } 274 }; 275 276 #endif // BASE_CONTAINERS_CHECKED_ITERATORS_H_ 277