• Main Page
  • Related Pages
  • Namespaces
  • Data Structures
  • Files
  • File List
  • Globals

MT_hashtable.h

Go to the documentation of this file.
00001 /** @file
00002 
00003   A brief file description
00004 
00005   @section license License
00006 
00007   Licensed to the Apache Software Foundation (ASF) under one
00008   or more contributor license agreements.  See the NOTICE file
00009   distributed with this work for additional information
00010   regarding copyright ownership.  The ASF licenses this file
00011   to you under the Apache License, Version 2.0 (the
00012   "License"); you may not use this file except in compliance
00013   with the License.  You may obtain a copy of the License at
00014 
00015       http://www.apache.org/licenses/LICENSE-2.0
00016 
00017   Unless required by applicable law or agreed to in writing, software
00018   distributed under the License is distributed on an "AS IS" BASIS,
00019   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00020   See the License for the specific language governing permissions and
00021   limitations under the License.
00022  */
00023 
00024 /****************************************************************************
00025 
00026   MT_hashtable.h
00027 
00028   Multithread Safe Hash table implementation
00029 
00030 
00031  ****************************************************************************/
00032 #ifndef MT_HASHTABLE_H_
00033 #define MT_HASHTABLE_H_
00034 //#include "Lock.h"
00035 
00036 #define MT_HASHTABLE_PARTITION_BITS 6
00037 #define MT_HASHTABLE_PARTITIONS (1<<MT_HASHTABLE_PARTITION_BITS)
00038 #define MT_HASHTABLE_PARTITION_MASK    (MT_HASHTABLE_PARTITIONS - 1)
00039 #define MT_HASHTABLE_MAX_CHAIN_AVG_LEN 4
00040 template<class key_t, class data_t> struct HashTableEntry
00041 {
00042   key_t key;
00043   data_t data;
00044   HashTableEntry *next;
00045 
00046   static HashTableEntry *alloc()
00047   {
00048     return (HashTableEntry *)ats_malloc(sizeof(HashTableEntry));
00049   }
00050 
00051   static void free(HashTableEntry * entry)
00052   {
00053     ats_free(entry);
00054   }
00055 };
00056 
00057 /*
00058 struct MT_ListEntry{
00059   MT_ListEntry():next(NULL),prev(NULL){}
00060   MT_ListEntry* next;
00061   MT_ListEntry* prev;
00062 };
00063 
00064 #define INIT_CHAIN_HEAD(h) {(h)->next = (h)->prev = (h);}
00065 #define APPEND_TO_CHAIN(h, p) {(p)->next = (h)->next; (h)->next->prev = (p); (p)->prev = (h); (h)->next = (p);}
00066 #define REMOVE_FROM_CHAIN(p) {(p)->next->prev = (p)->prev; (p)->prev->next = (p)->next; (p)->prev = (p)->next = NULL;}
00067 #define GET_OBJ_PTR(p, type, offset) ((type*)((char*)(p) - offset))
00068 */
00069 
00070 template<class key_t, class data_t> class HashTableIteratorState {
00071 public:
00072 HashTableIteratorState():cur_buck(-1), ppcur(NULL) {
00073   }
00074 
00075   int cur_buck;
00076   HashTableEntry<key_t, data_t> **ppcur;
00077 };
00078 
00079 template<class key_t, class data_t> class IMTHashTable {
00080 public:
00081   IMTHashTable(int size, bool(*gc_func) (data_t) = NULL, void (*pre_gc_func) (void) = NULL) {
00082     m_gc_func = gc_func;
00083     m_pre_gc_func = pre_gc_func;
00084     bucket_num = size;
00085     cur_size = 0;
00086     buckets = new HashTableEntry<key_t, data_t> *[bucket_num];
00087     memset(buckets, 0, bucket_num * sizeof(HashTableEntry<key_t, data_t> *));
00088   }
00089   ~IMTHashTable() {
00090     reset();
00091   }
00092 
00093   int getBucketNum()
00094   {
00095     return bucket_num;
00096   }
00097   int getCurSize()
00098   {
00099     return cur_size;
00100   }
00101 
00102   int bucket_id(key_t key, int a_bucket_num)
00103   {
00104     return (int) (((key >> MT_HASHTABLE_PARTITION_BITS) ^ key) % a_bucket_num);
00105   }
00106 
00107   int bucket_id(key_t key)
00108   {
00109     return bucket_id(key, bucket_num);
00110   }
00111 
00112   void reset()
00113   {
00114     HashTableEntry<key_t, data_t> *tmp;
00115     for (int i = 0; i < bucket_num; i++) {
00116       tmp = buckets[i];
00117       while (tmp) {
00118         buckets[i] = tmp->next;
00119         HashTableEntry<key_t, data_t>::free(tmp);
00120         tmp = buckets[i];
00121       }
00122     }
00123     delete[]buckets;
00124     buckets = NULL;
00125   }
00126 
00127   data_t insert_entry(key_t key, data_t data);
00128   data_t remove_entry(key_t key);
00129   data_t lookup_entry(key_t key);
00130 
00131   data_t first_entry(int bucket_id, HashTableIteratorState<key_t, data_t> *s);
00132   static data_t next_entry(HashTableIteratorState<key_t, data_t> *s);
00133   static data_t cur_entry(HashTableIteratorState<key_t, data_t> *s);
00134   data_t remove_entry(HashTableIteratorState<key_t, data_t> *s);
00135 
00136   void GC(void)
00137   {
00138     if (m_gc_func == NULL)
00139       return;
00140     if (m_pre_gc_func)
00141       m_pre_gc_func();
00142     for (int i = 0; i < bucket_num; i++) {
00143       HashTableEntry<key_t, data_t> *cur = buckets[i];
00144       HashTableEntry<key_t, data_t> *prev = NULL;
00145       HashTableEntry<key_t, data_t> *next = NULL;
00146       while (cur != NULL) {
00147         next = cur->next;
00148         if (m_gc_func(cur->data)) {
00149           if (prev != NULL)
00150             prev->next = next;
00151           else
00152             buckets[i] = next;
00153           delete cur;
00154           cur_size--;
00155         } else {
00156           prev = cur;
00157         }
00158         cur = next;
00159       }
00160     }
00161   }
00162 
00163   void resize(int size)
00164   {
00165     int new_bucket_num = size;
00166     HashTableEntry<key_t, data_t> **new_buckets = new HashTableEntry<key_t, data_t> *[new_bucket_num];
00167     memset(new_buckets, 0, new_bucket_num * sizeof(HashTableEntry<key_t, data_t> *));
00168 
00169     for (int i = 0; i < bucket_num; i++) {
00170       HashTableEntry<key_t, data_t> *cur = buckets[i];
00171       HashTableEntry<key_t, data_t> *next = NULL;
00172       while (cur != NULL) {
00173         next = cur->next;
00174         int new_id = bucket_id(cur->key, new_bucket_num);
00175         cur->next = new_buckets[new_id];
00176         new_buckets[new_id] = cur;
00177         cur = next;
00178       }
00179       buckets[i] = NULL;
00180     }
00181     delete[]buckets;
00182     buckets = new_buckets;
00183     bucket_num = new_bucket_num;
00184   }
00185 
00186 private:
00187   HashTableEntry<key_t, data_t> **buckets;
00188   int cur_size;
00189   int bucket_num;
00190   bool(*m_gc_func) (data_t);
00191   void (*m_pre_gc_func) (void);
00192 
00193 private:
00194   IMTHashTable();
00195   IMTHashTable(IMTHashTable &);
00196 };
00197 
00198 /*
00199  * we can use ClassAllocator here if the malloc performance becomes a problem
00200  */
00201 
00202 template<class key_t, class data_t> inline data_t IMTHashTable<key_t, data_t>::insert_entry(key_t key, data_t data)
00203 {
00204   int id = bucket_id(key);
00205   HashTableEntry<key_t, data_t> *cur = buckets[id];
00206   while (cur != NULL && cur->key != key) {
00207     cur = cur->next;
00208   }
00209   if (cur != NULL) {
00210     if (data == cur->data)
00211       return (data_t) 0;
00212     else {
00213       data_t tmp = cur->data;
00214       cur->data = data;
00215       // potential memory leak, need to check the return value by the caller
00216       return tmp;
00217     }
00218   }
00219 
00220   HashTableEntry<key_t, data_t> *newEntry = HashTableEntry<key_t, data_t>::alloc();
00221   newEntry->key = key;
00222   newEntry->data = data;
00223   newEntry->next = buckets[id];
00224   buckets[id] = newEntry;
00225   cur_size++;
00226   if (cur_size / bucket_num > MT_HASHTABLE_MAX_CHAIN_AVG_LEN) {
00227     GC();
00228     if (cur_size / bucket_num > MT_HASHTABLE_MAX_CHAIN_AVG_LEN)
00229       resize(bucket_num * 2);
00230   }
00231   return (data_t) 0;
00232 }
00233 
00234 
00235 template<class key_t, class data_t> inline data_t IMTHashTable<key_t, data_t>::remove_entry(key_t key)
00236 {
00237   int id = bucket_id(key);
00238   data_t ret = (data_t) 0;
00239   HashTableEntry<key_t, data_t> *cur = buckets[id];
00240   HashTableEntry<key_t, data_t> *prev = NULL;
00241   while (cur != NULL && cur->key != key) {
00242     prev = cur;
00243     cur = cur->next;
00244   }
00245   if (cur != NULL) {
00246     if (prev != NULL)
00247       prev->next = cur->next;
00248     else
00249       buckets[id] = cur->next;
00250     ret = cur->data;
00251     HashTableEntry<key_t, data_t>::free(cur);
00252     cur_size--;
00253   }
00254 
00255   return ret;
00256 }
00257 
00258 template<class key_t, class data_t> inline data_t IMTHashTable<key_t, data_t>::lookup_entry(key_t key)
00259 {
00260   int id = bucket_id(key);
00261   data_t ret = (data_t) 0;
00262   HashTableEntry<key_t, data_t> *cur = buckets[id];
00263   while (cur != NULL && cur->key != key) {
00264     cur = cur->next;
00265   }
00266   if (cur != NULL) {
00267     ret = cur->data;
00268   }
00269   return ret;
00270 }
00271 
00272 
00273 template<class key_t, class data_t>
00274   inline data_t IMTHashTable<key_t, data_t>::first_entry(int bucket_id, HashTableIteratorState<key_t, data_t> *s)
00275 {
00276   s->cur_buck = bucket_id;
00277   s->ppcur = &(buckets[bucket_id]);
00278   if (*(s->ppcur) != NULL)
00279     return (*(s->ppcur))->data;
00280   return (data_t) 0;
00281 }
00282 
00283 template<class key_t, class data_t>
00284   inline data_t IMTHashTable<key_t, data_t>::next_entry(HashTableIteratorState<key_t, data_t> *s)
00285 {
00286   if ((*(s->ppcur)) != NULL) {
00287     s->ppcur = &((*(s->ppcur))->next);
00288     if (*(s->ppcur) != NULL)
00289       return (*(s->ppcur))->data;
00290   }
00291   return (data_t) 0;
00292 }
00293 
00294 template<class key_t, class data_t>
00295   inline data_t IMTHashTable<key_t, data_t>::cur_entry(HashTableIteratorState<key_t, data_t> *s)
00296 {
00297   if (*(s->ppcur) == NULL)
00298     return (data_t) 0;
00299   return (*(s->ppcur))->data;
00300 }
00301 
00302 template<class key_t, class data_t>
00303   inline data_t IMTHashTable<key_t, data_t>::remove_entry(HashTableIteratorState<key_t, data_t> *s)
00304 {
00305   data_t data = (data_t) 0;
00306   HashTableEntry<key_t, data_t> *pEntry = *(s->ppcur);
00307   if (pEntry != NULL) {
00308     data = pEntry->data;
00309     (*(s->ppcur)) = pEntry->next;
00310     HashTableEntry<key_t, data_t>::free(pEntry);
00311     cur_size--;
00312   }
00313   return data;
00314 }
00315 
00316 template<class key_t, class data_t> class MTHashTable {
00317 public:
00318   MTHashTable(int size, bool(*gc_func) (data_t) = NULL, void (*pre_gc_func) (void) = NULL) {
00319     for (int i = 0; i < MT_HASHTABLE_PARTITIONS; i++) {
00320       locks[i] = new_ProxyMutex();
00321       hashTables[i] = new IMTHashTable<key_t, data_t> (size, gc_func, pre_gc_func);
00322       // INIT_CHAIN_HEAD(&chain_heads[i]);
00323       // last_GC_time[i] = 0;
00324     }
00325     //    cur_items = 0;
00326   }
00327   ~MTHashTable() {
00328     for (int i = 0; i < MT_HASHTABLE_PARTITIONS; i++) {
00329       locks[i] = NULL;
00330       delete hashTables[i];
00331     }
00332   }
00333   ProxyMutex *lock_for_key(key_t key)
00334   {
00335     return locks[part_num(key)];
00336   }
00337 
00338   int getSize()
00339   {
00340     return MT_HASHTABLE_PARTITIONS;
00341   }
00342   int part_num(key_t key)
00343   {
00344     return (int) (key & MT_HASHTABLE_PARTITION_MASK);
00345   }
00346   data_t insert_entry(key_t key, data_t data)
00347   {
00348     // ink_atomic_increment(&cur_items, 1);
00349     return hashTables[part_num(key)]->insert_entry(key, data);
00350   }
00351   data_t remove_entry(key_t key)
00352   {
00353     // ink_atomic_increment(&cur_items, -1);
00354     return hashTables[part_num(key)]->remove_entry(key);
00355   }
00356   data_t lookup_entry(key_t key)
00357   {
00358     return hashTables[part_num(key)]->lookup_entry(key);
00359   }
00360 
00361   data_t first_entry(int part_id, HashTableIteratorState<key_t, data_t> *s)
00362   {
00363     data_t ret = (data_t) 0;
00364     for (int i = 0; i < hashTables[part_id]->getBucketNum(); i++) {
00365       ret = hashTables[part_id]->first_entry(i, s);
00366       if (ret != (data_t) 0)
00367         return ret;
00368     }
00369     return (data_t) 0;
00370   }
00371 
00372   data_t cur_entry(int part_id, HashTableIteratorState<key_t, data_t> *s)
00373   {
00374     data_t data = IMTHashTable<key_t, data_t>::cur_entry(s);
00375     if (!data)
00376       data = next_entry(part_id, s);
00377     return data;
00378   };
00379   data_t next_entry(int part_id, HashTableIteratorState<key_t, data_t> *s)
00380   {
00381     data_t ret = IMTHashTable<key_t, data_t>::next_entry(s);
00382     if (ret != (data_t) 0)
00383       return ret;
00384     for (int i = s->cur_buck + 1; i < hashTables[part_id]->getBucketNum(); i++) {
00385       ret = hashTables[part_id]->first_entry(i, s);
00386       if (ret != (data_t) 0)
00387         return ret;
00388     }
00389     return (data_t) 0;
00390   }
00391   data_t remove_entry(int part_id, HashTableIteratorState<key_t, data_t> *s)
00392   {
00393     // ink_atomic_increment(&cur_items, -1);
00394     return hashTables[part_id]->remove_entry(s);
00395   }
00396 
00397 private:
00398   IMTHashTable<key_t, data_t> *hashTables[MT_HASHTABLE_PARTITIONS];
00399   Ptr<ProxyMutex> locks[MT_HASHTABLE_PARTITIONS];
00400   // MT_ListEntry chain_heads[MT_HASHTABLE_PARTITIONS];
00401   // int last_GC_time[MT_HASHTABLE_PARTITIONS];
00402   // int32_t cur_items;
00403 };
00404 
00405 #endif /* MT_HASHTABLE_H_ */

Generated by  doxygen 1.7.1