diff options
Diffstat (limited to 'src/lj_strfmt.c')
-rw-r--r-- | src/lj_strfmt.c | 295 |
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 | |||
18 | static 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 | |||
26 | SFormat 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; | ||
83 | retlit: | ||
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. */ | ||
91 | SBuf *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. */ | ||
103 | SBuf *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). */ | ||
116 | SBuf *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. */ | ||
146 | SBuf *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. */ | ||
219 | SBuf *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. */ | ||
229 | SBuf *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. */ | ||
243 | SBuf *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 | |||