1*4fdcd213SMatt Stancliff /* strbuf - String buffer routines
215108778Santirez *
3*4fdcd213SMatt Stancliff * Copyright (c) 2010-2012 Mark Pulford <[email protected]>
415108778Santirez *
515108778Santirez * Permission is hereby granted, free of charge, to any person obtaining
615108778Santirez * a copy of this software and associated documentation files (the
715108778Santirez * "Software"), to deal in the Software without restriction, including
815108778Santirez * without limitation the rights to use, copy, modify, merge, publish,
915108778Santirez * distribute, sublicense, and/or sell copies of the Software, and to
1015108778Santirez * permit persons to whom the Software is furnished to do so, subject to
1115108778Santirez * the following conditions:
1215108778Santirez *
1315108778Santirez * The above copyright notice and this permission notice shall be
1415108778Santirez * included in all copies or substantial portions of the Software.
1515108778Santirez *
1615108778Santirez * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
1715108778Santirez * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
1815108778Santirez * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
1915108778Santirez * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
2015108778Santirez * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
2115108778Santirez * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
2215108778Santirez * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2315108778Santirez */
2415108778Santirez
2515108778Santirez #include <stdio.h>
2615108778Santirez #include <stdlib.h>
2715108778Santirez #include <stdarg.h>
2815108778Santirez #include <string.h>
2915108778Santirez
3015108778Santirez #include "strbuf.h"
3115108778Santirez
die(const char * fmt,...)32*4fdcd213SMatt Stancliff static void die(const char *fmt, ...)
3315108778Santirez {
3415108778Santirez va_list arg;
3515108778Santirez
3615108778Santirez va_start(arg, fmt);
3715108778Santirez vfprintf(stderr, fmt, arg);
3815108778Santirez va_end(arg);
3915108778Santirez fprintf(stderr, "\n");
4015108778Santirez
4115108778Santirez exit(-1);
4215108778Santirez }
4315108778Santirez
strbuf_init(strbuf_t * s,int len)4415108778Santirez void strbuf_init(strbuf_t *s, int len)
4515108778Santirez {
4615108778Santirez int size;
4715108778Santirez
4815108778Santirez if (len <= 0)
4915108778Santirez size = STRBUF_DEFAULT_SIZE;
5015108778Santirez else
5115108778Santirez size = len + 1; /* \0 terminator */
5215108778Santirez
5315108778Santirez s->buf = NULL;
5415108778Santirez s->size = size;
5515108778Santirez s->length = 0;
5615108778Santirez s->increment = STRBUF_DEFAULT_INCREMENT;
5715108778Santirez s->dynamic = 0;
5815108778Santirez s->reallocs = 0;
5915108778Santirez s->debug = 0;
6015108778Santirez
6115108778Santirez s->buf = malloc(size);
6215108778Santirez if (!s->buf)
6315108778Santirez die("Out of memory");
6415108778Santirez
6515108778Santirez strbuf_ensure_null(s);
6615108778Santirez }
6715108778Santirez
strbuf_new(int len)6815108778Santirez strbuf_t *strbuf_new(int len)
6915108778Santirez {
7015108778Santirez strbuf_t *s;
7115108778Santirez
7215108778Santirez s = malloc(sizeof(strbuf_t));
7315108778Santirez if (!s)
7415108778Santirez die("Out of memory");
7515108778Santirez
7615108778Santirez strbuf_init(s, len);
7715108778Santirez
7815108778Santirez /* Dynamic strbuf allocation / deallocation */
7915108778Santirez s->dynamic = 1;
8015108778Santirez
8115108778Santirez return s;
8215108778Santirez }
8315108778Santirez
strbuf_set_increment(strbuf_t * s,int increment)8415108778Santirez void strbuf_set_increment(strbuf_t *s, int increment)
8515108778Santirez {
8615108778Santirez /* Increment > 0: Linear buffer growth rate
8715108778Santirez * Increment < -1: Exponential buffer growth rate */
8815108778Santirez if (increment == 0 || increment == -1)
8915108778Santirez die("BUG: Invalid string increment");
9015108778Santirez
9115108778Santirez s->increment = increment;
9215108778Santirez }
9315108778Santirez
debug_stats(strbuf_t * s)9415108778Santirez static inline void debug_stats(strbuf_t *s)
9515108778Santirez {
9615108778Santirez if (s->debug) {
9715108778Santirez fprintf(stderr, "strbuf(%lx) reallocs: %d, length: %d, size: %d\n",
9815108778Santirez (long)s, s->reallocs, s->length, s->size);
9915108778Santirez }
10015108778Santirez }
10115108778Santirez
10215108778Santirez /* If strbuf_t has not been dynamically allocated, strbuf_free() can
10315108778Santirez * be called any number of times strbuf_init() */
strbuf_free(strbuf_t * s)10415108778Santirez void strbuf_free(strbuf_t *s)
10515108778Santirez {
10615108778Santirez debug_stats(s);
10715108778Santirez
10815108778Santirez if (s->buf) {
10915108778Santirez free(s->buf);
11015108778Santirez s->buf = NULL;
11115108778Santirez }
11215108778Santirez if (s->dynamic)
11315108778Santirez free(s);
11415108778Santirez }
11515108778Santirez
strbuf_free_to_string(strbuf_t * s,int * len)11615108778Santirez char *strbuf_free_to_string(strbuf_t *s, int *len)
11715108778Santirez {
11815108778Santirez char *buf;
11915108778Santirez
12015108778Santirez debug_stats(s);
12115108778Santirez
12215108778Santirez strbuf_ensure_null(s);
12315108778Santirez
12415108778Santirez buf = s->buf;
12515108778Santirez if (len)
12615108778Santirez *len = s->length;
12715108778Santirez
12815108778Santirez if (s->dynamic)
12915108778Santirez free(s);
13015108778Santirez
13115108778Santirez return buf;
13215108778Santirez }
13315108778Santirez
calculate_new_size(strbuf_t * s,int len)13415108778Santirez static int calculate_new_size(strbuf_t *s, int len)
13515108778Santirez {
13615108778Santirez int reqsize, newsize;
13715108778Santirez
13815108778Santirez if (len <= 0)
13915108778Santirez die("BUG: Invalid strbuf length requested");
14015108778Santirez
14115108778Santirez /* Ensure there is room for optional NULL termination */
14215108778Santirez reqsize = len + 1;
14315108778Santirez
14415108778Santirez /* If the user has requested to shrink the buffer, do it exactly */
14515108778Santirez if (s->size > reqsize)
14615108778Santirez return reqsize;
14715108778Santirez
14815108778Santirez newsize = s->size;
14915108778Santirez if (s->increment < 0) {
15015108778Santirez /* Exponential sizing */
15115108778Santirez while (newsize < reqsize)
15215108778Santirez newsize *= -s->increment;
15315108778Santirez } else {
15415108778Santirez /* Linear sizing */
15515108778Santirez newsize = ((newsize + s->increment - 1) / s->increment) * s->increment;
15615108778Santirez }
15715108778Santirez
15815108778Santirez return newsize;
15915108778Santirez }
16015108778Santirez
16115108778Santirez
16215108778Santirez /* Ensure strbuf can handle a string length bytes long (ignoring NULL
16315108778Santirez * optional termination). */
strbuf_resize(strbuf_t * s,int len)16415108778Santirez void strbuf_resize(strbuf_t *s, int len)
16515108778Santirez {
16615108778Santirez int newsize;
16715108778Santirez
16815108778Santirez newsize = calculate_new_size(s, len);
16915108778Santirez
17015108778Santirez if (s->debug > 1) {
17115108778Santirez fprintf(stderr, "strbuf(%lx) resize: %d => %d\n",
17215108778Santirez (long)s, s->size, newsize);
17315108778Santirez }
17415108778Santirez
17515108778Santirez s->size = newsize;
17615108778Santirez s->buf = realloc(s->buf, s->size);
17715108778Santirez if (!s->buf)
17815108778Santirez die("Out of memory");
17915108778Santirez s->reallocs++;
18015108778Santirez }
18115108778Santirez
strbuf_append_string(strbuf_t * s,const char * str)18215108778Santirez void strbuf_append_string(strbuf_t *s, const char *str)
18315108778Santirez {
18415108778Santirez int space, i;
18515108778Santirez
18615108778Santirez space = strbuf_empty_length(s);
18715108778Santirez
18815108778Santirez for (i = 0; str[i]; i++) {
18915108778Santirez if (space < 1) {
19015108778Santirez strbuf_resize(s, s->length + 1);
19115108778Santirez space = strbuf_empty_length(s);
19215108778Santirez }
19315108778Santirez
19415108778Santirez s->buf[s->length] = str[i];
19515108778Santirez s->length++;
19615108778Santirez space--;
19715108778Santirez }
19815108778Santirez }
19915108778Santirez
20015108778Santirez /* strbuf_append_fmt() should only be used when an upper bound
20115108778Santirez * is known for the output string. */
strbuf_append_fmt(strbuf_t * s,int len,const char * fmt,...)20215108778Santirez void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...)
20315108778Santirez {
20415108778Santirez va_list arg;
20515108778Santirez int fmt_len;
20615108778Santirez
20715108778Santirez strbuf_ensure_empty_length(s, len);
20815108778Santirez
20915108778Santirez va_start(arg, fmt);
21015108778Santirez fmt_len = vsnprintf(s->buf + s->length, len, fmt, arg);
21115108778Santirez va_end(arg);
21215108778Santirez
21315108778Santirez if (fmt_len < 0)
21415108778Santirez die("BUG: Unable to convert number"); /* This should never happen.. */
21515108778Santirez
21615108778Santirez s->length += fmt_len;
21715108778Santirez }
21815108778Santirez
21915108778Santirez /* strbuf_append_fmt_retry() can be used when the there is no known
22015108778Santirez * upper bound for the output string. */
strbuf_append_fmt_retry(strbuf_t * s,const char * fmt,...)22115108778Santirez void strbuf_append_fmt_retry(strbuf_t *s, const char *fmt, ...)
22215108778Santirez {
22315108778Santirez va_list arg;
22415108778Santirez int fmt_len, try;
22515108778Santirez int empty_len;
22615108778Santirez
22715108778Santirez /* If the first attempt to append fails, resize the buffer appropriately
22815108778Santirez * and try again */
22915108778Santirez for (try = 0; ; try++) {
23015108778Santirez va_start(arg, fmt);
23115108778Santirez /* Append the new formatted string */
23215108778Santirez /* fmt_len is the length of the string required, excluding the
23315108778Santirez * trailing NULL */
23415108778Santirez empty_len = strbuf_empty_length(s);
23515108778Santirez /* Add 1 since there is also space to store the terminating NULL. */
23615108778Santirez fmt_len = vsnprintf(s->buf + s->length, empty_len + 1, fmt, arg);
23715108778Santirez va_end(arg);
23815108778Santirez
23915108778Santirez if (fmt_len <= empty_len)
24015108778Santirez break; /* SUCCESS */
24115108778Santirez if (try > 0)
24215108778Santirez die("BUG: length of formatted string changed");
24315108778Santirez
24415108778Santirez strbuf_resize(s, s->length + fmt_len);
24515108778Santirez }
24615108778Santirez
24715108778Santirez s->length += fmt_len;
24815108778Santirez }
24915108778Santirez
25015108778Santirez /* vi:ai et sw=4 ts=4:
25115108778Santirez */
252