blob: 964730e79520441fee6e5a3265e5d1142f809a73 [file] [log] [blame]
HYuana9b85752014-02-26 02:32:30 -06001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (C) 2014 Named Data Networking Project
4 * See COPYING for copyright and distribution information.
5 */
6
7// Name Tree (Name Prefix Hash Table)
8
9#include "name-tree.hpp"
10#include "core/logger.hpp"
11
12namespace nfd {
13
14NFD_LOG_INIT("NameTree");
15
16namespace name_tree {
17
18// Interface of different hash functions
19uint32_t
20hashName(const Name& prefix)
21{
22 // fixed value. Used for debugging only.
23 uint32_t ret = 0;
24
25 // Boost hash
26 // requires the /boost/functional/hash.hpp header file
27 // TODO: improve hash efficiency with Name type
28 boost::hash<std::string> stringHash;
29 ret = stringHash(prefix.toUri());
30
31 return ret;
32}
33
34} // namespace name_tree
35
36NameTree::NameTree(size_t nBuckets)
37 : m_nItems(0)
38 , m_nBuckets(nBuckets)
39 , m_loadFactor(0.5)
40 , m_resizeFactor(2)
41{
42 m_resizeThreshold = static_cast<size_t>(m_loadFactor *
43 static_cast<double>(m_nBuckets));
44
45 // array of node pointers
46 m_buckets = new name_tree::Node*[m_nBuckets];
47 // Initialize the pointer array
48 for (size_t i = 0; i < m_nBuckets; i++)
49 m_buckets[i] = 0;
50}
51
52NameTree::~NameTree()
53{
54 for (size_t i = 0; i < m_nBuckets; i++)
55 {
56 if (m_buckets[i] != 0)
57 delete m_buckets[i];
58 }
59
60 delete [] m_buckets;
61}
62
63// insert() is a private function, and called by only lookup()
64std::pair<shared_ptr<name_tree::Entry>, bool>
65NameTree::insert(const Name& prefix)
66{
67 NFD_LOG_DEBUG("insert " << prefix);
68
69 uint32_t hashValue = name_tree::hashName(prefix);
70 uint32_t loc = hashValue % m_nBuckets;
71
72 NFD_LOG_DEBUG("Name " << prefix << " hash value = " << hashValue << " location = " << loc);
73
74 // Check if this Name has been stored
75 name_tree::Node* node = m_buckets[loc];
76 name_tree::Node* nodePrev = node; // initialize nodePrev to node
77
78 for (node = m_buckets[loc]; node != 0; node = node->m_next)
79 {
80 if (static_cast<bool>(node->m_entry))
81 {
82 if (prefix == node->m_entry->m_prefix)
83 {
84 return std::make_pair(node->m_entry, false); // false: old entry
85 }
86 }
87 nodePrev = node;
88 }
89
90 NFD_LOG_DEBUG("Did not find " << prefix << ", need to insert it to the table");
91
92 // If no bucket is empty occupied, we need to create a new node, and it is
93 // linked from nodePrev
94 node = new name_tree::Node();
95 node->m_prev = nodePrev;
96
97 if (nodePrev == 0)
98 {
99 m_buckets[loc] = node;
100 }
101 else
102 {
103 nodePrev->m_next = node;
104 }
105
106 // Create a new Entry
107 shared_ptr<name_tree::Entry> entry(make_shared<name_tree::Entry>(prefix));
108 entry->setHash(hashValue);
109 node->m_entry = entry; // link the Entry to its Node
110 entry->m_node = node; // link the node to Entry. Used in eraseEntryIfEmpty.
111
112 return std::make_pair(entry, true); // true: new entry
113}
114
115// Name Prefix Lookup. Create Name Tree Entry if not found
116shared_ptr<name_tree::Entry>
117NameTree::lookup(const Name& prefix)
118{
119 NFD_LOG_DEBUG("lookup " << prefix);
120
121 shared_ptr<name_tree::Entry> entry;
122 shared_ptr<name_tree::Entry> parent;
123
124 for (size_t i = 0; i <= prefix.size(); i++)
125 {
126 Name temp = prefix.getPrefix(i);
127
128 // insert() will create the entry if it does not exist.
129 std::pair<shared_ptr<name_tree::Entry>, bool> ret = insert(temp);
130 entry = ret.first;
131
132 if (ret.second == true)
133 {
134 m_nItems++; /* Increase the counter */
135 entry->m_parent = parent;
136
137 if (static_cast<bool>(parent))
138 {
139 parent->m_children.push_back(entry);
140 }
141 }
142
143 if (m_nItems > m_resizeThreshold)
144 {
145 resize(m_resizeFactor * m_nBuckets);
146 }
147
148 parent = entry;
149 }
150 return entry;
151}
152
153// Exact Match
154shared_ptr<name_tree::Entry>
155NameTree::findExactMatch(const Name& prefix) const
156{
157 NFD_LOG_DEBUG("findExactMatch " << prefix);
158
159 uint32_t hashValue = name_tree::hashName(prefix);
160 uint32_t loc = hashValue % m_nBuckets;
161
162 NFD_LOG_DEBUG("Name " << prefix << " hash value = " << hashValue <<
163 " location = " << loc);
164
165 shared_ptr<name_tree::Entry> entry;
166 name_tree::Node* node = 0;
167
168 for (node = m_buckets[loc]; node != 0; node = node->m_next)
169 {
170 entry = node->m_entry;
171 if (static_cast<bool>(entry))
172 {
173 if (hashValue == entry->getHash() && prefix == entry->getPrefix())
174 {
175 return entry;
176 }
177 } // if entry
178 } // for node
179
180 // if not found, the default value of entry (null pointer) will be returned
181 entry.reset();
182 return entry;
183}
184
185// Longest Prefix Match
186// Return the longest matching Entry address
187// start from the full name, and then remove 1 name comp each time
188shared_ptr<name_tree::Entry>
Junxiao Shi40631842014-03-01 13:52:37 -0700189NameTree::findLongestPrefixMatch(const Name& prefix, const name_tree::EntrySelector& entrySelector)
HYuana9b85752014-02-26 02:32:30 -0600190{
191 NFD_LOG_DEBUG("findLongestPrefixMatch " << prefix);
192
193 shared_ptr<name_tree::Entry> entry;
194
195 for (int i = prefix.size(); i >= 0; i--)
196 {
197 entry = findExactMatch(prefix.getPrefix(i));
Junxiao Shi40631842014-03-01 13:52:37 -0700198 if (static_cast<bool>(entry) && entrySelector(*entry))
HYuana9b85752014-02-26 02:32:30 -0600199 return entry;
200 }
201
Junxiao Shi40631842014-03-01 13:52:37 -0700202 return shared_ptr<name_tree::Entry>();
HYuana9b85752014-02-26 02:32:30 -0600203}
204
205// return {false: this entry is not empty, true: this entry is empty and erased}
206bool
207NameTree::eraseEntryIfEmpty(shared_ptr<name_tree::Entry> entry)
208{
209 BOOST_ASSERT(static_cast<bool>(entry));
210
211 NFD_LOG_DEBUG("eraseEntryIfEmpty " << entry->getPrefix());
212
213 // first check if this Entry can be erased
Junxiao Shi40631842014-03-01 13:52:37 -0700214 if (entry->isEmpty())
HYuana9b85752014-02-26 02:32:30 -0600215 {
216 // update child-related info in the parent
217 shared_ptr<name_tree::Entry> parent = entry->getParent();
218
219 if (static_cast<bool>(parent))
220 {
221 std::vector<shared_ptr<name_tree::Entry> >& parentChildrenList =
222 parent->getChildren();
223
224 bool isFound = false;
225 size_t size = parentChildrenList.size();
226 for (size_t i = 0; i < size; i++)
227 {
228 if (parentChildrenList[i] == entry)
229 {
230 parentChildrenList[i] = parentChildrenList[size - 1];
231 parentChildrenList.pop_back();
232 isFound = true;
233 break;
234 }
235 }
236
237 BOOST_ASSERT(isFound == true);
238 }
239
240 // remove this Entry and its Name Tree Node
241 name_tree::Node* node = entry->m_node;
242 name_tree::Node* nodePrev = node->m_prev;
243
244 // configure the previous node
245 if (nodePrev != 0)
246 {
247 // link the previous node to the next node
248 nodePrev->m_next = node->m_next;
249 }
250 else
251 {
252 m_buckets[entry->getHash() % m_nBuckets] = node->m_next;
253 }
254
255 // link the previous node with the next node (skip the erased one)
256 if (node->m_next != 0)
257 {
258 node->m_next->m_prev = nodePrev;
259 node->m_next = 0;
260 }
261
262 BOOST_ASSERT(node->m_next == 0);
263
264 m_nItems--;
265 delete node;
266
267 if (static_cast<bool>(parent))
268 eraseEntryIfEmpty(parent);
269
270 return true;
271
272 } // if this entry is empty
273
274 return false; // if this entry is not empty
275}
276
277shared_ptr<std::vector<shared_ptr<name_tree::Entry> > >
Junxiao Shi40631842014-03-01 13:52:37 -0700278NameTree::fullEnumerate(const name_tree::EntrySelector& entrySelector)
HYuana9b85752014-02-26 02:32:30 -0600279{
280 NFD_LOG_DEBUG("fullEnumerate");
281
Junxiao Shi40631842014-03-01 13:52:37 -0700282 shared_ptr<std::vector<shared_ptr<name_tree::Entry> > > results =
HYuana9b85752014-02-26 02:32:30 -0600283 make_shared<std::vector<shared_ptr<name_tree::Entry> > >();
284
Junxiao Shi40631842014-03-01 13:52:37 -0700285 for (size_t i = 0; i < m_nBuckets; i++) {
286 for (name_tree::Node* node = m_buckets[i]; node != 0; node = node->m_next) {
287 if (static_cast<bool>(node->m_entry) && entrySelector(*node->m_entry)) {
288 results->push_back(node->m_entry);
289 }
HYuana9b85752014-02-26 02:32:30 -0600290 }
Junxiao Shi40631842014-03-01 13:52:37 -0700291 }
HYuana9b85752014-02-26 02:32:30 -0600292
Junxiao Shi40631842014-03-01 13:52:37 -0700293 return results;
HYuana9b85752014-02-26 02:32:30 -0600294}
295
296// Helper function for partialEnumerate()
297void
298NameTree::partialEnumerateAddChildren(shared_ptr<name_tree::Entry> entry,
Junxiao Shi40631842014-03-01 13:52:37 -0700299 const name_tree::EntrySelector& entrySelector,
300 std::vector<shared_ptr<name_tree::Entry> >& results)
HYuana9b85752014-02-26 02:32:30 -0600301{
302 BOOST_ASSERT(static_cast<bool>(entry));
Junxiao Shi40631842014-03-01 13:52:37 -0700303
304 if (!entrySelector(*entry)) {
305 return;
306 }
HYuana9b85752014-02-26 02:32:30 -0600307
Junxiao Shi40631842014-03-01 13:52:37 -0700308 results.push_back(entry);
HYuana9b85752014-02-26 02:32:30 -0600309 for (size_t i = 0; i < entry->m_children.size(); i++)
310 {
Junxiao Shi40631842014-03-01 13:52:37 -0700311 shared_ptr<name_tree::Entry> child = entry->m_children[i];
312 partialEnumerateAddChildren(child, entrySelector, results);
HYuana9b85752014-02-26 02:32:30 -0600313 }
314}
315
316shared_ptr<std::vector<shared_ptr<name_tree::Entry> > >
Junxiao Shi40631842014-03-01 13:52:37 -0700317NameTree::partialEnumerate(const Name& prefix,
318 const name_tree::EntrySelector& entrySelector)
HYuana9b85752014-02-26 02:32:30 -0600319{
320 NFD_LOG_DEBUG("partialEnumerate" << prefix);
321
Junxiao Shi40631842014-03-01 13:52:37 -0700322 shared_ptr<std::vector<shared_ptr<name_tree::Entry> > > results =
HYuana9b85752014-02-26 02:32:30 -0600323 make_shared<std::vector<shared_ptr<name_tree::Entry> > >();
324
325 // find the hash bucket corresponding to that prefix
326 shared_ptr<name_tree::Entry> entry = findExactMatch(prefix);
327
Junxiao Shi40631842014-03-01 13:52:37 -0700328 if (static_cast<bool>(entry)) {
329 // go through its children list via depth-first-search
330 partialEnumerateAddChildren(entry, entrySelector, *results);
331 }
HYuana9b85752014-02-26 02:32:30 -0600332
Junxiao Shi40631842014-03-01 13:52:37 -0700333 return results;
HYuana9b85752014-02-26 02:32:30 -0600334}
335
336// Hash Table Resize
337void
338NameTree::resize(size_t newNBuckets)
339{
340 NFD_LOG_DEBUG("resize");
341
342 name_tree::Node** newBuckets = new name_tree::Node*[newNBuckets];
Junxiao Shi40631842014-03-01 13:52:37 -0700343 size_t count = 0;
HYuana9b85752014-02-26 02:32:30 -0600344
345 // referenced ccnx hashtb.c hashtb_rehash()
346 name_tree::Node** pp = 0;
347 name_tree::Node* p = 0;
348 name_tree::Node* pre = 0;
349 name_tree::Node* q = 0; // record p->m_next
Junxiao Shi40631842014-03-01 13:52:37 -0700350 size_t i;
HYuana9b85752014-02-26 02:32:30 -0600351 uint32_t h;
352 uint32_t b;
353
354 for (i = 0; i < newNBuckets; i++)
355 {
356 newBuckets[i] = 0;
357 }
358
359 for (i = 0; i < m_nBuckets; i++)
360 {
361 for (p = m_buckets[i]; p != 0; p = q)
362 {
363 count++;
364 q = p->m_next;
365 BOOST_ASSERT(static_cast<bool>(p->m_entry));
366 h = p->m_entry->m_hash;
367 b = h % newNBuckets;
368 for (pp = &newBuckets[b]; *pp != 0; pp = &((*pp)->m_next))
369 {
370 pre = *pp;
371 continue;
372 }
373 p->m_prev = pre;
374 p->m_next = *pp; // Actually *pp always == 0 in this case
375 *pp = p;
376 }
377 }
378
379 BOOST_ASSERT(count == m_nItems);
380
381 name_tree::Node** oldBuckets = m_buckets;
382 m_buckets = newBuckets;
383 delete oldBuckets;
384
385 m_nBuckets = newNBuckets;
386 m_resizeThreshold = (int)(m_loadFactor * (double)m_nBuckets);
387}
388
389// For debugging
390void
391NameTree::dump(std::ostream& output)
392{
393 NFD_LOG_DEBUG("dump()");
394
395 name_tree::Node* node = 0;
396 shared_ptr<name_tree::Entry> entry;
397
398 using std::endl;
399
400 for (size_t i = 0; i < m_nBuckets; i++)
401 {
402 for (node = m_buckets[i]; node != 0; node = node->m_next)
403 {
404 entry = node->m_entry;
405
406 // if the Entry exist, dump its information
407 if (static_cast<bool>(entry))
408 {
409 output << "Bucket" << i << "\t" << entry->m_prefix.toUri() << endl;
410 output << "\t\tHash " << entry->m_hash << endl;
411
412 if (static_cast<bool>(entry->m_parent))
413 {
414 output << "\t\tparent->" << entry->m_parent->m_prefix.toUri();
415 }
416 else
417 {
418 output << "\t\tROOT";
419 }
420 output << endl;
421
422 if (entry->m_children.size() != 0)
423 {
424 output << "\t\tchildren = " << entry->m_children.size() << endl;
425
426 for (size_t j = 0; j < entry->m_children.size(); j++)
427 {
428 output << "\t\t\tChild " << j << " " <<
429 entry->m_children[j]->getPrefix() << endl;
430 }
431 }
432
433 } // if (static_cast<bool>(entry))
434
435 } // for node
436 } // for int i
437
438 output << "Bucket count = " << m_nBuckets << endl;
439 output << "Stored item = " << m_nItems << endl;
440 output << "--------------------------\n";
441}
442
443} // namespace nfd