blob: c4380cef76a827af933281537d705d365ffbb2a3 [file] [log] [blame]
HYuana9b85752014-02-26 02:32:30 -06001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Alexander Afanasyev9bcbc7c2014-04-06 19:37:37 -07003 * Copyright (c) 2014 Regents of the University of California,
4 * Arizona Board of Regents,
5 * Colorado State University,
6 * University Pierre & Marie Curie, Sorbonne University,
7 * Washington University in St. Louis,
8 * Beijing Institute of Technology
9 *
10 * This file is part of NFD (Named Data Networking Forwarding Daemon).
11 * See AUTHORS.md for complete list of NFD authors and contributors.
12 *
13 * NFD is free software: you can redistribute it and/or modify it under the terms
14 * of the GNU General Public License as published by the Free Software Foundation,
15 * either version 3 of the License, or (at your option) any later version.
16 *
17 * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
18 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
19 * PURPOSE. See the GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License along with
22 * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
23 **/
HYuana9b85752014-02-26 02:32:30 -060024
25// Name Tree (Name Prefix Hash Table)
26
27#include "name-tree.hpp"
28#include "core/logger.hpp"
29
30namespace nfd {
31
32NFD_LOG_INIT("NameTree");
33
34namespace name_tree {
35
36// Interface of different hash functions
37uint32_t
38hashName(const Name& prefix)
39{
40 // fixed value. Used for debugging only.
41 uint32_t ret = 0;
42
43 // Boost hash
44 // requires the /boost/functional/hash.hpp header file
45 // TODO: improve hash efficiency with Name type
46 boost::hash<std::string> stringHash;
47 ret = stringHash(prefix.toUri());
48
49 return ret;
50}
51
52} // namespace name_tree
53
54NameTree::NameTree(size_t nBuckets)
55 : m_nItems(0)
56 , m_nBuckets(nBuckets)
57 , m_loadFactor(0.5)
58 , m_resizeFactor(2)
Haowei Yuane1079fc2014-03-08 14:41:25 -060059 , m_endIterator(FULL_ENUMERATE_TYPE, *this, m_end)
HYuana9b85752014-02-26 02:32:30 -060060{
61 m_resizeThreshold = static_cast<size_t>(m_loadFactor *
62 static_cast<double>(m_nBuckets));
63
64 // array of node pointers
65 m_buckets = new name_tree::Node*[m_nBuckets];
66 // Initialize the pointer array
67 for (size_t i = 0; i < m_nBuckets; i++)
68 m_buckets[i] = 0;
69}
70
71NameTree::~NameTree()
72{
73 for (size_t i = 0; i < m_nBuckets; i++)
74 {
75 if (m_buckets[i] != 0)
76 delete m_buckets[i];
77 }
78
79 delete [] m_buckets;
80}
81
82// insert() is a private function, and called by only lookup()
83std::pair<shared_ptr<name_tree::Entry>, bool>
84NameTree::insert(const Name& prefix)
85{
Alexander Afanasyevbf9edee2014-03-31 23:05:27 -070086 NFD_LOG_TRACE("insert " << prefix);
HYuana9b85752014-02-26 02:32:30 -060087
88 uint32_t hashValue = name_tree::hashName(prefix);
89 uint32_t loc = hashValue % m_nBuckets;
90
Alexander Afanasyevbf9edee2014-03-31 23:05:27 -070091 NFD_LOG_TRACE("Name " << prefix << " hash value = " << hashValue << " location = " << loc);
HYuana9b85752014-02-26 02:32:30 -060092
93 // Check if this Name has been stored
94 name_tree::Node* node = m_buckets[loc];
95 name_tree::Node* nodePrev = node; // initialize nodePrev to node
96
97 for (node = m_buckets[loc]; node != 0; node = node->m_next)
98 {
99 if (static_cast<bool>(node->m_entry))
100 {
101 if (prefix == node->m_entry->m_prefix)
102 {
103 return std::make_pair(node->m_entry, false); // false: old entry
104 }
105 }
106 nodePrev = node;
107 }
108
Alexander Afanasyevbf9edee2014-03-31 23:05:27 -0700109 NFD_LOG_TRACE("Did not find " << prefix << ", need to insert it to the table");
HYuana9b85752014-02-26 02:32:30 -0600110
111 // If no bucket is empty occupied, we need to create a new node, and it is
112 // linked from nodePrev
113 node = new name_tree::Node();
114 node->m_prev = nodePrev;
115
116 if (nodePrev == 0)
117 {
118 m_buckets[loc] = node;
119 }
120 else
121 {
122 nodePrev->m_next = node;
123 }
124
125 // Create a new Entry
126 shared_ptr<name_tree::Entry> entry(make_shared<name_tree::Entry>(prefix));
127 entry->setHash(hashValue);
128 node->m_entry = entry; // link the Entry to its Node
129 entry->m_node = node; // link the node to Entry. Used in eraseEntryIfEmpty.
130
131 return std::make_pair(entry, true); // true: new entry
132}
133
134// Name Prefix Lookup. Create Name Tree Entry if not found
135shared_ptr<name_tree::Entry>
136NameTree::lookup(const Name& prefix)
137{
Alexander Afanasyevbf9edee2014-03-31 23:05:27 -0700138 NFD_LOG_TRACE("lookup " << prefix);
HYuana9b85752014-02-26 02:32:30 -0600139
140 shared_ptr<name_tree::Entry> entry;
141 shared_ptr<name_tree::Entry> parent;
142
143 for (size_t i = 0; i <= prefix.size(); i++)
144 {
145 Name temp = prefix.getPrefix(i);
146
147 // insert() will create the entry if it does not exist.
148 std::pair<shared_ptr<name_tree::Entry>, bool> ret = insert(temp);
149 entry = ret.first;
150
151 if (ret.second == true)
152 {
153 m_nItems++; /* Increase the counter */
154 entry->m_parent = parent;
155
156 if (static_cast<bool>(parent))
157 {
158 parent->m_children.push_back(entry);
159 }
160 }
161
162 if (m_nItems > m_resizeThreshold)
163 {
164 resize(m_resizeFactor * m_nBuckets);
165 }
166
167 parent = entry;
168 }
169 return entry;
170}
171
172// Exact Match
173shared_ptr<name_tree::Entry>
174NameTree::findExactMatch(const Name& prefix) const
175{
Alexander Afanasyevbf9edee2014-03-31 23:05:27 -0700176 NFD_LOG_TRACE("findExactMatch " << prefix);
HYuana9b85752014-02-26 02:32:30 -0600177
178 uint32_t hashValue = name_tree::hashName(prefix);
179 uint32_t loc = hashValue % m_nBuckets;
180
Alexander Afanasyevbf9edee2014-03-31 23:05:27 -0700181 NFD_LOG_TRACE("Name " << prefix << " hash value = " << hashValue <<
HYuana9b85752014-02-26 02:32:30 -0600182 " location = " << loc);
183
184 shared_ptr<name_tree::Entry> entry;
185 name_tree::Node* node = 0;
186
187 for (node = m_buckets[loc]; node != 0; node = node->m_next)
188 {
189 entry = node->m_entry;
190 if (static_cast<bool>(entry))
191 {
192 if (hashValue == entry->getHash() && prefix == entry->getPrefix())
193 {
194 return entry;
195 }
196 } // if entry
197 } // for node
198
199 // if not found, the default value of entry (null pointer) will be returned
200 entry.reset();
201 return entry;
202}
203
204// Longest Prefix Match
205// Return the longest matching Entry address
206// start from the full name, and then remove 1 name comp each time
207shared_ptr<name_tree::Entry>
Haowei Yuane1079fc2014-03-08 14:41:25 -0600208NameTree::findLongestPrefixMatch(const Name& prefix, const name_tree::EntrySelector& entrySelector) const
HYuana9b85752014-02-26 02:32:30 -0600209{
Alexander Afanasyevbf9edee2014-03-31 23:05:27 -0700210 NFD_LOG_TRACE("findLongestPrefixMatch " << prefix);
HYuana9b85752014-02-26 02:32:30 -0600211
212 shared_ptr<name_tree::Entry> entry;
213
214 for (int i = prefix.size(); i >= 0; i--)
215 {
216 entry = findExactMatch(prefix.getPrefix(i));
Junxiao Shi40631842014-03-01 13:52:37 -0700217 if (static_cast<bool>(entry) && entrySelector(*entry))
HYuana9b85752014-02-26 02:32:30 -0600218 return entry;
219 }
220
Junxiao Shi40631842014-03-01 13:52:37 -0700221 return shared_ptr<name_tree::Entry>();
HYuana9b85752014-02-26 02:32:30 -0600222}
223
HangZhangcb4fc832014-03-11 16:57:11 +0800224shared_ptr<name_tree::Entry>
225NameTree::findLongestPrefixMatch(shared_ptr<name_tree::Entry> entry,
226 const name_tree::EntrySelector& entrySelector) const
227{
228 while (static_cast<bool>(entry))
229 {
230 if (entrySelector(*entry))
231 return entry;
232 entry = entry->getParent();
233 }
234 return shared_ptr<name_tree::Entry>();
235}
236
HYuana9b85752014-02-26 02:32:30 -0600237// return {false: this entry is not empty, true: this entry is empty and erased}
238bool
239NameTree::eraseEntryIfEmpty(shared_ptr<name_tree::Entry> entry)
240{
241 BOOST_ASSERT(static_cast<bool>(entry));
242
Alexander Afanasyevbf9edee2014-03-31 23:05:27 -0700243 NFD_LOG_TRACE("eraseEntryIfEmpty " << entry->getPrefix());
HYuana9b85752014-02-26 02:32:30 -0600244
245 // first check if this Entry can be erased
Junxiao Shi40631842014-03-01 13:52:37 -0700246 if (entry->isEmpty())
HYuana9b85752014-02-26 02:32:30 -0600247 {
248 // update child-related info in the parent
249 shared_ptr<name_tree::Entry> parent = entry->getParent();
250
251 if (static_cast<bool>(parent))
252 {
253 std::vector<shared_ptr<name_tree::Entry> >& parentChildrenList =
254 parent->getChildren();
255
256 bool isFound = false;
257 size_t size = parentChildrenList.size();
258 for (size_t i = 0; i < size; i++)
259 {
260 if (parentChildrenList[i] == entry)
261 {
262 parentChildrenList[i] = parentChildrenList[size - 1];
263 parentChildrenList.pop_back();
264 isFound = true;
265 break;
266 }
267 }
268
269 BOOST_ASSERT(isFound == true);
270 }
271
272 // remove this Entry and its Name Tree Node
273 name_tree::Node* node = entry->m_node;
274 name_tree::Node* nodePrev = node->m_prev;
275
276 // configure the previous node
277 if (nodePrev != 0)
278 {
279 // link the previous node to the next node
280 nodePrev->m_next = node->m_next;
281 }
282 else
283 {
284 m_buckets[entry->getHash() % m_nBuckets] = node->m_next;
285 }
286
287 // link the previous node with the next node (skip the erased one)
288 if (node->m_next != 0)
289 {
290 node->m_next->m_prev = nodePrev;
291 node->m_next = 0;
292 }
293
294 BOOST_ASSERT(node->m_next == 0);
295
296 m_nItems--;
297 delete node;
298
299 if (static_cast<bool>(parent))
300 eraseEntryIfEmpty(parent);
301
302 return true;
303
304 } // if this entry is empty
305
306 return false; // if this entry is not empty
307}
308
Haowei Yuane1079fc2014-03-08 14:41:25 -0600309NameTree::const_iterator
310NameTree::fullEnumerate(const name_tree::EntrySelector& entrySelector) const
HYuana9b85752014-02-26 02:32:30 -0600311{
Alexander Afanasyevbf9edee2014-03-31 23:05:27 -0700312 NFD_LOG_TRACE("fullEnumerate");
HYuana9b85752014-02-26 02:32:30 -0600313
Haowei Yuane1079fc2014-03-08 14:41:25 -0600314 // find the first eligible entry
315 for (size_t i = 0; i < m_nBuckets; i++)
HYuana9b85752014-02-26 02:32:30 -0600316 {
Haowei Yuane1079fc2014-03-08 14:41:25 -0600317 for (name_tree::Node* node = m_buckets[i]; node != 0; node = node->m_next)
318 {
319 if (static_cast<bool>(node->m_entry) && entrySelector(*node->m_entry))
320 {
321 const_iterator it(FULL_ENUMERATE_TYPE, *this, node->m_entry, entrySelector);
322 return it;
323 }
324 }
325 }
326
327 // If none of the entry satisfies the requirements, then return the end() iterator.
328 return end();
329}
330
331NameTree::const_iterator
332NameTree::partialEnumerate(const Name& prefix,
333 const name_tree::EntrySubTreeSelector& entrySubTreeSelector) const
334{
335 // the first step is to process the root node
336 shared_ptr<name_tree::Entry> entry = findExactMatch(prefix);
337 if (!static_cast<bool>(entry))
338 {
339 return end();
340 }
341
342 std::pair<bool, bool>result = entrySubTreeSelector(*entry);
343 const_iterator it(PARTIAL_ENUMERATE_TYPE,
344 *this,
345 entry,
346 name_tree::AnyEntry(),
347 entrySubTreeSelector);
348
349 it.m_shouldVisitChildren = (result.second && entry->hasChildren());
350
351 if (result.first)
352 {
353 // root node is acceptable
354 return it;
355 }
356 else
357 {
358 // let the ++ operator handle it
359 ++it;
360 return it;
HYuana9b85752014-02-26 02:32:30 -0600361 }
362}
363
Haowei Yuane1079fc2014-03-08 14:41:25 -0600364NameTree::const_iterator
365NameTree::findAllMatches(const Name& prefix,
366 const name_tree::EntrySelector& entrySelector) const
HYuana9b85752014-02-26 02:32:30 -0600367{
Alexander Afanasyevbf9edee2014-03-31 23:05:27 -0700368 NFD_LOG_TRACE("NameTree::findAllMatches" << prefix);
HYuana9b85752014-02-26 02:32:30 -0600369
Haowei Yuane1079fc2014-03-08 14:41:25 -0600370 // As we are using Name Prefix Hash Table, and the current LPM() is
371 // implemented as starting from full name, and reduce the number of
372 // components by 1 each time, we could use it here.
373 // For trie-like design, it could be more efficient by walking down the
374 // trie from the root node.
HYuana9b85752014-02-26 02:32:30 -0600375
Haowei Yuane1079fc2014-03-08 14:41:25 -0600376 shared_ptr<name_tree::Entry> entry = findLongestPrefixMatch(prefix, entrySelector);
HYuana9b85752014-02-26 02:32:30 -0600377
Haowei Yuane1079fc2014-03-08 14:41:25 -0600378 if (static_cast<bool>(entry))
379 {
380 const_iterator it(FIND_ALL_MATCHES_TYPE, *this, entry, entrySelector);
381 return it;
382 }
383 // If none of the entry satisfies the requirements, then return the end() iterator.
384 return end();
HYuana9b85752014-02-26 02:32:30 -0600385}
386
387// Hash Table Resize
388void
389NameTree::resize(size_t newNBuckets)
390{
Alexander Afanasyevbf9edee2014-03-31 23:05:27 -0700391 NFD_LOG_TRACE("resize");
HYuana9b85752014-02-26 02:32:30 -0600392
393 name_tree::Node** newBuckets = new name_tree::Node*[newNBuckets];
Junxiao Shi40631842014-03-01 13:52:37 -0700394 size_t count = 0;
HYuana9b85752014-02-26 02:32:30 -0600395
396 // referenced ccnx hashtb.c hashtb_rehash()
397 name_tree::Node** pp = 0;
398 name_tree::Node* p = 0;
399 name_tree::Node* pre = 0;
400 name_tree::Node* q = 0; // record p->m_next
Junxiao Shi40631842014-03-01 13:52:37 -0700401 size_t i;
HYuana9b85752014-02-26 02:32:30 -0600402 uint32_t h;
403 uint32_t b;
404
405 for (i = 0; i < newNBuckets; i++)
406 {
407 newBuckets[i] = 0;
408 }
409
410 for (i = 0; i < m_nBuckets; i++)
411 {
412 for (p = m_buckets[i]; p != 0; p = q)
413 {
414 count++;
415 q = p->m_next;
416 BOOST_ASSERT(static_cast<bool>(p->m_entry));
417 h = p->m_entry->m_hash;
418 b = h % newNBuckets;
Haowei Yuan694bfb62014-04-01 14:44:11 -0500419 pre = 0;
HYuana9b85752014-02-26 02:32:30 -0600420 for (pp = &newBuckets[b]; *pp != 0; pp = &((*pp)->m_next))
421 {
422 pre = *pp;
423 continue;
424 }
425 p->m_prev = pre;
426 p->m_next = *pp; // Actually *pp always == 0 in this case
427 *pp = p;
428 }
429 }
430
431 BOOST_ASSERT(count == m_nItems);
432
433 name_tree::Node** oldBuckets = m_buckets;
434 m_buckets = newBuckets;
435 delete oldBuckets;
436
437 m_nBuckets = newNBuckets;
438 m_resizeThreshold = (int)(m_loadFactor * (double)m_nBuckets);
439}
440
441// For debugging
442void
Haowei Yuane1079fc2014-03-08 14:41:25 -0600443NameTree::dump(std::ostream& output) const
HYuana9b85752014-02-26 02:32:30 -0600444{
Alexander Afanasyevbf9edee2014-03-31 23:05:27 -0700445 NFD_LOG_TRACE("dump()");
HYuana9b85752014-02-26 02:32:30 -0600446
447 name_tree::Node* node = 0;
448 shared_ptr<name_tree::Entry> entry;
449
450 using std::endl;
451
452 for (size_t i = 0; i < m_nBuckets; i++)
453 {
454 for (node = m_buckets[i]; node != 0; node = node->m_next)
455 {
456 entry = node->m_entry;
457
458 // if the Entry exist, dump its information
459 if (static_cast<bool>(entry))
460 {
461 output << "Bucket" << i << "\t" << entry->m_prefix.toUri() << endl;
462 output << "\t\tHash " << entry->m_hash << endl;
463
464 if (static_cast<bool>(entry->m_parent))
465 {
466 output << "\t\tparent->" << entry->m_parent->m_prefix.toUri();
467 }
468 else
469 {
470 output << "\t\tROOT";
471 }
472 output << endl;
473
474 if (entry->m_children.size() != 0)
475 {
476 output << "\t\tchildren = " << entry->m_children.size() << endl;
477
478 for (size_t j = 0; j < entry->m_children.size(); j++)
479 {
480 output << "\t\t\tChild " << j << " " <<
481 entry->m_children[j]->getPrefix() << endl;
482 }
483 }
484
485 } // if (static_cast<bool>(entry))
486
487 } // for node
488 } // for int i
489
490 output << "Bucket count = " << m_nBuckets << endl;
491 output << "Stored item = " << m_nItems << endl;
492 output << "--------------------------\n";
493}
494
Haowei Yuane1079fc2014-03-08 14:41:25 -0600495NameTree::const_iterator::const_iterator(NameTree::IteratorType type,
496 const NameTree& nameTree,
497 shared_ptr<name_tree::Entry> entry,
498 const name_tree::EntrySelector& entrySelector,
499 const name_tree::EntrySubTreeSelector& entrySubTreeSelector)
500 : m_nameTree(nameTree)
501 , m_entry(entry)
502 , m_subTreeRoot(entry)
503 , m_entrySelector(make_shared<name_tree::EntrySelector>(entrySelector))
504 , m_entrySubTreeSelector(make_shared<name_tree::EntrySubTreeSelector>(entrySubTreeSelector))
505 , m_type(type)
506 , m_shouldVisitChildren(true)
507{
508}
509
510// operator++()
511NameTree::const_iterator
512NameTree::const_iterator::operator++()
513{
Alexander Afanasyevbf9edee2014-03-31 23:05:27 -0700514 NFD_LOG_TRACE("const_iterator::operator++()");
Haowei Yuane1079fc2014-03-08 14:41:25 -0600515
516 BOOST_ASSERT(m_entry != m_nameTree.m_end);
517
518 if (m_type == FULL_ENUMERATE_TYPE) // fullEnumerate
519 {
520 bool isFound = false;
521 // process the entries in the same bucket first
522 while (m_entry->m_node->m_next != 0)
523 {
524 m_entry = m_entry->m_node->m_next->m_entry;
525 if ((*m_entrySelector)(*m_entry))
526 {
527 isFound = true;
528 return *this;
529 }
530 }
531
532 // process other buckets
Junxiao Shiefceadc2014-03-09 18:52:57 -0700533
534 for (int newLocation = m_entry->m_hash % m_nameTree.m_nBuckets + 1;
535 newLocation < static_cast<int>(m_nameTree.m_nBuckets);
536 ++newLocation)
Haowei Yuane1079fc2014-03-08 14:41:25 -0600537 {
538 // process each bucket
539 name_tree::Node* node = m_nameTree.m_buckets[newLocation];
540 while (node != 0)
541 {
542 m_entry = node->m_entry;
543 if ((*m_entrySelector)(*m_entry))
544 {
545 isFound = true;
546 return *this;
547 }
548 node = node->m_next;
549 }
550 }
551 BOOST_ASSERT(isFound == false);
552 // Reach to the end()
553 m_entry = m_nameTree.m_end;
554 return *this;
555 }
556
557 if (m_type == PARTIAL_ENUMERATE_TYPE) // partialEnumerate
558 {
559 // We use pre-order traversal.
560 // if at the root, it could have already been accepted, or this
561 // iterator was just declared, and root doesn't satisfy the
562 // requirement
563 // The if() section handles this special case
564 // Essentially, we need to check root's fist child, and the rest will
565 // be the same as normal process
566 if (m_entry == m_subTreeRoot)
567 {
568 if (m_shouldVisitChildren)
569 {
570 m_entry = m_entry->getChildren()[0];
571 std::pair<bool, bool> result = ((*m_entrySubTreeSelector)(*m_entry));
572 m_shouldVisitChildren = (result.second && m_entry->hasChildren());
573 if(result.first)
574 {
575 return *this;
576 }
577 else
578 {
579 // the first child did not meet the requirement
580 // the rest of the process can just fall through the while loop
581 // as normal
582 }
583 }
584 else
585 {
586 // no children, should return end();
587 // just fall through
588 }
589 }
590
591 // The first thing to do is to visit its child, or go to find its possible
592 // siblings
593 while (m_entry != m_subTreeRoot)
594 {
595 if (m_shouldVisitChildren)
596 {
597 // If this subtree should be visited
598 m_entry = m_entry->getChildren()[0];
599 std::pair<bool, bool> result = ((*m_entrySubTreeSelector)(*m_entry));
600 m_shouldVisitChildren = (result.second && m_entry->hasChildren());
601 if (result.first) // if this node is acceptable
602 {
603 return *this;
604 }
605 else
606 {
607 // do nothing, as this node is essentially ignored
608 // send this node to the while loop.
609 }
610 }
611 else
612 {
613 // Should try to find its sibling
614 shared_ptr<name_tree::Entry> parent = m_entry->getParent();
615
616 std::vector<shared_ptr<name_tree::Entry> >& parentChildrenList = parent->getChildren();
617 bool isFound = false;
618 size_t i = 0;
619 for (i = 0; i < parentChildrenList.size(); i++)
620 {
621 if (parentChildrenList[i] == m_entry)
622 {
623 isFound = true;
624 break;
625 }
626 }
627
628 BOOST_ASSERT(isFound == true);
629 if (i < parentChildrenList.size() - 1) // m_entry not the last child
630 {
631 m_entry = parentChildrenList[i + 1];
632 std::pair<bool, bool> result = ((*m_entrySubTreeSelector)(*m_entry));
633 m_shouldVisitChildren = (result.second && m_entry->hasChildren());
634 if (result.first) // if this node is acceptable
635 {
636 return *this;
637 }
638 else
639 {
640 // do nothing, as this node is essentially ignored
641 // send this node to the while loop.
642 }
643 }
644 else
645 {
646 // m_entry is the last child, no more sibling, should try to find parent's sibling
647 m_shouldVisitChildren = false;
648 m_entry = parent;
649 }
650 }
651 }
652
653 m_entry = m_nameTree.m_end;
654 return *this;
655 }
656
657 if (m_type == FIND_ALL_MATCHES_TYPE) // findAllMatches
658 {
659 // Assumption: at the beginning, m_entry was initialized with the first
660 // eligible Name Tree entry (i.e., has a PIT entry that can be satisfied
661 // by the Data packet)
662
663 while (static_cast<bool>(m_entry->getParent()))
664 {
665 m_entry = m_entry->getParent();
666 if ((*m_entrySelector)(*m_entry))
667 return *this;
668 }
669
670 // Reach to the end (Root)
671 m_entry = m_nameTree.m_end;
672 return *this;
673 }
Junxiao Shiefceadc2014-03-09 18:52:57 -0700674
675 BOOST_ASSERT(false); // unknown type
676 return *this;
Haowei Yuane1079fc2014-03-08 14:41:25 -0600677}
678
HYuana9b85752014-02-26 02:32:30 -0600679} // namespace nfd