Roc Toolkit internal modules
Roc Toolkit: real-time audio streaming
ring_queue.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2024 Roc Streaming authors
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7  */
8 
9 //! @file roc_core/ring_queue.h
10 //! @brief Queue on continuous memory buffer.
11 
12 #ifndef ROC_CORE_RING_QUEUE_H_
13 #define ROC_CORE_RING_QUEUE_H_
14 
15 #include "roc_core/array.h"
16 #include "roc_core/iarena.h"
17 #include "roc_core/log.h"
18 #include "roc_core/noncopyable.h"
19 #include "roc_core/panic.h"
20 
21 namespace roc {
22 namespace core {
23 
24 //! Queue on continuous memory buffer.
25 //!
26 //! Elements are stored continuously in a memory chunk allocated using IArena,
27 //! or directly in Array object when number of elements is small.
28 //!
29 //! RingQueue supports inserting and removing elements to the beginning and to
30 //! the end with O(1) complexity.
31 //!
32 //! @tparam T defines array element type. It should have default constructor
33 //! and copy constructor.
34 //!
35 //! @tparam EmbeddedCapacity defines number of elements in the fixed-size chunk
36 //! embedded directly into RingQueue object.
37 template <class T, size_t EmbeddedCapacity = 0> class RingQueue : public NonCopyable<> {
38 public:
39  //! Initialize.
40  //! @remarks
41  //! Preallocate buffer in @p arena with @p max_len number of elements.
42  RingQueue(core::IArena& arena, size_t max_len)
43  : buff_(NULL)
44  , buff_len_(max_len)
45  , begin_(0)
46  , end_(0)
47  , arena_(arena) {
48  if (max_len == 0) {
49  roc_panic("ring queue: the length must be greater than 0");
50  }
51 
52  buff_ = allocate_(max_len);
53  }
54 
55  ~RingQueue() {
56  if (buff_) {
57  deallocate_(buff_);
58  }
59  }
60 
61  //! Check that initial allocation succeeded.
62  bool is_valid() const {
63  return buff_ != NULL;
64  }
65 
66  //! Get maximum number of elements in queue/
67  size_t capacity() const {
68  return buff_len_;
69  }
70 
71  //! Get current number of elements in the queue.
72  size_t size() const {
73  return (end_ - begin_ + buff_len_) % buff_len_;
74  }
75 
76  //! Is the queue empty.
77  bool is_empty() {
78  return begin_ == end_;
79  }
80 
81  //! Is the queue full.
82  bool is_full() {
83  return size() == capacity();
84  }
85 
86  //! Get reference of the front element.
87  //! @pre
88  //! Queue should not be empty.
89  T& front() {
90  if (is_empty()) {
91  roc_panic("ring queue: front() called on empty buffer");
92  }
93  return buff_[begin_];
94  }
95 
96  //! Get reference of the front element.
97  //! @pre
98  //! Queue should not be empty.
99  const T& front() const {
100  if (is_empty()) {
101  roc_panic("ring queue: front() called on empty buffer");
102  }
103  return buff_[begin_];
104  }
105 
106  //! Get reference of the back element.
107  //! @pre
108  //! Queue should not be empty.
109  T& back() {
110  if (is_empty()) {
111  roc_panic("ring queue: back() called on empty buffer");
112  }
113  return buff_[(end_ - 1 + buff_len_) % buff_len_];
114  }
115 
116  //! Get reference of the back element.
117  //! @pre
118  //! Queue should not be empty.
119  const T& back() const {
120  if (is_empty()) {
121  roc_panic("ring queue: back() called on empty buffer");
122  }
123  return buff_[(end_ - 1 + buff_len_) % buff_len_];
124  }
125 
126  //! Push an element to the front of the queue.
127  //! @pre
128  //! Queue should not be full.
129  void push_front(const T& x) {
130  if (is_full()) {
131  roc_panic("ring queue: buffer overflow");
132  }
133  begin_ = (begin_ - 1 + buff_len_) % buff_len_;
134  new (&buff_[begin_]) T(x);
135  }
136 
137  //! Remove the first element from the front.
138  //! @pre
139  //! Queue should not be empty.
140  void pop_front() {
141  if (is_empty()) {
142  roc_panic("ring queue: pop_front() called on empty buffer");
143  }
144  buff_[begin_].~T();
145  begin_ = (begin_ + 1) % buff_len_;
146  }
147 
148  //! Push an element to the backside of the queue.
149  //! @pre
150  //! Queue should not be full.
151  void push_back(const T& x) {
152  if (is_full()) {
153  roc_panic("ring queue: buffer overflow");
154  }
155  new (&buff_[end_]) T(x);
156  end_ = (end_ + 1) % buff_len_;
157  }
158 
159  //! Remove the first element from the back.
160  //! @pre
161  //! Queue should not be empty.
162  void pop_back() {
163  if (is_empty()) {
164  roc_panic("ring queue: pop_back() called on empty buffer");
165  }
166  buff_[end_].~T();
167  end_ = (end_ - 1 + buff_len_) % buff_len_;
168  }
169 
170 private:
171  T* allocate_(size_t n_elems) {
172  T* data = NULL;
173 
174  if (n_elems <= EmbeddedCapacity) {
175  data = (T*)embedded_data_.memory();
176  } else {
177  data = (T*)arena_.allocate(n_elems * sizeof(T));
178  }
179 
180  if (!data) {
182  "ring queue: can't allocate memory:"
183  " requested_cap=%lu embedded_cap=%lu",
184  (unsigned long)n_elems, (unsigned long)EmbeddedCapacity);
185  }
186 
187  return data;
188  }
189 
190  void deallocate_(T* data) {
191  if ((void*)data != (void*)embedded_data_.memory()) {
192  arena_.deallocate(data);
193  }
194  }
195 
196  T* buff_;
197  size_t buff_len_;
198  size_t begin_;
199  size_t end_;
200 
201  AlignedStorage<EmbeddedCapacity * sizeof(T)> embedded_data_;
202  IArena& arena_;
203 };
204 
205 } // namespace core
206 } // namespace roc
207 
208 #endif // ROC_CORE_RING_QUEUE_H_
Dynamic array.
Memory arena interface.
Definition: iarena.h:23
virtual void deallocate(void *ptr)=0
Deallocate previously allocated memory.
virtual void * allocate(size_t size)=0
Allocate memory.
Base class for non-copyable objects.
Definition: noncopyable.h:23
Queue on continuous memory buffer.
Definition: ring_queue.h:37
bool is_valid() const
Check that initial allocation succeeded.
Definition: ring_queue.h:62
const T & back() const
Get reference of the back element.
Definition: ring_queue.h:119
RingQueue(core::IArena &arena, size_t max_len)
Initialize.
Definition: ring_queue.h:42
void push_front(const T &x)
Push an element to the front of the queue.
Definition: ring_queue.h:129
T & front()
Get reference of the front element.
Definition: ring_queue.h:89
bool is_full()
Is the queue full.
Definition: ring_queue.h:82
void pop_back()
Remove the first element from the back.
Definition: ring_queue.h:162
bool is_empty()
Is the queue empty.
Definition: ring_queue.h:77
size_t capacity() const
Get maximum number of elements in queue/.
Definition: ring_queue.h:67
const T & front() const
Get reference of the front element.
Definition: ring_queue.h:99
T & back()
Get reference of the back element.
Definition: ring_queue.h:109
void pop_front()
Remove the first element from the front.
Definition: ring_queue.h:140
size_t size() const
Get current number of elements in the queue.
Definition: ring_queue.h:72
void push_back(const T &x)
Push an element to the backside of the queue.
Definition: ring_queue.h:151
Memory arena interface.
Logging.
#define roc_log(level,...)
Print message to log.
Definition: log.h:31
Root namespace.
@ LogError
Error message.
Definition: log.h:45
Non-copyable object.
Panic.
#define roc_panic(...)
Print error message and terminate program gracefully.
Definition: panic.h:50