diff --git a/include/bx/inline/ringbuffer.inl b/include/bx/inline/ringbuffer.inl index 921ff77..623f8fe 100644 --- a/include/bx/inline/ringbuffer.inl +++ b/include/bx/inline/ringbuffer.inl @@ -21,11 +21,6 @@ namespace bx { } - inline uint32_t RingBufferControl::available() const - { - return distance(m_read, m_current); - } - inline bool RingBufferControl::isEmpty() const { return m_read == m_write; @@ -119,7 +114,7 @@ namespace bx } inline SpScRingBufferControl::SpScRingBufferControl(uint32_t _size) - : m_size(_size) + : m_size(max(_size, 2) ) , m_current(0) , m_write(0) , m_read(0) @@ -130,11 +125,45 @@ namespace bx { } - inline uint32_t SpScRingBufferControl::available() const + inline bool SpScRingBufferControl::isEmpty() const + { + return m_read == m_write; + } + + inline uint32_t SpScRingBufferControl::getSize() const + { + return m_size; + } + + inline uint32_t SpScRingBufferControl::getNumEmpty() const + { + return m_size - distance(m_read, m_write) - 1; + } + + inline uint32_t SpScRingBufferControl::getNumUsed() const { return distance(m_read, m_current); } + inline uint32_t SpScRingBufferControl::getNumReserved() const + { + return distance(m_current, m_write); + } + + inline void SpScRingBufferControl::resize(int32_t _size) + { + _size = 0 > _size + // can shrink only by number of empty slots. + ? bx::max(_size, -int32_t(getNumEmpty() ) ) + : _size + ; + + m_size += _size; + + m_current += m_current >= m_write ? _size : 0; + m_read += m_read >= m_write ? _size : 0; + } + inline uint32_t SpScRingBufferControl::consume(uint32_t _size) { const uint32_t maxSize = distance(m_read, m_current); @@ -147,13 +176,13 @@ namespace bx return size; } - inline uint32_t SpScRingBufferControl::reserve(uint32_t _size) + inline uint32_t SpScRingBufferControl::reserve(uint32_t _size, bool _mustSucceed) { const uint32_t dist = distance(m_write, m_read)-1; const uint32_t maxSize = uint32_sels(dist, m_size-1, dist); const uint32_t sizeNoSign = uint32_and(_size, 0x7fffffff); const uint32_t test = uint32_sub(sizeNoSign, maxSize); - const uint32_t size = uint32_sels(test, _size, maxSize); + const uint32_t size = uint32_sels(test, _size, _mustSucceed ? 0 : maxSize); const uint32_t advance = uint32_add(m_write, size); const uint32_t write = uint32_mod(advance, m_size); m_write = write; @@ -200,7 +229,7 @@ namespace bx , m_size(_size) , m_buffer(_buffer) { - BX_ASSERT(_control.available() >= _size, "%d >= %d", _control.available(), _size); + BX_ASSERT(_control.getNumUsed() >= _size, "%d >= %d", _control.getNumUsed(), _size); } template diff --git a/include/bx/ringbuffer.h b/include/bx/ringbuffer.h index 20ea6f8..11299a7 100644 --- a/include/bx/ringbuffer.h +++ b/include/bx/ringbuffer.h @@ -37,12 +37,6 @@ namespace bx /// Destructor. ~RingBufferControl(); - /// Returns number of used slots. - /// - /// @returns Number of used slots. - /// - uint32_t available() const; - /// Returns 'true' if ring buffer is empty. /// /// @returns Returns 'true' if ring buffer is empty. @@ -122,6 +116,14 @@ namespace bx uint32_t m_read; //!< Read head. }; + /// Lock-less single producer, single consumer ring buffer control structure. Tracking "read", + /// "write", and "current" head. + /// + /// This is not container, and data control represents is user defined. Read/write/current are + /// just indices. + /// + /// @notice One slot is always reseved. When creating ring buffer of N slots, N-1 slots can be + /// used. /// class SpScRingBufferControl { @@ -131,34 +133,92 @@ namespace bx ); public: + /// Constructor. + /// + /// @param[in] _size Maximum number of slots. /// SpScRingBufferControl(uint32_t _size); - /// + /// Destructor. ~SpScRingBufferControl(); + /// Returns 'true' if ring buffer is empty. /// - uint32_t available() const; + /// @returns Returns 'true' if ring buffer is empty. + /// + bool isEmpty() const; + /// Returns total size of ring buffer. + /// + /// @returns Total size of ring buffer. + /// + uint32_t getSize() const; + + /// Returns number of empty slots. + /// + /// @returns Number of empty slots. + /// + uint32_t getNumEmpty() const; + + /// Returns number of used slots. + /// + /// @returns Number of used slots. + /// + uint32_t getNumUsed() const; + + /// Returns number of reserved slots. + /// + /// @returns Number of reserved slots. + /// + uint32_t getNumReserved() const; + + /// Resize ring buffer. Resize happens at write head, read and current head will be moved + /// forward or backward if write head is behind them. + /// + /// @param[in] _size Amount to resize. Value can be positive when growing size or negative + /// when shrinking size of buffer. + /// + void resize(int32_t _size); + + /// Consume slots, makes slots free to be reserved. Moves "read" head forward. + /// + /// @returns Number of reserved slots reserved. /// uint32_t consume(uint32_t _size); // consumer only + /// Reserve slots, makes slots non-free, but ready to be used yet. Moves "write" head forward. /// - uint32_t reserve(uint32_t _size); // producer only + /// @param[in] _size Number of slots. + /// @param[in] _mustSucceed If argument is true it will not reseve any slots unless `_size` + /// of slots is reseved. + /// + /// @returns Number of reserved slots reserved. + /// + uint32_t reserve(uint32_t _size, bool _mustSucceed = false); // producer only + /// Commit slots, makes slots used, and ready to be consumed. Moves "current" head forward. + /// + /// @param[in] _size Number of commited slots. /// uint32_t commit(uint32_t _size); // producer only + /// Calculate distance between two slots. Function takes wrapping into account. + /// + /// @param[in] _from From. + /// @param[in] _to To. + /// + /// @returns Distance between slots. /// uint32_t distance(uint32_t _from, uint32_t _to) const; // both + /// Invalidate ring buffer. /// void reset(); - const uint32_t m_size; - uint32_t m_current; - uint32_t m_write; - uint32_t m_read; + uint32_t m_size; //!< Size of ring buffer. + uint32_t m_current; //!< Currently operated area start. + uint32_t m_write; //!< Write head. + uint32_t m_read; //!< Read head. }; ///