Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-28 09:21:00

0001 // Copyright (c) 2026 OPEN CASCADE SAS
0002 //
0003 // This file is part of Open CASCADE Technology software library.
0004 //
0005 // This library is free software; you can redistribute it and/or modify it under
0006 // the terms of the GNU Lesser General Public License version 2.1 as published
0007 // by the Free Software Foundation, with special exception defined in the file
0008 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
0009 // distribution for complete text of the license and disclaimer of any warranty.
0010 //
0011 // Alternatively, this file may be used under the terms of Open CASCADE
0012 // commercial license or contractual agreement.
0013 
0014 #ifndef NCollection_OrderedDataMap_HeaderFile
0015 #define NCollection_OrderedDataMap_HeaderFile
0016 
0017 #include <NCollection_BaseMap.hxx>
0018 #include <NCollection_DefaultHasher.hxx>
0019 #include <NCollection_ItemsView.hxx>
0020 #include <NCollection_StlIterator.hxx>
0021 #include <NCollection_TListNode.hxx>
0022 #include <Standard_NoSuchObject.hxx>
0023 #include <Standard_OutOfRange.hxx>
0024 
0025 #include <functional>
0026 #include <optional>
0027 #include <type_traits>
0028 #include <utility>
0029 
0030 //! @brief Hash map that preserves insertion order.
0031 //!
0032 //! NCollection_OrderedDataMap is an alternative to NCollection_DataMap that
0033 //! maintains a doubly-linked list threaded through the hash nodes, so
0034 //! iteration always follows the order in which key-value pairs were inserted.
0035 //!
0036 //! Key features:
0037 //! - O(1) hash lookup (IsBound, Find, Seek, Contained)
0038 //! - O(1) insertion at tail (Bind, Emplace)
0039 //! - O(1) removal with linked-list unlink (UnBind)
0040 //! - O(1) access to first/last inserted pairs (First, Last, FirstValue, LastValue)
0041 //! - Deterministic iteration order across platforms
0042 //! - Structured binding support via Items() view
0043 //!
0044 //! Best suited for:
0045 //! - Key-value maps that require stable iteration order
0046 //! - Serialization-friendly maps (deterministic output)
0047 //! - Property registries where definition order matters
0048 //! - Cases where NCollection_IndexedDataMap overhead is unnecessary
0049 //!
0050 //! Compared to NCollection_IndexedDataMap:
0051 //! - UnBind is O(1) instead of O(n) (no swap-and-shrink on dense array)
0052 //! - No integer index access (use IndexedDataMap if indices are needed)
0053 //!
0054 //! The OrderedDataMap can be seen as an extended array where the Keys are
0055 //! the indices. For this reason the operator () is defined to fetch an
0056 //! Item from a Key.
0057 //!
0058 //! The number of buckets is managed automatically and grows when the number
0059 //! of keys exceeds the bucket count.
0060 //!
0061 //! @note This class is NOT thread-safe. External synchronization is required
0062 //!       for concurrent access from multiple threads.
0063 //!
0064 //! @tparam TheKeyType  Type of keys
0065 //! @tparam TheItemType Type of values
0066 //! @tparam Hasher      Hash and equality functor (default: NCollection_DefaultHasher)
0067 template <class TheKeyType, class TheItemType, class Hasher = NCollection_DefaultHasher<TheKeyType>>
0068 class NCollection_OrderedDataMap : public NCollection_BaseMap
0069 {
0070 public:
0071   //! STL-compliant typedef for key type
0072   typedef TheKeyType key_type;
0073   //! STL-compliant typedef for value type
0074   typedef TheItemType value_type;
0075 
0076 public:
0077   //! Adaptation of the TListNode to the ordered data map notations.
0078   //! Extends the hash-chain node with insertion-order linked list pointers.
0079   class OrderedDataMapNode : public NCollection_TListNode<TheItemType>
0080   {
0081   public:
0082     //! Constructor with copy key and copy item
0083     OrderedDataMapNode(const TheKeyType&     theKey,
0084                        const TheItemType&    theItem,
0085                        NCollection_ListNode* theNext)
0086         : NCollection_TListNode<TheItemType>(theItem, theNext),
0087           myOrderPrev(nullptr),
0088           myOrderNext(nullptr),
0089           myKey(theKey)
0090     {
0091     }
0092 
0093     //! Constructor with copy key and move item
0094     OrderedDataMapNode(const TheKeyType&     theKey,
0095                        TheItemType&&         theItem,
0096                        NCollection_ListNode* theNext)
0097         : NCollection_TListNode<TheItemType>(std::forward<TheItemType>(theItem), theNext),
0098           myOrderPrev(nullptr),
0099           myOrderNext(nullptr),
0100           myKey(theKey)
0101     {
0102     }
0103 
0104     //! Constructor with move key and copy item
0105     OrderedDataMapNode(TheKeyType&&          theKey,
0106                        const TheItemType&    theItem,
0107                        NCollection_ListNode* theNext)
0108         : NCollection_TListNode<TheItemType>(theItem, theNext),
0109           myOrderPrev(nullptr),
0110           myOrderNext(nullptr),
0111           myKey(std::forward<TheKeyType>(theKey))
0112     {
0113     }
0114 
0115     //! Constructor with move key and move item
0116     OrderedDataMapNode(TheKeyType&& theKey, TheItemType&& theItem, NCollection_ListNode* theNext)
0117         : NCollection_TListNode<TheItemType>(std::forward<TheItemType>(theItem), theNext),
0118           myOrderPrev(nullptr),
0119           myOrderNext(nullptr),
0120           myKey(std::forward<TheKeyType>(theKey))
0121     {
0122     }
0123 
0124     //! Constructor with in-place value construction
0125     template <typename K, typename... Args>
0126     OrderedDataMapNode(K&& theKey,
0127                        std::in_place_t,
0128                        NCollection_ListNode* theNext,
0129                        Args&&... theArgs)
0130         : NCollection_TListNode<TheItemType>(std::in_place,
0131                                              theNext,
0132                                              std::forward<Args>(theArgs)...),
0133           myOrderPrev(nullptr),
0134           myOrderNext(nullptr),
0135           myKey(std::forward<K>(theKey))
0136     {
0137     }
0138 
0139     //! Key
0140     const TheKeyType& Key() const noexcept { return myKey; }
0141 
0142     //! Static deleter to be passed to BaseMap
0143     static void delNode(NCollection_ListNode*                   theNode,
0144                         occ::handle<NCollection_BaseAllocator>& theAl) noexcept
0145     {
0146       ((OrderedDataMapNode*)theNode)->~OrderedDataMapNode();
0147       theAl->Free(theNode);
0148     }
0149 
0150     OrderedDataMapNode* myOrderPrev; //!< Previous node in insertion order
0151     OrderedDataMapNode* myOrderNext; //!< Next node in insertion order
0152 
0153   private:
0154     TheKeyType myKey;
0155   };
0156 
0157 public:
0158   //! Implementation of the Iterator interface.
0159   //! Iterates in insertion order by walking the doubly-linked list.
0160   class Iterator
0161   {
0162   public:
0163     //! Empty constructor
0164     Iterator() noexcept
0165         : myNode(nullptr)
0166     {
0167     }
0168 
0169     //! Constructor
0170     Iterator(const NCollection_OrderedDataMap& theMap) noexcept
0171         : myNode(theMap.myFirst)
0172     {
0173     }
0174 
0175     //! Query if the end of collection is reached by iterator
0176     bool More() const noexcept { return myNode != nullptr; }
0177 
0178     //! Make a step along the collection (in insertion order)
0179     void Next() noexcept
0180     {
0181       if (myNode)
0182         myNode = myNode->myOrderNext;
0183     }
0184 
0185     //! Value inquiry
0186     const TheItemType& Value() const
0187     {
0188       Standard_NoSuchObject_Raise_if(!More(), "NCollection_OrderedDataMap::Iterator::Value");
0189       return myNode->Value();
0190     }
0191 
0192     //! Value change access
0193     TheItemType& ChangeValue() const
0194     {
0195       Standard_NoSuchObject_Raise_if(!More(), "NCollection_OrderedDataMap::Iterator::ChangeValue");
0196       return myNode->ChangeValue();
0197     }
0198 
0199     //! Key
0200     const TheKeyType& Key() const
0201     {
0202       Standard_NoSuchObject_Raise_if(!More(), "NCollection_OrderedDataMap::Iterator::Key");
0203       return myNode->Key();
0204     }
0205 
0206     //! Performs comparison of two iterators.
0207     bool IsEqual(const Iterator& theOther) const noexcept { return myNode == theOther.myNode; }
0208 
0209     //! Initialize
0210     void Initialize(const NCollection_OrderedDataMap& theMap) noexcept { myNode = theMap.myFirst; }
0211 
0212     //! Reset
0213     void Reset() noexcept { myNode = nullptr; }
0214 
0215   private:
0216     OrderedDataMapNode* myNode; //!< Current node in insertion-order list
0217   };
0218 
0219   //! Shorthand for a regular iterator type.
0220   typedef NCollection_StlIterator<std::forward_iterator_tag, Iterator, TheItemType, false> iterator;
0221 
0222   //! Shorthand for a constant iterator type.
0223   typedef NCollection_StlIterator<std::forward_iterator_tag, Iterator, TheItemType, true>
0224     const_iterator;
0225 
0226   //! Returns an iterator pointing to the first element in the map.
0227   iterator begin() const noexcept { return Iterator(*this); }
0228 
0229   //! Returns an iterator referring to the past-the-end element in the map.
0230   iterator end() const noexcept { return Iterator(); }
0231 
0232   //! Returns a const iterator pointing to the first element in the map.
0233   const_iterator cbegin() const noexcept { return Iterator(*this); }
0234 
0235   //! Returns a const iterator referring to the past-the-end element in the map.
0236   const_iterator cend() const noexcept { return Iterator(); }
0237 
0238 public:
0239   // **************** Key-value pair iteration support for structured bindings
0240 
0241   //! Key-value pair reference for structured binding support.
0242   //! Enables: for (auto [key, value] : map.Items())
0243   using KeyValueRef = NCollection_ItemsView::KeyValueRef<TheKeyType, TheItemType, false>;
0244 
0245   //! Const key-value pair reference for structured binding support.
0246   using ConstKeyValueRef = NCollection_ItemsView::KeyValueRef<TheKeyType, TheItemType, true>;
0247 
0248 private:
0249   //! Extractor for mutable key-value pairs
0250   struct ItemsExtractor
0251   {
0252     static KeyValueRef Extract(const Iterator& theIter)
0253     {
0254       return {theIter.Key(), theIter.ChangeValue()};
0255     }
0256   };
0257 
0258   //! Extractor for const key-value pairs
0259   struct ConstItemsExtractor
0260   {
0261     static ConstKeyValueRef Extract(const Iterator& theIter)
0262     {
0263       return {theIter.Key(), theIter.Value()};
0264     }
0265   };
0266 
0267 public:
0268   //! View class for key-value pair iteration (mutable).
0269   using ItemsView =
0270     NCollection_ItemsView::View<NCollection_OrderedDataMap, KeyValueRef, ItemsExtractor, false>;
0271 
0272   //! View class for key-value pair iteration (const).
0273   using ConstItemsView = NCollection_ItemsView::
0274     View<NCollection_OrderedDataMap, ConstKeyValueRef, ConstItemsExtractor, true>;
0275 
0276   //! Returns a view for key-value pair iteration.
0277   //! Usage: for (auto [aKey, aValue] : aMap.Items())
0278   ItemsView Items() { return ItemsView(*this); }
0279 
0280   //! Returns a const view for key-value pair iteration.
0281   //! Usage: for (const auto& [aKey, aValue] : aMap.Items())
0282   ConstItemsView Items() const { return ConstItemsView(*this); }
0283 
0284 public:
0285   // ---------- PUBLIC METHODS ------------
0286 
0287   //! Empty Constructor.
0288   NCollection_OrderedDataMap()
0289       : NCollection_BaseMap(1, true, occ::handle<NCollection_BaseAllocator>()),
0290         myFirst(nullptr),
0291         myLast(nullptr)
0292   {
0293   }
0294 
0295   //! Constructor
0296   explicit NCollection_OrderedDataMap(
0297     const size_t                                  theNbBuckets,
0298     const occ::handle<NCollection_BaseAllocator>& theAllocator = nullptr)
0299       : NCollection_BaseMap(theNbBuckets, true, theAllocator),
0300         myFirst(nullptr),
0301         myLast(nullptr)
0302   {
0303   }
0304 
0305   //! Constructor (legacy int-taking).
0306   explicit NCollection_OrderedDataMap(
0307     const int                                     theNbBuckets,
0308     const occ::handle<NCollection_BaseAllocator>& theAllocator = nullptr)
0309       : NCollection_OrderedDataMap(NCollection_BaseMap::NbBucketsFromInt(theNbBuckets),
0310                                    theAllocator)
0311   {
0312   }
0313 
0314   //! Constructor with custom hasher (copy).
0315   //! @param theHasher custom hasher instance
0316   //! @param theNbBuckets initial number of buckets
0317   //! @param theAllocator custom memory allocator
0318   explicit NCollection_OrderedDataMap(
0319     const Hasher&                                 theHasher,
0320     const size_t                                  theNbBuckets = 1,
0321     const occ::handle<NCollection_BaseAllocator>& theAllocator = nullptr)
0322       : NCollection_BaseMap(theNbBuckets, true, theAllocator),
0323         myHasher(theHasher),
0324         myFirst(nullptr),
0325         myLast(nullptr)
0326   {
0327   }
0328 
0329   //! Constructor with custom hasher (copy, legacy int-taking).
0330   explicit NCollection_OrderedDataMap(
0331     const Hasher&                                 theHasher,
0332     const int                                     theNbBuckets,
0333     const occ::handle<NCollection_BaseAllocator>& theAllocator = nullptr)
0334       : NCollection_OrderedDataMap(theHasher,
0335                                    NCollection_BaseMap::NbBucketsFromInt(theNbBuckets),
0336                                    theAllocator)
0337   {
0338   }
0339 
0340   //! Constructor with custom hasher (move).
0341   //! @param theHasher custom hasher instance (moved)
0342   //! @param theNbBuckets initial number of buckets
0343   //! @param theAllocator custom memory allocator
0344   explicit NCollection_OrderedDataMap(
0345     Hasher&&                                      theHasher,
0346     const size_t                                  theNbBuckets = 1,
0347     const occ::handle<NCollection_BaseAllocator>& theAllocator = nullptr)
0348       : NCollection_BaseMap(theNbBuckets, true, theAllocator),
0349         myHasher(std::move(theHasher)),
0350         myFirst(nullptr),
0351         myLast(nullptr)
0352   {
0353   }
0354 
0355   //! Constructor with custom hasher (move, legacy int-taking).
0356   explicit NCollection_OrderedDataMap(
0357     Hasher&&                                      theHasher,
0358     const int                                     theNbBuckets,
0359     const occ::handle<NCollection_BaseAllocator>& theAllocator = nullptr)
0360       : NCollection_OrderedDataMap(std::move(theHasher),
0361                                    NCollection_BaseMap::NbBucketsFromInt(theNbBuckets),
0362                                    theAllocator)
0363   {
0364   }
0365 
0366   //! Copy constructor
0367   NCollection_OrderedDataMap(const NCollection_OrderedDataMap& theOther)
0368       : NCollection_BaseMap(theOther.NbBuckets(), true, theOther.myAllocator),
0369         myHasher(theOther.myHasher),
0370         myFirst(nullptr),
0371         myLast(nullptr)
0372   {
0373     const int anExt = theOther.Extent();
0374     if (anExt <= 0)
0375       return;
0376     ReSize(anExt - 1);
0377     for (Iterator anIter(theOther); anIter.More(); anIter.Next())
0378       Bind(anIter.Key(), anIter.Value());
0379   }
0380 
0381   //! Move constructor
0382   NCollection_OrderedDataMap(NCollection_OrderedDataMap&& theOther) noexcept
0383       : NCollection_BaseMap(std::forward<NCollection_BaseMap>(theOther)),
0384         myHasher(std::move(theOther.myHasher)),
0385         myFirst(theOther.myFirst),
0386         myLast(theOther.myLast)
0387   {
0388     theOther.myFirst = nullptr;
0389     theOther.myLast  = nullptr;
0390   }
0391 
0392   //! Exchange the content of two maps without re-allocations.
0393   //! Notice that allocators will be swapped as well!
0394   void Exchange(NCollection_OrderedDataMap& theOther) noexcept
0395   {
0396     this->exchangeMapsData(theOther);
0397     std::swap(myFirst, theOther.myFirst);
0398     std::swap(myLast, theOther.myLast);
0399     std::swap(myHasher, theOther.myHasher);
0400   }
0401 
0402   //! Returns const reference to the hasher.
0403   const Hasher& GetHasher() const noexcept { return myHasher; }
0404 
0405   //! Assignment.
0406   //! This method does not change the internal allocator.
0407   NCollection_OrderedDataMap& Assign(const NCollection_OrderedDataMap& theOther)
0408   {
0409     if (this == &theOther)
0410       return *this;
0411 
0412     Clear();
0413     int anExt = theOther.Extent();
0414     if (anExt)
0415     {
0416       ReSize(anExt - 1);
0417       Iterator anIter(theOther);
0418       for (; anIter.More(); anIter.Next())
0419         Bind(anIter.Key(), anIter.Value());
0420     }
0421     return *this;
0422   }
0423 
0424   //! Assignment operator
0425   NCollection_OrderedDataMap& operator=(const NCollection_OrderedDataMap& theOther)
0426   {
0427     return Assign(theOther);
0428   }
0429 
0430   //! Move operator
0431   NCollection_OrderedDataMap& operator=(NCollection_OrderedDataMap&& theOther) noexcept
0432   {
0433     if (this == &theOther)
0434       return *this;
0435     exchangeMapsData(theOther);
0436     std::swap(myFirst, theOther.myFirst);
0437     std::swap(myLast, theOther.myLast);
0438     return *this;
0439   }
0440 
0441   //! ReSize
0442   void ReSize(const size_t N)
0443   {
0444     NCollection_ListNode** newdata = nullptr;
0445     NCollection_ListNode** dummy   = nullptr;
0446     size_t                 newBuck;
0447     if (BeginResize(N, newBuck, newdata, dummy))
0448     {
0449       if (myData1)
0450       {
0451         OrderedDataMapNode** olddata = (OrderedDataMapNode**)myData1;
0452         OrderedDataMapNode * p, *q;
0453         for (size_t i = 0; i <= NbBuckets(); ++i)
0454         {
0455           if (olddata[i])
0456           {
0457             p = olddata[i];
0458             while (p)
0459             {
0460               const size_t k = HashCode(p->Key(), newBuck);
0461               q              = (OrderedDataMapNode*)p->Next();
0462               p->Next()      = newdata[k];
0463               newdata[k]     = p;
0464               p              = q;
0465             }
0466           }
0467         }
0468       }
0469       EndResize(N, newBuck, newdata, dummy);
0470     }
0471   }
0472 
0473   void ReSize(const int N)
0474   {
0475     Standard_OutOfRange_Raise_if(N < 0, "NCollection_OrderedDataMap::ReSize: negative size");
0476     ReSize(static_cast<size_t>(N));
0477   }
0478 
0479   //! Bind binds Item to Key in map.
0480   //! @param theKey  key to add/update
0481   //! @param theItem new item; overrides value previously bound to the key
0482   //! @return true if Key was not bound already
0483   bool Bind(const TheKeyType& theKey, const TheItemType& theItem)
0484   {
0485     return emplaceImpl(theKey, std::false_type{}, std::false_type{}, theItem);
0486   }
0487 
0488   //! Bind binds Item to Key in map.
0489   bool Bind(TheKeyType&& theKey, const TheItemType& theItem)
0490   {
0491     return emplaceImpl(std::move(theKey), std::false_type{}, std::false_type{}, theItem);
0492   }
0493 
0494   //! Bind binds Item to Key in map.
0495   bool Bind(const TheKeyType& theKey, TheItemType&& theItem)
0496   {
0497     return emplaceImpl(theKey, std::false_type{}, std::false_type{}, std::move(theItem));
0498   }
0499 
0500   //! Bind binds Item to Key in map.
0501   bool Bind(TheKeyType&& theKey, TheItemType&& theItem)
0502   {
0503     return emplaceImpl(std::move(theKey), std::false_type{}, std::false_type{}, std::move(theItem));
0504   }
0505 
0506   //! Bound binds Item to Key in map.
0507   //! @return pointer to modifiable Item
0508   TheItemType* Bound(const TheKeyType& theKey, const TheItemType& theItem)
0509   {
0510     return &emplaceImpl(theKey, std::false_type{}, std::true_type{}, theItem);
0511   }
0512 
0513   //! Bound binds Item to Key in map.
0514   TheItemType* Bound(TheKeyType&& theKey, const TheItemType& theItem)
0515   {
0516     return &emplaceImpl(std::move(theKey), std::false_type{}, std::true_type{}, theItem);
0517   }
0518 
0519   //! Bound binds Item to Key in map.
0520   TheItemType* Bound(const TheKeyType& theKey, TheItemType&& theItem)
0521   {
0522     return &emplaceImpl(theKey, std::false_type{}, std::true_type{}, std::move(theItem));
0523   }
0524 
0525   //! Bound binds Item to Key in map.
0526   TheItemType* Bound(TheKeyType&& theKey, TheItemType&& theItem)
0527   {
0528     return &emplaceImpl(std::move(theKey), std::false_type{}, std::true_type{}, std::move(theItem));
0529   }
0530 
0531   //! TryBind binds Item to Key in map only if Key is not yet bound.
0532   //! @return true if Key was newly bound, false if Key already existed (no replacement)
0533   bool TryBind(const TheKeyType& theKey, const TheItemType& theItem)
0534   {
0535     return emplaceImpl(theKey, std::true_type{}, std::false_type{}, theItem);
0536   }
0537 
0538   //! TryBind binds Item to Key in map only if Key is not yet bound.
0539   bool TryBind(TheKeyType&& theKey, const TheItemType& theItem)
0540   {
0541     return emplaceImpl(std::move(theKey), std::true_type{}, std::false_type{}, theItem);
0542   }
0543 
0544   //! TryBind binds Item to Key in map only if Key is not yet bound.
0545   bool TryBind(const TheKeyType& theKey, TheItemType&& theItem)
0546   {
0547     return emplaceImpl(theKey, std::true_type{}, std::false_type{}, std::move(theItem));
0548   }
0549 
0550   //! TryBind binds Item to Key in map only if Key is not yet bound.
0551   bool TryBind(TheKeyType&& theKey, TheItemType&& theItem)
0552   {
0553     return emplaceImpl(std::move(theKey), std::true_type{}, std::false_type{}, std::move(theItem));
0554   }
0555 
0556   //! TryBound binds Item to Key in map only if Key is not yet bound.
0557   //! @return reference to existing or newly bound Item
0558   TheItemType& TryBound(const TheKeyType& theKey, const TheItemType& theItem)
0559   {
0560     return emplaceImpl(theKey, std::true_type{}, std::true_type{}, theItem);
0561   }
0562 
0563   //! TryBound binds Item to Key in map only if Key is not yet bound.
0564   TheItemType& TryBound(TheKeyType&& theKey, const TheItemType& theItem)
0565   {
0566     return emplaceImpl(std::move(theKey), std::true_type{}, std::true_type{}, theItem);
0567   }
0568 
0569   //! TryBound binds Item to Key in map only if Key is not yet bound.
0570   TheItemType& TryBound(const TheKeyType& theKey, TheItemType&& theItem)
0571   {
0572     return emplaceImpl(theKey, std::true_type{}, std::true_type{}, std::move(theItem));
0573   }
0574 
0575   //! TryBound binds Item to Key in map only if Key is not yet bound.
0576   TheItemType& TryBound(TheKeyType&& theKey, TheItemType&& theItem)
0577   {
0578     return emplaceImpl(std::move(theKey), std::true_type{}, std::true_type{}, std::move(theItem));
0579   }
0580 
0581   //! Emplace constructs value in-place; if key exists, destroys and reconstructs value.
0582   //! @param theKey  key to add/update
0583   //! @param theArgs arguments forwarded to value constructor
0584   //! @return true if key was newly added, false if key already existed (and value was
0585   //! reconstructed)
0586   template <typename K, typename... Args>
0587   bool Emplace(K&& theKey, Args&&... theArgs)
0588   {
0589     return emplaceImpl(std::forward<K>(theKey),
0590                        std::false_type{},
0591                        std::false_type{},
0592                        std::forward<Args>(theArgs)...);
0593   }
0594 
0595   //! Emplaced constructs value in-place; if key exists, destroys and reconstructs value.
0596   //! @param theKey  key to add/update
0597   //! @param theArgs arguments forwarded to value constructor
0598   //! @return reference to the value (existing reconstructed or newly added)
0599   template <typename K, typename... Args>
0600   TheItemType& Emplaced(K&& theKey, Args&&... theArgs)
0601   {
0602     return emplaceImpl(std::forward<K>(theKey),
0603                        std::false_type{},
0604                        std::true_type{},
0605                        std::forward<Args>(theArgs)...);
0606   }
0607 
0608   //! TryEmplace constructs value in-place only if key not already bound.
0609   //! @param theKey  key to add
0610   //! @param theArgs arguments forwarded to value constructor
0611   //! @return true if key was newly added, false if key already existed
0612   template <typename K, typename... Args>
0613   bool TryEmplace(K&& theKey, Args&&... theArgs)
0614   {
0615     return emplaceImpl(std::forward<K>(theKey),
0616                        std::true_type{},
0617                        std::false_type{},
0618                        std::forward<Args>(theArgs)...);
0619   }
0620 
0621   //! TryEmplaced constructs value in-place only if key not already bound.
0622   //! @param theKey  key to add
0623   //! @param theArgs arguments forwarded to value constructor
0624   //! @return reference to the value (existing or newly added)
0625   template <typename K, typename... Args>
0626   TheItemType& TryEmplaced(K&& theKey, Args&&... theArgs)
0627   {
0628     return emplaceImpl(std::forward<K>(theKey),
0629                        std::true_type{},
0630                        std::true_type{},
0631                        std::forward<Args>(theArgs)...);
0632   }
0633 
0634   //! IsBound
0635   bool IsBound(const TheKeyType& theKey) const
0636   {
0637     OrderedDataMapNode* p;
0638     return lookup(theKey, p);
0639   }
0640 
0641   //! Contained returns optional pair of const references to key and value.
0642   //! Returns std::nullopt if the key is not found.
0643   std::optional<
0644     std::pair<std::reference_wrapper<const TheKeyType>, std::reference_wrapper<const TheItemType>>>
0645     Contained(const TheKeyType& theKey) const
0646   {
0647     OrderedDataMapNode* p = nullptr;
0648     if (!lookup(theKey, p))
0649       return std::nullopt;
0650     return std::make_pair(std::cref(p->Key()), std::cref(p->Value()));
0651   }
0652 
0653   //! Contained returns optional pair of const key reference and mutable value reference.
0654   //! Returns std::nullopt if the key is not found.
0655   std::optional<
0656     std::pair<std::reference_wrapper<const TheKeyType>, std::reference_wrapper<TheItemType>>>
0657     Contained(const TheKeyType& theKey)
0658   {
0659     OrderedDataMapNode* p = nullptr;
0660     if (!lookup(theKey, p))
0661       return std::nullopt;
0662     return std::make_pair(std::cref(p->Key()), std::ref(p->ChangeValue()));
0663   }
0664 
0665   //! UnBind removes Item Key pair from map
0666   bool UnBind(const TheKeyType& theKey)
0667   {
0668     if (IsEmpty())
0669       return false;
0670     OrderedDataMapNode** data = (OrderedDataMapNode**)myData1;
0671     const size_t         k    = HashCode(theKey, NbBuckets());
0672     OrderedDataMapNode*  p    = data[k];
0673     OrderedDataMapNode*  q    = nullptr;
0674     while (p)
0675     {
0676       if (IsEqual(p->Key(), theKey))
0677       {
0678         Decrement();
0679         if (q)
0680           q->Next() = p->Next();
0681         else
0682           data[k] = (OrderedDataMapNode*)p->Next();
0683         unlinkFromList(p);
0684         p->~OrderedDataMapNode();
0685         this->myAllocator->Free(p);
0686         return true;
0687       }
0688       q = p;
0689       p = (OrderedDataMapNode*)p->Next();
0690     }
0691     return false;
0692   }
0693 
0694   //! Seek returns pointer to Item by Key. Returns
0695   //! NULL if Key was not bound.
0696   const TheItemType* Seek(const TheKeyType& theKey) const
0697   {
0698     OrderedDataMapNode* p = nullptr;
0699     if (!lookup(theKey, p))
0700       return nullptr;
0701     return &p->Value();
0702   }
0703 
0704   //! Find returns the Item for Key. Raises if Key was not bound
0705   const TheItemType& Find(const TheKeyType& theKey) const
0706   {
0707     OrderedDataMapNode* p = nullptr;
0708     if (!lookup(theKey, p))
0709       throw Standard_NoSuchObject("NCollection_OrderedDataMap::Find");
0710     return p->Value();
0711   }
0712 
0713   //! Find Item for key with copying.
0714   //! @return true if key was found
0715   bool Find(const TheKeyType& theKey, TheItemType& theValue) const
0716   {
0717     OrderedDataMapNode* p = nullptr;
0718     if (!lookup(theKey, p))
0719       return false;
0720 
0721     theValue = p->Value();
0722     return true;
0723   }
0724 
0725   //! operator ()
0726   const TheItemType& operator()(const TheKeyType& theKey) const { return Find(theKey); }
0727 
0728   //! ChangeSeek returns modifiable pointer to Item by Key. Returns
0729   //! NULL if Key was not bound.
0730   TheItemType* ChangeSeek(const TheKeyType& theKey)
0731   {
0732     OrderedDataMapNode* p = nullptr;
0733     if (!lookup(theKey, p))
0734       return nullptr;
0735     return &p->ChangeValue();
0736   }
0737 
0738   //! ChangeFind returns modifiable Item by Key. Raises if Key was not bound
0739   TheItemType& ChangeFind(const TheKeyType& theKey)
0740   {
0741     OrderedDataMapNode* p = nullptr;
0742     if (!lookup(theKey, p))
0743       throw Standard_NoSuchObject("NCollection_OrderedDataMap::Find");
0744     return p->ChangeValue();
0745   }
0746 
0747   //! operator ()
0748   TheItemType& operator()(const TheKeyType& theKey) { return ChangeFind(theKey); }
0749 
0750   //! Clear data. If doReleaseMemory is false then the table of
0751   //! buckets is not released and will be reused.
0752   void Clear(const bool doReleaseMemory = false)
0753   {
0754     Destroy(OrderedDataMapNode::delNode, doReleaseMemory);
0755     myFirst = nullptr;
0756     myLast  = nullptr;
0757   }
0758 
0759   //! Clear data and reset allocator
0760   void Clear(const occ::handle<NCollection_BaseAllocator>& theAllocator)
0761   {
0762     Clear(theAllocator != this->myAllocator);
0763     this->myAllocator =
0764       (!theAllocator.IsNull() ? theAllocator : NCollection_BaseAllocator::CommonBaseAllocator());
0765   }
0766 
0767   //! Destructor
0768   ~NCollection_OrderedDataMap() override { Clear(true); }
0769 
0770   //! Returns the first key in insertion order.
0771   //! @throws Standard_NoSuchObject if map is empty
0772   const TheKeyType& First() const
0773   {
0774     if (IsEmpty())
0775       throw Standard_NoSuchObject("NCollection_OrderedDataMap::First");
0776     return myFirst->Key();
0777   }
0778 
0779   //! Returns the last key in insertion order.
0780   //! @throws Standard_NoSuchObject if map is empty
0781   const TheKeyType& Last() const
0782   {
0783     if (IsEmpty())
0784       throw Standard_NoSuchObject("NCollection_OrderedDataMap::Last");
0785     return myLast->Key();
0786   }
0787 
0788   //! Returns the first value in insertion order.
0789   //! @throws Standard_NoSuchObject if map is empty
0790   const TheItemType& FirstValue() const
0791   {
0792     if (IsEmpty())
0793       throw Standard_NoSuchObject("NCollection_OrderedDataMap::FirstValue");
0794     return myFirst->Value();
0795   }
0796 
0797   //! Returns the last value in insertion order.
0798   //! @throws Standard_NoSuchObject if map is empty
0799   const TheItemType& LastValue() const
0800   {
0801     if (IsEmpty())
0802       throw Standard_NoSuchObject("NCollection_OrderedDataMap::LastValue");
0803     return myLast->Value();
0804   }
0805 
0806   //! Returns modifiable first value in insertion order.
0807   //! @throws Standard_NoSuchObject if map is empty
0808   TheItemType& ChangeFirstValue()
0809   {
0810     if (IsEmpty())
0811       throw Standard_NoSuchObject("NCollection_OrderedDataMap::ChangeFirstValue");
0812     return myFirst->ChangeValue();
0813   }
0814 
0815   //! Returns modifiable last value in insertion order.
0816   //! @throws Standard_NoSuchObject if map is empty
0817   TheItemType& ChangeLastValue()
0818   {
0819     if (IsEmpty())
0820       throw Standard_NoSuchObject("NCollection_OrderedDataMap::ChangeLastValue");
0821     return myLast->ChangeValue();
0822   }
0823 
0824 protected:
0825   //! Lookup for particular key in map.
0826   //! @param[in] theKey key to compute hash
0827   //! @param[out] theNode the detected node with equal key. Can be null.
0828   //! @return true if key is found
0829   bool lookup(const TheKeyType& theKey, OrderedDataMapNode*& theNode) const
0830   {
0831     if (IsEmpty())
0832       return false;
0833     for (theNode = (OrderedDataMapNode*)myData1[HashCode(theKey, NbBuckets())]; theNode;
0834          theNode = (OrderedDataMapNode*)theNode->Next())
0835     {
0836       if (IsEqual(theNode->Key(), theKey))
0837         return true;
0838     }
0839     return false;
0840   }
0841 
0842   //! Lookup for particular key in map.
0843   //! @param[in] theKey key to compute hash
0844   //! @param[out] theNode the detected node with equal key. Can be null.
0845   //! @param[out] theHash computed bounded hash code for current key.
0846   //! @return true if key is found
0847   bool lookup(const TheKeyType& theKey, OrderedDataMapNode*& theNode, size_t& theHash) const
0848   {
0849     theHash = HashCode(theKey, NbBuckets());
0850     if (IsEmpty())
0851       return false;
0852     for (theNode = (OrderedDataMapNode*)myData1[theHash]; theNode;
0853          theNode = (OrderedDataMapNode*)theNode->Next())
0854     {
0855       if (IsEqual(theNode->Key(), theKey))
0856       {
0857         return true;
0858       }
0859     }
0860     return false;
0861   }
0862 
0863   bool IsEqual(const TheKeyType& theKey1, const TheKeyType& theKey2) const
0864   {
0865     return myHasher(theKey1, theKey2);
0866   }
0867 
0868   size_t HashCode(const TheKeyType& theKey, const size_t theUpperBound) const
0869   {
0870     return myHasher(theKey) % theUpperBound + 1;
0871   }
0872 
0873   //! Append a node to the tail of the insertion-order linked list.
0874   void appendToList(OrderedDataMapNode* theNode)
0875   {
0876     theNode->myOrderPrev = myLast;
0877     theNode->myOrderNext = nullptr;
0878     if (myLast)
0879       myLast->myOrderNext = theNode;
0880     else
0881       myFirst = theNode;
0882     myLast = theNode;
0883   }
0884 
0885   //! Unlink a node from the insertion-order linked list.
0886   void unlinkFromList(OrderedDataMapNode* theNode)
0887   {
0888     OrderedDataMapNode* aPrev = theNode->myOrderPrev;
0889     OrderedDataMapNode* aNext = theNode->myOrderNext;
0890     if (aPrev)
0891       aPrev->myOrderNext = aNext;
0892     else
0893       myFirst = aNext;
0894     if (aNext)
0895       aNext->myOrderPrev = aPrev;
0896     else
0897       myLast = aPrev;
0898   }
0899 
0900   //! Implementation helper for Bind/TryBind/Bound/TryBound/Emplace/TryEmplace/Emplaced/TryEmplaced.
0901   //! @tparam K forwarding reference type for key
0902   //! @tparam IsTry if true, does not overwrite existing; if false, destroys and reconstructs
0903   //! @tparam ReturnRef if true, returns reference; if false, returns bool
0904   //! @param theKey  key to add/update
0905   //! @param theArgs arguments forwarded to value constructor
0906   //! @return bool or TheItemType& depending on ReturnRef
0907   template <typename K, bool IsTry, bool ReturnRef, typename... Args>
0908   auto emplaceImpl(K&& theKey,
0909                    std::bool_constant<IsTry>,
0910                    std::bool_constant<ReturnRef>,
0911                    Args&&... theArgs) -> std::conditional_t<ReturnRef, TheItemType&, bool>
0912   {
0913     if (Resizable())
0914       ReSize(Extent());
0915     size_t              aHash;
0916     OrderedDataMapNode* aNode;
0917     if (lookup(theKey, aNode, aHash))
0918     {
0919       if constexpr (!IsTry)
0920       {
0921         aNode->ChangeValue() = TheItemType(std::forward<Args>(theArgs)...);
0922       }
0923       if constexpr (ReturnRef)
0924         return aNode->ChangeValue();
0925       else
0926         return false;
0927     }
0928     OrderedDataMapNode** data = (OrderedDataMapNode**)myData1;
0929     data[aHash]               = new (this->myAllocator) OrderedDataMapNode(std::forward<K>(theKey),
0930                                                              std::in_place,
0931                                                              data[aHash],
0932                                                              std::forward<Args>(theArgs)...);
0933     appendToList(data[aHash]);
0934     Increment();
0935     if constexpr (ReturnRef)
0936       return data[aHash]->ChangeValue();
0937     else
0938       return true;
0939   }
0940 
0941 private:
0942   Hasher              myHasher;
0943   OrderedDataMapNode* myFirst; //!< Head of insertion-order linked list
0944   OrderedDataMapNode* myLast;  //!< Tail of insertion-order linked list
0945 };
0946 
0947 #endif