aboutsummaryrefslogtreecommitdiff
path: root/src/lj_strfmt.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lj_strfmt.c')
-rw-r--r--src/lj_strfmt.c295
1 files changed, 295 insertions, 0 deletions
diff --git a/src/lj_strfmt.c b/src/lj_strfmt.c
new file mode 100644
index 00000000..9aaf08e2
--- /dev/null
+++ b/src/lj_strfmt.c
@@ -0,0 +1,295 @@
1/*
2** String formatting.
3** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
4*/
5
6#include <stdio.h>
7
8#define lj_strfmt_c
9#define LUA_CORE
10
11#include "lj_obj.h"
12#include "lj_buf.h"
13#include "lj_char.h"
14#include "lj_strfmt.h"
15
16/* -- Format parser ------------------------------------------------------- */
17
18static const uint8_t strfmt_map[('x'-'A')+1] = {
19 STRFMT_A,0,0,0,STRFMT_E,0,STRFMT_G,0,0,0,0,0,0,
20 0,0,0,0,0,0,0,0,0,0,STRFMT_X,0,0,
21 0,0,0,0,0,0,
22 STRFMT_A,0,STRFMT_C,STRFMT_D,STRFMT_E,STRFMT_F,STRFMT_G,0,STRFMT_I,0,0,0,0,
23 0,STRFMT_O,STRFMT_P,STRFMT_Q,0,STRFMT_S,0,STRFMT_U,0,0,STRFMT_X
24};
25
26SFormat LJ_FASTCALL lj_strfmt_parse(FormatState *fs)
27{
28 const uint8_t *p = fs->p, *e = fs->e;
29 fs->str = (const char *)p;
30 for (; p < e; p++) {
31 if (*p == '%') { /* Escape char? */
32 if (p[1] == '%') { /* '%%'? */
33 fs->p = ++p+1;
34 goto retlit;
35 } else {
36 SFormat sf = 0;
37 uint32_t c;
38 if (p != (const uint8_t *)fs->str)
39 break;
40 for (p++; (uint32_t)*p - ' ' <= (uint32_t)('0' - ' '); p++) {
41 /* Parse flags. */
42 if (*p == '-') sf |= STRFMT_F_LEFT;
43 else if (*p == '+') sf |= STRFMT_F_PLUS;
44 else if (*p == '0') sf |= STRFMT_F_ZERO;
45 else if (*p == ' ') sf |= STRFMT_F_SPACE;
46 else if (*p == '#') sf |= STRFMT_F_ALT;
47 else break;
48 }
49 if ((uint32_t)*p - '0' < 10) { /* Parse width. */
50 uint32_t width = (uint32_t)*p++ - '0';
51 if ((uint32_t)*p - '0' < 10)
52 width = (uint32_t)*p++ - '0' + width*10;
53 sf |= (width << STRFMT_SH_WIDTH);
54 }
55 if (*p == '.') { /* Parse precision. */
56 uint32_t prec = 0;
57 p++;
58 if ((uint32_t)*p - '0' < 10) {
59 prec = (uint32_t)*p++ - '0';
60 if ((uint32_t)*p - '0' < 10)
61 prec = (uint32_t)*p++ - '0' + prec*10;
62 }
63 sf |= ((prec+1) << STRFMT_SH_PREC);
64 }
65 /* Parse conversion. */
66 c = (uint32_t)*p - 'A';
67 if (LJ_LIKELY(c <= (uint32_t)('x' - 'A'))) {
68 uint32_t sx = strfmt_map[c];
69 if (sx) {
70 fs->p = p+1;
71 return (sf | sx | ((c & 0x20) ? 0 : STRFMT_F_UPPER));
72 }
73 }
74 /* Return error location. */
75 if (*p >= 32) p++;
76 fs->len = (MSize)(p - (const uint8_t *)fs->str);
77 fs->p = fs->e;
78 return STRFMT_ERR;
79 }
80 }
81 }
82 fs->p = p;
83retlit:
84 fs->len = (MSize)(p - (const uint8_t *)fs->str);
85 return fs->len ? STRFMT_LIT : STRFMT_EOF;
86}
87
88/* -- Format conversions -------------------------------------------------- */
89
90/* Add formatted char to buffer. */
91SBuf *lj_strfmt_putchar(SBuf *sb, SFormat sf, int32_t c)
92{
93 MSize width = STRFMT_WIDTH(sf);
94 char *p = lj_buf_more(sb, width > 1 ? width : 1);
95 if ((sf & STRFMT_F_LEFT)) *p++ = (char)c;
96 while (width-- > 1) *p++ = ' ';
97 if (!(sf & STRFMT_F_LEFT)) *p++ = (char)c;
98 setsbufP(sb, p);
99 return sb;
100}
101
102/* Add formatted string to buffer. */
103SBuf *lj_strfmt_putstr(SBuf *sb, SFormat sf, GCstr *str)
104{
105 MSize len = str->len <= STRFMT_PREC(sf) ? str->len : STRFMT_PREC(sf);
106 MSize width = STRFMT_WIDTH(sf);
107 char *p = lj_buf_more(sb, width > len ? width : len);
108 if ((sf & STRFMT_F_LEFT)) p = lj_buf_wmem(p, strdata(str), len);
109 while (width-- > len) *p++ = ' ';
110 if (!(sf & STRFMT_F_LEFT)) p = lj_buf_wmem(p, strdata(str), len);
111 setsbufP(sb, p);
112 return sb;
113}
114
115/* Add quoted string to buffer (no formatting). */
116SBuf *lj_strfmt_putquoted(SBuf *sb, GCstr *str)
117{
118 const char *s = strdata(str);
119 MSize len = str->len;
120 lj_buf_putb(sb, '"');
121 while (len--) {
122 uint32_t c = (uint32_t)(uint8_t)*s++;
123 char *p = lj_buf_more(sb, 4);
124 if (c == '"' || c == '\\' || c == '\n') {
125 *p++ = '\\';
126 } else if (lj_char_iscntrl(c)) { /* This can only be 0-31 or 127. */
127 uint32_t d;
128 *p++ = '\\';
129 if (c >= 100 || lj_char_isdigit((uint8_t)*s)) {
130 *p++ = (char)('0'+(c >= 100)); if (c >= 100) c -= 100;
131 goto tens;
132 } else if (c >= 10) {
133 tens:
134 d = (c * 205) >> 11; c -= d * 10; *p++ = (char)('0'+d);
135 }
136 c += '0';
137 }
138 *p++ = (char)c;
139 setsbufP(sb, p);
140 }
141 lj_buf_putb(sb, '"');
142 return sb;
143}
144
145/* Add formatted signed/unsigned integer to buffer. */
146SBuf *lj_strfmt_putxint(SBuf *sb, SFormat sf, uint64_t k)
147{
148 char buf[1+22], *q = buf + sizeof(buf), *p;
149#ifdef LUA_USE_ASSERT
150 char *ps;
151#endif
152 MSize prefix = 0, len, prec, pprec, width, need;
153
154 /* Figure out signed prefixes. */
155 if (STRFMT_TYPE(sf) == STRFMT_INT) {
156 if ((int64_t)k < 0) {
157 k = (uint64_t)-(int64_t)k;
158 prefix = 256 + '-';
159 } else if ((sf & STRFMT_F_PLUS)) {
160 prefix = 256 + '+';
161 } else if ((sf & STRFMT_F_SPACE)) {
162 prefix = 256 + ' ';
163 }
164 }
165
166 /* Convert number and store to fixed-size buffer in reverse order. */
167 prec = STRFMT_PREC(sf);
168 if ((int32_t)prec >= 0) sf &= ~STRFMT_F_ZERO;
169 if (k == 0) { /* Special-case zero argument. */
170 if (prec != 0 ||
171 (sf & (STRFMT_T_OCT|STRFMT_F_ALT)) == (STRFMT_T_OCT|STRFMT_F_ALT))
172 *--q = '0';
173 } else if (!(sf & (STRFMT_T_HEX|STRFMT_T_OCT))) { /* Decimal. */
174 uint32_t k2;
175 while ((k >> 32)) { *--q = (char)('0' + k % 10); k /= 10; }
176 k2 = (uint32_t)k;
177 do { *--q = (char)('0' + k2 % 10); k2 /= 10; } while (k2);
178 } else if ((sf & STRFMT_T_HEX)) { /* Hex. */
179 const char *hexdig = (sf & STRFMT_F_UPPER) ? "0123456789ABCDEF" :
180 "0123456789abcdef";
181 do { *--q = hexdig[(k & 15)]; k >>= 4; } while (k);
182 if ((sf & STRFMT_F_ALT)) prefix = 512 + 'x';
183 } else { /* Octal. */
184 do { *--q = (char)('0' + (uint32_t)(k & 7)); k >>= 3; } while (k);
185 if ((sf & STRFMT_F_ALT)) *--q = '0';
186 }
187
188 /* Calculate sizes. */
189 len = (MSize)(buf + sizeof(buf) - q);
190 if ((int32_t)len >= (int32_t)prec) prec = len;
191 width = STRFMT_WIDTH(sf);
192 pprec = prec + (prefix >> 8);
193 need = width > pprec ? width : pprec;
194 p = lj_buf_more(sb, need);
195#ifdef LUA_USE_ASSERT
196 ps = p;
197#endif
198
199 /* Format number with leading/trailing whitespace and zeros. */
200 if ((sf & (STRFMT_F_LEFT|STRFMT_F_ZERO)) == 0)
201 while (width-- > pprec) *p++ = ' ';
202 if (prefix) {
203 if ((char)prefix == 'x') *p++ = '0';
204 *p++ = (char)prefix;
205 }
206 if ((sf & (STRFMT_F_LEFT|STRFMT_F_ZERO)) == STRFMT_F_ZERO)
207 while (width-- > pprec) *p++ = '0';
208 while (prec-- > len) *p++ = '0';
209 while (q < buf + sizeof(buf)) *p++ = *q++; /* Add number itself. */
210 if ((sf & STRFMT_F_LEFT))
211 while (width-- > pprec) *p++ = ' ';
212
213 lua_assert(need == (MSize)(p - ps));
214 setsbufP(sb, p);
215 return sb;
216}
217
218/* Add number formatted as signed integer to buffer. */
219SBuf *lj_strfmt_putnum_int(SBuf *sb, SFormat sf, lua_Number n)
220{
221 int64_t k = (int64_t)n;
222 if (checki32(k) && sf == STRFMT_INT)
223 return lj_buf_putint(sb, k); /* Shortcut for plain %d. */
224 else
225 return lj_strfmt_putxint(sb, sf, (uint64_t)k);
226}
227
228/* Add number formatted as unsigned integer to buffer. */
229SBuf *lj_strfmt_putnum_uint(SBuf *sb, SFormat sf, lua_Number n)
230{
231 int64_t k;
232 if (n >= 9223372036854775808.0)
233 k = (int64_t)(n - 18446744073709551616.0);
234 else
235 k = (int64_t)n;
236 return lj_strfmt_putxint(sb, sf, (uint64_t)k);
237}
238
239/* Max. sprintf buffer size needed. At least #string.format("%.99f", -1e308). */
240#define STRFMT_FMTNUMBUF 512
241
242/* Add formatted floating-point number to buffer. */
243SBuf *lj_strfmt_putnum(SBuf *sb, SFormat sf, lua_Number n)
244{
245 TValue tv;
246 tv.n = n;
247 if (LJ_UNLIKELY((tv.u32.hi << 1) >= 0xffe00000)) {
248 /* Canonicalize output of non-finite values. */
249 MSize width = STRFMT_WIDTH(sf), len = 3;
250 int prefix = 0, ch = (sf & STRFMT_F_UPPER) ? 0x202020 : 0;
251 char *p;
252 if (((tv.u32.hi & 0x000fffff) | tv.u32.lo) != 0) {
253 ch ^= ('n' << 16) | ('a' << 8) | 'n';
254 if ((sf & STRFMT_F_SPACE)) prefix = ' ';
255 } else {
256 ch ^= ('i' << 16) | ('n' << 8) | 'f';
257 if ((tv.u32.hi & 0x80000000)) prefix = '-';
258 else if ((sf & STRFMT_F_PLUS)) prefix = '+';
259 else if ((sf & STRFMT_F_SPACE)) prefix = ' ';
260 }
261 if (prefix) len = 4;
262 p = lj_buf_more(sb, width > len ? width : len);
263 if (!(sf & STRFMT_F_LEFT)) while (width-- > len) *p++ = ' ';
264 if (prefix) *p++ = prefix;
265 *p++ = (char)(ch >> 16); *p++ = (char)(ch >> 8); *p++ = (char)ch;
266 if ((sf & STRFMT_F_LEFT)) while (width-- > len) *p++ = ' ';
267 setsbufP(sb, p);
268 } else { /* Delegate to sprintf() for now. */
269 uint8_t width = (uint8_t)STRFMT_WIDTH(sf), prec = (uint8_t)STRFMT_PREC(sf);
270 char fmt[1+5+2+3+1+1], *p = fmt;
271 *p++ = '%';
272 if ((sf & STRFMT_F_LEFT)) *p++ = '-';
273 if ((sf & STRFMT_F_PLUS)) *p++ = '+';
274 if ((sf & STRFMT_F_ZERO)) *p++ = '0';
275 if ((sf & STRFMT_F_SPACE)) *p++ = ' ';
276 if ((sf & STRFMT_F_ALT)) *p++ = '#';
277 if (width) {
278 uint8_t x = width / 10, y = width % 10;
279 if (x) *p++ = '0' + x;
280 *p++ = '0' + y;
281 }
282 if (prec != 255) {
283 uint8_t x = prec / 10, y = prec % 10;
284 *p++ = '.';
285 if (x) *p++ = '0' + x;
286 *p++ = '0' + y;
287 }
288 *p++ = (0x67666561 >> (STRFMT_FP(sf)<<3)) ^ ((sf & STRFMT_F_UPPER)?0x20:0);
289 *p = '\0';
290 p = lj_buf_more(sb, STRFMT_FMTNUMBUF);
291 setsbufP(sb, p + sprintf(p, fmt, n));
292 }
293 return sb;
294}
295