diff options
| author | Mike Pall <mike> | 2021-06-01 05:14:18 +0200 |
|---|---|---|
| committer | Mike Pall <mike> | 2021-06-01 05:14:18 +0200 |
| commit | edd5cbadc5cdc7b5b66d5340ee97c5abe5a3892a (patch) | |
| tree | 053c8bb18f0b9de559a4b712062fd99e96849be7 | |
| parent | 50d6883e6027c4c2f9a5e495fee6b7fff1bd73c9 (diff) | |
| download | luajit-edd5cbadc5cdc7b5b66d5340ee97c5abe5a3892a.tar.gz luajit-edd5cbadc5cdc7b5b66d5340ee97c5abe5a3892a.tar.bz2 luajit-edd5cbadc5cdc7b5b66d5340ee97c5abe5a3892a.zip | |
String buffers, part 2c: abstract out string.format.
Sponsored by fmad.io.
| -rw-r--r-- | src/Makefile.dep | 3 | ||||
| -rw-r--r-- | src/lib_string.c | 85 | ||||
| -rw-r--r-- | src/lj_strfmt.c | 117 | ||||
| -rw-r--r-- | src/lj_strfmt.h | 5 |
4 files changed, 121 insertions, 89 deletions
diff --git a/src/Makefile.dep b/src/Makefile.dep index 315bf632..0bf63391 100644 --- a/src/Makefile.dep +++ b/src/Makefile.dep | |||
| @@ -197,7 +197,8 @@ lj_state.o: lj_state.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ | |||
| 197 | lj_str.o: lj_str.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ | 197 | lj_str.o: lj_str.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ |
| 198 | lj_err.h lj_errmsg.h lj_str.h lj_char.h lj_prng.h | 198 | lj_err.h lj_errmsg.h lj_str.h lj_char.h lj_prng.h |
| 199 | lj_strfmt.o: lj_strfmt.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ | 199 | lj_strfmt.o: lj_strfmt.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
| 200 | lj_buf.h lj_gc.h lj_str.h lj_state.h lj_char.h lj_strfmt.h | 200 | lj_err.h lj_errmsg.h lj_buf.h lj_gc.h lj_str.h lj_meta.h lj_state.h \ |
| 201 | lj_char.h lj_strfmt.h lj_lib.h | ||
| 201 | lj_strfmt_num.o: lj_strfmt_num.c lj_obj.h lua.h luaconf.h lj_def.h \ | 202 | lj_strfmt_num.o: lj_strfmt_num.c lj_obj.h lua.h luaconf.h lj_def.h \ |
| 202 | lj_arch.h lj_buf.h lj_gc.h lj_str.h lj_strfmt.h | 203 | lj_arch.h lj_buf.h lj_gc.h lj_str.h lj_strfmt.h |
| 203 | lj_strscan.o: lj_strscan.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ | 204 | lj_strscan.o: lj_strscan.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
diff --git a/src/lib_string.c b/src/lib_string.c index 4a3ff372..75d855d6 100644 --- a/src/lib_string.c +++ b/src/lib_string.c | |||
| @@ -640,89 +640,14 @@ LJLIB_CF(string_gsub) | |||
| 640 | 640 | ||
| 641 | /* ------------------------------------------------------------------------ */ | 641 | /* ------------------------------------------------------------------------ */ |
| 642 | 642 | ||
| 643 | /* Emulate tostring() inline. */ | ||
| 644 | static 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 | |||
| 661 | LJLIB_CF(string_format) LJLIB_REC(.) | 643 | LJLIB_CF(string_format) LJLIB_REC(.) |
| 662 | { | 644 | { |
| 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; | 645 | int retry = 0; |
| 669 | again: | 646 | SBuf *sb; |
| 670 | arg = 1; | 647 | do { |
| 671 | sb = lj_buf_tmp_(L); | 648 | sb = lj_buf_tmp_(L); |
| 672 | fmt = lj_lib_checkstr(L, arg); | 649 | retry = lj_strfmt_putarg(L, sb, 1, -retry); |
| 673 | lj_strfmt_init(&fs, strdata(fmt), fmt->len); | 650 | } while (retry > 0); |
| 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)); | 651 | setstrV(L, L->top-1, lj_buf_str(L, sb)); |
| 727 | lj_gc_check(L); | 652 | lj_gc_check(L); |
| 728 | return 1; | 653 | return 1; |
diff --git a/src/lj_strfmt.c b/src/lj_strfmt.c index bde3ec0e..a9541d41 100644 --- a/src/lj_strfmt.c +++ b/src/lj_strfmt.c | |||
| @@ -9,11 +9,14 @@ | |||
| 9 | #define LUA_CORE | 9 | #define LUA_CORE |
| 10 | 10 | ||
| 11 | #include "lj_obj.h" | 11 | #include "lj_obj.h" |
| 12 | #include "lj_err.h" | ||
| 12 | #include "lj_buf.h" | 13 | #include "lj_buf.h" |
| 13 | #include "lj_str.h" | 14 | #include "lj_str.h" |
| 15 | #include "lj_meta.h" | ||
| 14 | #include "lj_state.h" | 16 | #include "lj_state.h" |
| 15 | #include "lj_char.h" | 17 | #include "lj_char.h" |
| 16 | #include "lj_strfmt.h" | 18 | #include "lj_strfmt.h" |
| 19 | #include "lj_lib.h" | ||
| 17 | 20 | ||
| 18 | /* -- Format parser ------------------------------------------------------- */ | 21 | /* -- Format parser ------------------------------------------------------- */ |
| 19 | 22 | ||
| @@ -196,10 +199,8 @@ SBuf * LJ_FASTCALL lj_strfmt_putptr(SBuf *sb, const void *v) | |||
| 196 | } | 199 | } |
| 197 | 200 | ||
| 198 | /* Add quoted string to buffer. */ | 201 | /* Add quoted string to buffer. */ |
| 199 | SBuf * LJ_FASTCALL lj_strfmt_putquoted(SBuf *sb, GCstr *str) | 202 | static SBuf *strfmt_putquotedlen(SBuf *sb, const char *s, MSize len) |
| 200 | { | 203 | { |
| 201 | const char *s = strdata(str); | ||
| 202 | MSize len = str->len; | ||
| 203 | lj_buf_putb(sb, '"'); | 204 | lj_buf_putb(sb, '"'); |
| 204 | while (len--) { | 205 | while (len--) { |
| 205 | uint32_t c = (uint32_t)(uint8_t)*s++; | 206 | uint32_t c = (uint32_t)(uint8_t)*s++; |
| @@ -225,6 +226,13 @@ SBuf * LJ_FASTCALL lj_strfmt_putquoted(SBuf *sb, GCstr *str) | |||
| 225 | return sb; | 226 | return sb; |
| 226 | } | 227 | } |
| 227 | 228 | ||
| 229 | #if LJ_HASJIT | ||
| 230 | SBuf * LJ_FASTCALL lj_strfmt_putquoted(SBuf *sb, GCstr *str) | ||
| 231 | { | ||
| 232 | return strfmt_putquotedlen(sb, strdata(str), str->len); | ||
| 233 | } | ||
| 234 | #endif | ||
| 235 | |||
| 228 | /* -- Formatted conversions to buffer ------------------------------------- */ | 236 | /* -- Formatted conversions to buffer ------------------------------------- */ |
| 229 | 237 | ||
| 230 | /* Add formatted char to buffer. */ | 238 | /* Add formatted char to buffer. */ |
| @@ -240,18 +248,26 @@ SBuf *lj_strfmt_putfchar(SBuf *sb, SFormat sf, int32_t c) | |||
| 240 | } | 248 | } |
| 241 | 249 | ||
| 242 | /* Add formatted string to buffer. */ | 250 | /* Add formatted string to buffer. */ |
| 243 | SBuf *lj_strfmt_putfstr(SBuf *sb, SFormat sf, GCstr *str) | 251 | static SBuf *strfmt_putfstrlen(SBuf *sb, SFormat sf, const char *s, MSize len) |
| 244 | { | 252 | { |
| 245 | MSize len = str->len <= STRFMT_PREC(sf) ? str->len : STRFMT_PREC(sf); | ||
| 246 | MSize width = STRFMT_WIDTH(sf); | 253 | MSize width = STRFMT_WIDTH(sf); |
| 247 | char *w = lj_buf_more(sb, width > len ? width : len); | 254 | char *w; |
| 248 | if ((sf & STRFMT_F_LEFT)) w = lj_buf_wmem(w, strdata(str), len); | 255 | if (len > STRFMT_PREC(sf)) len = STRFMT_PREC(sf); |
| 256 | w = lj_buf_more(sb, width > len ? width : len); | ||
| 257 | if ((sf & STRFMT_F_LEFT)) w = lj_buf_wmem(w, s, len); | ||
| 249 | while (width-- > len) *w++ = ' '; | 258 | while (width-- > len) *w++ = ' '; |
| 250 | if (!(sf & STRFMT_F_LEFT)) w = lj_buf_wmem(w, strdata(str), len); | 259 | if (!(sf & STRFMT_F_LEFT)) w = lj_buf_wmem(w, s, len); |
| 251 | sb->w = w; | 260 | sb->w = w; |
| 252 | return sb; | 261 | return sb; |
| 253 | } | 262 | } |
| 254 | 263 | ||
| 264 | #if LJ_HASJIT | ||
| 265 | SBuf *lj_strfmt_putfstr(SBuf *sb, SFormat sf, GCstr *str) | ||
| 266 | { | ||
| 267 | return strfmt_putfstrlen(sb, sf, strdata(str), str->len); | ||
| 268 | } | ||
| 269 | #endif | ||
| 270 | |||
| 255 | /* Add formatted signed/unsigned integer to buffer. */ | 271 | /* Add formatted signed/unsigned integer to buffer. */ |
| 256 | SBuf *lj_strfmt_putfxint(SBuf *sb, SFormat sf, uint64_t k) | 272 | SBuf *lj_strfmt_putfxint(SBuf *sb, SFormat sf, uint64_t k) |
| 257 | { | 273 | { |
| @@ -346,6 +362,91 @@ SBuf *lj_strfmt_putfnum_uint(SBuf *sb, SFormat sf, lua_Number n) | |||
| 346 | return lj_strfmt_putfxint(sb, sf, (uint64_t)k); | 362 | return lj_strfmt_putfxint(sb, sf, (uint64_t)k); |
| 347 | } | 363 | } |
| 348 | 364 | ||
| 365 | /* Format stack arguments to buffer. */ | ||
| 366 | int lj_strfmt_putarg(lua_State *L, SBuf *sb, int arg, int retry) | ||
| 367 | { | ||
| 368 | int narg = (int)(L->top - L->base); | ||
| 369 | GCstr *fmt = lj_lib_checkstr(L, arg); | ||
| 370 | FormatState fs; | ||
| 371 | SFormat sf; | ||
| 372 | lj_strfmt_init(&fs, strdata(fmt), fmt->len); | ||
| 373 | while ((sf = lj_strfmt_parse(&fs)) != STRFMT_EOF) { | ||
| 374 | if (sf == STRFMT_LIT) { | ||
| 375 | lj_buf_putmem(sb, fs.str, fs.len); | ||
| 376 | } else if (sf == STRFMT_ERR) { | ||
| 377 | lj_err_callerv(L, LJ_ERR_STRFMT, | ||
| 378 | strdata(lj_str_new(L, fs.str, fs.len))); | ||
| 379 | } else { | ||
| 380 | TValue *o = &L->base[arg++]; | ||
| 381 | if (arg > narg) | ||
| 382 | lj_err_arg(L, arg, LJ_ERR_NOVAL); | ||
| 383 | switch (STRFMT_TYPE(sf)) { | ||
| 384 | case STRFMT_INT: | ||
| 385 | if (tvisint(o)) { | ||
| 386 | int32_t k = intV(o); | ||
| 387 | if (sf == STRFMT_INT) | ||
| 388 | lj_strfmt_putint(sb, k); /* Shortcut for plain %d. */ | ||
| 389 | else | ||
| 390 | lj_strfmt_putfxint(sb, sf, k); | ||
| 391 | } else { | ||
| 392 | lj_strfmt_putfnum_int(sb, sf, lj_lib_checknum(L, arg)); | ||
| 393 | } | ||
| 394 | break; | ||
| 395 | case STRFMT_UINT: | ||
| 396 | if (tvisint(o)) | ||
| 397 | lj_strfmt_putfxint(sb, sf, intV(o)); | ||
| 398 | else | ||
| 399 | lj_strfmt_putfnum_uint(sb, sf, lj_lib_checknum(L, arg)); | ||
| 400 | break; | ||
| 401 | case STRFMT_NUM: | ||
| 402 | lj_strfmt_putfnum(sb, sf, lj_lib_checknum(L, arg)); | ||
| 403 | break; | ||
| 404 | case STRFMT_STR: { | ||
| 405 | MSize len; | ||
| 406 | const char *s; | ||
| 407 | cTValue *mo; | ||
| 408 | if (LJ_UNLIKELY(!tvisstr(o)) && retry >= 0 && | ||
| 409 | !tvisnil(mo = lj_meta_lookup(L, o, MM_tostring))) { | ||
| 410 | /* Call __tostring metamethod once. */ | ||
| 411 | copyTV(L, L->top++, mo); | ||
| 412 | copyTV(L, L->top++, o); | ||
| 413 | lua_call(L, 1, 1); | ||
| 414 | o = &L->base[arg-1]; /* Stack may have been reallocated. */ | ||
| 415 | copyTV(L, o, --L->top); /* Replace inline for retry. */ | ||
| 416 | if (retry < 2) { /* Global buffer may have been overwritten. */ | ||
| 417 | retry = 1; | ||
| 418 | break; | ||
| 419 | } | ||
| 420 | } | ||
| 421 | if (LJ_LIKELY(tvisstr(o))) { | ||
| 422 | len = strV(o)->len; | ||
| 423 | s = strVdata(o); | ||
| 424 | } else { | ||
| 425 | GCstr *str = lj_strfmt_obj(L, o); | ||
| 426 | len = str->len; | ||
| 427 | s = strdata(str); | ||
| 428 | } | ||
| 429 | if ((sf & STRFMT_T_QUOTED)) | ||
| 430 | strfmt_putquotedlen(sb, s, len); /* No formatting. */ | ||
| 431 | else | ||
| 432 | strfmt_putfstrlen(sb, sf, s, len); | ||
| 433 | break; | ||
| 434 | } | ||
| 435 | case STRFMT_CHAR: | ||
| 436 | lj_strfmt_putfchar(sb, sf, lj_lib_checkint(L, arg)); | ||
| 437 | break; | ||
| 438 | case STRFMT_PTR: /* No formatting. */ | ||
| 439 | lj_strfmt_putptr(sb, lj_obj_ptr(G(L), o)); | ||
| 440 | break; | ||
| 441 | default: | ||
| 442 | lj_assertL(0, "bad string format type"); | ||
| 443 | break; | ||
| 444 | } | ||
| 445 | } | ||
| 446 | } | ||
| 447 | return retry; | ||
| 448 | } | ||
| 449 | |||
| 349 | /* -- Conversions to strings ---------------------------------------------- */ | 450 | /* -- Conversions to strings ---------------------------------------------- */ |
| 350 | 451 | ||
| 351 | /* Convert integer to string. */ | 452 | /* Convert integer to string. */ |
diff --git a/src/lj_strfmt.h b/src/lj_strfmt.h index fa25dcd0..cb2c7360 100644 --- a/src/lj_strfmt.h +++ b/src/lj_strfmt.h | |||
| @@ -95,7 +95,9 @@ LJ_FUNC SBuf * LJ_FASTCALL lj_strfmt_putint(SBuf *sb, int32_t k); | |||
| 95 | LJ_FUNC SBuf * LJ_FASTCALL lj_strfmt_putnum(SBuf *sb, cTValue *o); | 95 | LJ_FUNC SBuf * LJ_FASTCALL lj_strfmt_putnum(SBuf *sb, cTValue *o); |
| 96 | #endif | 96 | #endif |
| 97 | LJ_FUNC SBuf * LJ_FASTCALL lj_strfmt_putptr(SBuf *sb, const void *v); | 97 | LJ_FUNC SBuf * LJ_FASTCALL lj_strfmt_putptr(SBuf *sb, const void *v); |
| 98 | #if LJ_HASJIT | ||
| 98 | LJ_FUNC SBuf * LJ_FASTCALL lj_strfmt_putquoted(SBuf *sb, GCstr *str); | 99 | LJ_FUNC SBuf * LJ_FASTCALL lj_strfmt_putquoted(SBuf *sb, GCstr *str); |
| 100 | #endif | ||
| 99 | 101 | ||
| 100 | /* Formatted conversions to buffer. */ | 102 | /* Formatted conversions to buffer. */ |
| 101 | LJ_FUNC SBuf *lj_strfmt_putfxint(SBuf *sb, SFormat sf, uint64_t k); | 103 | LJ_FUNC SBuf *lj_strfmt_putfxint(SBuf *sb, SFormat sf, uint64_t k); |
| @@ -103,7 +105,10 @@ LJ_FUNC SBuf *lj_strfmt_putfnum_int(SBuf *sb, SFormat sf, lua_Number n); | |||
| 103 | LJ_FUNC SBuf *lj_strfmt_putfnum_uint(SBuf *sb, SFormat sf, lua_Number n); | 105 | LJ_FUNC SBuf *lj_strfmt_putfnum_uint(SBuf *sb, SFormat sf, lua_Number n); |
| 104 | LJ_FUNC SBuf *lj_strfmt_putfnum(SBuf *sb, SFormat, lua_Number n); | 106 | LJ_FUNC SBuf *lj_strfmt_putfnum(SBuf *sb, SFormat, lua_Number n); |
| 105 | LJ_FUNC SBuf *lj_strfmt_putfchar(SBuf *sb, SFormat, int32_t c); | 107 | LJ_FUNC SBuf *lj_strfmt_putfchar(SBuf *sb, SFormat, int32_t c); |
| 108 | #if LJ_HASJIT | ||
| 106 | LJ_FUNC SBuf *lj_strfmt_putfstr(SBuf *sb, SFormat, GCstr *str); | 109 | LJ_FUNC SBuf *lj_strfmt_putfstr(SBuf *sb, SFormat, GCstr *str); |
| 110 | #endif | ||
| 111 | LJ_FUNC int lj_strfmt_putarg(lua_State *L, SBuf *sb, int arg, int retry); | ||
| 107 | 112 | ||
| 108 | /* Conversions to strings. */ | 113 | /* Conversions to strings. */ |
| 109 | LJ_FUNC GCstr * LJ_FASTCALL lj_strfmt_int(lua_State *L, int32_t k); | 114 | LJ_FUNC GCstr * LJ_FASTCALL lj_strfmt_int(lua_State *L, int32_t k); |
