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)
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) // MSVC
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)
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)
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
104 struct MallocDeleter {
operatorMallocDeleter105 void operator()(void* ptr) const { free(ptr); }
106 };
107
108 struct AlignedDeleter {
operatorAlignedDeleter109 void operator()(void* ptr) const { AlignedFree(ptr); }
110 };
111
112 template <typename T>
113 using AlignedUniquePtr = std::unique_ptr<T, AlignedDeleter>;
114
115 // Allocates aligned memory for an array of |count| elements of type T.
116 template <typename T>
MakeAlignedUniquePtr(size_t alignment,size_t count)117 inline AlignedUniquePtr<T> MakeAlignedUniquePtr(size_t alignment,
118 size_t count) {
119 return AlignedUniquePtr<T>(
120 static_cast<T*>(AlignedAlloc(alignment, count * sizeof(T))));
121 }
122
123 // A base class with custom new and delete operators. The exception-throwing
124 // new operators are deleted. The "new (std::nothrow)" form must be used.
125 //
126 // The new operators return nullptr if the requested size is greater than
127 // 0x40000000 bytes (1 GB). TODO(wtc): Make the maximum allocable memory size
128 // a compile-time configuration macro.
129 //
130 // See https://en.cppreference.com/w/cpp/memory/new/operator_new and
131 // https://en.cppreference.com/w/cpp/memory/new/operator_delete.
132 //
133 // NOTE: The allocation and deallocation functions are static member functions
134 // whether the keyword 'static' is used or not.
135 struct Allocable {
136 // Class-specific allocation functions.
137 static void* operator new(size_t size) = delete;
138 static void* operator new[](size_t size) = delete;
139
140 // Class-specific non-throwing allocation functions
newAllocable141 static void* operator new(size_t size, const std::nothrow_t& tag) noexcept {
142 if (size > 0x40000000) return nullptr;
143 return ::operator new(size, tag);
144 }
145 static void* operator new[](size_t size, const std::nothrow_t& tag) noexcept {
146 if (size > 0x40000000) return nullptr;
147 return ::operator new[](size, tag);
148 }
149
150 // Class-specific deallocation functions.
deleteAllocable151 static void operator delete(void* ptr) noexcept { ::operator delete(ptr); }
152 static void operator delete[](void* ptr) noexcept {
153 ::operator delete[](ptr);
154 }
155
156 // Only called if new (std::nothrow) is used and the constructor throws an
157 // exception.
deleteAllocable158 static void operator delete(void* ptr, const std::nothrow_t& tag) noexcept {
159 ::operator delete(ptr, tag);
160 }
161 // Only called if new[] (std::nothrow) is used and the constructor throws an
162 // exception.
163 static void operator delete[](void* ptr, const std::nothrow_t& tag) noexcept {
164 ::operator delete[](ptr, tag);
165 }
166 };
167
168 // A variant of Allocable that forces allocations to be aligned to
169 // kMaxAlignment bytes. This is intended for use with classes that use
170 // alignas() with this value. C++17 aligned new/delete are used if available,
171 // otherwise we use AlignedAlloc/Free.
172 struct MaxAlignedAllocable {
173 // Class-specific allocation functions.
174 static void* operator new(size_t size) = delete;
175 static void* operator new[](size_t size) = delete;
176
177 // Class-specific non-throwing allocation functions
newMaxAlignedAllocable178 static void* operator new(size_t size, const std::nothrow_t& tag) noexcept {
179 if (size > 0x40000000) return nullptr;
180 #ifdef __cpp_aligned_new
181 return ::operator new(size, std::align_val_t(kMaxAlignment), tag);
182 #else
183 static_cast<void>(tag);
184 return AlignedAlloc(kMaxAlignment, size);
185 #endif
186 }
187 static void* operator new[](size_t size, const std::nothrow_t& tag) noexcept {
188 if (size > 0x40000000) return nullptr;
189 #ifdef __cpp_aligned_new
190 return ::operator new[](size, std::align_val_t(kMaxAlignment), tag);
191 #else
192 static_cast<void>(tag);
193 return AlignedAlloc(kMaxAlignment, size);
194 #endif
195 }
196
197 // Class-specific deallocation functions.
deleteMaxAlignedAllocable198 static void operator delete(void* ptr) noexcept {
199 #ifdef __cpp_aligned_new
200 ::operator delete(ptr, std::align_val_t(kMaxAlignment));
201 #else
202 AlignedFree(ptr);
203 #endif
204 }
205 static void operator delete[](void* ptr) noexcept {
206 #ifdef __cpp_aligned_new
207 ::operator delete[](ptr, std::align_val_t(kMaxAlignment));
208 #else
209 AlignedFree(ptr);
210 #endif
211 }
212
213 // Only called if new (std::nothrow) is used and the constructor throws an
214 // exception.
deleteMaxAlignedAllocable215 static void operator delete(void* ptr, const std::nothrow_t& tag) noexcept {
216 #ifdef __cpp_aligned_new
217 ::operator delete(ptr, std::align_val_t(kMaxAlignment), tag);
218 #else
219 static_cast<void>(tag);
220 AlignedFree(ptr);
221 #endif
222 }
223 // Only called if new[] (std::nothrow) is used and the constructor throws an
224 // exception.
225 static void operator delete[](void* ptr, const std::nothrow_t& tag) noexcept {
226 #ifdef __cpp_aligned_new
227 ::operator delete[](ptr, std::align_val_t(kMaxAlignment), tag);
228 #else
229 static_cast<void>(tag);
230 AlignedFree(ptr);
231 #endif
232 }
233 };
234
235 } // namespace libgav1
236
237 #endif // LIBGAV1_SRC_UTILS_MEMORY_H_
238