blob: 2dbc6f04a365a4fea2595cac332c250fb96475cf [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) {
Chris Angelico2ba22992014-06-04 05:28:12 +1000202 // TODO: Can this be simplified and deduplicated?
203 // Is it worth just calling vstr_add_len(vstr, 4)?
204 if (c < 0x80) {
205 byte *buf = (byte*)vstr_add_len(vstr, 1);
206 if (buf == NULL) {
207 return;
208 }
209 *buf = (byte)c;
210 } else if (c < 0x800) {
211 byte *buf = (byte*)vstr_add_len(vstr, 2);
212 if (buf == NULL) {
213 return;
214 }
215 buf[0] = (c >> 6) | 0xC0;
216 buf[1] = (c & 0x3F) | 0x80;
217 } else if (c < 0x10000) {
218 byte *buf = (byte*)vstr_add_len(vstr, 3);
219 if (buf == NULL) {
220 return;
221 }
222 buf[0] = (c >> 12) | 0xE0;
223 buf[1] = ((c >> 6) & 0x3F) | 0x80;
224 buf[2] = (c & 0x3F) | 0x80;
225 } else {
226 assert(c < 0x110000);
227 byte *buf = (byte*)vstr_add_len(vstr, 4);
228 if (buf == NULL) {
229 return;
230 }
231 buf[0] = (c >> 18) | 0xF0;
232 buf[1] = ((c >> 12) & 0x3F) | 0x80;
233 buf[2] = ((c >> 6) & 0x3F) | 0x80;
234 buf[3] = (c & 0x3F) | 0x80;
Damien68f59a92013-10-20 14:39:58 +0100235 }
Damien68f59a92013-10-20 14:39:58 +0100236}
237
238void vstr_add_str(vstr_t *vstr, const char *str) {
239 vstr_add_strn(vstr, str, strlen(str));
240}
241
242void vstr_add_strn(vstr_t *vstr, const char *str, int len) {
243 if (vstr->had_error || !vstr_ensure_extra(vstr, len)) {
Damien George354d15a2014-02-06 21:11:19 +0000244 // if buf is fixed, we got here because there isn't enough room left
245 // so just try to copy as much as we can, with room for null byte
246 if (vstr->fixed_buf && vstr->len + 1 < vstr->alloc) {
247 len = vstr->alloc - vstr->len - 1;
248 goto copy;
249 }
Damien68f59a92013-10-20 14:39:58 +0100250 return;
251 }
Damien George354d15a2014-02-06 21:11:19 +0000252copy:
Damien68f59a92013-10-20 14:39:58 +0100253 memmove(vstr->buf + vstr->len, str, len);
254 vstr->len += len;
255 vstr->buf[vstr->len] = 0;
256}
257
258/*
259void vstr_add_le16(vstr_t *vstr, unsigned short v) {
260 byte *buf = (byte*)vstr_add_len(vstr, 2);
261 if (buf == NULL) {
262 return;
263 }
264 encode_le16(buf, v);
265}
266
267void vstr_add_le32(vstr_t *vstr, unsigned int v) {
268 byte *buf = (byte*)vstr_add_len(vstr, 4);
269 if (buf == NULL) {
270 return;
271 }
272 encode_le32(buf, v);
273}
274*/
275
Damien George280e7202014-03-15 14:33:09 +0000276char *vstr_ins_blank_bytes(vstr_t *vstr, uint byte_pos, uint byte_len) {
277 if (vstr->had_error) {
278 return NULL;
279 }
280 uint l = vstr->len;
281 if (byte_pos > l) {
282 byte_pos = l;
283 }
284 if (byte_len > 0) {
285 // ensure room for the new bytes
286 if (!vstr_ensure_extra(vstr, byte_len)) {
287 return NULL;
288 }
Damien Georgeecd58ae2014-03-15 16:54:06 +0000289 // copy up the string to make room for the new bytes; +1 for the null byte
290 memmove(vstr->buf + byte_pos + byte_len, vstr->buf + byte_pos, l - byte_pos + 1);
Damien George280e7202014-03-15 14:33:09 +0000291 // increase the length
292 vstr->len += byte_len;
Damien George280e7202014-03-15 14:33:09 +0000293 }
294 return vstr->buf + byte_pos;
295}
296
297void vstr_ins_byte(vstr_t *vstr, uint byte_pos, byte b) {
298 char *s = vstr_ins_blank_bytes(vstr, byte_pos, 1);
299 if (s != NULL) {
300 *s = b;
301 }
302}
303
Damien Georgeecd58ae2014-03-15 16:54:06 +0000304void vstr_ins_char(vstr_t *vstr, uint char_pos, unichar chr) {
Damien George280e7202014-03-15 14:33:09 +0000305 // TODO UNICODE
Damien Georgeecd58ae2014-03-15 16:54:06 +0000306 char *s = vstr_ins_blank_bytes(vstr, char_pos, 1);
Damien George280e7202014-03-15 14:33:09 +0000307 if (s != NULL) {
308 *s = chr;
309 }
310}
311
312void vstr_cut_head_bytes(vstr_t *vstr, uint bytes_to_cut) {
313 vstr_cut_out_bytes(vstr, 0, bytes_to_cut);
314}
315
316void vstr_cut_tail_bytes(vstr_t *vstr, uint len) {
Damien68f59a92013-10-20 14:39:58 +0100317 if (vstr->had_error) {
318 return;
319 }
320 if (len > vstr->len) {
321 vstr->len = 0;
322 } else {
323 vstr->len -= len;
324 }
Damien Georgef64086f2014-01-22 23:18:50 +0000325 vstr->buf[vstr->len] = 0;
Damien68f59a92013-10-20 14:39:58 +0100326}
327
Damien George280e7202014-03-15 14:33:09 +0000328void vstr_cut_out_bytes(vstr_t *vstr, uint byte_pos, uint bytes_to_cut) {
329 if (vstr->had_error || byte_pos >= vstr->len) {
330 return;
331 } else if (byte_pos + bytes_to_cut >= vstr->len) {
332 vstr->len = byte_pos;
333 vstr->buf[vstr->len] = 0;
334 } else {
335 // move includes +1 for null byte at the end
336 memmove(vstr->buf + byte_pos, vstr->buf + byte_pos + bytes_to_cut, vstr->len - byte_pos - bytes_to_cut + 1);
337 vstr->len -= bytes_to_cut;
338 }
339}
340
Damien68f59a92013-10-20 14:39:58 +0100341void vstr_printf(vstr_t *vstr, const char *fmt, ...) {
Damien2f06c572013-11-03 18:20:56 +0000342 va_list ap;
343 va_start(ap, fmt);
344 vstr_vprintf(vstr, fmt, ap);
345 va_end(ap);
346}
347
348void vstr_vprintf(vstr_t *vstr, const char *fmt, va_list ap) {
Damien68f59a92013-10-20 14:39:58 +0100349 if (vstr->had_error || !vstr_ensure_extra(vstr, strlen(fmt))) {
350 return;
351 }
352
353 while (1) {
354 // try to print in the allocated space
Damien George66028ab2014-01-03 14:03:48 +0000355 // need to make a copy of the va_list because we may call vsnprintf multiple times
Damien68f59a92013-10-20 14:39:58 +0100356 int size = vstr->alloc - vstr->len;
Damien George66028ab2014-01-03 14:03:48 +0000357 va_list ap2;
358 va_copy(ap2, ap);
359 int n = vsnprintf(vstr->buf + vstr->len, size, fmt, ap2);
360 va_end(ap2);
Damien68f59a92013-10-20 14:39:58 +0100361
362 // if that worked, return
363 if (n > -1 && n < size) {
364 vstr->len += n;
365 return;
366 }
367
368 // else try again with more space
369 if (n > -1) { // glibc 2.1
370 // n + 1 is precisely what is needed
371 if (!vstr_ensure_extra(vstr, n + 1)) {
372 return;
373 }
374 } else { // glibc 2.0
375 // increase to twice the old size
376 if (!vstr_ensure_extra(vstr, size * 2)) {
377 return;
378 }
379 }
380 }
381}
Damien68f59a92013-10-20 14:39:58 +0100382
383/** testing *****************************************************/
384
385/*
Damien8b3a7c22013-10-23 20:20:17 +0100386int main(void) {
Damien68f59a92013-10-20 14:39:58 +0100387 vstr_t *vstr = vstr_new();
388 int i;
389 for (i = 0; i < 10; i++) {
390 vstr_printf(vstr, "%d) this is a test %d %s\n", i, 1234, "'a string'");
391 vstr_add_str(vstr, "-----");
392 vstr_printf(vstr, "this is another test %d %s\n", 1234, "'a second string'");
393 printf("%s", vstr->buf);
394 }
395}
396*/