diff options
| -rw-r--r-- | lobject.c | 144 | ||||
| -rw-r--r-- | ltests.c | 9 | ||||
| -rw-r--r-- | luaconf.h | 7 | ||||
| -rw-r--r-- | testes/strings.lua | 61 |
4 files changed, 177 insertions, 44 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 | ||
| @@ -1481,6 +1481,15 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) { | |||
| 1481 | else if EQ("pushvalue") { | 1481 | else if EQ("pushvalue") { |
| 1482 | lua_pushvalue(L1, getindex); | 1482 | lua_pushvalue(L1, getindex); |
| 1483 | } | 1483 | } |
| 1484 | else if EQ("pushfstringI") { | ||
| 1485 | lua_pushfstring(L1, lua_tostring(L, -2), (int)lua_tointeger(L, -1)); | ||
| 1486 | } | ||
| 1487 | else if EQ("pushfstringS") { | ||
| 1488 | lua_pushfstring(L1, lua_tostring(L, -2), lua_tostring(L, -1)); | ||
| 1489 | } | ||
| 1490 | else if EQ("pushfstringP") { | ||
| 1491 | lua_pushfstring(L1, lua_tostring(L, -2), lua_topointer(L, -1)); | ||
| 1492 | } | ||
| 1484 | else if EQ("rawgeti") { | 1493 | else if EQ("rawgeti") { |
| 1485 | int t = getindex; | 1494 | int t = getindex; |
| 1486 | lua_rawgeti(L1, t, getnum); | 1495 | lua_rawgeti(L1, t, getnum); |
| @@ -579,13 +579,6 @@ | |||
| 579 | 579 | ||
| 580 | 580 | ||
| 581 | /* | 581 | /* |
| 582 | @@ lua_pointer2str converts a pointer to a readable string in a | ||
| 583 | ** non-specified way. | ||
| 584 | */ | ||
| 585 | #define lua_pointer2str(buff,sz,p) l_sprintf(buff,sz,"%p",p) | ||
| 586 | |||
| 587 | |||
| 588 | /* | ||
| 589 | @@ lua_number2strx converts a float to a hexadecimal numeric string. | 582 | @@ lua_number2strx converts a float to a hexadecimal numeric string. |
| 590 | ** In C99, 'sprintf' (with format specifiers '%a'/'%A') does that. | 583 | ** In C99, 'sprintf' (with format specifiers '%a'/'%A') does that. |
| 591 | ** Otherwise, you can leave 'lua_number2strx' undefined and Lua will | 584 | ** Otherwise, you can leave 'lua_number2strx' undefined and Lua will |
diff --git a/testes/strings.lua b/testes/strings.lua index 8bcbb391..66c1176d 100644 --- a/testes/strings.lua +++ b/testes/strings.lua | |||
| @@ -400,5 +400,66 @@ do | |||
| 400 | assert(co() == "2") | 400 | assert(co() == "2") |
| 401 | end | 401 | end |
| 402 | 402 | ||
| 403 | |||
| 404 | if T==nil then | ||
| 405 | (Message or print) | ||
| 406 | ("\n >>> testC not active: skipping 'pushfstring' tests <<<\n") | ||
| 407 | else | ||
| 408 | |||
| 409 | print"testing 'pushfstring'" | ||
| 410 | |||
| 411 | -- formats %U, %f, %I already tested elsewhere | ||
| 412 | |||
| 413 | local blen = 400 -- internal buffer length in 'luaO_pushfstring' | ||
| 414 | |||
| 415 | local function callpfs (op, fmt, n) | ||
| 416 | local x = {T.testC("pushfstring" .. op .. "; return *", fmt, n)} | ||
| 417 | -- stack has code, 'fmt', 'n', and result from operation | ||
| 418 | assert(#x == 4) -- make sure nothing else was left in the stack | ||
| 419 | return x[4] | ||
| 420 | end | ||
| 421 | |||
| 422 | local function testpfs (op, fmt, n) | ||
| 423 | assert(callpfs(op, fmt, n) == string.format(fmt, n)) | ||
| 424 | end | ||
| 425 | |||
| 426 | testpfs("I", "", 0) | ||
| 427 | testpfs("I", string.rep("a", blen - 1), 0) | ||
| 428 | testpfs("I", string.rep("a", blen), 0) | ||
| 429 | testpfs("I", string.rep("a", blen + 1), 0) | ||
| 430 | |||
| 431 | local str = string.rep("ab", blen) .. "%d" .. string.rep("d", blen / 2) | ||
| 432 | testpfs("I", str, 2^14) | ||
| 433 | testpfs("I", str, -2^15) | ||
| 434 | |||
| 435 | str = "%d" .. string.rep("cd", blen) | ||
| 436 | testpfs("I", str, 2^14) | ||
| 437 | testpfs("I", str, -2^15) | ||
| 438 | |||
| 439 | str = string.rep("c", blen - 2) .. "%d" | ||
| 440 | testpfs("I", str, 2^14) | ||
| 441 | testpfs("I", str, -2^15) | ||
| 442 | |||
| 443 | for l = 12, 14 do | ||
| 444 | local str1 = string.rep("a", l) | ||
| 445 | for i = 0, 500, 13 do | ||
| 446 | for j = 0, 500, 13 do | ||
| 447 | str = string.rep("a", i) .. "%s" .. string.rep("d", j) | ||
| 448 | testpfs("S", str, str1) | ||
| 449 | testpfs("S", str, str) | ||
| 450 | end | ||
| 451 | end | ||
| 452 | end | ||
| 453 | |||
| 454 | str = "abc %c def" | ||
| 455 | testpfs("I", str, string.byte("A")) | ||
| 456 | -- non-printable character | ||
| 457 | assert(callpfs("I", str, 255) == "abc <\\255> def") | ||
| 458 | |||
| 459 | str = string.rep("a", blen - 1) .. "%p" .. string.rep("cd", blen) | ||
| 460 | testpfs("P", str, {}) | ||
| 461 | end | ||
| 462 | |||
| 463 | |||
| 403 | print('OK') | 464 | print('OK') |
| 404 | 465 | ||
