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