xref: /aosp_15_r20/external/libgav1/src/utils/memory.h (revision 095378508e87ed692bf8dfeb34008b65b3735891)
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