0024971: Incomplete interface of NCollection classes
[occt.git] / src / NCollection / NCollection_IndexedMap.hxx
1 // Created on: 2002-04-24
2 // Created by: Alexander KARTOMIN (akm)
3 // Copyright (c) 2002-2014 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
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.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #ifndef NCollection_IndexedMap_HeaderFile
17 #define NCollection_IndexedMap_HeaderFile
18
19 #include <NCollection_BaseMap.hxx>
20 #include <NCollection_TListNode.hxx>
21 #include <NCollection_StlIterator.hxx>
22 #include <Standard_NoSuchObject.hxx>
23 #include <Standard_ImmutableObject.hxx>
24
25 #include <NCollection_DefaultHasher.hxx>
26
27 #include <Standard_OutOfRange.hxx>
28
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  */            
39
40 template < class TheKeyType, 
41            class Hasher = NCollection_DefaultHasher<TheKeyType> > 
42 class NCollection_IndexedMap : public NCollection_BaseMap
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) :
54       NCollection_TListNode<TheKeyType> (theKey1, theNext1),
55       myKey2(theKey2),
56       myNext2((IndexedMapNode*)theNext2)
57     { 
58     }
59     //! Key1
60     TheKeyType& Key1 (void)
61     { return this->ChangeValue(); }
62     //! Key2
63     const Standard_Integer& Key2 (void)
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.
84   class Iterator
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
96     Standard_Boolean More(void) const
97     { return (myMap != NULL) && (myIndex <= myMap->Extent()); }
98     //! Make a step along the collection
99     void Next(void)
100     { myIndex++; }
101     //! Value access
102     const TheKeyType& Value(void) const
103     {
104       Standard_NoSuchObject_Raise_if(!More(), "NCollection_IndexedMap::Iterator::Value");
105       return myMap->FindKey(myIndex);
106     }
107     //! Value change access denied - use Substitute
108     TheKeyType& ChangeValue(void) const
109     {  
110       Standard_ImmutableObject::Raise ("impossible to ChangeValue");
111       return * (TheKeyType *) NULL; // This for compiler
112     }
113     //! Performs comparison of two iterators.
114     Standard_Boolean IsEqual (const Iterator& theOther) const
115     {
116       return myMap == theOther.myMap && myIndex == theOther.myIndex;
117     }
118     
119   private:
120     NCollection_IndexedMap * myMap;   // Pointer to the map being iterated
121     Standard_Integer         myIndex; // Current index
122   };
123   
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   
133  public:
134   // ---------- PUBLIC METHODS ------------
135
136   //! Constructor
137   NCollection_IndexedMap (const Standard_Integer NbBuckets=1,
138                           const Handle(NCollection_BaseAllocator)& theAllocator=0L)
139   : NCollection_BaseMap (NbBuckets, Standard_False, theAllocator) {}
140
141   //! Copy constructor
142   NCollection_IndexedMap (const NCollection_IndexedMap& theOther)
143   : NCollection_BaseMap (theOther.NbBuckets(), Standard_False, theOther.myAllocator)
144   { *this = theOther; }
145
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   {
150     this->exchangeMapsData (theOther);
151   }
152
153   //! Assign
154   NCollection_IndexedMap& Assign (const NCollection_IndexedMap& theOther)
155   { 
156     if (this == &theOther)
157       return *this;
158
159     Clear(theOther.myAllocator);
160     ReSize (theOther.Extent()-1);
161     Standard_Integer i, iLength=theOther.Extent();
162     for (i=1; i<=iLength; i++)
163     {
164       TheKeyType aKey1 = theOther(i);
165       Standard_Integer iK1 = Hasher::HashCode (aKey1, NbBuckets());
166       Standard_Integer iK2 = ::HashCode (i, NbBuckets());
167       IndexedMapNode * pNode = new (this->myAllocator) IndexedMapNode (aKey1, i, 
168                                                                        myData1[iK1], 
169                                                                        myData2[iK2]);
170       myData1[iK1] = pNode;
171       myData2[iK2] = pNode;
172       Increment();
173     }
174     return *this;
175   }
176
177   //! Assignment operator
178   NCollection_IndexedMap& operator= (const NCollection_IndexedMap& theOther)
179   {
180     return Assign (theOther);
181   }
182
183   //! ReSize
184   void ReSize (const Standard_Integer N)
185   {
186     NCollection_ListNode** ppNewData1 = NULL;
187     NCollection_ListNode** ppNewData2 = NULL;
188     Standard_Integer newBuck;
189     if (BeginResize (N, newBuck, ppNewData1, ppNewData2))
190     {
191       if (myData1) 
192       {
193         IndexedMapNode *p, *q;
194         Standard_Integer i, iK1, iK2;
195         for (i = 0; i <= NbBuckets(); i++) 
196         {
197           if (myData1[i]) 
198           {
199             p = (IndexedMapNode *) myData1[i];
200             while (p) 
201             {
202               iK1 =Hasher::HashCode(p->Key1(), newBuck);
203               q = (IndexedMapNode*) p->Next();
204               p->Next()  = ppNewData1[iK1];
205               ppNewData1[iK1] = p;
206               if (p->Key2() > 0) 
207               {
208                 iK2 = ::HashCode (p->Key2(), newBuck);
209                 p->Next2() = (IndexedMapNode*)ppNewData2[iK2];
210                 ppNewData2[iK2] = p;
211               }
212               p = q;
213             }
214           }
215         }
216       }
217       EndResize (N, newBuck, ppNewData1, ppNewData2);
218     }
219   }
220
221   //! Add
222   Standard_Integer Add (const TheKeyType& theKey1)
223   {
224     if (Resizable()) 
225       ReSize(Extent());
226     Standard_Integer iK1 = Hasher::HashCode (theKey1, NbBuckets());
227     IndexedMapNode * pNode;
228     pNode = (IndexedMapNode *) myData1[iK1];
229     while (pNode)
230     {
231       if (Hasher::IsEqual (pNode->Key1(), theKey1))
232         return pNode->Key2();
233       pNode = (IndexedMapNode *) pNode->Next();
234     }
235     Increment();
236     Standard_Integer iK2 = ::HashCode(Extent(),NbBuckets());
237     pNode = new (this->myAllocator) IndexedMapNode (theKey1, Extent(), 
238                                                     myData1[iK1], myData2[iK2]);
239     myData1[iK1] = pNode;
240     myData2[iK2] = pNode;
241     return Extent();
242   }
243
244   //! Contains
245   Standard_Boolean Contains (const TheKeyType& theKey1) const
246   {
247     if (IsEmpty()) 
248       return Standard_False;
249     Standard_Integer iK1 = Hasher::HashCode (theKey1, NbBuckets());
250     IndexedMapNode * pNode1;
251     pNode1 = (IndexedMapNode *) myData1[iK1];
252     while (pNode1) 
253     {
254       if (Hasher::IsEqual(pNode1->Key1(), theKey1)) 
255         return Standard_True;
256       pNode1 = (IndexedMapNode *) pNode1->Next();
257     }
258     return Standard_False;
259   }
260
261   //! Substitute
262   void Substitute (const Standard_Integer theIndex,
263                    const TheKeyType& theKey1)
264   {
265     Standard_OutOfRange_Raise_if (theIndex < 1 || theIndex > Extent(), "NCollection_IndexedMap::Substitute");
266
267     IndexedMapNode * p;
268     // check if theKey1 is not already in the map
269     Standard_Integer iK1 = Hasher::HashCode (theKey1, NbBuckets());
270     p = (IndexedMapNode *) myData1[iK1];
271     while (p) 
272     {
273       if (Hasher::IsEqual (p->Key1(), theKey1)) 
274         Standard_DomainError::Raise("NCollection_IndexedMap::Substitute");
275       p = (IndexedMapNode *) p->Next();
276     }
277
278     // Find the node for the index I
279     Standard_Integer iK2 = ::HashCode (theIndex, NbBuckets());
280     p = (IndexedMapNode *) myData2[iK2];
281     while (p) 
282     {
283       if (p->Key2() == theIndex) 
284         break;
285       p = (IndexedMapNode*) p->Next2();
286     }
287     
288     // remove the old key
289     Standard_Integer iK = Hasher::HashCode (p->Key1(), NbBuckets());
290     IndexedMapNode * q = (IndexedMapNode *) myData1[iK];
291     if (q == p)
292       myData1[iK] = (IndexedMapNode *) p->Next();
293     else 
294     {
295       while (q->Next() != p) 
296         q = (IndexedMapNode *) q->Next();
297       q->Next() = p->Next();
298     }
299
300     // update the node
301     p->Key1() = theKey1;
302     p->Next() = myData1[iK1];
303     myData1[iK1] = p;
304   }
305
306   //! RemoveLast
307   void RemoveLast (void)
308   {
309     Standard_OutOfRange_Raise_if (Extent() == 0, "NCollection_IndexedMap::RemoveLast");
310
311     IndexedMapNode * p, * q;
312     // Find the node for the last index and remove it
313     Standard_Integer iK2 = ::HashCode (Extent(), NbBuckets());
314     p = (IndexedMapNode *) myData2[iK2];
315     q = NULL;
316     while (p) 
317     {
318       if (p->Key2() == Extent()) 
319         break;
320       q = p;
321       p = (IndexedMapNode*) p->Next2();
322     }
323     if (q == NULL) 
324       myData2[iK2] = (IndexedMapNode *) p->Next2();
325     else 
326       q->Next2() = p->Next2();
327     
328     // remove the key
329     Standard_Integer iK1 = Hasher::HashCode (p->Key1(), NbBuckets());
330     q = (IndexedMapNode *) myData1[iK1];
331     if (q == p)
332       myData1[iK1] = (IndexedMapNode *) p->Next();
333     else 
334     {
335       while (q->Next() != p) 
336         q = (IndexedMapNode *) q->Next();
337       q->Next() = p->Next();
338     }
339     p->~IndexedMapNode();
340     this->myAllocator->Free(p);
341     Decrement();
342   }
343
344   //! FindKey
345   const TheKeyType& FindKey (const Standard_Integer theKey2) const
346   {
347     Standard_OutOfRange_Raise_if (theKey2 < 1 || theKey2 > Extent(), "NCollection_IndexedMap::FindKey");
348
349     IndexedMapNode * pNode2 =
350       (IndexedMapNode *) myData2[::HashCode(theKey2,NbBuckets())];
351     while (pNode2)
352     {
353       if (pNode2->Key2() == theKey2) 
354         return pNode2->Key1();
355       pNode2 = (IndexedMapNode*) pNode2->Next2();
356     }
357     Standard_NoSuchObject::Raise("NCollection_IndexedMap::FindKey");
358     return pNode2->Key1(); // This for compiler
359   }
360
361   //! operator ()
362   const TheKeyType& operator() (const Standard_Integer theKey2) const
363   { return FindKey (theKey2); }
364
365   //! FindIndex
366   Standard_Integer FindIndex(const TheKeyType& theKey1) const
367   {
368     if (IsEmpty()) return 0;
369     IndexedMapNode * pNode1 = 
370       (IndexedMapNode *) myData1[Hasher::HashCode(theKey1,NbBuckets())];
371     while (pNode1)
372     {
373       if (Hasher::IsEqual (pNode1->Key1(), theKey1)) 
374         return pNode1->Key2();
375       pNode1 = (IndexedMapNode*) pNode1->Next();
376     }
377     return 0;
378   }
379
380   //! Clear data. If doReleaseMemory is false then the table of
381   //! buckets is not released and will be reused.
382   void Clear(const Standard_Boolean doReleaseMemory = Standard_True)
383   { Destroy (IndexedMapNode::delNode, doReleaseMemory); }
384
385   //! Clear data and reset allocator
386   void Clear (const Handle(NCollection_BaseAllocator)& theAllocator)
387   { 
388     Clear();
389     this->myAllocator = ( ! theAllocator.IsNull() ? theAllocator :
390                     NCollection_BaseAllocator::CommonBaseAllocator() );
391   }
392
393   //! Destructor
394   ~NCollection_IndexedMap (void)
395   { Clear(); }
396
397   //! Size
398   Standard_Integer Size(void) const
399   { return Extent(); }
400 };
401
402 #endif