blob: 22274a2ad7f503e7e30dba37f5bf447227a2596a [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
Damien Georgefa1edff2015-03-14 22:32:40 +0000181int pfenv_print_mp_int(const pfenv_t *pfenv, mp_obj_t x, int base, int base_char, int flags, char fill, int width, int prec) {
Dave Hylandsc4029e52014-04-07 11:19:51 -0700182 if (!MP_OBJ_IS_INT(x)) {
183 // This will convert booleans to int, or raise an error for
184 // non-integer types.
185 x = MP_OBJ_NEW_SMALL_INT(mp_obj_get_int(x));
186 }
187
Dave Hylandsb69f9fa2014-06-05 23:09:02 -0700188 if ((flags & (PF_FLAG_LEFT_ADJUST | PF_FLAG_CENTER_ADJUST)) == 0 && fill == '0') {
189 if (prec > width) {
190 width = prec;
191 }
192 prec = 0;
193 }
Dave Hylandsc4029e52014-04-07 11:19:51 -0700194 char prefix_buf[4];
195 char *prefix = prefix_buf;
196
197 if (mp_obj_int_is_positive(x)) {
198 if (flags & PF_FLAG_SHOW_SIGN) {
199 *prefix++ = '+';
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700200 } else if (flags & PF_FLAG_SPACE_SIGN) {
Dave Hylandsc4029e52014-04-07 11:19:51 -0700201 *prefix++ = ' ';
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700202 }
203 }
204
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700205 if (flags & PF_FLAG_SHOW_PREFIX) {
206 if (base == 2) {
Dave Hylandsc4029e52014-04-07 11:19:51 -0700207 *prefix++ = '0';
208 *prefix++ = base_char + 'b' - 'a';
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700209 } else if (base == 8) {
Dave Hylandsc4029e52014-04-07 11:19:51 -0700210 *prefix++ = '0';
211 if (flags & PF_FLAG_SHOW_OCTAL_LETTER) {
212 *prefix++ = base_char + 'o' - 'a';
213 }
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700214 } else if (base == 16) {
Dave Hylandsc4029e52014-04-07 11:19:51 -0700215 *prefix++ = '0';
216 *prefix++ = base_char + 'x' - 'a';
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700217 }
218 }
Dave Hylandsc4029e52014-04-07 11:19:51 -0700219 *prefix = '\0';
220 int prefix_len = prefix - prefix_buf;
221 prefix = prefix_buf;
222
223 char comma = '\0';
224 if (flags & PF_FLAG_SHOW_COMMA) {
225 comma = ',';
226 }
227
228 // The size of this buffer is rather arbitrary. If it's not large
229 // enough, a dynamic one will be allocated.
Damien George40f3c022014-07-03 13:25:24 +0100230 char stack_buf[sizeof(mp_int_t) * 4];
Dave Hylandsc4029e52014-04-07 11:19:51 -0700231 char *buf = stack_buf;
Damien George42f3de92014-10-03 17:44:14 +0000232 mp_uint_t buf_size = sizeof(stack_buf);
233 mp_uint_t fmt_size = 0;
Dave Hylandsc4029e52014-04-07 11:19:51 -0700234 char *str;
235
Dave Hylandsb69f9fa2014-06-05 23:09:02 -0700236 if (prec > 1) {
237 flags |= PF_FLAG_PAD_AFTER_SIGN;
238 }
Dave Hylandsc4029e52014-04-07 11:19:51 -0700239 char sign = '\0';
240 if (flags & PF_FLAG_PAD_AFTER_SIGN) {
241 // We add the pad in this function, so since the pad goes after
242 // the sign & prefix, we format without a prefix
243 str = mp_obj_int_formatted(&buf, &buf_size, &fmt_size,
244 x, base, NULL, base_char, comma);
245 if (*str == '-') {
246 sign = *str++;
247 fmt_size--;
248 }
249 } else {
250 str = mp_obj_int_formatted(&buf, &buf_size, &fmt_size,
251 x, base, prefix, base_char, comma);
252 }
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700253
Dave Hylandsb69f9fa2014-06-05 23:09:02 -0700254 int spaces_before = 0;
255 int spaces_after = 0;
256
257 if (prec > 1) {
258 // If prec was specified, then prec specifies the width to zero-pad the
259 // the number to. This zero-padded number then gets left or right
260 // aligned in width characters.
261
262 int prec_width = fmt_size; // The digits
263 if (prec_width < prec) {
264 prec_width = prec;
265 }
266 if (flags & PF_FLAG_PAD_AFTER_SIGN) {
267 if (sign) {
268 prec_width++;
269 }
270 prec_width += prefix_len;
271 }
272 if (prec_width < width) {
273 if (flags & PF_FLAG_LEFT_ADJUST) {
274 spaces_after = width - prec_width;
275 } else {
276 spaces_before = width - prec_width;
277 }
278 }
279 fill = '0';
280 flags &= ~PF_FLAG_LEFT_ADJUST;
281 }
282
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700283 int len = 0;
Dave Hylandsb69f9fa2014-06-05 23:09:02 -0700284 if (spaces_before) {
285 len += pfenv_print_strn(pfenv, "", 0, 0, ' ', spaces_before);
286 }
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700287 if (flags & PF_FLAG_PAD_AFTER_SIGN) {
Dave Hylandsc4029e52014-04-07 11:19:51 -0700288 // pad after sign implies pad after prefix as well.
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700289 if (sign) {
Dave Hylandsc4029e52014-04-07 11:19:51 -0700290 len += pfenv_print_strn(pfenv, &sign, 1, 0, 0, 1);
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700291 width--;
292 }
Dave Hylandsc4029e52014-04-07 11:19:51 -0700293 if (prefix_len) {
294 len += pfenv_print_strn(pfenv, prefix, prefix_len, 0, 0, 1);
295 width -= prefix_len;
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700296 }
297 }
Dave Hylandsb69f9fa2014-06-05 23:09:02 -0700298 if (prec > 1) {
299 width = prec;
300 }
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700301
Dave Hylandsc4029e52014-04-07 11:19:51 -0700302 len += pfenv_print_strn(pfenv, str, fmt_size, flags, fill, width);
303
Dave Hylandsb69f9fa2014-06-05 23:09:02 -0700304 if (spaces_after) {
305 len += pfenv_print_strn(pfenv, "", 0, 0, ' ', spaces_after);
306 }
307
Dave Hylandsc4029e52014-04-07 11:19:51 -0700308 if (buf != stack_buf) {
Damien George4d77e1a2015-02-27 09:34:51 +0000309 m_del(char, buf, buf_size);
Dave Hylandsc4029e52014-04-07 11:19:51 -0700310 }
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700311 return len;
312}
313
Damien Georgefb510b32014-06-01 13:32:54 +0100314#if MICROPY_PY_BUILTINS_FLOAT
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700315int pfenv_print_float(const pfenv_t *pfenv, mp_float_t f, char fmt, int flags, char fill, int width, int prec) {
316 char buf[32];
317 char sign = '\0';
318 int chrs = 0;
319
320 if (flags & PF_FLAG_SHOW_SIGN) {
321 sign = '+';
322 }
323 else
324 if (flags & PF_FLAG_SPACE_SIGN) {
325 sign = ' ';
326 }
327 int len;
328#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
Damien George51dfcb42015-01-01 20:27:54 +0000329 len = mp_format_float(f, buf, sizeof(buf), fmt, prec, sign);
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700330#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
331 char fmt_buf[6];
332 char *fmt_s = fmt_buf;
333
334 *fmt_s++ = '%';
335 if (sign) {
336 *fmt_s++ = sign;
337 }
338 *fmt_s++ = '.';
339 *fmt_s++ = '*';
340 *fmt_s++ = fmt;
341 *fmt_s = '\0';
342
343 len = snprintf(buf, sizeof(buf), fmt_buf, prec, f);
Damien George963a5a32015-01-16 17:47:07 +0000344 if (len < 0) {
345 len = 0;
346 }
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700347#else
348#error Unknown MICROPY FLOAT IMPL
349#endif
350 char *s = buf;
351
Damien George963a5a32015-01-16 17:47:07 +0000352 if ((flags & PF_FLAG_ADD_PERCENT) && (size_t)(len + 1) < sizeof(buf)) {
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700353 buf[len++] = '%';
354 buf[len] = '\0';
355 }
356
357 // buf[0] < '0' returns true if the first character is space, + or -
358 if ((flags & PF_FLAG_PAD_AFTER_SIGN) && buf[0] < '0') {
359 // We have a sign character
360 s++;
361 if (*s <= '9' || (flags & PF_FLAG_PAD_NAN_INF)) {
362 // We have a number, or we have a inf/nan and PAD_NAN_INF is set
363 // With '{:06e}'.format(float('-inf')) you get '-00inf'
364 chrs += pfenv_print_strn(pfenv, &buf[0], 1, 0, 0, 1);
365 width--;
366 len--;
367 }
368 }
369
370 if (*s > 'A' && (flags & PF_FLAG_PAD_NAN_INF) == 0) {
371 // We have one of the inf or nan variants, suppress zero fill.
372 // With printf, if you use: printf("%06e", -inf) then you get " -inf"
373 // so suppress the zero fill.
374 fill = ' ';
375 }
Damien Georgee3e05002014-04-01 21:15:03 +0100376 chrs += pfenv_print_strn(pfenv, s, len, flags, fill, width);
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700377
378 return chrs;
379}
380#endif