NS-3 based Named Data Networking (NDN) simulator
ndnSIM 2.3: NDN, CCN, CCNx, content centric networks
API Documentation
name-tree-hashtable.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
26 #include "name-tree-hashtable.hpp"
27 #include "core/logger.hpp"
28 #include "core/city-hash.hpp"
29 
30 namespace nfd {
31 namespace name_tree {
32 
33 NFD_LOG_INIT("NameTreeHashtable");
34 
35 class Hash32
36 {
37 public:
38  static HashValue
39  compute(const void* buffer, size_t length)
40  {
41  return static_cast<HashValue>(CityHash32(reinterpret_cast<const char*>(buffer), length));
42  }
43 };
44 
45 class Hash64
46 {
47 public:
48  static HashValue
49  compute(const void* buffer, size_t length)
50  {
51  return static_cast<HashValue>(CityHash64(reinterpret_cast<const char*>(buffer), length));
52  }
53 };
54 
57 typedef std::conditional<(sizeof(HashValue) > 4), Hash64, Hash32>::type HashFunc;
58 
60 computeHash(const Name& name, ssize_t prefixLen)
61 {
62  name.wireEncode(); // ensure wire buffer exists
63 
64  HashValue h = 0;
65  for (size_t i = 0, last = prefixLen < 0 ? name.size() : prefixLen; i < last; ++i) {
66  const name::Component& comp = name[i];
67  h ^= HashFunc::compute(comp.wire(), comp.size());
68  }
69  return h;
70 }
71 
74 {
75  name.wireEncode(); // ensure wire buffer exists
76 
77  HashSequence seq;
78  seq.reserve(name.size() + 1);
79 
80  HashValue h = 0;
81  seq.push_back(h);
82 
83  for (const name::Component& comp : name) {
84  h ^= HashFunc::compute(comp.wire(), comp.size());
85  seq.push_back(h);
86  }
87  return seq;
88 }
89 
91  : hash(h)
92  , prev(nullptr)
93  , next(nullptr)
94  , entry(name, this)
95 {
96 }
97 
99 {
100  BOOST_ASSERT(prev == nullptr);
101  BOOST_ASSERT(next == nullptr);
102 }
103 
104 Node*
106 {
107  return entry.m_node;
108 }
109 
111  : initialSize(size)
112  , minSize(size)
113 {
114 }
115 
117  : m_options(options)
118  , m_size(0)
119 {
120  BOOST_ASSERT(m_options.minSize > 0);
121  BOOST_ASSERT(m_options.initialSize >= m_options.minSize);
122  BOOST_ASSERT(m_options.expandLoadFactor > 0.0);
123  BOOST_ASSERT(m_options.expandLoadFactor <= 1.0);
124  BOOST_ASSERT(m_options.expandFactor > 1.0);
125  BOOST_ASSERT(m_options.shrinkLoadFactor >= 0.0);
126  BOOST_ASSERT(m_options.shrinkLoadFactor < 1.0);
127  BOOST_ASSERT(m_options.shrinkFactor > 0.0);
128  BOOST_ASSERT(m_options.shrinkFactor < 1.0);
129 
130  m_buckets.resize(options.initialSize);
131  this->computeThresholds();
132 }
133 
135 {
136  for (size_t i = 0; i < m_buckets.size(); ++i) {
137  foreachNode(m_buckets[i], [] (Node* node) {
138  node->prev = node->next = nullptr;
139  delete node;
140  });
141  }
142 }
143 
144 void
145 Hashtable::attach(size_t bucket, Node* node)
146 {
147  node->prev = nullptr;
148  node->next = m_buckets[bucket];
149 
150  if (node->next != nullptr) {
151  BOOST_ASSERT(node->next->prev == nullptr);
152  node->next->prev = node;
153  }
154 
155  m_buckets[bucket] = node;
156 }
157 
158 void
159 Hashtable::detach(size_t bucket, Node* node)
160 {
161  if (node->prev != nullptr) {
162  BOOST_ASSERT(node->prev->next == node);
163  node->prev->next = node->next;
164  }
165  else {
166  BOOST_ASSERT(m_buckets[bucket] == node);
167  m_buckets[bucket] = node->next;
168  }
169 
170  if (node->next != nullptr) {
171  BOOST_ASSERT(node->next->prev == node);
172  node->next->prev = node->prev;
173  }
174 
175  node->prev = node->next = nullptr;
176 }
177 
178 std::pair<const Node*, bool>
179 Hashtable::findOrInsert(const Name& name, size_t prefixLen, HashValue h, bool allowInsert)
180 {
181  size_t bucket = this->computeBucketIndex(h);
182 
183  for (const Node* node = m_buckets[bucket]; node != nullptr; node = node->next) {
184  if (node->hash == h && name.compare(0, prefixLen, node->entry.getName()) == 0) {
185  NFD_LOG_TRACE("found " << name.getPrefix(prefixLen) << " hash=" << h << " bucket=" << bucket);
186  return {node, false};
187  }
188  }
189 
190  if (!allowInsert) {
191  NFD_LOG_TRACE("not-found " << name.getPrefix(prefixLen) << " hash=" << h << " bucket=" << bucket);
192  return {nullptr, false};
193  }
194 
195  Node* node = new Node(h, name.getPrefix(prefixLen));
196  this->attach(bucket, node);
197  NFD_LOG_TRACE("insert " << node->entry.getName() << " hash=" << h << " bucket=" << bucket);
198  ++m_size;
199 
200  if (m_size > m_expandThreshold) {
201  this->resize(static_cast<size_t>(m_options.expandFactor * this->getNBuckets()));
202  }
203 
204  return {node, true};
205 }
206 
207 const Node*
208 Hashtable::find(const Name& name, size_t prefixLen) const
209 {
210  HashValue h = computeHash(name, prefixLen);
211  return const_cast<Hashtable*>(this)->findOrInsert(name, prefixLen, h, false).first;
212 }
213 
214 const Node*
215 Hashtable::find(const Name& name, size_t prefixLen, const HashSequence& hashes) const
216 {
217  BOOST_ASSERT(hashes.at(prefixLen) == computeHash(name, prefixLen));
218  return const_cast<Hashtable*>(this)->findOrInsert(name, prefixLen, hashes[prefixLen], false).first;
219 }
220 
221 std::pair<const Node*, bool>
222 Hashtable::insert(const Name& name, size_t prefixLen, const HashSequence& hashes)
223 {
224  BOOST_ASSERT(hashes.at(prefixLen) == computeHash(name, prefixLen));
225  return this->findOrInsert(name, prefixLen, hashes[prefixLen], true);
226 }
227 
228 void
230 {
231  BOOST_ASSERT(node != nullptr);
232  BOOST_ASSERT(node->entry.getParent() == nullptr);
233 
234  size_t bucket = this->computeBucketIndex(node->hash);
235  NFD_LOG_TRACE("erase " << node->entry.getName() << " hash=" << node->hash << " bucket=" << bucket);
236 
237  this->detach(bucket, node);
238  delete node;
239  --m_size;
240 
241  if (m_size < m_shrinkThreshold) {
242  size_t newNBuckets = std::max(m_options.minSize,
243  static_cast<size_t>(m_options.shrinkFactor * this->getNBuckets()));
244  this->resize(newNBuckets);
245  }
246 }
247 
248 void
249 Hashtable::computeThresholds()
250 {
251  m_expandThreshold = static_cast<size_t>(m_options.expandLoadFactor * this->getNBuckets());
252  m_shrinkThreshold = static_cast<size_t>(m_options.shrinkLoadFactor * this->getNBuckets());
253  NFD_LOG_TRACE("thresholds expand=" << m_expandThreshold << " shrink=" << m_shrinkThreshold);
254 }
255 
256 void
257 Hashtable::resize(size_t newNBuckets)
258 {
259  if (this->getNBuckets() == newNBuckets) {
260  return;
261  }
262  NFD_LOG_DEBUG("resize from=" << this->getNBuckets() << " to=" << newNBuckets);
263 
264  std::vector<Node*> oldBuckets;
265  oldBuckets.swap(m_buckets);
266  m_buckets.resize(newNBuckets);
267 
268  for (Node* head : oldBuckets) {
269  foreachNode(head, [this] (Node* node) {
270  size_t bucket = this->computeBucketIndex(node->hash);
271  this->attach(bucket, node);
272  });
273  }
274 
275  this->computeThresholds();
276 }
277 
278 } // namespace name_tree
279 } // namespace nfd
PartialName getPrefix(ssize_t nComponents) const
Extract a prefix (PartialName) of the name, containing first nComponents components.
Definition: name.hpp:241
HashSequence computeHashes(const Name &name)
computes hash values for each prefix of name
float expandLoadFactor
if hashtable has more than nBuckets*expandLoadFactor nodes, it will be expanded
void foreachNode(N *head, const F &func)
invoke a function for each node in a doubly linked list
HashtableOptions(size_t size=16)
constructor
std::pair< const Node *, bool > insert(const Name &name, size_t prefixLen, const HashSequence &hashes)
find or insert node for name.getPrefix(prefixLen)
Node(HashValue h, const Name &name)
uint32 CityHash32(const char *s, size_t len)
Definition: city-hash.cpp:182
float expandFactor
when hashtable is expanded, its new size is nBuckets*expandFactor
const uint8_t * wire() const
Definition: block.cpp:495
size_t computeBucketIndex(HashValue h) const
std::conditional<(sizeof(HashValue) > 4), Hash64, Hash32 >::type HashFunc
a type with compute static method to compute hash value from a raw buffer
#define NFD_LOG_DEBUG(expression)
Definition: logger.hpp:55
provides options for Hashtable
const Node * find(const Name &name, size_t prefixLen) const
find node for name.getPrefix(prefixLen)
float shrinkLoadFactor
if hashtable has less than nBuckets*shrinkLoadFactor nodes, it will be shrunk
a hashtable for fast exact name lookup
~Hashtable()
deallocates all nodes
uint64 CityHash64(const char *s, size_t len)
Definition: city-hash.cpp:359
#define NFD_LOG_TRACE(expression)
Definition: logger.hpp:54
size_t size() const
Definition: block.cpp:504
int compare(const Name &other) const
Compare this to the other Name using NDN canonical ordering.
Definition: name.hpp:466
Copyright (c) 2011-2015 Regents of the University of California.
Definition: ndn-common.hpp:40
Name abstraction to represent an absolute name.
Definition: name.hpp:46
size_t HashValue
a single hash value
std::vector< HashValue > HashSequence
a sequence of hash values
Node * getNode(const Entry &entry)
size_t size() const
Get the number of components.
Definition: name.hpp:400
HashValue computeHash(const Name &name, ssize_t prefixLen)
computes a single hash value
size_t initialSize
initial number of buckets
Component holds a read-only name component value.
Hashtable(const Options &options)
float shrinkFactor
when hashtable is shrunk, its new size is max(nBuckets*shrinkFactor, minSize)
size_t minSize
minimal number of buckets
size_t wireEncode(EncodingImpl< TAG > &encoder) const
Fast encoding or block size estimation.
Definition: name.cpp:122
static HashValue compute(const void *buffer, size_t length)
static HashValue compute(const void *buffer, size_t length)
Entry * getParent() const
#define NFD_LOG_INIT(name)
Definition: logger.hpp:34
const Name & getName() const
an entry in the name tree
void erase(Node *node)
delete node