aboutsummaryrefslogtreecommitdiffstats
path: root/common/almalloc.h
blob: 82f050e4e6805e6d069294c1d431dfd6f62f21b1 (plain)
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
#ifndef AL_MALLOC_H
#define AL_MALLOC_H

#include <algorithm>
#include <cstddef>
#include <iterator>
#include <limits>
#include <memory>
#include <new>
#include <type_traits>
#include <utility>

#include "pragmadefs.h"


namespace gsl {
template<typename T> using owner = T;
};

void al_free(gsl::owner<void*> ptr) noexcept;
[[gnu::alloc_align(1), gnu::alloc_size(2), gnu::malloc]]
gsl::owner<void*> al_malloc(size_t alignment, size_t size);
[[gnu::alloc_align(1), gnu::alloc_size(2), gnu::malloc]]
gsl::owner<void*> al_calloc(size_t alignment, size_t size);


#define DISABLE_ALLOC                                                         \
    void *operator new(size_t) = delete;                                      \
    void *operator new[](size_t) = delete;                                    \
    void operator delete(void*) noexcept = delete;                            \
    void operator delete[](void*) noexcept = delete;


enum FamCount : size_t { };

#define DEF_FAM_NEWDEL(T, FamMem)                                             \
    static constexpr size_t Sizeof(size_t count) noexcept                     \
    {                                                                         \
        static_assert(&Sizeof == &T::Sizeof,                                  \
            "Incorrect container type specified");                            \
        return std::max(decltype(FamMem)::Sizeof(count, offsetof(T, FamMem)), \
            sizeof(T));                                                       \
    }                                                                         \
                                                                              \
    gsl::owner<void*> operator new(size_t /*size*/, FamCount count)           \
    {                                                                         \
        const auto alignment = std::align_val_t{alignof(T)};                  \
        return ::operator new[](T::Sizeof(count), alignment);                 \
    }                                                                         \
    void operator delete(gsl::owner<void*> block, FamCount) noexcept          \
    { ::operator delete[](block, std::align_val_t{alignof(T)}); }             \
    void operator delete(gsl::owner<void*> block) noexcept                    \
    { ::operator delete[](block, std::align_val_t{alignof(T)}); }             \
    void *operator new[](size_t /*size*/) = delete;                           \
    void operator delete[](void* /*block*/) = delete;


namespace al {

template<typename T, std::size_t AlignV=alignof(T)>
struct allocator {
    static constexpr auto Alignment = std::max(AlignV, alignof(T));
    static constexpr auto AlignVal = std::align_val_t{Alignment};

    using value_type = T;
    using reference = T&;
    using const_reference = const T&;
    using pointer = T*;
    using const_pointer = const T*;
    using size_type = std::size_t;
    using difference_type = std::ptrdiff_t;
    using is_always_equal = std::true_type;

    template<typename U>
    struct rebind {
        using other = std::enable_if_t<alignof(U) <= Alignment, allocator<U,Alignment>>;
    };

    constexpr explicit allocator() noexcept = default;
    template<typename U, std::size_t N>
    constexpr explicit allocator(const allocator<U,N>&) noexcept
    { static_assert(Alignment == allocator<U,N>::Alignment); }

    gsl::owner<T*> allocate(std::size_t n)
    {
        if(n > std::numeric_limits<std::size_t>::max()/sizeof(T)) throw std::bad_alloc();
        return static_cast<gsl::owner<T*>>(::operator new[](n*sizeof(T), AlignVal));
    }
    void deallocate(gsl::owner<T*> p, std::size_t) noexcept
    { ::operator delete[](gsl::owner<void*>{p}, AlignVal); }
};
template<typename T, std::size_t N, typename U, std::size_t M>
constexpr bool operator==(const allocator<T,N>&, const allocator<U,M>&) noexcept
{ return allocator<T,N>::Alignment == allocator<U,M>::Alignment; }
template<typename T, std::size_t N, typename U, std::size_t M>
constexpr bool operator!=(const allocator<T,N>&, const allocator<U,M>&) noexcept
{ return allocator<T,N>::Alignment != allocator<U,M>::Alignment; }


template<typename T>
constexpr T *to_address(T *p) noexcept
{
    static_assert(!std::is_function<T>::value, "Can't be a function type");
    return p;
}

template<typename T>
constexpr auto to_address(const T &p) noexcept
{
    return ::al::to_address(p.operator->());
}


template<typename T, typename ...Args>
constexpr T* construct_at(T *ptr, Args&& ...args)
    noexcept(std::is_nothrow_constructible_v<T, Args...>)
{
    /* NOLINTBEGIN(cppcoreguidelines-owning-memory) construct_at doesn't
     * necessarily handle the address from an owner, while placement new
     * expects to.
     */
    return ::new(static_cast<void*>(ptr)) T{std::forward<Args>(args)...};
    /* NOLINTEND(cppcoreguidelines-owning-memory) */
}

} // namespace al

#endif /* AL_MALLOC_H */