align.h (4228B)
1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> 2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) 3 4 #pragma once 5 6 #include "types.h" 7 8 #include <cstdlib> 9 #include <memory> 10 #include <type_traits> 11 12 #ifdef _MSC_VER 13 #include <malloc.h> 14 #endif 15 16 namespace Common { 17 template<typename T> 18 constexpr bool IsAligned(T value, unsigned int alignment) 19 { 20 return (value % static_cast<T>(alignment)) == 0; 21 } 22 template<typename T> 23 constexpr T AlignUp(T value, unsigned int alignment) 24 { 25 return (value + static_cast<T>(alignment - 1)) / static_cast<T>(alignment) * static_cast<T>(alignment); 26 } 27 template<typename T> 28 constexpr T AlignDown(T value, unsigned int alignment) 29 { 30 return value / static_cast<T>(alignment) * static_cast<T>(alignment); 31 } 32 template<typename T> 33 constexpr bool IsAlignedPow2(T value, unsigned int alignment) 34 { 35 return (value & static_cast<T>(alignment - 1)) == 0; 36 } 37 template<typename T> 38 constexpr T AlignUpPow2(T value, unsigned int alignment) 39 { 40 return (value + static_cast<T>(alignment - 1)) & static_cast<T>(~static_cast<T>(alignment - 1)); 41 } 42 template<typename T> 43 constexpr T AlignDownPow2(T value, unsigned int alignment) 44 { 45 return value & static_cast<T>(~static_cast<T>(alignment - 1)); 46 } 47 template<typename T> 48 constexpr bool IsPow2(T value) 49 { 50 return (value & (value - 1)) == 0; 51 } 52 template<typename T> 53 constexpr T PreviousPow2(T value) 54 { 55 if (value == static_cast<T>(0)) 56 return 0; 57 58 value |= (value >> 1); 59 value |= (value >> 2); 60 value |= (value >> 4); 61 if constexpr (sizeof(T) >= 16) 62 value |= (value >> 8); 63 if constexpr (sizeof(T) >= 32) 64 value |= (value >> 16); 65 if constexpr (sizeof(T) >= 64) 66 value |= (value >> 32); 67 return value - (value >> 1); 68 } 69 template<typename T> 70 constexpr T NextPow2(T value) 71 { 72 // https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 73 if (value == static_cast<T>(0)) 74 return 0; 75 76 value--; 77 value |= (value >> 1); 78 value |= (value >> 2); 79 value |= (value >> 4); 80 if constexpr (sizeof(T) >= 16) 81 value |= (value >> 8); 82 if constexpr (sizeof(T) >= 32) 83 value |= (value >> 16); 84 if constexpr (sizeof(T) >= 64) 85 value |= (value >> 32); 86 value++; 87 return value; 88 } 89 90 ALWAYS_INLINE static void* AlignedMalloc(size_t size, size_t alignment) 91 { 92 #ifdef _MSC_VER 93 return _aligned_malloc(size, alignment); 94 #else 95 // Unaligned sizes are slow on macOS. 96 #ifdef __APPLE__ 97 if (IsPow2(alignment)) 98 size = (size + alignment - 1) & ~(alignment - 1); 99 #endif 100 void* ret = nullptr; 101 return (posix_memalign(&ret, alignment, size) == 0) ? ret : nullptr; 102 #endif 103 } 104 105 ALWAYS_INLINE static void AlignedFree(void* ptr) 106 { 107 #ifdef _MSC_VER 108 _aligned_free(ptr); 109 #else 110 free(ptr); 111 #endif 112 } 113 114 namespace detail { 115 template<class T> 116 struct unique_aligned_ptr_deleter 117 { 118 ALWAYS_INLINE void operator()(T* ptr) { Common::AlignedFree(ptr); } 119 }; 120 121 template<class> 122 constexpr bool is_unbounded_array_v = false; 123 template<class T> 124 constexpr bool is_unbounded_array_v<T[]> = true; 125 126 template<class> 127 constexpr bool is_bounded_array_v = false; 128 template<class T, std::size_t N> 129 constexpr bool is_bounded_array_v<T[N]> = true; 130 } // namespace detail 131 132 template<class T> 133 using unique_aligned_ptr = std::unique_ptr<T, detail::unique_aligned_ptr_deleter<std::remove_extent_t<T>>>; 134 135 template<class T, class... Args> 136 requires(std::is_unbounded_array_v<T>, std::is_trivially_default_constructible_v<std::remove_extent_t<T>>, 137 std::is_trivially_destructible_v<std::remove_extent_t<T>>) 138 unique_aligned_ptr<T> make_unique_aligned(size_t alignment, size_t n) 139 { 140 unique_aligned_ptr<T> ptr( 141 static_cast<std::remove_extent_t<T>*>(AlignedMalloc(sizeof(std::remove_extent_t<T>) * n, alignment))); 142 if (ptr) 143 new (ptr.get()) std::remove_extent_t<T>[ n ](); 144 return ptr; 145 } 146 147 template<class T, class... Args> 148 requires(std::is_unbounded_array_v<T>, std::is_trivially_default_constructible_v<std::remove_extent_t<T>>, 149 std::is_trivially_destructible_v<std::remove_extent_t<T>>) 150 unique_aligned_ptr<T> make_unique_aligned_for_overwrite(size_t alignment, size_t n) 151 { 152 return unique_aligned_ptr<T>( 153 static_cast<std::remove_extent_t<T>*>(AlignedMalloc(sizeof(std::remove_extent_t<T>) * n, alignment))); 154 } 155 156 } // namespace Common