#include #include #include #include #include #define TH_MAX_SUM 21 typedef struct thorough THOROUGH; typedef struct testcase TESTCASE; struct thorough { int N; int c[TH_MAX_SUM]; int spre[TH_MAX_SUM]; int spost[TH_MAX_SUM]; int n; } ; struct testcase { const char *name; const unsigned char *decoded; int declen; const char *encoded; int enclen; } ; static const TESTCASE testcases[] = { { "\"a\"", "a", 1, "YQ==", 4 }, { "\"ab\"", "ab", 2, "YWI=", 4 }, { "\"abc\"", "abc", 3, "YWJj", 4 }, { "\"abcdefgh\"", "abcdefgh", 8, "YWJjZGVmZ2g=", 12 }, { "random binary data", "\x1d\xa1\xbc\x6b\x84\x30\x97\x15\x0e", 9, "HaG8a4QwlxUO", 12 }, { "abc...xyz 208", "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", 208, "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXphYmNkZWZnaGlq" "a2xtbm9wcXJzdHV2d3h5emFiY2RlZmdoaWprbG1ub3BxcnN0" "dXZ3eHl6YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXphYmNk" "ZWZnaGlqa2xtbm9wcXJzdHV2d3h5emFiY2RlZmdoaWprbG1u" "b3BxcnN0dXZ3eHl6YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4" "eXphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5eg==", 280 } }; #define N_TESTCASES (sizeof(testcases) / sizeof(testcases[0])) static char cookie_val; static const char *i_s; static int i_l; static int i_p; static const char *o_s; static int o_l; static int o_p; static THOROUGH th_i; static int i_x; static int i_o; static THOROUGH th_o; static int o_x; static int o_o; static void fail(const char *, ...) __attribute__((__format__(__printf__,1,2))); static void fail(const char *fmt, ...) { va_list ap; printf("FAILURE: "); va_start(ap,fmt); vprintf(fmt,ap); va_end(ap); printf("\n"); fflush(0); abort(); } static void thorough_init(THOROUGH *th, int n) { int i; if (n > TH_MAX_SUM) abort(); th->N = n; for (i=0;ic[i] = 1; th->spre[i] = i; th->spost[i] = i + 1; } th->n = n; } static int thorough_next(THOROUGH *th) { int x; x = th->n - 2; if (x < 0) return(0); th->c[x] ++; th->spost[x] ++; while (th->spost[x] < th->N) { x ++; th->spre[x] = th->spost[x-1]; th->c[x] = 1; th->spost[x] = th->spre[x] + 1; } th->n = x + 1; return(1); } static int r_in_blob(void *cookie, void *buf, int len) { if (cookie != &cookie_val) fail("cookie wrong"); if (len < 1) fail("asking for nothing"); if (i_p >= i_l) return(0); if (len > i_l-i_p) len = i_l - i_p; bcopy(i_s+i_p,buf,len); i_p += len; return(len); } static int r_in_onechar(void *cookie, void *buf, int len) { if (cookie != &cookie_val) fail("cookie wrong"); if (len < 1) fail("asking for nothing"); if (i_p >= i_l) return(0); *(char *)buf = i_s[i_p++]; return(1); } static int r_in_thorough(void *cookie, void *buf, int len) { int n; if (cookie != &cookie_val) fail("cookie wrong"); if (i_x >= th_i.n) return(0); n = th_i.c[i_x] - i_o; if (n > len) n = len; bcopy(i_s+th_i.spre[i_x]+i_o,buf,n); i_o += n; if (i_o > th_i.c[i_x]) abort(); if (i_o == th_i.c[i_x]) { i_x ++; i_o = 0; } return(n); } static int r_out_blob(int max) { if (max < 1) fail("asking for nothing"); return(max); } static int r_out_onechar(int max) { if (max < 1) fail("asking for nothing"); return(1); } static int r_out_thorough(int max) { int n; if (max < 1) fail("asking for nothing"); if (o_x >= th_o.n) return(0); n = th_o.c[o_x] - o_o; if (n > max) n = max; o_o += n; if (o_o > th_o.c[o_x]) abort(); if (o_o == th_o.c[o_x]) { o_x ++; o_o = 0; } return(n); } static void r_test( const char *in, int inlen, void *(*fn)(void *, int (*)(void *, void *, int)), int (*icb)(void *, void *, int), int (*ocb)(int), const char *out, int outlen ) { void *h; char obuf[8192]; int want; int got; i_s = in; i_l = inlen; i_p = 0; o_s = out; o_l = outlen; o_p = 0; h = (*fn)(&cookie_val,icb); want = -1; while (1) { if (want <= 0) want = (*ocb)(sizeof(obuf)); got = base64_r(h,&obuf[0],want); if (got < 0) fail("read error"); if (got > want) fail("read overrun"); if (got > o_l-o_p) fail("read too much"); if (! got) { if (o_p < o_l) fail("premature EOF"); break; } if (bcmp(&obuf[0],o_s+o_p,got)) fail("read wrong data"); o_p += got; want -= got; } base64_close(h); } static int stdio_r_in(void *cookie __attribute__((__unused__)), char *data, int len) { int n; n = i_l - i_p; if (n > len) n = len; if (n > 0) bcopy(i_s+i_p,data,n); i_p += n; return(n); } static void stdio_r_test(const char *in, int inlen, unsigned int op, const char *out, int outlen) { FILE *f; FILE *i; int c; int ox; i_s = in; i_l = inlen; i_p = 0; i = fropen(0,&stdio_r_in); if (! i) fail("stdio read inner open failed"); f = base64_fwrap(i,op|BASE64_DIR_R|BASE64_CLOSE); if (! f) fail("stdio read outer open failed"); for (ox=0;ox= 0) fail("stdio read overrun"); if (ferror(f)) fail("stdio read error 2"); if (! feof(f)) fail("stdio weird error"); fclose(f); } static int stdio_w_out(void *cookie __attribute__((__unused__)), const char *data, int len) { if (len > o_l-o_p) fail("stdio write overrun"); if (bcmp(o_s+o_p,data,len)) fail("stdio write data wrong"); o_p += len; return(len); } static void stdio_w_test(const char *in, int inlen, unsigned int op, const char *out, int outlen) { FILE *f; FILE *o; int n; o_s = out; o_l = outlen; o_p = 0; o = fwopen(0,&stdio_w_out); if (! o) fail("stdio write inner open failed"); f = base64_fwrap(o,op|BASE64_DIR_W|BASE64_CLOSE); if (! f) fail("stdio write outer open failed"); n = fwrite(in,1,inlen,f); if (n < 0) fail("stdio write error"); if (n != inlen) fail("stdio write shortfall"); fclose(f); if (o_p != o_l) fail("stdio write underrun"); } static void r_tests( const char *in, int inlen, void *(*fn)(void *, int (*)(void *, void *, int)), unsigned int op, const char *out, int outlen ) { r_test(in,inlen,fn,&r_in_blob,&r_out_blob,out,outlen); r_test(in,inlen,fn,&r_in_onechar,&r_out_blob,out,outlen); r_test(in,inlen,fn,&r_in_blob,&r_out_onechar,out,outlen); r_test(in,inlen,fn,&r_in_onechar,&r_out_onechar,out,outlen); if (inlen+outlen <= TH_MAX_SUM) { thorough_init(&th_i,inlen); do { thorough_init(&th_o,outlen); do { i_x = 0; i_o = 0; o_x = 0; o_o = 0; r_test(in,inlen,fn,&r_in_thorough,&r_out_thorough,out,outlen); } while (thorough_next(&th_o)); } while (thorough_next(&th_i)); } stdio_r_test(in,inlen,op,out,outlen); } static int w_output_cb(void *cookie, const void *data, int len) { if (cookie != &cookie_val) fail("cookie wrong"); if (len < 1) fail("write callback interface violated"); if (len > o_l-o_p) fail("write overrun"); if (bcmp(data,o_s+o_p,len)) fail("write wrong data"); o_p += len; return(len); } static int w_in_blob(void) { return(i_l-i_p); } static int w_in_onechar(void) { return(1); } static int w_in_thorough(void) { if (i_x >= th_i.n) abort(); /* can't happen: tester wants too much */ return(th_i.c[i_x++]); } static void w_test( const char *in, int inlen, void *(*fn)(void *, int (*)(void *, const void *, int)), int (*icb)(void), const char *out, int outlen ) { void *h; int n; int w; i_s = in; i_l = inlen; i_p = 0; o_s = out; o_l = outlen; o_p = 0; h = (*fn)(&cookie_val,&w_output_cb); while (i_p < i_l) { n = (*icb)(); if (i_p+n > i_l) abort(); /* can't happen: (*icb)() violated interface */ w = base64_w(h,i_s+i_p,n); if (w < 0) fail("write error"); if (w != n) fail("base64_w violated interface"); i_p += n; } base64_close(h); if (o_p != o_l) fail("output underrun"); } static void w_tests( const char *in, int inlen, void *(*fn)(void *, int (*)(void *, const void *, int)), unsigned int op, const char *out, int outlen ) { w_test(in,inlen,fn,&w_in_blob,out,outlen); w_test(in,inlen,fn,&w_in_onechar,out,outlen); if (inlen <= TH_MAX_SUM) { thorough_init(&th_i,inlen); do { i_x = 0; w_test(in,inlen,fn,&w_in_thorough,out,outlen); } while (thorough_next(&th_i)); } stdio_w_test(in,inlen,op,out,outlen); } static void run_read_test_e_r(const TESTCASE *tc) { printf("Read encode %s: ",tc->name); fflush(stdout); r_tests(tc->decoded,tc->declen,&base64_e_r_cb,BASE64_OP_E,tc->encoded,tc->enclen); printf("passed\n"); } static void run_read_test_d_r(const TESTCASE *tc) { printf("Read decode %s: ",tc->name); fflush(stdout); r_tests(tc->encoded,tc->enclen,&base64_d_r_cb,BASE64_OP_D,tc->decoded,tc->declen); printf("passed\n"); } static void run_read_test_e_w(const TESTCASE *tc) { printf("Write encode %s: ",tc->name); fflush(stdout); w_tests(tc->decoded,tc->declen,&base64_e_w_cb,BASE64_OP_E,tc->encoded,tc->enclen); printf("passed\n"); } static void run_read_test_d_w(const TESTCASE *tc) { printf("Write decode %s: ",tc->name); fflush(stdout); w_tests(tc->encoded,tc->enclen,&base64_d_w_cb,BASE64_OP_D,tc->decoded,tc->declen); printf("passed\n"); } static void for_each_test(void (*fn)(const TESTCASE *)) { int i; for (i=0;i