Loading...
Searching...
No Matches
array.h
Go to the documentation of this file.
1//
2// Copyright 2016 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_VT_ARRAY_H
8#define PXR_BASE_VT_ARRAY_H
9
11
12#include "pxr/pxr.h"
13#include "pxr/base/vt/api.h"
14#include "pxr/base/vt/hash.h"
15#include "pxr/base/vt/streamOut.h"
16#include "pxr/base/vt/traits.h"
17#include "pxr/base/vt/types.h"
18
23#include "pxr/base/tf/preprocessorUtilsLite.h"
24
25#include <algorithm>
26#include <atomic>
27#include <cstddef>
28#include <cstdlib>
29#include <iterator>
30#include <limits>
31#include <memory>
32#include <new>
33#include <type_traits>
34
35PXR_NAMESPACE_OPEN_SCOPE
36
37// Helper class for clients that create VtArrays referring to foreign-owned
38// data.
39class Vt_ArrayForeignDataSource
40{
41public:
42 explicit Vt_ArrayForeignDataSource(
43 void (*detachedFn)(Vt_ArrayForeignDataSource *self) = nullptr,
44 size_t initRefCount = 0)
45 : _refCount(initRefCount)
46 , _detachedFn(detachedFn) {}
47
48private:
49 template <class T> friend class VtArray;
50 // Invoked when no more arrays share this data source.
51 void _ArraysDetached() { if (_detachedFn) { _detachedFn(this); } }
52protected:
53 std::atomic<size_t> _refCount;
54 void (*_detachedFn)(Vt_ArrayForeignDataSource *self);
55};
56
57// Private base class helper for VtArray implementation.
58class Vt_ArrayBase
59{
60public:
61 Vt_ArrayBase() : _shapeData { 0 }, _foreignSource(nullptr) {}
62
63 Vt_ArrayBase(Vt_ArrayForeignDataSource *foreignSrc)
64 : _shapeData { 0 }, _foreignSource(foreignSrc) {}
65
66 Vt_ArrayBase(Vt_ArrayBase const &other) = default;
67 Vt_ArrayBase(Vt_ArrayBase &&other) : Vt_ArrayBase(other) {
68 other._shapeData.clear();
69 other._foreignSource = nullptr;
70 }
71
72 Vt_ArrayBase &operator=(Vt_ArrayBase const &other) = default;
73 Vt_ArrayBase &operator=(Vt_ArrayBase &&other) {
74 if (this == &other)
75 return *this;
76 *this = other;
77 other._shapeData.clear();
78 other._foreignSource = nullptr;
79 return *this;
80 }
81
82protected:
83 // Control block header for native data representation. Houses refcount and
84 // capacity. For arrays with native data, this structure always lives
85 // immediately preceding the start of the array's _data in memory. See
86 // _GetControlBlock() for details.
87 struct _ControlBlock {
88 _ControlBlock() : nativeRefCount(0), capacity(0) {}
89 _ControlBlock(size_t initCount, size_t initCap)
90 : nativeRefCount(initCount), capacity(initCap) {}
91 mutable std::atomic<size_t> nativeRefCount;
92 size_t capacity;
93 };
94
95 _ControlBlock &_GetControlBlock(void *nativeData) {
96 TF_DEV_AXIOM(!_foreignSource);
97 return *(reinterpret_cast<_ControlBlock *>(nativeData) - 1);
98 }
99
100 _ControlBlock const &_GetControlBlock(void *nativeData) const {
101 TF_DEV_AXIOM(!_foreignSource);
102 return *(reinterpret_cast<_ControlBlock *>(nativeData) - 1);
103 }
104
105 // Mutable ref count, as is standard.
106 std::atomic<size_t> &_GetNativeRefCount(void *nativeData) const {
107 return _GetControlBlock(nativeData).nativeRefCount;
108 }
109
110 size_t &_GetCapacity(void *nativeData) {
111 return _GetControlBlock(nativeData).capacity;
112 }
113 size_t const &_GetCapacity(void *nativeData) const {
114 return _GetControlBlock(nativeData).capacity;
115 }
116
117 VT_API void _DetachCopyHook(char const *funcName) const;
118
119 Vt_ShapeData _shapeData;
120 Vt_ArrayForeignDataSource *_foreignSource;
121};
122
210ARCH_PRAGMA_PUSH
211ARCH_PRAGMA_NON_EXPORTED_BASE_CLASS
212template<typename ELEM>
213class VtArray : public Vt_ArrayBase {
214ARCH_PRAGMA_POP
215 public:
216
218 typedef ELEM ElementType;
219 typedef ELEM value_type;
220
223
228
230 typedef std::reverse_iterator<iterator> reverse_iterator;
232 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
233
242
244
246 VtArray() : _data(nullptr) {}
247
260 template <typename LegacyInputIterator>
261 VtArray(LegacyInputIterator first, LegacyInputIterator last,
262 std::enable_if_t<
263 !std::is_integral_v<LegacyInputIterator>> * = nullptr)
264 : VtArray() {
265 assign(first, last);
266 }
267
269 VtArray(Vt_ArrayForeignDataSource *foreignSrc,
270 ElementType *data, size_t size, bool addRef = true)
271 : Vt_ArrayBase(foreignSrc)
272 , _data(data) {
273 if (addRef) {
274 foreignSrc->_refCount.fetch_add(1, std::memory_order_relaxed);
275 }
276 _shapeData.totalSize = size;
277 }
278
280 VtArray(VtArray const &other) : Vt_ArrayBase(other)
281 , _data(other._data) {
282 if (!_data)
283 return;
284
285 if (ARCH_LIKELY(!_foreignSource)) {
286 _GetNativeRefCount(_data).fetch_add(1, std::memory_order_relaxed);
287 }
288 else {
289 _foreignSource->_refCount.fetch_add(1, std::memory_order_relaxed);
290 }
291 }
292
295 VtArray(VtArray &&other) : Vt_ArrayBase(std::move(other))
296 , _data(other._data) {
297 other._data = nullptr;
298 }
299
301 VtArray(std::initializer_list<ELEM> initializerList)
302 : VtArray() {
303 assign(initializerList);
304 }
305
307 explicit VtArray(size_t n)
308 : VtArray() {
309 assign(n, value_type());
310 }
311
313 explicit VtArray(size_t n, value_type const &value)
314 : VtArray() {
315 assign(n, value);
316 }
317
320 VtArray &operator=(VtArray const &other) {
321 // This might look recursive but it's really invoking move-assign, since
322 // we create a temporary copy (an rvalue).
323 if (this != &other)
324 *this = VtArray(other);
325 return *this;
326 }
327
331 if (this == &other)
332 return *this;
333 _DecRef();
334 static_cast<Vt_ArrayBase &>(*this) = std::move(other);
335 _data = other._data;
336 other._data = nullptr;
337 return *this;
338 }
339
341 VtArray &operator=(std::initializer_list<ELEM> initializerList) {
342 this->assign(initializerList.begin(), initializerList.end());
343 return *this;
344 }
345
346 ~VtArray() { _DecRef(); }
347
356 VtArray const &AsConst() const noexcept {
357 return *this;
358 }
359
362
365 iterator begin() { return iterator(data()); }
368 iterator end() { return iterator(data() + size()); }
369
373 const_iterator end() const { return const_iterator(data() + size()); }
374
376 const_iterator cbegin() const { return begin(); }
378 const_iterator cend() const { return end(); }
379
386
389 return const_reverse_iterator(end());
390 }
394 }
395
399 const_reverse_iterator crend() const { return rend(); }
400
403 pointer data() { _DetachIfNotUnique(); return _data; }
405 const_pointer data() const { return _data; }
407 const_pointer cdata() const { return _data; }
408
414 template <typename... Args>
415 void emplace_back(Args&&... args) {
416 // If this is a non-pxr array with rank > 1, disallow push_back.
417 if (ARCH_UNLIKELY(_shapeData.otherDims[0])) {
418 TF_CODING_ERROR("Array rank %u != 1", _shapeData.GetRank());
419 return;
420 }
421 // If we don't own the data, or if we need more space, realloc.
422 size_t curSize = size();
423 if (ARCH_UNLIKELY(
424 _foreignSource || !_IsUnique() || curSize == capacity())) {
425 value_type *newData = _AllocateCopy(
426 _data, _CapacityForSize(curSize + 1), curSize);
427 ::new (static_cast<void*>(newData + curSize)) value_type(
428 std::forward<Args>(args)...);
429 _DecRef();
430 _data = newData;
431 }
432 else {
433 ::new (static_cast<void*>(_data + curSize)) value_type(
434 std::forward<Args>(args)...);
435 }
436 // Adjust size.
437 ++_shapeData.totalSize;
438 }
439
445 void push_back(ElementType const& element) {
446 emplace_back(element);
447 }
448
454 void push_back(ElementType&& element) {
455 emplace_back(std::move(element));
456 }
457
460 void pop_back() {
461 // If this is a presto array with rank > 1, disallow push_back.
462 if (ARCH_UNLIKELY(_shapeData.otherDims[0])) {
463 TF_CODING_ERROR("Array rank %u != 1", _shapeData.GetRank());
464 return;
465 }
466 _DetachIfNotUnique();
467 // Invoke the destructor.
468 (_data + size() - 1)->~value_type();
469 // Adjust size.
470 --_shapeData.totalSize;
471 }
472
474 size_t size() const { return _shapeData.totalSize; }
475
480 size_t capacity() const {
481 if (!_data) {
482 return 0;
483 }
484 // We do not allow mutation to foreign source data, so always report
485 // foreign sourced arrays as at capacity.
486 return ARCH_UNLIKELY(_foreignSource) ? size() : _GetCapacity(_data);
487 }
488
492 constexpr size_t max_size() const {
493 // Popular compilers limit object sizes to half the address space, so we
494 // follow suit, taking max bytes as one less than the maximum value of a
495 // difference between pointers. This is unobtainable in practice: on a
496 // 64-bit machine this is more than 8 million terabytes.
497 return (std::numeric_limits<ptrdiff_t>::max() - 1 -
498 sizeof(_ControlBlock)) / sizeof(value_type);
499 }
500
502 bool empty() const { return size() == 0; }
503
507 void reserve(size_t num) {
508 if (num <= capacity())
509 return;
510
511 value_type *newData =
512 _data ? _AllocateCopy(_data, num, size()) : _AllocateNew(num);
513
514 _DecRef();
515 _data = newData;
516 }
517
521 reference front() { return *begin(); }
524 const_reference front() const { return *begin(); }
527 const_reference cfront() const { return *begin(); }
528
532 reference back() { return *rbegin(); }
535 const_reference back() const { return *rbegin(); }
538 const_reference cback() const { return *rbegin(); }
539
545 void resize(size_t newSize) {
546 return resize(newSize, value_type());
547 }
548
551 void resize(size_t newSize, value_type const &value) {
552 return resize(newSize,
553 [&value](pointer b, pointer e) {
554 std::uninitialized_fill(b, e, value);
555 });
556 }
557
560 void resize(size_t newSize, value_type &value) {
561 return resize(newSize, const_cast<value_type const &>(value));
562 }
563
566 void resize(size_t newSize, value_type &&value) {
567 return resize(newSize, const_cast<value_type const &>(value));
568 }
569
574 template <class FillElemsFn>
575 void resize(size_t newSize, FillElemsFn &&fillElems) {
576 const size_t oldSize = size();
577 if (oldSize == newSize) {
578 return;
579 }
580 if (newSize == 0) {
581 clear();
582 return;
583 }
584
585 const bool growing = newSize > oldSize;
586 value_type *newData = _data;
587
588 if (!_data) {
589 // Allocate newSize elements and initialize.
590 newData = _AllocateNew(newSize);
591 std::forward<FillElemsFn>(fillElems)(newData, newData + newSize);
592 }
593 else if (_IsUnique()) {
594 if (growing) {
595 if (newSize > _GetCapacity(_data)) {
596 newData = _AllocateCopy(_data, newSize, oldSize);
597 }
598 // fill with newly added elements from oldSize to newSize.
599 std::forward<FillElemsFn>(fillElems)(newData + oldSize,
600 newData + newSize);
601 }
602 else {
603 // destroy removed elements
604 for (auto *cur = newData + newSize,
605 *end = newData + oldSize; cur != end; ++cur) {
606 cur->~value_type();
607 }
608 }
609 }
610 else {
611 newData =
612 _AllocateCopy(_data, newSize, growing ? oldSize : newSize);
613 if (growing) {
614 // fill with newly added elements from oldSize to newSize.
615 std::forward<FillElemsFn>(fillElems)(newData + oldSize,
616 newData + newSize);
617 }
618 }
619
620 // If we created new data, clean up the old and move over to the new.
621 if (newData != _data) {
622 _DecRef();
623 _data = newData;
624 }
625 // Adjust size.
626 _shapeData.totalSize = newSize;
627 }
628
630 void clear() {
631 if (!_data)
632 return;
633 if (_IsUnique()) {
634 // Clear out elements, run dtors, keep capacity.
635 for (value_type *p = _data, *e = _data + size(); p != e; ++p) {
636 p->~value_type();
637 }
638 }
639 else {
640 // Detach to empty.
641 _DecRef();
642 }
643 _shapeData.clear();
644 }
645
648 iterator insert(const_iterator pos, value_type const &value) {
649 // If value is an element of the array, we make a copy and move-insert
650 // it. std::less (and friends) and the standard's guarantee of a strict
651 // pointer total order ensures this works.
652 const const_pointer valuePtr = std::addressof(value);
653 if (std::less_equal<const_pointer>{}(cdata(), valuePtr) &&
654 std::less<const_pointer>{}(valuePtr, cdata() + size())) {
655 value_type tmp { value };
656 return insert(pos,
657 std::make_move_iterator(std::addressof(tmp)),
658 std::make_move_iterator(std::addressof(tmp) + 1));
659 }
660 return insert(pos, valuePtr, valuePtr + 1);
661 }
662
665 iterator insert(const_iterator pos, value_type &&value) {
666 // If value is an element of the array, we move it to a temp spot and
667 // move-insert that. std::less (and friends) and the standard's
668 // guarantee of a strict pointer total order ensures this works.
669 const const_pointer valuePtr = std::addressof(value);
670 if (std::less_equal<const_pointer>{}(cdata(), valuePtr) &&
671 std::less<const_pointer>{}(valuePtr, cdata() + size())) {
672 value_type tmp { std::move(value) };
673 return insert(pos,
674 std::make_move_iterator(std::addressof(tmp)),
675 std::make_move_iterator(std::addressof(tmp) + 1));
676 }
677 return insert(pos,
678 std::make_move_iterator(valuePtr),
679 std::make_move_iterator(valuePtr + 1));
680 }
681
685 iterator insert(const_iterator pos, size_t count, value_type const &fill) {
686 // If fill is an element of the array, we copy it to a temporary and
687 // insert that. std::less (and friends) and the standard's guarantee of
688 // a strict pointer total order ensures this works.
689 const const_pointer fillPtr = std::addressof(fill);
690 if (std::less_equal<const_pointer>{}(cdata(), fillPtr) &&
691 std::less<const_pointer>{}(fillPtr, cdata() + size())) {
692 value_type tmp { fill };
693 return insert(pos, count, [&tmp](pointer b, pointer e) {
694 std::uninitialized_fill(b, e, tmp);
695 });
696 }
697 return insert(pos, count, [&fill](pointer b, pointer e) {
698 std::uninitialized_fill(b, e, fill);
699 });
700 }
701
705 iterator insert(const_iterator pos, std::initializer_list<ELEM> ilist) {
706 return insert(pos, ilist.begin(), ilist.end());
707 }
708
713 template <class LegacyInputIterator>
714 std::enable_if_t<
715 !std::is_integral_v<LegacyInputIterator>, iterator>
717 const_iterator pos,
718 LegacyInputIterator first, LegacyInputIterator last) {
719 return insert(pos, std::distance(first, last),
720 [&first, &last](pointer b, pointer e) {
721 std::uninitialized_copy(first, last, b);
722 });
723 }
724
731 template <class FillElemsFn>
732 std::enable_if_t<
733 !std::is_integral_v<std::decay_t<FillElemsFn>>,
734 iterator>
735 insert(const_iterator pos, size_t count, FillElemsFn &&fillElems) {
736 if (count == 0) {
737 return begin() + std::distance(cbegin(), pos);
738 }
739 if (empty()) {
740 resize(count, std::forward<FillElemsFn>(fillElems));
741 return begin();
742 }
743 // This array is not empty, and we will insert at least one element.
744 const size_t newSize = size() + count;
745 if (_IsUnique()) {
746 if (newSize <= capacity()) {
747 // We can avoid reallocation.
748 iterator ncpos = begin() + std::distance(cbegin(), pos);
749 iterator iend = end();
750 if (pos != cend()) {
751 // The 'count' tail elements must be uninitialized_move'd
752 // over. The remainder must be move_backward'd.
753 std::uninitialized_copy(
754 std::make_move_iterator(iend-count),
755 std::make_move_iterator(iend), iend);
756 std::move_backward(ncpos, iend-count, iend);
757 }
758 // Fill new elements.
759 value_type *p = std::addressof(*ncpos);
760 std::forward<FillElemsFn>(fillElems)(p, p + count);
761 // Set new size.
762 _shapeData.totalSize = newSize;
763 return ncpos;
764 }
765 else {
766 // We need to allocate, but can move items.
767 value_type* newData = _AllocateNew(newSize);
768 size_t posOffset = std::distance(cbegin(), pos);
769 std::uninitialized_copy(
770 std::make_move_iterator(begin()),
771 std::make_move_iterator(begin() + posOffset), newData);
772 std::uninitialized_copy(
773 std::make_move_iterator(begin() + posOffset),
774 std::make_move_iterator(end()),
775 newData + posOffset + count);
776 // Drop the old data and fill the new elements.
777 _DecRef();
778 _data = newData;
779 _shapeData.totalSize = newSize;
780 std::forward<FillElemsFn>(fillElems)(
781 newData + posOffset, newData + posOffset + count);
782 return begin() + posOffset;
783 }
784 }
785 else {
786 // Allocate new space and copy.
787 value_type* newData = _AllocateNew(newSize);
788 size_t posOffset = std::distance(cbegin(), pos);
789 std::uninitialized_copy(cbegin(), pos, newData);
790 std::uninitialized_copy(pos, cend(), newData + posOffset + count);
791 // Drop the old data and fill the new elements.
792 _DecRef();
793 _data = newData;
794 _shapeData.totalSize = newSize;
795 std::forward<FillElemsFn>(fillElems)(
796 newData + posOffset, newData + posOffset + count);
797 return begin() + posOffset;
798 }
799 // unreachable.
800 }
801
815 TF_DEV_AXIOM(pos != cend());
816 return erase(pos, pos+1);
817 }
818
835 if (first == last) {
836 return std::next(begin(), std::distance(cbegin(), last));
837 }
838 if ((first == cbegin()) && (last == cend())) {
839 clear();
840 return end();
841 }
842 // Given the previous two conditions, we know that we are removing
843 // at least one element and the result array will contain at least one
844 // element.
845 value_type* removeStart =
846 std::next(_data, std::distance(cbegin(), first));
847 value_type* removeEnd = std::next(_data, std::distance(cbegin(), last));
848 value_type* endIt = std::next(_data, size());
849 size_t newSize = size() - std::distance(first, last);
850 if (_IsUnique()) {
851 // If the array is unique, we can simply move the tail elements
852 // and free to the end of the array.
853 value_type* deleteIt = std::move(removeEnd, endIt, removeStart);
854 for (; deleteIt != endIt; ++deleteIt) {
855 deleteIt->~value_type();
856 }
857 _shapeData.totalSize = newSize;
858 return iterator(removeStart);
859 } else {
860 // If the array is not unique, we want to avoid copying the
861 // elements in the range we are erasing. We allocate a
862 // new buffer and copy the head and tail ranges, omitting
863 // [first, last)
864 value_type* newData = _AllocateNew(newSize);
865 value_type* newMiddle = std::uninitialized_copy(
866 _data, removeStart, newData);
867 value_type* newEnd = std::uninitialized_copy(
868 removeEnd, endIt, newMiddle);
869 TF_DEV_AXIOM(newEnd == std::next(newData, newSize));
870 TF_DEV_AXIOM(std::distance(newData, newMiddle) ==
871 std::distance(_data, removeStart));
872 _DecRef();
873 _data = newData;
874 _shapeData.totalSize = newSize;
875 return iterator(newMiddle);
876 }
877 }
878
885 template <class ForwardIter>
886 typename std::enable_if<!std::is_integral<ForwardIter>::value>::type
887 assign(ForwardIter first, ForwardIter last) {
888 struct _Copier {
889 void operator()(pointer b, pointer e) const {
890 std::uninitialized_copy(first, last, b);
891 }
892 ForwardIter const &first, &last;
893 };
894 clear();
895 resize(std::distance(first, last), _Copier { first, last });
896 }
897
904 void assign(size_t n, const value_type &fill) {
905 struct _Filler {
906 void operator()(pointer b, pointer e) const {
907 std::uninitialized_fill(b, e, fill);
908 }
909 const value_type &fill;
910 };
911 clear();
912 resize(n, _Filler { fill });
913 }
914
920 void assign(std::initializer_list<ELEM> initializerList) {
921 assign(initializerList.begin(), initializerList.end());
922 }
923
925 void swap(VtArray &other) {
926 std::swap(_data, other._data);
927 std::swap(_shapeData, other._shapeData);
928 std::swap(_foreignSource, other._foreignSource);
929 }
930
932
934 ElementType &operator[](size_t index) {
935 return data()[index];
936 }
937
939 ElementType const &operator[](size_t index) const {
940 return data()[index];
941 }
942
945 bool IsIdentical(VtArray const & other) const {
946 return
947 _data == other._data &&
948 _shapeData == other._shapeData &&
949 _foreignSource == other._foreignSource;
950 }
951
953 bool operator == (VtArray const & other) const {
954 return IsIdentical(other) ||
955 (*_GetShapeData() == *other._GetShapeData() &&
956 std::equal(cbegin(), cend(), other.cbegin()));
957 }
958
960 bool operator != (VtArray const &other) const {
961 return !(*this == other);
962 }
963
971 bool MakeUnique() {
972 return _DetachIfNotUnique(/*invokeHook=*/false);
973 }
974
975 public:
976 // XXX -- Public so VtValue::_ArrayHelper<T,U>::GetShapeData() has access.
977 Vt_ShapeData const *_GetShapeData() const {
978 return &_shapeData;
979 }
980 Vt_ShapeData *_GetShapeData() {
981 return &_shapeData;
982 }
983
984 private:
985 class _Streamer {
986 public:
987 explicit _Streamer(const_pointer data) : _p(data) { }
988 void operator()(std::ostream &out) const {
989 VtStreamOut(*_p++, out);
990 }
991
992 private:
993 mutable const_pointer _p;
994 };
995
998 friend std::ostream &operator <<(std::ostream &out, const VtArray &self) {
999 VtArray::_Streamer streamer(self.cdata());
1000 VtStreamOutArray(out, self._GetShapeData(), streamer);
1001 return out;
1002 }
1003
1005 friend void swap(VtArray &lhs, VtArray &rhs) {
1006 lhs.swap(rhs);
1007 }
1008
1009 bool _DetachIfNotUnique(bool invokeHook=true) {
1010 if (_IsUnique()) {
1011 return false;
1012 }
1013 // Copy to local.
1014 if (invokeHook) {
1015 _DetachCopyHook(__ARCH_PRETTY_FUNCTION__);
1016 }
1017 auto *newData = _AllocateCopy(_data, size(), size());
1018 _DecRef();
1019 _data = newData;
1020 return true;
1021 }
1022
1023 inline bool _IsUnique() const {
1024 return !_data ||
1025 (ARCH_LIKELY(!_foreignSource) && _GetNativeRefCount(_data) == 1);
1026 }
1027
1028 inline size_t _CapacityForSize(size_t sz) const {
1029 // Currently just successive powers of two.
1030 size_t cap = 1;
1031 while (cap < sz) {
1032 cap += cap;
1033 }
1034 return cap;
1035 }
1036
1037 value_type *_AllocateNew(size_t capacity) {
1038 TfAutoMallocTag2 tag("VtArray::_AllocateNew", __ARCH_PRETTY_FUNCTION__);
1039 // Need space for the control block and capacity elements.
1040 // Exceptionally large capacity requests can overflow the arithmetic
1041 // here. If that happens we'll just attempt to allocate the max size_t
1042 // value and let new() throw.
1043 size_t numBytes = (capacity <= max_size())
1044 ? sizeof(_ControlBlock) + capacity * sizeof(value_type)
1045 : std::numeric_limits<size_t>::max();
1046 void *data = ::operator new(numBytes);
1047 // Placement-new a control block.
1048 ::new (data) _ControlBlock(/*count=*/1, capacity);
1049 // Data starts after the block.
1050 return reinterpret_cast<value_type *>(
1051 static_cast<_ControlBlock *>(data) + 1);
1052 }
1053
1054 value_type *_AllocateCopy(value_type *src, size_t newCapacity,
1055 size_t numToCopy) {
1056 // Allocate and copy elements.
1057 value_type *newData = _AllocateNew(newCapacity);
1058 std::uninitialized_copy(src, src + numToCopy, newData);
1059 return newData;
1060 }
1061
1062 void _DecRef() {
1063 if (!_data)
1064 return;
1065 if (ARCH_LIKELY(!_foreignSource)) {
1066 // Drop the refcount. If we take it to zero, destroy the data.
1067 if (_GetNativeRefCount(_data).fetch_sub(
1068 1, std::memory_order_release) == 1) {
1069 std::atomic_thread_fence(std::memory_order_acquire);
1070 for (value_type *p = _data, *e = _data + _shapeData.totalSize;
1071 p != e; ++p) {
1072 p->~value_type();
1073 }
1074 ::operator delete(static_cast<void *>(
1075 std::addressof(_GetControlBlock(_data))));
1076 }
1077 }
1078 else {
1079 // Drop the refcount in the foreign source. If we take it to zero,
1080 // invoke the function pointer to alert the foreign source.
1081 if (_foreignSource->_refCount.fetch_sub(
1082 1, std::memory_order_release) == 1) {
1083 std::atomic_thread_fence(std::memory_order_acquire);
1084 _foreignSource->_ArraysDetached();
1085 }
1086 }
1087 _foreignSource = nullptr;
1088 _data = nullptr;
1089 }
1090
1091 value_type *_data;
1092};
1093
1094// VtArray can transform if the underlying element type can.
1095template <class ELEM>
1096struct VtValueTypeCanTransform<VtArray<ELEM>> :
1097 VtValueTypeCanTransform<ELEM> {};
1098
1099// Declare basic array instantiations as extern templates. They are explicitly
1100// instantiated in array.cpp.
1101#define VT_ARRAY_EXTERN_TMPL(unused, elem) \
1102 VT_API_TEMPLATE_CLASS(VtArray< VT_TYPE(elem) >);
1103TF_PP_SEQ_FOR_EACH(VT_ARRAY_EXTERN_TMPL, ~, VT_SCALAR_VALUE_TYPES)
1104
1105template <class HashState, class ELEM>
1106inline std::enable_if_t<VtIsHashable<ELEM>()>
1107TfHashAppend(HashState &h, VtArray<ELEM> const &array)
1108{
1109 h.Append(array.size());
1110 h.AppendContiguous(array.cdata(), array.size());
1111}
1112
1113template <class ELEM>
1114typename std::enable_if<VtIsHashable<ELEM>(), size_t>::type
1115hash_value(VtArray<ELEM> const &array) {
1116 return TfHash()(array);
1117}
1118
1119// Specialize traits so others can figure out that VtArray is an array.
1120template <typename T>
1121struct VtIsArray< VtArray <T> > : public std::true_type {};
1122
1123template <class T>
1124struct Vt_ArrayOpHelp {
1125 static T Add(T l, T r) { return l + r; }
1126 static T Sub(T l, T r) { return l - r; }
1127 static T Mul(T l, T r) { return l * r; }
1128 static T Div(T l, T r) { return l / r; }
1129 static T Mod(T l, T r) { return l % r; }
1130};
1131
1132template <class T>
1133struct Vt_ArrayOpHelpScalar {
1134 static T Mul(T l, double r) { return l * r; }
1135 static T Mul(double l, T r) { return l * r; }
1136 static T Div(T l, double r) { return l / r; }
1137 static T Div(double l, T r) { return l / r; }
1138};
1139
1140// These operations on bool-arrays are highly questionable, but this preserves
1141// existing behavior in the name of Hyrum's Law.
1142template <>
1143struct Vt_ArrayOpHelp<bool> {
1144 static bool Add(bool l, bool r) { return l | r; }
1145 static bool Sub(bool l, bool r) { return l ^ r; }
1146 static bool Mul(bool l, bool r) { return l & r; }
1147 static bool Div(bool l, bool r) { return l; }
1148 static bool Mod(bool l, bool r) { return false; }
1149};
1150
1151template <>
1152struct Vt_ArrayOpHelpScalar<bool> {
1153 static bool Mul(bool l, double r) { return l && (r != 0.0); }
1154 static bool Mul(double l, bool r) { return (l != 0.0) && r; }
1155 static bool Div(bool l, double r) { return (r == 0.0) || l; }
1156 static bool Div(double l, bool r) { return !r || (l != 0.0); }
1157};
1158
1159#define VTOPERATOR_CPPARRAY(op, opName) \
1160 template <class T> \
1161 VtArray<T> \
1162 operator op (VtArray<T> const &lhs, VtArray<T> const &rhs) \
1163 { \
1164 using Op = Vt_ArrayOpHelp<T>; \
1165 /* accept empty vecs */ \
1166 if (!lhs.empty() && !rhs.empty() && lhs.size() != rhs.size()) { \
1167 TF_CODING_ERROR("Non-conforming inputs for operator %s", #op); \
1168 return VtArray<T>(); \
1169 } \
1170 /* promote empty vecs to vecs of zeros */ \
1171 const bool leftEmpty = lhs.size() == 0, rightEmpty = rhs.size() == 0; \
1172 VtArray<T> ret(leftEmpty ? rhs.size() : lhs.size()); \
1173 T zero = VtZero<T>(); \
1174 if (leftEmpty) { \
1175 std::transform( \
1176 rhs.begin(), rhs.end(), ret.begin(), \
1177 [zero](T const &r) { return Op:: opName (zero, r); }); \
1178 } \
1179 else if (rightEmpty) { \
1180 std::transform( \
1181 lhs.begin(), lhs.end(), ret.begin(), \
1182 [zero](T const &l) { return Op:: opName (l, zero); }); \
1183 } \
1184 else { \
1185 std::transform( \
1186 lhs.begin(), lhs.end(), rhs.begin(), ret.begin(), \
1187 [](T const &l, T const &r) { return Op:: opName (l, r); }); \
1188 } \
1189 return ret; \
1190 }
1191
1192ARCH_PRAGMA_PUSH
1193ARCH_PRAGMA_FORCING_TO_BOOL
1194ARCH_PRAGMA_UNSAFE_USE_OF_BOOL
1195ARCH_PRAGMA_UNARY_MINUS_ON_UNSIGNED
1196
1197VTOPERATOR_CPPARRAY(+, Add);
1198VTOPERATOR_CPPARRAY(-, Sub);
1199VTOPERATOR_CPPARRAY(*, Mul);
1200VTOPERATOR_CPPARRAY(/, Div);
1201VTOPERATOR_CPPARRAY(%, Mod);
1202
1203template <class T>
1205operator-(VtArray<T> const &a) {
1206 VtArray<T> ret(a.size());
1207 std::transform(a.begin(), a.end(), ret.begin(),
1208 [](T const &x) { return -x; });
1209 return ret;
1210}
1211
1212ARCH_PRAGMA_POP
1213
1214// Operations on scalars and arrays
1215// These are free functions defined in Array.h
1216#define VTOPERATOR_CPPSCALAR(op,opName) \
1217 template<typename T> \
1218 VtArray<T> operator op (T const &scalar, VtArray<T> const &arr) { \
1219 using Op = Vt_ArrayOpHelp<T>; \
1220 VtArray<T> ret(arr.size()); \
1221 std::transform(arr.begin(), arr.end(), ret.begin(), \
1222 [&scalar](T const &aObj) { \
1223 return Op:: opName (scalar, aObj); \
1224 }); \
1225 return ret; \
1226 } \
1227 template<typename T> \
1228 VtArray<T> operator op (VtArray<T> const &arr, T const &scalar) { \
1229 using Op = Vt_ArrayOpHelp<T>; \
1230 VtArray<T> ret(arr.size()); \
1231 std::transform(arr.begin(), arr.end(), ret.begin(), \
1232 [&scalar](T const &aObj) { \
1233 return Op:: opName (aObj, scalar); \
1234 }); \
1235 return ret; \
1236 }
1237
1238// define special-case operators on arrays and doubles - except if the array
1239// holds doubles, in which case we already defined the operator (with
1240// VTOPERATOR_CPPSCALAR above) so we can't do it again!
1241#define VTOPERATOR_CPPSCALAR_DOUBLE(op,opName) \
1242 template<typename T> \
1243 std::enable_if_t<!std::is_same<T, double>::value, VtArray<T>> \
1244 operator op (double const &scalar, VtArray<T> const &arr) { \
1245 using Op = Vt_ArrayOpHelpScalar<T>; \
1246 VtArray<T> ret(arr.size()); \
1247 std::transform(arr.begin(), arr.end(), ret.begin(), \
1248 [&scalar](T const &aObj) { \
1249 return Op:: opName (scalar, aObj); \
1250 }); \
1251 return ret; \
1252 } \
1253 template<typename T> \
1254 std::enable_if_t<!std::is_same<T, double>::value, VtArray<T>> \
1255 operator op (VtArray<T> const &arr, double const &scalar) { \
1256 using Op = Vt_ArrayOpHelpScalar<T>; \
1257 VtArray<T> ret(arr.size()); \
1258 std::transform(arr.begin(), arr.end(), ret.begin(), \
1259 [&scalar](T const &aObj) { \
1260 return Op:: opName (aObj, scalar); \
1261 }); \
1262 return ret; \
1263 }
1264
1265// free functions for operators combining scalar and array types
1266ARCH_PRAGMA_PUSH
1267ARCH_PRAGMA_FORCING_TO_BOOL
1268ARCH_PRAGMA_UNSAFE_USE_OF_BOOL
1269ARCH_PRAGMA_UNARY_MINUS_ON_UNSIGNED
1270VTOPERATOR_CPPSCALAR(+, Add)
1271VTOPERATOR_CPPSCALAR(-, Sub)
1272VTOPERATOR_CPPSCALAR(*, Mul)
1273VTOPERATOR_CPPSCALAR_DOUBLE(*, Mul)
1274VTOPERATOR_CPPSCALAR(/, Div)
1275VTOPERATOR_CPPSCALAR_DOUBLE(/, Div)
1276VTOPERATOR_CPPSCALAR(%, Mod)
1277ARCH_PRAGMA_POP
1278
1279PXR_NAMESPACE_CLOSE_SCOPE
1280
1281#endif // PXR_BASE_VT_ARRAY_H
Low-level utilities for informing users of various internal and external diagnostic conditions.
Defines all the types "TYPED" for which Vt creates a VtTYPEDArray typedef.
A user-extensible hashing mechanism for use with runtime hash tables.
Definition: hash.h:472
Scoped (i.e.
Definition: mallocTag.h:249
Represents an arbitrary dimensional rectangular container class.
Definition: array.h:213
VtArray(Vt_ArrayForeignDataSource *foreignSrc, ElementType *data, size_t size, bool addRef=true)
Create an array with foreign source.
Definition: array.h:269
ELEM ElementType
Type this array holds.
Definition: array.h:218
VtArray(LegacyInputIterator first, LegacyInputIterator last, std::enable_if_t< !std::is_integral_v< LegacyInputIterator > > *=nullptr)
Create an array from a pair of iterators.
Definition: array.h:261
VtArray const & AsConst() const noexcept
Return *this as a const reference.
Definition: array.h:356
VtArray & operator=(VtArray &&other)
Move assign from other.
Definition: array.h:330
VtArray & operator=(std::initializer_list< ELEM > initializerList)
Replace current array contents with those in initializerList.
Definition: array.h:341
friend void swap(VtArray &lhs, VtArray &rhs)
Swap array contents.
Definition: array.h:1005
bool IsIdentical(VtArray const &other) const
Tests if two arrays are identical, i.e.
Definition: array.h:945
friend std::ostream & operator<<(std::ostream &out, const VtArray &self)
Outputs a comma-separated list of the values in the array surrounded by square brackets [].
Definition: array.h:998
bool operator!=(VtArray const &other) const
Tests two arrays for inequality.
Definition: array.h:960
VtArray(std::initializer_list< ELEM > initializerList)
Initialize array from the contents of a initializerList.
Definition: array.h:301
bool MakeUnique()
Ensure that this array does not share its underlying data with any other instance by making a copy if...
Definition: array.h:971
VtArray(VtArray const &other)
Copy other. The new array shares underlying data with other.
Definition: array.h:280
VtArray(size_t n)
Create an array filled with n value-initialized elements.
Definition: array.h:307
ElementType & operator[](size_t index)
Allows usage of [i].
Definition: array.h:934
VtArray()
Create an empty array.
Definition: array.h:246
VtArray(VtArray &&other)
Move from other.
Definition: array.h:295
ElementType const & operator[](size_t index) const
Allows usage of [i].
Definition: array.h:939
VtArray & operator=(VtArray const &other)
Copy assign from other.
Definition: array.h:320
bool operator==(VtArray const &other) const
Tests two arrays for equality. See also IsIdentical().
Definition: array.h:953
VtArray(size_t n, value_type const &value)
Create an array filled with n copies of value.
Definition: array.h:313
Define preprocessor function name macros.
void resize(size_t newSize, value_type &value)
Resize this array.
Definition: array.h:560
void assign(std::initializer_list< ELEM > initializerList)
Assign array contents via intializer list Equivalent to:
Definition: array.h:920
void pop_back()
Remove the last element of an array.
Definition: array.h:460
ElementType & reference
Reference type.
Definition: array.h:235
std::enable_if_t< !std::is_integral_v< std::decay_t< FillElemsFn > >, iterator > insert(const_iterator pos, size_t count, FillElemsFn &&fillElems)
Insert count elements into the array starting at pos, calling fillElems(first, last) to construct the...
Definition: array.h:735
const_reverse_iterator rend() const
Return a const reverse iterator to the start of the array.
Definition: array.h:392
const_reference front() const
Return a const reference to the first element in this array.
Definition: array.h:524
const_pointer cdata() const
Return a const pointer to the data held by this array.
Definition: array.h:407
ElementType * iterator
Iterator type.
Definition: array.h:225
size_t size() const
Return the total number of elements in this array.
Definition: array.h:474
const_iterator begin() const
Return a const iterator to the start of the array.
Definition: array.h:371
iterator erase(const_iterator pos)
Removes a single element at pos from the array.
Definition: array.h:814
std::enable_if_t< !std::is_integral_v< LegacyInputIterator >, iterator > insert(const_iterator pos, LegacyInputIterator first, LegacyInputIterator last)
Insert the elements from the range [first, last) into the array at pos.
Definition: array.h:716
constexpr size_t max_size() const
Return a theoretical maximum size limit for the container.
Definition: array.h:492
const_pointer data() const
Return a const pointer to this array's data.
Definition: array.h:405
pointer data()
Return a non-const pointer to this array's data.
Definition: array.h:403
void reserve(size_t num)
Ensure enough memory is allocated to hold num elements.
Definition: array.h:507
const_iterator cbegin() const
Return a const iterator to the start of the array.
Definition: array.h:376
iterator insert(const_iterator pos, value_type &&value)
Insert by moving a single element at pos into the array.
Definition: array.h:665
std::reverse_iterator< const_iterator > const_reverse_iterator
Reverse const iterator type.
Definition: array.h:232
void swap(VtArray &other)
Swap the contents of this array with other.
Definition: array.h:925
void push_back(ElementType const &element)
Appends an element at the end of the array.
Definition: array.h:445
ElementType * pointer
Pointer type.
Definition: array.h:239
iterator insert(const_iterator pos, value_type const &value)
Insert a copy of a single element at pos into the array.
Definition: array.h:648
ElementType const * const_iterator
Const iterator type.
Definition: array.h:227
void assign(size_t n, const value_type &fill)
Assign array contents.
Definition: array.h:904
iterator insert(const_iterator pos, size_t count, value_type const &fill)
Insert count copies of fill starting at pos in the array.
Definition: array.h:685
const_reference back() const
Return a const reference to the last element in this array.
Definition: array.h:535
ElementType const & const_reference
Const reference type.
Definition: array.h:237
bool empty() const
Return true if this array contains no elements, false otherwise.
Definition: array.h:502
reverse_iterator rend()
Return a reverse iterator to the start of the array.
Definition: array.h:385
reference front()
Return a non-const reference to the first element in this array.
Definition: array.h:521
std::enable_if<!std::is_integral< ForwardIter >::value >::type assign(ForwardIter first, ForwardIter last)
Assign array contents.
Definition: array.h:887
const_reference cback() const
Return a const reference to the last element in this array.
Definition: array.h:538
size_t capacity() const
Return the number of items this container can grow to hold without triggering a (re)allocation.
Definition: array.h:480
void resize(size_t newSize)
Resize this array.
Definition: array.h:545
void resize(size_t newSize, value_type &&value)
Resize this array.
Definition: array.h:566
const_reverse_iterator crbegin() const
Return a const reverse iterator to the end of the array.
Definition: array.h:397
ElementType const * const_pointer
Const pointer type.
Definition: array.h:241
std::reverse_iterator< iterator > reverse_iterator
Reverse iterator type.
Definition: array.h:230
const_iterator cend() const
Return a const iterator to the end of the array.
Definition: array.h:378
iterator erase(const_iterator first, const_iterator last)
Remove a range of elements [first, last) from the array.
Definition: array.h:834
void resize(size_t newSize, value_type const &value)
Resize this array.
Definition: array.h:551
iterator insert(const_iterator pos, std::initializer_list< ELEM > ilist)
Insert the contents of ilist into the array at pos.
Definition: array.h:705
const_reverse_iterator crend() const
Return a const reverse iterator to the start of the array.
Definition: array.h:399
void emplace_back(Args &&... args)
Initializes a new element at the end of the array.
Definition: array.h:415
void clear()
Equivalent to resize(0).
Definition: array.h:630
iterator end()
Returns a non-const iterator to the end of the array.
Definition: array.h:368
const_iterator end() const
Return a const iterator to the end of the array.
Definition: array.h:373
reverse_iterator rbegin()
Return a non-const reverse iterator to the end of the array.
Definition: array.h:382
void push_back(ElementType &&element)
Appends an element at the end of the array.
Definition: array.h:454
iterator begin()
Return a non-const iterator to the start of the array.
Definition: array.h:365
void resize(size_t newSize, FillElemsFn &&fillElems)
Resize this array.
Definition: array.h:575
const_reference cfront() const
Return a const reference to the first element in this array.
Definition: array.h:527
const_reverse_iterator rbegin() const
Return a const reverse iterator to the end of the array.
Definition: array.h:388
reference back()
Return a reference to the last element in this array.
Definition: array.h:532
#define TF_DEV_AXIOM(cond)
The same as TF_AXIOM, but compiled only in dev builds.
Definition: diagnostic.h:205
#define TF_CODING_ERROR(fmt, args)
Issue an internal programming error, but continue execution.
Definition: diagnostic.h:68
std::size_t hash_value(const half h)
Overload hash_value for half.
Definition: half.h:30
STL namespace.
Pragmas for controlling compiler-specific behaviors.
A trait to detect instantiations of VtArray, specialized in array.h.
Definition: traits.h:22
A trait indicating whether VtValue transform functionality can be registered for a type.
Definition: traits.h:147