aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lobject.c144
-rw-r--r--ltests.c9
-rw-r--r--luaconf.h7
-rw-r--r--testes/strings.lua61
4 files changed, 177 insertions, 44 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
diff --git a/ltests.c b/ltests.c
index 40de2292..7d441d1a 100644
--- a/ltests.c
+++ b/ltests.c
@@ -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);
diff --git a/luaconf.h b/luaconf.h
index 76a61616..019f2eb6 100644
--- a/luaconf.h
+++ b/luaconf.h
@@ -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")
401end 401end
402 402
403
404if T==nil then
405 (Message or print)
406 ("\n >>> testC not active: skipping 'pushfstring' tests <<<\n")
407else
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, {})
461end
462
463
403print('OK') 464print('OK')
404 465