yum-mirror/slang

Making it easier to work with shaders

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

Harsh Aggarwal (NVIDIA)Fix crash when loading modules with syntax errors (#6993) (#7288)624770a1e

master
22.8 KiB774 linesraw
1#ifndef SLANG_CORE_DICTIONARY_H
2#define SLANG_CORE_DICTIONARY_H
3
4#include "slang-common.h"
5#include "slang-exception.h"
6#include "slang-hash.h"
7#include "slang-linked-list.h"
8#include "slang-list.h"
9#include "slang-math.h"
10#include "slang-uint-set.h"
11
12#include <ankerl/unordered_dense.h>
13#include <initializer_list>
14
15namespace Slang
16{
17template<typename TKey, typename TValue>
18class KeyValuePair
19{
20public:
21    TKey key;
22    TValue value;
23    KeyValuePair() {}
24    KeyValuePair(const TKey& inKey, const TValue& inValue)
25    {
26        key = inKey;
27        value = inValue;
28    }
29    KeyValuePair(TKey&& inKey, TValue&& inValue)
30    {
31        key = _Move(inKey);
32        value = _Move(inValue);
33    }
34    KeyValuePair(TKey&& inKey, const TValue& inValue)
35    {
36        key = _Move(inKey);
37        value = inValue;
38    }
39    KeyValuePair(const KeyValuePair<TKey, TValue>& that)
40    {
41        key = that.key;
42        value = that.value;
43    }
44    KeyValuePair(KeyValuePair<TKey, TValue>&& that) { operator=(_Move(that)); }
45    KeyValuePair& operator=(KeyValuePair<TKey, TValue>&& that)
46    {
47        key = _Move(that.key);
48        value = _Move(that.value);
49        return *this;
50    }
51    KeyValuePair& operator=(const KeyValuePair<TKey, TValue>& that)
52    {
53        key = that.key;
54        value = that.value;
55        return *this;
56    }
57    HashCode getHashCode() const
58    {
59        return combineHash(Slang::getHashCode(key), Slang::getHashCode(value));
60    }
61    bool operator==(const KeyValuePair<TKey, TValue>& that) const
62    {
63        return (key == that.key) && (value == that.value);
64    }
65};
66
67template<typename TKey, typename TValue>
68inline KeyValuePair<TKey, TValue> KVPair(const TKey& k, const TValue& v)
69{
70    return KeyValuePair<TKey, TValue>(k, v);
71}
72
73namespace KeyValueDetail
74{
75
76template<typename KEY, typename VALUE>
77SLANG_FORCE_INLINE const KEY* getKey(const std::pair<KEY, VALUE>* in)
78{
79    return &in->first;
80}
81template<typename KEY, typename VALUE>
82SLANG_FORCE_INLINE const KEY* getKey(const KeyValuePair<KEY, VALUE>* in)
83{
84    return &in->key;
85}
86
87template<typename KEY, typename VALUE>
88SLANG_FORCE_INLINE const VALUE* getValue(const std::pair<KEY, VALUE>* in)
89{
90    return &in->second;
91}
92template<typename KEY, typename VALUE>
93SLANG_FORCE_INLINE const VALUE* getValue(const KeyValuePair<KEY, VALUE>* in)
94{
95    return &in->value;
96}
97
98} // namespace KeyValueDetail
99
100const float kMaxLoadFactor = 0.7f;
101
102template<
103    typename TKey,
104    typename TValue,
105    typename Hash = Slang::Hash<TKey>,
106    typename KeyEqual = std::equal_to<TKey>>
107class Dictionary
108{
109    using InnerMap = ankerl::unordered_dense::map<TKey, TValue, Hash, KeyEqual>;
110    using ThisType = Dictionary<TKey, TValue, Hash, KeyEqual>;
111    InnerMap map;
112
113public:
114    Dictionary() = default;
115    Dictionary(const Dictionary&) = default;
116    Dictionary(Dictionary&&) = default;
117    ThisType& operator=(const ThisType&) = default;
118    ThisType& operator=(ThisType&&) = default;
119    Dictionary(std::initializer_list<typename InnerMap::value_type> inits)
120        : map(std::move(inits))
121    {
122    }
123
124    //
125    // Types
126    //
127    using Iterator = typename InnerMap::iterator;
128    using ConstIterator = typename InnerMap::const_iterator;
129    using KeyType = TKey;
130    using ValueType = TValue;
131
132    //
133    // Iterators
134    //
135
136    auto begin() { return map.begin(); }
137    auto begin() const { return map.begin(); }
138    auto end() { return map.end(); }
139    auto end() const { return map.end(); }
140
141    //
142    // Modifiers
143    //
144
145    // Removes all values from the map
146    void clear() { map.clear(); }
147
148    // Erases the value at the specified key if it exists
149    void remove(const TKey& key) { map.erase(key); }
150
151    // Removes all values satifying the predicate:
152    // bool predicate(pair<Key, Value>)
153    template<typename Predicate>
154    void removeIf(Predicate&& predicate)
155    {
156        auto it = begin();
157        while (it != end())
158        {
159            if (predicate(*it))
160            {
161                it = map.erase(it);
162            }
163            else
164            {
165                ++it;
166            }
167        }
168    }
169
170    // Reserves enough space for the specified number of values
171    void reserve(Index size) { map.reserve(std::size_t(size)); };
172
173    // Swap with another map
174    void swapWith(ThisType& rhs) { std::swap(*this, rhs); }
175
176    //
177    // Query capacity
178    //
179
180    std::size_t getCount() const { return map.size(); }
181
182    //
183    // Lookup
184    //
185
186    // Returns true if the map contains an equivalent key
187    template<typename K>
188    bool containsKey(const K& k) const
189    {
190        return map.contains(k);
191    }
192
193    // Returns a valid pointer to the requested element, or nullptr if it
194    // doesn't exist
195    template<typename K>
196    const TValue* tryGetValue(const K& key) const
197    {
198        auto i = map.find(key);
199        return i == map.end() ? nullptr : &(i->second);
200    }
201    // Returns a valid pointer to the requested element, or nullptr if it
202    // doesn't exist
203    template<typename K>
204    TValue* tryGetValue(const K& key)
205    {
206        auto i = map.find(key);
207        return i == map.end() ? nullptr : std::addressof(i->second);
208    }
209
210    // Returns true and copies the element into 'value' if present.
211    // Otherwise returns false and value unmodified.
212    template<typename K>
213    bool tryGetValue(const K& key, TValue& value) const
214    {
215        auto i = map.find(key);
216        if (i == map.end())
217            return false;
218        value = i->second;
219        return true;
220    }
221
222    // Returns a const reference to the value at the given key. Asserts if
223    // the value doesn't exist
224    const TValue& getValue(const TKey& key) const
225    {
226        if (const auto x = tryGetValue(key))
227            return *x;
228        SLANG_ASSERT_FAILURE("The key does not exist in dictionary.");
229    }
230
231    // Returns a reference to the value at the given key. Asserts if the
232    // value doesn't exist
233    TValue& getValue(const TKey& key)
234    {
235        if (const auto x = tryGetValue(key))
236            return *x;
237        SLANG_ASSERT_FAILURE("The key does not exist in dictionary.");
238    }
239
240    //
241    // Combined Lookup and Insertion
242    //
243
244    // Tries to insert the given element, if a value was already present at
245    // the given key then returns a pointer to that element instead.
246    // Returns nullptr if insertion was successful.
247    TValue* tryGetValueOrAdd(const typename InnerMap::value_type& kvPair)
248    {
249        const auto& [iterator, inserted] = map.insert(kvPair);
250        return inserted ? nullptr : std::addressof(iterator->second);
251    }
252    // Tries to insert the given element, if a value was already present at
253    // the given key then returns a pointer to that element instead.
254    // Returns nullptr if insertion was successful.
255    TValue* tryGetValueOrAdd(typename InnerMap::value_type&& kvPair)
256    {
257        const auto& [iterator, inserted] = map.insert(std::move(kvPair));
258        return inserted ? nullptr : std::addressof(iterator->second);
259    }
260    // Tries to insert the given element, if a value was already present at
261    // the given key then returns a pointer to that element instead.
262    // Returns nullptr if insertion was successful.
263    TValue* tryGetValueOrAdd(const TKey& key, const TValue& value)
264    {
265        return tryGetValueOrAdd({key, value});
266    }
267
268    // Inserts the given value if it doesn't exist already
269    // Return a reference to the (possibly new) value in the map
270    TValue& getOrAddValue(const TKey& key, const TValue& defaultValue)
271    {
272        auto [iterator, inserted] = map.insert({key, defaultValue});
273        return iterator->second;
274    }
275
276    // Returns a reference to the value at the specified key, default
277    // initializing it if it doesn't already exist
278    TValue& operator[](const TKey& key) { return map[key]; }
279    // Returns a reference to the value at the specified key, default
280    // initializing it if it doesn't already exist
281    TValue& operator[](TKey&& key) { return map[std::move(key)]; }
282
283    //
284    // Insertion
285    //
286
287    // Returns true if the value was inserted, returns false if the map
288    // already has a value associated with this key
289    bool addIfNotExists(typename InnerMap::value_type&& kvPair)
290    {
291        return !tryGetValueOrAdd(std::move(kvPair));
292    }
293    // Returns true if the value was inserted, returns false if the map
294    // already has a value associated with this key
295    bool addIfNotExists(const typename InnerMap::value_type& kvPair)
296    {
297        return !tryGetValueOrAdd(kvPair);
298    }
299    // Returns true if the value was inserted, returns false if the map
300    // already has a value associated with this key
301    bool addIfNotExists(const TKey& k, const TValue& v) { return addIfNotExists({k, v}); }
302    // Returns true if the value was inserted, returns false if the map
303    // already has a value associated with this key
304    bool addIfNotExists(TKey&& k, TValue&& v)
305    {
306        return addIfNotExists({std::move(k), std::move(v)});
307    }
308
309    // Asserts if the key already exists in the dictionary
310    void add(typename InnerMap::value_type&& kvPair)
311    {
312        if (!addIfNotExists(std::move(kvPair)))
313            SLANG_ASSERT_FAILURE("The key already exists in Dictionary.");
314    }
315    // Asserts if the key already exists in the dictionary
316    void add(const typename InnerMap::value_type& kvPair)
317    {
318        if (!addIfNotExists(kvPair))
319            SLANG_ASSERT_FAILURE("The key already exists in Dictionary.");
320    }
321    // Asserts if the key already exists in the dictionary
322    void add(const TKey& key, const TValue& value) { add({key, value}); }
323    // Asserts if the key already exists in the dictionary
324    void add(TKey&& key, TValue&& value) { add({std::move(key), std::move(value)}); }
325
326    // Inserts into the dictionary or assigns if the key already exists
327    void set(const TKey& key, const TValue& value) { map.insert_or_assign(key, value); }
328};
329
330/* We may want to rename this, as strictly speaking _Caps names are reserved */
331class _DummyClass
332{
333};
334
335template<typename T, typename DictionaryType>
336class HashSetBase
337{
338protected:
339    DictionaryType dict;
340
341private:
342    void init() {} // Base case for recursion
343    template<typename... Args>
344    void init(const T& v, Args... args)
345    {
346        add(v);
347        init(args...);
348    }
349
350public:
351    HashSetBase() {}
352    template<typename Arg, typename... Args>
353    HashSetBase(Arg arg, Args... args)
354    {
355        init(arg, args...);
356    }
357    HashSetBase(const HashSetBase& set) { operator=(set); }
358    HashSetBase(HashSetBase&& set) { operator=(_Move(set)); }
359    HashSetBase& operator=(const HashSetBase& set)
360    {
361        dict = set.dict;
362        return *this;
363    }
364    HashSetBase& operator=(HashSetBase&& set)
365    {
366        dict = _Move(set.dict);
367        return *this;
368    }
369
370public:
371    class Iterator
372    {
373    private:
374        typename DictionaryType::ConstIterator iter;
375
376    public:
377        Iterator() = default;
378        const T& operator*() const { return *KeyValueDetail::getKey(std::addressof(*iter)); }
379        const T* operator->() const { return KeyValueDetail::getKey(std::addressof(*iter)); }
380
381        Iterator& operator++()
382        {
383            ++iter;
384            return *this;
385        }
386        Iterator operator++(int)
387        {
388            Iterator rs = *this;
389            operator++();
390            return rs;
391        }
392        bool operator!=(const Iterator& that) const { return iter != that.iter; }
393        bool operator==(const Iterator& that) const { return iter == that.iter; }
394        Iterator(const typename DictionaryType::ConstIterator& _iter) { this->iter = _iter; }
395    };
396    Iterator begin() const { return Iterator(dict.begin()); }
397    Iterator end() const { return Iterator(dict.end()); }
398
399public:
400    auto getCount() const { return dict.getCount(); }
401    void clear() { dict.clear(); }
402    bool add(const T& obj) { return dict.addIfNotExists(obj, _DummyClass()); }
403    bool add(T&& obj) { return dict.addIfNotExists(_Move(obj), _DummyClass()); }
404    void remove(const T& obj) { dict.remove(obj); }
405    bool contains(const T& obj) const { return dict.containsKey(obj); }
406};
407template<typename T>
408class HashSet : public HashSetBase<T, Dictionary<T, _DummyClass>>
409{
410public:
411    using HashSetBase<T, Dictionary<T, _DummyClass>>::HashSetBase;
412};
413
414template<typename TKey, typename TValue>
415class OrderedDictionary
416{
417    friend class Iterator;
418    friend class ItemProxy;
419
420private:
421    inline int getProbeOffset(int /*probeIdx*/) const
422    {
423        // quadratic probing
424        return 1;
425    }
426
427private:
428    int m_bucketCountMinusOne;
429    int m_count;
430    UIntSet m_marks;
431
432    LinkedList<KeyValuePair<TKey, TValue>> m_kvPairs;
433    LinkedNode<KeyValuePair<TKey, TValue>>** m_hashMap;
434    void deallocateAll()
435    {
436        if (m_hashMap)
437            delete[] m_hashMap;
438        m_hashMap = nullptr;
439        m_kvPairs.clear();
440    }
441    inline bool isDeleted(int pos) const { return m_marks.contains((pos << 1) + 1); }
442    inline bool isEmpty(int pos) const { return !m_marks.contains((pos << 1)); }
443    inline void setDeleted(int pos, bool val)
444    {
445        if (val)
446            m_marks.add((pos << 1) + 1);
447        else
448            m_marks.remove((pos << 1) + 1);
449    }
450    inline void setEmpty(int pos, bool val)
451    {
452        if (val)
453            m_marks.remove((pos << 1));
454        else
455            m_marks.add((pos << 1));
456    }
457    struct FindPositionResult
458    {
459        int objectPosition;
460        int insertionPosition;
461        FindPositionResult()
462        {
463            objectPosition = -1;
464            insertionPosition = -1;
465        }
466        FindPositionResult(int objPos, int insertPos)
467        {
468            objectPosition = objPos;
469            insertionPosition = insertPos;
470        }
471    };
472    template<typename T>
473    inline int getHashPos(T& key) const
474    {
475        const unsigned int hash = (unsigned int)getHashCode(key);
476        return ((unsigned int)(hash * 2654435761)) % m_bucketCountMinusOne;
477    }
478    template<typename T>
479    FindPositionResult findPosition(const T& key) const
480    {
481        int hashPos = getHashPos((T&)key);
482        int insertPos = -1;
483        int numProbes = 0;
484        while (numProbes <= m_bucketCountMinusOne)
485        {
486            if (isEmpty(hashPos))
487            {
488                if (insertPos == -1)
489                    return FindPositionResult(-1, hashPos);
490                else
491                    return FindPositionResult(-1, insertPos);
492            }
493            else if (isDeleted(hashPos))
494            {
495                if (insertPos == -1)
496                    insertPos = hashPos;
497            }
498            else if (m_hashMap[hashPos]->value.key == key)
499            {
500                return FindPositionResult(hashPos, -1);
501            }
502            numProbes++;
503            hashPos = (hashPos + getProbeOffset(numProbes)) & m_bucketCountMinusOne;
504        }
505        if (insertPos != -1)
506            return FindPositionResult(-1, insertPos);
507        SLANG_ASSERT_FAILURE(
508            "Hash map is full. This indicates an error in Key::Equal or Key::GetHashCode.");
509    }
510    TValue& _insert(KeyValuePair<TKey, TValue>&& kvPair, int pos)
511    {
512        auto node = m_kvPairs.addLast();
513        node->value = _Move(kvPair);
514        m_hashMap[pos] = node;
515        setEmpty(pos, false);
516        setDeleted(pos, false);
517        return node->value.value;
518    }
519    void maybeRehash()
520    {
521        if (m_bucketCountMinusOne == -1 || m_count / (float)m_bucketCountMinusOne >= kMaxLoadFactor)
522        {
523            int newSize = (m_bucketCountMinusOne + 1) * 2;
524            if (newSize == 0)
525            {
526                newSize = 128;
527            }
528            OrderedDictionary<TKey, TValue> newDict;
529            newDict.m_bucketCountMinusOne = newSize - 1;
530            newDict.m_hashMap = new LinkedNode<KeyValuePair<TKey, TValue>>*[newSize];
531            newDict.m_marks.resizeAndClear(newSize * 2);
532            if (m_hashMap)
533            {
534                for (auto& kvPair : *this)
535                {
536                    newDict.add(_Move(kvPair));
537                }
538            }
539            *this = _Move(newDict);
540        }
541    }
542
543    bool addIfNotExists(KeyValuePair<TKey, TValue>&& kvPair)
544    {
545        maybeRehash();
546        auto pos = findPosition(kvPair.key);
547        if (pos.objectPosition != -1)
548            return false;
549        else if (pos.insertionPosition != -1)
550        {
551            m_count++;
552            _insert(_Move(kvPair), pos.insertionPosition);
553            return true;
554        }
555        else
556            SLANG_ASSERT_FAILURE(
557                "Inconsistent find result returned. This is a bug in Dictionary implementation.");
558    }
559    void add(KeyValuePair<TKey, TValue>&& kvPair)
560    {
561        if (!addIfNotExists(_Move(kvPair)))
562            SLANG_ASSERT_FAILURE("The key already exists in Dictionary.");
563    }
564    TValue& set(KeyValuePair<TKey, TValue>&& kvPair)
565    {
566        maybeRehash();
567        auto pos = findPosition(kvPair.key);
568        if (pos.objectPosition != -1)
569        {
570            m_hashMap[pos.objectPosition]->removeAndDelete();
571            return _insert(_Move(kvPair), pos.objectPosition);
572        }
573        else if (pos.insertionPosition != -1)
574        {
575            m_count++;
576            return _insert(_Move(kvPair), pos.insertionPosition);
577        }
578        else
579            SLANG_ASSERT_FAILURE(
580                "Inconsistent find result returned. This is a bug in Dictionary implementation.");
581    }
582
583public:
584    using Iterator = typename LinkedList<KeyValuePair<TKey, TValue>>::Iterator;
585    using ConstIterator = typename LinkedList<KeyValuePair<TKey, TValue>>::ConstIterator;
586
587    Iterator begin() { return m_kvPairs.begin(); }
588    Iterator end() { return m_kvPairs.end(); }
589    ConstIterator begin() const { return m_kvPairs.begin(); }
590    ConstIterator end() const { return m_kvPairs.end(); }
591
592public:
593    void add(const TKey& key, const TValue& value) { add(KeyValuePair<TKey, TValue>(key, value)); }
594    void add(TKey&& key, TValue&& value)
595    {
596        add(KeyValuePair<TKey, TValue>(_Move(key), _Move(value)));
597    }
598    bool addIfNotExists(const TKey& key, const TValue& value)
599    {
600        return addIfNotExists(KeyValuePair<TKey, TValue>(key, value));
601    }
602    bool addIfNotExists(TKey&& key, TValue&& value)
603    {
604        return addIfNotExists(KeyValuePair<TKey, TValue>(_Move(key), _Move(value)));
605    }
606    void remove(const TKey& key)
607    {
608        if (m_count > 0)
609        {
610            auto pos = findPosition(key);
611            if (pos.objectPosition != -1)
612            {
613                m_kvPairs.removeAndDelete(m_hashMap[pos.objectPosition]);
614                m_hashMap[pos.objectPosition] = 0;
615                setDeleted(pos.objectPosition, true);
616                m_count--;
617            }
618        }
619    }
620    void clear()
621    {
622        m_count = 0;
623        m_kvPairs.clear();
624        m_marks.clear();
625    }
626    template<typename T>
627    bool containsKey(const T& key) const
628    {
629        if (m_bucketCountMinusOne == -1)
630            return false;
631        auto pos = findPosition(key);
632        return pos.objectPosition != -1;
633    }
634    template<typename T>
635    TValue* tryGetValue(const T& key) const
636    {
637        if (m_bucketCountMinusOne == -1)
638            return nullptr;
639        auto pos = findPosition(key);
640        if (pos.objectPosition != -1)
641        {
642            return &(m_hashMap[pos.objectPosition]->value.value);
643        }
644        return nullptr;
645    }
646    template<typename T>
647    bool tryGetValue(const T& key, TValue& value) const
648    {
649        if (m_bucketCountMinusOne == -1)
650            return false;
651        auto pos = findPosition(key);
652        if (pos.objectPosition != -1)
653        {
654            value = m_hashMap[pos.objectPosition]->value.value;
655            return true;
656        }
657        return false;
658    }
659    class ItemProxy
660    {
661    private:
662        const OrderedDictionary<TKey, TValue>* dict;
663        TKey key;
664
665    public:
666        ItemProxy(const TKey& _key, const OrderedDictionary<TKey, TValue>* _dict)
667        {
668            this->dict = _dict;
669            this->key = _key;
670        }
671        ItemProxy(TKey&& _key, const OrderedDictionary<TKey, TValue>* _dict)
672        {
673            this->dict = _dict;
674            this->key = _Move(_key);
675        }
676        TValue& getValue() const
677        {
678            auto pos = dict->findPosition(key);
679            if (pos.objectPosition != -1)
680            {
681                return dict->m_hashMap[pos.objectPosition]->value.value;
682            }
683            else
684            {
685                SLANG_ASSERT_FAILURE("The key does not exists in dictionary.");
686            }
687        }
688        inline TValue& operator()() const { return getValue(); }
689        operator TValue&() const { return getValue(); }
690        TValue& operator=(const TValue& val)
691        {
692            return ((OrderedDictionary<TKey, TValue>*)dict)
693                ->set(KeyValuePair<TKey, TValue>(_Move(key), val));
694        }
695        TValue& operator=(TValue&& val)
696        {
697            return ((OrderedDictionary<TKey, TValue>*)dict)
698                ->set(KeyValuePair<TKey, TValue>(_Move(key), _Move(val)));
699        }
700    };
701    ItemProxy operator[](const TKey& key) const { return ItemProxy(key, this); }
702    ItemProxy operator[](TKey&& key) const { return ItemProxy(_Move(key), this); }
703
704    int getCount() const { return m_count; }
705    KeyValuePair<TKey, TValue>& getFirst() const { return m_kvPairs.getFirst(); }
706    KeyValuePair<TKey, TValue>& getLast() const { return m_kvPairs.getLast(); }
707
708private:
709    template<typename... Args>
710    void init(const KeyValuePair<TKey, TValue>& kvPair, Args... args)
711    {
712        add(kvPair);
713        init(args...);
714    }
715
716public:
717    OrderedDictionary()
718    {
719        m_bucketCountMinusOne = -1;
720        m_count = 0;
721        m_hashMap = 0;
722    }
723    template<typename Arg, typename... Args>
724    OrderedDictionary(Arg arg, Args... args)
725    {
726        init(arg, args...);
727    }
728    OrderedDictionary(const OrderedDictionary<TKey, TValue>& other)
729        : m_bucketCountMinusOne(-1), m_count(0), m_hashMap(0)
730    {
731        *this = other;
732    }
733    OrderedDictionary(OrderedDictionary<TKey, TValue>&& other)
734        : m_bucketCountMinusOne(-1), m_count(0), m_hashMap(0)
735    {
736        *this = (_Move(other));
737    }
738    OrderedDictionary<TKey, TValue>& operator=(const OrderedDictionary<TKey, TValue>& other)
739    {
740        if (this == &other)
741            return *this;
742        clear();
743        for (auto& item : other)
744            add(item.key, item.value);
745        return *this;
746    }
747    OrderedDictionary<TKey, TValue>& operator=(OrderedDictionary<TKey, TValue>&& other)
748    {
749        if (this == &other)
750            return *this;
751        deallocateAll();
752        m_bucketCountMinusOne = other.m_bucketCountMinusOne;
753        m_count = other.m_count;
754        m_hashMap = other.m_hashMap;
755        m_marks = _Move(other.m_marks);
756        other.m_hashMap = 0;
757        other.m_count = 0;
758        other.m_bucketCountMinusOne = -1;
759        m_kvPairs = _Move(other.m_kvPairs);
760        return *this;
761    }
762    ~OrderedDictionary() { deallocateAll(); }
763};
764
765template<typename T>
766class OrderedHashSet : public HashSetBase<T, OrderedDictionary<T, _DummyClass>>
767{
768public:
769    T& getLast() { return this->dict.getLast().key; }
770    void removeLast() { this->remove(getLast()); }
771};
772} // namespace Slang
773
774#endif