yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

Ronancanonical type equality constraint (#8445)ee5adb870

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