1
#pragma once
2

            
3
#if defined(__ANDROID_API__) && __ANDROID_API__ < 28
4
#include <stdlib.h>
5

            
6
#define ALIGNED_ALLOCATOR_USE_POSIX_MEMALIGN 1
7
#endif // if defined(__ANDROID_API__) && __ANDROID_API__ < 28
8

            
9
#include <cstddef>
10
#include <cstdlib>
11

            
12
namespace Envoy {
13
namespace Memory {
14

            
15
// Custom allocator using std::aligned_alloc to allocate |T|s at |Alignment|.
16
template <typename T, std::size_t Alignment> class AlignedAllocator {
17
public:
18
  static_assert((Alignment & (Alignment - 1)) == 0, "Alignment must be a power of 2");
19
#ifdef ALIGNED_ALLOCATOR_USE_POSIX_MEMALIGN
20
  static_assert((Alignment % sizeof(void*)) == 0,
21
                "Alignment must be a multiple of sizeof(void*) when using posix_memalign");
22
#endif
23
  using value_type = T;
24

            
25
  AlignedAllocator() noexcept = default;
26

            
27
  // Copy constructor for rebind compatibility.
28
  template <typename U> explicit AlignedAllocator(const AlignedAllocator<U, Alignment>&) noexcept {}
29

            
30
49895
  static std::size_t round_up_to_alignment(std::size_t bytes) {
31
49895
    return (bytes + Alignment - 1) & ~(Alignment - 1);
32
49895
  }
33

            
34
  // Allocate aligned memory.
35
  // This never throws std::bad_alloc, it returns nullptr on failure.
36
49885
  T* allocate(std::size_t n) {
37
    // STL containers never call allocate with n=0.
38
49885
    if (n == 0) {
39
1
      return nullptr;
40
1
    }
41
49884
    std::size_t bytes = n * sizeof(T);
42
#ifdef ALIGNED_ALLOCATOR_USE_POSIX_MEMALIGN
43
    void* ptr = nullptr;
44
    if (posix_memalign(&ptr, Alignment, bytes) != 0) {
45
      return nullptr;
46
    }
47
    return static_cast<T*>(ptr);
48
#else
49
    // Ensure bytes is a multiple of Alignment, which is required by std::aligned_alloc.
50
49884
    bytes = round_up_to_alignment(bytes);
51
49884
    return static_cast<T*>(std::aligned_alloc(Alignment, bytes));
52
49885
#endif
53
49885
  }
54

            
55
49883
  void deallocate(T* p, std::size_t) noexcept {
56
49883
    if (p != nullptr) {
57
#ifdef ALIGNED_ALLOCATOR_USE_POSIX_MEMALIGN
58
      free(p);
59
#else
60
49882
      std::free(p);
61
49882
#endif
62
49882
    }
63
49883
  }
64

            
65
  // Equality operators (required for allocator_traits)
66
  template <typename U> bool operator==(const AlignedAllocator<U, Alignment>&) const noexcept {
67
    return true;
68
  }
69

            
70
  template <typename U> bool operator!=(const AlignedAllocator<U, Alignment>&) const noexcept {
71
    return false;
72
  }
73

            
74
  // Satisfy libc++ requirement.
75
  template <typename U> struct rebind {
76
    using other = AlignedAllocator<U, Alignment>;
77
  };
78
};
79

            
80
} // namespace Memory
81
} // namespace Envoy