1 | /* |
2 | * This file is part of the MicroPython project, http://micropython.org/ |
3 | * |
4 | * The MIT License (MIT) |
5 | * |
6 | * Copyright (c) 2013-2015 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 <stdarg.h> |
29 | #include <stdint.h> |
30 | #include <stdio.h> |
31 | #include <string.h> |
32 | |
33 | #include "py/mphal.h" |
34 | #include "py/mpprint.h" |
35 | #include "py/obj.h" |
36 | #include "py/objint.h" |
37 | #include "py/runtime.h" |
38 | |
39 | #if MICROPY_PY_BUILTINS_FLOAT |
40 | #include "py/formatfloat.h" |
41 | #endif |
42 | |
43 | static const char pad_spaces[] = " " ; |
44 | static const char pad_zeroes[] = "0000000000000000" ; |
45 | |
46 | STATIC void plat_print_strn(void *env, const char *str, size_t len) { |
47 | (void)env; |
48 | MP_PLAT_PRINT_STRN(str, len); |
49 | } |
50 | |
51 | const mp_print_t mp_plat_print = {NULL, plat_print_strn}; |
52 | |
53 | int mp_print_str(const mp_print_t *print, const char *str) { |
54 | size_t len = strlen(str); |
55 | if (len) { |
56 | print->print_strn(print->data, str, len); |
57 | } |
58 | return len; |
59 | } |
60 | |
61 | int mp_print_strn(const mp_print_t *print, const char *str, size_t len, int flags, char fill, int width) { |
62 | int left_pad = 0; |
63 | int right_pad = 0; |
64 | int pad = width - len; |
65 | int pad_size; |
66 | int total_chars_printed = 0; |
67 | const char *pad_chars; |
68 | |
69 | if (!fill || fill == ' ') { |
70 | pad_chars = pad_spaces; |
71 | pad_size = sizeof(pad_spaces) - 1; |
72 | } else if (fill == '0') { |
73 | pad_chars = pad_zeroes; |
74 | pad_size = sizeof(pad_zeroes) - 1; |
75 | } else { |
76 | // Other pad characters are fairly unusual, so we'll take the hit |
77 | // and output them 1 at a time. |
78 | pad_chars = &fill; |
79 | pad_size = 1; |
80 | } |
81 | |
82 | if (flags & PF_FLAG_CENTER_ADJUST) { |
83 | left_pad = pad / 2; |
84 | right_pad = pad - left_pad; |
85 | } else if (flags & PF_FLAG_LEFT_ADJUST) { |
86 | right_pad = pad; |
87 | } else { |
88 | left_pad = pad; |
89 | } |
90 | |
91 | if (left_pad > 0) { |
92 | total_chars_printed += left_pad; |
93 | while (left_pad > 0) { |
94 | int p = left_pad; |
95 | if (p > pad_size) { |
96 | p = pad_size; |
97 | } |
98 | print->print_strn(print->data, pad_chars, p); |
99 | left_pad -= p; |
100 | } |
101 | } |
102 | if (len) { |
103 | print->print_strn(print->data, str, len); |
104 | total_chars_printed += len; |
105 | } |
106 | if (right_pad > 0) { |
107 | total_chars_printed += right_pad; |
108 | while (right_pad > 0) { |
109 | int p = right_pad; |
110 | if (p > pad_size) { |
111 | p = pad_size; |
112 | } |
113 | print->print_strn(print->data, pad_chars, p); |
114 | right_pad -= p; |
115 | } |
116 | } |
117 | return total_chars_printed; |
118 | } |
119 | |
120 | // 32-bits is 10 digits, add 3 for commas, 1 for sign, 1 for terminating null |
121 | // We can use 16 characters for 32-bit and 32 characters for 64-bit |
122 | #define INT_BUF_SIZE (sizeof(mp_int_t) * 4) |
123 | |
124 | // Our mp_vprintf function below does not support the '#' format modifier to |
125 | // print the prefix of a non-base-10 number, so we don't need code for this. |
126 | #define SUPPORT_INT_BASE_PREFIX (0) |
127 | |
128 | // This function is used exclusively by mp_vprintf to format ints. |
129 | // It needs to be a separate function to mp_print_mp_int, since converting to a mp_int looses the MSB. |
130 | STATIC int mp_print_int(const mp_print_t *print, mp_uint_t x, int sgn, int base, int base_char, int flags, char fill, int width) { |
131 | char sign = 0; |
132 | if (sgn) { |
133 | if ((mp_int_t)x < 0) { |
134 | sign = '-'; |
135 | x = -x; |
136 | } else if (flags & PF_FLAG_SHOW_SIGN) { |
137 | sign = '+'; |
138 | } else if (flags & PF_FLAG_SPACE_SIGN) { |
139 | sign = ' '; |
140 | } |
141 | } |
142 | |
143 | char buf[INT_BUF_SIZE]; |
144 | char *b = buf + INT_BUF_SIZE; |
145 | |
146 | if (x == 0) { |
147 | *(--b) = '0'; |
148 | } else { |
149 | do { |
150 | int c = x % base; |
151 | x /= base; |
152 | if (c >= 10) { |
153 | c += base_char - 10; |
154 | } else { |
155 | c += '0'; |
156 | } |
157 | *(--b) = c; |
158 | } while (b > buf && x != 0); |
159 | } |
160 | |
161 | #if SUPPORT_INT_BASE_PREFIX |
162 | char prefix_char = '\0'; |
163 | |
164 | if (flags & PF_FLAG_SHOW_PREFIX) { |
165 | if (base == 2) { |
166 | prefix_char = base_char + 'b' - 'a'; |
167 | } else if (base == 8) { |
168 | prefix_char = base_char + 'o' - 'a'; |
169 | } else if (base == 16) { |
170 | prefix_char = base_char + 'x' - 'a'; |
171 | } |
172 | } |
173 | #endif |
174 | |
175 | int len = 0; |
176 | if (flags & PF_FLAG_PAD_AFTER_SIGN) { |
177 | if (sign) { |
178 | len += mp_print_strn(print, &sign, 1, flags, fill, 1); |
179 | width--; |
180 | } |
181 | #if SUPPORT_INT_BASE_PREFIX |
182 | if (prefix_char) { |
183 | len += mp_print_strn(print, "0" , 1, flags, fill, 1); |
184 | len += mp_print_strn(print, &prefix_char, 1, flags, fill, 1); |
185 | width -= 2; |
186 | } |
187 | #endif |
188 | } else { |
189 | #if SUPPORT_INT_BASE_PREFIX |
190 | if (prefix_char && b > &buf[1]) { |
191 | *(--b) = prefix_char; |
192 | *(--b) = '0'; |
193 | } |
194 | #endif |
195 | if (sign && b > buf) { |
196 | *(--b) = sign; |
197 | } |
198 | } |
199 | |
200 | len += mp_print_strn(print, b, buf + INT_BUF_SIZE - b, flags, fill, width); |
201 | return len; |
202 | } |
203 | |
204 | int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, int base, int base_char, int flags, char fill, int width, int prec) { |
205 | // These are the only values for "base" that are required to be supported by this |
206 | // function, since Python only allows the user to format integers in these bases. |
207 | // If needed this function could be generalised to handle other values. |
208 | assert(base == 2 || base == 8 || base == 10 || base == 16); |
209 | |
210 | if (!mp_obj_is_int(x)) { |
211 | // This will convert booleans to int, or raise an error for |
212 | // non-integer types. |
213 | x = MP_OBJ_NEW_SMALL_INT(mp_obj_get_int(x)); |
214 | } |
215 | |
216 | if ((flags & (PF_FLAG_LEFT_ADJUST | PF_FLAG_CENTER_ADJUST)) == 0 && fill == '0') { |
217 | if (prec > width) { |
218 | width = prec; |
219 | } |
220 | prec = 0; |
221 | } |
222 | char prefix_buf[4]; |
223 | char *prefix = prefix_buf; |
224 | |
225 | if (mp_obj_int_sign(x) >= 0) { |
226 | if (flags & PF_FLAG_SHOW_SIGN) { |
227 | *prefix++ = '+'; |
228 | } else if (flags & PF_FLAG_SPACE_SIGN) { |
229 | *prefix++ = ' '; |
230 | } |
231 | } |
232 | |
233 | if (flags & PF_FLAG_SHOW_PREFIX) { |
234 | if (base == 2) { |
235 | *prefix++ = '0'; |
236 | *prefix++ = base_char + 'b' - 'a'; |
237 | } else if (base == 8) { |
238 | *prefix++ = '0'; |
239 | if (flags & PF_FLAG_SHOW_OCTAL_LETTER) { |
240 | *prefix++ = base_char + 'o' - 'a'; |
241 | } |
242 | } else if (base == 16) { |
243 | *prefix++ = '0'; |
244 | *prefix++ = base_char + 'x' - 'a'; |
245 | } |
246 | } |
247 | *prefix = '\0'; |
248 | int prefix_len = prefix - prefix_buf; |
249 | prefix = prefix_buf; |
250 | |
251 | char comma = '\0'; |
252 | if (flags & PF_FLAG_SHOW_COMMA) { |
253 | comma = ','; |
254 | } |
255 | |
256 | // The size of this buffer is rather arbitrary. If it's not large |
257 | // enough, a dynamic one will be allocated. |
258 | char stack_buf[sizeof(mp_int_t) * 4]; |
259 | char *buf = stack_buf; |
260 | size_t buf_size = sizeof(stack_buf); |
261 | size_t fmt_size = 0; |
262 | char *str; |
263 | |
264 | if (prec > 1) { |
265 | flags |= PF_FLAG_PAD_AFTER_SIGN; |
266 | } |
267 | char sign = '\0'; |
268 | if (flags & PF_FLAG_PAD_AFTER_SIGN) { |
269 | // We add the pad in this function, so since the pad goes after |
270 | // the sign & prefix, we format without a prefix |
271 | str = mp_obj_int_formatted(&buf, &buf_size, &fmt_size, |
272 | x, base, NULL, base_char, comma); |
273 | if (*str == '-') { |
274 | sign = *str++; |
275 | fmt_size--; |
276 | } |
277 | } else { |
278 | str = mp_obj_int_formatted(&buf, &buf_size, &fmt_size, |
279 | x, base, prefix, base_char, comma); |
280 | } |
281 | |
282 | int spaces_before = 0; |
283 | int spaces_after = 0; |
284 | |
285 | if (prec > 1) { |
286 | // If prec was specified, then prec specifies the width to zero-pad the |
287 | // the number to. This zero-padded number then gets left or right |
288 | // aligned in width characters. |
289 | |
290 | int prec_width = fmt_size; // The digits |
291 | if (prec_width < prec) { |
292 | prec_width = prec; |
293 | } |
294 | if (flags & PF_FLAG_PAD_AFTER_SIGN) { |
295 | if (sign) { |
296 | prec_width++; |
297 | } |
298 | prec_width += prefix_len; |
299 | } |
300 | if (prec_width < width) { |
301 | if (flags & PF_FLAG_LEFT_ADJUST) { |
302 | spaces_after = width - prec_width; |
303 | } else { |
304 | spaces_before = width - prec_width; |
305 | } |
306 | } |
307 | fill = '0'; |
308 | flags &= ~PF_FLAG_LEFT_ADJUST; |
309 | } |
310 | |
311 | int len = 0; |
312 | if (spaces_before) { |
313 | len += mp_print_strn(print, "" , 0, 0, ' ', spaces_before); |
314 | } |
315 | if (flags & PF_FLAG_PAD_AFTER_SIGN) { |
316 | // pad after sign implies pad after prefix as well. |
317 | if (sign) { |
318 | len += mp_print_strn(print, &sign, 1, 0, 0, 1); |
319 | width--; |
320 | } |
321 | if (prefix_len) { |
322 | len += mp_print_strn(print, prefix, prefix_len, 0, 0, 1); |
323 | width -= prefix_len; |
324 | } |
325 | } |
326 | if (prec > 1) { |
327 | width = prec; |
328 | } |
329 | |
330 | len += mp_print_strn(print, str, fmt_size, flags, fill, width); |
331 | |
332 | if (spaces_after) { |
333 | len += mp_print_strn(print, "" , 0, 0, ' ', spaces_after); |
334 | } |
335 | |
336 | if (buf != stack_buf) { |
337 | m_del(char, buf, buf_size); |
338 | } |
339 | return len; |
340 | } |
341 | |
342 | #if MICROPY_PY_BUILTINS_FLOAT |
343 | int mp_print_float(const mp_print_t *print, mp_float_t f, char fmt, int flags, char fill, int width, int prec) { |
344 | char buf[32]; |
345 | char sign = '\0'; |
346 | int chrs = 0; |
347 | |
348 | if (flags & PF_FLAG_SHOW_SIGN) { |
349 | sign = '+'; |
350 | } else |
351 | if (flags & PF_FLAG_SPACE_SIGN) { |
352 | sign = ' '; |
353 | } |
354 | |
355 | int len = mp_format_float(f, buf, sizeof(buf), fmt, prec, sign); |
356 | |
357 | char *s = buf; |
358 | |
359 | if ((flags & PF_FLAG_ADD_PERCENT) && (size_t)(len + 1) < sizeof(buf)) { |
360 | buf[len++] = '%'; |
361 | buf[len] = '\0'; |
362 | } |
363 | |
364 | // buf[0] < '0' returns true if the first character is space, + or - |
365 | if ((flags & PF_FLAG_PAD_AFTER_SIGN) && buf[0] < '0') { |
366 | // We have a sign character |
367 | s++; |
368 | chrs += mp_print_strn(print, &buf[0], 1, 0, 0, 1); |
369 | width--; |
370 | len--; |
371 | } |
372 | |
373 | chrs += mp_print_strn(print, s, len, flags, fill, width); |
374 | |
375 | return chrs; |
376 | } |
377 | #endif |
378 | |
379 | int mp_printf(const mp_print_t *print, const char *fmt, ...) { |
380 | va_list ap; |
381 | va_start(ap, fmt); |
382 | int ret = mp_vprintf(print, fmt, ap); |
383 | va_end(ap); |
384 | return ret; |
385 | } |
386 | |
387 | int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { |
388 | int chrs = 0; |
389 | for (;;) { |
390 | { |
391 | const char *f = fmt; |
392 | while (*f != '\0' && *f != '%') { |
393 | ++f; // XXX UTF8 advance char |
394 | } |
395 | if (f > fmt) { |
396 | print->print_strn(print->data, fmt, f - fmt); |
397 | chrs += f - fmt; |
398 | fmt = f; |
399 | } |
400 | } |
401 | |
402 | if (*fmt == '\0') { |
403 | break; |
404 | } |
405 | |
406 | // move past % character |
407 | ++fmt; |
408 | |
409 | // parse flags, if they exist |
410 | int flags = 0; |
411 | char fill = ' '; |
412 | while (*fmt != '\0') { |
413 | if (*fmt == '-') { |
414 | flags |= PF_FLAG_LEFT_ADJUST; |
415 | } else if (*fmt == '+') { |
416 | flags |= PF_FLAG_SHOW_SIGN; |
417 | } else if (*fmt == ' ') { |
418 | flags |= PF_FLAG_SPACE_SIGN; |
419 | } else if (*fmt == '!') { |
420 | flags |= PF_FLAG_NO_TRAILZ; |
421 | } else if (*fmt == '0') { |
422 | flags |= PF_FLAG_PAD_AFTER_SIGN; |
423 | fill = '0'; |
424 | } else { |
425 | break; |
426 | } |
427 | ++fmt; |
428 | } |
429 | |
430 | // parse width, if it exists |
431 | int width = 0; |
432 | for (; '0' <= *fmt && *fmt <= '9'; ++fmt) { |
433 | width = width * 10 + *fmt - '0'; |
434 | } |
435 | |
436 | // parse precision, if it exists |
437 | int prec = -1; |
438 | if (*fmt == '.') { |
439 | ++fmt; |
440 | if (*fmt == '*') { |
441 | ++fmt; |
442 | prec = va_arg(args, int); |
443 | } else { |
444 | prec = 0; |
445 | for (; '0' <= *fmt && *fmt <= '9'; ++fmt) { |
446 | prec = prec * 10 + *fmt - '0'; |
447 | } |
448 | } |
449 | if (prec < 0) { |
450 | prec = 0; |
451 | } |
452 | } |
453 | |
454 | // parse long specifiers (only for LP64 model where they make a difference) |
455 | #ifndef __LP64__ |
456 | const |
457 | #endif |
458 | bool long_arg = false; |
459 | if (*fmt == 'l') { |
460 | ++fmt; |
461 | #ifdef __LP64__ |
462 | long_arg = true; |
463 | #endif |
464 | } |
465 | |
466 | if (*fmt == '\0') { |
467 | break; |
468 | } |
469 | |
470 | switch (*fmt) { |
471 | case 'b': |
472 | if (va_arg(args, int)) { |
473 | chrs += mp_print_strn(print, "true" , 4, flags, fill, width); |
474 | } else { |
475 | chrs += mp_print_strn(print, "false" , 5, flags, fill, width); |
476 | } |
477 | break; |
478 | case 'c': { |
479 | char str = va_arg(args, int); |
480 | chrs += mp_print_strn(print, &str, 1, flags, fill, width); |
481 | break; |
482 | } |
483 | case 'q': { |
484 | qstr qst = va_arg(args, qstr); |
485 | size_t len; |
486 | const char *str = (const char *)qstr_data(qst, &len); |
487 | if (prec >= 0 && (size_t)prec < len) { |
488 | len = prec; |
489 | } |
490 | chrs += mp_print_strn(print, str, len, flags, fill, width); |
491 | break; |
492 | } |
493 | case 's': { |
494 | const char *str = va_arg(args, const char *); |
495 | #ifndef NDEBUG |
496 | // With debugging enabled, catch printing of null string pointers |
497 | if (prec != 0 && str == NULL) { |
498 | chrs += mp_print_strn(print, "(null)" , 6, flags, fill, width); |
499 | break; |
500 | } |
501 | #endif |
502 | size_t len = strlen(str); |
503 | if (prec >= 0 && (size_t)prec < len) { |
504 | len = prec; |
505 | } |
506 | chrs += mp_print_strn(print, str, len, flags, fill, width); |
507 | break; |
508 | } |
509 | case 'd': { |
510 | mp_int_t val; |
511 | if (long_arg) { |
512 | val = va_arg(args, long int); |
513 | } else { |
514 | val = va_arg(args, int); |
515 | } |
516 | chrs += mp_print_int(print, val, 1, 10, 'a', flags, fill, width); |
517 | break; |
518 | } |
519 | case 'u': |
520 | case 'x': |
521 | case 'X': { |
522 | int base = 16 - ((*fmt + 1) & 6); // maps char u/x/X to base 10/16/16 |
523 | char fmt_c = (*fmt & 0xf0) - 'P' + 'A'; // maps char u/x/X to char a/a/A |
524 | mp_uint_t val; |
525 | if (long_arg) { |
526 | val = va_arg(args, unsigned long int); |
527 | } else { |
528 | val = va_arg(args, unsigned int); |
529 | } |
530 | chrs += mp_print_int(print, val, 0, base, fmt_c, flags, fill, width); |
531 | break; |
532 | } |
533 | case 'p': |
534 | case 'P': // don't bother to handle upcase for 'P' |
535 | // Use unsigned long int to work on both ILP32 and LP64 systems |
536 | chrs += mp_print_int(print, va_arg(args, unsigned long int), 0, 16, 'a', flags, fill, width); |
537 | break; |
538 | #if MICROPY_PY_BUILTINS_FLOAT |
539 | case 'e': |
540 | case 'E': |
541 | case 'f': |
542 | case 'F': |
543 | case 'g': |
544 | case 'G': { |
545 | #if ((MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT) || (MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE)) |
546 | mp_float_t f = (mp_float_t)va_arg(args, double); |
547 | chrs += mp_print_float(print, f, *fmt, flags, fill, width, prec); |
548 | #else |
549 | #error Unknown MICROPY FLOAT IMPL |
550 | #endif |
551 | break; |
552 | } |
553 | #endif |
554 | // Because 'l' is eaten above, another 'l' means %ll. We need to support |
555 | // this length specifier for OBJ_REPR_D (64-bit NaN boxing). |
556 | // TODO Either enable this unconditionally, or provide a specific config var. |
557 | #if (MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D) || defined(_WIN64) |
558 | case 'l': { |
559 | unsigned long long int arg_value = va_arg(args, unsigned long long int); |
560 | ++fmt; |
561 | assert(*fmt == 'u' || *fmt == 'd' || !"unsupported fmt char" ); |
562 | chrs += mp_print_int(print, arg_value, *fmt == 'd', 10, 'a', flags, fill, width); |
563 | break; |
564 | } |
565 | #endif |
566 | default: |
567 | // if it's not %% then it's an unsupported format character |
568 | assert(*fmt == '%' || !"unsupported fmt char" ); |
569 | print->print_strn(print->data, fmt, 1); |
570 | chrs += 1; |
571 | break; |
572 | } |
573 | ++fmt; |
574 | } |
575 | return chrs; |
576 | } |
577 | |