diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-11-13 13:50:33 -0200 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-11-13 13:50:33 -0200 |
commit | 8cb84210ab95e882c01363a6794508ec923ade90 (patch) | |
tree | 816598810dbf22e7bb119977863501edf067ffab | |
parent | 5fda30b4f9f82b901113a6e666c797f835c708eb (diff) | |
download | lua-8cb84210ab95e882c01363a6794508ec923ade90.tar.gz lua-8cb84210ab95e882c01363a6794508ec923ade90.tar.bz2 lua-8cb84210ab95e882c01363a6794508ec923ade90.zip |
String buffer using to-be-closed variable
The string buffers in the C API now mark their boxes as to-be-closed
variables, to release their buffers in case of errors.
-rw-r--r-- | lauxlib.c | 26 | ||||
-rw-r--r-- | testes/locals.lua | 32 |
2 files changed, 47 insertions, 11 deletions
@@ -470,10 +470,8 @@ static void *resizebox (lua_State *L, int idx, size_t newsize) { | |||
470 | lua_Alloc allocf = lua_getallocf(L, &ud); | 470 | lua_Alloc allocf = lua_getallocf(L, &ud); |
471 | UBox *box = (UBox *)lua_touserdata(L, idx); | 471 | UBox *box = (UBox *)lua_touserdata(L, idx); |
472 | void *temp = allocf(ud, box->box, box->bsize, newsize); | 472 | void *temp = allocf(ud, box->box, box->bsize, newsize); |
473 | if (temp == NULL && newsize > 0) { /* allocation error? */ | 473 | if (temp == NULL && newsize > 0) /* allocation error? */ |
474 | resizebox(L, idx, 0); /* free buffer */ | ||
475 | luaL_error(L, "not enough memory for buffer allocation"); | 474 | luaL_error(L, "not enough memory for buffer allocation"); |
476 | } | ||
477 | box->box = temp; | 475 | box->box = temp; |
478 | box->bsize = newsize; | 476 | box->bsize = newsize; |
479 | return temp; | 477 | return temp; |
@@ -486,16 +484,20 @@ static int boxgc (lua_State *L) { | |||
486 | } | 484 | } |
487 | 485 | ||
488 | 486 | ||
489 | static void *newbox (lua_State *L, size_t newsize) { | 487 | static const luaL_Reg boxmt[] = { /* box metamethods */ |
488 | {"__gc", boxgc}, | ||
489 | {"__close", boxgc}, | ||
490 | {NULL, NULL} | ||
491 | }; | ||
492 | |||
493 | |||
494 | static void newbox (lua_State *L) { | ||
490 | UBox *box = (UBox *)lua_newuserdatauv(L, sizeof(UBox), 0); | 495 | UBox *box = (UBox *)lua_newuserdatauv(L, sizeof(UBox), 0); |
491 | box->box = NULL; | 496 | box->box = NULL; |
492 | box->bsize = 0; | 497 | box->bsize = 0; |
493 | if (luaL_newmetatable(L, "_UBOX*")) { /* creating metatable? */ | 498 | if (luaL_newmetatable(L, "_UBOX*")) /* creating metatable? */ |
494 | lua_pushcfunction(L, boxgc); | 499 | luaL_setfuncs(L, boxmt, 0); /* set its metamethods */ |
495 | lua_setfield(L, -2, "__gc"); /* metatable.__gc = boxgc */ | ||
496 | } | ||
497 | lua_setmetatable(L, -2); | 500 | lua_setmetatable(L, -2); |
498 | return resizebox(L, -1, newsize); | ||
499 | } | 501 | } |
500 | 502 | ||
501 | 503 | ||
@@ -536,9 +538,11 @@ static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) { | |||
536 | if (buffonstack(B)) /* buffer already has a box? */ | 538 | if (buffonstack(B)) /* buffer already has a box? */ |
537 | newbuff = (char *)resizebox(L, boxidx, newsize); /* resize it */ | 539 | newbuff = (char *)resizebox(L, boxidx, newsize); /* resize it */ |
538 | else { /* no box yet */ | 540 | else { /* no box yet */ |
539 | newbuff = (char *)newbox(L, newsize); /* create a new box */ | 541 | newbox(L); /* create a new box */ |
540 | memcpy(newbuff, B->b, B->n * sizeof(char)); /* copy original content */ | ||
541 | lua_insert(L, boxidx); /* move box to its intended position */ | 542 | lua_insert(L, boxidx); /* move box to its intended position */ |
543 | lua_toclose(L, boxidx); | ||
544 | newbuff = (char *)resizebox(L, boxidx, newsize); | ||
545 | memcpy(newbuff, B->b, B->n * sizeof(char)); /* copy original content */ | ||
542 | } | 546 | } |
543 | B->b = newbuff; | 547 | B->b = newbuff; |
544 | B->size = newsize; | 548 | B->size = newsize; |
diff --git a/testes/locals.lua b/testes/locals.lua index 28f88e54..8766b3d5 100644 --- a/testes/locals.lua +++ b/testes/locals.lua | |||
@@ -324,6 +324,38 @@ if rawget(_G, "T") then | |||
324 | local _, msg = pcall(test) | 324 | local _, msg = pcall(test) |
325 | assert(msg == 1000) | 325 | assert(msg == 1000) |
326 | 326 | ||
327 | |||
328 | do -- testing 'toclose' in C string buffer | ||
329 | local s = string.rep("a", 10000) | ||
330 | local a = {s, s} | ||
331 | |||
332 | -- ensure proper initialization (stack space, metatable) | ||
333 | table.concat(a) | ||
334 | collectgarbage(); collectgarbage() | ||
335 | |||
336 | local m = T.totalmem() | ||
337 | |||
338 | -- error in the second buffer allocation | ||
339 | T.alloccount(3) | ||
340 | assert(not pcall(table.concat, a)) | ||
341 | T.alloccount() | ||
342 | -- first buffer was released by 'toclose' | ||
343 | assert(T.totalmem() - m <= 5000) | ||
344 | |||
345 | -- error in creation of final string | ||
346 | T.alloccount(4) | ||
347 | assert(not pcall(table.concat, a)) | ||
348 | T.alloccount() | ||
349 | -- second buffer was released by 'toclose' | ||
350 | assert(T.totalmem() - m <= 5000) | ||
351 | |||
352 | -- userdata, upvalue, buffer, buffer, string | ||
353 | T.alloccount(5) | ||
354 | assert(#table.concat(a) == 20000) | ||
355 | T.alloccount() | ||
356 | |||
357 | print'+' | ||
358 | end | ||
327 | end | 359 | end |
328 | 360 | ||
329 | 361 | ||