X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Futils%2Fstrbuf.c;h=d8a95ac42f60a4003d9cdf21924cbfbf2a5ccee1;hb=d25f3c59b5cf5387acd4e6d1467ce9efffa40f9a;hp=2b64480c55fc5960c816f83ef3a6b3bf626c1faf;hpb=9fb079db93a6964c160439d851270b3433db59f5;p=sysdb.git diff --git a/src/utils/strbuf.c b/src/utils/strbuf.c index 2b64480..d8a95ac 100644 --- a/src/utils/strbuf.c +++ b/src/utils/strbuf.c @@ -25,6 +25,7 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "sysdb.h" #include "utils/strbuf.h" #include @@ -36,6 +37,15 @@ #include +/* free memory if most of the buffer is unused */ +#define CHECK_SHRINK(buf) \ + do { \ + if (((buf)->pos < (buf)->size / 3) \ + && (2 * (buf)->pos > (buf)->min_size)) \ + /* don't free all memory to avoid churn */ \ + strbuf_resize((buf), 2 * (buf)->pos); \ + } while (0) + /* * private data structures */ @@ -44,6 +54,9 @@ struct sdb_strbuf { char *string; size_t size; size_t pos; + + /* min size to shrink the buffer to */ + size_t min_size; }; /* @@ -51,19 +64,29 @@ struct sdb_strbuf { */ static int -strbuf_resize(sdb_strbuf_t *strbuf, size_t new_size) +strbuf_resize(sdb_strbuf_t *buf, size_t new_size) { + size_t tmp_size; char *tmp; - if (new_size <= strbuf->size) - return 0; + if (new_size <= buf->pos) + return -1; + + tmp_size = SDB_MAX(buf->size, buf->min_size); + if (! tmp_size) + tmp_size = 64; + while (tmp_size < new_size) + tmp_size *= 2; - tmp = realloc(strbuf->string, new_size); + tmp = realloc(buf->string, new_size); if (! tmp) return -1; - strbuf->string = tmp; - strbuf->size = new_size; + if (new_size) + buf->string = tmp; + else + buf->string = NULL; + buf->size = new_size; return 0; } /* strbuf_resize */ @@ -74,194 +97,214 @@ strbuf_resize(sdb_strbuf_t *strbuf, size_t new_size) sdb_strbuf_t * sdb_strbuf_create(size_t size) { - sdb_strbuf_t *strbuf; + sdb_strbuf_t *buf; - strbuf = calloc(1, sizeof(*strbuf)); - if (! strbuf) + buf = calloc(1, sizeof(*buf)); + if (! buf) return NULL; - strbuf->string = NULL; + buf->string = NULL; if (size) { - strbuf->string = malloc(size); - if (! strbuf->string) { - free(strbuf); + buf->string = malloc(size); + if (! buf->string) { + free(buf); return NULL; } - strbuf->string[0] = '\0'; + buf->string[0] = '\0'; + buf->min_size = size; } + else + buf->min_size = 64; - strbuf->size = size; - strbuf->pos = 0; + buf->size = size; + buf->pos = 0; - return strbuf; + return buf; } /* sdb_strbuf_create */ void -sdb_strbuf_destroy(sdb_strbuf_t *strbuf) +sdb_strbuf_destroy(sdb_strbuf_t *buf) { - if (! strbuf) + if (! buf) return; - if (strbuf->string) - free(strbuf->string); - free(strbuf); + if (buf->string) + free(buf->string); + free(buf); } /* sdb_strbuf_destroy */ ssize_t -sdb_strbuf_vappend(sdb_strbuf_t *strbuf, const char *fmt, va_list ap) +sdb_strbuf_vappend(sdb_strbuf_t *buf, const char *fmt, va_list ap) { va_list aq; int status; - if ((! strbuf) || (! fmt)) + if ((! buf) || (! fmt)) return -1; - assert((strbuf->size == 0) || (strbuf->string[strbuf->pos] == '\0')); + assert((buf->size == 0) || (buf->string[buf->pos] == '\0')); - if (strbuf->pos >= strbuf->size) + if (! buf->size) { /* use some arbitrary but somewhat reasonable default */ - if (strbuf_resize(strbuf, strbuf->size ? 2 * strbuf->size : 64)) + if (strbuf_resize(buf, 64)) + return -1; + } + /* make sure to reserve space for the nul-byte */ + else if (buf->pos >= buf->size - 1) + if (strbuf_resize(buf, 2 * buf->size)) return -1; - assert(strbuf->size && strbuf->string); - assert(strbuf->pos < strbuf->size); + assert(buf->size && buf->string); + assert(buf->pos < buf->size); /* 'ap' is invalid after calling vsnprintf; thus copy before using it */ va_copy(aq, ap); - status = vsnprintf(strbuf->string + strbuf->pos, - strbuf->size - strbuf->pos, fmt, ap); + status = vsnprintf(buf->string + buf->pos, + buf->size - buf->pos, fmt, ap); if (status < 0) { va_end(aq); return status; } - if ((size_t)status >= strbuf->size - strbuf->pos) { - strbuf_resize(strbuf, (size_t)status + 1); + /* 'status' does not include nul-byte */ + if ((size_t)status >= buf->size - buf->pos) { + if (strbuf_resize(buf, buf->pos + (size_t)status + 1)) { + va_end(aq); + return -1; + } /* reset string and try again */ - strbuf->string[strbuf->pos] = '\0'; - status = (int)sdb_strbuf_vappend(strbuf, fmt, aq); + buf->string[buf->pos] = '\0'; + status = (int)sdb_strbuf_vappend(buf, fmt, aq); } else - strbuf->pos += (size_t)status; + buf->pos += (size_t)status; va_end(aq); + + /* even though this function always appends to the existing buffer, the + * size might have previously been reset */ + CHECK_SHRINK(buf); + return (ssize_t)status; } /* sdb_strbuf_vappend */ ssize_t -sdb_strbuf_append(sdb_strbuf_t *strbuf, const char *fmt, ...) +sdb_strbuf_append(sdb_strbuf_t *buf, const char *fmt, ...) { va_list ap; ssize_t status; va_start(ap, fmt); - status = sdb_strbuf_vappend(strbuf, fmt, ap); + status = sdb_strbuf_vappend(buf, fmt, ap); va_end(ap); return status; } /* sdb_strbuf_append */ ssize_t -sdb_strbuf_vsprintf(sdb_strbuf_t *strbuf, const char *fmt, va_list ap) +sdb_strbuf_vsprintf(sdb_strbuf_t *buf, const char *fmt, va_list ap) { - if (! strbuf) + if (! buf) return -1; - if (strbuf->size) { - strbuf->string[0] = '\0'; - strbuf->pos = 0; + if (buf->size) { + buf->string[0] = '\0'; + buf->pos = 0; } - return sdb_strbuf_vappend(strbuf, fmt, ap); + return sdb_strbuf_vappend(buf, fmt, ap); } /* sdb_strbuf_vsprintf */ ssize_t -sdb_strbuf_sprintf(sdb_strbuf_t *strbuf, const char *fmt, ...) +sdb_strbuf_sprintf(sdb_strbuf_t *buf, const char *fmt, ...) { va_list ap; ssize_t status; va_start(ap, fmt); - status = sdb_strbuf_vsprintf(strbuf, fmt, ap); + status = sdb_strbuf_vsprintf(buf, fmt, ap); va_end(ap); return status; } /* sdb_strbuf_sprintf */ ssize_t -sdb_strbuf_memappend(sdb_strbuf_t *strbuf, const void *data, size_t n) +sdb_strbuf_memappend(sdb_strbuf_t *buf, const void *data, size_t n) { - if ((! strbuf) || (! data)) + if ((! buf) || (! data)) return -1; - assert((strbuf->size == 0) || (strbuf->string[strbuf->pos] == '\0')); - - if (strbuf->pos + n + 1 >= strbuf->size) { - size_t newsize = strbuf->size * 2; + assert((buf->size == 0) || (buf->string[buf->pos] == '\0')); - if (! newsize) - newsize = 64; - while (strbuf->pos + n + 1 >= newsize) - newsize *= 2; - - if (strbuf_resize(strbuf, newsize)) + if (buf->pos + n + 1 > buf->size) { + if (strbuf_resize(buf, buf->pos + n + 1)) return -1; } - assert(strbuf->size && strbuf->string); - assert(strbuf->pos < strbuf->size); + assert(buf->size && buf->string); + assert(buf->pos < buf->size); + + memcpy((void *)(buf->string + buf->pos), data, n); + buf->pos += n; + buf->string[buf->pos] = '\0'; - memcpy((void *)(strbuf->string + strbuf->pos), data, n); - strbuf->pos += n; - strbuf->string[strbuf->pos] = '\0'; + /* even though this function always appends to the existing buffer, the + * size might have previously been reset */ + CHECK_SHRINK(buf); return (ssize_t)n; } /* sdb_strbuf_memappend */ ssize_t -sdb_strbuf_memcpy(sdb_strbuf_t *strbuf, const void *data, size_t n) +sdb_strbuf_memcpy(sdb_strbuf_t *buf, const void *data, size_t n) { - if ((! strbuf) || (! data)) + if ((! buf) || (! data)) return -1; - if (strbuf->size) { - strbuf->string[0] = '\0'; - strbuf->pos = 0; + if (buf->size) { + buf->string[0] = '\0'; + buf->pos = 0; } - return sdb_strbuf_memappend(strbuf, data, n); + return sdb_strbuf_memappend(buf, data, n); } /* sdb_strbuf_memcpy */ ssize_t -sdb_strbuf_read(sdb_strbuf_t *strbuf, int fd, size_t n) +sdb_strbuf_read(sdb_strbuf_t *buf, int fd, size_t n) { - if (! strbuf) - return -1; + ssize_t ret; - if (strbuf_resize(strbuf, strbuf->pos + n + 1)) + if (! buf) return -1; - return read(fd, strbuf->string + strbuf->pos, n); + if (buf->pos + n + 1 >= buf->size) + if (strbuf_resize(buf, buf->pos + n + 1)) + return -1; + + ret = read(fd, buf->string + buf->pos, n); + if (ret > 0) + buf->pos += (size_t)ret; + return ret; } /* sdb_strbuf_read */ ssize_t -sdb_strbuf_chomp(sdb_strbuf_t *strbuf) +sdb_strbuf_chomp(sdb_strbuf_t *buf) { ssize_t ret = 0; - if (! strbuf) + if (! buf) return -1; - assert((!strbuf->size) || (strbuf->pos < strbuf->size)); - assert(strbuf->pos <= strbuf->size); + assert((!buf->size) || (buf->pos < buf->size)); + assert(buf->pos <= buf->size); - while ((strbuf->pos > 0) - && (strbuf->string[strbuf->pos - 1] == '\n')) { - --strbuf->pos; - strbuf->string[strbuf->pos] = '\0'; + while ((buf->pos > 0) + && (buf->string[buf->pos - 1] == '\n')) { + --buf->pos; + buf->string[buf->pos] = '\0'; ++ret; } @@ -269,40 +312,73 @@ sdb_strbuf_chomp(sdb_strbuf_t *strbuf) } /* sdb_strbuf_chomp */ void -sdb_strbuf_skip(sdb_strbuf_t *strbuf, size_t n) +sdb_strbuf_skip(sdb_strbuf_t *buf, size_t offset, size_t n) { - if ((! strbuf) || (! n)) + char *start; + size_t len; + + if ((! buf) || (! n)) return; - if (n >= strbuf->pos) { - strbuf->string[0] = '\0'; - strbuf->pos = 0; + if (offset >= buf->pos) + return; + + len = buf->pos - offset; + + if (n >= len) { + buf->string[offset] = '\0'; + buf->pos = offset; return; } - assert(n < strbuf->pos); - memmove(strbuf->string, strbuf->string + n, strbuf->pos - n); - strbuf->pos -= n; - strbuf->string[strbuf->pos] = '\0'; + assert(offset + n < buf->pos); + assert(offset < buf->pos); + + start = buf->string + offset; + memmove(start, start + n, len - n); + buf->pos -= n; + buf->string[buf->pos] = '\0'; + + /* don't resize now but wait for the next write to avoid churn */ } /* sdb_strbuf_skip */ +void +sdb_strbuf_clear(sdb_strbuf_t *buf) +{ + if ((! buf) || (! buf->size)) + return; + + buf->string[0] = '\0'; + buf->pos = 0; + + /* don't resize now but wait for the next write to avoid churn */ +} /* sdb_strbuf_clear */ + const char * -sdb_strbuf_string(sdb_strbuf_t *strbuf) +sdb_strbuf_string(sdb_strbuf_t *buf) { - if (! strbuf) + if (! buf) return NULL; - if (! strbuf->size) + if (! buf->size) return ""; - return strbuf->string; + return buf->string; } /* sdb_strbuf_string */ size_t -sdb_strbuf_len(sdb_strbuf_t *strbuf) +sdb_strbuf_len(sdb_strbuf_t *buf) { - if (! strbuf) + if (! buf) return 0; - return strbuf->pos; + return buf->pos; } /* sdb_strbuf_string */ +size_t +sdb_strbuf_cap(sdb_strbuf_t *buf) +{ + if (! buf) + return 0; + return buf->size; +} /* sdb_strbuf_cap */ + /* vim: set tw=78 sw=4 ts=4 noexpandtab : */