1 /*
2  * This file is part of the MicroPython project, http://micropython.org/
3  *
4  * The MIT License (MIT)
5  *
6  * Copyright (c) 2013, 2014 Damien P. George
7  * Copyright (c) 2014 Paul Sokolovsky
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a copy
10  * of this software and associated documentation files (the "Software"), to deal
11  * in the Software without restriction, including without limitation the rights
12  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13  * copies of the Software, and to permit persons to whom the Software is
14  * furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included in
17  * all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25  * THE SOFTWARE.
26  */
27 
28 #include <stdio.h>
29 #include <stdarg.h>
30 #include <string.h>
31 #include <assert.h>
32 
33 #include "py/mpconfig.h"
34 #include "py/runtime.h"
35 #include "py/mpprint.h"
36 
37 // returned value is always at least 1 greater than argument
38 #define ROUND_ALLOC(a) (((a) & ((~0U) - 7)) + 8)
39 
40 // Init the vstr so it allocs exactly given number of bytes.  Set length to zero.
vstr_init(vstr_t * vstr,size_t alloc)41 void vstr_init(vstr_t *vstr, size_t alloc) {
42     if (alloc < 1) {
43         alloc = 1;
44     }
45     vstr->alloc = alloc;
46     vstr->len = 0;
47     vstr->buf = m_new(char, vstr->alloc);
48     vstr->fixed_buf = false;
49 }
50 
51 // Init the vstr so it allocs exactly enough ram to hold a null-terminated
52 // string of the given length, and set the length.
vstr_init_len(vstr_t * vstr,size_t len)53 void vstr_init_len(vstr_t *vstr, size_t len) {
54     vstr_init(vstr, len + 1);
55     vstr->len = len;
56 }
57 
vstr_init_fixed_buf(vstr_t * vstr,size_t alloc,char * buf)58 void vstr_init_fixed_buf(vstr_t *vstr, size_t alloc, char *buf) {
59     vstr->alloc = alloc;
60     vstr->len = 0;
61     vstr->buf = buf;
62     vstr->fixed_buf = true;
63 }
64 
vstr_init_print(vstr_t * vstr,size_t alloc,mp_print_t * print)65 void vstr_init_print(vstr_t *vstr, size_t alloc, mp_print_t *print) {
66     vstr_init(vstr, alloc);
67     print->data = vstr;
68     print->print_strn = (mp_print_strn_t)vstr_add_strn;
69 }
70 
vstr_clear(vstr_t * vstr)71 void vstr_clear(vstr_t *vstr) {
72     if (!vstr->fixed_buf) {
73         m_del(char, vstr->buf, vstr->alloc);
74     }
75     vstr->buf = NULL;
76 }
77 
vstr_new(size_t alloc)78 vstr_t *vstr_new(size_t alloc) {
79     vstr_t *vstr = m_new_obj(vstr_t);
80     vstr_init(vstr, alloc);
81     return vstr;
82 }
83 
vstr_free(vstr_t * vstr)84 void vstr_free(vstr_t *vstr) {
85     if (vstr != NULL) {
86         if (!vstr->fixed_buf) {
87             m_del(char, vstr->buf, vstr->alloc);
88         }
89         m_del_obj(vstr_t, vstr);
90     }
91 }
92 
93 // Extend vstr strictly by requested size, return pointer to newly added chunk.
vstr_extend(vstr_t * vstr,size_t size)94 char *vstr_extend(vstr_t *vstr, size_t size) {
95     if (vstr->fixed_buf) {
96         // We can't reallocate, and the caller is expecting the space to
97         // be there, so the only safe option is to raise an exception.
98         mp_raise_msg(&mp_type_RuntimeError, NULL);
99     }
100     char *new_buf = m_renew(char, vstr->buf, vstr->alloc, vstr->alloc + size);
101     char *p = new_buf + vstr->alloc;
102     vstr->alloc += size;
103     vstr->buf = new_buf;
104     return p;
105 }
106 
vstr_ensure_extra(vstr_t * vstr,size_t size)107 STATIC void vstr_ensure_extra(vstr_t *vstr, size_t size) {
108     if (vstr->len + size > vstr->alloc) {
109         if (vstr->fixed_buf) {
110             // We can't reallocate, and the caller is expecting the space to
111             // be there, so the only safe option is to raise an exception.
112             mp_raise_msg(&mp_type_RuntimeError, NULL);
113         }
114         size_t new_alloc = ROUND_ALLOC((vstr->len + size) + 16);
115         char *new_buf = m_renew(char, vstr->buf, vstr->alloc, new_alloc);
116         vstr->alloc = new_alloc;
117         vstr->buf = new_buf;
118     }
119 }
120 
vstr_hint_size(vstr_t * vstr,size_t size)121 void vstr_hint_size(vstr_t *vstr, size_t size) {
122     vstr_ensure_extra(vstr, size);
123 }
124 
vstr_add_len(vstr_t * vstr,size_t len)125 char *vstr_add_len(vstr_t *vstr, size_t len) {
126     vstr_ensure_extra(vstr, len);
127     char *buf = vstr->buf + vstr->len;
128     vstr->len += len;
129     return buf;
130 }
131 
132 // Doesn't increase len, just makes sure there is a null byte at the end
vstr_null_terminated_str(vstr_t * vstr)133 char *vstr_null_terminated_str(vstr_t *vstr) {
134     // If there's no more room, add single byte
135     if (vstr->alloc == vstr->len) {
136         vstr_extend(vstr, 1);
137     }
138     vstr->buf[vstr->len] = '\0';
139     return vstr->buf;
140 }
141 
vstr_add_byte(vstr_t * vstr,byte b)142 void vstr_add_byte(vstr_t *vstr, byte b) {
143     byte *buf = (byte *)vstr_add_len(vstr, 1);
144     buf[0] = b;
145 }
146 
vstr_add_char(vstr_t * vstr,unichar c)147 void vstr_add_char(vstr_t *vstr, unichar c) {
148     #if MICROPY_PY_BUILTINS_STR_UNICODE
149     // TODO: Can this be simplified and deduplicated?
150     // Is it worth just calling vstr_add_len(vstr, 4)?
151     if (c < 0x80) {
152         byte *buf = (byte *)vstr_add_len(vstr, 1);
153         *buf = (byte)c;
154     } else if (c < 0x800) {
155         byte *buf = (byte *)vstr_add_len(vstr, 2);
156         buf[0] = (c >> 6) | 0xC0;
157         buf[1] = (c & 0x3F) | 0x80;
158     } else if (c < 0x10000) {
159         byte *buf = (byte *)vstr_add_len(vstr, 3);
160         buf[0] = (c >> 12) | 0xE0;
161         buf[1] = ((c >> 6) & 0x3F) | 0x80;
162         buf[2] = (c & 0x3F) | 0x80;
163     } else {
164         assert(c < 0x110000);
165         byte *buf = (byte *)vstr_add_len(vstr, 4);
166         buf[0] = (c >> 18) | 0xF0;
167         buf[1] = ((c >> 12) & 0x3F) | 0x80;
168         buf[2] = ((c >> 6) & 0x3F) | 0x80;
169         buf[3] = (c & 0x3F) | 0x80;
170     }
171     #else
172     vstr_add_byte(vstr, c);
173     #endif
174 }
175 
vstr_add_str(vstr_t * vstr,const char * str)176 void vstr_add_str(vstr_t *vstr, const char *str) {
177     vstr_add_strn(vstr, str, strlen(str));
178 }
179 
vstr_add_strn(vstr_t * vstr,const char * str,size_t len)180 void vstr_add_strn(vstr_t *vstr, const char *str, size_t len) {
181     vstr_ensure_extra(vstr, len);
182     memmove(vstr->buf + vstr->len, str, len);
183     vstr->len += len;
184 }
185 
vstr_ins_blank_bytes(vstr_t * vstr,size_t byte_pos,size_t byte_len)186 STATIC char *vstr_ins_blank_bytes(vstr_t *vstr, size_t byte_pos, size_t byte_len) {
187     size_t l = vstr->len;
188     if (byte_pos > l) {
189         byte_pos = l;
190     }
191     if (byte_len > 0) {
192         // ensure room for the new bytes
193         vstr_ensure_extra(vstr, byte_len);
194         // copy up the string to make room for the new bytes
195         memmove(vstr->buf + byte_pos + byte_len, vstr->buf + byte_pos, l - byte_pos);
196         // increase the length
197         vstr->len += byte_len;
198     }
199     return vstr->buf + byte_pos;
200 }
201 
vstr_ins_byte(vstr_t * vstr,size_t byte_pos,byte b)202 void vstr_ins_byte(vstr_t *vstr, size_t byte_pos, byte b) {
203     char *s = vstr_ins_blank_bytes(vstr, byte_pos, 1);
204     *s = b;
205 }
206 
vstr_ins_char(vstr_t * vstr,size_t char_pos,unichar chr)207 void vstr_ins_char(vstr_t *vstr, size_t char_pos, unichar chr) {
208     // TODO UNICODE
209     char *s = vstr_ins_blank_bytes(vstr, char_pos, 1);
210     *s = chr;
211 }
212 
vstr_cut_head_bytes(vstr_t * vstr,size_t bytes_to_cut)213 void vstr_cut_head_bytes(vstr_t *vstr, size_t bytes_to_cut) {
214     vstr_cut_out_bytes(vstr, 0, bytes_to_cut);
215 }
216 
vstr_cut_tail_bytes(vstr_t * vstr,size_t len)217 void vstr_cut_tail_bytes(vstr_t *vstr, size_t len) {
218     if (len > vstr->len) {
219         vstr->len = 0;
220     } else {
221         vstr->len -= len;
222     }
223 }
224 
vstr_cut_out_bytes(vstr_t * vstr,size_t byte_pos,size_t bytes_to_cut)225 void vstr_cut_out_bytes(vstr_t *vstr, size_t byte_pos, size_t bytes_to_cut) {
226     if (byte_pos >= vstr->len) {
227         return;
228     } else if (byte_pos + bytes_to_cut >= vstr->len) {
229         vstr->len = byte_pos;
230     } else {
231         memmove(vstr->buf + byte_pos, vstr->buf + byte_pos + bytes_to_cut, vstr->len - byte_pos - bytes_to_cut);
232         vstr->len -= bytes_to_cut;
233     }
234 }
235 
vstr_printf(vstr_t * vstr,const char * fmt,...)236 void vstr_printf(vstr_t *vstr, const char *fmt, ...) {
237     va_list ap;
238     va_start(ap, fmt);
239     vstr_vprintf(vstr, fmt, ap);
240     va_end(ap);
241 }
242 
vstr_vprintf(vstr_t * vstr,const char * fmt,va_list ap)243 void vstr_vprintf(vstr_t *vstr, const char *fmt, va_list ap) {
244     mp_print_t print = {vstr, (mp_print_strn_t)vstr_add_strn};
245     mp_vprintf(&print, fmt, ap);
246 }
247