/* data_buf.c -- * Copyright 2007,2011 Red Hat Inc., Durham, North Carolina. * All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Authors: * John Dennis */ /* * gcc -DTEST -g data_buf.c -o data_buf * gcc -DTEST -g data_buf.c -o data_buf && valgrind --leak-check=yes ./data_buf */ /*****************************************************************************/ /******************************** Documentation ******************************/ /*****************************************************************************/ /*****************************************************************************/ /******************************* Include Files *******************************/ /*****************************************************************************/ #include #include #include #include #include #include #include "data_buf.h" /*****************************************************************************/ /****************************** Internal Defines *****************************/ /*****************************************************************************/ #ifndef MIN #define MIN(a,b) (((a)<=(b))?(a):(b)) #endif #ifndef MAX #define MAX(a,b) (((a)>=(b))?(a):(b)) #endif //#define DEBUG 1 #ifdef DEBUG #define DATABUF_VALIDATE(db) \ { \ if (db->alloc_ptr == NULL || db->alloc_size == 0) { \ assert(db->alloc_ptr == NULL); \ assert(db->alloc_size == 0); \ assert(db->len == 0); \ } else { \ assert(db->offset <= db->alloc_size); \ assert(db->len <= db->alloc_size); \ assert(db->offset+db->len <= db->alloc_size); \ } \ } #else #define DATABUF_VALIDATE(db) #endif /*****************************************************************************/ /************************** Internal Type Definitions ************************/ /*****************************************************************************/ /*****************************************************************************/ /********************** External Function Declarations *********************/ /*****************************************************************************/ /*****************************************************************************/ /********************** Internal Function Declarations *********************/ /*****************************************************************************/ static int databuf_shift_data_to_beginning(DataBuf *db); static int databuf_strcat(DataBuf *db, const char *str); /*****************************************************************************/ /************************* External Global Variables ***********************/ /*****************************************************************************/ /*****************************************************************************/ /************************* Internal Global Variables ***********************/ /*****************************************************************************/ #ifdef DEBUG static int debug = 0; #endif /*****************************************************************************/ /**************************** Inline Functions *****************************/ /*****************************************************************************/ static inline char *databuf_end(DataBuf *db) {return (db->alloc_ptr == NULL) ? NULL : db->alloc_ptr+db->offset+db->len;} static inline char *databuf_alloc_end(DataBuf *db) {return (db->alloc_ptr == NULL) ? NULL : db->alloc_ptr+db->alloc_size;} static inline int databuf_tail_size(DataBuf *db) {return db->alloc_size - (db->offset+db->len);} static inline int databuf_tail_available(DataBuf *db, size_t append_len) {return append_len <= databuf_tail_size(db);} static inline size_t databuf_free_size(DataBuf *db) {return db->alloc_size-db->len;} /*****************************************************************************/ /*************************** Internal Functions ****************************/ /*****************************************************************************/ static int databuf_shift_data_to_beginning(DataBuf *db) { DATABUF_VALIDATE(db); if (db->flags & DATABUF_FLAG_PRESERVE_HEAD) return -1; if (databuf_beg(db) == NULL) return 1; if (db->offset) { memmove(db->alloc_ptr, databuf_beg(db), db->len); db->offset = 0; } DATABUF_VALIDATE(db); return 1; } /*****************************************************************************/ /**************************** Exported Functions ***************************/ /*****************************************************************************/ void databuf_print(DataBuf *db, int print_data, char *fmt, ...) { va_list ap; va_start(ap, fmt); if (fmt) { vprintf(fmt, ap); } printf("%salloc_size=%zu alloc_ptr=%p offset=%zu beg=%p len=%zu max_len=%zu flags=[", fmt?" ":"", db->alloc_size, db->alloc_ptr, db->offset, databuf_beg(db), db->len, db->max_len); if (db->flags & DATABUF_FLAG_PRESERVE_HEAD) printf("PRESERVE_HEAD "); if (db->flags & DATABUF_FLAG_STRING) printf("STRING "); printf("]"); if (print_data) { printf(" ["); fwrite(databuf_beg(db), 1, db->len, stdout); printf("]"); } printf("\n"); va_end(ap); } int databuf_init(DataBuf *db, size_t size, unsigned flags) { db->alloc_ptr = NULL; db->alloc_size = 0; db->offset = 0; db->len = 0; db->max_len = 0; db->flags = flags; if (size) { if ((db->alloc_ptr = malloc(size))) { db->alloc_size = size; return 1; } else { return -1; } } // For strings intialize with initial NULL terminator if (flags & DATABUF_FLAG_STRING) databuf_strcat(db, ""); return 1; } void databuf_free(DataBuf *db) { DATABUF_VALIDATE(db); if (db->alloc_ptr != NULL) { free(db->alloc_ptr); } db->alloc_ptr = NULL; db->alloc_size = 0; db->offset = 0; db->len = 0; db->max_len = 0; DATABUF_VALIDATE(db); } int databuf_append(DataBuf *db, const char *src, size_t src_size) { size_t new_size; DATABUF_VALIDATE(db); if (src == NULL || src_size == 0) return 0; new_size = db->len+src_size; #ifdef DEBUG if (debug) databuf_print(db, 1, "databuf_append() size=%zd", src_size); #endif if ((new_size > db->alloc_size) || ((db->flags & DATABUF_FLAG_PRESERVE_HEAD) && !databuf_tail_available(db, src_size))) { /* not enough room, we must realloc */ void *new_alloc; databuf_shift_data_to_beginning(db); if ((new_alloc = realloc(db->alloc_ptr, new_size))) { db->alloc_ptr = new_alloc; db->alloc_size = new_size; } else { return -1; /* realloc failed */ } } else { /* we can fit within current allocation, but can we append? */ if (!databuf_tail_available(db, src_size)) { /* we can't append in place, must create room at tail by shifting data forward to the beginning of the allocation block */ databuf_shift_data_to_beginning(db); } } #ifdef DEBUG if (debug) databuf_print(db, 1, "databuf_append() about to memmove()"); #endif /* pointers all set up and room availble, move the data and update */ memmove(databuf_end(db), src, src_size); db->len = new_size; db->max_len = MAX(db->max_len, new_size); #ifdef DEBUG if (debug) databuf_print(db, 1, "databuf_append() conclusion"); #endif DATABUF_VALIDATE(db); return 1; } static int databuf_strcat(DataBuf *db, const char *str) { size_t str_len; DATABUF_VALIDATE(db); if (str == NULL) return 0; // +1 so the data append also copies the NULL terminator str_len = strlen(str) + 1; // If there is a NULL terminator exclude it so the subsequent // data append produces a proper string concatenation if (db->len > 0) { char *last_char = databuf_end(db) - 1; if (*last_char == 0) { db->len--; // backup over NULL terminator } } // Copy string and NULL terminator databuf_append(db, str, str_len); DATABUF_VALIDATE(db); return 1; } int databuf_advance(DataBuf *db, size_t advance) { size_t actual_advance; DATABUF_VALIDATE(db); #ifdef DEBUG if (debug) databuf_print(db, 1, "databuf_advance() enter, advance=%zd", advance); #endif actual_advance = MIN(advance, db->len); db->offset += actual_advance; db->len -= actual_advance; #ifdef DEBUG if (debug) databuf_print(db, 1, "databuf_advance() leave, actual_advance=%zd", actual_advance); #endif DATABUF_VALIDATE(db); if (advance == actual_advance) { return 1; } else { errno = ESPIPE; // Illegal seek return -1; } } int databuf_reset(DataBuf *db) { #ifdef DEBUG if (debug) databuf_print(db, 1, "databuf_reset() entry"); #endif if (!(db->flags & DATABUF_FLAG_PRESERVE_HEAD)) return -1; db->offset = 0; db->len = MIN(db->alloc_size, db->max_len); #ifdef DEBUG if (debug) databuf_print(db, 1, "databuf_reset() exit"); #endif return 1; } /*****************************************************************************/ /******************************* Test Program ******************************/ /*****************************************************************************/ #ifdef TEST static char *make_data(size_t size, const char *fill) { int n=0; char *data = malloc(size); if (data == NULL) { fprintf(stderr, "ERROR: make_data malloc failed\n"); exit(1); } n += snprintf(data, size, "%d", size); while (n < size) { n += snprintf(data+n, size-n, "%s", fill); } return data; } int main(int argc, char **argv) { size_t size = 0; DataBuf buf; char *data; assert(databuf_init(&buf, size, DATABUF_FLAG_STRING)); databuf_print(&buf, 1, "after init size=%d", size); #if 1 data = "a"; assert(databuf_strcat(&buf, data)); databuf_print(&buf, 1, "after strcat(%s)", data); data = "bb"; assert(databuf_strcat(&buf, data)); databuf_print(&buf, 1, "after strcat(%s)", data); data = "ccc"; assert(databuf_strcat(&buf, data)); databuf_print(&buf, 1, "after strcat(%s)", data); #endif databuf_free(&buf); #if 0 assert(databuf_init(&buf, size, 0)); databuf_print(&buf, 1, "after init size=%d", size); size = 8; data = make_data(size, "a"); assert(databuf_append(&buf, data, size)); databuf_print(&buf, 1, "after append size=%d", size); assert(databuf_append(&buf, data, size)); free(data); databuf_print(&buf, 1, "after append size=%d", size); assert(databuf_advance(&buf, 4)); databuf_print(&buf, 1, "after databuf_advance(%d", 4); size = 5; data = make_data(size, "b"); assert(databuf_append(&buf, data, size)); free(data); databuf_print(&buf, 1, "after append size=%d", size); size = 7; data = make_data(size, "c"); assert(databuf_append(&buf, data, size)); free(data); databuf_print(&buf, 1, "after append size=%d", size); databuf_free(&buf); #endif exit(0); } #endif