X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;ds=sidebyside;f=src%2Futils%2Fstrbuf.c;h=7d8bc5527f6138f7a1c8ad6cf6730c6445c3460b;hb=e747b18c932d05b21a2b3f03390a2997117dbcdb;hp=4d8fc8b91ba234df10edd8d15d552169bcbf9930;hpb=a6753b33e70f0934d4b34b19366eddda56f05d9c;p=sysdb.git diff --git a/src/utils/strbuf.c b/src/utils/strbuf.c index 4d8fc8b..7d8bc55 100644 --- a/src/utils/strbuf.c +++ b/src/utils/strbuf.c @@ -36,6 +36,14 @@ #include +/* free memory if most of the buffer is unused */ +#define CHECK_SHRINK(strbuf) \ + do { \ + if ((strbuf)->pos < (strbuf)->size / 3) \ + /* don't free all memory to avoid churn */ \ + strbuf_resize((strbuf), 2 * (strbuf)->pos); \ + } while (0) + /* * private data structures */ @@ -55,14 +63,17 @@ strbuf_resize(sdb_strbuf_t *strbuf, size_t new_size) { char *tmp; - if (new_size <= strbuf->size) - return 0; + if (new_size <= strbuf->pos) + return -1; tmp = realloc(strbuf->string, new_size); if (! tmp) return -1; - strbuf->string = tmp; + if (new_size) + strbuf->string = tmp; + else + strbuf->string = NULL; strbuf->size = new_size; return 0; } /* strbuf_resize */ @@ -119,9 +130,14 @@ sdb_strbuf_vappend(sdb_strbuf_t *strbuf, const char *fmt, va_list ap) assert((strbuf->size == 0) || (strbuf->string[strbuf->pos] == '\0')); - if (strbuf->pos >= strbuf->size) + if (! strbuf->size) { /* use some arbitrary but somewhat reasonable default */ - if (strbuf_resize(strbuf, strbuf->size ? 2 * strbuf->size : 64)) + if (strbuf_resize(strbuf, 64)) + return -1; + } + /* make sure to reserve space for the nul-byte */ + else if (strbuf->pos >= strbuf->size - 1) + if (strbuf_resize(strbuf, 2 * strbuf->size)) return -1; assert(strbuf->size && strbuf->string); @@ -137,8 +153,12 @@ sdb_strbuf_vappend(sdb_strbuf_t *strbuf, const char *fmt, va_list ap) return status; } + /* 'status' does not include nul-byte */ if ((size_t)status >= strbuf->size - strbuf->pos) { - strbuf_resize(strbuf, (size_t)status + 1); + if (strbuf_resize(strbuf, strbuf->pos + (size_t)status + 1)) { + va_end(aq); + return -1; + } /* reset string and try again */ strbuf->string[strbuf->pos] = '\0'; @@ -148,6 +168,11 @@ sdb_strbuf_vappend(sdb_strbuf_t *strbuf, const char *fmt, va_list ap) strbuf->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(strbuf); + return (ssize_t)status; } /* sdb_strbuf_vappend */ @@ -218,6 +243,10 @@ sdb_strbuf_memappend(sdb_strbuf_t *strbuf, const void *data, size_t 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(strbuf); + return (ssize_t)n; } /* sdb_strbuf_memappend */ @@ -243,8 +272,9 @@ sdb_strbuf_read(sdb_strbuf_t *strbuf, int fd, size_t n) if (! strbuf) return -1; - if (strbuf_resize(strbuf, strbuf->pos + n + 1)) - return -1; + if (strbuf->pos + n + 1 >= strbuf->size) + if (strbuf_resize(strbuf, strbuf->pos + n + 1)) + return -1; ret = read(fd, strbuf->string + strbuf->pos, n); if (ret > 0) @@ -274,23 +304,48 @@ 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 *strbuf, size_t offset, size_t n) { + char *start; + size_t len; + if ((! strbuf) || (! n)) return; - if (n >= strbuf->pos) { - strbuf->string[0] = '\0'; - strbuf->pos = 0; + if (offset >= strbuf->pos) + return; + + len = strbuf->pos - offset; + + if (n >= len) { + strbuf->string[offset] = '\0'; + strbuf->pos = offset; return; } - assert(n < strbuf->pos); - memmove(strbuf->string, strbuf->string + n, strbuf->pos - n); + assert(offset + n < strbuf->pos); + assert(offset < strbuf->pos); + + start = strbuf->string + offset; + memmove(start, start + n, len - n); strbuf->pos -= n; strbuf->string[strbuf->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 *strbuf) +{ + if ((! strbuf) || (! strbuf->size)) + return; + + strbuf->string[0] = '\0'; + strbuf->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) { @@ -309,5 +364,13 @@ sdb_strbuf_len(sdb_strbuf_t *strbuf) return strbuf->pos; } /* sdb_strbuf_string */ +size_t +sdb_strbuf_cap(sdb_strbuf_t *strbuf) +{ + if (! strbuf) + return 0; + return strbuf->size; +} /* sdb_strbuf_cap */ + /* vim: set tw=78 sw=4 ts=4 noexpandtab : */