Kagome
Polkadot Runtime Engine in C++17
size_limited_containers.hpp
Go to the documentation of this file.
1 
6 #ifndef KAGOME_COMMON_SIZELIMITEDCONTAINER
7 #define KAGOME_COMMON_SIZELIMITEDCONTAINER
8 
9 #include <fmt/format.h>
10 #include <gsl/span>
11 #include <limits>
12 #include <stdexcept>
13 #include <type_traits>
14 #include <vector>
15 
16 #ifdef __has_builtin
17 #if __has_builtin(__builtin_expect)
18 #define unlikely(x) __builtin_expect((x), 0)
19 #endif
20 #endif
21 #ifndef unlikely
22 #define unlikely(x) (x)
23 #endif
24 
25 namespace kagome::common {
26 
27  class MaxSizeException : public std::length_error {
28  public:
29  template <typename... Args>
30  MaxSizeException(Args &&...args)
31  : std::length_error(fmt::format(std::forward<Args>(args)...)) {}
32  };
33 
34  template <typename BaseContainer, std::size_t MaxSize>
35  class SizeLimitedContainer : public BaseContainer {
36  protected:
37  using Base = BaseContainer;
38  using Span = gsl::span<typename Base::value_type>;
39 
40  static constexpr bool size_check_is_enabled =
41  MaxSize < std::numeric_limits<typename Base::size_type>::max();
42 
43  public:
44  // Next line is required at least for the scale-codec
45  static constexpr bool is_static_collection = false;
46 
47  [[nodiscard]] inline constexpr typename Base::size_type max_size() {
48  return MaxSize;
49  }
50 
51  SizeLimitedContainer() = default;
52 
53  SizeLimitedContainer(size_t size)
54  : Base([&] {
55  if constexpr (size_check_is_enabled) {
56  if (unlikely(size > max_size())) {
57  throw MaxSizeException(
58  "Destination has limited size by {}; requested size is {}",
59  max_size(),
60  size);
61  }
62  }
63  return Base(size);
64  }()) {}
65 
66  SizeLimitedContainer(size_t size, const typename Base::value_type &value)
67  : Base([&] {
68  if constexpr (size_check_is_enabled) {
69  if (unlikely(size > max_size())) {
70  throw MaxSizeException(
71  "Destination has limited size by {}; requested size is {}",
72  max_size(),
73  size);
74  }
75  }
76  return Base(size, value);
77  }()) {}
78 
79  SizeLimitedContainer(const Base &other)
80  : Base([&] {
81  if constexpr (size_check_is_enabled) {
82  if (unlikely(other.size() > max_size())) {
83  throw MaxSizeException(
84  "Destination has limited size by {}; Source size is {}",
85  max_size(),
86  other.size());
87  }
88  }
89  return other;
90  }()) {}
91 
93  : Base([&] {
94  if constexpr (size_check_is_enabled) {
95  if (unlikely(other.size() > max_size())) {
96  throw MaxSizeException(
97  "Destination has limited size by {}; Source size is {}",
98  max_size(),
99  other.size());
100  }
101  }
102  return std::move(other);
103  }()) {}
104 
105  template <typename Iter,
106  typename = std::enable_if_t<std::is_base_of_v<
107  std::input_iterator_tag,
108  typename std::iterator_traits<Iter>::iterator_category>>>
109  SizeLimitedContainer(Iter begin, Iter end)
110  : Base([&] {
111  if constexpr (size_check_is_enabled) {
112  const size_t size = std::distance(begin, end);
113  if (unlikely(size > max_size())) {
114  throw MaxSizeException(
115  "Container has limited size by {}; Source range size is {}",
116  max_size(),
117  Base::size());
118  }
119  }
120  return Base(std::move(begin), std::move(end));
121  }()) {}
122 
123  SizeLimitedContainer(std::initializer_list<typename Base::value_type> list)
124  : SizeLimitedContainer(list.begin(), list.end()) {}
125 
127  if constexpr (size_check_is_enabled) {
128  if (unlikely(other.size() > max_size())) {
129  throw MaxSizeException(
130  "Destination has limited size by {}; Source size is {}",
131  max_size(),
132  other.size());
133  }
134  }
135  static_cast<Base &>(*this) = other;
136  return *this;
137  }
138 
140  if constexpr (size_check_is_enabled) {
141  if (unlikely(other.size() > max_size())) {
142  throw MaxSizeException(
143  "Destination has limited size by {}; Source size is {}",
144  max_size(),
145  other.size());
146  }
147  }
148  static_cast<Base &>(*this) = std::move(other);
149  return *this;
150  }
151 
153  std::initializer_list<typename Base::value_type> list) {
154  if constexpr (size_check_is_enabled) {
155  if (unlikely(list.size() > max_size())) {
156  throw MaxSizeException(
157  "Destination has limited size by {}; Source size is {}",
158  max_size(),
159  list.size());
160  }
161  }
162  static_cast<Base &>(*this) =
163  std::forward<std::initializer_list<typename Base::value_type>>(list);
164  return *this;
165  }
166 
167  void assign(typename Base::size_type size,
168  const typename Base::value_type &value) {
169  if constexpr (size_check_is_enabled) {
170  if (unlikely(size > max_size())) {
171  throw MaxSizeException(
172  "Destination has limited size by {}; Requested size is {}",
173  max_size(),
174  size);
175  }
176  }
177  return Base::assign(size, value);
178  }
179 
180  template <typename Iter,
181  typename = std::enable_if_t<std::is_base_of_v<
182  std::input_iterator_tag,
183  typename std::iterator_traits<Iter>::iterator_category>>>
184  void assign(Iter begin, Iter end) {
185  if constexpr (size_check_is_enabled) {
186  const size_t size = std::distance(begin, end);
187  if (unlikely(size > max_size())) {
188  throw MaxSizeException(
189  "Container has limited size by {}; Source range size is {}",
190  max_size(),
191  Base::size());
192  }
193  }
194  return Base::assign(std::move(begin), std::move(end));
195  }
196 
197  void assign(std::initializer_list<typename Base::value_type> list) {
198  if constexpr (size_check_is_enabled) {
199  if (unlikely(list.size() > max_size())) {
200  throw MaxSizeException(
201  "Container has limited size by {}; Source range size is {}",
202  max_size(),
203  Base::size());
204  }
205  }
206  return Base::assign(std::move(list));
207  }
208 
209  template <typename... Args>
210  typename Base::reference &emplace_back(Args &&...args) {
211  if constexpr (size_check_is_enabled) {
212  if (unlikely(Base::size() >= max_size())) {
213  throw MaxSizeException(
214  "Container has limited size by {}; Size is already {} ",
215  max_size(),
216  Base::size());
217  }
218  }
219  return Base::emplace_back(std::forward<Args>(args)...);
220  }
221 
222  template <
223  typename Iter,
224  typename... Args,
225  bool isIter = std::is_same_v<Iter, typename Base::iterator>,
226  bool isConstIter = std::is_same_v<Iter, typename Base::const_iterator>,
227  typename = std::enable_if_t<isIter or isConstIter>>
228  typename Base::iterator emplace(Iter pos, Args &&...args) {
229  if constexpr (size_check_is_enabled) {
230  if (unlikely(Base::size() >= max_size())) {
231  throw MaxSizeException(
232  "Container has limited size by {}; Size is already {} ",
233  max_size(),
234  Base::size());
235  }
236  }
237  return Base::emplace(std::move(pos), std::forward<Args>(args)...);
238  }
239 
240  template <
241  typename Iter,
242  bool isIter = std::is_same_v<Iter, typename Base::iterator>,
243  bool isConstIter = std::is_same_v<Iter, typename Base::const_iterator>,
244  typename = std::enable_if_t<isIter or isConstIter>>
245  typename Base::iterator insert(Iter pos,
246  const typename Base::value_type &value) {
247  if constexpr (size_check_is_enabled) {
248  if (unlikely(Base::size() >= max_size())) {
249  throw MaxSizeException(
250  "Destination has limited size by {}; Size is already {} ",
251  max_size(),
252  Base::size());
253  }
254  }
255  return Base::insert(std::move(pos), value);
256  }
257 
258  template <
259  typename Iter,
260  bool isIter = std::is_same_v<Iter, typename Base::iterator>,
261  bool isConstIter = std::is_same_v<Iter, typename Base::const_iterator>,
262  typename = std::enable_if_t<isIter or isConstIter>>
263  typename Base::iterator insert(Iter pos,
264  typename Base::size_type size,
265  const typename Base::value_type &value) {
266  if constexpr (size_check_is_enabled) {
267  const auto available = max_size() - Base::size();
268  if (unlikely(available < size)) {
269  throw MaxSizeException(
270  "Destination has limited size by {}; Requested size is {}",
271  max_size(),
272  size);
273  }
274  }
275  return Base::insert(std::move(pos), size, value);
276  }
277 
278  template <
279  typename OutIt,
280  typename InIt,
281  bool isIter = std::is_same_v<OutIt, typename Base::iterator>,
282  bool isConstIter = std::is_same_v<OutIt, typename Base::const_iterator>,
283  typename = std::enable_if_t<isIter or isConstIter>,
284  typename = std::enable_if_t<std::is_base_of_v<
285  std::input_iterator_tag,
286  typename std::iterator_traits<InIt>::iterator_category>>>
287  typename Base::iterator insert(OutIt pos, InIt begin, InIt end) {
288  if constexpr (size_check_is_enabled) {
289  const size_t size = std::distance(begin, end);
290  const auto available = max_size() - Base::size();
291  if (unlikely(available < size)) {
292  throw MaxSizeException(
293  "Destination has limited size by {} and current size is {}; "
294  "Source range size is {} and would overflow destination",
295  max_size(),
296  Base::size(),
297  size);
298  }
299  }
300  return Base::insert(std::move(pos), std::move(begin), std::move(end));
301  }
302 
303  template <
304  typename Iter,
305  bool isIter = std::is_same_v<Iter, typename Base::iterator>,
306  bool isConstIter = std::is_same_v<Iter, typename Base::const_iterator>,
307  typename = std::enable_if_t<isIter or isConstIter>>
308  typename Base::iterator insert(
309  Iter pos, std::initializer_list<typename Base::value_type> &&list) {
310  if constexpr (size_check_is_enabled) {
311  const auto available = max_size() - Base::size();
312  if (unlikely(available < list.size())) {
313  throw MaxSizeException(
314  "Container has limited size by {}; Source range size is {}",
315  max_size(),
316  Base::size());
317  }
318  }
319  return Base::insert(pos, std::move(list));
320  }
321 
322  template <typename V>
323  void push_back(V &&value) {
324  if constexpr (size_check_is_enabled) {
325  if (unlikely(Base::size() >= max_size())) {
326  throw MaxSizeException(
327  "Container has limited size by {}; Size is already maximum",
328  max_size());
329  }
330  }
331  Base::push_back(std::forward<V>(value));
332  }
333 
334  void reserve(typename Base::size_type size) {
335  if constexpr (size_check_is_enabled) {
336  if (unlikely(size > max_size())) {
337  throw MaxSizeException(
338  "Destination has limited size by {}; Requested size is {}",
339  max_size(),
340  size);
341  }
342  }
343  return Base::reserve(size);
344  }
345 
346  void resize(typename Base::size_type size) {
347  if constexpr (size_check_is_enabled) {
348  if (unlikely(size > max_size())) {
349  throw MaxSizeException(
350  "Destination has limited size by {}; Requested size is {}",
351  max_size(),
352  size);
353  }
354  }
355  return Base::resize(size);
356  }
357 
358  void resize(typename Base::size_type size,
359  const typename Base::value_type &value) {
360  if constexpr (size_check_is_enabled) {
361  if (unlikely(size > max_size())) {
362  throw MaxSizeException(
363  "Destination has limited size by {}; Requested size is {}",
364  max_size(),
365  size);
366  }
367  }
368  return Base::resize(size, value);
369  }
370 
371  bool operator==(const Base &other) const noexcept {
372  return std::equal(
373  Base::cbegin(), Base::cend(), other.cbegin(), other.cend());
374  }
375 
376  bool operator==(const Span &other) const noexcept {
377  return std::equal(
378  Base::cbegin(), Base::cend(), other.cbegin(), other.cend());
379  }
380 
381  bool operator!=(const Base &other) const noexcept {
382  return not(*this == other);
383  }
384 
385  bool operator!=(const Span &other) const noexcept {
386  return not(*this == other);
387  }
388 
389  bool operator<(const Base &other) const noexcept {
390  return std::lexicographical_compare(
391  Base::cbegin(), Base::cend(), other.cbegin(), other.cend());
392  }
393 
394  bool operator<(const Span &other) const noexcept {
395  return std::lexicographical_compare(
396  Base::cbegin(), Base::cend(), other.cbegin(), other.cend());
397  }
398  };
399 
400  template <typename ElementType, size_t MaxSize, typename... Args>
401  using SLVector =
402  SizeLimitedContainer<std::vector<ElementType, Args...>, MaxSize>;
403 
404 } // namespace kagome::common
405 
406 #undef unlikely
407 
408 #endif // KAGOME_COMMON_SIZELIMITEDCONTAINER
Base::iterator insert(Iter pos, const typename Base::value_type &value)
void resize(typename Base::size_type size)
SizeLimitedContainer(std::initializer_list< typename Base::value_type > list)
SizeLimitedContainer & operator=(std::initializer_list< typename Base::value_type > list)
STL namespace.
SizeLimitedContainer & operator=(const Base &other)
bool operator!=(const Base &other) const noexcept
bool operator<(const Span &other) const noexcept
Base::reference & emplace_back(Args &&...args)
void assign(std::initializer_list< typename Base::value_type > list)
bool operator!=(const Span &other) const noexcept
bool operator==(const Span &other) const noexcept
void reserve(typename Base::size_type size)
SizeLimitedContainer(size_t size, const typename Base::value_type &value)
bool operator==(const Base &other) const noexcept
void resize(typename Base::size_type size, const typename Base::value_type &value)
SizeLimitedContainer & operator=(Base &&other)
Base::iterator insert(Iter pos, typename Base::size_type size, const typename Base::value_type &value)
Base::iterator insert(Iter pos, std::initializer_list< typename Base::value_type > &&list)
Base::iterator insert(OutIt pos, InIt begin, InIt end)
bool operator<(const Base &other) const noexcept
Base::iterator emplace(Iter pos, Args &&...args)
void assign(typename Base::size_type size, const typename Base::value_type &value)
#define unlikely(x)