1/*
2** String library.
3** Copyright (C) 2005-2021 Mike Pall. See Copyright Notice in luajit.h
4**
5** Major portions taken verbatim or adapted from the Lua interpreter.
6** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
7*/
8
9#define lib_string_c
10#define LUA_LIB
11
12#include "lua.h"
13#include "lauxlib.h"
14#include "lualib.h"
15
16#include "lj_obj.h"
17#include "lj_gc.h"
18#include "lj_err.h"
19#include "lj_buf.h"
20#include "lj_str.h"
21#include "lj_tab.h"
22#include "lj_meta.h"
23#include "lj_state.h"
24#include "lj_ff.h"
25#include "lj_bcdump.h"
26#include "lj_char.h"
27#include "lj_strfmt.h"
28#include "lj_lib.h"
29
30/* ------------------------------------------------------------------------ */
31
32#define LJLIB_MODULE_string
33
34LJLIB_LUA(string_len) /*
35 function(s)
36 CHECK_str(s)
37 return #s
38 end
39*/
40
41LJLIB_ASM(string_byte) LJLIB_REC(string_range 0)
42{
43 GCstr *s = lj_lib_checkstr(L, 1);
44 int32_t len = (int32_t)s->len;
45 int32_t start = lj_lib_optint(L, 2, 1);
46 int32_t stop = lj_lib_optint(L, 3, start);
47 int32_t n, i;
48 const unsigned char *p;
49 if (stop < 0) stop += len+1;
50 if (start < 0) start += len+1;
51 if (start <= 0) start = 1;
52 if (stop > len) stop = len;
53 if (start > stop) return FFH_RES(0); /* Empty interval: return no results. */
54 start--;
55 n = stop - start;
56 if ((uint32_t)n > LUAI_MAXCSTACK)
57 lj_err_caller(L, LJ_ERR_STRSLC);
58 lj_state_checkstack(L, (MSize)n);
59 p = (const unsigned char *)strdata(s) + start;
60 for (i = 0; i < n; i++)
61 setintV(L->base + i-1-LJ_FR2, p[i]);
62 return FFH_RES(n);
63}
64
65LJLIB_ASM(string_char) LJLIB_REC(.)
66{
67 int i, nargs = (int)(L->top - L->base);
68 char *buf = lj_buf_tmp(L, (MSize)nargs);
69 for (i = 1; i <= nargs; i++) {
70 int32_t k = lj_lib_checkint(L, i);
71 if (!checku8(k))
72 lj_err_arg(L, i, LJ_ERR_BADVAL);
73 buf[i-1] = (char)k;
74 }
75 setstrV(L, L->base-1-LJ_FR2, lj_str_new(L, buf, (size_t)nargs));
76 return FFH_RES(1);
77}
78
79LJLIB_ASM(string_sub) LJLIB_REC(string_range 1)
80{
81 lj_lib_checkstr(L, 1);
82 lj_lib_checkint(L, 2);
83 setintV(L->base+2, lj_lib_optint(L, 3, -1));
84 return FFH_RETRY;
85}
86
87LJLIB_CF(string_rep) LJLIB_REC(.)
88{
89 GCstr *s = lj_lib_checkstr(L, 1);
90 int32_t rep = lj_lib_checkint(L, 2);
91 GCstr *sep = lj_lib_optstr(L, 3);
92 SBuf *sb = lj_buf_tmp_(L);
93 if (sep && rep > 1) {
94 GCstr *s2 = lj_buf_cat2str(L, sep, s);
95 lj_buf_reset(sb);
96 lj_buf_putstr(sb, s);
97 s = s2;
98 rep--;
99 }
100 sb = lj_buf_putstr_rep(sb, s, rep);
101 setstrV(L, L->top-1, lj_buf_str(L, sb));
102 lj_gc_check(L);
103 return 1;
104}
105
106LJLIB_ASM(string_reverse) LJLIB_REC(string_op IRCALL_lj_buf_putstr_reverse)
107{
108 lj_lib_checkstr(L, 1);
109 return FFH_RETRY;
110}
111LJLIB_ASM_(string_lower) LJLIB_REC(string_op IRCALL_lj_buf_putstr_lower)
112LJLIB_ASM_(string_upper) LJLIB_REC(string_op IRCALL_lj_buf_putstr_upper)
113
114/* ------------------------------------------------------------------------ */
115
116static int writer_buf(lua_State *L, const void *p, size_t size, void *sb)
117{
118 lj_buf_putmem((SBuf *)sb, p, (MSize)size);
119 UNUSED(L);
120 return 0;
121}
122
123LJLIB_CF(string_dump)
124{
125 GCfunc *fn = lj_lib_checkfunc(L, 1);
126 int strip = L->base+1 < L->top && tvistruecond(L->base+1);
127 SBuf *sb = lj_buf_tmp_(L); /* Assumes lj_bcwrite() doesn't use tmpbuf. */
128 L->top = L->base+1;
129 if (!isluafunc(fn) || lj_bcwrite(L, funcproto(fn), writer_buf, sb, strip))
130 lj_err_caller(L, LJ_ERR_STRDUMP);
131 setstrV(L, L->top-1, lj_buf_str(L, sb));
132 lj_gc_check(L);
133 return 1;
134}
135
136/* ------------------------------------------------------------------------ */
137
138/* macro to `unsign' a character */
139#define uchar(c) ((unsigned char)(c))
140
141#define CAP_UNFINISHED (-1)
142#define CAP_POSITION (-2)
143
144typedef struct MatchState {
145 const char *src_init; /* init of source string */
146 const char *src_end; /* end (`\0') of source string */
147 lua_State *L;
148 int level; /* total number of captures (finished or unfinished) */
149 int depth;
150 struct {
151 const char *init;
152 ptrdiff_t len;
153 } capture[LUA_MAXCAPTURES];
154} MatchState;
155
156#define L_ESC '%'
157
158static int check_capture(MatchState *ms, int l)
159{
160 l -= '1';
161 if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED)
162 lj_err_caller(ms->L, LJ_ERR_STRCAPI);
163 return l;
164}
165
166static int capture_to_close(MatchState *ms)
167{
168 int level = ms->level;
169 for (level--; level>=0; level--)
170 if (ms->capture[level].len == CAP_UNFINISHED) return level;
171 lj_err_caller(ms->L, LJ_ERR_STRPATC);
172 return 0; /* unreachable */
173}
174
175static const char *classend(MatchState *ms, const char *p)
176{
177 switch (*p++) {
178 case L_ESC:
179 if (*p == '\0')
180 lj_err_caller(ms->L, LJ_ERR_STRPATE);
181 return p+1;
182 case '[':
183 if (*p == '^') p++;
184 do { /* look for a `]' */
185 if (*p == '\0')
186 lj_err_caller(ms->L, LJ_ERR_STRPATM);
187 if (*(p++) == L_ESC && *p != '\0')
188 p++; /* skip escapes (e.g. `%]') */
189 } while (*p != ']');
190 return p+1;
191 default:
192 return p;
193 }
194}
195
196static const unsigned char match_class_map[32] = {
197 0,LJ_CHAR_ALPHA,0,LJ_CHAR_CNTRL,LJ_CHAR_DIGIT,0,0,LJ_CHAR_GRAPH,0,0,0,0,
198 LJ_CHAR_LOWER,0,0,0,LJ_CHAR_PUNCT,0,0,LJ_CHAR_SPACE,0,
199 LJ_CHAR_UPPER,0,LJ_CHAR_ALNUM,LJ_CHAR_XDIGIT,0,0,0,0,0,0,0
200};
201
202static int match_class(int c, int cl)
203{
204 if ((cl & 0xc0) == 0x40) {
205 int t = match_class_map[(cl&0x1f)];
206 if (t) {
207 t = lj_char_isa(c, t);
208 return (cl & 0x20) ? t : !t;
209 }
210 if (cl == 'z') return c == 0;
211 if (cl == 'Z') return c != 0;
212 }
213 return (cl == c);
214}
215
216static int matchbracketclass(int c, const char *p, const char *ec)
217{
218 int sig = 1;
219 if (*(p+1) == '^') {
220 sig = 0;
221 p++; /* skip the `^' */
222 }
223 while (++p < ec) {
224 if (*p == L_ESC) {
225 p++;
226 if (match_class(c, uchar(*p)))
227 return sig;
228 }
229 else if ((*(p+1) == '-') && (p+2 < ec)) {
230 p+=2;
231 if (uchar(*(p-2)) <= c && c <= uchar(*p))
232 return sig;
233 }
234 else if (uchar(*p) == c) return sig;
235 }
236 return !sig;
237}
238
239static int singlematch(int c, const char *p, const char *ep)
240{
241 switch (*p) {
242 case '.': return 1; /* matches any char */
243 case L_ESC: return match_class(c, uchar(*(p+1)));
244 case '[': return matchbracketclass(c, p, ep-1);
245 default: return (uchar(*p) == c);
246 }
247}
248
249static const char *match(MatchState *ms, const char *s, const char *p);
250
251static const char *matchbalance(MatchState *ms, const char *s, const char *p)
252{
253 if (*p == 0 || *(p+1) == 0)
254 lj_err_caller(ms->L, LJ_ERR_STRPATU);
255 if (*s != *p) {
256 return NULL;
257 } else {
258 int b = *p;
259 int e = *(p+1);
260 int cont = 1;
261 while (++s < ms->src_end) {
262 if (*s == e) {
263 if (--cont == 0) return s+1;
264 } else if (*s == b) {
265 cont++;
266 }
267 }
268 }
269 return NULL; /* string ends out of balance */
270}
271
272static const char *max_expand(MatchState *ms, const char *s,
273 const char *p, const char *ep)
274{
275 ptrdiff_t i = 0; /* counts maximum expand for item */
276 while ((s+i)<ms->src_end && singlematch(uchar(*(s+i)), p, ep))
277 i++;
278 /* keeps trying to match with the maximum repetitions */
279 while (i>=0) {
280 const char *res = match(ms, (s+i), ep+1);
281 if (res) return res;
282 i--; /* else didn't match; reduce 1 repetition to try again */
283 }
284 return NULL;
285}
286
287static const char *min_expand(MatchState *ms, const char *s,
288 const char *p, const char *ep)
289{
290 for (;;) {
291 const char *res = match(ms, s, ep+1);
292 if (res != NULL)
293 return res;
294 else if (s<ms->src_end && singlematch(uchar(*s), p, ep))
295 s++; /* try with one more repetition */
296 else
297 return NULL;
298 }
299}
300
301static const char *start_capture(MatchState *ms, const char *s,
302 const char *p, int what)
303{
304 const char *res;
305 int level = ms->level;
306 if (level >= LUA_MAXCAPTURES) lj_err_caller(ms->L, LJ_ERR_STRCAPN);
307 ms->capture[level].init = s;
308 ms->capture[level].len = what;
309 ms->level = level+1;
310 if ((res=match(ms, s, p)) == NULL) /* match failed? */
311 ms->level--; /* undo capture */
312 return res;
313}
314
315static const char *end_capture(MatchState *ms, const char *s,
316 const char *p)
317{
318 int l = capture_to_close(ms);
319 const char *res;
320 ms->capture[l].len = s - ms->capture[l].init; /* close capture */
321 if ((res = match(ms, s, p)) == NULL) /* match failed? */
322 ms->capture[l].len = CAP_UNFINISHED; /* undo capture */
323 return res;
324}
325
326static const char *match_capture(MatchState *ms, const char *s, int l)
327{
328 size_t len;
329 l = check_capture(ms, l);
330 len = (size_t)ms->capture[l].len;
331 if ((size_t)(ms->src_end-s) >= len &&
332 memcmp(ms->capture[l].init, s, len) == 0)
333 return s+len;
334 else
335 return NULL;
336}
337
338static const char *match(MatchState *ms, const char *s, const char *p)
339{
340 if (++ms->depth > LJ_MAX_XLEVEL)
341 lj_err_caller(ms->L, LJ_ERR_STRPATX);
342 init: /* using goto's to optimize tail recursion */
343 switch (*p) {
344 case '(': /* start capture */
345 if (*(p+1) == ')') /* position capture? */
346 s = start_capture(ms, s, p+2, CAP_POSITION);
347 else
348 s = start_capture(ms, s, p+1, CAP_UNFINISHED);
349 break;
350 case ')': /* end capture */
351 s = end_capture(ms, s, p+1);
352 break;
353 case L_ESC:
354 switch (*(p+1)) {
355 case 'b': /* balanced string? */
356 s = matchbalance(ms, s, p+2);
357 if (s == NULL) break;
358 p+=4;
359 goto init; /* else s = match(ms, s, p+4); */
360 case 'f': { /* frontier? */
361 const char *ep; char previous;
362 p += 2;
363 if (*p != '[')
364 lj_err_caller(ms->L, LJ_ERR_STRPATB);
365 ep = classend(ms, p); /* points to what is next */
366 previous = (s == ms->src_init) ? '\0' : *(s-1);
367 if (matchbracketclass(uchar(previous), p, ep-1) ||
368 !matchbracketclass(uchar(*s), p, ep-1)) { s = NULL; break; }
369 p=ep;
370 goto init; /* else s = match(ms, s, ep); */
371 }
372 default:
373 if (lj_char_isdigit(uchar(*(p+1)))) { /* capture results (%0-%9)? */
374 s = match_capture(ms, s, uchar(*(p+1)));
375 if (s == NULL) break;
376 p+=2;
377 goto init; /* else s = match(ms, s, p+2) */
378 }
379 goto dflt; /* case default */
380 }
381 break;
382 case '\0': /* end of pattern */
383 break; /* match succeeded */
384 case '$':
385 /* is the `$' the last char in pattern? */
386 if (*(p+1) != '\0') goto dflt;
387 if (s != ms->src_end) s = NULL; /* check end of string */
388 break;
389 default: dflt: { /* it is a pattern item */
390 const char *ep = classend(ms, p); /* points to what is next */
391 int m = s<ms->src_end && singlematch(uchar(*s), p, ep);
392 switch (*ep) {
393 case '?': { /* optional */
394 const char *res;
395 if (m && ((res=match(ms, s+1, ep+1)) != NULL)) {
396 s = res;
397 break;
398 }
399 p=ep+1;
400 goto init; /* else s = match(ms, s, ep+1); */
401 }
402 case '*': /* 0 or more repetitions */
403 s = max_expand(ms, s, p, ep);
404 break;
405 case '+': /* 1 or more repetitions */
406 s = (m ? max_expand(ms, s+1, p, ep) : NULL);
407 break;
408 case '-': /* 0 or more repetitions (minimum) */
409 s = min_expand(ms, s, p, ep);
410 break;
411 default:
412 if (m) { s++; p=ep; goto init; } /* else s = match(ms, s+1, ep); */
413 s = NULL;
414 break;
415 }
416 break;
417 }
418 }
419 ms->depth--;
420 return s;
421}
422
423static void push_onecapture(MatchState *ms, int i, const char *s, const char *e)
424{
425 if (i >= ms->level) {
426 if (i == 0) /* ms->level == 0, too */
427 lua_pushlstring(ms->L, s, (size_t)(e - s)); /* add whole match */
428 else
429 lj_err_caller(ms->L, LJ_ERR_STRCAPI);
430 } else {
431 ptrdiff_t l = ms->capture[i].len;
432 if (l == CAP_UNFINISHED) lj_err_caller(ms->L, LJ_ERR_STRCAPU);
433 if (l == CAP_POSITION)
434 lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1);
435 else
436 lua_pushlstring(ms->L, ms->capture[i].init, (size_t)l);
437 }
438}
439
440static int push_captures(MatchState *ms, const char *s, const char *e)
441{
442 int i;
443 int nlevels = (ms->level == 0 && s) ? 1 : ms->level;
444 luaL_checkstack(ms->L, nlevels, "too many captures");
445 for (i = 0; i < nlevels; i++)
446 push_onecapture(ms, i, s, e);
447 return nlevels; /* number of strings pushed */
448}
449
450static int str_find_aux(lua_State *L, int find)
451{
452 GCstr *s = lj_lib_checkstr(L, 1);
453 GCstr *p = lj_lib_checkstr(L, 2);
454 int32_t start = lj_lib_optint(L, 3, 1);
455 MSize st;
456 if (start < 0) start += (int32_t)s->len; else start--;
457 if (start < 0) start = 0;
458 st = (MSize)start;
459 if (st > s->len) {
460#if LJ_52
461 setnilV(L->top-1);
462 return 1;
463#else
464 st = s->len;
465#endif
466 }
467 if (find && ((L->base+3 < L->top && tvistruecond(L->base+3)) ||
468 !lj_str_haspattern(p))) { /* Search for fixed string. */
469 const char *q = lj_str_find(strdata(s)+st, strdata(p), s->len-st, p->len);
470 if (q) {
471 setintV(L->top-2, (int32_t)(q-strdata(s)) + 1);
472 setintV(L->top-1, (int32_t)(q-strdata(s)) + (int32_t)p->len);
473 return 2;
474 }
475 } else { /* Search for pattern. */
476 MatchState ms;
477 const char *pstr = strdata(p);
478 const char *sstr = strdata(s) + st;
479 int anchor = 0;
480 if (*pstr == '^') { pstr++; anchor = 1; }
481 ms.L = L;
482 ms.src_init = strdata(s);
483 ms.src_end = strdata(s) + s->len;
484 do { /* Loop through string and try to match the pattern. */
485 const char *q;
486 ms.level = ms.depth = 0;
487 q = match(&ms, sstr, pstr);
488 if (q) {
489 if (find) {
490 setintV(L->top++, (int32_t)(sstr-(strdata(s)-1)));
491 setintV(L->top++, (int32_t)(q-strdata(s)));
492 return push_captures(&ms, NULL, NULL) + 2;
493 } else {
494 return push_captures(&ms, sstr, q);
495 }
496 }
497 } while (sstr++ < ms.src_end && !anchor);
498 }
499 setnilV(L->top-1); /* Not found. */
500 return 1;
501}
502
503LJLIB_CF(string_find) LJLIB_REC(.)
504{
505 return str_find_aux(L, 1);
506}
507
508LJLIB_CF(string_match)
509{
510 return str_find_aux(L, 0);
511}
512
513LJLIB_NOREG LJLIB_CF(string_gmatch_aux)
514{
515 const char *p = strVdata(lj_lib_upvalue(L, 2));
516 GCstr *str = strV(lj_lib_upvalue(L, 1));
517 const char *s = strdata(str);
518 TValue *tvpos = lj_lib_upvalue(L, 3);
519 const char *src = s + tvpos->u32.lo;
520 MatchState ms;
521 ms.L = L;
522 ms.src_init = s;
523 ms.src_end = s + str->len;
524 for (; src <= ms.src_end; src++) {
525 const char *e;
526 ms.level = ms.depth = 0;
527 if ((e = match(&ms, src, p)) != NULL) {
528 int32_t pos = (int32_t)(e - s);
529 if (e == src) pos++; /* Ensure progress for empty match. */
530 tvpos->u32.lo = (uint32_t)pos;
531 return push_captures(&ms, src, e);
532 }
533 }
534 return 0; /* not found */
535}
536
537LJLIB_CF(string_gmatch)
538{
539 lj_lib_checkstr(L, 1);
540 lj_lib_checkstr(L, 2);
541 L->top = L->base+3;
542 (L->top-1)->u64 = 0;
543 lj_lib_pushcc(L, lj_cf_string_gmatch_aux, FF_string_gmatch_aux, 3);
544 return 1;
545}
546
547static void add_s(MatchState *ms, luaL_Buffer *b, const char *s, const char *e)
548{
549 size_t l, i;
550 const char *news = lua_tolstring(ms->L, 3, &l);
551 for (i = 0; i < l; i++) {
552 if (news[i] != L_ESC) {
553 luaL_addchar(b, news[i]);
554 } else {
555 i++; /* skip ESC */
556 if (!lj_char_isdigit(uchar(news[i]))) {
557 luaL_addchar(b, news[i]);
558 } else if (news[i] == '0') {
559 luaL_addlstring(b, s, (size_t)(e - s));
560 } else {
561 push_onecapture(ms, news[i] - '1', s, e);
562 luaL_addvalue(b); /* add capture to accumulated result */
563 }
564 }
565 }
566}
567
568static void add_value(MatchState *ms, luaL_Buffer *b,
569 const char *s, const char *e)
570{
571 lua_State *L = ms->L;
572 switch (lua_type(L, 3)) {
573 case LUA_TNUMBER:
574 case LUA_TSTRING: {
575 add_s(ms, b, s, e);
576 return;
577 }
578 case LUA_TFUNCTION: {
579 int n;
580 lua_pushvalue(L, 3);
581 n = push_captures(ms, s, e);
582 lua_call(L, n, 1);
583 break;
584 }
585 case LUA_TTABLE: {
586 push_onecapture(ms, 0, s, e);
587 lua_gettable(L, 3);
588 break;
589 }
590 }
591 if (!lua_toboolean(L, -1)) { /* nil or false? */
592 lua_pop(L, 1);
593 lua_pushlstring(L, s, (size_t)(e - s)); /* keep original text */
594 } else if (!lua_isstring(L, -1)) {
595 lj_err_callerv(L, LJ_ERR_STRGSRV, luaL_typename(L, -1));
596 }
597 luaL_addvalue(b); /* add result to accumulator */
598}
599
600LJLIB_CF(string_gsub)
601{
602 size_t srcl;
603 const char *src = luaL_checklstring(L, 1, &srcl);
604 const char *p = luaL_checkstring(L, 2);
605 int tr = lua_type(L, 3);
606 int max_s = luaL_optint(L, 4, (int)(srcl+1));
607 int anchor = (*p == '^') ? (p++, 1) : 0;
608 int n = 0;
609 MatchState ms;
610 luaL_Buffer b;
611 if (!(tr == LUA_TNUMBER || tr == LUA_TSTRING ||
612 tr == LUA_TFUNCTION || tr == LUA_TTABLE))
613 lj_err_arg(L, 3, LJ_ERR_NOSFT);
614 luaL_buffinit(L, &b);
615 ms.L = L;
616 ms.src_init = src;
617 ms.src_end = src+srcl;
618 while (n < max_s) {
619 const char *e;
620 ms.level = ms.depth = 0;
621 e = match(&ms, src, p);
622 if (e) {
623 n++;
624 add_value(&ms, &b, src, e);
625 }
626 if (e && e>src) /* non empty match? */
627 src = e; /* skip it */
628 else if (src < ms.src_end)
629 luaL_addchar(&b, *src++);
630 else
631 break;
632 if (anchor)
633 break;
634 }
635 luaL_addlstring(&b, src, (size_t)(ms.src_end-src));
636 luaL_pushresult(&b);
637 lua_pushinteger(L, n); /* number of substitutions */
638 return 2;
639}
640
641/* ------------------------------------------------------------------------ */
642
643/* Emulate tostring() inline. */
644static GCstr *string_fmt_tostring(lua_State *L, int arg, int retry)
645{
646 TValue *o = L->base+arg-1;
647 cTValue *mo;
648 lj_assertL(o < L->top, "bad usage"); /* Caller already checks for existence. */
649 if (LJ_LIKELY(tvisstr(o)))
650 return strV(o);
651 if (retry != 2 && !tvisnil(mo = lj_meta_lookup(L, o, MM_tostring))) {
652 copyTV(L, L->top++, mo);
653 copyTV(L, L->top++, o);
654 lua_call(L, 1, 1);
655 copyTV(L, L->base+arg-1, --L->top);
656 return NULL; /* Buffer may be overwritten, retry. */
657 }
658 return lj_strfmt_obj(L, o);
659}
660
661LJLIB_CF(string_format) LJLIB_REC(.)
662{
663 int arg, top = (int)(L->top - L->base);
664 GCstr *fmt;
665 SBuf *sb;
666 FormatState fs;
667 SFormat sf;
668 int retry = 0;
669again:
670 arg = 1;
671 sb = lj_buf_tmp_(L);
672 fmt = lj_lib_checkstr(L, arg);
673 lj_strfmt_init(&fs, strdata(fmt), fmt->len);
674 while ((sf = lj_strfmt_parse(&fs)) != STRFMT_EOF) {
675 if (sf == STRFMT_LIT) {
676 lj_buf_putmem(sb, fs.str, fs.len);
677 } else if (sf == STRFMT_ERR) {
678 lj_err_callerv(L, LJ_ERR_STRFMT, strdata(lj_str_new(L, fs.str, fs.len)));
679 } else {
680 if (++arg > top)
681 luaL_argerror(L, arg, lj_obj_typename[0]);
682 switch (STRFMT_TYPE(sf)) {
683 case STRFMT_INT:
684 if (tvisint(L->base+arg-1)) {
685 int32_t k = intV(L->base+arg-1);
686 if (sf == STRFMT_INT)
687 lj_strfmt_putint(sb, k); /* Shortcut for plain %d. */
688 else
689 lj_strfmt_putfxint(sb, sf, k);
690 } else {
691 lj_strfmt_putfnum_int(sb, sf, lj_lib_checknum(L, arg));
692 }
693 break;
694 case STRFMT_UINT:
695 if (tvisint(L->base+arg-1))
696 lj_strfmt_putfxint(sb, sf, intV(L->base+arg-1));
697 else
698 lj_strfmt_putfnum_uint(sb, sf, lj_lib_checknum(L, arg));
699 break;
700 case STRFMT_NUM:
701 lj_strfmt_putfnum(sb, sf, lj_lib_checknum(L, arg));
702 break;
703 case STRFMT_STR: {
704 GCstr *str = string_fmt_tostring(L, arg, retry);
705 if (str == NULL)
706 retry = 1;
707 else if ((sf & STRFMT_T_QUOTED))
708 lj_strfmt_putquoted(sb, str); /* No formatting. */
709 else
710 lj_strfmt_putfstr(sb, sf, str);
711 break;
712 }
713 case STRFMT_CHAR:
714 lj_strfmt_putfchar(sb, sf, lj_lib_checkint(L, arg));
715 break;
716 case STRFMT_PTR: /* No formatting. */
717 lj_strfmt_putptr(sb, lj_obj_ptr(G(L), L->base+arg-1));
718 break;
719 default:
720 lj_assertL(0, "bad string format type");
721 break;
722 }
723 }
724 }
725 if (retry++ == 1) goto again;
726 setstrV(L, L->top-1, lj_buf_str(L, sb));
727 lj_gc_check(L);
728 return 1;
729}
730
731/* ------------------------------------------------------------------------ */
732
733#include "lj_libdef.h"
734
735LUALIB_API int luaopen_string(lua_State *L)
736{
737 GCtab *mt;
738 global_State *g;
739 LJ_LIB_REG(L, LUA_STRLIBNAME, string);
740 mt = lj_tab_new(L, 0, 1);
741 /* NOBARRIER: basemt is a GC root. */
742 g = G(L);
743 setgcref(basemt_it(g, LJ_TSTR), obj2gco(mt));
744 settabV(L, lj_tab_setstr(L, mt, mmname_str(g, MM_index)), tabV(L->top-1));
745 mt->nomm = (uint8_t)(~(1u<<MM_index));
746#if LJ_HASBUFFER
747 lj_lib_prereg(L, LUA_STRLIBNAME ".buffer", luaopen_string_buffer, tabV(L->top-1));
748#endif
749 return 1;
750}
751
752