1 #ifndef MRB_WITHOUT_FLOAT
2 #ifdef MRB_DISABLE_STDIO
3 /*
4
5 Most code in this file originates from musl (src/stdio/vfprintf.c)
6 which, just like mruby itself, is licensed under the MIT license.
7
8 Copyright (c) 2005-2014 Rich Felker, et al.
9
10 Permission is hereby granted, free of charge, to any person obtaining
11 a copy of this software and associated documentation files (the
12 "Software"), to deal in the Software without restriction, including
13 without limitation the rights to use, copy, modify, merge, publish,
14 distribute, sublicense, and/or sell copies of the Software, and to
15 permit persons to whom the Software is furnished to do so, subject to
16 the following conditions:
17
18 The above copyright notice and this permission notice shall be
19 included in all copies or substantial portions of the Software.
20
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
24 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
25 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
26 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
27 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
29 */
30
31 #include <limits.h>
32 #include <string.h>
33 #include <stdint.h>
34 #include <math.h>
35 #include <float.h>
36 #include <ctype.h>
37
38 #include <mruby.h>
39 #include <mruby/string.h>
40
41 struct fmt_args {
42 mrb_state *mrb;
43 mrb_value str;
44 };
45
46 #define MAX(a,b) ((a)>(b) ? (a) : (b))
47 #define MIN(a,b) ((a)<(b) ? (a) : (b))
48
49 /* Convenient bit representation for modifier flags, which all fall
50 * within 31 codepoints of the space character. */
51
52 #define ALT_FORM (1U<<('#'-' '))
53 #define ZERO_PAD (1U<<('0'-' '))
54 #define LEFT_ADJ (1U<<('-'-' '))
55 #define PAD_POS (1U<<(' '-' '))
56 #define MARK_POS (1U<<('+'-' '))
57
58 static void
out(struct fmt_args * f,const char * s,size_t l)59 out(struct fmt_args *f, const char *s, size_t l)
60 {
61 mrb_str_cat(f->mrb, f->str, s, l);
62 }
63
64 #define PAD_SIZE 256
65 static void
pad(struct fmt_args * f,char c,ptrdiff_t w,ptrdiff_t l,uint8_t fl)66 pad(struct fmt_args *f, char c, ptrdiff_t w, ptrdiff_t l, uint8_t fl)
67 {
68 char pad[PAD_SIZE];
69 if (fl & (LEFT_ADJ | ZERO_PAD) || l >= w) return;
70 l = w - l;
71 memset(pad, c, l>PAD_SIZE ? PAD_SIZE : l);
72 for (; l >= PAD_SIZE; l -= PAD_SIZE)
73 out(f, pad, PAD_SIZE);
74 out(f, pad, l);
75 }
76
77 static const char xdigits[16] = {
78 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
79 };
80
81 static char*
fmt_u(uint32_t x,char * s)82 fmt_u(uint32_t x, char *s)
83 {
84 for (; x; x /= 10) *--s = '0' + x % 10;
85 return s;
86 }
87
88 /* Do not override this check. The floating point printing code below
89 * depends on the float.h constants being right. If they are wrong, it
90 * may overflow the stack. */
91 #if LDBL_MANT_DIG == 53
92 typedef char compiler_defines_long_double_incorrectly[9-(int)sizeof(long double)];
93 #endif
94
95 static int
fmt_fp(struct fmt_args * f,long double y,ptrdiff_t p,uint8_t fl,int t)96 fmt_fp(struct fmt_args *f, long double y, ptrdiff_t p, uint8_t fl, int t)
97 {
98 uint32_t big[(LDBL_MANT_DIG+28)/29 + 1 // mantissa expansion
99 + (LDBL_MAX_EXP+LDBL_MANT_DIG+28+8)/9]; // exponent expansion
100 uint32_t *a, *d, *r, *z;
101 uint32_t i;
102 int e2=0, e, j;
103 ptrdiff_t l;
104 char buf[9+LDBL_MANT_DIG/4], *s;
105 const char *prefix="-0X+0X 0X-0x+0x 0x";
106 ptrdiff_t pl;
107 char ebuf0[3*sizeof(int)], *ebuf=&ebuf0[3*sizeof(int)], *estr;
108
109 pl=1;
110 if (signbit(y)) {
111 y=-y;
112 } else if (fl & MARK_POS) {
113 prefix+=3;
114 } else if (fl & PAD_POS) {
115 prefix+=6;
116 } else prefix++, pl=0;
117
118 if (!isfinite(y)) {
119 const char *ss = (t&32)?"inf":"INF";
120 if (y!=y) ss=(t&32)?"nan":"NAN";
121 pad(f, ' ', 0, 3+pl, fl&~ZERO_PAD);
122 out(f, prefix, pl);
123 out(f, ss, 3);
124 pad(f, ' ', 0, 3+pl, fl^LEFT_ADJ);
125 return 3+(int)pl;
126 }
127
128 y = frexp((double)y, &e2) * 2;
129 if (y) e2--;
130
131 if ((t|32)=='a') {
132 long double round = 8.0;
133 ptrdiff_t re;
134
135 if (t&32) prefix += 9;
136 pl += 2;
137
138 if (p<0 || p>=LDBL_MANT_DIG/4-1) re=0;
139 else re=LDBL_MANT_DIG/4-1-p;
140
141 if (re) {
142 while (re--) round*=16;
143 if (*prefix=='-') {
144 y=-y;
145 y-=round;
146 y+=round;
147 y=-y;
148 }
149 else {
150 y+=round;
151 y-=round;
152 }
153 }
154
155 estr=fmt_u(e2<0 ? -e2 : e2, ebuf);
156 if (estr==ebuf) *--estr='0';
157 *--estr = (e2<0 ? '-' : '+');
158 *--estr = t+('p'-'a');
159
160 s=buf;
161 do {
162 int x=(int)y;
163 *s++=xdigits[x]|(t&32);
164 y=16*(y-x);
165 if (s-buf==1 && (y||p>0||(fl&ALT_FORM))) *s++='.';
166 } while (y);
167
168 if (p && s-buf-2 < p)
169 l = (p+2) + (ebuf-estr);
170 else
171 l = (s-buf) + (ebuf-estr);
172
173 pad(f, ' ', 0, pl+l, fl);
174 out(f, prefix, pl);
175 pad(f, '0', 0, pl+l, fl^ZERO_PAD);
176 out(f, buf, s-buf);
177 pad(f, '0', l-(ebuf-estr)-(s-buf), 0, 0);
178 out(f, estr, ebuf-estr);
179 pad(f, ' ', 0, pl+l, fl^LEFT_ADJ);
180 return (int)pl+(int)l;
181 }
182 if (p<0) p=6;
183
184 if (y) y *= 268435456.0, e2-=28;
185
186 if (e2<0) a=r=z=big;
187 else a=r=z=big+sizeof(big)/sizeof(*big) - LDBL_MANT_DIG - 1;
188
189 do {
190 *z = (uint32_t)y;
191 y = 1000000000*(y-*z++);
192 } while (y);
193
194 while (e2>0) {
195 uint32_t carry=0;
196 int sh=MIN(29,e2);
197 for (d=z-1; d>=a; d--) {
198 uint64_t x = ((uint64_t)*d<<sh)+carry;
199 *d = x % 1000000000;
200 carry = (uint32_t)(x / 1000000000);
201 }
202 if (carry) *--a = carry;
203 while (z>a && !z[-1]) z--;
204 e2-=sh;
205 }
206 while (e2<0) {
207 uint32_t carry=0, *b;
208 int sh=MIN(9,-e2), need=1+((int)p+LDBL_MANT_DIG/3+8)/9;
209 for (d=a; d<z; d++) {
210 uint32_t rm = *d & ((1<<sh)-1);
211 *d = (*d>>sh) + carry;
212 carry = (1000000000>>sh) * rm;
213 }
214 if (!*a) a++;
215 if (carry) *z++ = carry;
216 /* Avoid (slow!) computation past requested precision */
217 b = (t|32)=='f' ? r : a;
218 if (z-b > need) z = b+need;
219 e2+=sh;
220 }
221
222 if (a<z) for (i=10, e=9*(int)(r-a); *a>=i; i*=10, e++);
223 else e=0;
224
225 /* Perform rounding: j is precision after the radix (possibly neg) */
226 j = (int)p - ((t|32)!='f')*e - ((t|32)=='g' && p);
227 if (j < 9*(z-r-1)) {
228 uint32_t x;
229 /* We avoid C's broken division of negative numbers */
230 d = r + 1 + ((j+9*LDBL_MAX_EXP)/9 - LDBL_MAX_EXP);
231 j += 9*LDBL_MAX_EXP;
232 j %= 9;
233 for (i=10, j++; j<9; i*=10, j++);
234 x = *d % i;
235 /* Are there any significant digits past j? */
236 if (x || d+1!=z) {
237 long double round = 2/LDBL_EPSILON;
238 long double small;
239 if (*d/i & 1) round += 2;
240 if (x<i/2) small=0.5;
241 else if (x==i/2 && d+1==z) small=1.0;
242 else small=1.5;
243 if (pl && *prefix=='-') round*=-1, small*=-1;
244 *d -= x;
245 /* Decide whether to round by probing round+small */
246 if (round+small != round) {
247 *d = *d + i;
248 while (*d > 999999999) {
249 *d--=0;
250 if (d<a) *--a=0;
251 (*d)++;
252 }
253 for (i=10, e=9*(int)(r-a); *a>=i; i*=10, e++);
254 }
255 }
256 if (z>d+1) z=d+1;
257 }
258 for (; z>a && !z[-1]; z--);
259
260 if ((t|32)=='g') {
261 if (!p) p++;
262 if (p>e && e>=-4) {
263 t--;
264 p-=e+1;
265 }
266 else {
267 t-=2;
268 p--;
269 }
270 if (!(fl&ALT_FORM)) {
271 /* Count trailing zeros in last place */
272 if (z>a && z[-1]) for (i=10, j=0; z[-1]%i==0; i*=10, j++);
273 else j=9;
274 if ((t|32)=='f')
275 p = MIN(p,MAX(0,9*(z-r-1)-j));
276 else
277 p = MIN(p,MAX(0,9*(z-r-1)+e-j));
278 }
279 }
280 l = 1 + p + (p || (fl&ALT_FORM));
281 if ((t|32)=='f') {
282 if (e>0) l+=e;
283 }
284 else {
285 estr=fmt_u(e<0 ? -e : e, ebuf);
286 while(ebuf-estr<2) *--estr='0';
287 *--estr = (e<0 ? '-' : '+');
288 *--estr = t;
289 l += ebuf-estr;
290 }
291
292 pad(f, ' ', 0, pl+l, fl);
293 out(f, prefix, pl);
294 pad(f, '0', 0, pl+l, fl^ZERO_PAD);
295
296 if ((t|32)=='f') {
297 if (a>r) a=r;
298 for (d=a; d<=r; d++) {
299 char *ss = fmt_u(*d, buf+9);
300 if (d!=a) while (ss>buf) *--ss='0';
301 else if (ss==buf+9) *--ss='0';
302 out(f, ss, buf+9-ss);
303 }
304 if (p || (fl&ALT_FORM)) out(f, ".", 1);
305 for (; d<z && p>0; d++, p-=9) {
306 char *ss = fmt_u(*d, buf+9);
307 while (ss>buf) *--ss='0';
308 out(f, ss, MIN(9,p));
309 }
310 pad(f, '0', p+9, 9, 0);
311 }
312 else {
313 if (z<=a) z=a+1;
314 for (d=a; d<z && p>=0; d++) {
315 char *ss = fmt_u(*d, buf+9);
316 if (ss==buf+9) *--ss='0';
317 if (d!=a) while (ss>buf) *--ss='0';
318 else {
319 out(f, ss++, 1);
320 if (p>0||(fl&ALT_FORM)) out(f, ".", 1);
321 }
322 out(f, ss, MIN(buf+9-ss, p));
323 p -= (int)(buf+9-ss);
324 }
325 pad(f, '0', p+18, 18, 0);
326 out(f, estr, ebuf-estr);
327 }
328
329 pad(f, ' ', 0, pl+l, fl^LEFT_ADJ);
330
331 return (int)pl+(int)l;
332 }
333
334 static int
fmt_core(struct fmt_args * f,const char * fmt,mrb_float flo)335 fmt_core(struct fmt_args *f, const char *fmt, mrb_float flo)
336 {
337 ptrdiff_t p;
338
339 if (*fmt != '%') {
340 return -1;
341 }
342 ++fmt;
343
344 if (*fmt == '.') {
345 ++fmt;
346 for (p = 0; ISDIGIT(*fmt); ++fmt) {
347 p = 10 * p + (*fmt - '0');
348 }
349 }
350 else {
351 p = -1;
352 }
353
354 switch (*fmt) {
355 case 'e': case 'f': case 'g': case 'a':
356 case 'E': case 'F': case 'G': case 'A':
357 return fmt_fp(f, flo, p, 0, *fmt);
358 default:
359 return -1;
360 }
361 }
362
363 mrb_value
mrb_float_to_str(mrb_state * mrb,mrb_value flo,const char * fmt)364 mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt)
365 {
366 struct fmt_args f;
367
368 f.mrb = mrb;
369 f.str = mrb_str_new_capa(mrb, 24);
370 if (fmt_core(&f, fmt, mrb_float(flo)) < 0) {
371 mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format string");
372 }
373 return f.str;
374 }
375 #else /* MRB_DISABLE_STDIO */
376 #include <mruby.h>
377 #include <stdio.h>
378
379 mrb_value
mrb_float_to_str(mrb_state * mrb,mrb_value flo,const char * fmt)380 mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt)
381 {
382 char buf[25];
383
384 snprintf(buf, sizeof(buf), fmt, mrb_float(flo));
385 return mrb_str_new_cstr(mrb, buf);
386 }
387 #endif /* MRB_DISABLE_STDIO */
388 #endif
389