Below is my conversion of LihO's C++ answer to C: Base64 decode snippet in C++
As I mentioned in the comments, I thought it would be simple. But,there wasn't a simple cookbook to give to someone not familiar with C++. So, to do the conversion properly, I did it myself.
And, there were a few issues:
- The answer did a lot of unnecessary buffer copying.
- Using
std::string
and std::vector
, although pretty fast, are somewhat slower than using raw byte
buffers.
- Using
base64_chars.find
is O(n) but if an inverse table is created/used, it can be O(1).
- The answer did not support line breaks (e.g.
\n
) as most base64 programs do.
Anyway, below are the source files I came up with. They can be compiled with:
cc -I. -o b64test b64test.c base64.c dbgcom.c
Note that the mmap
mode (i.e. -m
) is untested.
FILE: base64.h
// base64.h -- base64 definitions
#ifndef _base64_h_
#define _base64_h_
#include <dbgcom.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
typedef struct {
u32 buf_opt; // buffer options
const char *buf_tag; // buffer name
byte *buf_base; // buffer pointer
size_t buf_len; // buffer length
int buf_fdmap; // mmap file descriptor
} b64buf_t;
#define B64BUFDEF(_sym) \
b64buf_t _sym = { .buf_tag = #_sym }
typedef struct {
//u32 ctl_opt; // options
const char *ctl_tag; // control name
b64buf_t ctl_inb; // input buffer
b64buf_t ctl_out; // output buffer
int ctl_wrapnow; // last output char was newline
int ctl_wraplen; // output wrap length
int ctl_wrapmax; // output wrap max
int ctl_fdchk; // file cross check
} b64ctl_t;
#define B64CTLDEF(_sym) \
b64ctl_t _sym = { .ctl_tag = #_sym }
#define B64MMAP (1u << 0) // 1=use mmap on output
#define B64TRIM (1u << 1) // 1=trim output file
// b64encode -- encode raw data to base64
// RETURNS actual output length
size_t
b64encode(b64ctl_t *ctl);
// b64decode -- decode base64 into raw binary
// RETURNS: actual output length
size_t
b64decode(b64ctl_t *ctl);
// b64bufdup -- safe buffer duplicate
void
b64bufdup(b64buf_t *dst,const b64buf_t *src);
// b64bufput -- output buffer to file
void
b64bufput(const b64buf_t *bufp,const char *ofile);
// b64bufget -- input file to buffer
void
b64bufget(b64buf_t *bufp,const char *ifile);
// b64setup -- passive setup
void
b64setup(b64ctl_t *ctl,u32 opt,const char *tag);
// b64init_encode -- initialize for encode
// RETURNS: 0=okay, -1=error
int
b64init_encode(b64ctl_t *ctl,const b64buf_t *inb,int wrapmax);
// b64init_decode -- initialize for decode
// RETURNS: 0=okay, -1=error
int
b64init_decode(b64ctl_t *ctl,b64buf_t *inb);
// b64destroy -- destroy/deinit output buffer
void
b64destroy(b64ctl_t *ctl);
// b64bufrls -- destroy/deinit output buffer
void
b64bufrls(b64buf_t *buf,const char *who);
#endif
FILE: base64.c
// base64.c -- base 64 encoder/decoder
#include <base64.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/mman.h>
static const char *base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
#ifndef B64DECODE_FAST
#define B64DECODE_FAST 1
#endif
#if B64DECODE_FAST
static byte decode_fast[256];
#endif
#define FMT "%2.2X/%c"
#define PRT(_c) _c,PRTABLE(_c)
#define PUT4(_idx,_val) \
arr4[_idx] = _val
#define PUTE(_idx,_val) \
arr4[_idx] = base64_chars[_val]
#define OUTLEN(_buf,_ptr) \
(_ptr - (byte *) (_buf)->buf_base)
// encode_xcheck -- cross-check encoding
#if B64ENCODE_XCHECK
#define encode_xcheck(_ctl,_arr4,_a4siz,_arr3,_a3siz,_pad) \
_encode_xcheck(_ctl,_arr4,_a4siz,_arr3,_a3siz,_pad)
static void
_encode_xcheck(b64ctl_t *ctl,
const byte *arr4,int a4siz,
const byte *arr3,int a3siz,int pad)
{
int xlen;
int oidx;
byte buf[a4siz];
dbgprt(ZPXHOWCHR,"encode_xcheck: ENTER a4siz=%d a3siz=%d pad=%d\n",
a4siz,a3siz,pad);
xlen = read(ctl->ctl_fdchk,buf,a4siz);
if (xlen != a4siz)
sysfault("encode_xcheck: read error -- a4siz=%d xlen=%d\n",
a4siz,xlen);
// check for error/mismatch
int errany = 0;
for (oidx = 0; oidx < a4siz; ++oidx) {
errany = (arr4[oidx] != buf[oidx]);
if (errany)
break;
}
do {
// only print if verbose mode or we have an error
if (! XVERBOSE) {
if (! errany)
break;
}
int totsiz = a4siz;
if (a3siz > totsiz)
totsiz = a3siz;
for (oidx = 0; oidx < totsiz; ++oidx) {
byte bp = 0;
byte a4 = 0;
byte a3 = 0;
size_t o4off = 0;
size_t o3off = 0;
if (oidx < a4siz) {
bp = buf[oidx];
a4 = arr4[oidx];
o4off = OUTLEN(&ctl->ctl_out,arr4);
}
if (oidx < a3siz) {
a3 = arr3[oidx];
o3off = OUTLEN(&ctl->ctl_inb,arr3);
}
int errcur = (arr4[oidx] != buf[oidx]);
printf("encode_xcheck: arr=%8.8zX/",o4off);
if (pad)
printf("censored");
else
printf("%8.8zX",o3off);
printf(" oidx=%d exp=" FMT " act=" FMT " arr3=" FMT,
oidx,PRT(bp),PRT(a4),PRT(a3));
if (errany)
printf(" ANY");
if (errcur)
printf("/CUR");
printf("\n");
}
if (errany)
sysfault("encode_xcheck: fault\n");
} while (0);
dbgprt(ZPXHOWCHR,"encode_xcheck: EXIT\n");
}
#else
#define encode_xcheck(_ctl,_arr4,_a4siz,_arr3,_a3siz,_pad) \
do { } while (0)
#endif
// encode_wrap -- perform encoding and inject newline
static inline_nodebug byte *
encode_wrap(b64ctl_t *ctl,byte *arr4)
{
dbgprt(ZPXHOWCHR,"encode_wrap: ENTER ctl_wraplen=%d ctl_wrapmax=%d\n",
ctl->ctl_wraplen,ctl->ctl_wrapmax);
do {
// bug out if wrap disabled
if (ctl->ctl_wrapmax <= 0)
break;
// wait for wrap
ctl->ctl_wraplen += 4;
ctl->ctl_wrapnow = 0;
if (ctl->ctl_wraplen < ctl->ctl_wrapmax)
break;
ctl->ctl_wraplen = 0;
ctl->ctl_wrapnow = 1;
PUT4(0,'\n');
encode_xcheck(ctl,arr4,1,NULL,0,-1);
++arr4;
} while (0);
dbgprt(ZPXHOWCHR,"encode_wrap: EXIT ctl_wraplen=%d ctl_wrapnow=%d\n",
ctl->ctl_wraplen,ctl->ctl_wrapnow);
return arr4;
}
// encode_putx -- perform encoding
static inline_nodebug byte *
encode_putx(b64ctl_t *ctl,byte *arr4,const byte *arr3,int padlen)
{
dbgprt(ZPXHOWCHR,"b64encode: LOOP arr3=(" FMT " " FMT " " FMT ") padlen=%d\n",
PRT(arr3[0]),PRT(arr3[1]),PRT(arr3[2]),padlen);
PUTE(0,(arr3[0] & 0xfc) >> 2);
PUTE(1,((arr3[0] & 0x03) << 4) + ((arr3[1] & 0xf0) >> 4));
PUTE(2,((arr3[1] & 0x0f) << 2) + ((arr3[2] & 0xc0) >> 6));
PUTE(3,arr3[2] & 0x3f);
switch (padlen) {
case 2:
PUT4(2,'=');
// fall through
case 1:
PUT4(3,'=');
break;
}
encode_xcheck(ctl,arr4,4,arr3,3,padlen);
arr4 += 4;
return arr4;
}
// is_base64 -- ensure char is a valid base64 encoding char
#if ! B64DECODE_FAST
static inline int
is_base64(byte c)
{
return (isalnum(c) || (c == '+') || (c == '/'));
}
#endif
// decode_find -- convert encoded base64 char into binary byte
static inline byte
decode_find(byte chr)
{
byte out;
#if B64DECODE_FAST
out = decode_fast[chr];
#else
const char *find = strchr(base64_chars,chr);
out = find - base64_chars;
#endif
dbgprt(ZPXHOWCHR,"decode_find: XLAT out=" FMT " chr=" FMT "\n",
PRT(out),PRT(chr));
if (out == 0xFF)
sysfault("b64decode: NONBASE64 chr=" FMT "\n",PRT(chr));
return out;
}
// decode_put -- convert 4 byte encoded base64 cell into 3 binary bytes
static inline_nodebug byte *
decode_put(byte *arr3,byte *arr4,int a4len)
{
int idx;
int a3len;
dbgprt(ZPXHOWCHR,"decode_put: ENTER a4len=%d\n",a4len);
// convert to binary codes
idx = 0;
for (; idx < a4len; ++idx)
arr4[idx] = decode_find(arr4[idx]);
// add zero padding
#if 0
for (; idx < 4; ++idx)
arr4[idx] = 0;
#endif
a3len = (a4len * 3) / 4;
// store 3 binary chars for 4 byte cell
arr3[0] = (arr4[0] << 2) + ((arr4[1] & 0x30) >> 4);
arr3[1] = ((arr4[1] & 0xf) << 4) + ((arr4[2] & 0x3c) >> 2);
arr3[2] = ((arr4[2] & 0x3) << 6) + arr4[3];
#if B64VERBOSE
for (idx = 0; idx < a3len; ++idx)
dbgprt(ZPXHOWCHR,"decode_put: arr3[%d]=" FMT "\n",PRT(arr3[idx]));
#endif
// advance output pointer
arr3 += a3len;
dbgprt(ZPXHOWCHR,"decode_put: EXIT a3len=%d\n",a3len);
return arr3;
}
// finish -- finish an operation
static size_t
finish(b64ctl_t *ctl,byte *out)
{
b64buf_t *buf = &ctl->ctl_out;
size_t outlen = OUTLEN(buf,out);
if (outlen > buf->buf_len)
sysfault("finish: buffer overflow -- buf_len=%zu outlen=%zu\n",
buf->buf_len,outlen);
buf->buf_len = outlen;
return outlen;
}
// b64encode -- encode raw data to base64
// RETURNS actual output length
size_t
b64encode(b64ctl_t *ctl)
{
const byte *inb = ctl->ctl_inb.buf_base;
ssize_t inblen = ctl->ctl_inb.buf_len;
byte *arr4 = ctl->ctl_out.buf_base;
int anyflg = (inblen > 0);
dbgprt(ZPXHOWB64,"b64encode: ENTER inb=%p inblen=%zu\n",inb,inblen);
for (; inblen >= 3; inblen -= 3, inb += 3) {
arr4 = encode_putx(ctl,arr4,inb,0);
arr4 = encode_wrap(ctl,arr4);
}
if (inblen) {
byte arr3[3];
u32 j = 0;
int padlen = 3 - inblen;
dbgprt(ZPXHOWB64,"b64encode: POST inblen=%zu padlen=%d\n",
inblen,padlen);
// copy over valid bytes
for (; inblen > 0; --inblen, ++j)
arr3[j] = inb[j];
// add zeroes as padding?
for (; j < 3; ++j)
arr3[j] = 0;
// add final padding
arr4 = encode_putx(ctl,arr4,arr3,padlen);
arr4 = encode_wrap(ctl,arr4);
}
// sequence should end with newline
do {
dbgprt(ZPXHOWB64,"b64encode: WRAP anyflg=%d ctl_wrapnow=%d\n",
anyflg,ctl->ctl_wrapnow);
// no newline generation
if (ctl->ctl_wrapmax <= 0)
break;
// no legit data
if (! anyflg)
break;
// don't double up newlines if we just did a newline
if (ctl->ctl_wrapnow)
break;
PUT4(0,'\n');
encode_xcheck(ctl,arr4,1,NULL,0,0);
++arr4;
} while (0);
// always add EOS
#if 0
*arr4 = 0;
#endif
size_t outlen = finish(ctl,arr4);
dbgprt(ZPXHOWB64,"b64encode: EXIT outlen=%zu\n",outlen);
return outlen;
}
// b64decode -- decode base64 into raw binary
// RETURNS: actual output length
size_t
b64decode(b64ctl_t *ctl)
{
const char *inb = (const char *) ctl->ctl_inb.buf_base;
size_t inblen = ctl->ctl_inb.buf_len;
size_t inbidx = 0;
byte *arr3 = ctl->ctl_out.buf_base;
int outidx = 0;
byte arr4[4];
dbgprt(ZPXHOWB64,"b64decode: ENTER inb=%p\n",inb);
for (inbidx = 0; inbidx < inblen; ++inbidx) {
int chr = inb[inbidx];
dbgprt(ZPXHOWCHR,"b64decode: LOOP chr=" FMT "\n",PRT(chr));
// stop when we hit a pad char
if (chr == '=')
break;
// ignore newlines
if (chr == '\n')
continue;
// not a valid char
#if ! B64DECODE_FAST
if (! is_base64(chr)) {
sysfault("b64decode: NONBASE64 chr=" FMT "\n",PRT(chr));
break;
}
#endif
arr4[outidx++] = chr;
if (outidx == 4) {
arr3 = decode_put(arr3,arr4,4);
outidx = 0;
}
}
if (outidx > 0) {
dbgprt(ZPXHOWB64,"b64decode: POST outidx=%d inblen=%zu\n",
outidx,inblen);
// fill remainder with pad/zeroes
for (inbidx = outidx; inbidx < 4; ++inbidx)
arr4[inbidx] = 0;
arr3 = decode_put(arr3,arr4,outidx);
}
size_t outlen = finish(ctl,arr3);
dbgprt(ZPXHOWB64,"b64decode: EXIT outlen=%zu\n",outlen);
return outlen;
}
// b64bufdup -- safe buffer duplicate
void
b64bufdup(b64buf_t *dst,const b64buf_t *src)
{
const char *tag = dst->buf_tag;
dbgprt(ZPXHOWB64,"b64bufdup: ENTER dst=%s/%p src=%s/%p\n",
dst->buf_tag,dst->buf_base,src->buf_tag,src->buf_base);
if (dst->buf_base != NULL)
sysfault("b64bufdup: non-null -- dst=%s buf_base=%p src=%s\n",
tag,dst->buf_base,src->buf_tag);
*dst = *src;
dst->buf_tag = tag;
dbgprt(ZPXHOWB64,"b64bufdup: EXIT\n");
}
// b64bufput -- output buffer to file
void
b64bufput(const b64buf_t *bufp,const char *ofile)
{
int fd;
b64buf_t buf_;
b64buf_t *buf = &buf_;
ssize_t xlen;
*buf = *bufp;
printf("b64bufput: %s buf_base=%p buf_len=%zu\n",
ofile,buf->buf_base,buf->buf_len);
do {
if (buf->buf_opt & B64MMAP)
break;
fd = open(ofile,O_WRONLY | O_TRUNC | O_CREAT,0644);
if (fd < 0)
sysfault("b64bufput: unable to open '%s' -- %s\n",
ofile,strerror(errno));
for (; buf->buf_len > 0;
buf->buf_len -= xlen, buf->buf_base += xlen) {
xlen = write(fd,buf->buf_base,buf->buf_len);
if (xlen < 0)
sysfault("bufput: write error -- %s\n",strerror(errno));
}
CLOSEME(fd);
} while (0);
}
// b64bufget -- input file to buffer
void
b64bufget(b64buf_t *buf,const char *ifile)
{
struct stat st;
size_t cap;
int fd;
const char *etag = NULL;
ssize_t xlen;
do {
fd = open(ifile,O_RDONLY);
if (fd < 0) {
etag = "open";
break;
}
if (buf->buf_opt & B64MMAP) {
buf->buf_fdmap = fd;
// get file size
if (fstat(fd,&st) < 0) {
etag = "fstat";
break;
}
buf->buf_len = st.st_size;
buf->buf_base = mmap(NULL,buf->buf_len,PROT_READ,
MAP_SHARED,buf->buf_fdmap,0);
if (buf->buf_base == MAP_FAILED) {
etag = "mmap";
break;
}
break;
}
cap = 0;
buf->buf_len = 0;
buf->buf_base = NULL;
while (1) {
if (buf->buf_len >= cap) {
cap += 4096;
buf->buf_base = realloc(buf->buf_base,cap + 1);
if (buf->buf_base == NULL) {
etag = "realloc";
break;
}
}
xlen = read(fd,&buf->buf_base[buf->buf_len],cap - buf->buf_len);
if (xlen < 0) {
etag = "read";
break;
}
if (xlen == 0)
break;
buf->buf_len += xlen;
}
} while (0);
if (etag != NULL)
sysfault("b64bufget: unable to %s '%s' -- %s\n",
etag,ifile,strerror(errno));
do {
if (buf->buf_opt & B64MMAP)
break;
CLOSEME(buf->buf_fdmap);
cap = buf->buf_len;
buf->buf_base[cap] = 0;
buf->buf_base = realloc(buf->buf_base,cap + 1);
if (buf->buf_base == NULL)
sysfault("b64bufget: realloc fail -- %s\n",strerror(errno));
} while (0);
printf("b64bufget: %s buf_base=%p buf_len=%zu\n",
ifile,buf->buf_base,buf->buf_len);
}
// b64setup -- passive setup
void
b64setup(b64ctl_t *ctl,u32 opt,const char *tag)
{
b64buf_t *buf;
memset(ctl,0,sizeof(*ctl));
ctl->ctl_tag = tag;
buf = &ctl->ctl_out;
buf->buf_fdmap = -1;
buf->buf_opt = opt;
buf->buf_tag = "ctl_out";
buf = &ctl->ctl_inb;
buf->buf_fdmap = -1;
buf->buf_opt = opt;
buf->buf_tag = "ctl_inb";
ctl->ctl_fdchk = -1;
}
// init_outbuf -- initialize output buffer
static int
init_outbuf(b64ctl_t *ctl,size_t outlen)
{
b64buf_t *buf = &ctl->ctl_out;
int err = -1;
dbgprt(ZPXHOWB64,"init_outbuf: ENTER ctl=%s buf=%s buf_base=%p buf_len=%zu outlen=%zu\n",
ctl->ctl_tag,buf->buf_tag,buf->buf_base,buf->buf_len,outlen);
buf->buf_len = outlen;
do {
if (! (buf->buf_opt & B64MMAP)) {
buf->buf_base = realloc(buf->buf_base,outlen);
if (buf->buf_base == NULL)
break;
err = 0;
break;
}
if (ftruncate(buf->buf_fdmap,outlen) < 0)
break;
buf->buf_base = mmap(NULL,outlen,PROT_READ | PROT_WRITE,
MAP_SHARED,buf->buf_fdmap,0);
if (buf->buf_base == MAP_FAILED) {
buf->buf_base = NULL;
break;
}
err = 0;
} while (0);
dbgprt(ZPXHOWB64,"init_outbuf: EXIT err=%d\n",err);
return err;
}
// b64init_encode -- initialize for encode
// RETURNS: 0=okay, -1=error
int
b64init_encode(b64ctl_t *ctl,const b64buf_t *inb,int wrapmax)
{
size_t outlen;
// copy in the buffer
b64bufdup(&ctl->ctl_inb,inb);
// we need for 4 bytes of output for each 3 bytes of input
outlen = inb->buf_len;
outlen *= 4;
outlen /= 3;
outlen += 4;
// space for padding
outlen += 4;
// we wrap with newline every N bytes
if (wrapmax == 0)
wrapmax = 76;
ctl->ctl_wrapmax = wrapmax;
if (wrapmax > 0)
outlen += (outlen + wrapmax) / wrapmax;
return init_outbuf(ctl,outlen);
}
#if B64DECODE_FAST
// init_fast -- initialize fast decode lookup
static void
init_fast(void)
{
const char *cp = base64_chars;
memset(decode_fast,0xFF,sizeof(decode_fast));
for (int chr = *cp; chr != 0; ++cp, chr = *cp) {
size_t off = cp - base64_chars;
decode_fast[chr] = off;
dbgprt(ZPXHOWB64,"init_fast: FASTINIT chr='%c' off=%zu\n",
chr,off);
}
}
#endif
// b64init_decode -- initialize for decode
// RETURNS: 0=okay, -1=error
int
b64init_decode(b64ctl_t *ctl,b64buf_t *inb)
{
size_t outlen;
int err;
dbgprt(ZPXHOWB64,"b64init_decode: ENTER buf_len=%zu\n",inb->buf_len);
#if B64DECODE_FAST
if (decode_fast['B'] == 0)
init_fast();
#endif
if (inb != &ctl->ctl_inb)
ctl->ctl_inb = *inb;
inb = &ctl->ctl_inb;
// caller may already know the input length, but ...
#if 0
if (inb->buf_len == 0) {
inb->buf_len = strlen(inb->buf_base);
dbgprt(ZPXHOWB64,"b64init_decode: STRLEN buf_len=%zu\n",inb->buf_len);
}
#endif
// we need for 3 bytes of output for each 4 bytes of input
outlen = inb->buf_len;
outlen *= 3;
outlen += 3;
outlen /= 4;
err = init_outbuf(ctl,outlen);
dbgprt(ZPXHOWB64,"b64init_decode: EXIT err=%d outlen=%zu\n",err,outlen);
return err;
}
// b64destroy -- destroy/deinit output buffer
void
b64destroy(b64ctl_t *ctl)
{
b64bufrls(&ctl->ctl_out,"ctl_out");
b64bufrls(&ctl->ctl_inb,"ctl_inb");
}
// b64bufrls -- destroy/deinit output buffer
void
b64bufrls(b64buf_t *buf,const char *who)
{
size_t outlen = buf->buf_len;
int err = -1;
dbgprt(ZPXHOWB64,"b64bufrls: ENTER buf_opt=%8.8X buf_base=%p outlen=%zu (from %s)\n",
buf->buf_opt,buf->buf_base,outlen,who);
do {
if (! (buf->buf_opt & B64MMAP)) {
FREEME(buf->buf_base);
err = 0;
break;
}
if (munmap(buf->buf_base,outlen) < 0)
break;
if (buf->buf_opt & B64TRIM) {
if (ftruncate(buf->buf_fdmap,outlen) < 0)
break;
}
CLOSEME(buf->buf_fdmap);
} while (0);
if (err < 0)
exit(1);
buf->buf_base = NULL;
buf->buf_len = 0;
dbgprt(ZPXHOWB64,"b64bufrls: EXIT\n");
}
FILE: b64test.c
// b64test.c -- test program for base64
#include <base64.h>
int opt_a; // exhaustive test
int opt_L; // binary file length
int opt_k; // keep temp files
int opt_m; // use mmap
u32 opt_R = 1; // random seed
int opt_T; // number of tests
int opt_W; // wrap length
int tstno = 1; // current test number
#define ALLFILES(_cmd) \
_cmd(bin) \
_cmd(encexp) \
_cmd(encact) \
_cmd(dcdexp) \
_cmd(dcdact)
#define FILEDEF(_pre) \
const char *_pre##_file = #_pre ".TMP";
ALLFILES(FILEDEF)
#define FILERM(_pre) \
unlink(_pre##_file);
// initial random binary buffer
B64BUFDEF(binbuf);
// encoding control
B64BUFDEF(encbuf);
B64CTLDEF(ctlenc);
// decoding control
B64BUFDEF(dcdbuf);
B64CTLDEF(ctldcd);
int
doexec(const char *cmd)
{
printf("DOEXEC: %s\n",cmd);
int ignflg = (cmd[0] == '!');
if (ignflg)
++cmd;
int code = system(cmd);
if (code && (! ignflg))
exit(1);
return code;
}
int
xrand(void)
{
return rand_r(&opt_R);
}
// doclean -- remove temp files
void
doclean(void)
{
do {
if (opt_k)
break;
ALLFILES(FILERM);
} while (0);
}
void
dotest(int binlen)
{
int err;
u32 opt = 0;
b64ctl_t *ctl;
b64buf_t *buf;
char cmd[1000];
printf("\n");
for (int col = 1; col <= 80; ++col)
putchar('-');
printf("\n");
dbgprt(ZPXHOWB64,"dotest: ENTER binlen=%d\n",binlen);
buf = &binbuf;
if (binlen <= 0)
buf->buf_len = -binlen;
else
buf->buf_len = xrand() % binlen;
printf("dotest: %d -- R=%u L=%8.8zX/%zu\n",
tstno,opt_R,buf->buf_len,buf->buf_len);
doclean();
if (opt_m) {
opt |= B64MMAP;
opt |= B64TRIM;
}
// NOTE: this gets freed in the b64destroy for ctlenc below
buf->buf_base = NULL;
buf->buf_base = realloc(buf->buf_base,buf->buf_len);
if (buf->buf_base == NULL)
sysfault("dotest: realloc failure -- %s\n",strerror(errno));
// get a random buffer
for (size_t bufidx = 0; bufidx < buf->buf_len; ++bufidx)
buf->buf_base[bufidx] = xrand();
// dump it for base64 program
b64bufput(buf,bin_file);
// have base64 encode it
sprintf(cmd,"base64 %s > %s",bin_file,encexp_file);
doexec(cmd);
ctl = &ctlenc;
b64setup(ctl,opt,"ctlenc");
err = b64init_encode(ctl,buf,opt_W);
if (err < 0)
sysfault("dotest: b64init_encode failure -- %s\n",strerror(errno));
#if B64ENCODE_XCHECK
ctl->ctl_fdchk = open(encexp_file,O_RDONLY);
#endif
// have our routine encode it
b64encode(ctl);
#if B64ENCODE_XCHECK
CLOSEME(ctl->ctl_fdchk);
#endif
// save it to a file
b64bufput(&ctl->ctl_out,encact_file);
b64destroy(ctl);
// compare them
sprintf(cmd,"diff -u %s %s",encexp_file,encact_file);
doexec(cmd);
// decode it
sprintf(cmd,"base64 -d %s > %s",encexp_file,dcdexp_file);
doexec(cmd);
// passive setup
ctl = &ctldcd;
b64setup(ctl,opt,"ctldcd");
// load up the encoded file into the decode input buffer
b64bufget(&dcdbuf,encact_file);
// decode the data
err = b64init_decode(ctl,&dcdbuf);
if (err < 0)
sysfault("dotest: b64init_decode failure -- %s\n",strerror(errno));
// have our routine decode it
b64decode(ctl);
b64bufput(&ctl->ctl_out,dcdact_file);
b64destroy(ctl);
// compare them
sprintf(cmd,"cmp %s %s",dcdexp_file,dcdact_file);
doexec(cmd);
// final temp file cleanup
doclean();
printf("dotest: complete\n");
dbgprt(ZPXHOWB64,"dotest: EXIT\n");
}
int
main(int argc,char **argv)
{
char *cp;
--argc;
++argv;
setlinebuf(stdout);
// use a special output directory
do {
cp = getenv("GENDIR");
if (cp == NULL)
break;
printf("GENDIR = %s\n",cp);
if (chdir(cp) < 0)
sysfault("main: chdir fault -- %s\n",strerror(errno));
} while (0);
// parse options
for (; argc > 0; --argc, ++argv) {
cp = *argv;
if (*cp != '-')
break;
cp += 2;
switch (cp[-1]) {
case 'a':
opt_a = (*cp != 0) ? atoi(cp) : 1;
break;
case 'L':
opt_L = (*cp != 0) ? atoi(cp) : 1000;
break;
case 'k':
opt_k = ! opt_k;
break;
case 'm':
opt_m = ! opt_m;
break;
case 'R':
opt_R = (*cp != 0) ? atoi(cp) : 1;
break;
case 'T':
opt_T = (*cp != 0) ? atoi(cp) : 100;
break;
case 'W':
opt_W = atoi(cp);
break;
}
}
// ensure at least one test
if (opt_T <= 0)
opt_T = 1;
do {
// ensure we have a non-zero length
if (opt_L == 0)
opt_L = 100;
// do random testing
if (opt_a < 0) {
for (; tstno <= opt_T; ++tstno)
dotest(opt_L);
break;
}
// get absolute value
if (opt_L < 0)
opt_L = -opt_L;
// ensure we get at least one test
if (opt_L < opt_a)
opt_L = opt_a;
// do a range of explicit lengths
for (int binlen = opt_a; binlen <= opt_L; ++binlen, ++tstno)
dotest(-binlen);
} while (0);
printf("%d tests completed\n",tstno);
return 0;
}
FILE: dbgcom.h
// dbgflg.h -- base/debug definitions
#ifndef _dbgflg_h_
#define _dbgflg_h_
#include <stdio.h>
#include <stdlib.h>
typedef unsigned char byte;
typedef unsigned int u32;
#define ZPXHOWALL(_cmd) \
_cmd(ZPXHOWB64) \
_cmd(ZPXHOWCHR) \
_cmd(ZPXHOWTST)
#define _DBGENUM(_sym) \
_sym,
enum {
ZPXHOWANY,
ZPXHOWALL(_DBGENUM)
ZPXHOWMAX
};
byte dbgflg[ZPXHOWMAX];
static inline byte
dbgok(int lvl)
{
return dbgflg[lvl];
}
#if DEBUG || _USE_ZPRT_
#define XVERBOSE 1
#define dbgprt(_lvl,_fmt...) \
do { \
if (! dbgok(_lvl)) \
break; \
int sverr = errno; \
printf(_fmt); \
errno = sverr; \
} while (0)
#define inline_nodebug /**/
#else
#define XVERBOSE 0
#define inline_nodebug inline
#define dbgprt(_fmt...) \
do { } while (0)
#endif
#define sysfault(_fmt...) \
do { \
printf(_fmt); \
exit(1); \
} while (0)
#define RNGE(_val,_lo,_hi) \
(((_val) >= (_lo)) && ((_val) <= (_hi)))
#define PRTABLE(_val) \
RNGE(_val,0x20,0x7E) ? (_val) : '.'
#define CLOSEME(_fd) \
do { \
if (_fd < 0) \
break; \
dbgprt(ZPXHOWB64,"CLOSEME: fd=%d (from %d)\n",_fd,__LINE__); \
close(_fd); \
_fd = -1; \
} while (0)
#define FREEME(_ptr) \
do { \
dbgprt(ZPXHOWB64,"FREEME: ptr=%p (from %d)\n",_ptr,__LINE__); \
free(_ptr); \
_ptr = NULL; \
} while (0)
#endif
FILE: dbgcom.c
// dbgcom.c -- tracing setup
#include <dbgcom.h>
struct dbgdef {
int dbg_lvl;
const char *dbg_sym;
};
#define _ZPXDEF(_sym) \
{ .dbg_lvl = _sym, .dbg_sym = #_sym },
struct dbgdef dbgdef[] = {
ZPXHOWALL(_ZPXDEF)
{ .dbg_sym = NULL }
};
void __attribute__((constructor))
dbginit(void)
{
struct dbgdef *dbg = dbgdef;
for (; dbg->dbg_sym != NULL; ++dbg) {
char *cp = getenv(dbg->dbg_sym);
if (cp == NULL)
continue;
if (! atoi(cp))
continue;
dbgflg[dbg->dbg_lvl] = 1;
dbgflg[ZPXHOWANY] = 1;
}
}