diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2019-04-24 14:01:20 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2019-04-24 14:01:20 -0300 |
commit | 3da34a5fa70a51f0cf06d677a4f07b470693260c (patch) | |
tree | 384aeb18a404a076a57188070f1e155a7cc24b3d /lobject.c | |
parent | 20b161e2859837e4f7fb1c19440ad7efe1588f1f (diff) | |
download | lua-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.c | 144 |
1 files changed, 107 insertions, 37 deletions
@@ -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 | */ |
369 | void luaO_tostring (lua_State *L, TValue *obj) { | 369 | static 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 | */ | ||
388 | void 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' */ | ||
399 | typedef 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 | |||
386 | static void pushstr (lua_State *L, const char *str, size_t l) { | 405 | static 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 | */ | ||
414 | static 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 | */ | ||
426 | static 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 | */ | ||
448 | static 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 | */ |
396 | const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { | 459 | const 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 | ||