2 * Copyright (C) 2024-2026 David C. Manuelda (StormBytePP)
4 * This file is part of StormByte-Buffer.
6 * StormByte-Buffer is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License version 3
8 * or later, as published by the Free Software Foundation.
10 * StormByte-Buffer is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with StormByte-Buffer. If not, see
17 * <https://www.gnu.org/licenses/lgpl-3.0.html>.
22#include <StormByte/type_traits.hxx>
25#include <condition_variable>
32namespace StormByte::Buffer {
34 * @class Hopper<T>::Implementation
35 * @brief Internal implementation of Hopper queue details.
37 * Handles mutex-protected queue operations, atomic capacity settings,
38 * condition variable notifications, and EoF flags.
40 template<Type::MoveConstructible T>
41 class Hopper<T>::Implementation {
44 * @brief Constructs an unbounded Implementation instance.
46 Implementation() noexcept
47 : m_eof(false), m_wake(nullptr), m_cap(0), m_writers(1) {}
50 * @brief Constructs a bounded Implementation instance.
51 * @param capacity Maximum items allowed.
53 explicit Implementation(std::size_t capacity) noexcept
54 : m_eof(false), m_wake(nullptr), m_cap(capacity), m_writers(1) {}
57 * @brief Destructor. Marks EoF and wakes waiting producers.
59 ~Implementation() noexcept {
60 m_eof.store(true, std::memory_order_release);
65 * @brief Gets capacity ceiling.
66 * @return Capacity value.
68 std::size_t Capacity() const noexcept {
69 return m_cap.load(std::memory_order_acquire);
73 * @brief Sets capacity ceiling.
74 * @param capacity New capacity value.
76 void Capacity(std::size_t capacity) noexcept {
77 m_cap.store(capacity, std::memory_order_release);
82 * @brief Gets item count in queue.
83 * @return Count of items.
85 std::size_t Size() const noexcept {
86 std::lock_guard<std::mutex> lock(m_mutex);
87 return m_items.size();
91 * @brief Checks if bounded bucket is full.
92 * @return true if capacity > 0 and size >= capacity.
94 bool Full() const noexcept {
95 const std::size_t cap = m_cap.load(std::memory_order_acquire);
102 * @brief Enqueues an item, waiting if full.
103 * @param item Item to enqueue.
105 void Push(T item) noexcept {
106 if constexpr (Type::NullablePointer<T>) {
111 std::unique_lock<std::mutex> lock(m_mutex);
112 m_space.wait(lock, [this]() {
113 const std::size_t cap = m_cap.load(std::memory_order_acquire);
115 || m_items.size() < cap
116 || m_eof.load(std::memory_order_acquire);
118 if (m_eof.load(std::memory_order_acquire))
120 m_items.push(std::move(item));
126 * @brief Signals end of production.
128 void Eof() noexcept {
129 m_eof.store(true, std::memory_order_release);
131 m_space.notify_all();
135 * @brief Registers an extra writer.
137 void AddWriter() noexcept {
138 m_writers.fetch_add(1, std::memory_order_acq_rel);
142 * @brief Releases one writer. Last writer force-closes.
144 void CloseWriter() noexcept {
145 unsigned prev = m_writers.load(std::memory_order_acquire);
147 if (m_writers.compare_exchange_weak(prev, prev - 1,
148 std::memory_order_acq_rel, std::memory_order_acquire)) {
157 * @brief Pops next item from queue without waiting.
158 * @return Next item, or default T if empty.
163 std::lock_guard<std::mutex> lock(m_mutex);
166 item = std::move(m_items.front());
169 m_space.notify_one();
174 * @brief Checks if Eof was signaled.
175 * @return true if Eof set.
177 bool EoF() const noexcept {
178 return m_eof.load(std::memory_order_acquire);
182 * @brief Checks if queue is empty.
183 * @return true if empty.
185 bool Empty() const noexcept {
186 std::lock_guard<std::mutex> lock(m_mutex);
187 return m_items.empty();
190 void Notify(std::condition_variable& wake) noexcept {
191 m_wake.store(&wake, std::memory_order_release);
194 void Unnotify() noexcept {
195 m_wake.store(nullptr, std::memory_order_release);
200 * @brief Notifies registered consumer condition variable if set.
202 void SignalConsumer() noexcept {
203 std::condition_variable* wake = m_wake.load(std::memory_order_acquire);
209 mutable std::mutex m_mutex; ///< Guards queue access.
210 std::condition_variable m_space; ///< Producer wait condition when full.
211 std::queue<T> m_items; ///< Queue of stored items.
212 std::atomic<bool> m_eof; ///< End of production flag.
213 std::atomic<std::condition_variable*> m_wake; ///< Consumer condition variable.
214 std::atomic<std::size_t> m_cap; ///< Capacity ceiling (0 = unbounded).
215 std::atomic<unsigned> m_writers; ///< Live writers; last CloseWriter Eofs.
218 template<Type::MoveConstructible T>
219 Hopper<T>::Hopper() noexcept
220 : m_impl(std::make_unique<Implementation>()) {}
222 template<Type::MoveConstructible T>
223 Hopper<T>::Hopper(std::size_t capacity) noexcept
224 : m_impl(std::make_unique<Implementation>(capacity)) {}
226 template<Type::MoveConstructible T>
227 Hopper<T>::~Hopper() noexcept = default;
229 template<Type::MoveConstructible T>
230 std::size_t Hopper<T>::Capacity() const noexcept {
231 return m_impl->Capacity();
234 template<Type::MoveConstructible T>
235 void Hopper<T>::Capacity(std::size_t capacity) noexcept {
236 m_impl->Capacity(capacity);
239 template<Type::MoveConstructible T>
240 std::size_t Hopper<T>::Size() const noexcept {
241 return m_impl->Size();
244 template<Type::MoveConstructible T>
245 bool Hopper<T>::Full() const noexcept {
246 return m_impl->Full();
249 template<Type::MoveConstructible T>
250 void Hopper<T>::Push(T item) noexcept {
251 m_impl->Push(std::move(item));
254 template<Type::MoveConstructible T>
255 Hopper<T>& Hopper<T>::operator<<(T item) noexcept {
256 Push(std::move(item));
260 template<Type::MoveConstructible T>
261 void Hopper<T>::Eof() noexcept {
265 template<Type::MoveConstructible T>
266 void Hopper<T>::AddWriter() noexcept {
270 template<Type::MoveConstructible T>
271 void Hopper<T>::CloseWriter() noexcept {
272 m_impl->CloseWriter();
275 template<Type::MoveConstructible T>
276 T Hopper<T>::Pop() noexcept {
277 return m_impl->Pop();
280 template<Type::MoveConstructible T>
281 Hopper<T>& Hopper<T>::operator>>(T& item) noexcept {
286 template<Type::MoveConstructible T>
287 bool Hopper<T>::EoF() const noexcept {
288 return m_impl->EoF();
291 template<Type::MoveConstructible T>
292 bool Hopper<T>::Empty() const noexcept {
293 return m_impl->Empty();
296 template<Type::MoveConstructible T>
297 void Hopper<T>::Notify(std::condition_variable& wake) noexcept {
298 m_impl->Notify(wake);
301 template<Type::MoveConstructible T>
302 void Hopper<T>::Unnotify() noexcept {