buf.c

Simple byte buffers
git clone git://git.finwo.net/lib/buf.c
Log | Files | Refs | README | LICENSE

buf.c (3031B)


      1 #include <string.h>
      2 #include <stdlib.h>
      3 
      4 #include "buf.h"
      5 
      6 #ifdef __cplusplus
      7 extern "C" {
      8 #endif // __cplusplus
      9 
     10 #ifndef MAX
     11 #define MAX(a,b) ((a)>(b)?(a):(b))
     12 #endif
     13 
     14 #ifndef MIN
     15 #define MIN(a,b) ((a)<(b)?(a):(b))
     16 #endif
     17 
     18 #ifndef NULL
     19 #define NULL ((void*)0)
     20 #endif
     21 
     22 #define ARRAY_SIZE(x) ((sizeof x) / (sizeof *x))
     23 
     24 const char *error_messages[] = {
     25   "Unknown error",
     26   "Missing subject",
     27   "Missing source",
     28   "Negative length",
     29 };
     30 
     31 #define BUF_ERRNO_UNKNOWN         -1
     32 #define BUF_ERRNO_OK               0
     33 #define BUF_ERRNO_MISSING_SUBJECT  1
     34 #define BUF_ERRNO_MISSING_SOURCE   2
     35 #define BUF_ERRNO_NEGATIVE_LENGTH  3
     36 
     37 static void *(*_realloc)(void* ptr,size_t size) = realloc;
     38 static void (*_free)(void* ptr) = free;
     39 
     40 void buf_set_allocator(void *(*realloc)(void*,size_t), void (*free)(void*)) {
     41   _realloc = realloc;
     42   _free    = free;
     43 }
     44 
     45 const char *buf_error(int errno) {
     46 
     47   // No error
     48   if (!errno) return NULL;
     49 
     50   // Known error
     51   if ((errno > 0) && (errno < ARRAY_SIZE(error_messages))) {
     52     return error_messages[errno];
     53   }
     54 
     55   // Unknown error
     56   return error_messages[0];
     57 }
     58 
     59 int buf_append(struct buf *subject, const char *source, const size_t length) {
     60   if (!subject) return BUF_ERRNO_MISSING_SUBJECT;
     61   if (!source) return BUF_ERRNO_MISSING_SOURCE;
     62   if (length < 0) return BUF_ERRNO_NEGATIVE_LENGTH;
     63 
     64   // Initial alloc if blank buffer
     65   if (!subject->alloc) {
     66     size_t l = MAX(32, length);
     67     subject->alloc = _realloc(NULL, l);
     68     subject->dat   = subject->alloc;
     69     subject->cap   = l;
     70     subject->len   = 0;
     71     goto lbl_buf_append_copy; // Simply skips cap checks
     72   }
     73 
     74   // Eject consumed data if we need space
     75   if (
     76       (subject->dat != subject->alloc) &&
     77       ((subject->len + length) > subject->cap)
     78   ) {
     79     size_t diff = subject->dat - subject->alloc;
     80     memmove(subject->alloc, subject->dat, subject->len);
     81     subject->cap += diff;
     82     subject->dat  = subject->alloc;
     83   }
     84 
     85   // Grow buffer if still not enough space
     86   if ((subject->len + length) > subject->cap) {
     87     subject->cap   = MAX(32, subject->cap * 2);
     88     subject->alloc = _realloc(subject->alloc, subject->cap);
     89     subject->dat   = subject->alloc;
     90   }
     91 
     92   // Add the data to the end
     93 lbl_buf_append_copy:
     94   memcpy(subject->dat + subject->len, source, length);
     95   subject->len += length;
     96 
     97   return BUF_ERRNO_OK;
     98 }
     99 
    100 int buf_append_byte(struct buf *subject, const char value) {
    101   return buf_append(subject, &value, sizeof(char));
    102 }
    103 
    104 size_t buf_consume(struct buf *subject, size_t length) {
    105   if (!subject) return 0;
    106   if (length <= 0) return 0;
    107 
    108   size_t n = MIN(length, subject->len);
    109   subject->dat += n;
    110   subject->len -= n;
    111   subject->cap -= n;
    112 
    113   return n;
    114 }
    115 
    116 /**
    117  * Clears out all data from the buffer
    118  */
    119 int buf_clear(struct buf *subject) {
    120   if (!subject) return BUF_ERRNO_MISSING_SUBJECT;
    121 
    122   if (subject->alloc) {
    123     _free(subject->alloc);
    124     subject->alloc = NULL;
    125   }
    126 
    127   subject->dat = NULL;
    128   subject->len = 0;
    129   subject->cap = 0;
    130 
    131   return BUF_ERRNO_OK;
    132 }
    133 
    134 #ifdef __cplusplus
    135 } // extern "C" {
    136 #endif // __cplusplus