/* * Copyright 2019 The libgav1 Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef LIBGAV1_SRC_UTILS_MEMORY_H_ #define LIBGAV1_SRC_UTILS_MEMORY_H_ #if defined(__ANDROID__) || defined(_MSC_VER) || defined(__MINGW32__) #include #endif #include #include #include #include #include #include #include namespace libgav1 { enum { // The byte alignment required for buffers used with SIMD code to be read or // written with aligned operations. #if defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || \ defined(_M_X64) kMaxAlignment = 32, // extended alignment is safe on x86. #else kMaxAlignment = alignof(max_align_t), #endif }; // AlignedAlloc, AlignedFree // // void* AlignedAlloc(size_t alignment, size_t size); // Allocate aligned memory. // |alignment| must be a power of 2. // Unlike posix_memalign(), |alignment| may be smaller than sizeof(void*). // Unlike aligned_alloc(), |size| does not need to be a multiple of // |alignment|. // The returned pointer should be freed by AlignedFree(). // // void AlignedFree(void* aligned_memory); // Free aligned memory. #if defined(_MSC_VER) || defined(__MINGW32__) inline void* AlignedAlloc(size_t alignment, size_t size) { return _aligned_malloc(size, alignment); } inline void AlignedFree(void* aligned_memory) { _aligned_free(aligned_memory); } #else // !(defined(_MSC_VER) || defined(__MINGW32__)) inline void* AlignedAlloc(size_t alignment, size_t size) { #if defined(__ANDROID__) // Although posix_memalign() was introduced in Android API level 17, it is // more convenient to use memalign(). Unlike glibc, Android does not consider // memalign() an obsolete function. return memalign(alignment, size); #else // !defined(__ANDROID__) void* ptr = nullptr; // posix_memalign requires that the requested alignment be at least // sizeof(void*). In this case, fall back on malloc which should return // memory aligned to at least the size of a pointer. const size_t required_alignment = sizeof(void*); if (alignment < required_alignment) return malloc(size); const int error = posix_memalign(&ptr, alignment, size); if (error != 0) { errno = error; return nullptr; } return ptr; #endif // defined(__ANDROID__) } inline void AlignedFree(void* aligned_memory) { free(aligned_memory); } #endif // defined(_MSC_VER) || defined(__MINGW32__) inline void Memset(uint8_t* const dst, int value, size_t count) { memset(dst, value, count); } inline void Memset(uint16_t* const dst, int value, size_t count) { for (size_t i = 0; i < count; ++i) { dst[i] = static_cast(value); } } inline void Memset(int16_t* const dst, int value, size_t count) { for (size_t i = 0; i < count; ++i) { dst[i] = static_cast(value); } } struct MallocDeleter { void operator()(void* ptr) const { free(ptr); } }; struct AlignedDeleter { void operator()(void* ptr) const { AlignedFree(ptr); } }; template using AlignedUniquePtr = std::unique_ptr; // Allocates aligned memory for an array of |count| elements of type T. template inline AlignedUniquePtr MakeAlignedUniquePtr(size_t alignment, size_t count) { return AlignedUniquePtr( static_cast(AlignedAlloc(alignment, count * sizeof(T)))); } // A base class with custom new and delete operators. The exception-throwing // new operators are deleted. The "new (std::nothrow)" form must be used. // // The new operators return nullptr if the requested size is greater than // 0x40000000 bytes (1 GB). TODO(wtc): Make the maximum allocable memory size // a compile-time configuration macro. // // See https://en.cppreference.com/w/cpp/memory/new/operator_new and // https://en.cppreference.com/w/cpp/memory/new/operator_delete. // // NOTE: The allocation and deallocation functions are static member functions // whether the keyword 'static' is used or not. struct Allocable { // Class-specific allocation functions. static void* operator new(size_t size) = delete; static void* operator new[](size_t size) = delete; // Class-specific non-throwing allocation functions static void* operator new(size_t size, const std::nothrow_t& tag) noexcept { if (size > 0x40000000) return nullptr; return ::operator new(size, tag); } static void* operator new[](size_t size, const std::nothrow_t& tag) noexcept { if (size > 0x40000000) return nullptr; return ::operator new[](size, tag); } // Class-specific deallocation functions. static void operator delete(void* ptr) noexcept { ::operator delete(ptr); } static void operator delete[](void* ptr) noexcept { ::operator delete[](ptr); } // Only called if new (std::nothrow) is used and the constructor throws an // exception. static void operator delete(void* ptr, const std::nothrow_t& tag) noexcept { ::operator delete(ptr, tag); } // Only called if new[] (std::nothrow) is used and the constructor throws an // exception. static void operator delete[](void* ptr, const std::nothrow_t& tag) noexcept { ::operator delete[](ptr, tag); } }; // A variant of Allocable that forces allocations to be aligned to // kMaxAlignment bytes. This is intended for use with classes that use // alignas() with this value. C++17 aligned new/delete are used if available, // otherwise we use AlignedAlloc/Free. struct MaxAlignedAllocable { // Class-specific allocation functions. static void* operator new(size_t size) = delete; static void* operator new[](size_t size) = delete; // Class-specific non-throwing allocation functions static void* operator new(size_t size, const std::nothrow_t& tag) noexcept { if (size > 0x40000000) return nullptr; #ifdef __cpp_aligned_new return ::operator new(size, std::align_val_t(kMaxAlignment), tag); #else static_cast(tag); return AlignedAlloc(kMaxAlignment, size); #endif } static void* operator new[](size_t size, const std::nothrow_t& tag) noexcept { if (size > 0x40000000) return nullptr; #ifdef __cpp_aligned_new return ::operator new[](size, std::align_val_t(kMaxAlignment), tag); #else static_cast(tag); return AlignedAlloc(kMaxAlignment, size); #endif } // Class-specific deallocation functions. static void operator delete(void* ptr) noexcept { #ifdef __cpp_aligned_new ::operator delete(ptr, std::align_val_t(kMaxAlignment)); #else AlignedFree(ptr); #endif } static void operator delete[](void* ptr) noexcept { #ifdef __cpp_aligned_new ::operator delete[](ptr, std::align_val_t(kMaxAlignment)); #else AlignedFree(ptr); #endif } // Only called if new (std::nothrow) is used and the constructor throws an // exception. static void operator delete(void* ptr, const std::nothrow_t& tag) noexcept { #ifdef __cpp_aligned_new ::operator delete(ptr, std::align_val_t(kMaxAlignment), tag); #else static_cast(tag); AlignedFree(ptr); #endif } // Only called if new[] (std::nothrow) is used and the constructor throws an // exception. static void operator delete[](void* ptr, const std::nothrow_t& tag) noexcept { #ifdef __cpp_aligned_new ::operator delete[](ptr, std::align_val_t(kMaxAlignment), tag); #else static_cast(tag); AlignedFree(ptr); #endif } }; } // namespace libgav1 #endif // LIBGAV1_SRC_UTILS_MEMORY_H_