char * lpbitmap = NULL;
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bmpScreen.bmWidth;
bi.biHeight = bmpScreen.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = 24;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;
hDIB = GlobalAlloc(GHND, dwBmpSize);
lpbitmap = (char*)GlobalLock(hDIB);
I am using the windows GDI header file to create a bitmap, the goal is to convert the lpbitmap buffer to a base64 string so that it can be represented as such data:image/bmp;base64,Qk06
as far as I understand the bitmap is 24bit where every 3 chars represent RGB values example
How to convert each RGB value into a base64 value
Update
I changed the bitcount to 24
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;
}
}
iv tried a lot of solutions to try to get this working (i.e using memcpy etc) I cant seem to find the issue, depending on what I try I either end up with gibberish or SEGV
iv spent a lot of time already googling and trying different ways, i still cant figure out why the arrays won't combine successfully
#include <stdio.h>
#include <stdint.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/syscall.h>
#include <unistd.h>
#define log_info printf
typedef struct
{
char* name;
//size_t size;
} entry_t;
/* qsort struct comparison function (C-string field) */
static int struct_cmp_by_name(const void* a, const void* b)
{
entry_t* ia = (entry_t*)a;
entry_t* ib = (entry_t*)b;
return strcmp(ia->name, ib->name);
/* strcmp functions works exactly as expected from comparison function */
}
entry_t* get_item_entries(const char* dirpath, int* count)
{
struct dirent* dent;
char buffer[512]; // fixed buffer
int dfd = 0,
n, r = 1; // item counter, rounds to loop
entry_t* p = NULL; // we fill this struct with items
loop:
n = 0;
printf("loop: %d, count:%d\n", r, *count);
// try to open dir
dfd = open(dirpath, O_RDONLY, 0);
if (dfd < 0)
{
printf("Invalid directory. (%s)\n", dirpath);
*count = -1;
return NULL;
}
else
{
printf("open(%s)\n", dirpath);
}
memset(buffer, 0, sizeof(buffer));
while (syscall(SYS_getdents, dfd, buffer, sizeof(buffer)) != 0)
{
dent = (struct dirent*)buffer;
while (dent->d_fileno)
{ // skip `.` and `..`
if (!strncmp(dent->d_name, "..", 2)
|| !strncmp(dent->d_name, ".", 1)) goto skip_dent;
// deal with filtering outside of this function, we just skip .., .
switch (r)
{ // first round: just count items
case 1:
{
// skip special cases
if (dent->d_fileno == 0) goto skip_dent;
break;
}
// second round: store filenames
case 0: p[n].name = strdup(dent->d_name); break;
}
n++;
skip_dent:
dent = (struct dirent*)((void*)dent + dent->d_reclen);
if (dent == (void*)&buffer[512]) break; // refill buffer
}
memset(buffer, 0, sizeof(buffer));
}
close(dfd);
// on first round, calloc for our list
if (!p)
{ // now n holds total item count, note it
p = calloc(n, sizeof(entry_t));
*count = n;
}
// first round passed, loop
r--; if (!r) goto loop;
// report count
printf("%d items at %p, from 1-%d\n", *count, (void*)p, *count);
/* resort using custom comparision function */
qsort(p, *count, sizeof(entry_t), struct_cmp_by_name);
// report items
//for (int i = 0; i < num; ++i) log_error( "%s", p[i].name);
return p;
}
int main(int argc, char* argv[])
{
int HDD_count = -1;
uint32_t total = -1;
int ext_count = -1;
entry_t* e = NULL;
entry_t *HDD = get_item_entries("/mnt/f/n", &HDD_count);
entry_t* ext = get_item_entries("/mnt/f/dls", &ext_count);
total = ext_count + HDD_count;
e = (entry_t*)malloc(sizeof *e * total);
if (e != NULL)
{
for (int i = 1; i < HDD_count; i++)
{
log_info("HDD[%i].name %s\n", i, HDD[i].name);
e[i].name = strdup(HDD[i].name);
}
for (int i = 1; i < ext_count; i++)
{
log_info("ext[%i].name %s\n", i, ext[i].name);
e[i + HDD_count].name = strdup(ext[i].name);
}
}
else
printf("Failed to Allocate the Array");
char tmp[256];
int i = 1, j;
for(j = 1; j <= total; j++)
{
snprintf(&tmp[0], 255, "%s", e[ j].name);
log_info("%i:%s\n", j , tmp);
}
return 0;
}
Here is a rewrite of a snippet of main() that I mentioned in my comment above:
#define CHECK(p, msg) if(!(p)) { printf("%s:%d: %s", __FILE__, __LINE__, msg); return 1;}
...
entry_t *HDD = get_item_entries("/mnt/f/n", &HDD_count);
CHECK(HDD, "HDD failed to get entries");
entry_t *ext = get_item_entries("/mnt/f/dls", &ext_count);
CHECK(ext, "ext failed to get entries");
uint32_t total = HDD_count + ext_count;
entry_t *e = malloc(total * sizeof(*e));
CHECK(e, "malloc failed");
for(int i = 0; i < HDD_count; i++) {
log_info("HDD[%i].name %s\n", i, HDD[i].name);
e[i].name = strdup(HDD[i].name);
}
// write a function instead of duplicating code?
for (int i = 0; i < ext_count; i++) {
log_info("ext[%i].name %s\n", i, ext[i].name);
e[HDD_count + i].name = strdup(ext[i].name);
}
It looks like a short lived program, but I would still free the values from strdup() and e itself.
This program tries to simulate FIFO and LRU page replacement. I am trying to implement a simple queue using a dynamically allocated array for the FIFO queue. I want the "page" to be stored in the array.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
/*
* Some compile-time constants.
*/
#define REPLACE_NONE 0
#define REPLACE_FIFO 1
#define REPLACE_LRU 2
#define REPLACE_SECONDCHANCE 3
#define REPLACE_OPTIMAL 4
#define TRUE 1
#define FALSE 0
#define PROGRESS_BAR_WIDTH 60
#define MAX_LINE_LEN 100
/*
* Some function prototypes to keep the compiler happy.
*/
int setup(void);
int teardown(void);
int output_report(void);
long resolve_address(long, int);
void error_resolve_address(long, int);
/*
* Variables used to keep track of the number of memory-system events
* that are simulated.
*/
int page_faults = 0;
int mem_refs = 0;
int swap_outs = 0;
int swap_ins = 0;
/*
* Page-table information. You may want to modify this in order to
* implement schemes such as SECONDCHANCE. However, you are not required
* to do so.
*/
struct page_table_entry *page_table = NULL;
struct page_table_entry {
long page_num;
int dirty;
int free;
};
/*
* These global variables will be set in the main() function. The default
* values here are non-sensical, but it is safer to zero out a variable
* rather than trust to random data that might be stored in it -- this
* helps with debugging (i.e., eliminates a possible source of randomness
* in misbehaving programs).
*/
int size_of_frame = 0; /* power of 2 */
int size_of_memory = 0; /* number of frames */
int page_replacement_scheme = REPLACE_NONE;
long *queue;
void add_end(long page_num){
for(int i=0; i<size_of_memory; i++){
if(queue[i] == NULL){
queue[i] = page_num;
break;
}
}
}
long peek_front(){
return queue[0];
}
void remove_front(){
for(int i=0; i<size_of_memory; i++){
queue[i] = queue[i+1];
}
}
// typedef struct page_in_queue page_in_queue;
// struct page_in_queue {
// long page_num;
// page_in_queue *next;
// };
// page_in_queue *new_page(){
// page_in_queue *new_page;
// new_page = (page_in_queue *) malloc(sizeof(page_in_queue));
// new_page->next = NULL;
// return new_page;
// }
// page_in_queue *add_end(page_in_queue *queue, page_in_queue *page){
// page_in_queue *curr;
// if (queue == NULL) {
// page->next = NULL;
// return page;
// }
// for (curr = queue; curr->next != NULL; curr = curr->next);
// curr->next = page;
// page->next = NULL;
// return queue;
// }
// page_in_queue *peek_front(page_in_queue *queue) {
// return queue;
// }
// page_in_queue *remove_front(page_in_queue *queue){
// if (queue == NULL) {
// return NULL;
// }
// page_in_queue *new_front_page = queue->next;
// free(queue);
// return new_front_page;
// }
long *list;
void add(long page_num){
int i;
for (i=0; i<size_of_memory; i++){
list[i] = list[i+1];
}
list[i] = page_num;
}
long peek_least_used(){
return list[0];
}
/*
* Function to convert a logical address into its corresponding
* physical address. The value returned by this function is the
* physical address (or -1 if no physical address can exist for
* the logical address given the current page-allocation state.
*/
long resolve_address(long logical, int memwrite)
{
int i;
long page, frame;
long offset;
long mask = 0;
long effective;
/* Get the page and offset */
page = (logical >> size_of_frame);
for (i=0; i<size_of_frame; i++) {
mask = mask << 1;
mask |= 1;
}
offset = logical & mask;
if (page_replacement_scheme == 2){
add(page);
}
/* Find page in the inverted page table. */
frame = -1;
for ( i = 0; i < size_of_memory; i++ ) {
if (!page_table[i].free && page_table[i].page_num == page) {
frame = i;
break;
}
}
/* If frame is not -1, then we can successfully resolve the
* address and return the result. */
if (frame != -1) {
effective = (frame << size_of_frame) | offset;
return effective;
}
/* If we reach this point, there was a page fault. Find
* a free frame. */
page_faults++;
for ( i = 0; i < size_of_memory; i++) {
if (page_table[i].free) {
frame = i;
break;
}
}
// page_in_queue *temp_page;
// page_in_queue *queue;
long rem_page;
/* If we found a free frame, then patch up the
* page table entry and compute the effective
* address. Otherwise return -1.
*/
if (frame != -1) {
page_table[frame].page_num = page;
page_table[i].free = FALSE;
swap_ins++;
if (page_replacement_scheme == 1){
// temp_page = new_page();
// temp_page->page_num = page;
add_end(page);
}
effective = (frame << size_of_frame) | offset;
return effective;
}
else {
if (page_replacement_scheme == 1){
rem_page = peek_front();
for ( i = 0; i < size_of_memory; i++){
if(page_table[i].page_num == rem_page){
page_table[i].page_num = page;
page_table[i].free = FALSE;
page_table[i].dirty = memwrite;
swap_ins++;
if(page_table[i].dirty == 1){
swap_outs++;
}
frame = i;
break;
}
}
remove_front();
effective = (frame << size_of_frame) | offset;
return effective;
}
if (page_replacement_scheme == 2){
long temp = peek_least_used();
for ( i = 0; i < size_of_memory; i++){
if(page_table[i].page_num == temp){
page_table[i].page_num = page;
page_table[i].free = FALSE;
page_table[i].dirty = memwrite;
swap_ins++;
if(page_table[i].dirty == 1){
swap_outs++;
}
frame = i;
break;
}
}
effective = (frame << size_of_frame) | offset;
return effective;
}
if (page_replacement_scheme == 3){
}
}
}
/*
* Super-simple progress bar.
*/
void display_progress(int percent)
{
int to_date = PROGRESS_BAR_WIDTH * percent / 100;
static int last_to_date = 0;
int i;
if (last_to_date < to_date) {
last_to_date = to_date;
} else {
return;
}
printf("Progress [");
for (i=0; i<to_date; i++) {
printf(".");
}
for (; i<PROGRESS_BAR_WIDTH; i++) {
printf(" ");
}
printf("] %3d%%", percent);
printf("\r");
fflush(stdout);
}
int setup()
{
int i;
page_table = (struct page_table_entry *)malloc(
sizeof(struct page_table_entry) * size_of_memory
);
if (page_table == NULL) {
fprintf(stderr,
"Simulator error: cannot allocate memory for page table.\n");
exit(1);
}
for (i=0; i<size_of_memory; i++) {
page_table[i].free = TRUE;
}
return -1;
}
int teardown()
{
return -1;
}
void error_resolve_address(long a, int l)
{
fprintf(stderr, "\n");
fprintf(stderr,
"Simulator error: cannot resolve address 0x%lx at line %d\n",
a, l
);
exit(1);
}
int output_report()
{
printf("\n");
printf("Memory references: %d\n", mem_refs);
printf("Page faults: %d\n", page_faults);
printf("Swap ins: %d\n", swap_ins);
printf("Swap outs: %d\n", swap_outs);
return -1;
}
int main(int argc, char **argv)
{
/* For working with command-line arguments. */
int i;
char *s;
/* For working with input file. */
FILE *infile = NULL;
char *infile_name = NULL;
struct stat infile_stat;
int line_num = 0;
int infile_size = 0;
/* For processing each individual line in the input file. */
char buffer[MAX_LINE_LEN];
long addr;
char addr_type;
int is_write;
/* For making visible the work being done by the simulator. */
int show_progress = FALSE;
/* Process the command-line parameters. Note that the
* REPLACE_OPTIMAL scheme is not required for A#3.
*/
for (i=1; i < argc; i++) {
if (strncmp(argv[i], "--replace=", 9) == 0) {
s = strstr(argv[i], "=") + 1;
if (strcmp(s, "fifo") == 0) {
page_replacement_scheme = REPLACE_FIFO;
} else if (strcmp(s, "lru") == 0) {
page_replacement_scheme = REPLACE_LRU;
} else if (strcmp(s, "secondchance") == 0) {
page_replacement_scheme = REPLACE_SECONDCHANCE;
} else if (strcmp(s, "optimal") == 0) {
page_replacement_scheme = REPLACE_OPTIMAL;
} else {
page_replacement_scheme = REPLACE_NONE;
}
} else if (strncmp(argv[i], "--file=", 7) == 0) {
infile_name = strstr(argv[i], "=") + 1;
} else if (strncmp(argv[i], "--framesize=", 12) == 0) {
s = strstr(argv[i], "=") + 1;
size_of_frame = atoi(s);
} else if (strncmp(argv[i], "--numframes=", 12) == 0) {
s = strstr(argv[i], "=") + 1;
size_of_memory = atoi(s);
if (page_replacement_scheme == 1){
queue = (long *)malloc(sizeof(long)*size_of_memory);
}
if (page_replacement_scheme == 2){
list = (long *)malloc(sizeof(long)*size_of_memory);
}
} else if (strcmp(argv[i], "--progress") == 0) {
show_progress = TRUE;
}
}
if (infile_name == NULL) {
infile = stdin;
} else if (stat(infile_name, &infile_stat) == 0) {
infile_size = (int)(infile_stat.st_size);
/* If this fails, infile will be null */
infile = fopen(infile_name, "r");
}
if (page_replacement_scheme == REPLACE_NONE ||
size_of_frame <= 0 ||
size_of_memory <= 0 ||
infile == NULL)
{
fprintf(stderr,
"usage: %s --framesize=<m> --numframes=<n>", argv[0]);
fprintf(stderr,
" --replace={fifo|lru|optimal} [--file=<filename>]\n");
exit(1);
}
setup();
while (fgets(buffer, MAX_LINE_LEN-1, infile)) {
line_num++;
if (strstr(buffer, ":")) {
sscanf(buffer, "%c: %lx", &addr_type, &addr);
if (addr_type == 'W') {
is_write = TRUE;
} else {
is_write = FALSE;
}
if (resolve_address(addr, is_write) == -1) {
error_resolve_address(addr, line_num);
}
mem_refs++;
}
if (show_progress) {
display_progress(ftell(infile) * 100 / infile_size);
}
}
teardown();
output_report();
fclose(infile);
exit(0);
}
The file is saved as virtmem.c. This is the makefile:
#
# "makefile" for the virtual-memory simulation.
#
CC=gcc
CFLAGS=-c -Wall -g
all: virtmem
virtmem.o: virtmem.c
$(CC) $(CFLAGS) virtmem.c
virtmem: virtmem.o
$(CC) virtmem.o -o virtmem
clean:
rm -rf *.o virtmem
After running the "make" command, I run the executable with these inputs
./virtmem --framesize=12 --numframes=100 --replace=fifo --file=traces/ls_out.txt --progress
But it is giving a segmentation fault at the conditional "if(queue[i] == NULL)", saying the memory location cannot be accessed. The gdb output is as follows:
Program received signal SIGSEGV, Segmentation fault.
0x0000555555554bea in add_end (page_num=34158723704) at virtmem.c:80
80 if(queue[i] == (long)0){
(gdb) print queue[i]
Cannot access memory at address 0x0
(gdb)
Running my program, I appear to not be writing the correct amount of frames according to the index.
$ ./test
Now recording!! Please speak into the microphone.
index = 0
Writing to: test.flac
audio.h:
#include <stdint.h>
#include <string.h>
typedef struct
{
uint32_t duration;
uint16_t format_type;
uint16_t number_of_channels;
uint32_t sample_rate;
uint32_t frameIndex; /* Index into sample array. */
uint32_t maxFrameIndex;
char* recordedSamples;
} AudioData;
int recordFLAC(AudioData* data, const char *fileName);
AudioData* initAudioData(uint32_t sample_rate, uint16_t channels, uint32_t duration);
capture.c:
#include <stdio.h>
#include <stdlib.h>
#include <portaudio.h>
#include <sndfile.h>
#include "audio.h"
AudioData* initAudioData(uint32_t sample_rate, uint16_t channels, uint32_t duration)
{
AudioData* data = malloc(sizeof(*data));
if (!data) return NULL;
data->duration = duration;
data->format_type = 1;
data->number_of_channels = channels;
data->sample_rate = sample_rate;
data->frameIndex = 0;
data->maxFrameIndex = sample_rate * duration;
data->recordedSamples = malloc(sizeof(data->maxFrameIndex));
if(!data->maxFrameIndex) return NULL;
return data;
}
static int recordCallback(const void *inputBuffer, void *outputBuffer, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
{
AudioData* data = (AudioData*)userData;
const char* buffer_ptr = (const char*)inputBuffer;
char* index_ptr = &data->recordedSamples[data->frameIndex];
(void) outputBuffer;
(void) timeInfo;
(void) statusFlags;
long framesToCalc;
long i;
int finished;
unsigned long framesLeft = data->maxFrameIndex - data->frameIndex;
if(framesLeft < frameCount){
framesToCalc = framesLeft;
finished = paComplete;
}else{
framesToCalc = frameCount;
finished = paContinue;
}
if(!inputBuffer){
for(i = 0; i < framesToCalc; i++){
*index_ptr++ = 0;
}
}else{
for(i = 0; i < framesToCalc; i++){
*index_ptr++ = *buffer_ptr++;
}
}
data->frameIndex += framesToCalc;
return finished;
}
int recordFLAC(AudioData* data, const char *fileName)
{
PaStreamParameters inputParameters;
PaStream* stream;
int err = 0;
int totalFrames = data->maxFrameIndex;
int numSamples;
int numBytes;
char max, val;
double average;
numSamples = totalFrames * data->number_of_channels;
numBytes = numSamples;
data->recordedSamples = malloc(numBytes);
if(!data->recordedSamples)
{
printf("Could not allocate record array.\n");
goto done;
}
for(int i = 0; i < numSamples; i++) data->recordedSamples[i] = 0;
if((err = Pa_Initialize())) goto done;
inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
if (inputParameters.device == paNoDevice) {
fprintf(stderr,"Error: No default input device.\n");
goto done;
}
inputParameters.channelCount = data->number_of_channels; /* stereo input */
inputParameters.sampleFormat = data->format_type;
inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL;
/* Record some audio. -------------------------------------------- */
err = Pa_OpenStream(&stream, &inputParameters, NULL, data->sample_rate, paFramesPerBufferUnspecified, paClipOff, recordCallback, &data);
if(err) goto done;
if((err = Pa_StartStream(stream))) goto done;
puts("Now recording!! Please speak into the microphone.");
while((err = Pa_IsStreamActive(stream)) == 1)
{
Pa_Sleep(1000);
printf("index = %d\n", data->frameIndex);
}
if( err < 0 ) goto done;
err = Pa_CloseStream(stream);
if(err) goto done;
/* Measure maximum peak amplitude. */
max = 0;
average = 0.0;
for(int i = 0; i < numSamples; i++)
{
val = data->recordedSamples[i];
val = abs(val);
if( val > max )
{
max = val;
}
average += val;
}
average /= (double)numSamples;
done:
Pa_Terminate();
if(err)
{
fprintf(stderr, "An error occured while using the portaudio stream\n");
fprintf(stderr, "Error number: %d\n", err);
fprintf(stderr, "Error message: %s\n", Pa_GetErrorText(err));
err = 1; /* Always return 0 or 1, but no other return codes. */
}
else
{
SF_INFO sfinfo;
sfinfo.channels = 1;
sfinfo.samplerate = data->sample_rate;
sfinfo.format = SF_FORMAT_FLAC | SF_FORMAT_PCM_16;
// open to file
printf("Writing to: %s\n", fileName);
SNDFILE * outfile = sf_open(fileName, SFM_WRITE, &sfinfo);
if (!outfile) return -1;
// prepare a 3 second long buffer (sine wave)
const int size = data->sample_rate * 3;
// write the entire buffer to the file
sf_write_raw(outfile, data->recordedSamples, size);
// force write to disk
sf_write_sync(outfile);
// don't forget to close the file
sf_close(outfile);
}
return err;
}
I'm not quite sure where I am going wrong, I know I need to be writing more frames. Any suggestions?
There seems to be something wrong with your assumptions about the sample format. In the callback you are using char * (single bytes) for the sample format, but in your libsndfile call you're opening a 16 bit file with SF_FORMAT_PCM_16.
This is not so good:
data->format_type = 1;
I recommend using one of the symbolic constants in the PortAudio library for sample formatting. Maybe you want a 16 bit one? And if so, you want to be using short* not char* in the PA callback.
Finally, if your channel count is not 1, the copy loops are incorrect:
for(i = 0; i < framesToCalc; i++){
*index_ptr++ = 0;
}
A "frame" contains data for all channels, so for example, if it's a stereo input your iteration needs to deal with both left and right channels like this:
for(i = 0; i < framesToCalc; i++){
*index_ptr++ = 0; // left
*index_ptr++ = 0; // right
}
Same for the other loops.
I have a string like that:
4;4=3;1=0,2=2,3=1,4=1,5=1;0003013340f59bce000002aaf01620e620198b2240002710;
It is separated into sections by ";" and each section can have one or more key/value pairs like 5=1 and so on, as you can see.
I want to parse it in pure C and I started working with strtok as I am showing in code here:
const wuint8 section_delimiter[] = ";";
const wuint8 field_delimiter[] = ",";
const wuint8 value_delimiter[] = "=";
printf("%s\n",data->msg);
token = strtok(data->msg,section_delimiter);
while(token != NULL) {
indicator = atoi(token);
printf("indicator: %d\n", indicator);
switch(indicator) {
case TYPE_1: {
printf("type: %d\n",TYPE_1);
wuint16 i, headerType, headerSubType;
for(i = 1; i < TP_MAX; i++) {
if(i == atoi(token)) {
token = strtok(NULL,value_delimiter);
headerType = i;
headerSubType = atoi(token);
break;
}
}
break;
}
case TYPE_2: {
printf("type: %d\n",TYPE_3);
break;
}
case TYPE_3: {
printf("type: %d\n",TYPE_3);
break;
}
case TYPE_4: {
printf("type: %d\n",TYPE_4);
break;
}
I am not sure how to do that correctly.
It also gets complicated, because not every string has the same structure, sometimes only one or two sections can be present. E.g.: 3;4=3;1=0,2=2,3=1,4=1,5=1;
Is there a how to do that showing the best and most convenient way?
strtok can't, AFAICR, be used in nested loops like this due to the global state it manages itself.
I suggest parsing each semicolon-delimited part out first, then handling them sequentially - or just implement something akin to strtok for your semicolon case yourself, then happily use strtok in the inner loop.
Using strcspn(). Fixed buffers, results go into global variables. data[] buffer is altered (and thus needs to be writable). YMMV
/*
It is separated into sections by ";" and each section can have one or more
key/value pairs like 5=1 and so on, as you can see. I want to parse it in
pure C and I started working with strtok as I am showing in code here:
*/
char data[] = "4;4=3;1=0,2=2,3=1,4=1,5=1;0003013340f59bce000002aaf01620e620198b2240002710;" ;
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct header {
int l;
int r;
} headers[123];
unsigned nheader;
int indicator;
char rest [123];
int tokenise(char * buff);
unsigned tokenise2(struct header *dst, char * buff);
/****************/
int tokenise(char * buff)
{
char *ptrs[14];
unsigned nptr;
unsigned len, pos;
ptrs[nptr=0] = NULL;
for (len = pos=0; buff[pos]; pos += len ) {
len = strcspn(buff+pos, ";");
ptrs[nptr++] = buff+pos;
ptrs[nptr] = NULL;
if (!buff[pos+len] ) break;
buff[pos+len] = 0;
len +=1;
}
if ( nptr> 0 && ptrs[0]) indicator = atoi(ptrs[0]); else indicator = -1;
if ( nptr> 1 && ptrs[1]) nheader = tokenise2 (headers, ptrs[1] ); else nheader = 0;
if ( nptr> 2 && ptrs[2]) nheader += tokenise2 (headers+nheader, ptrs[2] ); else nheader += 0;
if ( nptr> 3 && ptrs[3]) strcpy (rest, ptrs[3]); else rest[0] = 0;
return 0; /* or something useful ... */
}
unsigned tokenise2(struct header *target, char * buff)
{
char *ptrs[123];
unsigned nptr, iptr;
unsigned len, pos;
ptrs[nptr=0] = NULL;
for (len = pos=0; buff[pos]; pos += len ) {
len = strcspn(buff+pos, "," );
ptrs[nptr++] = buff+pos;
ptrs[nptr] = NULL;
if (!buff[pos+len] ) break;
buff[pos+len] = 0;
len +=1;
}
for ( iptr=0; iptr < nptr; iptr++) {
if (! ptrs[iptr] ) break;
len = strcspn(ptrs[iptr], "=" );
if (!len) break;
target[iptr].l = atoi (ptrs[iptr] );
target[iptr].r = atoi (ptrs[iptr]+len+1 );
}
return iptr; /* something useful ... */
}
int main(void)
{
int rc;
unsigned idx;
fprintf(stderr, "Org=[%s]\n", data );
rc = tokenise(data);
printf("Indicator=%d\n", indicator );
for (idx=0; idx < nheader; idx++) {
printf("%u: %d=%d\n", idx, headers[idx].l , headers[idx].r );
}
printf("Rest=%s\n", rest );
return 0;
}