0

I have a following problem and despite many approached all fail to satisfy my needs: Having a circular buffer where index is size_t and underlying buffer may be anything I please, I need to iterate both forward and backwards like so: from head to tail and from tail to head (in other words I need to be able to iterate over all cells in this buffer).

I have following functions:

size_t prevIdxCircularBuffer(size_t head, size_t size, size_t capacity, size_t idx)
{
    const size_t sizeWithGuard = size + 1;
    const size_t prevOffset = (idx + capacity - 1 - head) % capacity;
    const size_t sizeCorrectedPrevOffset = prevOffset % sizeWithGuard;
    return (head + sizeCorrectedPrevOffset) % capacity;
}

size_t nextIdxCircularBuffer(size_t head, size_t size, size_t capacity, size_t idx)
{
    const size_t sizeWithGuard = size + 1;
    const size_t nextOffset = (idx + capacity + 1 - head) % capacity;
    const size_t sizeCorrectedNextOffset = nextOffset % sizeWithGuard;
    return (head + sizeCorrectedNextOffset) % capacity;
}

Small description: sizeWithGuard is size plus tail 'guard' element (tail is empty).

And while next works fine, previous always fails to iterate from head to tail when idx == head (it always results in capacity - 1). I am looking for a clue on how to change this algorithm to work always.

What is crucial for me is that index needs to be of size_t type and there are no branches in the code (otherwise this problem would be non-existent :)

Celeborth
  • 48
  • 4

1 Answers1

0

The trick is that there are two types of indexes: list indexes, ranging from 0 to size - 1 (or, in your case, 0 to size with the tail guard) and buffer indexes ranging from 0 to capacity - 1. I propose changing the code a bit to clarify this.

size_t prevIdxCircularBuffer(size_t head, size_t size, size_t capacity, size_t idx) {
    // at the beginning, idx is a buffer index
    const size_t sizeWithGuard = size + 1;

    // listIdx is the same index, just translated to a list index
    const size_t listIdx = (idx + capacity - head) % capacity;

    // sizeCorrectedPrevOffset is the list index of the previous element
    // (ranging from 0 to size inclusively)
    const size_t sizeCorrectedPrevOffset = (prevOffset + sizeWithGuard - 1) % sizeWithGuard;

    // finally convert sizeCorrectedPrevOffset back to a buffer index
    // and return it
    return (head + sizeCorrectedPrevOffset) % capacity;
}

So what I changed was:

  1. Removed the -1 subtraction from prevOffset. Renamed that variable to listIdx since it is no longer "previous".
  2. Included the -1 subtraction in sizeCorrectedPrevOffset (also added sizeWithGuard so we stay within bounds).

What is the difference? Here is an example:

        tail                               head=idx
+-----------+-----------------------------+---------------+
|           |                             |               |
+-----------+-----------------------------+---------------+
 0         8                               20           36

capacity=37
head=20
idx=20
size=25+1 guard element, spanning positions [20-36] and [0-8]

My version of the code does:

  • listIdx = (20 + 37 - 20) % 27 = 0: it correctly realizes its list index is 0 (the head);
  • sizeCorrectedPrevOffset = (0 + 26 - 1) % 26 = 25: the new list index should be 25 (the tail);
  • returns (20 + 25) % 37 = 8: the buffer index of the tail element

In contrast, your code does:

  • prevOffset = (20 + 37 - 1 - 20) % 37 = 36 % 37 = 36 and it goes off-track from here.
Cătălin Frâncu
  • 1,179
  • 8
  • 15