root/trunk/dep/include/g3dlite/G3D/Table.h @ 2

Revision 2, 17.2 kB (checked in by yumileroy, 17 years ago)

[svn] * Proper SVN structure

Original author: Neo2003
Date: 2008-10-02 16:23:55-05:00

Line 
1/**
2  @file Table.h
3
4  Templated hash table class.
5
6  @maintainer Morgan McGuire, matrix@graphics3d.com
7  @created 2001-04-22
8  @edited  2006-10-14
9  Copyright 2000-2006, Morgan McGuire.
10  All rights reserved.
11 */
12
13#ifndef G3D_TABLE_H
14#define G3D_TABLE_H
15
16#include "G3D/platform.h"
17#include "G3D/Array.h"
18#include "G3D/debug.h"
19#include "G3D/System.h"
20#include "G3D/g3dmath.h"
21#include "G3D/Crypto.h"
22#include <cstddef>
23#include <string>
24
25#ifdef G3D_WIN32
26#   pragma warning (push)
27    // Debug name too long warning
28#   pragma warning (disable : 4786)
29#endif
30
31
32template<typename Key>
33struct GHashCode{};
34
35template <>
36struct GHashCode<int>
37{
38    size_t operator()(int key) const { return static_cast<size_t>(key); }
39};
40
41template <>
42struct GHashCode<G3D::uint32>
43{
44    size_t operator()(G3D::uint32 key) const { return static_cast<size_t>(key); }
45};
46
47template <>
48struct GHashCode<G3D::uint64>
49{
50    size_t operator()(G3D::uint64 key) const { return static_cast<size_t>(key); }
51};
52
53template <>
54struct GHashCode<void*>
55{
56    size_t operator()(const void* key) const { return reinterpret_cast<size_t>(key); }
57};
58
59template<class T>
60struct GHashCode<T*>
61{
62    size_t operator()(const T* key) const { return reinterpret_cast<size_t>(key); }
63};
64
65template <>
66struct GHashCode<const std::string>
67{
68    size_t operator()(const std::string& key) const { return static_cast<size_t>(G3D::Crypto::crc32(key.c_str(), key.size())); }
69};
70
71template <>
72struct GHashCode<std::string>
73{
74    size_t operator()(const std::string& key) const { return static_cast<size_t>(G3D::Crypto::crc32(key.c_str(), key.size())); }
75};
76
77namespace G3D {
78
79
80/**
81 An unordered data structure mapping keys to values.
82
83 Key must be a pointer, an int, a std::string, a class with a hashCode() method,
84 or provide overloads for:
85
86  <PRE>
87    template<> struct GHashCode<class Key> {
88        size_t operator()(Key key) const { return reinterpret_cast<size_t>( ... ); }
89    };
90
91   bool operator==(const Key&, const Key&);
92  </PRE>
93
94 G3D pre-defines GHashCode functions for common types (like <CODE>int</CODE> and <CODE>std::string</CODE>).
95 If you use a Table with a different type you must write those functions yourself.  For example,
96 an enum would use:
97
98  <PRE>
99    template<> struct GHashCode<MyEnum> {
100        size_t operator()(MyEnum key) const { return reinterpret_cast<size_t>( key ); }
101    };
102  </PRE>
103
104  And rely on the default enum operator==.
105
106
107  Periodically check that debugGetLoad() is low (> 0.1).  When it gets near
108  1.0 your hash function is badly designed and maps too many inputs to
109  the same output.
110 */
111template<class Key, class Value, class HashFunc = GHashCode<Key> > 
112class Table {
113public:
114
115    /**
116     The pairs returned by iterator.
117     */
118    class Entry {
119    public:
120        Key    key;
121        Value  value;
122    };
123
124private:
125    /**
126     Linked list nodes used internally by HashTable.
127     */
128    class Node {
129    public:
130        size_t      hashCode;
131        Entry       entry;
132        Node*       next;
133
134       
135        /** Provide pooled allocation for speed. */
136        inline void* operator new (size_t size) {
137            return System::malloc(size);
138        }
139       
140        inline void operator delete (void* p) {
141            System::free(p);
142        }
143
144
145        Node(Key key, Value value, size_t hashCode, Node* next) {
146            this->entry.key   = key;
147            this->entry.value = value;
148            this->hashCode    = hashCode;
149            this->next        = next;
150        }
151
152        /**
153        Clones a whole chain;
154        */
155        Node* clone() {
156           return new Node(this->entry.key, this->entry.value, hashCode, (next == NULL) ? NULL : next->clone());
157        }
158    };
159
160    HashFunc m_HashFunc;
161
162    /**
163     Number of elements in the table.
164     */
165    size_t  _size;
166
167    /**
168     Array of Node*.
169     We don't use Array<Node*> because Table is lower level.
170     Some elements may be NULL.
171     */
172    Node**  bucket;
173   
174    /**
175     Length of the bucket array.
176     */
177    size_t  numBuckets;
178
179    /**
180     Re-hashes for a larger bucket size.
181     */
182    void resize(size_t numBuckets) {
183
184        Node** oldBucket = bucket;
185        bucket = (Node**)System::alignedMalloc(sizeof(Node*) * numBuckets, 16);
186        System::memset(bucket, 0, sizeof(Node*) * numBuckets);
187
188        for (size_t b = 0; b < this->numBuckets; b++) {
189            Node* node = oldBucket[b];
190         
191            while (node != NULL) {
192                Node* nextNode = node->next;
193       
194                // insert at the head of the list for bucket[i]
195                size_t i = node->hashCode % numBuckets;
196                node->next = bucket[i];
197                bucket[i] = node;
198       
199                node = nextNode;
200            }
201        }
202
203        System::alignedFree(oldBucket);
204        this->numBuckets = numBuckets;
205    }
206
207    void copyFrom(const Table<Key, Value>& h) {
208        this->_size = h._size;
209        this->numBuckets = h.numBuckets;
210        this->bucket = (Node**)System::alignedMalloc(sizeof(Node*) * numBuckets, 16);
211        System::memset(this->bucket, 0, sizeof(Node*) * numBuckets);
212        for (size_t b = 0; b < this->numBuckets; b++) {
213            if (h.bucket[b] != NULL) {
214                bucket[b] = h.bucket[b]->clone();
215            }
216        }
217    }
218
219    /**
220     Frees the heap structures for the nodes.
221     */
222    void freeMemory() {
223        for (size_t b = 0; b < numBuckets; b++) {
224           Node* node = bucket[b];
225           while (node != NULL) {
226                Node* next = node->next;
227                delete node;
228                node = next;
229           }
230        }
231        System::alignedFree(bucket);
232        bucket     = NULL;
233        numBuckets = 0;
234        _size     = 0;
235    }
236
237public:
238
239    /**
240     Creates an empty hash table.  This causes some heap allocation to occur.
241     */
242    Table() {
243        numBuckets = 10;
244        _size      = 0;
245        bucket     = (Node**)System::alignedMalloc(sizeof(Node*) * numBuckets, 16);
246        System::memset(bucket, 0, sizeof(Node*) * numBuckets);
247    }
248
249    /**
250    Destroys all of the memory allocated by the table, but does <B>not</B>
251    call delete on keys or values if they are pointers.  If you want to
252    deallocate things that the table points at, use getKeys() and Array::deleteAll()
253    to delete them.
254    */
255    virtual ~Table() {
256        freeMemory();
257    }
258
259    Table(const Table<Key, Value>& h) {
260        this->copyFrom(h);
261    }
262
263    Table& operator=(const Table<Key, Value>& h) {
264        // No need to copy if the argument is this
265        if (this != &h) {
266            // Free the existing nodes
267            freeMemory();
268            this->copyFrom(h);
269        }
270        return *this;
271    }
272
273    /**
274     Returns the length of the deepest bucket.
275     */
276    size_t debugGetDeepestBucketSize() const {
277        size_t deepest = 0;
278
279        for (size_t b = 0; b < numBuckets; b++) {
280            size_t  count = 0;
281            Node*   node = bucket[b];
282            while (node != NULL) {
283                node = node->next;
284                ++count;
285            }
286
287            if (count > deepest) {
288                deepest = count;
289            }
290        }
291
292        return deepest;
293    }
294
295    /**
296     A small load (close to zero) means the hash table is acting very
297     efficiently most of the time.  A large load (close to 1) means
298     the hash table is acting poorly-- all operations will be very slow.
299     A large load will result from a bad hash function that maps too
300     many keys to the same code.
301     */
302    double debugGetLoad() const {
303        return debugGetDeepestBucketSize() / (double)size();
304    }
305
306    /**
307     Returns the number of buckets.
308     */
309    size_t debugGetNumBuckets() const {
310        return numBuckets;
311    }
312
313    /**
314     C++ STL style iterator variable.  See begin().
315     */
316    class Iterator {
317    private:
318        friend class Table<Key, Value>;
319
320        /**
321         Bucket index.
322         */
323        size_t              index;
324
325        /**
326         Linked list node.
327         */
328        Node*               node;
329        Table<Key, Value>*  table;
330        size_t              numBuckets;
331        Node**              bucket;
332        bool                isDone;
333
334        /**
335         Creates the end iterator.
336         */
337        Iterator(const Table<Key, Value>* table) : table(const_cast<Table<Key, Value>*>(table)) {
338            isDone = true;
339        }
340
341        Iterator(const Table<Key, Value>* table, size_t numBuckets, Node** bucket) :
342            table(const_cast<Table<Key, Value>*>(table)),
343            numBuckets(numBuckets),
344            bucket(bucket) {
345           
346            if (numBuckets == 0) {
347                // Empty table
348                isDone = true;
349                return;
350            }
351
352            index = 0;
353            node = bucket[index];
354            isDone = false;
355            findNext();
356        }
357
358        /**
359         Finds the next element, setting isDone if one can't be found.
360         Looks at the current element first.
361         */
362        void findNext() {
363            while (node == NULL) {
364                index++;
365                if (index >= numBuckets) {
366                    isDone = true;
367                    break;
368                } else {
369                    node = bucket[index];
370                }
371            }
372        }
373
374    public:
375        inline bool operator!=(const Iterator& other) const {
376            return !(*this == other);
377        }
378
379        bool operator==(const Iterator& other) const {
380            if (other.isDone || isDone) {
381                // Common case; check against isDone.
382                return (isDone == other.isDone) && (other.table == table);
383            } else {
384                return
385                    (table == other.table) &&
386                    (node == other.node) && 
387                    (index == other.index);
388            }
389        }
390
391        /**
392         Pre increment.
393         */
394        Iterator& operator++() {
395            node = node->next;
396            findNext();
397            return *this;
398        }
399
400        /**
401         Post increment (slower than preincrement).
402         */
403        Iterator operator++(int) {
404            Iterator old = *this;
405            ++(*this);
406            return old;
407        }
408
409        const Entry& operator*() const {
410            return node->entry;
411        }
412
413        Entry* operator->() const {
414            return &(node->entry);
415        }
416
417        operator Entry*() const {
418            return &(node->entry);
419        }
420    };
421
422
423    /**
424     C++ STL style iterator method.  Returns the first Entry, which
425     contains a key and value.  Use preincrement (++entry) to get to
426     the next element.  Do not modify the table while iterating.
427     */
428    Iterator begin() const {
429        return Iterator(this, numBuckets, bucket);
430    }
431
432    /**
433     C++ STL style iterator method.  Returns one after the last iterator
434     element.
435     */
436    const Iterator end() const {
437        return Iterator(this);
438    }
439
440    /**
441     Removes all elements
442     */
443    void clear() {
444         freeMemory();
445         numBuckets = 20;
446         _size = 0;
447         bucket = (Node**)System::alignedMalloc(sizeof(Node*) * numBuckets, 16);
448         System::memset(bucket, 0, sizeof(Node*) * numBuckets);
449    }
450
451   
452    /**
453     Returns the number of keys.
454     */
455    size_t size() const {
456        return _size;
457    }
458
459
460    /**
461     If you insert a pointer into the key or value of a table, you are
462     responsible for deallocating the object eventually.  Inserting
463     key into a table is O(1), but may cause a potentially slow rehashing.
464     */
465    void set(const Key& key, const Value& value) {
466        size_t code = m_HashFunc(key);
467        size_t b = code % numBuckets;
468       
469        // Go to the bucket
470        Node* n = bucket[b];
471
472        // No bucket, so this must be the first
473        if (n == NULL) {
474            bucket[b] = new Node(key, value, code, NULL);
475            ++_size;
476            return;
477        }
478
479        size_t bucketLength = 1;
480
481        // Sometimes a bad hash code will cause all elements
482        // to collide.  Detect this case and don't rehash when
483        // it occurs; nothing good will come from the rehashing.
484        bool allSameCode = true;
485
486        // Try to find the node
487        do {
488            allSameCode = allSameCode && (code == n->hashCode);
489
490            if ((code == n->hashCode) && (n->entry.key == key)) {
491               // Replace the existing node.
492               n->entry.value = value;
493               return;
494            }
495
496            n = n->next;
497            ++bucketLength;
498        } while (n != NULL);
499
500        const size_t maxBucketLength = 5;
501        if ((bucketLength > maxBucketLength) & ! allSameCode && (numBuckets < _size * 20)) {
502            // This bucket was really large; rehash if all elements
503            // don't have the same hashcode the number of buckets is reasonable.
504            resize(numBuckets * 2 + 1);
505        }
506
507        // Not found; insert at the head.
508        b = code % numBuckets;
509        bucket[b] = new Node(key, value, code, bucket[b]);
510        ++_size;
511   }
512
513   /**
514    Removes an element from the table if it is present.  It is an error
515    to remove an element that isn't present.
516    */
517   void remove(const Key& key) {
518     
519      size_t code = m_HashFunc(key);
520      size_t b = code % numBuckets;
521
522      // Go to the bucket
523      Node* n = bucket[b];
524
525      // Make sure it was found
526      alwaysAssertM(n != NULL, "Tried to remove a key that was not in the table.");
527
528      Node* previous = NULL;
529
530      // Try to find the node
531      do {
532          if ((code == n->hashCode) && (n->entry.key == key)) {
533              // This is the node; remove it
534              if (previous == NULL) {
535                  bucket[b] = n->next;
536              } else {
537                  previous->next = n->next;
538              }
539              // Delete the node
540              delete n;
541              --_size;
542              return;
543          }
544
545          previous = n;
546          n = n->next;
547       } while (n != NULL);
548
549
550      alwaysAssertM(false, "Tried to remove a key that was not in the table.");
551   }
552
553   /**
554    Returns the value associated with key.
555    @deprecated Use get(key, val) or
556    */
557   Value& get(const Key& key) const {
558
559        size_t  code = m_HashFunc(key);
560        size_t b = code % numBuckets;
561
562        Node* node = bucket[b];
563
564        while (node != NULL) {
565            if ((node->hashCode == code) && (node->entry.key == key)) {
566                return node->entry.value;
567            }
568            node = node->next;
569        }
570
571        debugAssertM(false, "Key not found");
572        // The next line is here just to make
573        // a compiler warning go away.
574        return node->entry.value;
575   }
576
577   /**
578    If the key is present in the table, val is set to the associated value and returns true.
579    If the key is not present, returns false.
580    */
581   bool get(const Key& key, Value& val) const {
582      size_t code = m_HashFunc(key);
583      size_t b = code % numBuckets;
584
585      Node* node = bucket[b];
586
587      while (node != NULL) {
588          if ((node->hashCode == code) && (node->entry.key == key)) {
589             // found key
590             val = node->entry.value;
591             return true;
592          }
593          node = node->next;
594      }
595
596      // Failed to find key
597      return false;
598   }
599
600   /**
601    Returns true if key is in the table.
602    */
603   bool containsKey(const Key& key) const {
604       size_t code = m_HashFunc(key);
605       size_t b = code % numBuckets;
606
607       Node* node = bucket[b];
608
609       while (node != NULL) {
610           if ((node->hashCode == code) && (node->entry.key == key)) {
611              return true;
612           }
613           node = node->next;
614       } while (node != NULL);
615
616       return false;
617   }
618
619
620   /**
621    Short syntax for get.
622    */
623   inline Value& operator[](const Key &key) const {
624      return get(key);
625   }
626
627
628   /**
629    Returns an array of all of the keys in the table.
630    You can iterate over the keys to get the values.
631    */
632   Array<Key> getKeys() const {
633       Array<Key> keyArray;
634       getKeys(keyArray);
635       return keyArray;
636   }
637
638   void getKeys(Array<Key>& keyArray) const {
639       keyArray.resize(0, DONT_SHRINK_UNDERLYING_ARRAY);
640       for (size_t i = 0; i < numBuckets; i++) {
641           Node* node = bucket[i];
642           while (node != NULL) {
643               keyArray.append(node->entry.key);
644               node = node->next;
645           }
646       }
647   }
648
649   /**
650    Calls delete on all of the keys.  Does not clear the table,
651    however, so you are left with a table of dangling pointers.
652
653    Same as <CODE>getKeys().deleteAll();</CODE>
654
655    To delete all of the values, you may want something like
656    <PRE>
657        Array<Key> keys = table.getKeys();
658        Set<Value> value;
659        for (int k = 0; k < keys.length(); k++) {
660           value.insert(keys[k]);
661        }
662        value.getMembers().deleteAll();
663        keys.deleteAll();
664    </PRE>
665    */
666   void deleteKeys() {
667       getKeys().deleteAll();
668   }
669
670   /**
671    Calls delete on all of the values.  This is unsafe--
672    do not call unless you know that each value appears
673    at most once.
674
675    Does not clear the table, so you are left with a table
676    of dangling pointers.
677    */
678   void deleteValues() {
679       for (int i = 0; i < numBuckets; i++) {
680           Node* node = bucket[i];
681           while (node != NULL) {
682               delete node->entry.value;
683               node = node->next;
684           }
685       }
686   }
687};
688
689} // namespace
690
691#ifdef G3D_WIN32
692#   pragma warning (pop)
693#endif
694
695#endif
Note: See TracBrowser for help on using the browser.