Micro template library A library for building device drivers
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 

226 lignes
9.8 KiB

  1. /*!
  2. * \file container/deque.h
  3. * \brief
  4. * A statically allocated deque based on a ring buffer.
  5. *
  6. * \copyright Copyright (C) 2021 Christos Choutouridis <christos@choutouridis.net>
  7. *
  8. * <dl class=\"section copyright\"><dt>License</dt><dd>
  9. * The MIT License (MIT)
  10. *
  11. * Permission is hereby granted, free of charge, to any person obtaining a copy
  12. * of this software and associated documentation files (the "Software"), to deal
  13. * in the Software without restriction, including without limitation the rights
  14. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  15. * copies of the Software, and to permit persons to whom the Software is
  16. * furnished to do so, subject to the following conditions:
  17. *
  18. * The above copyright notice and this permission notice shall be included in all
  19. * copies or substantial portions of the Software.
  20. *
  21. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  22. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  23. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  24. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  25. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  26. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  27. * SOFTWARE.
  28. * </dd></dl>
  29. */
  30. #ifndef utl_container_deque_h__
  31. #define utl_container_deque_h__
  32. #include <utl/core/impl.h>
  33. #include <utl/container/ring_iterator.h>
  34. #include <utl/container/range.h>
  35. #include <array>
  36. #include <atomic>
  37. namespace utl {
  38. /*!
  39. * \class deque
  40. * \brief
  41. * A statically allocated deque based on a ring buffer
  42. *
  43. * The deque uses two ring_iterators one for the front and one for the rear. The iterators
  44. * are pointing to the next available spot, not on the last inserted spot. This way at the
  45. * initialization the iterators wont "pretend" to point to a valid item .
  46. *
  47. * We use a ring buffer of size \c N+1. We start the front iterator at the last location of the buffer
  48. * and the rear on the first. This way when the queue is full the iterators are pointing to the same location.
  49. *
  50. * \tparam Data_t The char-like queued item type. Usually \c char
  51. * \tparam N The size of deque
  52. * \tparam SemiAtomic True for semi-atomic operation. In that case the \c ring_iterator is also atomic.
  53. * \note
  54. * SemiAtomic means it is safe to access different ends from different threads. For example one thread can
  55. * push only from front and another can pop from back to implement a queue.
  56. */
  57. template <typename Data_t, size_t N, bool SemiAtomic =false>
  58. class deque {
  59. public:
  60. // meta-identity type
  61. using type = deque<Data_t, N>;
  62. using buffer_t = std::array<Data_t, N+1>; // We need N+1 spaces ring buffer for N spaces deque
  63. using iterator_t = ring_iterator<Data_t*, N+1, SemiAtomic>;
  64. using range_t = range<iterator_t>;
  65. // STL
  66. using value_type = Data_t;
  67. using reference = Data_t&;
  68. using const_reference = const Data_t&;
  69. using pointer = Data_t*;
  70. using const_pointer = const Data_t*;
  71. using iterator = iterator_t;
  72. using const_iterator = const iterator_t;
  73. using reverse_iterator = std::reverse_iterator<iterator>;
  74. using const_reverse_iterator = std::reverse_iterator<const_iterator>;
  75. //! \name Constructor / Destructor
  76. //! @{
  77. public:
  78. //! Default constructor
  79. constexpr deque () noexcept :
  80. data_{},
  81. f{data_.data(), N},
  82. r{data_.data()} {
  83. if constexpr (SemiAtomic)
  84. std::atomic_thread_fence(std::memory_order_release);
  85. }
  86. //! fill contructor
  87. constexpr deque(const Data_t& value) noexcept {
  88. data_.fill(value);
  89. f = iterator(data_.data(), N);
  90. r = iterator(data_.data(), N);
  91. if constexpr (SemiAtomic)
  92. std::atomic_thread_fence(std::memory_order_release);
  93. }
  94. //! Initializer list contructor
  95. template <typename ...It>
  96. constexpr deque(It&& ...it) noexcept :
  97. data_{{std::forward<It>(it)...}},
  98. f(data_.data(), N),
  99. r(data_.data(), sizeof...(It)) {
  100. if constexpr (SemiAtomic)
  101. std::atomic_thread_fence(std::memory_order_release);
  102. }
  103. deque(const deque&) = delete; //!< No copies
  104. deque& operator= (const deque&) = delete; //!< No copy assignments
  105. ~deque () = default; //!< default destructor
  106. //! @}
  107. //! \name Iterators
  108. //! @{
  109. public:
  110. constexpr iterator begin() noexcept { iterator ret = f; return ++ret; }
  111. constexpr const_iterator begin() const noexcept { iterator ret = f; return ++ret; }
  112. constexpr const_iterator cbegin() const noexcept { iterator ret = f; return ++ret; }
  113. constexpr iterator end() noexcept { return r; }
  114. constexpr const_iterator end() const noexcept { return r; }
  115. constexpr const_iterator cend() const noexcept { return r; }
  116. constexpr reverse_iterator rbegin() noexcept { return r; }
  117. constexpr const_reverse_iterator rbegin() const noexcept { return r; }
  118. constexpr const_reverse_iterator crbegin() const noexcept { return r; }
  119. constexpr reverse_iterator rend() noexcept { reverse_iterator ret = f; return ++ret; }
  120. constexpr const_reverse_iterator rend() const noexcept { reverse_iterator ret = f; return ++ret; }
  121. constexpr const_reverse_iterator crend() const noexcept { reverse_iterator ret = f; return ++ret; }
  122. //! @}
  123. //! \name Capacity
  124. //! @{
  125. public:
  126. //! \return The size of the deque. The items currently in queue.
  127. constexpr size_t size() noexcept {
  128. return r - (f +1);
  129. }
  130. constexpr size_t size() const noexcept {
  131. return r - (f +1);
  132. }
  133. //! \return The maximum size of the deque. The items the queue can hold.
  134. constexpr size_t max_size() noexcept { return N; }
  135. //! \return The capacity of the deque. The items the queue can hold.
  136. constexpr size_t capacity() noexcept { return N; }
  137. //! \return True if the deque is empty
  138. constexpr bool empty() noexcept { return size() == 0 ? true : false; }
  139. //! \return True if the deque is full
  140. constexpr bool full() noexcept { return size() == N ? true : false; }
  141. //! @}
  142. //! \name Member access
  143. //! @{
  144. public:
  145. //! \brief Clears-empty the deque and return it to init state, without
  146. //! really deleting the contents.
  147. constexpr void clear() noexcept {
  148. f = iterator_t(data_.data(), N);
  149. r = iterator_t(data_.data());
  150. if constexpr (SemiAtomic)
  151. std::atomic_thread_fence(std::memory_order_release);
  152. }
  153. //! \brief Push an item in the front of the deque
  154. //! \param it The item to push
  155. constexpr void push_front (const Data_t& it) noexcept {
  156. if (full()) return;
  157. *f = it;
  158. --f; // keep this separate for thread safety
  159. }
  160. //! \brief Push an item in the back of the deque
  161. //! \param it The item to push
  162. constexpr void push_back (const Data_t& it) noexcept {
  163. if (full()) return;
  164. *r = it;
  165. ++r; // keep this separate for thread safety
  166. }
  167. //! \brief Extract an item from the front of the deque and remove it from the deque
  168. //! \param it The item to push
  169. constexpr Data_t pop_front () noexcept {
  170. if (empty()) return Data_t{};
  171. return *++f;
  172. }
  173. //! \brief Extract an item from the back of the deque and remove it from the deque
  174. //! \param it The item to push
  175. constexpr Data_t pop_back () noexcept {
  176. if (empty()) return Data_t{};
  177. return *--r;
  178. }
  179. //! \brief Get a reference to the item in the front of the deque without extracting it.
  180. //! \return Reference to the item
  181. constexpr Data_t& front() noexcept { iterator_t it = f; return *++it; }
  182. constexpr const Data_t& front() const noexcept { iterator_t it = f; return *++it; }
  183. //! \brief Get a reference to the item in the front of the deque without extracting it.
  184. //! \return Reference to the item
  185. constexpr Data_t& back() noexcept { iterator_t it = r; return *--it; }
  186. constexpr const Data_t& back() const noexcept { iterator_t it = r; return *--it; }
  187. //! \brief Get a pointer to the begin of the items on the deque
  188. //! \return
  189. constexpr Data_t* data() noexcept { return &front(); }
  190. constexpr const Data_t* data() const noexcept { return &front(); }
  191. //! \brief Get a range for the data in queue
  192. //! \return A begin-end iterator pair struct
  193. constexpr range_t contents () noexcept { iterator_t b = f; return {++b, r}; }
  194. constexpr const range_t contents () const noexcept { iterator_t b = f; return {++b, r}; }
  195. //! @}
  196. private:
  197. buffer_t data_{}; //!< The statically allocated buffer
  198. iterator_t f{data_.data(), N}; //!< A ring iterator for the front (points to the next available location)
  199. iterator_t r{data_.data()}; //!< A ring iterator for the rear (points to the next available location).
  200. };
  201. } // namespace utl
  202. #endif /* utl_container_deque_h__ */