async_mqtt 9.0.1
Loading...
Searching...
No Matches
value_allocator.hpp
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(ASYNC_MQTT_UTIL_VALUE_ALLOCATOR_HPP)
8#define ASYNC_MQTT_UTIL_VALUE_ALLOCATOR_HPP
9
10#include <ostream>
11#include <limits>
12#include <optional>
13
14#include <boost/multi_index_container.hpp>
15#include <boost/multi_index/ordered_index.hpp>
16#include <boost/multi_index/identity.hpp>
17
18namespace async_mqtt {
19
20namespace mi = boost::multi_index;
21
22template <typename T>
23class value_allocator {
24 using value_type = T;
25
26 class value_interval {
27 public:
28 explicit value_interval(value_type v) : low_{v}, high_{v} {}
29 explicit value_interval(value_type l, value_type h) : low_{l}, high_{h} {}
30
31 // true
32 // | lhs | | rhs |
33 //
34 // false
35 // | lhs |
36 // | rhs |
37 //
38 // false
39 // | lhs |
40 // | rhs |
41 //
42 // false
43 // | lhs |
44 // | rhs |
45 //
46 // false
47 // | lhs |
48 // | rhs |
49 //
50 // false
51 // | rhs | | lhs |
52 //
53 friend bool operator<(value_interval const& lhs, value_interval const& rhs) {
54 return
55 (lhs.low_ < rhs.low_) &&
56 (lhs.high_ < rhs.low_);
57 }
58 value_type low() const {
59 return low_;
60 }
61 value_type high() const {
62 return high_;
63 }
64
65 friend std::ostream& operator<<(std::ostream& o, value_interval const v) {
66 o << '[' << v.low() << ',' << v.high() << ']';
67 return o;
68 }
69
70 private:
71 value_type low_;
72 value_type high_;
73 };
74
75public:
82 explicit value_allocator(value_type lowest, value_type highest)
83 :lowest_{lowest}, highest_{highest} {
84
85 BOOST_ASSERT(std::numeric_limits<value_type>::min() <= lowest);
86 BOOST_ASSERT(highest <= std::numeric_limits<value_type>::max());
87 // create one interval that contains whole values
88 pool_.emplace(lowest_, highest_);
89 }
90
95 std::optional<value_type> allocate() {
96 if (pool_.empty()) return std::nullopt;
97
98 // The smallest interval is the target.
99 auto it = pool_.begin();
100 value_type value = it->low();
101
102 if (it->low() + 1 <= it->high()) {
103 // If the interval contains other value, then update the interval.
104 pool_.modify(
105 it,
106 [&](auto& e) {
107 BOOST_ASSERT(it->low() < highest_);
108 e = value_interval{value_type(it->low() + 1) , it->high()};
109 }
110 );
111 }
112 else {
113 pool_.erase(it);
114 }
115
116 return value;
117 }
118
123 std::optional<value_type> first_vacant() const {
124 if (pool_.empty()) return std::nullopt;
125
126 // The smallest interval is the target.
127 auto it = pool_.begin();
128 return it->low();
129 }
130
135 void deallocate(value_type value) {
136 BOOST_ASSERT(lowest_ <= value && value <= highest_);
137 auto itr = pool_.upper_bound(value_interval{value});
138 if (itr == pool_.end()) {
139
140 // ..... v
141
142 if (itr == pool_.begin()) {
143 // value is fully allocated
144 pool_.emplace(value);
145 return;
146 }
147
148 auto itl = itr;
149 --itl;
150 if (itl->high() + 1 == value) { // Can concat to the left interval
151 // Concat left
152 pool_.modify(
153 itl,
154 [&](auto& e) {
155 e = value_interval{itl->low(), value};
156 }
157 );
158 }
159 else {
160 // No concat
161 pool_.emplace(value);
162 }
163 }
164 else if (itr == pool_.begin()) {
165
166 // v .....
167
168 if (value + 1 == itr->low()) { // Can concat to the right interval
169 // Concat right
170 pool_.modify(
171 itr,
172 [&](auto& e) {
173 e = value_interval{value, itr->high()};
174 }
175 );
176 }
177 else {
178 // No concat
179 pool_.emplace(value);
180 }
181 }
182 else {
183
184 // .. v ..
185
186 auto itl = itr;
187 --itl;
188 if (itl->high() + 1 == value) { // Can concat to the left interval
189 if (value + 1 == itr->low()) { // Can concat to the right interval
190 // Concat both
191 auto right = itr->high();
192 pool_.erase(itr);
193 pool_.modify(
194 itl,
195 [&](auto& e) {
196 e = value_interval{itl->low(), right};
197 }
198 );
199
200 }
201 else {
202 // Concat left
203 pool_.modify(
204 itl,
205 [&](auto& e) {
206 e = value_interval{itl->low(), value};
207 }
208 );
209 }
210 }
211 else {
212 if (value + 1 == itr->low()) { // Can concat to the right interval
213 // Concat right
214 pool_.modify(
215 itr,
216 [&](auto& e) {
217 e = value_interval{value, itr->high()};
218 }
219 );
220 }
221 else {
222 // No concat
223 pool_.emplace(value);
224 }
225 }
226 }
227 }
228
234 bool use(value_type value) {
235 auto it = pool_.find(value_interval{value});
236 if (it == pool_.end()) return false;
237
238 value_interval iv = *it;
239 pool_.erase (it);
240 if (iv.low() < value) {
241 pool_.emplace(iv.low(), value - 1);
242 }
243 if (value + 1 <= iv.high()) {
244 pool_.emplace(value + 1, iv.high());
245 }
246 return true;
247 }
248
254 bool is_used(value_type value) const {
255 auto it = pool_.find(value_interval{value});
256 return it == pool_.end();
257 }
258
262 void clear() {
263 pool_.clear();
264 pool_.emplace(lowest_, highest_);
265 }
266
267 std::size_t interval_count() const {
268 return pool_.size();
269 }
270
271 std::ostream& dump(std::ostream& o) {
272 for (auto const& e : pool_) {
273 o << e;
274 }
275 return o;
276 }
277private:
278 using mi_value_interval = mi::multi_index_container<
279 value_interval,
280 mi::indexed_by<
281 mi::ordered_unique<
282 mi::identity<value_interval>
283 >
284 >
285 >;
286 mi_value_interval pool_;
287 value_type lowest_;
288 value_type highest_;
289};
290
291} // namespace async_mqtt
292
293#endif // ASYNC_MQTT_UTIL_VALUE_ALLOCATOR_HPP