|  | // Copyright Microsoft and CHERIoT Contributors. | 
|  | // SPDX-License-Identifier: MIT | 
|  |  | 
|  | /** | 
|  | * @file Pointer utilities | 
|  | */ | 
|  |  | 
|  | #pragma once | 
|  |  | 
|  | #include <cheri.hh> | 
|  | #include <concepts> | 
|  |  | 
|  | namespace ds::pointer | 
|  | { | 
|  |  | 
|  | /** | 
|  | * Offset a pointer by a number of bytes.  The return type must be | 
|  | * explicitly specified by the caller.  The type of the displacement offset | 
|  | * (Offset) is templated so that we can accept both signed and unsigned | 
|  | * offsets. | 
|  | */ | 
|  | template<typename T, typename U> | 
|  | static inline __always_inline T *offset(U *base, std::integral auto offset) | 
|  | { | 
|  | CHERI::Capability c{base}; | 
|  | c.address() += offset; | 
|  | return c.template cast<void>().template cast<T>(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Compute the unsigned difference in bytes between two pointers' target | 
|  | * addresses.  To be standards-compliant, cursor must be part of the same | 
|  | * allocation as base and at a higher address. | 
|  | */ | 
|  | static inline __always_inline size_t diff(const void *base, | 
|  | const void *cursor) | 
|  | { | 
|  | return static_cast<size_t>(reinterpret_cast<const char *>(cursor) - | 
|  | reinterpret_cast<const char *>(base)); | 
|  | } | 
|  |  | 
|  | namespace proxy | 
|  | { | 
|  |  | 
|  | /** | 
|  | * Proxies<P,T> if P is a proxy object for T*-s. | 
|  | */ | 
|  | template<typename P, typename T> | 
|  | concept Proxies = std::same_as<T, typename P::Type> && | 
|  | requires(P &proxy, P &proxy2, T *ptr) | 
|  | { | 
|  | /* Probe for operator=(T*) */ | 
|  | { | 
|  | proxy = ptr | 
|  | } -> std::same_as<P &>; | 
|  |  | 
|  | /* Probe for operator T*() */ | 
|  | { | 
|  | ptr == proxy | 
|  | } -> std::same_as<bool>; | 
|  |  | 
|  | /* TODO: How to probe for operator-> ? */ | 
|  |  | 
|  | /* Probe for operator==(T*) */ | 
|  | { | 
|  | proxy == ptr | 
|  | } -> std::same_as<bool>; | 
|  |  | 
|  | /* Probe for operator==(P&) */ | 
|  | { | 
|  | proxy == proxy2 | 
|  | } -> std::same_as<bool>; | 
|  |  | 
|  | /* Probe for operator<=>(T*) */ | 
|  | { | 
|  | proxy <=> ptr | 
|  | } -> std::same_as<std::strong_ordering>; | 
|  |  | 
|  | /* Probe for operator<=>(P) */ | 
|  | { | 
|  | proxy <=> proxy2 | 
|  | } -> std::same_as<std::strong_ordering>; | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Pointer references are pointer proxies, shockingly enough. | 
|  | */ | 
|  | template<typename T> | 
|  | class Pointer | 
|  | { | 
|  | T *&ref; | 
|  |  | 
|  | public: | 
|  | using Type = T; | 
|  |  | 
|  | __always_inline Pointer(T *&r) : ref(r) {} | 
|  |  | 
|  | __always_inline operator T *() | 
|  | { | 
|  | return ref; | 
|  | } | 
|  |  | 
|  | __always_inline T *operator->() | 
|  | { | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | __always_inline Pointer<T> &operator=(T *t) | 
|  | { | 
|  | ref = t; | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | __always_inline Pointer<T> &operator=(Pointer const &p) | 
|  | { | 
|  | ref = p.ref; | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | __always_inline bool operator==(Pointer &p) | 
|  | { | 
|  | return this->ref == p.ref; | 
|  | } | 
|  |  | 
|  | __always_inline auto operator<=>(Pointer &p) | 
|  | { | 
|  | return this->ref <=> p.ref; | 
|  | } | 
|  | }; | 
|  | static_assert(Proxies<Pointer<void>, void>); | 
|  |  | 
|  | /** | 
|  | * Equipped with a context for bounds, an address reference can be a | 
|  | * proxy for a pointer. | 
|  | */ | 
|  | template<typename T> | 
|  | class PtrAddr | 
|  | { | 
|  | CHERI::Capability<void> ctx; | 
|  | ptraddr_t              &ref; | 
|  |  | 
|  | public: | 
|  | using Type = T; | 
|  |  | 
|  | __always_inline PtrAddr(void *c, ptraddr_t &r) : ctx(c), ref(r) {} | 
|  |  | 
|  | __always_inline operator T *() | 
|  | { | 
|  | auto c      = ctx; | 
|  | c.address() = ref; | 
|  | return c.cast<T>().get(); | 
|  | } | 
|  |  | 
|  | __always_inline T *operator->() | 
|  | { | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | __always_inline PtrAddr &operator=(T *p) | 
|  | { | 
|  | ref = CHERI::Capability{p}.address(); | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | __always_inline PtrAddr &operator=(PtrAddr const &p) | 
|  | { | 
|  | ref = p.ref; | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Since the context is used only for bounds, don't bother | 
|  | * implicitly converting both proxies up to T* | 
|  | */ | 
|  |  | 
|  | __always_inline bool operator==(PtrAddr &p) | 
|  | { | 
|  | return ref == p.ref; | 
|  | } | 
|  |  | 
|  | __always_inline auto operator<=>(PtrAddr &p) | 
|  | { | 
|  | return ref <=> p.ref; | 
|  | } | 
|  | }; | 
|  | static_assert(Proxies<PtrAddr<void>, void>); | 
|  |  | 
|  | /** | 
|  | * Deduction gude for the common enough case where the context | 
|  | * type and the represented type are equal. | 
|  | */ | 
|  | template<typename T> | 
|  | PtrAddr(T *, ptraddr_t) -> PtrAddr<T>; | 
|  |  | 
|  | /** | 
|  | * Like the above, but with a constant offset on the interpretation of | 
|  | * its addresss fields.  This is useful for building points-to-container | 
|  | * data structures (rather than points-to-member as with the above two). | 
|  | * The container_of and address-taking operations that move back and | 
|  | * forth between container and link member should fuse away with the | 
|  | * offsetting operations herein.  You may prefer this if your common or | 
|  | * fast-paths involve lots of container_of operations. | 
|  | */ | 
|  | template<ptrdiff_t Offset, typename T> | 
|  | class OffsetPtrAddr | 
|  | { | 
|  | CHERI::Capability<void> ctx; | 
|  | ptraddr_t              &ref; | 
|  |  | 
|  | public: | 
|  | using Type = T; | 
|  |  | 
|  | __always_inline OffsetPtrAddr(void *c, ptraddr_t &r) | 
|  | : ctx(c), ref(r) | 
|  | { | 
|  | } | 
|  |  | 
|  | __always_inline operator T *() | 
|  | { | 
|  | auto c      = ctx; | 
|  | c.address() = ref + Offset; | 
|  | return c.cast<T>().get(); | 
|  | } | 
|  |  | 
|  | __always_inline T *operator->() | 
|  | { | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | __always_inline OffsetPtrAddr &operator=(T *p) | 
|  | { | 
|  | ref = CHERI::Capability{p}.address() - Offset; | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | __always_inline OffsetPtrAddr &operator=(OffsetPtrAddr const &p) | 
|  | { | 
|  | ref = p.ref; | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Since the context is used only for bounds, don't bother | 
|  | * implicitly converting both proxies up to T*.  This also probably | 
|  | * saves the optimizer the effort of cancelling the Offset | 
|  | * arithmetic on either side of the comparison. | 
|  | */ | 
|  |  | 
|  | __always_inline bool operator==(OffsetPtrAddr &p) | 
|  | { | 
|  | return ref == p.ref; | 
|  | } | 
|  |  | 
|  | __always_inline auto operator<=>(OffsetPtrAddr &p) | 
|  | { | 
|  | return ref <=> p.ref; | 
|  | } | 
|  | }; | 
|  | static_assert(Proxies<OffsetPtrAddr<8, void>, void>); | 
|  |  | 
|  | } // namespace proxy | 
|  |  | 
|  | } // namespace ds::pointer |