• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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