77#include < type_traits>
88#include < algorithm>
99#include < cstring>
10+ #include < utility>
1011#include < vector>
1112#pragma once
1213namespace buffers {
@@ -62,12 +63,12 @@ namespace buffers {
6263 [[nodiscard]] const_pointer operator ->() const noexcept {
6364 return &((*source_)[index_]);
6465 }
65- [[nodiscard]] self_type& operator ++() noexcept {
66+ self_type& operator ++() noexcept {
6667 index_ = ++index_ % N;
6768 ++count_;
6869 return *this ;
6970 }
70- [[nodiscard]] self_type operator ++(int ) noexcept {
71+ self_type operator ++(int ) noexcept {
7172 auto result = *this ;
7273 ++(*this );
7374 return result;
@@ -78,6 +79,9 @@ namespace buffers {
7879 [[nodiscard]] size_type count () const noexcept {
7980 return count_;
8081 }
82+ [[nodiscard]] buffer_t source () const noexcept {
83+ return source_;
84+ }
8185 ~ring_buffer_iterator () = default ;
8286 private:
8387 buffer_t source_{};
@@ -88,13 +92,13 @@ namespace buffers {
8892 template <typename T, size_t N, bool C, bool Overwrite>
8993 bool operator ==(ring_buffer_iterator<T,N,C,Overwrite> const & l,
9094 ring_buffer_iterator<T,N,C,Overwrite> const & r) noexcept {
91- return l.count () == r.count ();
95+ return l.source () == r. source () && l. count () == r.count () && l. index () == r. index ();
9296 }
9397
9498 template <typename T, size_t N, bool C, bool Overwrite>
9599 bool operator !=(ring_buffer_iterator<T,N,C,Overwrite> const & l,
96100 ring_buffer_iterator<T,N,C,Overwrite> const & r) noexcept {
97- return l.count () != r.count ();
101+ return l.source () != r. source () || l. count () != r.count () || l. index () != r. index ();
98102 }
99103
100104 }
@@ -134,11 +138,14 @@ using std::bool_constant;
134138 using iterator = detail::ring_buffer_iterator<T, N, false , Overwrite>;
135139 using const_iterator = detail::ring_buffer_iterator<T, N, true , Overwrite>;
136140
141+ // Create an empty ring buffer.
137142 ring_buffer () noexcept = default ;
143+ // Copy contents and state from another buffer.
138144 ring_buffer (ring_buffer const & rhs) noexcept (is_nothrow_copy_constructible_v<value_type>)
139145 {
140146 copy_impl (rhs, bool_constant<is_trivially_copyable_v<T>>{});
141147 }
148+ // Assign from another buffer.
142149 ring_buffer& operator =(ring_buffer const & rhs) noexcept (is_nothrow_copy_constructible_v<value_type>) {
143150 if (this == &rhs)
144151 return *this ;
@@ -148,10 +155,31 @@ using std::bool_constant;
148155
149156 return *this ;
150157 }
158+ // Move contents and state from another buffer.
159+ ring_buffer (ring_buffer&& rhs) noexcept (std::is_nothrow_move_constructible<value_type>::value)
160+ {
161+ move_impl (rhs, bool_constant<is_trivially_copyable_v<T>>{});
162+ }
163+ // Move-assign from another buffer.
164+ ring_buffer& operator =(ring_buffer&& rhs) noexcept (std::is_nothrow_move_constructible<value_type>::value) {
165+ if (this == &rhs)
166+ return *this ;
167+
168+ destroy_all (bool_constant<is_trivially_copyable_v<T>>{});
169+ move_impl (rhs, bool_constant<is_trivially_copyable_v<T>>{});
170+
171+ return *this ;
172+ }
173+ // Swap contents with another buffer.
174+ void swap (self_type& rhs) noexcept (noexcept (swap_impl(rhs, bool_constant<is_trivially_copyable_v<T>>{}))) {
175+ swap_impl (rhs, bool_constant<is_trivially_copyable_v<T>>{});
176+ }
177+ // Append an element, overwriting when configured.
151178 template <typename U>
152179 void push_back (U&& value) {
153180 push_back (std::forward<U>(value), bool_constant<Overwrite>{});
154181 }
182+ // Remove the oldest element if present.
155183 void pop_front () noexcept {
156184 if (empty ())
157185 return ;
@@ -161,36 +189,52 @@ using std::bool_constant;
161189 --size_;
162190 tail_ = ++tail_ %N;
163191 }
192+ // Access the newest element.
164193 [[nodiscard]] reference back () noexcept {
165194 return reinterpret_cast <reference>(elements_[(head_ + N - 1 ) % N]);
166195 }
196+ // Access the newest element (const).
167197 [[nodiscard]] const_reference back () const noexcept {
168198 return const_cast <self_type*>(this )->back ();
169199 }
200+ // Access the oldest element.
170201 [[nodiscard]] reference front () noexcept { return reinterpret_cast <reference >(elements_[tail_]); }
202+ // Access the oldest element (const).
171203 [[nodiscard]] const_reference front () const noexcept {
172204 return const_cast <self_type*>(this )->front ();
173205 }
206+ // Direct access by internal storage index.
174207 [[nodiscard]] reference operator [](size_type index) noexcept {
175208 return reinterpret_cast <reference >(elements_[index]);
176209 }
210+ // Direct access by internal storage index (const).
177211 [[nodiscard]] const_reference operator [](size_type index) const noexcept {
178212 return const_cast <self_type *>(this )->operator [](index);
179213 }
214+ // Iterator to oldest element.
180215 [[nodiscard]] iterator begin () noexcept { return iterator{this , tail_, 0 };}
216+ // Iterator to one past newest element.
181217 [[nodiscard]] iterator end () noexcept { return iterator{this , head_, size_};}
218+ // Const iterator to oldest element.
182219 [[nodiscard]] const_iterator cbegin () const noexcept { return const_iterator{this , tail_, 0 };}
220+ // Const iterator to one past newest element.
183221 [[nodiscard]] const_iterator cend () const noexcept { return const_iterator{this , head_, size_};}
222+ // Check if buffer has no elements.
184223 [[nodiscard]] bool empty () const noexcept { return size_ == 0 ; }
224+ // Check if buffer is at capacity.
185225 [[nodiscard]] bool full () const noexcept { return size_ == N; }
226+ // Current element count.
186227 [[nodiscard]] size_type size () const noexcept { return size_; }
228+ // Maximum element count.
187229 [[nodiscard]] size_type capacity () const noexcept { return N; }
230+ // Remove all elements and reset indices.
188231 void clear () noexcept {
189232 destroy_all (bool_constant<is_trivially_destructible_v<value_type>>{});
190233 size_ = 0 ;
191234 head_ = 0 ;
192235 tail_ = 0 ;
193236 }
237+ // Destroy elements on teardown.
194238 ~ring_buffer () {
195239 clear ();
196240 };
@@ -204,7 +248,7 @@ using std::bool_constant;
204248 }
205249 }
206250 void copy_impl (self_type const & rhs, std::true_type) {
207- std::memcpy (elements_, rhs.elements_ , rhs. size_ * sizeof (T));
251+ std::memcpy (elements_, rhs.elements_ , N * sizeof (T));
208252 size_ = rhs.size_ ;
209253 tail_ = rhs.tail_ ;
210254 head_ = rhs.head_ ;
@@ -248,6 +292,59 @@ using std::bool_constant;
248292
249293#endif
250294 }
295+ void move_impl (self_type& rhs, std::true_type) {
296+ std::memcpy (elements_, rhs.elements_ , N * sizeof (T));
297+ size_ = rhs.size_ ;
298+ tail_ = rhs.tail_ ;
299+ head_ = rhs.head_ ;
300+ rhs.clear ();
301+ }
302+ void move_impl (self_type& rhs, std::false_type) {
303+ tail_ = rhs.tail_ ;
304+ head_ = rhs.head_ ;
305+ size_ = rhs.size_ ;
306+ #ifdef __cpp_exceptions
307+ try {
308+ for (auto i = 0 ; i < size_; ++i)
309+ new ( elements_ + ((tail_ + i) % N)) T (std::move (rhs[(tail_ + i) % N]));
310+ }catch (...) {
311+ while (!empty ()) {
312+ destroy (tail_, bool_constant<std::is_trivially_destructible_v<value_type>>{});
313+ tail_ = ++tail_ % N;
314+ --size_;
315+ }
316+ throw ;
317+ }
318+ #else
319+ storage_type *p = nullptr ;
320+ for (auto i = 0 ; i < size_; ++i) {
321+ p =reinterpret_cast <storage_type *>(new (elements_ + ((tail_ + i) % N)) T (std::move (rhs[(tail_ + i) % N])));
322+ if (!p) {
323+ break ;
324+ }
325+ }
326+ if (!p) {
327+ while (!empty ()) {
328+ destroy (tail_, bool_constant<is_trivially_destructible_v<value_type>>{});
329+ tail_ = ++tail_ % N;
330+ --size_;
331+ }
332+ }
333+ #endif
334+ rhs.clear ();
335+ }
336+ void swap_impl (self_type& rhs, std::true_type) noexcept {
337+ std::swap (elements_, rhs.elements_ );
338+ std::swap (head_, rhs.head_ );
339+ std::swap (tail_, rhs.tail_ );
340+ std::swap (size_, rhs.size_ );
341+ }
342+ void swap_impl (self_type& rhs, std::false_type) {
343+ self_type temp;
344+ temp.move_impl (*this , std::false_type{});
345+ this ->move_impl (rhs, std::false_type{});
346+ rhs.move_impl (temp, std::false_type{});
347+ }
251348 template <typename U>
252349 void push_back (U&& value, std::true_type) {
253350 push_back_impl (std::forward<U>(value));
@@ -284,5 +381,10 @@ using std::bool_constant;
284381 size_type size_{};
285382 };
286383
384+ template <typename T, size_t N, bool Overwrite>
385+ void swap (ring_buffer<T, N, Overwrite>& lhs, ring_buffer<T, N, Overwrite>& rhs) noexcept (noexcept (lhs.swap(rhs))) {
386+ lhs.swap (rhs);
387+ }
388+
287389}
288390#endif // RINGBUFFERTEST_RINGBUFFER_HPP
0 commit comments