blob: c518fa7048a249f6be6465d1140cedb8f67a283c [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"
Paul Sokolovsky520e2f52014-02-12 18:31:30 +02006#include "mpconfig.h"
Damien68f59a92013-10-20 14:39:58 +01007
8// returned value is always at least 1 greater than argument
9#define ROUND_ALLOC(a) (((a) & ((~0) - 7)) + 8)
10
Paul Sokolovsky5d2499c2014-01-13 23:15:23 +020011void vstr_init(vstr_t *vstr, int alloc) {
12 vstr->alloc = alloc;
Damien68f59a92013-10-20 14:39:58 +010013 vstr->len = 0;
14 vstr->buf = m_new(char, vstr->alloc);
15 if (vstr->buf == NULL) {
Damien68f59a92013-10-20 14:39:58 +010016 vstr->had_error = true;
17 return;
18 }
19 vstr->buf[0] = 0;
20 vstr->had_error = false;
Damien George354d15a2014-02-06 21:11:19 +000021 vstr->fixed_buf = false;
22}
23
24void vstr_init_fixed_buf(vstr_t *vstr, int alloc, char *buf) {
25 assert(alloc > 0); // need at least room for the null byte
26 vstr->alloc = alloc;
27 vstr->len = 0;
28 vstr->buf = buf;
29 vstr->buf[0] = 0;
30 vstr->had_error = false;
31 vstr->fixed_buf = true;
Damien68f59a92013-10-20 14:39:58 +010032}
33
34void vstr_clear(vstr_t *vstr) {
Damien George354d15a2014-02-06 21:11:19 +000035 if (!vstr->fixed_buf) {
36 m_del(char, vstr->buf, vstr->alloc);
37 }
Damien68f59a92013-10-20 14:39:58 +010038 vstr->buf = NULL;
39}
40
Damien8b3a7c22013-10-23 20:20:17 +010041vstr_t *vstr_new(void) {
Damien68f59a92013-10-20 14:39:58 +010042 vstr_t *vstr = m_new(vstr_t, 1);
43 if (vstr == NULL) {
44 return NULL;
45 }
Paul Sokolovsky5d2499c2014-01-13 23:15:23 +020046 vstr_init(vstr, 32);
47 return vstr;
48}
49
50vstr_t *vstr_new_size(int alloc) {
51 vstr_t *vstr = m_new(vstr_t, 1);
52 if (vstr == NULL) {
53 return NULL;
54 }
55 vstr_init(vstr, alloc);
Damien68f59a92013-10-20 14:39:58 +010056 return vstr;
57}
58
59void vstr_free(vstr_t *vstr) {
60 if (vstr != NULL) {
Damien George354d15a2014-02-06 21:11:19 +000061 if (!vstr->fixed_buf) {
62 m_del(char, vstr->buf, vstr->alloc);
63 }
Damien732407f2013-12-29 19:33:23 +000064 m_del_obj(vstr_t, vstr);
Damien68f59a92013-10-20 14:39:58 +010065 }
66}
67
68void vstr_reset(vstr_t *vstr) {
69 vstr->len = 0;
70 vstr->buf[0] = 0;
71 vstr->had_error = false;
72}
73
74bool vstr_had_error(vstr_t *vstr) {
75 return vstr->had_error;
76}
77
78char *vstr_str(vstr_t *vstr) {
79 if (vstr->had_error) {
80 return NULL;
81 }
82 return vstr->buf;
83}
84
85int vstr_len(vstr_t *vstr) {
86 if (vstr->had_error) {
87 return 0;
88 }
89 return vstr->len;
90}
91
Paul Sokolovsky5d2499c2014-01-13 23:15:23 +020092// Extend vstr strictly to by requested size, return pointer to newly added chunk
Damien George354d15a2014-02-06 21:11:19 +000093char *vstr_extend(vstr_t *vstr, int size) {
94 if (vstr->fixed_buf) {
95 return NULL;
96 }
Paul Sokolovsky5d2499c2014-01-13 23:15:23 +020097 char *new_buf = m_renew(char, vstr->buf, vstr->alloc, vstr->alloc + size);
98 if (new_buf == NULL) {
99 vstr->had_error = true;
100 return NULL;
101 }
102 char *p = new_buf + vstr->alloc;
103 vstr->alloc += size;
104 vstr->buf = new_buf;
105 return p;
106}
107
108// Shrink vstr to be given size
109bool vstr_set_size(vstr_t *vstr, int size) {
Damien George354d15a2014-02-06 21:11:19 +0000110 if (vstr->fixed_buf) {
111 return false;
112 }
Paul Sokolovsky5d2499c2014-01-13 23:15:23 +0200113 char *new_buf = m_renew(char, vstr->buf, vstr->alloc, size);
114 if (new_buf == NULL) {
115 vstr->had_error = true;
116 return false;
117 }
118 vstr->buf = new_buf;
119 vstr->alloc = vstr->len = size;
120 return true;
121}
122
123// Shrink vstr allocation to its actual length
124bool vstr_shrink(vstr_t *vstr) {
125 return vstr_set_size(vstr, vstr->len);
126}
127
Paul Sokolovsky520e2f52014-02-12 18:31:30 +0200128STATIC bool vstr_ensure_extra(vstr_t *vstr, int size) {
Damien68f59a92013-10-20 14:39:58 +0100129 if (vstr->len + size + 1 > vstr->alloc) {
Damien George354d15a2014-02-06 21:11:19 +0000130 if (vstr->fixed_buf) {
131 return false;
132 }
Damien68f59a92013-10-20 14:39:58 +0100133 int new_alloc = ROUND_ALLOC((vstr->len + size + 1) * 2);
Damien732407f2013-12-29 19:33:23 +0000134 char *new_buf = m_renew(char, vstr->buf, vstr->alloc, new_alloc);
Damien68f59a92013-10-20 14:39:58 +0100135 if (new_buf == NULL) {
136 vstr->had_error = true;
137 return false;
138 }
139 vstr->alloc = new_alloc;
140 vstr->buf = new_buf;
141 }
142 return true;
143}
144
145void vstr_hint_size(vstr_t *vstr, int size) {
146 // it's not an error if we fail to allocate for the size hint
147 bool er = vstr->had_error;
148 vstr_ensure_extra(vstr, size);
149 vstr->had_error = er;
150}
151
152char *vstr_add_len(vstr_t *vstr, int len) {
153 if (vstr->had_error || !vstr_ensure_extra(vstr, len)) {
154 return NULL;
155 }
156 char *buf = vstr->buf + vstr->len;
157 vstr->len += len;
158 vstr->buf[vstr->len] = 0;
159 return buf;
160}
161
Damien George280e7202014-03-15 14:33:09 +0000162void vstr_add_byte(vstr_t *vstr, byte b) {
Damien68f59a92013-10-20 14:39:58 +0100163 byte *buf = (byte*)vstr_add_len(vstr, 1);
164 if (buf == NULL) {
165 return;
166 }
Damien George280e7202014-03-15 14:33:09 +0000167 buf[0] = b;
Damien68f59a92013-10-20 14:39:58 +0100168}
169
170void vstr_add_char(vstr_t *vstr, unichar c) {
171 // TODO UNICODE
172 byte *buf = (byte*)vstr_add_len(vstr, 1);
173 if (buf == NULL) {
174 return;
175 }
176 buf[0] = c;
177}
178
179void vstr_add_str(vstr_t *vstr, const char *str) {
180 vstr_add_strn(vstr, str, strlen(str));
181}
182
183void vstr_add_strn(vstr_t *vstr, const char *str, int len) {
184 if (vstr->had_error || !vstr_ensure_extra(vstr, len)) {
Damien George354d15a2014-02-06 21:11:19 +0000185 // if buf is fixed, we got here because there isn't enough room left
186 // so just try to copy as much as we can, with room for null byte
187 if (vstr->fixed_buf && vstr->len + 1 < vstr->alloc) {
188 len = vstr->alloc - vstr->len - 1;
189 goto copy;
190 }
Damien68f59a92013-10-20 14:39:58 +0100191 return;
192 }
Damien George354d15a2014-02-06 21:11:19 +0000193copy:
Damien68f59a92013-10-20 14:39:58 +0100194 memmove(vstr->buf + vstr->len, str, len);
195 vstr->len += len;
196 vstr->buf[vstr->len] = 0;
197}
198
199/*
200void vstr_add_le16(vstr_t *vstr, unsigned short v) {
201 byte *buf = (byte*)vstr_add_len(vstr, 2);
202 if (buf == NULL) {
203 return;
204 }
205 encode_le16(buf, v);
206}
207
208void vstr_add_le32(vstr_t *vstr, unsigned int v) {
209 byte *buf = (byte*)vstr_add_len(vstr, 4);
210 if (buf == NULL) {
211 return;
212 }
213 encode_le32(buf, v);
214}
215*/
216
Damien George280e7202014-03-15 14:33:09 +0000217char *vstr_ins_blank_bytes(vstr_t *vstr, uint byte_pos, uint byte_len) {
218 if (vstr->had_error) {
219 return NULL;
220 }
221 uint l = vstr->len;
222 if (byte_pos > l) {
223 byte_pos = l;
224 }
225 if (byte_len > 0) {
226 // ensure room for the new bytes
227 if (!vstr_ensure_extra(vstr, byte_len)) {
228 return NULL;
229 }
Damien Georgeecd58ae2014-03-15 16:54:06 +0000230 // copy up the string to make room for the new bytes; +1 for the null byte
231 memmove(vstr->buf + byte_pos + byte_len, vstr->buf + byte_pos, l - byte_pos + 1);
Damien George280e7202014-03-15 14:33:09 +0000232 // increase the length
233 vstr->len += byte_len;
Damien George280e7202014-03-15 14:33:09 +0000234 }
235 return vstr->buf + byte_pos;
236}
237
238void vstr_ins_byte(vstr_t *vstr, uint byte_pos, byte b) {
239 char *s = vstr_ins_blank_bytes(vstr, byte_pos, 1);
240 if (s != NULL) {
241 *s = b;
242 }
243}
244
Damien Georgeecd58ae2014-03-15 16:54:06 +0000245void vstr_ins_char(vstr_t *vstr, uint char_pos, unichar chr) {
Damien George280e7202014-03-15 14:33:09 +0000246 // TODO UNICODE
Damien Georgeecd58ae2014-03-15 16:54:06 +0000247 char *s = vstr_ins_blank_bytes(vstr, char_pos, 1);
Damien George280e7202014-03-15 14:33:09 +0000248 if (s != NULL) {
249 *s = chr;
250 }
251}
252
253void vstr_cut_head_bytes(vstr_t *vstr, uint bytes_to_cut) {
254 vstr_cut_out_bytes(vstr, 0, bytes_to_cut);
255}
256
257void vstr_cut_tail_bytes(vstr_t *vstr, uint len) {
Damien68f59a92013-10-20 14:39:58 +0100258 if (vstr->had_error) {
259 return;
260 }
261 if (len > vstr->len) {
262 vstr->len = 0;
263 } else {
264 vstr->len -= len;
265 }
Damien Georgef64086f2014-01-22 23:18:50 +0000266 vstr->buf[vstr->len] = 0;
Damien68f59a92013-10-20 14:39:58 +0100267}
268
Damien George280e7202014-03-15 14:33:09 +0000269void vstr_cut_out_bytes(vstr_t *vstr, uint byte_pos, uint bytes_to_cut) {
270 if (vstr->had_error || byte_pos >= vstr->len) {
271 return;
272 } else if (byte_pos + bytes_to_cut >= vstr->len) {
273 vstr->len = byte_pos;
274 vstr->buf[vstr->len] = 0;
275 } else {
276 // move includes +1 for null byte at the end
277 memmove(vstr->buf + byte_pos, vstr->buf + byte_pos + bytes_to_cut, vstr->len - byte_pos - bytes_to_cut + 1);
278 vstr->len -= bytes_to_cut;
279 }
280}
281
Damien68f59a92013-10-20 14:39:58 +0100282void vstr_printf(vstr_t *vstr, const char *fmt, ...) {
Damien2f06c572013-11-03 18:20:56 +0000283 va_list ap;
284 va_start(ap, fmt);
285 vstr_vprintf(vstr, fmt, ap);
286 va_end(ap);
287}
288
289void vstr_vprintf(vstr_t *vstr, const char *fmt, va_list ap) {
Damien68f59a92013-10-20 14:39:58 +0100290 if (vstr->had_error || !vstr_ensure_extra(vstr, strlen(fmt))) {
291 return;
292 }
293
294 while (1) {
295 // try to print in the allocated space
Damien George66028ab2014-01-03 14:03:48 +0000296 // need to make a copy of the va_list because we may call vsnprintf multiple times
Damien68f59a92013-10-20 14:39:58 +0100297 int size = vstr->alloc - vstr->len;
Damien George66028ab2014-01-03 14:03:48 +0000298 va_list ap2;
299 va_copy(ap2, ap);
300 int n = vsnprintf(vstr->buf + vstr->len, size, fmt, ap2);
301 va_end(ap2);
Damien68f59a92013-10-20 14:39:58 +0100302
303 // if that worked, return
304 if (n > -1 && n < size) {
305 vstr->len += n;
306 return;
307 }
308
309 // else try again with more space
310 if (n > -1) { // glibc 2.1
311 // n + 1 is precisely what is needed
312 if (!vstr_ensure_extra(vstr, n + 1)) {
313 return;
314 }
315 } else { // glibc 2.0
316 // increase to twice the old size
317 if (!vstr_ensure_extra(vstr, size * 2)) {
318 return;
319 }
320 }
321 }
322}
Damien68f59a92013-10-20 14:39:58 +0100323
324/** testing *****************************************************/
325
326/*
Damien8b3a7c22013-10-23 20:20:17 +0100327int main(void) {
Damien68f59a92013-10-20 14:39:58 +0100328 vstr_t *vstr = vstr_new();
329 int i;
330 for (i = 0; i < 10; i++) {
331 vstr_printf(vstr, "%d) this is a test %d %s\n", i, 1234, "'a string'");
332 vstr_add_str(vstr, "-----");
333 vstr_printf(vstr, "this is another test %d %s\n", 1234, "'a second string'");
334 printf("%s", vstr->buf);
335 }
336}
337*/