blob: e87aef9e4862f196301fdc2dcf3250f6901c1d47 [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
Damien68f59a92013-10-20 14:39:58 +010027#include <stdio.h>
28#include <stdarg.h>
29#include <string.h>
Damien George354d15a2014-02-06 21:11:19 +000030#include <assert.h>
Damien George51dfcb42015-01-01 20:27:54 +000031
32#include "py/mpconfig.h"
33#include "py/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
Damien Georgeb0261342014-09-23 18:10:17 +010038void vstr_init(vstr_t *vstr, size_t 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
Damien Georgeb0261342014-09-23 18:10:17 +010055void vstr_init_fixed_buf(vstr_t *vstr, size_t alloc, char *buf) {
Damien George354d15a2014-02-06 21:11:19 +000056 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
Damien Georgeb0261342014-09-23 18:10:17 +010081vstr_t *vstr_new_size(size_t alloc) {
Paul Sokolovsky5d2499c2014-01-13 23:15:23 +020082 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
Damien Georgeb0261342014-09-23 18:10:17 +0100116size_t vstr_len(vstr_t *vstr) {
Damien68f59a92013-10-20 14:39:58 +0100117 if (vstr->had_error) {
118 return 0;
119 }
120 return vstr->len;
121}
122
Damien Georgeb0261342014-09-23 18:10:17 +0100123// Extend vstr strictly by requested size, return pointer to newly added chunk
124char *vstr_extend(vstr_t *vstr, size_t size) {
Damien George354d15a2014-02-06 21:11:19 +0000125 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
Damien Georgeb0261342014-09-23 18:10:17 +0100140bool vstr_set_size(vstr_t *vstr, size_t 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
Damien Georgeb0261342014-09-23 18:10:17 +0100159STATIC bool vstr_ensure_extra(vstr_t *vstr, size_t 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 }
Damien Georgeb0261342014-09-23 18:10:17 +0100164 size_t 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
Damien Georgeb0261342014-09-23 18:10:17 +0100176void vstr_hint_size(vstr_t *vstr, size_t size) {
Damien68f59a92013-10-20 14:39:58 +0100177 // 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
Damien Georgeb0261342014-09-23 18:10:17 +0100183char *vstr_add_len(vstr_t *vstr, size_t len) {
Damien68f59a92013-10-20 14:39:58 +0100184 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) {
Paul Sokolovsky165eb692014-06-13 02:42:34 +0300202#if MICROPY_PY_BUILTINS_STR_UNICODE
Chris Angelico2ba22992014-06-04 05:28:12 +1000203 // TODO: Can this be simplified and deduplicated?
204 // Is it worth just calling vstr_add_len(vstr, 4)?
205 if (c < 0x80) {
206 byte *buf = (byte*)vstr_add_len(vstr, 1);
207 if (buf == NULL) {
208 return;
209 }
210 *buf = (byte)c;
211 } else if (c < 0x800) {
212 byte *buf = (byte*)vstr_add_len(vstr, 2);
213 if (buf == NULL) {
214 return;
215 }
216 buf[0] = (c >> 6) | 0xC0;
217 buf[1] = (c & 0x3F) | 0x80;
218 } else if (c < 0x10000) {
219 byte *buf = (byte*)vstr_add_len(vstr, 3);
220 if (buf == NULL) {
221 return;
222 }
223 buf[0] = (c >> 12) | 0xE0;
224 buf[1] = ((c >> 6) & 0x3F) | 0x80;
225 buf[2] = (c & 0x3F) | 0x80;
226 } else {
227 assert(c < 0x110000);
228 byte *buf = (byte*)vstr_add_len(vstr, 4);
229 if (buf == NULL) {
230 return;
231 }
232 buf[0] = (c >> 18) | 0xF0;
233 buf[1] = ((c >> 12) & 0x3F) | 0x80;
234 buf[2] = ((c >> 6) & 0x3F) | 0x80;
235 buf[3] = (c & 0x3F) | 0x80;
Damien68f59a92013-10-20 14:39:58 +0100236 }
Paul Sokolovsky165eb692014-06-13 02:42:34 +0300237#else
238 byte *buf = (byte*)vstr_add_len(vstr, 1);
239 if (buf == NULL) {
240 return;
241 }
242 buf[0] = c;
243#endif
Damien68f59a92013-10-20 14:39:58 +0100244}
245
246void vstr_add_str(vstr_t *vstr, const char *str) {
247 vstr_add_strn(vstr, str, strlen(str));
248}
249
Damien Georgeb0261342014-09-23 18:10:17 +0100250void vstr_add_strn(vstr_t *vstr, const char *str, size_t len) {
Damien68f59a92013-10-20 14:39:58 +0100251 if (vstr->had_error || !vstr_ensure_extra(vstr, len)) {
Damien George354d15a2014-02-06 21:11:19 +0000252 // if buf is fixed, we got here because there isn't enough room left
253 // so just try to copy as much as we can, with room for null byte
254 if (vstr->fixed_buf && vstr->len + 1 < vstr->alloc) {
255 len = vstr->alloc - vstr->len - 1;
256 goto copy;
257 }
Damien68f59a92013-10-20 14:39:58 +0100258 return;
259 }
Damien George354d15a2014-02-06 21:11:19 +0000260copy:
Damien68f59a92013-10-20 14:39:58 +0100261 memmove(vstr->buf + vstr->len, str, len);
262 vstr->len += len;
263 vstr->buf[vstr->len] = 0;
264}
265
Damien George969a6b32014-12-10 22:07:04 +0000266STATIC char *vstr_ins_blank_bytes(vstr_t *vstr, size_t byte_pos, size_t byte_len) {
Damien George280e7202014-03-15 14:33:09 +0000267 if (vstr->had_error) {
268 return NULL;
269 }
Damien Georgeb0261342014-09-23 18:10:17 +0100270 size_t l = vstr->len;
Damien George280e7202014-03-15 14:33:09 +0000271 if (byte_pos > l) {
272 byte_pos = l;
273 }
274 if (byte_len > 0) {
275 // ensure room for the new bytes
276 if (!vstr_ensure_extra(vstr, byte_len)) {
277 return NULL;
278 }
Damien Georgeecd58ae2014-03-15 16:54:06 +0000279 // copy up the string to make room for the new bytes; +1 for the null byte
280 memmove(vstr->buf + byte_pos + byte_len, vstr->buf + byte_pos, l - byte_pos + 1);
Damien George280e7202014-03-15 14:33:09 +0000281 // increase the length
282 vstr->len += byte_len;
Damien George280e7202014-03-15 14:33:09 +0000283 }
284 return vstr->buf + byte_pos;
285}
286
Damien Georgeb0261342014-09-23 18:10:17 +0100287void vstr_ins_byte(vstr_t *vstr, size_t byte_pos, byte b) {
Damien George280e7202014-03-15 14:33:09 +0000288 char *s = vstr_ins_blank_bytes(vstr, byte_pos, 1);
289 if (s != NULL) {
290 *s = b;
291 }
292}
293
Damien Georgeb0261342014-09-23 18:10:17 +0100294void vstr_ins_char(vstr_t *vstr, size_t char_pos, unichar chr) {
Damien George280e7202014-03-15 14:33:09 +0000295 // TODO UNICODE
Damien Georgeecd58ae2014-03-15 16:54:06 +0000296 char *s = vstr_ins_blank_bytes(vstr, char_pos, 1);
Damien George280e7202014-03-15 14:33:09 +0000297 if (s != NULL) {
298 *s = chr;
299 }
300}
301
Damien Georgeb0261342014-09-23 18:10:17 +0100302void vstr_cut_head_bytes(vstr_t *vstr, size_t bytes_to_cut) {
Damien George280e7202014-03-15 14:33:09 +0000303 vstr_cut_out_bytes(vstr, 0, bytes_to_cut);
304}
305
Damien Georgeb0261342014-09-23 18:10:17 +0100306void vstr_cut_tail_bytes(vstr_t *vstr, size_t len) {
Damien68f59a92013-10-20 14:39:58 +0100307 if (vstr->had_error) {
308 return;
309 }
310 if (len > vstr->len) {
311 vstr->len = 0;
312 } else {
313 vstr->len -= len;
314 }
Damien Georgef64086f2014-01-22 23:18:50 +0000315 vstr->buf[vstr->len] = 0;
Damien68f59a92013-10-20 14:39:58 +0100316}
317
Damien Georgeb0261342014-09-23 18:10:17 +0100318void vstr_cut_out_bytes(vstr_t *vstr, size_t byte_pos, size_t bytes_to_cut) {
Damien George280e7202014-03-15 14:33:09 +0000319 if (vstr->had_error || byte_pos >= vstr->len) {
320 return;
321 } else if (byte_pos + bytes_to_cut >= vstr->len) {
322 vstr->len = byte_pos;
323 vstr->buf[vstr->len] = 0;
324 } else {
325 // move includes +1 for null byte at the end
326 memmove(vstr->buf + byte_pos, vstr->buf + byte_pos + bytes_to_cut, vstr->len - byte_pos - bytes_to_cut + 1);
327 vstr->len -= bytes_to_cut;
328 }
329}
330
Damien68f59a92013-10-20 14:39:58 +0100331void vstr_printf(vstr_t *vstr, const char *fmt, ...) {
Damien2f06c572013-11-03 18:20:56 +0000332 va_list ap;
333 va_start(ap, fmt);
334 vstr_vprintf(vstr, fmt, ap);
335 va_end(ap);
336}
337
338void vstr_vprintf(vstr_t *vstr, const char *fmt, va_list ap) {
Damien68f59a92013-10-20 14:39:58 +0100339 if (vstr->had_error || !vstr_ensure_extra(vstr, strlen(fmt))) {
340 return;
341 }
342
343 while (1) {
344 // try to print in the allocated space
Damien George66028ab2014-01-03 14:03:48 +0000345 // need to make a copy of the va_list because we may call vsnprintf multiple times
Damien Georgeb0261342014-09-23 18:10:17 +0100346 size_t size = vstr->alloc - vstr->len;
Damien George66028ab2014-01-03 14:03:48 +0000347 va_list ap2;
348 va_copy(ap2, ap);
349 int n = vsnprintf(vstr->buf + vstr->len, size, fmt, ap2);
350 va_end(ap2);
Damien68f59a92013-10-20 14:39:58 +0100351
352 // if that worked, return
353 if (n > -1 && n < size) {
354 vstr->len += n;
355 return;
356 }
357
358 // else try again with more space
359 if (n > -1) { // glibc 2.1
360 // n + 1 is precisely what is needed
361 if (!vstr_ensure_extra(vstr, n + 1)) {
362 return;
363 }
364 } else { // glibc 2.0
365 // increase to twice the old size
366 if (!vstr_ensure_extra(vstr, size * 2)) {
367 return;
368 }
369 }
370 }
371}