strbuf.c (4566B)
1 /* strbuf - String buffer routines 2 * 3 * Copyright (c) 2010-2012 Mark Pulford <mark@kyne.com.au> 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining 6 * a copy of this software and associated documentation files (the 7 * "Software"), to deal in the Software without restriction, including 8 * without limitation the rights to use, copy, modify, merge, publish, 9 * distribute, sublicense, and/or sell copies of the Software, and to 10 * permit persons to whom the Software is furnished to do so, subject to 11 * the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be 14 * included in all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 */ 24 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <stdarg.h> 28 #include <string.h> 29 #include <stdint.h> 30 31 #include "strbuf.h" 32 33 static void die(const char *fmt, ...) 34 { 35 va_list arg; 36 37 va_start(arg, fmt); 38 vfprintf(stderr, fmt, arg); 39 va_end(arg); 40 fprintf(stderr, "\n"); 41 42 abort(); 43 } 44 45 void strbuf_init(strbuf_t *s, size_t len) 46 { 47 size_t size; 48 49 if (!len) 50 size = STRBUF_DEFAULT_SIZE; 51 else 52 size = len + 1; /* \0 terminator */ 53 if (size < len) 54 die("Overflow, len: %zu", len); 55 s->buf = NULL; 56 s->size = size; 57 s->length = 0; 58 s->dynamic = 0; 59 s->reallocs = 0; 60 s->debug = 0; 61 62 s->buf = (char *)malloc(size); 63 if (!s->buf) 64 die("Out of memory"); 65 66 strbuf_ensure_null(s); 67 } 68 69 strbuf_t *strbuf_new(size_t len) 70 { 71 strbuf_t *s; 72 73 s = (strbuf_t*)malloc(sizeof(strbuf_t)); 74 if (!s) 75 die("Out of memory"); 76 77 strbuf_init(s, len); 78 79 /* Dynamic strbuf allocation / deallocation */ 80 s->dynamic = 1; 81 82 return s; 83 } 84 85 static inline void debug_stats(strbuf_t *s) 86 { 87 if (s->debug) { 88 fprintf(stderr, "strbuf(%lx) reallocs: %d, length: %zd, size: %zd\n", 89 (long)s, s->reallocs, s->length, s->size); 90 } 91 } 92 93 /* If strbuf_t has not been dynamically allocated, strbuf_free() can 94 * be called any number of times strbuf_init() */ 95 void strbuf_free(strbuf_t *s) 96 { 97 debug_stats(s); 98 99 if (s->buf) { 100 free(s->buf); 101 s->buf = NULL; 102 } 103 if (s->dynamic) 104 free(s); 105 } 106 107 char *strbuf_free_to_string(strbuf_t *s, size_t *len) 108 { 109 char *buf; 110 111 debug_stats(s); 112 113 strbuf_ensure_null(s); 114 115 buf = s->buf; 116 if (len) 117 *len = s->length; 118 119 if (s->dynamic) 120 free(s); 121 122 return buf; 123 } 124 125 static size_t calculate_new_size(strbuf_t *s, size_t len) 126 { 127 size_t reqsize, newsize; 128 129 if (len <= 0) 130 die("BUG: Invalid strbuf length requested"); 131 132 /* Ensure there is room for optional NULL termination */ 133 reqsize = len + 1; 134 if (reqsize < len) 135 die("Overflow, len: %zu", len); 136 137 /* If the user has requested to shrink the buffer, do it exactly */ 138 if (s->size > reqsize) 139 return reqsize; 140 141 newsize = s->size; 142 if (reqsize >= SIZE_MAX / 2) { 143 newsize = reqsize; 144 } else { 145 /* Exponential sizing */ 146 while (newsize < reqsize) 147 newsize *= 2; 148 } 149 150 if (newsize < reqsize) 151 die("BUG: strbuf length would overflow, len: %zu", len); 152 153 154 return newsize; 155 } 156 157 158 /* Ensure strbuf can handle a string length bytes long (ignoring NULL 159 * optional termination). */ 160 void strbuf_resize(strbuf_t *s, size_t len) 161 { 162 size_t newsize; 163 164 newsize = calculate_new_size(s, len); 165 166 if (s->debug > 1) { 167 fprintf(stderr, "strbuf(%lx) resize: %zd => %zd\n", 168 (long)s, s->size, newsize); 169 } 170 171 s->size = newsize; 172 s->buf = (char *)realloc(s->buf, s->size); 173 if (!s->buf) 174 die("Out of memory, len: %zu", len); 175 s->reallocs++; 176 } 177 178 void strbuf_append_string(strbuf_t *s, const char *str) 179 { 180 int i; 181 size_t space; 182 183 space = strbuf_empty_length(s); 184 185 for (i = 0; str[i]; i++) { 186 if (space < 1) { 187 strbuf_resize(s, s->length + 1); 188 space = strbuf_empty_length(s); 189 } 190 191 s->buf[s->length] = str[i]; 192 s->length++; 193 space--; 194 } 195 } 196 197 198 /* vi:ai et sw=4 ts=4: 199 */