buf.c

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

buf.c (2929B)


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