-
Notifications
You must be signed in to change notification settings - Fork 16
Expand file tree
/
Copy pathraw_storage.hpp
More file actions
377 lines (338 loc) · 16.7 KB
/
Copy pathraw_storage.hpp
File metadata and controls
377 lines (338 loc) · 16.7 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
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
374
375
376
377
// Copyright (C) 2018 Jonathan Müller <[email protected]>
// This file is subject to the license terms in the LICENSE file
// found in the top-level directory of this distribution.
#ifndef FOONATHAN_ARRAY_RAW_STORAGE_HPP_INCLUDED
#define FOONATHAN_ARRAY_RAW_STORAGE_HPP_INCLUDED
#include <cassert>
#include <cstring>
#include <iterator>
#include <new>
#include <type_traits>
#include <foonathan/array/detail/is_trivial.hpp>
#include <foonathan/array/contiguous_iterator.hpp>
#include <foonathan/array/memory_block.hpp>
namespace foonathan
{
namespace array
{
/// \effects Creates a new object at the given location using default initialization.
/// \returns A pointer to the newly created object.
/// \notes Default initialization may not do any initialization at all.
template <typename T>
T* default_construct_object(raw_pointer ptr)
{
return ::new (to_void_pointer(ptr)) T;
}
/// \effects Creates a new object at the given location using value initialization.
/// \returns A pointer to the newly created object.
/// \notes Value initialization will always do zero initialization, even for types without default constructors.
template <typename T>
T* value_construct_object(raw_pointer ptr)
{
return ::new (to_void_pointer(ptr)) T();
}
/// \effects Creates a new object at the given location using `T(std::forward<Args>(args)...)`.
/// \returns A pointer to the newly created object.
template <typename T, typename... Args>
T* paren_construct_object(raw_pointer ptr, Args&&... args)
{
return ::new (to_void_pointer(ptr)) T(std::forward<Args>(args)...);
}
/// \effects Creates a new object at the given location using `T{std::forward<Args>(args)...}`.
/// \returns A pointer to the newly created object.
template <typename T, typename... Args>
T* list_construct_object(raw_pointer ptr, Args&&... args)
{
return ::new (to_void_pointer(ptr)) T{std::forward<Args>(args)...};
}
namespace detail
{
#if defined(__cpp_lib_is_aggregate)
template <typename T, typename... Args>
struct use_list_construct : std::is_aggregate<T>
{
};
#else
template <typename T, typename... Args>
struct use_list_construct
: std::integral_constant<bool, !std::is_constructible<T, Args...>::value>
{
};
#endif
template <typename T, typename... Args>
T* construct_object_impl(std::true_type, raw_pointer ptr, Args&&... args)
{
return list_construct_object<T>(ptr, std::forward<Args>(args)...);
}
template <typename T, typename... Args>
T* construct_object_impl(std::false_type, raw_pointer ptr, Args&&... args)
{
return paren_construct_object<T>(ptr, std::forward<Args>(args)...);
}
} // namespace detail
/// \effects Creates a new object at the given location using `paren_construct_object()`,
/// unless it is an aggregate, then it uses `list_construct_object()`.
/// \returns A pointer to the newly created object.
/// \notes `std::is_aggregate` is only available in C++17 or higher,
/// before that it tries to use list initialization whenever the other one doesn't compile.
template <typename T, typename... Args>
T* construct_object(raw_pointer ptr, Args&&... args)
{
return detail::construct_object_impl<T>(detail::use_list_construct<T, Args...>{}, ptr,
std::forward<Args>(args)...);
}
/// \effects Destroys an object at the given location.
template <typename T>
raw_pointer destroy_object(T* object) noexcept
{
object->~T();
return to_raw_pointer(object);
}
namespace detail
{
template <typename T, typename FwdIter>
void destroy_range_impl(std::true_type, FwdIter, FwdIter) noexcept
{
// trivially destructible, no need to do anything
}
template <typename T, typename FwdIter>
void destroy_range_impl(std::false_type, FwdIter begin, FwdIter end) noexcept
{
for (auto cur = begin; cur != end; ++cur)
cur->~T();
}
} // namespace detail
/// \effects Destroys all objects in the given range.
template <typename FwdIter>
void destroy_range(FwdIter begin, FwdIter end) noexcept
{
using type = typename std::iterator_traits<FwdIter>::value_type;
detail::destroy_range_impl<type>(std::is_trivially_destructible<type>{}, begin, end);
}
/// A RAII object to manage created objects in a range.
template <typename T>
class partially_constructed_range
{
public:
/// \effects Creates it giving it the memory block it uses to create the objects in.
explicit partially_constructed_range(const memory_block& block)
: begin_(to_pointer<T>(block.begin())), cur_end_(block.begin()), max_end_(block.end())
{
}
partially_constructed_range(const partially_constructed_range&) = delete;
partially_constructed_range& operator=(const partially_constructed_range&) = delete;
/// \effects Destroys all objects already created.
~partially_constructed_range() noexcept
{
destroy_range(begin_, to_pointer<T>(cur_end_));
}
/// \effects Creates a new object at the end using the corresponding free function.
/// \returns A pointer to the created object.
/// \notes Does not check whether there is enough memory left.
/// \group construct
template <typename... Args>
T* construct_object(Args&&... args)
{
assert(cur_end_ + sizeof(T) <= max_end_);
auto result =
foonathan::array::construct_object<T>(cur_end_, std::forward<Args>(args)...);
cur_end_ += sizeof(T);
return result;
}
/// \group construct
template <typename... Args>
T* brace_construct_object(Args&&... args)
{
assert(cur_end_ + sizeof(T) <= max_end_);
auto result =
foonathan::array::list_construct_object<T>(cur_end_,
std::forward<Args>(args)...);
cur_end_ += sizeof(T);
return result;
}
/// \group construct
template <typename... Args>
T* paren_construct_object(Args&&... args)
{
assert(cur_end_ + sizeof(T) <= max_end_);
auto result =
foonathan::array::paren_construct_object<T>(cur_end_,
std::forward<Args>(args)...);
cur_end_ += sizeof(T);
return result;
}
/// \group construct
T* default_construct_object()
{
assert(cur_end_ + sizeof(T) <= max_end_);
auto result = foonathan::array::default_construct_object<T>(cur_end_);
cur_end_ += sizeof(T);
return result;
}
/// \group construct
T* value_construct_object()
{
assert(cur_end_ + sizeof(T) <= max_end_);
auto result = foonathan::array::value_construct_object<T>(cur_end_);
cur_end_ += sizeof(T);
return result;
}
/// \effects Releases ownership over the created objects.
/// \returns A pointer to the next free memory space.
raw_pointer release() && noexcept
{
auto result = cur_end_;
begin_ = nullptr;
cur_end_ = nullptr;
max_end_ = nullptr;
return result;
}
private:
T* begin_;
raw_pointer cur_end_, max_end_;
};
/// \effects Creates `n` objects of type `T` in the memory block using [array::default_construct_object]().
/// \returns A pointer after the last created object.
template <typename T>
raw_pointer uninitialized_default_construct(const memory_block& block, size_type n)
{
partially_constructed_range<T> range(block);
for (auto i = size_type(0); i != n; ++i)
range.default_construct_object();
return std::move(range).release();
}
/// \effects Creates `n` objects of type `T` in the memory block using [array::value_construct_object]().
/// \returns A pointer after the last created object.
template <typename T>
raw_pointer uninitialized_value_construct(const memory_block& block, size_type n)
{
partially_constructed_range<T> range(block);
for (auto i = size_type(0); i != n; ++i)
range.value_construct_object();
return std::move(range).release();
}
/// \effects Creates `n` objects of type `T` in the memory block by copying the existing one.
/// \returns A pointer after the last created object.
template <typename T>
raw_pointer uninitialized_fill(const memory_block& block, size_type n, const T& obj)
{
partially_constructed_range<T> range(block);
for (auto i = size_type(0); i != n; ++i)
range.construct_object(obj);
return std::move(range).release();
}
namespace detail
{
template <typename InputIter, typename T>
struct can_memcpy
: std::integral_constant<bool, is_contiguous_iterator<InputIter>::value
&& detail::is_trivially_copyable<T>::value>
{
};
template <typename T, typename, typename ContIter>
raw_pointer uninitialized_move_copy_impl(std::true_type, ContIter begin, ContIter end,
const memory_block& block) noexcept
{
auto no_elements = std::size_t(end - begin);
auto size = no_elements * sizeof(T);
assert(block.size() >= size);
auto source_begin_ptr = iterator_to_pointer(begin);
if (source_begin_ptr != nullptr)
{
auto block_begin_ptr = to_void_pointer(block.begin());
assert(block_begin_ptr != nullptr);
std::memcpy(block_begin_ptr, source_begin_ptr, size);
}
else
assert(no_elements == 0);
return block.begin() + size;
}
template <typename T, typename TargetT, typename InputIter>
raw_pointer uninitialized_move_copy_impl(std::false_type, InputIter begin,
InputIter end, const memory_block& block)
{
partially_constructed_range<T> range(block);
for (auto cur = begin; cur != end; ++cur)
range.construct_object(static_cast<TargetT>(*cur));
return std::move(range).release();
}
template <typename T, typename TargetT, typename InputIter>
raw_pointer uninitialized_move_copy(InputIter begin, InputIter end,
const memory_block& block)
{
return uninitialized_move_copy_impl<T, TargetT>(can_memcpy<InputIter, T>{}, begin,
end, block);
}
} // namespace detail
/// \effects Moves elements of the given range to the uninitialized memory of the given block.
/// \returns A pointer past the last created object.
/// \notes If an exception is thrown, some objects in the original range may already be in the moved-from state,
/// however all objects created at the new location will be destroyed.
template <typename InputIter>
raw_pointer uninitialized_move(InputIter begin, InputIter end, const memory_block& block)
{
using type = typename std::iterator_traits<InputIter>::value_type;
return detail::uninitialized_move_copy<type, type&&>(begin, end, block);
}
/// \effects [std::move_if_noexcept]() elements of the given range to the uninitialized memory of the given block.
/// \returns A pointer past the last created object.
/// \notes If an exception is thrown, the old range has not been modified and all objects created at the new location will be destroyed.
template <typename InputIter>
raw_pointer uninitialized_move_if_noexcept(InputIter begin, InputIter end,
const memory_block& block)
{
using type = typename std::iterator_traits<InputIter>::value_type;
using target_type =
typename std::conditional<!std::is_nothrow_move_constructible<type>::value
&& std::is_copy_constructible<type>::value,
const type&, type &&>::type;
return detail::uninitialized_move_copy<type, target_type>(begin, end, block);
}
/// \effects Copies elements of the given range to the uninitialized memory of the given block.
/// \returns A pointer past the last created object.
/// \notes If an exception is thrown, the old range has not been modified and all objects created at the new location will be destroyed.
template <typename InputIter>
raw_pointer uninitialized_copy(InputIter begin, InputIter end, const memory_block& block)
{
using type = typename std::iterator_traits<InputIter>::value_type;
return detail::uninitialized_move_copy<type, const type&>(begin, end, block);
}
/// \effects Copies elements of the given range to the uninitialized memory of the given block, implicitly converting all elements.
/// \returns A pointer past the last created object.
/// \notes If an exception is thrown, the old range has not been modified and all objects created at the new location will be destroyed.
template <typename T, typename InputIter>
raw_pointer uninitialized_copy_convert(InputIter begin, InputIter end,
const memory_block& block)
{
partially_constructed_range<T> range(block);
for (auto cur = begin; cur != end; ++cur)
range.construct_object(*cur);
return std::move(range).release();
}
/// \effects Moves elements of the given range to the uninitialized memory of the given block, implicitly converting all elements.
/// \returns A pointer past the last created object.
/// \notes If an exception is thrown, the old range has not been modified and all objects created at the new location will be destroyed.
template <typename T, typename InputIter>
raw_pointer uninitialized_move_convert(InputIter begin, InputIter end,
const memory_block& block)
{
partially_constructed_range<T> range(block);
for (auto cur = begin; cur != end; ++cur)
range.construct_object(std::move(*cur));
return std::move(range).release();
}
/// \effects [std::move_if_noexcept]() elements of the given range to the uninitialized memory of the given block,
/// then destroys them at the old location.
/// \returns A pointer past the last created object.
/// \notes If an exception is thrown, the old range has not been modified and all objects created at the new location will be destroyed.
template <typename FwdIter>
raw_pointer uninitialized_destructive_move(FwdIter begin, FwdIter end,
const memory_block& block)
{
auto result = uninitialized_move_if_noexcept(begin, end, block);
destroy_range(begin, end);
return result;
}
} // namespace array
} // namespace foonathan
#endif // FOONATHAN_ARRAY_RAW_STORAGE_HPP_INCLUDED