0025571: Avoid base Classes without virtual Destructors
[occt.git] / src / NCollection / NCollection_IndexedMap.hxx
CommitLineData
b311480e 1// Created on: 2002-04-24
2// Created by: Alexander KARTOMIN (akm)
973c2be1 3// Copyright (c) 2002-2014 OPEN CASCADE SAS
b311480e 4//
973c2be1 5// This file is part of Open CASCADE Technology software library.
b311480e 6//
d5f74e42 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
973c2be1 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.
b311480e 12//
973c2be1 13// Alternatively, this file may be used under the terms of Open CASCADE
14// commercial license or contractual agreement.
b311480e 15
7fd59977 16#ifndef NCollection_IndexedMap_HeaderFile
17#define NCollection_IndexedMap_HeaderFile
18
7fd59977 19#include <NCollection_BaseMap.hxx>
20#include <NCollection_TListNode.hxx>
79a35943 21#include <NCollection_StlIterator.hxx>
7fd59977 22#include <Standard_NoSuchObject.hxx>
23#include <Standard_ImmutableObject.hxx>
24
9991c9c2 25#include <NCollection_DefaultHasher.hxx>
26
7fd59977 27#include <Standard_OutOfRange.hxx>
7fd59977 28
7fd59977 29/**
30 * Purpose: An indexed map is used to store keys and to bind
31 * an index to them. Each new key stored in the map
32 * gets an index. Index are incremented as keys are
33 * stored in the map. A key can be found by the index
34 * and an index by the key. No key but the last can
35 * be removed so the indices are in the range 1..Extent.
36 * See the class Map from NCollection for a
37 * discussion about the number of buckets.
38 */
9991c9c2 39
40template < class TheKeyType,
41 class Hasher = NCollection_DefaultHasher<TheKeyType> >
ddf2fe8e 42class NCollection_IndexedMap : public NCollection_BaseMap
7fd59977 43{
44 // **************** Adaptation of the TListNode to the INDEXEDmap
45 private:
46 class IndexedMapNode : public NCollection_TListNode<TheKeyType>
47 {
48 public:
49 //! Constructor with 'Next'
50 IndexedMapNode (const TheKeyType& theKey1,
51 const Standard_Integer theKey2,
52 NCollection_ListNode* theNext1,
53 NCollection_ListNode* theNext2) :
e3a6386d 54 NCollection_TListNode<TheKeyType> (theKey1, theNext1),
55 myKey2(theKey2),
56 myNext2((IndexedMapNode*)theNext2)
7fd59977 57 {
7fd59977 58 }
59 //! Key1
60 TheKeyType& Key1 (void)
61 { return this->ChangeValue(); }
62 //! Key2
9e506120 63 Standard_Integer& Key2 (void)
7fd59977 64 { return myKey2; }
65 //! Next2
66 IndexedMapNode*& Next2 (void)
67 { return myNext2; }
68
69 //! Static deleter to be passed to BaseList
70 static void delNode (NCollection_ListNode * theNode,
71 Handle(NCollection_BaseAllocator)& theAl)
72 {
73 ((IndexedMapNode *) theNode)->~IndexedMapNode();
74 theAl->Free(theNode);
75 }
76
77 private:
78 Standard_Integer myKey2;
79 IndexedMapNode * myNext2;
80 };
81
82 public:
83 // **************** Implementation of the Iterator interface.
ddf2fe8e 84 class Iterator
7fd59977 85 {
86 public:
87 //! Empty constructor
88 Iterator (void) :
89 myMap(NULL),
90 myIndex(0) {}
91 //! Constructor
92 Iterator (const NCollection_IndexedMap& theMap) :
93 myMap((NCollection_IndexedMap *) &theMap),
94 myIndex(1) {}
95 //! Query if the end of collection is reached by iterator
ddf2fe8e 96 Standard_Boolean More(void) const
79a35943 97 { return (myMap != NULL) && (myIndex <= myMap->Extent()); }
7fd59977 98 //! Make a step along the collection
ddf2fe8e 99 void Next(void)
7fd59977 100 { myIndex++; }
101 //! Value access
ddf2fe8e 102 const TheKeyType& Value(void) const
7fd59977 103 {
e3a6386d 104 Standard_NoSuchObject_Raise_if(!More(), "NCollection_IndexedMap::Iterator::Value");
7fd59977 105 return myMap->FindKey(myIndex);
106 }
107 //! Value change access denied - use Substitute
ddf2fe8e 108 TheKeyType& ChangeValue(void) const
7fd59977 109 {
110 Standard_ImmutableObject::Raise ("impossible to ChangeValue");
111 return * (TheKeyType *) NULL; // This for compiler
112 }
79a35943 113 //! Performs comparison of two iterators.
ddf2fe8e 114 Standard_Boolean IsEqual (const Iterator& theOther) const
79a35943 115 {
116 return myMap == theOther.myMap && myIndex == theOther.myIndex;
117 }
7fd59977 118
7fd59977 119 private:
120 NCollection_IndexedMap * myMap; // Pointer to the map being iterated
121 Standard_Integer myIndex; // Current index
122 };
123
79a35943 124 //! Shorthand for a constant iterator type.
125 typedef NCollection_StlIterator<std::forward_iterator_tag, Iterator, TheKeyType, true> const_iterator;
126
127 //! Returns a const iterator pointing to the first element in the map.
128 const_iterator cbegin() const { return Iterator (*this); }
129
130 //! Returns a const iterator referring to the past-the-end element in the map.
131 const_iterator cend() const { return Iterator(); }
132
7fd59977 133 public:
134 // ---------- PUBLIC METHODS ------------
135
136 //! Constructor
137 NCollection_IndexedMap (const Standard_Integer NbBuckets=1,
ddf2fe8e 138 const Handle(NCollection_BaseAllocator)& theAllocator=0L)
139 : NCollection_BaseMap (NbBuckets, Standard_False, theAllocator) {}
7fd59977 140
141 //! Copy constructor
ddf2fe8e 142 NCollection_IndexedMap (const NCollection_IndexedMap& theOther)
143 : NCollection_BaseMap (theOther.NbBuckets(), Standard_False, theOther.myAllocator)
7fd59977 144 { *this = theOther; }
145
ab2db9a5 146 //! Exchange the content of two maps without re-allocations.
147 //! Notice that allocators will be swapped as well!
148 void Exchange (NCollection_IndexedMap& theOther)
149 {
ddf2fe8e 150 this->exchangeMapsData (theOther);
ab2db9a5 151 }
152
5e452c37 153 //! Assign.
154 //! This method does not change the internal allocator.
ddf2fe8e 155 NCollection_IndexedMap& Assign (const NCollection_IndexedMap& theOther)
7fd59977 156 {
157 if (this == &theOther)
158 return *this;
159
5e452c37 160 Clear();
7fd59977 161 ReSize (theOther.Extent()-1);
162 Standard_Integer i, iLength=theOther.Extent();
163 for (i=1; i<=iLength; i++)
164 {
165 TheKeyType aKey1 = theOther(i);
9991c9c2 166 Standard_Integer iK1 = Hasher::HashCode (aKey1, NbBuckets());
167 Standard_Integer iK2 = ::HashCode (i, NbBuckets());
7fd59977 168 IndexedMapNode * pNode = new (this->myAllocator) IndexedMapNode (aKey1, i,
169 myData1[iK1],
170 myData2[iK2]);
171 myData1[iK1] = pNode;
172 myData2[iK2] = pNode;
173 Increment();
174 }
175 return *this;
176 }
177
ddf2fe8e 178 //! Assignment operator
179 NCollection_IndexedMap& operator= (const NCollection_IndexedMap& theOther)
180 {
181 return Assign (theOther);
182 }
183
7fd59977 184 //! ReSize
185 void ReSize (const Standard_Integer N)
186 {
fd03ee4b 187 NCollection_ListNode** ppNewData1 = NULL;
188 NCollection_ListNode** ppNewData2 = NULL;
7fd59977 189 Standard_Integer newBuck;
ddf2fe8e 190 if (BeginResize (N, newBuck, ppNewData1, ppNewData2))
7fd59977 191 {
192 if (myData1)
193 {
194 IndexedMapNode *p, *q;
195 Standard_Integer i, iK1, iK2;
196 for (i = 0; i <= NbBuckets(); i++)
197 {
198 if (myData1[i])
199 {
200 p = (IndexedMapNode *) myData1[i];
201 while (p)
202 {
9991c9c2 203 iK1 =Hasher::HashCode(p->Key1(), newBuck);
7fd59977 204 q = (IndexedMapNode*) p->Next();
205 p->Next() = ppNewData1[iK1];
206 ppNewData1[iK1] = p;
207 if (p->Key2() > 0)
208 {
9991c9c2 209 iK2 = ::HashCode (p->Key2(), newBuck);
fd03ee4b 210 p->Next2() = (IndexedMapNode*)ppNewData2[iK2];
7fd59977 211 ppNewData2[iK2] = p;
212 }
213 p = q;
214 }
215 }
216 }
217 }
ddf2fe8e 218 EndResize (N, newBuck, ppNewData1, ppNewData2);
7fd59977 219 }
220 }
221
222 //! Add
223 Standard_Integer Add (const TheKeyType& theKey1)
224 {
225 if (Resizable())
226 ReSize(Extent());
9991c9c2 227 Standard_Integer iK1 = Hasher::HashCode (theKey1, NbBuckets());
7fd59977 228 IndexedMapNode * pNode;
229 pNode = (IndexedMapNode *) myData1[iK1];
230 while (pNode)
231 {
9991c9c2 232 if (Hasher::IsEqual (pNode->Key1(), theKey1))
7fd59977 233 return pNode->Key2();
234 pNode = (IndexedMapNode *) pNode->Next();
235 }
236 Increment();
9991c9c2 237 Standard_Integer iK2 = ::HashCode(Extent(),NbBuckets());
7fd59977 238 pNode = new (this->myAllocator) IndexedMapNode (theKey1, Extent(),
239 myData1[iK1], myData2[iK2]);
240 myData1[iK1] = pNode;
241 myData2[iK2] = pNode;
242 return Extent();
243 }
244
245 //! Contains
246 Standard_Boolean Contains (const TheKeyType& theKey1) const
247 {
248 if (IsEmpty())
249 return Standard_False;
9991c9c2 250 Standard_Integer iK1 = Hasher::HashCode (theKey1, NbBuckets());
7fd59977 251 IndexedMapNode * pNode1;
252 pNode1 = (IndexedMapNode *) myData1[iK1];
253 while (pNode1)
254 {
9991c9c2 255 if (Hasher::IsEqual(pNode1->Key1(), theKey1))
7fd59977 256 return Standard_True;
257 pNode1 = (IndexedMapNode *) pNode1->Next();
258 }
259 return Standard_False;
260 }
261
262 //! Substitute
263 void Substitute (const Standard_Integer theIndex,
264 const TheKeyType& theKey1)
265 {
985aed12 266 Standard_OutOfRange_Raise_if (theIndex < 1 || theIndex > Extent(),
267 "NCollection_IndexedMap::Substitute : "
268 "Index is out of range");
e3a6386d 269
7fd59977 270 IndexedMapNode * p;
271 // check if theKey1 is not already in the map
9991c9c2 272 Standard_Integer iK1 = Hasher::HashCode (theKey1, NbBuckets());
7fd59977 273 p = (IndexedMapNode *) myData1[iK1];
985aed12 274 while (p)
7fd59977 275 {
985aed12 276 if (Hasher::IsEqual (p->Key1(), theKey1))
277 {
278 if (p->Key2() != theIndex)
279 {
280 Standard_DomainError::Raise ("NCollection_IndexedMap::Substitute : "
281 "Attempt to substitute existing key");
282 }
283 p->Key1() = theKey1;
284 return;
285 }
7fd59977 286 p = (IndexedMapNode *) p->Next();
287 }
288
289 // Find the node for the index I
9991c9c2 290 Standard_Integer iK2 = ::HashCode (theIndex, NbBuckets());
7fd59977 291 p = (IndexedMapNode *) myData2[iK2];
292 while (p)
293 {
294 if (p->Key2() == theIndex)
295 break;
296 p = (IndexedMapNode*) p->Next2();
297 }
298
299 // remove the old key
9991c9c2 300 Standard_Integer iK = Hasher::HashCode (p->Key1(), NbBuckets());
7fd59977 301 IndexedMapNode * q = (IndexedMapNode *) myData1[iK];
302 if (q == p)
303 myData1[iK] = (IndexedMapNode *) p->Next();
304 else
305 {
306 while (q->Next() != p)
307 q = (IndexedMapNode *) q->Next();
308 q->Next() = p->Next();
309 }
310
311 // update the node
312 p->Key1() = theKey1;
313 p->Next() = myData1[iK1];
314 myData1[iK1] = p;
315 }
316
9e506120 317 //! Swaps two elements with the given indices.
318 void Swap (const Standard_Integer theIndex1,
319 const Standard_Integer theIndex2)
320 {
321 Standard_OutOfRange_Raise_if (theIndex1 < 1 || theIndex1 > Extent()
322 || theIndex2 < 1 || theIndex2 > Extent(), "NCollection_IndexedMap::Swap");
323
324 if (theIndex1 == theIndex2)
325 {
326 return;
327 }
328
329 const Standard_Integer aK1 = ::HashCode (theIndex1, NbBuckets());
330 const Standard_Integer aK2 = ::HashCode (theIndex2, NbBuckets());
331
332 IndexedMapNode* aP1 = (IndexedMapNode*) myData2[aK1];
333 IndexedMapNode* aP2 = (IndexedMapNode*) myData2[aK2];
334
335 if (aP1->Key2() == theIndex1)
336 {
337 myData2[aK1] = (IndexedMapNode *) aP1->Next2();
338 }
339 else
340 {
341 IndexedMapNode* aQ = aP1;
342 for (aP1 = aQ->Next2(); aP1->Key2() != theIndex1; aQ = aP1, aP1 = aQ->Next2()) { }
343
344 aQ->Next2() = aP1->Next2();
345 }
346
347 if (aP2->Key2() == theIndex2)
348 {
349 myData2[aK2] = (IndexedMapNode *) aP2->Next2();
350 }
351 else
352 {
353 IndexedMapNode* aQ = aP2;
354 for (aP2 = aQ->Next2(); aP2->Key2() != theIndex2; aQ = aP2, aP2 = aQ->Next2()) { }
355
356 aQ->Next2() = aP2->Next2();
357 }
358
359 std::swap (aP1->Key2(),
360 aP2->Key2());
361
362 aP1->Next2() = (IndexedMapNode*) myData2[aK2];
363 myData2[aK2] = aP1;
364
365 aP2->Next2() = (IndexedMapNode*) myData2[aK1];
366 myData2[aK1] = aP2;
367 }
368
7fd59977 369 //! RemoveLast
370 void RemoveLast (void)
371 {
e3a6386d 372 Standard_OutOfRange_Raise_if (Extent() == 0, "NCollection_IndexedMap::RemoveLast");
373
7fd59977 374 IndexedMapNode * p, * q;
375 // Find the node for the last index and remove it
9991c9c2 376 Standard_Integer iK2 = ::HashCode (Extent(), NbBuckets());
7fd59977 377 p = (IndexedMapNode *) myData2[iK2];
378 q = NULL;
379 while (p)
380 {
381 if (p->Key2() == Extent())
382 break;
383 q = p;
384 p = (IndexedMapNode*) p->Next2();
385 }
386 if (q == NULL)
387 myData2[iK2] = (IndexedMapNode *) p->Next2();
388 else
389 q->Next2() = p->Next2();
390
391 // remove the key
9991c9c2 392 Standard_Integer iK1 = Hasher::HashCode (p->Key1(), NbBuckets());
7fd59977 393 q = (IndexedMapNode *) myData1[iK1];
394 if (q == p)
395 myData1[iK1] = (IndexedMapNode *) p->Next();
396 else
397 {
398 while (q->Next() != p)
399 q = (IndexedMapNode *) q->Next();
400 q->Next() = p->Next();
401 }
402 p->~IndexedMapNode();
403 this->myAllocator->Free(p);
404 Decrement();
405 }
406
407 //! FindKey
408 const TheKeyType& FindKey (const Standard_Integer theKey2) const
409 {
e3a6386d 410 Standard_OutOfRange_Raise_if (theKey2 < 1 || theKey2 > Extent(), "NCollection_IndexedMap::FindKey");
411
7fd59977 412 IndexedMapNode * pNode2 =
9991c9c2 413 (IndexedMapNode *) myData2[::HashCode(theKey2,NbBuckets())];
7fd59977 414 while (pNode2)
415 {
416 if (pNode2->Key2() == theKey2)
417 return pNode2->Key1();
418 pNode2 = (IndexedMapNode*) pNode2->Next2();
419 }
420 Standard_NoSuchObject::Raise("NCollection_IndexedMap::FindKey");
421 return pNode2->Key1(); // This for compiler
422 }
423
424 //! operator ()
425 const TheKeyType& operator() (const Standard_Integer theKey2) const
426 { return FindKey (theKey2); }
427
428 //! FindIndex
429 Standard_Integer FindIndex(const TheKeyType& theKey1) const
430 {
431 if (IsEmpty()) return 0;
432 IndexedMapNode * pNode1 =
9991c9c2 433 (IndexedMapNode *) myData1[Hasher::HashCode(theKey1,NbBuckets())];
7fd59977 434 while (pNode1)
435 {
9991c9c2 436 if (Hasher::IsEqual (pNode1->Key1(), theKey1))
7fd59977 437 return pNode1->Key2();
438 pNode1 = (IndexedMapNode*) pNode1->Next();
439 }
440 return 0;
441 }
442
443 //! Clear data. If doReleaseMemory is false then the table of
444 //! buckets is not released and will be reused.
445 void Clear(const Standard_Boolean doReleaseMemory = Standard_True)
ddf2fe8e 446 { Destroy (IndexedMapNode::delNode, doReleaseMemory); }
7fd59977 447
448 //! Clear data and reset allocator
449 void Clear (const Handle(NCollection_BaseAllocator)& theAllocator)
450 {
451 Clear();
452 this->myAllocator = ( ! theAllocator.IsNull() ? theAllocator :
453 NCollection_BaseAllocator::CommonBaseAllocator() );
454 }
455
456 //! Destructor
6928e351 457 virtual ~NCollection_IndexedMap (void)
7fd59977 458 { Clear(); }
459
460 //! Size
ddf2fe8e 461 Standard_Integer Size(void) const
7fd59977 462 { return Extent(); }
7fd59977 463};
464
7fd59977 465#endif