1 // Created on: 2002-04-24
2 // Created by: Alexander KARTOMIN (akm)
3 // Copyright (c) 2002-2014 OPEN CASCADE SAS
5 // This file is part of Open CASCADE Technology software library.
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
16 #ifndef NCollection_IndexedMap_HeaderFile
17 #define NCollection_IndexedMap_HeaderFile
19 #include <NCollection_BaseMap.hxx>
20 #include <NCollection_TListNode.hxx>
21 #include <NCollection_StlIterator.hxx>
22 #include <Standard_NoSuchObject.hxx>
24 #include <NCollection_DefaultHasher.hxx>
26 #include <Standard_OutOfRange.hxx>
29 * Purpose: An indexed map is used to store keys and to bind
30 * an index to them. Each new key stored in the map
31 * gets an index. Index are incremented as keys are
32 * stored in the map. A key can be found by the index
33 * and an index by the key. No key but the last can
34 * be removed so the indices are in the range 1..Extent.
35 * See the class Map from NCollection for a
36 * discussion about the number of buckets.
39 template < class TheKeyType,
40 class Hasher = NCollection_DefaultHasher<TheKeyType> >
41 class NCollection_IndexedMap : public NCollection_BaseMap
44 //! STL-compliant typedef for key type
45 typedef TheKeyType key_type;
48 //! Adaptation of the TListNode to the INDEXEDmap
49 class IndexedMapNode : public NCollection_TListNode<TheKeyType>
52 //! Constructor with 'Next'
53 IndexedMapNode (const TheKeyType& theKey1,
54 const Standard_Integer theIndex,
55 NCollection_ListNode* theNext1)
56 : NCollection_TListNode<TheKeyType> (theKey1, theNext1),
59 //! Constructor with 'Next'
60 IndexedMapNode (TheKeyType&& theKey1,
61 const Standard_Integer theIndex,
62 NCollection_ListNode* theNext1)
63 : NCollection_TListNode<TheKeyType> (std::forward<TheKeyType>(theKey1), theNext1),
67 TheKeyType& Key1() { return this->ChangeValue(); }
70 Standard_Integer& Index() { return myIndex; }
72 //! Static deleter to be passed to BaseList
73 static void delNode (NCollection_ListNode * theNode,
74 Handle(NCollection_BaseAllocator)& theAl)
76 ((IndexedMapNode *) theNode)->~IndexedMapNode();
81 Standard_Integer myIndex;
85 // **************** Implementation of the Iterator interface.
94 Iterator (const NCollection_IndexedMap& theMap) :
95 myMap((NCollection_IndexedMap *) &theMap),
97 //! Query if the end of collection is reached by iterator
98 Standard_Boolean More(void) const
99 { return (myMap != NULL) && (myIndex <= myMap->Extent()); }
100 //! Make a step along the collection
104 const TheKeyType& Value(void) const
106 Standard_NoSuchObject_Raise_if(!More(), "NCollection_IndexedMap::Iterator::Value");
107 return myMap->FindKey(myIndex);
110 //! Performs comparison of two iterators.
111 Standard_Boolean IsEqual (const Iterator& theOther) const
113 return myMap == theOther.myMap && myIndex == theOther.myIndex;
117 NCollection_IndexedMap * myMap; // Pointer to the map being iterated
118 Standard_Integer myIndex; // Current index
121 //! Shorthand for a constant iterator type.
122 typedef NCollection_StlIterator<std::forward_iterator_tag, Iterator, TheKeyType, true> const_iterator;
124 //! Returns a const iterator pointing to the first element in the map.
125 const_iterator cbegin() const { return Iterator (*this); }
127 //! Returns a const iterator referring to the past-the-end element in the map.
128 const_iterator cend() const { return Iterator(); }
131 // ---------- PUBLIC METHODS ------------
133 //! Empty constructor.
134 NCollection_IndexedMap() : NCollection_BaseMap (1, true, Handle(NCollection_BaseAllocator)()) {}
137 explicit NCollection_IndexedMap (const Standard_Integer theNbBuckets,
138 const Handle(NCollection_BaseAllocator)& theAllocator=0L)
139 : NCollection_BaseMap (theNbBuckets, true, theAllocator) {}
142 NCollection_IndexedMap (const NCollection_IndexedMap& theOther)
143 : NCollection_BaseMap (theOther.NbBuckets(), true, theOther.myAllocator)
144 { *this = theOther; }
147 NCollection_IndexedMap(NCollection_IndexedMap&& theOther) noexcept :
148 NCollection_BaseMap(std::forward<NCollection_BaseMap>(theOther))
151 //! Exchange the content of two maps without re-allocations.
152 //! Notice that allocators will be swapped as well!
153 void Exchange (NCollection_IndexedMap& theOther)
155 this->exchangeMapsData (theOther);
159 //! This method does not change the internal allocator.
160 NCollection_IndexedMap& Assign (const NCollection_IndexedMap& theOther)
162 if (this == &theOther)
166 Standard_Integer anExt = theOther.Extent();
169 ReSize (anExt-1); //mySize is same after resize
170 for (Standard_Integer anIndexIter = 1; anIndexIter <= anExt; ++anIndexIter)
172 const TheKeyType& aKey1 = theOther.FindKey (anIndexIter);
173 const size_t iK1 = HashCode (aKey1, NbBuckets());
174 IndexedMapNode* pNode = new (this->myAllocator) IndexedMapNode (aKey1, anIndexIter, myData1[iK1]);
175 myData1[iK1] = pNode;
176 myData2[anIndexIter - 1] = pNode;
183 //! Assignment operator
184 NCollection_IndexedMap& operator= (const NCollection_IndexedMap& theOther)
186 return Assign (theOther);
190 NCollection_IndexedMap& operator= (NCollection_IndexedMap&& theOther) noexcept
192 if (this == &theOther)
194 exchangeMapsData(theOther);
199 void ReSize (const Standard_Integer theExtent)
201 NCollection_ListNode** ppNewData1 = NULL;
202 NCollection_ListNode** ppNewData2 = NULL;
203 Standard_Integer newBuck;
204 if (BeginResize (theExtent, newBuck, ppNewData1, ppNewData2))
208 for (Standard_Integer aBucketIter = 0; aBucketIter <= NbBuckets(); ++aBucketIter)
210 if (myData1[aBucketIter])
212 IndexedMapNode* p = (IndexedMapNode* )myData1[aBucketIter];
215 const size_t iK1 = HashCode (p->Key1(), newBuck);
216 IndexedMapNode* q = (IndexedMapNode* )p->Next();
217 p->Next() = ppNewData1[iK1];
224 EndResize (theExtent, newBuck, ppNewData1, (NCollection_ListNode**)
225 Standard::Reallocate(myData2, (newBuck + 1) * sizeof(NCollection_ListNode*)));
230 Standard_Integer Add (const TheKeyType& theKey1)
236 IndexedMapNode* aNode;
238 if (lookup(theKey1, aNode, aHash))
240 return aNode->Index();
242 const Standard_Integer aNewIndex = Increment();
243 aNode = new (this->myAllocator) IndexedMapNode (theKey1, aNewIndex, myData1[aHash]);
244 myData1[aHash] = aNode;
245 myData2[aNewIndex - 1] = aNode;
250 Standard_Integer Add (TheKeyType&& theKey1)
257 IndexedMapNode* aNode;
258 if (lookup(theKey1, aNode, aHash))
260 return aNode->Index();
262 const Standard_Integer aNewIndex = Increment();
263 aNode = new (this->myAllocator) IndexedMapNode(std::forward<TheKeyType>(theKey1),
264 aNewIndex, myData1[aHash]);
265 myData1[aHash] = aNode;
266 myData2[aNewIndex - 1] = aNode;
271 Standard_Boolean Contains (const TheKeyType& theKey1) const
274 return lookup(theKey1, p);
278 void Substitute (const Standard_Integer theIndex,
279 const TheKeyType& theKey1)
281 Standard_OutOfRange_Raise_if (theIndex < 1 || theIndex > Extent(),
282 "NCollection_IndexedMap::Substitute : "
283 "Index is out of range");
285 // check if theKey1 is not already in the map
286 IndexedMapNode* aNode;
288 if (lookup(theKey1, aNode, aHash))
290 if (aNode->Index() != theIndex)
292 throw Standard_DomainError ("NCollection_IndexedMap::Substitute : "
293 "Attempt to substitute existing key");
295 aNode->Key1() = theKey1;
298 // Find the node for the index I
299 aNode = (IndexedMapNode* )myData2[theIndex - 1];
301 // remove the old key
302 const size_t iK = HashCode (aNode->Key1(), NbBuckets());
303 IndexedMapNode * q = (IndexedMapNode *) myData1[iK];
305 myData1[iK] = (IndexedMapNode *) aNode->Next();
308 while (q->Next() != aNode)
309 q = (IndexedMapNode *) q->Next();
310 q->Next() = aNode->Next();
314 aNode->Key1() = theKey1;
315 aNode->Next() = myData1[aHash];
316 myData1[aHash] = aNode;
319 //! Swaps two elements with the given indices.
320 void Swap (const Standard_Integer theIndex1,
321 const Standard_Integer theIndex2)
323 Standard_OutOfRange_Raise_if (theIndex1 < 1 || theIndex1 > Extent()
324 || theIndex2 < 1 || theIndex2 > Extent(), "NCollection_IndexedMap::Swap");
326 if (theIndex1 == theIndex2)
331 IndexedMapNode* aP1 = (IndexedMapNode* )myData2[theIndex1 - 1];
332 IndexedMapNode* aP2 = (IndexedMapNode* )myData2[theIndex2 - 1];
333 std::swap (aP1->Index(), aP2->Index());
334 myData2[theIndex2 - 1] = aP1;
335 myData2[theIndex1 - 1] = aP2;
339 void RemoveLast (void)
341 const Standard_Integer aLastIndex = Extent();
342 Standard_OutOfRange_Raise_if (aLastIndex == 0, "NCollection_IndexedMap::RemoveLast");
344 // Find the node for the last index and remove it
345 IndexedMapNode* p = (IndexedMapNode* )myData2[aLastIndex - 1];
346 myData2[aLastIndex - 1] = NULL;
349 const size_t iK1 = HashCode (p->Key1(), NbBuckets());
350 IndexedMapNode* q = (IndexedMapNode *) myData1[iK1];
352 myData1[iK1] = (IndexedMapNode *) p->Next();
355 while (q->Next() != p)
356 q = (IndexedMapNode *) q->Next();
357 q->Next() = p->Next();
359 p->~IndexedMapNode();
360 this->myAllocator->Free(p);
364 //! Remove the key of the given index.
365 //! Caution! The index of the last key can be changed.
366 void RemoveFromIndex(const Standard_Integer theIndex)
368 Standard_OutOfRange_Raise_if(theIndex < 1 || theIndex > Extent(), "NCollection_IndexedMap::RemoveFromIndex");
369 const Standard_Integer aLastInd = Extent();
370 if (theIndex != aLastInd)
372 Swap(theIndex, aLastInd);
377 //! Remove the given key.
378 //! Caution! The index of the last key can be changed.
379 Standard_Boolean RemoveKey (const TheKeyType& theKey1)
381 Standard_Integer anIndToRemove = FindIndex(theKey1);
382 if (anIndToRemove < 1)
384 return Standard_False;
387 RemoveFromIndex (anIndToRemove);
388 return Standard_True;
392 const TheKeyType& FindKey (const Standard_Integer theIndex) const
394 Standard_OutOfRange_Raise_if (theIndex < 1 || theIndex > Extent(), "NCollection_IndexedMap::FindKey");
395 IndexedMapNode* pNode2 = (IndexedMapNode* )myData2[theIndex - 1];
396 return pNode2->Key1();
400 const TheKeyType& operator() (const Standard_Integer theIndex) const
401 { return FindKey (theIndex); }
404 Standard_Integer FindIndex(const TheKeyType& theKey1) const
406 IndexedMapNode* aNode;
407 if (lookup(theKey1, aNode))
409 return aNode->Index();
414 //! Clear data. If doReleaseMemory is false then the table of
415 //! buckets is not released and will be reused.
416 void Clear(const Standard_Boolean doReleaseMemory = Standard_False)
417 { Destroy (IndexedMapNode::delNode, doReleaseMemory); }
419 //! Clear data and reset allocator
420 void Clear (const Handle(NCollection_BaseAllocator)& theAllocator)
422 Clear(theAllocator != this->myAllocator);
423 this->myAllocator = ( ! theAllocator.IsNull() ? theAllocator :
424 NCollection_BaseAllocator::CommonBaseAllocator() );
428 virtual ~NCollection_IndexedMap (void)
432 Standard_Integer Size(void) const
437 //! Lookup for particular key in map.
438 //! @param[in] theKey key to compute hash
439 //! @param[out] theNode the detected node with equal key. Can be null.
440 //! @param[out] theHash computed bounded hash code for current key.
441 //! @return true if key is found
442 Standard_Boolean lookup(const TheKeyType& theKey, IndexedMapNode*& theNode, size_t& theHash) const
444 theHash = HashCode(theKey, NbBuckets());
446 return Standard_False; // Not found
447 for (theNode = (IndexedMapNode*)myData1[theHash];
448 theNode; theNode = (IndexedMapNode*)theNode->Next())
450 if (IsEqual(theNode->Key1(), theKey))
451 return Standard_True;
453 return Standard_False; // Not found
456 //! Lookup for particular key in map.
457 //! @param[in] theKey key to compute hash
458 //! @param[out] theNode the detected node with equal key. Can be null.
459 //! @return true if key is found
460 Standard_Boolean lookup(const TheKeyType& theKey, IndexedMapNode*& theNode) const
463 return Standard_False; // Not found
464 for (theNode = (IndexedMapNode*)myData1[HashCode(theKey, NbBuckets())];
465 theNode; theNode = (IndexedMapNode*)theNode->Next())
467 if (IsEqual(theNode->Key1(), theKey))
469 return Standard_True;
472 return Standard_False; // Not found
475 bool IsEqual(const TheKeyType& theKey1,
476 const TheKeyType& theKey2) const
478 return myHasher(theKey1, theKey2);
481 size_t HashCode(const TheKeyType& theKey,
482 const int theUpperBound) const
484 return myHasher(theKey) % theUpperBound + 1;