blob: 69f2b5aff6637b612c69f4618c817a4b74ee7cce [file] [log] [blame]
Jiewen Tan99135962014-09-20 02:18:53 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Yingdi Yu404eafd2016-03-06 14:54:25 -08003 * Copyright (c) 2013-2016 Regents of the University of California.
Jiewen Tan99135962014-09-20 02:18:53 -07004 *
5 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
6 *
7 * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8 * terms of the GNU Lesser General Public License as published by the Free Software
9 * Foundation, either version 3 of the License, or (at your option) any later version.
10 *
11 * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14 *
15 * You should have received copies of the GNU General Public License and GNU Lesser
16 * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17 * <http://www.gnu.org/licenses/>.
18 *
19 * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
20 */
21
22#include "in-memory-storage.hpp"
23#include "in-memory-storage-entry.hpp"
24
25#include "crypto.hpp"
26
27#include "../security/signature-sha256-with-rsa.hpp"
28
29namespace ndn {
30namespace util {
31
Yingdi Yu404eafd2016-03-06 14:54:25 -080032const time::milliseconds InMemoryStorage::INFINITE_WINDOW(-1);
33const time::milliseconds InMemoryStorage::ZERO_WINDOW(0);
34
Jiewen Tan99135962014-09-20 02:18:53 -070035InMemoryStorage::const_iterator::const_iterator(const Data* ptr, const Cache* cache,
36 Cache::index<byFullName>::type::iterator it)
37 : m_ptr(ptr)
38 , m_cache(cache)
39 , m_it(it)
40{
41}
42
43InMemoryStorage::const_iterator&
44InMemoryStorage::const_iterator::operator++()
45{
46 m_it++;
47 if (m_it != m_cache->get<byFullName>().end()) {
48 m_ptr = &((*m_it)->getData());
49 }
50 else {
51 m_ptr = 0;
52 }
53
54 return *this;
55}
56
57InMemoryStorage::const_iterator
58InMemoryStorage::const_iterator::operator++(int)
59{
60 InMemoryStorage::const_iterator i(*this);
61 this->operator++();
62 return i;
63}
64
65const Data&
66InMemoryStorage::const_iterator::operator*()
67{
68 return *m_ptr;
69}
70
71const Data*
72InMemoryStorage::const_iterator::operator->()
73{
74 return m_ptr;
75}
76
77bool
78InMemoryStorage::const_iterator::operator==(const const_iterator& rhs)
79{
80 return m_it == rhs.m_it;
81}
82
83bool
84InMemoryStorage::const_iterator::operator!=(const const_iterator& rhs)
85{
86 return m_it != rhs.m_it;
87}
88
89InMemoryStorage::InMemoryStorage(size_t limit)
90 : m_limit(limit)
91 , m_nPackets(0)
92{
Yingdi Yu404eafd2016-03-06 14:54:25 -080093 init();
94}
95
96InMemoryStorage::InMemoryStorage(boost::asio::io_service& ioService, size_t limit)
97 : m_limit(limit)
98 , m_nPackets(0)
99{
100 m_scheduler = make_unique<Scheduler>(ioService);
101 init();
102}
103
104void
105InMemoryStorage::init()
106{
Jiewen Tan99135962014-09-20 02:18:53 -0700107 // TODO consider a more suitable initial value
108 m_capacity = 10;
109
Yingdi Yu404eafd2016-03-06 14:54:25 -0800110 if (m_limit != std::numeric_limits<size_t>::max() && m_capacity > m_limit) {
Jiewen Tan99135962014-09-20 02:18:53 -0700111 m_capacity = m_limit;
112 }
113
114 for (size_t i = 0; i < m_capacity; i++) {
115 m_freeEntries.push(new InMemoryStorageEntry());
116 }
117}
118
119InMemoryStorage::~InMemoryStorage()
120{
121 // evict all items from cache
122 Cache::iterator it = m_cache.begin();
123 while (it != m_cache.end()) {
Alexander Afanasyev8e131fd2014-12-15 21:31:50 -0800124 it = freeEntry(it);
Jiewen Tan99135962014-09-20 02:18:53 -0700125 }
126
127 BOOST_ASSERT(m_freeEntries.size() == m_capacity);
128
129 while (!m_freeEntries.empty()) {
130 delete m_freeEntries.top();
131 m_freeEntries.pop();
132 }
133}
134
135void
136InMemoryStorage::setCapacity(size_t capacity)
137{
138 size_t oldCapacity = m_capacity;
139 m_capacity = capacity;
140
141 if (size() > m_capacity) {
142 ssize_t nAllowedFailures = size() - m_capacity;
143 while (size() > m_capacity) {
144 if (!evictItem() && --nAllowedFailures < 0) {
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700145 BOOST_THROW_EXCEPTION(Error());
Jiewen Tan99135962014-09-20 02:18:53 -0700146 }
147 }
148 }
149
150 if (m_capacity >= oldCapacity) {
151 for (size_t i = oldCapacity; i < m_capacity; i++) {
152 m_freeEntries.push(new InMemoryStorageEntry());
153 }
154 }
155 else {
156 for (size_t i = oldCapacity; i > m_capacity; i--) {
157 delete m_freeEntries.top();
158 m_freeEntries.pop();
159 }
160 }
161
162 BOOST_ASSERT(size() + m_freeEntries.size() == m_capacity);
163}
164
165void
Yingdi Yu404eafd2016-03-06 14:54:25 -0800166InMemoryStorage::insert(const Data& data, const time::milliseconds& mustBeFreshProcessingWindow)
Jiewen Tan99135962014-09-20 02:18:53 -0700167{
168 //check if identical Data/Name already exists
169 Cache::index<byFullName>::type::iterator it = m_cache.get<byFullName>().find(data.getFullName());
170 if (it != m_cache.get<byFullName>().end())
171 return;
172
173 //if full, double the capacity
174 bool doesReachLimit = (getLimit() == getCapacity());
175 if (isFull() && !doesReachLimit) {
176 // note: This is incorrect if 2*capacity overflows, but memory should run out before that
177 size_t newCapacity = std::min(2 * getCapacity(), getLimit());
178 setCapacity(newCapacity);
179 }
180
181 //if full and reach limitation of the capacity, employ replacement policy
182 if (isFull() && doesReachLimit) {
183 evictItem();
184 }
185
186 //insert to cache
187 BOOST_ASSERT(m_freeEntries.size() > 0);
188 // take entry for the memory pool
189 InMemoryStorageEntry* entry = m_freeEntries.top();
190 m_freeEntries.pop();
191 m_nPackets++;
192 entry->setData(data);
Yingdi Yu404eafd2016-03-06 14:54:25 -0800193 if (m_scheduler != nullptr && mustBeFreshProcessingWindow > ZERO_WINDOW) {
194 auto eventId = make_unique<scheduler::ScopedEventId>(*m_scheduler);
195 *eventId = m_scheduler->scheduleEvent(mustBeFreshProcessingWindow,
196 bind(&InMemoryStorageEntry::markStale, entry));
197 entry->setMarkStaleEventId(std::move(eventId));
198 }
Jiewen Tan99135962014-09-20 02:18:53 -0700199 m_cache.insert(entry);
200
201 //let derived class do something with the entry
202 afterInsert(entry);
203}
204
205shared_ptr<const Data>
206InMemoryStorage::find(const Name& name)
207{
208 Cache::index<byFullName>::type::iterator it = m_cache.get<byFullName>().lower_bound(name);
209
210 //if not found, return null
211 if (it == m_cache.get<byFullName>().end()) {
212 return shared_ptr<const Data>();
213 }
214
215 //if the given name is not the prefix of the lower_bound, return null
216 if (!name.isPrefixOf((*it)->getFullName())) {
217 return shared_ptr<const Data>();
218 }
219
220 afterAccess(*it);
221 return ((*it)->getData()).shared_from_this();
222}
223
224shared_ptr<const Data>
225InMemoryStorage::find(const Interest& interest)
226{
227 //if the interest contains implicit digest, it is possible to directly locate a packet.
228 Cache::index<byFullName>::type::iterator it = m_cache.get<byFullName>()
229 .find(interest.getName());
230
231 //if a packet is located by its full name, it must be the packet to return.
232 if (it != m_cache.get<byFullName>().end()) {
233 return ((*it)->getData()).shared_from_this();
234 }
235
236 //if the packet is not discovered by last step, either the packet is not in the storage or
237 //the interest doesn't contains implicit digest.
238 it = m_cache.get<byFullName>().lower_bound(interest.getName());
239
240 if (it == m_cache.get<byFullName>().end()) {
241 return shared_ptr<const Data>();
242 }
243
Jiewen Tan99135962014-09-20 02:18:53 -0700244 //to locate the element that has a just smaller name than the interest's
245 if (it != m_cache.get<byFullName>().begin())
246 it--;
247
248 InMemoryStorageEntry* ret = selectChild(interest, it);
249 if (ret != 0) {
250 //let derived class do something with the entry
251 afterAccess(ret);
252 return ret->getData().shared_from_this();
253 }
254 else {
255 return shared_ptr<const Data>();
256 }
257}
258
Yingdi Yu404eafd2016-03-06 14:54:25 -0800259InMemoryStorage::Cache::index<InMemoryStorage::byFullName>::type::iterator
260InMemoryStorage::findNextFresh(Cache::index<byFullName>::type::iterator it) const
261{
262 for (; it != m_cache.get<byFullName>().end(); it++) {
263 if ((*it)->isFresh())
264 return it;
265 }
266
267 return it;
268}
269
Jiewen Tan99135962014-09-20 02:18:53 -0700270InMemoryStorageEntry*
271InMemoryStorage::selectChild(const Interest& interest,
272 Cache::index<byFullName>::type::iterator startingPoint) const
273{
274 BOOST_ASSERT(startingPoint != m_cache.get<byFullName>().end());
275
276 if (startingPoint != m_cache.get<byFullName>().begin())
277 {
278 BOOST_ASSERT((*startingPoint)->getFullName() < interest.getName());
279 }
280
281 bool hasLeftmostSelector = (interest.getChildSelector() <= 0);
282 bool hasRightmostSelector = !hasLeftmostSelector;
283
Yingdi Yu404eafd2016-03-06 14:54:25 -0800284 // filter out "stale" data
285 if (interest.getMustBeFresh())
286 startingPoint = findNextFresh(startingPoint);
287
288 if (startingPoint == m_cache.get<byFullName>().end()) {
289 return nullptr;
290 }
291
Jiewen Tan99135962014-09-20 02:18:53 -0700292 if (hasLeftmostSelector)
293 {
294 if (interest.matchesData((*startingPoint)->getData()))
295 {
296 return *startingPoint;
297 }
298 }
299
300 //iterate to the right
301 Cache::index<byFullName>::type::iterator rightmost = startingPoint;
302 if (startingPoint != m_cache.get<byFullName>().end())
303 {
304 Cache::index<byFullName>::type::iterator rightmostCandidate = startingPoint;
305 Name currentChildPrefix("");
306
307 while (true)
308 {
309 ++rightmostCandidate;
Yingdi Yu404eafd2016-03-06 14:54:25 -0800310 // filter out "stale" data
311 if (interest.getMustBeFresh())
312 rightmostCandidate = findNextFresh(rightmostCandidate);
Jiewen Tan99135962014-09-20 02:18:53 -0700313
314 bool isInBoundaries = (rightmostCandidate != m_cache.get<byFullName>().end());
315 bool isInPrefix = false;
316 if (isInBoundaries)
317 {
318 isInPrefix = interest.getName().isPrefixOf((*rightmostCandidate)->getFullName());
319 }
320
321 if (isInPrefix)
322 {
323 if (interest.matchesData((*rightmostCandidate)->getData()))
324 {
325 if (hasLeftmostSelector)
326 {
327 return *rightmostCandidate;
328 }
329
330 if (hasRightmostSelector)
331 {
332 // get prefix which is one component longer than Interest name
333 const Name& childPrefix = (*rightmostCandidate)->getFullName()
334 .getPrefix(interest.getName().size() + 1);
335
336 if (currentChildPrefix.empty() || (childPrefix != currentChildPrefix))
337 {
338 currentChildPrefix = childPrefix;
339 rightmost = rightmostCandidate;
340 }
341 }
342 }
343 }
344 else
345 break;
346 }
347 }
348
349 if (rightmost != startingPoint)
350 {
351 return *rightmost;
352 }
353
354 if (hasRightmostSelector) // if rightmost was not found, try starting point
355 {
356 if (interest.matchesData((*startingPoint)->getData()))
357 {
358 return *startingPoint;
359 }
360 }
361
362 return 0;
363}
364
Alexander Afanasyev8e131fd2014-12-15 21:31:50 -0800365InMemoryStorage::Cache::iterator
366InMemoryStorage::freeEntry(Cache::iterator it)
367{
Jiewen Tan99135962014-09-20 02:18:53 -0700368 //push the *empty* entry into mem pool
369 (*it)->release();
370 m_freeEntries.push(*it);
371 m_nPackets--;
Alexander Afanasyev8e131fd2014-12-15 21:31:50 -0800372 return m_cache.erase(it);
Jiewen Tan99135962014-09-20 02:18:53 -0700373}
374
375void
376InMemoryStorage::erase(const Name& prefix, const bool isPrefix)
377{
378 if (isPrefix) {
379 Cache::index<byFullName>::type::iterator it = m_cache.get<byFullName>().lower_bound(prefix);
380
Jiewen Tan99135962014-09-20 02:18:53 -0700381 while (it != m_cache.get<byFullName>().end() && prefix.isPrefixOf((*it)->getName())) {
382 //let derived class do something with the entry
383 beforeErase(*it);
Alexander Afanasyev8e131fd2014-12-15 21:31:50 -0800384 it = freeEntry(it);
Jiewen Tan99135962014-09-20 02:18:53 -0700385 }
386 }
387 else {
388 Cache::index<byFullName>::type::iterator it = m_cache.get<byFullName>().find(prefix);
389
390 if (it == m_cache.get<byFullName>().end())
391 return;
392
393 //let derived class do something with the entry
394 beforeErase(*it);
395 freeEntry(it);
396 }
397
398 if (m_freeEntries.size() > (2 * size()))
399 setCapacity(getCapacity() / 2);
400}
401
402void
403InMemoryStorage::eraseImpl(const Name& name)
404{
405 Cache::index<byFullName>::type::iterator it = m_cache.get<byFullName>().find(name);
406
407 if (it == m_cache.get<byFullName>().end())
408 return;
409
410 freeEntry(it);
411}
412
413InMemoryStorage::const_iterator
414InMemoryStorage::begin() const
415{
416 Cache::index<byFullName>::type::iterator it = m_cache.get<byFullName>().begin();
417
418 return const_iterator(&((*it)->getData()), &m_cache, it);
419}
420
421InMemoryStorage::const_iterator
422InMemoryStorage::end() const
423{
424 Cache::index<byFullName>::type::iterator it = m_cache.get<byFullName>().end();
425
426 const Data* ptr = NULL;
427
428 return const_iterator(ptr, &m_cache, it);
429}
430
431void
432InMemoryStorage::afterInsert(InMemoryStorageEntry* entry)
433{
434}
435
436void
437InMemoryStorage::beforeErase(InMemoryStorageEntry* entry)
438{
439}
440
441void
442InMemoryStorage::afterAccess(InMemoryStorageEntry* entry)
443{
444}
445
446void
447InMemoryStorage::printCache(std::ostream& os) const
448{
449 //start from the upper layer towards bottom
450 const Cache::index<byFullName>::type& cacheIndex = m_cache.get<byFullName>();
451 for (Cache::index<byFullName>::type::iterator it = cacheIndex.begin();
452 it != cacheIndex.end(); it++)
453 os << (*it)->getFullName() << std::endl;
454}
455
456} // namespace util
457} // namespace ndn