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