]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/boost/histogram/storage_adaptor.hpp
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / boost / boost / histogram / storage_adaptor.hpp
1 // Copyright 2018-2019 Hans Dembinski
2 //
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt
5 // or copy at http://www.boost.org/LICENSE_1_0.txt)
6
7 #ifndef BOOST_HISTOGRAM_STORAGE_ADAPTOR_HPP
8 #define BOOST_HISTOGRAM_STORAGE_ADAPTOR_HPP
9
10 #include <algorithm>
11 #include <boost/core/nvp.hpp>
12 #include <boost/histogram/accumulators/is_thread_safe.hpp>
13 #include <boost/histogram/detail/array_wrapper.hpp>
14 #include <boost/histogram/detail/detect.hpp>
15 #include <boost/histogram/detail/iterator_adaptor.hpp>
16 #include <boost/histogram/detail/safe_comparison.hpp>
17 #include <boost/histogram/fwd.hpp>
18 #include <boost/mp11/utility.hpp>
19 #include <boost/throw_exception.hpp>
20 #include <stdexcept>
21 #include <type_traits>
22
23 namespace boost {
24 namespace histogram {
25 namespace detail {
26
27 template <class T>
28 struct vector_impl : T {
29 using allocator_type = typename T::allocator_type;
30
31 static constexpr bool has_threading_support =
32 accumulators::is_thread_safe<typename T::value_type>::value;
33
34 vector_impl(const allocator_type& a = {}) : T(a) {}
35 vector_impl(const vector_impl&) = default;
36 vector_impl& operator=(const vector_impl&) = default;
37 vector_impl(vector_impl&&) = default;
38 vector_impl& operator=(vector_impl&&) = default;
39
40 explicit vector_impl(T&& t) : T(std::move(t)) {}
41 explicit vector_impl(const T& t) : T(t) {}
42
43 template <class U, class = requires_iterable<U>>
44 explicit vector_impl(const U& u, const allocator_type& a = {})
45 : T(std::begin(u), std::end(u), a) {}
46
47 template <class U, class = requires_iterable<U>>
48 vector_impl& operator=(const U& u) {
49 T::resize(u.size());
50 auto it = T::begin();
51 for (auto&& x : u) *it++ = x;
52 return *this;
53 }
54
55 void reset(std::size_t n) {
56 using value_type = typename T::value_type;
57 const auto old_size = T::size();
58 T::resize(n, value_type());
59 std::fill_n(T::begin(), (std::min)(n, old_size), value_type());
60 }
61
62 template <class Archive>
63 void serialize(Archive& ar, unsigned /* version */) {
64 ar& make_nvp("vector", static_cast<T&>(*this));
65 }
66 };
67
68 template <class T>
69 struct array_impl : T {
70 static constexpr bool has_threading_support =
71 accumulators::is_thread_safe<typename T::value_type>::value;
72
73 array_impl() = default;
74 array_impl(const array_impl& t) : T(t), size_(t.size_) {}
75 array_impl& operator=(const array_impl& t) {
76 T::operator=(t);
77 size_ = t.size_;
78 return *this;
79 }
80
81 explicit array_impl(T&& t) : T(std::move(t)) {}
82 explicit array_impl(const T& t) : T(t) {}
83
84 template <class U, class = requires_iterable<U>>
85 explicit array_impl(const U& u) : size_(u.size()) {
86 using std::begin;
87 using std::end;
88 std::copy(begin(u), end(u), this->begin());
89 }
90
91 template <class U, class = requires_iterable<U>>
92 array_impl& operator=(const U& u) {
93 if (u.size() > T::max_size()) // for std::arra
94 BOOST_THROW_EXCEPTION(std::length_error("argument size exceeds maximum capacity"));
95 size_ = u.size();
96 using std::begin;
97 using std::end;
98 std::copy(begin(u), end(u), T::begin());
99 return *this;
100 }
101
102 void reset(std::size_t n) {
103 using value_type = typename T::value_type;
104 if (n > T::max_size()) // for std::array
105 BOOST_THROW_EXCEPTION(std::length_error("argument size exceeds maximum capacity"));
106 std::fill_n(T::begin(), n, value_type());
107 size_ = n;
108 }
109
110 typename T::iterator end() noexcept { return T::begin() + size_; }
111 typename T::const_iterator end() const noexcept { return T::begin() + size_; }
112
113 std::size_t size() const noexcept { return size_; }
114
115 template <class Archive>
116 void serialize(Archive& ar, unsigned /* version */) {
117 ar& make_nvp("size", size_);
118 auto w = detail::make_array_wrapper(T::data(), size_);
119 ar& make_nvp("array", w);
120 }
121
122 std::size_t size_ = 0;
123 };
124
125 template <class T>
126 struct map_impl : T {
127 static_assert(std::is_same<typename T::key_type, std::size_t>::value,
128 "requires std::size_t as key_type");
129
130 using value_type = typename T::mapped_type;
131 using const_reference = const value_type&;
132
133 static constexpr bool has_threading_support = false;
134 static_assert(
135 !accumulators::is_thread_safe<value_type>::value,
136 "std::map and std::unordered_map do not support thread-safe element access. "
137 "If you have a map with thread-safe element access, please file an issue and"
138 "support will be added.");
139
140 struct reference {
141 reference(map_impl* m, std::size_t i) noexcept : map(m), idx(i) {}
142
143 reference(const reference&) noexcept = default;
144 reference& operator=(const reference& o) {
145 if (this != &o) operator=(static_cast<const_reference>(o));
146 return *this;
147 }
148
149 operator const_reference() const noexcept {
150 return static_cast<const map_impl*>(map)->operator[](idx);
151 }
152
153 reference& operator=(const_reference u) {
154 auto it = map->find(idx);
155 if (u == value_type{}) {
156 if (it != static_cast<T*>(map)->end()) { map->erase(it); }
157 } else {
158 if (it != static_cast<T*>(map)->end()) {
159 it->second = u;
160 } else {
161 map->emplace(idx, u);
162 }
163 }
164 return *this;
165 }
166
167 template <class U, class V = value_type,
168 class = std::enable_if_t<has_operator_radd<V, U>::value>>
169 reference& operator+=(const U& u) {
170 auto it = map->find(idx);
171 if (it != static_cast<T*>(map)->end()) {
172 it->second += u;
173 } else {
174 map->emplace(idx, u);
175 }
176 return *this;
177 }
178
179 template <class U, class V = value_type,
180 class = std::enable_if_t<has_operator_rsub<V, U>::value>>
181 reference& operator-=(const U& u) {
182 auto it = map->find(idx);
183 if (it != static_cast<T*>(map)->end()) {
184 it->second -= u;
185 } else {
186 map->emplace(idx, -u);
187 }
188 return *this;
189 }
190
191 template <class U, class V = value_type,
192 class = std::enable_if_t<has_operator_rmul<V, U>::value>>
193 reference& operator*=(const U& u) {
194 auto it = map->find(idx);
195 if (it != static_cast<T*>(map)->end()) it->second *= u;
196 return *this;
197 }
198
199 template <class U, class V = value_type,
200 class = std::enable_if_t<has_operator_rdiv<V, U>::value>>
201 reference& operator/=(const U& u) {
202 auto it = map->find(idx);
203 if (it != static_cast<T*>(map)->end()) {
204 it->second /= u;
205 } else if (!(value_type{} / u == value_type{})) {
206 map->emplace(idx, value_type{} / u);
207 }
208 return *this;
209 }
210
211 template <class V = value_type,
212 class = std::enable_if_t<has_operator_preincrement<V>::value>>
213 reference operator++() {
214 auto it = map->find(idx);
215 if (it != static_cast<T*>(map)->end()) {
216 ++it->second;
217 } else {
218 value_type tmp{};
219 ++tmp;
220 map->emplace(idx, tmp);
221 }
222 return *this;
223 }
224
225 template <class V = value_type,
226 class = std::enable_if_t<has_operator_preincrement<V>::value>>
227 value_type operator++(int) {
228 const value_type tmp = *this;
229 operator++();
230 return tmp;
231 }
232
233 template <class U, class = std::enable_if_t<has_operator_equal<value_type, U>::value>>
234 bool operator==(const U& rhs) const {
235 return operator const_reference() == rhs;
236 }
237
238 template <class U, class = std::enable_if_t<has_operator_equal<value_type, U>::value>>
239 bool operator!=(const U& rhs) const {
240 return !operator==(rhs);
241 }
242
243 template <class CharT, class Traits>
244 friend std::basic_ostream<CharT, Traits>& operator<<(
245 std::basic_ostream<CharT, Traits>& os, reference x) {
246 os << static_cast<const_reference>(x);
247 return os;
248 }
249
250 template <class... Ts>
251 auto operator()(const Ts&... args) -> decltype(std::declval<value_type>()(args...)) {
252 return (*map)[idx](args...);
253 }
254
255 map_impl* map;
256 std::size_t idx;
257 };
258
259 template <class Value, class Reference, class MapPtr>
260 struct iterator_t
261 : iterator_adaptor<iterator_t<Value, Reference, MapPtr>, std::size_t, Reference> {
262 iterator_t() = default;
263 template <class V, class R, class M,
264 class = std::enable_if_t<std::is_convertible<M, MapPtr>::value>>
265 iterator_t(const iterator_t<V, R, M>& it) noexcept : iterator_t(it.map_, it.base()) {}
266 iterator_t(MapPtr m, std::size_t i) noexcept
267 : iterator_t::iterator_adaptor_(i), map_(m) {}
268 template <class V, class R, class M>
269 bool equal(const iterator_t<V, R, M>& rhs) const noexcept {
270 return map_ == rhs.map_ && iterator_t::base() == rhs.base();
271 }
272 Reference operator*() const { return (*map_)[iterator_t::base()]; }
273 MapPtr map_ = nullptr;
274 };
275
276 using iterator = iterator_t<value_type, reference, map_impl*>;
277 using const_iterator = iterator_t<const value_type, const_reference, const map_impl*>;
278
279 using allocator_type = typename T::allocator_type;
280
281 map_impl(const allocator_type& a = {}) : T(a) {}
282
283 map_impl(const map_impl&) = default;
284 map_impl& operator=(const map_impl&) = default;
285 map_impl(map_impl&&) = default;
286 map_impl& operator=(map_impl&&) = default;
287
288 map_impl(const T& t) : T(t), size_(t.size()) {}
289 map_impl(T&& t) : T(std::move(t)), size_(t.size()) {}
290
291 template <class U, class = requires_iterable<U>>
292 explicit map_impl(const U& u, const allocator_type& a = {}) : T(a), size_(u.size()) {
293 using std::begin;
294 using std::end;
295 std::copy(begin(u), end(u), this->begin());
296 }
297
298 template <class U, class = requires_iterable<U>>
299 map_impl& operator=(const U& u) {
300 if (u.size() < size_)
301 reset(u.size());
302 else
303 size_ = u.size();
304 using std::begin;
305 using std::end;
306 std::copy(begin(u), end(u), this->begin());
307 return *this;
308 }
309
310 void reset(std::size_t n) {
311 T::clear();
312 size_ = n;
313 }
314
315 reference operator[](std::size_t i) noexcept { return {this, i}; }
316 const_reference operator[](std::size_t i) const noexcept {
317 auto it = T::find(i);
318 static const value_type null = value_type{};
319 if (it == T::end()) return null;
320 return it->second;
321 }
322
323 iterator begin() noexcept { return {this, 0}; }
324 iterator end() noexcept { return {this, size_}; }
325
326 const_iterator begin() const noexcept { return {this, 0}; }
327 const_iterator end() const noexcept { return {this, size_}; }
328
329 std::size_t size() const noexcept { return size_; }
330
331 template <class Archive>
332 void serialize(Archive& ar, unsigned /* version */) {
333 ar& make_nvp("size", size_);
334 ar& make_nvp("map", static_cast<T&>(*this));
335 }
336
337 std::size_t size_ = 0;
338 };
339
340 template <class T>
341 struct ERROR_type_passed_to_storage_adaptor_not_recognized;
342
343 // clang-format off
344 template <class T>
345 using storage_adaptor_impl =
346 mp11::mp_cond<
347 is_vector_like<T>, vector_impl<T>,
348 is_array_like<T>, array_impl<T>,
349 is_map_like<T>, map_impl<T>,
350 std::true_type, ERROR_type_passed_to_storage_adaptor_not_recognized<T>
351 >;
352 // clang-format on
353 } // namespace detail
354
355 /// Turns any vector-like, array-like, and map-like container into a storage type.
356 template <class T>
357 class storage_adaptor : public detail::storage_adaptor_impl<T> {
358 using impl_type = detail::storage_adaptor_impl<T>;
359
360 public:
361 // standard copy, move, assign
362 storage_adaptor(storage_adaptor&&) = default;
363 storage_adaptor(const storage_adaptor&) = default;
364 storage_adaptor& operator=(storage_adaptor&&) = default;
365 storage_adaptor& operator=(const storage_adaptor&) = default;
366
367 // forwarding constructor
368 template <class... Ts>
369 storage_adaptor(Ts&&... ts) : impl_type(std::forward<Ts>(ts)...) {}
370
371 // forwarding assign
372 template <class U>
373 storage_adaptor& operator=(U&& u) {
374 impl_type::operator=(std::forward<U>(u));
375 return *this;
376 }
377
378 template <class U, class = detail::requires_iterable<U>>
379 bool operator==(const U& u) const {
380 using std::begin;
381 using std::end;
382 return std::equal(this->begin(), this->end(), begin(u), end(u), detail::safe_equal{});
383 }
384
385 template <class Archive>
386 void serialize(Archive& ar, unsigned /* version */) {
387 ar& make_nvp("impl", static_cast<impl_type&>(*this));
388 }
389
390 private:
391 friend struct unsafe_access;
392 };
393
394 } // namespace histogram
395 } // namespace boost
396
397 #endif