queue.hpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. // SPDX-License-Identifier: MIT
  2. // SPDX-FileCopyrightText: 2022-2024 Ivan Baidakou
  3. #pragma once
  4. #include "message.hpp"
  5. #include <cassert>
  6. #include <cstddef>
  7. #include <limits>
  8. #include <tuple>
  9. #include <type_traits>
  10. #include <utility>
  11. namespace rotor_light {
  12. struct ItemQueueBase;
  13. /** \struct ItemGuard
  14. * \brief Message removal helper after message delivery
  15. */
  16. struct ItemGuard {
  17. ItemGuard() : message{nullptr} {}
  18. /** records message for removal */
  19. ItemGuard(Message *message_, ItemQueueBase *item_queue_)
  20. : message{message_}, item_queue{item_queue_} {}
  21. ItemGuard(const ItemGuard &) = delete;
  22. ItemGuard(ItemGuard &&) = delete;
  23. /** copy-assignment operator */
  24. ItemGuard &operator=(const ItemGuard &) = default;
  25. /** returns true if the guard holds a message */
  26. inline operator bool() { return message; }
  27. /** derefers to a message */
  28. inline Message &operator*() { return *message; }
  29. /** guarded message */
  30. Message *message;
  31. /** message origin/queue */
  32. ItemQueueBase *item_queue;
  33. };
  34. /** \struct ItemQueueBase
  35. * \brief base class for queues.
  36. *
  37. * Item = Message
  38. */
  39. struct ItemQueueBase {
  40. /** initializes queue with the message size and maximum amount of messsages */
  41. ItemQueueBase(size_t item_size, size_t items_count);
  42. ItemQueueBase(const ItemQueueBase &) = delete;
  43. ItemQueueBase(ItemQueueBase &&) = delete;
  44. /** returns the next stored message if there is one; otherwise nullptr
  45. * is returned */
  46. Message *next();
  47. /** constructs in-place new message by forwarding args into
  48. * message ctor.
  49. *
  50. * returns true if message was successfully inserted
  51. */
  52. template <typename MessageType, bool force, typename... Args>
  53. bool put(Args &&...args) {
  54. static_assert(std::is_base_of<Message, MessageType>::value,
  55. "type should be inherited from Message");
  56. assert((sizeof(MessageType) <= item_size) &&
  57. "no storage for the message, increase ItemSize");
  58. bool full = (items_size == items_count);
  59. bool r;
  60. if constexpr (!force) {
  61. if (full) {
  62. return false;
  63. }
  64. auto ptr = buff_ptr + item_size * free_index;
  65. if (free_index == items_size - 1) {
  66. free_index = 0;
  67. } else {
  68. ++free_index;
  69. }
  70. auto message = new (ptr) MessageType(std::forward<Args>(args)...);
  71. message->type = MessageType::type_id;
  72. ++items_count;
  73. r = true;
  74. } else {
  75. auto ptr = buff_ptr + item_size * free_index;
  76. if (!full) {
  77. ++items_count;
  78. if (free_index == items_size - 1) {
  79. free_index = 0;
  80. } else {
  81. ++free_index;
  82. }
  83. } else {
  84. ++free_index;
  85. if (occupied_index + 1 == items_size) {
  86. occupied_index = 0;
  87. } else {
  88. ++occupied_index;
  89. }
  90. }
  91. auto message = new (ptr) MessageType(std::forward<Args>(args)...);
  92. message->type = MessageType::type_id;
  93. r = full;
  94. }
  95. #ifdef ROTOR_LIGHT_QUEUE_SZ
  96. if (items_count_maxinum < items_count) {
  97. items_count_maxinum = items_count;
  98. }
  99. #endif
  100. return r;
  101. }
  102. #ifdef ROTOR_LIGHT_QUEUE_SZ
  103. /** returns maximum items count during queue lifetime */
  104. Index max_items() const;
  105. #endif
  106. /** deletes the last message from the queue */
  107. void release();
  108. /** \brief generic non-public fields accessor */
  109. template <typename T> auto &access() noexcept;
  110. protected:
  111. friend struct Message;
  112. /** records buffer pointer */
  113. void post_constructor(char *buff);
  114. private:
  115. char *buff_ptr;
  116. Index item_size;
  117. Index items_size;
  118. Index items_count;
  119. Index free_index;
  120. Index occupied_index;
  121. #ifdef ROTOR_LIGHT_QUEUE_SZ
  122. Index items_count_maxinum;
  123. #endif
  124. };
  125. /** \struct ItemQueue
  126. * \brief convenient helper for storing fixed amount of messages
  127. */
  128. template <typename Storage, size_t ItemsCount>
  129. struct ItemQueue : ItemQueueBase {
  130. static_assert(Storage::item_size > 0, "queue size have to be positive");
  131. ItemQueue() : ItemQueueBase(Storage::item_size, ItemsCount) {
  132. post_constructor(reinterpret_cast<char *>(&storage));
  133. }
  134. private:
  135. using Item = typename Storage::Item;
  136. Item storage[ItemsCount];
  137. };
  138. /** \struct QueueBase
  139. * \brief base class for meta-queue (list of queues, ordered by priorities))
  140. */
  141. struct QueueBase {
  142. /** records storage of all sub-queues and their amount */
  143. QueueBase(ItemQueueBase **queues, size_t queue_count);
  144. QueueBase(const QueueBase &) = delete;
  145. QueueBase(QueueBase &&) = delete;
  146. /** constructs in-place new message by forwarding args into
  147. * message ctor.
  148. *
  149. * returns `true` if message is successfully created
  150. **/
  151. template <typename MessageType, bool force, typename... Args>
  152. bool put(Index queue_index, Args &&...args) {
  153. assert((queue_index < queue_count) && "valid queue/priority");
  154. auto &queue = queues[queue_index];
  155. return queue->put<MessageType, force>(std::forward<Args>(args)...);
  156. }
  157. #ifdef ROTOR_LIGHT_QUEUE_SZ
  158. /** returns maximum items count during queue lifetime */
  159. Index max_items(Index queue_index) const;
  160. #endif
  161. /** returns item-guard for the next message. Queues priorities are taken
  162. * into the account */
  163. ItemGuard next();
  164. /** \brief generic non-public fields accessor */
  165. template <typename T> auto &access() noexcept;
  166. private:
  167. ItemQueueBase **queues;
  168. uint8_t queue_count;
  169. };
  170. /** \struct Queue
  171. * \brief conveninent helper for meta-queue (list of queues, ordered by
  172. * priorities)
  173. */
  174. template <typename Storage, size_t... Counts> struct Queue : QueueBase {
  175. static_assert(Storage::item_size > sizeof(Message),
  176. "not enough storage for a message");
  177. /** tuple alias to store different queues */
  178. using Queues = std::tuple<ItemQueue<Storage, Counts>...>;
  179. /** the total amount of sub-queues */
  180. static constexpr size_t Size = std::tuple_size<Queues>::value;
  181. static_assert(Size > 0, "there should be at least one queue");
  182. Queue() : QueueBase(queues, Size) { fill_queue<0>(); }
  183. private:
  184. template <Index Index> void fill_queue() {
  185. auto &queue = std::get<Index>(queues_storage);
  186. queues[Index] = &queue;
  187. if constexpr (Index + 1 < Size) {
  188. fill_queue<Index + 1>();
  189. }
  190. }
  191. Queues queues_storage;
  192. ItemQueueBase *queues[Size];
  193. };
  194. } // namespace rotor_light