Added charstring "class"
[mmh] / sbr / charstring.c
1 /* charstring.c -- dynamically-sized char array that can report size
2  *                         in both characters and bytes
3  *
4  * This code is Copyright (c) 2014, by the authors of nmh.  See the
5  * COPYRIGHT file in the root directory of the nmh distribution for
6  * complete copyright information.
7  */
8
9 #include <h/mh.h>
10 #include <h/utils.h>
11
12 #define CHARSTRING_DEFAULT_SIZE 64
13
14 struct charstring {
15         char *buffer;  /* the char array, not always null-terminated */
16         size_t max;    /* current size of the char array, in bytes */
17         char *cur;     /* size in bytes = cur - buffer, without trailing null */
18 };
19
20
21 static void
22 charstring_reserve(charstring_t s, size_t need)
23 {
24         const size_t cur = s->cur - s->buffer;
25
26         while (need >= s->max - cur) {
27                 /* Insufficient capacity, so double it. */
28                 s->buffer = mh_xrealloc(s->buffer, s->max *= 2);
29                 s->cur = s->buffer + cur;
30         }
31 }
32
33 /*
34  * max is in characters
35  */
36 charstring_t
37 charstring_create(size_t max)
38 {
39         charstring_t s = mh_xcalloc(1, sizeof(*s));
40
41         s->max = max ? max : CHARSTRING_DEFAULT_SIZE;
42         s->cur = s->buffer = mh_xcalloc(s->max, 1);
43
44         return s;
45 }
46
47 charstring_t
48 charstring_copy(const charstring_t src)
49 {
50         const size_t num = src->cur - src->buffer;
51         charstring_t s = mh_xcalloc(1, sizeof(*s));
52
53         s->max = src->max;
54         s->buffer = mh_xcalloc(s->max, 1);
55         memcpy(s->buffer, src->buffer, num);
56         s->cur = s->buffer + num;
57
58         return s;
59 }
60
61 /*
62  * OK to call charstring_free with a NULL argument.
63  */
64 void
65 charstring_free(charstring_t s)
66 {
67         if (s) {
68                 free(s->buffer);
69                 free(s);
70         }
71 }
72
73 void
74 charstring_push_back(charstring_t s, const char c)
75 {
76         charstring_reserve(s, s->cur - s->buffer + 1);
77         *s->cur++ = c;
78 }
79
80 /*
81  * num is the number of bytes in c
82  */
83 void
84 charstring_push_back_chars(charstring_t s, const char c[], size_t num)
85 {
86         charstring_reserve(s, s->cur - s->buffer + num);
87         memcpy(s->cur, c, num);
88         s->cur += num;
89 }
90
91 void
92 charstring_append(charstring_t dest, const charstring_t src)
93 {
94         const size_t num = src->cur - src->buffer;
95
96         if (num > 0) {
97                 charstring_reserve(dest, dest->cur - dest->buffer + num);
98                 memcpy(dest->cur, src->buffer, num);
99                 dest->cur += num;
100         }
101 }
102
103 void
104 charstring_append_cstring(charstring_t dest, const char src[])
105 {
106         const size_t num = strlen(src);
107
108         if (num > 0) {
109                 charstring_reserve(dest, dest->cur - dest->buffer + num);
110                 memcpy(dest->cur, src, num);  /* Exclude src's trailing NUL. */
111                 dest->cur += num;
112         }
113 }
114
115 void
116 charstring_clear(charstring_t s)
117 {
118         s->cur = s->buffer;
119 }
120
121 /*
122  * Don't store return value of charstring_buffer() and use later after
123  * intervening push_back's; use charstring_buffer_copy() instead.
124  */
125 const char *
126 charstring_buffer(const charstring_t s)
127 {
128         charstring_reserve(s, s->cur - s->buffer + 1);
129
130         /* This is the only place that we null-terminate the buffer. */
131         *s->cur = '\0';
132         /* Don't increment cur so that more can be appended later, and so
133            that charstring_bytes() behaves as strlen() by not counting the
134            null. */
135
136         return s->buffer;
137 }
138
139 char *
140 charstring_buffer_copy(const charstring_t s)
141 {
142         char *copy = mh_xcalloc(s->cur - s->buffer + 1, 1);
143
144         /* Use charstring_buffer() to null terminate the buffer. */
145         memcpy(copy, charstring_buffer(s), s->cur - s->buffer + 1);
146
147         return copy;
148 }
149
150 size_t
151 charstring_bytes(const charstring_t s)
152 {
153         return s->cur - s->buffer;
154 }