blob: 92c40184154704ddaefed227f100c3ceedf36096 [file] [log] [blame]
Dave Hylandsbaf6f142014-03-30 21:06:50 -07001#include <stdint.h>
2#include <string.h>
3
Dave Hylandsbaf6f142014-03-30 21:06:50 -07004#include "misc.h"
5#include "mpconfig.h"
6#include "qstr.h"
7#include "obj.h"
Dave Hylandsc4029e52014-04-07 11:19:51 -07008#include "mpz.h"
9#include "objint.h"
Dave Hylandsbaf6f142014-03-30 21:06:50 -070010#include "pfenv.h"
11
12#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
13#include <stdio.h>
14#endif
15
16#if MICROPY_ENABLE_FLOAT
17#include "formatfloat.h"
18#endif
19
Dave Hylands1c6b4b22014-04-01 11:59:31 -070020static const char pad_spaces[] = " ";
21static const char pad_zeroes[] = "0000000000000000";
Dave Hylandsbaf6f142014-03-30 21:06:50 -070022
23void pfenv_vstr_add_strn(void *data, const char *str, unsigned int len){
24 vstr_add_strn(data, str, len);
25}
26
27int pfenv_print_strn(const pfenv_t *pfenv, const char *str, unsigned int len, int flags, char fill, int width) {
28 int left_pad = 0;
29 int right_pad = 0;
30 int pad = width - len;
Dave Hylands1c6b4b22014-04-01 11:59:31 -070031 int pad_size;
Damien Georged0f9f6c2014-04-17 18:58:09 +010032 int total_chars_printed = 0;
Dave Hylandsbaf6f142014-03-30 21:06:50 -070033 const char *pad_chars;
34
35 if (!fill || fill == ' ' ) {
36 pad_chars = pad_spaces;
Dave Hylands1c6b4b22014-04-01 11:59:31 -070037 pad_size = sizeof(pad_spaces) - 1;
Dave Hylandsbaf6f142014-03-30 21:06:50 -070038 } else if (fill == '0') {
39 pad_chars = pad_zeroes;
Dave Hylands1c6b4b22014-04-01 11:59:31 -070040 pad_size = sizeof(pad_zeroes) - 1;
Dave Hylandsbaf6f142014-03-30 21:06:50 -070041 } else {
Dave Hylands1c6b4b22014-04-01 11:59:31 -070042 // Other pad characters are fairly unusual, so we'll take the hit
43 // and output them 1 at a time.
44 pad_chars = &fill;
45 pad_size = 1;
Dave Hylandsbaf6f142014-03-30 21:06:50 -070046 }
47
48 if (flags & PF_FLAG_CENTER_ADJUST) {
49 left_pad = pad / 2;
50 right_pad = pad - left_pad;
51 } else if (flags & PF_FLAG_LEFT_ADJUST) {
52 right_pad = pad;
53 } else {
54 left_pad = pad;
55 }
56
Damien Georged0f9f6c2014-04-17 18:58:09 +010057 if (left_pad > 0) {
58 total_chars_printed += left_pad;
Dave Hylandsbaf6f142014-03-30 21:06:50 -070059 while (left_pad > 0) {
60 int p = left_pad;
Damien Georgee3e05002014-04-01 21:15:03 +010061 if (p > pad_size) {
Dave Hylands1c6b4b22014-04-01 11:59:31 -070062 p = pad_size;
Damien Georgee3e05002014-04-01 21:15:03 +010063 }
Dave Hylandsbaf6f142014-03-30 21:06:50 -070064 pfenv->print_strn(pfenv->data, pad_chars, p);
65 left_pad -= p;
66 }
67 }
68 pfenv->print_strn(pfenv->data, str, len);
Damien Georged0f9f6c2014-04-17 18:58:09 +010069 total_chars_printed += len;
70 if (right_pad > 0) {
71 total_chars_printed += right_pad;
Dave Hylandsbaf6f142014-03-30 21:06:50 -070072 while (right_pad > 0) {
73 int p = right_pad;
Damien Georgee3e05002014-04-01 21:15:03 +010074 if (p > pad_size) {
Dave Hylands1c6b4b22014-04-01 11:59:31 -070075 p = pad_size;
Damien Georgee3e05002014-04-01 21:15:03 +010076 }
Dave Hylandsbaf6f142014-03-30 21:06:50 -070077 pfenv->print_strn(pfenv->data, pad_chars, p);
78 right_pad -= p;
79 }
80 }
Damien Georged0f9f6c2014-04-17 18:58:09 +010081 return total_chars_printed;
Dave Hylandsbaf6f142014-03-30 21:06:50 -070082}
83
Dave Hylands80359aa2014-04-01 11:01:55 -070084// 32-bits is 10 digits, add 3 for commas, 1 for sign, 1 for terminating null
85// We can use 16 characters for 32-bit and 32 characters for 64-bit
86#define INT_BUF_SIZE (sizeof(machine_int_t) * 4)
Dave Hylandsbaf6f142014-03-30 21:06:50 -070087
Damien George348435d2014-04-08 22:10:37 +010088// This function is used by stmhal port to implement printf.
89// It needs to be a separate function to pfenv_print_mp_int, since converting to a mp_int looses the MSB.
90int pfenv_print_int(const pfenv_t *pfenv, machine_uint_t x, int sgn, int base, int base_char, int flags, char fill, int width) {
91 char sign = 0;
92 if (sgn) {
93 if ((machine_int_t)x < 0) {
94 sign = '-';
95 x = -x;
96 } else if (flags & PF_FLAG_SHOW_SIGN) {
97 sign = '+';
98 } else if (flags & PF_FLAG_SPACE_SIGN) {
99 sign = ' ';
100 }
101 }
102
103 char buf[INT_BUF_SIZE];
104 char *b = buf + INT_BUF_SIZE;
105
106 if (x == 0) {
107 *(--b) = '0';
108 } else {
109 do {
110 int c = x % base;
111 x /= base;
112 if (c >= 10) {
113 c += base_char - 10;
114 } else {
115 c += '0';
116 }
117 *(--b) = c;
118 } while (b > buf && x != 0);
119 }
120
121 char prefix_char = '\0';
122
123 if (flags & PF_FLAG_SHOW_PREFIX) {
124 if (base == 2) {
125 prefix_char = base_char + 'b' - 'a';
126 } else if (base == 8) {
127 prefix_char = base_char + 'o' - 'a';
128 } else if (base == 16) {
129 prefix_char = base_char + 'x' - 'a';
130 }
131 }
132
133 int len = 0;
134 if (flags & PF_FLAG_PAD_AFTER_SIGN) {
135 if (sign) {
136 len += pfenv_print_strn(pfenv, &sign, 1, flags, fill, 1);
137 width--;
138 }
139 if (prefix_char) {
140 len += pfenv_print_strn(pfenv, "0", 1, flags, fill, 1);
141 len += pfenv_print_strn(pfenv, &prefix_char, 1, flags, fill, 1);
142 width -= 2;
143 }
144 } else {
145 if (prefix_char && b > &buf[1]) {
146 *(--b) = prefix_char;
147 *(--b) = '0';
148 }
149 if (sign && b > buf) {
150 *(--b) = sign;
151 }
152 }
153
154 len += pfenv_print_strn(pfenv, b, buf + INT_BUF_SIZE - b, flags, fill, width);
155 return len;
156}
157
Damien Georgea12a0f72014-04-08 01:29:53 +0100158int 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) {
Dave Hylandsc4029e52014-04-07 11:19:51 -0700159 if (!MP_OBJ_IS_INT(x)) {
160 // This will convert booleans to int, or raise an error for
161 // non-integer types.
162 x = MP_OBJ_NEW_SMALL_INT(mp_obj_get_int(x));
163 }
164
165 char prefix_buf[4];
166 char *prefix = prefix_buf;
167
168 if (mp_obj_int_is_positive(x)) {
169 if (flags & PF_FLAG_SHOW_SIGN) {
170 *prefix++ = '+';
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700171 } else if (flags & PF_FLAG_SPACE_SIGN) {
Dave Hylandsc4029e52014-04-07 11:19:51 -0700172 *prefix++ = ' ';
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700173 }
174 }
175
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700176 if (flags & PF_FLAG_SHOW_PREFIX) {
177 if (base == 2) {
Dave Hylandsc4029e52014-04-07 11:19:51 -0700178 *prefix++ = '0';
179 *prefix++ = base_char + 'b' - 'a';
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700180 } else if (base == 8) {
Dave Hylandsc4029e52014-04-07 11:19:51 -0700181 *prefix++ = '0';
182 if (flags & PF_FLAG_SHOW_OCTAL_LETTER) {
183 *prefix++ = base_char + 'o' - 'a';
184 }
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700185 } else if (base == 16) {
Dave Hylandsc4029e52014-04-07 11:19:51 -0700186 *prefix++ = '0';
187 *prefix++ = base_char + 'x' - 'a';
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700188 }
189 }
Dave Hylandsc4029e52014-04-07 11:19:51 -0700190 *prefix = '\0';
191 int prefix_len = prefix - prefix_buf;
192 prefix = prefix_buf;
193
194 char comma = '\0';
195 if (flags & PF_FLAG_SHOW_COMMA) {
196 comma = ',';
197 }
198
199 // The size of this buffer is rather arbitrary. If it's not large
200 // enough, a dynamic one will be allocated.
201 char stack_buf[sizeof(machine_int_t) * 4];
202 char *buf = stack_buf;
203 int buf_size = sizeof(stack_buf);
204 int fmt_size = 0;
205 char *str;
206
207 char sign = '\0';
208 if (flags & PF_FLAG_PAD_AFTER_SIGN) {
209 // We add the pad in this function, so since the pad goes after
210 // the sign & prefix, we format without a prefix
211 str = mp_obj_int_formatted(&buf, &buf_size, &fmt_size,
212 x, base, NULL, base_char, comma);
213 if (*str == '-') {
214 sign = *str++;
215 fmt_size--;
216 }
217 } else {
218 str = mp_obj_int_formatted(&buf, &buf_size, &fmt_size,
219 x, base, prefix, base_char, comma);
220 }
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700221
222 int len = 0;
223 if (flags & PF_FLAG_PAD_AFTER_SIGN) {
Dave Hylandsc4029e52014-04-07 11:19:51 -0700224 // pad after sign implies pad after prefix as well.
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700225 if (sign) {
Dave Hylandsc4029e52014-04-07 11:19:51 -0700226 len += pfenv_print_strn(pfenv, &sign, 1, 0, 0, 1);
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700227 width--;
228 }
Dave Hylandsc4029e52014-04-07 11:19:51 -0700229 if (prefix_len) {
230 len += pfenv_print_strn(pfenv, prefix, prefix_len, 0, 0, 1);
231 width -= prefix_len;
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700232 }
233 }
234
Dave Hylandsc4029e52014-04-07 11:19:51 -0700235 len += pfenv_print_strn(pfenv, str, fmt_size, flags, fill, width);
236
237 if (buf != stack_buf) {
238 m_free(buf, buf_size);
239 }
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700240 return len;
241}
242
243#if MICROPY_ENABLE_FLOAT
244int pfenv_print_float(const pfenv_t *pfenv, mp_float_t f, char fmt, int flags, char fill, int width, int prec) {
245 char buf[32];
246 char sign = '\0';
247 int chrs = 0;
248
249 if (flags & PF_FLAG_SHOW_SIGN) {
250 sign = '+';
251 }
252 else
253 if (flags & PF_FLAG_SPACE_SIGN) {
254 sign = ' ';
255 }
256 int len;
257#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
258 len = format_float(f, buf, sizeof(buf), fmt, prec, sign);
259#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
260 char fmt_buf[6];
261 char *fmt_s = fmt_buf;
262
263 *fmt_s++ = '%';
264 if (sign) {
265 *fmt_s++ = sign;
266 }
267 *fmt_s++ = '.';
268 *fmt_s++ = '*';
269 *fmt_s++ = fmt;
270 *fmt_s = '\0';
271
272 len = snprintf(buf, sizeof(buf), fmt_buf, prec, f);
273#else
274#error Unknown MICROPY FLOAT IMPL
275#endif
276 char *s = buf;
277
278 if ((flags & PF_FLAG_ADD_PERCENT) && (len + 1) < sizeof(buf)) {
279 buf[len++] = '%';
280 buf[len] = '\0';
281 }
282
283 // buf[0] < '0' returns true if the first character is space, + or -
284 if ((flags & PF_FLAG_PAD_AFTER_SIGN) && buf[0] < '0') {
285 // We have a sign character
286 s++;
287 if (*s <= '9' || (flags & PF_FLAG_PAD_NAN_INF)) {
288 // We have a number, or we have a inf/nan and PAD_NAN_INF is set
289 // With '{:06e}'.format(float('-inf')) you get '-00inf'
290 chrs += pfenv_print_strn(pfenv, &buf[0], 1, 0, 0, 1);
291 width--;
292 len--;
293 }
294 }
295
296 if (*s > 'A' && (flags & PF_FLAG_PAD_NAN_INF) == 0) {
297 // We have one of the inf or nan variants, suppress zero fill.
298 // With printf, if you use: printf("%06e", -inf) then you get " -inf"
299 // so suppress the zero fill.
300 fill = ' ';
301 }
Damien Georgee3e05002014-04-01 21:15:03 +0100302 chrs += pfenv_print_strn(pfenv, s, len, flags, fill, width);
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700303
304 return chrs;
305}
306#endif