/* * hashmap.{cc,hh} -- a hash table template * Eddie Kohler * * Copyright (c) 2000 Mazu Networks, Inc. * Copyright (c) 2003 International Computer Science Institute * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, subject to the conditions * listed in the Click LICENSE file. These conditions include: you must * preserve this copyright notice, and you cannot mention the copyright * holders in advertising related to the Software without their permission. * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This * notice is a summary of the Click LICENSE file; the license in that file is * legally binding. */ #ifndef CLICK_HASHMAP_CC #define CLICK_HASHMAP_CC #include #include #include CLICK_DECLS /** @cond never */ #define BIGHASHMAP_REARRANGE_ON_FIND 0 template void HashMap::initialize(HashMap_ArenaFactory *factory, size_t initial_nbuckets) { _nbuckets = initial_nbuckets; _buckets = (Elt **) CLICK_LALLOC(_nbuckets * sizeof(Elt *)); for (size_t i = 0; i < _nbuckets; i++) _buckets[i] = 0; set_dynamic_resizing(true); _n = 0; set_arena(factory); } template HashMap::HashMap() : _default_value(), _arena(0) { initialize(0, DEFAULT_INITIAL_NBUCKETS); } template HashMap::HashMap(const V &def, HashMap_ArenaFactory *factory) : _default_value(def), _arena(0) { initialize(factory, DEFAULT_INITIAL_NBUCKETS); } template void HashMap::copy_from(const HashMap &o) // requires that 'this' is empty and has the same number of buckets as 'o' // and the same resize policy { for (size_t i = 0; i < _nbuckets; i++) { Elt **pprev = &_buckets[i]; *pprev = 0; for (const Elt *e = o._buckets[i]; e; e = e->next) { Elt *ee = reinterpret_cast(_arena->alloc()); new(reinterpret_cast(&ee->key)) K(e->key); new(reinterpret_cast(&ee->value)) V(e->value); ee->next = 0; *pprev = ee; pprev = &ee->next; } } _n = o._n; } template HashMap::HashMap(const HashMap &o) : _buckets((Elt **) CLICK_LALLOC(o._nbuckets * sizeof(Elt *))), _nbuckets(o._nbuckets), _default_value(o._default_value), _capacity(o._capacity), _arena(o._arena) { _arena->use(); copy_from(o); } template HashMap & HashMap::operator=(const HashMap &o) { if (&o != this) { clear(); _default_value = o._default_value; if (_nbuckets < o._nbuckets) resize0(o._nbuckets); _nbuckets = o._nbuckets; _capacity = o._capacity; copy_from(o); } return *this; } template HashMap::~HashMap() { for (size_t i = 0; i < _nbuckets; i++) for (Elt *e = _buckets[i]; e; ) { Elt *next = e->next; e->key.~K(); e->value.~V(); _arena->free(e); e = next; } CLICK_LFREE(_buckets, _nbuckets * sizeof(Elt *)); _arena->unuse(); } template void HashMap::set_dynamic_resizing(bool on) { if (!on) _capacity = 0x7FFFFFFF; else if (_nbuckets >= MAX_NBUCKETS) _capacity = 0x7FFFFFFE; else _capacity = DEFAULT_RESIZE_THRESHOLD * _nbuckets; } template void HashMap::set_arena(HashMap_ArenaFactory *factory) { assert(empty()); if (_arena) _arena->unuse(); _arena = HashMap_ArenaFactory::get_arena(sizeof(Elt), factory); _arena->use(); } template inline size_t HashMap::bucket(const K &key) const { return ((size_t) hashcode(key)) % _nbuckets; } template typename HashMap::Pair * HashMap::find_pair(const K &key) const { #if BIGHASHMAP_REARRANGE_ON_FIND Elt *prev = 0; size_t b = bucket(key); for (Elt *e = _buckets[b]; e; prev = e, e = e->next) if (e->key == key) { if (prev) { // move to front prev->next = e->next; e->next = _buckets[b]; _buckets[b] = e; } return e; } return 0; #else for (Elt *e = _buckets[bucket(key)]; e; e = e->next) if (e->key == key) return e; return 0; #endif } template void HashMap::resize0(size_t new_nbuckets) { Elt **new_buckets = (Elt **) CLICK_LALLOC(new_nbuckets * sizeof(Elt *)); for (size_t i = 0; i < new_nbuckets; i++) new_buckets[i] = 0; size_t old_nbuckets = _nbuckets; Elt **old_buckets = _buckets; _nbuckets = new_nbuckets; _buckets = new_buckets; if (dynamic_resizing()) set_dynamic_resizing(true); // reset threshold for (size_t i = 0; i < old_nbuckets; i++) for (Elt *e = old_buckets[i]; e; ) { Elt *n = e->next; size_t b = bucket(e->key); e->next = new_buckets[b]; new_buckets[b] = e; e = n; } CLICK_LFREE(old_buckets, old_nbuckets * sizeof(Elt *)); } template void HashMap::resize(size_t want_nbuckets) { size_t new_nbuckets = 1; while (new_nbuckets < want_nbuckets && new_nbuckets < MAX_NBUCKETS) new_nbuckets = ((new_nbuckets + 1) << 1) - 1; assert(new_nbuckets > 0 && new_nbuckets <= MAX_NBUCKETS); if (_nbuckets != new_nbuckets) resize0(new_nbuckets); } template bool HashMap::insert(const K &key, const V &value) { size_t b = bucket(key); for (Elt *e = _buckets[b]; e; e = e->next) if (e->key == key) { e->value = value; return false; } if (_n >= _capacity) { resize(_nbuckets + 1); b = bucket(key); } if (Elt *e = reinterpret_cast(_arena->alloc())) { new(reinterpret_cast(&e->key)) K(key); new(reinterpret_cast(&e->value)) V(value); e->next = _buckets[b]; _buckets[b] = e; _n++; } return true; } template bool HashMap::erase(const K &key) { size_t b = bucket(key); Elt *prev = 0; Elt *e = _buckets[b]; while (e && !(e->key == key)) { prev = e; e = e->next; } if (e) { if (prev) prev->next = e->next; else _buckets[b] = e->next; e->key.~K(); e->value.~V(); _arena->free(e); _n--; return true; } else return false; } template typename HashMap::Pair * HashMap::find_pair_force(const K &key, const V &default_value) { size_t b = bucket(key); for (Elt *e = _buckets[b]; e; e = e->next) if (e->key == key) return e; if (_n >= _capacity) { resize(_nbuckets + 1); b = bucket(key); } if (Elt *e = reinterpret_cast(_arena->alloc())) { new(reinterpret_cast(&e->key)) K(key); new(reinterpret_cast(&e->value)) V(default_value); e->next = _buckets[b]; _buckets[b] = e; _n++; return e; } else return 0; } template void HashMap::clear() { for (size_t i = 0; i < _nbuckets; i++) { for (Elt *e = _buckets[i]; e; ) { Elt *next = e->next; e->key.~K(); e->value.~V(); _arena->free(e); e = next; } _buckets[i] = 0; } _n = 0; } template void HashMap::swap(HashMap &o) { Elt **t_elts; V t_v; size_t t_size; HashMap_Arena *t_arena; t_elts = _buckets; _buckets = o._buckets; o._buckets = t_elts; t_size = _nbuckets; _nbuckets = o._nbuckets; o._nbuckets = t_size; t_v = _default_value; _default_value = o._default_value; o._default_value = t_v; t_size = _n; _n = o._n; o._n = t_size; t_size = _capacity; _capacity = o._capacity; o._capacity = t_size; t_arena = _arena; _arena = o._arena; o._arena = t_arena; } template _HashMap_const_iterator::_HashMap_const_iterator(const HashMap *hm, bool begin) : _hm(hm) { size_t nb = _hm->_nbuckets; typename HashMap::Elt **b = _hm->_buckets; for (_bucket = 0; _bucket < nb && begin; _bucket++) if (b[_bucket]) { _elt = b[_bucket]; return; } _elt = 0; } template void _HashMap_const_iterator::operator++(int) { if (_elt->next) _elt = _elt->next; else { size_t nb = _hm->_nbuckets; typename HashMap::Elt **b = _hm->_buckets; for (_bucket++; _bucket < nb; _bucket++) if (b[_bucket]) { _elt = b[_bucket]; return; } _elt = 0; } } #if 0 static size_t HashMap_partition_elts(void **elts, size_t left, size_t right) { void *pivot = elts[(left + right) / 2]; // loop invariant: // elts[i] < pivot for all left_init <= i < left // elts[i] > pivot for all right < i <= right_init while (left < right) { if (elts[left] < pivot) left++; else if (elts[right] > pivot) right--; else { void *x = elts[left]; elts[left] = elts[right]; elts[right] = x; } } return left; } void HashMap_qsort_elts(void **elts, size_t left, size_t right) { if (left < right) { size_t split = HashMap_partition_elts(elts, left, right); HashMap_qsort_elts(elts, left, split); HashMap_qsort_elts(elts, split, right); } } #endif // void * partial specialization template void HashMap::initialize(HashMap_ArenaFactory *factory, size_t initial_nbuckets) { _nbuckets = initial_nbuckets; _buckets = (Elt **) CLICK_LALLOC(_nbuckets * sizeof(Elt *)); for (size_t i = 0; i < _nbuckets; i++) _buckets[i] = 0; set_dynamic_resizing(true); _n = 0; set_arena(factory); } template HashMap::HashMap() : _default_value(0), _arena(0) { initialize(0, DEFAULT_INITIAL_NBUCKETS); } template HashMap::HashMap(void *def, HashMap_ArenaFactory *factory) : _default_value(def), _arena(0) { initialize(factory, DEFAULT_INITIAL_NBUCKETS); } template void HashMap::copy_from(const HashMap &o) { for (size_t i = 0; i < _nbuckets; i++) { Elt **pprev = &_buckets[i]; *pprev = 0; for (const Elt *e = o._buckets[i]; e; e = e->next) { Elt *ee = reinterpret_cast(_arena->alloc()); new(reinterpret_cast(&ee->key)) K(e->key); ee->value = e->value; ee->next = 0; *pprev = ee; pprev = &ee->next; } } _n = o._n; } template HashMap::HashMap(const HashMap &o) : _buckets((Elt **) CLICK_LALLOC(o._nbuckets * sizeof(Elt *))), _nbuckets(o._nbuckets), _default_value(o._default_value), _capacity(o._capacity), _arena(o._arena) { _arena->use(); copy_from(o); } template HashMap & HashMap::operator=(const HashMap &o) { if (&o != this) { clear(); _default_value = o._default_value; if (_nbuckets < o._nbuckets) resize0(o._nbuckets); _nbuckets = o._nbuckets; _capacity = o._capacity; copy_from(o); } return *this; } template HashMap::~HashMap() { for (size_t i = 0; i < _nbuckets; i++) for (Elt *e = _buckets[i]; e; ) { Elt *next = e->next; e->key.~K(); _arena->free(e); e = next; } CLICK_LFREE(_buckets, _nbuckets * sizeof(Elt *)); _arena->unuse(); } template void HashMap::set_dynamic_resizing(bool on) { if (!on) _capacity = 0x7FFFFFFF; else if (_nbuckets >= MAX_NBUCKETS) _capacity = 0x7FFFFFFE; else _capacity = DEFAULT_RESIZE_THRESHOLD * _nbuckets; } template void HashMap::set_arena(HashMap_ArenaFactory *factory) { assert(empty()); if (_arena) _arena->unuse(); _arena = HashMap_ArenaFactory::get_arena(sizeof(Elt), factory); _arena->use(); } template inline size_t HashMap::bucket(const K &key) const { return ((size_t) hashcode(key)) % _nbuckets; } template typename HashMap::Pair * HashMap::find_pair(const K &key) const { #if BIGHASHMAP_REARRANGE_ON_FIND Elt *prev = 0; size_t b = bucket(key); for (Elt *e = _buckets[b]; e; prev = e, e = e->next) if (e->key == key) { if (prev) { // move to front prev->next = e->next; e->next = _buckets[b]; _buckets[b] = e; } return e; } return 0; #else for (Elt *e = _buckets[bucket(key)]; e; e = e->next) if (e->key == key) return e; return 0; #endif } template void HashMap::resize0(size_t new_nbuckets) { Elt **new_buckets = (Elt **) CLICK_LALLOC(new_nbuckets * sizeof(Elt *)); for (size_t i = 0; i < new_nbuckets; i++) new_buckets[i] = 0; size_t old_nbuckets = _nbuckets; Elt **old_buckets = _buckets; _nbuckets = new_nbuckets; _buckets = new_buckets; if (dynamic_resizing()) set_dynamic_resizing(true); // reset threshold for (size_t i = 0; i < old_nbuckets; i++) for (Elt *e = old_buckets[i]; e; ) { Elt *n = e->next; size_t b = bucket(e->key); e->next = new_buckets[b]; new_buckets[b] = e; e = n; } CLICK_LFREE(old_buckets, old_nbuckets * sizeof(Elt *)); } template void HashMap::resize(size_t want_nbuckets) { size_t new_nbuckets = 1; while (new_nbuckets < want_nbuckets && new_nbuckets < MAX_NBUCKETS) new_nbuckets = ((new_nbuckets + 1) << 1) - 1; assert(new_nbuckets > 0 && new_nbuckets <= MAX_NBUCKETS); if (_nbuckets != new_nbuckets) resize0(new_nbuckets); } template bool HashMap::insert(const K &key, void *value) { size_t b = bucket(key); for (Elt *e = _buckets[b]; e; e = e->next) if (e->key == key) { e->value = value; return false; } if (_n >= _capacity) { resize(_nbuckets + 1); b = bucket(key); } if (Elt *e = reinterpret_cast(_arena->alloc())) { new(reinterpret_cast(&e->key)) K(key); e->value = value; e->next = _buckets[b]; _buckets[b] = e; _n++; } return true; } template bool HashMap::erase(const K &key) { size_t b = bucket(key); Elt *prev = 0; Elt *e = _buckets[b]; while (e && !(e->key == key)) { prev = e; e = e->next; } if (e) { if (prev) prev->next = e->next; else _buckets[b] = e->next; e->key.~K(); _arena->free(e); _n--; return true; } else return false; } template typename HashMap::Pair * HashMap::find_pair_force(const K &key, void *default_value) { size_t b = bucket(key); for (Elt *e = _buckets[b]; e; e = e->next) if (e->key == key) return e; if (_n >= _capacity) { resize(_nbuckets + 1); b = bucket(key); } if (Elt *e = reinterpret_cast(_arena->alloc())) { new(reinterpret_cast(&e->key)) K(key); e->value = default_value; e->next = _buckets[b]; _buckets[b] = e; _n++; return e; } else return 0; } template void HashMap::clear() { for (size_t i = 0; i < _nbuckets; i++) { for (Elt *e = _buckets[i]; e; ) { Elt *next = e->next; e->key.~K(); _arena->free(e); e = next; } _buckets[i] = 0; } _n = 0; } template void HashMap::swap(HashMap &o) { Elt **t_elts; void *t_v; size_t t_size; HashMap_Arena *t_arena; t_elts = _buckets; _buckets = o._buckets; o._buckets = t_elts; t_size = _nbuckets; _nbuckets = o._nbuckets; o._nbuckets = t_size; t_v = _default_value; _default_value = o._default_value; o._default_value = t_v; t_size = _n; _n = o._n; o._n = t_size; t_size = _capacity; _capacity = o._capacity; o._capacity = t_size; t_arena = _arena; _arena = o._arena; o._arena = t_arena; } template _HashMap_const_iterator::_HashMap_const_iterator(const HashMap *hm, bool begin) : _hm(hm) { size_t nb = _hm->_nbuckets; typename HashMap::Elt **b = _hm->_buckets; for (_bucket = 0; _bucket < nb && begin; _bucket++) if (b[_bucket]) { _elt = b[_bucket]; return; } _elt = 0; } template void _HashMap_const_iterator::operator++(int) { if (_elt->next) _elt = _elt->next; else { size_t nb = _hm->_nbuckets; typename HashMap::Elt **b = _hm->_buckets; for (_bucket++; _bucket < nb; _bucket++) if (b[_bucket]) { _elt = b[_bucket]; return; } _elt = 0; } } /** @endcond */ CLICK_ENDDECLS #endif