#ifndef TOOLS_UTIL_ALIGNED_ALLOC_H_
#define TOOLS_UTIL_ALIGNED_ALLOC_H_
#ifdef __APPLE__
#include <Availability.h>
#endif
#include <cstdlib>
#define IMPL_ALIGNED_ALLOC_CXX17 1
#define IMPL_ALIGNED_ALLOC_WIN32 2
#define IMPL_ALIGNED_ALLOC_MALLOC 3
#ifndef IMPL_ALIGNED_ALLOC
#ifdef _WIN32
#define IMPL_ALIGNED_ALLOC IMPL_ALIGNED_ALLOC_WIN32
#elif defined(__APPLE__) && defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && \
__MAC_OS_X_VERSION_MIN_REQUIRED < 101500
#define IMPL_ALIGNED_ALLOC IMPL_ALIGNED_ALLOC_MALLOC
#else
#define IMPL_ALIGNED_ALLOC IMPL_ALIGNED_ALLOC_CXX17
#endif
#endif
#if IMPL_ALIGNED_ALLOC == IMPL_ALIGNED_ALLOC_WIN32
#include <malloc.h>
#endif
#if IMPL_ALIGNED_ALLOC == IMPL_ALIGNED_ALLOC_MALLOC
#include "base/logging.h"
#endif
template <size_t ALIGNMENT>
struct AlignedAlloc {
static void* Alloc(size_t size) {
static_assert((ALIGNMENT & (ALIGNMENT - 1)) == 0,
"ALIGNMENT must be a power of 2");
#if IMPL_ALIGNED_ALLOC == IMPL_ALIGNED_ALLOC_WIN32
return _aligned_malloc(size, ALIGNMENT);
#elif IMPL_ALIGNED_ALLOC == IMPL_ALIGNED_ALLOC_MALLOC
if (ALIGNMENT <= sizeof(void*)) {
return ::malloc(size);
} else if (size == 0) {
return nullptr;
} else {
DCHECK((size % ALIGNMENT) == 0);
void* real_block = ::malloc(size + ALIGNMENT - sizeof(void*));
auto addr = reinterpret_cast<uintptr_t>(real_block) + sizeof(void*);
uintptr_t padding = (ALIGNMENT - addr) % ALIGNMENT;
addr += padding;
reinterpret_cast<void**>(addr - sizeof(void*))[0] = real_block;
return reinterpret_cast<void*>(addr);
}
#else
return std::aligned_alloc(ALIGNMENT, size);
#endif
}
static void Free(void* block) {
#if IMPL_ALIGNED_ALLOC == IMPL_ALIGNED_ALLOC_WIN32
_aligned_free(block);
#elif IMPL_ALIGNED_ALLOC == IMPL_ALIGNED_ALLOC_MALLOC
if (ALIGNMENT <= sizeof(void*)) {
::free(block);
} else if (block) {
if (ALIGNMENT > sizeof(void*)) {
block = *(reinterpret_cast<void**>(block) - 1);
}
::free(block);
}
#else
return std::free(block);
#endif
}
};
#endif