1// Copyright 2022 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#ifndef TOOLS_UTIL_ALIGNED_ALLOC_H_ 6#define TOOLS_UTIL_ALIGNED_ALLOC_H_ 7 8#ifdef __APPLE__ 9#include <Availability.h> 10#endif 11 12#include <cstdlib> 13 14#define IMPL_ALIGNED_ALLOC_CXX17 1 15#define IMPL_ALIGNED_ALLOC_WIN32 2 16#define IMPL_ALIGNED_ALLOC_MALLOC 3 17 18#ifndef IMPL_ALIGNED_ALLOC 19#ifdef _WIN32 20#define IMPL_ALIGNED_ALLOC IMPL_ALIGNED_ALLOC_WIN32 21#elif defined(__APPLE__) && defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && \ 22 __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 23// Note that aligned_alloc() is only available at runtime starting from 24// OSX 10.15, so use malloc() when compiling binaries that must run on older 25// releases. 26#define IMPL_ALIGNED_ALLOC IMPL_ALIGNED_ALLOC_MALLOC 27#else 28#define IMPL_ALIGNED_ALLOC IMPL_ALIGNED_ALLOC_CXX17 29#endif 30#endif 31 32#if IMPL_ALIGNED_ALLOC == IMPL_ALIGNED_ALLOC_WIN32 33#include <malloc.h> // for _aligned_malloc() and _aligned_free() 34#endif 35 36#if IMPL_ALIGNED_ALLOC == IMPL_ALIGNED_ALLOC_MALLOC 37#include "base/logging.h" // For DCHECK() 38#endif 39 40// AlignedAlloc<N> provides Alloc() and Free() methods that can be 41// used to allocate and release blocks of heap memory aligned with 42// N bytes. 43// 44// The implementation uses std::aligned_alloc() when it is available, 45// or uses fallbacks otherwise. On Win32, _aligned_malloc() and 46// _aligned_free() are used, while for older MacOS releases, ::malloc() is 47// used directly with a small trick. 48template <size_t ALIGNMENT> 49struct AlignedAlloc { 50 static void* Alloc(size_t size) { 51 static_assert((ALIGNMENT & (ALIGNMENT - 1)) == 0, 52 "ALIGNMENT must be a power of 2"); 53#if IMPL_ALIGNED_ALLOC == IMPL_ALIGNED_ALLOC_WIN32 54 return _aligned_malloc(size, ALIGNMENT); 55#elif IMPL_ALIGNED_ALLOC == IMPL_ALIGNED_ALLOC_MALLOC 56 if (ALIGNMENT <= sizeof(void*)) { 57 return ::malloc(size); 58 } else if (size == 0) { 59 return nullptr; 60 } else { 61 // Allocation size must be a multiple of ALIGNMENT 62 DCHECK((size % ALIGNMENT) == 0); 63 64 // Allocate block and store its address just before just before the 65 // result's address, as in: 66 // ________________________________________ 67 // | | | | 68 // | | real_ptr | | 69 // |____|__________|________________________| 70 // 71 // ^ ^ 72 // real_ptr result 73 // 74 // Note that malloc() guarantees that results are aligned on sizeof(void*) 75 // if the allocation size if larger than sizeof(void*). Hence, only 76 // |ALIGNMENT - sizeof(void*)| extra bytes are required. 77 void* real_block = ::malloc(size + ALIGNMENT - sizeof(void*)); 78 auto addr = reinterpret_cast<uintptr_t>(real_block) + sizeof(void*); 79 uintptr_t padding = (ALIGNMENT - addr) % ALIGNMENT; 80 addr += padding; 81 reinterpret_cast<void**>(addr - sizeof(void*))[0] = real_block; 82 return reinterpret_cast<void*>(addr); 83 } 84#else // IMPL_ALIGNED_ALLOC_CXX17 85 return std::aligned_alloc(ALIGNMENT, size); 86#endif 87 } 88 89 static void Free(void* block) { 90#if IMPL_ALIGNED_ALLOC == IMPL_ALIGNED_ALLOC_WIN32 91 _aligned_free(block); 92#elif IMPL_ALIGNED_ALLOC == IMPL_ALIGNED_ALLOC_MALLOC 93 if (ALIGNMENT <= sizeof(void*)) { 94 ::free(block); 95 } else if (block) { 96 if (ALIGNMENT > sizeof(void*)) { 97 // Read address of real block just before the aligned block. 98 block = *(reinterpret_cast<void**>(block) - 1); 99 } 100 ::free(block); 101 } 102#else 103 // Allocation came from std::aligned_alloc() 104 return std::free(block); 105#endif 106 } 107}; 108 109#endif // TOOLS_UTIL_ALIGNED_ALLOC_H_ 110