Vanetza
 
Loading...
Searching...
No Matches
soft_state_map.hpp
1#ifndef SOFT_STATE_MAP_HPP_B0MARRWZ
2#define SOFT_STATE_MAP_HPP_B0MARRWZ
3
4#include <vanetza/common/clock.hpp>
5#include <vanetza/common/runtime.hpp>
6#include <boost/heap/binomial_heap.hpp>
7#include <boost/range/iterator_range_core.hpp>
8#include <boost/range/adaptor/filtered.hpp>
9#include <boost/range/adaptor/transformed.hpp>
10#include <cassert>
11#include <functional>
12#include <type_traits>
13#include <unordered_map>
14
15namespace vanetza
16{
17namespace geonet
18{
19
20template<typename VALUE>
22{
23 VALUE operator()() { return VALUE(); }
24};
25
26/**
27 * SoftStateMap is a map data structure with expiring entries
28 * \tparam KEY key type
29 * \tparam VALUE mapped type
30 * \tparam CTOR optional creator of values
31 */
32template<typename KEY, typename VALUE, typename CTOR = SoftStateDefaultCreator<VALUE>>
34{
35public:
36 using key_type = KEY;
37 using mapped_type = VALUE;
38 using value_type = std::pair<const key_type&, mapped_type&>;
39 using creator_type = CTOR;
40
41private:
42 class ExpiryWithKey : public Clock::time_point
43 {
44 public:
45 ExpiryWithKey() = default;
46 ExpiryWithKey(const key_type& key, Clock::time_point expiry) :
47 Clock::time_point(expiry), m_key(key) {}
48
49 const key_type& key() const { return m_key; }
50
51 private:
52 key_type m_key;
53 };
54 using heap_type = boost::heap::binomial_heap<ExpiryWithKey, boost::heap::compare<std::greater<ExpiryWithKey>>>;
55
57 {
58 ValueWithHandle(mapped_type&& v) : value(std::move(v)) {}
59
60 typename heap_type::handle_type handle;
61 mapped_type value;
62
63 mapped_type& operator*() { return value; }
64 const mapped_type& operator*() const { return value; }
65 };
66 using map_type = std::unordered_map<key_type, ValueWithHandle>;
67
68 using data_range = boost::iterator_range<typename map_type::iterator>;
69 using data_filter = std::function<bool(const typename map_type::value_type&)>;
70 using data_transform = std::function<value_type(typename map_type::value_type&)>;
71
72public:
73 /**
74 * Construct SoftStateMap
75 * \param rt runtime object
76 * \note This constructor is only available if CTOR is default constructible
77 */
78 template<typename T = CTOR>
79 SoftStateMap(const Runtime& rt, typename std::enable_if<std::is_default_constructible<T>::value>::type* = nullptr) :
80 m_runtime(rt)
81 {
82 }
83
84 /**
85 * Construct SoftStateMap
86 * \param rt runtime object
87 * \param ctor value creator
88 */
89 SoftStateMap(const Runtime& rt, creator_type&& ctor) :
90 m_runtime(rt), m_creator(std::move(ctor))
91 {
92 }
93
94 /**
95 * Set lifetime duration used for new and refreshed entries
96 * \param lifetime entry lieftime
97 */
98 void set_lifetime(Clock::duration lifetime)
99 {
100 m_lifetime = lifetime;
101 }
102
103 /**
104 * Get value mapped to key
105 * \param key
106 * \return existing value entry or just created entry
107 */
108 mapped_type& get_value(const key_type& key)
109 {
110 return get_data(key).value;
111 }
112
113 /**
114 * Get non-expired value pointer mapped to key
115 * \param key
116 * \return pointer to value or nullptr if not existing
117 */
118 mapped_type* get_value_ptr(const key_type& key)
119 {
120 auto* data = get_data_ptr(key);
121 return data && !is_expired(*data->handle) ? &data->value : nullptr;
122 }
123
124 /**
125 * Get non-expired value pointer mapped to key
126 * \param key
127 * \return pointer to value or nullptr if not existing
128 */
129 const mapped_type* get_value_ptr(const key_type& key) const
130 {
131 auto* data = get_data_ptr(key);
132 return data && !is_expired(*data->handle) ? &data->value : nullptr;
133 }
134
135 /**
136 * Check if non-expired value for given key exists
137 * \param key
138 * \return true if entry exists
139 */
140 bool has_value(const key_type& key) const
141 {
142 return get_value_ptr(key) != nullptr;
143 }
144
145 /**
146 * Refresh lifetime of entry associated with given key
147 * \param key
148 * \return associated value (might have been created)
149 */
150 mapped_type& refresh(const key_type& key)
151 {
152 auto* data = get_data_ptr(key);
153 if (data) {
154 refresh(data->handle);
155 } else {
156 data = &get_data(key);
157 assert(data != nullptr);
158 }
159 return data->value;
160 }
161
162 /**
163 * Drop all entries with expired lifetime.
164 * Expired but still stored entries are only hided at retrieval until calling this method.
165 */
167 {
168 while (!m_heap.empty() && is_expired(m_heap.top())) {
169 m_map.erase(m_heap.top().key());
170 m_heap.pop();
171 }
172 }
173
174 using map_range = boost::transformed_range<data_transform, const boost::filtered_range<data_filter, data_range>>;
175
176 /**
177 * Create a range of all non-expired entries mimicking STL's map interface
178 */
179 map_range map()
180 {
181 data_filter filter_fn = [this](const typename map_type::value_type& v) {
182 return !this->is_expired(*v.second.handle);
183 };
184 data_transform transform_fn = [](typename map_type::value_type& v) {
185 return value_type { v.first, v.second.value };
186 };
187 using namespace boost::adaptors;
188 data_range range_all = boost::make_iterator_range(m_map.begin(), m_map.end());
189 return range_all | filtered(filter_fn) | transformed(transform_fn);
190 }
191
192 const map_range map() const
193 {
194 return const_cast<SoftStateMap*>(this)->map();
195 }
196
197private:
198 ValueWithHandle& get_data(const key_type& key)
199 {
200 auto* data = get_data_ptr(key);
201 if (!data) {
202 auto insertion = m_map.emplace(std::piecewise_construct,
203 std::forward_as_tuple(key), std::forward_as_tuple(m_creator()));
204 data = &insertion.first->second;
205 data->handle = m_heap.push(ExpiryWithKey {key, m_runtime.now() + m_lifetime});
206 } else if (is_expired(*data->handle)) {
207 // resurrect this data element, i.e. pretend it has just been created
208 refresh(data->handle);
209 }
210 return *data;
211 }
212
213 ValueWithHandle* get_data_ptr(const key_type& key)
214 {
215 auto it = m_map.find(key);
216 return it != m_map.end() ? &it->second : nullptr;
217 }
218
219 const ValueWithHandle* get_data_ptr(const key_type& key) const
220 {
221 auto it = m_map.find(key);
222 return it != m_map.end() ? &it->second : nullptr;
223 }
224
225 bool is_expired(const ExpiryWithKey& expiry) const
226 {
227 return m_runtime.now() > expiry;
228 }
229
230 void refresh(typename heap_type::handle_type& handle)
231 {
232 ExpiryWithKey& expiry = *handle;
233 static_cast<Clock::time_point&>(expiry) = m_runtime.now() + m_lifetime;
234 m_heap.update(handle);
235 }
236
237 const Runtime& m_runtime;
238 Clock::duration m_lifetime;
239 creator_type m_creator;
240 heap_type m_heap;
241 map_type m_map;
242};
243
244} // namespace geonet
245} // namespace vanetza
246
247#endif /* SOFT_STATE_MAP_HPP_B0MARRWZ */
virtual Clock::time_point now() const =0
SoftStateMap(const Runtime &rt, typename std::enable_if< std::is_default_constructible< T >::value >::type *=nullptr)
SoftStateMap(const Runtime &rt, creator_type &&ctor)
bool has_value(const key_type &key) const
mapped_type & refresh(const key_type &key)
const mapped_type * get_value_ptr(const key_type &key) const
void set_lifetime(Clock::duration lifetime)
mapped_type & get_value(const key_type &key)
mapped_type * get_value_ptr(const key_type &key)
STL namespace.