blob: 80c1d8c107a6ecb19903e0cbd78b27e2ee88b300 [file] [log] [blame]
/*
* 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)
#include <malloc.h>
#endif
#include <cerrno>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <memory>
#include <new>
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)
kMaxAlignment = 16, // 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) // MSVC
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)
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)
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<uint16_t>(value);
}
}
struct MallocDeleter {
void operator()(void* ptr) const { free(ptr); }
};
struct AlignedDeleter {
void operator()(void* ptr) const { AlignedFree(ptr); }
};
template <typename T>
using AlignedUniquePtr = std::unique_ptr<T, AlignedDeleter>;
// Allocates aligned memory for an array of |count| elements of type T.
template <typename T>
inline AlignedUniquePtr<T> MakeAlignedUniquePtr(size_t alignment,
size_t count) {
return AlignedUniquePtr<T>(
static_cast<T*>(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<void>(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<void>(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<void>(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<void>(tag);
AlignedFree(ptr);
#endif
}
};
} // namespace libgav1
#endif // LIBGAV1_SRC_UTILS_MEMORY_H_