aboutsummaryrefslogtreecommitdiff
path: root/lobject.c
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2019-04-24 14:01:20 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2019-04-24 14:01:20 -0300
commit3da34a5fa70a51f0cf06d677a4f07b470693260c (patch)
tree384aeb18a404a076a57188070f1e155a7cc24b3d /lobject.c
parent20b161e2859837e4f7fb1c19440ad7efe1588f1f (diff)
downloadlua-3da34a5fa70a51f0cf06d677a4f07b470693260c.tar.gz
lua-3da34a5fa70a51f0cf06d677a4f07b470693260c.tar.bz2
lua-3da34a5fa70a51f0cf06d677a4f07b470693260c.zip
Revamp of 'lua_pushfstring' / 'luaO_pushvfstring'
The function 'luaO_pushvfstring' now uses an internal buffer to concatenate small strings, instead of pushing all pieces on the stack. This avoids the creation of several small Lua strings for each piece of the result. (For instance, a format like "n: '%d'" used to create three intermediate strings: "n: '", the numeral, and "'". Now it creates none.)
Diffstat (limited to 'lobject.c')
-rw-r--r--lobject.c144
1 files changed, 107 insertions, 37 deletions
diff --git a/lobject.c b/lobject.c
index 67c37124..123f0e57 100644
--- a/lobject.c
+++ b/lobject.c
@@ -364,25 +364,44 @@ int luaO_utf8esc (char *buff, unsigned long x) {
364 364
365 365
366/* 366/*
367** Convert a number object to a string 367** Convert a number object to a string, adding it to a buffer
368*/ 368*/
369void luaO_tostring (lua_State *L, TValue *obj) { 369static size_t tostringbuff (TValue *obj, char *buff) {
370 char buff[MAXNUMBER2STR];
371 size_t len; 370 size_t len;
372 lua_assert(ttisnumber(obj)); 371 lua_assert(ttisnumber(obj));
373 if (ttisinteger(obj)) 372 if (ttisinteger(obj))
374 len = lua_integer2str(buff, sizeof(buff), ivalue(obj)); 373 len = lua_integer2str(buff, MAXNUMBER2STR, ivalue(obj));
375 else { 374 else {
376 len = lua_number2str(buff, sizeof(buff), fltvalue(obj)); 375 len = lua_number2str(buff, MAXNUMBER2STR, fltvalue(obj));
377 if (buff[strspn(buff, "-0123456789")] == '\0') { /* looks like an int? */ 376 if (buff[strspn(buff, "-0123456789")] == '\0') { /* looks like an int? */
378 buff[len++] = lua_getlocaledecpoint(); 377 buff[len++] = lua_getlocaledecpoint();
379 buff[len++] = '0'; /* adds '.0' to result */ 378 buff[len++] = '0'; /* adds '.0' to result */
380 } 379 }
381 } 380 }
381 return len;
382}
383
384
385/*
386** Convert a number object to a Lua string, replacing the value at 'obj'
387*/
388void luaO_tostring (lua_State *L, TValue *obj) {
389 char buff[MAXNUMBER2STR];
390 size_t len = tostringbuff(obj, buff);
382 setsvalue(L, obj, luaS_newlstr(L, buff, len)); 391 setsvalue(L, obj, luaS_newlstr(L, buff, len));
383} 392}
384 393
385 394
395/* size for buffer used by 'luaO_pushvfstring' */
396#define BUFVFS 400
397
398/* buffer used by 'luaO_pushvfstring' */
399typedef struct BuffFS {
400 int blen; /* length of partial string in 'buff' */
401 char buff[BUFVFS]; /* holds last part of the result */
402} BuffFS;
403
404
386static void pushstr (lua_State *L, const char *str, size_t l) { 405static void pushstr (lua_State *L, const char *str, size_t l) {
387 setsvalue2s(L, L->top, luaS_newlstr(L, str, l)); 406 setsvalue2s(L, L->top, luaS_newlstr(L, str, l));
388 L->top++; 407 L->top++;
@@ -390,59 +409,109 @@ static void pushstr (lua_State *L, const char *str, size_t l) {
390 409
391 410
392/* 411/*
412** empty the buffer into the stack
413*/
414static void clearbuff (lua_State *L, BuffFS *buff) {
415 pushstr(L, buff->buff, buff->blen); /* push buffer */
416 buff->blen = 0; /* buffer now is empty */
417}
418
419
420/*
421** Add 'str' to the buffer. It buffer has no enough space,
422** empty the buffer. If string is still larger than the buffer,
423** push the string directly to the stack. Return number of items
424** pushed.
425*/
426static int addstr2buff (lua_State *L, BuffFS *buff, const char *str,
427 size_t slen) {
428 int pushed = 0; /* number of items pushed to the stack */
429 lua_assert(buff->blen <= BUFVFS);
430 if (slen > BUFVFS - cast_sizet(buff->blen)) { /* string does not fit? */
431 clearbuff(L, buff);
432 pushed = 1;
433 if (slen >= BUFVFS) { /* string still does not fit into buffer? */
434 pushstr(L, str, slen); /* push string */
435 return 2;
436 }
437 }
438 memcpy(buff->buff + buff->blen, str, slen); /* add string to buffer */
439 buff->blen += slen;
440 return pushed;
441}
442
443
444/*
445** Add a number to the buffer; return number of strings pushed into
446** the stack. (At most one, to free buffer space.)
447*/
448static int addnum2buff (lua_State *L, BuffFS *buff, TValue *num) {
449 char numbuff[MAXNUMBER2STR];
450 size_t len = tostringbuff(num, numbuff); /* format number into 'numbuff' */
451 return addstr2buff(L, buff, numbuff, len);
452}
453
454
455/*
393** this function handles only '%d', '%c', '%f', '%p', and '%s' 456** this function handles only '%d', '%c', '%f', '%p', and '%s'
394 conventional formats, plus Lua-specific '%I' and '%U' 457 conventional formats, plus Lua-specific '%I' and '%U'
395*/ 458*/
396const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { 459const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
397 int n = 0; /* number of strings in the stack to concatenate */ 460 BuffFS buff; /* holds last part of the result */
398 const char *e; /* points to next conversion specifier */ 461 int pushed = 0; /* number of strings in the stack to concatenate */
462 const char *e; /* points to next '%' */
463 buff.blen = 0;
399 while ((e = strchr(fmt, '%')) != NULL) { 464 while ((e = strchr(fmt, '%')) != NULL) {
400 pushstr(L, fmt, e - fmt); /* string up to conversion specifier */ 465 pushed += addstr2buff(L, &buff, fmt, e - fmt); /* add 'fmt' up to '%' */
401 switch (*(e+1)) { 466 switch (*(e + 1)) { /* conversion specifier */
402 case 's': { /* zero-terminated string */ 467 case 's': { /* zero-terminated string */
403 const char *s = va_arg(argp, char *); 468 const char *s = va_arg(argp, char *);
404 if (s == NULL) s = "(null)"; 469 if (s == NULL) s = "(null)";
405 pushstr(L, s, strlen(s)); 470 pushed += addstr2buff(L, &buff, s, strlen(s));
406 break; 471 break;
407 } 472 }
408 case 'c': { /* an 'int' as a character */ 473 case 'c': { /* an 'int' as a character */
409 char buff = cast_char(va_arg(argp, int)); 474 /* if non-printable character, print its code */
410 if (lisprint(cast_uchar(buff))) 475 char bf[10];
411 pushstr(L, &buff, 1); 476 int c = va_arg(argp, int);
412 else /* non-printable character; print its code */ 477 int l = (lisprint(c)) ? l_sprintf(bf, sizeof(bf), "%c", c)
413 luaO_pushfstring(L, "<\\%d>", cast_uchar(buff)); 478 : l_sprintf(bf, sizeof(bf), "<\\%u>", c);
479 pushed += addstr2buff(L, &buff, bf, l);
414 break; 480 break;
415 } 481 }
416 case 'd': { /* an 'int' */ 482 case 'd': { /* an 'int' */
417 setivalue(s2v(L->top), va_arg(argp, int)); 483 TValue num;
418 goto top2str; 484 setivalue(&num, va_arg(argp, int));
485 pushed += addnum2buff(L, &buff, &num);
486 break;
419 } 487 }
420 case 'I': { /* a 'lua_Integer' */ 488 case 'I': { /* a 'lua_Integer' */
421 setivalue(s2v(L->top), cast(lua_Integer, va_arg(argp, l_uacInt))); 489 TValue num;
422 goto top2str; 490 setivalue(&num, cast(lua_Integer, va_arg(argp, l_uacInt)));
491 pushed += addnum2buff(L, &buff, &num);
492 break;
423 } 493 }
424 case 'f': { /* a 'lua_Number' */ 494 case 'f': { /* a 'lua_Number' */
425 setfltvalue(s2v(L->top), cast_num(va_arg(argp, l_uacNumber))); 495 TValue num;
426 top2str: /* convert the top element to a string */ 496 setfltvalue(&num, cast_num(va_arg(argp, l_uacNumber)));
427 L->top++; 497 pushed += addnum2buff(L, &buff, &num);
428 luaO_tostring(L, s2v(L->top - 1));
429 break; 498 break;
430 } 499 }
431 case 'p': { /* a pointer */ 500 case 'p': { /* a pointer */
432 char buff[4*sizeof(void *) + 8]; /* should be enough space for a '%p' */ 501 char bf[3 * sizeof(void*) + 8]; /* should be enough space for '%p' */
433 void *p = va_arg(argp, void *); 502 void *p = va_arg(argp, void *);
434 int l = lua_pointer2str(buff, sizeof(buff), p); 503 int l = l_sprintf(bf, sizeof(bf), "%p", p);
435 pushstr(L, buff, l); 504 pushed += addstr2buff(L, &buff, bf, l);
436 break; 505 break;
437 } 506 }
438 case 'U': { /* a 'long' as a UTF-8 sequence */ 507 case 'U': { /* a 'long' as a UTF-8 sequence */
439 char buff[UTF8BUFFSZ]; 508 char bf[UTF8BUFFSZ];
440 int l = luaO_utf8esc(buff, va_arg(argp, long)); 509 int l = luaO_utf8esc(bf, va_arg(argp, long));
441 pushstr(L, buff + UTF8BUFFSZ - l, l); 510 pushed += addstr2buff(L, &buff, bf + UTF8BUFFSZ - l, l);
442 break; 511 break;
443 } 512 }
444 case '%': { 513 case '%': {
445 pushstr(L, "%", 1); 514 pushed += addstr2buff(L, &buff, "%", 1);
446 break; 515 break;
447 } 516 }
448 default: { 517 default: {
@@ -450,15 +519,16 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
450 *(e + 1)); 519 *(e + 1));
451 } 520 }
452 } 521 }
453 n += 2; 522 if (pushed > 1 && L->top + 2 > L->stack_last) { /* no free stack space? */
454 if (L->top + 2 > L->stack_last) { /* no free stack space? */ 523 luaV_concat(L, pushed); /* join all partial results into one */
455 luaV_concat(L, n); 524 pushed = 1;
456 n = 1;
457 } 525 }
458 fmt = e + 2; 526 fmt = e + 2; /* skip '%' and the specifier */
459 } 527 }
460 pushstr(L, fmt, strlen(fmt)); 528 pushed += addstr2buff(L, &buff, fmt, strlen(fmt)); /* rest of 'fmt' */
461 if (n > 0) luaV_concat(L, n + 1); 529 clearbuff(L, &buff); /* empty buffer into the stack */
530 if (pushed > 0)
531 luaV_concat(L, pushed + 1); /* join all partial results */
462 return svalue(s2v(L->top - 1)); 532 return svalue(s2v(L->top - 1));
463} 533}
464 534