]> git.saurik.com Git - apple/xnu.git/blame - libkern/libkern/c++/bounded_ptr.h
xnu-7195.101.1.tar.gz
[apple/xnu.git] / libkern / libkern / c++ / bounded_ptr.h
CommitLineData
f427ee49
A
1//
2// Copyright (c) 2019 Apple, Inc. All rights reserved.
3//
4// @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5//
6// This file contains Original Code and/or Modifications of Original Code
7// as defined in and that are subject to the Apple Public Source License
8// Version 2.0 (the 'License'). You may not use this file except in
9// compliance with the License. The rights granted to you under the License
10// may not be used to create, or enable the creation or redistribution of,
11// unlawful or unlicensed copies of an Apple operating system, or to
12// circumvent, violate, or enable the circumvention or violation of, any
13// terms of an Apple operating system software license agreement.
14//
15// Please obtain a copy of the License at
16// http://www.opensource.apple.com/apsl/ and read it before using this file.
17//
18// The Original Code and all software distributed under the License are
19// distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20// EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21// INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22// FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23// Please see the License for the specific language governing rights and
24// limitations under the License.
25//
26// @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27//
28
29#ifndef XNU_LIBKERN_LIBKERN_CXX_BOUNDED_PTR_H
30#define XNU_LIBKERN_LIBKERN_CXX_BOUNDED_PTR_H
31
32#include <stddef.h>
33#include <stdint.h>
34#include <os/overflow.h>
35#include <os/base.h>
36
37#if !defined(__improbable)
38# define __improbable(...) __builtin_expect((__VA_ARGS__), 0)
39#endif
40
41namespace libkern {
42namespace detail {
43// Reimplementation of things in <type_traits> because we don't seem
44// to have the right to rely on the C++ Standard Library (based on
45// attempts to compile IOHIDFamily).
46// TODO: Do we really need to re-implement this here?
47template <typename ...> using void_t = void;
48template <typename T> T && declval() noexcept;
49using nullptr_t = decltype(nullptr);
50template <bool Cond, typename T = void> struct enable_if;
51template <typename T> struct enable_if<true, T> { using type = T; };
52template <bool Cond, typename T = void> using enable_if_t = typename enable_if<Cond, T>::type;
53template <typename T1, typename T2>
54constexpr bool is_convertible_v = __is_convertible_to(T1, T2);
55
56template <typename T> inline constexpr bool is_void_v = false;
57template <> inline constexpr bool is_void_v<void> = true;
58template <> inline constexpr bool is_void_v<void const> = true;
59
60template <typename T, typename U> struct copy_const { using type = U; };
61template <typename T, typename U> struct copy_const<T const, U> { using type = U const; };
62template <typename T, typename U> using copy_const_t = typename copy_const<T, U>::type;
63
64template <typename T, typename U> struct copy_cv { using type = U; };
65template <typename T, typename U> struct copy_cv<T const, U> { using type = U const; };
66template <typename T, typename U> struct copy_cv<T volatile, U> { using type = U volatile; };
67template <typename T, typename U> struct copy_cv<T const volatile, U> { using type = U const volatile; };
68template <typename T, typename U> using copy_cv_t = typename copy_cv<T, U>::type;
69
70template <typename T, typename U>
71using WhenComparable = void_t<
72 decltype(declval<T>() == declval<U>()),
73 decltype(declval<T>() != declval<U>())
74 >;
75
76template <typename T, typename U>
77using WhenOrderable = void_t <
78 decltype(declval<T>() < declval<U>()),
79decltype(declval<T>() > declval<U>()),
80decltype(declval<T>() >= declval<U>()),
81decltype(declval<T>() <= declval<U>())
82>;
83
84// Pretend that sizeof(void) is 1, otherwise the in-bounds check doesn't
85// make sense for `bounded_ptr<void>`.
86template <typename T> constexpr size_t sizeof_v = sizeof(T);
87template <> inline constexpr size_t sizeof_v<void> = 1;
88template <> inline constexpr size_t sizeof_v<void const> = 1;
89template <> inline constexpr size_t sizeof_v<void volatile> = 1;
90template <> inline constexpr size_t sizeof_v<void const volatile> = 1;
91} // end namespace detail
92
93// Non-owning pointer to an object (or a range of objects) of type `T`
94// that validates that the address is within some specified bounds on
95// dereference-like operations.
96//
97// Conceptually, a `bounded_ptr` points within a range of memory `[begin, end)`.
98// If accessing any part of the result of dereferencing the pointer would
99// lead to an access outside of the `[begin, end)` range, the pointer is
100// said to be out-of-bounds. Due to representational constraints, the range
101// of in-bounds memory must be no larger than 4GB.
102//
103// Dereference-like operations (dereference, subscript, pointer member access)
104// validate that the pointer is not out-of-bounds. If an out-of-bounds pointer
105// is dereferenced, the `TrappingPolicy` is called as
106// `TrappingPolicy::trap(some-message)`, and the operation is said to "trap".
107// This terminology is used below to describe the behavior of the `TrappingPolicy`.
108//
109// Pointer arithmetic is allowed (and the bounds are not validated), so it is
110// entirely possible to make a `bounded_ptr` point outside of its range.
111// However, overflow checking is performed on arithmetic operations, and
112// any operation resulting in an overflow will also "trap".
113//
114// The behavior of the `TrappingPolicy` can be customized as desired, however
115// a trap should never return, causing the current `bounded_ptr` operation to
116// be aborted. This is important since the trap could signify an integer
117// overflow, a null-pointer dereference or something else that would lead to
118// undefined behavior (UB) if `TrappingPolicy::trap` were to return.
119//
120// Creation of `bounded_ptr`s
121// ==========================
122// `bounded_ptr` provides a single constructor allowing the bounds of the
123// pointer to be specified. When integrating `bounded_ptr` into an existing
124// code base, it is recommended to use `bounded_ptr` as an iterator obtained
125// from other container-like abstractions, instead of manually using the
126// constructor that allows specifying a range. Specifying the range manually
127// on construction is error-prone, and `bounded_ptr` can't help reduce
128// out-of-bounds accesses if the bounds are specified incorrectly.
129//
130// Furthermore, it is a design choice to not provide a constructor that uses
131// relative offsets from the pointer itself to determine the range, because
132// such a constructor is deemed more confusing than helpful. For example, is
133// the offset a number of bytes or a number of objects? Is the offset inclusive
134// or exclusive? Instead, factory functions should be used to create `bounded_ptr`s.
135//
136// Remark on const-ness
137// ====================
138// Like for raw pointers, the const-ness of a `bounded_ptr` has no bearing on
139// whether the pointee is const. Hence, it is possible to obtain a non-const
140// reference to an object from a const `bounded_ptr`. To encode a
141// pointer-to-const, simply create a `bounded_ptr<T const>`.
142template <typename T, typename TrappingPolicy>
143struct __attribute__((trivial_abi)) bounded_ptr {
144private:
145 using CharType = detail::copy_cv_t<T, char>;
146
147public:
148 // Creates a null `bounded_ptr`.
149 //
150 // A null `bounded_ptr` does not point to any object and is conceptually
151 // out of bounds, so dereferencing it will trap. "Observing" operations
152 // like comparison and check-for-null, along with assignment, are valid
153 // operations on a null `bounded_ptr`.
154 OS_ALWAYS_INLINE constexpr
155 bounded_ptr(detail::nullptr_t)
156 : base_(nullptr), count_(0), offset_(0)
157 {
158 }
159
160 OS_ALWAYS_INLINE constexpr
161 explicit
162 bounded_ptr()
163 : bounded_ptr(nullptr)
164 {
165 }
166
167 // Creates a `bounded_ptr` pointing to the given object, and whose bounds
168 // are described by the provided `[begin, end)` range.
169 //
170 // This constructor does not check whether the constructed pointer is
171 // within its bounds. However, it does check that the provided `[begin, end)`
172 // range is a valid range (that is, `begin <= end`).
173 //
174 // Furthermore, the number of bytes in the range of in-bounds memory must be
175 // representable by a uint32_t, which means that there can be no more than
176 // 2^32 bytes (i.e. 4GB) in that range. Otherwise, the constructor will trap.
177 OS_ALWAYS_INLINE explicit
178 bounded_ptr(T* pointer, T const* begin, T const* end)
179 {
180 base_ = reinterpret_cast<CharType*>(const_cast<T*>(begin));
181
182 // Store (end - begin) into count_, making sure we don't overflow
183 if (__improbable(os_sub_overflow(reinterpret_cast<uintptr_t>(end),
184 reinterpret_cast<uintptr_t>(begin),
185 &count_))) {
186 TrappingPolicy::trap("The range of valid memory is too large to be represented "
187 "by this type, or [begin, end) is not a well-formed range");
188 }
189
190 // Store (pointer - begin) into offset_, making sure we don't overflow.
191 // Note that offset_ can be negative if `pointer` is outside of the
192 // range delimited by [begin, end), which can be valid if it represents
193 // e.g. a subrange of an array.
194 if (__improbable(os_sub_overflow(reinterpret_cast<uintptr_t>(pointer),
195 reinterpret_cast<uintptr_t>(begin),
196 &offset_))) {
197 TrappingPolicy::trap("The offset of the pointer inside its valid memory "
198 "range can't be represented using int32_t");
199 }
200 }
201
202 // Creates a `bounded_ptr` to a type `T` from a `bounded_ptr` to a type `U`.
203 //
204 // This converting constructor is enabled whenever `U*` is implicitly
205 // convertible to `T*`. This allows the usual implicit conversions
206 // between base-and-derived types, and also from any type `U*` to a
207 // `void*`. If other casts (like between unrelated pointer types) are
208 // desired, `libkern::reinterpret_pointer_cast` can be used instead.
209 //
210 // The bounds on the resulting `bounded_ptr` are inherited from the
211 // original `bounded_ptr`.
212 template <typename U, typename Policy, typename = detail::enable_if_t<detail::is_convertible_v<U*, T*> > >
213 OS_ALWAYS_INLINE
214 bounded_ptr(bounded_ptr<U, Policy> const & other)
215 : base_(other.base_)
216 , count_(other.count_)
217 , offset_(static_cast<int32_t>(reinterpret_cast<CharType*>(static_cast<T*>(other.get_ptr_())) - other.base_))
218 {
219 }
220
221 // Assigns a `bounded_ptr` to a type `U` to a `bounded_ptr` to a type `T`,
222 // as long as `U*` is convertible to `T*`.
223 //
224 // This is a rebinding operation, like assignment between raw pointers,
225 // and the destination `bounded_ptr` will inherit the bounds of the
226 // source `bounded_ptr`.
227 template <typename U, typename Policy, typename = detail::enable_if_t<detail::is_convertible_v<U*, T*> > >
228 OS_ALWAYS_INLINE bounded_ptr&
229 operator=(bounded_ptr<U, Policy> const& other)
230 {
231 base_ = other.base_;
232 count_ = other.count_;
233 offset_ = static_cast<int32_t>(reinterpret_cast<CharType*>(static_cast<T*>(other.get_ptr_())) - other.base_);
234 return *this;
235 }
236
237 // Sets a `bounded_ptr` to null.
238 //
239 // This is effectively equivalent to assigning a default-constructed
240 // `bounded_ptr` to the target. As a result, the original bounds of
241 // the `bounded_ptr` are discarded, and the resulting `bounded_ptr`
242 // is both out-of-bounds and also has no bounds assigned to it (like
243 // a default-constructed `bounded_ptr`).
244 OS_ALWAYS_INLINE bounded_ptr&
245 operator=(detail::nullptr_t)
246 {
247 *this = bounded_ptr();
248 return *this;
249 }
250
251 // Returns a reference to the object pointed-to by the `bounded_ptr`.
252 //
253 // Traps if the pointer is pointing outside of its bounds.
254 //
255 // Also note that this function will trap when dereferencing a null
256 // `bounded_ptr`, unless the bounds of the pointer have been set and
257 // include address 0, in which case there's effectively nothing to
258 // diagnose.
259 template <typename T_ = T> // delay instantiation to avoid forming invalid ref for bounded_ptr<void>
260 OS_ALWAYS_INLINE T_&
261 operator*() const
262 {
263 if (__improbable(!in_bounds_())) {
264 TrappingPolicy::trap("bounded_ptr<T>::operator*: Dereferencing this pointer "
265 "would access memory outside of the bounds set originally");
266 }
267 return *get_ptr_();
268 }
269
270 OS_ALWAYS_INLINE T*
271 operator->() const
272 {
273 if (__improbable(!in_bounds_())) {
274 TrappingPolicy::trap("bounded_ptr<T>::operator->: Accessing a member through this pointer "
275 "would access memory outside of the bounds set originally");
276 }
277 return get_ptr_();
278 }
279
280 // Provides access to the n-th element past the given pointer.
281 //
282 // The `bounded_ptr` validates whether the provided index is within the
283 // bounds of the `bounded_ptr`. Like for raw pointers, a negative index
284 // may be passed, in which case the pointer is accessed at a negative
285 // offset (which must still be in bounds).
286 template <typename T_ = T> // delay instantiation to avoid forming invalid ref for bounded_ptr<void>
287 OS_ALWAYS_INLINE T_&
288 operator[](ptrdiff_t n) const
289 {
290 return *(*this + n);
291 }
292
293 // Converts a `bounded_ptr` to a raw pointer, after checking it is within
294 // its bounds.
295 //
296 // The primary intended usage of this function is to aid bridging between
297 // code that uses `bounded_ptr`s and code that does not.
298 OS_ALWAYS_INLINE T*
299 discard_bounds() const
300 {
301 if (__improbable(!in_bounds_())) {
302 TrappingPolicy::trap("bounded_ptr<T>::discard_bounds: Discarding the bounds on "
303 "this pointer would lose the fact that it is outside of the "
304 "bounds set originally");
305 }
306 return get_ptr_();
307 }
308
309 // Converts a `bounded_ptr` to a raw pointer, without checking whether the
310 // pointer is within its bounds.
311 //
312 // Like `discard_bounds()`, the primary intended usage of this function
313 // is to aid bridging between code that uses `bounded_ptr`s and code that
314 // does not. However, unlike `discard_bounds()`, this function does not
315 // validate that the returned pointer is in bounds. This functionality is
316 // necessary when the pointer represents something that can't be
317 // dereferenced (hence it's OK for it to be out-of-bounds), but that
318 // is still useful for other purposes like comparing against other
319 // pointers. An example of that is the `end` pointer in a half-open
320 // interval `[begin, end)`, where the `end` pointer is out-of-bounds and
321 // can't be dereferenced, yet it's still useful to delimit the range.
322 OS_ALWAYS_INLINE T*
323 unsafe_discard_bounds() const
324 {
325 return get_ptr_();
326 }
327
328 // Implicit conversion to bool, returning whether the pointer is null.
329 //
330 // This operation does not perform any validation of the bounds.
331 OS_ALWAYS_INLINE explicit
332 operator bool() const
333 {
334 return get_ptr_() != nullptr;
335 }
336
337 // Increment/decrement a `bounded_ptr`.
338 //
339 // Like for other arithmetic operations, this does not check whether the
340 // increment or decrement operation results in an out-of-bounds pointer.
341 OS_ALWAYS_INLINE bounded_ptr&
342 operator++()
343 {
344 *this += 1;
345 return *this;
346 }
347 OS_ALWAYS_INLINE bounded_ptr
348 operator++(int)
349 {
350 bounded_ptr old = *this;
351 ++*this;
352 return old;
353 }
354 OS_ALWAYS_INLINE bounded_ptr&
355 operator--()
356 {
357 *this -= 1;
358 return *this;
359 }
360 OS_ALWAYS_INLINE bounded_ptr
361 operator--(int)
362 {
363 bounded_ptr old = *this;
364 --*this;
365 return old;
366 }
367
368 // Increment or decrement a `bounded_ptr` by a given offset.
369 //
370 // This is equivalent to adding the given offset to the underlying raw
371 // pointer. In particular, the bounds of the `bounded_ptr` are left
372 // untouched by this operation. Furthermore, like for raw pointers, it
373 // is possible to provide a negative offset, which will have the effect
374 // of decrementing the `bounded_ptr` instead of incrementing it.
375 //
376 // Also note that the offset is NOT a number of bytes -- just like for
377 // raw pointers, it is a number of "positions" to move the pointer from,
378 // which essentially means `n * sizeof(T)` bytes. Again, this works exactly
379 // the same as a raw pointer to an object of type `T`.
380 //
381 // Like other arithmetic operations, this does not check whether the
382 // increment or decrement operation results in an out-of-bounds pointer.
383 // However, this does check whether the arithmetic operation would result
384 // in an overflow, in which case the operation will trap.
385 template <typename T_ = T>
386 OS_ALWAYS_INLINE bounded_ptr&
387 operator+=(ptrdiff_t n)
388 {
389 static_assert(!detail::is_void_v<T_>, "Arithmetic on bounded_ptr<void> is not allowed.");
390
391 ptrdiff_t bytes;
392 if (__improbable(os_mul_overflow(n, sizeof(T), &bytes))) {
393 TrappingPolicy::trap(
394 "bounded_ptr<T>::operator+=(n): Calculating the number of bytes to "
395 "add to the offset (n * sizeof(T)) would trigger an overflow");
396 }
397 if (__improbable(os_add_overflow(offset_, bytes, &offset_))) {
398 TrappingPolicy::trap(
399 "bounded_ptr<T>::operator+=(n): Adding the specified number of bytes "
400 "to the offset representing the current position would overflow.");
401 }
402 return *this;
403 }
404
405 template <typename T_ = T>
406 OS_ALWAYS_INLINE bounded_ptr&
407 operator-=(ptrdiff_t n)
408 {
409 static_assert(!detail::is_void_v<T_>, "Arithmetic on bounded_ptr<void> is not allowed.");
410
411 ptrdiff_t bytes;
412 if (__improbable(os_mul_overflow(n, sizeof(T), &bytes))) {
413 TrappingPolicy::trap(
414 "bounded_ptr<T>::operator-=(n): Calculating the number of bytes to "
415 "subtract from the offset (n * sizeof(T)) would trigger an overflow");
416 }
417 if (__improbable(os_sub_overflow(offset_, bytes, &offset_))) {
418 TrappingPolicy::trap(
419 "bounded_ptr<T>::operator-=(n): Subtracting the specified number of bytes "
420 "from the offset representing the current position would overflow.");
421 }
422 return *this;
423 }
424
425 friend OS_ALWAYS_INLINE bounded_ptr
426 operator+(bounded_ptr p, ptrdiff_t n)
427 {
428 p += n;
429 return p;
430 }
431 friend OS_ALWAYS_INLINE bounded_ptr
432 operator+(ptrdiff_t n, bounded_ptr p)
433 {
434 p += n;
435 return p;
436 }
437 friend OS_ALWAYS_INLINE bounded_ptr
438 operator-(bounded_ptr p, ptrdiff_t n)
439 {
440 p -= n;
441 return p;
442 }
443
444 // Returns the difference between two `bounded_ptr`s.
445 //
446 // This is semantically equivalent to subtracting the two underlying
447 // pointers. The bounds of the pointers are not validated by this
448 // operation.
449 friend OS_ALWAYS_INLINE ptrdiff_t
450 operator-(bounded_ptr const& a, bounded_ptr const& b)
451 {
452 return a.get_ptr_() - b.get_ptr_();
453 }
454
455 friend OS_ALWAYS_INLINE ptrdiff_t
456 operator-(bounded_ptr const& a, T const* b)
457 {
458 return a.get_ptr_() - b;
459 }
460
461 friend OS_ALWAYS_INLINE ptrdiff_t
462 operator-(T const* a, bounded_ptr const& b)
463 {
464 return a - b.get_ptr_();
465 }
466
467private:
468 OS_ALWAYS_INLINE bool
469 in_bounds_() const
470 {
471 static_assert(detail::sizeof_v<T> <= UINT32_MAX - INT32_MAX,
472 "The type pointed-to by bounded_ptr is too large, which would defeat "
473 "our optimization to check for inboundedness using arithmetic on unsigned");
474 return offset_ >= 0 && static_cast<uint32_t>(offset_) + static_cast<uint32_t>(detail::sizeof_v<T>) <= count_;
475 }
476
477 OS_ALWAYS_INLINE T*
478 get_ptr_() const
479 {
480 // Compute `base_ + offset_`, catching overflows.
481 uintptr_t ptr;
482 if (__improbable(os_add_overflow(reinterpret_cast<uintptr_t>(base_), offset_, &ptr))) {
483 TrappingPolicy::trap("This bounded_ptr is pointing to memory outside of what can "
484 "be represented by a native pointer.");
485 }
486 return reinterpret_cast<T*>(ptr);
487 }
488
489 template <typename T_, typename U, typename Policy>
490 friend bounded_ptr<T_, Policy> reinterpret_pointer_cast(bounded_ptr<U, Policy> const&) noexcept;
491
492 template <typename U, typename P> friend struct bounded_ptr; // for cross-type operations and conversions
493
494 CharType* base_; // pointer to the beginning of the valid address range
495 uint32_t count_; // number of bytes considered in-bounds (non-negative)
496 int32_t offset_; // current offset into the range, in bytes
497};
498
499// Returns whether two `bounded_ptr`s point to the same object.
500//
501// This comparison is semantically equivalent to comparing the underlying
502// raw pointers. In particular, it doesn't validate the bounds of either
503// `bounded_ptr`, nor does it compare whether the two `bounded_ptr`s have
504// the same bounds.
505//
506// This comparison is enabled between `bounded_ptr`s whenever the two
507// corresponding raw pointer types are comparable. Comparison between a
508// raw pointer and a `bounded_ptr` is also allowed, so long as the
509// two corresponding raw pointer types are comparable.
510template <typename T, typename P1, typename U, typename P2, typename = detail::WhenComparable<T*, U*> >
511OS_ALWAYS_INLINE bool
512operator==(bounded_ptr<T, P1> const& a, bounded_ptr<U, P2> const& b)
513{
514 return a.unsafe_discard_bounds() == b.unsafe_discard_bounds();
515}
516
517template <typename T, typename P1, typename U, typename P2, typename = detail::WhenComparable<T*, U*> >
518OS_ALWAYS_INLINE bool
519operator!=(bounded_ptr<T, P1> const& a, bounded_ptr<U, P2> const& b)
520{
521 return !(a == b);
522}
523
524template <typename T, typename P, typename U, typename = detail::WhenComparable<T*, U*> >
525OS_ALWAYS_INLINE bool
526operator==(bounded_ptr<T, P> const& a, U* b)
527{
528 return a.unsafe_discard_bounds() == b;
529}
530
531template <typename T, typename P, typename U, typename = detail::WhenComparable<T*, U*> >
532OS_ALWAYS_INLINE bool
533operator==(U* a, bounded_ptr<T, P> const& b)
534{
535 return a == b.unsafe_discard_bounds();
536}
537
538template <typename T, typename P, typename U, typename = detail::WhenComparable<T*, U*> >
539OS_ALWAYS_INLINE bool
540operator!=(bounded_ptr<T, P> const& a, U* b)
541{
542 return !(a == b);
543}
544
545template <typename T, typename P, typename U, typename = detail::WhenComparable<T*, U*> >
546OS_ALWAYS_INLINE bool
547operator!=(U* a, bounded_ptr<T, P> const& b)
548{
549 return !(a == b);
550}
551
552template <typename T, typename Policy>
553OS_ALWAYS_INLINE bool
554operator==(detail::nullptr_t, bounded_ptr<T, Policy> const& p)
555{
556 return p.unsafe_discard_bounds() == nullptr;
557}
558
559template <typename T, typename Policy>
560OS_ALWAYS_INLINE bool
561operator!=(detail::nullptr_t, bounded_ptr<T, Policy> const& p)
562{
563 return p.unsafe_discard_bounds() != nullptr;
564}
565
566template <typename T, typename Policy>
567OS_ALWAYS_INLINE bool
568operator==(bounded_ptr<T, Policy> const& p, detail::nullptr_t)
569{
570 return p.unsafe_discard_bounds() == nullptr;
571}
572
573template <typename T, typename Policy>
574OS_ALWAYS_INLINE bool
575operator!=(bounded_ptr<T, Policy> const& p, detail::nullptr_t)
576{
577 return p.unsafe_discard_bounds() != nullptr;
578}
579
580// Returns whether a `bounded_ptr` points to an address that is {less-than,
581// less-than-or-equal-to, greater-than, greater-than-or-equal-to} the address
582// held in another `bounded_ptr`.
583//
584// This doesn't validate the bounds of either `bounded_ptr`, nor does it
585// compare those bounds to determine the ordering result. This ordering is
586// semantically equivalent to ordering the result of calling `get()` on both
587// `bounded_ptr`s.
588//
589// This ordering is enabled between `bounded_ptr`s whenever the two
590// corresponding raw pointer types are orderable. Ordering between a
591// raw pointer and a `bounded_ptr` is also allowed, so long as the
592// two corresponding raw pointer types are orderable.
593//
594
595template <typename T, typename U, typename P1, typename P2, typename = detail::WhenOrderable<T*, U*> >
596OS_ALWAYS_INLINE bool
597operator<(bounded_ptr<T, P1> const& a, bounded_ptr<U, P2> const& b)
598{
599 return a.unsafe_discard_bounds() < b.unsafe_discard_bounds();
600}
601
602template <typename T, typename U, typename P1, typename P2, typename = detail::WhenOrderable<T*, U*> >
603OS_ALWAYS_INLINE bool
604operator<=(bounded_ptr<T, P1> const& a, bounded_ptr<U, P2> const& b)
605{
606 return a.unsafe_discard_bounds() <= b.unsafe_discard_bounds();
607}
608
609template <typename T, typename U, typename P1, typename P2, typename = detail::WhenOrderable<T*, U*> >
610OS_ALWAYS_INLINE bool
611operator>(bounded_ptr<T, P1> const& a, bounded_ptr<U, P2> const& b)
612{
613 return a.unsafe_discard_bounds() > b.unsafe_discard_bounds();
614}
615
616template <typename T, typename U, typename P1, typename P2, typename = detail::WhenOrderable<T*, U*> >
617OS_ALWAYS_INLINE bool
618operator>=(bounded_ptr<T, P1> const& a, bounded_ptr<U, P2> const& b)
619{
620 return a.unsafe_discard_bounds() >= b.unsafe_discard_bounds();
621}
622
623template <typename T, typename U, typename P, typename = detail::WhenOrderable<T*, U*> >
624OS_ALWAYS_INLINE bool
625operator<(T* a, bounded_ptr<U, P> const& b)
626{
627 return a < b.unsafe_discard_bounds();
628}
629
630template <typename T, typename U, typename P, typename = detail::WhenOrderable<T*, U*> >
631OS_ALWAYS_INLINE bool
632operator<(bounded_ptr<T, P> const& a, U* b)
633{
634 return a.unsafe_discard_bounds() < b;
635}
636
637template <typename T, typename U, typename P, typename = detail::WhenOrderable<T*, U*> >
638OS_ALWAYS_INLINE bool
639operator<=(T* a, bounded_ptr<U, P> const& b)
640{
641 return a <= b.unsafe_discard_bounds();
642}
643
644template <typename T, typename U, typename P, typename = detail::WhenOrderable<T*, U*> >
645OS_ALWAYS_INLINE bool
646operator<=(bounded_ptr<T, P> const& a, U* b)
647{
648 return a.unsafe_discard_bounds() <= b;
649}
650
651template <typename T, typename U, typename P, typename = detail::WhenOrderable<T*, U*> >
652OS_ALWAYS_INLINE bool
653operator>(T* a, bounded_ptr<U, P> const& b)
654{
655 return a > b.unsafe_discard_bounds();
656}
657
658template <typename T, typename U, typename P, typename = detail::WhenOrderable<T*, U*> >
659OS_ALWAYS_INLINE bool
660operator>(bounded_ptr<T, P> const& a, U* b)
661{
662 return a.unsafe_discard_bounds() > b;
663}
664
665template <typename T, typename U, typename P, typename = detail::WhenOrderable<T*, U*> >
666OS_ALWAYS_INLINE bool
667operator>=(T* a, bounded_ptr<U, P> const& b)
668{
669 return a >= b.unsafe_discard_bounds();
670}
671
672template <typename T, typename U, typename P, typename = detail::WhenOrderable<T*, U*> >
673OS_ALWAYS_INLINE bool
674operator>=(bounded_ptr<T, P> const& a, U* b)
675{
676 return a.unsafe_discard_bounds() >= b;
677}
678
679template <typename T, typename U>
680OS_ALWAYS_INLINE T*
681reinterpret_pointer_cast(U* p) noexcept
682{
683 return reinterpret_cast<T*>(p);
684}
685
686// Reinterprets a `bounded_ptr` to a type `T` to a `bounded_ptr` to a type `U`.
687//
688// This is equivalent to `reinterpret_cast`ing the underlying pointer as well
689// as the bounds of the original pointer. Like for a raw `reinterpret_cast`,
690// no offset adjustment is performed (even if needed, e.g. for derived-to-base
691// casts with multiple inheritance). Because this is extremely unsafe, it should
692// be used extremely sparingly.
693template <typename T, typename U, typename Policy>
694OS_ALWAYS_INLINE bounded_ptr<T, Policy>
695reinterpret_pointer_cast(bounded_ptr<U, Policy> const& p) noexcept
696{
697 using CharType = detail::copy_cv_t<T, char>;
698 CharType* new_begin = reinterpret_cast<CharType*>(p.base_);
699 CharType* new_end = new_begin + p.count_;
700 return bounded_ptr<T, Policy>(reinterpret_cast<T*>(p.get_ptr_()),
701 reinterpret_cast<T const*>(new_begin),
702 reinterpret_cast<T const*>(new_end));
703}
704} // end namespace libkern
705
706#endif // !XNU_LIBKERN_LIBKERN_CXX_BOUNDED_PTR_H