blob: bc1beac8c30899421030d19f46a7d16681108157 [file] [log] [blame]
Jiewen Tan99135962014-09-20 02:18:53 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Davide Pesavento10b24be2017-07-12 23:23:46 -04002/*
3 * Copyright (c) 2013-2017 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
Jiewen Tan99135962014-09-20 02:18:53 -070025#include "../security/signature-sha256-with-rsa.hpp"
26
27namespace ndn {
28namespace util {
29
Yingdi Yu404eafd2016-03-06 14:54:25 -080030const time::milliseconds InMemoryStorage::INFINITE_WINDOW(-1);
31const time::milliseconds InMemoryStorage::ZERO_WINDOW(0);
32
Jiewen Tan99135962014-09-20 02:18:53 -070033InMemoryStorage::const_iterator::const_iterator(const Data* ptr, const Cache* cache,
34 Cache::index<byFullName>::type::iterator it)
35 : m_ptr(ptr)
36 , m_cache(cache)
37 , m_it(it)
38{
39}
40
41InMemoryStorage::const_iterator&
42InMemoryStorage::const_iterator::operator++()
43{
44 m_it++;
45 if (m_it != m_cache->get<byFullName>().end()) {
46 m_ptr = &((*m_it)->getData());
47 }
48 else {
49 m_ptr = 0;
50 }
51
52 return *this;
53}
54
55InMemoryStorage::const_iterator
56InMemoryStorage::const_iterator::operator++(int)
57{
58 InMemoryStorage::const_iterator i(*this);
59 this->operator++();
60 return i;
61}
62
63const Data&
64InMemoryStorage::const_iterator::operator*()
65{
66 return *m_ptr;
67}
68
69const Data*
70InMemoryStorage::const_iterator::operator->()
71{
72 return m_ptr;
73}
74
75bool
76InMemoryStorage::const_iterator::operator==(const const_iterator& rhs)
77{
78 return m_it == rhs.m_it;
79}
80
81bool
82InMemoryStorage::const_iterator::operator!=(const const_iterator& rhs)
83{
84 return m_it != rhs.m_it;
85}
86
87InMemoryStorage::InMemoryStorage(size_t limit)
88 : m_limit(limit)
89 , m_nPackets(0)
90{
Yingdi Yu404eafd2016-03-06 14:54:25 -080091 init();
92}
93
94InMemoryStorage::InMemoryStorage(boost::asio::io_service& ioService, size_t limit)
95 : m_limit(limit)
96 , m_nPackets(0)
97{
98 m_scheduler = make_unique<Scheduler>(ioService);
99 init();
100}
101
102void
103InMemoryStorage::init()
104{
Jiewen Tan99135962014-09-20 02:18:53 -0700105 // TODO consider a more suitable initial value
106 m_capacity = 10;
107
Yingdi Yu404eafd2016-03-06 14:54:25 -0800108 if (m_limit != std::numeric_limits<size_t>::max() && m_capacity > m_limit) {
Jiewen Tan99135962014-09-20 02:18:53 -0700109 m_capacity = m_limit;
110 }
111
112 for (size_t i = 0; i < m_capacity; i++) {
113 m_freeEntries.push(new InMemoryStorageEntry());
114 }
115}
116
117InMemoryStorage::~InMemoryStorage()
118{
119 // evict all items from cache
120 Cache::iterator it = m_cache.begin();
121 while (it != m_cache.end()) {
Alexander Afanasyev8e131fd2014-12-15 21:31:50 -0800122 it = freeEntry(it);
Jiewen Tan99135962014-09-20 02:18:53 -0700123 }
124
125 BOOST_ASSERT(m_freeEntries.size() == m_capacity);
126
127 while (!m_freeEntries.empty()) {
128 delete m_freeEntries.top();
129 m_freeEntries.pop();
130 }
131}
132
133void
134InMemoryStorage::setCapacity(size_t capacity)
135{
136 size_t oldCapacity = m_capacity;
137 m_capacity = capacity;
138
139 if (size() > m_capacity) {
140 ssize_t nAllowedFailures = size() - m_capacity;
141 while (size() > m_capacity) {
142 if (!evictItem() && --nAllowedFailures < 0) {
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700143 BOOST_THROW_EXCEPTION(Error());
Jiewen Tan99135962014-09-20 02:18:53 -0700144 }
145 }
146 }
147
148 if (m_capacity >= oldCapacity) {
149 for (size_t i = oldCapacity; i < m_capacity; i++) {
150 m_freeEntries.push(new InMemoryStorageEntry());
151 }
152 }
153 else {
154 for (size_t i = oldCapacity; i > m_capacity; i--) {
155 delete m_freeEntries.top();
156 m_freeEntries.pop();
157 }
158 }
159
160 BOOST_ASSERT(size() + m_freeEntries.size() == m_capacity);
161}
162
163void
Yingdi Yu404eafd2016-03-06 14:54:25 -0800164InMemoryStorage::insert(const Data& data, const time::milliseconds& mustBeFreshProcessingWindow)
Jiewen Tan99135962014-09-20 02:18:53 -0700165{
166 //check if identical Data/Name already exists
167 Cache::index<byFullName>::type::iterator it = m_cache.get<byFullName>().find(data.getFullName());
168 if (it != m_cache.get<byFullName>().end())
169 return;
170
171 //if full, double the capacity
172 bool doesReachLimit = (getLimit() == getCapacity());
173 if (isFull() && !doesReachLimit) {
174 // note: This is incorrect if 2*capacity overflows, but memory should run out before that
175 size_t newCapacity = std::min(2 * getCapacity(), getLimit());
176 setCapacity(newCapacity);
177 }
178
179 //if full and reach limitation of the capacity, employ replacement policy
180 if (isFull() && doesReachLimit) {
181 evictItem();
182 }
183
184 //insert to cache
185 BOOST_ASSERT(m_freeEntries.size() > 0);
186 // take entry for the memory pool
187 InMemoryStorageEntry* entry = m_freeEntries.top();
188 m_freeEntries.pop();
189 m_nPackets++;
190 entry->setData(data);
Yingdi Yu404eafd2016-03-06 14:54:25 -0800191 if (m_scheduler != nullptr && mustBeFreshProcessingWindow > ZERO_WINDOW) {
192 auto eventId = make_unique<scheduler::ScopedEventId>(*m_scheduler);
193 *eventId = m_scheduler->scheduleEvent(mustBeFreshProcessingWindow,
194 bind(&InMemoryStorageEntry::markStale, entry));
195 entry->setMarkStaleEventId(std::move(eventId));
196 }
Jiewen Tan99135962014-09-20 02:18:53 -0700197 m_cache.insert(entry);
198
199 //let derived class do something with the entry
200 afterInsert(entry);
201}
202
203shared_ptr<const Data>
204InMemoryStorage::find(const Name& name)
205{
206 Cache::index<byFullName>::type::iterator it = m_cache.get<byFullName>().lower_bound(name);
207
208 //if not found, return null
209 if (it == m_cache.get<byFullName>().end()) {
210 return shared_ptr<const Data>();
211 }
212
213 //if the given name is not the prefix of the lower_bound, return null
214 if (!name.isPrefixOf((*it)->getFullName())) {
215 return shared_ptr<const Data>();
216 }
217
218 afterAccess(*it);
219 return ((*it)->getData()).shared_from_this();
220}
221
222shared_ptr<const Data>
223InMemoryStorage::find(const Interest& interest)
224{
225 //if the interest contains implicit digest, it is possible to directly locate a packet.
226 Cache::index<byFullName>::type::iterator it = m_cache.get<byFullName>()
227 .find(interest.getName());
228
229 //if a packet is located by its full name, it must be the packet to return.
230 if (it != m_cache.get<byFullName>().end()) {
231 return ((*it)->getData()).shared_from_this();
232 }
233
234 //if the packet is not discovered by last step, either the packet is not in the storage or
235 //the interest doesn't contains implicit digest.
236 it = m_cache.get<byFullName>().lower_bound(interest.getName());
237
238 if (it == m_cache.get<byFullName>().end()) {
239 return shared_ptr<const Data>();
240 }
241
Jiewen Tan99135962014-09-20 02:18:53 -0700242 //to locate the element that has a just smaller name than the interest's
243 if (it != m_cache.get<byFullName>().begin())
244 it--;
245
246 InMemoryStorageEntry* ret = selectChild(interest, it);
247 if (ret != 0) {
248 //let derived class do something with the entry
249 afterAccess(ret);
250 return ret->getData().shared_from_this();
251 }
252 else {
253 return shared_ptr<const Data>();
254 }
255}
256
Yingdi Yu404eafd2016-03-06 14:54:25 -0800257InMemoryStorage::Cache::index<InMemoryStorage::byFullName>::type::iterator
258InMemoryStorage::findNextFresh(Cache::index<byFullName>::type::iterator it) const
259{
260 for (; it != m_cache.get<byFullName>().end(); it++) {
261 if ((*it)->isFresh())
262 return it;
263 }
264
265 return it;
266}
267
Jiewen Tan99135962014-09-20 02:18:53 -0700268InMemoryStorageEntry*
269InMemoryStorage::selectChild(const Interest& interest,
270 Cache::index<byFullName>::type::iterator startingPoint) const
271{
272 BOOST_ASSERT(startingPoint != m_cache.get<byFullName>().end());
273
274 if (startingPoint != m_cache.get<byFullName>().begin())
275 {
276 BOOST_ASSERT((*startingPoint)->getFullName() < interest.getName());
277 }
278
279 bool hasLeftmostSelector = (interest.getChildSelector() <= 0);
280 bool hasRightmostSelector = !hasLeftmostSelector;
281
Yingdi Yu404eafd2016-03-06 14:54:25 -0800282 // filter out "stale" data
283 if (interest.getMustBeFresh())
284 startingPoint = findNextFresh(startingPoint);
285
286 if (startingPoint == m_cache.get<byFullName>().end()) {
287 return nullptr;
288 }
289
Jiewen Tan99135962014-09-20 02:18:53 -0700290 if (hasLeftmostSelector)
291 {
292 if (interest.matchesData((*startingPoint)->getData()))
293 {
294 return *startingPoint;
295 }
296 }
297
298 //iterate to the right
299 Cache::index<byFullName>::type::iterator rightmost = startingPoint;
300 if (startingPoint != m_cache.get<byFullName>().end())
301 {
302 Cache::index<byFullName>::type::iterator rightmostCandidate = startingPoint;
303 Name currentChildPrefix("");
304
305 while (true)
306 {
307 ++rightmostCandidate;
Yingdi Yu404eafd2016-03-06 14:54:25 -0800308 // filter out "stale" data
309 if (interest.getMustBeFresh())
310 rightmostCandidate = findNextFresh(rightmostCandidate);
Jiewen Tan99135962014-09-20 02:18:53 -0700311
312 bool isInBoundaries = (rightmostCandidate != m_cache.get<byFullName>().end());
313 bool isInPrefix = false;
314 if (isInBoundaries)
315 {
316 isInPrefix = interest.getName().isPrefixOf((*rightmostCandidate)->getFullName());
317 }
318
319 if (isInPrefix)
320 {
321 if (interest.matchesData((*rightmostCandidate)->getData()))
322 {
323 if (hasLeftmostSelector)
324 {
325 return *rightmostCandidate;
326 }
327
328 if (hasRightmostSelector)
329 {
330 // get prefix which is one component longer than Interest name
331 const Name& childPrefix = (*rightmostCandidate)->getFullName()
332 .getPrefix(interest.getName().size() + 1);
333
334 if (currentChildPrefix.empty() || (childPrefix != currentChildPrefix))
335 {
336 currentChildPrefix = childPrefix;
337 rightmost = rightmostCandidate;
338 }
339 }
340 }
341 }
342 else
343 break;
344 }
345 }
346
347 if (rightmost != startingPoint)
348 {
349 return *rightmost;
350 }
351
352 if (hasRightmostSelector) // if rightmost was not found, try starting point
353 {
354 if (interest.matchesData((*startingPoint)->getData()))
355 {
356 return *startingPoint;
357 }
358 }
359
360 return 0;
361}
362
Alexander Afanasyev8e131fd2014-12-15 21:31:50 -0800363InMemoryStorage::Cache::iterator
364InMemoryStorage::freeEntry(Cache::iterator it)
365{
Jiewen Tan99135962014-09-20 02:18:53 -0700366 //push the *empty* entry into mem pool
367 (*it)->release();
368 m_freeEntries.push(*it);
369 m_nPackets--;
Alexander Afanasyev8e131fd2014-12-15 21:31:50 -0800370 return m_cache.erase(it);
Jiewen Tan99135962014-09-20 02:18:53 -0700371}
372
373void
374InMemoryStorage::erase(const Name& prefix, const bool isPrefix)
375{
376 if (isPrefix) {
377 Cache::index<byFullName>::type::iterator it = m_cache.get<byFullName>().lower_bound(prefix);
378
Jiewen Tan99135962014-09-20 02:18:53 -0700379 while (it != m_cache.get<byFullName>().end() && prefix.isPrefixOf((*it)->getName())) {
380 //let derived class do something with the entry
381 beforeErase(*it);
Alexander Afanasyev8e131fd2014-12-15 21:31:50 -0800382 it = freeEntry(it);
Jiewen Tan99135962014-09-20 02:18:53 -0700383 }
384 }
385 else {
386 Cache::index<byFullName>::type::iterator it = m_cache.get<byFullName>().find(prefix);
387
388 if (it == m_cache.get<byFullName>().end())
389 return;
390
391 //let derived class do something with the entry
392 beforeErase(*it);
393 freeEntry(it);
394 }
395
396 if (m_freeEntries.size() > (2 * size()))
397 setCapacity(getCapacity() / 2);
398}
399
400void
401InMemoryStorage::eraseImpl(const Name& name)
402{
403 Cache::index<byFullName>::type::iterator it = m_cache.get<byFullName>().find(name);
404
405 if (it == m_cache.get<byFullName>().end())
406 return;
407
408 freeEntry(it);
409}
410
411InMemoryStorage::const_iterator
412InMemoryStorage::begin() const
413{
414 Cache::index<byFullName>::type::iterator it = m_cache.get<byFullName>().begin();
415
416 return const_iterator(&((*it)->getData()), &m_cache, it);
417}
418
419InMemoryStorage::const_iterator
420InMemoryStorage::end() const
421{
422 Cache::index<byFullName>::type::iterator it = m_cache.get<byFullName>().end();
423
Davide Pesavento10b24be2017-07-12 23:23:46 -0400424 return const_iterator(nullptr, &m_cache, it);
Jiewen Tan99135962014-09-20 02:18:53 -0700425}
426
427void
428InMemoryStorage::afterInsert(InMemoryStorageEntry* entry)
429{
430}
431
432void
433InMemoryStorage::beforeErase(InMemoryStorageEntry* entry)
434{
435}
436
437void
438InMemoryStorage::afterAccess(InMemoryStorageEntry* entry)
439{
440}
441
442void
443InMemoryStorage::printCache(std::ostream& os) const
444{
445 //start from the upper layer towards bottom
446 const Cache::index<byFullName>::type& cacheIndex = m_cache.get<byFullName>();
447 for (Cache::index<byFullName>::type::iterator it = cacheIndex.begin();
448 it != cacheIndex.end(); it++)
449 os << (*it)->getFullName() << std::endl;
450}
451
452} // namespace util
453} // namespace ndn