// Copyright 2022 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef TOOLS_UTIL_ALIGNED_ALLOC_H_ #define TOOLS_UTIL_ALIGNED_ALLOC_H_ #ifdef __APPLE__ #include #endif #include #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 // Note that aligned_alloc() is only available at runtime starting from // OSX 10.15, so use malloc() when compiling binaries that must run on older // releases. #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 // for _aligned_malloc() and _aligned_free() #endif #if IMPL_ALIGNED_ALLOC == IMPL_ALIGNED_ALLOC_MALLOC #include "base/logging.h" // For DCHECK() #endif // AlignedAlloc provides Alloc() and Free() methods that can be // used to allocate and release blocks of heap memory aligned with // N bytes. // // The implementation uses std::aligned_alloc() when it is available, // or uses fallbacks otherwise. On Win32, _aligned_malloc() and // _aligned_free() are used, while for older MacOS releases, ::malloc() is // used directly with a small trick. template 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 { // Allocation size must be a multiple of ALIGNMENT DCHECK((size % ALIGNMENT) == 0); // Allocate block and store its address just before just before the // result's address, as in: // ________________________________________ // | | | | // | | real_ptr | | // |____|__________|________________________| // // ^ ^ // real_ptr result // // Note that malloc() guarantees that results are aligned on sizeof(void*) // if the allocation size if larger than sizeof(void*). Hence, only // |ALIGNMENT - sizeof(void*)| extra bytes are required. void* real_block = ::malloc(size + ALIGNMENT - sizeof(void*)); auto addr = reinterpret_cast(real_block) + sizeof(void*); uintptr_t padding = (ALIGNMENT - addr) % ALIGNMENT; addr += padding; reinterpret_cast(addr - sizeof(void*))[0] = real_block; return reinterpret_cast(addr); } #else // IMPL_ALIGNED_ALLOC_CXX17 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*)) { // Read address of real block just before the aligned block. block = *(reinterpret_cast(block) - 1); } ::free(block); } #else // Allocation came from std::aligned_alloc() return std::free(block); #endif } }; #endif // TOOLS_UTIL_ALIGNED_ALLOC_H_