From e663a24ab03a54fa221c20a793812e5c5ffdf94f Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Fri, 8 Dec 2017 15:28:25 -0200 Subject: more freedom in handling memory-allocation errors (not all allocations automatically raise an error), which allows fixing a bug when resizing a table. --- ltable.c | 95 ++++++++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 63 insertions(+), 32 deletions(-) (limited to 'ltable.c') diff --git a/ltable.c b/ltable.c index d1345009..85101a8a 100644 --- a/ltable.c +++ b/ltable.c @@ -1,5 +1,5 @@ /* -** $Id: ltable.c,v 2.127 2017/11/23 19:29:04 roberto Exp roberto $ +** $Id: ltable.c,v 2.128 2017/12/07 18:59:52 roberto Exp roberto $ ** Lua tables (hash) ** See Copyright Notice in lua.h */ @@ -357,15 +357,6 @@ static int numusehash (const Table *t, unsigned int *nums, unsigned int *pna) { } -static void setarrayvector (lua_State *L, Table *t, unsigned int size) { - unsigned int i; - luaM_reallocvector(L, t->array, t->sizearray, size, TValue); - for (i=t->sizearray; iarray[i]); - t->sizearray = size; -} - - /* ** Creates an array for the hash part of a table with the given ** size, or reuses the dummy node if size is zero. @@ -398,39 +389,79 @@ static void setnodevector (lua_State *L, Table *t, unsigned int size) { } -void luaH_resize (lua_State *L, Table *t, unsigned int nasize, +/* +** (Re)insert all elements from list 'nodes' into table 't'. +*/ +static void reinsert(lua_State *L, Node *nodes, int nsize, Table *t) { + int j; + for (j = nsize - 1; j >= 0; j--) { + Node *old = nodes + j; + if (!ttisnil(gval(old))) { + /* doesn't need barrier/invalidate cache, as entry was + already present in the table */ + TValue k; + getnodekey(L, &k, old); + setobjt2t(L, luaH_set(L, t, &k), gval(old)); + } + } +} + + +/* +** Resize table 't' for the new given sizes. Both allocations +** (for the hash part and for the array part) can fail, which +** creates some subtleties. If the first allocation, for the hash +** part, fails, an error is raised and that is it. Otherwise, +** copy the elements in the shrinking part of the array (if it +** is shrinking) into the new hash. Then it reallocates the array part. +** If that fails, it frees the new hash part and restores the old hash +** part (to restore the original state of the table), and then raises +** the allocation error. Otherwise, initialize the new part of the +** array (if any) with nils and reinsert the elements in the old +** hash back into the new parts of the table. +*/ +void luaH_resize (lua_State *L, Table *t, unsigned int newasize, unsigned int nhsize) { unsigned int i; - int j; - unsigned int oldasize = t->sizearray; + Node *oldnode = t->node; /* save old hash ... */ + Node *oldlastfree = t->lastfree; + int oldlsizenode = t->lsizenode; int oldhsize = allocsizenode(t); - Node *nold = t->node; /* save old hash ... */ - if (nasize > oldasize) /* array part must grow? */ - setarrayvector(L, t, nasize); + unsigned int oldasize = t->sizearray; + TValue *newarray; /* create new hash part with appropriate size */ setnodevector(L, t, nhsize); - if (nasize < oldasize) { /* array part must shrink? */ - t->sizearray = nasize; - /* re-insert elements from vanishing slice */ - for (i=nasize; isizearray = newasize; /* pretend array has new size */ + for (i = newasize; i < oldasize; i++) { if (!ttisnil(&t->array[i])) luaH_setint(L, t, i + 1, &t->array[i]); } - /* shrink array */ - luaM_reallocvector(L, t->array, oldasize, nasize, TValue); + t->sizearray = oldasize; /* restore current size */ } - /* re-insert elements from hash part */ - for (j = oldhsize - 1; j >= 0; j--) { - Node *old = nold + j; - if (!ttisnil(gval(old))) { - /* doesn't need barrier/invalidate cache, as entry was - already present in the table */ - TValue k; getnodekey(L, &k, old); - setobjt2t(L, luaH_set(L, t, &k), gval(old)); - } + /* allocate new array */ + newarray = luaM_reallocvector(L, t->array, oldasize, newasize, TValue); + if (newarray == NULL && newasize > 0) { /* allocation failed? */ + if (nhsize > 0) /* not the dummy node? */ + luaM_freearray(L, t->node, allocsizenode(t)); /* release new hash part */ + t->node = oldnode; /* restore original hash part */ + t->lastfree = oldlastfree; + t->lsizenode = oldlsizenode; + lua_assert(!isdummy(t) == (t->node != dummynode)); + luaM_error(L); /* error with array unchanged */ } + /* allocation ok; initialize new part of the array */ + t->array = newarray; + t->sizearray = newasize; + for (i = oldasize; i < newasize; i++) + setnilvalue(&t->array[i]); + /* re-insert elements from old hash part into new parts */ + reinsert(L, oldnode, oldhsize, t); + /* free old hash */ if (oldhsize > 0) /* not the dummy node? */ - luaM_freearray(L, nold, cast(size_t, oldhsize)); /* free old hash */ + luaM_freearray(L, oldnode, cast(size_t, oldhsize)); + lua_assert(!isdummy(t) == (t->node != dummynode)); } -- cgit v1.2.3-55-g6feb