New function 'lua_numbertostrbuff'

It converts a Lua number to a string in a buffer, without creating
a new Lua string.
This commit is contained in:
Roberto Ierusalimschy 2024-10-23 17:16:17 -03:00
parent 5ffcd458f0
commit e3ce88c9e8
7 changed files with 67 additions and 40 deletions

12
lapi.c
View File

@ -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) { LUA_API size_t lua_stringtonumber (lua_State *L, const char *s) {
size_t sz = luaO_str2num(s, s2v(L->top.p)); size_t sz = luaO_str2num(s, s2v(L->top.p));
if (sz != 0) if (sz != 0)

View File

@ -920,10 +920,9 @@ LUALIB_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) {
else { else {
switch (lua_type(L, idx)) { switch (lua_type(L, idx)) {
case LUA_TNUMBER: { case LUA_TNUMBER: {
if (lua_isinteger(L, idx)) char buff[LUA_N2SBUFFSZ];
lua_pushfstring(L, "%I", (LUAI_UACINT)lua_tointeger(L, idx)); lua_numbertostrbuff(L, idx, buff);
else lua_pushstring(L, buff);
lua_pushfstring(L, "%f", (LUAI_UACNUMBER)lua_tonumber(L, idx));
break; break;
} }
case LUA_TSTRING: case LUA_TSTRING:

View File

@ -665,20 +665,16 @@ static int g_write (lua_State *L, FILE *f, int arg) {
int status = 1; int status = 1;
errno = 0; errno = 0;
for (; nargs--; arg++) { for (; nargs--; arg++) {
if (lua_type(L, arg) == LUA_TNUMBER) { char buff[LUA_N2SBUFFSZ];
/* optimization: could be done exactly as for strings */ const char *s;
int len = lua_isinteger(L, arg) size_t len = lua_numbertostrbuff(L, arg, buff); /* try as a number */
? fprintf(f, LUA_INTEGER_FMT, if (len > 0) { /* did conversion work (value was a number)? */
(LUAI_UACINT)lua_tointeger(L, arg)) s = buff;
: fprintf(f, LUA_NUMBER_FMT, len--;
(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);
} }
else /* must be a string */
s = luaL_checklstring(L, arg, &len);
status = status && (fwrite(s, sizeof(char), len, f) == len);
} }
if (l_likely(status)) if (l_likely(status))
return 1; /* file handle already on stack top */ return 1; /* file handle already on stack top */

View File

@ -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 ** The size of the buffer for the conversion of a number to a string
** enough to accommodate both LUA_INTEGER_FMT and LUA_NUMBER_FMT. ** 'LUA_N2SBUFFSZ' must be enough to accommodate both LUA_INTEGER_FMT
** For a long long int, this is 19 digits plus a sign and a final '\0', ** and LUA_NUMBER_FMT. For a long long int, this is 19 digits plus a
** adding to 21. For a long double, it can go to a sign, the dot, an ** sign and a final '\0', adding to 21. For a long double, it can go to
** exponent letter, an exponent sign, 4 exponent digits, the final ** a sign, the dot, an exponent letter, an exponent sign, 4 exponent
** '\0', plus the significant digits, which are approximately the *_DIG ** digits, the final '\0', plus the significant digits, which are
** attribute. ** 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) { static int tostringbuffFloat (lua_Number n, char *buff) {
/* first conversion */ /* first conversion */
int len = l_sprintf(buff, MAXNUMBER2STR, LUA_NUMBER_FMT, int len = l_sprintf(buff, LUA_N2SBUFFSZ, LUA_NUMBER_FMT,
(LUAI_UACNUMBER)n); (LUAI_UACNUMBER)n);
lua_Number check = lua_str2number(buff, NULL); /* read it back */ lua_Number check = lua_str2number(buff, NULL); /* read it back */
if (check != n) { /* not enough precision? */ if (check != n) { /* not enough precision? */
/* convert again with more 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); (LUAI_UACNUMBER)n);
} }
/* looks like an integer? */ /* 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. ** 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; int len;
lua_assert(ttisnumber(obj)); lua_assert(ttisnumber(obj));
if (ttisinteger(obj)) if (ttisinteger(obj))
len = lua_integer2str(buff, MAXNUMBER2STR, ivalue(obj)); len = lua_integer2str(buff, LUA_N2SBUFFSZ, ivalue(obj));
else else
len = tostringbuffFloat(fltvalue(obj), buff); len = tostringbuffFloat(fltvalue(obj), buff);
lua_assert(len < MAXNUMBER2STR); lua_assert(len < LUA_N2SBUFFSZ);
return cast_uint(len); 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' ** Convert a number object to a Lua string, replacing the value at 'obj'
*/ */
void luaO_tostring (lua_State *L, TValue *obj) { void luaO_tostring (lua_State *L, TValue *obj) {
char buff[MAXNUMBER2STR]; char buff[LUA_N2SBUFFSZ];
unsigned len = tostringbuff(obj, buff); unsigned len = luaO_tostringbuff(obj, buff);
setsvalue(L, obj, luaS_newlstr(L, buff, len)); 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 ** 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. ** 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 ** 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. ** Add a numeral to the buffer.
*/ */
static void addnum2buff (BuffFS *buff, TValue *num) { static void addnum2buff (BuffFS *buff, TValue *num) {
char numbuff[MAXNUMBER2STR]; char numbuff[LUA_N2SBUFFSZ];
unsigned len = tostringbuff(num, numbuff); /* format number into 'numbuff' */ unsigned len = luaO_tostringbuff(num, numbuff);
addstr2buff(buff, numbuff, len); addstr2buff(buff, numbuff, len);
} }
@ -626,9 +628,9 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
break; break;
} }
case 'p': { /* a pointer */ 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 *); 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)); addstr2buff(&buff, bf, cast_uint(len));
break; break;
} }

View File

@ -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, LUAI_FUNC void luaO_arith (lua_State *L, int op, const TValue *p1,
const TValue *p2, StkId res); const TValue *p2, StkId res);
LUAI_FUNC size_t luaO_str2num (const char *s, TValue *o); 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 lu_byte luaO_hexavalue (int c);
LUAI_FUNC void luaO_tostring (lua_State *L, TValue *obj); LUAI_FUNC void luaO_tostring (lua_State *L, TValue *obj);
LUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt, LUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt,

4
lua.h
View File

@ -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_concat) (lua_State *L, int n);
LUA_API void (lua_len) (lua_State *L, int idx); 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 lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud); LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud);

View File

@ -675,7 +675,7 @@ approximately @M{n} bytes between steps.
The garbage-collector step multiplier The garbage-collector step multiplier
controls how much work each incremental step does. controls how much work each incremental step does.
A value of @M{n} means the interpreter will execute 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 A unit of work corresponds roughly to traversing one slot
or sweeping one object. or sweeping one object.
Larger values make the collector more aggressive. 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);| @APIEntry{int lua_pcall (lua_State *L, int nargs, int nresults, int msgh);|
@apii{nargs + 1,nresults|1,-} @apii{nargs + 1,nresults|1,-}
Calls a function (or a callable object) in protected mode. Calls a function (or a callable object) in protected mode.
Both @id{nargs} and @id{nresults} have the same meaning as Both @id{nargs} and @id{nresults} have the same meaning as
in @Lid{lua_call}. in @Lid{lua_call}.
If there are no errors during the call, If there are no errors during the call,