checked_iterators.h (9715B)
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 <iterator> 9 #include <memory> 10 #include <type_traits> 11 12 #include "base/check_op.h" 13 #include "base/containers/util.h" 14 #include "base/memory/raw_ptr_exclusion.h" 15 #include "build/build_config.h" 16 17 namespace base { 18 19 template <typename T> 20 class CheckedContiguousIterator { 21 public: 22 using difference_type = std::ptrdiff_t; 23 using value_type = std::remove_cv_t<T>; 24 using pointer = T*; 25 using reference = T&; 26 using iterator_category = std::random_access_iterator_tag; 27 #if defined(__cpp_lib_ranges) 28 using iterator_concept = std::contiguous_iterator_tag; 29 #endif 30 31 // Required for converting constructor below. 32 template <typename U> 33 friend class CheckedContiguousIterator; 34 35 // Required for certain libc++ algorithm optimizations that are not available 36 // for NaCl. 37 template <typename Ptr> 38 friend struct std::pointer_traits; 39 40 constexpr CheckedContiguousIterator() = default; 41 42 constexpr CheckedContiguousIterator(T* start, const T* end) 43 : CheckedContiguousIterator(start, start, end) {} 44 45 constexpr CheckedContiguousIterator(const T* start, T* current, const T* end) 46 : start_(start), current_(current), end_(end) { 47 CHECK_LE(start, current); 48 CHECK_LE(current, end); 49 } 50 51 constexpr CheckedContiguousIterator(const CheckedContiguousIterator& other) = 52 default; 53 54 // Converting constructor allowing conversions like CCI<T> to CCI<const T>, 55 // but disallowing CCI<const T> to CCI<T> or CCI<Derived> to CCI<Base>, which 56 // are unsafe. Furthermore, this is the same condition as used by the 57 // converting constructors of std::span<T> and std::unique_ptr<T[]>. 58 // See https://wg21.link/n4042 for details. 59 template < 60 typename U, 61 std::enable_if_t<std::is_convertible_v<U (*)[], T (*)[]>>* = nullptr> 62 constexpr CheckedContiguousIterator(const CheckedContiguousIterator<U>& other) 63 : start_(other.start_), current_(other.current_), end_(other.end_) { 64 // We explicitly don't delegate to the 3-argument constructor here. Its 65 // CHECKs would be redundant, since we expect |other| to maintain its own 66 // invariant. However, DCHECKs never hurt anybody. Presumably. 67 DCHECK_LE(other.start_, other.current_); 68 DCHECK_LE(other.current_, other.end_); 69 } 70 71 ~CheckedContiguousIterator() = default; 72 73 constexpr CheckedContiguousIterator& operator=( 74 const CheckedContiguousIterator& other) = default; 75 76 friend constexpr bool operator==(const CheckedContiguousIterator& lhs, 77 const CheckedContiguousIterator& rhs) { 78 lhs.CheckComparable(rhs); 79 return lhs.current_ == rhs.current_; 80 } 81 82 friend constexpr bool operator!=(const CheckedContiguousIterator& lhs, 83 const CheckedContiguousIterator& rhs) { 84 lhs.CheckComparable(rhs); 85 return lhs.current_ != rhs.current_; 86 } 87 88 friend constexpr bool operator<(const CheckedContiguousIterator& lhs, 89 const CheckedContiguousIterator& rhs) { 90 lhs.CheckComparable(rhs); 91 return lhs.current_ < rhs.current_; 92 } 93 94 friend constexpr bool operator<=(const CheckedContiguousIterator& lhs, 95 const CheckedContiguousIterator& rhs) { 96 lhs.CheckComparable(rhs); 97 return lhs.current_ <= rhs.current_; 98 } 99 friend constexpr bool operator>(const CheckedContiguousIterator& lhs, 100 const CheckedContiguousIterator& rhs) { 101 lhs.CheckComparable(rhs); 102 return lhs.current_ > rhs.current_; 103 } 104 105 friend constexpr bool operator>=(const CheckedContiguousIterator& lhs, 106 const CheckedContiguousIterator& rhs) { 107 lhs.CheckComparable(rhs); 108 return lhs.current_ >= rhs.current_; 109 } 110 111 constexpr CheckedContiguousIterator& operator++() { 112 CHECK_NE(current_, end_); 113 ++current_; 114 return *this; 115 } 116 117 constexpr CheckedContiguousIterator operator++(int) { 118 CheckedContiguousIterator old = *this; 119 ++*this; 120 return old; 121 } 122 123 constexpr CheckedContiguousIterator& operator--() { 124 CHECK_NE(current_, start_); 125 --current_; 126 return *this; 127 } 128 129 constexpr CheckedContiguousIterator operator--(int) { 130 CheckedContiguousIterator old = *this; 131 --*this; 132 return old; 133 } 134 135 constexpr CheckedContiguousIterator& operator+=(difference_type rhs) { 136 if (rhs > 0) { 137 CHECK_LE(rhs, end_ - current_); 138 } else { 139 CHECK_LE(-rhs, current_ - start_); 140 } 141 current_ += rhs; 142 return *this; 143 } 144 145 constexpr CheckedContiguousIterator operator+(difference_type rhs) const { 146 CheckedContiguousIterator it = *this; 147 it += rhs; 148 return it; 149 } 150 151 constexpr friend CheckedContiguousIterator operator+( 152 difference_type lhs, 153 const CheckedContiguousIterator& rhs) { 154 return rhs + lhs; 155 } 156 157 constexpr CheckedContiguousIterator& operator-=(difference_type rhs) { 158 if (rhs < 0) { 159 CHECK_LE(-rhs, end_ - current_); 160 } else { 161 CHECK_LE(rhs, current_ - start_); 162 } 163 current_ -= rhs; 164 return *this; 165 } 166 167 constexpr CheckedContiguousIterator operator-(difference_type rhs) const { 168 CheckedContiguousIterator it = *this; 169 it -= rhs; 170 return it; 171 } 172 173 constexpr friend difference_type operator-( 174 const CheckedContiguousIterator& lhs, 175 const CheckedContiguousIterator& rhs) { 176 lhs.CheckComparable(rhs); 177 return lhs.current_ - rhs.current_; 178 } 179 180 constexpr reference operator*() const { 181 CHECK_NE(current_, end_); 182 return *current_; 183 } 184 185 constexpr pointer operator->() const { 186 CHECK_NE(current_, end_); 187 return current_; 188 } 189 190 constexpr reference operator[](difference_type rhs) const { 191 CHECK_GE(rhs, 0); 192 CHECK_LT(rhs, end_ - current_); 193 return current_[rhs]; 194 } 195 196 [[nodiscard]] static bool IsRangeMoveSafe( 197 const CheckedContiguousIterator& from_begin, 198 const CheckedContiguousIterator& from_end, 199 const CheckedContiguousIterator& to) { 200 if (from_end < from_begin) 201 return false; 202 const auto from_begin_uintptr = get_uintptr(from_begin.current_); 203 const auto from_end_uintptr = get_uintptr(from_end.current_); 204 const auto to_begin_uintptr = get_uintptr(to.current_); 205 const auto to_end_uintptr = 206 get_uintptr((to + std::distance(from_begin, from_end)).current_); 207 208 return to_begin_uintptr >= from_end_uintptr || 209 to_end_uintptr <= from_begin_uintptr; 210 } 211 212 private: 213 constexpr void CheckComparable(const CheckedContiguousIterator& other) const { 214 CHECK_EQ(start_, other.start_); 215 CHECK_EQ(end_, other.end_); 216 } 217 218 // This field is not a raw_ptr<> because it was filtered by the rewriter for: 219 // #union, #constexpr-ctor-field-initializer 220 RAW_PTR_EXCLUSION const T* start_ = nullptr; 221 // This field is not a raw_ptr<> because it was filtered by the rewriter for: 222 // #union, #constexpr-ctor-field-initializer 223 RAW_PTR_EXCLUSION T* current_ = nullptr; 224 // This field is not a raw_ptr<> because it was filtered by the rewriter for: 225 // #union, #constexpr-ctor-field-initializer 226 RAW_PTR_EXCLUSION const T* end_ = nullptr; 227 }; 228 229 template <typename T> 230 using CheckedContiguousConstIterator = CheckedContiguousIterator<const T>; 231 232 } // namespace base 233 234 // Specialize both std::__is_cpp17_contiguous_iterator and std::pointer_traits 235 // for CCI in case we compile with libc++ outside of NaCl. The former is 236 // required to enable certain algorithm optimizations (e.g. std::copy can be a 237 // simple std::memmove under certain circumstances), and is a precursor to 238 // C++20's std::contiguous_iterator concept [1]. Once we actually use C++20 it 239 // will be enough to add `using iterator_concept = std::contiguous_iterator_tag` 240 // to the iterator class [2], and we can get rid of this non-standard 241 // specialization. 242 // 243 // The latter is required to obtain the underlying raw pointer without resulting 244 // in CHECK failures. The important bit is the `to_address(pointer)` overload, 245 // which is the standard blessed way to customize `std::to_address(pointer)` in 246 // C++20 [3]. 247 // 248 // [1] https://wg21.link/iterator.concept.contiguous 249 // [2] https://wg21.link/std.iterator.tags 250 // [3] https://wg21.link/pointer.traits.optmem 251 252 #if defined(_LIBCPP_VERSION) 253 254 // TODO(crbug.com/1284275): Remove when C++20 is on by default, as the use 255 // of `iterator_concept` above should suffice. 256 _LIBCPP_BEGIN_NAMESPACE_STD 257 258 // TODO(crbug.com/1449299): https://reviews.llvm.org/D150801 renamed this from 259 // `__is_cpp17_contiguous_iterator` to `__libcpp_is_contiguous_iterator`. Clean 260 // up the old spelling after libc++ rolls. 261 template <typename T> 262 struct __is_cpp17_contiguous_iterator; 263 template <typename T> 264 struct __is_cpp17_contiguous_iterator<::base::CheckedContiguousIterator<T>> 265 : true_type {}; 266 267 template <typename T> 268 struct __libcpp_is_contiguous_iterator; 269 template <typename T> 270 struct __libcpp_is_contiguous_iterator<::base::CheckedContiguousIterator<T>> 271 : true_type {}; 272 273 _LIBCPP_END_NAMESPACE_STD 274 275 #endif 276 277 namespace std { 278 279 template <typename T> 280 struct pointer_traits<::base::CheckedContiguousIterator<T>> { 281 using pointer = ::base::CheckedContiguousIterator<T>; 282 using element_type = T; 283 using difference_type = ptrdiff_t; 284 285 template <typename U> 286 using rebind = ::base::CheckedContiguousIterator<U>; 287 288 static constexpr pointer pointer_to(element_type& r) noexcept { 289 return pointer(&r, &r); 290 } 291 292 static constexpr element_type* to_address(pointer p) noexcept { 293 return p.current_; 294 } 295 }; 296 297 } // namespace std 298 299 #endif // BASE_CONTAINERS_CHECKED_ITERATORS_H_