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. 48 template <size_t ALIGNMENT> 49 struct AlignedAlloc { AllocAlignedAlloc50 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 FreeAlignedAlloc89 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