blob: 63358d5957f54bce4615be96b86a8379c7d40fd6 [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 George05005f62015-01-21 22:48:37 +000055void vstr_init_len(vstr_t *vstr, size_t len) {
56 vstr_init(vstr, len + 1);
57 vstr_add_len(vstr, len);
58}
59
Damien Georgeb0261342014-09-23 18:10:17 +010060void vstr_init_fixed_buf(vstr_t *vstr, size_t alloc, char *buf) {
Damien George354d15a2014-02-06 21:11:19 +000061 assert(alloc > 0); // need at least room for the null byte
62 vstr->alloc = alloc;
63 vstr->len = 0;
64 vstr->buf = buf;
65 vstr->buf[0] = 0;
66 vstr->had_error = false;
67 vstr->fixed_buf = true;
Damien68f59a92013-10-20 14:39:58 +010068}
69
70void vstr_clear(vstr_t *vstr) {
Damien George354d15a2014-02-06 21:11:19 +000071 if (!vstr->fixed_buf) {
72 m_del(char, vstr->buf, vstr->alloc);
73 }
Damien68f59a92013-10-20 14:39:58 +010074 vstr->buf = NULL;
75}
76
Damien8b3a7c22013-10-23 20:20:17 +010077vstr_t *vstr_new(void) {
Damien68f59a92013-10-20 14:39:58 +010078 vstr_t *vstr = m_new(vstr_t, 1);
79 if (vstr == NULL) {
80 return NULL;
81 }
Paul Sokolovsky5d2499c2014-01-13 23:15:23 +020082 vstr_init(vstr, 32);
83 return vstr;
84}
85
Damien Georgeb0261342014-09-23 18:10:17 +010086vstr_t *vstr_new_size(size_t alloc) {
Paul Sokolovsky5d2499c2014-01-13 23:15:23 +020087 vstr_t *vstr = m_new(vstr_t, 1);
88 if (vstr == NULL) {
89 return NULL;
90 }
91 vstr_init(vstr, alloc);
Damien68f59a92013-10-20 14:39:58 +010092 return vstr;
93}
94
95void vstr_free(vstr_t *vstr) {
96 if (vstr != NULL) {
Damien George354d15a2014-02-06 21:11:19 +000097 if (!vstr->fixed_buf) {
98 m_del(char, vstr->buf, vstr->alloc);
99 }
Damien732407f2013-12-29 19:33:23 +0000100 m_del_obj(vstr_t, vstr);
Damien68f59a92013-10-20 14:39:58 +0100101 }
102}
103
104void vstr_reset(vstr_t *vstr) {
105 vstr->len = 0;
106 vstr->buf[0] = 0;
107 vstr->had_error = false;
108}
109
110bool vstr_had_error(vstr_t *vstr) {
111 return vstr->had_error;
112}
113
114char *vstr_str(vstr_t *vstr) {
115 if (vstr->had_error) {
116 return NULL;
117 }
118 return vstr->buf;
119}
120
Damien Georgeb0261342014-09-23 18:10:17 +0100121size_t vstr_len(vstr_t *vstr) {
Damien68f59a92013-10-20 14:39:58 +0100122 if (vstr->had_error) {
123 return 0;
124 }
125 return vstr->len;
126}
127
Damien Georgeb0261342014-09-23 18:10:17 +0100128// Extend vstr strictly by requested size, return pointer to newly added chunk
129char *vstr_extend(vstr_t *vstr, size_t size) {
Damien George354d15a2014-02-06 21:11:19 +0000130 if (vstr->fixed_buf) {
131 return NULL;
132 }
Paul Sokolovsky5d2499c2014-01-13 23:15:23 +0200133 char *new_buf = m_renew(char, vstr->buf, vstr->alloc, vstr->alloc + size);
134 if (new_buf == NULL) {
135 vstr->had_error = true;
136 return NULL;
137 }
138 char *p = new_buf + vstr->alloc;
139 vstr->alloc += size;
140 vstr->buf = new_buf;
141 return p;
142}
143
Damien Georgeb0261342014-09-23 18:10:17 +0100144STATIC bool vstr_ensure_extra(vstr_t *vstr, size_t size) {
Damien68f59a92013-10-20 14:39:58 +0100145 if (vstr->len + size + 1 > vstr->alloc) {
Damien George354d15a2014-02-06 21:11:19 +0000146 if (vstr->fixed_buf) {
147 return false;
148 }
Damien Georgeb0261342014-09-23 18:10:17 +0100149 size_t new_alloc = ROUND_ALLOC((vstr->len + size + 1) * 2);
Damien732407f2013-12-29 19:33:23 +0000150 char *new_buf = m_renew(char, vstr->buf, vstr->alloc, new_alloc);
Damien68f59a92013-10-20 14:39:58 +0100151 if (new_buf == NULL) {
152 vstr->had_error = true;
153 return false;
154 }
155 vstr->alloc = new_alloc;
156 vstr->buf = new_buf;
157 }
158 return true;
159}
160
Damien Georgeb0261342014-09-23 18:10:17 +0100161void vstr_hint_size(vstr_t *vstr, size_t size) {
Damien68f59a92013-10-20 14:39:58 +0100162 // it's not an error if we fail to allocate for the size hint
163 bool er = vstr->had_error;
164 vstr_ensure_extra(vstr, size);
165 vstr->had_error = er;
166}
167
Damien Georgeb0261342014-09-23 18:10:17 +0100168char *vstr_add_len(vstr_t *vstr, size_t len) {
Damien68f59a92013-10-20 14:39:58 +0100169 if (vstr->had_error || !vstr_ensure_extra(vstr, len)) {
170 return NULL;
171 }
172 char *buf = vstr->buf + vstr->len;
173 vstr->len += len;
174 vstr->buf[vstr->len] = 0;
175 return buf;
176}
177
Damien George280e7202014-03-15 14:33:09 +0000178void vstr_add_byte(vstr_t *vstr, byte b) {
Damien68f59a92013-10-20 14:39:58 +0100179 byte *buf = (byte*)vstr_add_len(vstr, 1);
180 if (buf == NULL) {
181 return;
182 }
Damien George280e7202014-03-15 14:33:09 +0000183 buf[0] = b;
Damien68f59a92013-10-20 14:39:58 +0100184}
185
186void vstr_add_char(vstr_t *vstr, unichar c) {
Paul Sokolovsky165eb692014-06-13 02:42:34 +0300187#if MICROPY_PY_BUILTINS_STR_UNICODE
Chris Angelico2ba22992014-06-04 05:28:12 +1000188 // TODO: Can this be simplified and deduplicated?
189 // Is it worth just calling vstr_add_len(vstr, 4)?
190 if (c < 0x80) {
191 byte *buf = (byte*)vstr_add_len(vstr, 1);
192 if (buf == NULL) {
193 return;
194 }
195 *buf = (byte)c;
196 } else if (c < 0x800) {
197 byte *buf = (byte*)vstr_add_len(vstr, 2);
198 if (buf == NULL) {
199 return;
200 }
201 buf[0] = (c >> 6) | 0xC0;
202 buf[1] = (c & 0x3F) | 0x80;
203 } else if (c < 0x10000) {
204 byte *buf = (byte*)vstr_add_len(vstr, 3);
205 if (buf == NULL) {
206 return;
207 }
208 buf[0] = (c >> 12) | 0xE0;
209 buf[1] = ((c >> 6) & 0x3F) | 0x80;
210 buf[2] = (c & 0x3F) | 0x80;
211 } else {
212 assert(c < 0x110000);
213 byte *buf = (byte*)vstr_add_len(vstr, 4);
214 if (buf == NULL) {
215 return;
216 }
217 buf[0] = (c >> 18) | 0xF0;
218 buf[1] = ((c >> 12) & 0x3F) | 0x80;
219 buf[2] = ((c >> 6) & 0x3F) | 0x80;
220 buf[3] = (c & 0x3F) | 0x80;
Damien68f59a92013-10-20 14:39:58 +0100221 }
Paul Sokolovsky165eb692014-06-13 02:42:34 +0300222#else
223 byte *buf = (byte*)vstr_add_len(vstr, 1);
224 if (buf == NULL) {
225 return;
226 }
227 buf[0] = c;
228#endif
Damien68f59a92013-10-20 14:39:58 +0100229}
230
231void vstr_add_str(vstr_t *vstr, const char *str) {
232 vstr_add_strn(vstr, str, strlen(str));
233}
234
Damien Georgeb0261342014-09-23 18:10:17 +0100235void vstr_add_strn(vstr_t *vstr, const char *str, size_t len) {
Damien68f59a92013-10-20 14:39:58 +0100236 if (vstr->had_error || !vstr_ensure_extra(vstr, len)) {
Damien George354d15a2014-02-06 21:11:19 +0000237 // if buf is fixed, we got here because there isn't enough room left
238 // so just try to copy as much as we can, with room for null byte
239 if (vstr->fixed_buf && vstr->len + 1 < vstr->alloc) {
240 len = vstr->alloc - vstr->len - 1;
241 goto copy;
242 }
Damien68f59a92013-10-20 14:39:58 +0100243 return;
244 }
Damien George354d15a2014-02-06 21:11:19 +0000245copy:
Damien68f59a92013-10-20 14:39:58 +0100246 memmove(vstr->buf + vstr->len, str, len);
247 vstr->len += len;
248 vstr->buf[vstr->len] = 0;
249}
250
Damien George969a6b32014-12-10 22:07:04 +0000251STATIC char *vstr_ins_blank_bytes(vstr_t *vstr, size_t byte_pos, size_t byte_len) {
Damien George280e7202014-03-15 14:33:09 +0000252 if (vstr->had_error) {
253 return NULL;
254 }
Damien Georgeb0261342014-09-23 18:10:17 +0100255 size_t l = vstr->len;
Damien George280e7202014-03-15 14:33:09 +0000256 if (byte_pos > l) {
257 byte_pos = l;
258 }
259 if (byte_len > 0) {
260 // ensure room for the new bytes
261 if (!vstr_ensure_extra(vstr, byte_len)) {
262 return NULL;
263 }
Damien Georgeecd58ae2014-03-15 16:54:06 +0000264 // copy up the string to make room for the new bytes; +1 for the null byte
265 memmove(vstr->buf + byte_pos + byte_len, vstr->buf + byte_pos, l - byte_pos + 1);
Damien George280e7202014-03-15 14:33:09 +0000266 // increase the length
267 vstr->len += byte_len;
Damien George280e7202014-03-15 14:33:09 +0000268 }
269 return vstr->buf + byte_pos;
270}
271
Damien Georgeb0261342014-09-23 18:10:17 +0100272void vstr_ins_byte(vstr_t *vstr, size_t byte_pos, byte b) {
Damien George280e7202014-03-15 14:33:09 +0000273 char *s = vstr_ins_blank_bytes(vstr, byte_pos, 1);
274 if (s != NULL) {
275 *s = b;
276 }
277}
278
Damien Georgeb0261342014-09-23 18:10:17 +0100279void vstr_ins_char(vstr_t *vstr, size_t char_pos, unichar chr) {
Damien George280e7202014-03-15 14:33:09 +0000280 // TODO UNICODE
Damien Georgeecd58ae2014-03-15 16:54:06 +0000281 char *s = vstr_ins_blank_bytes(vstr, char_pos, 1);
Damien George280e7202014-03-15 14:33:09 +0000282 if (s != NULL) {
283 *s = chr;
284 }
285}
286
Damien Georgeb0261342014-09-23 18:10:17 +0100287void vstr_cut_head_bytes(vstr_t *vstr, size_t bytes_to_cut) {
Damien George280e7202014-03-15 14:33:09 +0000288 vstr_cut_out_bytes(vstr, 0, bytes_to_cut);
289}
290
Damien Georgeb0261342014-09-23 18:10:17 +0100291void vstr_cut_tail_bytes(vstr_t *vstr, size_t len) {
Damien68f59a92013-10-20 14:39:58 +0100292 if (vstr->had_error) {
293 return;
294 }
295 if (len > vstr->len) {
296 vstr->len = 0;
297 } else {
298 vstr->len -= len;
299 }
Damien Georgef64086f2014-01-22 23:18:50 +0000300 vstr->buf[vstr->len] = 0;
Damien68f59a92013-10-20 14:39:58 +0100301}
302
Damien Georgeb0261342014-09-23 18:10:17 +0100303void vstr_cut_out_bytes(vstr_t *vstr, size_t byte_pos, size_t bytes_to_cut) {
Damien George280e7202014-03-15 14:33:09 +0000304 if (vstr->had_error || byte_pos >= vstr->len) {
305 return;
306 } else if (byte_pos + bytes_to_cut >= vstr->len) {
307 vstr->len = byte_pos;
308 vstr->buf[vstr->len] = 0;
309 } else {
310 // move includes +1 for null byte at the end
311 memmove(vstr->buf + byte_pos, vstr->buf + byte_pos + bytes_to_cut, vstr->len - byte_pos - bytes_to_cut + 1);
312 vstr->len -= bytes_to_cut;
313 }
314}
315
Damien68f59a92013-10-20 14:39:58 +0100316void vstr_printf(vstr_t *vstr, const char *fmt, ...) {
Damien2f06c572013-11-03 18:20:56 +0000317 va_list ap;
318 va_start(ap, fmt);
319 vstr_vprintf(vstr, fmt, ap);
320 va_end(ap);
321}
322
323void vstr_vprintf(vstr_t *vstr, const char *fmt, va_list ap) {
Damien68f59a92013-10-20 14:39:58 +0100324 if (vstr->had_error || !vstr_ensure_extra(vstr, strlen(fmt))) {
325 return;
326 }
327
328 while (1) {
329 // try to print in the allocated space
Damien George66028ab2014-01-03 14:03:48 +0000330 // need to make a copy of the va_list because we may call vsnprintf multiple times
Damien Georgeb0261342014-09-23 18:10:17 +0100331 size_t size = vstr->alloc - vstr->len;
Damien George66028ab2014-01-03 14:03:48 +0000332 va_list ap2;
333 va_copy(ap2, ap);
334 int n = vsnprintf(vstr->buf + vstr->len, size, fmt, ap2);
335 va_end(ap2);
Damien68f59a92013-10-20 14:39:58 +0100336
337 // if that worked, return
Damien George963a5a32015-01-16 17:47:07 +0000338 if (n > -1 && (size_t)n < size) {
Damien68f59a92013-10-20 14:39:58 +0100339 vstr->len += n;
340 return;
341 }
342
343 // else try again with more space
344 if (n > -1) { // glibc 2.1
345 // n + 1 is precisely what is needed
346 if (!vstr_ensure_extra(vstr, n + 1)) {
347 return;
348 }
349 } else { // glibc 2.0
350 // increase to twice the old size
351 if (!vstr_ensure_extra(vstr, size * 2)) {
352 return;
353 }
354 }
355 }
356}