1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170

base / memory / aligned_memory.h [blame]

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_MEMORY_ALIGNED_MEMORY_H_
#define BASE_MEMORY_ALIGNED_MEMORY_H_

#include <stddef.h>
#include <stdint.h>

#include <algorithm>
#include <bit>
#include <ostream>

#include "base/base_export.h"
#include "base/check.h"
#include "base/containers/heap_array.h"
#include "base/containers/span.h"
#include "build/build_config.h"

#if defined(COMPILER_MSVC)
#include <malloc.h>
#else
#include <stdlib.h>
#endif

// A runtime sized aligned allocation for objects of type `T` with a runtime
// sized alignment:
//
//   base::AlignedHeapArray<float> array = base::AlignedUninit<float>(
//       size, alignment);
//   CHECK(reinterpret_cast<uintptr_t>(array.data()) % alignment == 0);
//
// A runtime sized aligned allocation for objects of type `T` but represented as
// a char array, along with a span accessing that memory as `T*` for in-place
// construction:
//
//   auto [a, s] = base::AlignedUninitCharArray<float>(size, alignment);
//   base::AlignedHeapArray<char> array = std::move(a);
//   base::span<float> span = s;
//   CHECK(reinterpret_cast<uintptr_t>(array.data()) % alignment == 0);
//   CHECK(reinterpret_cast<uintptr_t>(span.data()) % alignment == 0);
//
// With manual memory management, a runtime sized aligned allocation can be
// created:
//
//   float* my_array = static_cast<float*>(AlignedAlloc(size, alignment));
//   CHECK(reinterpret_cast<uintptr_t>(my_array) % alignment == 0);
//   memset(my_array, 0, size);  // fills entire object.
//
//   // ... later, to release the memory:
//   AlignedFree(my_array);

namespace base {

// Allocate memory of size `size` aligned to `alignment`.
//
// Prefer `AlignedUninit()` to make a `base::HeapArray` that has a runtime-sized
// alignment.
//
// When the caller will be managing the lifetimes of the objects in the array
// with in-place construction and destruction, `AlignedUninitCharArray()`
// provides safe ownership of the memory and access to memory aligned for `T` as
// `span<T>`.
//
// TODO(https://crbug.com/40255447): Convert usage to / convert to use
// `std::aligned_alloc` to the extent that it can be done (since
// `std::aligned_alloc` can't be used on Windows). When that happens, note that
// `std::aligned_alloc` requires the `size` parameter be an integral multiple of
// `alignment` while this implementation does not.
BASE_EXPORT void* AlignedAlloc(size_t size, size_t alignment);

// Deallocate memory allocated by `AlignedAlloc`.
inline void AlignedFree(void* ptr) {
#if defined(COMPILER_MSVC)
  _aligned_free(ptr);
#else
  free(ptr);
#endif
}

// Deleter for use with unique_ptr. E.g., use as
//   std::unique_ptr<Foo, base::AlignedFreeDeleter> foo;
struct AlignedFreeDeleter {
  inline void operator()(void* ptr) const {
    AlignedFree(ptr);
  }
};

template <class T>
using AlignedHeapArray = HeapArray<T, AlignedFreeDeleter>;

// Constructs a `base::AlignedHeapArray<T>` that is sized to hold `capacity`
// many objects of type `T` and is aligned to `alignment`. The memory is
// uninitialized.
//
// The `alignment` defaults to `alignof(T)` and can be omitted, but the
// alignment used will always be at least the alignment of a pointer.
template <class T>
AlignedHeapArray<T> AlignedUninit(size_t capacity,
                                  size_t alignment = alignof(T)) {
  alignment = std::max(alignment, alignof(void*));
  CHECK_GE(alignment, alignof(T));
  CHECK_LE(capacity, SIZE_MAX / sizeof(T));
  const size_t bytes = capacity * sizeof(T);
  // SAFETY: AlignedAlloc() allocates `bytes` many chars, which has room for
  // `capacity` many `T` objects by construction, so we specify `capacity` as
  // the size of the `HeapArray<T>`.
  return UNSAFE_BUFFERS(HeapArray<T, AlignedFreeDeleter>::FromOwningPointer(
      static_cast<T*>(AlignedAlloc(bytes, alignment)), capacity));
}

// Constructs a AlignedHeapArray<char> that is sized to hold `capacity` many
// objects of type `T` and is aligned to `alignment`.
//
// The `alignment` defaults to `alignof(T)` and can be omitted, but the
// alignment used will always be at least the alignment of a pointer.
//
// Returns a pair of `AlignedHeapArray<char>` and a `span<T>` for the entire
// _uninitialized_ `AlignedHeapArray`.
//
// It is up to the caller to construct objects of type `T` in the array
// in-place, and to destruct them before destroying the `AlignedHeapArray`.
//
// Note that using `span[index]` to make a reference to uninitialized memory is
// undefined behaviour. In-place construction must use the unsafe `span.data() +
// index` to avoid constructing a reference.
template <class T>
auto AlignedUninitCharArray(size_t capacity, size_t alignment = alignof(T)) {
  alignment = std::max(alignment, alignof(void*));
  CHECK_GE(alignment, alignof(T));
  CHECK_LE(capacity, SIZE_MAX / sizeof(T));
  const size_t bytes = capacity * sizeof(T);
  // SAFETY: AlignedAlloc() allocates `bytes` many chars, and we give the same
  // `bytes` as the size for `HeapArray`.
  auto uninit_array =
      UNSAFE_BUFFERS(HeapArray<char, AlignedFreeDeleter>::FromOwningPointer(
          static_cast<char*>(AlignedAlloc(bytes, alignment)), bytes));
  // SAFETY: `uninit_array` holds `capacity * sizeof(T)` bytes, so it has room
  // for `capacity` many objects of type `T`.
  auto uninit_span =
      UNSAFE_BUFFERS(span(reinterpret_cast<T*>(uninit_array.data()), capacity));
  return std::make_pair(std::move(uninit_array), std::move(uninit_span));
}

#ifdef __has_builtin
#define SUPPORTS_BUILTIN_IS_ALIGNED (__has_builtin(__builtin_is_aligned))
#else
#define SUPPORTS_BUILTIN_IS_ALIGNED 0
#endif

inline bool IsAligned(uintptr_t val, size_t alignment) {
  // If the compiler supports builtin alignment checks prefer them.
#if SUPPORTS_BUILTIN_IS_ALIGNED
  return __builtin_is_aligned(val, alignment);
#else
  DCHECK(std::has_single_bit(alignment)) << alignment << " is not a power of 2";
  return (val & (alignment - 1)) == 0;
#endif
}

#undef SUPPORTS_BUILTIN_IS_ALIGNED

inline bool IsAligned(const void* val, size_t alignment) {
  return IsAligned(reinterpret_cast<uintptr_t>(val), alignment);
}

}  // namespace base

#endif  // BASE_MEMORY_ALIGNED_MEMORY_H_