// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin // SPDX-License-Identifier: CC-BY-NC-ND-4.0 #pragma once #include "types.h" #include #include #include #ifdef _MSC_VER #include #endif namespace Common { template constexpr bool IsAligned(T value, unsigned int alignment) { return (value % static_cast(alignment)) == 0; } template constexpr T AlignUp(T value, unsigned int alignment) { return (value + static_cast(alignment - 1)) / static_cast(alignment) * static_cast(alignment); } template constexpr T AlignDown(T value, unsigned int alignment) { return value / static_cast(alignment) * static_cast(alignment); } template constexpr bool IsAlignedPow2(T value, unsigned int alignment) { return (value & static_cast(alignment - 1)) == 0; } template constexpr T AlignUpPow2(T value, unsigned int alignment) { return (value + static_cast(alignment - 1)) & static_cast(~static_cast(alignment - 1)); } template constexpr T AlignDownPow2(T value, unsigned int alignment) { return value & static_cast(~static_cast(alignment - 1)); } template constexpr bool IsPow2(T value) { return (value & (value - 1)) == 0; } template constexpr T PreviousPow2(T value) { if (value == static_cast(0)) return 0; value |= (value >> 1); value |= (value >> 2); value |= (value >> 4); if constexpr (sizeof(T) >= 16) value |= (value >> 8); if constexpr (sizeof(T) >= 32) value |= (value >> 16); if constexpr (sizeof(T) >= 64) value |= (value >> 32); return value - (value >> 1); } template constexpr T NextPow2(T value) { // https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 if (value == static_cast(0)) return 0; value--; value |= (value >> 1); value |= (value >> 2); value |= (value >> 4); if constexpr (sizeof(T) >= 16) value |= (value >> 8); if constexpr (sizeof(T) >= 32) value |= (value >> 16); if constexpr (sizeof(T) >= 64) value |= (value >> 32); value++; return value; } ALWAYS_INLINE static void* AlignedMalloc(size_t size, size_t alignment) { #ifdef _MSC_VER return _aligned_malloc(size, alignment); #else // Unaligned sizes are slow on macOS. #ifdef __APPLE__ if (IsPow2(alignment)) size = (size + alignment - 1) & ~(alignment - 1); #endif void* ret = nullptr; return (posix_memalign(&ret, alignment, size) == 0) ? ret : nullptr; #endif } ALWAYS_INLINE static void AlignedFree(void* ptr) { #ifdef _MSC_VER _aligned_free(ptr); #else free(ptr); #endif } namespace detail { template struct unique_aligned_ptr_deleter { ALWAYS_INLINE void operator()(T* ptr) { Common::AlignedFree(ptr); } }; template constexpr bool is_unbounded_array_v = false; template constexpr bool is_unbounded_array_v = true; template constexpr bool is_bounded_array_v = false; template constexpr bool is_bounded_array_v = true; } // namespace detail template using unique_aligned_ptr = std::unique_ptr>>; template requires(std::is_unbounded_array_v, std::is_trivially_default_constructible_v>, std::is_trivially_destructible_v>) unique_aligned_ptr make_unique_aligned(size_t alignment, size_t n) { unique_aligned_ptr ptr( static_cast*>(AlignedMalloc(sizeof(std::remove_extent_t) * n, alignment))); if (ptr) new (ptr.get()) std::remove_extent_t[ n ](); return ptr; } template requires(std::is_unbounded_array_v, std::is_trivially_default_constructible_v>, std::is_trivially_destructible_v>) unique_aligned_ptr make_unique_aligned_for_overwrite(size_t alignment, size_t n) { return unique_aligned_ptr( static_cast*>(AlignedMalloc(sizeof(std::remove_extent_t) * n, alignment))); } } // namespace Common