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