aboutsummaryrefslogtreecommitdiffstats
path: root/common/flexarray.h
blob: 7975a52a063eb37e0ce19f7864f0cef7ec7e8147 (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
129
#ifndef AL_FLEXARRAY_H
#define AL_FLEXARRAY_H

#include <cstddef>
#include <stdexcept>
#include <type_traits>

#include "almalloc.h"
#include "alspan.h"

namespace al {

/* Storage for flexible array data. This is trivially destructible if type T is
 * trivially destructible.
 */
template<typename T, size_t alignment, bool = std::is_trivially_destructible<T>::value>
struct FlexArrayStorage {
    alignas(alignment) const ::al::span<T> mData;

    static constexpr size_t Sizeof(size_t count, size_t base=0u) noexcept
    { return sizeof(FlexArrayStorage) + sizeof(T)*count + base; }

    FlexArrayStorage(size_t size) noexcept(std::is_nothrow_constructible_v<T>)
        : mData{::new(static_cast<void*>(this+1)) T[size], size}
    { }
    ~FlexArrayStorage() = default;

    FlexArrayStorage(const FlexArrayStorage&) = delete;
    FlexArrayStorage& operator=(const FlexArrayStorage&) = delete;
};

template<typename T, size_t alignment>
struct FlexArrayStorage<T,alignment,false> {
    alignas(alignment) const ::al::span<T> mData;

    static constexpr size_t Sizeof(size_t count, size_t base=0u) noexcept
    { return sizeof(FlexArrayStorage) + sizeof(T)*count + base; }

    FlexArrayStorage(size_t size) noexcept(std::is_nothrow_constructible_v<T>)
        : mData{::new(static_cast<void*>(this+1)) T[size], size}
    { }
    ~FlexArrayStorage() { std::destroy(mData.begin(), mData.end()); }

    FlexArrayStorage(const FlexArrayStorage&) = delete;
    FlexArrayStorage& operator=(const FlexArrayStorage&) = delete;
};

/* A flexible array type. Used either standalone or at the end of a parent
 * struct, with placement new, to have a run-time-sized array that's embedded
 * with its size.
 */
template<typename T, size_t alignment=alignof(T)>
struct FlexArray {
    using element_type = T;
    using value_type = std::remove_cv_t<T>;
    using index_type = size_t;
    using difference_type = ptrdiff_t;

    using pointer = T*;
    using const_pointer = const T*;
    using reference = T&;
    using const_reference = const T&;

    using iterator = pointer;
    using const_iterator = const_pointer;
    using reverse_iterator = std::reverse_iterator<iterator>;
    using const_reverse_iterator = std::reverse_iterator<const_iterator>;

    using Storage_t_ = FlexArrayStorage<element_type,alignment>;

    Storage_t_ mStore;

    static constexpr index_type Sizeof(index_type count, index_type base=0u) noexcept
    { return Storage_t_::Sizeof(count, base); }
    static std::unique_ptr<FlexArray> Create(index_type count)
    {
        if(gsl::owner<void*> ptr{al_calloc(alignof(FlexArray), Sizeof(count))})
        {
            try {
                return std::unique_ptr<FlexArray>{::new(ptr) FlexArray{count}};
            }
            catch(...) {
                al_free(ptr);
                throw;
            }
        }
        throw std::bad_alloc();
    }

    FlexArray(index_type size) noexcept(std::is_nothrow_constructible_v<Storage_t_,index_type>)
        : mStore{size}
    { }
    ~FlexArray() = default;

    [[nodiscard]] auto size() const noexcept -> index_type { return mStore.mData.size(); }
    [[nodiscard]] auto empty() const noexcept -> bool { return mStore.mData.empty(); }

    [[nodiscard]] auto data() noexcept -> pointer { return mStore.mData.data(); }
    [[nodiscard]] auto data() const noexcept -> const_pointer { return mStore.mData.data(); }

    [[nodiscard]] auto operator[](index_type i) noexcept -> reference { return mStore.mData[i]; }
    [[nodiscard]] auto operator[](index_type i) const noexcept -> const_reference { return mStore.mData[i]; }

    [[nodiscard]] auto front() noexcept -> reference { return mStore.mData.front(); }
    [[nodiscard]] auto front() const noexcept -> const_reference { return mStore.mData.front(); }

    [[nodiscard]] auto back() noexcept -> reference { return mStore.mData.back(); }
    [[nodiscard]] auto back() const noexcept -> const_reference { return mStore.mData.back(); }

    [[nodiscard]] auto begin() noexcept -> iterator { return mStore.mData.begin(); }
    [[nodiscard]] auto begin() const noexcept -> const_iterator { return mStore.mData.begin(); }
    [[nodiscard]] auto cbegin() const noexcept -> const_iterator { return mStore.mData.cbegin(); }
    [[nodiscard]] auto end() noexcept -> iterator { return mStore.mData.end(); }
    [[nodiscard]] auto end() const noexcept -> const_iterator { return mStore.mData.end(); }
    [[nodiscard]] auto cend() const noexcept -> const_iterator { return mStore.mData.cend(); }

    [[nodiscard]] auto rbegin() noexcept -> reverse_iterator { return end(); }
    [[nodiscard]] auto rbegin() const noexcept -> const_reverse_iterator { return end(); }
    [[nodiscard]] auto crbegin() const noexcept -> const_reverse_iterator { return cend(); }
    [[nodiscard]] auto rend() noexcept -> reverse_iterator { return begin(); }
    [[nodiscard]] auto rend() const noexcept -> const_reverse_iterator { return begin(); }
    [[nodiscard]] auto crend() const noexcept -> const_reverse_iterator { return cbegin(); }

    DEF_PLACE_NEWDEL
};

} // namespace al

#endif /* AL_FLEXARRAY_H */