Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/common/align.h
7367 views
1
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <[email protected]>
2
// SPDX-License-Identifier: 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
ALWAYS_INLINE constexpr bool IsAligned(T value, unsigned int alignment)
19
{
20
return (value % static_cast<T>(alignment)) == 0;
21
}
22
template<typename T>
23
ALWAYS_INLINE 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
ALWAYS_INLINE 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
ALWAYS_INLINE constexpr bool IsAlignedPow2(T value, unsigned int alignment)
34
{
35
return (value & static_cast<T>(alignment - 1)) == 0;
36
}
37
template<typename T>
38
ALWAYS_INLINE 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
ALWAYS_INLINE 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
ALWAYS_INLINE constexpr bool IsPow2(T value)
49
{
50
return (value & (value - 1)) == 0;
51
}
52
template<typename T>
53
ALWAYS_INLINE 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) >= 2)
62
value |= (value >> 8);
63
if constexpr (sizeof(T) >= 4)
64
value |= (value >> 16);
65
if constexpr (sizeof(T) >= 8)
66
value |= (value >> 32);
67
68
return (value >> 1) + 1;
69
}
70
71
/// NOTE: Undefined for values greater than (1 << BITS-1), i.e. 0x80000000 for 32-bit.
72
template<typename T>
73
ALWAYS_INLINE constexpr T NextPow2(T value)
74
{
75
// https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
76
if (value == static_cast<T>(0))
77
return 0;
78
79
value--;
80
value |= (value >> 1);
81
value |= (value >> 2);
82
value |= (value >> 4);
83
if constexpr (sizeof(T) >= 2)
84
value |= (value >> 8);
85
if constexpr (sizeof(T) >= 4)
86
value |= (value >> 16);
87
if constexpr (sizeof(T) >= 8)
88
value |= (value >> 32);
89
value++;
90
return value;
91
}
92
93
ALWAYS_INLINE void* AlignedMalloc(size_t size, size_t alignment)
94
{
95
#ifdef _MSC_VER
96
return _aligned_malloc(size, alignment);
97
#else
98
// Unaligned sizes are slow on macOS.
99
#ifdef __APPLE__
100
if (IsPow2(alignment))
101
size = (size + alignment - 1) & ~(alignment - 1);
102
#endif
103
void* ret = nullptr;
104
return (posix_memalign(&ret, alignment, size) == 0) ? ret : nullptr;
105
#endif
106
}
107
108
ALWAYS_INLINE void AlignedFree(void* ptr)
109
{
110
#ifdef _MSC_VER
111
_aligned_free(ptr);
112
#else
113
free(ptr);
114
#endif
115
}
116
117
namespace detail {
118
template<class T>
119
struct unique_aligned_ptr_deleter
120
{
121
ALWAYS_INLINE void operator()(T* ptr) const
122
{
123
// Array types - do nothing, elements must be trivially destructible
124
if constexpr (!std::is_array_v<T> && !std::is_trivially_destructible_v<T>)
125
{
126
if (!ptr)
127
return;
128
129
ptr->~T();
130
}
131
132
Common::AlignedFree(const_cast<std::remove_cv_t<T>*>(ptr));
133
}
134
135
// Allow conversion between compatible deleters for derived-to-base conversions
136
template<class U>
137
requires std::is_convertible_v<U*, T*>
138
constexpr unique_aligned_ptr_deleter(const unique_aligned_ptr_deleter<U>&) noexcept
139
{
140
}
141
142
constexpr unique_aligned_ptr_deleter() noexcept = default;
143
};
144
} // namespace detail
145
146
template<class T>
147
using unique_aligned_ptr = std::unique_ptr<T, detail::unique_aligned_ptr_deleter<std::remove_extent_t<T>>>;
148
149
template<class T, class... Args>
150
requires(!std::is_array_v<T>)
151
unique_aligned_ptr<T> make_unique_aligned(size_t alignment, Args&&... args)
152
{
153
unique_aligned_ptr<T> ptr(static_cast<T*>(AlignedMalloc(sizeof(T), alignment)));
154
if (ptr)
155
new (ptr.get()) T(std::forward<Args>(args)...);
156
return ptr;
157
}
158
159
template<class T, class... Args>
160
requires(std::is_unbounded_array_v<T> && std::is_trivially_default_constructible_v<std::remove_extent_t<T>> &&
161
std::is_trivially_destructible_v<std::remove_extent_t<T>>)
162
unique_aligned_ptr<T> make_unique_aligned(size_t alignment, size_t n)
163
{
164
unique_aligned_ptr<T> ptr(
165
static_cast<std::remove_extent_t<T>*>(AlignedMalloc(sizeof(std::remove_extent_t<T>) * n, alignment)));
166
if (ptr)
167
new (ptr.get()) std::remove_extent_t<T>[ n ]();
168
return ptr;
169
}
170
171
template<class T, class... Args>
172
requires(std::is_unbounded_array_v<T> && std::is_trivially_default_constructible_v<std::remove_extent_t<T>> &&
173
std::is_trivially_destructible_v<std::remove_extent_t<T>>)
174
unique_aligned_ptr<T> make_unique_aligned_for_overwrite(size_t alignment, size_t n)
175
{
176
return unique_aligned_ptr<T>(
177
static_cast<std::remove_extent_t<T>*>(AlignedMalloc(sizeof(std::remove_extent_t<T>) * n, alignment)));
178
}
179
180
} // namespace Common
181
182