blob: b71c9708f6439a54f37bffce9cf2fb9ba2ed31ae [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)
Haowei Yuane1079fc2014-03-08 14:41:25 -060041 , m_endIterator(FULL_ENUMERATE_TYPE, *this, m_end)
HYuana9b85752014-02-26 02:32:30 -060042{
43 m_resizeThreshold = static_cast<size_t>(m_loadFactor *
44 static_cast<double>(m_nBuckets));
45
46 // array of node pointers
47 m_buckets = new name_tree::Node*[m_nBuckets];
48 // Initialize the pointer array
49 for (size_t i = 0; i < m_nBuckets; i++)
50 m_buckets[i] = 0;
51}
52
53NameTree::~NameTree()
54{
55 for (size_t i = 0; i < m_nBuckets; i++)
56 {
57 if (m_buckets[i] != 0)
58 delete m_buckets[i];
59 }
60
61 delete [] m_buckets;
62}
63
64// insert() is a private function, and called by only lookup()
65std::pair<shared_ptr<name_tree::Entry>, bool>
66NameTree::insert(const Name& prefix)
67{
Alexander Afanasyevbf9edee2014-03-31 23:05:27 -070068 NFD_LOG_TRACE("insert " << prefix);
HYuana9b85752014-02-26 02:32:30 -060069
70 uint32_t hashValue = name_tree::hashName(prefix);
71 uint32_t loc = hashValue % m_nBuckets;
72
Alexander Afanasyevbf9edee2014-03-31 23:05:27 -070073 NFD_LOG_TRACE("Name " << prefix << " hash value = " << hashValue << " location = " << loc);
HYuana9b85752014-02-26 02:32:30 -060074
75 // Check if this Name has been stored
76 name_tree::Node* node = m_buckets[loc];
77 name_tree::Node* nodePrev = node; // initialize nodePrev to node
78
79 for (node = m_buckets[loc]; node != 0; node = node->m_next)
80 {
81 if (static_cast<bool>(node->m_entry))
82 {
83 if (prefix == node->m_entry->m_prefix)
84 {
85 return std::make_pair(node->m_entry, false); // false: old entry
86 }
87 }
88 nodePrev = node;
89 }
90
Alexander Afanasyevbf9edee2014-03-31 23:05:27 -070091 NFD_LOG_TRACE("Did not find " << prefix << ", need to insert it to the table");
HYuana9b85752014-02-26 02:32:30 -060092
93 // If no bucket is empty occupied, we need to create a new node, and it is
94 // linked from nodePrev
95 node = new name_tree::Node();
96 node->m_prev = nodePrev;
97
98 if (nodePrev == 0)
99 {
100 m_buckets[loc] = node;
101 }
102 else
103 {
104 nodePrev->m_next = node;
105 }
106
107 // Create a new Entry
108 shared_ptr<name_tree::Entry> entry(make_shared<name_tree::Entry>(prefix));
109 entry->setHash(hashValue);
110 node->m_entry = entry; // link the Entry to its Node
111 entry->m_node = node; // link the node to Entry. Used in eraseEntryIfEmpty.
112
113 return std::make_pair(entry, true); // true: new entry
114}
115
116// Name Prefix Lookup. Create Name Tree Entry if not found
117shared_ptr<name_tree::Entry>
118NameTree::lookup(const Name& prefix)
119{
Alexander Afanasyevbf9edee2014-03-31 23:05:27 -0700120 NFD_LOG_TRACE("lookup " << prefix);
HYuana9b85752014-02-26 02:32:30 -0600121
122 shared_ptr<name_tree::Entry> entry;
123 shared_ptr<name_tree::Entry> parent;
124
125 for (size_t i = 0; i <= prefix.size(); i++)
126 {
127 Name temp = prefix.getPrefix(i);
128
129 // insert() will create the entry if it does not exist.
130 std::pair<shared_ptr<name_tree::Entry>, bool> ret = insert(temp);
131 entry = ret.first;
132
133 if (ret.second == true)
134 {
135 m_nItems++; /* Increase the counter */
136 entry->m_parent = parent;
137
138 if (static_cast<bool>(parent))
139 {
140 parent->m_children.push_back(entry);
141 }
142 }
143
144 if (m_nItems > m_resizeThreshold)
145 {
146 resize(m_resizeFactor * m_nBuckets);
147 }
148
149 parent = entry;
150 }
151 return entry;
152}
153
154// Exact Match
155shared_ptr<name_tree::Entry>
156NameTree::findExactMatch(const Name& prefix) const
157{
Alexander Afanasyevbf9edee2014-03-31 23:05:27 -0700158 NFD_LOG_TRACE("findExactMatch " << prefix);
HYuana9b85752014-02-26 02:32:30 -0600159
160 uint32_t hashValue = name_tree::hashName(prefix);
161 uint32_t loc = hashValue % m_nBuckets;
162
Alexander Afanasyevbf9edee2014-03-31 23:05:27 -0700163 NFD_LOG_TRACE("Name " << prefix << " hash value = " << hashValue <<
HYuana9b85752014-02-26 02:32:30 -0600164 " location = " << loc);
165
166 shared_ptr<name_tree::Entry> entry;
167 name_tree::Node* node = 0;
168
169 for (node = m_buckets[loc]; node != 0; node = node->m_next)
170 {
171 entry = node->m_entry;
172 if (static_cast<bool>(entry))
173 {
174 if (hashValue == entry->getHash() && prefix == entry->getPrefix())
175 {
176 return entry;
177 }
178 } // if entry
179 } // for node
180
181 // if not found, the default value of entry (null pointer) will be returned
182 entry.reset();
183 return entry;
184}
185
186// Longest Prefix Match
187// Return the longest matching Entry address
188// start from the full name, and then remove 1 name comp each time
189shared_ptr<name_tree::Entry>
Haowei Yuane1079fc2014-03-08 14:41:25 -0600190NameTree::findLongestPrefixMatch(const Name& prefix, const name_tree::EntrySelector& entrySelector) const
HYuana9b85752014-02-26 02:32:30 -0600191{
Alexander Afanasyevbf9edee2014-03-31 23:05:27 -0700192 NFD_LOG_TRACE("findLongestPrefixMatch " << prefix);
HYuana9b85752014-02-26 02:32:30 -0600193
194 shared_ptr<name_tree::Entry> entry;
195
196 for (int i = prefix.size(); i >= 0; i--)
197 {
198 entry = findExactMatch(prefix.getPrefix(i));
Junxiao Shi40631842014-03-01 13:52:37 -0700199 if (static_cast<bool>(entry) && entrySelector(*entry))
HYuana9b85752014-02-26 02:32:30 -0600200 return entry;
201 }
202
Junxiao Shi40631842014-03-01 13:52:37 -0700203 return shared_ptr<name_tree::Entry>();
HYuana9b85752014-02-26 02:32:30 -0600204}
205
HangZhangcb4fc832014-03-11 16:57:11 +0800206shared_ptr<name_tree::Entry>
207NameTree::findLongestPrefixMatch(shared_ptr<name_tree::Entry> entry,
208 const name_tree::EntrySelector& entrySelector) const
209{
210 while (static_cast<bool>(entry))
211 {
212 if (entrySelector(*entry))
213 return entry;
214 entry = entry->getParent();
215 }
216 return shared_ptr<name_tree::Entry>();
217}
218
HYuana9b85752014-02-26 02:32:30 -0600219// return {false: this entry is not empty, true: this entry is empty and erased}
220bool
221NameTree::eraseEntryIfEmpty(shared_ptr<name_tree::Entry> entry)
222{
223 BOOST_ASSERT(static_cast<bool>(entry));
224
Alexander Afanasyevbf9edee2014-03-31 23:05:27 -0700225 NFD_LOG_TRACE("eraseEntryIfEmpty " << entry->getPrefix());
HYuana9b85752014-02-26 02:32:30 -0600226
227 // first check if this Entry can be erased
Junxiao Shi40631842014-03-01 13:52:37 -0700228 if (entry->isEmpty())
HYuana9b85752014-02-26 02:32:30 -0600229 {
230 // update child-related info in the parent
231 shared_ptr<name_tree::Entry> parent = entry->getParent();
232
233 if (static_cast<bool>(parent))
234 {
235 std::vector<shared_ptr<name_tree::Entry> >& parentChildrenList =
236 parent->getChildren();
237
238 bool isFound = false;
239 size_t size = parentChildrenList.size();
240 for (size_t i = 0; i < size; i++)
241 {
242 if (parentChildrenList[i] == entry)
243 {
244 parentChildrenList[i] = parentChildrenList[size - 1];
245 parentChildrenList.pop_back();
246 isFound = true;
247 break;
248 }
249 }
250
251 BOOST_ASSERT(isFound == true);
252 }
253
254 // remove this Entry and its Name Tree Node
255 name_tree::Node* node = entry->m_node;
256 name_tree::Node* nodePrev = node->m_prev;
257
258 // configure the previous node
259 if (nodePrev != 0)
260 {
261 // link the previous node to the next node
262 nodePrev->m_next = node->m_next;
263 }
264 else
265 {
266 m_buckets[entry->getHash() % m_nBuckets] = node->m_next;
267 }
268
269 // link the previous node with the next node (skip the erased one)
270 if (node->m_next != 0)
271 {
272 node->m_next->m_prev = nodePrev;
273 node->m_next = 0;
274 }
275
276 BOOST_ASSERT(node->m_next == 0);
277
278 m_nItems--;
279 delete node;
280
281 if (static_cast<bool>(parent))
282 eraseEntryIfEmpty(parent);
283
284 return true;
285
286 } // if this entry is empty
287
288 return false; // if this entry is not empty
289}
290
Haowei Yuane1079fc2014-03-08 14:41:25 -0600291NameTree::const_iterator
292NameTree::fullEnumerate(const name_tree::EntrySelector& entrySelector) const
HYuana9b85752014-02-26 02:32:30 -0600293{
Alexander Afanasyevbf9edee2014-03-31 23:05:27 -0700294 NFD_LOG_TRACE("fullEnumerate");
HYuana9b85752014-02-26 02:32:30 -0600295
Haowei Yuane1079fc2014-03-08 14:41:25 -0600296 // find the first eligible entry
297 for (size_t i = 0; i < m_nBuckets; i++)
HYuana9b85752014-02-26 02:32:30 -0600298 {
Haowei Yuane1079fc2014-03-08 14:41:25 -0600299 for (name_tree::Node* node = m_buckets[i]; node != 0; node = node->m_next)
300 {
301 if (static_cast<bool>(node->m_entry) && entrySelector(*node->m_entry))
302 {
303 const_iterator it(FULL_ENUMERATE_TYPE, *this, node->m_entry, entrySelector);
304 return it;
305 }
306 }
307 }
308
309 // If none of the entry satisfies the requirements, then return the end() iterator.
310 return end();
311}
312
313NameTree::const_iterator
314NameTree::partialEnumerate(const Name& prefix,
315 const name_tree::EntrySubTreeSelector& entrySubTreeSelector) const
316{
317 // the first step is to process the root node
318 shared_ptr<name_tree::Entry> entry = findExactMatch(prefix);
319 if (!static_cast<bool>(entry))
320 {
321 return end();
322 }
323
324 std::pair<bool, bool>result = entrySubTreeSelector(*entry);
325 const_iterator it(PARTIAL_ENUMERATE_TYPE,
326 *this,
327 entry,
328 name_tree::AnyEntry(),
329 entrySubTreeSelector);
330
331 it.m_shouldVisitChildren = (result.second && entry->hasChildren());
332
333 if (result.first)
334 {
335 // root node is acceptable
336 return it;
337 }
338 else
339 {
340 // let the ++ operator handle it
341 ++it;
342 return it;
HYuana9b85752014-02-26 02:32:30 -0600343 }
344}
345
Haowei Yuane1079fc2014-03-08 14:41:25 -0600346NameTree::const_iterator
347NameTree::findAllMatches(const Name& prefix,
348 const name_tree::EntrySelector& entrySelector) const
HYuana9b85752014-02-26 02:32:30 -0600349{
Alexander Afanasyevbf9edee2014-03-31 23:05:27 -0700350 NFD_LOG_TRACE("NameTree::findAllMatches" << prefix);
HYuana9b85752014-02-26 02:32:30 -0600351
Haowei Yuane1079fc2014-03-08 14:41:25 -0600352 // As we are using Name Prefix Hash Table, and the current LPM() is
353 // implemented as starting from full name, and reduce the number of
354 // components by 1 each time, we could use it here.
355 // For trie-like design, it could be more efficient by walking down the
356 // trie from the root node.
HYuana9b85752014-02-26 02:32:30 -0600357
Haowei Yuane1079fc2014-03-08 14:41:25 -0600358 shared_ptr<name_tree::Entry> entry = findLongestPrefixMatch(prefix, entrySelector);
HYuana9b85752014-02-26 02:32:30 -0600359
Haowei Yuane1079fc2014-03-08 14:41:25 -0600360 if (static_cast<bool>(entry))
361 {
362 const_iterator it(FIND_ALL_MATCHES_TYPE, *this, entry, entrySelector);
363 return it;
364 }
365 // If none of the entry satisfies the requirements, then return the end() iterator.
366 return end();
HYuana9b85752014-02-26 02:32:30 -0600367}
368
369// Hash Table Resize
370void
371NameTree::resize(size_t newNBuckets)
372{
Alexander Afanasyevbf9edee2014-03-31 23:05:27 -0700373 NFD_LOG_TRACE("resize");
HYuana9b85752014-02-26 02:32:30 -0600374
375 name_tree::Node** newBuckets = new name_tree::Node*[newNBuckets];
Junxiao Shi40631842014-03-01 13:52:37 -0700376 size_t count = 0;
HYuana9b85752014-02-26 02:32:30 -0600377
378 // referenced ccnx hashtb.c hashtb_rehash()
379 name_tree::Node** pp = 0;
380 name_tree::Node* p = 0;
381 name_tree::Node* pre = 0;
382 name_tree::Node* q = 0; // record p->m_next
Junxiao Shi40631842014-03-01 13:52:37 -0700383 size_t i;
HYuana9b85752014-02-26 02:32:30 -0600384 uint32_t h;
385 uint32_t b;
386
387 for (i = 0; i < newNBuckets; i++)
388 {
389 newBuckets[i] = 0;
390 }
391
392 for (i = 0; i < m_nBuckets; i++)
393 {
394 for (p = m_buckets[i]; p != 0; p = q)
395 {
396 count++;
397 q = p->m_next;
398 BOOST_ASSERT(static_cast<bool>(p->m_entry));
399 h = p->m_entry->m_hash;
400 b = h % newNBuckets;
Haowei Yuan694bfb62014-04-01 14:44:11 -0500401 pre = 0;
HYuana9b85752014-02-26 02:32:30 -0600402 for (pp = &newBuckets[b]; *pp != 0; pp = &((*pp)->m_next))
403 {
404 pre = *pp;
405 continue;
406 }
407 p->m_prev = pre;
408 p->m_next = *pp; // Actually *pp always == 0 in this case
409 *pp = p;
410 }
411 }
412
413 BOOST_ASSERT(count == m_nItems);
414
415 name_tree::Node** oldBuckets = m_buckets;
416 m_buckets = newBuckets;
417 delete oldBuckets;
418
419 m_nBuckets = newNBuckets;
420 m_resizeThreshold = (int)(m_loadFactor * (double)m_nBuckets);
421}
422
423// For debugging
424void
Haowei Yuane1079fc2014-03-08 14:41:25 -0600425NameTree::dump(std::ostream& output) const
HYuana9b85752014-02-26 02:32:30 -0600426{
Alexander Afanasyevbf9edee2014-03-31 23:05:27 -0700427 NFD_LOG_TRACE("dump()");
HYuana9b85752014-02-26 02:32:30 -0600428
429 name_tree::Node* node = 0;
430 shared_ptr<name_tree::Entry> entry;
431
432 using std::endl;
433
434 for (size_t i = 0; i < m_nBuckets; i++)
435 {
436 for (node = m_buckets[i]; node != 0; node = node->m_next)
437 {
438 entry = node->m_entry;
439
440 // if the Entry exist, dump its information
441 if (static_cast<bool>(entry))
442 {
443 output << "Bucket" << i << "\t" << entry->m_prefix.toUri() << endl;
444 output << "\t\tHash " << entry->m_hash << endl;
445
446 if (static_cast<bool>(entry->m_parent))
447 {
448 output << "\t\tparent->" << entry->m_parent->m_prefix.toUri();
449 }
450 else
451 {
452 output << "\t\tROOT";
453 }
454 output << endl;
455
456 if (entry->m_children.size() != 0)
457 {
458 output << "\t\tchildren = " << entry->m_children.size() << endl;
459
460 for (size_t j = 0; j < entry->m_children.size(); j++)
461 {
462 output << "\t\t\tChild " << j << " " <<
463 entry->m_children[j]->getPrefix() << endl;
464 }
465 }
466
467 } // if (static_cast<bool>(entry))
468
469 } // for node
470 } // for int i
471
472 output << "Bucket count = " << m_nBuckets << endl;
473 output << "Stored item = " << m_nItems << endl;
474 output << "--------------------------\n";
475}
476
Haowei Yuane1079fc2014-03-08 14:41:25 -0600477NameTree::const_iterator::const_iterator(NameTree::IteratorType type,
478 const NameTree& nameTree,
479 shared_ptr<name_tree::Entry> entry,
480 const name_tree::EntrySelector& entrySelector,
481 const name_tree::EntrySubTreeSelector& entrySubTreeSelector)
482 : m_nameTree(nameTree)
483 , m_entry(entry)
484 , m_subTreeRoot(entry)
485 , m_entrySelector(make_shared<name_tree::EntrySelector>(entrySelector))
486 , m_entrySubTreeSelector(make_shared<name_tree::EntrySubTreeSelector>(entrySubTreeSelector))
487 , m_type(type)
488 , m_shouldVisitChildren(true)
489{
490}
491
492// operator++()
493NameTree::const_iterator
494NameTree::const_iterator::operator++()
495{
Alexander Afanasyevbf9edee2014-03-31 23:05:27 -0700496 NFD_LOG_TRACE("const_iterator::operator++()");
Haowei Yuane1079fc2014-03-08 14:41:25 -0600497
498 BOOST_ASSERT(m_entry != m_nameTree.m_end);
499
500 if (m_type == FULL_ENUMERATE_TYPE) // fullEnumerate
501 {
502 bool isFound = false;
503 // process the entries in the same bucket first
504 while (m_entry->m_node->m_next != 0)
505 {
506 m_entry = m_entry->m_node->m_next->m_entry;
507 if ((*m_entrySelector)(*m_entry))
508 {
509 isFound = true;
510 return *this;
511 }
512 }
513
514 // process other buckets
Junxiao Shiefceadc2014-03-09 18:52:57 -0700515
516 for (int newLocation = m_entry->m_hash % m_nameTree.m_nBuckets + 1;
517 newLocation < static_cast<int>(m_nameTree.m_nBuckets);
518 ++newLocation)
Haowei Yuane1079fc2014-03-08 14:41:25 -0600519 {
520 // process each bucket
521 name_tree::Node* node = m_nameTree.m_buckets[newLocation];
522 while (node != 0)
523 {
524 m_entry = node->m_entry;
525 if ((*m_entrySelector)(*m_entry))
526 {
527 isFound = true;
528 return *this;
529 }
530 node = node->m_next;
531 }
532 }
533 BOOST_ASSERT(isFound == false);
534 // Reach to the end()
535 m_entry = m_nameTree.m_end;
536 return *this;
537 }
538
539 if (m_type == PARTIAL_ENUMERATE_TYPE) // partialEnumerate
540 {
541 // We use pre-order traversal.
542 // if at the root, it could have already been accepted, or this
543 // iterator was just declared, and root doesn't satisfy the
544 // requirement
545 // The if() section handles this special case
546 // Essentially, we need to check root's fist child, and the rest will
547 // be the same as normal process
548 if (m_entry == m_subTreeRoot)
549 {
550 if (m_shouldVisitChildren)
551 {
552 m_entry = m_entry->getChildren()[0];
553 std::pair<bool, bool> result = ((*m_entrySubTreeSelector)(*m_entry));
554 m_shouldVisitChildren = (result.second && m_entry->hasChildren());
555 if(result.first)
556 {
557 return *this;
558 }
559 else
560 {
561 // the first child did not meet the requirement
562 // the rest of the process can just fall through the while loop
563 // as normal
564 }
565 }
566 else
567 {
568 // no children, should return end();
569 // just fall through
570 }
571 }
572
573 // The first thing to do is to visit its child, or go to find its possible
574 // siblings
575 while (m_entry != m_subTreeRoot)
576 {
577 if (m_shouldVisitChildren)
578 {
579 // If this subtree should be visited
580 m_entry = m_entry->getChildren()[0];
581 std::pair<bool, bool> result = ((*m_entrySubTreeSelector)(*m_entry));
582 m_shouldVisitChildren = (result.second && m_entry->hasChildren());
583 if (result.first) // if this node is acceptable
584 {
585 return *this;
586 }
587 else
588 {
589 // do nothing, as this node is essentially ignored
590 // send this node to the while loop.
591 }
592 }
593 else
594 {
595 // Should try to find its sibling
596 shared_ptr<name_tree::Entry> parent = m_entry->getParent();
597
598 std::vector<shared_ptr<name_tree::Entry> >& parentChildrenList = parent->getChildren();
599 bool isFound = false;
600 size_t i = 0;
601 for (i = 0; i < parentChildrenList.size(); i++)
602 {
603 if (parentChildrenList[i] == m_entry)
604 {
605 isFound = true;
606 break;
607 }
608 }
609
610 BOOST_ASSERT(isFound == true);
611 if (i < parentChildrenList.size() - 1) // m_entry not the last child
612 {
613 m_entry = parentChildrenList[i + 1];
614 std::pair<bool, bool> result = ((*m_entrySubTreeSelector)(*m_entry));
615 m_shouldVisitChildren = (result.second && m_entry->hasChildren());
616 if (result.first) // if this node is acceptable
617 {
618 return *this;
619 }
620 else
621 {
622 // do nothing, as this node is essentially ignored
623 // send this node to the while loop.
624 }
625 }
626 else
627 {
628 // m_entry is the last child, no more sibling, should try to find parent's sibling
629 m_shouldVisitChildren = false;
630 m_entry = parent;
631 }
632 }
633 }
634
635 m_entry = m_nameTree.m_end;
636 return *this;
637 }
638
639 if (m_type == FIND_ALL_MATCHES_TYPE) // findAllMatches
640 {
641 // Assumption: at the beginning, m_entry was initialized with the first
642 // eligible Name Tree entry (i.e., has a PIT entry that can be satisfied
643 // by the Data packet)
644
645 while (static_cast<bool>(m_entry->getParent()))
646 {
647 m_entry = m_entry->getParent();
648 if ((*m_entrySelector)(*m_entry))
649 return *this;
650 }
651
652 // Reach to the end (Root)
653 m_entry = m_nameTree.m_end;
654 return *this;
655 }
Junxiao Shiefceadc2014-03-09 18:52:57 -0700656
657 BOOST_ASSERT(false); // unknown type
658 return *this;
Haowei Yuane1079fc2014-03-08 14:41:25 -0600659}
660
HYuana9b85752014-02-26 02:32:30 -0600661} // namespace nfd