jpayne@69
|
1 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
|
jpayne@69
|
2 // Licensed under the MIT License:
|
jpayne@69
|
3 //
|
jpayne@69
|
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
|
jpayne@69
|
5 // of this software and associated documentation files (the "Software"), to deal
|
jpayne@69
|
6 // in the Software without restriction, including without limitation the rights
|
jpayne@69
|
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
jpayne@69
|
8 // copies of the Software, and to permit persons to whom the Software is
|
jpayne@69
|
9 // furnished to do so, subject to the following conditions:
|
jpayne@69
|
10 //
|
jpayne@69
|
11 // The above copyright notice and this permission notice shall be included in
|
jpayne@69
|
12 // all copies or substantial portions of the Software.
|
jpayne@69
|
13 //
|
jpayne@69
|
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
jpayne@69
|
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
jpayne@69
|
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
jpayne@69
|
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
jpayne@69
|
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
jpayne@69
|
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
jpayne@69
|
20 // THE SOFTWARE.
|
jpayne@69
|
21
|
jpayne@69
|
22 #pragma once
|
jpayne@69
|
23
|
jpayne@69
|
24 #include "memory.h"
|
jpayne@69
|
25 #include <string.h>
|
jpayne@69
|
26 #include <initializer_list>
|
jpayne@69
|
27
|
jpayne@69
|
28 KJ_BEGIN_HEADER
|
jpayne@69
|
29
|
jpayne@69
|
30 namespace kj {
|
jpayne@69
|
31
|
jpayne@69
|
32 // =======================================================================================
|
jpayne@69
|
33 // ArrayDisposer -- Implementation details.
|
jpayne@69
|
34
|
jpayne@69
|
35 class ArrayDisposer {
|
jpayne@69
|
36 // Much like Disposer from memory.h.
|
jpayne@69
|
37
|
jpayne@69
|
38 protected:
|
jpayne@69
|
39 // Do not declare a destructor, as doing so will force a global initializer for
|
jpayne@69
|
40 // HeapArrayDisposer::instance.
|
jpayne@69
|
41
|
jpayne@69
|
42 virtual void disposeImpl(void* firstElement, size_t elementSize, size_t elementCount,
|
jpayne@69
|
43 size_t capacity, void (*destroyElement)(void*)) const = 0;
|
jpayne@69
|
44 // Disposes of the array. `destroyElement` invokes the destructor of each element, or is nullptr
|
jpayne@69
|
45 // if the elements have trivial destructors. `capacity` is the amount of space that was
|
jpayne@69
|
46 // allocated while `elementCount` is the number of elements that were actually constructed;
|
jpayne@69
|
47 // these are always the same number for Array<T> but may be different when using ArrayBuilder<T>.
|
jpayne@69
|
48
|
jpayne@69
|
49 public:
|
jpayne@69
|
50
|
jpayne@69
|
51 template <typename T>
|
jpayne@69
|
52 void dispose(T* firstElement, size_t elementCount, size_t capacity) const;
|
jpayne@69
|
53 // Helper wrapper around disposeImpl().
|
jpayne@69
|
54 //
|
jpayne@69
|
55 // Callers must not call dispose() on the same array twice, even if the first call throws
|
jpayne@69
|
56 // an exception.
|
jpayne@69
|
57
|
jpayne@69
|
58 private:
|
jpayne@69
|
59 template <typename T, bool hasTrivialDestructor = KJ_HAS_TRIVIAL_DESTRUCTOR(T)>
|
jpayne@69
|
60 struct Dispose_;
|
jpayne@69
|
61 };
|
jpayne@69
|
62
|
jpayne@69
|
63 class ExceptionSafeArrayUtil {
|
jpayne@69
|
64 // Utility class that assists in constructing or destroying elements of an array, where the
|
jpayne@69
|
65 // constructor or destructor could throw exceptions. In case of an exception,
|
jpayne@69
|
66 // ExceptionSafeArrayUtil's destructor will call destructors on all elements that have been
|
jpayne@69
|
67 // constructed but not destroyed. Remember that destructors that throw exceptions are required
|
jpayne@69
|
68 // to use UnwindDetector to detect unwind and avoid exceptions in this case. Therefore, no more
|
jpayne@69
|
69 // than one exception will be thrown (and the program will not terminate).
|
jpayne@69
|
70
|
jpayne@69
|
71 public:
|
jpayne@69
|
72 inline ExceptionSafeArrayUtil(void* ptr, size_t elementSize, size_t constructedElementCount,
|
jpayne@69
|
73 void (*destroyElement)(void*))
|
jpayne@69
|
74 : pos(reinterpret_cast<byte*>(ptr) + elementSize * constructedElementCount),
|
jpayne@69
|
75 elementSize(elementSize), constructedElementCount(constructedElementCount),
|
jpayne@69
|
76 destroyElement(destroyElement) {}
|
jpayne@69
|
77 KJ_DISALLOW_COPY_AND_MOVE(ExceptionSafeArrayUtil);
|
jpayne@69
|
78
|
jpayne@69
|
79 inline ~ExceptionSafeArrayUtil() noexcept(false) {
|
jpayne@69
|
80 if (constructedElementCount > 0) destroyAll();
|
jpayne@69
|
81 }
|
jpayne@69
|
82
|
jpayne@69
|
83 void construct(size_t count, void (*constructElement)(void*));
|
jpayne@69
|
84 // Construct the given number of elements.
|
jpayne@69
|
85
|
jpayne@69
|
86 void destroyAll();
|
jpayne@69
|
87 // Destroy all elements. Call this immediately before ExceptionSafeArrayUtil goes out-of-scope
|
jpayne@69
|
88 // to ensure that one element throwing an exception does not prevent the others from being
|
jpayne@69
|
89 // destroyed.
|
jpayne@69
|
90
|
jpayne@69
|
91 void release() { constructedElementCount = 0; }
|
jpayne@69
|
92 // Prevent ExceptionSafeArrayUtil's destructor from destroying the constructed elements.
|
jpayne@69
|
93 // Call this after you've successfully finished constructing.
|
jpayne@69
|
94
|
jpayne@69
|
95 private:
|
jpayne@69
|
96 byte* pos;
|
jpayne@69
|
97 size_t elementSize;
|
jpayne@69
|
98 size_t constructedElementCount;
|
jpayne@69
|
99 void (*destroyElement)(void*);
|
jpayne@69
|
100 };
|
jpayne@69
|
101
|
jpayne@69
|
102 class DestructorOnlyArrayDisposer: public ArrayDisposer {
|
jpayne@69
|
103 public:
|
jpayne@69
|
104 static const DestructorOnlyArrayDisposer instance;
|
jpayne@69
|
105
|
jpayne@69
|
106 void disposeImpl(void* firstElement, size_t elementSize, size_t elementCount,
|
jpayne@69
|
107 size_t capacity, void (*destroyElement)(void*)) const override;
|
jpayne@69
|
108 };
|
jpayne@69
|
109
|
jpayne@69
|
110 class NullArrayDisposer: public ArrayDisposer {
|
jpayne@69
|
111 // An ArrayDisposer that does nothing. Can be used to construct a fake Arrays that doesn't
|
jpayne@69
|
112 // actually own its content.
|
jpayne@69
|
113
|
jpayne@69
|
114 public:
|
jpayne@69
|
115 static const NullArrayDisposer instance;
|
jpayne@69
|
116
|
jpayne@69
|
117 void disposeImpl(void* firstElement, size_t elementSize, size_t elementCount,
|
jpayne@69
|
118 size_t capacity, void (*destroyElement)(void*)) const override;
|
jpayne@69
|
119 };
|
jpayne@69
|
120
|
jpayne@69
|
121 // =======================================================================================
|
jpayne@69
|
122 // Array
|
jpayne@69
|
123
|
jpayne@69
|
124 template <typename T>
|
jpayne@69
|
125 class Array {
|
jpayne@69
|
126 // An owned array which will automatically be disposed of (using an ArrayDisposer) in the
|
jpayne@69
|
127 // destructor. Can be moved, but not copied. Much like Own<T>, but for arrays rather than
|
jpayne@69
|
128 // single objects.
|
jpayne@69
|
129
|
jpayne@69
|
130 public:
|
jpayne@69
|
131 inline Array(): ptr(nullptr), size_(0), disposer(nullptr) {}
|
jpayne@69
|
132 inline Array(decltype(nullptr)): ptr(nullptr), size_(0), disposer(nullptr) {}
|
jpayne@69
|
133 inline Array(Array&& other) noexcept
|
jpayne@69
|
134 : ptr(other.ptr), size_(other.size_), disposer(other.disposer) {
|
jpayne@69
|
135 other.ptr = nullptr;
|
jpayne@69
|
136 other.size_ = 0;
|
jpayne@69
|
137 }
|
jpayne@69
|
138 inline Array(Array<RemoveConstOrDisable<T>>&& other) noexcept
|
jpayne@69
|
139 : ptr(other.ptr), size_(other.size_), disposer(other.disposer) {
|
jpayne@69
|
140 other.ptr = nullptr;
|
jpayne@69
|
141 other.size_ = 0;
|
jpayne@69
|
142 }
|
jpayne@69
|
143 inline Array(T* firstElement KJ_LIFETIMEBOUND, size_t size, const ArrayDisposer& disposer)
|
jpayne@69
|
144 : ptr(firstElement), size_(size), disposer(&disposer) {}
|
jpayne@69
|
145
|
jpayne@69
|
146 KJ_DISALLOW_COPY(Array);
|
jpayne@69
|
147 inline ~Array() noexcept { dispose(); }
|
jpayne@69
|
148
|
jpayne@69
|
149 inline operator ArrayPtr<T>() KJ_LIFETIMEBOUND {
|
jpayne@69
|
150 return ArrayPtr<T>(ptr, size_);
|
jpayne@69
|
151 }
|
jpayne@69
|
152 inline operator ArrayPtr<const T>() const KJ_LIFETIMEBOUND {
|
jpayne@69
|
153 return ArrayPtr<T>(ptr, size_);
|
jpayne@69
|
154 }
|
jpayne@69
|
155 inline ArrayPtr<T> asPtr() KJ_LIFETIMEBOUND {
|
jpayne@69
|
156 return ArrayPtr<T>(ptr, size_);
|
jpayne@69
|
157 }
|
jpayne@69
|
158 inline ArrayPtr<const T> asPtr() const KJ_LIFETIMEBOUND {
|
jpayne@69
|
159 return ArrayPtr<T>(ptr, size_);
|
jpayne@69
|
160 }
|
jpayne@69
|
161
|
jpayne@69
|
162 inline size_t size() const { return size_; }
|
jpayne@69
|
163 inline T& operator[](size_t index) KJ_LIFETIMEBOUND {
|
jpayne@69
|
164 KJ_IREQUIRE(index < size_, "Out-of-bounds Array access.");
|
jpayne@69
|
165 return ptr[index];
|
jpayne@69
|
166 }
|
jpayne@69
|
167 inline const T& operator[](size_t index) const KJ_LIFETIMEBOUND {
|
jpayne@69
|
168 KJ_IREQUIRE(index < size_, "Out-of-bounds Array access.");
|
jpayne@69
|
169 return ptr[index];
|
jpayne@69
|
170 }
|
jpayne@69
|
171
|
jpayne@69
|
172 inline const T* begin() const KJ_LIFETIMEBOUND { return ptr; }
|
jpayne@69
|
173 inline const T* end() const KJ_LIFETIMEBOUND { return ptr + size_; }
|
jpayne@69
|
174 inline const T& front() const KJ_LIFETIMEBOUND { return *ptr; }
|
jpayne@69
|
175 inline const T& back() const KJ_LIFETIMEBOUND { return *(ptr + size_ - 1); }
|
jpayne@69
|
176 inline T* begin() KJ_LIFETIMEBOUND { return ptr; }
|
jpayne@69
|
177 inline T* end() KJ_LIFETIMEBOUND { return ptr + size_; }
|
jpayne@69
|
178 inline T& front() KJ_LIFETIMEBOUND { return *ptr; }
|
jpayne@69
|
179 inline T& back() KJ_LIFETIMEBOUND { return *(ptr + size_ - 1); }
|
jpayne@69
|
180
|
jpayne@69
|
181 template <typename U>
|
jpayne@69
|
182 inline bool operator==(const U& other) const { return asPtr() == other; }
|
jpayne@69
|
183 template <typename U>
|
jpayne@69
|
184 inline bool operator!=(const U& other) const { return asPtr() != other; }
|
jpayne@69
|
185
|
jpayne@69
|
186 inline ArrayPtr<T> slice(size_t start, size_t end) KJ_LIFETIMEBOUND {
|
jpayne@69
|
187 KJ_IREQUIRE(start <= end && end <= size_, "Out-of-bounds Array::slice().");
|
jpayne@69
|
188 return ArrayPtr<T>(ptr + start, end - start);
|
jpayne@69
|
189 }
|
jpayne@69
|
190 inline ArrayPtr<const T> slice(size_t start, size_t end) const KJ_LIFETIMEBOUND {
|
jpayne@69
|
191 KJ_IREQUIRE(start <= end && end <= size_, "Out-of-bounds Array::slice().");
|
jpayne@69
|
192 return ArrayPtr<const T>(ptr + start, end - start);
|
jpayne@69
|
193 }
|
jpayne@69
|
194
|
jpayne@69
|
195 inline ArrayPtr<const byte> asBytes() const KJ_LIFETIMEBOUND { return asPtr().asBytes(); }
|
jpayne@69
|
196 inline ArrayPtr<PropagateConst<T, byte>> asBytes() KJ_LIFETIMEBOUND { return asPtr().asBytes(); }
|
jpayne@69
|
197 inline ArrayPtr<const char> asChars() const KJ_LIFETIMEBOUND { return asPtr().asChars(); }
|
jpayne@69
|
198 inline ArrayPtr<PropagateConst<T, char>> asChars() KJ_LIFETIMEBOUND { return asPtr().asChars(); }
|
jpayne@69
|
199
|
jpayne@69
|
200 inline Array<PropagateConst<T, byte>> releaseAsBytes() {
|
jpayne@69
|
201 // Like asBytes() but transfers ownership.
|
jpayne@69
|
202 static_assert(sizeof(T) == sizeof(byte),
|
jpayne@69
|
203 "releaseAsBytes() only possible on arrays with byte-size elements (e.g. chars).");
|
jpayne@69
|
204 Array<PropagateConst<T, byte>> result(
|
jpayne@69
|
205 reinterpret_cast<PropagateConst<T, byte>*>(ptr), size_, *disposer);
|
jpayne@69
|
206 ptr = nullptr;
|
jpayne@69
|
207 size_ = 0;
|
jpayne@69
|
208 return result;
|
jpayne@69
|
209 }
|
jpayne@69
|
210 inline Array<PropagateConst<T, char>> releaseAsChars() {
|
jpayne@69
|
211 // Like asChars() but transfers ownership.
|
jpayne@69
|
212 static_assert(sizeof(T) == sizeof(PropagateConst<T, char>),
|
jpayne@69
|
213 "releaseAsChars() only possible on arrays with char-size elements (e.g. bytes).");
|
jpayne@69
|
214 Array<PropagateConst<T, char>> result(
|
jpayne@69
|
215 reinterpret_cast<PropagateConst<T, char>*>(ptr), size_, *disposer);
|
jpayne@69
|
216 ptr = nullptr;
|
jpayne@69
|
217 size_ = 0;
|
jpayne@69
|
218 return result;
|
jpayne@69
|
219 }
|
jpayne@69
|
220
|
jpayne@69
|
221 inline bool operator==(decltype(nullptr)) const { return size_ == 0; }
|
jpayne@69
|
222 inline bool operator!=(decltype(nullptr)) const { return size_ != 0; }
|
jpayne@69
|
223
|
jpayne@69
|
224 inline Array& operator=(decltype(nullptr)) {
|
jpayne@69
|
225 dispose();
|
jpayne@69
|
226 return *this;
|
jpayne@69
|
227 }
|
jpayne@69
|
228
|
jpayne@69
|
229 inline Array& operator=(Array&& other) {
|
jpayne@69
|
230 dispose();
|
jpayne@69
|
231 ptr = other.ptr;
|
jpayne@69
|
232 size_ = other.size_;
|
jpayne@69
|
233 disposer = other.disposer;
|
jpayne@69
|
234 other.ptr = nullptr;
|
jpayne@69
|
235 other.size_ = 0;
|
jpayne@69
|
236 return *this;
|
jpayne@69
|
237 }
|
jpayne@69
|
238
|
jpayne@69
|
239 template <typename... Attachments>
|
jpayne@69
|
240 Array<T> attach(Attachments&&... attachments) KJ_WARN_UNUSED_RESULT;
|
jpayne@69
|
241 // Like Own<T>::attach(), but attaches to an Array.
|
jpayne@69
|
242
|
jpayne@69
|
243 private:
|
jpayne@69
|
244 T* ptr;
|
jpayne@69
|
245 size_t size_;
|
jpayne@69
|
246 const ArrayDisposer* disposer;
|
jpayne@69
|
247
|
jpayne@69
|
248 inline void dispose() {
|
jpayne@69
|
249 // Make sure that if an exception is thrown, we are left with a null ptr, so we won't possibly
|
jpayne@69
|
250 // dispose again.
|
jpayne@69
|
251 T* ptrCopy = ptr;
|
jpayne@69
|
252 size_t sizeCopy = size_;
|
jpayne@69
|
253 if (ptrCopy != nullptr) {
|
jpayne@69
|
254 ptr = nullptr;
|
jpayne@69
|
255 size_ = 0;
|
jpayne@69
|
256 disposer->dispose(ptrCopy, sizeCopy, sizeCopy);
|
jpayne@69
|
257 }
|
jpayne@69
|
258 }
|
jpayne@69
|
259
|
jpayne@69
|
260 template <typename U>
|
jpayne@69
|
261 friend class Array;
|
jpayne@69
|
262 template <typename U>
|
jpayne@69
|
263 friend class ArrayBuilder;
|
jpayne@69
|
264 };
|
jpayne@69
|
265
|
jpayne@69
|
266 static_assert(!canMemcpy<Array<char>>(), "canMemcpy<>() is broken");
|
jpayne@69
|
267
|
jpayne@69
|
268 namespace _ { // private
|
jpayne@69
|
269
|
jpayne@69
|
270 class HeapArrayDisposer final: public ArrayDisposer {
|
jpayne@69
|
271 public:
|
jpayne@69
|
272 template <typename T>
|
jpayne@69
|
273 static T* allocate(size_t count);
|
jpayne@69
|
274 template <typename T>
|
jpayne@69
|
275 static T* allocateUninitialized(size_t count);
|
jpayne@69
|
276
|
jpayne@69
|
277 static const HeapArrayDisposer instance;
|
jpayne@69
|
278
|
jpayne@69
|
279 private:
|
jpayne@69
|
280 static void* allocateImpl(size_t elementSize, size_t elementCount, size_t capacity,
|
jpayne@69
|
281 void (*constructElement)(void*), void (*destroyElement)(void*));
|
jpayne@69
|
282 // Allocates and constructs the array. Both function pointers are null if the constructor is
|
jpayne@69
|
283 // trivial, otherwise destroyElement is null if the constructor doesn't throw.
|
jpayne@69
|
284
|
jpayne@69
|
285 virtual void disposeImpl(void* firstElement, size_t elementSize, size_t elementCount,
|
jpayne@69
|
286 size_t capacity, void (*destroyElement)(void*)) const override;
|
jpayne@69
|
287
|
jpayne@69
|
288 template <typename T, bool hasTrivialConstructor = KJ_HAS_TRIVIAL_CONSTRUCTOR(T),
|
jpayne@69
|
289 bool hasNothrowConstructor = KJ_HAS_NOTHROW_CONSTRUCTOR(T)>
|
jpayne@69
|
290 struct Allocate_;
|
jpayne@69
|
291 };
|
jpayne@69
|
292
|
jpayne@69
|
293 } // namespace _ (private)
|
jpayne@69
|
294
|
jpayne@69
|
295 template <typename T>
|
jpayne@69
|
296 inline Array<T> heapArray(size_t size) {
|
jpayne@69
|
297 // Much like `heap<T>()` from memory.h, allocates a new array on the heap.
|
jpayne@69
|
298
|
jpayne@69
|
299 return Array<T>(_::HeapArrayDisposer::allocate<T>(size), size,
|
jpayne@69
|
300 _::HeapArrayDisposer::instance);
|
jpayne@69
|
301 }
|
jpayne@69
|
302
|
jpayne@69
|
303 template <typename T> Array<T> heapArray(const T* content, size_t size);
|
jpayne@69
|
304 template <typename T> Array<T> heapArray(ArrayPtr<T> content);
|
jpayne@69
|
305 template <typename T> Array<T> heapArray(ArrayPtr<const T> content);
|
jpayne@69
|
306 template <typename T, typename Iterator> Array<T> heapArray(Iterator begin, Iterator end);
|
jpayne@69
|
307 template <typename T> Array<T> heapArray(std::initializer_list<T> init);
|
jpayne@69
|
308 // Allocate a heap array containing a copy of the given content.
|
jpayne@69
|
309
|
jpayne@69
|
310 template <typename T, typename Container>
|
jpayne@69
|
311 Array<T> heapArrayFromIterable(Container&& a) { return heapArray<T>(a.begin(), a.end()); }
|
jpayne@69
|
312 template <typename T>
|
jpayne@69
|
313 Array<T> heapArrayFromIterable(Array<T>&& a) { return mv(a); }
|
jpayne@69
|
314
|
jpayne@69
|
315 // =======================================================================================
|
jpayne@69
|
316 // ArrayBuilder
|
jpayne@69
|
317
|
jpayne@69
|
318 template <typename T>
|
jpayne@69
|
319 class ArrayBuilder {
|
jpayne@69
|
320 // Class which lets you build an Array<T> specifying the exact constructor arguments for each
|
jpayne@69
|
321 // element, rather than starting by default-constructing them.
|
jpayne@69
|
322
|
jpayne@69
|
323 public:
|
jpayne@69
|
324 ArrayBuilder(): ptr(nullptr), pos(nullptr), endPtr(nullptr) {}
|
jpayne@69
|
325 ArrayBuilder(decltype(nullptr)): ptr(nullptr), pos(nullptr), endPtr(nullptr) {}
|
jpayne@69
|
326 explicit ArrayBuilder(RemoveConst<T>* firstElement, size_t capacity,
|
jpayne@69
|
327 const ArrayDisposer& disposer)
|
jpayne@69
|
328 : ptr(firstElement), pos(firstElement), endPtr(firstElement + capacity),
|
jpayne@69
|
329 disposer(&disposer) {}
|
jpayne@69
|
330 ArrayBuilder(ArrayBuilder&& other)
|
jpayne@69
|
331 : ptr(other.ptr), pos(other.pos), endPtr(other.endPtr), disposer(other.disposer) {
|
jpayne@69
|
332 other.ptr = nullptr;
|
jpayne@69
|
333 other.pos = nullptr;
|
jpayne@69
|
334 other.endPtr = nullptr;
|
jpayne@69
|
335 }
|
jpayne@69
|
336 ArrayBuilder(Array<T>&& other)
|
jpayne@69
|
337 : ptr(other.ptr), pos(other.ptr + other.size_), endPtr(pos), disposer(other.disposer) {
|
jpayne@69
|
338 // Create an already-full ArrayBuilder from an Array of the same type. This constructor
|
jpayne@69
|
339 // primarily exists to enable Vector<T> to be constructed from Array<T>.
|
jpayne@69
|
340 other.ptr = nullptr;
|
jpayne@69
|
341 other.size_ = 0;
|
jpayne@69
|
342 }
|
jpayne@69
|
343 KJ_DISALLOW_COPY(ArrayBuilder);
|
jpayne@69
|
344 inline ~ArrayBuilder() noexcept(false) { dispose(); }
|
jpayne@69
|
345
|
jpayne@69
|
346 inline operator ArrayPtr<T>() KJ_LIFETIMEBOUND {
|
jpayne@69
|
347 return arrayPtr(ptr, pos);
|
jpayne@69
|
348 }
|
jpayne@69
|
349 inline operator ArrayPtr<const T>() const KJ_LIFETIMEBOUND {
|
jpayne@69
|
350 return arrayPtr(ptr, pos);
|
jpayne@69
|
351 }
|
jpayne@69
|
352 inline ArrayPtr<T> asPtr() KJ_LIFETIMEBOUND {
|
jpayne@69
|
353 return arrayPtr(ptr, pos);
|
jpayne@69
|
354 }
|
jpayne@69
|
355 inline ArrayPtr<const T> asPtr() const KJ_LIFETIMEBOUND {
|
jpayne@69
|
356 return arrayPtr(ptr, pos);
|
jpayne@69
|
357 }
|
jpayne@69
|
358
|
jpayne@69
|
359 inline size_t size() const { return pos - ptr; }
|
jpayne@69
|
360 inline size_t capacity() const { return endPtr - ptr; }
|
jpayne@69
|
361 inline T& operator[](size_t index) KJ_LIFETIMEBOUND {
|
jpayne@69
|
362 KJ_IREQUIRE(index < implicitCast<size_t>(pos - ptr), "Out-of-bounds Array access.");
|
jpayne@69
|
363 return ptr[index];
|
jpayne@69
|
364 }
|
jpayne@69
|
365 inline const T& operator[](size_t index) const KJ_LIFETIMEBOUND {
|
jpayne@69
|
366 KJ_IREQUIRE(index < implicitCast<size_t>(pos - ptr), "Out-of-bounds Array access.");
|
jpayne@69
|
367 return ptr[index];
|
jpayne@69
|
368 }
|
jpayne@69
|
369
|
jpayne@69
|
370 inline const T* begin() const KJ_LIFETIMEBOUND { return ptr; }
|
jpayne@69
|
371 inline const T* end() const KJ_LIFETIMEBOUND { return pos; }
|
jpayne@69
|
372 inline const T& front() const KJ_LIFETIMEBOUND { return *ptr; }
|
jpayne@69
|
373 inline const T& back() const KJ_LIFETIMEBOUND { return *(pos - 1); }
|
jpayne@69
|
374 inline T* begin() KJ_LIFETIMEBOUND { return ptr; }
|
jpayne@69
|
375 inline T* end() KJ_LIFETIMEBOUND { return pos; }
|
jpayne@69
|
376 inline T& front() KJ_LIFETIMEBOUND { return *ptr; }
|
jpayne@69
|
377 inline T& back() KJ_LIFETIMEBOUND { return *(pos - 1); }
|
jpayne@69
|
378
|
jpayne@69
|
379 ArrayBuilder& operator=(ArrayBuilder&& other) {
|
jpayne@69
|
380 dispose();
|
jpayne@69
|
381 ptr = other.ptr;
|
jpayne@69
|
382 pos = other.pos;
|
jpayne@69
|
383 endPtr = other.endPtr;
|
jpayne@69
|
384 disposer = other.disposer;
|
jpayne@69
|
385 other.ptr = nullptr;
|
jpayne@69
|
386 other.pos = nullptr;
|
jpayne@69
|
387 other.endPtr = nullptr;
|
jpayne@69
|
388 return *this;
|
jpayne@69
|
389 }
|
jpayne@69
|
390 ArrayBuilder& operator=(decltype(nullptr)) {
|
jpayne@69
|
391 dispose();
|
jpayne@69
|
392 return *this;
|
jpayne@69
|
393 }
|
jpayne@69
|
394
|
jpayne@69
|
395 template <typename... Params>
|
jpayne@69
|
396 T& add(Params&&... params) KJ_LIFETIMEBOUND {
|
jpayne@69
|
397 KJ_IREQUIRE(pos < endPtr, "Added too many elements to ArrayBuilder.");
|
jpayne@69
|
398 ctor(*pos, kj::fwd<Params>(params)...);
|
jpayne@69
|
399 return *pos++;
|
jpayne@69
|
400 }
|
jpayne@69
|
401
|
jpayne@69
|
402 template <typename Container>
|
jpayne@69
|
403 void addAll(Container&& container) {
|
jpayne@69
|
404 addAll<decltype(container.begin()), !isReference<Container>()>(
|
jpayne@69
|
405 container.begin(), container.end());
|
jpayne@69
|
406 }
|
jpayne@69
|
407
|
jpayne@69
|
408 template <typename Iterator, bool move = false>
|
jpayne@69
|
409 void addAll(Iterator start, Iterator end);
|
jpayne@69
|
410
|
jpayne@69
|
411 void removeLast() {
|
jpayne@69
|
412 KJ_IREQUIRE(pos > ptr, "No elements present to remove.");
|
jpayne@69
|
413 kj::dtor(*--pos);
|
jpayne@69
|
414 }
|
jpayne@69
|
415
|
jpayne@69
|
416 void truncate(size_t size) {
|
jpayne@69
|
417 KJ_IREQUIRE(size <= this->size(), "can't use truncate() to expand");
|
jpayne@69
|
418
|
jpayne@69
|
419 T* target = ptr + size;
|
jpayne@69
|
420 if (KJ_HAS_TRIVIAL_DESTRUCTOR(T)) {
|
jpayne@69
|
421 pos = target;
|
jpayne@69
|
422 } else {
|
jpayne@69
|
423 while (pos > target) {
|
jpayne@69
|
424 kj::dtor(*--pos);
|
jpayne@69
|
425 }
|
jpayne@69
|
426 }
|
jpayne@69
|
427 }
|
jpayne@69
|
428
|
jpayne@69
|
429 void clear() {
|
jpayne@69
|
430 if (KJ_HAS_TRIVIAL_DESTRUCTOR(T)) {
|
jpayne@69
|
431 pos = ptr;
|
jpayne@69
|
432 } else {
|
jpayne@69
|
433 while (pos > ptr) {
|
jpayne@69
|
434 kj::dtor(*--pos);
|
jpayne@69
|
435 }
|
jpayne@69
|
436 }
|
jpayne@69
|
437 }
|
jpayne@69
|
438
|
jpayne@69
|
439 void resize(size_t size) {
|
jpayne@69
|
440 KJ_IREQUIRE(size <= capacity(), "can't resize past capacity");
|
jpayne@69
|
441
|
jpayne@69
|
442 T* target = ptr + size;
|
jpayne@69
|
443 if (target > pos) {
|
jpayne@69
|
444 // expand
|
jpayne@69
|
445 if (KJ_HAS_TRIVIAL_CONSTRUCTOR(T)) {
|
jpayne@69
|
446 pos = target;
|
jpayne@69
|
447 } else {
|
jpayne@69
|
448 while (pos < target) {
|
jpayne@69
|
449 kj::ctor(*pos++);
|
jpayne@69
|
450 }
|
jpayne@69
|
451 }
|
jpayne@69
|
452 } else {
|
jpayne@69
|
453 // truncate
|
jpayne@69
|
454 if (KJ_HAS_TRIVIAL_DESTRUCTOR(T)) {
|
jpayne@69
|
455 pos = target;
|
jpayne@69
|
456 } else {
|
jpayne@69
|
457 while (pos > target) {
|
jpayne@69
|
458 kj::dtor(*--pos);
|
jpayne@69
|
459 }
|
jpayne@69
|
460 }
|
jpayne@69
|
461 }
|
jpayne@69
|
462 }
|
jpayne@69
|
463
|
jpayne@69
|
464 Array<T> finish() {
|
jpayne@69
|
465 // We could safely remove this check if we assume that the disposer implementation doesn't
|
jpayne@69
|
466 // need to know the original capacity, as is the case with HeapArrayDisposer since it uses
|
jpayne@69
|
467 // operator new() or if we created a custom disposer for ArrayBuilder which stores the capacity
|
jpayne@69
|
468 // in a prefix. But that would make it hard to write cleverer heap allocators, and anyway this
|
jpayne@69
|
469 // check might catch bugs. Probably people should use Vector if they want to build arrays
|
jpayne@69
|
470 // without knowing the final size in advance.
|
jpayne@69
|
471 KJ_IREQUIRE(pos == endPtr, "ArrayBuilder::finish() called prematurely.");
|
jpayne@69
|
472 Array<T> result(reinterpret_cast<T*>(ptr), pos - ptr, *disposer);
|
jpayne@69
|
473 ptr = nullptr;
|
jpayne@69
|
474 pos = nullptr;
|
jpayne@69
|
475 endPtr = nullptr;
|
jpayne@69
|
476 return result;
|
jpayne@69
|
477 }
|
jpayne@69
|
478
|
jpayne@69
|
479 inline bool isFull() const {
|
jpayne@69
|
480 return pos == endPtr;
|
jpayne@69
|
481 }
|
jpayne@69
|
482
|
jpayne@69
|
483 private:
|
jpayne@69
|
484 T* ptr;
|
jpayne@69
|
485 RemoveConst<T>* pos;
|
jpayne@69
|
486 T* endPtr;
|
jpayne@69
|
487 const ArrayDisposer* disposer = &NullArrayDisposer::instance;
|
jpayne@69
|
488
|
jpayne@69
|
489 inline void dispose() {
|
jpayne@69
|
490 // Make sure that if an exception is thrown, we are left with a null ptr, so we won't possibly
|
jpayne@69
|
491 // dispose again.
|
jpayne@69
|
492 T* ptrCopy = ptr;
|
jpayne@69
|
493 T* posCopy = pos;
|
jpayne@69
|
494 T* endCopy = endPtr;
|
jpayne@69
|
495 if (ptrCopy != nullptr) {
|
jpayne@69
|
496 ptr = nullptr;
|
jpayne@69
|
497 pos = nullptr;
|
jpayne@69
|
498 endPtr = nullptr;
|
jpayne@69
|
499 disposer->dispose(ptrCopy, posCopy - ptrCopy, endCopy - ptrCopy);
|
jpayne@69
|
500 }
|
jpayne@69
|
501 }
|
jpayne@69
|
502 };
|
jpayne@69
|
503
|
jpayne@69
|
504 template <typename T>
|
jpayne@69
|
505 inline ArrayBuilder<T> heapArrayBuilder(size_t size) {
|
jpayne@69
|
506 // Like `heapArray<T>()` but does not default-construct the elements. You must construct them
|
jpayne@69
|
507 // manually by calling `add()`.
|
jpayne@69
|
508
|
jpayne@69
|
509 return ArrayBuilder<T>(_::HeapArrayDisposer::allocateUninitialized<RemoveConst<T>>(size),
|
jpayne@69
|
510 size, _::HeapArrayDisposer::instance);
|
jpayne@69
|
511 }
|
jpayne@69
|
512
|
jpayne@69
|
513 // =======================================================================================
|
jpayne@69
|
514 // Inline Arrays
|
jpayne@69
|
515
|
jpayne@69
|
516 template <typename T, size_t fixedSize>
|
jpayne@69
|
517 class FixedArray {
|
jpayne@69
|
518 // A fixed-width array whose storage is allocated inline rather than on the heap.
|
jpayne@69
|
519
|
jpayne@69
|
520 public:
|
jpayne@69
|
521 inline constexpr size_t size() const { return fixedSize; }
|
jpayne@69
|
522 inline constexpr T* begin() KJ_LIFETIMEBOUND { return content; }
|
jpayne@69
|
523 inline constexpr T* end() KJ_LIFETIMEBOUND { return content + fixedSize; }
|
jpayne@69
|
524 inline constexpr const T* begin() const KJ_LIFETIMEBOUND { return content; }
|
jpayne@69
|
525 inline constexpr const T* end() const KJ_LIFETIMEBOUND { return content + fixedSize; }
|
jpayne@69
|
526
|
jpayne@69
|
527 inline constexpr operator ArrayPtr<T>() KJ_LIFETIMEBOUND {
|
jpayne@69
|
528 return arrayPtr(content, fixedSize);
|
jpayne@69
|
529 }
|
jpayne@69
|
530 inline constexpr operator ArrayPtr<const T>() const KJ_LIFETIMEBOUND {
|
jpayne@69
|
531 return arrayPtr(content, fixedSize);
|
jpayne@69
|
532 }
|
jpayne@69
|
533
|
jpayne@69
|
534 inline constexpr T& operator[](size_t index) KJ_LIFETIMEBOUND { return content[index]; }
|
jpayne@69
|
535 inline constexpr const T& operator[](size_t index) const KJ_LIFETIMEBOUND {
|
jpayne@69
|
536 return content[index];
|
jpayne@69
|
537 }
|
jpayne@69
|
538
|
jpayne@69
|
539 private:
|
jpayne@69
|
540 T content[fixedSize];
|
jpayne@69
|
541 };
|
jpayne@69
|
542
|
jpayne@69
|
543 template <typename T, size_t fixedSize>
|
jpayne@69
|
544 class CappedArray {
|
jpayne@69
|
545 // Like `FixedArray` but can be dynamically resized as long as the size does not exceed the limit
|
jpayne@69
|
546 // specified by the template parameter.
|
jpayne@69
|
547 //
|
jpayne@69
|
548 // TODO(someday): Don't construct elements past currentSize?
|
jpayne@69
|
549
|
jpayne@69
|
550 public:
|
jpayne@69
|
551 inline KJ_CONSTEXPR() CappedArray(): currentSize(fixedSize) {}
|
jpayne@69
|
552 inline explicit constexpr CappedArray(size_t s): currentSize(s) {}
|
jpayne@69
|
553
|
jpayne@69
|
554 inline size_t size() const { return currentSize; }
|
jpayne@69
|
555 inline void setSize(size_t s) { KJ_IREQUIRE(s <= fixedSize); currentSize = s; }
|
jpayne@69
|
556 inline T* begin() KJ_LIFETIMEBOUND { return content; }
|
jpayne@69
|
557 inline T* end() KJ_LIFETIMEBOUND { return content + currentSize; }
|
jpayne@69
|
558 inline const T* begin() const KJ_LIFETIMEBOUND { return content; }
|
jpayne@69
|
559 inline const T* end() const KJ_LIFETIMEBOUND { return content + currentSize; }
|
jpayne@69
|
560
|
jpayne@69
|
561 inline operator ArrayPtr<T>() KJ_LIFETIMEBOUND {
|
jpayne@69
|
562 return arrayPtr(content, currentSize);
|
jpayne@69
|
563 }
|
jpayne@69
|
564 inline operator ArrayPtr<const T>() const KJ_LIFETIMEBOUND {
|
jpayne@69
|
565 return arrayPtr(content, currentSize);
|
jpayne@69
|
566 }
|
jpayne@69
|
567
|
jpayne@69
|
568 inline T& operator[](size_t index) KJ_LIFETIMEBOUND { return content[index]; }
|
jpayne@69
|
569 inline const T& operator[](size_t index) const KJ_LIFETIMEBOUND { return content[index]; }
|
jpayne@69
|
570
|
jpayne@69
|
571 private:
|
jpayne@69
|
572 size_t currentSize;
|
jpayne@69
|
573 T content[fixedSize];
|
jpayne@69
|
574 };
|
jpayne@69
|
575
|
jpayne@69
|
576 // =======================================================================================
|
jpayne@69
|
577 // KJ_MAP
|
jpayne@69
|
578
|
jpayne@69
|
579 #define KJ_MAP(elementName, array) \
|
jpayne@69
|
580 ::kj::_::Mapper<KJ_DECLTYPE_REF(array)>(array) * \
|
jpayne@69
|
581 [&](typename ::kj::_::Mapper<KJ_DECLTYPE_REF(array)>::Element elementName)
|
jpayne@69
|
582 // Applies some function to every element of an array, returning an Array of the results, with
|
jpayne@69
|
583 // nice syntax. Example:
|
jpayne@69
|
584 //
|
jpayne@69
|
585 // StringPtr foo = "abcd";
|
jpayne@69
|
586 // Array<char> bar = KJ_MAP(c, foo) -> char { return c + 1; };
|
jpayne@69
|
587 // KJ_ASSERT(str(bar) == "bcde");
|
jpayne@69
|
588
|
jpayne@69
|
589 namespace _ { // private
|
jpayne@69
|
590
|
jpayne@69
|
591 template <typename T>
|
jpayne@69
|
592 struct Mapper {
|
jpayne@69
|
593 T array;
|
jpayne@69
|
594 Mapper(T&& array): array(kj::fwd<T>(array)) {}
|
jpayne@69
|
595 template <typename Func>
|
jpayne@69
|
596 auto operator*(Func&& func) -> Array<decltype(func(*array.begin()))> {
|
jpayne@69
|
597 auto builder = heapArrayBuilder<decltype(func(*array.begin()))>(array.size());
|
jpayne@69
|
598 for (auto iter = array.begin(); iter != array.end(); ++iter) {
|
jpayne@69
|
599 builder.add(func(*iter));
|
jpayne@69
|
600 }
|
jpayne@69
|
601 return builder.finish();
|
jpayne@69
|
602 }
|
jpayne@69
|
603 typedef decltype(*kj::instance<T>().begin()) Element;
|
jpayne@69
|
604 };
|
jpayne@69
|
605
|
jpayne@69
|
606 template <typename T, size_t s>
|
jpayne@69
|
607 struct Mapper<T(&)[s]> {
|
jpayne@69
|
608 T* array;
|
jpayne@69
|
609 Mapper(T* array): array(array) {}
|
jpayne@69
|
610 template <typename Func>
|
jpayne@69
|
611 auto operator*(Func&& func) -> Array<decltype(func(*array))> {
|
jpayne@69
|
612 auto builder = heapArrayBuilder<decltype(func(*array))>(s);
|
jpayne@69
|
613 for (size_t i = 0; i < s; i++) {
|
jpayne@69
|
614 builder.add(func(array[i]));
|
jpayne@69
|
615 }
|
jpayne@69
|
616 return builder.finish();
|
jpayne@69
|
617 }
|
jpayne@69
|
618 typedef decltype(*array)& Element;
|
jpayne@69
|
619 };
|
jpayne@69
|
620
|
jpayne@69
|
621 } // namespace _ (private)
|
jpayne@69
|
622
|
jpayne@69
|
623 // =======================================================================================
|
jpayne@69
|
624 // Inline implementation details
|
jpayne@69
|
625
|
jpayne@69
|
626 template <typename T>
|
jpayne@69
|
627 struct ArrayDisposer::Dispose_<T, true> {
|
jpayne@69
|
628 static void dispose(T* firstElement, size_t elementCount, size_t capacity,
|
jpayne@69
|
629 const ArrayDisposer& disposer) {
|
jpayne@69
|
630 disposer.disposeImpl(const_cast<RemoveConst<T>*>(firstElement),
|
jpayne@69
|
631 sizeof(T), elementCount, capacity, nullptr);
|
jpayne@69
|
632 }
|
jpayne@69
|
633 };
|
jpayne@69
|
634 template <typename T>
|
jpayne@69
|
635 struct ArrayDisposer::Dispose_<T, false> {
|
jpayne@69
|
636 static void destruct(void* ptr) {
|
jpayne@69
|
637 kj::dtor(*reinterpret_cast<T*>(ptr));
|
jpayne@69
|
638 }
|
jpayne@69
|
639
|
jpayne@69
|
640 static void dispose(T* firstElement, size_t elementCount, size_t capacity,
|
jpayne@69
|
641 const ArrayDisposer& disposer) {
|
jpayne@69
|
642 disposer.disposeImpl(const_cast<RemoveConst<T>*>(firstElement),
|
jpayne@69
|
643 sizeof(T), elementCount, capacity, &destruct);
|
jpayne@69
|
644 }
|
jpayne@69
|
645 };
|
jpayne@69
|
646
|
jpayne@69
|
647 template <typename T>
|
jpayne@69
|
648 void ArrayDisposer::dispose(T* firstElement, size_t elementCount, size_t capacity) const {
|
jpayne@69
|
649 Dispose_<T>::dispose(firstElement, elementCount, capacity, *this);
|
jpayne@69
|
650 }
|
jpayne@69
|
651
|
jpayne@69
|
652 namespace _ { // private
|
jpayne@69
|
653
|
jpayne@69
|
654 template <typename T>
|
jpayne@69
|
655 struct HeapArrayDisposer::Allocate_<T, true, true> {
|
jpayne@69
|
656 static T* allocate(size_t elementCount, size_t capacity) {
|
jpayne@69
|
657 return reinterpret_cast<T*>(allocateImpl(
|
jpayne@69
|
658 sizeof(T), elementCount, capacity, nullptr, nullptr));
|
jpayne@69
|
659 }
|
jpayne@69
|
660 };
|
jpayne@69
|
661 template <typename T>
|
jpayne@69
|
662 struct HeapArrayDisposer::Allocate_<T, false, true> {
|
jpayne@69
|
663 static void construct(void* ptr) {
|
jpayne@69
|
664 kj::ctor(*reinterpret_cast<T*>(ptr));
|
jpayne@69
|
665 }
|
jpayne@69
|
666 static T* allocate(size_t elementCount, size_t capacity) {
|
jpayne@69
|
667 return reinterpret_cast<T*>(allocateImpl(
|
jpayne@69
|
668 sizeof(T), elementCount, capacity, &construct, nullptr));
|
jpayne@69
|
669 }
|
jpayne@69
|
670 };
|
jpayne@69
|
671 template <typename T>
|
jpayne@69
|
672 struct HeapArrayDisposer::Allocate_<T, false, false> {
|
jpayne@69
|
673 static void construct(void* ptr) {
|
jpayne@69
|
674 kj::ctor(*reinterpret_cast<T*>(ptr));
|
jpayne@69
|
675 }
|
jpayne@69
|
676 static void destruct(void* ptr) {
|
jpayne@69
|
677 kj::dtor(*reinterpret_cast<T*>(ptr));
|
jpayne@69
|
678 }
|
jpayne@69
|
679 static T* allocate(size_t elementCount, size_t capacity) {
|
jpayne@69
|
680 return reinterpret_cast<T*>(allocateImpl(
|
jpayne@69
|
681 sizeof(T), elementCount, capacity, &construct, &destruct));
|
jpayne@69
|
682 }
|
jpayne@69
|
683 };
|
jpayne@69
|
684
|
jpayne@69
|
685 template <typename T>
|
jpayne@69
|
686 T* HeapArrayDisposer::allocate(size_t count) {
|
jpayne@69
|
687 return Allocate_<T>::allocate(count, count);
|
jpayne@69
|
688 }
|
jpayne@69
|
689
|
jpayne@69
|
690 template <typename T>
|
jpayne@69
|
691 T* HeapArrayDisposer::allocateUninitialized(size_t count) {
|
jpayne@69
|
692 return Allocate_<T, true, true>::allocate(0, count);
|
jpayne@69
|
693 }
|
jpayne@69
|
694
|
jpayne@69
|
695 template <typename Element, typename Iterator, bool move, bool = canMemcpy<Element>()>
|
jpayne@69
|
696 struct CopyConstructArray_;
|
jpayne@69
|
697
|
jpayne@69
|
698 template <typename T, bool move>
|
jpayne@69
|
699 struct CopyConstructArray_<T, T*, move, true> {
|
jpayne@69
|
700 static inline T* apply(T* __restrict__ pos, T* start, T* end) {
|
jpayne@69
|
701 if (end != start) {
|
jpayne@69
|
702 memcpy(pos, start, reinterpret_cast<byte*>(end) - reinterpret_cast<byte*>(start));
|
jpayne@69
|
703 }
|
jpayne@69
|
704 return pos + (end - start);
|
jpayne@69
|
705 }
|
jpayne@69
|
706 };
|
jpayne@69
|
707
|
jpayne@69
|
708 template <typename T>
|
jpayne@69
|
709 struct CopyConstructArray_<T, const T*, false, true> {
|
jpayne@69
|
710 static inline T* apply(T* __restrict__ pos, const T* start, const T* end) {
|
jpayne@69
|
711 if (end != start) {
|
jpayne@69
|
712 memcpy(pos, start, reinterpret_cast<const byte*>(end) - reinterpret_cast<const byte*>(start));
|
jpayne@69
|
713 }
|
jpayne@69
|
714 return pos + (end - start);
|
jpayne@69
|
715 }
|
jpayne@69
|
716 };
|
jpayne@69
|
717
|
jpayne@69
|
718 template <typename T, typename Iterator, bool move>
|
jpayne@69
|
719 struct CopyConstructArray_<T, Iterator, move, true> {
|
jpayne@69
|
720 static inline T* apply(T* __restrict__ pos, Iterator start, Iterator end) {
|
jpayne@69
|
721 // Since both the copy constructor and assignment operator are trivial, we know that assignment
|
jpayne@69
|
722 // is equivalent to copy-constructing. So we can make this case somewhat easier for the
|
jpayne@69
|
723 // compiler to optimize.
|
jpayne@69
|
724 while (start != end) {
|
jpayne@69
|
725 *pos++ = *start++;
|
jpayne@69
|
726 }
|
jpayne@69
|
727 return pos;
|
jpayne@69
|
728 }
|
jpayne@69
|
729 };
|
jpayne@69
|
730
|
jpayne@69
|
731 template <typename T, typename Iterator>
|
jpayne@69
|
732 struct CopyConstructArray_<T, Iterator, false, false> {
|
jpayne@69
|
733 struct ExceptionGuard {
|
jpayne@69
|
734 T* start;
|
jpayne@69
|
735 T* pos;
|
jpayne@69
|
736 inline explicit ExceptionGuard(T* pos): start(pos), pos(pos) {}
|
jpayne@69
|
737 ~ExceptionGuard() noexcept(false) {
|
jpayne@69
|
738 while (pos > start) {
|
jpayne@69
|
739 dtor(*--pos);
|
jpayne@69
|
740 }
|
jpayne@69
|
741 }
|
jpayne@69
|
742 };
|
jpayne@69
|
743
|
jpayne@69
|
744 static T* apply(T* __restrict__ pos, Iterator start, Iterator end) {
|
jpayne@69
|
745 // Verify that T can be *implicitly* constructed from the source values.
|
jpayne@69
|
746 if (false) implicitCast<T>(*start);
|
jpayne@69
|
747
|
jpayne@69
|
748 if (noexcept(T(*start))) {
|
jpayne@69
|
749 while (start != end) {
|
jpayne@69
|
750 ctor(*pos++, *start++);
|
jpayne@69
|
751 }
|
jpayne@69
|
752 return pos;
|
jpayne@69
|
753 } else {
|
jpayne@69
|
754 // Crap. This is complicated.
|
jpayne@69
|
755 ExceptionGuard guard(pos);
|
jpayne@69
|
756 while (start != end) {
|
jpayne@69
|
757 ctor(*guard.pos, *start++);
|
jpayne@69
|
758 ++guard.pos;
|
jpayne@69
|
759 }
|
jpayne@69
|
760 guard.start = guard.pos;
|
jpayne@69
|
761 return guard.pos;
|
jpayne@69
|
762 }
|
jpayne@69
|
763 }
|
jpayne@69
|
764 };
|
jpayne@69
|
765
|
jpayne@69
|
766 template <typename T, typename Iterator>
|
jpayne@69
|
767 struct CopyConstructArray_<T, Iterator, true, false> {
|
jpayne@69
|
768 // Actually move-construct.
|
jpayne@69
|
769
|
jpayne@69
|
770 struct ExceptionGuard {
|
jpayne@69
|
771 T* start;
|
jpayne@69
|
772 T* pos;
|
jpayne@69
|
773 inline explicit ExceptionGuard(T* pos): start(pos), pos(pos) {}
|
jpayne@69
|
774 ~ExceptionGuard() noexcept(false) {
|
jpayne@69
|
775 while (pos > start) {
|
jpayne@69
|
776 dtor(*--pos);
|
jpayne@69
|
777 }
|
jpayne@69
|
778 }
|
jpayne@69
|
779 };
|
jpayne@69
|
780
|
jpayne@69
|
781 static T* apply(T* __restrict__ pos, Iterator start, Iterator end) {
|
jpayne@69
|
782 // Verify that T can be *implicitly* constructed from the source values.
|
jpayne@69
|
783 if (false) implicitCast<T>(kj::mv(*start));
|
jpayne@69
|
784
|
jpayne@69
|
785 if (noexcept(T(kj::mv(*start)))) {
|
jpayne@69
|
786 while (start != end) {
|
jpayne@69
|
787 ctor(*pos++, kj::mv(*start++));
|
jpayne@69
|
788 }
|
jpayne@69
|
789 return pos;
|
jpayne@69
|
790 } else {
|
jpayne@69
|
791 // Crap. This is complicated.
|
jpayne@69
|
792 ExceptionGuard guard(pos);
|
jpayne@69
|
793 while (start != end) {
|
jpayne@69
|
794 ctor(*guard.pos, kj::mv(*start++));
|
jpayne@69
|
795 ++guard.pos;
|
jpayne@69
|
796 }
|
jpayne@69
|
797 guard.start = guard.pos;
|
jpayne@69
|
798 return guard.pos;
|
jpayne@69
|
799 }
|
jpayne@69
|
800 }
|
jpayne@69
|
801 };
|
jpayne@69
|
802
|
jpayne@69
|
803 } // namespace _ (private)
|
jpayne@69
|
804
|
jpayne@69
|
805 template <typename T>
|
jpayne@69
|
806 template <typename Iterator, bool move>
|
jpayne@69
|
807 void ArrayBuilder<T>::addAll(Iterator start, Iterator end) {
|
jpayne@69
|
808 pos = _::CopyConstructArray_<RemoveConst<T>, Decay<Iterator>, move>::apply(pos, start, end);
|
jpayne@69
|
809 }
|
jpayne@69
|
810
|
jpayne@69
|
811 template <typename T>
|
jpayne@69
|
812 Array<T> heapArray(const T* content, size_t size) {
|
jpayne@69
|
813 ArrayBuilder<T> builder = heapArrayBuilder<T>(size);
|
jpayne@69
|
814 builder.addAll(content, content + size);
|
jpayne@69
|
815 return builder.finish();
|
jpayne@69
|
816 }
|
jpayne@69
|
817
|
jpayne@69
|
818 template <typename T>
|
jpayne@69
|
819 Array<T> heapArray(T* content, size_t size) {
|
jpayne@69
|
820 ArrayBuilder<T> builder = heapArrayBuilder<T>(size);
|
jpayne@69
|
821 builder.addAll(content, content + size);
|
jpayne@69
|
822 return builder.finish();
|
jpayne@69
|
823 }
|
jpayne@69
|
824
|
jpayne@69
|
825 template <typename T>
|
jpayne@69
|
826 Array<T> heapArray(ArrayPtr<T> content) {
|
jpayne@69
|
827 ArrayBuilder<T> builder = heapArrayBuilder<T>(content.size());
|
jpayne@69
|
828 builder.addAll(content);
|
jpayne@69
|
829 return builder.finish();
|
jpayne@69
|
830 }
|
jpayne@69
|
831
|
jpayne@69
|
832 template <typename T>
|
jpayne@69
|
833 Array<T> heapArray(ArrayPtr<const T> content) {
|
jpayne@69
|
834 ArrayBuilder<T> builder = heapArrayBuilder<T>(content.size());
|
jpayne@69
|
835 builder.addAll(content);
|
jpayne@69
|
836 return builder.finish();
|
jpayne@69
|
837 }
|
jpayne@69
|
838
|
jpayne@69
|
839 template <typename T, typename Iterator> Array<T>
|
jpayne@69
|
840 heapArray(Iterator begin, Iterator end) {
|
jpayne@69
|
841 ArrayBuilder<T> builder = heapArrayBuilder<T>(end - begin);
|
jpayne@69
|
842 builder.addAll(begin, end);
|
jpayne@69
|
843 return builder.finish();
|
jpayne@69
|
844 }
|
jpayne@69
|
845
|
jpayne@69
|
846 template <typename T>
|
jpayne@69
|
847 inline Array<T> heapArray(std::initializer_list<T> init) {
|
jpayne@69
|
848 return heapArray<T>(init.begin(), init.end());
|
jpayne@69
|
849 }
|
jpayne@69
|
850
|
jpayne@69
|
851 #if KJ_CPP_STD > 201402L
|
jpayne@69
|
852 template <typename T, typename... Params>
|
jpayne@69
|
853 inline Array<Decay<T>> arr(T&& param1, Params&&... params) {
|
jpayne@69
|
854 ArrayBuilder<Decay<T>> builder = heapArrayBuilder<Decay<T>>(sizeof...(params) + 1);
|
jpayne@69
|
855 (builder.add(kj::fwd<T>(param1)), ... , builder.add(kj::fwd<Params>(params)));
|
jpayne@69
|
856 return builder.finish();
|
jpayne@69
|
857 }
|
jpayne@69
|
858 template <typename T, typename... Params>
|
jpayne@69
|
859 inline Array<Decay<T>> arrOf(Params&&... params) {
|
jpayne@69
|
860 ArrayBuilder<Decay<T>> builder = heapArrayBuilder<Decay<T>>(sizeof...(params));
|
jpayne@69
|
861 (... , builder.add(kj::fwd<Params>(params)));
|
jpayne@69
|
862 return builder.finish();
|
jpayne@69
|
863 }
|
jpayne@69
|
864 #endif
|
jpayne@69
|
865
|
jpayne@69
|
866 namespace _ { // private
|
jpayne@69
|
867
|
jpayne@69
|
868 template <typename... T>
|
jpayne@69
|
869 struct ArrayDisposableOwnedBundle final: public ArrayDisposer, public OwnedBundle<T...> {
|
jpayne@69
|
870 ArrayDisposableOwnedBundle(T&&... values): OwnedBundle<T...>(kj::fwd<T>(values)...) {}
|
jpayne@69
|
871 void disposeImpl(void*, size_t, size_t, size_t, void (*)(void*)) const override { delete this; }
|
jpayne@69
|
872 };
|
jpayne@69
|
873
|
jpayne@69
|
874 } // namespace _ (private)
|
jpayne@69
|
875
|
jpayne@69
|
876 template <typename T>
|
jpayne@69
|
877 template <typename... Attachments>
|
jpayne@69
|
878 Array<T> Array<T>::attach(Attachments&&... attachments) {
|
jpayne@69
|
879 T* ptrCopy = ptr;
|
jpayne@69
|
880 auto sizeCopy = size_;
|
jpayne@69
|
881
|
jpayne@69
|
882 KJ_IREQUIRE(ptrCopy != nullptr, "cannot attach to null pointer");
|
jpayne@69
|
883
|
jpayne@69
|
884 // HACK: If someone accidentally calls .attach() on a null pointer in opt mode, try our best to
|
jpayne@69
|
885 // accomplish reasonable behavior: We turn the pointer non-null but still invalid, so that the
|
jpayne@69
|
886 // disposer will still be called when the pointer goes out of scope.
|
jpayne@69
|
887 if (ptrCopy == nullptr) ptrCopy = reinterpret_cast<T*>(1);
|
jpayne@69
|
888
|
jpayne@69
|
889 auto bundle = new _::ArrayDisposableOwnedBundle<Array<T>, Attachments...>(
|
jpayne@69
|
890 kj::mv(*this), kj::fwd<Attachments>(attachments)...);
|
jpayne@69
|
891 return Array<T>(ptrCopy, sizeCopy, *bundle);
|
jpayne@69
|
892 }
|
jpayne@69
|
893
|
jpayne@69
|
894 template <typename T>
|
jpayne@69
|
895 template <typename... Attachments>
|
jpayne@69
|
896 Array<T> ArrayPtr<T>::attach(Attachments&&... attachments) const {
|
jpayne@69
|
897 T* ptrCopy = ptr;
|
jpayne@69
|
898
|
jpayne@69
|
899 KJ_IREQUIRE(ptrCopy != nullptr, "cannot attach to null pointer");
|
jpayne@69
|
900
|
jpayne@69
|
901 // HACK: If someone accidentally calls .attach() on a null pointer in opt mode, try our best to
|
jpayne@69
|
902 // accomplish reasonable behavior: We turn the pointer non-null but still invalid, so that the
|
jpayne@69
|
903 // disposer will still be called when the pointer goes out of scope.
|
jpayne@69
|
904 if (ptrCopy == nullptr) ptrCopy = reinterpret_cast<T*>(1);
|
jpayne@69
|
905
|
jpayne@69
|
906 auto bundle = new _::ArrayDisposableOwnedBundle<Attachments...>(
|
jpayne@69
|
907 kj::fwd<Attachments>(attachments)...);
|
jpayne@69
|
908 return Array<T>(ptrCopy, size_, *bundle);
|
jpayne@69
|
909 }
|
jpayne@69
|
910
|
jpayne@69
|
911 } // namespace kj
|
jpayne@69
|
912
|
jpayne@69
|
913 KJ_END_HEADER
|