Loading...
Searching...
No Matches
indexedData.h
Go to the documentation of this file.
1//
2// Copyright 2025 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_EXEC_VDF_INDEXED_DATA_H
8#define PXR_EXEC_VDF_INDEXED_DATA_H
9
11
12#include "pxr/pxr.h"
13
15#include "pxr/exec/vdf/traits.h"
16
18#include "pxr/base/tf/hash.h"
20#include "pxr/base/tf/tf.h"
21
22#include <algorithm>
23#include <ostream>
24#include <vector>
25
26PXR_NAMESPACE_OPEN_SCOPE
27
29template<typename T>
30class VdfIndexedData
31{
32public:
35 using DataType = T;
36
40 void Add(int index, VdfByValueOrConstRef<T> data) {
41 TfAutoMallocTag2 tag("Vdf", "VdfIndexedData::Add");
42 TfAutoMallocTag tag2(__ARCH_PRETTY_FUNCTION__);
43 if (!_indices.empty() && index <= _indices.back()) {
44 TF_CODING_ERROR("Indexed data must be added in sorted order.");
45 return;
46 }
47 _indices.push_back(index);
48 _data.push_back(data);
49 }
50
53 size_t GetSize() const {
54 return _indices.size();
55 }
56
60 void Reserve(size_t size) {
61 _indices.reserve(size);
62 _data.reserve(size);
63 }
64
67 bool IsEmpty() const {
68 return _indices.empty();
69 }
70
73 int GetIndex(size_t i) const {
74 return _indices[i];
75 }
76
79 VdfByValueOrConstRef<T> GetData(size_t i) const {
80 return _data[i];
81 }
82
85 T &operator[](size_t i) {
86 // This used to be called GetData(), but that caused a const/non-const
87 // ambiguity (which in turn was problamatic for std::vector<bool>).
88 return _data[i];
89 }
90
96 size_t GetFirstDataIndex(size_t currentIndex) const;
97
102 // (c.f., GetSize()) if no such index has been found.
104 size_t GetFirstDataIndex(size_t currentIndex, size_t startIndex) const {
105 const size_t size = _indices.size();
106 for (size_t i = startIndex; i < size; ++i) {
107 if (_indices[i] >= 0 &&
108 static_cast<unsigned int>(_indices[i]) >= currentIndex) {
109 return i;
110 }
111 }
112 return size;
113 }
114
128 static void Compose(VdfIndexedData *result,
129 const VdfIndexedData &weak,
130 const VdfIndexedData &strong);
131
132 // CODE_COVERAGE_OFF_VDF_DIAGNOSTICS
135 size_t GetMemoryUsage() const {
136 return sizeof(*this) +
137 sizeof(int)*_indices.capacity() +
138 sizeof(T)*_data.capacity();
139 }
140 // CODE_COVERAGE_ON
141
145 template <class HashState>
146 friend void TfHashAppend(HashState &h, const VdfIndexedData &d) {
147 h.Append(d._indices, d._data);
148 }
149
152 bool operator==(const VdfIndexedData &rhs) const {
153 return _indices == rhs._indices &&
154 _data == rhs._data;
155 }
156
159 bool operator!=(const VdfIndexedData &rhs) const {
160 return !(*this == rhs);
161 }
162
165 friend void swap(VdfIndexedData &lhs, VdfIndexedData &rhs) {
166 lhs._indices.swap(rhs._indices);
167 lhs._data.swap(rhs._data);
168 }
169
173 typedef VdfIndexedDataIterator<int> IndexIterator;
174 typedef VdfIndexedDataIterator<T> DataIterator;
175
176 // XXX
177 // Supplying a range of iterators as a pair is consistent with other usages
178 // of iterator ranges in the STL and our code base, but it would be
179 // preferred to replace that idiom entirely with a dedicated range type.
180 typedef std::pair<IndexIterator, IndexIterator> IndexIteratorRange;
181 typedef std::pair<DataIterator, DataIterator> DataIteratorRange;
182
190 IndexIteratorRange GetIndexIterators() const {
191 return std::make_pair(IndexIterator(_indices.begin()),
192 IndexIterator(_indices.end()));
193 }
194
201 DataIteratorRange GetDataIterators() const {
202 return std::make_pair(DataIterator(_data.begin()),
203 DataIterator(_data.end()));
204 }
205
206protected:
207 // Returns the non-const indices.
208 std::vector<int> &_GetWriteIndices() {
209 return _indices;
210 }
211
212 // Returns the indices.
213 const std::vector<int> &_GetReadIndices() const {
214 return _indices;
215 }
216
217 // Returns the non-const data.
218 std::vector<T> &_GetWriteData() {
219 return _data;
220 }
221
222 // Returns the data.
223 const std::vector<T> &_GetReadData() const {
224 return _data;
225 }
226
227 // Provided access to the above protected member functions for derived
228 // classes that need to call them on instances other than themselves.
229 static std::vector<int> &_GetWriteIndices(
230 VdfIndexedData<T> *o) {
231 return o->_GetWriteIndices();
232 }
233
234 static const std::vector<int> &_GetReadIndices(
235 const VdfIndexedData<T> *o) {
236 return o->_GetReadIndices();
237 }
238
239 static std::vector<T> &_GetWriteData(
240 VdfIndexedData<T> *o) {
241 return o->_GetWriteData();
242 }
243
244 static const std::vector<T> &_GetReadData(
245 const VdfIndexedData<T> *o) {
246 return o->_GetReadData();
247 }
248
249private:
250
251 // The indices corresponding to the data in _data.
252 std::vector<int> _indices;
253
254 // The data corresponding to the indices in _indices.
255 std::vector<T> _data;
256
257};
258
259template<class T>
260std::ostream &
261operator<<(std::ostream &os, const VdfIndexedData<T> &data)
262{
263 size_t sz = data.GetSize();
264
265 // Output the VdfIndexedData<T> as a python like ordered list of tuples.
266 // This seems to be the most reasonable thing to do.
267 os << '[';
268 for(size_t i=0; i<sz; i++) {
269 os << '(' << data.GetIndex(i) << ", " << data.GetData(i) << ')';
270 if (i+1 < sz)
271 os << ", ";
272 }
273 os << ']';
274 return os;
275}
276
277template<typename T>
278size_t
279VdfIndexedData<T>::GetFirstDataIndex(size_t currentIndex) const
280{
281 std::vector<int>::const_iterator it = std::lower_bound(
282 _indices.begin(), _indices.end(), currentIndex);
283 return std::distance(_indices.begin(), it);
284}
285
286template<typename T>
287void
288VdfIndexedData<T>::Compose(VdfIndexedData<T> *result,
289 const VdfIndexedData<T> &weak,
290 const VdfIndexedData<T> &strong)
291{
292
293 if ((&weak == result) ||
294 (&strong == result)) {
295 TF_CODING_ERROR("Result indexData must be different than strong or weak.");
296 return;
297 }
298
299 // Quick returns in the case where one of the vectors is empty
300 //
301 if (strong._indices.empty()) {
302 result->_indices = weak._indices;
303 result->_data = weak._data;
304 return;
305 }
306
307 if (weak._indices.empty()) {
308 result->_indices = strong._indices;
309 result->_data = strong._data;
310 return;
311 }
312
313 std::vector<int> &resultIndices = result->_indices;
314 std::vector<T> &resultData = result->_data;
315
316 // clear vector but don't free memory, should be fast once
317 // result has been resized
318 resultIndices.clear();
319 resultData.clear();
320
321 // Compose vectors in a single pass, take advantage of the fact that
322 // _indices is sorted, this is verified in Add.
323 size_t strongIndex = 0; // index into both vectors in strong
324 bool strongDone = false;// becomes true when strongIndex >= strong.size()
325
326 size_t weakIndex = 0; // index into both vectors in weak
327 bool weakDone = false;// becomes true when weakIndex >= weak.size()
328
329 // Set up local variables for accessing the indices.
330 const std::vector<int> &strongIndices = strong._indices;
331 const std::vector<int> &weakIndices = weak._indices;
332
333 size_t numStrongIndices = strongIndices.size();
334 size_t numWeakIndices = weakIndices.size();
335
336 // Iterate from start of both indexedData, stop this iteration
337 // when we hit the end of either vector
338 while ( (weakDone == false) || (strongDone == false)) {
339
340 // default to appending value from strong indexedData
341 // onto result indexedData;
342 bool useStrongValue = true;
343
344 int strongIndexValue = -1;
345 int weakIndexValue = -1;
346
347 if (strongDone) {
348
349 // if strong indexedData is empty, use weak value
350 useStrongValue = false;
351 weakIndexValue = weakIndices[weakIndex];
352
353 } else if (weakDone) {
354
355 // No need to set useStrongValue, initialized = true
356 strongIndexValue = strongIndices[strongIndex];
357
358 } else {
359
360 // we're still iterating through both vectors, do
361 // the composition depending on current index values
362
363 strongIndexValue = strongIndices[strongIndex];
364 weakIndexValue = weakIndices[weakIndex];
365
366 if (weakIndexValue < strongIndexValue) {
367
368 // Only case where we use weak value.
369 //
370 // If the values are equal then use data from strong
371 // because both indexedData have data for the same index,
372 // strong wins in this case as this is an "over"
373 // composition.
374 //
375 // if strongIndexValue > weakIndexValue, then use value
376 // from strong because weak doesn't have data for the index.
377 //
378 useStrongValue = false;
379 }
380 }
381
382 // Push back on the two std::vectors within result that we're
383 // accumulating into.
384 //
385 // XXX:optimization
386 // Hopefully result indexedData is re-used as a buffer and these
387 // push_backs don't have to malloc.
388 //
389 if (useStrongValue) {
390
391 TF_DEV_AXIOM(strongIndexValue >= 0);
392
393 resultIndices.push_back(strongIndexValue);
394 resultData.push_back(strong._data[strongIndex]);
395 ++strongIndex;
396 if (weakIndexValue == strongIndexValue) {
397 ++weakIndex;
398 }
399
400 } else {
401
402 TF_DEV_AXIOM(weakIndexValue >= 0);
403
404 // Use weak value
405 resultIndices.push_back(weakIndexValue);
406 resultData.push_back(weak._data[weakIndex]);
407 ++weakIndex;
408 }
409
410 // check if we've hit the end of either vector
411 if (strongIndex >= numStrongIndices) {
412 strongDone = true;
413 }
414 if (weakIndex >= numWeakIndices) {
415 weakDone = true;
416 }
417 }
418}
419
420// VdfIndexedData<T> is equality comparable iff T is equality comparable.
421template <typename T>
422constexpr bool VdfIsEqualityComparable<VdfIndexedData<T>> =
423 VdfIsEqualityComparable<T>;
424
425PXR_NAMESPACE_CLOSE_SCOPE
426
427#endif
Low-level utilities for informing users of various internal and external diagnostic conditions.
Scoped (i.e.
Definition: mallocTag.h:249
This is a simple "iterator filter" that erases the type and traits of the container used by VdfIndexe...
typename std::conditional_t< std::is_pointer_v< T >||Vdf_AndTypeIsSmall< T, std::is_arithmetic_v< T > >||Vdf_AndTypeIsSmall< T, std::is_enum_v< T > >, T, const T & > VdfByValueOrConstRef
Template that evaluates to either T or const T & depending on whether T best be passed as value or co...
Definition: traits.h:108
GF_API std::ostream & operator<<(std::ostream &, const GfBBox3d &)
Output a GfBBox3d using the format [(range) matrix zeroArea].
#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
A file containing basic constants and definitions.