@@ -23,18 +23,22 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
2323#ifdef ARDUINO
2424#include < Arduino.h>
2525#endif
26+
27+ #if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
2628#include < atomic>
2729#include < memory>
2830#include < algorithm>
2931#include < functional>
32+ using std::min;
33+ #else
34+ #include " ghostl.h"
35+ #endif
3036
3137#if !defined(ESP32) && !defined(ESP8266)
3238#define ICACHE_RAM_ATTR
3339#define IRAM_ATTR
3440#endif
3541
36- using std::min;
37-
3842/* !
3943 @brief Instance class for a single-producer, single-consumer circular queue / ring buffer (FIFO).
4044 This implementation is lock-free between producer and consumer for the available(), peek(),
@@ -123,19 +127,35 @@ class circular_queue
123127 }
124128
125129 /* !
126- @brief Peek at the next element pop returns without removing it from the queue.
127- @return An rvalue copy of the next element that can be popped, or a default
128- value of type T if the queue is empty .
130+ @brief Peek at the next element pop will return without removing it from the queue.
131+ @return An rvalue copy of the next element that can be popped. If the queue is empty,
132+ return an rvalue copy of the element that is pending the next push .
129133 */
130134 T peek () const
131135 {
132- const auto outPos = m_outPos.load (std::memory_order_acquire);
136+ const auto outPos = m_outPos.load (std::memory_order_relaxed);
137+ std::atomic_thread_fence (std::memory_order_acquire);
138+ return m_buffer[outPos];
139+ }
140+
141+ /* !
142+ @brief Peek at the next pending input value.
143+ @return A reference to the next element that can be pushed.
144+ */
145+ T& IRAM_ATTR pushpeek ()
146+ {
133147 const auto inPos = m_inPos.load (std::memory_order_relaxed);
134148 std::atomic_thread_fence (std::memory_order_acquire);
135- if (inPos == outPos) return defaultValue;
136- else return m_buffer[outPos];
149+ return m_buffer[inPos];
137150 }
138151
152+ /* !
153+ @brief Release the next pending input value, accessible by pushpeek(), into the queue.
154+ @return true if the queue accepted the value, false if the queue
155+ was full.
156+ */
157+ bool IRAM_ATTR push ();
158+
139159 /* !
140160 @brief Move the rvalue parameter into the queue.
141161 @return true if the queue accepted the value, false if the queue
@@ -153,13 +173,15 @@ class circular_queue
153173 return push (T (val));
154174 }
155175
176+ #if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
156177 /* !
157178 @brief Push copies of multiple elements from a buffer into the queue,
158179 in order, beginning at buffer's head.
159180 @return The number of elements actually copied into the queue, counted
160181 from the buffer head.
161182 */
162183 size_t push_n (const T* buffer, size_t size);
184+ #endif
163185
164186 /* !
165187 @brief Pop the next available element from the queue.
@@ -168,31 +190,46 @@ class circular_queue
168190 */
169191 T pop ();
170192
193+ #if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
171194 /* !
172195 @brief Pop multiple elements in ordered sequence from the queue to a buffer.
196+ If buffer is nullptr, simply discards up to size elements from the queue.
173197 @return The number of elements actually popped from the queue to
174198 buffer.
175199 */
176200 size_t pop_n (T* buffer, size_t size);
201+ #endif
177202
178203 /* !
179204 @brief Iterate over and remove each available element from queue,
180205 calling back fun with an rvalue reference of every single element.
181206 */
207+ #if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
182208 void for_each (const std::function<void (T&&)>& fun);
209+ #else
210+ void for_each (std::function<void (T&&)> fun);
211+ #endif
183212
184213 /* !
185214 @brief In reverse order, iterate over, pop and optionally requeue each available element from the queue,
186215 calling back fun with a reference of every single element.
187216 Requeuing is dependent on the return boolean of the callback function. If it
188217 returns true, the requeue occurs.
189218 */
219+ #if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
190220 bool for_each_rev_requeue (const std::function<bool (T&)>& fun);
221+ #else
222+ bool for_each_rev_requeue (std::function<bool (T&)> fun);
223+ #endif
191224
192225protected:
193226 const T defaultValue = {};
194227 unsigned m_bufSize;
195- std::unique_ptr<T[] > m_buffer;
228+ #if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
229+ std::unique_ptr<T[]> m_buffer;
230+ #else
231+ std::unique_ptr<T> m_buffer;
232+ #endif
196233 std::atomic<unsigned > m_inPos;
197234 std::atomic<unsigned > m_outPos;
198235};
@@ -212,6 +249,21 @@ bool circular_queue<T>::capacity(const size_t cap)
212249 return true ;
213250}
214251
252+ template < typename T >
253+ bool IRAM_ATTR circular_queue<T>::push()
254+ {
255+ const auto inPos = m_inPos.load (std::memory_order_acquire);
256+ const unsigned next = (inPos + 1 ) % m_bufSize;
257+ if (next == m_outPos.load (std::memory_order_relaxed)) {
258+ return false ;
259+ }
260+
261+ std::atomic_thread_fence (std::memory_order_acquire);
262+
263+ m_inPos.store (next, std::memory_order_release);
264+ return true ;
265+ }
266+
215267template < typename T >
216268bool IRAM_ATTR circular_queue<T>::push(T&& val)
217269{
@@ -231,6 +283,7 @@ bool IRAM_ATTR circular_queue<T>::push(T&& val)
231283 return true ;
232284}
233285
286+ #if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
234287template < typename T >
235288size_t circular_queue<T>::push_n(const T* buffer, size_t size)
236289{
@@ -256,6 +309,7 @@ size_t circular_queue<T>::push_n(const T* buffer, size_t size)
256309 m_inPos.store (next, std::memory_order_release);
257310 return blockSize + size;
258311}
312+ #endif
259313
260314template < typename T >
261315T circular_queue<T>::pop()
@@ -273,6 +327,7 @@ T circular_queue<T>::pop()
273327 return val;
274328}
275329
330+ #if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
276331template < typename T >
277332size_t circular_queue<T>::pop_n(T* buffer, size_t size) {
278333 size_t avail = size = min (size, available ());
@@ -282,18 +337,25 @@ size_t circular_queue<T>::pop_n(T* buffer, size_t size) {
282337
283338 std::atomic_thread_fence (std::memory_order_acquire);
284339
285- buffer = std::copy_n (std::make_move_iterator (m_buffer.get () + outPos), n, buffer);
286- avail -= n;
287- std::copy_n (std::make_move_iterator (m_buffer.get ()), avail, buffer);
340+ if (buffer) {
341+ buffer = std::copy_n (std::make_move_iterator (m_buffer.get () + outPos), n, buffer);
342+ avail -= n;
343+ std::copy_n (std::make_move_iterator (m_buffer.get ()), avail, buffer);
344+ }
288345
289346 std::atomic_thread_fence (std::memory_order_release);
290347
291348 m_outPos.store ((outPos + size) % m_bufSize, std::memory_order_release);
292349 return size;
293350}
351+ #endif
294352
295353template < typename T >
354+ #if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
296355void circular_queue<T>::for_each(const std::function<void (T&&)>& fun)
356+ #else
357+ void circular_queue<T>::for_each(std::function<void (T&&)> fun)
358+ #endif
297359{
298360 auto outPos = m_outPos.load (std::memory_order_acquire);
299361 const auto inPos = m_inPos.load (std::memory_order_relaxed);
@@ -308,7 +370,11 @@ void circular_queue<T>::for_each(const std::function<void(T&&)>& fun)
308370}
309371
310372template < typename T >
373+ #if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO)
311374bool circular_queue<T>::for_each_rev_requeue(const std::function<bool (T&)>& fun)
375+ #else
376+ bool circular_queue<T>::for_each_rev_requeue(std::function<bool (T&)> fun)
377+ #endif
312378{
313379 auto inPos0 = circular_queue<T>::m_inPos.load (std::memory_order_acquire);
314380 auto outPos = circular_queue<T>::m_outPos.load (std::memory_order_relaxed);
0 commit comments