/* This file is part of Jellyfish. Jellyfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Jellyfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Jellyfish. If not, see . */ #ifndef __JELLYFISH_CIRCULAR_BUFFER_HPP__ #define __JELLYFISH_CIRCULAR_BUFFER_HPP__ #include #include #include #include namespace jflib { template class basic_circular_buffer { static const unsigned int m = std::numeric_limits::digits - n; struct splitT { T id:m; T val:n; }; union elt { T binary; splitT split; }; // size_t _size; divisor64 _size; T *_buffer; size_t _head; size_t _tail; bool _closed; public: typedef T value_type; static const T guard = g; basic_circular_buffer(size_t size) : _size(size+1), _buffer(new T[_size.d()]), _head(0), _tail(0), _closed(false) { elt init; init.split.id = 0; init.split.val = guard; for(size_t i = 0; i < _size.d(); ++i) _buffer[i] = init.binary; } virtual ~basic_circular_buffer() { if(_buffer) delete [] _buffer; } /** Enqueue an element. * @return false if the FIFO is full. */ bool enqueue(const T &v); /** Enqueue an element, optimization. No check is made that the * FIFO is full. Undetermined behavior if an element is inserted * in a full FIFO. */ void enqueue_no_check(const T &v); /** Dequeue an element. * @return 0 if the FIFO is empty. */ T dequeue(); bool is_closed() const { return a_load(_closed); } void close() { a_store(_closed, true); } /// Return capacity of circular buffer size_t size() { return _size.d(); } /// Return the number of element currently in circular buffer size_t fill() { size_t head, tail; size_t nhead = a_load(_head); do { head = nhead; tail = a_load(_tail); } while(head != (nhead = a_load(_head))); return head >= tail ? head - tail : head + _size.d() - tail; } }; template class circular_buffer : public basic_circular_buffer::digits, g> { public: circular_buffer(size_t size) : basic_circular_buffer::digits, g>(size) { } virtual ~circular_buffer() { } bool enqueue(const T &v) { return basic_circular_buffer::digits, g>::enqueue((uint64_t)v); } T dequeue() { return basic_circular_buffer::digits, g>::dequeue(); } }; } template bool jflib::basic_circular_buffer::enqueue(const T &v) { bool done = false; size_t chead = a_load(_head); while(!done) { size_t ctail = a_load(_tail); elt celt; celt.binary = a_load(_buffer[chead % _size]); size_t achead = a_load(_head); if(achead != chead) { chead = achead; continue; } size_t nhead = chead + 1; if(nhead % _size == ctail % _size) return false; if(celt.split.val == guard) { // entry is empty elt nelt; nelt.split.id = celt.split.id + 1; nelt.split.val = v; done = cas(&_buffer[chead % _size], celt.binary, nelt.binary); // done == true <=> sucessfully written entry } cas(&_head, chead, nhead, &chead); } return true; } template void jflib::basic_circular_buffer::enqueue_no_check(const T &v) { bool done = false; size_t chead = a_load(_head); while(!done) { elt celt; celt.binary = a_load(_buffer[chead % _size]); size_t achead = a_load(_head); if(achead != chead) { chead = achead; continue; } size_t nhead = chead + 1; if(celt.split.val == guard) { // entry is empty elt nelt; nelt.split.id = celt.split.id + 1; nelt.split.val = v; done = cas(&_buffer[chead % _size], celt.binary, nelt.binary); // done == true <=> sucessfully written entry } cas(&_head, chead, nhead, &chead); } } template T jflib::basic_circular_buffer::dequeue() { bool done = false; elt res; size_t ctail = a_load(_tail); while(!done) { bool dequeued = false; do { if(ctail % _size == a_load(_head) % _size) return guard; size_t ntail = ctail + 1; dequeued = cas(&_tail, ctail, ntail, &ctail); } while(!dequeued); res.binary = a_load(_buffer[ctail % _size]); elt nres; nres.split.val = guard; while(true) { nres.split.id = res.split.id + 1; if(res.split.val == guard) { if(cas(&_buffer[ctail % _size], res.binary, nres.binary, &res.binary)) break; } else { done = cas(&_buffer[ctail % _size], res.binary, nres.binary); break; } } } return res.split.val; } #endif /* __JELLYFISH_CIRCULAR_BUFFER_HPP__ */