in-memory-storage.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
22 #include "in-memory-storage.hpp"
24 
25 #include "crypto.hpp"
26 
27 #include "../security/signature-sha256-with-rsa.hpp"
28 
29 namespace ndn {
30 namespace util {
31 
32 const time::milliseconds InMemoryStorage::INFINITE_WINDOW(-1);
33 const time::milliseconds InMemoryStorage::ZERO_WINDOW(0);
34 
36  Cache::index<byFullName>::type::iterator it)
37  : m_ptr(ptr)
38  , m_cache(cache)
39  , m_it(it)
40 {
41 }
42 
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 
59 {
61  this->operator++();
62  return i;
63 }
64 
65 const Data&
67 {
68  return *m_ptr;
69 }
70 
71 const Data*
73 {
74  return m_ptr;
75 }
76 
77 bool
79 {
80  return m_it == rhs.m_it;
81 }
82 
83 bool
85 {
86  return m_it != rhs.m_it;
87 }
88 
90  : m_limit(limit)
91  , m_nPackets(0)
92 {
93  init();
94 }
95 
96 InMemoryStorage::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 
104 void
105 InMemoryStorage::init()
106 {
107  // TODO consider a more suitable initial value
108  m_capacity = 10;
109 
110  if (m_limit != std::numeric_limits<size_t>::max() && m_capacity > m_limit) {
111  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 
120 {
121  // evict all items from cache
122  Cache::iterator it = m_cache.begin();
123  while (it != m_cache.end()) {
124  it = freeEntry(it);
125  }
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 
135 void
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) {
145  BOOST_THROW_EXCEPTION(Error());
146  }
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 
165 void
166 InMemoryStorage::insert(const Data& data, const time::milliseconds& mustBeFreshProcessingWindow)
167 {
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);
193  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  }
199  m_cache.insert(entry);
200 
201  //let derived class do something with the entry
202  afterInsert(entry);
203 }
204 
205 shared_ptr<const Data>
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 
224 shared_ptr<const Data>
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 
244  //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 
259 InMemoryStorage::Cache::index<InMemoryStorage::byFullName>::type::iterator
260 InMemoryStorage::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 
270 InMemoryStorageEntry*
271 InMemoryStorage::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 
284  // 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 
292  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;
310  // filter out "stale" data
311  if (interest.getMustBeFresh())
312  rightmostCandidate = findNextFresh(rightmostCandidate);
313 
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 
365 InMemoryStorage::Cache::iterator
366 InMemoryStorage::freeEntry(Cache::iterator it)
367 {
368  //push the *empty* entry into mem pool
369  (*it)->release();
370  m_freeEntries.push(*it);
371  m_nPackets--;
372  return m_cache.erase(it);
373 }
374 
375 void
376 InMemoryStorage::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 
381  while (it != m_cache.get<byFullName>().end() && prefix.isPrefixOf((*it)->getName())) {
382  //let derived class do something with the entry
383  beforeErase(*it);
384  it = freeEntry(it);
385  }
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 
402 void
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 
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 
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 
431 void
433 {
434 }
435 
436 void
438 {
439 }
440 
441 void
443 {
444 }
445 
446 void
447 InMemoryStorage::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
virtual void afterAccess(InMemoryStorageEntry *entry)
Update the entry when the entry is returned by the find() function according to derived class impleme...
const Name & getName() const
Definition: interest.hpp:226
Copyright (c) 2013-2016 Regents of the University of California.
Definition: common.hpp:74
bool isFull() const
returns true if the in-memory storage uses up the current capacity, false otherwise ...
void markStale()
Disable the data from satisfying interest with MustBeFresh.
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...
shared_ptr< const Data > find(const Interest &interest)
Finds the best match Data for an Interest.
Represents a self-defined const_iterator for the in-memory storage.
const Data & getData() const
Returns the Data packet stored in the in-memory storage entry.
represents an Interest packet
Definition: interest.hpp:42
bool operator!=(const const_iterator &rhs)
InMemoryStorage::const_iterator end() const
Returns end iterator of the in-memory storage ordering by name with digest.
InMemoryStorage::const_iterator begin() const
Returns begin iterator of the in-memory storage ordering by name with digest.
virtual bool evictItem()=0
Removes one Data packet from in-memory storage based on derived class implemented replacement policy...
virtual void beforeErase(InMemoryStorageEntry *entry)
Update the entry or other data structures before a entry is successfully erased according to derived ...
void eraseImpl(const Name &name)
deletes in-memory storage entries by the Name with implicit digest.
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
size_t getCapacity() const
returns current capacity of in-memory storage (in packets)
Represents an in-memory storage entry.
Name abstraction to represent an absolute name.
Definition: name.hpp:46
const_iterator(const Data *ptr, const Cache *cache, Cache::index< byFullName >::type::iterator it)
bool operator==(const const_iterator &rhs)
virtual void afterInsert(InMemoryStorageEntry *entry)
Update the entry or other data structures after a entry is successfully inserted according to derived...
void insert(const Data &data, const time::milliseconds &mustBeFreshProcessingWindow=INFINITE_WINDOW)
Inserts a Data packet.
bool isPrefixOf(const Name &name) const
Check if the N components of this name are the same as the first N components of the given name...
Definition: name.cpp:308
static const time::milliseconds INFINITE_WINDOW
void setCapacity(size_t nMaxPackets)
sets current capacity of in-memory storage (in packets)
const Name & getFullName() const
Get full name of Data packet, including the implicit digest.
Definition: data.cpp:179
Represents an error might be thrown during reduce the current capacity of the in-memory storage throu...
represents a Data packet
Definition: data.hpp:37
void erase(const Name &prefix, const bool isPrefix=true)
Deletes in-memory storage entry by prefix by default.
void printCache(std::ostream &os) const
Prints contents of the in-memory storage.