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