• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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