#pragma once #include "../pal/pal_consts.h" #include "bits.h" #include namespace snmalloc { /** * The type used for an address. Currently, all addresses are assumed to be * provenance-carrying values and so it is possible to cast back from the * result of arithmetic on an address_t. Eventually, this will want to be * separated into two types, one for raw addresses and one for addresses that * can be cast back to pointers. */ using address_t = uintptr_t; /** * Perform pointer arithmetic and return the adjusted pointer. */ template inline T* pointer_offset(T* base, size_t diff) { return reinterpret_cast(reinterpret_cast(base) + diff); } /** * Perform pointer arithmetic and return the adjusted pointer. */ template inline T* pointer_offset_signed(T* base, ptrdiff_t diff) { return reinterpret_cast(reinterpret_cast(base) + diff); } /** * Cast from a pointer type to an address. */ template inline address_t address_cast(T* ptr) { return reinterpret_cast(ptr); } /** * Test if a pointer is aligned to a given size, which must be a power of * two. */ template static inline bool is_aligned_block(void* p, size_t size) { static_assert(bits::next_pow2_const(alignment) == alignment); return ((static_cast(address_cast(p)) | size) & (alignment - 1)) == 0; } /** * Align a pointer down to a statically specified granularity, which must be a * power of two. */ template SNMALLOC_FAST_PATH T* pointer_align_down(void* p) { static_assert(alignment > 0); static_assert(bits::next_pow2_const(alignment) == alignment); if constexpr (alignment == 1) return static_cast(p); else { #if __has_builtin(__builtin_align_down) return static_cast(__builtin_align_down(p, alignment)); #else return reinterpret_cast( bits::align_down(reinterpret_cast(p), alignment)); #endif } } /** * Align a pointer up to a statically specified granularity, which must be a * power of two. */ template inline T* pointer_align_up(void* p) { static_assert(alignment > 0); static_assert(bits::next_pow2_const(alignment) == alignment); if constexpr (alignment == 1) return static_cast(p); else { #if __has_builtin(__builtin_align_up) return static_cast(__builtin_align_up(p, alignment)); #else return reinterpret_cast( bits::align_up(reinterpret_cast(p), alignment)); #endif } } /** * Align a pointer down to a dynamically specified granularity, which must be * a power of two. */ template SNMALLOC_FAST_PATH T* pointer_align_down(void* p, size_t alignment) { SNMALLOC_ASSERT(alignment > 0); SNMALLOC_ASSERT(bits::next_pow2(alignment) == alignment); #if __has_builtin(__builtin_align_down) return static_cast(__builtin_align_down(p, alignment)); #else return reinterpret_cast( bits::align_down(reinterpret_cast(p), alignment)); #endif } /** * Align a pointer up to a dynamically specified granularity, which must * be a power of two. */ template inline T* pointer_align_up(void* p, size_t alignment) { SNMALLOC_ASSERT(alignment > 0); SNMALLOC_ASSERT(bits::next_pow2(alignment) == alignment); #if __has_builtin(__builtin_align_up) return static_cast(__builtin_align_up(p, alignment)); #else return reinterpret_cast( bits::align_up(reinterpret_cast(p), alignment)); #endif } /** * Compute the difference in pointers in units of char. base is * expected to point to the base of some (sub)allocation into which cursor * points; would-be negative answers trip an assertion in debug builds. */ inline size_t pointer_diff(void* base, void* cursor) { SNMALLOC_ASSERT(cursor >= base); return static_cast( static_cast(cursor) - static_cast(base)); } /** * Compute the difference in pointers in units of char. This can be used * across allocations. */ inline ptrdiff_t pointer_diff_signed(void* base, void* cursor) { return static_cast( static_cast(cursor) - static_cast(base)); } } // namespace snmalloc