Commit e0fd4d11 authored by Rusty Russell's avatar Rusty Russell

tally: implement tally_total()

Not as trivial as you might think, with overflow and underflow.
parent 95bf6d3f
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
#include <stdio.h> #include <stdio.h>
#include <assert.h> #include <assert.h>
#define MAX_STEP_BITS (sizeof(size_t)*CHAR_BIT) #define SIZET_BITS (sizeof(size_t)*CHAR_BIT)
/* We use power of 2 steps. I tried being tricky, but it got buggy. */ /* We use power of 2 steps. I tried being tricky, but it got buggy. */
struct tally { struct tally {
...@@ -55,9 +55,9 @@ struct tally *tally_new(size_t buckets) ...@@ -55,9 +55,9 @@ struct tally *tally_new(size_t buckets)
static unsigned bucket_of(ssize_t min, unsigned step_bits, ssize_t val) static unsigned bucket_of(ssize_t min, unsigned step_bits, ssize_t val)
{ {
/* Don't over-shift. */ /* Don't over-shift. */
if (step_bits == MAX_STEP_BITS) if (step_bits == SIZET_BITS)
return 0; return 0;
assert(step_bits < MAX_STEP_BITS); assert(step_bits < SIZET_BITS);
return (size_t)(val - min) >> step_bits; return (size_t)(val - min) >> step_bits;
} }
...@@ -65,9 +65,9 @@ static unsigned bucket_of(ssize_t min, unsigned step_bits, ssize_t val) ...@@ -65,9 +65,9 @@ static unsigned bucket_of(ssize_t min, unsigned step_bits, ssize_t val)
static ssize_t bucket_min(ssize_t min, unsigned step_bits, unsigned b) static ssize_t bucket_min(ssize_t min, unsigned step_bits, unsigned b)
{ {
/* Don't over-shift. */ /* Don't over-shift. */
if (step_bits == MAX_STEP_BITS) if (step_bits == SIZET_BITS)
return min; return min;
assert(step_bits < MAX_STEP_BITS); assert(step_bits < SIZET_BITS);
return min + ((ssize_t)b << step_bits); return min + ((ssize_t)b << step_bits);
} }
...@@ -324,6 +324,33 @@ ssize_t tally_mean(const struct tally *tally) ...@@ -324,6 +324,33 @@ ssize_t tally_mean(const struct tally *tally)
return divls64(tally->total[1], tally->total[0], count); return divls64(tally->total[1], tally->total[0], count);
} }
ssize_t tally_total(const struct tally *tally, ssize_t *overflow)
{
if (overflow) {
*overflow = tally->total[1];
return tally->total[0];
}
/* If result is negative, make sure we can represent it. */
if (tally->total[1] & (1 << (SIZET_BITS-1))) {
/* Must have only underflowed once, and must be able to
* represent result at ssize_t. */
if ((~tally->total[1])+1 != 0
|| (ssize_t)tally->total[0] >= 0) {
/* Underflow, return minimum. */
return (ssize_t)((size_t)1 << (SIZET_BITS - 1));
}
} else {
/* Result is positive, must not have overflowed, and must be
* able to represent as ssize_t. */
if (tally->total[1] || (ssize_t)tally->total[0] < 0) {
/* Overflow. Return maximum. */
return (ssize_t)~((size_t)1 << (SIZET_BITS - 1));
}
}
return tally->total[0];
}
static ssize_t bucket_range(const struct tally *tally, unsigned b, size_t *err) static ssize_t bucket_range(const struct tally *tally, unsigned b, size_t *err)
{ {
ssize_t min, max; ssize_t min, max;
......
...@@ -51,6 +51,17 @@ ssize_t tally_max(const struct tally *tally); ...@@ -51,6 +51,17 @@ ssize_t tally_max(const struct tally *tally);
*/ */
ssize_t tally_mean(const struct tally *tally); ssize_t tally_mean(const struct tally *tally);
/**
* tally_total - the total value passed to tally_add.
* @tally: the tally structure.
* @overflow: the overflow value (or NULL).
*
* If your total can't overflow a ssize_t, you don't need @overflow.
* Otherwise, @overflow is the upper ssize_t, and the return value should
* be treated as the lower size_t (ie. the sign bit is in @overflow).
*/
ssize_t tally_total(const struct tally *tally, ssize_t *overflow);
/** /**
* tally_approx_median - the approximate median value passed to tally_add. * tally_approx_median - the approximate median value passed to tally_add.
* @tally: the tally structure. * @tally: the tally structure.
......
#include <ccan/tally/tally.c>
#include <ccan/tap/tap.h>
int main(void)
{
struct tally *tally;
ssize_t total, overflow;
ssize_t min, max;
max = (ssize_t)~(1ULL << (sizeof(max)*CHAR_BIT - 1));
min = (ssize_t)(1ULL << (sizeof(max)*CHAR_BIT - 1));
plan_tests(15);
/* Simple case. */
tally = tally_new(0);
tally_add(tally, min);
ok1(tally_total(tally, NULL) == min);
ok1(tally_total(tally, &overflow) == min);
ok1(overflow == -1);
/* Underflow. */
tally_add(tally, min);
total = tally_total(tally, &overflow);
ok1(overflow == -1);
ok1((size_t)total == 0);
ok1(tally_total(tally, NULL) == min);
free(tally);
/* Simple case. */
tally = tally_new(0);
tally_add(tally, max);
ok1(tally_total(tally, NULL) == max);
ok1(tally_total(tally, &overflow) == max);
ok1(overflow == 0);
/* Overflow into sign bit... */
tally_add(tally, max);
total = tally_total(tally, &overflow);
ok1(overflow == 0);
ok1((size_t)total == 0xFFFFFFFE);
ok1(tally_total(tally, NULL) == max);
/* Overflow into upper size_t. */
tally_add(tally, max);
total = tally_total(tally, &overflow);
ok1(overflow == 1);
ok1((size_t)total == 0x7FFFFFFD);
ok1(tally_total(tally, NULL) == max);
free(tally);
return exit_status();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment