1 /*
2 * Copyright 2019 The libgav1 Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #ifndef LIBGAV1_SRC_UTILS_MEMORY_H_
18 #define LIBGAV1_SRC_UTILS_MEMORY_H_
19
20 #if defined(__ANDROID__) || defined(_MSC_VER) || defined(__MINGW32__)
21 #include <malloc.h>
22 #endif
23
24 #include <cerrno>
25 #include <cstddef>
26 #include <cstdint>
27 #include <cstdlib>
28 #include <cstring>
29 #include <memory>
30 #include <new>
31
32 namespace libgav1 {
33
34 enum {
35 // The byte alignment required for buffers used with SIMD code to be read or
36 // written with aligned operations.
37 #if defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || \
38 defined(_M_X64)
39 kMaxAlignment = 32, // extended alignment is safe on x86.
40 #else
41 kMaxAlignment = alignof(max_align_t),
42 #endif
43 };
44
45 // AlignedAlloc, AlignedFree
46 //
47 // void* AlignedAlloc(size_t alignment, size_t size);
48 // Allocate aligned memory.
49 // |alignment| must be a power of 2.
50 // Unlike posix_memalign(), |alignment| may be smaller than sizeof(void*).
51 // Unlike aligned_alloc(), |size| does not need to be a multiple of
52 // |alignment|.
53 // The returned pointer should be freed by AlignedFree().
54 //
55 // void AlignedFree(void* aligned_memory);
56 // Free aligned memory.
57
58 #if defined(_MSC_VER) || defined(__MINGW32__)
59
AlignedAlloc(size_t alignment,size_t size)60 inline void* AlignedAlloc(size_t alignment, size_t size) {
61 return _aligned_malloc(size, alignment);
62 }
63
AlignedFree(void * aligned_memory)64 inline void AlignedFree(void* aligned_memory) { _aligned_free(aligned_memory); }
65
66 #else // !(defined(_MSC_VER) || defined(__MINGW32__))
67
AlignedAlloc(size_t alignment,size_t size)68 inline void* AlignedAlloc(size_t alignment, size_t size) {
69 #if defined(__ANDROID__)
70 // Although posix_memalign() was introduced in Android API level 17, it is
71 // more convenient to use memalign(). Unlike glibc, Android does not consider
72 // memalign() an obsolete function.
73 return memalign(alignment, size);
74 #else // !defined(__ANDROID__)
75 void* ptr = nullptr;
76 // posix_memalign requires that the requested alignment be at least
77 // sizeof(void*). In this case, fall back on malloc which should return
78 // memory aligned to at least the size of a pointer.
79 const size_t required_alignment = sizeof(void*);
80 if (alignment < required_alignment) return malloc(size);
81 const int error = posix_memalign(&ptr, alignment, size);
82 if (error != 0) {
83 errno = error;
84 return nullptr;
85 }
86 return ptr;
87 #endif // defined(__ANDROID__)
88 }
89
AlignedFree(void * aligned_memory)90 inline void AlignedFree(void* aligned_memory) { free(aligned_memory); }
91
92 #endif // defined(_MSC_VER) || defined(__MINGW32__)
93
Memset(uint8_t * const dst,int value,size_t count)94 inline void Memset(uint8_t* const dst, int value, size_t count) {
95 memset(dst, value, count);
96 }
97
Memset(uint16_t * const dst,int value,size_t count)98 inline void Memset(uint16_t* const dst, int value, size_t count) {
99 for (size_t i = 0; i < count; ++i) {
100 dst[i] = static_cast<uint16_t>(value);
101 }
102 }
103
Memset(int16_t * const dst,int value,size_t count)104 inline void Memset(int16_t* const dst, int value, size_t count) {
105 for (size_t i = 0; i < count; ++i) {
106 dst[i] = static_cast<int16_t>(value);
107 }
108 }
109
110 struct MallocDeleter {
operatorMallocDeleter111 void operator()(void* ptr) const { free(ptr); }
112 };
113
114 struct AlignedDeleter {
operatorAlignedDeleter115 void operator()(void* ptr) const { AlignedFree(ptr); }
116 };
117
118 template <typename T>
119 using AlignedUniquePtr = std::unique_ptr<T, AlignedDeleter>;
120
121 // Allocates aligned memory for an array of |count| elements of type T.
122 template <typename T>
MakeAlignedUniquePtr(size_t alignment,size_t count)123 inline AlignedUniquePtr<T> MakeAlignedUniquePtr(size_t alignment,
124 size_t count) {
125 return AlignedUniquePtr<T>(
126 static_cast<T*>(AlignedAlloc(alignment, count * sizeof(T))));
127 }
128
129 // A base class with custom new and delete operators. The exception-throwing
130 // new operators are deleted. The "new (std::nothrow)" form must be used.
131 //
132 // The new operators return nullptr if the requested size is greater than
133 // 0x40000000 bytes (1 GB). TODO(wtc): Make the maximum allocable memory size
134 // a compile-time configuration macro.
135 //
136 // See https://en.cppreference.com/w/cpp/memory/new/operator_new and
137 // https://en.cppreference.com/w/cpp/memory/new/operator_delete.
138 //
139 // NOTE: The allocation and deallocation functions are static member functions
140 // whether the keyword 'static' is used or not.
141 struct Allocable {
142 // Class-specific allocation functions.
143 static void* operator new(size_t size) = delete;
144 static void* operator new[](size_t size) = delete;
145
146 // Class-specific non-throwing allocation functions
newAllocable147 static void* operator new(size_t size, const std::nothrow_t& tag) noexcept {
148 if (size > 0x40000000) return nullptr;
149 return ::operator new(size, tag);
150 }
151 static void* operator new[](size_t size, const std::nothrow_t& tag) noexcept {
152 if (size > 0x40000000) return nullptr;
153 return ::operator new[](size, tag);
154 }
155
156 // Class-specific deallocation functions.
deleteAllocable157 static void operator delete(void* ptr) noexcept { ::operator delete(ptr); }
158 static void operator delete[](void* ptr) noexcept {
159 ::operator delete[](ptr);
160 }
161
162 // Only called if new (std::nothrow) is used and the constructor throws an
163 // exception.
deleteAllocable164 static void operator delete(void* ptr, const std::nothrow_t& tag) noexcept {
165 ::operator delete(ptr, tag);
166 }
167 // Only called if new[] (std::nothrow) is used and the constructor throws an
168 // exception.
169 static void operator delete[](void* ptr, const std::nothrow_t& tag) noexcept {
170 ::operator delete[](ptr, tag);
171 }
172 };
173
174 // A variant of Allocable that forces allocations to be aligned to
175 // kMaxAlignment bytes. This is intended for use with classes that use
176 // alignas() with this value. C++17 aligned new/delete are used if available,
177 // otherwise we use AlignedAlloc/Free.
178 struct MaxAlignedAllocable {
179 // Class-specific allocation functions.
180 static void* operator new(size_t size) = delete;
181 static void* operator new[](size_t size) = delete;
182
183 // Class-specific non-throwing allocation functions
newMaxAlignedAllocable184 static void* operator new(size_t size, const std::nothrow_t& tag) noexcept {
185 if (size > 0x40000000) return nullptr;
186 #ifdef __cpp_aligned_new
187 return ::operator new(size, std::align_val_t(kMaxAlignment), tag);
188 #else
189 static_cast<void>(tag);
190 return AlignedAlloc(kMaxAlignment, size);
191 #endif
192 }
193 static void* operator new[](size_t size, const std::nothrow_t& tag) noexcept {
194 if (size > 0x40000000) return nullptr;
195 #ifdef __cpp_aligned_new
196 return ::operator new[](size, std::align_val_t(kMaxAlignment), tag);
197 #else
198 static_cast<void>(tag);
199 return AlignedAlloc(kMaxAlignment, size);
200 #endif
201 }
202
203 // Class-specific deallocation functions.
deleteMaxAlignedAllocable204 static void operator delete(void* ptr) noexcept {
205 #ifdef __cpp_aligned_new
206 ::operator delete(ptr, std::align_val_t(kMaxAlignment));
207 #else
208 AlignedFree(ptr);
209 #endif
210 }
211 static void operator delete[](void* ptr) noexcept {
212 #ifdef __cpp_aligned_new
213 ::operator delete[](ptr, std::align_val_t(kMaxAlignment));
214 #else
215 AlignedFree(ptr);
216 #endif
217 }
218
219 // Only called if new (std::nothrow) is used and the constructor throws an
220 // exception.
deleteMaxAlignedAllocable221 static void operator delete(void* ptr, const std::nothrow_t& tag) noexcept {
222 #ifdef __cpp_aligned_new
223 ::operator delete(ptr, std::align_val_t(kMaxAlignment), tag);
224 #else
225 static_cast<void>(tag);
226 AlignedFree(ptr);
227 #endif
228 }
229 // Only called if new[] (std::nothrow) is used and the constructor throws an
230 // exception.
231 static void operator delete[](void* ptr, const std::nothrow_t& tag) noexcept {
232 #ifdef __cpp_aligned_new
233 ::operator delete[](ptr, std::align_val_t(kMaxAlignment), tag);
234 #else
235 static_cast<void>(tag);
236 AlignedFree(ptr);
237 #endif
238 }
239 };
240
241 } // namespace libgav1
242
243 #endif // LIBGAV1_SRC_UTILS_MEMORY_H_
244