Added charstring "class"
[mmh] / sbr / charstring.c
diff --git a/sbr/charstring.c b/sbr/charstring.c
new file mode 100644 (file)
index 0000000..ff311f1
--- /dev/null
@@ -0,0 +1,154 @@
+/* charstring.c -- dynamically-sized char array that can report size
+ *                        in both characters and bytes
+ *
+ * This code is Copyright (c) 2014, by the authors of nmh.  See the
+ * COPYRIGHT file in the root directory of the nmh distribution for
+ * complete copyright information.
+ */
+
+#include <h/mh.h>
+#include <h/utils.h>
+
+#define CHARSTRING_DEFAULT_SIZE 64
+
+struct charstring {
+       char *buffer;  /* the char array, not always null-terminated */
+       size_t max;    /* current size of the char array, in bytes */
+       char *cur;     /* size in bytes = cur - buffer, without trailing null */
+};
+
+
+static void
+charstring_reserve(charstring_t s, size_t need)
+{
+       const size_t cur = s->cur - s->buffer;
+
+       while (need >= s->max - cur) {
+               /* Insufficient capacity, so double it. */
+               s->buffer = mh_xrealloc(s->buffer, s->max *= 2);
+               s->cur = s->buffer + cur;
+       }
+}
+
+/*
+ * max is in characters
+ */
+charstring_t
+charstring_create(size_t max)
+{
+       charstring_t s = mh_xcalloc(1, sizeof(*s));
+
+       s->max = max ? max : CHARSTRING_DEFAULT_SIZE;
+       s->cur = s->buffer = mh_xcalloc(s->max, 1);
+
+       return s;
+}
+
+charstring_t
+charstring_copy(const charstring_t src)
+{
+       const size_t num = src->cur - src->buffer;
+       charstring_t s = mh_xcalloc(1, sizeof(*s));
+
+       s->max = src->max;
+       s->buffer = mh_xcalloc(s->max, 1);
+       memcpy(s->buffer, src->buffer, num);
+       s->cur = s->buffer + num;
+
+       return s;
+}
+
+/*
+ * OK to call charstring_free with a NULL argument.
+ */
+void
+charstring_free(charstring_t s)
+{
+       if (s) {
+               free(s->buffer);
+               free(s);
+       }
+}
+
+void
+charstring_push_back(charstring_t s, const char c)
+{
+       charstring_reserve(s, s->cur - s->buffer + 1);
+       *s->cur++ = c;
+}
+
+/*
+ * num is the number of bytes in c
+ */
+void
+charstring_push_back_chars(charstring_t s, const char c[], size_t num)
+{
+       charstring_reserve(s, s->cur - s->buffer + num);
+       memcpy(s->cur, c, num);
+       s->cur += num;
+}
+
+void
+charstring_append(charstring_t dest, const charstring_t src)
+{
+       const size_t num = src->cur - src->buffer;
+
+       if (num > 0) {
+               charstring_reserve(dest, dest->cur - dest->buffer + num);
+               memcpy(dest->cur, src->buffer, num);
+               dest->cur += num;
+       }
+}
+
+void
+charstring_append_cstring(charstring_t dest, const char src[])
+{
+       const size_t num = strlen(src);
+
+       if (num > 0) {
+               charstring_reserve(dest, dest->cur - dest->buffer + num);
+               memcpy(dest->cur, src, num);  /* Exclude src's trailing NUL. */
+               dest->cur += num;
+       }
+}
+
+void
+charstring_clear(charstring_t s)
+{
+       s->cur = s->buffer;
+}
+
+/*
+ * Don't store return value of charstring_buffer() and use later after
+ * intervening push_back's; use charstring_buffer_copy() instead.
+ */
+const char *
+charstring_buffer(const charstring_t s)
+{
+       charstring_reserve(s, s->cur - s->buffer + 1);
+
+       /* This is the only place that we null-terminate the buffer. */
+       *s->cur = '\0';
+       /* Don't increment cur so that more can be appended later, and so
+          that charstring_bytes() behaves as strlen() by not counting the
+          null. */
+
+       return s->buffer;
+}
+
+char *
+charstring_buffer_copy(const charstring_t s)
+{
+       char *copy = mh_xcalloc(s->cur - s->buffer + 1, 1);
+
+       /* Use charstring_buffer() to null terminate the buffer. */
+       memcpy(copy, charstring_buffer(s), s->cur - s->buffer + 1);
+
+       return copy;
+}
+
+size_t
+charstring_bytes(const charstring_t s)
+{
+       return s->cur - s->buffer;
+}