in-memory-storage.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2013-2018 Regents of the University of California.
4  *
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"
24 
25 namespace ndn {
26 
27 const time::milliseconds InMemoryStorage::INFINITE_WINDOW(-1);
28 const time::milliseconds InMemoryStorage::ZERO_WINDOW(0);
29 
31  Cache::index<byFullName>::type::iterator it)
32  : m_ptr(ptr)
33  , m_cache(cache)
34  , m_it(it)
35 {
36 }
37 
40 {
41  m_it++;
42  if (m_it != m_cache->get<byFullName>().end()) {
43  m_ptr = &((*m_it)->getData());
44  }
45  else {
46  m_ptr = nullptr;
47  }
48 
49  return *this;
50 }
51 
54 {
56  this->operator++();
57  return i;
58 }
59 
62 {
63  return *m_ptr;
64 }
65 
68 {
69  return m_ptr;
70 }
71 
72 bool
74 {
75  return m_it == rhs.m_it;
76 }
77 
78 bool
80 {
81  return m_it != rhs.m_it;
82 }
83 
85  : m_limit(limit)
86  , m_nPackets(0)
87 {
88  init();
89 }
90 
91 InMemoryStorage::InMemoryStorage(boost::asio::io_service& ioService, size_t limit)
92  : m_limit(limit)
93  , m_nPackets(0)
94 {
95  m_scheduler = make_unique<Scheduler>(ioService);
96  init();
97 }
98 
99 void
100 InMemoryStorage::init()
101 {
102  // TODO consider a more suitable initial value
103  m_capacity = 10;
104 
105  if (m_limit != std::numeric_limits<size_t>::max() && m_capacity > m_limit) {
106  m_capacity = m_limit;
107  }
108 
109  for (size_t i = 0; i < m_capacity; i++) {
110  m_freeEntries.push(new InMemoryStorageEntry());
111  }
112 }
113 
115 {
116  // evict all items from cache
117  Cache::iterator it = m_cache.begin();
118  while (it != m_cache.end()) {
119  it = freeEntry(it);
120  }
121 
122  BOOST_ASSERT(m_freeEntries.size() == m_capacity);
123 
124  while (!m_freeEntries.empty()) {
125  delete m_freeEntries.top();
126  m_freeEntries.pop();
127  }
128 }
129 
130 void
132 {
133  size_t oldCapacity = m_capacity;
134  m_capacity = capacity;
135 
136  if (size() > m_capacity) {
137  ssize_t nAllowedFailures = size() - m_capacity;
138  while (size() > m_capacity) {
139  if (!evictItem() && --nAllowedFailures < 0) {
140  BOOST_THROW_EXCEPTION(Error());
141  }
142  }
143  }
144 
145  if (m_capacity >= oldCapacity) {
146  for (size_t i = oldCapacity; i < m_capacity; i++) {
147  m_freeEntries.push(new InMemoryStorageEntry());
148  }
149  }
150  else {
151  for (size_t i = oldCapacity; i > m_capacity; i--) {
152  delete m_freeEntries.top();
153  m_freeEntries.pop();
154  }
155  }
156 
157  BOOST_ASSERT(size() + m_freeEntries.size() == m_capacity);
158 }
159 
160 void
161 InMemoryStorage::insert(const Data& data, const time::milliseconds& mustBeFreshProcessingWindow)
162 {
163  // check if identical Data/Name already exists
164  auto it = m_cache.get<byFullName>().find(data.getFullName());
165  if (it != m_cache.get<byFullName>().end())
166  return;
167 
168  //if full, double the capacity
169  bool doesReachLimit = (getLimit() == getCapacity());
170  if (isFull() && !doesReachLimit) {
171  // note: This is incorrect if 2*capacity overflows, but memory should run out before that
172  size_t newCapacity = std::min(2 * getCapacity(), getLimit());
173  setCapacity(newCapacity);
174  }
175 
176  //if full and reach limitation of the capacity, employ replacement policy
177  if (isFull() && doesReachLimit) {
178  evictItem();
179  }
180 
181  //insert to cache
182  BOOST_ASSERT(m_freeEntries.size() > 0);
183  // take entry for the memory pool
184  InMemoryStorageEntry* entry = m_freeEntries.top();
185  m_freeEntries.pop();
186  m_nPackets++;
187  entry->setData(data);
188  if (m_scheduler != nullptr && mustBeFreshProcessingWindow > ZERO_WINDOW) {
189  auto eventId = make_unique<util::scheduler::ScopedEventId>(*m_scheduler);
190  *eventId = m_scheduler->scheduleEvent(mustBeFreshProcessingWindow, [entry] { entry->markStale(); });
191  entry->setMarkStaleEventId(std::move(eventId));
192  }
193  m_cache.insert(entry);
194 
195  //let derived class do something with the entry
196  afterInsert(entry);
197 }
198 
199 shared_ptr<const Data>
201 {
202  auto it = m_cache.get<byFullName>().lower_bound(name);
203 
204  // if not found, return null
205  if (it == m_cache.get<byFullName>().end()) {
206  return nullptr;
207  }
208 
209  // if the given name is not the prefix of the lower_bound, return null
210  if (!name.isPrefixOf((*it)->getFullName())) {
211  return nullptr;
212  }
213 
214  afterAccess(*it);
215  return ((*it)->getData()).shared_from_this();
216 }
217 
218 shared_ptr<const Data>
220 {
221  // if the interest contains implicit digest, it is possible to directly locate a packet.
222  auto it = m_cache.get<byFullName>().find(interest.getName());
223 
224  // if a packet is located by its full name, it must be the packet to return.
225  if (it != m_cache.get<byFullName>().end()) {
226  return ((*it)->getData()).shared_from_this();
227  }
228 
229  // if the packet is not discovered by last step, either the packet is not in the storage or
230  // the interest doesn't contains implicit digest.
231  it = m_cache.get<byFullName>().lower_bound(interest.getName());
232 
233  if (it == m_cache.get<byFullName>().end()) {
234  return nullptr;
235  }
236 
237  // to locate the element that has a just smaller name than the interest's
238  if (it != m_cache.get<byFullName>().begin()) {
239  it--;
240  }
241 
242  InMemoryStorageEntry* ret = selectChild(interest, it);
243  if (ret == nullptr) {
244  return nullptr;
245  }
246 
247  // let derived class do something with the entry
248  afterAccess(ret);
249  return ret->getData().shared_from_this();
250 }
251 
252 InMemoryStorage::Cache::index<InMemoryStorage::byFullName>::type::iterator
253 InMemoryStorage::findNextFresh(Cache::index<byFullName>::type::iterator it) const
254 {
255  for (; it != m_cache.get<byFullName>().end(); it++) {
256  if ((*it)->isFresh())
257  return it;
258  }
259 
260  return it;
261 }
262 
264 InMemoryStorage::selectChild(const Interest& interest,
265  Cache::index<byFullName>::type::iterator startingPoint) const
266 {
267  BOOST_ASSERT(startingPoint != m_cache.get<byFullName>().end());
268 
269  if (startingPoint != m_cache.get<byFullName>().begin()) {
270  BOOST_ASSERT((*startingPoint)->getFullName() < interest.getName());
271  }
272 
273  bool hasLeftmostSelector = (interest.getChildSelector() <= 0);
274  bool hasRightmostSelector = !hasLeftmostSelector;
275 
276  // filter out "stale" data
277  if (interest.getMustBeFresh()) {
278  startingPoint = findNextFresh(startingPoint);
279  }
280 
281  if (startingPoint == m_cache.get<byFullName>().end()) {
282  return nullptr;
283  }
284 
285  if (hasLeftmostSelector) {
286  if (interest.matchesData((*startingPoint)->getData())) {
287  return *startingPoint;
288  }
289  }
290 
291  // iterate to the right
292  auto rightmost = startingPoint;
293  if (startingPoint != m_cache.get<byFullName>().end()) {
294  auto rightmostCandidate = startingPoint;
295  Name currentChildPrefix("");
296 
297  while (true) {
298  ++rightmostCandidate;
299  // filter out "stale" data
300  if (interest.getMustBeFresh()) {
301  rightmostCandidate = findNextFresh(rightmostCandidate);
302  }
303 
304  bool isInBoundaries = (rightmostCandidate != m_cache.get<byFullName>().end());
305  bool isInPrefix = false;
306  if (isInBoundaries) {
307  isInPrefix = interest.getName().isPrefixOf((*rightmostCandidate)->getFullName());
308  }
309 
310  if (isInPrefix) {
311  if (interest.matchesData((*rightmostCandidate)->getData())) {
312  if (hasLeftmostSelector) {
313  return *rightmostCandidate;
314  }
315 
316  if (hasRightmostSelector) {
317  // get prefix which is one component longer than Interest name
318  const Name& childPrefix = (*rightmostCandidate)->getFullName().getPrefix(interest.getName().size() + 1);
319  if (currentChildPrefix.empty() || (childPrefix != currentChildPrefix)) {
320  currentChildPrefix = childPrefix;
321  rightmost = rightmostCandidate;
322  }
323  }
324  }
325  }
326  else {
327  break;
328  }
329  }
330  }
331 
332  if (rightmost != startingPoint) {
333  return *rightmost;
334  }
335 
336  if (hasRightmostSelector) { // if rightmost was not found, try starting point
337  if (interest.matchesData((*startingPoint)->getData())) {
338  return *startingPoint;
339  }
340  }
341 
342  return nullptr;
343 }
344 
345 InMemoryStorage::Cache::iterator
346 InMemoryStorage::freeEntry(Cache::iterator it)
347 {
348  // push the *empty* entry into mem pool
349  (*it)->release();
350  m_freeEntries.push(*it);
351  m_nPackets--;
352  return m_cache.erase(it);
353 }
354 
355 void
356 InMemoryStorage::erase(const Name& prefix, const bool isPrefix)
357 {
358  if (isPrefix) {
359  auto it = m_cache.get<byFullName>().lower_bound(prefix);
360  while (it != m_cache.get<byFullName>().end() && prefix.isPrefixOf((*it)->getName())) {
361  // let derived class do something with the entry
362  beforeErase(*it);
363  it = freeEntry(it);
364  }
365  }
366  else {
367  auto it = m_cache.get<byFullName>().find(prefix);
368  if (it == m_cache.get<byFullName>().end())
369  return;
370 
371  // let derived class do something with the entry
372  beforeErase(*it);
373  freeEntry(it);
374  }
375 
376  if (m_freeEntries.size() > (2 * size()))
377  setCapacity(getCapacity() / 2);
378 }
379 
380 void
382 {
383  auto it = m_cache.get<byFullName>().find(name);
384  if (it == m_cache.get<byFullName>().end())
385  return;
386 
387  freeEntry(it);
388 }
389 
392 {
393  auto it = m_cache.get<byFullName>().begin();
394  return const_iterator(&((*it)->getData()), &m_cache, it);
395 }
396 
399 {
400  auto it = m_cache.get<byFullName>().end();
401  return const_iterator(nullptr, &m_cache, it);
402 }
403 
404 void
406 {
407 }
408 
409 void
411 {
412 }
413 
414 void
416 {
417 }
418 
419 void
420 InMemoryStorage::printCache(std::ostream& os) const
421 {
422  // start from the upper layer towards bottom
423  for (const auto& elem : m_cache.get<byFullName>())
424  os << elem->getFullName() << std::endl;
425 }
426 
427 } // namespace ndn
const Name & getName() const
Definition: interest.hpp:133
Copyright (c) 2013-2017 Regents of the University of California.
Definition: common.hpp:65
void erase(const Name &prefix, const bool isPrefix=true)
Deletes in-memory storage entry by prefix by default.
void setCapacity(size_t nMaxPackets)
sets current capacity of in-memory storage (in packets)
virtual bool evictItem()=0
Removes one Data packet from in-memory storage based on derived class implemented replacement policy...
bool isFull() const
returns true if the in-memory storage uses up the current capacity, false otherwise ...
Represents an Interest packet.
Definition: interest.hpp:43
bool operator==(const const_iterator &rhs)
boost::multi_index_container< InMemoryStorageEntry *, boost::multi_index::indexed_by< boost::multi_index::ordered_unique< boost::multi_index::tag< byFullName >, boost::multi_index::const_mem_fun< InMemoryStorageEntry, const Name &,&InMemoryStorageEntry::getFullName >, std::less< Name > > > > Cache
bool operator!=(const const_iterator &rhs)
size_t getCapacity() const
returns current capacity of in-memory storage (in packets)
int getChildSelector() const
Definition: interest.hpp:426
virtual void beforeErase(InMemoryStorageEntry *entry)
Update the entry or other data structures before a entry is successfully erased according to derived ...
InMemoryStorage(size_t limit=std::numeric_limits< size_t >::max())
Create a InMemoryStorage with up to limit entries The InMemoryStorage created through this method wil...
Represents an error might be thrown during reduce the current capacity of the in-memory storage throu...
shared_ptr< const Data > find(const Interest &interest)
Finds the best match Data for an Interest.
virtual void afterAccess(InMemoryStorageEntry *entry)
Update the entry when the entry is returned by the find() function according to derived class impleme...
virtual void afterInsert(InMemoryStorageEntry *entry)
Update the entry or other data structures after a entry is successfully inserted according to derived...
Represents a self-defined const_iterator for the in-memory storage.
size_t size() const
Get number of components.
Definition: name.hpp:146
Represents an absolute name.
Definition: name.hpp:42
bool isPrefixOf(const Name &other) const
Check if this name is a prefix of another name.
Definition: name.cpp:252
bool matchesData(const Data &data) const
Check if Interest can be satisfied by data.
Definition: interest.cpp:453
Represents an in-memory storage entry.
InMemoryStorage::const_iterator begin() const
Returns begin iterator of the in-memory storage ordering by name with digest.
const_iterator(const Data *ptr, const Cache *cache, Cache::index< byFullName >::type::iterator it)
const Data & getData() const
Returns the Data packet stored in the in-memory storage entry.
InMemoryStorage::const_iterator end() const
Returns end iterator of the in-memory storage ordering by name with digest.
void printCache(std::ostream &os) const
Prints contents of the in-memory storage.
bool empty() const
Check if name is empty.
Definition: name.hpp:138
void insert(const Data &data, const time::milliseconds &mustBeFreshProcessingWindow=INFINITE_WINDOW)
Inserts a Data packet.
PartialName getPrefix(ssize_t nComponents) const
Extract a prefix of the name.
Definition: name.hpp:202
const Name & getFullName() const
Get full name including implicit digest.
Definition: data.cpp:197
Represents a Data packet.
Definition: data.hpp:35
bool getMustBeFresh() const
Check whether the MustBeFresh element is present.
Definition: interest.hpp:201
void eraseImpl(const Name &name)
deletes in-memory storage entries by the Name with implicit digest.
static const time::milliseconds INFINITE_WINDOW