From bf9190ef628c1aa04791af1bd7cd4905e9c24658 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Thu, 12 Nov 2020 01:11:11 +0100 Subject: Move includes to a conventional include directory --- include/raul/RingBuffer.hpp | 219 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 include/raul/RingBuffer.hpp (limited to 'include/raul/RingBuffer.hpp') diff --git a/include/raul/RingBuffer.hpp b/include/raul/RingBuffer.hpp new file mode 100644 index 0000000..a7bbfb7 --- /dev/null +++ b/include/raul/RingBuffer.hpp @@ -0,0 +1,219 @@ +/* + This file is part of Raul. + Copyright 2007-2012 David Robillard + + Raul 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 any later version. + + Raul 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 Raul. If not, see . +*/ + +#ifndef RAUL_RINGBUFFER_HPP +#define RAUL_RINGBUFFER_HPP + +#include "raul/Noncopyable.hpp" + +#include +#include +#include +#include +#include +#include + +namespace Raul { + +/** + A lock-free RingBuffer. + + Thread-safe with a single reader and single writer, and real-time safe + on both ends. + + @ingroup raul +*/ +class RingBuffer : public Noncopyable { +public: + /** + Create a new RingBuffer. + @param size Size in bytes (note this may be rounded up). + */ + explicit RingBuffer(uint32_t size) + : _write_head(0) + , _read_head(0) + , _size(next_power_of_two(size)) + , _size_mask(_size - 1) + , _buf(new char[_size]) + { + assert(read_space() == 0); + assert(write_space() == _size - 1); + } + + /** + Destroy a RingBuffer. + */ + inline ~RingBuffer() = default; + + /** + Reset (empty) the RingBuffer. + + This method is NOT thread-safe, it may only be called when there are no + readers or writers. + */ + inline void reset() { + _write_head = 0; + _read_head = 0; + } + + /** + Return the number of bytes of space available for reading. + */ + inline uint32_t read_space() const { + return read_space_internal(_read_head, _write_head); + } + + /** + Return the number of bytes of space available for writing. + */ + inline uint32_t write_space() const { + return write_space_internal(_read_head, _write_head); + } + + /** + Return the capacity (i.e. total write space when empty). + */ + inline uint32_t capacity() const { return _size - 1; } + + /** + Read from the RingBuffer without advancing the read head. + */ + inline uint32_t peek(uint32_t size, void* dst) { + return peek_internal(_read_head, _write_head, size, dst); + } + + /** + Read from the RingBuffer and advance the read head. + */ + inline uint32_t read(uint32_t size, void* dst) { + const uint32_t r = _read_head; + const uint32_t w = _write_head; + + if (peek_internal(r, w, size, dst)) { + std::atomic_thread_fence(std::memory_order_acquire); + _read_head = (r + size) & _size_mask; + return size; + } else { + return 0; + } + } + + /** + Skip data in the RingBuffer (advance read head without reading). + */ + inline uint32_t skip(uint32_t size) { + const uint32_t r = _read_head; + const uint32_t w = _write_head; + if (read_space_internal(r, w) < size) { + return 0; + } + + std::atomic_thread_fence(std::memory_order_acquire); + _read_head = (r + size) & _size_mask; + return size; + } + + /** + Write data to the RingBuffer. + */ + inline uint32_t write(uint32_t size, const void* src) { + const uint32_t r = _read_head; + const uint32_t w = _write_head; + if (write_space_internal(r, w) < size) { + return 0; + } + + if (w + size <= _size) { + memcpy(&_buf[w], src, size); + std::atomic_thread_fence(std::memory_order_release); + _write_head = (w + size) & _size_mask; + } else { + const uint32_t this_size = _size - w; + assert(this_size < size); + assert(w + this_size <= _size); + memcpy(&_buf[w], src, this_size); + memcpy(&_buf[0], + static_cast(src) + this_size, + size - this_size); + std::atomic_thread_fence(std::memory_order_release); + _write_head = size - this_size; + } + + return size; + } + +private: + static inline uint32_t next_power_of_two(uint32_t size) { + // http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 + size--; + size |= size >> 1U; + size |= size >> 2U; + size |= size >> 4U; + size |= size >> 8U; + size |= size >> 16U; + size++; + return size; + } + + inline uint32_t write_space_internal(uint32_t r, uint32_t w) const { + if (r == w) { + return _size - 1; + } else if (r < w) { + return ((r - w + _size) & _size_mask) - 1; + } else { + return (r - w) - 1; + } + } + + inline uint32_t read_space_internal(uint32_t r, uint32_t w) const { + if (r < w) { + return w - r; + } else { + return (w - r + _size) & _size_mask; + } + } + + inline uint32_t peek_internal(uint32_t r, uint32_t w, + uint32_t size, void* dst) const { + if (read_space_internal(r, w) < size) { + return 0; + } + + if (r + size < _size) { + memcpy(dst, &_buf[r], size); + } else { + const uint32_t first_size = _size - r; + memcpy(dst, &_buf[r], first_size); + memcpy(static_cast(dst) + first_size, + &_buf[0], + size - first_size); + } + + return size; + } + + mutable uint32_t _write_head; ///< Read index into _buf + mutable uint32_t _read_head; ///< Write index into _buf + + const uint32_t _size; ///< Size (capacity) in bytes + const uint32_t _size_mask; ///< Mask for fast modulo + + const std::unique_ptr _buf; ///< Contents +}; + +} // namespace Raul + +#endif // RAUL_RINGBUFFER_HPP -- cgit v1.2.1