From e3ce88c9e850b7e79751083014699c5eae1bff31 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Wed, 23 Oct 2024 17:16:17 -0300 Subject: New function 'lua_numbertostrbuff' It converts a Lua number to a string in a buffer, without creating a new Lua string. --- lapi.c | 12 ++++++++++++ lauxlib.c | 7 +++---- liolib.c | 22 +++++++++------------- lobject.c | 44 +++++++++++++++++++++++--------------------- lobject.h | 1 + lua.h | 4 +++- manual/manual.of | 17 ++++++++++++++++- 7 files changed, 67 insertions(+), 40 deletions(-) diff --git a/lapi.c b/lapi.c index fffd7d26..631cf44e 100644 --- a/lapi.c +++ b/lapi.c @@ -368,6 +368,18 @@ LUA_API int lua_compare (lua_State *L, int index1, int index2, int op) { } +LUA_API unsigned (lua_numbertostrbuff) (lua_State *L, int idx, char *buff) { + const TValue *o = index2value(L, idx); + if (ttisnumber(o)) { + unsigned len = luaO_tostringbuff(o, buff); + buff[len++] = '\0'; /* add final zero */ + return len; + } + else + return 0; +} + + LUA_API size_t lua_stringtonumber (lua_State *L, const char *s) { size_t sz = luaO_str2num(s, s2v(L->top.p)); if (sz != 0) diff --git a/lauxlib.c b/lauxlib.c index a36655f2..e4b12587 100644 --- a/lauxlib.c +++ b/lauxlib.c @@ -920,10 +920,9 @@ LUALIB_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) { else { switch (lua_type(L, idx)) { case LUA_TNUMBER: { - if (lua_isinteger(L, idx)) - lua_pushfstring(L, "%I", (LUAI_UACINT)lua_tointeger(L, idx)); - else - lua_pushfstring(L, "%f", (LUAI_UACNUMBER)lua_tonumber(L, idx)); + char buff[LUA_N2SBUFFSZ]; + lua_numbertostrbuff(L, idx, buff); + lua_pushstring(L, buff); break; } case LUA_TSTRING: diff --git a/liolib.c b/liolib.c index 17522bb2..98ff3d0d 100644 --- a/liolib.c +++ b/liolib.c @@ -665,20 +665,16 @@ static int g_write (lua_State *L, FILE *f, int arg) { int status = 1; errno = 0; for (; nargs--; arg++) { - if (lua_type(L, arg) == LUA_TNUMBER) { - /* optimization: could be done exactly as for strings */ - int len = lua_isinteger(L, arg) - ? fprintf(f, LUA_INTEGER_FMT, - (LUAI_UACINT)lua_tointeger(L, arg)) - : fprintf(f, LUA_NUMBER_FMT, - (LUAI_UACNUMBER)lua_tonumber(L, arg)); - status = status && (len > 0); - } - else { - size_t l; - const char *s = luaL_checklstring(L, arg, &l); - status = status && (fwrite(s, sizeof(char), l, f) == l); + char buff[LUA_N2SBUFFSZ]; + const char *s; + size_t len = lua_numbertostrbuff(L, arg, buff); /* try as a number */ + if (len > 0) { /* did conversion work (value was a number)? */ + s = buff; + len--; } + else /* must be a string */ + s = luaL_checklstring(L, arg, &len); + status = status && (fwrite(s, sizeof(char), len, f) == len); } if (l_likely(status)) return 1; /* file handle already on stack top */ diff --git a/lobject.c b/lobject.c index ba10189d..97dacaf5 100644 --- a/lobject.c +++ b/lobject.c @@ -400,15 +400,17 @@ int luaO_utf8esc (char *buff, unsigned long x) { /* -** Maximum length of the conversion of a number to a string. Must be -** enough to accommodate both LUA_INTEGER_FMT and LUA_NUMBER_FMT. -** For a long long int, this is 19 digits plus a sign and a final '\0', -** adding to 21. For a long double, it can go to a sign, the dot, an -** exponent letter, an exponent sign, 4 exponent digits, the final -** '\0', plus the significant digits, which are approximately the *_DIG -** attribute. +** The size of the buffer for the conversion of a number to a string +** 'LUA_N2SBUFFSZ' must be enough to accommodate both LUA_INTEGER_FMT +** and LUA_NUMBER_FMT. For a long long int, this is 19 digits plus a +** sign and a final '\0', adding to 21. For a long double, it can go to +** a sign, the dot, an exponent letter, an exponent sign, 4 exponent +** digits, the final '\0', plus the significant digits, which are +** approximately the *_DIG attribute. */ -#define MAXNUMBER2STR (20 + l_floatatt(DIG)) +#if LUA_N2SBUFFSZ < (20 + l_floatatt(DIG)) +#error "invalid value for LUA_N2SBUFFSZ" +#endif /* @@ -422,12 +424,12 @@ int luaO_utf8esc (char *buff, unsigned long x) { */ static int tostringbuffFloat (lua_Number n, char *buff) { /* first conversion */ - int len = l_sprintf(buff, MAXNUMBER2STR, LUA_NUMBER_FMT, + int len = l_sprintf(buff, LUA_N2SBUFFSZ, LUA_NUMBER_FMT, (LUAI_UACNUMBER)n); lua_Number check = lua_str2number(buff, NULL); /* read it back */ if (check != n) { /* not enough precision? */ /* convert again with more precision */ - len = l_sprintf(buff, MAXNUMBER2STR, LUA_NUMBER_FMT_N, + len = l_sprintf(buff, LUA_N2SBUFFSZ, LUA_NUMBER_FMT_N, (LUAI_UACNUMBER)n); } /* looks like an integer? */ @@ -442,14 +444,14 @@ static int tostringbuffFloat (lua_Number n, char *buff) { /* ** Convert a number object to a string, adding it to a buffer. */ -static unsigned tostringbuff (TValue *obj, char *buff) { +unsigned luaO_tostringbuff (const TValue *obj, char *buff) { int len; lua_assert(ttisnumber(obj)); if (ttisinteger(obj)) - len = lua_integer2str(buff, MAXNUMBER2STR, ivalue(obj)); + len = lua_integer2str(buff, LUA_N2SBUFFSZ, ivalue(obj)); else len = tostringbuffFloat(fltvalue(obj), buff); - lua_assert(len < MAXNUMBER2STR); + lua_assert(len < LUA_N2SBUFFSZ); return cast_uint(len); } @@ -458,8 +460,8 @@ static unsigned tostringbuff (TValue *obj, char *buff) { ** Convert a number object to a Lua string, replacing the value at 'obj' */ void luaO_tostring (lua_State *L, TValue *obj) { - char buff[MAXNUMBER2STR]; - unsigned len = tostringbuff(obj, buff); + char buff[LUA_N2SBUFFSZ]; + unsigned len = luaO_tostringbuff(obj, buff); setsvalue(L, obj, luaS_newlstr(L, buff, len)); } @@ -474,10 +476,10 @@ void luaO_tostring (lua_State *L, TValue *obj) { /* ** Size for buffer space used by 'luaO_pushvfstring'. It should be -** (LUA_IDSIZE + MAXNUMBER2STR) + a minimal space for basic messages, +** (LUA_IDSIZE + LUA_N2SBUFFSZ) + a minimal space for basic messages, ** 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 + LUA_N2SBUFFSZ + 95) /* ** Buffer used by 'luaO_pushvfstring'. 'err' signals an error while @@ -579,8 +581,8 @@ static void addstr2buff (BuffFS *buff, const char *str, size_t slen) { ** Add a numeral to the buffer. */ static void addnum2buff (BuffFS *buff, TValue *num) { - char numbuff[MAXNUMBER2STR]; - unsigned len = tostringbuff(num, numbuff); /* format number into 'numbuff' */ + char numbuff[LUA_N2SBUFFSZ]; + unsigned len = luaO_tostringbuff(num, numbuff); addstr2buff(buff, numbuff, len); } @@ -626,9 +628,9 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { break; } case 'p': { /* a pointer */ - char bf[MAXNUMBER2STR]; /* enough space for '%p' */ + char bf[LUA_N2SBUFFSZ]; /* enough space for '%p' */ void *p = va_arg(argp, void *); - int len = lua_pointer2str(bf, MAXNUMBER2STR, p); + int len = lua_pointer2str(bf, LUA_N2SBUFFSZ, p); addstr2buff(&buff, bf, cast_uint(len)); break; } diff --git a/lobject.h b/lobject.h index 2411410b..b1407b77 100644 --- a/lobject.h +++ b/lobject.h @@ -845,6 +845,7 @@ LUAI_FUNC int luaO_rawarith (lua_State *L, int op, const TValue *p1, LUAI_FUNC void luaO_arith (lua_State *L, int op, const TValue *p1, const TValue *p2, StkId res); LUAI_FUNC size_t luaO_str2num (const char *s, TValue *o); +LUAI_FUNC unsigned luaO_tostringbuff (const TValue *obj, char *buff); LUAI_FUNC lu_byte luaO_hexavalue (int c); LUAI_FUNC void luaO_tostring (lua_State *L, TValue *obj); LUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt, diff --git a/lua.h b/lua.h index dcf49264..5fbc9d34 100644 --- a/lua.h +++ b/lua.h @@ -371,7 +371,9 @@ LUA_API int (lua_next) (lua_State *L, int idx); LUA_API void (lua_concat) (lua_State *L, int n); LUA_API void (lua_len) (lua_State *L, int idx); -LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s); +#define LUA_N2SBUFFSZ 64 +LUA_API unsigned (lua_numbertostrbuff) (lua_State *L, int idx, char *buff); +LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s); LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud); diff --git a/manual/manual.of b/manual/manual.of index 6947b2a0..f0b17b4c 100644 --- a/manual/manual.of +++ b/manual/manual.of @@ -675,7 +675,7 @@ approximately @M{n} bytes between steps. The garbage-collector step multiplier controls how much work each incremental step does. A value of @M{n} means the interpreter will execute -@M{n%} @emphx{units of work} for each byte allocated. +@M{n%} @emphx{units of work} for each word allocated. A unit of work corresponds roughly to traversing one slot or sweeping one object. Larger values make the collector more aggressive. @@ -3829,11 +3829,26 @@ This macro may evaluate its arguments more than once. } +@APIEntry{unsigned (lua_numbertostrbuff) (lua_State *L, int idx, + char *buff);| +@apii{0,0,-} + +Converts the number at acceptable index @id{idx} to a string +and puts the result in @id{buff}. +The buffer must have a size of at least @Lid{LUA_N2SBUFFSZ} bytes. +The conversion follows a non-specified format @see{coercion}. +The function returns the number of bytes written to the buffer +(including the final zero), +or zero if the value at @id{idx} is not a number. + +} + @APIEntry{int lua_pcall (lua_State *L, int nargs, int nresults, int msgh);| @apii{nargs + 1,nresults|1,-} Calls a function (or a callable object) in protected mode. + Both @id{nargs} and @id{nresults} have the same meaning as in @Lid{lua_call}. If there are no errors during the call, -- cgit v1.2.3-55-g6feb