2 * This file is open source software, licensed to you under the terms
3 * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4 * distributed with this work for additional information regarding copyright
5 * ownership. You may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing,
12 * software distributed under the License is distributed on an
13 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 * KIND, either express or implied. See the License for the
15 * specific language governing permissions and limitations
19 * Copyright (C) 2014 Cloudius Systems, Ltd.
24 #include <seastar/core/transfer.hh>
25 #include <seastar/core/bitops.hh>
31 /// A growable double-ended queue container that can be efficiently
32 /// extended (and shrunk) from both ends. Implementation is a single
35 /// Similar to libstdc++'s std::deque, except that it uses a single
36 /// level store, and so is more efficient for simple stored items.
37 /// Similar to boost::circular_buffer_space_optimized, except it uses
38 /// uninitialized storage for unoccupied elements (and thus move/copy
39 /// constructors instead of move/copy assignments, which are less
42 /// The storage of the circular_buffer is expanded automatically in
43 /// exponential increments.
44 /// When adding new elements:
45 /// * if size + 1 > capacity: all iterators and references are
47 /// * otherwise only the begin() or end() iterator is invalidated:
48 /// * push_front() and emplace_front() will invalidate begin() and
49 /// * push_back() and emplace_back() will invalidate end().
51 /// Removing elements never invalidates any references and only
52 /// invalidates begin() or end() iterators:
53 /// * pop_front() will invalidate begin() and
54 /// * pop_back() will invalidate end().
56 /// reserve() may also invalidate all iterators and references.
57 template <typename T, typename Alloc = std::allocator<T>>
58 class circular_buffer {
61 // begin, end interpreted (mod capacity)
69 using size_type = size_t;
72 using const_reference = const T&;
73 using const_pointer = const T*;
75 circular_buffer() = default;
76 circular_buffer(circular_buffer&& X) noexcept;
77 circular_buffer(const circular_buffer& X) = delete;
79 circular_buffer& operator=(const circular_buffer&) = delete;
80 circular_buffer& operator=(circular_buffer&& b) noexcept;
81 void push_front(const T& data);
82 void push_front(T&& data);
83 template <typename... A>
84 void emplace_front(A&&... args);
85 void push_back(const T& data);
86 void push_back(T&& data);
87 template <typename... A>
88 void emplace_back(A&&... args);
90 const T& front() const;
92 const T& back() const;
97 size_t capacity() const;
100 T& operator[](size_t idx);
101 const T& operator[](size_t idx) const;
102 template <typename Func>
103 void for_each(Func func);
104 // access an element, may return wrong or destroyed element
105 // only useful if you do not rely on data accuracy (e.g. prefetch)
106 T& access_element_unsafe(size_t idx);
110 void maybe_expand(size_t nr = 1);
111 size_t mask(size_t idx) const;
113 template<typename CB, typename ValueType>
114 struct cbiterator : std::iterator<std::random_access_iterator_tag, ValueType> {
115 typedef std::iterator<std::random_access_iterator_tag, ValueType> super_t;
117 ValueType& operator*() const { return cb->_impl.storage[cb->mask(idx)]; }
118 ValueType* operator->() const { return &cb->_impl.storage[cb->mask(idx)]; }
120 cbiterator<CB, ValueType>& operator++() {
125 cbiterator<CB, ValueType> operator++(int unused) {
131 cbiterator<CB, ValueType>& operator--() {
136 cbiterator<CB, ValueType> operator--(int unused) {
141 cbiterator<CB, ValueType> operator+(typename super_t::difference_type n) const {
142 return cbiterator<CB, ValueType>(cb, idx + n);
144 cbiterator<CB, ValueType> operator-(typename super_t::difference_type n) const {
145 return cbiterator<CB, ValueType>(cb, idx - n);
147 cbiterator<CB, ValueType>& operator+=(typename super_t::difference_type n) {
151 cbiterator<CB, ValueType>& operator-=(typename super_t::difference_type n) {
155 bool operator==(const cbiterator<CB, ValueType>& rhs) const {
156 return idx == rhs.idx;
158 bool operator!=(const cbiterator<CB, ValueType>& rhs) const {
159 return idx != rhs.idx;
161 bool operator<(const cbiterator<CB, ValueType>& rhs) const {
162 return idx < rhs.idx;
164 bool operator>(const cbiterator<CB, ValueType>& rhs) const {
165 return idx > rhs.idx;
167 bool operator>=(const cbiterator<CB, ValueType>& rhs) const {
168 return idx >= rhs.idx;
170 bool operator<=(const cbiterator<CB, ValueType>& rhs) const {
171 return idx <= rhs.idx;
173 typename super_t::difference_type operator-(const cbiterator<CB, ValueType>& rhs) const {
174 return idx - rhs.idx;
179 cbiterator<CB, ValueType>(CB* b, size_t i) : cb(b), idx(i) {}
180 friend class circular_buffer;
182 friend class iterator;
185 typedef cbiterator<circular_buffer, T> iterator;
186 typedef cbiterator<const circular_buffer, const T> const_iterator;
189 return iterator(this, _impl.begin);
191 const_iterator begin() const {
192 return const_iterator(this, _impl.begin);
195 return iterator(this, _impl.end);
197 const_iterator end() const {
198 return const_iterator(this, _impl.end);
200 const_iterator cbegin() const {
201 return const_iterator(this, _impl.begin);
203 const_iterator cend() const {
204 return const_iterator(this, _impl.end);
206 iterator erase(iterator first, iterator last);
209 template <typename T, typename Alloc>
212 circular_buffer<T, Alloc>::mask(size_t idx) const {
213 return idx & (_impl.capacity - 1);
216 template <typename T, typename Alloc>
219 circular_buffer<T, Alloc>::empty() const {
220 return _impl.begin == _impl.end;
223 template <typename T, typename Alloc>
226 circular_buffer<T, Alloc>::size() const {
227 return _impl.end - _impl.begin;
230 template <typename T, typename Alloc>
233 circular_buffer<T, Alloc>::capacity() const {
234 return _impl.capacity;
237 template <typename T, typename Alloc>
240 circular_buffer<T, Alloc>::reserve(size_t size) {
241 if (capacity() < size) {
242 // Make sure that the new capacity is a power of two.
243 expand(size_t(1) << log2ceil(size));
247 template <typename T, typename Alloc>
250 circular_buffer<T, Alloc>::clear() {
251 erase(begin(), end());
254 template <typename T, typename Alloc>
256 circular_buffer<T, Alloc>::circular_buffer(circular_buffer&& x) noexcept
257 : _impl(std::move(x._impl)) {
261 template <typename T, typename Alloc>
263 circular_buffer<T, Alloc>& circular_buffer<T, Alloc>::operator=(circular_buffer&& x) noexcept {
265 this->~circular_buffer();
266 new (this) circular_buffer(std::move(x));
271 template <typename T, typename Alloc>
272 template <typename Func>
275 circular_buffer<T, Alloc>::for_each(Func func) {
276 auto s = _impl.storage;
277 auto m = _impl.capacity - 1;
278 for (auto i = _impl.begin; i != _impl.end; ++i) {
283 template <typename T, typename Alloc>
285 circular_buffer<T, Alloc>::~circular_buffer() {
286 for_each([this] (T& obj) {
289 _impl.deallocate(_impl.storage, _impl.capacity);
292 template <typename T, typename Alloc>
294 circular_buffer<T, Alloc>::expand() {
295 expand(std::max<size_t>(_impl.capacity * 2, 1));
298 template <typename T, typename Alloc>
300 circular_buffer<T, Alloc>::expand(size_t new_cap) {
301 auto new_storage = _impl.allocate(new_cap);
302 auto p = new_storage;
304 for_each([this, &p] (T& obj) {
305 transfer_pass1(_impl, &obj, p);
309 while (p != new_storage) {
312 _impl.deallocate(new_storage, new_cap);
316 for_each([this, &p] (T& obj) {
317 transfer_pass2(_impl, &obj, p++);
319 std::swap(_impl.storage, new_storage);
320 std::swap(_impl.capacity, new_cap);
322 _impl.end = p - _impl.storage;
323 _impl.deallocate(new_storage, new_cap);
326 template <typename T, typename Alloc>
329 circular_buffer<T, Alloc>::maybe_expand(size_t nr) {
330 if (_impl.end - _impl.begin + nr > _impl.capacity) {
335 template <typename T, typename Alloc>
338 circular_buffer<T, Alloc>::push_front(const T& data) {
340 auto p = &_impl.storage[mask(_impl.begin - 1)];
341 _impl.construct(p, data);
345 template <typename T, typename Alloc>
348 circular_buffer<T, Alloc>::push_front(T&& data) {
350 auto p = &_impl.storage[mask(_impl.begin - 1)];
351 _impl.construct(p, std::move(data));
355 template <typename T, typename Alloc>
356 template <typename... Args>
359 circular_buffer<T, Alloc>::emplace_front(Args&&... args) {
361 auto p = &_impl.storage[mask(_impl.begin - 1)];
362 _impl.construct(p, std::forward<Args>(args)...);
366 template <typename T, typename Alloc>
369 circular_buffer<T, Alloc>::push_back(const T& data) {
371 auto p = &_impl.storage[mask(_impl.end)];
372 _impl.construct(p, data);
376 template <typename T, typename Alloc>
379 circular_buffer<T, Alloc>::push_back(T&& data) {
381 auto p = &_impl.storage[mask(_impl.end)];
382 _impl.construct(p, std::move(data));
386 template <typename T, typename Alloc>
387 template <typename... Args>
390 circular_buffer<T, Alloc>::emplace_back(Args&&... args) {
392 auto p = &_impl.storage[mask(_impl.end)];
393 _impl.construct(p, std::forward<Args>(args)...);
397 template <typename T, typename Alloc>
400 circular_buffer<T, Alloc>::front() {
401 return _impl.storage[mask(_impl.begin)];
404 template <typename T, typename Alloc>
407 circular_buffer<T, Alloc>::front() const {
408 return _impl.storage[mask(_impl.begin)];
411 template <typename T, typename Alloc>
414 circular_buffer<T, Alloc>::back() {
415 return _impl.storage[mask(_impl.end - 1)];
418 template <typename T, typename Alloc>
421 circular_buffer<T, Alloc>::back() const {
422 return _impl.storage[mask(_impl.end - 1)];
425 template <typename T, typename Alloc>
428 circular_buffer<T, Alloc>::pop_front() {
429 _impl.destroy(&front());
433 template <typename T, typename Alloc>
436 circular_buffer<T, Alloc>::pop_back() {
437 _impl.destroy(&back());
441 template <typename T, typename Alloc>
444 circular_buffer<T, Alloc>::operator[](size_t idx) {
445 return _impl.storage[mask(_impl.begin + idx)];
448 template <typename T, typename Alloc>
451 circular_buffer<T, Alloc>::operator[](size_t idx) const {
452 return _impl.storage[mask(_impl.begin + idx)];
455 template <typename T, typename Alloc>
458 circular_buffer<T, Alloc>::access_element_unsafe(size_t idx) {
459 return _impl.storage[mask(_impl.begin + idx)];
462 template <typename T, typename Alloc>
464 typename circular_buffer<T, Alloc>::iterator
465 circular_buffer<T, Alloc>::erase(iterator first, iterator last) {
466 static_assert(std::is_nothrow_move_assignable<T>::value, "erase() assumes move assignment does not throw");
470 // Move to the left or right depending on which would result in least amount of moves.
471 // This also guarantees that iterators will be stable when removing from either front or back.
472 if (std::distance(begin(), first) < std::distance(last, end())) {
473 auto new_start = std::move_backward(begin(), first, last);
475 while (i < new_start) {
476 _impl.destroy(&*i++);
478 _impl.begin = new_start.idx;
481 auto new_end = std::move(last, end(), first);
485 _impl.destroy(&*i++);
487 _impl.end = new_end.idx;