HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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 
10 /// \file
11 
12 #include "pxr/pxr.h"
13 
15 #include "pxr/exec/vdf/traits.h"
16 
17 #include "pxr/base/tf/diagnostic.h"
18 #include "pxr/base/tf/hash.h"
19 #include "pxr/base/tf/mallocTag.h"
20 #include "pxr/base/tf/tf.h"
21 
22 #include <algorithm>
23 #include <ostream>
24 #include <vector>
25 
27 
28 ////////////////////////////////////////////////////////////////////////////////
29 template<typename T>
31 {
32 public:
33  /// The type of data held in the indexed data object.
34  ///
35  using DataType = T;
36 
37  /// Adds a corresponding (index, data) pair to this object. The index
38  /// values must be added in strictly increasing order.
39  ///
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 
51  /// Returns the number of indexed data.
52  ///
53  size_t GetSize() const {
54  return _indices.size();
55  }
56 
57  /// Reserve a size for the arrays.
58  ///
59  /// Reserving a non-zero size avoids repeated resizing.
60  void Reserve(size_t size) {
61  _indices.reserve(size);
62  _data.reserve(size);
63  }
64 
65  /// Returns true if this structure is empty, and false otherwise.
66  ///
67  bool IsEmpty() const {
68  return _indices.empty();
69  }
70 
71  /// Returns the i'th index.
72  ///
73  int GetIndex(size_t i) const {
74  return _indices[i];
75  }
76 
77  /// Returns the i'th data.
78  ///
79  VdfByValueOrConstRef<T> GetData(size_t i) const {
80  return _data[i];
81  }
82 
83  /// Returns a non-const reference to the i'th data.
84  ///
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 
91  /// Returns the first index that is greater than or equal to
92  /// \p currentIndex.
93  ///
94  /// Returns size (c.f., GetSize()) if no such index has been found.
95  ///
96  size_t GetFirstDataIndex(size_t currentIndex) const;
97 
98  /// Returns the first index that is greater than or equal to
99  /// \p currentIndex.
100  ///
101  /// Performs a linear search starting at \p startIndex. Returns size
102  // (c.f., GetSize()) if no such index has been found.
103  ///
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 
115  /// Composes indexedData with an over composition, strong over weak.
116  ///
117  /// Indices are the union of weak and strong, strong wins if they
118  /// both have data for the same index.
119  ///
120  /// Result needs to be different than weak and strong, we're modifying
121  /// result indexedData in place.
122  ///
123  /// XXX:optimization
124  /// Try to re-use result indexedData many times, onced it's been
125  /// expanded in size this composition doesn't do any mallocs. The
126  /// first time through there are mallocs due to push_backs
127  ///
128  static void Compose(VdfIndexedData *result,
129  const VdfIndexedData &weak,
130  const VdfIndexedData &strong);
131 
132  // CODE_COVERAGE_OFF_VDF_DIAGNOSTICS
133  /// Returns the amount of memory used by this data structure.
134  ///
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 
142  /// Overload for TfHashAppend. This makes VdfIndexedData hashable by
143  /// TfHash.
144  ///
145  template <class HashState>
146  friend void TfHashAppend(HashState &h, const VdfIndexedData &d) {
147  h.Append(d._indices, d._data);
148  }
149 
150  /// Returns true, if two objects are equal.
151  ///
152  bool operator==(const VdfIndexedData &rhs) const {
153  return _indices == rhs._indices &&
154  _data == rhs._data;
155  }
156 
157  /// Returns false if two objects are equal.
158  ///
159  bool operator!=(const VdfIndexedData &rhs) const {
160  return !(*this == rhs);
161  }
162 
163  /// Support for swapping two VdfIndexedData instances.
164  ///
165  friend void swap(VdfIndexedData &lhs, VdfIndexedData &rhs) {
166  lhs._indices.swap(rhs._indices);
167  lhs._data.swap(rhs._data);
168  }
169 
170  /// The following types are const forward iterators which provide limited
171  /// read access to the "index" and "data" member containers of this class.
172  ///
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 
183  /// Return a pair of STL-compliant forward input iterators that bracket the
184  /// full range of the unspecified container holding the "index" values in
185  /// this object. For read-only use by STL algorithms.
186  ///
187  /// Note that the "index" values held by this object are guaranteed to be in
188  /// sorted incrementing-value order.
189  ///
191  return std::make_pair(IndexIterator(_indices.begin()),
192  IndexIterator(_indices.end()));
193  }
194 
195  /// Return a pair of STL-compliant forward input iterators that bracket the
196  /// full range of the unspecified container holding the "data" values in
197  /// this object. For read-only use by STL algorithms.
198  ///
199  /// Note that the "data" values held by this object have no defined order.
200  ///
202  return std::make_pair(DataIterator(_data.begin()),
203  DataIterator(_data.end()));
204  }
205 
206 protected:
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 
249 private:
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 
259 template<class T>
260 std::ostream &
261 operator<<(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 
277 template<typename T>
278 size_t
279 VdfIndexedData<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 
286 template<typename T>
287 void
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.
421 template <typename T>
422 constexpr bool VdfIsEqualityComparable<VdfIndexedData<T>> =
423  VdfIsEqualityComparable<T>;
424 
426 
427 #endif
typedef int(APIENTRYP RE_PFNGLXSWAPINTERVALSGIPROC)(int)
GLboolean * data
Definition: glcorearb.h:131
#define PXR_NAMESPACE_OPEN_SCOPE
Definition: pxr.h:73
#define TF_CODING_ERROR
static const std::vector< T > & _GetReadData(const VdfIndexedData< T > *o)
Definition: indexedData.h:244
**But if you need a result
Definition: thread.h:622
size_t GetFirstDataIndex(size_t currentIndex, size_t startIndex) const
Definition: indexedData.h:104
friend void swap(VdfIndexedData &lhs, VdfIndexedData &rhs)
Definition: indexedData.h:165
static std::vector< T > & _GetWriteData(VdfIndexedData< T > *o)
Definition: indexedData.h:239
size_t GetFirstDataIndex(size_t currentIndex) const
Definition: indexedData.h:279
VdfIndexedDataIterator< int > IndexIterator
Definition: indexedData.h:173
DataIteratorRange GetDataIterators() const
Definition: indexedData.h:201
size_t GetSize() const
Definition: indexedData.h:53
#define TF_DEV_AXIOM(cond)
bool operator!=(const VdfIndexedData &rhs) const
Definition: indexedData.h:159
#define __ARCH_PRETTY_FUNCTION__
Definition: functionLite.h:29
void Add(int index, VdfByValueOrConstRef< T > data)
Definition: indexedData.h:40
static void Compose(VdfIndexedData *result, const VdfIndexedData &weak, const VdfIndexedData &strong)
Definition: indexedData.h:288
size_t GetMemoryUsage() const
Definition: indexedData.h:135
T & operator[](size_t i)
Definition: indexedData.h:85
std::vector< T > & _GetWriteData()
Definition: indexedData.h:218
void Reserve(size_t size)
Definition: indexedData.h:60
const std::vector< int > & _GetReadIndices() const
Definition: indexedData.h:213
std::pair< DataIterator, DataIterator > DataIteratorRange
Definition: indexedData.h:181
static std::vector< int > & _GetWriteIndices(VdfIndexedData< T > *o)
Definition: indexedData.h:229
GLsizeiptr size
Definition: glcorearb.h:664
GLfloat GLfloat GLfloat GLfloat h
Definition: glcorearb.h:2002
GLuint index
Definition: glcorearb.h:786
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
bool operator==(const VdfIndexedData &rhs) const
Definition: indexedData.h:152
bool IsEmpty() const
Definition: indexedData.h:67
SIM_API const UT_StringHolder distance
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
Definition: traits.h:108
VdfByValueOrConstRef< T > GetData(size_t i) const
Definition: indexedData.h:79
int GetIndex(size_t i) const
Definition: indexedData.h:73
static const std::vector< int > & _GetReadIndices(const VdfIndexedData< T > *o)
Definition: indexedData.h:234
VdfIndexedDataIterator< T > DataIterator
Definition: indexedData.h:174
friend void TfHashAppend(HashState &h, const VdfIndexedData &d)
Definition: indexedData.h:146
Definition: format.h:1821
IndexIteratorRange GetIndexIterators() const
Definition: indexedData.h:190
std::pair< IndexIterator, IndexIterator > IndexIteratorRange
Definition: indexedData.h:180
std::vector< int > & _GetWriteIndices()
Definition: indexedData.h:208
const std::vector< T > & _GetReadData() const
Definition: indexedData.h:223