yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
ee5adb870
master
1#ifndef SLANG_CORE_LIST_H 2#define SLANG_CORE_LIST_H 3 4#include "slang-allocator.h" 5#include "slang-array-view.h" 6#include "slang-math.h" 7#include "slang.h" 8 9#include <algorithm> 10#include <new> 11#include <type_traits> 12 13 14namespace Slang 15{ 16// List is container of values of a type held consecutively in memory (much like std::vector) 17// 18// Note that in this implementation, the underlying memory is backed via an allocation of 19// T[capacity] This means that all values have to be in a valid state *even if they are not used* 20// (ie indices >= m_count must be valid) 21// 22// Also note this implementation does not necessarily 'initialize' an element which is no longer 23// used, and this may lead to surprising behavior. Say the list contains a single smart pointer, and 24// the last element is removed (say with removeLast). The smart pointer will *not* be released. The 25// smart pointer will be released if the that index is used (via say an add) or the List goes out of 26// scope. 27template < typename T ,typename TAllocator = StandardAllocator > 28class List 29{ 30private : 31static const Index kInitialCount = 16 ; 32 33public : 34typedef List ThisType ; 35 36List () 37 :m_buffer (nullptr ),m_count (0 ),m_capacity (0 ) 38 { 39 } 40template < typename ...Args > 41List (const T & val ,Args ...args ) 42 :m_buffer (nullptr ),m_count (0 ),m_capacity (0 ) 43 { 44_init (val ,args ...); 45 } 46List (const List < T >& list ) 47 :m_buffer (nullptr ),m_count (0 ),m_capacity (0 ) 48 { 49this -> operator = (list ); 50 } 51List (List < T >&& list ) 52 :m_buffer (nullptr ),m_count (0 ),m_capacity (0 ) 53 { 54this -> operator = (static_cast < List < T >&& >(list)); 55} 56List (ArrayView < T > view) 57: List () 58{ 59addRange (view); 60} 61static List < T > makeRepeated ( const T & val, Index count) 62{ 63List < T > rs; 64rs. setCount (count); 65for ( Index i = 0 ; i < count; i ++ ) 66rs[i] = val; 67return rs; 68} 69~ List () { _deallocateBuffer (); } 70List < T >& operator = ( const List < T >& list) 71{ 72clearAndDeallocate (); 73addRange (list); 74return * this; 75} 76 77List < T >& operator = (List < T >&& list) 78{ 79// Could just do a swap here, and memory would be freed on rhs dtor 80 81_deallocateBuffer (); 82m_count = list. m_count ; 83m_capacity = list. m_capacity ; 84m_buffer = list. m_buffer ; 85 86list. m_buffer = nullptr ; 87list. m_count = 0 ; 88list. m_capacity = 0 ; 89return * this; 90} 91 92const T * begin () const { return m_buffer; } 93const T * end () const { return m_buffer + m_count; } 94 95T * begin () { return m_buffer; } 96T * end () { return m_buffer + m_count; } 97 98const T & getFirst () const 99{ 100SLANG_ASSERT ( m_count > 0 ); 101return m_buffer[ 0 ]; 102} 103 104T & getFirst () 105{ 106SLANG_ASSERT (m_count > 0 ); 107return m_buffer[ 0 ]; 108} 109 110const T & getLast () const 111{ 112SLANG_ASSERT ( m_count > 0 ); 113return m_buffer[m_count - 1 ]; 114} 115 116T & getLast () 117{ 118SLANG_ASSERT (m_count > 0 ); 119return m_buffer[m_count - 1 ]; 120} 121 122void removeLast () 123{ 124SLANG_ASSERT (m_count > 0 ); 125m_count -- ; 126} 127 128inline void swapWith ( List < T , TAllocator >& other) 129{ 130T * buffer = m_buffer; 131m_buffer = other. m_buffer ; 132other. m_buffer = buffer; 133 134auto bufferSize = m_capacity; 135m_capacity = other. m_capacity ; 136other. m_capacity = bufferSize; 137 138auto count = m_count; 139m_count = other. m_count ; 140other. m_count = count; 141} 142 143T * detachBuffer () 144{ 145T * rs = m_buffer; 146m_buffer = nullptr ; 147m_count = 0 ; 148m_capacity = 0 ; 149return rs; 150} 151void attachBuffer ( T * buffer, Index count, Index capacity) 152{ 153// Can only attach a buffer if there isn't a buffer already associated 154SLANG_ASSERT (m_buffer == nullptr ); 155SLANG_ASSERT (count <= capacity); 156m_buffer = buffer; 157m_count = count; 158m_capacity = capacity; 159} 160 161inline ArrayView < T > getArrayView () const { return ArrayView < T > (m_buffer, m_count); } 162 163inline ArrayView < T > getArrayView ( Index start, Index count) const 164{ 165SLANG_ASSERT (start >= 0 && count >= 0 && start + count <= m_count); 166return ArrayView < T > (m_buffer + start, count); 167} 168 169void _maybeReserveForAdd () 170{ 171if (m_capacity <= m_count) 172{ 173Index newBufferSize = kInitialCount; 174if (m_capacity) 175newBufferSize = (m_capacity << 1 ); 176 177reserve (newBufferSize); 178} 179} 180 181void add ( T && obj) 182{ 183_maybeReserveForAdd (); 184m_buffer[m_count ++ ] = static_cast < T &&> (obj); 185} 186 187void add ( const T & obj) 188{ 189_maybeReserveForAdd (); 190m_buffer[m_count ++ ] = obj; 191} 192 193Index getCount () const { return m_count; } 194Index getCapacity () const { return m_capacity; } 195template < typename Predicate > 196Index countIf (Predicate predicate) const 197{ 198Index count = 0 ; 199for ( Index i = 0 ; i < getCount (); ++ i) 200{ 201if ( predicate (( * this)[i])) 202count ++ ; 203} 204return count; 205} 206 207 208const T * getBuffer () const { return m_buffer; } 209T * getBuffer () { return m_buffer; } 210 211bool operator == ( const ThisType & rhs) const 212{ 213if ( & rhs == this) 214{ 215return true; 216} 217const Index count = getCount (); 218if (count != rhs. getCount ()) 219{ 220return false; 221} 222for ( Index i = 0 ; i < count; ++ i) 223{ 224if (( * this)[i] != rhs[i]) 225{ 226return false; 227} 228} 229return true; 230} 231SLANG_FORCE_INLINE bool operator != ( const ThisType & rhs) const { return !( * this == rhs); } 232 233void insert ( Index idx, const T & val) { insertRange (idx, & val, 1 ); } 234 235void insertRange ( Index idx, const T * vals, Index n) 236{ 237if (m_capacity < m_count + n) 238{ 239Index newBufferCount = kInitialCount; 240while (newBufferCount < m_count + n) 241newBufferCount = newBufferCount << 1 ; 242 243T * newBuffer = _allocate (newBufferCount); 244if (m_capacity) 245{ 246/*if (std::has_trivial_copy_assign<T>::value && 247std::has_trivial_destructor<T>::value) 248{ 249memcpy(newBuffer, buffer, sizeof(T) * id); 250memcpy(newBuffer + id + n, buffer + id, sizeof(T) * (_count - id)); 251} 252else*/ 253{ 254for ( Index i = 0 ; i < idx; i ++ ) 255newBuffer[i] = m_buffer[i]; 256for ( Index i = idx; i < m_count; i ++ ) 257newBuffer[i + n] = T (static_cast < T &&> (m_buffer[i])); 258} 259_deallocateBuffer (); 260} 261m_buffer = newBuffer; 262m_capacity = newBufferCount; 263} 264else 265{ 266/*if (std::has_trivial_copy_assign<T>::value && std::has_trivial_destructor<T>::value) 267memmove(buffer + id + n, buffer + id, sizeof(T) * (_count - id)); 268else*/ 269{ 270for ( Index i = m_count; i > idx; i -- ) 271m_buffer[i + n - 1 ] = static_cast < T &&> (m_buffer[i - 1 ]); 272} 273} 274/*if (std::has_trivial_copy_assign<T>::value && std::has_trivial_destructor<T>::value) 275memcpy(buffer + id, vals, sizeof(T) * n); 276else*/ 277for ( Index i = 0 ; i < n; i ++ ) 278m_buffer[idx + i] = vals[i]; 279 280m_count += n; 281} 282 283void insertRange ( Index id, const List < T >& list) 284{ 285insertRange (id, list. m_buffer , list. m_count ); 286} 287 288void addRange ( ArrayView < T > list) { insertRange (m_count, list. getBuffer (), list. getCount ()); } 289 290void addRange ( const T * vals, Index n) { insertRange (m_count, vals, n); } 291 292void addRange ( const List < T >& list) { insertRange (m_count, list. m_buffer , list. m_count ); } 293 294void removeRange ( Index idx, Index count) 295{ 296SLANG_ASSERT (idx >= 0 && idx <= m_count); 297 298const Index actualDeleteCount = ((idx + count) >= m_count) ? (m_count - idx) : count; 299for ( Index i = idx + actualDeleteCount; i < m_count; i ++ ) 300m_buffer[i - actualDeleteCount] = static_cast < T &&> (m_buffer[i]); 301m_count -= actualDeleteCount; 302} 303 304void removeAt ( Index id) { removeRange (id, 1 ); } 305 306void remove ( const T & val) 307{ 308Index idx = indexOf (val); 309if (idx != -1 ) 310removeAt (idx); 311} 312 313void reverse () 314{ 315for ( Index i = 0 ; i < (m_count >> 1 ); i ++ ) 316{ 317swapElements (m_buffer, i, m_count - i - 1 ); 318} 319} 320 321void fastRemove ( const T & val) 322{ 323Index idx = indexOf (val); 324if (idx >= 0 ) 325{ 326fastRemoveAt (idx); 327} 328} 329 330void fastRemoveAt ( Index idx) 331{ 332SLANG_ASSERT (idx >= 0 && idx < m_count); 333// We do not test for idx == m_count - 1 (ie the move is to current index). With the 334// assumption that any reasonable move implementation tests and ignores this case 335if (idx != m_count - 1 ) 336{ 337m_buffer[idx] = _Move (m_buffer[m_count - 1 ]); 338} 339m_count -- ; 340} 341 342void clear () { m_count = 0 ; } 343 344void clearAndDeallocate () 345{ 346_deallocateBuffer (); 347m_count = m_capacity = 0 ; 348} 349 350void reserve ( Index size) 351{ 352// The cast for this comparison is needed, otherwise some compilers erroneously detect 353// the possiblity of a zero sized allocation (possible if m_capacity is assumed to be 354// negative). 355if ( UIndex (size) > UIndex (m_capacity)) 356{ 357T * newBuffer = _allocate (size); 358if (m_capacity) 359{ 360/*if (std::has_trivial_copy_assign<T>::value && 361std::has_trivial_destructor<T>::value) memcpy(newBuffer, buffer, _count * 362sizeof(T)); else*/ 363{ 364for ( Index i = 0 ; i < m_count; i ++ ) 365newBuffer[i] = static_cast < T &&> (m_buffer[i]); 366 367// Default-initialize the remaining elements 368for ( Index i = m_count; i < size; i ++ ) 369{ 370new (newBuffer + i) T (); 371} 372} 373_deallocateBuffer (); 374} 375m_buffer = newBuffer; 376m_capacity = size; 377} 378} 379 380void growToCount ( Index count) 381{ 382Index newBufferCount = Index ( 1 ) << Math:: Log2Ceil (( unsigned int )count); 383if (m_capacity < newBufferCount) 384{ 385reserve (newBufferCount); 386} 387m_count = count; 388} 389 390void setCount ( Index count) 391{ 392reserve (count); 393m_count = count; 394} 395 396void unsafeShrinkToCount ( Index count) { m_count = count; } 397 398void compress () 399{ 400if (m_capacity > m_count && m_count > 0 ) 401{ 402T * newBuffer = _allocate (m_count); 403for ( Index i = 0 ; i < m_count; i ++ ) 404newBuffer[i] = static_cast < T &&> (m_buffer[i]); 405 406_deallocateBuffer (); 407m_buffer = newBuffer; 408m_capacity = m_count; 409} 410} 411 412SLANG_FORCE_INLINE const T & operator[]( Index idx) const 413{ 414SLANG_ASSERT (idx >= 0 && idx < m_count); 415return m_buffer[idx]; 416} 417 418SLANG_FORCE_INLINE T & operator[]( Index idx) 419{ 420SLANG_ASSERT (idx >= 0 && idx < m_count); 421return m_buffer[idx]; 422} 423 424template < typename Func > 425Index findFirstIndex( const Func & predicate) const 426{ 427for ( Index i = 0 ; i < m_count; i ++ ) 428{ 429if ( predicate (m_buffer[i])) 430return i; 431} 432return -1 ; 433} 434 435template < typename T2 > 436Index indexOf( const T2 & val) const 437{ 438for ( Index i = 0 ; i < m_count; i ++ ) 439{ 440if (m_buffer[i] == val) 441return i; 442} 443return -1 ; 444} 445 446template < typename Func > 447Index findLastIndex( const Func & predicate) const 448{ 449for ( Index i = m_count - 1 ; i >= 0 ; i -- ) 450{ 451if ( predicate (m_buffer[i])) 452return i; 453} 454return -1 ; 455} 456 457template < typename T2 > 458Index lastIndexOf( const T2 & val) const 459{ 460for ( Index i = m_count - 1 ; i >= 0 ; i -- ) 461{ 462if (m_buffer[i] == val) 463return i; 464} 465return -1 ; 466} 467 468bool contains ( const T & val) const { return indexOf (val) != Index ( -1 ); } 469 470void sort () 471{ 472sort( []( const T & t1 , const T & t2 ) { return t1 < t2 ; }); 473} 474 475template < typename Comparer > 476void sort ( Comparer compare ) 477{ 478// insertionSort(buffer, 0, _count - 1); 479// quickSort(buffer, 0, _count - 1, compare); 480std :: sort ( m_buffer , m_buffer + m_count , compare ); 481} 482 483void stableSort () 484{ 485stableSort ([]( const T & t1 , const T & t2 ) { return t1 < t2 ; }); 486} 487 488template < typename Comparer > 489void stableSort ( Comparer compare ) 490{ 491std :: stable_sort ( m_buffer , m_buffer + m_count , compare ); 492} 493 494template < typename IterateFunc > 495void forEach ( IterateFunc f ) const 496{ 497for (Index i = 0 ; i < m_count ; i ++ ) 498f ( m_buffer [ i ]); 499} 500 501template < typename Comparer > 502void quickSort ( T * vals , Index startIndex , Index endIndex , Comparer comparer ) 503{ 504static const Index kMinQSortSize = 32 ; 505 506if ( startIndex < endIndex ) 507{ 508if ( endIndex - startIndex < kMinQSortSize ) 509insertionSort ( vals , startIndex , endIndex , comparer ); 510else 511{ 512Index pivotIndex = ( startIndex + endIndex ) >> 1 ; 513Index pivotNewIndex = partition ( vals , startIndex , endIndex , pivotIndex , comparer ); 514quickSort ( vals , startIndex , pivotNewIndex - 1 , comparer ); 515quickSort ( vals , pivotNewIndex + 1 , endIndex , comparer ); 516} 517} 518} 519template < typename Comparer > 520Index partition ( T * vals , Index left , Index right , Index pivotIndex , Comparer comparer ) 521{ 522T pivotValue = vals [ pivotIndex ]; 523swapElements ( vals , right , pivotIndex ); 524Index storeIndex = left ; 525for (Index i = left ; i < right ; i ++ ) 526{ 527if ( comparer ( vals [ i ], pivotValue )) 528{ 529swapElements ( vals , i , storeIndex ); 530storeIndex ++ ; 531} 532} 533swapElements ( vals , storeIndex , right ); 534return storeIndex ; 535} 536template < typename Comparer > 537void insertionSort ( T * vals , Index startIndex , Index endIndex , Comparer comparer ) 538{ 539for (Index i = startIndex + 1 ; i <= endIndex ; i ++ ) 540{ 541T insertValue = static_cast < T &&> ( vals [ i ]); 542Index insertIndex = i - 1 ; 543while ( insertIndex >= startIndex && comparer ( insertValue , vals [ insertIndex ])) 544{ 545vals [ insertIndex + 1 ] = static_cast < T &&> ( vals [ insertIndex ]); 546insertIndex -- ; 547} 548vals [ insertIndex + 1 ] = static_cast < T &&> ( insertValue ); 549} 550} 551 552inline static void swapElements (T * vals , Index index1 , Index index2 ) 553{ 554if ( index1 != index2 ) 555{ 556T tmp = static_cast < T &&> ( vals [ index1 ]); 557vals [ index1 ] = static_cast < T &&> ( vals [ index2 ]); 558vals [ index2 ] = static_cast < T &&> ( tmp ); 559} 560} 561 562inline void swapElements (Index index1 , Index index2 ) { swapElements ( m_buffer , index1 , index2 ); } 563 564template < typename T2 , typename Comparer > 565Index binarySearch ( const T2 & obj , Comparer comparer ) const 566{ 567Index imin = 0 , imax = m_count - 1 ; 568while ( imax >= imin ) 569{ 570Index imid = imin + (( imax - imin ) >> 1 ); 571int compareResult = comparer ( m_buffer [ imid ], obj ); 572if ( compareResult == 0 ) 573return imid ; 574else if ( compareResult < 0 ) 575imin = imid + 1 ; 576else 577imax = imid - 1 ; 578} 579// TODO: The return value on a failed search should be 580// the bitwise negation of the index where `obj` should 581// be inserted to be in the proper sorted location. 582return -1 ; 583} 584 585template < typename T2 > 586Index binarySearch ( const T2 & obj ) const 587{ 588return binarySearch ( 589obj, 590[]( T & curObj, const T2 & thatObj) -> int 591{ 592if (curObj < thatObj) 593return -1 ; 594else if (curObj == thatObj) 595return 0 ; 596else 597return 1 ; 598}); 599} 600 601private : 602T * m_buffer; ///< A new T[N] allocated buffer. NOTE! All elements up to capacity are in some 603///< valid form for T. 604Index m_capacity; ///< The total capacity of elements 605Index m_count; ///< The amount of elements 606 607void _deallocateBuffer () 608{ 609if (m_buffer) 610{ 611AllocateMethod < T , TAllocator > :: deallocateArray (m_buffer, m_capacity); 612m_buffer = nullptr ; 613} 614} 615static inline T * _allocate ( Index count) 616{ 617return AllocateMethod < T , TAllocator > :: allocateArray (count); 618} 619static void _free ( T * buffer, Index count) 620{ 621return AllocateMethod < T , TAllocator > :: deallocateArray (buffer, count); 622} 623 624template < typename... Args > 625void _init( const T & val, Args... args) 626{ 627add (val); 628_init( args ...); 629} 630 631void _init () {} 632}; 633 634template < typename T > 635T calcMin( const List < T >& list) 636{ 637T minVal = list. getFirst (); 638for ( Index i = 1 ; i < list. getCount (); i ++ ) 639if (list[i] < minVal) 640minVal = list[i]; 641return minVal; 642} 643 644template < typename T > 645T calcMax( const List < T >& list) 646{ 647T maxVal = list. getFirst (); 648for ( Index i = 1 ; i < list. getCount (); i ++ ) 649if (list[i] > maxVal) 650maxVal = list[i]; 651return maxVal; 652} 653} // namespace Slang 654 655#endif