-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathAllocatedArrayUtils.h
More file actions
183 lines (168 loc) · 7.35 KB
/
AllocatedArrayUtils.h
File metadata and controls
183 lines (168 loc) · 7.35 KB
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
171
172
173
174
175
176
177
178
179
180
181
182
183
#pragma once
#include "MoveSliceOf.h"
#include "SliceOf.h"
#include <new> // ::operator new, ::operator delete
#include <stddef.h> // size_t
#include <string.h> // memcpy
#include <type_traits>
#include <utility>
namespace array19 {
/// Utility functions that implement the meat of allocated arrays
///
/// note:
/// * This replaces the algorithmns form std <memory> header.
/// * It works with Slices instead of iterators to simplify the interface
template<class T> struct AllocatedArrayUtils {
static_assert(std::is_nothrow_move_constructible_v<T>, "Please ensure move constructior is marked noexcept!");
static_assert(std::is_nothrow_move_assignable_v<T>, "Please ensure move assignment is marked noexcept!");
static_assert(std::is_nothrow_destructible_v<T>, "Please ensure destructor is marked noexcept!");
using Slice = SliceOf<T>;
using ConstSlice = SliceOf<const T>;
using MoveSlice = MoveSliceOf<T>;
/// returns uninitialized storage for \param count elements with proper alignment
/// note:
/// * use deallocate to free the storage!
/// * exceptions from new will terminate - without memory no recovery is possible!
[[nodiscard]] static auto allocate(size_t count) noexcept -> T* {
if (0 == count) return nullptr;
if constexpr (__STDCPP_DEFAULT_NEW_ALIGNMENT__ < alignof(T)) {
return reinterpret_cast<T*>(::operator new[](count * sizeof(T), std::align_val_t{alignof(T)}));
}
else {
return reinterpret_cast<T*>(::operator new[](count * sizeof(T)));
}
}
/// frees the storage pointed to by the slice
/// note:
/// * slice.begin() has to be the pointer returned by allocate(size.count())
/// * exceptions form delete will terminate - we assume memory is corrupted!
static void deallocate(Slice slice) noexcept {
#if __cpp_sized_deallocation
if constexpr (__STDCPP_DEFAULT_NEW_ALIGNMENT__ < alignof(T)) {
::operator delete[](slice.begin(), slice.count() * sizeof(T), std::align_val_t{alignof(T)});
}
else {
::operator delete[](slice.begin(), slice.count() * sizeof(T));
}
#else
if constexpr (__STDCPP_DEFAULT_NEW_ALIGNMENT__ < alignof(T)) {
::operator delete[](slice.begin(), std::align_val_t{alignof(T)});
}
else {
::operator delete[](slice.begin());
}
#endif
}
/// default construct every element of \param slice if necessary
static void defaultConstruct(Slice slice) noexcept(std::is_nothrow_default_constructible_v<T>) {
if constexpr (!std::is_trivially_default_constructible_v<T>)
for (auto& e : slice) new (&e) T();
}
/// calls destructor for every element of \param slice if necessary
/// note:
/// * we assume destructors are always noexcept
static void destruct(Slice slice) noexcept {
if constexpr (!std::is_trivially_destructible_v<T>)
for (auto& elem : slice) elem.~T();
}
/// constructs a copy of \param fromSlice at \param toPointer
/// note:
/// * toPointer has to point to least fromSlice.count() elements
/// * assumes toPointer and fromSlice do not overlap
static void copyConstruct(T* toPointer, ConstSlice fromSlice) noexcept(std::is_nothrow_copy_constructible_v<T>) {
if constexpr (std::is_trivially_copy_constructible_v<T>) {
if (!fromSlice.isEmpty()) {
memcpy(toPointer, fromSlice.begin(), fromSlice.count() * sizeof(T));
}
}
else {
for (auto& from : fromSlice) new (toPointer++) T(from);
}
}
/// move constructs elements of \param fromSlice into \param to Pointer
/// note:
/// * toPointer has to point to least fromSlice.count() elements
/// * assumes toPointer and fromSlice do not overlap
/// * we assume move is always noexcept!
static void moveConstruct(T* toPointer, MoveSlice fromSlice) noexcept {
if constexpr (std::is_trivially_move_constructible_v<T>) {
if (!fromSlice.isEmpty()) {
memcpy(toPointer, fromSlice.begin(), fromSlice.count() * sizeof(T));
}
}
else {
for (auto& from : fromSlice) new (toPointer++) T(std::move(from));
}
}
/// assigns elements of \param fromSlice into \param toPointer
/// note:
/// * toPointer has to point to least fromSlice.count() initialized elements
/// * assumes toPointer and fromSlice do not overlap
static void copyAssign(T* toPointer, ConstSlice fromSlice) noexcept(std::is_nothrow_copy_assignable_v<T>) {
if constexpr (std::is_trivially_copy_assignable_v<T>) {
if (!fromSlice.isEmpty()) {
memcpy(toPointer, fromSlice.begin(), fromSlice.count() * sizeof(T));
}
}
else {
for (const auto& from : fromSlice) *toPointer++ = from;
}
}
/// move assigns elements of \param fromSlice into \param to Pointer
/// note:
/// * toPointer has to point to least fromSlice.count() elements
/// * assumes toPointer and fromSlice do not overlap
/// * we assume move is always noexcept!
static void moveAssign(T* toPointer, MoveSlice fromSlice) noexcept {
if constexpr (std::is_trivially_move_assignable_v<T>) {
if (!fromSlice.isEmpty()) {
memcpy(toPointer, fromSlice.begin(), fromSlice.count() * sizeof(T));
}
}
else {
for (auto& from : fromSlice) *toPointer++ = std::move(from);
}
}
/// move assigns elements of \param fromSlice into \param to Pointer
/// note:
/// * toPointer has to point to least fromSlice.count() elements
/// * toPointer has to be before fromSlice.begin() if ranges overlap
/// * we assume move is always noexcept!
///
/// example:
/// [ e e e e e e e ]
/// toPointer ^ ^ fromSlice.begin()
static void moveAssignForward(T* toPointer, MoveSlice fromSlice) noexcept {
if constexpr (std::is_trivially_move_constructible_v<T>) {
if (!fromSlice.isEmpty()) {
memmove(toPointer, fromSlice.begin(), fromSlice.count() * sizeof(T));
}
}
else {
for (auto& from : fromSlice) *toPointer++ = std::move(from);
}
}
/// move assigns elements of \param fromSlice into \param to Pointer
/// note:
/// * toPointer has to point to least fromSlice.count() elements
/// * toPointer has to be behind fromSlice.begin() if ranges overlap
/// * we assume move is always noexcept!
///
/// example:
/// [ e e e e e e e ]
/// fromSlice.begin() ^ ^ toPointer
static void moveAssignReverse(T* toPointer, MoveSlice fromSlice) noexcept {
if constexpr (std::is_trivially_move_assignable_v<T>) {
if (!fromSlice.isEmpty()) {
memmove(toPointer, fromSlice.begin(), fromSlice.count() * sizeof(T));
}
}
else {
auto rTo = toPointer + fromSlice.count();
auto rFrom = fromSlice.end();
auto rFromEnd = fromSlice.begin();
while (rFrom != rFromEnd) *(--rTo) = std::move(*(--rFrom));
}
}
};
} // namespace array19