我有以下问题,尽管有很多方法都不能满足我的需求:有一个索引为size_t
的循环缓冲区,而底层缓冲区可能是我喜欢的任何东西,我需要像这样迭代前后循环:从头到尾,从尾到头(换句话说,我需要能够遍历此缓冲区中的所有单元格)。
我具有以下功能:
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;
}
简短说明:sizeWithGuard
是尺寸加上尾部'guard'元素(尾巴为空)。
尽管下一个很好,但是当idx == head时,前一个总是不能从头到尾进行迭代(它总是导致capacity - 1
)。我正在寻找有关如何更改此算法以使其始终运行的线索。
对我来说,至关重要的是索引必须为size_t
类型,并且代码中没有分支(否则该问题将不存在:)
技巧是,有两种类型的索引:列表索引,范围从0到size-1(或者,在您的情况下,从0到size,带有尾部保护)和缓冲区索引的范围是0到capacity-1。我建议稍微更改一下代码以阐明这一点。
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;
}
所以我更改的是:
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]
我的代码版本是: