aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMike Pall <mike>2013-03-20 22:45:52 +0100
committerMike Pall <mike>2013-03-21 22:17:59 +0100
commitf1dbd65c0ec8024105ef5b059886ba0ff2783080 (patch)
tree881c7cdea926156bba04c9b063fe190b1368eda2 /src
parentfecde1b22254339dc76376e95ccc9f05de1efb39 (diff)
downloadluajit-f1dbd65c0ec8024105ef5b059886ba0ff2783080.tar.gz
luajit-f1dbd65c0ec8024105ef5b059886ba0ff2783080.tar.bz2
luajit-f1dbd65c0ec8024105ef5b059886ba0ff2783080.zip
Use string buffer for string.format().
Diffstat (limited to 'src')
-rw-r--r--src/lib_string.c178
-rw-r--r--src/lj_str.c41
-rw-r--r--src/lj_str.h2
3 files changed, 117 insertions, 104 deletions
diff --git a/src/lib_string.c b/src/lib_string.c
index ada0bf6d..09010b15 100644
--- a/src/lib_string.c
+++ b/src/lib_string.c
@@ -702,76 +702,81 @@ LJLIB_CF(string_gsub)
702 702
703/* ------------------------------------------------------------------------ */ 703/* ------------------------------------------------------------------------ */
704 704
705/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */ 705/* Max. buffer size needed (at least #string.format("%99.99f", -1e308)). */
706#define MAX_FMTITEM 512 706#define STRING_FMT_MAXBUF 512
707/* valid flags in a format specification */ 707/* Valid format specifier flags. */
708#define FMT_FLAGS "-+ #0" 708#define STRING_FMT_FLAGS "-+ #0"
709/* 709/* Max. format specifier size. */
710** maximum size of each format specification (such as '%-099.99d') 710#define STRING_FMT_MAXSPEC \
711** (+10 accounts for %99.99x plus margin of error) 711 (sizeof(STRING_FMT_FLAGS) + sizeof(LUA_INTFRMLEN) + 10)
712*/
713#define MAX_FMTSPEC (sizeof(FMT_FLAGS) + sizeof(LUA_INTFRMLEN) + 10)
714 712
715static void addquoted(lua_State *L, luaL_Buffer *b, int arg) 713/* Add quoted string to buffer. */
714static void string_fmt_quoted(SBuf *sb, GCstr *str)
716{ 715{
717 GCstr *str = lj_lib_checkstr(L, arg);
718 int32_t len = (int32_t)str->len;
719 const char *s = strdata(str); 716 const char *s = strdata(str);
720 luaL_addchar(b, '"'); 717 MSize len = str->len;
718 lj_buf_putb(sb, '"');
721 while (len--) { 719 while (len--) {
722 uint32_t c = uchar(*s); 720 uint32_t c = (uint32_t)(uint8_t)*s++;
721 char *p = lj_buf_more(sb, 4);
723 if (c == '"' || c == '\\' || c == '\n') { 722 if (c == '"' || c == '\\' || c == '\n') {
724 luaL_addchar(b, '\\'); 723 *p++ = '\\';
725 } else if (lj_char_iscntrl(c)) { /* This can only be 0-31 or 127. */ 724 } else if (lj_char_iscntrl(c)) { /* This can only be 0-31 or 127. */
726 uint32_t d; 725 uint32_t d;
727 luaL_addchar(b, '\\'); 726 *p++ = '\\';
728 if (c >= 100 || lj_char_isdigit(uchar(s[1]))) { 727 if (c >= 100 || lj_char_isdigit((uint8_t)*s)) {
729 luaL_addchar(b, '0'+(c >= 100)); if (c >= 100) c -= 100; 728 *p++ = (char)('0'+(c >= 100)); if (c >= 100) c -= 100;
730 goto tens; 729 goto tens;
731 } else if (c >= 10) { 730 } else if (c >= 10) {
732 tens: 731 tens:
733 d = (c * 205) >> 11; c -= d * 10; luaL_addchar(b, '0'+d); 732 d = (c * 205) >> 11; c -= d * 10; *p++ = (char)('0'+d);
734 } 733 }
735 c += '0'; 734 c += '0';
736 } 735 }
737 luaL_addchar(b, c); 736 *p++ = (char)c;
738 s++; 737 setsbufP(sb, p);
739 } 738 }
740 luaL_addchar(b, '"'); 739 lj_buf_putb(sb, '"');
741} 740}
742 741
743static const char *scanformat(lua_State *L, const char *strfrmt, char *form) 742/* Scan format and generate format specifier. */
743static const char *string_fmt_scan(lua_State *L, char *spec, const char *fmt)
744{ 744{
745 const char *p = strfrmt; 745 const char *p = fmt;
746 while (*p != '\0' && strchr(FMT_FLAGS, *p) != NULL) p++; /* skip flags */ 746 while (*p && strchr(STRING_FMT_FLAGS, *p) != NULL) p++; /* Skip flags. */
747 if ((size_t)(p - strfrmt) >= sizeof(FMT_FLAGS)) 747 if ((size_t)(p - fmt) >= sizeof(STRING_FMT_FLAGS))
748 lj_err_caller(L, LJ_ERR_STRFMTR); 748 lj_err_caller(L, LJ_ERR_STRFMTR);
749 if (lj_char_isdigit(uchar(*p))) p++; /* skip width */ 749 if (lj_char_isdigit((uint8_t)*p)) p++; /* Skip max. 2 digits for width. */
750 if (lj_char_isdigit(uchar(*p))) p++; /* (2 digits at most) */ 750 if (lj_char_isdigit((uint8_t)*p)) p++;
751 if (*p == '.') { 751 if (*p == '.') {
752 p++; 752 p++;
753 if (lj_char_isdigit(uchar(*p))) p++; /* skip precision */ 753 if (lj_char_isdigit((uint8_t)*p)) p++; /* Skip max. 2 digits for prec. */
754 if (lj_char_isdigit(uchar(*p))) p++; /* (2 digits at most) */ 754 if (lj_char_isdigit((uint8_t)*p)) p++;
755 } 755 }
756 if (lj_char_isdigit(uchar(*p))) 756 if (lj_char_isdigit((uint8_t)*p))
757 lj_err_caller(L, LJ_ERR_STRFMTW); 757 lj_err_caller(L, LJ_ERR_STRFMTW);
758 *(form++) = '%'; 758 *spec++ = '%';
759 strncpy(form, strfrmt, (size_t)(p - strfrmt + 1)); 759 strncpy(spec, fmt, (size_t)(p - fmt + 1));
760 form += p - strfrmt + 1; 760 spec += p - fmt + 1;
761 *form = '\0'; 761 *spec = '\0';
762 return p; 762 return p;
763} 763}
764 764
765static void addintlen(char *form) 765/* Patch LUA_INTRFRMLEN into integer format specifier. */
766static void string_fmt_intfmt(char *spec)
766{ 767{
767 size_t l = strlen(form); 768 char c;
768 char spec = form[l - 1]; 769 do {
769 strcpy(form + l - 1, LUA_INTFRMLEN); 770 c = *spec++;
770 form[l + sizeof(LUA_INTFRMLEN) - 2] = spec; 771 } while (*spec);
771 form[l + sizeof(LUA_INTFRMLEN) - 1] = '\0'; 772 *--spec = (LUA_INTFRMLEN)[0];
773 if ((LUA_INTFRMLEN)[1]) *++spec = (LUA_INTFRMLEN)[1];
774 *++spec = c;
775 *++spec = '\0';
772} 776}
773 777
774static unsigned LUA_INTFRM_T num2intfrm(lua_State *L, int arg) 778/* Derive sprintf argument for integer format. Ugly. */
779static LUA_INTFRM_T string_fmt_intarg(lua_State *L, int arg)
775{ 780{
776 if (sizeof(LUA_INTFRM_T) == 4) { 781 if (sizeof(LUA_INTFRM_T) == 4) {
777 return (LUA_INTFRM_T)lj_lib_checkbit(L, arg); 782 return (LUA_INTFRM_T)lj_lib_checkbit(L, arg);
@@ -786,7 +791,8 @@ static unsigned LUA_INTFRM_T num2intfrm(lua_State *L, int arg)
786 } 791 }
787} 792}
788 793
789static unsigned LUA_INTFRM_T num2uintfrm(lua_State *L, int arg) 794/* Derive sprintf argument for unsigned integer format. Ugly. */
795static unsigned LUA_INTFRM_T string_fmt_uintarg(lua_State *L, int arg)
790{ 796{
791 if (sizeof(LUA_INTFRM_T) == 4) { 797 if (sizeof(LUA_INTFRM_T) == 4) {
792 return (unsigned LUA_INTFRM_T)lj_lib_checkbit(L, arg); 798 return (unsigned LUA_INTFRM_T)lj_lib_checkbit(L, arg);
@@ -803,7 +809,8 @@ static unsigned LUA_INTFRM_T num2uintfrm(lua_State *L, int arg)
803 } 809 }
804} 810}
805 811
806static GCstr *meta_tostring(lua_State *L, int arg) 812/* Emulate tostring() inline. */
813static GCstr *string_fmt_tostring(lua_State *L, int arg)
807{ 814{
808 TValue *o = L->base+arg-1; 815 TValue *o = L->base+arg-1;
809 cTValue *mo; 816 cTValue *mo;
@@ -841,33 +848,33 @@ static GCstr *meta_tostring(lua_State *L, int arg)
841LJLIB_CF(string_format) 848LJLIB_CF(string_format)
842{ 849{
843 int arg = 1, top = (int)(L->top - L->base); 850 int arg = 1, top = (int)(L->top - L->base);
844 GCstr *fmt = lj_lib_checkstr(L, arg); 851 GCstr *sfmt = lj_lib_checkstr(L, arg);
845 const char *strfrmt = strdata(fmt); 852 const char *fmt = strdata(sfmt);
846 const char *strfrmt_end = strfrmt + fmt->len; 853 const char *efmt = fmt + sfmt->len;
847 luaL_Buffer b; 854 SBuf *sb = &G(L)->tmpbuf;
848 luaL_buffinit(L, &b); 855 setmref(sb->L, L);
849 while (strfrmt < strfrmt_end) { 856 lj_buf_reset(sb);
850 if (*strfrmt != L_ESC) { 857 while (fmt < efmt) {
851 luaL_addchar(&b, *strfrmt++); 858 if (*fmt != L_ESC || *++fmt == L_ESC) {
852 } else if (*++strfrmt == L_ESC) { 859 lj_buf_putb(sb, *fmt++);
853 luaL_addchar(&b, *strfrmt++); /* %% */ 860 } else {
854 } else { /* format item */ 861 char buf[STRING_FMT_MAXBUF];
855 char form[MAX_FMTSPEC]; /* to store the format (`%...') */ 862 char spec[STRING_FMT_MAXSPEC];
856 char buff[MAX_FMTITEM]; /* to store the formatted item */ 863 MSize len = 0;
857 if (++arg > top) 864 if (++arg > top)
858 luaL_argerror(L, arg, lj_obj_typename[0]); 865 luaL_argerror(L, arg, lj_obj_typename[0]);
859 strfrmt = scanformat(L, strfrmt, form); 866 fmt = string_fmt_scan(L, spec, fmt);
860 switch (*strfrmt++) { 867 switch (*fmt++) {
861 case 'c': 868 case 'c':
862 sprintf(buff, form, lj_lib_checkint(L, arg)); 869 len = (MSize)sprintf(buf, spec, lj_lib_checkint(L, arg));
863 break; 870 break;
864 case 'd': case 'i': 871 case 'd': case 'i':
865 addintlen(form); 872 string_fmt_intfmt(spec);
866 sprintf(buff, form, num2intfrm(L, arg)); 873 len = (MSize)sprintf(buf, spec, string_fmt_intarg(L, arg));
867 break; 874 break;
868 case 'o': case 'u': case 'x': case 'X': 875 case 'o': case 'u': case 'x': case 'X':
869 addintlen(form); 876 string_fmt_intfmt(spec);
870 sprintf(buff, form, num2uintfrm(L, arg)); 877 len = (MSize)sprintf(buf, spec, string_fmt_uintarg(L, arg));
871 break; 878 break;
872 case 'e': case 'E': case 'f': case 'g': case 'G': case 'a': case 'A': { 879 case 'e': case 'E': case 'f': case 'g': case 'G': case 'a': case 'A': {
873 TValue tv; 880 TValue tv;
@@ -875,48 +882,45 @@ LJLIB_CF(string_format)
875 if (LJ_UNLIKELY((tv.u32.hi << 1) >= 0xffe00000)) { 882 if (LJ_UNLIKELY((tv.u32.hi << 1) >= 0xffe00000)) {
876 /* Canonicalize output of non-finite values. */ 883 /* Canonicalize output of non-finite values. */
877 char *p, nbuf[LJ_STR_NUMBUF]; 884 char *p, nbuf[LJ_STR_NUMBUF];
878 MSize len = lj_str_bufnum(nbuf, &tv); 885 MSize n = lj_str_bufnum(nbuf, &tv);
879 if (strfrmt[-1] < 'a') { 886 if (fmt[-1] < 'a') {
880 nbuf[len-3] = nbuf[len-3] - 0x20; 887 nbuf[n-3] = nbuf[n-3] - 0x20;
881 nbuf[len-2] = nbuf[len-2] - 0x20; 888 nbuf[n-2] = nbuf[n-2] - 0x20;
882 nbuf[len-1] = nbuf[len-1] - 0x20; 889 nbuf[n-1] = nbuf[n-1] - 0x20;
883 } 890 }
884 nbuf[len] = '\0'; 891 nbuf[n] = '\0';
885 for (p = form; *p < 'A' && *p != '.'; p++) ; 892 for (p = spec; *p < 'A' && *p != '.'; p++) ;
886 *p++ = 's'; *p = '\0'; 893 *p++ = 's'; *p = '\0';
887 sprintf(buff, form, nbuf); 894 len = (MSize)sprintf(buf, spec, nbuf);
888 break; 895 break;
889 } 896 }
890 sprintf(buff, form, (double)tv.n); 897 len = (MSize)sprintf(buf, spec, (double)tv.n);
891 break; 898 break;
892 } 899 }
893 case 'q': 900 case 'q':
894 addquoted(L, &b, arg); 901 string_fmt_quoted(sb, lj_lib_checkstr(L, arg));
895 continue; 902 continue;
896 case 'p': 903 case 'p':
897 lj_str_pushf(L, "%p", lua_topointer(L, arg)); 904 len = lj_str_bufptr(buf, lua_topointer(L, arg));
898 luaL_addvalue(&b); 905 break;
899 continue;
900 case 's': { 906 case 's': {
901 GCstr *str = meta_tostring(L, arg); 907 GCstr *str = string_fmt_tostring(L, arg);
902 if (!strchr(form, '.') && str->len >= 100) { 908 if (!strchr(spec, '.') && str->len >= 100) { /* Format overflow? */
903 /* no precision and string is too long to be formatted; 909 lj_buf_putmem(sb, strdata(str), str->len); /* Use orig string. */
904 keep original string */
905 setstrV(L, L->top++, str);
906 luaL_addvalue(&b);
907 continue; 910 continue;
908 } 911 }
909 sprintf(buff, form, strdata(str)); 912 len = (MSize)sprintf(buf, spec, strdata(str));
910 break; 913 break;
911 } 914 }
912 default: 915 default:
913 lj_err_callerv(L, LJ_ERR_STRFMTO, *(strfrmt -1)); 916 lj_err_callerv(L, LJ_ERR_STRFMTO, fmt[-1] ? fmt[-1] : ' ');
914 break; 917 break;
915 } 918 }
916 luaL_addlstring(&b, buff, strlen(buff)); 919 lj_buf_putmem(sb, buf, len);
917 } 920 }
918 } 921 }
919 luaL_pushresult(&b); 922 setstrV(L, L->top-1, lj_buf_str(L, sb));
923 lj_gc_check(L);
920 return 1; 924 return 1;
921} 925}
922 926
diff --git a/src/lj_str.c b/src/lj_str.c
index 5482f7c4..9eb04c57 100644
--- a/src/lj_str.c
+++ b/src/lj_str.c
@@ -186,7 +186,7 @@ MSize LJ_FASTCALL lj_str_bufnum(char *s, cTValue *o)
186} 186}
187 187
188/* Print integer to buffer. Returns pointer to start (!= buffer start). */ 188/* Print integer to buffer. Returns pointer to start (!= buffer start). */
189static char * str_bufint(char *p, int32_t k) 189static char *str_bufint(char *p, int32_t k)
190{ 190{
191 uint32_t u = (uint32_t)(k < 0 ? -k : k); 191 uint32_t u = (uint32_t)(k < 0 ? -k : k);
192 p += LJ_STR_INTBUF; 192 p += LJ_STR_INTBUF;
@@ -195,6 +195,26 @@ static char * str_bufint(char *p, int32_t k)
195 return p; 195 return p;
196} 196}
197 197
198/* Print pointer to buffer. */
199MSize LJ_FASTCALL lj_str_bufptr(char *p, const void *v)
200{
201 ptrdiff_t x = (ptrdiff_t)v;
202 MSize i, n = LJ_STR_PTRBUF;
203 if (x == 0) {
204 p[0] = 'N'; p[1] = 'U'; p[2] = 'L'; p[3] = 'L';
205 return 4;
206 }
207#if LJ_64
208 /* Shorten output for 64 bit pointers. */
209 n = 2+2*4+((x >> 32) ? 2+2*(lj_fls((uint32_t)(x >> 32))>>3) : 0);
210#endif
211 p[0] = '0';
212 p[1] = 'x';
213 for (i = n-1; i >= 2; i--, x >>= 4)
214 p[i] = "0123456789abcdef"[(x & 15)];
215 return n;
216}
217
198/* Print TValue to buffer (only for numbers) and return pointer to start. */ 218/* Print TValue to buffer (only for numbers) and return pointer to start. */
199const char *lj_str_buftv(char *buf, cTValue *o, MSize *lenp) 219const char *lj_str_buftv(char *buf, cTValue *o, MSize *lenp)
200{ 220{
@@ -275,22 +295,9 @@ const char *lj_str_pushvf(lua_State *L, const char *fmt, va_list argp)
275 } 295 }
276 case 'p': { 296 case 'p': {
277#define FMTP_CHARS (2*sizeof(ptrdiff_t)) 297#define FMTP_CHARS (2*sizeof(ptrdiff_t))
278 char buf[2+FMTP_CHARS]; 298 char buf[LJ_STR_PTRBUF];
279 ptrdiff_t p = (ptrdiff_t)(va_arg(argp, void *)); 299 MSize len = lj_str_bufptr(buf, va_arg(argp, void *));
280 ptrdiff_t i, lasti = 2+FMTP_CHARS; 300 lj_buf_putmem(sb, buf, len);
281 if (p == 0) {
282 lj_buf_putmem(sb, "NULL", 4);
283 break;
284 }
285#if LJ_64
286 /* Shorten output for 64 bit pointers. */
287 lasti = 2+2*4+((p >> 32) ? 2+2*(lj_fls((uint32_t)(p >> 32))>>3) : 0);
288#endif
289 buf[0] = '0';
290 buf[1] = 'x';
291 for (i = lasti-1; i >= 2; i--, p >>= 4)
292 buf[i] = "0123456789abcdef"[(p & 15)];
293 lj_buf_putmem(sb, buf, (MSize)lasti);
294 break; 301 break;
295 } 302 }
296 case '%': 303 case '%':
diff --git a/src/lj_str.h b/src/lj_str.h
index 5d91203b..6317e794 100644
--- a/src/lj_str.h
+++ b/src/lj_str.h
@@ -21,6 +21,7 @@ LJ_FUNC void LJ_FASTCALL lj_str_free(global_State *g, GCstr *s);
21 21
22/* Type conversions. */ 22/* Type conversions. */
23LJ_FUNC MSize LJ_FASTCALL lj_str_bufnum(char *s, cTValue *o); 23LJ_FUNC MSize LJ_FASTCALL lj_str_bufnum(char *s, cTValue *o);
24LJ_FUNC MSize LJ_FASTCALL lj_str_bufptr(char *p, const void *v);
24LJ_FUNC const char *lj_str_buftv(char *buf, cTValue *o, MSize *lenp); 25LJ_FUNC const char *lj_str_buftv(char *buf, cTValue *o, MSize *lenp);
25LJ_FUNCA GCstr * LJ_FASTCALL lj_str_fromnum(lua_State *L, const lua_Number *np); 26LJ_FUNCA GCstr * LJ_FASTCALL lj_str_fromnum(lua_State *L, const lua_Number *np);
26LJ_FUNC GCstr * LJ_FASTCALL lj_str_fromint(lua_State *L, int32_t k); 27LJ_FUNC GCstr * LJ_FASTCALL lj_str_fromint(lua_State *L, int32_t k);
@@ -29,6 +30,7 @@ LJ_FUNCA GCstr * LJ_FASTCALL lj_str_fromnumber(lua_State *L, cTValue *o);
29#define LJ_STR_INTBUF (1+10) 30#define LJ_STR_INTBUF (1+10)
30#define LJ_STR_NUMBUF LUAI_MAXNUMBER2STR 31#define LJ_STR_NUMBUF LUAI_MAXNUMBER2STR
31#define LJ_STR_NUMBERBUF LUAI_MAXNUMBER2STR 32#define LJ_STR_NUMBERBUF LUAI_MAXNUMBER2STR
33#define LJ_STR_PTRBUF (2*sizeof(ptrdiff_t)+2)
32 34
33/* String formatting. */ 35/* String formatting. */
34LJ_FUNC const char *lj_str_pushvf(lua_State *L, const char *fmt, va_list argp); 36LJ_FUNC const char *lj_str_pushvf(lua_State *L, const char *fmt, va_list argp);