mqtt_cpp
value_allocator.hpp
Go to the documentation of this file.
1 // Copyright Takatoshi Kondo 2020
2 //
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
6 
7 #if !defined(MQTT_VALUE_ALLOCATOR_HPP)
8 #define MQTT_VALUE_ALLOCATOR_HPP
9 
10 #include <mqtt/config.hpp> // should be top to configure variant limit
11 
12 #include <ostream>
13 #include <limits>
14 
15 #include <boost/multi_index_container.hpp>
16 #include <boost/multi_index/ordered_index.hpp>
17 #include <boost/multi_index/identity.hpp>
18 
19 #include <mqtt/optional.hpp>
20 
21 namespace MQTT_NS {
22 
23 namespace mi = boost::multi_index;
24 
25 template <typename T>
27  using value_type = T;
28 
29  class value_interval {
30  public:
31  explicit value_interval(value_type v) : low_{v}, high_{v} {}
32  value_interval(value_type l, value_type h) : low_{l}, high_{h} {}
33 
34  // true
35  // | lhs | | rhs |
36  //
37  // false
38  // | lhs |
39  // | rhs |
40  //
41  // false
42  // | lhs |
43  // | rhs |
44  //
45  // false
46  // | lhs |
47  // | rhs |
48  //
49  // false
50  // | lhs |
51  // | rhs |
52  //
53  // false
54  // | rhs | | lhs |
55  //
56  friend bool operator<(value_interval const& lhs, value_interval const& rhs) {
57  return
58  (lhs.low_ < rhs.low_) &&
59  (lhs.high_ < rhs.low_);
60  }
61  value_type low() const {
62  return low_;
63  }
64  value_type high() const {
65  return high_;
66  }
67 
68  friend std::ostream& operator<<(std::ostream& o, value_interval const v) {
69  o << '[' << v.low() << ',' << v.high() << ']';
70  return o;
71  }
72 
73  private:
74  value_type low_;
75  value_type high_;
76  };
77 
78 public:
85  value_allocator(value_type lowest, value_type highest)
86  :lowest_{lowest}, highest_{highest} {
87 
88  BOOST_ASSERT(std::numeric_limits<value_type>::min() <= lowest);
89  BOOST_ASSERT(highest <= std::numeric_limits<value_type>::max());
90  // create one interval that contains whole values
91  pool_.emplace(lowest_, highest_);
92  }
93 
98  optional<value_type> allocate() {
99  if (pool_.empty()) return nullopt;
100 
101  // The smallest interval is the target.
102  auto it = pool_.begin();
103  value_type value = it->low();
104 
105  if (it->low() + 1 <= it->high()) {
106  // If the interval contains other value, then update the interval.
107  pool_.modify(
108  it,
109  [&](auto& e) {
110  BOOST_ASSERT(it->low() < highest_);
111  e = value_interval{value_type(it->low() + 1) , it->high()};
112  }
113  );
114  }
115  else {
116  pool_.erase(it);
117  }
118 
119  return value;
120  }
121 
126  optional<value_type> first_vacant() const {
127  if (pool_.empty()) return nullopt;
128 
129  // The smallest interval is the target.
130  auto it = pool_.begin();
131  return it->low();
132  }
133 
138  void deallocate(value_type value) {
139  BOOST_ASSERT(lowest_ <= value && value <= highest_);
140  auto itr = pool_.upper_bound(value_interval{value});
141  if (itr == pool_.end()) {
142 
143  // ..... v
144 
145  if (itr == pool_.begin()) {
146  // value is fully allocated
147  pool_.emplace(value);
148  return;
149  }
150 
151  auto itl = itr;
152  --itl;
153  if (itl->high() + 1 == value) { // Can concat to the left interval
154  // Concat left
155  pool_.modify(
156  itl,
157  [&](auto& e) {
158  e = value_interval{itl->low(), value};
159  }
160  );
161  }
162  else {
163  // No concat
164  pool_.emplace(value);
165  }
166  }
167  else if (itr == pool_.begin()) {
168 
169  // v .....
170 
171  if (value + 1 == itr->low()) { // Can concat to the right interval
172  // Concat right
173  pool_.modify(
174  itr,
175  [&](auto& e) {
176  e = value_interval{value, itr->high()};
177  }
178  );
179  }
180  else {
181  // No concat
182  pool_.emplace(value);
183  }
184  }
185  else {
186 
187  // .. v ..
188 
189  auto itl = itr;
190  --itl;
191  if (itl->high() + 1 == value) { // Can concat to the left interval
192  if (value + 1 == itr->low()) { // Can concat to the right interval
193  // Concat both
194  auto right = itr->high();
195  pool_.erase(itr);
196  pool_.modify(
197  itl,
198  [&](auto& e) {
199  e = value_interval{itl->low(), right};
200  }
201  );
202 
203  }
204  else {
205  // Concat left
206  pool_.modify(
207  itl,
208  [&](auto& e) {
209  e = value_interval{itl->low(), value};
210  }
211  );
212  }
213  }
214  else {
215  if (value + 1 == itr->low()) { // Can concat to the right interval
216  // Concat right
217  pool_.modify(
218  itr,
219  [&](auto& e) {
220  e = value_interval{value, itr->high()};
221  }
222  );
223  }
224  else {
225  // No concat
226  pool_.emplace(value);
227  }
228  }
229  }
230  }
231 
237  bool use(value_type value) {
238  auto it = pool_.find(value_interval{value});
239  if (it == pool_.end()) return false;
240 
241  value_interval iv = *it;
242  pool_.erase (it);
243  if (iv.low() < value) {
244  pool_.emplace(iv.low(), value - 1);
245  }
246  if (value + 1 <= iv.high()) {
247  pool_.emplace(value + 1, iv.high());
248  }
249  return true;
250  }
251 
255  void clear() {
256  pool_.clear();
257  pool_.emplace(lowest_, highest_);
258  }
259 
260  std::size_t interval_count() const {
261  return pool_.size();
262  }
263 
264  std::ostream& dump(std::ostream& o) {
265  for (auto const& e : pool_) {
266  o << e;
267  }
268  return o;
269  }
270 private:
271  using mi_value_interval = mi::multi_index_container<
272  value_interval,
273  mi::indexed_by<
274  mi::ordered_unique<
275  mi::identity<value_interval>
276  >
277  >
278  >;
279  mi_value_interval pool_;
280  value_type lowest_;
281  value_type highest_;
282 };
283 
284 } // namespace MQTT_NS
285 
286 #endif // MQTT_VALUE_ALLOCATOR_HPP
Definition: value_allocator.hpp:26
std::size_t interval_count() const
Definition: value_allocator.hpp:260
void deallocate(value_type value)
Dellocate one value.
Definition: value_allocator.hpp:138
value_allocator(value_type lowest, value_type highest)
Create value_allocator The allocator has [lowest, highest] values.
Definition: value_allocator.hpp:85
optional< value_type > allocate()
Allocate one value.
Definition: value_allocator.hpp:98
optional< value_type > first_vacant() const
Get the first vacant value.
Definition: value_allocator.hpp:126
void clear()
Clear all allocated or used values.
Definition: value_allocator.hpp:255
bool use(value_type value)
Declare the value as used.
Definition: value_allocator.hpp:237
std::ostream & dump(std::ostream &o)
Definition: value_allocator.hpp:264
Definition: any.hpp:27
std::ostream & operator<<(std::ostream &os, connect_return_code val)
Definition: connect_return_code.hpp:43
bool operator<(share_name_topic_filter const &lhs, share_name_topic_filter const &rhs)
Definition: shared_subscriptions.hpp:36