diff --git a/lobject.c b/lobject.c index 123f0e57..58eecd4a 100644 --- a/lobject.c +++ b/lobject.c @@ -392,126 +392,144 @@ void luaO_tostring (lua_State *L, TValue *obj) { } -/* size for buffer used by 'luaO_pushvfstring' */ +/* size for buffer space used by 'luaO_pushvfstring' */ #define BUFVFS 400 /* buffer used by 'luaO_pushvfstring' */ typedef struct BuffFS { - int blen; /* length of partial string in 'buff' */ - char buff[BUFVFS]; /* holds last part of the result */ + int pushed; /* number of string pieces already on the stack */ + int blen; /* length of partial string in 'space' */ + char space[BUFVFS]; /* holds last part of the result */ } BuffFS; -static void pushstr (lua_State *L, const char *str, size_t l) { +/* +** Push given string to the stack, as part of the buffer. If the stack +** is almost full, join all partial strings in the stack into one. +*/ +static void pushstr (lua_State *L, BuffFS *buff, const char *str, size_t l) { setsvalue2s(L, L->top, luaS_newlstr(L, str, l)); L->top++; + buff->pushed++; + if (buff->pushed > 1 && L->top + 2 > L->stack_last) { + luaV_concat(L, buff->pushed); /* join all partial results into one */ + buff->pushed = 1; + } } /* -** empty the buffer into the stack +** empty the buffer space into the stack */ static void clearbuff (lua_State *L, BuffFS *buff) { - pushstr(L, buff->buff, buff->blen); /* push buffer */ - buff->blen = 0; /* buffer now is empty */ + pushstr(L, buff, buff->space, buff->blen); /* push buffer contents */ + buff->blen = 0; /* space now is empty */ } /* -** Add 'str' to the buffer. It buffer has no enough space, -** empty the buffer. If string is still larger than the buffer, -** push the string directly to the stack. Return number of items -** pushed. +** Get a space of size 'sz' in the buffer. If buffer has not enough +** space, empty it. 'sz' must fit in an empty space. */ -static int addstr2buff (lua_State *L, BuffFS *buff, const char *str, - size_t slen) { - int pushed = 0; /* number of items pushed to the stack */ - lua_assert(buff->blen <= BUFVFS); - if (slen > BUFVFS - cast_sizet(buff->blen)) { /* string does not fit? */ +static char *getbuff (lua_State *L, BuffFS *buff, size_t sz) { + lua_assert(buff->blen <= BUFVFS); lua_assert(sz <= BUFVFS); + if (sz > BUFVFS - cast_sizet(buff->blen)) /* string does not fit? */ clearbuff(L, buff); - pushed = 1; - if (slen >= BUFVFS) { /* string still does not fit into buffer? */ - pushstr(L, str, slen); /* push string */ - return 2; - } - } - memcpy(buff->buff + buff->blen, str, slen); /* add string to buffer */ - buff->blen += slen; - return pushed; + return buff->space + buff->blen; } +#define addsize(b,sz) ((b)->blen += (sz)) + + /* -** Add a number to the buffer; return number of strings pushed into -** the stack. (At most one, to free buffer space.) +** Add 'str' to the buffer. If string is larger than the buffer space, +** push the string directly to the stack. */ -static int addnum2buff (lua_State *L, BuffFS *buff, TValue *num) { - char numbuff[MAXNUMBER2STR]; - size_t len = tostringbuff(num, numbuff); /* format number into 'numbuff' */ - return addstr2buff(L, buff, numbuff, len); +static void addstr2buff (lua_State *L, BuffFS *buff, const char *str, + size_t slen) { + if (slen <= BUFVFS) { /* does string fit into buffer? */ + char *bf = getbuff(L, buff, slen); + memcpy(bf, str, slen); /* add string to buffer */ + addsize(buff, slen); + } + else { /* string larger than buffer */ + clearbuff(L, buff); /* string comes after buffer's content */ + pushstr(L, buff, str, slen); /* push string */ + } } /* -** this function handles only '%d', '%c', '%f', '%p', and '%s' +** Add a number to the buffer. +*/ +static void addnum2buff (lua_State *L, BuffFS *buff, TValue *num) { + char *numbuff = getbuff(L, buff, MAXNUMBER2STR); + size_t len = tostringbuff(num, numbuff); /* format number into 'numbuff' */ + addsize(buff, len); +} + + +/* +** this function handles only '%d', '%c', '%f', '%p', '%s', and '%%' conventional formats, plus Lua-specific '%I' and '%U' */ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { BuffFS buff; /* holds last part of the result */ - int pushed = 0; /* number of strings in the stack to concatenate */ const char *e; /* points to next '%' */ - buff.blen = 0; + buff.pushed = buff.blen = 0; while ((e = strchr(fmt, '%')) != NULL) { - pushed += addstr2buff(L, &buff, fmt, e - fmt); /* add 'fmt' up to '%' */ + addstr2buff(L, &buff, fmt, e - fmt); /* add 'fmt' up to '%' */ switch (*(e + 1)) { /* conversion specifier */ case 's': { /* zero-terminated string */ const char *s = va_arg(argp, char *); if (s == NULL) s = "(null)"; - pushed += addstr2buff(L, &buff, s, strlen(s)); + addstr2buff(L, &buff, s, strlen(s)); break; } case 'c': { /* an 'int' as a character */ /* if non-printable character, print its code */ - char bf[10]; + char *bf = getbuff(L, &buff, 10); int c = va_arg(argp, int); - int l = (lisprint(c)) ? l_sprintf(bf, sizeof(bf), "%c", c) - : l_sprintf(bf, sizeof(bf), "<\\%u>", c); - pushed += addstr2buff(L, &buff, bf, l); + int len = (lisprint(c)) ? l_sprintf(bf, 10, "%c", c) + : l_sprintf(bf, 10, "<\\%u>", c); + addsize(&buff, len); break; } case 'd': { /* an 'int' */ TValue num; setivalue(&num, va_arg(argp, int)); - pushed += addnum2buff(L, &buff, &num); + addnum2buff(L, &buff, &num); break; } case 'I': { /* a 'lua_Integer' */ TValue num; setivalue(&num, cast(lua_Integer, va_arg(argp, l_uacInt))); - pushed += addnum2buff(L, &buff, &num); + addnum2buff(L, &buff, &num); break; } case 'f': { /* a 'lua_Number' */ TValue num; setfltvalue(&num, cast_num(va_arg(argp, l_uacNumber))); - pushed += addnum2buff(L, &buff, &num); + addnum2buff(L, &buff, &num); break; } case 'p': { /* a pointer */ - char bf[3 * sizeof(void*) + 8]; /* should be enough space for '%p' */ + const int sz = 3 * sizeof(void*) + 8; /* enough space for '%p' */ + char *bf = getbuff(L, &buff, sz); void *p = va_arg(argp, void *); - int l = l_sprintf(bf, sizeof(bf), "%p", p); - pushed += addstr2buff(L, &buff, bf, l); + int len = l_sprintf(bf, sz, "%p", p); + addsize(&buff, len); break; } case 'U': { /* a 'long' as a UTF-8 sequence */ char bf[UTF8BUFFSZ]; - int l = luaO_utf8esc(bf, va_arg(argp, long)); - pushed += addstr2buff(L, &buff, bf + UTF8BUFFSZ - l, l); + int len = luaO_utf8esc(bf, va_arg(argp, long)); + addstr2buff(L, &buff, bf + UTF8BUFFSZ - len, len); break; } case '%': { - pushed += addstr2buff(L, &buff, "%", 1); + addstr2buff(L, &buff, "%", 1); break; } default: { @@ -519,16 +537,12 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { *(e + 1)); } } - if (pushed > 1 && L->top + 2 > L->stack_last) { /* no free stack space? */ - luaV_concat(L, pushed); /* join all partial results into one */ - pushed = 1; - } fmt = e + 2; /* skip '%' and the specifier */ } - pushed += addstr2buff(L, &buff, fmt, strlen(fmt)); /* rest of 'fmt' */ + addstr2buff(L, &buff, fmt, strlen(fmt)); /* rest of 'fmt' */ clearbuff(L, &buff); /* empty buffer into the stack */ - if (pushed > 0) - luaV_concat(L, pushed + 1); /* join all partial results */ + if (buff.pushed > 1) + luaV_concat(L, buff.pushed); /* join all partial results */ return svalue(s2v(L->top - 1)); } diff --git a/testes/strings.lua b/testes/strings.lua index 66c1176d..bc123d1a 100644 --- a/testes/strings.lua +++ b/testes/strings.lua @@ -458,6 +458,9 @@ else str = string.rep("a", blen - 1) .. "%p" .. string.rep("cd", blen) testpfs("P", str, {}) + + str = string.rep("%%", 3 * blen) .. "%p" .. string.rep("%%", 2 * blen) + testpfs("P", str, {}) end