Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
THashTable.cxx
Go to the documentation of this file.
1 // @(#)root/cont:$Id$
2 // Author: Fons Rademakers 27/09/95
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 /** \class THashTable
13 \ingroup Containers
14 THashTable implements a hash table to store TObject's. The hash
15 value is calculated using the value returned by the TObject's
16 Hash() function. Each class inheriting from TObject can override
17 Hash() as it sees fit.
18 
19 THashTable does not preserve the insertion order of the objects.
20 If the insertion order is important AND fast retrieval is needed
21 use THashList instead.
22 */
23 
24 #include "THashTable.h"
25 #include "TObjectTable.h"
26 #include "TList.h"
27 #include "TError.h"
28 #include "TROOT.h"
29 
30 ClassImp(THashTable);
31 
32 ////////////////////////////////////////////////////////////////////////////////
33 /// Create a THashTable object. Capacity is the initial hashtable capacity
34 /// (i.e. number of slots), by default kInitHashTableCapacity = 17, and
35 /// rehashlevel is the value at which a rehash will be triggered. I.e. when
36 /// the average size of the linked lists at a slot becomes longer than
37 /// rehashlevel then the hashtable will be resized and refilled to reduce
38 /// the collision rate to about 1. The higher the collision rate, i.e. the
39 /// longer the linked lists, the longer lookup will take. If rehashlevel=0
40 /// the table will NOT automatically be rehashed. Use Rehash() for manual
41 /// rehashing.
42 
43 THashTable::THashTable(Int_t capacity, Int_t rehashlevel)
44 {
45  if (capacity < 0) {
46  Warning("THashTable", "capacity (%d) < 0", capacity);
47  capacity = TCollection::kInitHashTableCapacity;
48  } else if (capacity == 0)
49  capacity = TCollection::kInitHashTableCapacity;
50 
51  fSize = (Int_t)TMath::NextPrime(TMath::Max(capacity,(int)TCollection::kInitHashTableCapacity));
52  fCont = new TList* [fSize];
53  memset(fCont, 0, fSize*sizeof(TList*));
54 
55  fEntries = 0;
56  fUsedSlots = 0;
57  if (rehashlevel < 2) rehashlevel = 0;
58  fRehashLevel = rehashlevel;
59 }
60 
61 ////////////////////////////////////////////////////////////////////////////////
62 /// Delete a hashtable. Objects are not deleted unless the THashTable is the
63 /// owner (set via SetOwner()).
64 
65 THashTable::~THashTable()
66 {
67  if (fCont) Clear();
68  delete [] fCont;
69  fCont = 0;
70  fSize = 0;
71 }
72 
73 ////////////////////////////////////////////////////////////////////////////////
74 /// Helper function doing the actual add to the table give a slot and object.
75 /// This does not take any lock.
76 
77 inline
78 void THashTable::AddImpl(Int_t slot, TObject *obj)
79 {
80  if (!fCont[slot]) {
81  fCont[slot] = new TList;
82  ++fUsedSlots;
83  }
84  fCont[slot]->Add(obj);
85  ++fEntries;
86 }
87 
88 ////////////////////////////////////////////////////////////////////////////////
89 /// Add object to the hash table. Its position in the table will be
90 /// determined by the value returned by its Hash() function.
91 
92 void THashTable::Add(TObject *obj)
93 {
94  if (IsArgNull("Add", obj)) return;
95 
96  Int_t slot = GetCheckedHashValue(obj);
97 
98  R__COLLECTION_WRITE_LOCKGUARD(ROOT::gCoreMutex);
99 
100  AddImpl(slot,obj);
101 
102  if (fRehashLevel && AverageCollisions() > fRehashLevel)
103  Rehash(fEntries);
104 }
105 
106 ////////////////////////////////////////////////////////////////////////////////
107 /// Add object to the hash table. Its position in the table will be
108 /// determined by the value returned by its Hash() function.
109 /// If and only if 'before' is in the same bucket as obj, obj is added
110 /// in front of 'before' within the bucket's list.
111 
112 void THashTable::AddBefore(const TObject *before, TObject *obj)
113 {
114  if (IsArgNull("Add", obj)) return;
115 
116  Int_t slot = GetCheckedHashValue(obj);
117 
118  R__COLLECTION_WRITE_LOCKGUARD(ROOT::gCoreMutex);
119  if (!fCont[slot]) {
120  fCont[slot] = new TList;
121  fUsedSlots++;
122  }
123  if (before && GetHashValue(before) == slot) {
124  fCont[slot]->AddBefore(before,obj);
125  } else {
126  fCont[slot]->Add(obj);
127  }
128  fEntries++;
129 
130  if (fRehashLevel && AverageCollisions() > fRehashLevel)
131  Rehash(fEntries);
132 }
133 
134 ////////////////////////////////////////////////////////////////////////////////
135 /// Add all objects from collection col to this collection.
136 /// Implemented for more efficient rehashing.
137 
138 void THashTable::AddAll(const TCollection *col)
139 {
140  R__COLLECTION_WRITE_LOCKGUARD(ROOT::gCoreMutex);
141 
142  // Hashing after AddAll can be much more expensive than
143  // hashing before, as we need to add more elements.
144  // We assume an ideal hash, i.e. fUsedSlots==fSize.
145  Int_t sumEntries=fEntries+col->GetEntries();
146  Bool_t rehashBefore=fRehashLevel && (sumEntries > fSize*fRehashLevel);
147  if (rehashBefore)
148  Rehash(sumEntries);
149 
150  // prevent Add from Rehashing
151  Int_t saveRehashLevel=fRehashLevel;
152  fRehashLevel=0;
153 
154  TCollection::AddAll(col);
155 
156  fRehashLevel=saveRehashLevel;
157  // If we didn't Rehash before, we might have to do it
158  // now, due to a non-perfect hash function.
159  if (!rehashBefore && fRehashLevel && AverageCollisions() > fRehashLevel)
160  Rehash(fEntries);
161 }
162 
163 ////////////////////////////////////////////////////////////////////////////////
164 /// Remove all objects from the table. Does not delete the objects
165 /// unless the THashTable is the owner (set via SetOwner()).
166 
167 void THashTable::Clear(Option_t *option)
168 {
169  R__COLLECTION_WRITE_LOCKGUARD(ROOT::gCoreMutex);
170 
171  for (int i = 0; i < fSize; i++) {
172  // option "nodelete" is passed when Clear is called from
173  // THashList::Clear() or THashList::Delete() or Rehash().
174  if (fCont[i]) {
175  if (IsOwner())
176  fCont[i]->SetOwner();
177  fCont[i]->Clear(option);
178  }
179  SafeDelete(fCont[i]);
180  }
181 
182  fEntries = 0;
183  fUsedSlots = 0;
184 }
185 
186 ////////////////////////////////////////////////////////////////////////////////
187 /// Returns the number of collisions for an object with a certain name
188 /// (i.e. number of objects in same slot in the hash table, i.e. length
189 /// of linked list).
190 
191 Int_t THashTable::Collisions(const char *name) const
192 {
193  Int_t slot = GetHashValue(name);
194 
195  R__COLLECTION_READ_LOCKGUARD(ROOT::gCoreMutex);
196 
197  if (fCont[slot]) return fCont[slot]->GetSize();
198  return 0;
199 }
200 
201 ////////////////////////////////////////////////////////////////////////////////
202 /// Returns the number of collisions for an object (i.e. number of objects
203 /// in same slot in the hash table, i.e. length of linked list).
204 
205 Int_t THashTable::Collisions(TObject *obj) const
206 {
207  if (IsArgNull("Collisions", obj)) return 0;
208 
209  Int_t slot = GetHashValue(obj);
210 
211  R__COLLECTION_READ_LOCKGUARD(ROOT::gCoreMutex);
212 
213  if (fCont[slot]) return fCont[slot]->GetSize();
214  return 0;
215 }
216 
217 ////////////////////////////////////////////////////////////////////////////////
218 /// Remove all objects from the table AND delete all heap based objects.
219 
220 void THashTable::Delete(Option_t *)
221 {
222  R__COLLECTION_WRITE_LOCKGUARD(ROOT::gCoreMutex);
223 
224  for (int i = 0; i < fSize; i++)
225  if (fCont[i]) {
226  fCont[i]->Delete();
227  SafeDelete(fCont[i]);
228  }
229 
230  fEntries = 0;
231  fUsedSlots = 0;
232 }
233 
234 ////////////////////////////////////////////////////////////////////////////////
235 /// Find object using its name. Uses the hash value returned by the
236 /// TString::Hash() after converting name to a TString.
237 
238 TObject *THashTable::FindObject(const char *name) const
239 {
240  Int_t slot = GetHashValue(name);
241 
242  R__COLLECTION_READ_LOCKGUARD(ROOT::gCoreMutex);
243 
244  if (fCont[slot]) return fCont[slot]->FindObject(name);
245  return 0;
246 }
247 
248 ////////////////////////////////////////////////////////////////////////////////
249 /// Find object using its hash value (returned by its Hash() member).
250 
251 TObject *THashTable::FindObject(const TObject *obj) const
252 {
253  if (IsArgNull("FindObject", obj)) return 0;
254 
255  Int_t slot = GetHashValue(obj);
256  if (fCont[slot]) return fCont[slot]->FindObject(obj);
257  return 0;
258 }
259 
260 ////////////////////////////////////////////////////////////////////////////////
261 /// Return the TList corresponding to object's name based hash value.
262 /// One can iterate this list "manually" to find, e.g. objects with
263 /// the same name.
264 
265 const TList *THashTable::GetListForObject(const char *name) const
266 {
267  Int_t slot = GetHashValue(name);
268 
269  R__COLLECTION_READ_LOCKGUARD(ROOT::gCoreMutex);
270 
271  return fCont[slot];
272 }
273 
274 ////////////////////////////////////////////////////////////////////////////////
275 /// Return the TList corresponding to object's hash value.
276 /// One can iterate this list "manually" to find, e.g. identical
277 /// objects.
278 
279 const TList *THashTable::GetListForObject(const TObject *obj) const
280 {
281  if (IsArgNull("GetListForObject", obj)) return 0;
282 
283  Int_t slot = GetHashValue(obj);
284 
285  R__COLLECTION_READ_LOCKGUARD(ROOT::gCoreMutex);
286 
287  return fCont[slot];
288 }
289 
290 ////////////////////////////////////////////////////////////////////////////////
291 /// Return address of pointer to obj
292 
293 TObject **THashTable::GetObjectRef(const TObject *obj) const
294 {
295  if (IsArgNull("GetObjectRef", obj)) return 0;
296 
297  Int_t slot = GetHashValue(obj);
298 
299  R__COLLECTION_READ_LOCKGUARD(ROOT::gCoreMutex);
300 
301  if (fCont[slot]) return fCont[slot]->GetObjectRef(obj);
302  return 0;
303 }
304 
305 ////////////////////////////////////////////////////////////////////////////////
306 /// Returns a hash table iterator.
307 
308 TIterator *THashTable::MakeIterator(Bool_t dir) const
309 {
310  return new THashTableIter(this, dir);
311 }
312 
313 ////////////////////////////////////////////////////////////////////////////
314 /// Print the collection header and its elements.
315 ///
316 /// If recurse is non-zero, descend into printing of
317 /// collection-entries with recurse - 1.
318 /// This means, if recurse is negative, the recursion is infinite.
319 ///
320 /// If option contains "details", Print will show the content of
321 /// each of the hash-slots.
322 ///
323 /// Option is passed recursively.
324 
325 void THashTable::Print(Option_t *option, Int_t recurse) const
326 {
327  if (strstr(option,"details")==nullptr) {
328  TCollection::Print(option,recurse);
329  return;
330  }
331 
332  PrintCollectionHeader(option);
333 
334  if (recurse != 0)
335  {
336  TROOT::IncreaseDirLevel();
337  for (Int_t cursor = 0; cursor < Capacity();
338  cursor++) {
339  printf("Slot #%d:\n",cursor);
340  if (fCont[cursor])
341  fCont[cursor]->Print();
342  else {
343  TROOT::IndentLevel();
344  printf("empty\n");
345  }
346 
347  }
348  TROOT::DecreaseDirLevel();
349  }
350 }
351 
352 ////////////////////////////////////////////////////////////////////////////////
353 /// Rehash the hashtable. If the collision rate becomes too high (i.e.
354 /// the average size of the linked lists become too long) then lookup
355 /// efficiency decreases since relatively long lists have to be searched
356 /// every time. To improve performance rehash the hashtable. This resizes
357 /// the table to newCapacity slots and refills the table. Use
358 /// AverageCollisions() to check if you need to rehash. Set checkObjValidity
359 /// to kFALSE if you know that all objects in the table are still valid
360 /// (i.e. have not been deleted from the system in the meanwhile).
361 
362 void THashTable::Rehash(Int_t newCapacity, Bool_t checkObjValidity)
363 {
364  THashTable *ht = new THashTable(newCapacity);
365 
366  R__COLLECTION_WRITE_LOCKGUARD(ROOT::gCoreMutex);
367 
368  TIter next(this);
369  TObject *obj;
370 
371  auto initialSize = GetEntries();
372 
373  if (checkObjValidity && TObject::GetObjectStat() && gObjectTable) {
374  while ((obj = next()))
375  if (gObjectTable->PtrIsValid(obj))
376  ht->AddImpl(ht->GetHashValue(obj),obj);
377  } else {
378  while ((obj = next()))
379  ht->AddImpl(ht->GetHashValue(obj),obj);
380  }
381 
382  if (initialSize != GetEntries()) {
383  // Somehow in the process of copy the pointer from one hash to
384  // other we ended up inducing the addition of more element to
385  // the table. Most likely those elements have not been copied ....
386  // i.e. Adding *during* the Rehashing is illegal and fatal.
387 
388  Fatal("Rehash",
389  "During the rehash of %p one or more element was added or removed. The initalize size was %d and now it is %d",
390  this, initialSize, GetEntries());
391 
392  }
393 
394  Clear("nodelete");
395  delete [] fCont;
396  fCont = ht->fCont;
397  ht->fCont = 0;
398 
399  fSize = ht->fSize; // idem
400  fEntries = ht->fEntries;
401  fUsedSlots = ht->fUsedSlots;
402 
403  // this should not happen, but it will prevent an endless loop
404  // in case of a very bad hash function
405  if (fRehashLevel && AverageCollisions() > fRehashLevel)
406  fRehashLevel = (int)AverageCollisions() + 1;
407 
408  delete ht;
409 }
410 
411 ////////////////////////////////////////////////////////////////////////////////
412 /// Remove object from the hashtable.
413 
414 TObject *THashTable::Remove(TObject *obj)
415 {
416  Int_t slot = GetHashValue(obj);
417 
418  R__COLLECTION_READ_LOCKGUARD(ROOT::gCoreMutex);
419 
420  if (fCont[slot]) {
421  R__COLLECTION_WRITE_LOCKGUARD(ROOT::gCoreMutex);
422 
423  TObject *ob = fCont[slot]->Remove(obj);
424  if (ob) {
425  fEntries--;
426  if (fCont[slot]->GetSize() == 0) {
427  SafeDelete(fCont[slot]);
428  fUsedSlots--;
429  }
430  return ob;
431  }
432  }
433  return 0;
434 }
435 
436 ////////////////////////////////////////////////////////////////////////////////
437 /// Remove object from the hashtable without using the hash value.
438 
439 TObject *THashTable::RemoveSlow(TObject *obj)
440 {
441 
442  R__COLLECTION_WRITE_LOCKGUARD(ROOT::gCoreMutex);
443 
444  for (int i = 0; i < fSize; i++) {
445  if (fCont[i]) {
446  TObject *ob = fCont[i]->Remove(obj);
447  if (ob) {
448  fEntries--;
449  if (fCont[i]->GetSize() == 0) {
450  SafeDelete(fCont[i]);
451  fUsedSlots--;
452  }
453  return ob;
454  }
455  }
456  }
457  return 0;
458 }
459 
460 /** \class THashTableIter
461 Iterator of hash table.
462 */
463 
464 ClassImp(THashTableIter);
465 
466 ////////////////////////////////////////////////////////////////////////////////
467 /// Create a hashtable iterator. By default the iteration direction
468 /// is kIterForward. To go backward use kIterBackward.
469 
470 THashTableIter::THashTableIter(const THashTable *ht, Bool_t dir)
471 {
472  fTable = ht;
473  fDirection = dir;
474  fListCursor = 0;
475  Reset();
476 }
477 
478 ////////////////////////////////////////////////////////////////////////////////
479 /// Copy ctor.
480 
481 THashTableIter::THashTableIter(const THashTableIter &iter) : TIterator(iter)
482 {
483  fTable = iter.fTable;
484  fDirection = iter.fDirection;
485  fCursor = iter.fCursor;
486  fListCursor = 0;
487  if (iter.fListCursor) {
488  fListCursor = (TListIter *)iter.fListCursor->GetCollection()->MakeIterator();
489  if (fListCursor)
490  fListCursor->operator=(*iter.fListCursor);
491  }
492 }
493 
494 ////////////////////////////////////////////////////////////////////////////////
495 /// Overridden assignment operator.
496 
497 TIterator &THashTableIter::operator=(const TIterator &rhs)
498 {
499  if (this != &rhs && rhs.IsA() == THashTableIter::Class()) {
500  const THashTableIter &rhs1 = (const THashTableIter &)rhs;
501  fTable = rhs1.fTable;
502  fDirection = rhs1.fDirection;
503  fCursor = rhs1.fCursor;
504  if (rhs1.fListCursor) {
505  // R__COLLECTION_READ_LOCKGUARD(ROOT::gCoreMutex);
506 
507  fListCursor = (TListIter *)rhs1.fListCursor->GetCollection()->MakeIterator();
508  if (fListCursor)
509  fListCursor->operator=(*rhs1.fListCursor);
510  }
511  }
512  return *this;
513 }
514 
515 ////////////////////////////////////////////////////////////////////////////////
516 /// Overloaded assignment operator.
517 
518 THashTableIter &THashTableIter::operator=(const THashTableIter &rhs)
519 {
520  if (this != &rhs) {
521  fTable = rhs.fTable;
522  fDirection = rhs.fDirection;
523  fCursor = rhs.fCursor;
524  if (rhs.fListCursor) {
525  // R__COLLECTION_READ_LOCKGUARD(ROOT::gCoreMutex);
526 
527  fListCursor = (TListIter *)rhs.fListCursor->GetCollection()->MakeIterator();
528  if (fListCursor)
529  fListCursor->operator=(*rhs.fListCursor);
530  }
531  }
532  return *this;
533 }
534 
535 ////////////////////////////////////////////////////////////////////////////////
536 /// Delete hashtable iterator.
537 
538 THashTableIter::~THashTableIter()
539 {
540  delete fListCursor;
541 }
542 
543 ////////////////////////////////////////////////////////////////////////////////
544 /// Return next object in hashtable. Returns 0 when no more objects in table.
545 
546 TObject *THashTableIter::Next()
547 {
548  // R__COLLECTION_READ_LOCKGUARD(ROOT::gCoreMutex);
549 
550  while (kTRUE) {
551  if (!fListCursor) {
552  int slot = NextSlot();
553  if (slot == -1) return 0;
554  fListCursor = new TListIter(fTable->fCont[slot], fDirection);
555  }
556 
557  TObject *obj = fListCursor->Next();
558  if (obj) return obj;
559 
560  SafeDelete(fListCursor);
561  }
562 }
563 
564 ////////////////////////////////////////////////////////////////////////////////
565 /// Returns index of next slot in table containing list to be iterated.
566 
567 Int_t THashTableIter::NextSlot()
568 {
569  // R__COLLECTION_READ_LOCKGUARD(ROOT::gCoreMutex);
570 
571  if (fDirection == kIterForward) {
572  for ( ; fCursor < fTable->Capacity() && fTable->fCont[fCursor] == 0;
573  fCursor++) { }
574 
575  if (fCursor < fTable->Capacity())
576  return fCursor++;
577 
578  } else {
579  for ( ; fCursor >= 0 && fTable->fCont[fCursor] == 0;
580  fCursor--) { }
581 
582  if (fCursor >= 0)
583  return fCursor--;
584  }
585  return -1;
586 }
587 
588 ////////////////////////////////////////////////////////////////////////////////
589 /// Reset the hashtable iterator. Either to beginning or end, depending on
590 /// the initial iteration direction.
591 
592 void THashTableIter::Reset()
593 {
594  if (fDirection == kIterForward)
595  fCursor = 0;
596  else
597  fCursor = fTable->Capacity() - 1;
598  SafeDelete(fListCursor);
599 }
600 
601 ////////////////////////////////////////////////////////////////////////////////
602 /// This operator compares two TIterator objects.
603 
604 Bool_t THashTableIter::operator!=(const TIterator &aIter) const
605 {
606  if (aIter.IsA() == THashTableIter::Class()) {
607  const THashTableIter &iter(dynamic_cast<const THashTableIter &>(aIter));
608  return (fListCursor != iter.fListCursor);
609  }
610  return false; // for base class we don't implement a comparison
611 }
612 
613 ////////////////////////////////////////////////////////////////////////////////
614 /// This operator compares two THashTableIter objects.
615 
616 Bool_t THashTableIter::operator!=(const THashTableIter &aIter) const
617 {
618  return (fListCursor != aIter.fListCursor);
619 }
620 
621 ////////////////////////////////////////////////////////////////////////////////
622 /// Return pointer to current object or nullptr.
623 
624 TObject *THashTableIter::operator*() const
625 {
626  return (fListCursor ? fListCursor->operator*() : nullptr);
627 }