Rewrite unit tests for the Fletcher and Adler checksums to exercise

boundary conditions (low and high word overflow).
This commit is contained in:
Dag-Erling Smørgrav 2015-08-28 08:38:49 +00:00 committed by des
parent 8351719b84
commit 0961e4b2e0
2 changed files with 257 additions and 111 deletions

View file

@ -30,97 +30,95 @@
#include "cryb/impl.h" #include "cryb/impl.h"
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
#include <string.h> #include <string.h>
#include <cryb/endian.h>
#include <cryb/hash.h> #include <cryb/hash.h>
#include "t.h" #include "t.h"
#define ADLER32_INVALID 0xffffffffLU
#define TRI(n) (n * (n + 1) / 2)
static uint8_t ones8[65536];
struct t_case { struct t_case {
const char *desc; const char *desc;
const char *data; const void *data;
size_t len; size_t len;
uint32_t sum; uint32_t sum;
}; };
/*************************************************************************** /***************************************************************************
* Test cases * Test cases
*
* Most of these test cases use input sequences where each word has the
* value 1. To understand the test cases, it helps to realize that (until
* it overflows) the low word of the Adler checksum of such a sequence of
* length n is equal to n + 1, while the high word is equal to the nth
* triangular number plus n, i.e. (n*(n+1))/2+n. Also, unlike Fletcher32,
* the input width for Adler32 is always 8 bits.
*/ */
#define TRIpN(n) ((n * (n + 1) / 2) + n)
static struct t_case t_cases[] = { static struct t_case t_cases[] = {
/* trivial cases */
{ {
.desc = "len == 0", .desc = "all-zeroes message",
.data = "\x01\x02\x03\x04\x05\x06\x07\x08", .data = t_zero,
.len = sizeof t_zero,
.sum = sizeof t_zero << 16 | 0x0001U,
},
{
.desc = "length 0",
.data = ones8,
.len = 0, .len = 0,
.sum = 0x00000001, .sum = 0x00000001,
}, },
/* overflow */
{ {
.desc = "len == 1", .desc = "high overflow - 1",
.data = "\x01\x02\x03\x04\x05\x06\x07\x08", .data = ones8,
.len = 1, .len = 359,
.sum = 0x00020002, .sum = (TRIpN(359U) % 65521U) << 16 | 360U,
}, },
{ {
.desc = "len == 2", .desc = "high overflow",
.data = "\x01\x02\x03\x04\x05\x06\x07\x08", .data = ones8,
.len = 2, .len = 363,
.sum = 0x00060004, .sum = (TRIpN(363U) % 65521U) << 16 | 364U,
}, },
{ {
.desc = "len == 3", .desc = "low overflow - 1",
.data = "\x01\x02\x03\x04\x05\x06\x07\x08", .data = ones8,
.len = 3, .len = 65519,
.sum = 0x000d0007, .sum = (TRIpN(65519U) % 65521U) << 16 | 65520U,
}, },
{ {
.desc = "len == 4", .desc = "low overflow",
.data = "\x01\x02\x03\x04\x05\x06\x07\x08", .data = ones8,
.len = 4, .len = 65520,
.sum = 0x0018000b, .sum = (TRIpN(65520U) % 65521U) << 16 | 0U,
}, },
{ {
.desc = "len == 5", .desc = "low overflow + 1",
.data = "\x01\x02\x03\x04\x05\x06\x07\x08", .data = ones8,
.len = 5, .len = 65521,
.sum = 0x00280010, .sum = ((TRIpN(65520U) + 1) % 65521U) << 16 | 1U,
}, },
{
.desc = "len == 6",
.data = "\x01\x02\x03\x04\x05\x06\x07\x08",
.len = 6,
.sum = 0x003e0016,
},
{
.desc = "len == 7",
.data = "\x01\x02\x03\x04\x05\x06\x07\x08",
.len = 7,
.sum = 0x005b001d,
},
{
.desc = "len == 8",
.data = "\x01\x02\x03\x04\x05\x06\x07\x08",
.len = 8,
.sum = 0x00800025,
},
{
.desc = "zeroes",
.data = (const char *)t_zero,
.len = sizeof t_zero,
.sum = (sizeof t_zero << 16) | 1,
},
/* XXX need non-zero cases which exercise modulo */
}; };
/*************************************************************************** /***************************************************************************
* Test function * Test function
*/ */
static int static int
t_adler(char **desc CRYB_UNUSED, void *arg) t_adler32(char **desc CRYB_UNUSED, void *arg)
{ {
struct t_case *t = arg; struct t_case *t = arg;
int ret;
ret = t_compare_x32(t->sum, adler32_hash(t->data, t->len)); return (t_compare_x32(t->sum, adler32_hash(t->data, t->len)));
return (ret);
} }
@ -131,13 +129,20 @@ t_adler(char **desc CRYB_UNUSED, void *arg)
int int
t_prepare(int argc, char *argv[]) t_prepare(int argc, char *argv[])
{ {
int i, n; unsigned int i, n;
(void)argc; (void)argc;
(void)argv; (void)argv;
ones8[0] = 1;
be16enc(&ones16[0], 1);
for (i = 1; i < 65536; ++i) {
ones8[i] = ones8[0];
ones16[i] = ones16[0];
}
n = sizeof t_cases / sizeof t_cases[0]; n = sizeof t_cases / sizeof t_cases[0];
for (i = 0; i < n; ++i) for (i = 0; i < n; ++i)
t_add_test(t_adler, &t_cases[i], t_cases[i].desc); if (t_cases[i].sum != 0xffffffffLU)
t_add_test(t_adler32, &t_cases[i], t_cases[i].desc);
return (0); return (0);
} }

View file

@ -30,15 +30,25 @@
#include "cryb/impl.h" #include "cryb/impl.h"
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
#include <string.h> #include <string.h>
#include <cryb/endian.h>
#include <cryb/hash.h> #include <cryb/hash.h>
#include "t.h" #include "t.h"
#define FLETCHER16_INVALID 0xffffU
#define FLETCHER32_INVALID 0xffffffffLU
#define FLETCHER64_INVALID 0xffffffffffffffffLLU
static uint8_t ones8[65536];
static uint16_t ones16[65536];
static uint32_t ones32[65536];
struct t_case { struct t_case {
const char *desc; const char *desc;
const char *data; const void *data;
size_t len; size_t len;
uint16_t sum16; uint16_t sum16;
uint32_t sum32; uint32_t sum32;
@ -47,105 +57,221 @@ struct t_case {
/*************************************************************************** /***************************************************************************
* Test cases * Test cases
*
* Most of these test cases use input sequences where each word has the
* value 1. To understand the test cases, it helps to realize that (until
* it overflows) the low word of the Fletcher checksum of such a sequence
* of length n is equal to n, while the high word is equal to the nth
* triangular number, i.e. (n*(n+1))/2+n. Also, remember that the input
* width for Fletcher is half the checksum width.
*/ */
#define TRI(n) (n * (n + 1) / 2)
static struct t_case t_cases[] = { static struct t_case t_cases[] = {
/* trivial cases */
{ {
.desc = "len == 0", .desc = "all-zeroes message",
.data = "\x01\x02\x03\x04\x05\x06\x07\x08", .data = t_zero,
.len = 0, .len = sizeof t_zero,
.sum16 = 0x00, .sum16 = 0x00,
.sum32 = 0x0000, .sum32 = 0x0000,
.sum64 = 0x00000000, .sum64 = 0x00000000,
}, },
{ {
.desc = "len == 1", .desc = "length 0",
.data = "\x01\x02\x03\x04\x05\x06\x07\x08", .data = ones8,
.len = 0,
.sum16 = 0x00,
.sum32 = 0x0000,
.sum64 = 0x00000000,
},
/* padding */
{
.desc = "length 1",
.data = ones8,
.len = 1, .len = 1,
.sum16 = 0x0101, .sum16 = 0x0101,
.sum32 = 0x01000100, .sum32 = 0x01000100,
.sum64 = 0x0100000001000000, .sum64 = 0x0100000001000000,
}, },
{ {
.desc = "len == 2", .desc = "length 2",
.data = "\x01\x02\x03\x04\x05\x06\x07\x08", .data = ones8,
.len = 2, .len = 2,
.sum16 = 0x0403, .sum16 = 0x0302,
.sum32 = 0x01020102, .sum32 = 0x01010101,
.sum64 = 0x0102000001020000, .sum64 = 0x0101000001010000,
}, },
{ {
.desc = "len == 3", .desc = "length 3",
.data = "\x01\x02\x03\x04\x05\x06\x07\x08", .data = ones8,
.len = 3, .len = 3,
.sum16 = 0x0a06, .sum16 = 0x0603,
.sum32 = 0x05040402, .sum32 = 0x03020201,
.sum64 = 0x102030001020300, .sum64 = 0x0101010001010100,
}, },
{ {
.desc = "len == 4", .desc = "length 4",
.data = "\x01\x02\x03\x04\x05\x06\x07\x08", .data = ones8,
.len = 4, .len = 4,
.sum16 = 0x140a, .sum16 = 0x0a04,
.sum32 = 0x05080406, .sum32 = 0x03030202,
.sum64 = 0x0102030401020304, .sum64 = 0x0101010101010101,
}, },
{ {
.desc = "len == 5", .desc = "length 5",
.data = "\x01\x02\x03\x04\x05\x06\x07\x08", .data = ones8,
.len = 5, .len = 5,
.sum16 = 0x230f, .sum16 = 0x0f05,
.sum32 = 0x0e0e0906, .sum32 = 0x06050302,
.sum64 = 0x0704060806020304, .sum64 = 0x0302020202010101,
}, },
{ {
.desc = "len == 6", .desc = "length 6",
.data = "\x01\x02\x03\x04\x05\x06\x07\x08", .data = ones8,
.len = 6, .len = 6,
.sum16 = 0x3815, .sum16 = 0x1506,
.sum32 = 0x0e14090c, .sum32 = 0x06060303,
.sum64 = 0x070a060806080304, .sum64 = 0x0303020202020101,
}, },
{ {
.desc = "len == 7", .desc = "length 7",
.data = "\x01\x02\x03\x04\x05\x06\x07\x08", .data = ones8,
.len = 7, .len = 7,
.sum16 = 0x541c, .sum16 = 0x1c07,
.sum32 = 0x1e20100c, .sum32 = 0x0a090403,
.sum64 = 0x070a0d0806080a04, .sum64 = 0x0303030202020201,
}, },
{ {
.desc = "len == 8", .desc = "length 8",
.data = "\x01\x02\x03\x04\x05\x06\x07\x08", .data = ones8,
.len = 8, .len = 8,
.sum16 = 0x7824, .sum16 = 0x2408,
.sum32 = 0x1e281014, .sum32 = 0x0a0a0404,
.sum64 = 0x070a0d1006080a0c, .sum64 = 0x0303030302020202,
}, },
/* not really useful... */
/* overflow (16-bit checksum) */
{ {
.desc = "zeroes", .desc = "high overflow - 1",
.data = (const char *)t_zero, .data = ones8,
.len = sizeof t_zero, .len = 22,
.sum16 = 0x00, .sum16 = (TRI(22) % 255) << 8 | 22,
.sum32 = 0x0000, .sum32 = FLETCHER32_INVALID,
.sum64 = 0x00000000, .sum64 = FLETCHER64_INVALID,
}, },
/* XXX need non-zero cases which exercise modulo */ {
.desc = "high overflow",
.data = ones8,
.len = 23,
.sum16 = (TRI(23) % 255) << 8 | 23,
.sum32 = FLETCHER32_INVALID,
.sum64 = FLETCHER64_INVALID,
},
{
.desc = "low overflow - 1",
.data = ones8,
.len = 254,
.sum16 = (TRI(254) % 255) << 8 | 254,
.sum32 = FLETCHER32_INVALID,
.sum64 = FLETCHER64_INVALID,
},
{
.desc = "low overflow",
.data = ones8,
.len = 255,
.sum16 = (TRI(254) % 255) << 8 | 0,
.sum32 = FLETCHER32_INVALID,
.sum64 = FLETCHER64_INVALID,
},
{
.desc = "low overflow + 1",
.data = ones8,
.len = 256,
.sum16 = ((TRI(254) + 1) % 255) << 8 | 1,
.sum32 = FLETCHER32_INVALID,
.sum64 = FLETCHER64_INVALID,
},
/* overflow (32-bit checksum) */
{
.desc = "high overflow - 1",
.data = ones16,
.len = 362 * sizeof ones16[0],
.sum16 = FLETCHER16_INVALID,
.sum32 = (TRI(362U) % 65535U) << 16 | 362U,
.sum64 = FLETCHER64_INVALID,
},
{
.desc = "high overflow",
.data = ones16,
.len = 363 * sizeof ones16[0],
.sum16 = FLETCHER16_INVALID,
.sum32 = (TRI(363U) % 65535U) << 16 | 363U,
.sum64 = FLETCHER64_INVALID,
},
{
.desc = "low overflow - 1",
.data = ones16,
.len = 65534 * sizeof ones16[0],
.sum16 = FLETCHER16_INVALID,
.sum32 = (TRI(65534U) % 65535U) << 16 | 65534U,
.sum64 = FLETCHER64_INVALID,
},
{
.desc = "low overflow",
.data = ones16,
.len = 65535 * sizeof ones16[0],
.sum16 = FLETCHER16_INVALID,
.sum32 = (TRI(65534U) % 65535U) << 16 | 0U,
.sum64 = FLETCHER64_INVALID,
},
{
.desc = "low overflow + 1",
.data = ones16,
.len = 65536 * sizeof ones16[0],
.sum16 = FLETCHER16_INVALID,
.sum32 = ((TRI(65534U) + 1) % 65535U) << 16 | 1U,
.sum64 = FLETCHER64_INVALID,
},
/* overflow (64-bit checksum */
/*
* This is currently not realistic. We need to change the
* fletcher*() API to take a seed so it can do partial /
* incremental checksums, and provide a precomputed seed just
* below the overflow point.
*/
}; };
/*************************************************************************** /***************************************************************************
* Test function * Test function
*/ */
static int static int
t_fletcher(char **desc CRYB_UNUSED, void *arg) t_fletcher16(char **desc, void *arg)
{ {
struct t_case *t = arg; struct t_case *t = arg;
int ret;
ret = t_compare_x16(t->sum16, fletcher16_hash(t->data, t->len)) & asprintf(desc, "(16-bit) %s", t->desc);
t_compare_x32(t->sum32, fletcher32_hash(t->data, t->len)) & return (t_compare_x16(t->sum16, fletcher16_hash(t->data, t->len)));
t_compare_x64(t->sum64, fletcher64_hash(t->data, t->len)); }
return (ret);
static int
t_fletcher32(char **desc, void *arg)
{
struct t_case *t = arg;
asprintf(desc, "(32-bit) %s", t->desc);
return (t_compare_x32(t->sum32, fletcher32_hash(t->data, t->len)));
}
static int
t_fletcher64(char **desc, void *arg)
{
struct t_case *t = arg;
asprintf(desc, "(64-bit) %s", t->desc);
return (t_compare_x64(t->sum64, fletcher64_hash(t->data, t->len)));
} }
@ -156,13 +282,28 @@ t_fletcher(char **desc CRYB_UNUSED, void *arg)
int int
t_prepare(int argc, char *argv[]) t_prepare(int argc, char *argv[])
{ {
int i, n; unsigned int i, n;
(void)argc; (void)argc;
(void)argv; (void)argv;
ones8[0] = 1;
be16enc(&ones16[0], 1);
be32enc(&ones32[0], 1);
for (i = 1; i < 65536; ++i) {
ones8[i] = ones8[0];
ones16[i] = ones16[0];
ones32[i] = ones32[0];
}
n = sizeof t_cases / sizeof t_cases[0]; n = sizeof t_cases / sizeof t_cases[0];
for (i = 0; i < n; ++i) for (i = 0; i < n; ++i)
t_add_test(t_fletcher, &t_cases[i], t_cases[i].desc); if (t_cases[i].sum16 != 0xffffU)
t_add_test(t_fletcher16, &t_cases[i], t_cases[i].desc);
for (i = 0; i < n; ++i)
if (t_cases[i].sum32 != 0xffffffffLU)
t_add_test(t_fletcher32, &t_cases[i], t_cases[i].desc);
for (i = 0; i < n; ++i)
if (t_cases[i].sum64 != 0xffffffffffffffffLLU)
t_add_test(t_fletcher64, &t_cases[i], t_cases[i].desc);
return (0); return (0);
} }