pabigot  0.1.1
C++ support classes
container.hpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: Apache-2.0 */
2 /* Copyright 2015-2019 Peter A. Bigot */
3 
10 #ifndef PABIGOT_CONTAINER_HPP
11 #define PABIGOT_CONTAINER_HPP
12 #pragma once
13 
14 #include <array>
15 #include <cinttypes>
16 #include <functional>
17 #include <limits>
18 
19 #include <pabigot/common.hpp>
20 
21 namespace pabigot {
22 
24 namespace container {
25 
34 template <typename T = uint8_t>
36 {
37 public:
39  using value_type = T;
40 
48  using size_type = uint16_t;
49 
51  using ssize_type = std::make_signed<size_type>::type;
52 
54  static constexpr size_type EMPTY_HEAD = -1;
55 
63  constexpr rr_adaptor (value_type* data,
64  size_type count) :
65  data_{data},
66  count_{count}
67  { }
68 
69  rr_adaptor () = delete;
70  rr_adaptor (const rr_adaptor&) = delete;
71  rr_adaptor& operator= (const rr_adaptor&) = delete;
72  rr_adaptor (rr_adaptor&&) = delete;
73  rr_adaptor& operator= (rr_adaptor&&) = delete;
74 
76  bool empty () const noexcept
77  {
78  return EMPTY_HEAD == head_;
79  }
80 
83  bool full () const noexcept
84  {
85  return size() == max_size();
86  }
87 
92  size_type max_size () const noexcept
93  {
94  return count_;
95  }
96 
98  size_type size () const noexcept
99  {
100  size_type rv = 0;
101  if (!empty()) {
102  /* Get the signed difference between the head and tail. Negative means the
103  * range wraps. */
104  ssize_type n = head_ - tail_;
105  if (0 >= n) {
106  n += count_;
107  }
108  rv = n;
109  }
110  return rv;
111  }
112 
120  bool push (value_type v) noexcept
121  {
122  bool rv = false;
123 
124  if (empty()) {
125  head_ = tail_ = 0;
126  } else if (tail_ == head_) {
127  rv = true;
128  (void)pop();
129  }
130  auto nh = next_index_(head_);
131  data_[head_] = v;
132  head_ = nh;
133  return rv;
134  }
135 
140  value_type pop () noexcept
141  {
142  if (empty()) {
143  return value_type{};
144  }
145  size_type tail = tail_;
146  tail_ = next_index_(tail_);
147  if (head_ == tail_) {
148  head_ = EMPTY_HEAD;
149  }
150  return data_[tail];
151  }
152 
154  void clear () noexcept
155  {
156  head_ = EMPTY_HEAD;
157  }
158 
159 private:
160  size_type next_index_ (size_type v) const
161  {
162  if (++v >= count_) {
163  v = 0;
164  }
165  return v;
166  }
167 
168  value_type* data_;
169  size_type const count_;
170  size_type head_ = EMPTY_HEAD;
171  size_type tail_ = 0;
172 };
173 
238 template <typename T,
239  typename REF_NEXT>
241 {
242 public:
245 
247  using value_type = T;
248 
251 
253  using predicate_type = std::function<bool(const value_type&)>;
254 
256  static constexpr uintptr_t UNLINKED_PTR = -1;
257 
259  static pointer_type unlinked_ptr () noexcept
260  {
261  return reinterpret_cast<pointer_type>(UNLINKED_PTR);
262  }
263 
265  bool is_unlinked (pointer_type ptr) const noexcept
266  {
267  return unlinked_ptr() == ptr;
268  }
269 
271  bool is_unlinked (value_type& value) const noexcept
272  {
273  return is_unlinked(ref_next(value));
274  }
275 
276  /* You can default-construct these. */
277  forward_chain () noexcept = default;
278 
279  /* You can't copy them. */
280  forward_chain (const forward_chain&) = delete;
281  forward_chain& operator= (const forward_chain&) = delete;
282 
283  /* You can move them. */
284  forward_chain (forward_chain&& from) noexcept :
285  front_{from.front_},
286  back_{from.back_}
287  {
288  from.front_ = from.back_ = nullptr;
289  }
290 
291  forward_chain& operator= (forward_chain&& from) noexcept
292  {
293  front_ = from.front_;
294  back_ = from.back_;
295  from.front_ = from.back_ = nullptr;
296  return *this;
297  }
298 
300  class end_iterator_type { };
301 
316  {
317  friend chain_type;
318 
319  chain_iterator_type (const chain_type& chain) :
320  chain{chain},
321  current{chain.front_},
322  next{current ? chain.ref_next(*current) : nullptr}
323  { }
324 
325  const chain_type& chain;
326 
328  pointer_type current = nullptr;
329 
334  pointer_type next = nullptr;
335 
336  public:
339  {
340  return !current;
341  }
342 
347  {
348  return !operator==(rhs);
349  }
350 
353  {
354  current = next;
355  if (current) {
356  next = chain.ref_next(*current);
357  /* Avoid segfault if somebody removed a value that we iterated into.
358  * See RangeFor whitebox testing. */
359  if (chain.is_unlinked(next)) {
360  next = nullptr;
361  }
362  }
363  return *this;
364  }
365 
370  value_type& operator* () noexcept
371  {
372  return *current;
373  }
374  };
375 
377  chain_iterator_type begin () const noexcept
378  {
379  return {*this};
380  }
381 
384  end_iterator_type end () const noexcept
385  {
386  return {};
387  }
388 
390  bool empty () const noexcept
391  {
392  return !front_;
393  }
394 
396  pointer_type front () const noexcept
397  {
398  return front_;
399  }
400 
407  pointer_type next (value_type& elt) const noexcept
408  {
409  return ref_next(elt);
410  }
411 
413  pointer_type back () const noexcept
414  {
415  return back_;
416  }
417 
421  void link_front (value_type &value) noexcept
422  {
423  if (empty()) {
424  back_ = &value;
425  }
426  ref_next(value) = front_;
427  front_ = &value;
428  }
429 
432  {
433  auto rv = front_;
434  if (rv) {
435  auto& next = ref_next(*rv);
436  front_ = next;
437  next = unlinked_ptr();
438  if (back_ == rv) {
439  back_ = nullptr;
440  }
441  }
442  return rv;
443  }
444 
449  void link_after (value_type &pos,
450  value_type &value) noexcept
451  {
452  ref_next(value) = ref_next(pos);
453  ref_next(pos) = &value;
454  if (back_ == &pos) {
455  back_ = &value;
456  }
457  }
458 
469  void link_before (value_type &value,
470  predicate_type pred) noexcept
471  {
472  auto npp = &front_;
473  while (*npp) {
474  auto np = *npp;
475  if (pred(*np)) {
476  ref_next(value) = np;
477  *npp = &value;
478  return;
479  }
480  npp = &ref_next(*np);
481  }
482  return link_back(value);
483  }
484 
497  {
498  if (empty()
499  || (!pred(*front_))) {
500  return {};
501  }
502  auto prev = front_;
503  auto npp = &ref_next(*prev);
504  while ((*npp) && pred(**npp)) {
505  prev = *npp;
506  npp = &ref_next(*prev);
507  };
508  auto front = front_;
509  front_ = *npp;
510  if (!front_) {
511  back_ = nullptr;
512  }
513  *npp = nullptr;
514  return {front, prev};
515  }
516 
520  void link_back (value_type &value) noexcept
521  {
522  if (empty()) {
523  return link_front(value);
524  }
525  ref_next(value) = nullptr;
526  ref_next(*back_) = &value;
527  back_ = &value;
528  }
529 
536  pointer_type unlink (value_type& value) noexcept
537  {
538  auto lpp = &front_;
539  pointer_type prev = nullptr;
540 
541  while (*lpp) {
542  auto lp = *lpp;
543  if (&value == lp) {
544  auto& next = ref_next(value);
545  *lpp = next;
546  next = unlinked_ptr();
547  if (&value == back_) {
548  back_ = prev;
549  }
550  return &value;
551  }
552  prev = lp;
553  lpp = &ref_next(*lp);
554  }
555  return nullptr;
556  }
557 
559  void clear () noexcept
560  {
561  auto p = front_;
562  while (p) {
563  auto& next = ref_next(*p);
564  p = next;
565  next = unlinked_ptr();
566  }
567  front_ = back_ = nullptr;
568  }
569 
570 private:
573  pointer_type back) noexcept :
574  front_{front},
575  back_{back}
576  { }
577 
580  pointer_type& ref_next (value_type & elt) const noexcept
581  {
582  return REF_NEXT{}(elt);
583  }
584 
586  pointer_type front_{};
587 
589  pointer_type back_{};
590 };
591 
592 } // ns container
593 
594 } // ns pabigot
595 
596 #endif /* PABIGOT_CONTAINER_HPP */
pabigot::container::forward_chain::link_front
void link_front(value_type &value) noexcept
Add the value to the front of the sequence.
Definition: container.hpp:421
pabigot::container::forward_chain::UNLINKED_PTR
static constexpr uintptr_t UNLINKED_PTR
Integral value of an invalid pointer used to denote unlinked objects.
Definition: container.hpp:256
pabigot::container::rr_adaptor::size_type
uint16_t size_type
Type alias for representing the size of the buffer.
Definition: container.hpp:48
pabigot::container::rr_adaptor::EMPTY_HEAD
static constexpr size_type EMPTY_HEAD
Marker value stored in #head_ to indicate that the buffer is empty.
Definition: container.hpp:54
pabigot::container::rr_adaptor::push
bool push(value_type v) noexcept
Push a new value at the front of the buffer.
Definition: container.hpp:120
pabigot::container::forward_chain::unlink_front
pointer_type unlink_front() noexcept
Remove and return a pointer to the first value of the sequence.
Definition: container.hpp:431
pabigot::container::forward_chain::link_after
void link_after(value_type &pos, value_type &value) noexcept
Add the value immediately after an value already in the sequence.
Definition: container.hpp:449
common.hpp
General-use material that applies to all of pabigot.
pabigot::container::forward_chain::link_back
void link_back(value_type &value) noexcept
Add the value to the end of the sequence.
Definition: container.hpp:520
pabigot::container::forward_chain::chain_iterator_type::operator==
bool operator==(const end_iterator_type &)
Return true iff this iterator is past the end of its chain.
Definition: container.hpp:338
pabigot::container::forward_chain::empty
bool empty() const noexcept
Indicate whether the sequence is empty.
Definition: container.hpp:390
pabigot::container::rr_adaptor::pop
value_type pop() noexcept
Pop a value from the back of the buffer.
Definition: container.hpp:140
pabigot::container::rr_adaptor::size
size_type size() const noexcept
The number of values currently stored in the buffer.
Definition: container.hpp:98
pabigot::container::rr_adaptor::max_size
size_type max_size() const noexcept
The maximum number of values that can be stored in the buffer.
Definition: container.hpp:92
pabigot::container::forward_chain::is_unlinked
bool is_unlinked(pointer_type ptr) const noexcept
Test whether a pointer is unlinked.
Definition: container.hpp:265
pabigot::container::forward_chain::back
pointer_type back() const noexcept
Return a pointer to the last value in the sequence.
Definition: container.hpp:413
pabigot::container::forward_chain::unlink
pointer_type unlink(value_type &value) noexcept
Remove an value from the sequence at any position.
Definition: container.hpp:536
pabigot::container::rr_adaptor::ssize_type
std::make_signed< size_type >::type ssize_type
Type alias for signed size values.
Definition: container.hpp:51
pabigot::container::rr_adaptor::rr_adaptor
constexpr rr_adaptor(value_type *data, size_type count)
Create an adaptor for round-robin access to a fixed buffer.
Definition: container.hpp:63
pabigot::container::forward_chain::chain_iterator_type::operator!=
bool operator!=(const end_iterator_type &rhs)
Minimum operator required for range-for support.
Definition: container.hpp:346
pabigot::container::forward_chain::end_iterator_type
Sentinal type used as end-of-chain iterator value.
Definition: container.hpp:300
pabigot::container::rr_adaptor::clear
void clear() noexcept
Restore the buffer to an empty state.
Definition: container.hpp:154
pabigot::container::forward_chain::pointer_type
value_type * pointer_type
The type for a pointer to value_type.
Definition: container.hpp:250
pabigot::container::forward_chain::link_before
void link_before(value_type &value, predicate_type pred) noexcept
Add the value immediately before the first value in the sequence for which pred is true.
Definition: container.hpp:469
pabigot::container::forward_chain::chain_iterator_type
Iterator type designed to support range-for.
Definition: container.hpp:315
pabigot::container::rr_adaptor::value_type
T value_type
Type alias for the elements of the buffer.
Definition: container.hpp:39
pabigot::container::rr_adaptor
A basic round-robin (circular) homogeneous buffer with externally-allocated capacity.
Definition: container.hpp:35
pabigot
Root for all pabigot namespaces.
Definition: gap.hpp:15
pabigot::container::forward_chain::next
pointer_type next(value_type &elt) const noexcept
Return a pointer to the next value in the sequence.
Definition: container.hpp:407
pabigot::container::forward_chain::chain_iterator_type::operator++
chain_iterator_type & operator++()
Increment to reference the cached successor of the current.
Definition: container.hpp:352
pabigot::container::forward_chain::value_type
T value_type
The type of object linked by this chain.
Definition: container.hpp:247
pabigot::container::forward_chain::begin
chain_iterator_type begin() const noexcept
Get an iterator that starts at the beginning of the chain.
Definition: container.hpp:377
pabigot::container::forward_chain::is_unlinked
bool is_unlinked(value_type &value) const noexcept
Test whether a value is unlinked.
Definition: container.hpp:271
pabigot::container::forward_chain::split_through
chain_type split_through(predicate_type pred) noexcept
Strip off a chain of all leading elements that satisfy a predicate.
Definition: container.hpp:496
pabigot::container::rr_adaptor::empty
bool empty() const noexcept
true iff the buffer has no data in it.
Definition: container.hpp:76
pabigot::container::forward_chain::front
pointer_type front() const noexcept
Return a pointer to the first value in the sequence.
Definition: container.hpp:396
pabigot::container::forward_chain::clear
void clear() noexcept
Remove all values from the sequence.
Definition: container.hpp:559
pabigot::container::forward_chain::predicate_type
std::function< bool(const value_type &)> predicate_type
The type for a function that tests a value_type for a condition.
Definition: container.hpp:253
pabigot::container::forward_chain::unlinked_ptr
static pointer_type unlinked_ptr() noexcept
Test whether a pointer is unlinked.
Definition: container.hpp:259
pabigot::container::forward_chain::end
end_iterator_type end() const noexcept
Get a value end for which iter != end will return false only when iter is a chain_iterator_type that ...
Definition: container.hpp:384
pabigot::container::forward_chain
Container used to link objects into a sequence.
Definition: container.hpp:240
pabigot::container::forward_chain::chain_iterator_type::operator*
value_type & operator*() noexcept
Get a reference to the value in the chain.
Definition: container.hpp:370
pabigot::container::rr_adaptor::full
bool full() const noexcept
true if the buffer cannot receive more data without discarding an element.
Definition: container.hpp:83