Browse Source

New function 'lua_numbertostrbuff'

It converts a Lua number to a string in a buffer, without creating
a new Lua string.
Roberto Ierusalimschy 10 months ago
parent
commit
e3ce88c9e8
7 changed files with 67 additions and 40 deletions
  1. 12 0
      lapi.c
  2. 3 4
      lauxlib.c
  3. 9 13
      liolib.c
  4. 23 21
      lobject.c
  5. 1 0
      lobject.h
  6. 3 1
      lua.h
  7. 16 1
      manual/manual.of

+ 12 - 0
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) {
 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)

+ 3 - 4
lauxlib.c

@@ -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))
-          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;
         break;
       }
       }
       case LUA_TSTRING:
       case LUA_TSTRING:

+ 9 - 13
liolib.c

@@ -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) {
-      /* 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))
   if (l_likely(status))
     return 1;  /* file handle already on stack top */
     return 1;  /* file handle already on stack top */

+ 23 - 21
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) {
 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];
-  unsigned len = tostringbuff(obj, buff);
+  char buff[LUA_N2SBUFFSZ];
+  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];
-  unsigned len = tostringbuff(num, numbuff);  /* format number into 'numbuff' */
+  char numbuff[LUA_N2SBUFFSZ];
+  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;
       }
       }

+ 1 - 0
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,
 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,

+ 3 - 1
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_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);

+ 16 - 1
manual/manual.of

@@ -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,