File indexing completed on 2026-09-28 09:20:58
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
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
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067 template <class TheKeyType, class Hasher = NCollection_DefaultHasher<TheKeyType>>
0068 class NCollection_FlatMap
0069 {
0070 public:
0071
0072 using key_type = TheKeyType;
0073
0074 private:
0075
0076 static constexpr size_t THE_DEFAULT_CAPACITY = 8;
0077
0078 static constexpr size_t THE_MAX_LOAD_NUMERATOR = 13;
0079
0080 static constexpr size_t THE_MAX_LOAD_DENOMINATOR = 16;
0081
0082
0083
0084 struct Slot
0085 {
0086 alignas(TheKeyType) char myKeyStorage[sizeof(TheKeyType)];
0087 size_t myHash;
0088
0089 size_t myProbeDistancePlus1;
0090
0091 Slot() noexcept
0092 : myHash(0),
0093 myProbeDistancePlus1(0)
0094 {
0095
0096 }
0097
0098
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
0122
0123
0124 class Iterator
0125 {
0126 public:
0127
0128 Iterator() noexcept
0129 : mySlots(nullptr),
0130 myCapacity(0),
0131 myIndex(0)
0132 {
0133 }
0134
0135
0136 Iterator(const NCollection_FlatMap& theMap) noexcept
0137 : mySlots(theMap.mySlots),
0138 myCapacity(theMap.myCapacity),
0139 myIndex(0)
0140 {
0141
0142 while (myIndex < myCapacity && !mySlots[myIndex].IsUsed())
0143 {
0144 ++myIndex;
0145 }
0146 }
0147
0148
0149 bool More() const noexcept { return myIndex < myCapacity; }
0150
0151
0152 void Next() noexcept
0153 {
0154 ++myIndex;
0155 while (myIndex < myCapacity && !mySlots[myIndex].IsUsed())
0156 {
0157 ++myIndex;
0158 }
0159 }
0160
0161
0162 const TheKeyType& Key() const
0163 {
0164 Standard_OutOfRange_Raise_if(!More(), "NCollection_FlatMap::Iterator::Key");
0165 return mySlots[myIndex].Key();
0166 }
0167
0168
0169 const TheKeyType& Value() const { return Key(); }
0170
0171
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
0185
0186
0187 NCollection_FlatMap()
0188 : mySlots(nullptr),
0189 myCapacity(0),
0190 mySize(0)
0191 {
0192 }
0193
0194
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
0207
0208
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
0222
0223
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
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
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
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
0279 ~NCollection_FlatMap() { Clear(true); }
0280
0281
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
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
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
0332
0333
0334 size_t Size() const noexcept { return mySize; }
0335
0336
0337 bool IsEmpty() const noexcept { return mySize == 0; }
0338
0339
0340 size_t Capacity() const noexcept { return myCapacity; }
0341
0342
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
0352
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
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
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
0387
0388
0389
0390 bool Add(const TheKeyType& theKey)
0391 {
0392 ensureCapacity();
0393 return insertImpl(theKey);
0394 }
0395
0396
0397 bool Add(TheKeyType&& theKey)
0398 {
0399 ensureCapacity();
0400 return insertImpl(std::forward<TheKeyType>(theKey));
0401 }
0402
0403
0404
0405
0406
0407 const TheKeyType& Added(const TheKeyType& theKey)
0408 {
0409 ensureCapacity();
0410 return insertRefImpl(theKey, std::false_type{});
0411 }
0412
0413
0414
0415
0416
0417 const TheKeyType& Added(TheKeyType&& theKey)
0418 {
0419 ensureCapacity();
0420 return insertRefImpl(std::move(theKey), std::false_type{});
0421 }
0422
0423
0424
0425
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
0435
0436
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
0446
0447
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
0457
0458
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
0468
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
0483 mySlots[aIndex].Key().~TheKeyType();
0484 mySlots[aIndex].SetEmpty();
0485 --mySize;
0486
0487
0488 backwardShiftDelete(aIndex);
0489
0490 return true;
0491 }
0492
0493
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
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
0527 const Hasher& GetHasher() const noexcept { return myHasher; }
0528
0529
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
0542 void Reserve(const size_t theN) { reserve(theN); }
0543
0544 public:
0545
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
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
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
0614
0615
0616
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
0717
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
0728
0729
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
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
0796 mySlots[aNext].Key().~TheKeyType();
0797
0798 aCurrent = aNext;
0799 aNext = (aNext + 1) & aMask;
0800 }
0801
0802
0803
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