// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- /* * This file is open source software, licensed to you under the terms * of the Apache License, Version 2.0 (the "License"). See the NOTICE file * distributed with this work for additional information regarding copyright * ownership. You may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* * Copyright (C) 2014 Cloudius Systems, Ltd. */ #ifndef CEPH_CIRCULAR_BUFFER_HH_ #define CEPH_CIRCULAR_BUFFER_HH_ // A growable double-ended queue container that can be efficiently // extended (and shrunk) from both ends. Implementation is a single // storage vector. // // Similar to libstdc++'s std::deque, except that it uses a single level // store, and so is more efficient for simple stored items. // Similar to boost::circular_buffer_space_optimized, except it uses // uninitialized storage for unoccupied elements (and thus move/copy // constructors instead of move/copy assignments, which are less efficient). #include #include #include "transfer.h" template > class circular_buffer { struct impl : Alloc { T* storage = nullptr; // begin, end interpreted (mod capacity) size_t begin = 0; size_t end = 0; size_t capacity = 0; }; impl _impl; public: using value_type = T; using size_type = size_t; using reference = T&; using pointer = T*; using const_reference = const T&; using const_pointer = const T*; public: circular_buffer() = default; circular_buffer(circular_buffer&& X); circular_buffer(const circular_buffer& X) = delete; ~circular_buffer(); circular_buffer& operator=(const circular_buffer&) = delete; circular_buffer& operator=(circular_buffer&&) = delete; void push_front(const T& data); void push_front(T&& data); template void emplace_front(A&&... args); void push_back(const T& data); void push_back(T&& data); template void emplace_back(A&&... args); T& front(); T& back(); void pop_front(); void pop_back(); bool empty() const; size_t size() const; size_t capacity() const; T& operator[](size_t idx); template void for_each(Func func); // access an element, may return wrong or destroyed element // only useful if you do not rely on data accuracy (e.g. prefetch) T& access_element_unsafe(size_t idx); private: void expand(); void maybe_expand(size_t nr = 1); size_t mask(size_t idx) const; template struct cbiterator : std::iterator { typedef std::iterator super_t; ValueType& operator*() const { return cb->_impl.storage[cb->mask(idx)]; } ValueType* operator->() const { return &cb->_impl.storage[cb->mask(idx)]; } // prefix cbiterator& operator++() { idx++; return *this; } // postfix cbiterator operator++(int unused) { auto v = *this; idx++; return v; } // prefix cbiterator& operator--() { idx--; return *this; } // postfix cbiterator operator--(int unused) { auto v = *this; idx--; return v; } cbiterator operator+(typename super_t::difference_type n) const { return cbiterator(cb, idx + n); } cbiterator operator-(typename super_t::difference_type n) const { return cbiterator(cb, idx - n); } cbiterator& operator+=(typename super_t::difference_type n) { idx += n; return *this; } cbiterator& operator-=(typename super_t::difference_type n) { idx -= n; return *this; } bool operator==(const cbiterator& rhs) const { return idx == rhs.idx; } bool operator!=(const cbiterator& rhs) const { return idx != rhs.idx; } bool operator<(const cbiterator& rhs) const { return idx < rhs.idx; } bool operator>(const cbiterator& rhs) const { return idx > rhs.idx; } bool operator>=(const cbiterator& rhs) const { return idx >= rhs.idx; } bool operator<=(const cbiterator& rhs) const { return idx <= rhs.idx; } typename super_t::difference_type operator-(const cbiterator& rhs) const { return idx - rhs.idx; } private: CB* cb; size_t idx; cbiterator(CB* b, size_t i) : cb(b), idx(i) {} friend class circular_buffer; }; friend class iterator; public: typedef cbiterator iterator; typedef cbiterator const_iterator; iterator begin() { return iterator(this, _impl.begin); } const_iterator begin() const { return const_iterator(this, _impl.begin); } iterator end() { return iterator(this, _impl.end); } const_iterator end() const { return const_iterator(this, _impl.end); } const_iterator cbegin() const { return const_iterator(this, _impl.begin); } const_iterator cend() const { return const_iterator(this, _impl.end); } }; template inline size_t circular_buffer::mask(size_t idx) const { return idx & (_impl.capacity - 1); } template inline bool circular_buffer::empty() const { return _impl.begin == _impl.end; } template inline size_t circular_buffer::size() const { return _impl.end - _impl.begin; } template inline size_t circular_buffer::capacity() const { return _impl.capacity; } template inline circular_buffer::circular_buffer(circular_buffer&& x) : _impl(std::move(x._impl)) { x._impl = {}; } template template inline void circular_buffer::for_each(Func func) { auto s = _impl.storage; auto m = _impl.capacity - 1; for (auto i = _impl.begin; i != _impl.end; ++i) { func(s[i & m]); } } template inline circular_buffer::~circular_buffer() { for_each([this] (T& obj) { _impl.destroy(&obj); }); _impl.deallocate(_impl.storage, _impl.capacity); } template void circular_buffer::expand() { auto new_cap = std::max(_impl.capacity * 2, 1); auto new_storage = _impl.allocate(new_cap); auto p = new_storage; try { for_each([this, &p] (T& obj) { transfer_pass1(_impl, &obj, p); p++; }); } catch (...) { while (p != new_storage) { _impl.destroy(--p); } _impl.deallocate(new_storage, new_cap); throw; } p = new_storage; for_each([this, &p] (T& obj) { transfer_pass2(_impl, &obj, p++); }); std::swap(_impl.storage, new_storage); std::swap(_impl.capacity, new_cap); _impl.begin = 0; _impl.end = p - _impl.storage; _impl.deallocate(new_storage, new_cap); } template inline void circular_buffer::maybe_expand(size_t nr) { if (_impl.end - _impl.begin + nr > _impl.capacity) { expand(); } } template inline void circular_buffer::push_front(const T& data) { maybe_expand(); auto p = &_impl.storage[mask(_impl.begin - 1)]; _impl.construct(p, data); --_impl.begin; } template inline void circular_buffer::push_front(T&& data) { maybe_expand(); auto p = &_impl.storage[mask(_impl.begin - 1)]; _impl.construct(p, std::move(data)); --_impl.begin; } template template inline void circular_buffer::emplace_front(Args&&... args) { maybe_expand(); auto p = &_impl.storage[mask(_impl.begin - 1)]; _impl.construct(p, std::forward(args)...); --_impl.begin; } template inline void circular_buffer::push_back(const T& data) { maybe_expand(); auto p = &_impl.storage[mask(_impl.end)]; _impl.construct(p, data); ++_impl.end; } template inline void circular_buffer::push_back(T&& data) { maybe_expand(); auto p = &_impl.storage[mask(_impl.end)]; _impl.construct(p, std::move(data)); ++_impl.end; } template template inline void circular_buffer::emplace_back(Args&&... args) { maybe_expand(); auto p = &_impl.storage[mask(_impl.end)]; _impl.construct(p, std::forward(args)...); ++_impl.end; } template inline T& circular_buffer::front() { return _impl.storage[mask(_impl.begin)]; } template inline T& circular_buffer::back() { return _impl.storage[mask(_impl.end - 1)]; } template inline void circular_buffer::pop_front() { _impl.destroy(&front()); ++_impl.begin; } template inline void circular_buffer::pop_back() { _impl.destroy(&back()); --_impl.end; } template inline T& circular_buffer::operator[](size_t idx) { return _impl.storage[mask(_impl.begin + idx)]; } template inline T& circular_buffer::access_element_unsafe(size_t idx) { return _impl.storage[mask(_impl.begin + idx)]; } #endif /* CEPH_CIRCULAR_BUFFER_HH_ */