-
Notifications
You must be signed in to change notification settings - Fork 0
/
aligned_allocator.hpp
373 lines (318 loc) · 10 KB
/
aligned_allocator.hpp
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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
/**
* @file aligned_allocator.hpp
* @author Jens Munk Hansen <[email protected]>
* @date Wed Jul 22 23:18:58 2011
*
* @brief Aligned allocator for STL types
*
* Copyright 2017 Jens Munk Hansen
*/
/*
* This file is part of SOFUS.
*
* SOFUS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SOFUS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with SOFUS. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <sps/cenv.h> // SPS_UNREFERENCED_PARAMETER
#include <sps/mm_malloc.h> // Required for _mm_malloc() and _mm_free()
#ifdef CXX17
// TODO(JMH): Use std::aligned_alloc if C++17
#endif
#include <cstddef> // Required for size_t and ptrdiff_t and NULL
#include <new> // Required for placement new and std::bad_alloc
#include <stdexcept> // Required for std::length_error
#include <cstdlib> // Required for malloc() and free()
#include <memory> // std::allocator
#include <functional> // std::function
/*! Aligned allocator for STL containers. */
template <typename T,
std::size_t Alignment = 4*sizeof(T)> class aligned_allocator {
public:
/// <summary> STL standard aliases. . </summary>
typedef T * pointer;
typedef const T * const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef T value_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
/**
* Address of type T.
*
* @param r [in] The object of type T.
*
* @return the address
*/
T* address(T& r) const {
return &r;
}
/**
* Const address of type T.
*
* @param s The const object of type T.
*
* @return the address
*/
const T* address(const T& s) const {
return &s;
}
/**
* Gets the maximum size.
*
*
* @return the size
*/
size_t max_size() const {
return (static_cast<size_t>(0) - static_cast<size_t>(1)) / sizeof(T);
}
/*! Structure for rebinding */
template <typename U> struct rebind {
typedef aligned_allocator<U, Alignment> other;
};
/**
* Not equal to
*
* @param other
*
* @return The other
*/
bool operator!=(const aligned_allocator& other) const {
return !(*this == other);
}
/**
* Construct
*
* @param p [in] If non-null, the T* p is the address used for construction.
* @param t The object to displace.
*/
void construct(T* const p, const T& arg) const {
void* const pv = static_cast<void*>(p);
::new (pv) T(arg);
}
template<class U, class... Args>
void construct(U* p, Args&&... args) {
::new(p) U(std::forward<Args>(args)...);
}
/**
* Destroys the given p.
*
* @param p [in] If non-null, the T* const to destroy.
*/
void destroy(T* const p) const;
template<class U>
void destroy(U* p) const;
/**
* Equality operator.
*
* @param other
*
* @return true for stateless allocators.
*/
bool operator==(const aligned_allocator& other) const {
return true;
}
/**
* Default constructor - empty for stateless allocators.
*
*/
aligned_allocator() { }
/**
* Default copy constructor - empty for stateless allocators.
*
*/
aligned_allocator(const aligned_allocator&) { }
/**
* Default rebinding constructor - empty for stateless allocators.
*
* @param other
*/
template <typename U>
aligned_allocator(const aligned_allocator<U, Alignment>& other) {
SPS_UNREFERENCED_PARAMETER(other);
}
/**
* Destructor
*
*/
~aligned_allocator() { }
/// <summary> Allocates memory. </summary>
/// <param name="n"> The. </param>
/// <returns>
/**
* Allocates memory
*
* @exception std::length_error Thrown when length error.
* @exception std::bad_alloc Thrown when bad allocate.
* @param n The size
*
* @return null if it fails, else a reference pointer.
*/
T* allocate(const size_t n) const {
// The return value of allocate(0) is unspecified.
// aligned_allocator returns NULL in order to avoid depending
// on malloc(0)'s implementation-defined behavior
// (the implementation can define malloc(0) to return NULL,
// in which case the bad_alloc check below would fire).
// All allocators can return NULL in this case.
if (n == 0) {
return NULL;
}
// All allocators should contain an integer overflow check.
// The Standardization Committee recommends that std::length_error
// be thrown in the case of integer overflow.
if (n > max_size()) {
throw std::length_error("aligned_allocator<T>::allocate() - Integer overflow.");
}
// aligned_allocator wraps _mm_malloc().
void* const pv = _mm_malloc(n * sizeof(T), Alignment);
// Allocators should throw std::bad_alloc in the case of memory
// allocation failure.
if (pv == NULL) {
throw std::bad_alloc();
}
return static_cast<T*>(pv);
}
/**
* Deallocates the memory
*
* @param p [in] If non-null, the T* p is the address used for construction.
* @param n The length of the buffer.
*/
void deallocate(T* const p, const size_t n) const {
SPS_UNREFERENCED_PARAMETER(n);
_mm_free(p);
}
/**
* The allocator ignores hints, so the same as allocate.
*
* @param n
*
* @return null if it fails, else.
*/
template <class U>
T* allocate(const size_t n, const U* hint = 0) const {
return allocate(n);
}
// Allocators are not required to be assignable, so
// all allocators should have a private unimplemented
// assignment operator. Note that this will trigger the
// off-by-default (enabled under /Wall) warning C4626
// "assignment operator could not be generated because a
// base class assignment operator is inaccessible" within
// the STL headers, but that warning is useless.
private:
/**
* Private unimplemented assignment operator.
*
* @param other
*
* @return A shallow copy of this object.
*/
aligned_allocator& operator=(const aligned_allocator& other);
};
// A compiler bug causes it to believe that p->~T() doesn't reference p.
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4100) // Unreferenced formal parameter
#endif
/**
* Destroys the given p. The definition of destroy() must be the same for all allocators.
*
* @param p [in] If non-null, the T * const to destroy.
*/
template <typename T, std::size_t Alignment>
void aligned_allocator<T, Alignment>::destroy(T * const p) const {
p->~T();
}
template <typename T, std::size_t Alignment>
template<class U>
void aligned_allocator<T, Alignment>::destroy(U* p) const {
p->~T();
}
#ifdef _MSC_VER
# pragma warning(pop)
#endif
template <typename T, typename U, std::size_t Alignment>
inline bool operator==(const aligned_allocator<T, Alignment>&, const aligned_allocator<U, Alignment>&&) {
return true;
}
template <typename T, typename U, std::size_t Alignment>
inline bool operator!=(const aligned_allocator<T, Alignment>& a, const aligned_allocator<U, Alignment>& b) {
return !(a == b);
}
#if 0
// Initial version (could be needed for C++14 only)
template<typename T, typename... Args>
std::unique_ptr<T[], std::function<void(T *)>>
make_unique_array(std::allocator<T> alloc, std::size_t size, Args... args) {
T *ptr = alloc.allocate(size);
for (std::size_t i = 0; i < size; ++i) {
alloc.construct(&ptr[i], std::forward<Args>(args)...);
}
auto deleter = [](T *p, std::allocator<T> alloc, std::size_t size) {
for (std::size_t i = 0; i < size; ++i) {
alloc.destroy(&p[i]);
}
alloc.deallocate(p, sizeof(T) * size);
};
return {ptr, std::bind(deleter, std::placeholders::_1, alloc, size)};
}
// static_assert(alignof(T) % Alignment == 0 && std::is_same<Allocator, aligned_allocator<T>> || std::allocator)
template<typename T, std::size_t Alignment = 16, typename... Args>
std::unique_ptr<T[], std::function<void(T *)>>
make_unique_aligned_array(aligned_allocator<T, Alignment> alloc, std::size_t size, Args... args) {
static_assert(alignof(T) % Alignment == 0, "Bad alignment");
// This requires the object T itself to be aligned
T *ptr = alloc.allocate(size);
for (std::size_t i = 0; i < size; ++i) {
alloc.construct(&ptr[i], std::forward<Args>(args)...);
}
auto deleter = [](T *p, aligned_allocator<T, Alignment> alloc, std::size_t size) {
for (std::size_t i = 0; i < size; ++i) {
alloc.destroy(&p[i]);
}
alloc.deallocate(p, sizeof(T) * size);
};
return {ptr, std::bind(deleter, std::placeholders::_1, alloc, size)};
}
#endif
namespace sps {
template<typename T, typename Allocator = std::allocator<T>, typename... Args>
std::unique_ptr<T[], std::function<void(T*)>>
make_unique_array(std::size_t size, Args... args) {
Allocator alloc = Allocator();
T *ptr = alloc.allocate(size);
for (std::size_t i = 0; i < size; ++i) {
alloc.construct(&ptr[i], std::forward<Args>(args)...);
}
auto deleter = [](T *p, Allocator alloc, std::size_t size) {
for (std::size_t i = 0; i < size; ++i) {
alloc.destroy(&p[i]);
}
alloc.deallocate(p, sizeof(T) * size);
};
return {ptr, std::bind(deleter, std::placeholders::_1, alloc, size)};
}
template <typename T, std::size_t Alignment = 16, typename... Args>
auto make_unique_aligned_array(std::size_t size, Args... args)->decltype(sps::make_unique_array<T, aligned_allocator<T, Alignment>, Args...>(size, args...)) {
static_assert(alignof(T) % Alignment == 0, "Bad alignment");
return sps::make_unique_array<T, aligned_allocator<T, Alignment>, Args...>(size, std::forward<Args>(args)...);
}
} // namespace sps
/* Local variables: */
/* indent-tab-mode: nil */
/* tab-width: 2 */
/* c-basic-offset: 2 */
/* indent-tabs-mode: nil */
/* End: */