Marine Library  1.0
C++ library for Linux Networking Development
atomic_hash_table.hh
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2016 Zhao DAI <daidodo@gmail.com>
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or any
7  * later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see accompanying file LICENSE.txt
16  * or <http://www.gnu.org/licenses/>.
17  */
18 
25 #ifndef DOZERG_ATOMIC_HASH_TABLE_H_20130812
26 #define DOZERG_ATOMIC_HASH_TABLE_H_20130812
27 
28 #include <vector>
29 #include "tools/debug.hh"
30 #include "impl/atomic_hash_table_impl.hh"
31 
32 NS_SERVER_BEGIN
33 
65 template<
66  typename Key,
67  template<typename>class HashKey = CHashFn,
68  template<typename>class EqualKey = std::equal_to
70 {
72  typedef NS_IMPL::CAtomicHashHead __Head;
73  typedef NS_IMPL::CAtomicHashRowInfo __RowInfo;
74  typedef __RowInfo::__Node __Node;
75  typedef std::vector<__RowInfo> __Rows;
76  typedef typename __Rows::iterator __Iter;
77  typedef typename __Rows::const_iterator __CIter;
78 public:
79  typedef Key key_type;
80  typedef size_t size_type;
81  typedef EqualKey<key_type> key_equal;
82  typedef HashKey<
83  typename COmitCV<key_type>::result_type> hasher;
84  //constant
86  static const size_t kValueLenMax = (1UL << 24);
87  //functions:
103  static size_t CalcBufSize(size_t capacity, int row, size_t valueLen){
104  if(__Head::Check(capacity, row)){
105  std::vector<uint32_t> cols;
106  size_t realCapa = tools::PrimesGenerator(capacity, row, cols);
107  if(realCapa >= capacity)
108  return bufSize(row, realCapa, sizeof(key_type) + alignLen(valueLen));
109  }
110  return 0;
111  }
116  CAtomicHashTable():head_(NULL){}
121  CAtomicHashTable(char * buf, size_t sz)
122  : head_(NULL)
123  {
124  init(buf, sz);
125  }
130  CAtomicHashTable(char * buf, size_t sz, size_t capacity, int row, size_t valueLen, bool create = false)
131  : head_(NULL)
132  {
133  init(buf, sz, capacity, row, valueLen, create);
134  }
144  bool init(char * buf, size_t sz){
145  return initAux(buf, sz, false, 0, 0, 0);
146  }
166  bool init(char * buf, size_t sz, size_t capacity, int row, size_t valueLen, bool create = false){
167  valueLen = alignLen(valueLen);
168  if(create)
169  return this->create(buf, sz, capacity, row, valueLen);
170  return initAux(buf, sz, true, capacity, row, valueLen);
171  }
177  void uninit(){
178  head_ = NULL;
179  rows_.clear();
180  }
185  bool valid() const{return (NULL != head_ && !rows_.empty());}
195  int rowSize() const{return (head_ ? head_->row() : 0);}
201  size_t capacityOfRow(int row) const{return rows_[row].capacity();}
208  size_t capacity() const{return (head_ ? head_->realCapa() : 0);}
214  size_t sizeOfRow(int row) const{return rows_[row].used();}
219  size_t size() const{
220  size_t ret = 0;
221  for(int i = 0;i < rowSize();++i)
222  ret += sizeOfRow(i);
223  return ret;
224  }
229  bool empty() const{
230  for(int i = 0;i < rowSize();++i)
231  if(0 != sizeOfRow(i))
232  return false;
233  return true;
234  }
245  time_t createTime() const{return (head_ ? head_->createTime() : 0);}
252  time_t updateTime() const{return (head_ ? head_->modTime() : 0);}
259  time_t upgradeTime() const{return (head_ ? head_->upgradeTime() : 0);}
264  std::string toString() const{
265  CToString oss;
266  oss<<"{\n"
267  <<" head="<<tools::ToStringPtr(head_)<<'\n';
268  for(int i = 0;i < rowSize();++i)
269  oss<<" row["<<i<<"]="<<rows_[i].toString(head_->usedArray())<<'\n';
270  oss<<"}";
271  return oss.str();
272  }
286  bool insert(const key_type & key, const char * value, size_t len){
287  //check
288  if(!valid())
289  return false;
290  if(NULL == value)
291  len = 0;
292  if(sizeof key + len > kValueLenMax)
293  return false;
294  //insert
295  if(!insertAux(key, hasher()(key), value, len))
296  return false;
297  //set meta
298  head_->update();
299  return true;
300  }
309  bool insert(const key_type & key, const std::string & value){
310  return this->insert(key, value.c_str(), value.length());
311  }
322  bool get(const key_type & key, char * value, size_t & len) const{
323  //check
324  if(!valid())
325  return false;
326  //search first node
327  const uint32_t hash = hasher()(key);
328  const __Node * const p = searchNode(hash, key);
329  if(NULL == p)
330  return false;
331  //copy data
332  const size_t realLen = p->len();
333  if(realLen < sizeof(key_type))
334  return false;
335  size_t cur = readNodes(p, value, len, hash);
336  if(cur > len)
337  return false;
338  //check
339  if(cur + sizeof(key_type) != realLen)
340  return false;
341  len = cur;
342  return true;
343  }
350  bool get(const key_type & key, std::string & value) const{
351  //check
352  if(!valid())
353  return false;
354  //search first node
355  const uint32_t hash = hasher()(key);
356  const __Node * const p = searchNode(hash, key);
357  if(NULL == p)
358  return false;
359  //reserve value
360  const size_t realLen = p->len();
361  if(realLen < sizeof(key_type))
362  return false;
363  value.resize(realLen - sizeof(key_type));
364  //copy data
365  size_t cur = readNodes(p, &value[0], value.size(), hash);
366  return (cur == value.size());
367  }
373  bool has(const key_type & key) const{
374  if(!valid())
375  return false;
376  return (NULL != searchNode(hasher()(key), key));
377  }
387  bool update(const key_type & key, const char * value, size_t len){
388  //check
389  if(!valid())
390  return false;
391  if(NULL == value)
392  len = 0;
393  if(sizeof key + len > kValueLenMax)
394  return false;
395  //search old
396  const uint32_t hash = hasher()(key);
397  __Node * const p = searchNode(hash, key);
398  //insert new
399  if(!insertAux(key, hash, value, len))
400  return false;
401  //rm old
402  removeAux(p, hash);
403  //set meta
404  head_->update();
405  return true;
406  }
415  bool update(const key_type & key, const std::string & value){
416  return this->update(key, value.c_str(), value.length());
417  }
424  size_t remove(const key_type & key){
425  //check
426  if(!valid())
427  return false;
428  //rm
429  const uint32_t hash = hasher()(key);
430  const size_t ret = removeAux(searchNode(hash, key), hash);
431  if(ret > 0)
432  head_->update();
433  return ret;
434  }
440  void clear(){
441  if(valid())
442  for(__Iter it = rows_.begin();it != rows_.end();++it)
443  it->clear();
444  }
446 private:
447  static size_t alignLen(size_t len){return (len + 7) / 8 * 8;}
448  static size_t bufSize(int row, size_t realCapa, size_t valueSz){
449  return (__Head::HeadSize(row) + __Node::Offset(realCapa, valueSz));
450  }
451  void initRows(const uint32_t * cols, uint32_t * used){
452  assert(cols && used && head_ && rows_.empty());
453  char * p = head_->dataArray();
454  rows_.reserve(head_->row());
455  for(uint16_t i = 0;i < head_->row();++i)
456  rows_.push_back(__RowInfo(cols[i], &used[i], p, head_->valueSz()));
457  std::sort(rows_.begin(), rows_.end(), std::greater<__RowInfo>());
458  }
459  bool create(char * buf, size_t sz, size_t capacity, int row, size_t valueLen){
460  //check
461  if(valid())
462  return false;
463  if(NULL == buf || sz < sizeof(__Head))
464  return false;
465  if(!__Head::Check(capacity, row))
466  return false;
467  __Head & head = *reinterpret_cast<__Head *>(buf);
468  std::vector<uint32_t> cols;
469  const uint64_t realCapa = tools::PrimesGenerator(capacity, row, cols);
470  if(realCapa < capacity || size_t(row) != cols.size())
471  return false;
472  if(sz < bufSize(row, realCapa, sizeof(key_type) + valueLen))
473  return false;
474  //reset head
475  ::memset(buf, 0, __Head::HeadSize(row));
476  //head
477  head_ = &head;
478  head_->init<key_type>(capacity, valueLen, row, realCapa, true);
479  //used
480  uint32_t * const used = head_->usedArray();
481  //cols
482  uint32_t * const colp = head_->colsArray();
483  std::sort(cols.begin(), cols.end(), std::greater<uint32_t>());
484  std::copy(cols.begin(), cols.end(), colp);
485  //rows
486  initRows(colp, used);
487  //reset data
488  clear();
489  return true;
490  }
491  bool initAux(char * buf, size_t sz, bool hasFollowing, size_t capacity, int row, size_t valueLen){
492  //check
493  if(valid())
494  return false;
495  if(NULL == buf || sz < sizeof(__Head))
496  return false;
497  __Head & head = *reinterpret_cast<__Head *>(buf);
498  if(!head.check<key_type>())
499  return false;
500  if(hasFollowing && !head.check<key_type>(capacity, valueLen, row))
501  return false;
502  if(sz < bufSize(head.row(), head.realCapa(), head.valueSz()))
503  return false;
504  uint32_t * const used = head.usedArray();
505  const uint32_t * const cols = head.colsArray();
506  if(!head.check(cols, used))
507  return false;
508  //set
509  head_ = &head;
510  initRows(cols, used);
511  return true;
512  }
513  __Node * allocNode(uint32_t hash){
514  assert(valid());
515  for(__Iter it = rows_.begin();it != rows_.end();++it){
516  __Node * const p = it->allocNode(hash);
517  if(NULL != p)
518  return p;
519  }
520  return NULL;
521  }
522  void allocNodes(uint32_t hash, size_t cnt, std::vector<__Node *> & ret){
523  if(0 == cnt)
524  return;
525  ret.clear();
526  ret.reserve(cnt);
527  uint32_t h = hash;
528  bool fail = false;
529  for(size_t i = 0;i < cnt;++i, h = (h + 1) * (h + 2)){
530  __Node * const p = allocNode(h);
531  if(NULL == p){
532  fail = true;
533  break;
534  }
535  ret.push_back(p);
536  }
537  if(fail && !ret.empty()){
538  deallocNodes(ret);
539  ret.clear();
540  }
541  }
542  void deallocNode(__Node * p){
543  if(NULL != p && valid())
544  for(__Iter it = rows_.begin();it != rows_.end() && !it->deallocNode(p);++it);
545  }
546  void deallocNodes(const std::vector<__Node *> & nodes){
547  if(!nodes.empty())
548  for(std::vector<__Node *>::const_iterator i = nodes.begin();i != nodes.end();++i)
549  deallocNode(*i);
550  }
551  const __Node * searchNode(uint32_t hash, const key_type & key) const{
552  if(!valid())
553  return NULL;
554  for(__CIter it = rows_.begin();it != rows_.end();++it){
555  const __Node * const p = it->searchNode(hash);
556  if(NULL != p && key_equal()(key, p->key<key_type>()))
557  return p;
558  }
559  return NULL;
560  }
561  __Node * searchNode(uint32_t hash, const key_type & key){
562  if(!valid())
563  return NULL;
564  for(__Iter it = rows_.begin();it != rows_.end();++it){
565  __Node * const p = it->searchNode(hash);
566  if(NULL != p && key_equal()(key, p->key<key_type>()))
567  return p;
568  }
569  return NULL;
570  }
571  size_t readNodes(const __Node * p, char * value, size_t len, uint32_t hash) const{
572  assert(p);
573  size_t cur = p->getData<key_type>(value, len, head_->valueSz());
574  if(cur > len)
575  return -1;
576  for(p = head_->nextNode(p, hash);NULL != p;p = head_->nextNode(p, hash))
577  if(!p->getData(value, cur, len, head_->valueSz()))
578  return -1;
579  return cur;
580  }
581  bool insertAux(const key_type & key, uint32_t hash, const char * value, size_t len){
582  assert(valid());
583  //allocate nodes
584  std::vector<__Node *> nodes;
585  allocNodes(hash, head_->nodeCount<key_type>(len), nodes);
586  if(nodes.empty())
587  return false;
588  //copy data
589  std::vector<__Node *>::const_iterator i = nodes.begin();
590  size_t cur = (*i)->setData(key, value, len, head_->valueSz());
591  for(++i;i != nodes.end();++i){
592  if(cur >= len){
593  deallocNodes(nodes);
594  return false;
595  }
596  (*i)->setData(value, cur, len, head_->valueSz());
597  }
598  //set meta
599  i = nodes.begin();
600  for(std::vector<__Node *>::const_iterator j = i + 1;j != nodes.end();++i, ++j)
601  (*i)->setMetaMid(hash, head_->dataOffset(*j));
602  (*i)->setMetaEnd(hash, value, len);
603  nodes.front()->setFirst();
604  return true;
605  }
606  size_t removeAux(__Node * p, uint32_t hash){
607  assert(valid());
608  size_t cnt = 0;
609  for(;NULL != p;++cnt){
610  __Node * const n = head_->nextNode(p, hash);
611  deallocNode(p);
612  p = n;
613  }
614  return cnt;
615  }
616  //fields
617  mutable __Rows rows_;
618  __Head * head_; //hash table header
619 };
620 
621 NS_SERVER_END
622 
623 #endif
624 
void uninit()
Reset current object.
Definition: atomic_hash_table.hh:177
Definition: to_string.hh:43
time_t upgradeTime() const
Get latest upgrading time of the hash table.
Definition: atomic_hash_table.hh:259
static const size_t kValueLenMax
The maximum size of Key plus Value, i.e. 16MB.
Definition: atomic_hash_table.hh:86
CAtomicHashTable()
Prepare an uninitialized object.
Definition: atomic_hash_table.hh:116
CAtomicHashTable(char *buf, size_t sz, size_t capacity, int row, size_t valueLen, bool create=false)
Attach to or create a hash table in a memory buffer.
Definition: atomic_hash_table.hh:130
std::string toString() const
Get a description of the hash table.
Definition: atomic_hash_table.hh:264
size_t sizeOfRow(int row) const
Get number of key-value pairs in a row.
Definition: atomic_hash_table.hh:214
size_t capacity() const
Get capacity of the hash table.
Definition: atomic_hash_table.hh:208
bool has(const key_type &key) const
Test if a key exists in the hash table.
Definition: atomic_hash_table.hh:373
int rowSize() const
Get number of rows in hash table.
Definition: atomic_hash_table.hh:195
time_t updateTime() const
Get latest updating time of the hash table.
Definition: atomic_hash_table.hh:252
bool update(const key_type &key, const char *value, size_t len)
Update value of a key.
Definition: atomic_hash_table.hh:387
bool insert(const key_type &key, const char *value, size_t len)
Insert a key-value pair into the hash table.
Definition: atomic_hash_table.hh:286
size_t size() const
Get number of key-value pair in the hash table.
Definition: atomic_hash_table.hh:219
bool init(char *buf, size_t sz)
Attach to an existing hash table hosted in a memory buffer.
Definition: atomic_hash_table.hh:144
static size_t CalcBufSize(size_t capacity, int row, size_t valueLen)
Compute memory size needed for a hash table.
Definition: atomic_hash_table.hh:103
bool init(char *buf, size_t sz, size_t capacity, int row, size_t valueLen, bool create=false)
Attach to or create a hash table in a memory buffer.
Definition: atomic_hash_table.hh:166
bool valid() const
Test if current object is initialized.
Definition: atomic_hash_table.hh:185
bool insert(const key_type &key, const std::string &value)
Insert a key-value pair into the hash table.
Definition: atomic_hash_table.hh:309
time_t createTime() const
Get creation time of the hash table.
Definition: atomic_hash_table.hh:245
void clear()
Clear all data in the hash table.
Definition: atomic_hash_table.hh:440
A lock-free hash table that can be used in multi-thread or multi-process programs.
Definition: atomic_hash_table.hh:69
size_t capacityOfRow(int row) const
Get capacity of a row in hash table.
Definition: atomic_hash_table.hh:201
bool update(const key_type &key, const std::string &value)
Update value of a key.
Definition: atomic_hash_table.hh:415
Definition: template.hh:174
bool empty() const
Test if the hash table is empty.
Definition: atomic_hash_table.hh:229
CAtomicHashTable(char *buf, size_t sz)
Attach to an existing hash table hosted in a memory buffer.
Definition: atomic_hash_table.hh:121