ndn-cxx: NDN C++ Library 0.9.0-33-g832ea91d
Loading...
Searching...
No Matches
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-2024 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
24
25namespace ndn {
26
27constexpr size_t MIN_CAPACITY = 16;
29
32{
33 m_it++;
34 if (m_it != m_cache->get<byFullName>().end()) {
35 m_ptr = &((*m_it)->getData());
36 }
37 else {
38 m_ptr = nullptr;
39 }
40 return *this;
41}
42
44 : m_limit(limit)
45{
46 init();
47}
48
49InMemoryStorage::InMemoryStorage(boost::asio::io_context& ioCtx, size_t limit)
50 : m_limit(limit)
51{
52 m_scheduler = make_unique<Scheduler>(ioCtx);
53 init();
54}
55
56void
57InMemoryStorage::init()
58{
59 // TODO: consider a more suitable initial value
60 m_capacity = MIN_CAPACITY;
61
62 if (m_limit != std::numeric_limits<size_t>::max() && m_capacity > m_limit) {
63 m_capacity = m_limit;
64 }
65
66 for (size_t i = 0; i < m_capacity; i++) {
67 m_freeEntries.push(new InMemoryStorageEntry());
68 }
69}
70
72{
73 // evict all items from cache
74 auto it = m_cache.begin();
75 while (it != m_cache.end()) {
76 it = freeEntry(it);
77 }
78
79 BOOST_ASSERT(m_freeEntries.size() == m_capacity);
80
81 while (!m_freeEntries.empty()) {
82 delete m_freeEntries.top();
83 m_freeEntries.pop();
84 }
85}
86
87void
89{
90 size_t oldCapacity = m_capacity;
91 m_capacity = std::max(capacity, MIN_CAPACITY);
92
93 if (size() > m_capacity) {
94 ssize_t nAllowedFailures = size() - m_capacity;
95 while (size() > m_capacity) {
96 if (!evictItem() && --nAllowedFailures < 0) {
98 }
99 }
100 }
101
102 if (m_capacity >= oldCapacity) {
103 for (size_t i = oldCapacity; i < m_capacity; i++) {
104 m_freeEntries.push(new InMemoryStorageEntry());
105 }
106 }
107 else {
108 for (size_t i = oldCapacity; i > m_capacity; i--) {
109 delete m_freeEntries.top();
110 m_freeEntries.pop();
111 }
112 }
113
114 BOOST_ASSERT(size() + m_freeEntries.size() == m_capacity);
115}
116
117void
118InMemoryStorage::insert(const Data& data, const time::milliseconds& mustBeFreshProcessingWindow)
119{
120 // check if identical Data/Name already exists
121 auto it = m_cache.get<byFullName>().find(data.getFullName());
122 if (it != m_cache.get<byFullName>().end())
123 return;
124
125 // if full, double the capacity
126 bool doesReachLimit = (getLimit() == getCapacity());
127 if (isFull() && !doesReachLimit) {
128 // note: This is incorrect if 2*capacity overflows, but memory should run out before that
129 size_t newCapacity = std::min(2 * getCapacity(), getLimit());
130 setCapacity(newCapacity);
131 }
132
133 // if full and reach limitation of the capacity, employ replacement policy
134 if (isFull() && doesReachLimit) {
135 evictItem();
136 }
137
138 // insert to cache
139 BOOST_ASSERT(m_freeEntries.size() > 0);
140 // take entry for the memory pool
141 InMemoryStorageEntry* entry = m_freeEntries.top();
142 m_freeEntries.pop();
143 m_nPackets++;
144 entry->setData(data);
145 if (m_scheduler != nullptr && mustBeFreshProcessingWindow >= ZERO_WINDOW) {
146 entry->scheduleMarkStale(*m_scheduler, mustBeFreshProcessingWindow);
147 }
148 m_cache.insert(entry);
149
150 // let derived class do something with the entry
151 afterInsert(entry);
152}
153
154shared_ptr<const Data>
156{
157 auto it = m_cache.get<byFullName>().lower_bound(name);
158
159 // if not found, return null
160 if (it == m_cache.get<byFullName>().end()) {
161 return nullptr;
162 }
163
164 // if the given name is not the prefix of the lower_bound, return null
165 if (!name.isPrefixOf((*it)->getFullName())) {
166 return nullptr;
167 }
168
169 afterAccess(*it);
170 return ((*it)->getData()).shared_from_this();
171}
172
173shared_ptr<const Data>
175{
176 // if the interest contains implicit digest, it is possible to directly locate a packet.
177 auto it = m_cache.get<byFullName>().find(interest.getName());
178
179 // if a packet is located by its full name, it must be the packet to return.
180 if (it != m_cache.get<byFullName>().end()) {
181 return ((*it)->getData()).shared_from_this();
182 }
183
184 // if the packet is not discovered by last step, either the packet is not in the storage or
185 // the interest doesn't contains implicit digest.
186 it = m_cache.get<byFullName>().lower_bound(interest.getName());
187
188 if (it == m_cache.get<byFullName>().end()) {
189 return nullptr;
190 }
191
192 // to locate the element that has a just smaller name than the interest's
193 if (it != m_cache.get<byFullName>().begin()) {
194 it--;
195 }
196
197 InMemoryStorageEntry* ret = selectChild(interest, it);
198 if (ret == nullptr) {
199 return nullptr;
200 }
201
202 // let derived class do something with the entry
203 afterAccess(ret);
204 return ret->getData().shared_from_this();
205}
206
207InMemoryStorage::Cache::index<InMemoryStorage::byFullName>::type::iterator
208InMemoryStorage::findNextFresh(Cache::index<byFullName>::type::iterator it) const
209{
210 for (; it != m_cache.get<byFullName>().end(); it++) {
211 if ((*it)->isFresh())
212 return it;
213 }
214
215 return it;
216}
217
218InMemoryStorageEntry*
219InMemoryStorage::selectChild(const Interest& interest,
220 Cache::index<byFullName>::type::iterator startingPoint) const
221{
222 BOOST_ASSERT(startingPoint != m_cache.get<byFullName>().end());
223
224 if (startingPoint != m_cache.get<byFullName>().begin()) {
225 BOOST_ASSERT((*startingPoint)->getFullName() < interest.getName());
226 }
227
228 // filter out non-fresh data
229 if (interest.getMustBeFresh()) {
230 startingPoint = findNextFresh(startingPoint);
231 }
232
233 if (startingPoint == m_cache.get<byFullName>().end()) {
234 return nullptr;
235 }
236
237 if (interest.matchesData((*startingPoint)->getData())) {
238 return *startingPoint;
239 }
240
241 // iterate to the right
242 auto rightmostCandidate = startingPoint;
243 while (true) {
244 ++rightmostCandidate;
245 // filter out non-fresh data
246 if (interest.getMustBeFresh()) {
247 rightmostCandidate = findNextFresh(rightmostCandidate);
248 }
249
250 bool isInPrefix = false;
251 if (rightmostCandidate != m_cache.get<byFullName>().end()) {
252 isInPrefix = interest.getName().isPrefixOf((*rightmostCandidate)->getFullName());
253 }
254 if (isInPrefix) {
255 if (interest.matchesData((*rightmostCandidate)->getData())) {
256 return *rightmostCandidate;
257 }
258 }
259 else {
260 break;
261 }
262 }
263
264 return nullptr;
265}
266
267InMemoryStorage::Cache::iterator
268InMemoryStorage::freeEntry(Cache::iterator it)
269{
270 // push the *empty* entry into mem pool
271 (*it)->release();
272 m_freeEntries.push(*it);
273 m_nPackets--;
274 return m_cache.erase(it);
275}
276
277void
278InMemoryStorage::erase(const Name& prefix, const bool isPrefix)
279{
280 if (isPrefix) {
281 auto it = m_cache.get<byFullName>().lower_bound(prefix);
282 while (it != m_cache.get<byFullName>().end() && prefix.isPrefixOf((*it)->getName())) {
283 // let derived class do something with the entry
284 beforeErase(*it);
285 it = freeEntry(it);
286 }
287 }
288 else {
289 auto it = m_cache.get<byFullName>().find(prefix);
290 if (it == m_cache.get<byFullName>().end())
291 return;
292
293 // let derived class do something with the entry
294 beforeErase(*it);
295 freeEntry(it);
296 }
297
298 if (m_freeEntries.size() > (2 * size()))
300}
301
302void
304{
305 auto it = m_cache.get<byFullName>().find(name);
306 if (it == m_cache.get<byFullName>().end())
307 return;
308
309 freeEntry(it);
310}
311
314{
315 auto it = m_cache.get<byFullName>().begin();
316 return const_iterator(&((*it)->getData()), &m_cache, it);
317}
318
321{
322 auto it = m_cache.get<byFullName>().end();
323 return const_iterator(nullptr, &m_cache, it);
324}
325
326void
330
331void
335
336void
340
341void
342InMemoryStorage::printCache(std::ostream& os) const
343{
344 // start from the upper layer towards bottom
345 for (const auto& elem : m_cache.get<byFullName>())
346 os << elem->getFullName() << std::endl;
347}
348
349} // namespace ndn
Represents a Data packet.
Definition data.hpp:39
const Name & getFullName() const
Get the full name (including implicit digest).
Definition data.cpp:200
Represents an in-memory storage entry.
void scheduleMarkStale(Scheduler &sched, time::nanoseconds after)
Schedule an event to mark this entry as non-fresh.
void setData(const Data &data)
Changes the content of in-memory storage entry.
const Data & getData() const
Returns the Data packet stored in the in-memory storage entry.
Represents an error might be thrown during reduce the current capacity of the in-memory storage throu...
Represents a const_iterator for the in-memory storage.
InMemoryStorage(size_t limit=std::numeric_limits< size_t >::max())
Create a InMemoryStorage with up to limit entries.
void erase(const Name &prefix, const bool isPrefix=true)
Deletes in-memory storage entry by prefix by default.
virtual void afterAccess(InMemoryStorageEntry *entry)
Update the entry when the entry is returned by the find() function according to derived class impleme...
void insert(const Data &data, const time::milliseconds &mustBeFreshProcessingWindow=INFINITE_WINDOW)
Inserts a Data packet.
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.
virtual void beforeErase(InMemoryStorageEntry *entry)
Update the entry or other data structures before a entry is successfully erased according to derived ...
shared_ptr< const Data > find(const Interest &interest)
Finds the best match Data for an Interest.
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.
size_t getCapacity() const
Returns current capacity of in-memory storage (in packets).
InMemoryStorage::const_iterator begin() const
Returns begin iterator of the in-memory storage ordering by name with digest.
void eraseImpl(const Name &name)
Deletes in-memory storage entries by the Name with implicit digest.
virtual void afterInsert(InMemoryStorageEntry *entry)
Update the entry or other data structures after a entry is successfully inserted according to derived...
void setCapacity(size_t nMaxPackets)
Sets current capacity of in-memory storage (in packets).
Represents an Interest packet.
Definition interest.hpp:50
const Name & getName() const noexcept
Get the Interest name.
Definition interest.hpp:179
Represents an absolute name.
Definition name.hpp:45
bool isPrefixOf(const Name &other) const noexcept
Check if this name is a prefix of another name.
Definition name.cpp:275
#define NDN_THROW(e)
Definition exception.hpp:56
::boost::chrono::milliseconds milliseconds
Definition time.hpp:52
Definition data.cpp:25
constexpr time::milliseconds ZERO_WINDOW
constexpr size_t MIN_CAPACITY