aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2023-11-13 13:12:33 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2023-11-13 13:12:33 -0300
commit6d042a178fba32d10ec23c98fb2fd284397ccddc (patch)
treef68f9c48a5c3832e929f1317bfd83750ca13b17d
parenteabf425c76e0089eb88e102e2a44d8c8a37bc213 (diff)
downloadlua-6d042a178fba32d10ec23c98fb2fd284397ccddc.tar.gz
lua-6d042a178fba32d10ec23c98fb2fd284397ccddc.tar.bz2
lua-6d042a178fba32d10ec23c98fb2fd284397ccddc.zip
Auxiliary buffer uses external strings
The buffer system from the auxiliary library reuses its buffer as external memory when closing long strings.
Diffstat (limited to '')
-rw-r--r--lauxlib.c54
-rw-r--r--testes/gc.lua3
-rw-r--r--testes/locals.lua10
3 files changed, 40 insertions, 27 deletions
diff --git a/lauxlib.c b/lauxlib.c
index 555a5976..73190975 100644
--- a/lauxlib.c
+++ b/lauxlib.c
@@ -470,18 +470,27 @@ typedef struct UBox {
470} UBox; 470} UBox;
471 471
472 472
473/* Resize the buffer used by a box. Optimize for the common case of
474** resizing to the old size. (For instance, __gc will resize the box
475** to 0 even after it was closed. 'pushresult' may also resize it to a
476** final size that is equal to the one set when the buffer was created.)
477*/
473static void *resizebox (lua_State *L, int idx, size_t newsize) { 478static void *resizebox (lua_State *L, int idx, size_t newsize) {
474 void *ud;
475 lua_Alloc allocf = lua_getallocf(L, &ud);
476 UBox *box = (UBox *)lua_touserdata(L, idx); 479 UBox *box = (UBox *)lua_touserdata(L, idx);
477 void *temp = allocf(ud, box->box, box->bsize, newsize); 480 if (box->bsize == newsize) /* not changing size? */
478 if (l_unlikely(temp == NULL && newsize > 0)) { /* allocation error? */ 481 return box->box; /* keep the buffer */
479 lua_pushliteral(L, "not enough memory"); 482 else {
480 lua_error(L); /* raise a memory error */ 483 void *ud;
484 lua_Alloc allocf = lua_getallocf(L, &ud);
485 void *temp = allocf(ud, box->box, box->bsize, newsize);
486 if (l_unlikely(temp == NULL && newsize > 0)) { /* allocation error? */
487 lua_pushliteral(L, "not enough memory");
488 lua_error(L); /* raise a memory error */
489 }
490 box->box = temp;
491 box->bsize = newsize;
492 return temp;
481 } 493 }
482 box->box = temp;
483 box->bsize = newsize;
484 return temp;
485} 494}
486 495
487 496
@@ -526,15 +535,15 @@ static void newbox (lua_State *L) {
526 535
527/* 536/*
528** Compute new size for buffer 'B', enough to accommodate extra 'sz' 537** Compute new size for buffer 'B', enough to accommodate extra 'sz'
529** bytes. (The test for "not big enough" also gets the case when the 538** bytes plus one for a terminating zero. (The test for "not big enough"
530** computation of 'newsize' overflows.) 539** also gets the case when the computation of 'newsize' overflows.)
531*/ 540*/
532static size_t newbuffsize (luaL_Buffer *B, size_t sz) { 541static size_t newbuffsize (luaL_Buffer *B, size_t sz) {
533 size_t newsize = (B->size / 2) * 3; /* buffer size * 1.5 */ 542 size_t newsize = (B->size / 2) * 3; /* buffer size * 1.5 */
534 if (l_unlikely(MAX_SIZET - sz < B->n)) /* overflow in (B->n + sz)? */ 543 if (l_unlikely(MAX_SIZET - sz - 1 < B->n)) /* overflow in (B->n + sz + 1)? */
535 return luaL_error(B->L, "buffer too large"); 544 return luaL_error(B->L, "buffer too large");
536 if (newsize < B->n + sz) /* not big enough? */ 545 if (newsize < B->n + sz + 1) /* not big enough? */
537 newsize = B->n + sz; 546 newsize = B->n + sz + 1;
538 return newsize; 547 return newsize;
539} 548}
540 549
@@ -594,9 +603,22 @@ LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) {
594LUALIB_API void luaL_pushresult (luaL_Buffer *B) { 603LUALIB_API void luaL_pushresult (luaL_Buffer *B) {
595 lua_State *L = B->L; 604 lua_State *L = B->L;
596 checkbufferlevel(B, -1); 605 checkbufferlevel(B, -1);
597 lua_pushlstring(L, B->b, B->n); 606 if (!buffonstack(B)) /* using static buffer? */
598 if (buffonstack(B)) 607 lua_pushlstring(L, B->b, B->n); /* save result as regular string */
608 else { /* reuse buffer already allocated */
609 UBox *box = (UBox *)lua_touserdata(L, -1);
610 void *ud;
611 lua_Alloc allocf = lua_getallocf(L, &ud); /* function to free buffer */
612 size_t len = B->n; /* final string length */
613 char *s;
614 resizebox(L, -1, len + 1); /* adjust box size to content size */
615 s = (char*)box->box; /* final buffer address */
616 s[len] = '\0'; /* add ending zero */
617 /* clear box, as 'lua_pushextlstring' will take control over buffer */
618 box->bsize = 0; box->box = NULL;
619 lua_pushextlstring(L, s, len, allocf, ud);
599 lua_closeslot(L, -2); /* close the box */ 620 lua_closeslot(L, -2); /* close the box */
621 }
600 lua_remove(L, -2); /* remove box or placeholder from the stack */ 622 lua_remove(L, -2); /* remove box or placeholder from the stack */
601} 623}
602 624
diff --git a/testes/gc.lua b/testes/gc.lua
index 03093e34..3c928d7c 100644
--- a/testes/gc.lua
+++ b/testes/gc.lua
@@ -460,10 +460,7 @@ do -- tests for string keys in weak tables
460 a[string.rep("a", 2^22)] = 25 -- long string key -> number value 460 a[string.rep("a", 2^22)] = 25 -- long string key -> number value
461 a[string.rep("b", 2^22)] = {} -- long string key -> colectable value 461 a[string.rep("b", 2^22)] = {} -- long string key -> colectable value
462 a[{}] = 14 -- colectable key 462 a[{}] = 14 -- colectable key
463 assert(collectgarbage("count") > m + 2^13) -- 2^13 == 2 * 2^22 in KB
464 collectgarbage() 463 collectgarbage()
465 assert(collectgarbage("count") >= m + 2^12 and
466 collectgarbage("count") < m + 2^13) -- one key was collected
467 local k, v = next(a) -- string key with number value preserved 464 local k, v = next(a) -- string key with number value preserved
468 assert(k == string.rep("a", 2^22) and v == 25) 465 assert(k == string.rep("a", 2^22) and v == 25)
469 assert(next(a, k) == nil) -- everything else cleared 466 assert(next(a, k) == nil) -- everything else cleared
diff --git a/testes/locals.lua b/testes/locals.lua
index 2c48546d..090d846b 100644
--- a/testes/locals.lua
+++ b/testes/locals.lua
@@ -728,14 +728,8 @@ if rawget(_G, "T") then
728 -- first buffer was released by 'toclose' 728 -- first buffer was released by 'toclose'
729 assert(T.totalmem() - m <= extra) 729 assert(T.totalmem() - m <= extra)
730 730
731 -- error in creation of final string 731 -- userdata, buffer, final string
732 T.totalmem(m + 2 * lim + extra) 732 T.totalmem(m + 2*lim + extra)
733 assert(not pcall(table.concat, a))
734 -- second buffer was released by 'toclose'
735 assert(T.totalmem() - m <= extra)
736
737 -- userdata, buffer, buffer, final string
738 T.totalmem(m + 4*lim + extra)
739 assert(#table.concat(a) == 2*lim) 733 assert(#table.concat(a) == 2*lim)
740 734
741 T.totalmem(0) -- remove memory limit 735 T.totalmem(0) -- remove memory limit