@@ -211,6 +211,14 @@ class ChunkedStorage {
211
211
// / Get the number of elements stored in this ChunkedStorage
212
212
size_type size () const { return numElements; }
213
213
214
+ // / Is this storage empty?
215
+ bool empty () const { return size () == 0 ; }
216
+
217
+ // / Remove all elements
218
+ void clear () {
219
+ freeChunks ();
220
+ }
221
+
214
222
// / Emplace a value at the end
215
223
template <typename ... Args>
216
224
T& emplace_back (Args&&... args) {
@@ -315,7 +323,88 @@ class ParallelChunkedStorage {
315
323
};
316
324
317
325
private:
318
- using ChunkHeader = ChunkedStorage<T>::ChunkHeader;
326
+ using ChunkHeader = typename ChunkedStorage<T>::ChunkHeader;
327
+
328
+ // / The chunked storage for one thread
329
+ struct LocalChunkedStorageEntry {
330
+ // / The chunked storage
331
+ ChunkedStorage<T> storage;
332
+ // / The thread id given by a caller of `createLocalStorage`
333
+ uint32_t threadId;
334
+ // / The index of this entry. All indexes are unique but may not be
335
+ // / consistent with the order of the `next` pointers.
336
+ size_t index = -1 ;
337
+ // / The next entry
338
+ LocalChunkedStorageEntry* next = nullptr ;
339
+ };
340
+
341
+
342
+ // / An iterator over all elements in a `ParallelChunkedStorage`
343
+ template <bool isConst>
344
+ class Iterator {
345
+ public:
346
+ using difference_type = std::ptrdiff_t ;
347
+ using value_type = std::conditional_t <isConst, const T, T>;
348
+ using pointer = value_type*;
349
+ using reference = value_type&;
350
+ using iterator_category = std::forward_iterator_tag;
351
+
352
+ private:
353
+ friend class ParallelChunkedStorage ;
354
+
355
+ using storage_iterator = std::conditional_t <isConst, typename ChunkedStorage<T>::const_iterator, typename ChunkedStorage<T>::iterator>;
356
+ using storage_type = std::conditional_t <isConst, const ChunkedStorage<T>, ChunkedStorage<T>>;
357
+
358
+ // / The current entry
359
+ LocalChunkedStorageEntry* currentEntry = nullptr ;
360
+ // / The iterator of the current entry
361
+ storage_iterator it;
362
+
363
+ // / Make sure that the iterator points to a valid element or the end
364
+ void skipEmpty () {
365
+ while (currentEntry && it == currentEntry->storage .end ()) {
366
+ currentEntry = currentEntry->next ;
367
+ if (currentEntry)
368
+ it = static_cast <storage_type&>(currentEntry->storage ).begin ();
369
+ else
370
+ it = {};
371
+ }
372
+ }
373
+
374
+ // / Constructor
375
+ Iterator (LocalChunkedStorageEntry* entry, storage_iterator it) : currentEntry(entry), it(it) {
376
+ skipEmpty ();
377
+ }
378
+
379
+ public:
380
+ // / Default constructor
381
+ Iterator () = default ;
382
+
383
+ // / Dereference
384
+ reference operator *() const {
385
+ return *it;
386
+ }
387
+ // / Dereference
388
+ pointer operator ->() const {
389
+ return it.operator ->();
390
+ }
391
+
392
+ // / Pre-increment
393
+ Iterator& operator ++() {
394
+ ++it;
395
+ skipEmpty ();
396
+ return *this ;
397
+ }
398
+ // / Post-increment
399
+ Iterator operator ++(int ) {
400
+ Iterator it (*this );
401
+ operator ++();
402
+ return it;
403
+ }
404
+
405
+ // / Equality comparison
406
+ bool operator ==(const Iterator& other) const = default ;
407
+ };
319
408
320
409
// / A parallel iterator over all chunks in a `ParallelChunkedStorage`
321
410
template <bool isConst>
@@ -550,23 +639,12 @@ class ParallelChunkedStorage {
550
639
};
551
640
552
641
public:
642
+ using iterator = Iterator<false >;
643
+ using const_iterator = Iterator<true >;
553
644
using parallel_iterator = ParallelIterator<false >;
554
645
using const_parallel_iterator = ParallelIterator<true >;
555
646
556
647
private:
557
- // / The chunked storage for one thread
558
- struct LocalChunkedStorageEntry {
559
- // / The chunked storage
560
- ChunkedStorage<T> storage;
561
- // / The thread id given by a caller of `createLocalStorage`
562
- uint32_t threadId;
563
- // / The index of this entry. All indexes are unique but may not be
564
- // / consistent with the order of the `next` pointers.
565
- size_t index = -1 ;
566
- // / The next entry
567
- LocalChunkedStorageEntry* next = nullptr ;
568
- };
569
-
570
648
// / The first entry in the list of chunked storages
571
649
LocalChunkedStorageEntry* frontEntry = nullptr ;
572
650
// / The total number of entries
@@ -609,6 +687,15 @@ class ParallelChunkedStorage {
609
687
numEntries = 0 ;
610
688
}
611
689
690
+ // / The total number of elements. Note that this is not thread-safe and
691
+ // / linear in the number of threads.
692
+ size_t size () const {
693
+ size_t numElements = 0 ;
694
+ for (auto * entry = frontEntry; entry; entry = entry->next )
695
+ numElements += entry->storage .size ();
696
+ return numElements;
697
+ }
698
+
612
699
// / Create a new local chunked storage
613
700
LocalChunkedStorageRef createLocalStorage (uint32_t threadId) {
614
701
auto * entry = new LocalChunkedStorageEntry;
@@ -627,13 +714,37 @@ class ParallelChunkedStorage {
627
714
return ref;
628
715
}
629
716
717
+ // / Get an iterator to the first element
718
+ iterator begin () {
719
+ typename ChunkedStorage<T>::iterator it;
720
+ if (frontEntry)
721
+ it = std::begin (frontEntry->storage );
722
+ return iterator (frontEntry, it);
723
+ }
724
+ // / Get an iterator to the first element
725
+ const_iterator begin () const {
726
+ typename ChunkedStorage<T>::const_iterator it;
727
+ if (frontEntry)
728
+ it = std::cbegin (frontEntry->storage );
729
+ return const_iterator (frontEntry, it);
730
+ }
731
+
732
+ // / Get the end iterator
733
+ iterator end () {
734
+ return {};
735
+ }
736
+ // / Get the end iterator
737
+ const_iterator end () const {
738
+ return {};
739
+ }
740
+
630
741
// / Get a parallel iterator
631
742
parallel_iterator parallelIter () {
632
743
return parallel_iterator (*this );
633
744
}
634
745
// / Get a parallel iterator
635
746
const_parallel_iterator parallelIter () const {
636
- return parallel_iterator (*this );
747
+ return const_parallel_iterator (*this );
637
748
}
638
749
};
639
750
// ---------------------------------------------------------------------------
0 commit comments