aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Pall <mike>2013-05-12 22:37:02 +0200
committerMike Pall <mike>2013-05-12 23:13:27 +0200
commit5bb1f0edac809302b299e189fb3c4006e0bc939a (patch)
tree390fd09e3d3e5ad80f38ae6f7b78cc27dd769221
parentbb2cc1dcaff90406f431f90ef822ddd7df70200d (diff)
downloadluajit-5bb1f0edac809302b299e189fb3c4006e0bc939a.tar.gz
luajit-5bb1f0edac809302b299e189fb3c4006e0bc939a.tar.bz2
luajit-5bb1f0edac809302b299e189fb3c4006e0bc939a.zip
Refactor string.format().
-rw-r--r--src/Makefile3
-rw-r--r--src/Makefile.dep30
-rw-r--r--src/lib_string.c236
-rw-r--r--src/lj_errmsg.h5
-rw-r--r--src/lj_str.h2
-rw-r--r--src/lj_strfmt.c295
-rw-r--r--src/lj_strfmt.h84
-rw-r--r--src/ljamalg.c1
8 files changed, 463 insertions, 193 deletions
diff --git a/src/Makefile b/src/Makefile
index 0065b8c2..b9101a74 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -439,7 +439,8 @@ LJLIB_C= $(LJLIB_O:.o=.c)
439LJCORE_O= lj_gc.o lj_err.o lj_char.o lj_bc.o lj_obj.o lj_buf.o \ 439LJCORE_O= lj_gc.o lj_err.o lj_char.o lj_bc.o lj_obj.o lj_buf.o \
440 lj_str.o lj_tab.o lj_func.o lj_udata.o lj_meta.o lj_debug.o \ 440 lj_str.o lj_tab.o lj_func.o lj_udata.o lj_meta.o lj_debug.o \
441 lj_state.o lj_dispatch.o lj_vmevent.o lj_vmmath.o lj_strscan.o \ 441 lj_state.o lj_dispatch.o lj_vmevent.o lj_vmmath.o lj_strscan.o \
442 lj_api.o lj_lex.o lj_parse.o lj_bcread.o lj_bcwrite.o lj_load.o \ 442 lj_strfmt.o lj_api.o \
443 lj_lex.o lj_parse.o lj_bcread.o lj_bcwrite.o lj_load.o \
443 lj_ir.o lj_opt_mem.o lj_opt_fold.o lj_opt_narrow.o \ 444 lj_ir.o lj_opt_mem.o lj_opt_fold.o lj_opt_narrow.o \
444 lj_opt_dce.o lj_opt_loop.o lj_opt_split.o lj_opt_sink.o \ 445 lj_opt_dce.o lj_opt_loop.o lj_opt_split.o lj_opt_sink.o \
445 lj_mcode.o lj_snap.o lj_record.o lj_crecord.o lj_ffrecord.o \ 446 lj_mcode.o lj_snap.o lj_record.o lj_crecord.o lj_ffrecord.o \
diff --git a/src/Makefile.dep b/src/Makefile.dep
index 074d0908..8e01865c 100644
--- a/src/Makefile.dep
+++ b/src/Makefile.dep
@@ -35,7 +35,7 @@ lib_package.o: lib_package.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
35lib_string.o: lib_string.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \ 35lib_string.o: lib_string.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
36 lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h \ 36 lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h \
37 lj_tab.h lj_meta.h lj_state.h lj_ff.h lj_ffdef.h lj_bcdump.h lj_lex.h \ 37 lj_tab.h lj_meta.h lj_state.h lj_ff.h lj_ffdef.h lj_bcdump.h lj_lex.h \
38 lj_char.h lj_lib.h lj_libdef.h 38 lj_char.h lj_strfmt.h lj_lib.h lj_libdef.h
39lib_table.o: lib_table.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \ 39lib_table.o: lib_table.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
40 lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h \ 40 lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h \
41 lj_tab.h lj_lib.h lj_libdef.h 41 lj_tab.h lj_lib.h lj_libdef.h
@@ -179,6 +179,8 @@ lj_state.o: lj_state.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
179 lj_ir.h lj_dispatch.h lj_traceerr.h lj_vm.h lj_lex.h lj_alloc.h 179 lj_ir.h lj_dispatch.h lj_traceerr.h lj_vm.h lj_lex.h lj_alloc.h
180lj_str.o: lj_str.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ 180lj_str.o: lj_str.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
181 lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_state.h lj_char.h 181 lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_state.h lj_char.h
182lj_strfmt.o: lj_strfmt.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
183 lj_buf.h lj_gc.h lj_str.h lj_char.h lj_strfmt.h
182lj_strscan.o: lj_strscan.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ 184lj_strscan.o: lj_strscan.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
183 lj_char.h lj_strscan.h 185 lj_char.h lj_strscan.h
184lj_tab.o: lj_tab.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ 186lj_tab.o: lj_tab.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
@@ -203,19 +205,19 @@ ljamalg.o: ljamalg.c lua.h luaconf.h lauxlib.h lj_gc.c lj_obj.h lj_def.h \
203 lj_bc.c lj_bcdef.h lj_obj.c lj_buf.c lj_str.c lj_tab.c lj_func.c \ 205 lj_bc.c lj_bcdef.h lj_obj.c lj_buf.c lj_str.c lj_tab.c lj_func.c \
204 lj_udata.c lj_meta.c lj_strscan.h lj_lib.h lj_debug.c lj_state.c \ 206 lj_udata.c lj_meta.c lj_strscan.h lj_lib.h lj_debug.c lj_state.c \
205 lj_lex.h lj_alloc.h lj_dispatch.c lj_ccallback.h luajit.h lj_vmevent.c \ 207 lj_lex.h lj_alloc.h lj_dispatch.c lj_ccallback.h luajit.h lj_vmevent.c \
206 lj_vmevent.h lj_vmmath.c lj_strscan.c lj_api.c lj_lex.c lualib.h \ 208 lj_vmevent.h lj_vmmath.c lj_strscan.c lj_strfmt.c lj_strfmt.h lj_api.c \
207 lj_parse.h lj_parse.c lj_bcread.c lj_bcdump.h lj_bcwrite.c lj_load.c \ 209 lj_lex.c lualib.h lj_parse.h lj_parse.c lj_bcread.c lj_bcdump.h \
208 lj_ctype.c lj_cdata.c lj_cconv.h lj_cconv.c lj_ccall.c lj_ccall.h \ 210 lj_bcwrite.c lj_load.c lj_ctype.c lj_cdata.c lj_cconv.h lj_cconv.c \
209 lj_ccallback.c lj_target.h lj_target_*.h lj_mcode.h lj_carith.c \ 211 lj_ccall.c lj_ccall.h lj_ccallback.c lj_target.h lj_target_*.h \
210 lj_carith.h lj_clib.c lj_clib.h lj_cparse.c lj_cparse.h lj_lib.c lj_ir.c \ 212 lj_mcode.h lj_carith.c lj_carith.h lj_clib.c lj_clib.h lj_cparse.c \
211 lj_ircall.h lj_iropt.h lj_opt_mem.c lj_opt_fold.c lj_folddef.h \ 213 lj_cparse.h lj_lib.c lj_ir.c lj_ircall.h lj_iropt.h lj_opt_mem.c \
212 lj_opt_narrow.c lj_opt_dce.c lj_opt_loop.c lj_snap.h lj_opt_split.c \ 214 lj_opt_fold.c lj_folddef.h lj_opt_narrow.c lj_opt_dce.c lj_opt_loop.c \
213 lj_opt_sink.c lj_mcode.c lj_snap.c lj_record.c lj_record.h lj_ffrecord.h \ 215 lj_snap.h lj_opt_split.c lj_opt_sink.c lj_mcode.c lj_snap.c lj_record.c \
214 lj_crecord.c lj_crecord.h lj_ffrecord.c lj_recdef.h lj_asm.c lj_asm.h \ 216 lj_record.h lj_ffrecord.h lj_crecord.c lj_crecord.h lj_ffrecord.c \
215 lj_emit_*.h lj_asm_*.h lj_trace.c lj_gdbjit.h lj_gdbjit.c lj_alloc.c \ 217 lj_recdef.h lj_asm.c lj_asm.h lj_emit_*.h lj_asm_*.h lj_trace.c \
216 lib_aux.c lib_base.c lj_libdef.h lib_math.c lib_string.c lib_table.c \ 218 lj_gdbjit.h lj_gdbjit.c lj_alloc.c lib_aux.c lib_base.c lj_libdef.h \
217 lib_io.c lib_os.c lib_package.c lib_debug.c lib_bit.c lib_jit.c \ 219 lib_math.c lib_string.c lib_table.c lib_io.c lib_os.c lib_package.c \
218 lib_ffi.c lib_init.c 220 lib_debug.c lib_bit.c lib_jit.c lib_ffi.c lib_init.c
219luajit.o: luajit.c lua.h luaconf.h lauxlib.h lualib.h luajit.h lj_arch.h 221luajit.o: luajit.c lua.h luaconf.h lauxlib.h lualib.h luajit.h lj_arch.h
220host/buildvm.o: host/buildvm.c host/buildvm.h lj_def.h lua.h luaconf.h \ 222host/buildvm.o: host/buildvm.c host/buildvm.h lj_def.h lua.h luaconf.h \
221 lj_arch.h lj_obj.h lj_def.h lj_arch.h lj_gc.h lj_obj.h lj_bc.h lj_ir.h \ 223 lj_arch.h lj_obj.h lj_def.h lj_arch.h lj_gc.h lj_obj.h lj_bc.h lj_ir.h \
diff --git a/src/lib_string.c b/src/lib_string.c
index 2c86daa4..b955e933 100644
--- a/src/lib_string.c
+++ b/src/lib_string.c
@@ -6,8 +6,6 @@
6** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h 6** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
7*/ 7*/
8 8
9#include <stdio.h>
10
11#define lib_string_c 9#define lib_string_c
12#define LUA_LIB 10#define LUA_LIB
13 11
@@ -26,6 +24,7 @@
26#include "lj_ff.h" 24#include "lj_ff.h"
27#include "lj_bcdump.h" 25#include "lj_bcdump.h"
28#include "lj_char.h" 26#include "lj_char.h"
27#include "lj_strfmt.h"
29#include "lj_lib.h" 28#include "lj_lib.h"
30 29
31/* ------------------------------------------------------------------------ */ 30/* ------------------------------------------------------------------------ */
@@ -641,130 +640,20 @@ LJLIB_CF(string_gsub)
641 640
642/* ------------------------------------------------------------------------ */ 641/* ------------------------------------------------------------------------ */
643 642
644/* Max. buffer size needed (at least #string.format("%99.99f", -1e308)). */
645#define STRING_FMT_MAXBUF 512
646/* Valid format specifier flags. */
647#define STRING_FMT_FLAGS "-+ #0"
648/* Max. format specifier size. */
649#define STRING_FMT_MAXSPEC \
650 (sizeof(STRING_FMT_FLAGS) + sizeof(LUA_INTFRMLEN) + 10)
651
652/* Add quoted string to buffer. */
653static void string_fmt_quoted(SBuf *sb, GCstr *str)
654{
655 const char *s = strdata(str);
656 MSize len = str->len;
657 lj_buf_putb(sb, '"');
658 while (len--) {
659 uint32_t c = (uint32_t)(uint8_t)*s++;
660 char *p = lj_buf_more(sb, 4);
661 if (c == '"' || c == '\\' || c == '\n') {
662 *p++ = '\\';
663 } else if (lj_char_iscntrl(c)) { /* This can only be 0-31 or 127. */
664 uint32_t d;
665 *p++ = '\\';
666 if (c >= 100 || lj_char_isdigit((uint8_t)*s)) {
667 *p++ = (char)('0'+(c >= 100)); if (c >= 100) c -= 100;
668 goto tens;
669 } else if (c >= 10) {
670 tens:
671 d = (c * 205) >> 11; c -= d * 10; *p++ = (char)('0'+d);
672 }
673 c += '0';
674 }
675 *p++ = (char)c;
676 setsbufP(sb, p);
677 }
678 lj_buf_putb(sb, '"');
679}
680
681/* Scan format and generate format specifier. */
682static const char *string_fmt_scan(lua_State *L, char *spec, const char *fmt)
683{
684 const char *p = fmt;
685 while (*p && strchr(STRING_FMT_FLAGS, *p) != NULL) p++; /* Skip flags. */
686 if ((size_t)(p - fmt) >= sizeof(STRING_FMT_FLAGS))
687 lj_err_caller(L, LJ_ERR_STRFMTR);
688 if (lj_char_isdigit((uint8_t)*p)) p++; /* Skip max. 2 digits for width. */
689 if (lj_char_isdigit((uint8_t)*p)) p++;
690 if (*p == '.') {
691 p++;
692 if (lj_char_isdigit((uint8_t)*p)) p++; /* Skip max. 2 digits for prec. */
693 if (lj_char_isdigit((uint8_t)*p)) p++;
694 }
695 if (lj_char_isdigit((uint8_t)*p))
696 lj_err_caller(L, LJ_ERR_STRFMTW);
697 *spec++ = '%';
698 strncpy(spec, fmt, (size_t)(p - fmt + 1));
699 spec += p - fmt + 1;
700 *spec = '\0';
701 return p;
702}
703
704/* Patch LUA_INTRFRMLEN into integer format specifier. */
705static void string_fmt_intfmt(char *spec)
706{
707 char c;
708 do {
709 c = *spec++;
710 } while (*spec);
711 *--spec = (LUA_INTFRMLEN)[0];
712 if ((LUA_INTFRMLEN)[1]) *++spec = (LUA_INTFRMLEN)[1];
713 *++spec = c;
714 *++spec = '\0';
715}
716
717/* Derive sprintf argument for integer format. Ugly. */
718static LUA_INTFRM_T string_fmt_intarg(lua_State *L, int arg)
719{
720 if (sizeof(LUA_INTFRM_T) == 4) {
721 return (LUA_INTFRM_T)lj_lib_checkbit(L, arg);
722 } else {
723 cTValue *o;
724 lj_lib_checknumber(L, arg);
725 o = L->base+arg-1;
726 if (tvisint(o))
727 return (LUA_INTFRM_T)intV(o);
728 else
729 return (LUA_INTFRM_T)numV(o);
730 }
731}
732
733/* Derive sprintf argument for unsigned integer format. Ugly. */
734static unsigned LUA_INTFRM_T string_fmt_uintarg(lua_State *L, int arg)
735{
736 if (sizeof(LUA_INTFRM_T) == 4) {
737 return (unsigned LUA_INTFRM_T)lj_lib_checkbit(L, arg);
738 } else {
739 cTValue *o;
740 lj_lib_checknumber(L, arg);
741 o = L->base+arg-1;
742 if (tvisint(o))
743 return (unsigned LUA_INTFRM_T)intV(o);
744 else if ((int32_t)o->u32.hi < 0)
745 return (unsigned LUA_INTFRM_T)(LUA_INTFRM_T)numV(o);
746 else
747 return (unsigned LUA_INTFRM_T)numV(o);
748 }
749}
750
751/* Emulate tostring() inline. */ 643/* Emulate tostring() inline. */
752static GCstr *string_fmt_tostring(lua_State *L, int arg) 644static GCstr *string_fmt_tostring(lua_State *L, int arg, int retry)
753{ 645{
754 TValue *o = L->base+arg-1; 646 TValue *o = L->base+arg-1;
755 cTValue *mo; 647 cTValue *mo;
756 lua_assert(o < L->top); /* Caller already checks for existence. */ 648 lua_assert(o < L->top); /* Caller already checks for existence. */
757 if (LJ_LIKELY(tvisstr(o))) 649 if (LJ_LIKELY(tvisstr(o)))
758 return strV(o); 650 return strV(o);
759 if (!tvisnil(mo = lj_meta_lookup(L, o, MM_tostring))) { 651 if (retry != 2 && !tvisnil(mo = lj_meta_lookup(L, o, MM_tostring))) {
760 copyTV(L, L->top++, mo); 652 copyTV(L, L->top++, mo);
761 copyTV(L, L->top++, o); 653 copyTV(L, L->top++, o);
762 lua_call(L, 1, 1); 654 lua_call(L, 1, 1);
763 L->top--; 655 copyTV(L, L->base+arg-1, --L->top);
764 if (tvisstr(L->top)) 656 return NULL; /* Buffer may be overwritten, retry. */
765 return strV(L->top);
766 o = L->base+arg-1;
767 copyTV(L, o, L->top);
768 } 657 }
769 if (tvisnumber(o)) { 658 if (tvisnumber(o)) {
770 return lj_str_fromnumber(L, o); 659 return lj_str_fromnumber(L, o);
@@ -775,84 +664,85 @@ static GCstr *string_fmt_tostring(lua_State *L, int arg)
775 } else if (tvistrue(o)) { 664 } else if (tvistrue(o)) {
776 return lj_str_newlit(L, "true"); 665 return lj_str_newlit(L, "true");
777 } else { 666 } else {
778 if (tvisfunc(o) && isffunc(funcV(o))) 667 char buf[8+2+2+16], *p = buf;
779 lj_str_pushf(L, "function: builtin#%d", funcV(o)->c.ffid); 668 if (tvisfunc(o) && isffunc(funcV(o))) {
780 else 669 p = lj_buf_wmem(p, "function: builtin#", 18);
781 lj_str_pushf(L, "%s: %p", lj_typename(o), lua_topointer(L, arg)); 670 p = lj_str_bufint(p, funcV(o)->c.ffid);
782 L->top--; 671 } else {
783 return strV(L->top); 672 p = lj_buf_wmem(p, lj_typename(o), strlen(lj_typename(o)));
673 *p++ = ':'; *p++ = ' ';
674 p = lj_str_bufptr(p, lua_topointer(L, arg));
675 }
676 return lj_str_new(L, buf, (size_t)(p - buf));
784 } 677 }
785} 678}
786 679
787LJLIB_CF(string_format) 680LJLIB_CF(string_format)
788{ 681{
789 int arg = 1, top = (int)(L->top - L->base); 682 int arg, top = (int)(L->top - L->base);
790 GCstr *sfmt = lj_lib_checkstr(L, arg); 683 GCstr *sfmt;
791 const char *fmt = strdata(sfmt); 684 SBuf *sb;
792 const char *efmt = fmt + sfmt->len; 685 FormatState fs;
793 SBuf *sb = lj_buf_tmp_(L); 686 SFormat sf;
794 while (fmt < efmt) { 687 int retry = 0;
795 if (*fmt != L_ESC || *++fmt == L_ESC) { 688again:
796 lj_buf_putb(sb, *fmt++); 689 arg = 1;
690 sb = lj_buf_tmp_(L);
691 sfmt = lj_lib_checkstr(L, arg);
692 lj_strfmt_init(&fs, strdata(sfmt), sfmt->len);
693 while ((sf = lj_strfmt_parse(&fs)) != STRFMT_EOF) {
694 if (sf == STRFMT_LIT) {
695 lj_buf_putmem(sb, fs.str, fs.len);
696 } else if (sf == STRFMT_ERR) {
697 lj_err_callerv(L, LJ_ERR_STRFMT, strdata(lj_str_new(L, fs.str, fs.len)));
797 } else { 698 } else {
798 char buf[STRING_FMT_MAXBUF];
799 char spec[STRING_FMT_MAXSPEC];
800 MSize len = 0;
801 if (++arg > top) 699 if (++arg > top)
802 luaL_argerror(L, arg, lj_obj_typename[0]); 700 luaL_argerror(L, arg, lj_obj_typename[0]);
803 fmt = string_fmt_scan(L, spec, fmt); 701 switch (STRFMT_TYPE(sf)) {
804 switch (*fmt++) { 702 case STRFMT_INT:
805 case 'c': 703 if (tvisint(L->base+arg-1)) {
806 len = (MSize)sprintf(buf, spec, lj_lib_checkint(L, arg)); 704 int32_t k = intV(L->base+arg-1);
705 if (sf == STRFMT_INT)
706 lj_buf_putint(sb, k); /* Shortcut for plain %d. */
707 else
708 lj_strfmt_putxint(sb, sf, k);
709 } else {
710 lj_strfmt_putnum_int(sb, sf, lj_lib_checknum(L, arg));
711 }
807 break; 712 break;
808 case 'd': case 'i': 713 case STRFMT_UINT:
809 string_fmt_intfmt(spec); 714 if (tvisint(L->base+arg-1))
810 len = (MSize)sprintf(buf, spec, string_fmt_intarg(L, arg)); 715 lj_strfmt_putxint(sb, sf, intV(L->base+arg-1));
716 else
717 lj_strfmt_putnum_uint(sb, sf, lj_lib_checknum(L, arg));
811 break; 718 break;
812 case 'o': case 'u': case 'x': case 'X': 719 case STRFMT_NUM:
813 string_fmt_intfmt(spec); 720 lj_strfmt_putnum(sb, sf, lj_lib_checknum(L, arg));
814 len = (MSize)sprintf(buf, spec, string_fmt_uintarg(L, arg));
815 break; 721 break;
816 case 'e': case 'E': case 'f': case 'g': case 'G': case 'a': case 'A': { 722 case STRFMT_STR: {
817 TValue tv; 723 GCstr *str = string_fmt_tostring(L, arg, retry);
818 tv.n = lj_lib_checknum(L, arg); 724 if (str == NULL)
819 if (LJ_UNLIKELY((tv.u32.hi << 1) >= 0xffe00000)) { 725 retry = 1;
820 /* Canonicalize output of non-finite values. */ 726 else if ((sf & STRFMT_T_QUOTED))
821 char nbuf[LJ_STR_NUMBUF]; 727 lj_strfmt_putquoted(sb, str);
822 char *p = lj_str_bufnum(nbuf, &tv); 728 else
823 if (fmt[-1] < 'a') { *(p-3) -= 0x20; *(p-2) -= 0x20; *(p-1) -= 0x20; } 729 lj_strfmt_putstr(sb, sf, str);
824 *p = '\0';
825 for (p = spec; *p < 'A' && *p != '.'; p++) ;
826 *p++ = 's'; *p = '\0';
827 len = (MSize)sprintf(buf, spec, nbuf);
828 break;
829 }
830 len = (MSize)sprintf(buf, spec, (double)tv.n);
831 break; 730 break;
832 } 731 }
833 case 'q': 732 case STRFMT_CHAR:
834 string_fmt_quoted(sb, lj_lib_checkstr(L, arg)); 733 lj_strfmt_putchar(sb, sf, lj_lib_checkint(L, arg));
835 continue; 734 break;
836 case 'p': 735 case STRFMT_PTR: /* No formatting. */
837 setsbufP(sb, lj_str_bufptr(lj_buf_more(sb, LJ_STR_PTRBUF), 736 setsbufP(sb, lj_str_bufptr(lj_buf_more(sb, LJ_STR_PTRBUF),
838 lua_topointer(L, arg))); 737 lua_topointer(L, arg)));
839 continue;
840 case 's': {
841 GCstr *str = string_fmt_tostring(L, arg);
842 if (!strchr(spec, '.') && str->len >= 100) { /* Format overflow? */
843 lj_buf_putmem(sb, strdata(str), str->len); /* Use orig string. */
844 continue;
845 }
846 len = (MSize)sprintf(buf, spec, strdata(str));
847 break; 738 break;
848 }
849 default: 739 default:
850 lj_err_callerv(L, LJ_ERR_STRFMTO, fmt[-1] ? fmt[-1] : ' '); 740 lua_assert(0);
851 break; 741 break;
852 } 742 }
853 lj_buf_putmem(sb, buf, len);
854 } 743 }
855 } 744 }
745 if (retry++ == 1) goto again;
856 setstrV(L, L->top-1, lj_buf_str(L, sb)); 746 setstrV(L, L->top-1, lj_buf_str(L, sb));
857 lj_gc_check(L); 747 lj_gc_check(L);
858 return 1; 748 return 1;
diff --git a/src/lj_errmsg.h b/src/lj_errmsg.h
index fd46acd4..e62dc237 100644
--- a/src/lj_errmsg.h
+++ b/src/lj_errmsg.h
@@ -96,9 +96,7 @@ ERRDEF(STRPATX, "pattern too complex")
96ERRDEF(STRCAPI, "invalid capture index") 96ERRDEF(STRCAPI, "invalid capture index")
97ERRDEF(STRCAPN, "too many captures") 97ERRDEF(STRCAPN, "too many captures")
98ERRDEF(STRCAPU, "unfinished capture") 98ERRDEF(STRCAPU, "unfinished capture")
99ERRDEF(STRFMTO, "invalid option " LUA_QL("%%%c") " to " LUA_QL("format")) 99ERRDEF(STRFMT, "invalid option " LUA_QS " to " LUA_QL("format"))
100ERRDEF(STRFMTR, "invalid format (repeated flags)")
101ERRDEF(STRFMTW, "invalid format (width or precision too long)")
102ERRDEF(STRGSRV, "invalid replacement value (a %s)") 100ERRDEF(STRGSRV, "invalid replacement value (a %s)")
103ERRDEF(BADMODN, "name conflict for module " LUA_QS) 101ERRDEF(BADMODN, "name conflict for module " LUA_QS)
104#if LJ_HASJIT 102#if LJ_HASJIT
@@ -117,7 +115,6 @@ ERRDEF(JITOPT, "unknown or malformed optimization flag " LUA_QS)
117/* Lexer/parser errors. */ 115/* Lexer/parser errors. */
118ERRDEF(XMODE, "attempt to load chunk with wrong mode") 116ERRDEF(XMODE, "attempt to load chunk with wrong mode")
119ERRDEF(XNEAR, "%s near " LUA_QS) 117ERRDEF(XNEAR, "%s near " LUA_QS)
120ERRDEF(XELEM, "lexical element too long")
121ERRDEF(XLINES, "chunk has too many lines") 118ERRDEF(XLINES, "chunk has too many lines")
122ERRDEF(XLEVELS, "chunk has too many syntax levels") 119ERRDEF(XLEVELS, "chunk has too many syntax levels")
123ERRDEF(XNUMBER, "malformed number") 120ERRDEF(XNUMBER, "malformed number")
diff --git a/src/lj_str.h b/src/lj_str.h
index dd9b3d94..6e08764e 100644
--- a/src/lj_str.h
+++ b/src/lj_str.h
@@ -25,7 +25,7 @@ LJ_FUNC void LJ_FASTCALL lj_str_free(global_State *g, GCstr *s);
25#define lj_str_newlit(L, s) (lj_str_new(L, "" s, sizeof(s)-1)) 25#define lj_str_newlit(L, s) (lj_str_new(L, "" s, sizeof(s)-1))
26 26
27/* Type conversions. */ 27/* Type conversions. */
28LJ_FUNC char * LJ_FASTCALL lj_str_bufint(char *buf, int32_t k); 28LJ_FUNC char * LJ_FASTCALL lj_str_bufint(char *p, int32_t k);
29LJ_FUNC char * LJ_FASTCALL lj_str_bufnum(char *p, cTValue *o); 29LJ_FUNC char * LJ_FASTCALL lj_str_bufnum(char *p, cTValue *o);
30LJ_FUNC char * LJ_FASTCALL lj_str_bufptr(char *p, const void *v); 30LJ_FUNC char * LJ_FASTCALL lj_str_bufptr(char *p, const void *v);
31LJ_FUNC const char *lj_str_buftv(char *buf, cTValue *o, MSize *lenp); 31LJ_FUNC const char *lj_str_buftv(char *buf, cTValue *o, MSize *lenp);
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
diff --git a/src/lj_strfmt.h b/src/lj_strfmt.h
new file mode 100644
index 00000000..b3556f1a
--- /dev/null
+++ b/src/lj_strfmt.h
@@ -0,0 +1,84 @@
1/*
2** String formatting.
3** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
4*/
5
6#ifndef _LJ_STRFMT_H
7#define _LJ_STRFMT_H
8
9#include "lj_obj.h"
10
11typedef uint32_t SFormat; /* Format indicator. */
12
13/* Format parser state. */
14typedef struct FormatState {
15 const uint8_t *p; /* Current format string pointer. */
16 const uint8_t *e; /* End of format string. */
17 const char *str; /* Returned literal string. */
18 MSize len; /* Size of literal string. */
19} FormatState;
20
21/* Format types (max. 16). */
22typedef enum FormatType {
23 STRFMT_EOF, STRFMT_ERR, STRFMT_LIT,
24 STRFMT_INT, STRFMT_UINT, STRFMT_NUM, STRFMT_STR, STRFMT_CHAR, STRFMT_PTR
25} FormatType;
26
27/* Format subtypes (bits are reused). */
28#define STRFMT_T_HEX 0x0010 /* STRFMT_UINT */
29#define STRFMT_T_OCT 0x0020 /* STRFMT_UINT */
30#define STRFMT_T_FP_A 0x0000 /* STRFMT_NUM */
31#define STRFMT_T_FP_E 0x0010 /* STRFMT_NUM */
32#define STRFMT_T_FP_F 0x0020 /* STRFMT_NUM */
33#define STRFMT_T_FP_G 0x0030 /* STRFMT_NUM */
34#define STRFMT_T_QUOTED 0x0010 /* STRFMT_STR */
35
36/* Format flags. */
37#define STRFMT_F_LEFT 0x0100
38#define STRFMT_F_PLUS 0x0200
39#define STRFMT_F_ZERO 0x0400
40#define STRFMT_F_SPACE 0x0800
41#define STRFMT_F_ALT 0x1000
42#define STRFMT_F_UPPER 0x2000
43
44/* Format indicator fields. */
45#define STRFMT_SH_WIDTH 16
46#define STRFMT_SH_PREC 24
47
48#define STRFMT_TYPE(sf) ((FormatType)((sf) & 15))
49#define STRFMT_WIDTH(sf) (((sf) >> STRFMT_SH_WIDTH) & 255u)
50#define STRFMT_PREC(sf) ((((sf) >> STRFMT_SH_PREC) & 255u) - 1u)
51#define STRFMT_FP(sf) (((sf) >> 4) & 3)
52
53/* Formats for conversion characters. */
54#define STRFMT_A (STRFMT_NUM|STRFMT_T_FP_A)
55#define STRFMT_C (STRFMT_CHAR)
56#define STRFMT_D (STRFMT_INT)
57#define STRFMT_E (STRFMT_NUM|STRFMT_T_FP_E)
58#define STRFMT_F (STRFMT_NUM|STRFMT_T_FP_F)
59#define STRFMT_G (STRFMT_NUM|STRFMT_T_FP_G)
60#define STRFMT_I STRFMT_D
61#define STRFMT_O (STRFMT_UINT|STRFMT_T_OCT)
62#define STRFMT_P (STRFMT_PTR)
63#define STRFMT_Q (STRFMT_STR|STRFMT_T_QUOTED)
64#define STRFMT_S (STRFMT_STR)
65#define STRFMT_U (STRFMT_UINT)
66#define STRFMT_X (STRFMT_UINT|STRFMT_T_HEX)
67
68static LJ_AINLINE void lj_strfmt_init(FormatState *fs, const char *p, MSize len)
69{
70 fs->p = (const uint8_t *)p;
71 fs->e = (const uint8_t *)p + len;
72 lua_assert(*fs->e == 0); /* Must be NUL-terminated (may have NULs inside). */
73}
74
75LJ_FUNC SFormat LJ_FASTCALL lj_strfmt_parse(FormatState *fs);
76LJ_FUNC SBuf *lj_strfmt_putchar(SBuf *sb, SFormat, int32_t c);
77LJ_FUNC SBuf *lj_strfmt_putstr(SBuf *sb, SFormat, GCstr *str);
78LJ_FUNC SBuf *lj_strfmt_putquoted(SBuf *sb, GCstr *str);
79LJ_FUNC SBuf *lj_strfmt_putxint(SBuf *sb, SFormat sf, uint64_t k);
80LJ_FUNC SBuf *lj_strfmt_putnum_int(SBuf *sb, SFormat sf, lua_Number n);
81LJ_FUNC SBuf *lj_strfmt_putnum_uint(SBuf *sb, SFormat sf, lua_Number n);
82LJ_FUNC SBuf *lj_strfmt_putnum(SBuf *sb, SFormat, lua_Number n);
83
84#endif
diff --git a/src/ljamalg.c b/src/ljamalg.c
index 487609c4..7198a09f 100644
--- a/src/ljamalg.c
+++ b/src/ljamalg.c
@@ -45,6 +45,7 @@
45#include "lj_vmevent.c" 45#include "lj_vmevent.c"
46#include "lj_vmmath.c" 46#include "lj_vmmath.c"
47#include "lj_strscan.c" 47#include "lj_strscan.c"
48#include "lj_strfmt.c"
48#include "lj_api.c" 49#include "lj_api.c"
49#include "lj_lex.c" 50#include "lj_lex.c"
50#include "lj_parse.c" 51#include "lj_parse.c"