HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
UT_ArrayImpl.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * NAME: Utility Library (C++)
7  *
8  * COMMENTS:
9  * This is meant to be included by UT_Array.h and includes
10  * the template implementations needed by external code.
11  */
12 
13 #pragma once
14 
15 #ifndef __UT_ARRAYIMPL_H_INCLUDED__
16 #define __UT_ARRAYIMPL_H_INCLUDED__
17 
18 #include "UT_ArrayHelp.h"
19 #include "UT_Assert.h"
20 #include "UT_Compare.h"
21 #include "UT_Swap.h"
22 #include <VM/VM_Math.h>
23 #include <SYS/SYS_Math.h>
24 #include <SYS/SYS_TypeTraits.h>
25 #include <SYS/SYS_Pragma.h>
26 
27 #include <algorithm>
28 #include <utility>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 
33 // Increasing safety levels for relocation
34 #define UT_RELOCATION_SAFETY_NONE 0
35 #define UT_RELOCATION_SAFETY_PATCHY 1
36 #define UT_RELOCATION_SAFETY_PERMISSIVE 2
37 #define UT_RELOCATION_SAFETY_NORMAL 3
38 #define UT_RELOCATION_SAFETY_MAXIMUM 4
39 
40 // Set current level
41 #define UT_RELOCATION_SAFETY_LEVEL UT_RELOCATION_SAFETY_PATCHY
42 
43 
44 #if (UT_RELOCATION_SAFETY_LEVEL >= UT_RELOCATION_SAFETY_MAXIMUM)
45 
46 // Use trivial relocation for no types at all
47 template< typename T >
49 
50 #elif (UT_RELOCATION_SAFETY_LEVEL >= UT_RELOCATION_SAFETY_NORMAL)
51 
52 // Use trivial relocation only for types that are explicitly declared safe
53 template< typename T >
55 
56 #elif (UT_RELOCATION_SAFETY_LEVEL >= UT_RELOCATION_SAFETY_PERMISSIVE)
57 
58 // Use trivial relocation for types that are explicitly declared safe
59 // and for types that must use it because of legacy code
60 template< typename T >
63  SYS_IsTriviallyRelocatable< T >::value ||
64  LegacyTrivialRelocationNoCV< std::remove_cv_t< T > >::value
65  >
66 {};
67 
68 #elif (UT_RELOCATION_SAFETY_LEVEL >= UT_RELOCATION_SAFETY_PATCHY)
69 
70 // Use trivial relocation for types that are not explicitly declared unsafe
71 // and for types that must use it due to legacy code.
72 template< typename T >
75  ( ! UnsafeTrivialRelocationNoCV< std::remove_cv_t< T > >::value ) ||
76  LegacyTrivialRelocationNoCV< std::remove_cv_t< T > >::value
77  >
78 {
79  // If T is explicitly marked as unsafe using UnsafeTrivialRelocationNoCV,
80  // then it cannot be explicitly marked as safe
81  static_assert(
82  ( ! UnsafeTrivialRelocationNoCV< std::remove_cv_t< T > >::value ) ||
84  "Not trivially relocatable"
85  );
86 };
87 
88 #else // if (UT_RELOCATION_SAFETY_LEVEL >= UT_RELOCATION_SAFETY_NONE)
89 
90 // No safety at all: each type used with UT_Array is bitwise copied,
91 // regardless of whether it is safe to do so.
92 // This is the situation of Houdini 19.5.
93 template< typename T >
95 
96 #endif
97 
98 template< typename T >
100 
101 template <typename T>
102 typename UT_Array<T>::LabeledCapacity
103 UT_Array<T>::labelOwned(const exint capacity) noexcept
104 {
105  UT_ASSERT_P( capacity >= 0 );
106 
107 #ifdef UT_ARRAY_STRICT_LABELED_CAPACITY
108 
109  return ownedLabeledCapacity( capacity );
110 
111 #else
112 
113  UT_ASSERT_P( ( capacity & LABELED_CAPACITY_MASK_VALUE ) == capacity );
114 
115  // Don't set the external bit:
116  return capacity;
117 
118 #endif
119 }
120 
121 template <typename T>
122 typename UT_Array<T>::LabeledCapacity
123 UT_Array<T>::labelExternal(const exint capacity) noexcept
124 {
125  UT_ASSERT_P( capacity >= 0 );
126 
127 #ifdef UT_ARRAY_STRICT_LABELED_CAPACITY
128 
129  return externalLabeledCapacity( capacity );
130 
131 #else
132 
133  UT_ASSERT_P( ( capacity & LABELED_CAPACITY_MASK_VALUE ) == capacity );
134 
135  // Set the external bit:
136  return capacity | LABELED_CAPACITY_FLAG_EXTERNAL;
137 
138 #endif
139 }
140 
141 template <typename T>
142 exint
144 {
145 #ifdef UT_ARRAY_STRICT_LABELED_CAPACITY
146 
147  return capacityValue( myCapacity );
148 
149 #else
150 
151  return myCapacity & LABELED_CAPACITY_MASK_VALUE;
152 
153 #endif
154 }
155 
156 template <typename T>
157 T *
158 UT_Array<T>::allocateArray(const exint capacity) noexcept
159 {
160  constexpr auto elem_size = sizeof(T); // NOLINT
161 
162  UT_ASSERT( capacity > 0 );
163 
166  //TODO: Replace this C-style cast with a static_cast
167  return (T *)malloc( capacity * elem_size );
169 }
170 
171 template <typename T>
172 T *
173 UT_Array<T>::reallocateArray(T *data, const exint capacity) noexcept
174 {
175  constexpr auto elem_size = sizeof(T); // NOLINT
176 
177  UT_ASSERT( capacity > 0 );
178 
181  //TODO: Replace this C-style cast with a static_cast
182  return (T *)realloc( data, capacity * elem_size );
184 }
185 
186 template <typename T>
187 bool
188 UT_Array<T>::isHeapBuffer(T* data) const
189 {
190  return (data != (T *)(((char*)this) + sizeof(*this)));
191 }
192 
193 template <typename T>
194 T *
196 {
197  UT_ASSERT( capacity > 0 );
198 
199  T *data = allocateArray(capacity);
200 
201  // Avoid degenerate case if we happen to be aliased the wrong way
202  if (!isHeapBuffer(data))
203  {
204  // `data` points to the end of us which erroneously causes
205  // isHeapBuffer() to incorrectly identify us as a small buffer instead
206  // of a heap buffer. Retry the allocation. Since `data` is still
207  // occupied, the new allocation's address cannot be at our end anymore.
208  T *prev = data;
209  data = allocateArray(capacity);
210 
211  deallocateArray(prev);
212  }
213 
214  return data;
215 }
216 
217 template <typename T>
218 void
219 UT_Array<T>::deallocateArray(T *p) noexcept
220 {
224  free(p);
226 }
227 
228 template <typename T>
229 void SYS_FORCE_INLINE UT_Array<T>::constructElement(T &dst)
230 {
231  if constexpr( ! SYS_IsPod_v< T > )
232  {
233  new (&dst) T();
234  }
235  else
236  {
237  //TODO: Eliminate C-style cast:
238  memset((void *)&dst, 0, sizeof(T));
239  }
240 }
241 
242 template <typename T>
243 void UT_Array<T>::constructRange(T *dst, exint n)
244 {
245  if constexpr( ! SYS_IsPod_v< T > )
246  {
247  for (exint i = 0; i < n; i++)
248  {
249  new (&dst[i]) T();
250  }
251  }
252  else if (n == 1)
253  {
254  // Special case for n == 1. If the size parameter
255  // passed to memset is known at compile time, this
256  // function call will be inlined. This results in
257  // much faster performance than a real memset
258  // function call which is required in the case
259  // below, where n is not known until runtime.
260  // This makes calls to append() much faster.
261 
262  //TODO: Eliminate C-style cast:
263  memset((void *)dst, 0, sizeof(T));
264  }
265  else
266  {
267  //TODO: Eliminate C-style cast:
268  memset((void *)dst, 0, sizeof(T) * n);
269  }
270 }
271 
272 template <typename T>
273 SYS_FORCE_INLINE void UT_Array<T>::destroyElement(T &dst) noexcept
274 {
275  if constexpr( ! SYS_IsPod_v< T > )
276  {
277  dst.~T();
278  }
279 }
280 
281 template <typename T>
282 void UT_Array<T>::destroyRange([[maybe_unused]] T *dst, exint n) noexcept
283 {
284  if constexpr( ! SYS_IsPod_v< T > )
285  {
286  for (exint i = 0; i < n; i++)
287  {
288  dst[i].~T();
289  }
290  }
291 }
292 
293 template <typename T>
294 void
295 UT_Array<T>::standardRelocateIncreasing(T *const dst, T *const src, exint n)
296 {
297  for( exint i = 0; i != n; ++i )
298  {
299  new( dst + i ) T{ std::move( src[ i ] ) };
300  src[ i ].~T();
301  }
302 }
303 
304 template <typename T>
305 void
306 UT_Array<T>::standardRelocateDecreasing(T *const dst, T *const src, exint n)
307 {
308  for( exint i = n - 1; i >= 0; --i )
309  {
310  new( dst + i ) T{ std::move( src[ i ] ) };
311  src[ i ].~T();
312  }
313 }
314 
315 template <typename T>
316 void
317 UT_Array<T>::standardRelocate(T *const dst, T *const src, exint n)
318 {
319  if( dst < src )
320  {
321  standardRelocateIncreasing( dst, src, n );
322  }
323  else if( src < dst )
324  {
325  standardRelocateDecreasing( dst, src, n );
326  }
327 }
328 
329 // Return whether the ranges [ a, a + n ) and [ b, b + n ) overlap
330 template <typename T>
331 constexpr bool
332 UTareOverlapping( const T*const a, const T*const b, exint n ) noexcept
333 {
334  return ! (
335  ( b >= a + n ) ||
336  ( a >= b + n )
337  );
338 }
339 
340 template <typename T>
341 void
342 UT_Array<T>::bitwiseRelocate(T *const dst, const T *const src, exint n) noexcept
343 {
344  UT_ASSERT( dst != nullptr );
345  UT_ASSERT( src != nullptr );
346  UT_ASSERT( n >= 0 );
347 
348  //TODO: Eliminate these C-style casts;
349  // they cause undefined behavior and
350  // hide compiler warnings that indicate potential problems in our code base
351  // GCC 14 has problems with converting to a heap buffer because this code
352  // path only happens for UT_SmallArray and it will think we're copying
353  // from invalid memory.
356  ::memmove( (void*)dst, (const void*)src, n * sizeof(T) ); // NOLINT
358 }
359 
360 template <typename T>
361 void
362 UT_Array<T>::bitwiseRelocateNonoverlapping(
363  T *const dst,
364  const T *const src,
365  exint n) noexcept
366 {
367  UT_ASSERT( dst != nullptr );
368  UT_ASSERT( src != nullptr );
369  UT_ASSERT( n >= 0 );
370  UT_ASSERT( ! UTareOverlapping( dst, src, n ) );
371 
372  //TODO: Eliminate these C-style casts;
373  // they cause undefined behavior and
374  // hide compiler warnings that indicate potential problems in our code base
375  // GCC 14 has problems with converting to a heap buffer because this code
376  // path only happens for UT_SmallArray and it will think we're copying
377  // from invalid memory.
380  ::memcpy( (void*)dst, (const void*)src, n * sizeof(T) ); // NOLINT
382 }
383 
384 template <typename T>
385 void
386 UT_Array<T>::relocateNonoverlapping(T *const dst, T *const src, exint n)
387 {
388  UT_ASSERT( n >= 0 );
389  UT_ASSERT( ! UTareOverlapping( dst, src, n ) );
390 
391  if constexpr( SYS_UseTrivialRelocation_v< T > )
392  {
393  if( n > 0 )
394  {
395  UT_ASSERT( dst != nullptr );
396  UT_ASSERT( src != nullptr );
397 
398  bitwiseRelocateNonoverlapping( dst, src, n );
399  }
400  }
401  else // constexpr
402  {
403  standardRelocateIncreasing( dst, src, n );
404  }
405 }
406 
407 template <typename T>
408 void UT_Array<T>::relocateIncreasing(T *dst, T *src, exint n)
409 {
410  if constexpr( SYS_UseTrivialRelocation_v< T > )
411  {
412  if( n > 0 )
413  {
414  UT_ASSERT( dst != nullptr );
415  UT_ASSERT( src != nullptr );
416 
417  bitwiseRelocate( dst, src, n );
418  }
419  }
420  else // constexpr
421  {
422  standardRelocateIncreasing( dst, src, n );
423  }
424 }
425 
426 template <typename T>
427 void UT_Array<T>::relocateDecreasing(T *dst, T *src, exint n)
428 {
429  if constexpr( SYS_UseTrivialRelocation_v< T > )
430  {
431  if( n > 0 )
432  {
433  UT_ASSERT( dst != nullptr );
434  UT_ASSERT( src != nullptr );
435 
436  bitwiseRelocate( dst, src, n );
437  }
438  }
439  else // constexpr
440  {
441  standardRelocateDecreasing( dst, src, n );
442  }
443 }
444 
445 template <typename T>
446 void
447 UT_Array<T>::relocate(T *const dst, T *const src, exint n)
448 {
449  UT_ASSERT( n >= 0 );
450 
451  if constexpr( SYS_UseTrivialRelocation_v< T > )
452  {
453  if( n > 0 )
454  {
455  UT_ASSERT( dst != nullptr );
456  UT_ASSERT( src != nullptr );
457 
458  bitwiseRelocate( dst, src, n );
459  }
460  }
461  else // constexpr
462  {
463  standardRelocate( dst, src, n );
464  }
465 }
466 
467 template <typename T>
468 void
469 UT_Array<T>::swapNonoverlapping(T *const dst, T *const src, exint n)
470 {
471  UT_ASSERT( n >= 0 );
472  UT_ASSERT( ! UTareOverlapping( dst, src, n ) );
473 
474  if constexpr( SYS_UseTrivialRelocation_v< T > )
475  {
476  if( n > 0 )
477  {
478  UT_ASSERT( dst != nullptr );
479  UT_ASSERT( src != nullptr );
480 
481  //TODO: bitwiseSwapNonoverlapping( dst, src, n )
482  {
483  char* bytes_dst{ reinterpret_cast< char* >( dst ) };
484  char* bytes_src{ reinterpret_cast< char* >( src ) };
485 
486  const auto num_bytes{ n * sizeof( T ) };
487  for( exint b = 0; b != num_bytes; ++b )
488  {
489  UTswap( bytes_dst[ b ], bytes_src[ b ] );
490  }
491  }
492  }
493  }
494  else // constexpr
495  {
496  for( exint i = 0; i != n; ++i )
497  {
498  T t{ std::move( src[ i ] ) };
499 
500  src[ i ].~T();
501  new( src + i ) T{ std::move( dst[ i ] ) };
502 
503  dst[ i ].~T();
504  new( dst + i ) T{ std::move( t ) };
505  }
506  }
507 }
508 
509 template <typename T>
510 void
511 UT_Array<T>::copyNonoverlapping(T *dst, const T *src, exint n)
512 {
513  if constexpr( SYS_IsPod_v< T > )
514  {
515  if (n > 0)
516  {
517  bitwiseRelocateNonoverlapping(dst, src, n);
518  }
519  }
520  else // constexpr
521  {
522  for (exint i = 0; i < n; i++)
523  {
524  new ( dst + i ) T{ src[i] };
525  }
526  }
527 }
528 
529 template <typename T>
530 inline
532  : myCapacity(labelOwned(a.size())), mySize(a.size())
533 {
534  if (a.size() > 0)
535  {
536  myData = allocateArrayHeapIdentifiable(a.size());
537  copyNonoverlapping(myData, a.array(), a.size());
538  }
539  else
540  {
541  myData = nullptr;
542  }
543 }
544 
545 template <typename T>
546 inline
547 UT_Array<T>::UT_Array(std::initializer_list<T> init)
548  : myCapacity(labelOwned(init.size())), mySize(init.size())
549 {
550  if (init.size() > 0)
551  {
552  myData = allocateArrayHeapIdentifiable(init.size());
553  copyNonoverlapping(myData, init.begin(), init.size());
554  }
555  else
556  {
557  myData = nullptr;
558  }
559 }
560 
561 template <typename T>
562 inline
564  UT_Array{ UT_ArrayCT::GENERALIZED_MOVE, nullptr, 0, std::move( a ) }
565 {
566 }
567 
568 template <typename T>
569 inline
570 UT_Array<T>::UT_Array(const exint capacity, const exint size) :
571  myData{ capacity ? allocateArrayHeapIdentifiable(capacity) : nullptr },
572  myCapacity{ labelOwned(capacity) },
573  mySize{ (capacity < size) ? capacity : size }
574 {
575  UT_ASSERT(capacity >= size);
576  constructRange(myData, mySize);
577 }
578 
579 template <typename T>
580 inline
581 UT_Array<T>::UT_Array(const exint capacity) :
582  myData{ capacity ? allocateArrayHeapIdentifiable(capacity) : nullptr },
583  myCapacity{ labelOwned(capacity) },
584  mySize{ 0 }
585 {
586 }
587 
588 template <typename T>
589 inline
591 {
592  destroyRange(myData, mySize);
593  mySize = 0;
594 
595  if (isHeapBuffer())
596  {
597  deallocateArray(myData);
598  return;
599  }
600 
601  myData = nullptr;
602 
603  myCapacity = labelOwned(0);
604 }
605 
606 template <typename T>
608  const UT_ArrayCT::ExternalCapacity,
609  T *external_data,
610  const exint external_capacity
611 ) :
612  myData{ external_data },
613  myCapacity{ labelExternal(external_capacity) },
614  mySize{ 0 }
615 {
616 }
617 
618 template <typename T>
620  const UT_ArrayCT::ExternalMove,
621  T *external_data,
622  const exint external_capacity,
623  UT_Array&& a
624 ) :
625  UT_Array{ UT_ArrayCT::GENERALIZED_MOVE, external_data, external_capacity, std::move( a ) }
626 {
627 }
628 
629 template <typename T>
631  const UT_ArrayCT::GeneralizedMove,
632  T *external_data,
633  const exint external_capacity,
634  UT_Array&& a
635 ) :
636  myData{ external_data },
637  myCapacity{ labelExternal(external_capacity) },
638  mySize{ 0 }
639 {
640  if( ! a.isHeapBuffer() )
641  {
642  if( a.mySize > external_capacity )
643  {
644  myData = allocateArrayHeapIdentifiable(a.mySize);
645  myCapacity = labelOwned(a.mySize);
646  }
647 
648  relocateNonoverlapping(myData, a.myData, a.mySize);
649  mySize = std::exchange( a.mySize, 0 );
650 
651  return;
652  }
653 
654  myData = std::exchange( a.myData, nullptr );
655  myCapacity = std::exchange( a.myCapacity, labelOwned(0) );
656 
657  mySize = std::exchange( a.mySize, 0 );
658 }
659 
660 template <typename T>
661 void UT_Array<T>::convertToHeapBuffer(const exint capacity)
662 {
663  UT_ASSERT(!isHeapBuffer());
664  UT_ASSERT(mySize<=capacity);
665 
666  T *heap_data{ nullptr };
667 
668  if( capacity > 0 )
669  {
670  heap_data = allocateArray(capacity);
671  relocateNonoverlapping(heap_data, myData, mySize);
672  }
673 
674  myData = heap_data;
675  myCapacity = labelOwned(capacity);
676 
677  // Because we were a nonheap buffer before the call,
678  // it is for impossible myData to have been allocated exactly
679  // at the end of the UT_Array part.
680  // That part of memory is already occupied by UT_SmallArray.
681  // So there is no chance of *this being incorrectly identified
682  // as a nonheap buffer.
683  UT_ASSERT(isHeapBuffer());
684 }
685 
686 template <typename T>
687 inline void
689 {
690  if(
691  ( ( ! isHeapBuffer() ) || ( ! other.isHeapBuffer() ) ) &&
692  ( ( other.mySize <= capacity() ) && ( mySize <= other.capacity() ) )
693  )
694  {
695  // Optimization that applies when one or both
696  // UT_Array objects are nonheap buffers:
697  // As long as each array has the capacity to store the contents of
698  // the other, the elements can be swapped individually.
699  // This avoids the need to convert any nonheap buffer to a heap buffer.
700 
701  swapNonoverlapping( myData, other.myData, SYSmin( mySize, other.mySize ) );
702 
703  if( mySize < other.mySize )
704  {
705  relocateNonoverlapping( myData + mySize, other.myData + mySize, other.mySize - mySize );
706  }
707  else if( other.mySize < mySize )
708  {
709  relocateNonoverlapping( other.myData + other.mySize, myData + other.mySize, mySize - other.mySize );
710  }
711  }
712  else
713  {
714  if( ! isHeapBuffer() )
715  {
716  convertToHeapBuffer( capacity() );
717  }
718 
719  if( ! other.isHeapBuffer() )
720  {
721  other.convertToHeapBuffer( other.capacity() );
722  }
723 
724  UTswap(myData, other.myData);
725  UTswap(myCapacity, other.myCapacity);
726  }
727 
728  UTswap(mySize, other.mySize);
729 }
730 
731 template <typename T>
732 inline exint
734 {
735  if (index >= mySize)
736  {
737  bumpCapacity(index + 1);
738 
739  constructRange(myData + mySize, index - mySize + 1);
740 
741  mySize = index+1;
742  return index;
743  }
744  bumpCapacity(mySize + 1);
745 
746  UT_ASSERT_P(index >= 0);
747  relocateDecreasing(myData + index + 1, myData + index, mySize-index);
748 
749  constructElement(myData[index]);
750 
751  mySize++;
752  return index;
753 }
754 
755 template <typename T>
756 template <typename S>
757 inline exint
759 {
760  if (mySize == capacity())
761  {
762  exint idx = safeIndex(s);
763 
764  // NOTE: UTbumpAlloc always returns a strictly larger value.
765  setCapacity(UTbumpAlloc(capacity()));
766  if (idx >= 0)
767  construct(myData[mySize], std::forward<S>(myData[idx]));
768  else
769  construct(myData[mySize], std::forward<S>(s));
770  }
771  else
772  {
773  construct(myData[mySize], std::forward<S>(s));
774  }
775  return mySize++;
776 }
777 
778 template <typename T>
779 template <typename... S>
780 inline exint
782 {
783 #if UT_ASSERT_LEVEL >= UT_ASSERT_LEVEL_PARANOID
784  validateEmplaceArgs(std::forward<S>(s)...);
785 #endif
786 
787  if (mySize == capacity())
788  {
789  setCapacity(UTbumpAlloc(capacity()));
790  }
791 
792  construct(myData[mySize], std::forward<S>(s)...);
793  return mySize++;
794 }
795 
796 template <typename T>
797 inline void
799 {
800  bumpCapacity(mySize + count);
801  copyNonoverlapping(myData + mySize, pt, count);
802  mySize += count;
803 }
804 
805 template <typename T>
806 inline void
808 {
809  UT_ASSERT_P(count >= 0);
810  if (count <= 0)
811  return;
812  if (mySize + count >= capacity())
813  {
814  exint tidx = safeIndex(t);
815 
816  bumpCapacity(mySize + count);
817 
818  for (exint i = 0; i < count; i++)
819  copyConstruct(myData[mySize+i], tidx >= 0 ? myData[tidx] : t);
820  }
821  else
822  {
823  for (exint i = 0; i < count; i++)
824  copyConstruct(myData[mySize+i], t);
825  }
826  mySize += count;
827 }
828 
829 template <typename T>
830 inline exint
831 UT_Array<T>::sortedInsert(const T &t, Comparator compare)
832 {
833  return sortedInsert(t, UTcompareLess(compare));
834 }
835 
836 template <typename T>
837 template <typename ComparatorBool, typename>
838 inline exint
839 UT_Array<T>::sortedInsert(const T &t, ComparatorBool is_less)
840 {
841  exint low, mid, high;
842 
843  low = 0;
844  high = size() - 1;
845  while (low <= high)
846  {
847  mid = (low + high) / 2;
848  if (is_less(t, myData[mid]))
849  high = mid - 1;
850  else if (is_less(myData[mid], t))
851  low = mid + 1;
852  else
853  {
854  insertAt(t, mid);
855  return mid;
856  }
857  }
858  insertAt(t, low);
859  return low;
860 }
861 
862 template <typename T>
863 template <typename S>
864 inline exint
866 {
867  exint low, mid, high;
868 
869  low = 0;
870  high = size() - 1;
871  while (low <= high)
872  {
873  mid = (low + high) / 2;
874  if (compare(&s, &myData[mid]) < 0)
875  high = mid - 1;
876  else if (compare(&s, &myData[mid]) > 0)
877  low = mid + 1;
878  else
879  return (exint)mid;
880  }
881  insertImpl(std::forward<S>(s), low);
882  return low;
883 }
884 
885 template <typename T>
886 template <typename ComparatorBool, typename>
887 inline exint
888 UT_Array<T>::uniqueSortedInsert(const T &t, ComparatorBool is_less)
889 {
890  exint low, mid, high;
891 
892  low = 0;
893  high = size() - 1;
894  while (low <= high)
895  {
896  mid = (low + high) / 2;
897  if (t == myData[mid])
898  return (exint)mid;
899  else if (is_less(t, myData[mid]))
900  high = mid - 1;
901  else
902  low = mid + 1;
903  }
904  insertAt(t, low);
905  return low;
906 }
907 
908 template <typename T>
909 template <typename ComparatorBool, typename>
910 inline exint
911 UT_Array<T>::uniqueSortedFind(const T &item, ComparatorBool is_less) const
912 {
913  exint len = size();
914  exint idx = 0;
915 
916  while(len > 0)
917  {
918  exint h = len / 2;
919  if (is_less(myData[idx + h], item))
920  {
921  idx += h + 1;
922  len -= h + 1;
923  }
924  else
925  len = h;
926  }
927 
928  return (idx != size() && !is_less(item, myData[idx])) ? idx : -1;
929 }
930 
931 template <typename T>
932 inline exint
933 UT_Array<T>::uniqueSortedFind(const T &item, Comparator compare) const
934 {
935  return uniqueSortedFind(item, UTcompareLess(compare));
936 }
937 
938 template <typename T>
939 inline exint
940 UT_Array<T>::heapPush(const T &t, Comparator compare)
941 {
942  UT_ASSERT(safeIndex(t) < 0);
943  exint i = append(t);
944 
945  // walk up towards the root restoring the heap condition
946  while( i > 0 && compare(&myData[(i - 1) / 2], &t) < 0 )
947  {
948  myData[i] = myData[(i - 1) / 2];
949  i = (i - 1) / 2;
950  }
951  myData[i] = t;
952  return i;
953 }
954 
955 template <typename T>
956 inline T
958 {
959  UT_ASSERT_P(mySize > 0);
960 
961  T result = myData[0];
962  // move last item into the newly created hole
963  myData[0] = myData[mySize - 1];
964  removeAt(mySize - 1);
965  // percolate down until the heap condition is restored
966  exint idx = 0;
967  for(;;)
968  {
969  exint largest = idx;
970 
971  // test heap condition with left child
972  exint cidx = 2 * idx + 1;
973  if( cidx < mySize && compare(&myData[largest], &myData[cidx]) < 0 )
974  largest = cidx;
975  // test heap condition with right child
976  ++cidx;
977  if( cidx < mySize && compare(&myData[largest], &myData[cidx]) < 0 )
978  largest = cidx;
979  if( largest == idx )
980  {
981  // heap condition has been restored
982  break;
983  }
984 
985  UTswap(myData[idx], myData[largest]);
986  idx = largest;
987  }
988 
989  return result;
990 }
991 
992 template <typename T>
993 inline exint
995 {
996  bumpCapacity(mySize + a.mySize);
997  copyNonoverlapping(myData + mySize, a.myData, a.mySize);
998  mySize += a.mySize;
999 
1000  return mySize;
1001 }
1002 
1003 template <typename T>
1004 inline exint
1006 {
1007  // If this array was empty, we can just steal from the other array.
1008  if (mySize == 0)
1009  {
1010  operator=(std::move(a));
1011  return mySize;
1012  }
1013 
1014  const exint n = a.mySize;
1015  bumpCapacity(mySize + n);
1016  relocateNonoverlapping(myData + mySize, a.myData, n);
1017  mySize += n;
1018  a.mySize = 0;
1019 
1020  return mySize;
1021 }
1022 
1023 template <typename T>
1024 inline exint
1026 {
1027  exint end_index = beg_index + count;
1028 
1029  if (beg_index >= mySize)
1030  {
1031  bumpCapacity(end_index);
1032 
1033  constructRange(myData + mySize, end_index - mySize);
1034 
1035  mySize = end_index;
1036  return beg_index;
1037  }
1038  bumpCapacity(mySize+count);
1039 
1040  relocateDecreasing(myData + end_index, myData + beg_index, mySize-beg_index);
1041  mySize += count;
1042 
1043  constructRange(myData + beg_index, count);
1044 
1045  return beg_index;
1046 }
1047 
1048 template <typename T>
1049 template <typename S>
1050 inline exint
1052 {
1053  if (index == mySize)
1054  {
1055  // This case avoids an extraneous call to constructRange()
1056  // which the compiler may not optimize out.
1057  (void) appendImpl(std::forward<S>(s));
1058  }
1059  else if (index > mySize)
1060  {
1061  exint src_i = safeIndex(s);
1062 
1063  bumpCapacity(index + 1);
1064 
1065  constructRange(myData + mySize, index - mySize);
1066 
1067  if (src_i >= 0)
1068  construct(myData[index], std::forward<S>(myData[src_i]));
1069  else
1070  construct(myData[index], std::forward<S>(s));
1071 
1072  mySize = index + 1;
1073  }
1074  else // (index < mySize)
1075  {
1076  exint src_i = safeIndex(s);
1077 
1078  bumpCapacity(mySize + 1);
1079 
1080  relocateDecreasing(myData + index + 1, myData + index, mySize-index);
1081 
1082  if (src_i >= index)
1083  ++src_i;
1084 
1085  if (src_i >= 0)
1086  construct(myData[index], std::forward<S>(myData[src_i]));
1087  else
1088  construct(myData[index], std::forward<S>(s));
1089 
1090  ++mySize;
1091  }
1092 
1093  return index;
1094 }
1095 
1096 template <typename T>
1097 template <typename S>
1098 inline exint
1100 {
1101  exint idx = find(s);
1102  return (idx < 0) ? -1 : removeAt((exint)idx);
1103 }
1104 
1105 template <typename T>
1106 inline exint
1108 {
1109  destroyElement(myData[idx]);
1110  if (idx != --mySize)
1111  {
1112  relocateIncreasing(myData + idx, myData + idx + 1, mySize - idx);
1113  }
1114 
1115  return idx;
1116 }
1117 
1118 template <typename T>
1119 inline void
1121 {
1122  UT_ASSERT(begin_i <= end_i);
1123  UT_ASSERT(end_i <= mySize);
1124 
1125  const exint nelements = end_i - begin_i;
1126  if (nelements <= 0)
1127  {
1128  return;
1129  }
1130 
1131  destroyRange(myData + begin_i, nelements);
1132  relocateIncreasing(myData + begin_i, myData + end_i, mySize - end_i);
1133  mySize -= nelements;
1134 }
1135 
1136 template <typename T>
1137 inline void
1139 {
1140  UT_ASSERT_P(begin_i >= 0);
1141  UT_ASSERT_P(begin_i <= end_i);
1142  UT_ASSERT_P(end_i <= size());
1143  UT_ASSERT(this != &dest);
1144 
1145  exint nelements = end_i - begin_i;
1146 
1147  // grow the raw array if necessary.
1148  dest.setCapacityIfNeeded(nelements);
1149 
1150  if( nelements > 0 )
1151  {
1152  relocate(dest.myData, myData + begin_i, nelements);
1153  }
1154  dest.mySize = nelements;
1155 
1156  // we just asserted this was true, but just in case
1157  if (this != &dest)
1158  {
1159  if (end_i < size())
1160  {
1161  if( nelements > 0 )
1162  {
1163  relocateIncreasing(myData + begin_i, myData + end_i, mySize - end_i);
1164  }
1165  }
1166  mySize -= nelements;
1167  if (mySize<0)
1168  mySize=0;
1169  }
1170 }
1171 
1172 template <typename T>
1173 inline void
1174 UT_Array<T>::move(exint src_idx, exint dst_idx, exint how_many)
1175 {
1176  // Make sure all the parameters are valid.
1177  if (src_idx < 0)
1178  src_idx = 0;
1179  if (dst_idx < 0)
1180  dst_idx = 0;
1181  // If we are told to move a set of elements that would extend beyond the
1182  // end of the current array, trim the group.
1183  if (src_idx + how_many > size())
1184  how_many = size() - src_idx;
1185  // If the dst_idx would have us move the source beyond the end of the
1186  // current array, move the dst_idx back.
1187  if (dst_idx + how_many > size())
1188  dst_idx = size() - how_many;
1189  if (src_idx != dst_idx && how_many > 0)
1190  {
1191  exint savelen = SYSabs(src_idx - dst_idx);
1192 
1193  T* tmp = allocateArray(savelen);
1194 
1195  if (src_idx > dst_idx && how_many > 0)
1196  {
1197  // We're moving the group backwards. Save all the stuff that
1198  // we would overwrite, plus everything beyond that to the
1199  // start of the source group. Then move the source group, then
1200  // tack the saved data onto the end of the moved group.
1201  relocateNonoverlapping(tmp, &myData[dst_idx], savelen);
1202  relocate(&myData[dst_idx], &myData[src_idx], how_many);
1203  relocateNonoverlapping(&myData[dst_idx + how_many], tmp, savelen);
1204  }
1205  if (src_idx < dst_idx && how_many > 0)
1206  {
1207  // We're moving the group forwards. Save from the end of the
1208  // group being moved to the end of the where the destination
1209  // group will end up. Then copy the source to the destination.
1210  // Then move back up to the original source location and drop
1211  // in our saved data.
1212  relocateNonoverlapping(tmp, &myData[src_idx + how_many], savelen);
1213  relocate(&myData[dst_idx], &myData[src_idx], how_many);
1214  relocateNonoverlapping(&myData[src_idx], tmp, savelen);
1215  }
1216 
1217  deallocateArray(tmp);
1218  }
1219 }
1220 
1221 template <typename T>
1222 template <typename IsEqual>
1223 inline exint
1224 UT_Array<T>::removeIf(IsEqual is_equal)
1225 {
1226  // Move dst to the first element to remove.
1227  exint dst;
1228  for (dst = 0; dst < mySize; dst++)
1229  {
1230  if (is_equal(myData[dst]))
1231  break;
1232  }
1233  // Now start looking at all the elements past the first one to remove.
1234  for (exint idx = dst+1; idx < mySize; idx++)
1235  {
1236  if (!is_equal(myData[idx]))
1237  {
1238  UT_ASSERT(idx != dst);
1239  myData[dst] = std::move(myData[idx]);
1240  dst++;
1241  }
1242  // On match, ignore.
1243  }
1244 
1245  // Don't call setSize(dst) since it supports growing the array which
1246  // requires a default constructor. Avoiding it allows this to be used on
1247  // types that lack a default constructor.
1248  destroyRange(myData + dst, mySize - dst);
1249  mySize = dst;
1250 
1251  return mySize;
1252 }
1253 
1254 template <typename T>
1255 inline void
1257 {
1258  exint numShift; // The number of items we shift
1259  exint remaining; // mySize - numShift
1260 
1261  if (how_many == 0 || mySize < 1)
1262  return;
1263 
1264  numShift = how_many % (exint)mySize;
1265  if (numShift < 0) numShift += mySize;
1266  remaining = mySize - numShift;
1267 
1268  if( numShift == 0 )
1269  {
1270  return;
1271  }
1272 
1273  T* tmp = allocateArray(numShift);
1274 
1275  relocate(tmp, myData + remaining, numShift);
1276  relocate(myData + numShift, myData, remaining);
1277  relocate(myData + 0, tmp, numShift);
1278 
1279  deallocateArray(tmp);
1280 }
1281 
1282 template <typename T>
1283 inline void
1285 {
1286  for (exint i = 0; i < mySize; i++)
1287  {
1288  myData[i] = value;
1289  }
1290 }
1291 
1292 // This constant() specialization needs to be declared here and implemented
1293 // in UT_Array.C.
1294 template <>
1296 
1297 template <typename T>
1298 inline void
1300 {
1301  if constexpr( SYS_IsPod_v< T > )
1302  {
1303  ::memset((void *)myData, 0, mySize*sizeof(T)); // NOLINT
1304  }
1305  else // constexpr
1306  {
1307  constructRange(myData, mySize);
1308  }
1309 }
1310 
1311 template <typename T>
1312 template <typename S>
1313 inline exint
1314 UT_Array<T>::find(const S &s, exint start) const
1315 {
1316  const T *end = myData + mySize;
1317  for (const T *p = myData + start; p < end; ++p)
1318  if (*p == s)
1319  return (p - myData);
1320  return -1;
1321 }
1322 
1323 template <typename T>
1324 template <typename IsEqual>
1325 exint
1326 UT_Array<T>::findIf(IsEqual is_equal, exint start) const
1327 {
1328  const T *end = myData + mySize;
1329  for (const T *p = myData + start; p < end; ++p)
1330  if (is_equal(*p))
1331  return (p - myData);
1332  return -1;
1333 }
1334 
1335 template <typename T>
1336 exint
1337 UT_Array<T>::sortedFind(const T &t, Comparator compare) const
1338 {
1339  T *found;
1340 
1341  if( mySize == 0 ) return -1;
1342 
1343  // NOLINTNEXTLINE
1344  found = (T *)::bsearch(&t, myData, mySize, sizeof(T),
1345  (ut_ptr_compare_func_t)compare);
1346  return found ? (found - myData) : -1;
1347 }
1348 
1349 template <typename T>
1350 inline void
1352 {
1353  exint n = mySize / 2;
1354  for (exint i = 0; i < n; i++ )
1355  UTswap(myData[i], myData[mySize-1-i]);
1356 }
1357 
1358 template <typename T>
1359 inline void
1361 {
1362  std::sort(myData, myData + mySize, UTcompareLess(compare));
1363 }
1364 
1365 template <typename T>
1366 template <typename ComparatorBool>
1367 inline T
1368 UT_Array<T>::selectNthLargest(exint idx, ComparatorBool is_less)
1369 {
1370  // The idea of returning doesn't make sense if we have
1371  // an empty array.
1372  UT_ASSERT(size() > 0);
1373  if (size() == 0)
1374  return T();
1375 
1376  idx = SYSclamp(idx, (exint)0, (exint)(size())-1);
1377 
1378  UTnth_element(myData, &myData[idx], &myData[size()], is_less);
1379 
1380  return myData[idx];
1381 }
1382 
1383 template <typename T>
1384 inline void
1386 {
1387  // Do nothing when new capacity is the same as the current
1388  if (new_capacity == capacity())
1389  {
1390  return;
1391  }
1392 
1393  // Special case for non-heap buffers
1394  if (!isHeapBuffer())
1395  {
1396  if (new_capacity < mySize)
1397  {
1398  // Destroy the extra elements without changing capacity()
1399  destroyRange(myData + new_capacity, mySize - new_capacity);
1400  mySize = new_capacity;
1401  }
1402  else if (new_capacity > capacity())
1403  {
1404  convertToHeapBuffer(new_capacity);
1405  }
1406  else
1407  {
1408  // Keep capacity unchanged in this case
1409  UT_ASSERT_P(new_capacity >= mySize && new_capacity <= capacity());
1410  }
1411  return;
1412  }
1413 
1414  if (new_capacity == 0)
1415  {
1416  if (myData)
1417  {
1418  destroyRange(myData, mySize);
1419  deallocateArray(myData);
1420  }
1421  myData = nullptr;
1422  myCapacity = labelOwned(0);
1423  mySize = 0;
1424  return;
1425  }
1426 
1427  if (new_capacity < mySize)
1428  {
1429  destroyRange(myData + new_capacity, mySize - new_capacity);
1430  mySize = new_capacity;
1431  }
1432 
1433  if (myData)
1434  {
1435  if constexpr( SYS_UseTrivialRelocation_v< T > )
1436  {
1437  myData = reallocateArray(myData, new_capacity);
1438  }
1439  else // constexpr
1440  {
1441  T *prev = myData;
1442  myData = allocateArray(new_capacity);
1443  if (mySize > 0)
1444  {
1445  relocateNonoverlapping(myData, prev, mySize);
1446  }
1447 
1448  deallocateArray(prev);
1449  }
1450  }
1451  else
1452  {
1453  myData = allocateArray(new_capacity);
1454  }
1455 
1456  // Avoid degenerate case if we happen to be aliased the wrong way
1457  if (!isHeapBuffer())
1458  {
1459  // `myData` points to the end of us which erroneously causes
1460  // isHeapBuffer() to incorrectly identify us as a small buffer instead
1461  // of a heap buffer. Retry the allocation. Since `myData` is still
1462  // occupied, the new allocation's address cannot be at our end anymore.
1463  T *prev = myData;
1464  myData = allocateArray(new_capacity);
1465  if (mySize > 0)
1466  {
1467  relocateNonoverlapping(myData, prev, mySize);
1468  }
1469 
1470  deallocateArray(prev);
1471  }
1472 
1473  myCapacity = labelOwned(new_capacity);
1474  UT_ASSERT(myData);
1475 }
1476 
1477 template <typename T>
1478 inline UT_Array<T> &
1480 {
1481  if (this == &a)
1482  {
1483  return *this;
1484  }
1485 
1486  UT_Array t{ a };
1487  swap( t );
1488 
1489  return *this;
1490 }
1491 
1492 template <typename T>
1493 inline UT_Array<T> &
1494 UT_Array<T>::operator=(std::initializer_list<T> a)
1495 {
1496  const exint new_size = a.size();
1497 
1498  // Grow the raw array if necessary.
1499  setCapacityIfNeeded(new_size);
1500 
1501  // Make sure destructors and constructors are called on all elements
1502  // being removed/added.
1503  destroyRange(myData, mySize);
1504 
1505  copyNonoverlapping(myData, a.begin(), new_size);
1506 
1507  mySize = new_size;
1508 
1509  return *this;
1510 }
1511 
1512 template <typename T>
1513 inline UT_Array<T> &
1515 {
1516  // Satisfy requirement that if 'a' has a heap buffer,
1517  // then *this has to have a heap buffer as well.
1518  if((!isHeapBuffer()) && a.isHeapBuffer())
1519  {
1520  destroyRange(myData, mySize);
1521  mySize = 0;
1522  convertToHeapBuffer(0);
1523  }
1524 
1525  UT_Array t{ std::move( a ) };
1526  swap( t );
1527 
1528  return *this;
1529 }
1530 
1531 
1532 template <typename T>
1533 inline bool
1535 {
1536  if (this == &a) return true;
1537  if (mySize != a.size()) return false;
1538  for (exint i = 0; i < mySize; i++)
1539  if (!(myData[i] == a(i))) return false;
1540  return true;
1541 }
1542 
1543 template <typename T>
1544 inline bool
1546 {
1547  return (!operator==(a));
1548 }
1549 
1550 template <typename T>
1551 template <typename ComparatorBool, typename>
1552 inline bool
1553 UT_Array<T>::isEqual(const UT_Array<T> &a, ComparatorBool is_equal) const
1554 {
1555  if (this == &a) return true;
1556  if (mySize != a.size()) return false;
1557  for (exint i = 0; i < mySize; i++)
1558  {
1559  if (!is_equal(myData[i], a[i])) return false;
1560  }
1561  return true;
1562 }
1563 
1564 template <typename T>
1565 inline int
1566 UT_Array<T>::isEqual(const UT_Array<T> &a, Comparator compare) const
1567 {
1568  return isEqual(a, UTcompareEqual(compare));
1569 }
1570 
1571 template <typename T>
1572 inline exint
1573 UT_Array<T>::apply(int (*apply_func)(T &t, void *d), void *d)
1574 {
1575  exint i;
1576  for (i = 0; i < mySize; i++)
1577  {
1578  if (apply_func(myData[i], d))
1579  break;
1580  }
1581  return i;
1582 }
1583 
1584 // Merge the given array into us.
1585 // If direction is -1, then it assumes us and 'other' are both already
1586 // sorted in descending order. Similarly, +1 means ascending.
1587 // If allow_dups is false, then it further assumes that both arrays have no
1588 // duplicates and will produce a result that also has no duplicates.
1589 // More work will be needed if you want allow_dups to mean remove duplicates
1590 template <typename T>
1591 template <typename ComparatorBool>
1592 inline void
1594  const UT_Array<T> &other, int direction, bool allow_dups,
1595  ComparatorBool is_less)
1596 {
1598  exint our_idx;
1599  exint other_idx;
1600 
1601  // handle trivial cases to avoid extra work
1602  if (other.size() == 0)
1603  return;
1604  if (size() == 0)
1605  {
1606  concat(other);
1607  return;
1608  }
1609 
1610  UT_ASSERT( direction == -1 || direction == +1 );
1611  direction = (direction > 0) ? +1 : -1;
1612 
1613  our_idx = 0;
1614  other_idx = 0;
1615  while( our_idx < size() && other_idx < other.size() )
1616  {
1617  const T &our_item = (*this)(our_idx);
1618  const T &other_item = other(other_idx);
1619  exint item_dir;
1620 
1621  if (our_item == other_item)
1622  item_dir = 0;
1623  else if (is_less(our_item, other_item))
1624  item_dir = -1;
1625  else
1626  item_dir = +1;
1627 
1628  if( item_dir != 0 )
1629  {
1630  // we need to do an comparison in the next line to take care of the
1631  // fact that -INT_MIN is still less than 0.
1632  item_dir = ( (item_dir > 0) ? +1 : -1 ) * direction;
1633  }
1634 
1635  if( item_dir < 0 )
1636  {
1637  result.append( our_item );
1638  our_idx++;
1639  }
1640  else if( item_dir > 0 )
1641  {
1642  result.append( other_item );
1643  other_idx++;
1644  }
1645  else
1646  {
1647  result.append( our_item );
1648  our_idx++;
1649  if( allow_dups )
1650  result.append( other_item );
1651  other_idx++;
1652  }
1653  }
1654 
1655  UT_ASSERT( our_idx == size() || other_idx == other.size() );
1656  for( ; our_idx < size(); our_idx++ )
1657  result.append( (*this)(our_idx) );
1658  for( ; other_idx < other.size(); other_idx++ )
1659  result.append( other(other_idx) );
1660 
1661  // finally swap the result into us
1662  swap( result );
1663 }
1664 
1665 // A variant of merge(..) that takes an rvalue reference and moves the contents
1666 // of the incoming array into the result, instead of copying them. For example,
1667 // for merging two sorted arrays of unique_ptr<t> into a single array.
1668 template <typename T>
1669 template <typename ComparatorBool>
1670 inline void
1672  UT_Array<T> &&other, int direction, bool allow_dups,
1673  ComparatorBool is_less) noexcept
1674 {
1676  exint our_idx;
1677  exint other_idx;
1678 
1679  // handle trivial cases to avoid extra work
1680  if (other.size() == 0)
1681  return;
1682  if (size() == 0)
1683  {
1684  concat(std::move(other));
1685  return;
1686  }
1687 
1688  UT_ASSERT( direction == -1 || direction == +1 );
1689  direction = (direction > 0) ? +1 : -1;
1690 
1691  our_idx = 0;
1692  other_idx = 0;
1693  while( our_idx < size() && other_idx < other.size() )
1694  {
1695  T &our_item = (*this)(our_idx);
1696  T &other_item = other(other_idx);
1697  exint item_dir;
1698 
1699  if (our_item == other_item)
1700  item_dir = 0;
1701  else if (is_less(our_item, other_item))
1702  item_dir = -1;
1703  else
1704  item_dir = +1;
1705 
1706  if( item_dir != 0 )
1707  {
1708  // we need to do an comparison in the next line to take care of the
1709  // fact that -INT_MIN is still less than 0.
1710  item_dir = ( (item_dir > 0) ? +1 : -1 ) * direction;
1711  }
1712 
1713  if( item_dir < 0 )
1714  {
1715  result.append( std::move(our_item) );
1716  our_idx++;
1717  }
1718  else if( item_dir > 0 )
1719  {
1720  result.append( std::move(other_item) );
1721  other_idx++;
1722  }
1723  else
1724  {
1725  result.append( std::move(our_item) );
1726  our_idx++;
1727  if( allow_dups )
1728  result.append( std::move(other_item) );
1729  other_idx++;
1730  }
1731  }
1732 
1733  UT_ASSERT( our_idx == size() || other_idx == other.size() );
1734  for( ; our_idx < size(); our_idx++ )
1735  result.append( std::move((*this)(our_idx)) );
1736  for( ; other_idx < other.size(); other_idx++ )
1737  result.append( std::move(other(other_idx)) );
1738 
1739  // clear the input array to avoid leaving behind a non-zero sized
1740  // array of empty values, which have been moved into this array
1741  other.clear();
1742 
1743  // finally swap the result into us
1744  swap( result );
1745 }
1746 
1747 template <typename T>
1748 inline bool
1750  const UT_Array<T> &other,
1751  Comparator compare) const
1752 {
1753  return hasSortedSubset(other, UTcompareLess(compare));
1754 }
1755 
1756 template <typename T>
1757 template <typename ComparatorBool, typename>
1758 inline bool
1760  const UT_Array<T> &other,
1761  ComparatorBool compare) const
1762 {
1763  return std::includes(
1764  myData, myData + mySize,
1765  other.myData, other.myData + other.mySize,
1766  compare);
1767 }
1768 
1769 template <typename T>
1770 inline void
1772 {
1773  sortedUnion(other, UTcompareLess(compare));
1774 }
1775 
1776 template <typename T>
1777 inline void
1779  const UT_Array<T> &other,
1781  Comparator compare) const
1782 {
1783  sortedUnion(other, result, UTcompareLess(compare));
1784 }
1785 
1786 template <typename T>
1787 inline void
1789 {
1790  sortedIntersection(other, UTcompareLess(compare));
1791 }
1792 
1793 template <typename T>
1794 inline void
1796  const UT_Array<T> &other,
1798  Comparator compare) const
1799 {
1800  sortedIntersection(other, result, UTcompareLess(compare));
1801 }
1802 
1803 template <typename T>
1804 inline void
1806 {
1807  sortedSetDifference(other, UTcompareLess(compare));
1808 }
1809 
1810 template <typename T>
1811 inline void
1813  const UT_Array<T> &other,
1815  Comparator compare) const
1816 {
1817  sortedSetDifference(other, result, UTcompareLess(compare));
1818 }
1819 
1820 template <typename T>
1821 template <typename ComparatorBool, typename>
1822 inline void
1823 UT_Array<T>::sortedUnion(const UT_Array<T> &other, ComparatorBool is_less)
1824 {
1825  UT_Array<T> temp;
1826  sortedUnion( other, temp, is_less );
1827  swap( temp );
1828 }
1829 
1830 template <typename T>
1831 template <typename ComparatorBool, typename>
1832 inline void
1834  const UT_Array<T> &other,
1836  ComparatorBool is_less) const
1837 {
1838  // Can't store to either input.
1839  UT_ASSERT(&result != this && &result != &other);
1840  UT_ASSERT(result.size() == 0);
1841 
1842  std::set_union(
1843  begin(), end(), other.begin(), other.end(), AppendIterator(result),
1844  is_less);
1845 }
1846 
1847 template <typename T>
1848 template <typename ComparatorBool, typename>
1849 inline void
1851  const UT_Array<T> &other,
1852  ComparatorBool is_less)
1853 {
1854  UT_Array<T> temp;
1855  sortedIntersection( other, temp, is_less );
1856  swap( temp );
1857 }
1858 
1859 template <typename T>
1860 template <typename ComparatorBool, typename>
1861 inline void
1863  const UT_Array<T> &other,
1865  ComparatorBool is_less) const
1866 {
1867  // Can't store to either input.
1868  UT_ASSERT(&result != this && &result != &other);
1869  UT_ASSERT(result.size() == 0);
1870 
1871  std::set_intersection(
1872  begin(), end(), other.begin(), other.end(), AppendIterator(result),
1873  is_less);
1874 }
1875 
1876 template <typename T>
1877 template <typename ComparatorBool, typename>
1878 inline void
1880  const UT_Array<T> &other,
1881  ComparatorBool is_less)
1882 {
1883  UT_Array<T> temp;
1884  sortedSetDifference(other, temp, is_less);
1885  swap( temp );
1886 }
1887 
1888 template <typename T>
1889 template <typename ComparatorBool, typename>
1890 inline void
1892  const UT_Array<T> &other,
1894  ComparatorBool is_less) const
1895 {
1896  // Can't store to either input.
1897  UT_ASSERT(&result != this && &result != &other);
1898  UT_ASSERT(result.size() == 0);
1899 
1900  std::set_difference(
1901  begin(), end(), other.begin(), other.end(), AppendIterator(result),
1902  is_less);
1903 }
1904 
1905 template <typename T>
1906 template <typename CompareEqual>
1907 inline exint
1908 UT_Array<T>::sortedRemoveDuplicatesIf(CompareEqual compare_equal)
1909 {
1910  exint n = size();
1911 
1912  // Trivial, no duplicates!
1913  if (n < 2)
1914  return 0;
1915 
1916  exint dst = 1;
1917  for (exint i = 1; i < n; i++)
1918  {
1919  if (!compare_equal((*this)(i), (*this)(i-1)))
1920  {
1921  if (i != dst)
1922  (*this)(dst) = (*this)(i);
1923  dst++;
1924  }
1925  }
1926 
1927  // Store the number of remaining elements.
1928  setSize(dst);
1929  return n - dst; // Return the number of elements removed
1930 }
1931 
1932 namespace {
1933  template<typename T>
1934  struct srdCompareEqual
1935  {
1936  bool operator()(const T& x, const T& y) const { return (x == y); }
1937  };
1938 }
1939 
1940 template <typename T>
1941 inline exint
1943 {
1944  srdCompareEqual<T> cmp;
1945  return sortedRemoveDuplicatesIf(cmp);
1946 }
1947 
1948 template <typename T>
1949 template <typename BinaryOp>
1950 inline T
1951 UT_Array<T>::accumulate(const T &init_value, BinaryOp add) const
1952 {
1953  T sum(init_value);
1954  for (exint i = 0; i < mySize; i++)
1955  sum = add(sum, myData[i]);
1956  return sum;
1957 }
1958 
1959 #endif // __UT_ARRAYIMPL_H_INCLUDED__
void swap(ArAssetInfo &lhs, ArAssetInfo &rhs)
Definition: assetInfo.h:57
int(* ut_ptr_compare_func_t)(const void *, const void *)
Definition: UT_ArrayHelp.h:23
void merge(const UT_Array< T > &other, int direction, bool allow_dups, ComparatorBool is_less={})
bool isHeapBuffer() const
Returns true if the data used by the array was allocated on the heap.
Definition: UT_Array.h:1132
void UTnth_element(IT start, IT nth, IT end, COMPARE isAbeforeB)
Definition: UT_Permute.h:77
bool operator!=(const UT_Array< T > &a) const
#define SYS_PRAGMA_PUSH_WARN()
Definition: SYS_Pragma.h:34
void UTswap(T &a, T &b)
Definition: UT_Swap.h:35
exint insertImpl(S &&s, exint index)
Similar to appendImpl() but for insertion.
void
Definition: png.h:1083
GLboolean * data
Definition: glcorearb.h:131
exint findAndRemove(const S &s)
UT_Array< T > & operator=(const UT_Array< T > &a)
IMF_EXPORT IMATH_NAMESPACE::V3f direction(const IMATH_NAMESPACE::Box2i &dataWindow, const IMATH_NAMESPACE::V2f &pixelPosition)
GLuint start
Definition: glcorearb.h:475
GLsizei const GLfloat * value
Definition: glcorearb.h:824
void extractRange(exint begin_i, exint end_i, UT_Array< T > &dest)
CompareResults OIIO_API compare(const ImageBuf &A, const ImageBuf &B, float failthresh, float warnthresh, float failrelative, float warnrelative, ROI roi={}, int nthreads=0)
void zero()
Zeros the array if a POD type, else trivial constructs if a class type.
exint uniqueSortedFind(const T &item, ComparatorBool is_less={}) const
Definition: UT_ArrayImpl.h:911
int64 exint
Definition: SYS_Types.h:125
void move(exint src_idx, exint dst_idx, exint how_many)
GLboolean GLboolean GLboolean GLboolean a
Definition: glcorearb.h:1222
void cycle(exint how_many)
Cyclically shifts the entire array by how_many.
GLdouble s
Definition: glad.h:3009
#define SYSabs(a)
Definition: SYS_Math.h:1954
T * array()
Definition: UT_Array.h:863
static constexpr struct UT_ArrayCT::GeneralizedMove GENERALIZED_MOVE
#define UT_API
Definition: UT_API.h:14
void setCapacity(exint new_capacity)
PUGI__FN void sort(I begin, I end, const Pred &pred)
Definition: pugixml.cpp:7550
GLint y
Definition: glcorearb.h:103
exint concat(const UT_Array< T > &a)
Takes another T array and concatenate it onto my end.
Definition: UT_ArrayImpl.h:994
**But if you need a result
Definition: thread.h:622
FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr &out) -> bool
Definition: core.h:2138
constexpr UT_LabeledCapacityRep LABELED_CAPACITY_MASK_VALUE
exint uniqueSortedInsert(const T &t, Comparator compare)
Definition: UT_Array.h:233
exint find(const S &s, exint start=0) const
float fpreal32
Definition: SYS_Types.h:200
exint size() const
Definition: UT_Array.h:667
void sortedUnion(const UT_Array< T > &other, ComparatorBool is_less={})
constexpr UT_LabeledCapacityRep LABELED_CAPACITY_FLAG_EXTERNAL
IMATH_HOSTDEVICE constexpr int cmp(T a, T b) IMATH_NOEXCEPT
Definition: ImathFun.h:84
GLdouble n
Definition: glcorearb.h:2008
exint findIf(IsEqual is_equal, exint start=0) const
exint apply(int(*apply_func)(T &t, void *d), void *d)
exint emplace_back(S &&...s)
Definition: UT_ArrayImpl.h:781
constexpr UT_LabeledCapacity ownedLabeledCapacity(const exint capacity) noexcept
constexpr UT_LabeledCapacity externalLabeledCapacity(const exint capacity) noexcept
exint uniqueSortedInsertImpl(S &&s, Comparator compare)
Definition: UT_ArrayImpl.h:865
void sort(ComparatorBool is_less={})
Sort using std::sort with bool comparator. Defaults to operator<().
Definition: UT_Array.h:467
#define UT_ASSERT_P(ZZ)
Definition: UT_Assert.h:164
T accumulate(const T &init_value, BinaryOp add) const
GLuint GLuint end
Definition: glcorearb.h:475
#define SYS_FORCE_INLINE
Definition: SYS_Inline.h:45
exint capacity() const
Definition: UT_ArrayImpl.h:143
UT_Vector3T< T > SYSclamp(const UT_Vector3T< T > &v, const UT_Vector3T< T > &min, const UT_Vector3T< T > &max)
Definition: UT_Vector3.h:1059
exint sortedInsert(const T &t, Comparator compare)
Definition: UT_ArrayImpl.h:831
#define SYS_PRAGMA_DISABLE_FREE_NONHEAP_OBJECT()
Definition: SYS_Pragma.h:123
exint appendImpl(S &&s)
Definition: UT_ArrayImpl.h:758
void appendMultiple(const T &t, exint count)
Definition: UT_ArrayImpl.h:807
iterator begin()
Definition: UT_Array.h:1039
void setCapacityIfNeeded(exint min_capacity)
Definition: UT_Array.h:624
#define SYS_PRAGMA_POP_WARN()
Definition: SYS_Pragma.h:35
exint removeIf(IsEqual is_equal)
exint sortedRemoveDuplicates()
void sortedSetDifference(const UT_Array< T > &other, ComparatorBool is_less={})
GLboolean GLboolean GLboolean b
Definition: glcorearb.h:1222
constexpr exint capacityValue(const UT_LabeledCapacity &a) noexcept
constexpr bool UTareOverlapping(const T *const a, const T *const b, exint n) noexcept
Definition: UT_ArrayImpl.h:332
GLint GLenum GLint x
Definition: glcorearb.h:409
exint append()
Definition: UT_Array.h:142
GLdouble t
Definition: glad.h:2397
exint sortedRemoveDuplicatesIf(CompareEqual compare_equal)
T selectNthLargest(exint idx, ComparatorBool is_less={})
#define SYS_PRAGMA_DISABLE_ALLOC_SIZE_LARGER_THAN()
Definition: SYS_Pragma.h:187
GLsizeiptr size
Definition: glcorearb.h:664
GLfloat GLfloat GLfloat GLfloat h
Definition: glcorearb.h:2002
GLenum GLenum dst
Definition: glcorearb.h:1793
PcpNodeRef_ChildrenIterator begin(const PcpNodeRef::child_const_range &r)
Support for range-based for loops for PcpNodeRef children ranges.
Definition: node.h:587
UT_Compare::Less< T > UTcompareLess(UT_Compare::Ternary< T > compare)
Definition: UT_Compare.h:64
LeafData & operator=(const LeafData &)=delete
VULKAN_HPP_CONSTEXPR_14 VULKAN_HPP_INLINE T exchange(T &obj, U &&newValue)
Definition: vulkan_raii.hpp:25
GLuint index
Definition: glcorearb.h:786
UT_Compare::Equal< T > UTcompareEqual(UT_Compare::Ternary< T > compare)
Definition: UT_Compare.h:78
bool isEqual(const UT_Array< T > &a, ComparatorBool is_equal) const
#define SYS_PRAGMA_DISABLE_MISMATCHED_NEW_DELETE()
Definition: SYS_Pragma.h:194
GLuint GLfloat * val
Definition: glcorearb.h:1608
void constant(const T &v)
Quickly set the array to a single value.
#define UT_ASSERT(ZZ)
Definition: UT_Assert.h:165
void sortedIntersection(const UT_Array< T > &other, ComparatorBool is_less={})
ImageBuf OIIO_API add(Image_or_Const A, Image_or_Const B, ROI roi={}, int nthreads=0)
UT_Array(const UT_Array< T > &a)
Definition: UT_ArrayImpl.h:531
#define SYSmin(a, b)
Definition: SYS_Math.h:1953
T heapPop(Comparator compare)
Definition: UT_ArrayImpl.h:957
exint heapPush(const T &t, Comparator compare)
Definition: UT_ArrayImpl.h:940
std::string OIIO_UTIL_API concat(string_view s, string_view t)
void reverse()
Reverses the array by swapping elements in mirrored locations.
#define SYS_PRAGMA_DISABLE_STRINGOP_OVERREAD()
Definition: SYS_Pragma.h:208
exint multipleInsert(exint index, exint count)
Insert an element "count" times at the given index. Return the index.
void removeRange(exint begin_i, exint end_i)
void swap(UT_Array< T > &other)
Definition: UT_ArrayImpl.h:688
exint insert(exint index)
Definition: UT_ArrayImpl.h:733
bool hasSortedSubset(const UT_Array< T > &other, ComparatorBool is_less={}) const
GLint GLsizei count
Definition: glcorearb.h:405
Definition: format.h:1821
iterator end()
End iterator.
Definition: UT_Array.h:1044
constexpr auto SYS_UseTrivialRelocation_v
Definition: UT_ArrayImpl.h:99
GLenum src
Definition: glcorearb.h:1793
bool operator==(const UT_Array< T > &a) const
exint sortedFind(const T &t, Comparator compare) const