Fix errors in the ringbuffer operations

The ringbuffer calculations contained several flaws:

The ringbuffer_normalize() function doesn't shift the result from the
internal normalize call back to the range [begin,end-1]. The only reason
why this bug didn't cause any issues yet, is because this function isn't
used anywhere.

The ringbuffer_distance() function can return a wrong result whenever
the difference between the two values is an exact multiple of the
ringbuffer size. For example:

  distance(x,x,n)   == 0 or n (depending on the empty/full mode)
  distance(x,x+n,n) == 0
  distance(x+n,x,n) == n

So far this bug didn't cause any problems yet, because in practice this
function is always used with values inside the safe range [begin,end-1].

For input values outside the safe range [begin,end-1], only larger
values are accepted, while smaller values will trigger an assert.

A zero-length ringbuffer (e.g. begin == end) results in a division by
zero.
This commit is contained in:
Jef Driesen 2024-02-16 20:15:41 +01:00
parent 04fe252625
commit 1ba7e5cad0

View File

@ -25,39 +25,43 @@
static unsigned int static unsigned int
normalize (unsigned int a, unsigned int size) modulo (unsigned int x, unsigned int n, unsigned int d)
{ {
return a % size; unsigned int result = 0;
} if (d > x) {
#if 0
result = (n - (d - x) % n) % n;
static unsigned int #else
distance (unsigned int a, unsigned int b, int mode, unsigned int size) unsigned int m = (d - x) % n;
{ result = m ? n - m : m;
if (a < b) { #endif
return (b - a) % size;
} else if (a > b) {
return size - (a - b) % size;
} else { } else {
return (mode == 0 ? 0 : size); result = (x - d) % n;
} }
return result + d;
} }
static unsigned int static unsigned int
increment (unsigned int a, unsigned int delta, unsigned int size) distance (unsigned int a, unsigned int b, unsigned int n, unsigned int mode)
{ {
return (a + delta) % size; unsigned int result = 0;
} if (a > b) {
#if 0
result = (n - (a - b) % n) % n;
static unsigned int #else
decrement (unsigned int a, unsigned int delta, unsigned int size) unsigned int m = (a - b) % n;
{ result = m ? n - m : m;
if (delta <= a) { #endif
return (a - delta) % size;
} else { } else {
return size - (delta - a) % size; result = (b - a) % n;
}
if (result == 0) {
return (mode == 0 ? 0 : n);
} else {
return result;
} }
} }
@ -65,38 +69,38 @@ decrement (unsigned int a, unsigned int delta, unsigned int size)
unsigned int unsigned int
ringbuffer_normalize (unsigned int a, unsigned int begin, unsigned int end) ringbuffer_normalize (unsigned int a, unsigned int begin, unsigned int end)
{ {
assert (end >= begin); assert (end > begin);
assert (a >= begin);
return normalize (a, end - begin); unsigned int n = end - begin;
return modulo (a, n, begin);
} }
unsigned int unsigned int
ringbuffer_distance (unsigned int a, unsigned int b, int mode, unsigned int begin, unsigned int end) ringbuffer_distance (unsigned int a, unsigned int b, int mode, unsigned int begin, unsigned int end)
{ {
assert (end >= begin); assert (end > begin);
assert (a >= begin);
return distance (a, b, mode, end - begin); unsigned int n = end - begin;
return distance (a, b, n, mode);
} }
unsigned int unsigned int
ringbuffer_increment (unsigned int a, unsigned int delta, unsigned int begin, unsigned int end) ringbuffer_increment (unsigned int a, unsigned int delta, unsigned int begin, unsigned int end)
{ {
assert (end >= begin); assert (end > begin);
assert (a >= begin);
return increment (a - begin, delta, end - begin) + begin; unsigned int n = end - begin;
return modulo (a + delta % n, n, begin);
} }
unsigned int unsigned int
ringbuffer_decrement (unsigned int a, unsigned int delta, unsigned int begin, unsigned int end) ringbuffer_decrement (unsigned int a, unsigned int delta, unsigned int begin, unsigned int end)
{ {
assert (end >= begin); assert (end > begin);
assert (a >= begin);
return decrement (a - begin, delta, end - begin) + begin; unsigned int n = end - begin;
return modulo (a + n - delta % n, n, begin);
} }