• 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/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