blob: 8bc42dcdf99a7ff90778ea5d34a4b7aa7a3cf25a [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
Dave Hylandsbaf6f142014-03-30 21:06:50 -070027#include <stdint.h>
28#include <string.h>
29
Damien George51dfcb42015-01-01 20:27:54 +000030#include "py/objint.h"
31#include "py/pfenv.h"
Dave Hylandsbaf6f142014-03-30 21:06:50 -070032
33#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
34#include <stdio.h>
35#endif
36
Damien Georgefb510b32014-06-01 13:32:54 +010037#if MICROPY_PY_BUILTINS_FLOAT
Damien George51dfcb42015-01-01 20:27:54 +000038#include "py/formatfloat.h"
Dave Hylandsbaf6f142014-03-30 21:06:50 -070039#endif
40
Dave Hylands1c6b4b22014-04-01 11:59:31 -070041static const char pad_spaces[] = " ";
42static const char pad_zeroes[] = "0000000000000000";
Dave Hylandsbaf6f142014-03-30 21:06:50 -070043
Damien George42f3de92014-10-03 17:44:14 +000044void pfenv_vstr_add_strn(void *data, const char *str, mp_uint_t len){
Dave Hylandsbaf6f142014-03-30 21:06:50 -070045 vstr_add_strn(data, str, len);
46}
47
Damien George42f3de92014-10-03 17:44:14 +000048int pfenv_print_strn(const pfenv_t *pfenv, const char *str, mp_uint_t len, int flags, char fill, int width) {
Dave Hylandsbaf6f142014-03-30 21:06:50 -070049 int left_pad = 0;
50 int right_pad = 0;
51 int pad = width - len;
Dave Hylands1c6b4b22014-04-01 11:59:31 -070052 int pad_size;
Damien Georged0f9f6c2014-04-17 18:58:09 +010053 int total_chars_printed = 0;
Dave Hylandsbaf6f142014-03-30 21:06:50 -070054 const char *pad_chars;
55
56 if (!fill || fill == ' ' ) {
57 pad_chars = pad_spaces;
Dave Hylands1c6b4b22014-04-01 11:59:31 -070058 pad_size = sizeof(pad_spaces) - 1;
Dave Hylandsbaf6f142014-03-30 21:06:50 -070059 } else if (fill == '0') {
60 pad_chars = pad_zeroes;
Dave Hylands1c6b4b22014-04-01 11:59:31 -070061 pad_size = sizeof(pad_zeroes) - 1;
Dave Hylandsbaf6f142014-03-30 21:06:50 -070062 } else {
Dave Hylands1c6b4b22014-04-01 11:59:31 -070063 // Other pad characters are fairly unusual, so we'll take the hit
64 // and output them 1 at a time.
65 pad_chars = &fill;
66 pad_size = 1;
Dave Hylandsbaf6f142014-03-30 21:06:50 -070067 }
68
69 if (flags & PF_FLAG_CENTER_ADJUST) {
70 left_pad = pad / 2;
71 right_pad = pad - left_pad;
72 } else if (flags & PF_FLAG_LEFT_ADJUST) {
73 right_pad = pad;
74 } else {
75 left_pad = pad;
76 }
77
Damien Georged0f9f6c2014-04-17 18:58:09 +010078 if (left_pad > 0) {
79 total_chars_printed += left_pad;
Dave Hylandsbaf6f142014-03-30 21:06:50 -070080 while (left_pad > 0) {
81 int p = left_pad;
Damien Georgee3e05002014-04-01 21:15:03 +010082 if (p > pad_size) {
Dave Hylands1c6b4b22014-04-01 11:59:31 -070083 p = pad_size;
Damien Georgee3e05002014-04-01 21:15:03 +010084 }
Dave Hylandsbaf6f142014-03-30 21:06:50 -070085 pfenv->print_strn(pfenv->data, pad_chars, p);
86 left_pad -= p;
87 }
88 }
Dave Hylandsb69f9fa2014-06-05 23:09:02 -070089 if (len) {
90 pfenv->print_strn(pfenv->data, str, len);
91 total_chars_printed += len;
92 }
Damien Georged0f9f6c2014-04-17 18:58:09 +010093 if (right_pad > 0) {
94 total_chars_printed += right_pad;
Dave Hylandsbaf6f142014-03-30 21:06:50 -070095 while (right_pad > 0) {
96 int p = right_pad;
Damien Georgee3e05002014-04-01 21:15:03 +010097 if (p > pad_size) {
Dave Hylands1c6b4b22014-04-01 11:59:31 -070098 p = pad_size;
Damien Georgee3e05002014-04-01 21:15:03 +010099 }
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700100 pfenv->print_strn(pfenv->data, pad_chars, p);
101 right_pad -= p;
102 }
103 }
Damien Georged0f9f6c2014-04-17 18:58:09 +0100104 return total_chars_printed;
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700105}
106
Dave Hylands80359aa2014-04-01 11:01:55 -0700107// 32-bits is 10 digits, add 3 for commas, 1 for sign, 1 for terminating null
108// We can use 16 characters for 32-bit and 32 characters for 64-bit
Damien George40f3c022014-07-03 13:25:24 +0100109#define INT_BUF_SIZE (sizeof(mp_int_t) * 4)
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700110
Damien George348435d2014-04-08 22:10:37 +0100111// This function is used by stmhal port to implement printf.
112// It needs to be a separate function to pfenv_print_mp_int, since converting to a mp_int looses the MSB.
Damien George40f3c022014-07-03 13:25:24 +0100113int pfenv_print_int(const pfenv_t *pfenv, mp_uint_t x, int sgn, int base, int base_char, int flags, char fill, int width) {
Damien George348435d2014-04-08 22:10:37 +0100114 char sign = 0;
115 if (sgn) {
Damien George40f3c022014-07-03 13:25:24 +0100116 if ((mp_int_t)x < 0) {
Damien George348435d2014-04-08 22:10:37 +0100117 sign = '-';
118 x = -x;
119 } else if (flags & PF_FLAG_SHOW_SIGN) {
120 sign = '+';
121 } else if (flags & PF_FLAG_SPACE_SIGN) {
122 sign = ' ';
123 }
124 }
125
126 char buf[INT_BUF_SIZE];
127 char *b = buf + INT_BUF_SIZE;
128
129 if (x == 0) {
130 *(--b) = '0';
131 } else {
132 do {
133 int c = x % base;
134 x /= base;
135 if (c >= 10) {
136 c += base_char - 10;
137 } else {
138 c += '0';
139 }
140 *(--b) = c;
141 } while (b > buf && x != 0);
142 }
143
144 char prefix_char = '\0';
145
146 if (flags & PF_FLAG_SHOW_PREFIX) {
147 if (base == 2) {
148 prefix_char = base_char + 'b' - 'a';
149 } else if (base == 8) {
150 prefix_char = base_char + 'o' - 'a';
151 } else if (base == 16) {
152 prefix_char = base_char + 'x' - 'a';
153 }
154 }
155
156 int len = 0;
157 if (flags & PF_FLAG_PAD_AFTER_SIGN) {
158 if (sign) {
159 len += pfenv_print_strn(pfenv, &sign, 1, flags, fill, 1);
160 width--;
161 }
162 if (prefix_char) {
163 len += pfenv_print_strn(pfenv, "0", 1, flags, fill, 1);
164 len += pfenv_print_strn(pfenv, &prefix_char, 1, flags, fill, 1);
165 width -= 2;
166 }
167 } else {
168 if (prefix_char && b > &buf[1]) {
169 *(--b) = prefix_char;
170 *(--b) = '0';
171 }
172 if (sign && b > buf) {
173 *(--b) = sign;
174 }
175 }
176
177 len += pfenv_print_strn(pfenv, b, buf + INT_BUF_SIZE - b, flags, fill, width);
178 return len;
179}
180
Dave Hylandsb69f9fa2014-06-05 23:09:02 -0700181int pfenv_print_mp_int(const pfenv_t *pfenv, mp_obj_t x, int sgn, int base, int base_char, int flags, char fill, int width, int prec) {
Damien Georgeff8dd3f2015-01-20 12:47:20 +0000182 (void)sgn; // TODO why is sgn unused?
183
Dave Hylandsc4029e52014-04-07 11:19:51 -0700184 if (!MP_OBJ_IS_INT(x)) {
185 // This will convert booleans to int, or raise an error for
186 // non-integer types.
187 x = MP_OBJ_NEW_SMALL_INT(mp_obj_get_int(x));
188 }
189
Dave Hylandsb69f9fa2014-06-05 23:09:02 -0700190 if ((flags & (PF_FLAG_LEFT_ADJUST | PF_FLAG_CENTER_ADJUST)) == 0 && fill == '0') {
191 if (prec > width) {
192 width = prec;
193 }
194 prec = 0;
195 }
Dave Hylandsc4029e52014-04-07 11:19:51 -0700196 char prefix_buf[4];
197 char *prefix = prefix_buf;
198
199 if (mp_obj_int_is_positive(x)) {
200 if (flags & PF_FLAG_SHOW_SIGN) {
201 *prefix++ = '+';
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700202 } else if (flags & PF_FLAG_SPACE_SIGN) {
Dave Hylandsc4029e52014-04-07 11:19:51 -0700203 *prefix++ = ' ';
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700204 }
205 }
206
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700207 if (flags & PF_FLAG_SHOW_PREFIX) {
208 if (base == 2) {
Dave Hylandsc4029e52014-04-07 11:19:51 -0700209 *prefix++ = '0';
210 *prefix++ = base_char + 'b' - 'a';
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700211 } else if (base == 8) {
Dave Hylandsc4029e52014-04-07 11:19:51 -0700212 *prefix++ = '0';
213 if (flags & PF_FLAG_SHOW_OCTAL_LETTER) {
214 *prefix++ = base_char + 'o' - 'a';
215 }
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700216 } else if (base == 16) {
Dave Hylandsc4029e52014-04-07 11:19:51 -0700217 *prefix++ = '0';
218 *prefix++ = base_char + 'x' - 'a';
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700219 }
220 }
Dave Hylandsc4029e52014-04-07 11:19:51 -0700221 *prefix = '\0';
222 int prefix_len = prefix - prefix_buf;
223 prefix = prefix_buf;
224
225 char comma = '\0';
226 if (flags & PF_FLAG_SHOW_COMMA) {
227 comma = ',';
228 }
229
230 // The size of this buffer is rather arbitrary. If it's not large
231 // enough, a dynamic one will be allocated.
Damien George40f3c022014-07-03 13:25:24 +0100232 char stack_buf[sizeof(mp_int_t) * 4];
Dave Hylandsc4029e52014-04-07 11:19:51 -0700233 char *buf = stack_buf;
Damien George42f3de92014-10-03 17:44:14 +0000234 mp_uint_t buf_size = sizeof(stack_buf);
235 mp_uint_t fmt_size = 0;
Dave Hylandsc4029e52014-04-07 11:19:51 -0700236 char *str;
237
Dave Hylandsb69f9fa2014-06-05 23:09:02 -0700238 if (prec > 1) {
239 flags |= PF_FLAG_PAD_AFTER_SIGN;
240 }
Dave Hylandsc4029e52014-04-07 11:19:51 -0700241 char sign = '\0';
242 if (flags & PF_FLAG_PAD_AFTER_SIGN) {
243 // We add the pad in this function, so since the pad goes after
244 // the sign & prefix, we format without a prefix
245 str = mp_obj_int_formatted(&buf, &buf_size, &fmt_size,
246 x, base, NULL, base_char, comma);
247 if (*str == '-') {
248 sign = *str++;
249 fmt_size--;
250 }
251 } else {
252 str = mp_obj_int_formatted(&buf, &buf_size, &fmt_size,
253 x, base, prefix, base_char, comma);
254 }
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700255
Dave Hylandsb69f9fa2014-06-05 23:09:02 -0700256 int spaces_before = 0;
257 int spaces_after = 0;
258
259 if (prec > 1) {
260 // If prec was specified, then prec specifies the width to zero-pad the
261 // the number to. This zero-padded number then gets left or right
262 // aligned in width characters.
263
264 int prec_width = fmt_size; // The digits
265 if (prec_width < prec) {
266 prec_width = prec;
267 }
268 if (flags & PF_FLAG_PAD_AFTER_SIGN) {
269 if (sign) {
270 prec_width++;
271 }
272 prec_width += prefix_len;
273 }
274 if (prec_width < width) {
275 if (flags & PF_FLAG_LEFT_ADJUST) {
276 spaces_after = width - prec_width;
277 } else {
278 spaces_before = width - prec_width;
279 }
280 }
281 fill = '0';
282 flags &= ~PF_FLAG_LEFT_ADJUST;
283 }
284
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700285 int len = 0;
Dave Hylandsb69f9fa2014-06-05 23:09:02 -0700286 if (spaces_before) {
287 len += pfenv_print_strn(pfenv, "", 0, 0, ' ', spaces_before);
288 }
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700289 if (flags & PF_FLAG_PAD_AFTER_SIGN) {
Dave Hylandsc4029e52014-04-07 11:19:51 -0700290 // pad after sign implies pad after prefix as well.
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700291 if (sign) {
Dave Hylandsc4029e52014-04-07 11:19:51 -0700292 len += pfenv_print_strn(pfenv, &sign, 1, 0, 0, 1);
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700293 width--;
294 }
Dave Hylandsc4029e52014-04-07 11:19:51 -0700295 if (prefix_len) {
296 len += pfenv_print_strn(pfenv, prefix, prefix_len, 0, 0, 1);
297 width -= prefix_len;
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700298 }
299 }
Dave Hylandsb69f9fa2014-06-05 23:09:02 -0700300 if (prec > 1) {
301 width = prec;
302 }
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700303
Dave Hylandsc4029e52014-04-07 11:19:51 -0700304 len += pfenv_print_strn(pfenv, str, fmt_size, flags, fill, width);
305
Dave Hylandsb69f9fa2014-06-05 23:09:02 -0700306 if (spaces_after) {
307 len += pfenv_print_strn(pfenv, "", 0, 0, ' ', spaces_after);
308 }
309
Dave Hylandsc4029e52014-04-07 11:19:51 -0700310 if (buf != stack_buf) {
Damien George4d77e1a2015-02-27 09:34:51 +0000311 m_del(char, buf, buf_size);
Dave Hylandsc4029e52014-04-07 11:19:51 -0700312 }
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700313 return len;
314}
315
Damien Georgefb510b32014-06-01 13:32:54 +0100316#if MICROPY_PY_BUILTINS_FLOAT
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700317int pfenv_print_float(const pfenv_t *pfenv, mp_float_t f, char fmt, int flags, char fill, int width, int prec) {
318 char buf[32];
319 char sign = '\0';
320 int chrs = 0;
321
322 if (flags & PF_FLAG_SHOW_SIGN) {
323 sign = '+';
324 }
325 else
326 if (flags & PF_FLAG_SPACE_SIGN) {
327 sign = ' ';
328 }
329 int len;
330#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
Damien George51dfcb42015-01-01 20:27:54 +0000331 len = mp_format_float(f, buf, sizeof(buf), fmt, prec, sign);
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700332#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
333 char fmt_buf[6];
334 char *fmt_s = fmt_buf;
335
336 *fmt_s++ = '%';
337 if (sign) {
338 *fmt_s++ = sign;
339 }
340 *fmt_s++ = '.';
341 *fmt_s++ = '*';
342 *fmt_s++ = fmt;
343 *fmt_s = '\0';
344
345 len = snprintf(buf, sizeof(buf), fmt_buf, prec, f);
Damien George963a5a32015-01-16 17:47:07 +0000346 if (len < 0) {
347 len = 0;
348 }
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700349#else
350#error Unknown MICROPY FLOAT IMPL
351#endif
352 char *s = buf;
353
Damien George963a5a32015-01-16 17:47:07 +0000354 if ((flags & PF_FLAG_ADD_PERCENT) && (size_t)(len + 1) < sizeof(buf)) {
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700355 buf[len++] = '%';
356 buf[len] = '\0';
357 }
358
359 // buf[0] < '0' returns true if the first character is space, + or -
360 if ((flags & PF_FLAG_PAD_AFTER_SIGN) && buf[0] < '0') {
361 // We have a sign character
362 s++;
363 if (*s <= '9' || (flags & PF_FLAG_PAD_NAN_INF)) {
364 // We have a number, or we have a inf/nan and PAD_NAN_INF is set
365 // With '{:06e}'.format(float('-inf')) you get '-00inf'
366 chrs += pfenv_print_strn(pfenv, &buf[0], 1, 0, 0, 1);
367 width--;
368 len--;
369 }
370 }
371
372 if (*s > 'A' && (flags & PF_FLAG_PAD_NAN_INF) == 0) {
373 // We have one of the inf or nan variants, suppress zero fill.
374 // With printf, if you use: printf("%06e", -inf) then you get " -inf"
375 // so suppress the zero fill.
376 fill = ' ';
377 }
Damien Georgee3e05002014-04-01 21:15:03 +0100378 chrs += pfenv_print_strn(pfenv, s, len, flags, fill, width);
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700379
380 return chrs;
381}
382#endif