mirror of
https://github.com/lua/lua.git
synced 2025-01-28 06:03:00 +08:00
Towards no errors in 'luaO_pushvfstring'
Any call to 'va_start' must have a corresponding call to 'va_end'; so, functions called between them (luaO_pushvfstring in particular) cannot raise errors.
This commit is contained in:
parent
00e34375ec
commit
70d6975018
131
lobject.c
131
lobject.c
@ -475,74 +475,94 @@ void luaO_tostring (lua_State *L, TValue *obj) {
|
|||||||
/*
|
/*
|
||||||
** Size for buffer space used by 'luaO_pushvfstring'. It should be
|
** Size for buffer space used by 'luaO_pushvfstring'. It should be
|
||||||
** (LUA_IDSIZE + MAXNUMBER2STR) + a minimal space for basic messages,
|
** (LUA_IDSIZE + MAXNUMBER2STR) + a minimal space for basic messages,
|
||||||
** so that 'luaG_addinfo' can work directly on the buffer.
|
** so that 'luaG_addinfo' can work directly on the static buffer.
|
||||||
*/
|
*/
|
||||||
#define BUFVFS cast_uint(LUA_IDSIZE + MAXNUMBER2STR + 95)
|
#define BUFVFS cast_uint(LUA_IDSIZE + MAXNUMBER2STR + 95)
|
||||||
|
|
||||||
/* buffer used by 'luaO_pushvfstring' */
|
/*
|
||||||
|
** Buffer used by 'luaO_pushvfstring'. 'err' signals any error while
|
||||||
|
** building result (memory error [1] or buffer overflow [2]).
|
||||||
|
*/
|
||||||
typedef struct BuffFS {
|
typedef struct BuffFS {
|
||||||
lua_State *L;
|
lua_State *L;
|
||||||
int pushed; /* true if there is a part of the result on the stack */
|
char *b;
|
||||||
unsigned blen; /* length of partial string in 'space' */
|
size_t buffsize;
|
||||||
char space[BUFVFS]; /* holds last part of the result */
|
size_t blen; /* length of string in 'buff' */
|
||||||
|
int err;
|
||||||
|
char space[BUFVFS]; /* initial buffer */
|
||||||
} BuffFS;
|
} BuffFS;
|
||||||
|
|
||||||
|
|
||||||
|
static void initbuff (lua_State *L, BuffFS *buff) {
|
||||||
|
buff->L = L;
|
||||||
|
buff->b = buff->space;
|
||||||
|
buff->buffsize = sizeof(buff->space);
|
||||||
|
buff->blen = 0;
|
||||||
|
buff->err = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Push given string to the stack, as part of the result, and
|
** Push final result from 'luaO_pushvfstring'. This function may raise
|
||||||
** join it to previous partial result if there is one.
|
** errors explicitly or through memory errors, so it must run protected.
|
||||||
** It may call 'luaV_concat' while using one slot from EXTRA_STACK.
|
|
||||||
** This call cannot invoke metamethods, as both operands must be
|
|
||||||
** strings. It can, however, raise an error if the result is too
|
|
||||||
** long. In that case, 'luaV_concat' frees the extra slot before
|
|
||||||
** raising the error.
|
|
||||||
*/
|
*/
|
||||||
static void pushstr (BuffFS *buff, const char *str, size_t lstr) {
|
static void pushbuff (lua_State *L, void *ud) {
|
||||||
|
BuffFS *buff = cast(BuffFS*, ud);
|
||||||
|
switch (buff->err) {
|
||||||
|
case 1:
|
||||||
|
luaD_throw(L, LUA_ERRMEM);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
luaG_runerror(L, "buffer overflow");
|
||||||
|
break;
|
||||||
|
default: { /* no errors */
|
||||||
|
TString *ts = luaS_newlstr(L, buff->b, buff->blen);
|
||||||
|
setsvalue2s(L, L->top.p, ts);
|
||||||
|
L->top.p++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const char *clearbuff (BuffFS *buff) {
|
||||||
lua_State *L = buff->L;
|
lua_State *L = buff->L;
|
||||||
setsvalue2s(L, L->top.p, luaS_newlstr(L, str, lstr));
|
const char *res;
|
||||||
L->top.p++; /* may use one slot from EXTRA_STACK */
|
pushbuff(L, buff);
|
||||||
if (!buff->pushed) /* no previous string on the stack? */
|
res = getstr(tsvalue(s2v(L->top.p - 1)));
|
||||||
buff->pushed = 1; /* now there is one */
|
if (buff->b != buff->space) /* using dynamic buffer? */
|
||||||
else /* join previous string with new one */
|
luaM_freearray(L, buff->b, buff->buffsize); /* free it */
|
||||||
luaV_concat(L, 2);
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** empty the buffer space into the stack
|
|
||||||
*/
|
|
||||||
static void clearbuff (BuffFS *buff) {
|
|
||||||
pushstr(buff, buff->space, buff->blen); /* push buffer contents */
|
|
||||||
buff->blen = 0; /* space now is empty */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Get a space of size 'sz' in the buffer. If buffer has not enough
|
|
||||||
** space, empty it. 'sz' must fit in an empty buffer.
|
|
||||||
*/
|
|
||||||
static char *getbuff (BuffFS *buff, unsigned sz) {
|
|
||||||
lua_assert(buff->blen <= BUFVFS); lua_assert(sz <= BUFVFS);
|
|
||||||
if (sz > BUFVFS - buff->blen) /* not enough space? */
|
|
||||||
clearbuff(buff);
|
|
||||||
return buff->space + buff->blen;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Add 'str' to the buffer. If string is larger than the buffer space,
|
|
||||||
** push the string directly to the stack.
|
|
||||||
*/
|
|
||||||
static void addstr2buff (BuffFS *buff, const char *str, size_t slen) {
|
static void addstr2buff (BuffFS *buff, const char *str, size_t slen) {
|
||||||
if (slen <= BUFVFS) { /* does string fit into buffer? */
|
if (buff->err) /* do nothing else after an error */
|
||||||
char *bf = getbuff(buff, cast_uint(slen));
|
return;
|
||||||
memcpy(bf, str, slen); /* add string to buffer */
|
if (slen > buff->buffsize - buff->blen) {
|
||||||
buff->blen += cast_uint(slen);
|
/* new string doesn't fit into current buffer */
|
||||||
}
|
if (slen > ((MAX_SIZE/2) - buff->blen)) { /* overflow? */
|
||||||
else { /* string larger than buffer */
|
buff->err = 2;
|
||||||
clearbuff(buff); /* string comes after buffer's content */
|
return;
|
||||||
pushstr(buff, str, slen); /* push string */
|
}
|
||||||
|
else {
|
||||||
|
size_t newsize = buff->buffsize + slen; /* limited to MAX_SIZE/2 */
|
||||||
|
char *newb =
|
||||||
|
(buff->b == buff->space) /* still using static space? */
|
||||||
|
? luaM_reallocvector(buff->L, NULL, 0, newsize, char)
|
||||||
|
: luaM_reallocvector(buff->L, buff->b, buff->buffsize, newsize,
|
||||||
|
char);
|
||||||
|
if (newb == NULL) { /* allocation error? */
|
||||||
|
buff->err = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (buff->b == buff->space)
|
||||||
|
memcpy(newb, buff->b, buff->blen); /* copy previous content */
|
||||||
|
buff->b = newb;
|
||||||
|
buff->buffsize = newsize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
memcpy(buff->b + buff->blen, str, slen); /* copy new content */
|
||||||
|
buff->blen += slen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -563,8 +583,7 @@ static void addnum2buff (BuffFS *buff, TValue *num) {
|
|||||||
const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
|
const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
|
||||||
BuffFS buff; /* holds last part of the result */
|
BuffFS buff; /* holds last part of the result */
|
||||||
const char *e; /* points to next '%' */
|
const char *e; /* points to next '%' */
|
||||||
buff.pushed = 0; buff.blen = 0;
|
initbuff(L, &buff);
|
||||||
buff.L = L;
|
|
||||||
while ((e = strchr(fmt, '%')) != NULL) {
|
while ((e = strchr(fmt, '%')) != NULL) {
|
||||||
addstr2buff(&buff, fmt, ct_diff2sz(e - fmt)); /* add 'fmt' up to '%' */
|
addstr2buff(&buff, fmt, ct_diff2sz(e - fmt)); /* add 'fmt' up to '%' */
|
||||||
switch (*(e + 1)) { /* conversion specifier */
|
switch (*(e + 1)) { /* conversion specifier */
|
||||||
@ -622,9 +641,7 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
|
|||||||
fmt = e + 2; /* skip '%' and the specifier */
|
fmt = e + 2; /* skip '%' and the specifier */
|
||||||
}
|
}
|
||||||
addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */
|
addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */
|
||||||
clearbuff(&buff); /* empty buffer into the stack */
|
return clearbuff(&buff); /* empty buffer into a new string */
|
||||||
lua_assert(buff.pushed == 1);
|
|
||||||
return getstr(tsvalue(s2v(L->top.p - 1)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user