xref: /third_party/gn/src/util/aligned_alloc.h (revision 6d528ed9)
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