/* * Copyright (C) 2014 Cloudius Systems, Ltd. */ #ifndef CIRCULAR_BUFFER_HH_ #define 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 "transfer.hh" #include #include 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 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++); }); } 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 /* CIRCULAR_BUFFER_HH_ */