blob: f8b7e4dabcfdc6f57472b01123ea0fd925b2e439 [file] [log] [blame]
Damien George04b91472014-05-03 23:27:38 +01001/*
2 * This file is part of the Micro Python project, http://micropython.org/
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (c) 2013, 2014 Damien P. George
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 * THE SOFTWARE.
25 */
26
xbeefe34222014-03-16 00:14:26 -070027#include <stdbool.h>
Damien68f59a92013-10-20 14:39:58 +010028#include <stdio.h>
29#include <stdarg.h>
30#include <string.h>
Damien George354d15a2014-02-06 21:11:19 +000031#include <assert.h>
Paul Sokolovsky520e2f52014-02-12 18:31:30 +020032#include "mpconfig.h"
Paul Sokolovsky59c675a2014-06-21 22:43:22 +030033#include "misc.h"
Damien68f59a92013-10-20 14:39:58 +010034
35// returned value is always at least 1 greater than argument
36#define ROUND_ALLOC(a) (((a) & ((~0) - 7)) + 8)
37
Paul Sokolovsky5d2499c2014-01-13 23:15:23 +020038void vstr_init(vstr_t *vstr, int alloc) {
Damien George8cd72bd2014-03-31 17:10:59 +010039 if (alloc < 2) {
40 // need at least 1 byte for the null byte at the end
41 alloc = 2;
42 }
Paul Sokolovsky5d2499c2014-01-13 23:15:23 +020043 vstr->alloc = alloc;
Damien68f59a92013-10-20 14:39:58 +010044 vstr->len = 0;
45 vstr->buf = m_new(char, vstr->alloc);
46 if (vstr->buf == NULL) {
Damien68f59a92013-10-20 14:39:58 +010047 vstr->had_error = true;
48 return;
49 }
50 vstr->buf[0] = 0;
51 vstr->had_error = false;
Damien George354d15a2014-02-06 21:11:19 +000052 vstr->fixed_buf = false;
53}
54
55void vstr_init_fixed_buf(vstr_t *vstr, int alloc, char *buf) {
56 assert(alloc > 0); // need at least room for the null byte
57 vstr->alloc = alloc;
58 vstr->len = 0;
59 vstr->buf = buf;
60 vstr->buf[0] = 0;
61 vstr->had_error = false;
62 vstr->fixed_buf = true;
Damien68f59a92013-10-20 14:39:58 +010063}
64
65void vstr_clear(vstr_t *vstr) {
Damien George354d15a2014-02-06 21:11:19 +000066 if (!vstr->fixed_buf) {
67 m_del(char, vstr->buf, vstr->alloc);
68 }
Damien68f59a92013-10-20 14:39:58 +010069 vstr->buf = NULL;
70}
71
Damien8b3a7c22013-10-23 20:20:17 +010072vstr_t *vstr_new(void) {
Damien68f59a92013-10-20 14:39:58 +010073 vstr_t *vstr = m_new(vstr_t, 1);
74 if (vstr == NULL) {
75 return NULL;
76 }
Paul Sokolovsky5d2499c2014-01-13 23:15:23 +020077 vstr_init(vstr, 32);
78 return vstr;
79}
80
81vstr_t *vstr_new_size(int alloc) {
82 vstr_t *vstr = m_new(vstr_t, 1);
83 if (vstr == NULL) {
84 return NULL;
85 }
86 vstr_init(vstr, alloc);
Damien68f59a92013-10-20 14:39:58 +010087 return vstr;
88}
89
90void vstr_free(vstr_t *vstr) {
91 if (vstr != NULL) {
Damien George354d15a2014-02-06 21:11:19 +000092 if (!vstr->fixed_buf) {
93 m_del(char, vstr->buf, vstr->alloc);
94 }
Damien732407f2013-12-29 19:33:23 +000095 m_del_obj(vstr_t, vstr);
Damien68f59a92013-10-20 14:39:58 +010096 }
97}
98
99void vstr_reset(vstr_t *vstr) {
100 vstr->len = 0;
101 vstr->buf[0] = 0;
102 vstr->had_error = false;
103}
104
105bool vstr_had_error(vstr_t *vstr) {
106 return vstr->had_error;
107}
108
109char *vstr_str(vstr_t *vstr) {
110 if (vstr->had_error) {
111 return NULL;
112 }
113 return vstr->buf;
114}
115
116int vstr_len(vstr_t *vstr) {
117 if (vstr->had_error) {
118 return 0;
119 }
120 return vstr->len;
121}
122
Paul Sokolovsky5d2499c2014-01-13 23:15:23 +0200123// Extend vstr strictly to by requested size, return pointer to newly added chunk
Damien George354d15a2014-02-06 21:11:19 +0000124char *vstr_extend(vstr_t *vstr, int size) {
125 if (vstr->fixed_buf) {
126 return NULL;
127 }
Paul Sokolovsky5d2499c2014-01-13 23:15:23 +0200128 char *new_buf = m_renew(char, vstr->buf, vstr->alloc, vstr->alloc + size);
129 if (new_buf == NULL) {
130 vstr->had_error = true;
131 return NULL;
132 }
133 char *p = new_buf + vstr->alloc;
134 vstr->alloc += size;
135 vstr->buf = new_buf;
136 return p;
137}
138
139// Shrink vstr to be given size
140bool vstr_set_size(vstr_t *vstr, int size) {
Damien George354d15a2014-02-06 21:11:19 +0000141 if (vstr->fixed_buf) {
142 return false;
143 }
Paul Sokolovsky5d2499c2014-01-13 23:15:23 +0200144 char *new_buf = m_renew(char, vstr->buf, vstr->alloc, size);
145 if (new_buf == NULL) {
146 vstr->had_error = true;
147 return false;
148 }
149 vstr->buf = new_buf;
150 vstr->alloc = vstr->len = size;
151 return true;
152}
153
154// Shrink vstr allocation to its actual length
155bool vstr_shrink(vstr_t *vstr) {
156 return vstr_set_size(vstr, vstr->len);
157}
158
Paul Sokolovsky520e2f52014-02-12 18:31:30 +0200159STATIC bool vstr_ensure_extra(vstr_t *vstr, int size) {
Damien68f59a92013-10-20 14:39:58 +0100160 if (vstr->len + size + 1 > vstr->alloc) {
Damien George354d15a2014-02-06 21:11:19 +0000161 if (vstr->fixed_buf) {
162 return false;
163 }
Damien68f59a92013-10-20 14:39:58 +0100164 int new_alloc = ROUND_ALLOC((vstr->len + size + 1) * 2);
Damien732407f2013-12-29 19:33:23 +0000165 char *new_buf = m_renew(char, vstr->buf, vstr->alloc, new_alloc);
Damien68f59a92013-10-20 14:39:58 +0100166 if (new_buf == NULL) {
167 vstr->had_error = true;
168 return false;
169 }
170 vstr->alloc = new_alloc;
171 vstr->buf = new_buf;
172 }
173 return true;
174}
175
176void vstr_hint_size(vstr_t *vstr, int size) {
177 // it's not an error if we fail to allocate for the size hint
178 bool er = vstr->had_error;
179 vstr_ensure_extra(vstr, size);
180 vstr->had_error = er;
181}
182
183char *vstr_add_len(vstr_t *vstr, int len) {
184 if (vstr->had_error || !vstr_ensure_extra(vstr, len)) {
185 return NULL;
186 }
187 char *buf = vstr->buf + vstr->len;
188 vstr->len += len;
189 vstr->buf[vstr->len] = 0;
190 return buf;
191}
192
Damien George280e7202014-03-15 14:33:09 +0000193void vstr_add_byte(vstr_t *vstr, byte b) {
Damien68f59a92013-10-20 14:39:58 +0100194 byte *buf = (byte*)vstr_add_len(vstr, 1);
195 if (buf == NULL) {
196 return;
197 }
Damien George280e7202014-03-15 14:33:09 +0000198 buf[0] = b;
Damien68f59a92013-10-20 14:39:58 +0100199}
200
201void vstr_add_char(vstr_t *vstr, unichar c) {
202 // TODO UNICODE
203 byte *buf = (byte*)vstr_add_len(vstr, 1);
204 if (buf == NULL) {
205 return;
206 }
207 buf[0] = c;
208}
209
210void vstr_add_str(vstr_t *vstr, const char *str) {
211 vstr_add_strn(vstr, str, strlen(str));
212}
213
214void vstr_add_strn(vstr_t *vstr, const char *str, int len) {
215 if (vstr->had_error || !vstr_ensure_extra(vstr, len)) {
Damien George354d15a2014-02-06 21:11:19 +0000216 // if buf is fixed, we got here because there isn't enough room left
217 // so just try to copy as much as we can, with room for null byte
218 if (vstr->fixed_buf && vstr->len + 1 < vstr->alloc) {
219 len = vstr->alloc - vstr->len - 1;
220 goto copy;
221 }
Damien68f59a92013-10-20 14:39:58 +0100222 return;
223 }
Damien George354d15a2014-02-06 21:11:19 +0000224copy:
Damien68f59a92013-10-20 14:39:58 +0100225 memmove(vstr->buf + vstr->len, str, len);
226 vstr->len += len;
227 vstr->buf[vstr->len] = 0;
228}
229
230/*
231void vstr_add_le16(vstr_t *vstr, unsigned short v) {
232 byte *buf = (byte*)vstr_add_len(vstr, 2);
233 if (buf == NULL) {
234 return;
235 }
236 encode_le16(buf, v);
237}
238
239void vstr_add_le32(vstr_t *vstr, unsigned int v) {
240 byte *buf = (byte*)vstr_add_len(vstr, 4);
241 if (buf == NULL) {
242 return;
243 }
244 encode_le32(buf, v);
245}
246*/
247
Damien George280e7202014-03-15 14:33:09 +0000248char *vstr_ins_blank_bytes(vstr_t *vstr, uint byte_pos, uint byte_len) {
249 if (vstr->had_error) {
250 return NULL;
251 }
252 uint l = vstr->len;
253 if (byte_pos > l) {
254 byte_pos = l;
255 }
256 if (byte_len > 0) {
257 // ensure room for the new bytes
258 if (!vstr_ensure_extra(vstr, byte_len)) {
259 return NULL;
260 }
Damien Georgeecd58ae2014-03-15 16:54:06 +0000261 // copy up the string to make room for the new bytes; +1 for the null byte
262 memmove(vstr->buf + byte_pos + byte_len, vstr->buf + byte_pos, l - byte_pos + 1);
Damien George280e7202014-03-15 14:33:09 +0000263 // increase the length
264 vstr->len += byte_len;
Damien George280e7202014-03-15 14:33:09 +0000265 }
266 return vstr->buf + byte_pos;
267}
268
269void vstr_ins_byte(vstr_t *vstr, uint byte_pos, byte b) {
270 char *s = vstr_ins_blank_bytes(vstr, byte_pos, 1);
271 if (s != NULL) {
272 *s = b;
273 }
274}
275
Damien Georgeecd58ae2014-03-15 16:54:06 +0000276void vstr_ins_char(vstr_t *vstr, uint char_pos, unichar chr) {
Damien George280e7202014-03-15 14:33:09 +0000277 // TODO UNICODE
Damien Georgeecd58ae2014-03-15 16:54:06 +0000278 char *s = vstr_ins_blank_bytes(vstr, char_pos, 1);
Damien George280e7202014-03-15 14:33:09 +0000279 if (s != NULL) {
280 *s = chr;
281 }
282}
283
284void vstr_cut_head_bytes(vstr_t *vstr, uint bytes_to_cut) {
285 vstr_cut_out_bytes(vstr, 0, bytes_to_cut);
286}
287
288void vstr_cut_tail_bytes(vstr_t *vstr, uint len) {
Damien68f59a92013-10-20 14:39:58 +0100289 if (vstr->had_error) {
290 return;
291 }
292 if (len > vstr->len) {
293 vstr->len = 0;
294 } else {
295 vstr->len -= len;
296 }
Damien Georgef64086f2014-01-22 23:18:50 +0000297 vstr->buf[vstr->len] = 0;
Damien68f59a92013-10-20 14:39:58 +0100298}
299
Damien George280e7202014-03-15 14:33:09 +0000300void vstr_cut_out_bytes(vstr_t *vstr, uint byte_pos, uint bytes_to_cut) {
301 if (vstr->had_error || byte_pos >= vstr->len) {
302 return;
303 } else if (byte_pos + bytes_to_cut >= vstr->len) {
304 vstr->len = byte_pos;
305 vstr->buf[vstr->len] = 0;
306 } else {
307 // move includes +1 for null byte at the end
308 memmove(vstr->buf + byte_pos, vstr->buf + byte_pos + bytes_to_cut, vstr->len - byte_pos - bytes_to_cut + 1);
309 vstr->len -= bytes_to_cut;
310 }
311}
312
Damien68f59a92013-10-20 14:39:58 +0100313void vstr_printf(vstr_t *vstr, const char *fmt, ...) {
Damien2f06c572013-11-03 18:20:56 +0000314 va_list ap;
315 va_start(ap, fmt);
316 vstr_vprintf(vstr, fmt, ap);
317 va_end(ap);
318}
319
320void vstr_vprintf(vstr_t *vstr, const char *fmt, va_list ap) {
Damien68f59a92013-10-20 14:39:58 +0100321 if (vstr->had_error || !vstr_ensure_extra(vstr, strlen(fmt))) {
322 return;
323 }
324
325 while (1) {
326 // try to print in the allocated space
Damien George66028ab2014-01-03 14:03:48 +0000327 // need to make a copy of the va_list because we may call vsnprintf multiple times
Damien68f59a92013-10-20 14:39:58 +0100328 int size = vstr->alloc - vstr->len;
Damien George66028ab2014-01-03 14:03:48 +0000329 va_list ap2;
330 va_copy(ap2, ap);
331 int n = vsnprintf(vstr->buf + vstr->len, size, fmt, ap2);
332 va_end(ap2);
Damien68f59a92013-10-20 14:39:58 +0100333
334 // if that worked, return
335 if (n > -1 && n < size) {
336 vstr->len += n;
337 return;
338 }
339
340 // else try again with more space
341 if (n > -1) { // glibc 2.1
342 // n + 1 is precisely what is needed
343 if (!vstr_ensure_extra(vstr, n + 1)) {
344 return;
345 }
346 } else { // glibc 2.0
347 // increase to twice the old size
348 if (!vstr_ensure_extra(vstr, size * 2)) {
349 return;
350 }
351 }
352 }
353}
Damien68f59a92013-10-20 14:39:58 +0100354
355/** testing *****************************************************/
356
357/*
Damien8b3a7c22013-10-23 20:20:17 +0100358int main(void) {
Damien68f59a92013-10-20 14:39:58 +0100359 vstr_t *vstr = vstr_new();
360 int i;
361 for (i = 0; i < 10; i++) {
362 vstr_printf(vstr, "%d) this is a test %d %s\n", i, 1234, "'a string'");
363 vstr_add_str(vstr, "-----");
364 vstr_printf(vstr, "this is another test %d %s\n", 1234, "'a second string'");
365 printf("%s", vstr->buf);
366 }
367}
368*/