annotate CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/include/kj/array.h @ 69:33d812a61356

planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author jpayne
date Tue, 18 Mar 2025 17:55:14 -0400
parents
children
rev   line source
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