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 : 857825 : size_t qstr_compute_hash(const byte *data, size_t len) {
65 : : // djb2 algorithm; see http://www.cse.yorku.ca/~oz/hash.html
66 : 857825 : size_t hash = 5381;
67 [ + + ]: 13377197 : for (const byte *top = data + len; data < top; data++) {
68 : 12519372 : hash = ((hash << 5) + hash) ^ (*data); // hash * 33 ^ data
69 : : }
70 : 857825 : hash &= Q_HASH_MASK;
71 : : // Make sure that valid hash is never zero, zero means "hash not computed"
72 [ + + ]: 857825 : if (hash == 0) {
73 : 13 : hash++;
74 : : }
75 : 857825 : 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 : 3434 : void qstr_init(void) {
182 : 3434 : 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 : 3434 : MP_STATE_VM(qstr_last_chunk) = NULL;
184 : :
185 : : #if MICROPY_PY_THREAD && !MICROPY_PY_THREAD_GIL
186 : 3434 : mp_thread_mutex_init(&MP_STATE_VM(qstr_mutex));
187 : : #endif
188 : 3434 : }
189 : :
190 : 6032093 : 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 : 6032093 : const qstr_pool_t *pool = MP_STATE_VM(last_pool);
194 [ + + ]: 23863620 : while (*q < pool->total_prev_len) {
195 : 17831527 : pool = pool->prev;
196 : : }
197 : 6032093 : *q -= pool->total_prev_len;
198 [ - + ]: 6032093 : assert(*q < pool->len);
199 : 6032093 : return pool;
200 : : }
201 : :
202 : : // qstr_mutex must be taken while in this function
203 : 40715 : static qstr qstr_add(mp_uint_t len, const char *q_ptr) {
204 : : #if MICROPY_QSTR_BYTES_IN_HASH
205 : 40715 : mp_uint_t hash = qstr_compute_hash((const byte *)q_ptr, len);
206 : 40715 : 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 [ + + ]: 40715 : if (MP_STATE_VM(last_pool)->len >= MP_STATE_VM(last_pool)->alloc) {
213 : 3831 : 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 : 3831 : new_alloc = MAX(MICROPY_ALLOC_QSTR_ENTRIES_INIT, new_alloc);
217 : : #endif
218 : 3831 : 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 : 3831 : + sizeof(qstr_len_t)) * new_alloc;
224 : 3831 : qstr_pool_t *pool = (qstr_pool_t *)m_malloc_maybe(pool_size);
225 [ - + ]: 3831 : 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 : 3831 : pool->hashes = (qstr_hash_t *)(pool->qstrs + new_alloc);
237 : 3831 : pool->lengths = (qstr_len_t *)(pool->hashes + new_alloc);
238 : : #else
239 : : pool->lengths = (qstr_len_t *)(pool->qstrs + new_alloc);
240 : : #endif
241 : 3831 : pool->prev = MP_STATE_VM(last_pool);
242 : 3831 : pool->total_prev_len = MP_STATE_VM(last_pool)->total_prev_len + MP_STATE_VM(last_pool)->len;
243 : 3831 : pool->alloc = new_alloc;
244 : 3831 : pool->len = 0;
245 : 3831 : MP_STATE_VM(last_pool) = pool;
246 : 40715 : DEBUG_printf("QSTR: allocate new pool of size %d\n", MP_STATE_VM(last_pool)->alloc);
247 : : }
248 : :
249 : : // add the new qstr
250 : 40715 : mp_uint_t at = MP_STATE_VM(last_pool)->len;
251 : : #if MICROPY_QSTR_BYTES_IN_HASH
252 : 40715 : MP_STATE_VM(last_pool)->hashes[at] = hash;
253 : : #endif
254 : 40715 : MP_STATE_VM(last_pool)->lengths[at] = len;
255 : 40715 : MP_STATE_VM(last_pool)->qstrs[at] = q_ptr;
256 : 40715 : MP_STATE_VM(last_pool)->len++;
257 : :
258 : : // return id for the newly-added qstr
259 : 40715 : return MP_STATE_VM(last_pool)->total_prev_len + at;
260 : : }
261 : :
262 : 517285 : qstr qstr_find_strn(const char *str, size_t str_len) {
263 [ + + ]: 517285 : 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 : 515909 : size_t str_hash = qstr_compute_hash((const byte *)str, str_len);
271 : : #endif
272 : :
273 : : // search pools for the data
274 [ + + ]: 2227101 : for (const qstr_pool_t *pool = MP_STATE_VM(last_pool); pool != NULL; pool = pool->prev) {
275 : 1894964 : size_t low = 0;
276 : 1894964 : size_t high = pool->len - 1;
277 : :
278 : : // binary search inside the pool
279 [ + + ]: 1894964 : if (pool->is_sorted) {
280 [ + + ]: 7009471 : while (high - low > 1) {
281 : 6144415 : size_t mid = (low + high) / 2;
282 : 6144415 : int cmp = strncmp(str, pool->qstrs[mid], str_len);
283 [ + + ]: 6144415 : if (cmp <= 0) {
284 : : high = mid;
285 : : } else {
286 : 3263201 : low = mid;
287 : : }
288 : : }
289 : : }
290 : :
291 : : // sequential search for the remaining strings
292 [ + + ]: 94748018 : for (mp_uint_t at = low; at < high + 1; at++) {
293 : 93036826 : if (
294 : : #if MICROPY_QSTR_BYTES_IN_HASH
295 [ + + ]: 93036826 : pool->hashes[at] == str_hash &&
296 : : #endif
297 [ + + ]: 184129 : pool->lengths[at] == str_len
298 [ + + ]: 183785 : && memcmp(pool->qstrs[at], str, str_len) == 0) {
299 : 183772 : return pool->total_prev_len + at;
300 : : }
301 : : }
302 : : }
303 : :
304 : : // not found; return null qstr
305 : : return MP_QSTRnull;
306 : : }
307 : :
308 : 7176 : qstr qstr_from_str(const char *str) {
309 : 7176 : return qstr_from_strn(str, strlen(str));
310 : : }
311 : :
312 : 210909 : static qstr qstr_from_strn_helper(const char *str, size_t len, bool data_is_static) {
313 : 210909 : QSTR_ENTER();
314 : 210909 : qstr q = qstr_find_strn(str, len);
315 [ + + ]: 210909 : 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 [ + + ]: 40767 : 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 [ + + ]: 40715 : if (data_is_static) {
325 : : // Given string data will be forever available so use it directly.
326 [ - + ]: 2 : assert(str[len] == '\0');
327 : 2 : goto add;
328 : : }
329 : :
330 : : // compute number of bytes needed to intern this string
331 : 40713 : size_t n_bytes = len + 1;
332 : :
333 [ + + + + ]: 40713 : if (MP_STATE_VM(qstr_last_chunk) != NULL && MP_STATE_VM(qstr_last_used) + n_bytes > MP_STATE_VM(qstr_last_alloc)) {
334 : : // not enough room at end of previously interned string so try to grow
335 : 10304 : 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);
336 [ + + ]: 10304 : if (new_p == NULL) {
337 : : // could not grow existing memory; shrink it to fit previous
338 : 8452 : (void)m_renew_maybe(char, MP_STATE_VM(qstr_last_chunk), MP_STATE_VM(qstr_last_alloc), MP_STATE_VM(qstr_last_used), false);
339 : 8452 : MP_STATE_VM(qstr_last_chunk) = NULL;
340 : : } else {
341 : : // could grow existing memory
342 : 1852 : MP_STATE_VM(qstr_last_alloc) += n_bytes;
343 : : }
344 : : }
345 : :
346 [ + + ]: 40713 : if (MP_STATE_VM(qstr_last_chunk) == NULL) {
347 : : // no existing memory for the interned string so allocate a new chunk
348 : 11886 : size_t al = n_bytes;
349 [ + + ]: 11886 : if (al < MICROPY_ALLOC_QSTR_CHUNK_INIT) {
350 : 11868 : al = MICROPY_ALLOC_QSTR_CHUNK_INIT;
351 : : }
352 : 11886 : MP_STATE_VM(qstr_last_chunk) = m_new_maybe(char, al);
353 [ - + ]: 11886 : if (MP_STATE_VM(qstr_last_chunk) == NULL) {
354 : : // failed to allocate a large chunk so try with exact size
355 : 0 : MP_STATE_VM(qstr_last_chunk) = m_new_maybe(char, n_bytes);
356 [ # # ]: 0 : if (MP_STATE_VM(qstr_last_chunk) == NULL) {
357 : 0 : QSTR_EXIT();
358 : 0 : m_malloc_fail(n_bytes);
359 : : }
360 : : al = n_bytes;
361 : : }
362 : 11886 : MP_STATE_VM(qstr_last_alloc) = al;
363 : 11886 : MP_STATE_VM(qstr_last_used) = 0;
364 : : }
365 : :
366 : : // allocate memory from the chunk for this new interned string's data
367 : 40713 : char *q_ptr = MP_STATE_VM(qstr_last_chunk) + MP_STATE_VM(qstr_last_used);
368 : 40713 : MP_STATE_VM(qstr_last_used) += n_bytes;
369 : :
370 : : // store the interned strings' data
371 : 40713 : memcpy(q_ptr, str, len);
372 : 40713 : q_ptr[len] = '\0';
373 : 40713 : str = q_ptr;
374 : :
375 : 40715 : add:
376 : 40715 : q = qstr_add(len, str);
377 : : }
378 : 210857 : QSTR_EXIT();
379 : 210857 : return q;
380 : : }
381 : :
382 : 210899 : qstr qstr_from_strn(const char *str, size_t len) {
383 : 210899 : return qstr_from_strn_helper(str, len, false);
384 : : }
385 : :
386 : : #if MICROPY_VFS_ROM
387 : : // Create a new qstr that can forever reference the given string data.
388 : 10 : qstr qstr_from_strn_static(const char *str, size_t len) {
389 : 10 : return qstr_from_strn_helper(str, len, true);
390 : : }
391 : : #endif
392 : :
393 : 5231638 : mp_uint_t qstr_hash(qstr q) {
394 : 5231638 : const qstr_pool_t *pool = find_qstr(&q);
395 : : #if MICROPY_QSTR_BYTES_IN_HASH
396 : 5231640 : return pool->hashes[q];
397 : : #else
398 : : return qstr_compute_hash((byte *)pool->qstrs[q], pool->lengths[q]);
399 : : #endif
400 : : }
401 : :
402 : 20194 : size_t qstr_len(qstr q) {
403 : 20194 : const qstr_pool_t *pool = find_qstr(&q);
404 : 20194 : return pool->lengths[q];
405 : : }
406 : :
407 : 7174 : const char *qstr_str(qstr q) {
408 : 7174 : const qstr_pool_t *pool = find_qstr(&q);
409 : 7174 : return pool->qstrs[q];
410 : : }
411 : :
412 : 773105 : const byte *qstr_data(qstr q, size_t *len) {
413 : 773105 : const qstr_pool_t *pool = find_qstr(&q);
414 : 773105 : *len = pool->lengths[q];
415 : 773105 : return (byte *)pool->qstrs[q];
416 : : }
417 : :
418 : 8 : void qstr_pool_info(size_t *n_pool, size_t *n_qstr, size_t *n_str_data_bytes, size_t *n_total_bytes) {
419 : 8 : QSTR_ENTER();
420 : 8 : *n_pool = 0;
421 : 8 : *n_qstr = 0;
422 : 8 : *n_str_data_bytes = 0;
423 : 8 : *n_total_bytes = 0;
424 [ + - + + ]: 16 : for (const qstr_pool_t *pool = MP_STATE_VM(last_pool); pool != NULL && pool != &CONST_POOL; pool = pool->prev) {
425 : 8 : *n_pool += 1;
426 : 8 : *n_qstr += pool->len;
427 [ + + ]: 48 : for (qstr_len_t *l = pool->lengths, *l_top = pool->lengths + pool->len; l < l_top; l++) {
428 : 40 : *n_str_data_bytes += *l + 1;
429 : : }
430 : : #if MICROPY_ENABLE_GC
431 : 8 : *n_total_bytes += gc_nbytes(pool); // this counts actual bytes used in heap
432 : : #else
433 : : *n_total_bytes += sizeof(qstr_pool_t)
434 : : + (sizeof(const char *)
435 : : #if MICROPY_QSTR_BYTES_IN_HASH
436 : : + sizeof(qstr_hash_t)
437 : : #endif
438 : : + sizeof(qstr_len_t)) * pool->alloc;
439 : : #endif
440 : : }
441 : 8 : *n_total_bytes += *n_str_data_bytes;
442 : 8 : QSTR_EXIT();
443 : 8 : }
444 : :
445 : : #if MICROPY_PY_MICROPYTHON_MEM_INFO
446 : 4 : void qstr_dump_data(void) {
447 : 4 : QSTR_ENTER();
448 [ + - + + ]: 8 : for (const qstr_pool_t *pool = MP_STATE_VM(last_pool); pool != NULL && pool != &CONST_POOL; pool = pool->prev) {
449 [ + + ]: 24 : for (const char *const *q = pool->qstrs, *const *q_top = pool->qstrs + pool->len; q < q_top; q++) {
450 : 20 : mp_printf(&mp_plat_print, "Q(%s)\n", *q);
451 : : }
452 : : }
453 : 4 : QSTR_EXIT();
454 : 4 : }
455 : : #endif
456 : :
457 : : #if MICROPY_ROM_TEXT_COMPRESSION
458 : :
459 : : #ifdef NO_QSTR
460 : :
461 : : // If NO_QSTR is set, it means we're doing QSTR extraction.
462 : : // So we won't yet have "genhdr/compressed.data.h"
463 : :
464 : : #else
465 : :
466 : : // Emit the compressed_string_data string.
467 : : #define MP_COMPRESSED_DATA(x) static const char *compressed_string_data = x;
468 : : #define MP_MATCH_COMPRESSED(a, b)
469 : : #include "genhdr/compressed.data.h"
470 : : #undef MP_COMPRESSED_DATA
471 : : #undef MP_MATCH_COMPRESSED
472 : :
473 : : #endif // NO_QSTR
474 : :
475 : : // This implements the "common word" compression scheme (see makecompresseddata.py) where the most
476 : : // common 128 words in error messages are replaced by their index into the list of common words.
477 : :
478 : : // The compressed string data is delimited by setting high bit in the final char of each word.
479 : : // e.g. aaaa<0x80|a>bbbbbb<0x80|b>....
480 : : // This method finds the n'th string.
481 : 6415 : static const byte *find_uncompressed_string(uint8_t n) {
482 : 6415 : const byte *c = (byte *)compressed_string_data;
483 [ + + ]: 201670 : while (n > 0) {
484 [ + + ]: 1137464 : while ((*c & 0x80) == 0) {
485 : 942209 : ++c;
486 : : }
487 : 195255 : ++c;
488 : 195255 : --n;
489 : : }
490 : 6415 : return c;
491 : : }
492 : :
493 : : // Given a compressed string in src, decompresses it into dst.
494 : : // dst must be large enough (use MP_MAX_UNCOMPRESSED_TEXT_LEN+1).
495 : 1503 : void mp_decompress_rom_string(byte *dst, const mp_rom_error_text_t src_chr) {
496 : : // Skip past the 0xff marker.
497 : 1503 : const byte *src = (byte *)src_chr + 1;
498 : : // Need to add spaces around compressed words, except for the first (i.e. transition from 1<->2).
499 : : // 0 = start, 1 = compressed, 2 = regular.
500 : 1503 : int state = 0;
501 [ + + ]: 17483 : while (*src) {
502 [ + + ]: 15980 : if ((byte) * src >= 128) {
503 [ + + ]: 6415 : if (state != 0) {
504 : 5112 : *dst++ = ' ';
505 : : }
506 : 6415 : state = 1;
507 : :
508 : : // High bit set, replace with common word.
509 : 6415 : const byte *word = find_uncompressed_string(*src & 0x7f);
510 : : // The word is terminated by the final char having its high bit set.
511 [ + + ]: 31779 : while ((*word & 0x80) == 0) {
512 : 25364 : *dst++ = *word++;
513 : : }
514 : 6415 : *dst++ = (*word & 0x7f);
515 : : } else {
516 : : // Otherwise just copy one char.
517 [ + + ]: 9565 : if (state == 1) {
518 : 1079 : *dst++ = ' ';
519 : : }
520 : 9565 : state = 2;
521 : :
522 : 9565 : *dst++ = *src;
523 : : }
524 : 15980 : ++src;
525 : : }
526 : : // Add null-terminator.
527 : 1503 : *dst = 0;
528 : 1503 : }
529 : :
530 : : #endif // MICROPY_ROM_TEXT_COMPRESSION
|