X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Futils%2Fstrbuf.c;h=d8a95ac42f60a4003d9cdf21924cbfbf2a5ccee1;hb=13fe0f9ec3d161fab7a015054649910541d75f5e;hp=a48f7bc5765959d38050bdf2f19c027a9765f789;hpb=6f6e12a94487f7bdf9efe2d5f1733684df6d75a6;p=sysdb.git diff --git a/src/utils/strbuf.c b/src/utils/strbuf.c index a48f7bc..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 @@ -32,6 +33,18 @@ #include #include #include +#include + +#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 @@ -41,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; }; /* @@ -48,18 +64,29 @@ struct sdb_strbuf { */ static int -strbuf_resize(sdb_strbuf_t *strbuf) +strbuf_resize(sdb_strbuf_t *buf, size_t new_size) { + size_t tmp_size; char *tmp; - assert(strbuf->size); + 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, 2 * strbuf->size); + tmp = realloc(buf->string, new_size); if (! tmp) return -1; - strbuf->string = tmp; - strbuf->size *= 2; + if (new_size) + buf->string = tmp; + else + buf->string = NULL; + buf->size = new_size; return 0; } /* strbuf_resize */ @@ -70,130 +97,288 @@ strbuf_resize(sdb_strbuf_t *strbuf) sdb_strbuf_t * sdb_strbuf_create(size_t size) { - sdb_strbuf_t *strbuf; + sdb_strbuf_t *buf; - if (! size) + buf = calloc(1, sizeof(*buf)); + if (! buf) return NULL; - strbuf = calloc(1, sizeof(*strbuf)); - if (! strbuf) - return NULL; + buf->string = NULL; + if (size) { + buf->string = malloc(size); + if (! buf->string) { + free(buf); + return NULL; + } - strbuf->string = malloc(size); - if (! strbuf->string) { - free(strbuf); - return NULL; + buf->string[0] = '\0'; + buf->min_size = size; } + else + buf->min_size = 64; - strbuf->string[0] = '\0'; - 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; - 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->string[strbuf->pos] == '\0'); + assert((buf->size == 0) || (buf->string[buf->pos] == '\0')); - if (strbuf->pos >= strbuf->size) - if (strbuf_resize(strbuf)) + if (! buf->size) { + /* use some arbitrary but somewhat reasonable default */ + 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(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); + /* '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; - 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 *buf, const void *data, size_t n) +{ + if ((! buf) || (! data)) + return -1; + + assert((buf->size == 0) || (buf->string[buf->pos] == '\0')); + + if (buf->pos + n + 1 > buf->size) { + if (strbuf_resize(buf, buf->pos + n + 1)) + return -1; + } + + 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'; + + /* 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 *buf, const void *data, size_t n) +{ + if ((! buf) || (! data)) + return -1; + + if (buf->size) { + buf->string[0] = '\0'; + buf->pos = 0; + } + + return sdb_strbuf_memappend(buf, data, n); +} /* sdb_strbuf_memcpy */ + +ssize_t +sdb_strbuf_read(sdb_strbuf_t *buf, int fd, size_t n) +{ + ssize_t ret; + + if (! buf) + return -1; + + 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 *buf) +{ + ssize_t ret = 0; + + if (! buf) + return -1; + + assert((!buf->size) || (buf->pos < buf->size)); + assert(buf->pos <= buf->size); + + while ((buf->pos > 0) + && (buf->string[buf->pos - 1] == '\n')) { + --buf->pos; + buf->string[buf->pos] = '\0'; + ++ret; + } + + return ret; +} /* sdb_strbuf_chomp */ + +void +sdb_strbuf_skip(sdb_strbuf_t *buf, size_t offset, size_t n) +{ + char *start; + size_t len; + + if ((! buf) || (! n)) + return; + + if (offset >= buf->pos) + return; + + len = buf->pos - offset; + + if (n >= len) { + buf->string[offset] = '\0'; + buf->pos = offset; + return; + } + + 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; - return strbuf->string; + if (! buf->size) + return ""; + 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 : */