Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-28 09:20:58

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_FlatDataMap_HeaderFile
0015 #define NCollection_FlatDataMap_HeaderFile
0016 
0017 #include <Standard.hxx>
0018 #include <Standard_OutOfRange.hxx>
0019 #include <Standard_NoSuchObject.hxx>
0020 #include <NCollection_DefaultHasher.hxx>
0021 #include <NCollection_ItemsView.hxx>
0022 
0023 #include <functional>
0024 #include <new>
0025 #include <optional>
0026 #include <type_traits>
0027 #include <utility>
0028 
0029 /**
0030  * @brief High-performance hash map using open addressing with Robin Hood hashing.
0031  *
0032  * NCollection_FlatDataMap is an alternative to NCollection_DataMap that provides
0033  * better cache locality and reduced memory allocation overhead by storing all
0034  * key-value pairs inline in a contiguous array.
0035  *
0036  * Key features:
0037  * - Open addressing with linear probing (better cache locality)
0038  * - Robin Hood hashing (reduces probe sequence variance)
0039  * - Power-of-2 sizing for fast modulo operations
0040  * - No per-element allocations
0041  *
0042  * Typical faster usage patterns:
0043  * - POD or small key/value types
0044  * - Performance-critical code paths
0045  * - Lookup-heavy workloads
0046  * - Full traversal / iteration-heavy workloads
0047  * - Stable-size maps with Reserve() called once before bulk Bind()
0048  *
0049  * Container-specific implementation notes:
0050  * - UnBind() keeps probe clusters consistent using backward-shift compaction.
0051  *
0052  * Relative to NCollection_DataMap:
0053  * - Bind()/UnBind() can be faster in many workloads thanks to contiguous storage and
0054  *   no per-element node allocation.
0055  * - Iteration is often faster due to contiguous slot scanning and reduced pointer chasing.
0056  *
0057  * Limitations:
0058  * - Keys and values must be movable
0059  * - Higher memory usage at low load factors
0060  * - Iteration order is not insertion order
0061  * - Probe distance grows with collisions (bounded by table capacity)
0062  *
0063  * @note This class is NOT thread-safe. External synchronization is required
0064  *       for concurrent access from multiple threads.
0065  *
0066  * @tparam TheKeyType   Type of keys
0067  * @tparam TheItemType  Type of values
0068  * @tparam Hasher       Hash and equality functor (default: NCollection_DefaultHasher)
0069  */
0070 template <class TheKeyType, class TheItemType, class Hasher = NCollection_DefaultHasher<TheKeyType>>
0071 class NCollection_FlatDataMap
0072 {
0073 public:
0074   //! STL-compliant type alias for key type
0075   using key_type = TheKeyType;
0076 
0077   //! STL-compliant type alias for value type
0078   using value_type = TheItemType;
0079 
0080 private:
0081   //! Default initial capacity (must be power of 2)
0082   static constexpr size_t THE_DEFAULT_CAPACITY = 8;
0083   //! Maximum load factor numerator (13/16 = 81.25%).
0084   static constexpr size_t THE_MAX_LOAD_NUMERATOR = 13;
0085   //! Maximum load factor denominator.
0086   static constexpr size_t THE_MAX_LOAD_DENOMINATOR = 16;
0087 
0088   //! Internal slot structure holding key, value, and metadata.
0089   //! Key and item storage is uninitialized until state becomes Used.
0090 #ifdef _MSC_VER
0091   #pragma warning(push)
0092   #pragma warning(disable : 4324) // structure was padded due to alignment specifier
0093 #endif
0094   struct Slot
0095   {
0096     size_t myHash; //!< Cached hash code
0097     //! Distance from ideal bucket plus one; 0 means Empty, otherwise Used.
0098     size_t myProbeDistancePlus1;
0099     alignas(TheKeyType) char myKeyStorage[sizeof(TheKeyType)];
0100     alignas(TheItemType) char myItemStorage[sizeof(TheItemType)];
0101 
0102     Slot() noexcept
0103         : myHash(0),
0104           myProbeDistancePlus1(0)
0105     {
0106     }
0107 
0108     TheKeyType& Key() noexcept { return *reinterpret_cast<TheKeyType*>(myKeyStorage); }
0109 
0110     const TheKeyType& Key() const noexcept
0111     {
0112       return *reinterpret_cast<const TheKeyType*>(myKeyStorage);
0113     }
0114 
0115     TheItemType& Item() noexcept { return *reinterpret_cast<TheItemType*>(myItemStorage); }
0116 
0117     const TheItemType& Item() const noexcept
0118     {
0119       return *reinterpret_cast<const TheItemType*>(myItemStorage);
0120     }
0121 
0122     bool IsEmpty() const noexcept { return myProbeDistancePlus1 == 0; }
0123 
0124     bool IsUsed() const noexcept { return myProbeDistancePlus1 != 0; }
0125 
0126     size_t ProbeDistance() const noexcept { return myProbeDistancePlus1 - 1; }
0127 
0128     void SetProbeDistance(const size_t theProbeDistance) noexcept
0129     {
0130       myProbeDistancePlus1 = theProbeDistance + 1;
0131     }
0132 
0133     void SetEmpty() noexcept { myProbeDistancePlus1 = 0; }
0134   };
0135 #ifdef _MSC_VER
0136   #pragma warning(pop)
0137 #endif
0138 
0139 public:
0140   // **************** Iterator interface ****************
0141 
0142   //! Forward iterator for NCollection_FlatDataMap
0143   class Iterator
0144   {
0145   public:
0146     //! Empty constructor
0147     Iterator() noexcept
0148         : mySlots(nullptr),
0149           myCapacity(0),
0150           myIndex(0)
0151     {
0152     }
0153 
0154     //! Constructor from map
0155     Iterator(const NCollection_FlatDataMap& theMap) noexcept
0156         : mySlots(theMap.mySlots),
0157           myCapacity(theMap.myCapacity),
0158           myIndex(0)
0159     {
0160       // Find first used slot
0161       while (myIndex < myCapacity && !mySlots[myIndex].IsUsed())
0162       {
0163         ++myIndex;
0164       }
0165     }
0166 
0167     //! Check if there are more elements
0168     bool More() const noexcept { return myIndex < myCapacity; }
0169 
0170     //! Move to next element
0171     void Next() noexcept
0172     {
0173       ++myIndex;
0174       while (myIndex < myCapacity && !mySlots[myIndex].IsUsed())
0175       {
0176         ++myIndex;
0177       }
0178     }
0179 
0180     //! Get current key
0181     const TheKeyType& Key() const
0182     {
0183       Standard_OutOfRange_Raise_if(!More(), "NCollection_FlatDataMap::Iterator::Key");
0184       return mySlots[myIndex].Key();
0185     }
0186 
0187     //! Get current value (const)
0188     const TheItemType& Value() const
0189     {
0190       Standard_OutOfRange_Raise_if(!More(), "NCollection_FlatDataMap::Iterator::Value");
0191       return mySlots[myIndex].Item();
0192     }
0193 
0194     //! Get current value (mutable)
0195     TheItemType& ChangeValue() const
0196     {
0197       Standard_OutOfRange_Raise_if(!More(), "NCollection_FlatDataMap::Iterator::ChangeValue");
0198       return const_cast<Slot*>(mySlots)[myIndex].Item();
0199     }
0200 
0201     //! Performs comparison of two iterators.
0202     bool IsEqual(const Iterator& theOther) const noexcept
0203     {
0204       return mySlots == theOther.mySlots && myIndex == theOther.myIndex;
0205     }
0206 
0207   private:
0208     const Slot* mySlots;
0209     size_t      myCapacity;
0210     size_t      myIndex;
0211   };
0212 
0213 public:
0214   // **************** Constructors and destructor ****************
0215 
0216   //! Default constructor
0217   NCollection_FlatDataMap()
0218       : mySlots(nullptr),
0219         myCapacity(0),
0220         mySize(0)
0221   {
0222   }
0223 
0224   //! Constructor with initial capacity hint
0225   //! @param theNbBuckets initial capacity (will be rounded up to power of 2)
0226   explicit NCollection_FlatDataMap(const size_t theNbBuckets)
0227       : mySlots(nullptr),
0228         myCapacity(0),
0229         mySize(0)
0230   {
0231     if (theNbBuckets > 0)
0232     {
0233       reserve(static_cast<size_t>(theNbBuckets));
0234     }
0235   }
0236 
0237   //! Constructor with custom hasher (copy).
0238   //! @param theHasher custom hasher instance
0239   //! @param theNbBuckets initial capacity hint
0240   explicit NCollection_FlatDataMap(const Hasher& theHasher, const size_t theNbBuckets = 0)
0241       : mySlots(nullptr),
0242         myCapacity(0),
0243         mySize(0),
0244         myHasher(theHasher)
0245   {
0246     if (theNbBuckets > 0)
0247     {
0248       reserve(static_cast<size_t>(theNbBuckets));
0249     }
0250   }
0251 
0252   //! Constructor with custom hasher (move).
0253   //! @param theHasher custom hasher instance (moved)
0254   //! @param theNbBuckets initial capacity hint
0255   explicit NCollection_FlatDataMap(Hasher&& theHasher, const size_t theNbBuckets = 0)
0256       : mySlots(nullptr),
0257         myCapacity(0),
0258         mySize(0),
0259         myHasher(std::move(theHasher))
0260   {
0261     if (theNbBuckets > 0)
0262     {
0263       reserve(static_cast<size_t>(theNbBuckets));
0264     }
0265   }
0266 
0267   //! Copy constructor
0268   NCollection_FlatDataMap(const NCollection_FlatDataMap& theOther)
0269       : mySlots(nullptr),
0270         myCapacity(0),
0271         mySize(0),
0272         myHasher(theOther.myHasher)
0273   {
0274     if (theOther.mySize > 0)
0275     {
0276       // Allocate same capacity as the source (not through reserve which may change capacity)
0277       mySlots = static_cast<Slot*>(Standard::Allocate(theOther.myCapacity * sizeof(Slot)));
0278       for (size_t i = 0; i < theOther.myCapacity; ++i)
0279       {
0280         new (&mySlots[i]) Slot();
0281       }
0282       myCapacity = theOther.myCapacity;
0283 
0284       for (size_t i = 0; i < theOther.myCapacity; ++i)
0285       {
0286         if (theOther.mySlots[i].IsUsed())
0287         {
0288           new (&mySlots[i].Key()) TheKeyType(theOther.mySlots[i].Key());
0289           new (&mySlots[i].Item()) TheItemType(theOther.mySlots[i].Item());
0290           mySlots[i].myHash               = theOther.mySlots[i].myHash;
0291           mySlots[i].myProbeDistancePlus1 = theOther.mySlots[i].myProbeDistancePlus1;
0292         }
0293       }
0294       mySize = theOther.mySize;
0295     }
0296   }
0297 
0298   //! Move constructor
0299   NCollection_FlatDataMap(NCollection_FlatDataMap&& theOther) noexcept
0300       : mySlots(theOther.mySlots),
0301         myCapacity(theOther.myCapacity),
0302         mySize(theOther.mySize),
0303         myHasher(std::move(theOther.myHasher))
0304   {
0305     theOther.mySlots    = nullptr;
0306     theOther.myCapacity = 0;
0307     theOther.mySize     = 0;
0308   }
0309 
0310   //! Destructor
0311   ~NCollection_FlatDataMap() { Clear(true); }
0312 
0313   //! Copy assignment
0314   NCollection_FlatDataMap& operator=(const NCollection_FlatDataMap& theOther)
0315   {
0316     if (this != &theOther)
0317     {
0318       Clear(true);
0319       myHasher = theOther.myHasher;
0320       if (theOther.mySize > 0)
0321       {
0322         // Allocate same capacity as the source (not through reserve which may change capacity)
0323         mySlots = static_cast<Slot*>(Standard::Allocate(theOther.myCapacity * sizeof(Slot)));
0324         for (size_t i = 0; i < theOther.myCapacity; ++i)
0325         {
0326           new (&mySlots[i]) Slot();
0327         }
0328         myCapacity = theOther.myCapacity;
0329 
0330         for (size_t i = 0; i < theOther.myCapacity; ++i)
0331         {
0332           if (theOther.mySlots[i].IsUsed())
0333           {
0334             new (&mySlots[i].Key()) TheKeyType(theOther.mySlots[i].Key());
0335             new (&mySlots[i].Item()) TheItemType(theOther.mySlots[i].Item());
0336             mySlots[i].myHash               = theOther.mySlots[i].myHash;
0337             mySlots[i].myProbeDistancePlus1 = theOther.mySlots[i].myProbeDistancePlus1;
0338           }
0339         }
0340         mySize = theOther.mySize;
0341       }
0342     }
0343     return *this;
0344   }
0345 
0346   //! Move assignment
0347   NCollection_FlatDataMap& operator=(NCollection_FlatDataMap&& theOther) noexcept
0348   {
0349     if (this != &theOther)
0350     {
0351       Clear(true);
0352       mySlots             = theOther.mySlots;
0353       myCapacity          = theOther.myCapacity;
0354       mySize              = theOther.mySize;
0355       myHasher            = std::move(theOther.myHasher);
0356       theOther.mySlots    = nullptr;
0357       theOther.myCapacity = 0;
0358       theOther.mySize     = 0;
0359     }
0360     return *this;
0361   }
0362 
0363 public:
0364   // **************** Query methods ****************
0365 
0366   //! Returns number of elements.
0367   size_t Size() const noexcept { return mySize; }
0368 
0369   //! Returns number of elements (legacy int-returning API, convention shared with BaseMap).
0370   int Extent() const noexcept { return static_cast<int>(mySize); }
0371 
0372   //! Returns true if map is empty
0373   bool IsEmpty() const noexcept { return mySize == 0; }
0374 
0375   //! Returns current capacity
0376   size_t Capacity() const noexcept { return myCapacity; }
0377 
0378   //! Check if key exists
0379   bool IsBound(const TheKeyType& theKey) const
0380   {
0381     if (mySize == 0)
0382       return false;
0383     size_t anIndex = 0;
0384     return findSlotIndex(theKey, anIndex);
0385   }
0386 
0387   //! Contained returns optional pair of const references to key and value.
0388   //! Returns std::nullopt if the key is not found.
0389   std::optional<
0390     std::pair<std::reference_wrapper<const TheKeyType>, std::reference_wrapper<const TheItemType>>>
0391     Contained(const TheKeyType& theKey) const
0392   {
0393     if (mySize == 0)
0394       return std::nullopt;
0395     size_t aIdx = 0;
0396     if (!findSlotIndex(theKey, aIdx))
0397       return std::nullopt;
0398     return std::make_pair(std::cref(mySlots[aIdx].Key()), std::cref(mySlots[aIdx].Item()));
0399   }
0400 
0401   //! Contained returns optional pair of const key reference and mutable value reference.
0402   //! Returns std::nullopt if the key is not found.
0403   std::optional<
0404     std::pair<std::reference_wrapper<const TheKeyType>, std::reference_wrapper<TheItemType>>>
0405     Contained(const TheKeyType& theKey)
0406   {
0407     if (mySize == 0)
0408       return std::nullopt;
0409     size_t aIdx = 0;
0410     if (!findSlotIndex(theKey, aIdx))
0411       return std::nullopt;
0412     return std::make_pair(std::cref(mySlots[aIdx].Key()), std::ref(mySlots[aIdx].Item()));
0413   }
0414 
0415   //! Find value by key, returns nullptr if not found
0416   const TheItemType* Seek(const TheKeyType& theKey) const
0417   {
0418     if (mySize == 0)
0419       return nullptr;
0420     size_t aFoundIndex = 0;
0421     if (findSlotIndex(theKey, aFoundIndex))
0422     {
0423       return &mySlots[aFoundIndex].Item();
0424     }
0425     return nullptr;
0426   }
0427 
0428   //! Find value by key (mutable), returns nullptr if not found
0429   TheItemType* ChangeSeek(const TheKeyType& theKey)
0430   {
0431     if (mySize == 0)
0432       return nullptr;
0433     size_t aFoundIndex = 0;
0434     if (findSlotIndex(theKey, aFoundIndex))
0435     {
0436       return &mySlots[aFoundIndex].Item();
0437     }
0438     return nullptr;
0439   }
0440 
0441   //! Find value by key, throws if not found
0442   const TheItemType& Find(const TheKeyType& theKey) const
0443   {
0444     const TheItemType* aPtr = Seek(theKey);
0445     if (aPtr == nullptr)
0446     {
0447       throw Standard_NoSuchObject("NCollection_FlatDataMap::Find");
0448     }
0449     return *aPtr;
0450   }
0451 
0452   //! Find value by key (mutable), throws if not found
0453   TheItemType& ChangeFind(const TheKeyType& theKey)
0454   {
0455     TheItemType* aPtr = ChangeSeek(theKey);
0456     if (aPtr == nullptr)
0457     {
0458       throw Standard_NoSuchObject("NCollection_FlatDataMap::ChangeFind");
0459     }
0460     return *aPtr;
0461   }
0462 
0463   //! Operator() for const access
0464   const TheItemType& operator()(const TheKeyType& theKey) const { return Find(theKey); }
0465 
0466   //! Operator() for mutable access
0467   TheItemType& operator()(const TheKeyType& theKey) { return ChangeFind(theKey); }
0468 
0469 public:
0470   // **************** Modification methods ****************
0471 
0472   //! Bind key to value
0473   //! @return true if key was newly added, false if existing key was updated
0474   bool Bind(const TheKeyType& theKey, const TheItemType& theItem)
0475   {
0476     ensureCapacity();
0477     return insertImpl(theKey, theItem);
0478   }
0479 
0480   //! Bind key to value (move semantics for value)
0481   bool Bind(const TheKeyType& theKey, TheItemType&& theItem)
0482   {
0483     ensureCapacity();
0484     return insertImpl(theKey, std::forward<TheItemType>(theItem));
0485   }
0486 
0487   //! Bind key to value (move semantics for key)
0488   bool Bind(TheKeyType&& theKey, const TheItemType& theItem)
0489   {
0490     ensureCapacity();
0491     return insertImpl(std::forward<TheKeyType>(theKey), theItem);
0492   }
0493 
0494   //! Bind key to value (move semantics for both)
0495   bool Bind(TheKeyType&& theKey, TheItemType&& theItem)
0496   {
0497     ensureCapacity();
0498     return insertImpl(std::forward<TheKeyType>(theKey), std::forward<TheItemType>(theItem));
0499   }
0500 
0501   //! TryBind binds key to value only if key is not yet bound.
0502   //! @param theKey key to add
0503   //! @param theItem item to bind if key is not yet bound
0504   //! @return true if key was newly added, false if key already existed
0505   bool TryBind(const TheKeyType& theKey, const TheItemType& theItem)
0506   {
0507     ensureCapacity();
0508     return tryInsertImpl(theKey, theItem);
0509   }
0510 
0511   //! TryBind binds key to value only if key is not yet bound.
0512   bool TryBind(const TheKeyType& theKey, TheItemType&& theItem)
0513   {
0514     ensureCapacity();
0515     return tryInsertImpl(theKey, std::move(theItem));
0516   }
0517 
0518   //! TryBind binds key to value only if key is not yet bound.
0519   bool TryBind(TheKeyType&& theKey, const TheItemType& theItem)
0520   {
0521     ensureCapacity();
0522     return tryInsertImpl(std::move(theKey), theItem);
0523   }
0524 
0525   //! TryBind binds key to value only if key is not yet bound.
0526   bool TryBind(TheKeyType&& theKey, TheItemType&& theItem)
0527   {
0528     ensureCapacity();
0529     return tryInsertImpl(std::move(theKey), std::move(theItem));
0530   }
0531 
0532   //! Bound binds key to value and returns reference to the value.
0533   //! @param theKey key to add/update
0534   //! @param theItem new item; overrides value previously bound to the key
0535   //! @return reference to the value in the map
0536   TheItemType& Bound(const TheKeyType& theKey, const TheItemType& theItem)
0537   {
0538     ensureCapacity();
0539     return insertRefImpl(theKey, theItem, std::false_type{});
0540   }
0541 
0542   //! Bound binds key to value and returns reference to the value.
0543   TheItemType& Bound(const TheKeyType& theKey, TheItemType&& theItem)
0544   {
0545     ensureCapacity();
0546     return insertRefImpl(theKey, std::move(theItem), std::false_type{});
0547   }
0548 
0549   //! Bound binds key to value and returns reference to the value.
0550   TheItemType& Bound(TheKeyType&& theKey, const TheItemType& theItem)
0551   {
0552     ensureCapacity();
0553     return insertRefImpl(std::move(theKey), theItem, std::false_type{});
0554   }
0555 
0556   //! Bound binds key to value and returns reference to the value.
0557   TheItemType& Bound(TheKeyType&& theKey, TheItemType&& theItem)
0558   {
0559     ensureCapacity();
0560     return insertRefImpl(std::move(theKey), std::move(theItem), std::false_type{});
0561   }
0562 
0563   //! TryBound binds key to value only if key is not yet bound.
0564   //! @param theKey key to add
0565   //! @param theItem item to bind if key is not yet bound
0566   //! @return reference to existing or newly bound value
0567   TheItemType& TryBound(const TheKeyType& theKey, const TheItemType& theItem)
0568   {
0569     ensureCapacity();
0570     return insertRefImpl(theKey, theItem, std::true_type{});
0571   }
0572 
0573   //! TryBound binds key to value only if key is not yet bound.
0574   TheItemType& TryBound(const TheKeyType& theKey, TheItemType&& theItem)
0575   {
0576     ensureCapacity();
0577     return insertRefImpl(theKey, std::move(theItem), std::true_type{});
0578   }
0579 
0580   //! TryBound binds key to value only if key is not yet bound.
0581   TheItemType& TryBound(TheKeyType&& theKey, const TheItemType& theItem)
0582   {
0583     ensureCapacity();
0584     return insertRefImpl(std::move(theKey), theItem, std::true_type{});
0585   }
0586 
0587   //! TryBound binds key to value only if key is not yet bound.
0588   TheItemType& TryBound(TheKeyType&& theKey, TheItemType&& theItem)
0589   {
0590     ensureCapacity();
0591     return insertRefImpl(std::move(theKey), std::move(theItem), std::true_type{});
0592   }
0593 
0594   //! Emplace constructs value in-place; if key exists, updates the value.
0595   //! @param theKey key to add/update
0596   //! @param theArgs arguments forwarded to value constructor
0597   //! @return true if key was newly added, false if existing key was updated
0598   template <typename K, typename... Args>
0599   bool Emplace(K&& theKey, Args&&... theArgs)
0600   {
0601     ensureCapacity();
0602     return emplaceImpl(std::forward<K>(theKey), std::false_type{}, std::forward<Args>(theArgs)...);
0603   }
0604 
0605   //! Emplaced constructs value in-place; if key exists, updates the value.
0606   //! @param theKey key to add/update
0607   //! @param theArgs arguments forwarded to value constructor
0608   //! @return reference to the value (existing updated or newly added)
0609   template <typename K, typename... Args>
0610   TheItemType& Emplaced(K&& theKey, Args&&... theArgs)
0611   {
0612     ensureCapacity();
0613     return emplacedImpl(std::forward<K>(theKey), std::false_type{}, std::forward<Args>(theArgs)...);
0614   }
0615 
0616   //! TryEmplace constructs value in-place only if key not already bound.
0617   //! @param theKey key to add
0618   //! @param theArgs arguments forwarded to value constructor
0619   //! @return true if key was newly added, false if key already existed
0620   template <typename K, typename... Args>
0621   bool TryEmplace(K&& theKey, Args&&... theArgs)
0622   {
0623     ensureCapacity();
0624     return emplaceImpl(std::forward<K>(theKey), std::true_type{}, std::forward<Args>(theArgs)...);
0625   }
0626 
0627   //! TryEmplaced constructs value in-place only if key not already bound.
0628   //! @param theKey key to add
0629   //! @param theArgs arguments forwarded to value constructor
0630   //! @return reference to the value (existing or newly added)
0631   template <typename K, typename... Args>
0632   TheItemType& TryEmplaced(K&& theKey, Args&&... theArgs)
0633   {
0634     ensureCapacity();
0635     return emplacedImpl(std::forward<K>(theKey), std::true_type{}, std::forward<Args>(theArgs)...);
0636   }
0637 
0638   //! Remove key from map
0639   //! @return true if key was found and removed
0640   bool UnBind(const TheKeyType& theKey)
0641   {
0642     if (mySize == 0)
0643       return false;
0644 
0645     size_t aFoundIndex = 0;
0646     if (!findSlotIndex(theKey, aFoundIndex))
0647     {
0648       return false;
0649     }
0650 
0651     const size_t aIndex = aFoundIndex;
0652 
0653     mySlots[aIndex].Key().~TheKeyType();
0654     mySlots[aIndex].Item().~TheItemType();
0655     mySlots[aIndex].SetEmpty();
0656     --mySize;
0657 
0658     backwardShiftDelete(aIndex);
0659 
0660     return true;
0661   }
0662 
0663   //! Clear all elements
0664   //! @param doReleaseMemory if true, free the internal buffer
0665   void Clear(bool doReleaseMemory = false)
0666   {
0667     if (mySlots != nullptr)
0668     {
0669       for (size_t i = 0; i < myCapacity; ++i)
0670       {
0671         if (mySlots[i].IsUsed())
0672         {
0673           mySlots[i].Key().~TheKeyType();
0674           mySlots[i].Item().~TheItemType();
0675           mySlots[i].SetEmpty();
0676         }
0677       }
0678       mySize = 0;
0679 
0680       if (doReleaseMemory)
0681       {
0682         Standard::Free(mySlots);
0683         mySlots    = nullptr;
0684         myCapacity = 0;
0685       }
0686     }
0687   }
0688 
0689   //! Exchange content with another map
0690   void Exchange(NCollection_FlatDataMap& theOther) noexcept
0691   {
0692     std::swap(mySlots, theOther.mySlots);
0693     std::swap(myCapacity, theOther.myCapacity);
0694     std::swap(mySize, theOther.mySize);
0695     std::swap(myHasher, theOther.myHasher);
0696   }
0697 
0698   //! Returns const reference to the hasher.
0699   const Hasher& GetHasher() const noexcept { return myHasher; }
0700 
0701   //! Reserve capacity for at least theN elements
0702   void reserve(size_t theN)
0703   {
0704     const size_t aMinCapacity =
0705       (theN * THE_MAX_LOAD_DENOMINATOR + THE_MAX_LOAD_NUMERATOR - 1) / THE_MAX_LOAD_NUMERATOR;
0706     size_t aNewCapacity = nextPowerOf2(aMinCapacity);
0707     if (aNewCapacity > myCapacity)
0708     {
0709       rehash(aNewCapacity);
0710     }
0711   }
0712 
0713   //! Reserve capacity for at least theN elements
0714   void Reserve(const size_t theN) { reserve(theN); }
0715 
0716 public:
0717   // **************** Iterator access ****************
0718 
0719   //! Returns iterator to first element
0720   Iterator begin() const noexcept { return Iterator(*this); }
0721 
0722   //! Returns iterator past the end
0723   Iterator end() const noexcept { return Iterator(); }
0724 
0725   //! Returns iterator to first element
0726   Iterator cbegin() const noexcept { return Iterator(*this); }
0727 
0728   //! Returns iterator past the end
0729   Iterator cend() const noexcept { return Iterator(); }
0730 
0731 public:
0732   // **************** Key-value pair iteration support for structured bindings
0733 
0734   //! Key-value pair reference for structured binding support.
0735   //! Enables: for (auto [key, value] : map.Items())
0736   using KeyValueRef = NCollection_ItemsView::KeyValueRef<TheKeyType, TheItemType, false>;
0737 
0738   //! Const key-value pair reference for structured binding support.
0739   using ConstKeyValueRef = NCollection_ItemsView::KeyValueRef<TheKeyType, TheItemType, true>;
0740 
0741 private:
0742   //! Extractor for mutable key-value pairs
0743   struct ItemsExtractor
0744   {
0745     static KeyValueRef Extract(const Iterator& theIter)
0746     {
0747       return {theIter.Key(), theIter.ChangeValue()};
0748     }
0749   };
0750 
0751   //! Extractor for const key-value pairs
0752   struct ConstItemsExtractor
0753   {
0754     static ConstKeyValueRef Extract(const Iterator& theIter)
0755     {
0756       return {theIter.Key(), theIter.Value()};
0757     }
0758   };
0759 
0760 public:
0761   //! View class for key-value pair iteration (mutable).
0762   using ItemsView =
0763     NCollection_ItemsView::View<NCollection_FlatDataMap, KeyValueRef, ItemsExtractor, false>;
0764 
0765   //! View class for key-value pair iteration (const).
0766   using ConstItemsView = NCollection_ItemsView::
0767     View<NCollection_FlatDataMap, ConstKeyValueRef, ConstItemsExtractor, true>;
0768 
0769   //! Returns a view for key-value pair iteration.
0770   //! Usage: for (auto [aKey, aValue] : aMap.Items())
0771   ItemsView Items() { return ItemsView(*this); }
0772 
0773   //! Returns a const view for key-value pair iteration.
0774   //! Usage: for (const auto& [aKey, aValue] : aMap.Items())
0775   ConstItemsView Items() const { return ConstItemsView(*this); }
0776 
0777 private:
0778   // **************** Internal implementation ****************
0779 
0780   //! Get next power of 2 >= n
0781   static size_t nextPowerOf2(size_t n) noexcept
0782   {
0783     if (n == 0)
0784       return THE_DEFAULT_CAPACITY;
0785     --n;
0786     n |= n >> 1;
0787     n |= n >> 2;
0788     n |= n >> 4;
0789     n |= n >> 8;
0790     n |= n >> 16;
0791     if constexpr (sizeof(size_t) > 4)
0792     {
0793       n |= n >> 32;
0794     }
0795     return n + 1;
0796   }
0797 
0798   //! Ensure there's room for at least one more element
0799   void ensureCapacity()
0800   {
0801     // Grow at ~81.25% load factor.
0802     if (myCapacity == 0
0803         || (mySize + 1) * THE_MAX_LOAD_DENOMINATOR > myCapacity * THE_MAX_LOAD_NUMERATOR)
0804     {
0805       size_t aNewCapacity = myCapacity == 0 ? THE_DEFAULT_CAPACITY : myCapacity * 2;
0806       rehash(aNewCapacity);
0807     }
0808   }
0809 
0810   //! Rehash to new capacity
0811   void rehash(size_t theNewCapacity)
0812   {
0813     Slot*  aOldSlots    = mySlots;
0814     size_t aOldCapacity = myCapacity;
0815 
0816     // Allocate new buffer
0817     mySlots = static_cast<Slot*>(Standard::Allocate(theNewCapacity * sizeof(Slot)));
0818     for (size_t i = 0; i < theNewCapacity; ++i)
0819     {
0820       new (&mySlots[i]) Slot();
0821     }
0822     myCapacity = theNewCapacity;
0823     mySize     = 0;
0824 
0825     if (aOldSlots != nullptr)
0826     {
0827       for (size_t i = 0; i < aOldCapacity; ++i)
0828       {
0829         if (aOldSlots[i].IsUsed())
0830         {
0831           insertRehashedImpl(std::move(aOldSlots[i].Key()),
0832                              std::move(aOldSlots[i].Item()),
0833                              aOldSlots[i].myHash);
0834           aOldSlots[i].Key().~TheKeyType();
0835           aOldSlots[i].Item().~TheItemType();
0836         }
0837       }
0838       Standard::Free(aOldSlots);
0839     }
0840   }
0841 
0842   //! Find slot containing key.
0843   //! @param theKey key to find
0844   //! @param[out] theIndex found index
0845   //! @return true if key was found
0846   bool findSlotIndex(const TheKeyType& theKey, size_t& theIndex) const
0847   {
0848     const size_t aHash  = myHasher(theKey);
0849     const size_t aMask  = myCapacity - 1;
0850     size_t       aIndex = aHash & aMask;
0851 
0852     while (true)
0853     {
0854       const Slot& aSlot = mySlots[aIndex];
0855 
0856       if (aSlot.IsEmpty())
0857       {
0858         return false;
0859       }
0860 
0861       if (aSlot.myHash == aHash && myHasher(aSlot.Key(), theKey))
0862       {
0863         theIndex = aIndex;
0864         return true;
0865       }
0866       aIndex = (aIndex + 1) & aMask;
0867     }
0868   }
0869 
0870   template <typename K, typename V, bool CheckExisting, bool UpdateExisting>
0871   bool insertRehashedImpl(K&&          theKey,
0872                           V&&          theItem,
0873                           const size_t theHash,
0874                           std::bool_constant<CheckExisting>,
0875                           std::bool_constant<UpdateExisting>,
0876                           size_t* theInsertedIndex = nullptr)
0877   {
0878     const size_t aMask             = myCapacity - 1;
0879     size_t       aIndex            = theHash & aMask;
0880     size_t       aProbe            = 0;
0881     size_t       anInsertedIndex   = 0;
0882     bool         aHasInsertedIndex = false;
0883 
0884     TheKeyType  aKeyToInsert  = std::forward<K>(theKey);
0885     TheItemType aItemToInsert = std::forward<V>(theItem);
0886     size_t      aHashToInsert = theHash;
0887 
0888     while (true)
0889     {
0890       Slot& aSlot = mySlots[aIndex];
0891       if (aSlot.IsEmpty())
0892       {
0893         new (&aSlot.Key()) TheKeyType(std::move(aKeyToInsert));
0894         new (&aSlot.Item()) TheItemType(std::move(aItemToInsert));
0895         aSlot.myHash = aHashToInsert;
0896         aSlot.SetProbeDistance(aProbe);
0897         ++mySize;
0898         if (theInsertedIndex != nullptr)
0899         {
0900           *theInsertedIndex = aHasInsertedIndex ? anInsertedIndex : aIndex;
0901         }
0902         return true;
0903       }
0904 
0905       if constexpr (CheckExisting)
0906       {
0907         if (aSlot.myHash == aHashToInsert && myHasher(aSlot.Key(), aKeyToInsert))
0908         {
0909           if constexpr (UpdateExisting)
0910           {
0911             aSlot.Item() = std::move(aItemToInsert);
0912           }
0913           if (theInsertedIndex != nullptr)
0914           {
0915             *theInsertedIndex = aIndex;
0916           }
0917           return false;
0918         }
0919       }
0920 
0921       if (aProbe > aSlot.ProbeDistance())
0922       {
0923         std::swap(aKeyToInsert, aSlot.Key());
0924         std::swap(aItemToInsert, aSlot.Item());
0925         std::swap(aHashToInsert, aSlot.myHash);
0926         const size_t aTmp = aProbe;
0927         aProbe            = aSlot.ProbeDistance();
0928         aSlot.SetProbeDistance(aTmp);
0929         if (!aHasInsertedIndex)
0930         {
0931           anInsertedIndex   = aIndex;
0932           aHasInsertedIndex = true;
0933         }
0934       }
0935 
0936       ++aProbe;
0937       aIndex = (aIndex + 1) & aMask;
0938     }
0939   }
0940 
0941   template <typename K, typename V>
0942   void insertRehashedImpl(K&& theKey, V&& theItem, const size_t theHash)
0943   {
0944     (void)insertRehashedImpl(std::forward<K>(theKey),
0945                              std::forward<V>(theItem),
0946                              theHash,
0947                              std::false_type{},
0948                              std::false_type{});
0949   }
0950 
0951   template <typename K, typename V>
0952   bool insertImpl(K&& theKey, V&& theItem)
0953   {
0954     const size_t aHash = myHasher(theKey);
0955     return insertRehashedImpl(std::forward<K>(theKey),
0956                               std::forward<V>(theItem),
0957                               aHash,
0958                               std::true_type{},
0959                               std::true_type{});
0960   }
0961 
0962   template <typename K, typename V>
0963   bool tryInsertImpl(K&& theKey, V&& theItem)
0964   {
0965     const size_t aHash = myHasher(theKey);
0966     return insertRehashedImpl(std::forward<K>(theKey),
0967                               std::forward<V>(theItem),
0968                               aHash,
0969                               std::true_type{},
0970                               std::false_type{});
0971   }
0972 
0973   template <typename K, typename V, bool IsTry>
0974   TheItemType& insertRefImpl(K&& theKey, V&& theItem, std::bool_constant<IsTry>)
0975   {
0976     const size_t aHash  = myHasher(theKey);
0977     size_t       aIndex = 0;
0978     if constexpr (IsTry)
0979     {
0980       (void)insertRehashedImpl(std::forward<K>(theKey),
0981                                std::forward<V>(theItem),
0982                                aHash,
0983                                std::true_type{},
0984                                std::false_type{},
0985                                &aIndex);
0986     }
0987     else
0988     {
0989       (void)insertRehashedImpl(std::forward<K>(theKey),
0990                                std::forward<V>(theItem),
0991                                aHash,
0992                                std::true_type{},
0993                                std::true_type{},
0994                                &aIndex);
0995     }
0996     return mySlots[aIndex].Item();
0997   }
0998 
0999   template <typename K, bool IsTry, typename... Args>
1000   bool emplaceImpl(K&& theKey, std::bool_constant<IsTry>, Args&&... theArgs)
1001   {
1002     const size_t aHash  = myHasher(theKey);
1003     const size_t aMask  = myCapacity - 1;
1004     size_t       aIndex = aHash & aMask;
1005     size_t       aProbe = 0;
1006 
1007     TheKeyType aKeyToInsert  = std::forward<K>(theKey);
1008     size_t     aHashToInsert = aHash;
1009 
1010     while (true)
1011     {
1012       Slot& aSlot = mySlots[aIndex];
1013 
1014       if (aSlot.IsEmpty())
1015       {
1016         new (&aSlot.Key()) TheKeyType(std::move(aKeyToInsert));
1017         new (&aSlot.Item()) TheItemType(std::forward<Args>(theArgs)...);
1018         aSlot.myHash = aHashToInsert;
1019         aSlot.SetProbeDistance(aProbe);
1020         ++mySize;
1021         return true;
1022       }
1023 
1024       if (aSlot.myHash == aHashToInsert && myHasher(aSlot.Key(), aKeyToInsert))
1025       {
1026         if constexpr (!IsTry)
1027           aSlot.Item() = TheItemType(std::forward<Args>(theArgs)...);
1028         return false;
1029       }
1030 
1031       if (aProbe > aSlot.ProbeDistance())
1032       {
1033         TheItemType aItemToInsert(std::forward<Args>(theArgs)...);
1034 
1035         std::swap(aKeyToInsert, aSlot.Key());
1036         std::swap(aItemToInsert, aSlot.Item());
1037         std::swap(aHashToInsert, aSlot.myHash);
1038         const size_t aTmp = aProbe;
1039         aProbe            = aSlot.ProbeDistance();
1040         aSlot.SetProbeDistance(aTmp);
1041 
1042         ++aProbe;
1043         aIndex = (aIndex + 1) & aMask;
1044 
1045         while (true)
1046         {
1047           Slot& aSlot2 = mySlots[aIndex];
1048 
1049           if (aSlot2.IsEmpty())
1050           {
1051             new (&aSlot2.Key()) TheKeyType(std::move(aKeyToInsert));
1052             new (&aSlot2.Item()) TheItemType(std::move(aItemToInsert));
1053             aSlot2.myHash = aHashToInsert;
1054             aSlot2.SetProbeDistance(aProbe);
1055             ++mySize;
1056             return true;
1057           }
1058 
1059           if (aProbe > aSlot2.ProbeDistance())
1060           {
1061             std::swap(aKeyToInsert, aSlot2.Key());
1062             std::swap(aItemToInsert, aSlot2.Item());
1063             std::swap(aHashToInsert, aSlot2.myHash);
1064             const size_t aTmp2 = aProbe;
1065             aProbe             = aSlot2.ProbeDistance();
1066             aSlot2.SetProbeDistance(aTmp2);
1067           }
1068 
1069           ++aProbe;
1070           aIndex = (aIndex + 1) & aMask;
1071         }
1072       }
1073 
1074       ++aProbe;
1075       aIndex = (aIndex + 1) & aMask;
1076     }
1077   }
1078 
1079   template <typename K, bool IsTry, typename... Args>
1080   TheItemType& emplacedImpl(K&& theKey, std::bool_constant<IsTry>, Args&&... theArgs)
1081   {
1082     const size_t aHash  = myHasher(theKey);
1083     const size_t aMask  = myCapacity - 1;
1084     size_t       aIndex = aHash & aMask;
1085     size_t       aProbe = 0;
1086 
1087     TheKeyType aKeyToInsert  = std::forward<K>(theKey);
1088     size_t     aHashToInsert = aHash;
1089 
1090     while (true)
1091     {
1092       Slot& aSlot = mySlots[aIndex];
1093 
1094       if (aSlot.IsEmpty())
1095       {
1096         new (&aSlot.Key()) TheKeyType(std::move(aKeyToInsert));
1097         new (&aSlot.Item()) TheItemType(std::forward<Args>(theArgs)...);
1098         aSlot.myHash = aHashToInsert;
1099         aSlot.SetProbeDistance(aProbe);
1100         ++mySize;
1101         return aSlot.Item();
1102       }
1103 
1104       if (aSlot.myHash == aHashToInsert && myHasher(aSlot.Key(), aKeyToInsert))
1105       {
1106         if constexpr (!IsTry)
1107           aSlot.Item() = TheItemType(std::forward<Args>(theArgs)...);
1108         return aSlot.Item();
1109       }
1110 
1111       if (aProbe > aSlot.ProbeDistance())
1112       {
1113         TheItemType aItemToInsert(std::forward<Args>(theArgs)...);
1114 
1115         std::swap(aKeyToInsert, aSlot.Key());
1116         std::swap(aItemToInsert, aSlot.Item());
1117         std::swap(aHashToInsert, aSlot.myHash);
1118         const size_t aTmp = aProbe;
1119         aProbe            = aSlot.ProbeDistance();
1120         aSlot.SetProbeDistance(aTmp);
1121 
1122         TheItemType& aResult = aSlot.Item();
1123 
1124         ++aProbe;
1125         aIndex = (aIndex + 1) & aMask;
1126 
1127         while (true)
1128         {
1129           Slot& aSlot2 = mySlots[aIndex];
1130 
1131           if (aSlot2.IsEmpty())
1132           {
1133             new (&aSlot2.Key()) TheKeyType(std::move(aKeyToInsert));
1134             new (&aSlot2.Item()) TheItemType(std::move(aItemToInsert));
1135             aSlot2.myHash = aHashToInsert;
1136             aSlot2.SetProbeDistance(aProbe);
1137             ++mySize;
1138             return aResult;
1139           }
1140 
1141           if (aProbe > aSlot2.ProbeDistance())
1142           {
1143             std::swap(aKeyToInsert, aSlot2.Key());
1144             std::swap(aItemToInsert, aSlot2.Item());
1145             std::swap(aHashToInsert, aSlot2.myHash);
1146             const size_t aTmp2 = aProbe;
1147             aProbe             = aSlot2.ProbeDistance();
1148             aSlot2.SetProbeDistance(aTmp2);
1149           }
1150 
1151           ++aProbe;
1152           aIndex = (aIndex + 1) & aMask;
1153         }
1154       }
1155 
1156       ++aProbe;
1157       aIndex = (aIndex + 1) & aMask;
1158     }
1159   }
1160 
1161   void backwardShiftDelete(size_t theIndex)
1162   {
1163     const size_t aMask    = myCapacity - 1;
1164     size_t       aCurrent = theIndex;
1165     size_t       aNext    = (aCurrent + 1) & aMask;
1166 
1167     while (mySlots[aNext].IsUsed() && mySlots[aNext].ProbeDistance() > 0)
1168     {
1169       new (&mySlots[aCurrent].Key()) TheKeyType(std::move(mySlots[aNext].Key()));
1170       new (&mySlots[aCurrent].Item()) TheItemType(std::move(mySlots[aNext].Item()));
1171       mySlots[aCurrent].myHash = mySlots[aNext].myHash;
1172       mySlots[aCurrent].SetProbeDistance(mySlots[aNext].ProbeDistance() - 1);
1173 
1174       mySlots[aNext].Key().~TheKeyType();
1175       mySlots[aNext].Item().~TheItemType();
1176 
1177       aCurrent = aNext;
1178       aNext    = (aNext + 1) & aMask;
1179     }
1180 
1181     mySlots[aCurrent].SetEmpty();
1182   }
1183 
1184 private:
1185   Slot*  mySlots;    //!< Array of slots
1186   size_t myCapacity; //!< Total number of slots (always power of 2)
1187   size_t mySize;     //!< Number of used slots
1188   Hasher myHasher;   //!< Hash and equality functor
1189 };
1190 
1191 #endif // NCollection_FlatDataMap_HeaderFile