Branch data Line data Source code
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 : : *
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 : :
27 : : #include <assert.h>
28 : : #include <string.h>
29 : : #include <stdio.h>
30 : :
31 : : #include "py/mpstate.h"
32 : : #include "py/qstr.h"
33 : : #include "py/gc.h"
34 : : #include "py/runtime.h"
35 : :
36 : : #if MICROPY_DEBUG_VERBOSE // print debugging info
37 : : #define DEBUG_printf DEBUG_printf
38 : : #else // don't print debugging info
39 : : #define DEBUG_printf(...) (void)0
40 : : #endif
41 : :
42 : : // A qstr is an index into the qstr pool.
43 : : // The data for a qstr is \0 terminated (so they can be printed using printf)
44 : :
45 : : #if MICROPY_QSTR_BYTES_IN_HASH
46 : : #define Q_HASH_MASK ((1 << (8 * MICROPY_QSTR_BYTES_IN_HASH)) - 1)
47 : : #else
48 : : #define Q_HASH_MASK (0xffff)
49 : : #endif
50 : :
51 : : #if MICROPY_PY_THREAD && !MICROPY_PY_THREAD_GIL
52 : : #define QSTR_ENTER() mp_thread_mutex_lock(&MP_STATE_VM(qstr_mutex), 1)
53 : : #define QSTR_EXIT() mp_thread_mutex_unlock(&MP_STATE_VM(qstr_mutex))
54 : : #else
55 : : #define QSTR_ENTER()
56 : : #define QSTR_EXIT()
57 : : #endif
58 : :
59 : : // Initial number of entries for qstr pool, set so that the first dynamically
60 : : // allocated pool is twice this size. The value here must be <= MP_QSTRnumber_of.
61 : : #define MICROPY_ALLOC_QSTR_ENTRIES_INIT (10)
62 : :
63 : : // this must match the equivalent function in makeqstrdata.py
64 : 830745 : size_t qstr_compute_hash(const byte *data, size_t len) {
65 : : // djb2 algorithm; see http://www.cse.yorku.ca/~oz/hash.html
66 : 830745 : size_t hash = 5381;
67 [ + + ]: 12661356 : for (const byte *top = data + len; data < top; data++) {
68 : 11830611 : hash = ((hash << 5) + hash) ^ (*data); // hash * 33 ^ data
69 : : }
70 : 830745 : hash &= Q_HASH_MASK;
71 : : // Make sure that valid hash is never zero, zero means "hash not computed"
72 [ + + ]: 830745 : if (hash == 0) {
73 : 13 : hash++;
74 : : }
75 : 830745 : return hash;
76 : : }
77 : :
78 : : // The first pool is the static qstr table. The contents must remain stable as
79 : : // it is part of the .mpy ABI. See the top of py/persistentcode.c and
80 : : // static_qstr_list in makeqstrdata.py. This pool is unsorted (although in a
81 : : // future .mpy version we could re-order them and make it sorted). It also
82 : : // contains additional qstrs that must have IDs <256, see unsorted_qstr_list
83 : : // in makeqstrdata.py.
84 : : #if MICROPY_QSTR_BYTES_IN_HASH
85 : : const qstr_hash_t mp_qstr_const_hashes_static[] = {
86 : : #ifndef NO_QSTR
87 : : #define QDEF0(id, hash, len, str) hash,
88 : : #define QDEF1(id, hash, len, str)
89 : : #include "genhdr/qstrdefs.generated.h"
90 : : #undef QDEF0
91 : : #undef QDEF1
92 : : #endif
93 : : };
94 : : #endif
95 : :
96 : : const qstr_len_t mp_qstr_const_lengths_static[] = {
97 : : #ifndef NO_QSTR
98 : : #define QDEF0(id, hash, len, str) len,
99 : : #define QDEF1(id, hash, len, str)
100 : : #include "genhdr/qstrdefs.generated.h"
101 : : #undef QDEF0
102 : : #undef QDEF1
103 : : #endif
104 : : };
105 : :
106 : : const qstr_pool_t mp_qstr_const_pool_static = {
107 : : NULL, // no previous pool
108 : : 0, // no previous pool
109 : : false, // is_sorted
110 : : MICROPY_ALLOC_QSTR_ENTRIES_INIT,
111 : : MP_QSTRnumber_of_static, // corresponds to number of strings in array just below
112 : : #if MICROPY_QSTR_BYTES_IN_HASH
113 : : (qstr_hash_t *)mp_qstr_const_hashes_static,
114 : : #endif
115 : : (qstr_len_t *)mp_qstr_const_lengths_static,
116 : : {
117 : : #ifndef NO_QSTR
118 : : #define QDEF0(id, hash, len, str) str,
119 : : #define QDEF1(id, hash, len, str)
120 : : #include "genhdr/qstrdefs.generated.h"
121 : : #undef QDEF0
122 : : #undef QDEF1
123 : : #endif
124 : : },
125 : : };
126 : :
127 : : // The next pool is the remainder of the qstrs defined in the firmware. This
128 : : // is sorted.
129 : : #if MICROPY_QSTR_BYTES_IN_HASH
130 : : const qstr_hash_t mp_qstr_const_hashes[] = {
131 : : #ifndef NO_QSTR
132 : : #define QDEF0(id, hash, len, str)
133 : : #define QDEF1(id, hash, len, str) hash,
134 : : #include "genhdr/qstrdefs.generated.h"
135 : : #undef QDEF0
136 : : #undef QDEF1
137 : : #endif
138 : : };
139 : : #endif
140 : :
141 : : const qstr_len_t mp_qstr_const_lengths[] = {
142 : : #ifndef NO_QSTR
143 : : #define QDEF0(id, hash, len, str)
144 : : #define QDEF1(id, hash, len, str) len,
145 : : #include "genhdr/qstrdefs.generated.h"
146 : : #undef QDEF0
147 : : #undef QDEF1
148 : : #endif
149 : : };
150 : :
151 : : const qstr_pool_t mp_qstr_const_pool = {
152 : : &mp_qstr_const_pool_static,
153 : : MP_QSTRnumber_of_static,
154 : : true, // is_sorted
155 : : MICROPY_ALLOC_QSTR_ENTRIES_INIT,
156 : : MP_QSTRnumber_of - MP_QSTRnumber_of_static, // corresponds to number of strings in array just below
157 : : #if MICROPY_QSTR_BYTES_IN_HASH
158 : : (qstr_hash_t *)mp_qstr_const_hashes,
159 : : #endif
160 : : (qstr_len_t *)mp_qstr_const_lengths,
161 : : {
162 : : #ifndef NO_QSTR
163 : : #define QDEF0(id, hash, len, str)
164 : : #define QDEF1(id, hash, len, str) str,
165 : : #include "genhdr/qstrdefs.generated.h"
166 : : #undef QDEF0
167 : : #undef QDEF1
168 : : #endif
169 : : },
170 : : };
171 : :
172 : : // If frozen code is enabled, then there is an additional, sorted, ROM pool
173 : : // containing additional qstrs required by the frozen code.
174 : : #ifdef MICROPY_QSTR_EXTRA_POOL
175 : : extern const qstr_pool_t MICROPY_QSTR_EXTRA_POOL;
176 : : #define CONST_POOL MICROPY_QSTR_EXTRA_POOL
177 : : #else
178 : : #define CONST_POOL mp_qstr_const_pool
179 : : #endif
180 : :
181 : 3414 : void qstr_init(void) {
182 : 3414 : MP_STATE_VM(last_pool) = (qstr_pool_t *)&CONST_POOL; // we won't modify the const_pool since it has no allocated room left
183 : 3414 : MP_STATE_VM(qstr_last_chunk) = NULL;
184 : :
185 : : #if MICROPY_PY_THREAD && !MICROPY_PY_THREAD_GIL
186 : 3414 : mp_thread_mutex_init(&MP_STATE_VM(qstr_mutex));
187 : : #endif
188 : 3414 : }
189 : :
190 : 4883844 : static const qstr_pool_t *find_qstr(qstr *q) {
191 : : // search pool for this qstr
192 : : // total_prev_len==0 in the final pool, so the loop will always terminate
193 : 4883844 : const qstr_pool_t *pool = MP_STATE_VM(last_pool);
194 [ + + ]: 18740663 : while (*q < pool->total_prev_len) {
195 : 13856819 : pool = pool->prev;
196 : : }
197 : 4883844 : *q -= pool->total_prev_len;
198 [ - + ]: 4883844 : assert(*q < pool->len);
199 : 4883844 : return pool;
200 : : }
201 : :
202 : : // qstr_mutex must be taken while in this function
203 : 36386 : static qstr qstr_add(mp_uint_t len, const char *q_ptr) {
204 : : #if MICROPY_QSTR_BYTES_IN_HASH
205 : 36386 : mp_uint_t hash = qstr_compute_hash((const byte *)q_ptr, len);
206 : 36386 : DEBUG_printf("QSTR: add hash=%d len=%d data=%.*s\n", hash, len, len, q_ptr);
207 : : #else
208 : : DEBUG_printf("QSTR: add len=%d data=%.*s\n", len, len, q_ptr);
209 : : #endif
210 : :
211 : : // make sure we have room in the pool for a new qstr
212 [ + + ]: 36386 : if (MP_STATE_VM(last_pool)->len >= MP_STATE_VM(last_pool)->alloc) {
213 : 3772 : size_t new_alloc = MP_STATE_VM(last_pool)->alloc * 2;
214 : : #ifdef MICROPY_QSTR_EXTRA_POOL
215 : : // Put a lower bound on the allocation size in case the extra qstr pool has few entries
216 : 3772 : new_alloc = MAX(MICROPY_ALLOC_QSTR_ENTRIES_INIT, new_alloc);
217 : : #endif
218 : 3772 : mp_uint_t pool_size = sizeof(qstr_pool_t)
219 : : + (sizeof(const char *)
220 : : #if MICROPY_QSTR_BYTES_IN_HASH
221 : : + sizeof(qstr_hash_t)
222 : : #endif
223 : 3772 : + sizeof(qstr_len_t)) * new_alloc;
224 : 3772 : qstr_pool_t *pool = (qstr_pool_t *)m_malloc_maybe(pool_size);
225 [ - + ]: 3772 : if (pool == NULL) {
226 : : // Keep qstr_last_chunk consistent with qstr_pool_t: qstr_last_chunk is not scanned
227 : : // at garbage collection since it's reachable from a qstr_pool_t. And the caller of
228 : : // this function expects q_ptr to be stored in a qstr_pool_t so it can be reached
229 : : // by the collector. If qstr_pool_t allocation failed, qstr_last_chunk needs to be
230 : : // NULL'd. Otherwise it may become a dangling pointer at the next garbage collection.
231 : 0 : MP_STATE_VM(qstr_last_chunk) = NULL;
232 : 0 : QSTR_EXIT();
233 : 0 : m_malloc_fail(new_alloc);
234 : : }
235 : : #if MICROPY_QSTR_BYTES_IN_HASH
236 : 3772 : pool->hashes = (qstr_hash_t *)(pool->qstrs + new_alloc);
237 : 3772 : pool->lengths = (qstr_len_t *)(pool->hashes + new_alloc);
238 : : #else
239 : : pool->lengths = (qstr_len_t *)(pool->qstrs + new_alloc);
240 : : #endif
241 : 3772 : pool->prev = MP_STATE_VM(last_pool);
242 : 3772 : pool->total_prev_len = MP_STATE_VM(last_pool)->total_prev_len + MP_STATE_VM(last_pool)->len;
243 : 3772 : pool->alloc = new_alloc;
244 : 3772 : pool->len = 0;
245 : 3772 : MP_STATE_VM(last_pool) = pool;
246 : 36386 : DEBUG_printf("QSTR: allocate new pool of size %d\n", MP_STATE_VM(last_pool)->alloc);
247 : : }
248 : :
249 : : // add the new qstr
250 : 36386 : mp_uint_t at = MP_STATE_VM(last_pool)->len;
251 : : #if MICROPY_QSTR_BYTES_IN_HASH
252 : 36386 : MP_STATE_VM(last_pool)->hashes[at] = hash;
253 : : #endif
254 : 36386 : MP_STATE_VM(last_pool)->lengths[at] = len;
255 : 36386 : MP_STATE_VM(last_pool)->qstrs[at] = q_ptr;
256 : 36386 : MP_STATE_VM(last_pool)->len++;
257 : :
258 : : // return id for the newly-added qstr
259 : 36386 : return MP_STATE_VM(last_pool)->total_prev_len + at;
260 : : }
261 : :
262 : 501116 : qstr qstr_find_strn(const char *str, size_t str_len) {
263 [ + + ]: 501116 : if (str_len == 0) {
264 : : // strncmp behaviour is undefined for str==NULL.
265 : : return MP_QSTR_;
266 : : }
267 : :
268 : : #if MICROPY_QSTR_BYTES_IN_HASH
269 : : // work out hash of str
270 : 499832 : size_t str_hash = qstr_compute_hash((const byte *)str, str_len);
271 : : #endif
272 : :
273 : : // search pools for the data
274 [ + + ]: 2151149 : for (const qstr_pool_t *pool = MP_STATE_VM(last_pool); pool != NULL; pool = pool->prev) {
275 : 1829299 : size_t low = 0;
276 : 1829299 : size_t high = pool->len - 1;
277 : :
278 : : // binary search inside the pool
279 [ + + ]: 1829299 : if (pool->is_sorted) {
280 [ + + ]: 6799319 : while (high - low > 1) {
281 : 5960409 : size_t mid = (low + high) / 2;
282 : 5960409 : int cmp = strncmp(str, pool->qstrs[mid], str_len);
283 [ + + ]: 5960409 : if (cmp <= 0) {
284 : : high = mid;
285 : : } else {
286 : 3173595 : low = mid;
287 : : }
288 : : }
289 : : }
290 : :
291 : : // sequential search for the remaining strings
292 [ + + ]: 90972669 : for (mp_uint_t at = low; at < high + 1; at++) {
293 : 89321352 : if (
294 : : #if MICROPY_QSTR_BYTES_IN_HASH
295 [ + + ]: 89321352 : pool->hashes[at] == str_hash &&
296 : : #endif
297 [ + + ]: 178327 : pool->lengths[at] == str_len
298 [ + + ]: 177995 : && memcmp(pool->qstrs[at], str, str_len) == 0) {
299 : 177982 : return pool->total_prev_len + at;
300 : : }
301 : : }
302 : : }
303 : :
304 : : // not found; return null qstr
305 : : return MP_QSTRnull;
306 : : }
307 : :
308 : 7120 : qstr qstr_from_str(const char *str) {
309 : 7120 : return qstr_from_strn(str, strlen(str));
310 : : }
311 : :
312 : 200844 : qstr qstr_from_strn(const char *str, size_t len) {
313 : 200844 : QSTR_ENTER();
314 : 200846 : qstr q = qstr_find_strn(str, len);
315 [ + + ]: 200846 : if (q == 0) {
316 : : // qstr does not exist in interned pool so need to add it
317 : :
318 : : // check that len is not too big
319 [ + + ]: 36438 : if (len >= (1 << (8 * MICROPY_QSTR_BYTES_IN_LEN))) {
320 : 52 : QSTR_EXIT();
321 : 52 : mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("name too long"));
322 : : }
323 : :
324 : : // compute number of bytes needed to intern this string
325 : 36386 : size_t n_bytes = len + 1;
326 : :
327 [ + + + + ]: 36386 : if (MP_STATE_VM(qstr_last_chunk) != NULL && MP_STATE_VM(qstr_last_used) + n_bytes > MP_STATE_VM(qstr_last_alloc)) {
328 : : // not enough room at end of previously interned string so try to grow
329 : 4277 : char *new_p = m_renew_maybe(char, MP_STATE_VM(qstr_last_chunk), MP_STATE_VM(qstr_last_alloc), MP_STATE_VM(qstr_last_alloc) + n_bytes, false);
330 [ + + ]: 4277 : if (new_p == NULL) {
331 : : // could not grow existing memory; shrink it to fit previous
332 : 3238 : (void)m_renew_maybe(char, MP_STATE_VM(qstr_last_chunk), MP_STATE_VM(qstr_last_alloc), MP_STATE_VM(qstr_last_used), false);
333 : 3238 : MP_STATE_VM(qstr_last_chunk) = NULL;
334 : : } else {
335 : : // could grow existing memory
336 : 1039 : MP_STATE_VM(qstr_last_alloc) += n_bytes;
337 : : }
338 : : }
339 : :
340 [ + + ]: 36386 : if (MP_STATE_VM(qstr_last_chunk) == NULL) {
341 : : // no existing memory for the interned string so allocate a new chunk
342 : 6652 : size_t al = n_bytes;
343 [ + + ]: 6652 : if (al < MICROPY_ALLOC_QSTR_CHUNK_INIT) {
344 : 6634 : al = MICROPY_ALLOC_QSTR_CHUNK_INIT;
345 : : }
346 : 6652 : MP_STATE_VM(qstr_last_chunk) = m_new_maybe(char, al);
347 [ - + ]: 6652 : if (MP_STATE_VM(qstr_last_chunk) == NULL) {
348 : : // failed to allocate a large chunk so try with exact size
349 : 0 : MP_STATE_VM(qstr_last_chunk) = m_new_maybe(char, n_bytes);
350 [ # # ]: 0 : if (MP_STATE_VM(qstr_last_chunk) == NULL) {
351 : 0 : QSTR_EXIT();
352 : 0 : m_malloc_fail(n_bytes);
353 : : }
354 : : al = n_bytes;
355 : : }
356 : 6652 : MP_STATE_VM(qstr_last_alloc) = al;
357 : 6652 : MP_STATE_VM(qstr_last_used) = 0;
358 : : }
359 : :
360 : : // allocate memory from the chunk for this new interned string's data
361 : 36386 : char *q_ptr = MP_STATE_VM(qstr_last_chunk) + MP_STATE_VM(qstr_last_used);
362 : 36386 : MP_STATE_VM(qstr_last_used) += n_bytes;
363 : :
364 : : // store the interned strings' data
365 : 36386 : memcpy(q_ptr, str, len);
366 : 36386 : q_ptr[len] = '\0';
367 : 36386 : q = qstr_add(len, q_ptr);
368 : : }
369 : 200794 : QSTR_EXIT();
370 : 200794 : return q;
371 : : }
372 : :
373 : 4103752 : mp_uint_t qstr_hash(qstr q) {
374 : 4103752 : const qstr_pool_t *pool = find_qstr(&q);
375 : : #if MICROPY_QSTR_BYTES_IN_HASH
376 : 4103749 : return pool->hashes[q];
377 : : #else
378 : : return qstr_compute_hash((byte *)pool->qstrs[q], pool->lengths[q]);
379 : : #endif
380 : : }
381 : :
382 : 20194 : size_t qstr_len(qstr q) {
383 : 20194 : const qstr_pool_t *pool = find_qstr(&q);
384 : 20194 : return pool->lengths[q];
385 : : }
386 : :
387 : 6178 : const char *qstr_str(qstr q) {
388 : 6178 : const qstr_pool_t *pool = find_qstr(&q);
389 : 6178 : return pool->qstrs[q];
390 : : }
391 : :
392 : 753727 : const byte *qstr_data(qstr q, size_t *len) {
393 : 753727 : const qstr_pool_t *pool = find_qstr(&q);
394 : 753727 : *len = pool->lengths[q];
395 : 753727 : return (byte *)pool->qstrs[q];
396 : : }
397 : :
398 : 8 : void qstr_pool_info(size_t *n_pool, size_t *n_qstr, size_t *n_str_data_bytes, size_t *n_total_bytes) {
399 : 8 : QSTR_ENTER();
400 : 8 : *n_pool = 0;
401 : 8 : *n_qstr = 0;
402 : 8 : *n_str_data_bytes = 0;
403 : 8 : *n_total_bytes = 0;
404 [ + - + + ]: 16 : for (const qstr_pool_t *pool = MP_STATE_VM(last_pool); pool != NULL && pool != &CONST_POOL; pool = pool->prev) {
405 : 8 : *n_pool += 1;
406 : 8 : *n_qstr += pool->len;
407 [ + + ]: 40 : for (qstr_len_t *l = pool->lengths, *l_top = pool->lengths + pool->len; l < l_top; l++) {
408 : 32 : *n_str_data_bytes += *l + 1;
409 : : }
410 : : #if MICROPY_ENABLE_GC
411 : 8 : *n_total_bytes += gc_nbytes(pool); // this counts actual bytes used in heap
412 : : #else
413 : : *n_total_bytes += sizeof(qstr_pool_t)
414 : : + (sizeof(const char *)
415 : : #if MICROPY_QSTR_BYTES_IN_HASH
416 : : + sizeof(qstr_hash_t)
417 : : #endif
418 : : + sizeof(qstr_len_t)) * pool->alloc;
419 : : #endif
420 : : }
421 : 8 : *n_total_bytes += *n_str_data_bytes;
422 : 8 : QSTR_EXIT();
423 : 8 : }
424 : :
425 : : #if MICROPY_PY_MICROPYTHON_MEM_INFO
426 : 4 : void qstr_dump_data(void) {
427 : 4 : QSTR_ENTER();
428 [ + - + + ]: 8 : for (const qstr_pool_t *pool = MP_STATE_VM(last_pool); pool != NULL && pool != &CONST_POOL; pool = pool->prev) {
429 [ + + ]: 20 : for (const char *const *q = pool->qstrs, *const *q_top = pool->qstrs + pool->len; q < q_top; q++) {
430 : 16 : mp_printf(&mp_plat_print, "Q(%s)\n", *q);
431 : : }
432 : : }
433 : 4 : QSTR_EXIT();
434 : 4 : }
435 : : #endif
436 : :
437 : : #if MICROPY_ROM_TEXT_COMPRESSION
438 : :
439 : : #ifdef NO_QSTR
440 : :
441 : : // If NO_QSTR is set, it means we're doing QSTR extraction.
442 : : // So we won't yet have "genhdr/compressed.data.h"
443 : :
444 : : #else
445 : :
446 : : // Emit the compressed_string_data string.
447 : : #define MP_COMPRESSED_DATA(x) static const char *compressed_string_data = x;
448 : : #define MP_MATCH_COMPRESSED(a, b)
449 : : #include "genhdr/compressed.data.h"
450 : : #undef MP_COMPRESSED_DATA
451 : : #undef MP_MATCH_COMPRESSED
452 : :
453 : : #endif // NO_QSTR
454 : :
455 : : // This implements the "common word" compression scheme (see makecompresseddata.py) where the most
456 : : // common 128 words in error messages are replaced by their index into the list of common words.
457 : :
458 : : // The compressed string data is delimited by setting high bit in the final char of each word.
459 : : // e.g. aaaa<0x80|a>bbbbbb<0x80|b>....
460 : : // This method finds the n'th string.
461 : 6361 : static const byte *find_uncompressed_string(uint8_t n) {
462 : 6361 : const byte *c = (byte *)compressed_string_data;
463 [ + + ]: 199782 : while (n > 0) {
464 [ + + ]: 1126650 : while ((*c & 0x80) == 0) {
465 : 933229 : ++c;
466 : : }
467 : 193421 : ++c;
468 : 193421 : --n;
469 : : }
470 : 6361 : return c;
471 : : }
472 : :
473 : : // Given a compressed string in src, decompresses it into dst.
474 : : // dst must be large enough (use MP_MAX_UNCOMPRESSED_TEXT_LEN+1).
475 : 1483 : void mp_decompress_rom_string(byte *dst, const mp_rom_error_text_t src_chr) {
476 : : // Skip past the 0xff marker.
477 : 1483 : const byte *src = (byte *)src_chr + 1;
478 : : // Need to add spaces around compressed words, except for the first (i.e. transition from 1<->2).
479 : : // 0 = start, 1 = compressed, 2 = regular.
480 : 1483 : int state = 0;
481 [ + + ]: 17179 : while (*src) {
482 [ + + ]: 15696 : if ((byte) * src >= 128) {
483 [ + + ]: 6361 : if (state != 0) {
484 : 5078 : *dst++ = ' ';
485 : : }
486 : 6361 : state = 1;
487 : :
488 : : // High bit set, replace with common word.
489 : 6361 : const byte *word = find_uncompressed_string(*src & 0x7f);
490 : : // The word is terminated by the final char having its high bit set.
491 [ + + ]: 31515 : while ((*word & 0x80) == 0) {
492 : 25154 : *dst++ = *word++;
493 : : }
494 : 6361 : *dst++ = (*word & 0x7f);
495 : : } else {
496 : : // Otherwise just copy one char.
497 [ + + ]: 9335 : if (state == 1) {
498 : 1065 : *dst++ = ' ';
499 : : }
500 : 9335 : state = 2;
501 : :
502 : 9335 : *dst++ = *src;
503 : : }
504 : 15696 : ++src;
505 : : }
506 : : // Add null-terminator.
507 : 1483 : *dst = 0;
508 : 1483 : }
509 : :
510 : : #endif // MICROPY_ROM_TEXT_COMPRESSION
|