blob: 4d10dfba82baa83ac22029bc6ddd25605887e53a [file] [log] [blame]
Damien68f59a92013-10-20 14:39:58 +01001#include <stdio.h>
2#include <stdarg.h>
3#include <string.h>
Damien George354d15a2014-02-06 21:11:19 +00004#include <assert.h>
Damien68f59a92013-10-20 14:39:58 +01005#include "misc.h"
6
7// returned value is always at least 1 greater than argument
8#define ROUND_ALLOC(a) (((a) & ((~0) - 7)) + 8)
9
Paul Sokolovsky5d2499c2014-01-13 23:15:23 +020010void vstr_init(vstr_t *vstr, int alloc) {
11 vstr->alloc = alloc;
Damien68f59a92013-10-20 14:39:58 +010012 vstr->len = 0;
13 vstr->buf = m_new(char, vstr->alloc);
14 if (vstr->buf == NULL) {
Damien68f59a92013-10-20 14:39:58 +010015 vstr->had_error = true;
16 return;
17 }
18 vstr->buf[0] = 0;
19 vstr->had_error = false;
Damien George354d15a2014-02-06 21:11:19 +000020 vstr->fixed_buf = false;
21}
22
23void vstr_init_fixed_buf(vstr_t *vstr, int alloc, char *buf) {
24 assert(alloc > 0); // need at least room for the null byte
25 vstr->alloc = alloc;
26 vstr->len = 0;
27 vstr->buf = buf;
28 vstr->buf[0] = 0;
29 vstr->had_error = false;
30 vstr->fixed_buf = true;
Damien68f59a92013-10-20 14:39:58 +010031}
32
33void vstr_clear(vstr_t *vstr) {
Damien George354d15a2014-02-06 21:11:19 +000034 if (!vstr->fixed_buf) {
35 m_del(char, vstr->buf, vstr->alloc);
36 }
Damien68f59a92013-10-20 14:39:58 +010037 vstr->buf = NULL;
38}
39
Damien8b3a7c22013-10-23 20:20:17 +010040vstr_t *vstr_new(void) {
Damien68f59a92013-10-20 14:39:58 +010041 vstr_t *vstr = m_new(vstr_t, 1);
42 if (vstr == NULL) {
43 return NULL;
44 }
Paul Sokolovsky5d2499c2014-01-13 23:15:23 +020045 vstr_init(vstr, 32);
46 return vstr;
47}
48
49vstr_t *vstr_new_size(int alloc) {
50 vstr_t *vstr = m_new(vstr_t, 1);
51 if (vstr == NULL) {
52 return NULL;
53 }
54 vstr_init(vstr, alloc);
Damien68f59a92013-10-20 14:39:58 +010055 return vstr;
56}
57
58void vstr_free(vstr_t *vstr) {
59 if (vstr != NULL) {
Damien George354d15a2014-02-06 21:11:19 +000060 if (!vstr->fixed_buf) {
61 m_del(char, vstr->buf, vstr->alloc);
62 }
Damien732407f2013-12-29 19:33:23 +000063 m_del_obj(vstr_t, vstr);
Damien68f59a92013-10-20 14:39:58 +010064 }
65}
66
67void vstr_reset(vstr_t *vstr) {
68 vstr->len = 0;
69 vstr->buf[0] = 0;
70 vstr->had_error = false;
71}
72
73bool vstr_had_error(vstr_t *vstr) {
74 return vstr->had_error;
75}
76
77char *vstr_str(vstr_t *vstr) {
78 if (vstr->had_error) {
79 return NULL;
80 }
81 return vstr->buf;
82}
83
84int vstr_len(vstr_t *vstr) {
85 if (vstr->had_error) {
86 return 0;
87 }
88 return vstr->len;
89}
90
Paul Sokolovsky5d2499c2014-01-13 23:15:23 +020091// Extend vstr strictly to by requested size, return pointer to newly added chunk
Damien George354d15a2014-02-06 21:11:19 +000092char *vstr_extend(vstr_t *vstr, int size) {
93 if (vstr->fixed_buf) {
94 return NULL;
95 }
Paul Sokolovsky5d2499c2014-01-13 23:15:23 +020096 char *new_buf = m_renew(char, vstr->buf, vstr->alloc, vstr->alloc + size);
97 if (new_buf == NULL) {
98 vstr->had_error = true;
99 return NULL;
100 }
101 char *p = new_buf + vstr->alloc;
102 vstr->alloc += size;
103 vstr->buf = new_buf;
104 return p;
105}
106
107// Shrink vstr to be given size
108bool vstr_set_size(vstr_t *vstr, int size) {
Damien George354d15a2014-02-06 21:11:19 +0000109 if (vstr->fixed_buf) {
110 return false;
111 }
Paul Sokolovsky5d2499c2014-01-13 23:15:23 +0200112 char *new_buf = m_renew(char, vstr->buf, vstr->alloc, size);
113 if (new_buf == NULL) {
114 vstr->had_error = true;
115 return false;
116 }
117 vstr->buf = new_buf;
118 vstr->alloc = vstr->len = size;
119 return true;
120}
121
122// Shrink vstr allocation to its actual length
123bool vstr_shrink(vstr_t *vstr) {
124 return vstr_set_size(vstr, vstr->len);
125}
126
Damien George354d15a2014-02-06 21:11:19 +0000127static bool vstr_ensure_extra(vstr_t *vstr, int size) {
Damien68f59a92013-10-20 14:39:58 +0100128 if (vstr->len + size + 1 > vstr->alloc) {
Damien George354d15a2014-02-06 21:11:19 +0000129 if (vstr->fixed_buf) {
130 return false;
131 }
Damien68f59a92013-10-20 14:39:58 +0100132 int new_alloc = ROUND_ALLOC((vstr->len + size + 1) * 2);
Damien732407f2013-12-29 19:33:23 +0000133 char *new_buf = m_renew(char, vstr->buf, vstr->alloc, new_alloc);
Damien68f59a92013-10-20 14:39:58 +0100134 if (new_buf == NULL) {
135 vstr->had_error = true;
136 return false;
137 }
138 vstr->alloc = new_alloc;
139 vstr->buf = new_buf;
140 }
141 return true;
142}
143
144void vstr_hint_size(vstr_t *vstr, int size) {
145 // it's not an error if we fail to allocate for the size hint
146 bool er = vstr->had_error;
147 vstr_ensure_extra(vstr, size);
148 vstr->had_error = er;
149}
150
151char *vstr_add_len(vstr_t *vstr, int len) {
152 if (vstr->had_error || !vstr_ensure_extra(vstr, len)) {
153 return NULL;
154 }
155 char *buf = vstr->buf + vstr->len;
156 vstr->len += len;
157 vstr->buf[vstr->len] = 0;
158 return buf;
159}
160
161void vstr_add_byte(vstr_t *vstr, byte v) {
162 byte *buf = (byte*)vstr_add_len(vstr, 1);
163 if (buf == NULL) {
164 return;
165 }
166 buf[0] = v;
167}
168
169void vstr_add_char(vstr_t *vstr, unichar c) {
170 // TODO UNICODE
171 byte *buf = (byte*)vstr_add_len(vstr, 1);
172 if (buf == NULL) {
173 return;
174 }
175 buf[0] = c;
176}
177
178void vstr_add_str(vstr_t *vstr, const char *str) {
179 vstr_add_strn(vstr, str, strlen(str));
180}
181
182void vstr_add_strn(vstr_t *vstr, const char *str, int len) {
183 if (vstr->had_error || !vstr_ensure_extra(vstr, len)) {
Damien George354d15a2014-02-06 21:11:19 +0000184 // if buf is fixed, we got here because there isn't enough room left
185 // so just try to copy as much as we can, with room for null byte
186 if (vstr->fixed_buf && vstr->len + 1 < vstr->alloc) {
187 len = vstr->alloc - vstr->len - 1;
188 goto copy;
189 }
Damien68f59a92013-10-20 14:39:58 +0100190 return;
191 }
Damien George354d15a2014-02-06 21:11:19 +0000192copy:
Damien68f59a92013-10-20 14:39:58 +0100193 memmove(vstr->buf + vstr->len, str, len);
194 vstr->len += len;
195 vstr->buf[vstr->len] = 0;
196}
197
198/*
199void vstr_add_le16(vstr_t *vstr, unsigned short v) {
200 byte *buf = (byte*)vstr_add_len(vstr, 2);
201 if (buf == NULL) {
202 return;
203 }
204 encode_le16(buf, v);
205}
206
207void vstr_add_le32(vstr_t *vstr, unsigned int v) {
208 byte *buf = (byte*)vstr_add_len(vstr, 4);
209 if (buf == NULL) {
210 return;
211 }
212 encode_le32(buf, v);
213}
214*/
215
216void vstr_cut_tail(vstr_t *vstr, int len) {
217 if (vstr->had_error) {
218 return;
219 }
220 if (len > vstr->len) {
221 vstr->len = 0;
222 } else {
223 vstr->len -= len;
224 }
Damien Georgef64086f2014-01-22 23:18:50 +0000225 vstr->buf[vstr->len] = 0;
Damien68f59a92013-10-20 14:39:58 +0100226}
227
Damien68f59a92013-10-20 14:39:58 +0100228void vstr_printf(vstr_t *vstr, const char *fmt, ...) {
Damien2f06c572013-11-03 18:20:56 +0000229 va_list ap;
230 va_start(ap, fmt);
231 vstr_vprintf(vstr, fmt, ap);
232 va_end(ap);
233}
234
235void vstr_vprintf(vstr_t *vstr, const char *fmt, va_list ap) {
Damien68f59a92013-10-20 14:39:58 +0100236 if (vstr->had_error || !vstr_ensure_extra(vstr, strlen(fmt))) {
237 return;
238 }
239
240 while (1) {
241 // try to print in the allocated space
Damien George66028ab2014-01-03 14:03:48 +0000242 // need to make a copy of the va_list because we may call vsnprintf multiple times
Damien68f59a92013-10-20 14:39:58 +0100243 int size = vstr->alloc - vstr->len;
Damien George66028ab2014-01-03 14:03:48 +0000244 va_list ap2;
245 va_copy(ap2, ap);
246 int n = vsnprintf(vstr->buf + vstr->len, size, fmt, ap2);
247 va_end(ap2);
Damien68f59a92013-10-20 14:39:58 +0100248
249 // if that worked, return
250 if (n > -1 && n < size) {
251 vstr->len += n;
252 return;
253 }
254
255 // else try again with more space
256 if (n > -1) { // glibc 2.1
257 // n + 1 is precisely what is needed
258 if (!vstr_ensure_extra(vstr, n + 1)) {
259 return;
260 }
261 } else { // glibc 2.0
262 // increase to twice the old size
263 if (!vstr_ensure_extra(vstr, size * 2)) {
264 return;
265 }
266 }
267 }
268}
Damien68f59a92013-10-20 14:39:58 +0100269
270/** testing *****************************************************/
271
272/*
Damien8b3a7c22013-10-23 20:20:17 +0100273int main(void) {
Damien68f59a92013-10-20 14:39:58 +0100274 vstr_t *vstr = vstr_new();
275 int i;
276 for (i = 0; i < 10; i++) {
277 vstr_printf(vstr, "%d) this is a test %d %s\n", i, 1234, "'a string'");
278 vstr_add_str(vstr, "-----");
279 vstr_printf(vstr, "this is another test %d %s\n", 1234, "'a second string'");
280 printf("%s", vstr->buf);
281 }
282}
283*/