Loading...
Searching...
No Matches
smallVector.h
Go to the documentation of this file.
1//
2// Copyright 2019 Pixar
3//
4// Licensed under the terms set forth in the LICENSE.txt file available at
5// https://openusd.org/license.
6//
7#ifndef PXR_BASE_TF_SMALL_VECTOR_H
8#define PXR_BASE_TF_SMALL_VECTOR_H
9
12
13#include "pxr/pxr.h"
14
15#include "pxr/base/arch/defines.h"
16
17#include <algorithm>
18#include <cstddef>
19#include <cstdint>
20#include <cstdlib>
21#include <initializer_list>
22#include <iterator>
23#include <limits>
24#include <memory>
25#include <new>
26#include <type_traits>
27
28PXR_NAMESPACE_OPEN_SCOPE
29
30// Contains parts of the small vector implementation that do not depend on
31// *all* of TfSmallVector's template parameters.
32class TfSmallVectorBase
33{
34protected:
35 // We present the public size_type and difference_type as std::size_t and
36 // std::ptrdiff_t to match std::vector, but internally we store size &
37 // capacity as uint32_t.
38 using _SizeMemberType = std::uint32_t;
39
40 // Union type containing local storage or a pointer to heap storage.
41 template <size_t Size, size_t Align, size_t NumLocal>
42 union _DataUnion;
43
44 // Helper alias to produce the right _DataUnion instantiation for a given
45 // ValueType and NumLocal elements.
46 template <class ValueType, size_t NumLocal>
47 using _Data = _DataUnion<sizeof(ValueType), alignof(ValueType), NumLocal>;
48
49public:
50 using size_type = std::size_t;
51 using difference_type = std::ptrdiff_t;
52
53 // Returns the local capacity that may be used without increasing the size
54 // of the TfSmallVector. TfSmallVector<T, N> will never use more local
55 // capacity than is specified by N but clients that wish to maximize local
56 // occupancy in a generic way can compute N using this function.
57 template <typename U>
58 static constexpr size_type ComputeSerendipitousLocalCapacity() {
59 return (alignof(U) <= alignof(_Data<U, 0>))
60 ? sizeof(_Data<U, 0>) / sizeof(U)
61 : 0;
62 }
63
64protected:
65
66 // Enabler used to disambiguate the range-based constructor (begin, end)
67 // from the n-copies constructor (size_t n, value_type const &value)
68 // when the value_type is integral.
69 template<typename _ForwardIterator>
70 using _EnableIfForwardIterator =
71 std::enable_if_t<
72 std::is_convertible_v<
73 typename std::iterator_traits<
74 _ForwardIterator>::iterator_category,
75 std::forward_iterator_tag
76 >
77 >;
78
79 // Invoke std::uninitialized_copy that either moves or copies entries,
80 // depending on whether the type is move constructible or not.
81 template <typename Iterator>
82 static Iterator _UninitializedMove(
83 Iterator first, Iterator last, Iterator dest) {
84 return std::uninitialized_copy(
85 std::make_move_iterator(first),
86 std::make_move_iterator(last),
87 dest);
88 }
89
90 // Invokes either the move or copy constructor (via placement new),
91 // depending on whether U is move constructible or not.
92 template <typename U>
93 static void _MoveConstruct(U *p, U *src) {
94 new (p) U(std::move(*src));
95 }
96
97 // The data storage, which is a union of both the local storage, as well
98 // as a pointer, holding the address to the remote storage on the heap, if
99 // used.
100 template <size_t Size, size_t Align, size_t NumLocal>
101 union _DataUnion {
102 public:
103 // XXX: Could in principle assert in calls to GetLocalStorage() when
104 // HasLocal is false. Add dependency on tf/diagnostic.h?
105 static constexpr bool HasLocal = NumLocal != 0;
106
107 void *GetLocalStorage() {
108 return HasLocal ? _local : nullptr;
109 }
110 const void *GetLocalStorage() const {
111 return HasLocal ? _local : nullptr;
112 }
113
114 void *GetRemoteStorage() {
115 return _remote;
116 }
117 const void *GetRemoteStorage() const {
118 return _remote;
119 }
120
121 void SetRemoteStorage(void *p) {
122 _remote = p;
123 }
124 private:
125 // Pointer to heap storage.
126 void *_remote;
127 // Local storage -- min size is sizeof(_remote).
128 alignas(NumLocal == 0 ? std::alignment_of_v<void *> : Align)
129 char _local[std::max<size_t>(Size * NumLocal, sizeof(_remote))];
130 };
131};
132
155template <typename T, uint32_t N>
156class TfSmallVector : public TfSmallVectorBase
157{
158public:
165
168
169 typedef T value_type;
170 typedef T& reference;
171 typedef const T& const_reference;
172
174
177
178 using iterator = T*;
179 using const_iterator = const T*;
180 typedef std::reverse_iterator<iterator> reverse_iterator;
181 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
182
184
187 TfSmallVector() : _size(0), _capacity(N) {}
188
191 explicit TfSmallVector(size_type n) :
192 _capacity(N) {
193 _InitStorage(n);
194 value_type *d = data();
195 for (size_type i = 0; i < n; ++i) {
196 new (d + i) value_type();
197 }
198 }
199
202 TfSmallVector(size_type n, const value_type &v) :
203 _capacity(N) {
204 _InitStorage(n);
205 std::uninitialized_fill_n(data(), n, v);
206 }
207
210 enum DefaultInitTag { DefaultInit };
211 TfSmallVector(size_type n, DefaultInitTag) :
212 _capacity(N) {
213 _InitStorage(n);
214 value_type *d = data();
215 for (size_type i = 0; i < n; ++i) {
216 new (d + i) value_type;
217 }
218 }
219
222 TfSmallVector(const TfSmallVector &rhs) : _capacity(N) {
223 _InitStorage(rhs.size());
224 std::uninitialized_copy(rhs.begin(), rhs.end(), begin());
225 }
226
229 TfSmallVector(TfSmallVector &&rhs) : _size(0), _capacity(N) {
230 // If rhs can not be stored locally, take rhs's remote storage and
231 // reset rhs to empty.
232 if (rhs.size() > N) {
233 _SetRemoteStorage(rhs._GetRemoteStorage());
234 std::swap(_capacity, rhs._capacity);
235 }
236
237 // If rhs is stored locally, it's faster to simply move the entries
238 // into this vector's storage, destruct the entries at rhs, and swap
239 // sizes. Note that capacities will be the same in this case, so no
240 // need to swap those.
241 else {
242 // If N is 0, this means that rhs is empty and there are no
243 // elements that need moving or destroying.
244 // This avoids warnings in GCC.
245 if constexpr (N > 0) {
246 _UninitializedMove(rhs.begin(), rhs.end(), begin());
247 rhs._Destruct();
248 }
249 }
250 std::swap(_size, rhs._size);
251 }
252
254 TfSmallVector(std::initializer_list<T> values)
255 : TfSmallVector(values.begin(), values.end()) {
256 }
257
260 template<typename ForwardIterator,
261 typename = _EnableIfForwardIterator<ForwardIterator>>
262 TfSmallVector(ForwardIterator first, ForwardIterator last) : _capacity(N)
263 {
264 _InitStorage(std::distance(first, last));
265 std::uninitialized_copy(first, last, begin());
266 }
267
271 _Destruct();
272 _FreeStorage();
273 }
274
278 if (this != &rhs) {
279 assign(rhs.begin(), rhs.end());
280 }
281 return *this;
282 }
283
287 if (this != &rhs) {
288 swap(rhs);
289 }
290 return *this;
291 }
292
295 TfSmallVector &operator=(std::initializer_list<T> ilist) {
296 assign(ilist.begin(), ilist.end());
297 return *this;
298 }
299
302 void swap(TfSmallVector &rhs) {
303 // Both this vector and rhs are stored locally.
304 if (_IsLocal() && rhs._IsLocal()) {
305 TfSmallVector *smaller = size() < rhs.size() ? this : &rhs;
306 TfSmallVector *larger = size() < rhs.size() ? &rhs : this;
307
308 // Swap all the entries up to the size of the smaller vector.
309 std::swap_ranges(smaller->begin(), smaller->end(), larger->begin());
310
311 // Move the tail end of the entries, and destruct them at the
312 // source vector.
313 for (size_type i = smaller->size(); i < larger->size(); ++i) {
314 _MoveConstruct(smaller->data() + i, &(*larger)[i]);
315 (*larger)[i].~value_type();
316 }
317
318 // Swap sizes. Capacities are already equal in this case.
319 std::swap(smaller->_size, larger->_size);
320 }
321
322 // Both this vector and rhs are stored remotely. Simply swap the
323 // pointers, as well as size and capacity.
324 else if (!_IsLocal() && !rhs._IsLocal()) {
325 value_type *tmp = _GetRemoteStorage();
326 _SetRemoteStorage(rhs._GetRemoteStorage());
327 rhs._SetRemoteStorage(tmp);
328
329 std::swap(_size, rhs._size);
330 std::swap(_capacity, rhs._capacity);
331 }
332
333 // Either this vector or rhs is stored remotely, whereas the other
334 // one is stored locally.
335 else {
336 TfSmallVector *remote = _IsLocal() ? &rhs : this;
337 TfSmallVector *local = _IsLocal() ? this : &rhs;
338
339 // Get a pointer to the remote storage. We'll be overwriting the
340 // pointer value below, so gotta retain it first.
341 value_type *remoteStorage = remote->_GetStorage();
342
343 // Move all the entries from the vector with the local storage, to
344 // the other vector's local storage. This will overwrite the pointer
345 // to the other vectors remote storage. Note that we will have to
346 // also destruct the elements at the source's local storage. The
347 // source will become the one with the remote storage, so those
348 // entries will be essentially freed.
349 for (size_type i = 0; i < local->size(); ++i) {
350 _MoveConstruct(remote->_GetLocalStorage() + i, &(*local)[i]);
351 (*local)[i].~value_type();
352 }
353
354 // Swap the remote storage into the vector which previously had the
355 // local storage. It's been properly cleaned up now.
356 local->_SetRemoteStorage(remoteStorage);
357
358 // Swap sizes and capacities. Easy peasy.
359 std::swap(remote->_size, local->_size);
360 std::swap(remote->_capacity, local->_capacity);
361 }
362
363 }
364
367 iterator insert(const_iterator it, value_type &&v) {
368 return _Insert(it, std::move(v));
369 }
370
373 iterator insert(const_iterator it, const value_type &v) {
374 return _Insert(it, v);
375 }
376
379 iterator erase(const_iterator it) {
380 return erase(it, it + 1);
381 }
382
385 iterator erase(const_iterator it, const_iterator last) {
386 value_type *p = const_cast<value_type *>(&*it);
387 value_type *q = const_cast<value_type *>(&*last);
388
389 // If we're not removing anything, bail out.
390 if (p == q) {
391 return iterator(p);
392 }
393
394 const size_type num = std::distance(p, q);
395
396 // Move entries starting at last, down a few slots to starting a it.
397 value_type *e = data() + size();
398 std::move(q, e, p);
399
400 // Destruct all the freed up slots at the end of the vector.
401 for (value_type *i = (e - num); i != e; ++i) {
402 i->~value_type();
403 }
404
405 // Bump down the size.
406 _size -= num;
407
408 // Return an iterator to the next entry.
409 return iterator(p);
410 }
411
414 void reserve(size_type newCapacity) {
415 // Only reserve storage if the new capacity would grow past the local
416 // storage, or the currently allocated storage. We'll grow to
417 // accommodate exactly newCapacity entries.
418 if (newCapacity > capacity()) {
419 _GrowStorage(newCapacity);
420 }
421 }
422
425 void resize(size_type newSize, const value_type &v = value_type()) {
426 // If the new size is smaller than the current size, let go of some
427 // entries at the tail.
428 if (newSize < size()) {
429 erase(const_iterator(data() + newSize),
430 const_iterator(data() + size()));
431 }
432
433 // Otherwise, lets grow and fill: Reserve some storage, fill the tail
434 // end with copies of v, and update the new size.
435 else if (newSize > size()) {
436 reserve(newSize);
437 std::uninitialized_fill(data() + size(), data() + newSize, v);
438 _size = newSize;
439 }
440 }
441
445 void clear() {
446 _Destruct();
447 _size = 0;
448 }
449
453 template<typename ForwardIterator,
454 typename = _EnableIfForwardIterator<ForwardIterator>>
455 void assign(ForwardIterator first, ForwardIterator last) {
456 clear();
457 const size_type newSize = std::distance(first, last);
458 reserve(newSize);
459 std::uninitialized_copy(first, last, begin());
460 _size = newSize;
461 }
462
465 void assign(std::initializer_list<T> ilist) {
466 assign(ilist.begin(), ilist.end());
467 }
468
471 template < typename... Args >
472 void emplace_back(Args&&... args) {
473 if (size() == capacity()) {
474 _GrowStorage(_NextCapacity());
475 }
476 new (data() + size()) value_type(std::forward<Args>(args)...);
477 _size += 1;
478 }
479
482 void push_back(const value_type &v) {
483 emplace_back(v);
484 }
485
488 void push_back(value_type &&v) {
489 emplace_back(std::move(v));
490 }
491
495 template <typename ForwardIterator>
496 void insert(iterator pos, ForwardIterator first, ForwardIterator last)
497 {
498 static_assert(
499 std::is_convertible<
500 typename std::iterator_traits<ForwardIterator>::iterator_category,
501 std::forward_iterator_tag>::value,
502 "Input Iterators not supported.");
503
504 // Check for the insert-at-end special case as the very first thing so
505 // that we give the compiler the best possible opportunity to
506 // eliminate the general case code.
507 const bool insertAtEnd = pos == end();
508
509 const long numNewElems = std::distance(first, last);
510 const size_type neededCapacity = size() + numNewElems;
511 const size_type nextCapacity =
512 std::max(_NextCapacity(), neededCapacity);
513
514 // Insertions at the end would be handled correctly by the code below
515 // without this special case. However, insert(end(), f, l) is an
516 // extremely common operation so we provide this fast path both to
517 // avoid unneeded work and to make it easier for the compiler to
518 // eliminate dead code when pos == end().
519 if (insertAtEnd) {
520 // The reallocation here is not a simple reserve. We want to grow
521 // the storage only when there are too many new elements but the
522 // desired size is based on the growth factor.
523 if (neededCapacity > capacity()) {
524 _GrowStorage(nextCapacity);
525 }
526 std::uninitialized_copy(first, last, end());
527 _size += numNewElems;
528 return;
529 }
530
531 if (neededCapacity > capacity()) {
532 // Because we need to realloc, we can do the insertion by copying
533 // each range, [begin(), pos), [first, last), [pos, end()), into
534 // the new storage.
535
536 const size_type posI = std::distance(begin(), pos);
537 value_type *newStorage = _Allocate(nextCapacity);
538
539 iterator newPrefixBegin = iterator(newStorage);
540 iterator newPos = newPrefixBegin + posI;
541 iterator newSuffixBegin = newPos + numNewElems;
542 _UninitializedMove(begin(), pos, newPrefixBegin);
543 std::uninitialized_copy(first, last, newPos);
544 _UninitializedMove(pos, end(), newSuffixBegin);
545
546 // Destroy old data and set up this new buffer.
547 _Destruct();
548 _FreeStorage();
549 _SetRemoteStorage(newStorage);
550 _capacity = nextCapacity;
551 }
552 else {
553 // Insert in-place requires handling four ranges.
554 //
555 // For both the range-to-move [pos, end()) and the range-to-insert
556 // [first, last), there are two subranges: the subrange to copy
557 // and the subrange to uinitialized_copy. Note that only three of
558 // these ranges may be non-empty: either there is a non-empty
559 // prefix of [pos, end()) that needs to be copied over existing
560 // elements or there is a non-empty suffix of [first, last) that
561 // needs to be placed in uninitialized storage.
562
563 const long numMoveElems = std::distance(pos, end());
564 const long numUninitMoves = std::min(numNewElems, numMoveElems);
565 const long numInitMoves = numMoveElems - numUninitMoves;
566 const long numUninitNews = numNewElems - numUninitMoves;
567 const long numInitNews = numNewElems - numUninitNews;
568
569 // Move our existing elements out of the way of new elements.
570 iterator umSrc = pos + numInitMoves;
571 iterator umDst = end() + numUninitNews;
572 _UninitializedMove(umSrc, end(), umDst);
573 std::copy_backward(pos, umSrc, umDst);
574
575 // Copy new elements into place.
576 for (long i=0; i<numInitNews; ++i, ++first, ++pos) {
577 *pos = *first;
578 }
579 std::uninitialized_copy(first, last, end());
580 }
581
582 _size += numNewElems;
583 }
584
587 void insert(iterator pos, std::initializer_list<T> ilist) {
588 insert(pos, ilist.begin(), ilist.end());
589 }
590
593 void pop_back() {
594 back().~value_type();
595 _size -= 1;
596 }
597
600 size_type size() const {
601 return _size;
602 }
603
606 static constexpr size_type max_size() {
607 return std::numeric_limits<_SizeMemberType>::max();
608 }
609
612 bool empty() const {
613 return size() == 0;
614 }
615
621 size_type capacity() const {
622 return _capacity;
623 }
624
629 static constexpr size_type internal_capacity() {
630 return N;
631 }
632
635
636 iterator begin() {
637 return iterator(_GetStorage());
638 }
639
640 const_iterator begin() const {
641 return const_iterator(_GetStorage());
642 }
643
644 const_iterator cbegin() const {
645 return begin();
646 }
647
649
652
653 iterator end() {
654 return iterator(_GetStorage() + size());
655 }
656
657 const_iterator end() const {
658 return const_iterator(_GetStorage() + size());
659 }
660
661 const_iterator cend() const {
662 return end();
663 }
664
666
669
670 reverse_iterator rbegin() {
671 return reverse_iterator(end());
672 }
673
674 const_reverse_iterator rbegin() const {
675 return const_reverse_iterator(end());
676 }
677
678 const_reverse_iterator crbegin() const {
679 return rbegin();
680 }
681
683
686
687 reverse_iterator rend() {
688 return reverse_iterator(begin());
689 }
690
691 const_reverse_iterator rend() const {
692 return const_reverse_iterator(begin());
693 }
694
695 const_reverse_iterator crend() const {
696 return rend();
697 }
698
700
703 reference front() {
704 return *begin();
705 }
706
709 const_reference front() const {
710 return *begin();
711 }
712
715 reference back() {
716 return data()[size() - 1];
717 }
718
721 const_reference back() const {
722 return data()[size() - 1];
723 }
724
727 reference operator[](size_type i) {
728 return data()[i];
729 }
730
733 const_reference operator[](size_type i) const {
734 return data()[i];
735 }
736
739 value_type *data() {
740 return _GetStorage();
741 }
742
745 const value_type *data() const {
746 return _GetStorage();
747 }
748
751 bool operator==(const TfSmallVector &rhs) const {
752 return size() == rhs.size() && std::equal(begin(), end(), rhs.begin());
753 }
754
757 bool operator!=(const TfSmallVector &rhs) const {
758 return !operator==(rhs);
759 }
760
761private:
762
763 // Raw data access.
764 value_type *_GetLocalStorage() {
765 return static_cast<value_type *>(_data.GetLocalStorage());
766 }
767 const value_type *_GetLocalStorage() const {
768 return static_cast<const value_type *>(_data.GetLocalStorage());
769 }
770
771 value_type *_GetRemoteStorage() {
772 return static_cast<value_type *>(_data.GetRemoteStorage());
773 }
774 const value_type *_GetRemoteStorage() const {
775 return static_cast<const value_type *>(_data.GetRemoteStorage());
776 }
777
778 void _SetRemoteStorage(value_type *p) {
779 _data.SetRemoteStorage(static_cast<void *>(p));
780 }
781
782 // Returns true if the local storage is used.
783 bool _IsLocal() const {
784 return _capacity <= N;
785 }
786
787 // Return a pointer to the storage, which is either local or remote
788 // depending on the current capacity.
789 value_type *_GetStorage() {
790 return _IsLocal() ? _GetLocalStorage() : _GetRemoteStorage();
791 }
792
793 // Return a const pointer to the storage, which is either local or remote
794 // depending on the current capacity.
795 const value_type *_GetStorage() const {
796 return _IsLocal() ? _GetLocalStorage() : _GetRemoteStorage();
797 }
798
799 // Free the remotely allocated storage.
800 void _FreeStorage() {
801 if (!_IsLocal()) {
802 ::operator delete(_data.GetRemoteStorage(), std::nothrow);
803 }
804 }
805
806 // Destructs all the elements stored in this vector.
807 void _Destruct() {
808 value_type *b = data();
809 value_type *e = b + size();
810 for (value_type *p = b; p != e; ++p) {
811 p->~value_type();
812 }
813 }
814
815 // Allocate a buffer on the heap.
816 static value_type *_Allocate(size_type size) {
817 return static_cast<value_type *>(
818 ::operator new(sizeof(value_type) * size, std::nothrow));
819 }
820
821 // Initialize the vector with new storage, updating the capacity and size.
822 void _InitStorage(size_type size) {
823 if (size > capacity()) {
824 _SetRemoteStorage(_Allocate(size));
825 _capacity = size;
826 }
827#if defined(ARCH_COMPILER_GCC) && ARCH_COMPILER_GCC_MAJOR < 11
828 else if constexpr (!_data.HasLocal) {
829 // When there's no local storage and we're not allocating remote
830 // storage, initialize the remote storage pointer to avoid
831 // spurious compiler warnings about maybe-uninitialized values
832 // being used.
833
834 // This clause can be removed upon upgrade to gcc 11 as
835 // the new compiler no longer generates this warning in this case.
836 _data.SetRemoteStorage(nullptr);
837 }
838#endif
839 _size = size;
840 }
841
842 // Grow the storage to be able to accommodate newCapacity entries. This
843 // always allocates remote storage.
844 void _GrowStorage(const size_type newCapacity) {
845 value_type *newStorage = _Allocate(newCapacity);
846 _UninitializedMove(begin(), end(), iterator(newStorage));
847 _Destruct();
848 _FreeStorage();
849 _SetRemoteStorage(newStorage);
850 _capacity = newCapacity;
851 }
852
853 // Returns the next capacity to use for vector growth. The growth factor
854 // here is 1.5. A constant 1 is added so that we do not have to special
855 // case initial capacities of 0 and 1.
856 size_type _NextCapacity() const {
857 const size_type cap = capacity();
858 return cap + (cap / 2) + 1;
859 }
860
861 // Insert the value v at iterator it. We use this method that takes a
862 // universal reference to de-duplicate the logic required for the insert
863 // overloads, one taking an rvalue reference, and the other one taking a
864 // const reference. This way, we can take the most optimal code path (
865 // move, or copy without making redundant copies) based on whether v is
866 // a rvalue reference or const reference.
867 template < typename U >
868 iterator _Insert(const_iterator it, U &&v) {
869 value_type *newEntry;
870
871 // If the iterator points to the end, simply push back.
872 if (it == end()) {
873 push_back(std::forward<U>(v));
874 return end() - 1;
875 }
876
877 // Grow the remote storage, if we need to. This invalidates iterators,
878 // so special care must be taken in order to return a new, valid
879 // iterator.
880 else if (size() == capacity()) {
881 const size_type newCapacity = _NextCapacity();
882 value_type *newStorage = _Allocate(newCapacity);
883
884 value_type *i = const_cast<value_type *>(&*it);
885 value_type *curData = data();
886 newEntry = _UninitializedMove(curData, i, newStorage);
887
888 new (newEntry) value_type(std::forward<U>(v));
889
890 _UninitializedMove(i, curData + size(), newEntry + 1);
891
892 _Destruct();
893 _FreeStorage();
894
895 _SetRemoteStorage(newStorage);
896 _capacity = newCapacity;
897 }
898
899 // Our current capacity is big enough to allow us to simply shift
900 // elements up one slot and insert v at it.
901 else {
902 // Move all the elements after it up by one slot.
903 newEntry = const_cast<value_type *>(&*it);
904 value_type *last = const_cast<value_type *>(&back());
905 new (data() + size()) value_type(std::move(*last));
906 std::move_backward(newEntry, last, last + 1);
907
908 // Move v into the slot at the supplied iterator position.
909 newEntry->~value_type();
910 new (newEntry) value_type(std::forward<U>(v));
911 }
912
913 // Bump size and return an iterator to the newly inserted entry.
914 ++_size;
915 return iterator(newEntry);
916 }
917
918 // The vector storage, which is a union of the local storage and a pointer
919 // to the heap memory, if allocated.
920 _Data<value_type, N> _data;
921
922 // The current size of the vector, i.e. how many entries it contains.
923 _SizeMemberType _size;
924
925 // The current capacity of the vector, i.e. how big the currently allocated
926 // storage space is.
927 _SizeMemberType _capacity;
928};
929
931
932template < typename T, uint32_t N >
934{
935 a.swap(b);
936}
937
939
940// Return the largest local capacity N so that sizeof(TfSmallVector<T, N>) <=
941// SizeOf.
942template <class T, size_t SizeOf>
943constexpr uint32_t
944TfComputeSmallVectorLocalCapacityForTotalSize()
945{
946 // If T is over-aligned, sizeof(TfSmallVector<T, 1>) can be greater than
947 // sizeof(TfSmallVector<T, 0>) + sizeof(T). If sizeof(TfSmallVector<T, 1>)
948 // is not already too large, we take the total desired size minus
949 // sizeof(TfSmallVector<T, 1>), divide the difference by sizeof(T) and add
950 // one.
951 constexpr size_t SizeOfOne = sizeof(TfSmallVector<T, 1>);
952 constexpr size_t LocalCap = SizeOfOne > SizeOf ? 0
953 : ((SizeOf - SizeOfOne) / sizeof(T)) + 1;
954 constexpr size_t LocalCapMax = std::numeric_limits<uint32_t>::max();
955 constexpr uint32_t LocalCap32 = static_cast<uint32_t>(
956 LocalCap > LocalCapMax ? LocalCapMax : LocalCap);
957 static_assert(sizeof(TfSmallVector<T, LocalCap32>) <= SizeOf);
958 return LocalCap32;
959}
960
961PXR_NAMESPACE_CLOSE_SCOPE
962
963#endif
This is a small-vector class with local storage optimization, the local storage can be specified via ...
void push_back(const value_type &v)
Copy an entry to the back of the vector,.
void pop_back()
Remove the entry at the back of the vector.
const_reference front() const
Returns the first element in the vector.
reference operator[](size_type i)
Access the specified element.
void assign(ForwardIterator first, ForwardIterator last)
Clears any previously held entries, and copies entries between [ first, last ) to this vector.
void reserve(size_type newCapacity)
Reserve storage for newCapacity entries.
iterator erase(const_iterator it)
Erase an entry at the given iterator.
TfSmallVector(ForwardIterator first, ForwardIterator last)
Creates a new vector containing copies of the data between first and last.
void assign(std::initializer_list< T > ilist)
Replace existing contents with the contents of ilist.
TfSmallVector(std::initializer_list< T > values)
Construct a new vector from initializer list.
~TfSmallVector()
Destructor.
bool operator!=(const TfSmallVector &rhs) const
Lexicographically compares the elements in the vectors for inequality.
size_type size() const
Returns the current size of the vector.
const_reference back() const
Returns the last elements in the vector.
iterator erase(const_iterator it, const_iterator last)
Erase entries between [ first, last ) from the vector.
TfSmallVector & operator=(std::initializer_list< T > ilist)
Replace existing contents with the contents of ilist.
TfSmallVector(size_type n)
Construct a vector holding n value-initialized elements.
bool empty() const
Returns true if this vector is empty.
reference front()
Returns the first element in the vector.
static constexpr size_type max_size()
Returns the maximum size of this vector.
TfSmallVector(size_type n, const value_type &v)
Construct a vector holding n copies of v.
bool operator==(const TfSmallVector &rhs) const
Lexicographically compares the elements in the vectors for equality.
void swap(TfSmallVector &rhs)
Swap two vector instances.
void resize(size_type newSize, const value_type &v=value_type())
Resize the vector to newSize and insert copies of \v.
const value_type * data() const
Direct access to the underlying array.
DefaultInitTag
Construct a vector holding n default-initialized elements.
TfSmallVector(const TfSmallVector &rhs)
Copy constructor.
const_reference operator[](size_type i) const
Access the specified element.
TfSmallVector & operator=(const TfSmallVector &rhs)
Assignment operator.
static constexpr size_type internal_capacity()
Returns the local storage capacity.
void insert(iterator pos, ForwardIterator first, ForwardIterator last)
Copy the range denoted by [first, last) into this vector before pos.
void emplace_back(Args &&... args)
Emplace an entry at the back of the vector.
void clear()
Clear the entries in the vector.
iterator insert(const_iterator it, value_type &&v)
Insert an rvalue-reference entry at the given iterator position.
TfSmallVector & operator=(TfSmallVector &&rhs)
Move assignment operator.
size_type capacity() const
Returns the current capacity of this vector.
TfSmallVector(TfSmallVector &&rhs)
Move constructor.
void insert(iterator pos, std::initializer_list< T > ilist)
Insert elements from ilist starting at position pos.
value_type * data()
Direct access to the underlying array.
iterator insert(const_iterator it, const value_type &v)
Insert an entry at the given iterator.
reference back()
Returns the last element in the vector.
void push_back(value_type &&v)
Move an entry to the back of the vector.