blob: 20e09e4f7a409c81fe2729e57805b80ceeb7a4d1 [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>
189NameTree::findLongestPrefixMatch(const Name& prefix)
190{
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));
198 if (static_cast<bool>(entry))
199 return entry;
200 }
201
202 return entry;
203}
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
214 if (entry->getChildren().empty() &&
215 !(entry->getFibEntry()) &&
216 entry->getPitEntries().empty())
217 {
218 // update child-related info in the parent
219 shared_ptr<name_tree::Entry> parent = entry->getParent();
220
221 if (static_cast<bool>(parent))
222 {
223 std::vector<shared_ptr<name_tree::Entry> >& parentChildrenList =
224 parent->getChildren();
225
226 bool isFound = false;
227 size_t size = parentChildrenList.size();
228 for (size_t i = 0; i < size; i++)
229 {
230 if (parentChildrenList[i] == entry)
231 {
232 parentChildrenList[i] = parentChildrenList[size - 1];
233 parentChildrenList.pop_back();
234 isFound = true;
235 break;
236 }
237 }
238
239 BOOST_ASSERT(isFound == true);
240 }
241
242 // remove this Entry and its Name Tree Node
243 name_tree::Node* node = entry->m_node;
244 name_tree::Node* nodePrev = node->m_prev;
245
246 // configure the previous node
247 if (nodePrev != 0)
248 {
249 // link the previous node to the next node
250 nodePrev->m_next = node->m_next;
251 }
252 else
253 {
254 m_buckets[entry->getHash() % m_nBuckets] = node->m_next;
255 }
256
257 // link the previous node with the next node (skip the erased one)
258 if (node->m_next != 0)
259 {
260 node->m_next->m_prev = nodePrev;
261 node->m_next = 0;
262 }
263
264 BOOST_ASSERT(node->m_next == 0);
265
266 m_nItems--;
267 delete node;
268
269 if (static_cast<bool>(parent))
270 eraseEntryIfEmpty(parent);
271
272 return true;
273
274 } // if this entry is empty
275
276 return false; // if this entry is not empty
277}
278
279shared_ptr<std::vector<shared_ptr<name_tree::Entry> > >
280NameTree::fullEnumerate()
281{
282 NFD_LOG_DEBUG("fullEnumerate");
283
284 shared_ptr<std::vector<shared_ptr<name_tree::Entry> > > ret =
285 make_shared<std::vector<shared_ptr<name_tree::Entry> > >();
286
287 for (int i = 0; i < m_nBuckets; i++)
288 {
289 for (name_tree::Node* node = m_buckets[i]; node != 0; node = node->m_next)
290 {
291 if (static_cast<bool>(node->m_entry))
292 {
293 ret->push_back(node->m_entry);
294 }
295 }
296 }
297
298 return ret;
299}
300
301// Helper function for partialEnumerate()
302void
303NameTree::partialEnumerateAddChildren(shared_ptr<name_tree::Entry> entry,
304 shared_ptr<std::vector<shared_ptr<name_tree::Entry> > > ret)
305{
306 BOOST_ASSERT(static_cast<bool>(entry));
307
308 ret->push_back(entry);
309 for (size_t i = 0; i < entry->m_children.size(); i++)
310 {
311 shared_ptr<name_tree::Entry> temp = entry->m_children[i];
312 partialEnumerateAddChildren(temp, ret);
313 }
314}
315
316shared_ptr<std::vector<shared_ptr<name_tree::Entry> > >
317NameTree::partialEnumerate(const Name& prefix)
318{
319 NFD_LOG_DEBUG("partialEnumerate" << prefix);
320
321 shared_ptr<std::vector<shared_ptr<name_tree::Entry> > > ret =
322 make_shared<std::vector<shared_ptr<name_tree::Entry> > >();
323
324 // find the hash bucket corresponding to that prefix
325 shared_ptr<name_tree::Entry> entry = findExactMatch(prefix);
326
327 if (!static_cast<bool>(entry))
328 {
329 return ret;
330 }
331 else
332 {
333 // go through its children list via depth-first-search
334 ret->push_back(entry);
335 for (size_t i = 0; i < entry->m_children.size(); i++)
336 {
337 partialEnumerateAddChildren(entry->m_children[i], ret);
338 }
339 }
340
341 return ret;
342}
343
344// Hash Table Resize
345void
346NameTree::resize(size_t newNBuckets)
347{
348 NFD_LOG_DEBUG("resize");
349
350 name_tree::Node** newBuckets = new name_tree::Node*[newNBuckets];
351 int count = 0;
352
353 // referenced ccnx hashtb.c hashtb_rehash()
354 name_tree::Node** pp = 0;
355 name_tree::Node* p = 0;
356 name_tree::Node* pre = 0;
357 name_tree::Node* q = 0; // record p->m_next
358 int i;
359 uint32_t h;
360 uint32_t b;
361
362 for (i = 0; i < newNBuckets; i++)
363 {
364 newBuckets[i] = 0;
365 }
366
367 for (i = 0; i < m_nBuckets; i++)
368 {
369 for (p = m_buckets[i]; p != 0; p = q)
370 {
371 count++;
372 q = p->m_next;
373 BOOST_ASSERT(static_cast<bool>(p->m_entry));
374 h = p->m_entry->m_hash;
375 b = h % newNBuckets;
376 for (pp = &newBuckets[b]; *pp != 0; pp = &((*pp)->m_next))
377 {
378 pre = *pp;
379 continue;
380 }
381 p->m_prev = pre;
382 p->m_next = *pp; // Actually *pp always == 0 in this case
383 *pp = p;
384 }
385 }
386
387 BOOST_ASSERT(count == m_nItems);
388
389 name_tree::Node** oldBuckets = m_buckets;
390 m_buckets = newBuckets;
391 delete oldBuckets;
392
393 m_nBuckets = newNBuckets;
394 m_resizeThreshold = (int)(m_loadFactor * (double)m_nBuckets);
395}
396
397// For debugging
398void
399NameTree::dump(std::ostream& output)
400{
401 NFD_LOG_DEBUG("dump()");
402
403 name_tree::Node* node = 0;
404 shared_ptr<name_tree::Entry> entry;
405
406 using std::endl;
407
408 for (size_t i = 0; i < m_nBuckets; i++)
409 {
410 for (node = m_buckets[i]; node != 0; node = node->m_next)
411 {
412 entry = node->m_entry;
413
414 // if the Entry exist, dump its information
415 if (static_cast<bool>(entry))
416 {
417 output << "Bucket" << i << "\t" << entry->m_prefix.toUri() << endl;
418 output << "\t\tHash " << entry->m_hash << endl;
419
420 if (static_cast<bool>(entry->m_parent))
421 {
422 output << "\t\tparent->" << entry->m_parent->m_prefix.toUri();
423 }
424 else
425 {
426 output << "\t\tROOT";
427 }
428 output << endl;
429
430 if (entry->m_children.size() != 0)
431 {
432 output << "\t\tchildren = " << entry->m_children.size() << endl;
433
434 for (size_t j = 0; j < entry->m_children.size(); j++)
435 {
436 output << "\t\t\tChild " << j << " " <<
437 entry->m_children[j]->getPrefix() << endl;
438 }
439 }
440
441 } // if (static_cast<bool>(entry))
442
443 } // for node
444 } // for int i
445
446 output << "Bucket count = " << m_nBuckets << endl;
447 output << "Stored item = " << m_nItems << endl;
448 output << "--------------------------\n";
449}
450
451} // namespace nfd