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_FlatMap_HeaderFile
0015 #define NCollection_FlatMap_HeaderFile
0016 
0017 #include <Standard.hxx>
0018 #include <Standard_OutOfRange.hxx>
0019 #include <NCollection_DefaultHasher.hxx>
0020 
0021 #include <functional>
0022 #include <new>
0023 #include <optional>
0024 #include <type_traits>
0025 #include <utility>
0026 
0027 /**
0028  * @brief High-performance hash set using open addressing with Robin Hood hashing.
0029  *
0030  * NCollection_FlatMap is an alternative to NCollection_Map that provides
0031  * better cache locality and reduced memory allocation overhead by storing all
0032  * keys inline in a contiguous array.
0033  *
0034  * Key features:
0035  * - Open addressing with linear probing (better cache locality)
0036  * - Robin Hood hashing (reduces probe sequence variance)
0037  * - Power-of-2 sizing for fast modulo operations
0038  * - No per-element allocations
0039  *
0040  * Typical faster usage patterns:
0041  * - POD or small key types
0042  * - Performance-critical code paths
0043  * - Lookup-heavy workloads (Contains()/Seek())
0044  * - Full traversal / iteration-heavy workloads
0045  * - Stable-size maps with Reserve() called once before bulk insert
0046  *
0047  * Container-specific implementation notes:
0048  * - Remove() keeps probe clusters consistent using backward-shift compaction.
0049  *
0050  * Relative to NCollection_Map:
0051  * - Add()/Remove() can be faster in many workloads thanks to contiguous storage and
0052  *   no per-element node allocation.
0053  * - Iteration is often faster due to contiguous slot scanning and reduced pointer chasing.
0054  *
0055  * Limitations:
0056  * - Keys must be movable
0057  * - Higher memory usage at low load factors
0058  * - Iteration order is not insertion order
0059  * - Probe distance grows with collisions (bounded by table capacity)
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 Hasher     Hash and equality functor (default: NCollection_DefaultHasher)
0066  */
0067 template <class TheKeyType, class Hasher = NCollection_DefaultHasher<TheKeyType>>
0068 class NCollection_FlatMap
0069 {
0070 public:
0071   //! STL-compliant type alias for key type
0072   using key_type = TheKeyType;
0073 
0074 private:
0075   //! Default initial capacity (must be power of 2)
0076   static constexpr size_t THE_DEFAULT_CAPACITY = 8;
0077   //! Maximum load factor numerator (13/16 = 81.25%).
0078   static constexpr size_t THE_MAX_LOAD_NUMERATOR = 13;
0079   //! Maximum load factor denominator.
0080   static constexpr size_t THE_MAX_LOAD_DENOMINATOR = 16;
0081 
0082   //! Internal slot structure holding key and metadata.
0083   //! Key storage is uninitialized until state becomes Used.
0084   struct Slot
0085   {
0086     alignas(TheKeyType) char myKeyStorage[sizeof(TheKeyType)]; //!< Uninitialized key storage
0087     size_t myHash;                                             //!< Cached hash code
0088     //! Distance from ideal bucket plus one; 0 means Empty, otherwise Used.
0089     size_t myProbeDistancePlus1;
0090 
0091     Slot() noexcept
0092         : myHash(0),
0093           myProbeDistancePlus1(0)
0094     {
0095       // Key is NOT constructed - myKeyStorage is uninitialized
0096     }
0097 
0098     //! Access the key (only valid when IsUsed() == true)
0099     TheKeyType& Key() noexcept { return *reinterpret_cast<TheKeyType*>(myKeyStorage); }
0100 
0101     const TheKeyType& Key() const noexcept
0102     {
0103       return *reinterpret_cast<const TheKeyType*>(myKeyStorage);
0104     }
0105 
0106     bool IsEmpty() const noexcept { return myProbeDistancePlus1 == 0; }
0107 
0108     bool IsUsed() const noexcept { return myProbeDistancePlus1 != 0; }
0109 
0110     size_t ProbeDistance() const noexcept { return myProbeDistancePlus1 - 1; }
0111 
0112     void SetProbeDistance(const size_t theProbeDistance) noexcept
0113     {
0114       myProbeDistancePlus1 = theProbeDistance + 1;
0115     }
0116 
0117     void SetEmpty() noexcept { myProbeDistancePlus1 = 0; }
0118   };
0119 
0120 public:
0121   // **************** Iterator interface ****************
0122 
0123   //! Forward iterator for NCollection_FlatMap
0124   class Iterator
0125   {
0126   public:
0127     //! Empty constructor
0128     Iterator() noexcept
0129         : mySlots(nullptr),
0130           myCapacity(0),
0131           myIndex(0)
0132     {
0133     }
0134 
0135     //! Constructor from map
0136     Iterator(const NCollection_FlatMap& theMap) noexcept
0137         : mySlots(theMap.mySlots),
0138           myCapacity(theMap.myCapacity),
0139           myIndex(0)
0140     {
0141       // Find first used slot
0142       while (myIndex < myCapacity && !mySlots[myIndex].IsUsed())
0143       {
0144         ++myIndex;
0145       }
0146     }
0147 
0148     //! Check if there are more elements
0149     bool More() const noexcept { return myIndex < myCapacity; }
0150 
0151     //! Move to next element
0152     void Next() noexcept
0153     {
0154       ++myIndex;
0155       while (myIndex < myCapacity && !mySlots[myIndex].IsUsed())
0156       {
0157         ++myIndex;
0158       }
0159     }
0160 
0161     //! Get current key
0162     const TheKeyType& Key() const
0163     {
0164       Standard_OutOfRange_Raise_if(!More(), "NCollection_FlatMap::Iterator::Key");
0165       return mySlots[myIndex].Key();
0166     }
0167 
0168     //! Get current value (alias for Key for compatibility)
0169     const TheKeyType& Value() const { return Key(); }
0170 
0171     //! Performs comparison of two iterators.
0172     bool IsEqual(const Iterator& theOther) const noexcept
0173     {
0174       return mySlots == theOther.mySlots && myIndex == theOther.myIndex;
0175     }
0176 
0177   private:
0178     const Slot* mySlots;
0179     size_t      myCapacity;
0180     size_t      myIndex;
0181   };
0182 
0183 public:
0184   // **************** Constructors and destructor ****************
0185 
0186   //! Default constructor
0187   NCollection_FlatMap()
0188       : mySlots(nullptr),
0189         myCapacity(0),
0190         mySize(0)
0191   {
0192   }
0193 
0194   //! Constructor with initial capacity hint
0195   explicit NCollection_FlatMap(const size_t theNbBuckets)
0196       : mySlots(nullptr),
0197         myCapacity(0),
0198         mySize(0)
0199   {
0200     if (theNbBuckets > 0)
0201     {
0202       reserve(static_cast<size_t>(theNbBuckets));
0203     }
0204   }
0205 
0206   //! Constructor with custom hasher (copy).
0207   //! @param theHasher custom hasher instance
0208   //! @param theNbBuckets initial capacity hint
0209   explicit NCollection_FlatMap(const Hasher& theHasher, const size_t theNbBuckets = 0)
0210       : mySlots(nullptr),
0211         myCapacity(0),
0212         mySize(0),
0213         myHasher(theHasher)
0214   {
0215     if (theNbBuckets > 0)
0216     {
0217       reserve(static_cast<size_t>(theNbBuckets));
0218     }
0219   }
0220 
0221   //! Constructor with custom hasher (move).
0222   //! @param theHasher custom hasher instance (moved)
0223   //! @param theNbBuckets initial capacity hint
0224   explicit NCollection_FlatMap(Hasher&& theHasher, const size_t theNbBuckets = 0)
0225       : mySlots(nullptr),
0226         myCapacity(0),
0227         mySize(0),
0228         myHasher(std::move(theHasher))
0229   {
0230     if (theNbBuckets > 0)
0231     {
0232       reserve(static_cast<size_t>(theNbBuckets));
0233     }
0234   }
0235 
0236   //! Copy constructor
0237   NCollection_FlatMap(const NCollection_FlatMap& theOther)
0238       : mySlots(nullptr),
0239         myCapacity(0),
0240         mySize(0),
0241         myHasher(theOther.myHasher)
0242   {
0243     if (theOther.mySize > 0)
0244     {
0245       // Allocate same capacity as the source (not through reserve which may change capacity)
0246       mySlots = static_cast<Slot*>(Standard::Allocate(theOther.myCapacity * sizeof(Slot)));
0247       for (size_t i = 0; i < theOther.myCapacity; ++i)
0248       {
0249         new (&mySlots[i]) Slot();
0250       }
0251       myCapacity = theOther.myCapacity;
0252 
0253       for (size_t i = 0; i < theOther.myCapacity; ++i)
0254       {
0255         if (theOther.mySlots[i].IsUsed())
0256         {
0257           new (&mySlots[i].Key()) TheKeyType(theOther.mySlots[i].Key());
0258           mySlots[i].myHash               = theOther.mySlots[i].myHash;
0259           mySlots[i].myProbeDistancePlus1 = theOther.mySlots[i].myProbeDistancePlus1;
0260         }
0261       }
0262       mySize = theOther.mySize;
0263     }
0264   }
0265 
0266   //! Move constructor
0267   NCollection_FlatMap(NCollection_FlatMap&& theOther) noexcept
0268       : mySlots(theOther.mySlots),
0269         myCapacity(theOther.myCapacity),
0270         mySize(theOther.mySize),
0271         myHasher(std::move(theOther.myHasher))
0272   {
0273     theOther.mySlots    = nullptr;
0274     theOther.myCapacity = 0;
0275     theOther.mySize     = 0;
0276   }
0277 
0278   //! Destructor
0279   ~NCollection_FlatMap() { Clear(true); }
0280 
0281   //! Copy assignment
0282   NCollection_FlatMap& operator=(const NCollection_FlatMap& theOther)
0283   {
0284     if (this != &theOther)
0285     {
0286       Clear(true);
0287       myHasher = theOther.myHasher;
0288       if (theOther.mySize > 0)
0289       {
0290         // Allocate same capacity as the source (not through reserve which may change capacity)
0291         mySlots = static_cast<Slot*>(Standard::Allocate(theOther.myCapacity * sizeof(Slot)));
0292         for (size_t i = 0; i < theOther.myCapacity; ++i)
0293         {
0294           new (&mySlots[i]) Slot();
0295         }
0296         myCapacity = theOther.myCapacity;
0297 
0298         for (size_t i = 0; i < theOther.myCapacity; ++i)
0299         {
0300           if (theOther.mySlots[i].IsUsed())
0301           {
0302             new (&mySlots[i].Key()) TheKeyType(theOther.mySlots[i].Key());
0303             mySlots[i].myHash               = theOther.mySlots[i].myHash;
0304             mySlots[i].myProbeDistancePlus1 = theOther.mySlots[i].myProbeDistancePlus1;
0305           }
0306         }
0307         mySize = theOther.mySize;
0308       }
0309     }
0310     return *this;
0311   }
0312 
0313   //! Move assignment
0314   NCollection_FlatMap& operator=(NCollection_FlatMap&& theOther) noexcept
0315   {
0316     if (this != &theOther)
0317     {
0318       Clear(true);
0319       mySlots             = theOther.mySlots;
0320       myCapacity          = theOther.myCapacity;
0321       mySize              = theOther.mySize;
0322       myHasher            = std::move(theOther.myHasher);
0323       theOther.mySlots    = nullptr;
0324       theOther.myCapacity = 0;
0325       theOther.mySize     = 0;
0326     }
0327     return *this;
0328   }
0329 
0330 public:
0331   // **************** Query methods ****************
0332 
0333   //! Returns number of elements.
0334   size_t Size() const noexcept { return mySize; }
0335 
0336   //! Returns true if map is empty
0337   bool IsEmpty() const noexcept { return mySize == 0; }
0338 
0339   //! Returns current capacity
0340   size_t Capacity() const noexcept { return myCapacity; }
0341 
0342   //! Check if key exists
0343   bool Contains(const TheKeyType& theKey) const
0344   {
0345     if (mySize == 0)
0346       return false;
0347     size_t anIndex = 0;
0348     return findSlotIndex(theKey, anIndex);
0349   }
0350 
0351   //! Contained returns optional const reference to the key in the map.
0352   //! Returns std::nullopt if the key is not found.
0353   std::optional<std::reference_wrapper<const TheKeyType>> Contained(const TheKeyType& theKey) const
0354   {
0355     if (mySize == 0)
0356       return std::nullopt;
0357     size_t aIdx = 0;
0358     if (!findSlotIndex(theKey, aIdx))
0359       return std::nullopt;
0360     return std::cref(mySlots[aIdx].Key());
0361   }
0362 
0363   //! Seek returns pointer to key in map. Returns NULL if not found.
0364   const TheKeyType* Seek(const TheKeyType& theKey) const
0365   {
0366     if (mySize == 0)
0367       return nullptr;
0368     size_t aIdx = 0;
0369     if (!findSlotIndex(theKey, aIdx))
0370       return nullptr;
0371     return &mySlots[aIdx].Key();
0372   }
0373 
0374   //! ChangeSeek returns modifiable pointer to key in map. Returns NULL if not found.
0375   TheKeyType* ChangeSeek(const TheKeyType& theKey)
0376   {
0377     if (mySize == 0)
0378       return nullptr;
0379     size_t aIdx = 0;
0380     if (!findSlotIndex(theKey, aIdx))
0381       return nullptr;
0382     return &mySlots[aIdx].Key();
0383   }
0384 
0385 public:
0386   // **************** Modification methods ****************
0387 
0388   //! Add key to set
0389   //! @return true if key was newly added, false if already present
0390   bool Add(const TheKeyType& theKey)
0391   {
0392     ensureCapacity();
0393     return insertImpl(theKey);
0394   }
0395 
0396   //! Add key to set (move semantics)
0397   bool Add(TheKeyType&& theKey)
0398   {
0399     ensureCapacity();
0400     return insertImpl(std::forward<TheKeyType>(theKey));
0401   }
0402 
0403   //! Added: add a new key if not yet in the map, and return
0404   //! reference to either newly added or previously existing key.
0405   //! @param theKey key to add
0406   //! @return const reference to the key in the map
0407   const TheKeyType& Added(const TheKeyType& theKey)
0408   {
0409     ensureCapacity();
0410     return insertRefImpl(theKey, std::false_type{});
0411   }
0412 
0413   //! Added: add a new key if not yet in the map, and return
0414   //! reference to either newly added or previously existing key.
0415   //! @param theKey key to add
0416   //! @return const reference to the key in the map
0417   const TheKeyType& Added(TheKeyType&& theKey)
0418   {
0419     ensureCapacity();
0420     return insertRefImpl(std::move(theKey), std::false_type{});
0421   }
0422 
0423   //! Emplace constructs key in-place; if key exists, overwrites.
0424   //! @param theArgs arguments forwarded to key constructor
0425   //! @return true if key was newly added, false if key already existed
0426   template <typename... Args>
0427   bool Emplace(Args&&... theArgs)
0428   {
0429     ensureCapacity();
0430     TheKeyType aTempKey(std::forward<Args>(theArgs)...);
0431     return emplaceImpl(std::move(aTempKey), std::false_type{}, std::false_type{});
0432   }
0433 
0434   //! Emplaced constructs key in-place; if key exists, overwrites.
0435   //! @param theArgs arguments forwarded to key constructor
0436   //! @return const reference to the key in the map
0437   template <typename... Args>
0438   const TheKeyType& Emplaced(Args&&... theArgs)
0439   {
0440     ensureCapacity();
0441     TheKeyType aTempKey(std::forward<Args>(theArgs)...);
0442     return emplaceImpl(std::move(aTempKey), std::false_type{}, std::true_type{});
0443   }
0444 
0445   //! TryEmplace constructs key in-place only if not already present.
0446   //! @param theArgs arguments forwarded to key constructor
0447   //! @return true if key was newly added, false if key already existed
0448   template <typename... Args>
0449   bool TryEmplace(Args&&... theArgs)
0450   {
0451     ensureCapacity();
0452     TheKeyType aTempKey(std::forward<Args>(theArgs)...);
0453     return emplaceImpl(std::move(aTempKey), std::true_type{}, std::false_type{});
0454   }
0455 
0456   //! TryEmplaced constructs key in-place only if not already present.
0457   //! @param theArgs arguments forwarded to key constructor
0458   //! @return const reference to the key (existing or newly added)
0459   template <typename... Args>
0460   const TheKeyType& TryEmplaced(Args&&... theArgs)
0461   {
0462     ensureCapacity();
0463     TheKeyType aTempKey(std::forward<Args>(theArgs)...);
0464     return emplaceImpl(std::move(aTempKey), std::true_type{}, std::true_type{});
0465   }
0466 
0467   //! Remove key from set
0468   //! @return true if key was found and removed
0469   bool Remove(const TheKeyType& theKey)
0470   {
0471     if (mySize == 0)
0472       return false;
0473 
0474     size_t aFoundIndex = 0;
0475     if (!findSlotIndex(theKey, aFoundIndex))
0476     {
0477       return false;
0478     }
0479 
0480     const size_t aIndex = aFoundIndex;
0481 
0482     // Destroy key
0483     mySlots[aIndex].Key().~TheKeyType();
0484     mySlots[aIndex].SetEmpty();
0485     --mySize;
0486 
0487     // Backward shift delete
0488     backwardShiftDelete(aIndex);
0489 
0490     return true;
0491   }
0492 
0493   //! Clear all elements
0494   void Clear(bool doReleaseMemory = false)
0495   {
0496     if (mySlots != nullptr)
0497     {
0498       for (size_t i = 0; i < myCapacity; ++i)
0499       {
0500         if (mySlots[i].IsUsed())
0501         {
0502           mySlots[i].Key().~TheKeyType();
0503           mySlots[i].SetEmpty();
0504         }
0505       }
0506       mySize = 0;
0507 
0508       if (doReleaseMemory)
0509       {
0510         Standard::Free(mySlots);
0511         mySlots    = nullptr;
0512         myCapacity = 0;
0513       }
0514     }
0515   }
0516 
0517   //! Exchange content with another map
0518   void Exchange(NCollection_FlatMap& theOther) noexcept
0519   {
0520     std::swap(mySlots, theOther.mySlots);
0521     std::swap(myCapacity, theOther.myCapacity);
0522     std::swap(mySize, theOther.mySize);
0523     std::swap(myHasher, theOther.myHasher);
0524   }
0525 
0526   //! Returns const reference to the hasher.
0527   const Hasher& GetHasher() const noexcept { return myHasher; }
0528 
0529   //! Reserve capacity for at least theN elements
0530   void reserve(size_t theN)
0531   {
0532     const size_t aMinCapacity =
0533       (theN * THE_MAX_LOAD_DENOMINATOR + THE_MAX_LOAD_NUMERATOR - 1) / THE_MAX_LOAD_NUMERATOR;
0534     size_t aNewCapacity = nextPowerOf2(aMinCapacity);
0535     if (aNewCapacity > myCapacity)
0536     {
0537       rehash(aNewCapacity);
0538     }
0539   }
0540 
0541   //! Reserve capacity for at least theN elements
0542   void Reserve(const size_t theN) { reserve(theN); }
0543 
0544 public:
0545   // **************** Iterator access ****************
0546 
0547   Iterator begin() const noexcept { return Iterator(*this); }
0548 
0549   Iterator end() const noexcept { return Iterator(); }
0550 
0551   Iterator cbegin() const noexcept { return Iterator(*this); }
0552 
0553   Iterator cend() const noexcept { return Iterator(); }
0554 
0555 private:
0556   // **************** Internal implementation ****************
0557 
0558   static size_t nextPowerOf2(size_t n) noexcept
0559   {
0560     if (n == 0)
0561       return THE_DEFAULT_CAPACITY;
0562     --n;
0563     n |= n >> 1;
0564     n |= n >> 2;
0565     n |= n >> 4;
0566     n |= n >> 8;
0567     n |= n >> 16;
0568     if constexpr (sizeof(size_t) > 4)
0569     {
0570       n |= n >> 32;
0571     }
0572     return n + 1;
0573   }
0574 
0575   void ensureCapacity()
0576   {
0577     // Grow at ~81.25% load factor.
0578     if (myCapacity == 0
0579         || (mySize + 1) * THE_MAX_LOAD_DENOMINATOR > myCapacity * THE_MAX_LOAD_NUMERATOR)
0580     {
0581       size_t aNewCapacity = myCapacity == 0 ? THE_DEFAULT_CAPACITY : myCapacity * 2;
0582       rehash(aNewCapacity);
0583     }
0584   }
0585 
0586   void rehash(size_t theNewCapacity)
0587   {
0588     Slot*  aOldSlots    = mySlots;
0589     size_t aOldCapacity = myCapacity;
0590 
0591     mySlots = static_cast<Slot*>(Standard::Allocate(theNewCapacity * sizeof(Slot)));
0592     for (size_t i = 0; i < theNewCapacity; ++i)
0593     {
0594       new (&mySlots[i]) Slot();
0595     }
0596     myCapacity = theNewCapacity;
0597     mySize     = 0;
0598 
0599     if (aOldSlots != nullptr)
0600     {
0601       for (size_t i = 0; i < aOldCapacity; ++i)
0602       {
0603         if (aOldSlots[i].IsUsed())
0604         {
0605           insertRehashedImpl(std::move(aOldSlots[i].Key()), aOldSlots[i].myHash);
0606           aOldSlots[i].Key().~TheKeyType();
0607         }
0608       }
0609       Standard::Free(aOldSlots);
0610     }
0611   }
0612 
0613   //! Find slot containing key.
0614   //! @param theKey key to find
0615   //! @param[out] theIndex found index
0616   //! @return true if key was found
0617   bool findSlotIndex(const TheKeyType& theKey, size_t& theIndex) const
0618   {
0619     const size_t aHash  = myHasher(theKey);
0620     const size_t aMask  = myCapacity - 1;
0621     size_t       aIndex = aHash & aMask;
0622 
0623     while (true)
0624     {
0625       const Slot& aSlot = mySlots[aIndex];
0626 
0627       if (aSlot.IsEmpty())
0628       {
0629         return false;
0630       }
0631 
0632       if (aSlot.myHash == aHash && myHasher(aSlot.Key(), theKey))
0633       {
0634         theIndex = aIndex;
0635         return true;
0636       }
0637       aIndex = (aIndex + 1) & aMask;
0638     }
0639   }
0640 
0641   template <typename K, bool CheckExisting>
0642   bool insertRehashedImpl(K&&          theKey,
0643                           const size_t theHash,
0644                           std::bool_constant<CheckExisting>,
0645                           size_t* theInsertedIndex = nullptr)
0646   {
0647     const size_t aMask             = myCapacity - 1;
0648     size_t       aIndex            = theHash & aMask;
0649     size_t       aProbe            = 0;
0650     size_t       anInsertedIndex   = 0;
0651     bool         aHasInsertedIndex = false;
0652 
0653     TheKeyType aKeyToInsert  = std::forward<K>(theKey);
0654     size_t     aHashToInsert = theHash;
0655 
0656     while (true)
0657     {
0658       Slot& aSlot = mySlots[aIndex];
0659       if (aSlot.IsEmpty())
0660       {
0661         new (&aSlot.Key()) TheKeyType(std::move(aKeyToInsert));
0662         aSlot.myHash = aHashToInsert;
0663         aSlot.SetProbeDistance(aProbe);
0664         ++mySize;
0665         if (theInsertedIndex != nullptr)
0666         {
0667           *theInsertedIndex = aHasInsertedIndex ? anInsertedIndex : aIndex;
0668         }
0669         return true;
0670       }
0671 
0672       if constexpr (CheckExisting)
0673       {
0674         if (aSlot.myHash == aHashToInsert && myHasher(aSlot.Key(), aKeyToInsert))
0675         {
0676           if (theInsertedIndex != nullptr)
0677           {
0678             *theInsertedIndex = aIndex;
0679           }
0680           return false;
0681         }
0682       }
0683 
0684       if (aProbe > aSlot.ProbeDistance())
0685       {
0686         std::swap(aKeyToInsert, aSlot.Key());
0687         std::swap(aHashToInsert, aSlot.myHash);
0688         const size_t aTmp = aProbe;
0689         aProbe            = aSlot.ProbeDistance();
0690         aSlot.SetProbeDistance(aTmp);
0691         if (!aHasInsertedIndex)
0692         {
0693           anInsertedIndex   = aIndex;
0694           aHasInsertedIndex = true;
0695         }
0696       }
0697 
0698       ++aProbe;
0699       aIndex = (aIndex + 1) & aMask;
0700     }
0701   }
0702 
0703   template <typename K>
0704   void insertRehashedImpl(K&& theKey, const size_t theHash)
0705   {
0706     (void)insertRehashedImpl(std::forward<K>(theKey), theHash, std::false_type{});
0707   }
0708 
0709   template <typename K>
0710   bool insertImpl(K&& theKey)
0711   {
0712     const size_t aHash = myHasher(theKey);
0713     return insertRehashedImpl(std::forward<K>(theKey), aHash, std::true_type{});
0714   }
0715 
0716   //! Insert key and return reference to it (for Added method)
0717   //! @tparam IsTry if true, does not modify existing (not used for key-only map)
0718   template <typename K, bool IsTry>
0719   const TheKeyType& insertRefImpl(K&& theKey, std::bool_constant<IsTry>)
0720   {
0721     const size_t aHash  = myHasher(theKey);
0722     size_t       aIndex = 0;
0723     (void)insertRehashedImpl(std::forward<K>(theKey), aHash, std::true_type{}, &aIndex);
0724     return mySlots[aIndex].Key();
0725   }
0726 
0727   //! Implementation helper for Emplace/Emplaced.
0728   //! @tparam IsTry if true, does not modify existing; if false, overwrites
0729   //! @tparam ReturnRef if true, returns reference; if false, returns bool
0730   template <bool IsTry, bool ReturnRef>
0731   auto emplaceImpl(TheKeyType&& theKey, std::bool_constant<IsTry>, std::bool_constant<ReturnRef>)
0732     -> std::conditional_t<ReturnRef, const TheKeyType&, bool>
0733   {
0734     const size_t aHash  = myHasher(theKey);
0735     const size_t aMask  = myCapacity - 1;
0736     size_t       aIndex = aHash & aMask;
0737     size_t       aProbe = 0;
0738 
0739     TheKeyType aKeyToInsert  = std::move(theKey);
0740     size_t     aHashToInsert = aHash;
0741 
0742     while (true)
0743     {
0744       Slot& aSlot = mySlots[aIndex];
0745 
0746       if (aSlot.IsEmpty())
0747       {
0748         new (&aSlot.Key()) TheKeyType(std::move(aKeyToInsert));
0749         aSlot.myHash = aHashToInsert;
0750         aSlot.SetProbeDistance(aProbe);
0751         ++mySize;
0752         if constexpr (ReturnRef)
0753           return aSlot.Key();
0754         else
0755           return true;
0756       }
0757 
0758       if (aSlot.myHash == aHashToInsert && myHasher(aSlot.Key(), aKeyToInsert))
0759       {
0760         if constexpr (!IsTry)
0761           aSlot.Key() = std::move(aKeyToInsert);
0762         if constexpr (ReturnRef)
0763           return aSlot.Key();
0764         else
0765           return false;
0766       }
0767 
0768       if (aProbe > aSlot.ProbeDistance())
0769       {
0770         std::swap(aKeyToInsert, aSlot.Key());
0771         std::swap(aHashToInsert, aSlot.myHash);
0772         const size_t aTmp = aProbe;
0773         aProbe            = aSlot.ProbeDistance();
0774         aSlot.SetProbeDistance(aTmp);
0775       }
0776 
0777       ++aProbe;
0778       aIndex = (aIndex + 1) & aMask;
0779     }
0780   }
0781 
0782   void backwardShiftDelete(size_t theIndex)
0783   {
0784     const size_t aMask    = myCapacity - 1;
0785     size_t       aCurrent = theIndex;
0786     size_t       aNext    = (aCurrent + 1) & aMask;
0787 
0788     while (mySlots[aNext].IsUsed() && mySlots[aNext].ProbeDistance() > 0)
0789     {
0790       // Construct key at aCurrent (which was destroyed or never had a key)
0791       new (&mySlots[aCurrent].Key()) TheKeyType(std::move(mySlots[aNext].Key()));
0792       mySlots[aCurrent].myHash = mySlots[aNext].myHash;
0793       mySlots[aCurrent].SetProbeDistance(mySlots[aNext].ProbeDistance() - 1);
0794 
0795       // Destroy the moved-from key at aNext
0796       mySlots[aNext].Key().~TheKeyType();
0797 
0798       aCurrent = aNext;
0799       aNext    = (aNext + 1) & aMask;
0800     }
0801 
0802     // Mark final slot as Empty (removes tombstone; either original deleted slot or last
0803     // shifted-from slot)
0804     mySlots[aCurrent].SetEmpty();
0805   }
0806 
0807 private:
0808   Slot*  mySlots;
0809   size_t myCapacity;
0810   size_t mySize;
0811   Hasher myHasher;
0812 };
0813 
0814 #endif // NCollection_FlatMap_HeaderFile