Fixed RingBufferControl::resize moving heads that are at the write head, and leaving heads outside the buffer when shrinking past the end of buffer. (#436)

This commit is contained in:
Branimir Karadžić
2026-09-30 22:27:35 -07:00
committed by GitHub
parent 3b8366af83
commit 1c986bd1e9
3 changed files with 139 additions and 6 deletions

View File

@@ -54,10 +54,16 @@ namespace bx
: _size
;
// empty slots past the end of buffer are removed from the start of buffer.
const int32_t back = bx::max(_size, int32_t(m_write) - int32_t(m_size) );
const int32_t front = _size - back;
const uint32_t write = m_write;
m_size += _size;
m_current += m_current >= m_write ? _size : 0;
m_read += m_read >= m_write ? _size : 0;
m_current = (m_current + (m_current > write ? back : front) ) % m_size;
m_read = (m_read + (m_read > write ? back : front) ) % m_size;
m_write = (m_write + front) % m_size;
}
inline uint32_t RingBufferControl::consume(uint32_t _size)
@@ -177,10 +183,16 @@ namespace bx
: _size
;
// empty slots past the end of buffer are removed from the start of buffer.
const int32_t back = bx::max(_size, int32_t(m_write) - int32_t(m_size) );
const int32_t front = _size - back;
const uint32_t write = m_write;
m_size += _size;
m_current += m_current >= m_write ? _size : 0;
m_read += m_read >= m_write ? _size : 0;
m_current = (m_current + (m_current > write ? back : front) ) % m_size;
m_read = (m_read + (m_read > write ? back : front) ) % m_size;
m_write = (m_write + front) % m_size;
}
inline uint32_t SpScRingBufferControl::consume(uint32_t _size)

View File

@@ -71,7 +71,9 @@ namespace bx
/// 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.
/// when shrinking size of buffer. Shrinking is limited to number of empty slots. Empty slots
/// are removed from write head to the end of buffer, and remainder is removed from the start
/// of buffer.
///
void resize(int32_t _size);
@@ -176,7 +178,9 @@ namespace bx
/// 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.
/// when shrinking size of buffer. Shrinking is limited to number of empty slots. Empty slots
/// are removed from write head to the end of buffer, and remainder is removed from the start
/// of buffer.
///
void resize(int32_t _size);

View File

@@ -123,3 +123,120 @@ TEST_CASE("RingBufferControl resize", "")
REQUIRE(control.m_write == 0);
REQUIRE(control.m_read == 1);
}
TEST_CASE("RingBufferControl resize without reserved slots", "")
{
{
bx::RingBufferControl control(8);
REQUIRE(5 == control.reserve(5) );
REQUIRE(5 == control.commit(5) );
REQUIRE(2 == control.consume(2) );
control.resize(8);
REQUIRE(16 == control.getSize() );
REQUIRE(3 == control.getNumUsed() );
REQUIRE(0 == control.getNumReserved() );
REQUIRE(12 == control.getNumEmpty() );
REQUIRE(control.m_current == 5);
REQUIRE(control.m_write == 5);
REQUIRE(control.m_read == 2);
}
{
bx::RingBufferControl control(8);
REQUIRE(6 == control.reserve(6) );
REQUIRE(6 == control.commit(6) );
REQUIRE(5 == control.consume(5) );
REQUIRE(4 == control.reserve(4) );
REQUIRE(4 == control.commit(4) );
REQUIRE(control.m_current == 2);
REQUIRE(control.m_write == 2);
REQUIRE(control.m_read == 5);
control.resize(8);
REQUIRE(16 == control.getSize() );
REQUIRE(5 == control.getNumUsed() );
REQUIRE(0 == control.getNumReserved() );
REQUIRE(10 == control.getNumEmpty() );
REQUIRE(control.m_current == 2);
REQUIRE(control.m_write == 2);
REQUIRE(control.m_read == 13);
control.resize(-8);
REQUIRE(8 == control.getSize() );
REQUIRE(5 == control.getNumUsed() );
REQUIRE(0 == control.getNumReserved() );
REQUIRE(2 == control.getNumEmpty() );
REQUIRE(control.m_current == 2);
REQUIRE(control.m_write == 2);
REQUIRE(control.m_read == 5);
}
{
bx::RingBufferControl control(8);
REQUIRE(3 == control.reserve(3) );
REQUIRE(3 == control.commit(3) );
REQUIRE(3 == control.consume(3) );
control.resize(8);
REQUIRE(16 == control.getSize() );
REQUIRE(0 == control.getNumUsed() );
REQUIRE(0 == control.getNumReserved() );
REQUIRE(15 == control.getNumEmpty() );
REQUIRE(control.isEmpty() );
REQUIRE(control.m_current == 3);
REQUIRE(control.m_write == 3);
REQUIRE(control.m_read == 3);
}
}
TEST_CASE("RingBufferControl shrink past the end of buffer", "")
{
{
bx::RingBufferControl control(16);
REQUIRE(10 == control.reserve(10) );
REQUIRE(10 == control.commit(10) );
REQUIRE(8 == control.consume(8) );
REQUIRE(13 == control.getNumEmpty() );
control.resize(-13);
REQUIRE(3 == control.getSize() );
REQUIRE(2 == control.getNumUsed() );
REQUIRE(0 == control.getNumReserved() );
REQUIRE(0 == control.getNumEmpty() );
REQUIRE(control.m_current == 0);
REQUIRE(control.m_write == 0);
REQUIRE(control.m_read == 1);
}
{
bx::RingBufferControl control(32);
REQUIRE(29 == control.reserve(29) );
REQUIRE(29 == control.commit(29) );
REQUIRE(18 == control.consume(18) );
REQUIRE(20 == control.getNumEmpty() );
control.resize(-4);
REQUIRE(28 == control.getSize() );
REQUIRE(11 == control.getNumUsed() );
REQUIRE(0 == control.getNumReserved() );
REQUIRE(16 == control.getNumEmpty() );
REQUIRE(control.m_current == 0);
REQUIRE(control.m_write == 0);
REQUIRE(control.m_read == 17);
}
}