1#ifndef CLICK_HASHTABLEMP_HH
2#define CLICK_HASHTABLEMP_HH
3#include <click/glue.hh>
4#include <click/hashcode.hh>
5#include <click/hashcontainer.hh>
6#include <click/allocator.hh>
8#include <click/multithread.hh>
10# define click_hashmp_assert(x) assert(x)
12# define click_hashmp_assert(x)
15#define HASTABLEMP_POW2_SZ 1
24 MT_T(
bool mt =
true) {
28 inline bool mt()
const {
32 inline void disable_mt() {
39 inline bool mt()
const {
return false; };
40 inline void disable_mt() { };
54template <
typename K,
typename V,
typename Item,
typename MT=MT_T>
57 class ListItem {
public:
58 ListItem(
const ListItem &li) : item(li.item), key(li.key), _hashnext(0) {};
59 ListItem(
const K &k,
const V &v) : item(v), key(k), _hashnext(0) {};
67 class ListItemPtr {
public:
68 ListItemPtr() : head(0) {};
72 class Bucket {
public:
91 return _nbuckets_mask + 1;
96 #if HASTABLEMP_POW2_SZ
97 uint32_t _nbuckets_mask;
104 Table(
const Table &o) {};
105 void operator=(
const Table &o){};
108 typedef typename Item::ptr ptr;
109 typedef typename Item::write_ptr write_ptr;
119 if (_b < _h->_table->nbuckets()) {
120 if (likely(_h->mt()))
121 _h->_table->buckets[_b].list.
read_end();
123 if (likely(_h->mt()))
130 if (_b == _h->_table->nbuckets())
return;
132 _item = _item->_hashnext;
137 if (likely(_h->mt()))
138 _h->_table->buckets[_b].list.
read_end();
139 if (++_b == _h->_table->nbuckets())
return;
140 if (likely(_h->mt()))
142 _item = _h->_table->buckets[_b].list->head;
147 void operator++(
int) {
161 return _item->item.read();
165 return _item->item.read();
169 iterator(
const iterator &o) {
181 if (likely(_h->mt()))
183 _item = _h->_table->buckets[_b].list->head;
191 class write_iterator {
199 if (_b < _h->_table->nbuckets()) {
200 if (likely(_h->mt()))
201 _h->_table->buckets[_b].list.
write_end();
203 if (likely(_h->mt()))
211 if (_b == _h->_table->nbuckets())
return;
213 _item = _item->_hashnext;
217 if (likely(_h->mt()))
218 _h->_table->buckets[_b].list.
write_end();
219 if (++_b == _h->_table->nbuckets())
return;
220 if (likely(_h->mt()))
222 _item = _h->_table->buckets[_b].list->head;
227 void operator++(
int) {
240 write_ptr operator *() {
241 return _item->item.write();
244 write_ptr operator ->() {
245 return _item->item.write();
250 _prev->_hashnext = _item->_hashnext;
252 _h->_table->buckets[_b].list->head = _item->_hashnext;
254 ListItem* next = _item->_hashnext;
255 _h->erase_item(_item);
267 _item = _h->_table->buckets[_b].list->head;
275 static ListItem *&hashnext(ListItem *e) {
278 static const K& hashkey(
const ListItem *e) {
281 static bool hashkeyeq(
const K &a,
const K &b) {
285 typedef uint32_t size_type;
289 max_bucket_count = 4194303,
291 max_bucket_count = (uint32_t) -1,
293 initial_bucket_count = 63
322 s = _table->nbuckets();
339 r = _table->_size > 2 * _table->nbuckets() && _table->nbuckets() < max_bucket_count;
345 inline iterator begin();
347 inline write_iterator write_begin();
360 inline bool find_clean(
const K &key, std::function<
void(V &storage)> store, std::function<
bool(V &storage)> shouldremove);
366 inline bool find_erase_clean(
const K &key, std::function<
void(V &storage)> store, std::function<
bool(V &storage)> shouldremove);
370 inline ptr find_insert(
const K &key,
const V &value);
373 inline write_ptr find_insert_write(
const K &key,
const V &value);
378 inline ptr find_create(
const K &key,std::function<V(
void)> on_create);
385 inline void insert(
const K &key,
const V &value, std::function<
void(V&value)> on_replace = [](V&){});
388 inline void insert_clean(
const K &key,
const V &value, std::function<
bool(V &storage)> shouldremove);
391 inline void erase(
const K &key);
395 void rehash(size_type n);
397 inline bool alone(
bool atomic =
false);
401 return _table.refcnt();
404 inline void disable_mt() {
412 inline void resize_clear(uint32_t n) {
422 void initialize(size_type n);
429 size_type bucket(
const K &key);
432 void erase_item(ListItem* item) {
433 if (item->item.unshared()) {
434 _allocator.release(item);
436 item->_hashnext = _pending_release.get();
437 _pending_release.set(item);
441 ListItem* allocate(
const ListItem &li) {
443 if ((it = _pending_release.get()) && it->item.unshared()) {
444 _pending_release.set(it->_hashnext);
447 return _allocator.allocate(li);
450 void release_pending(
bool force=
false) {
453 for (
unsigned i = 0; i < _pending_release.
weight() ; i++) {
456 next = it->_hashnext;
457 _allocator.release(it);
463 template <
bool do_remove>
464 inline bool _find_clean(
const K &key, std::function<
void(V &storage)> store, std::function<
bool(V &storage)> shouldremove, std::function<
void(ListItem* &head)> on_missing = [](ListItem* &head){});
467 friend class iterator;
471template <
typename K,
typename V,
typename Item,
typename MT>
475#if HASTABLEMP_POW2_SZ
476 _table->_nbuckets_mask = next_pow2(n) - 1;
478 _table->_nbuckets = n;
480 _table->buckets = (Bucket *) CLICK_LALLOC(
sizeof(Bucket) * _table->nbuckets());
481 for (size_type b = 0; b < _table->nbuckets(); ++b)
482 new(&_table->buckets[b]) Bucket();
485template <
typename K,
typename V,
typename Item,
typename MT>
488 for (size_type b = 0; b < _table->nbuckets(); ++b)
489 _table->buckets[b].~Bucket();
490 CLICK_LFREE(_table->buckets,
sizeof(Bucket) * _table->nbuckets());
494template <
typename K,
typename V,
typename Item,
typename MT>
496 _mt(true), _table(), _pending_release(0)
498 initialize(initial_bucket_count);
501template <
typename K,
typename V,
typename Item,
typename MT>
503 _mt(true), _table(), _pending_release(0)
506 while (b < nb && b < max_bucket_count)
507 b = ((b + 1) << 1) - 1;
512template <
typename K,
typename V,
typename Item,
typename MT>
516 release_pending(
true);
519#define MAKE_FIND(ptr_type) \
521 _table.read_begin();\
522 size_type b = bucket(key);\
523 Bucket& bucket = _table->buckets[b];\
525 bucket.list.read_begin();\
530 for (pprev = bucket.list->head; pprev; pprev = hashnext(pprev))\
531 if (hashkeyeq(hashkey(pprev), key)) {\
532 p.assign(&pprev->item);\
536 bucket.list.read_end();\
538template <
typename K,
typename V,
typename Item,
typename MT>
539inline typename HashContainerMP<K,V,Item,MT>::ptr
546template <
typename K,
typename V,
typename Item,
typename MT>
547inline typename HashContainerMP<K,V,Item,MT>::write_ptr
550 MAKE_FIND(write_ptr);
560template <
typename K,
typename V,
typename Item,
typename MT>
template <
bool do_remove>
566 size_type b = bucket(key);
567 Bucket& bucket = _table->buckets[b];
569 bucket.list.write_begin();
572 ListItem *pprev = bucket.list->head;
573 ListItem* *pprev_ptr = &bucket.list->head;
576 if (shoulddelete(*pprev->item.unprotected_ptr())) {
577 *pprev_ptr = pprev->_hashnext;
578 ListItem* tmp = pprev;
583 if (hashkeyeq(hashkey(pprev), key)) {
584 store(*pprev->item.unprotected_ptr());
586 *pprev_ptr = pprev->_hashnext;
592 pprev_ptr = &pprev->_hashnext;
593 pprev = pprev->_hashnext;
596 on_missing(bucket.list->head);
598 bucket.list.write_end();
602 bucket.list.write_end();
611template <
typename K,
typename V,
typename Item,
typename MT>
615 return _find_clean<true>(key, store, shoulddelete);
622template <
typename K,
typename V,
typename Item,
typename MT>
626 return _find_clean<false>(key, store, shoulddelete);
633template <
typename K,
typename V,
typename Item,
typename MT>
637 auto inserter = [key,value,
this](ListItem* &head){
638 ListItem* e = allocate(ListItem(key,value));
639 *e->item.unprotected_ptr() = value;
640 click_hashmp_assert(e->item.unshared());
645 _find_clean<false>(key, [&value](V&c){c = value;}, shoulddelete, inserter);
654template <
typename K,
typename V,
typename Item,
typename MT>
657 return find_erase_clean(key, [&storage](V&c){storage = c;}, [](V&){
return false;});
665template <
typename K,
typename V,
typename Item,
typename MT>
668 find_erase_clean(key, [](V&){}, [](V&){
return false;});
671#define MAKE_FIND_INSERT(ptr_type,on_exists,value) \
673 _table.read_begin();\
674 size_type b = bucket(key);\
675 Bucket& bucket = _table->buckets[b];\
679 bucket.list.read_begin();\
683 for (pprev = bucket.list->head; pprev; pprev = hashnext(pprev)) {\
684 if (hashkeyeq(hashkey(pprev), key)) {\
685 p.assign(&pprev->item);\
690 on_exists(pprev->item);\
692 bucket.list.read_end();\
694 if (likely(mt()) && !bucket.list.read_to_write()) {\
697 ListItem* e = allocate(ListItem(key,value));\
698 click_hashmp_assert(e->item.unshared());\
699 e->_hashnext = bucket.list->head;\
701 bucket.list->head = e;\
707 bucket.list.write_end();\
712template <
typename K,
typename V,
typename Item,
typename MT>
713inline typename HashContainerMP<K,V,Item,MT>::ptr
715 MAKE_FIND_INSERT(ptr,(
void),value);
716 click_hashmp_assert(p.hasref());
723template <
typename K,
typename V,
typename Item,
typename MT>
726 MAKE_FIND_INSERT(write_ptr,{on_replace(*pprev->item.unprotected_ptr());pprev->item = value;},value);
729template <
typename K,
typename V,
typename Item,
typename MT>
730inline typename HashContainerMP<K,V,Item,MT>::ptr
732 MAKE_FIND_INSERT(ptr,(
void),on_create());
736template <
typename K,
typename V,
typename Item,
typename MT>
737inline typename HashContainerMP<K,V,Item,MT>::write_ptr
739 MAKE_FIND_INSERT(write_ptr,(
void),value);
740 click_hashmp_assert(p.write_held());
744template <
typename K,
typename V,
typename Item,
typename MT>
750 return iterator(
this);
753template <
typename K,
typename V,
typename Item,
typename MT>
759 return write_iterator(
this);
762template <
typename K,
typename V,
typename Item,
typename MT>
766 if (_table.refcnt() != 0)
return false;
774 for (
int i = 0; i < _table->nbuckets();i++) {
775 Bucket& b = _table->buckets[i];
776 if (b.list.refcnt() != 0) {
782 b.list.write_begin();
786 ListItem* item = b.list->head;
788 if (!item->item.unshared()) {
792 item = item->_hashnext;
815template <
typename K,
typename V,
typename Item,
typename MT>
816inline typename HashContainerMP<K,V,Item,MT>::size_type
819#if HASTABLEMP_POW2_SZ
820 return ((size_type) hashcode(key)) & _table->_nbuckets_mask;
822 return ((size_type) hashcode(key)) % _table->nbuckets();
826template <
typename K,
typename V,
typename Item,
typename MT>
831 for (size_type i = 0; i < _table->nbuckets(); ++i) {
832 Bucket& b = _table->buckets[i];
834 b.list.write_begin();
835 ListItem* item = b.list->head;
838 next = item->_hashnext;
852template <
typename K,
typename V,
typename Item,
typename MT>
857 Bucket &b = _table->buckets[bucket(key)];
862 for (ListItem* list = b.list->head; list; list = list->_hashnext) {
863 if (hashkeyeq(hashkey(list), key)) {
972template <
typename K,
typename V>
981template <
typename K,
typename V>
982class HashTableH :
public HashContainerMP<K,V,not_shared<V>,NOMT > {
public:
991template <
typename K,
typename V>
998template <
typename K,
typename Vin,
typename Time = click_jiffies_t,
template <
typename>
class Protector =
shared,
typename MT=MT_T>
1011 AgingTableMP(Time timeout = 60) : _table(), _timeout(timeout) {
1014 void set_timeout(Time timeout) {
1021 inline bool find(K key,
const Time &now, Vin& storage,
const bool update_age =
true) {
1022 return _table.
find_clean(key, [&storage,now,update_age](V&c){storage = c.value;
if (update_age) c.time = now;}, [now,
this](V& v){
1023 bool remove = v.time + _timeout < now;
1026 click_chatter(
"Removing entry upon search (time is %d, expired %d, now %d)",v.time,_timeout,now);
1035 inline void insert(
const K &key,
const Time &now,
const Vin& value) {
1036 const V val = {.value = value, .time = now};
1037 _table.insert_clean(key, val, [now,
this](V& v){
1038 bool remove = v.time + _timeout < now;
1041 click_chatter(
"Removing entry upon insertion (time is %d, expired %d, now %d)",v.time,_timeout,now);
1047 inline size_type size() {
1048 return _table.
size();
1052template <
typename K,
typename Vin,
typename Time = click_jiffies_t>
1053class AgingTable :
public AgingTableMP<K,Vin,Time,not_shared,NOMT> {
public:
1054 AgingTable(Time timeout = 60 * CLICK_HZ ) : AgingTableMP<K,Vin,Time,
not_shared,NOMT>(timeout) {
Definition multithread.hh:778
Intrusive hash table template, MT safe version.
Definition hashtablemp.hh:55
ptr find(const K &key)
Return a read pointer for the element with key, if any.
bool find_erase_clean(const K &key, std::function< void(V &storage)> store, std::function< bool(V &storage)> shouldremove)
Find a key, and call store if found before removing it. Calls clean on colliding entries upon the way...
bool contains(const K &key)
Test if an element with key key exists in the table.
write_ptr find_write(const K &key)
Return a write pointer for the element with key, if any.
bool find_erase(const K &key, V &storage)
Copy the value of V for K in storage if K is found, delete it from the table and return true....
bool empty()
Return true iff size() == 0.
Definition hashtablemp.hh:329
bool unbalanced()
Return true if this HashContainer should be rebalanced.
Definition hashtablemp.hh:335
size_type buckets()
Return the number of buckets.
Definition hashtablemp.hh:318
~HashContainerMP()
Destroy the HashContainer.
size_type size()
Return the number of elements stored.
Definition hashtablemp.hh:307
void insert(const K &key, const V &value, std::function< void(V &value)> on_replace=[](V &){})
Insert a key. Replace an item if it already exists, insert it if not. Gives the ability to do somethi...
bool find_clean(const K &key, std::function< void(V &storage)> store, std::function< bool(V &storage)> shouldremove)
Find a key K and call store if found. Calls clean on colliding entries upon the way.
HashContainerMP()
Construct an empty HashContainer.
HashContainerMP(size_type n)
Construct an empty HashContainer with at least n buckets.
void write_end()
Definition multithread.hh:721
void write_begin()
Definition multithread.hh:690
void read_begin()
Definition multithread.hh:656
void read_end()
Definition multithread.hh:670
A 32-bit integer with support for atomic operations.
Definition atomic.hh:71
Definition multithread.hh:519
unsigned weight() const
Definition sync.hh:203
T & get_value(int i) const
Definition sync.hh:190
Definition allocator.hh:59
Definition multithread.hh:390