/* buffer.c : buffer i/o library */ /* PUBLIC DOMAIN - NO COPYRIGHT CLAIMED - J.Mayo - April 2004 * Updated: 15 Aug 2008 */ /* define BUFFER_DEBUGGING and link to hexdump.c to get extra info */ #include #include #include #include #ifdef BUFFER_DEBUGGING #warning BUFFER_DEBUGGING enabled #undef NDEBUG #endif #include "buffer.h" #ifdef NDEBUG #define DEBUG(...) #define ERR(fmt, ...) fprintf(stderr, "ERROR:" fmt, ## __VA_ARGS__); #else #define DEBUG(fmt, ...) fprintf(stderr, "%s:%d:" fmt, __FILE__, __LINE__, ## __VA_ARGS__); #define ERR(fmt, ...) fprintf(stderr, "ERROR:%s:%d:" fmt, __FILE__, __LINE__, ## __VA_ARGS__); #endif #define NR(x) (sizeof(x)/sizeof*(x)) #ifdef BUFFER_DEBUGGING #include "hexdump.h" #endif void buffer_init(struct buffer *bd, int max) { if(max<=0) max=4096; bd->max=max; bd->len=0; bd->data=malloc(max); #ifndef NDEBUG memset(bd->data, '@', max); /* fill with @ characters to make bugs visible */ #endif } void buffer_free(struct buffer *bd) { free(bd->data); bd->data=0; bd->max=0; } const char *buffer_read_prepare(struct buffer *bd, size_t *len) { assert(bd!=NULL); assert(bd->len < 1073741824l); /* over 1gig is unreasonable */ *len=bd->len; return bd->data; } void buffer_read_commit(struct buffer *bd, size_t len) { assert(bd!=NULL); if(len>bd->len) { ERR("BUG: buffer_read_commit() called with a large length! len=%u greater-than bd->len=%u\n", (unsigned)len, bd->len); len=bd->len; } DEBUG("len=%d bd->len=%d\n", len, bd->len); bd->len-=len; memmove(bd->data, bd->data+len, bd->len); DEBUG("bd->len=%d\n", bd->len); #ifdef BUFFER_DEBUGGING { struct hexdump_handle hh; fprintf(stderr, "%s(): ", __func__); hexdump_start(&hh, (long)*bd->data, stderr); hexdump_data(&hh, bd->data, bd->len); hexdump_end(&hh); fprintf(stderr, "***\n"); } #endif } size_t buffer_read(struct buffer *bd, size_t len, char *data) { size_t len2; const char *buf; buf=buffer_read_prepare(bd, &len2); if(len>len2) len=len2; memcpy(data, buf, len); buffer_read_commit(bd, len); return len; } char *buffer_write_prepare(struct buffer *bd, size_t req_len) { /* TODO: limit growth by the 'resize' member */ if(bd->maxlen+req_len) { unsigned newmax=2*bd->max-bd->len+req_len; char *newdata; newdata=realloc(bd->data, newmax); if(!newdata) return 0; bd->data=newdata; bd->max=newmax; } DEBUG("bd->len=%zu req_len=%zu\n", bd->len, req_len); return bd->data+bd->len; /* point to end of buffer */ } void buffer_write_commit(struct buffer *bd, size_t len, int expand_crlf) { assert(bd!=NULL); DEBUG("bd=%p len=%zu\n", bd, len); assert(len < 1073741824l); /* over 1gig is unreasonable */ if(expand_crlf) { /* memmove LF to CR/LF */ size_t lfcnt, rem; char *e, *p; #ifdef BUFFER_DEBUGGING { struct hexdump_handle hh; fprintf(stderr, "%s():%u:\n", __func__, __LINE__); hexdump_start(&hh, (long)*bd->data, stderr); hexdump_data(&hh, bd->data, bd->len); hexdump_end(&hh); fprintf(stderr, "***\n"); } #endif /* count the number of LF characters */ for(p=bd->data+bd->len,rem=len,lfcnt=0;(e=memchr(p, '\n', rem));) { lfcnt++; e++; rem-=e-p; p=e; } if(lfcnt>buffer_avail(bd)) { /* allocate more space */ buffer_write_prepare(bd, lfcnt); } DEBUG("lfcnt=%d\n", lfcnt); for(p=bd->data+bd->len,rem=len;(e=memchr(p, '\n', rem));) { memmove(e+1, e, rem); *e='\r'; rem-=e-p; p=e+2; } len+=lfcnt; } bd->len+=len; assert(bd->len < 1073741824l); /* over 1gig is unreasonable */ } size_t buffer_write(struct buffer *bd, size_t len, const char *data, int expand_crlf) { char *dest; dest=buffer_write_prepare(bd, len); if(!dest) return 0; memcpy(dest, data, len); buffer_write_commit(bd, len, expand_crlf); return len; } /* returns number of bytes available in buffer */ size_t buffer_avail(struct buffer *bd) { return bd->max-bd->len; } size_t buffer_len(struct buffer *bd) { return bd->len; } /* reads a line and copies it into the buffer 'line'. returns the length or -1 * on failure. * * attempts to do some tricks to deal with CR, LF, CR LF, LF CR terminated * lines. */ int buffer_read_line(struct buffer *bd, size_t len, char *line) { size_t len2; const char *buf; size_t i; len--; buf=buffer_read_prepare(bd, &len2); if(!buf) return -1; if(len>len2) len=len2; for(i=0;ilen;i++) { printf(" %c", bd->data[i]); } printf("\nlen: %d\n", bd->len); } int main(int argc, char **argv) { /* TEST */ struct buffer bd; int len, j; char tmpbuf[32]; const char *buf, *linedata; size_t bufleft, linelen, i; const char *linetest_data[] = { "abcd\r\n", "hi\r\n", "okay\r", "m\r", "n\r\n", "\n", }; if(argc==2) { len=strtol(argv[1], 0, 0); } else { len=16; } buffer_init(&bd, len); buffer_write(&bd, 7, "testing", 0); buffer_write(&bd, 7, "testing", 0); buffer_write(&bd, 3, "1\n3", 0); printf("reading %d\n", buffer_read_line(&bd, sizeof tmpbuf, tmpbuf)); printf("line: '%s'\n", tmpbuf); buffer_write(&bd, 1, "!", 0); buffer_write(&bd, 3, "123", 0); buffer_write(&bd, 1, "!", 0); dump_buf(&bd); buffer_free(&bd); /* test the readline code */ buffer_init(&bd, len); for(j=0;j<3;j++) { for(i=0;i