diff options
-rw-r--r-- | lmem.h | 2 | ||||
-rw-r--r-- | lobject.h | 1 | ||||
-rw-r--r-- | ltable.c | 80 | ||||
-rw-r--r-- | ltable.h | 14 | ||||
-rw-r--r-- | ltests.c | 3 | ||||
-rw-r--r-- | ltm.h | 4 | ||||
-rw-r--r-- | testes/nextvar.lua | 4 |
7 files changed, 84 insertions, 24 deletions
@@ -63,6 +63,8 @@ | |||
63 | 63 | ||
64 | #define luaM_newobject(L,tag,s) luaM_malloc_(L, (s), tag) | 64 | #define luaM_newobject(L,tag,s) luaM_malloc_(L, (s), tag) |
65 | 65 | ||
66 | #define luaM_newblock(L, size) luaM_newvector(L, size, char) | ||
67 | |||
66 | #define luaM_growvector(L,v,nelems,size,t,limit,e) \ | 68 | #define luaM_growvector(L,v,nelems,size,t,limit,e) \ |
67 | ((v)=cast(t *, luaM_growaux_(L,v,nelems,&(size),sizeof(t), \ | 69 | ((v)=cast(t *, luaM_growaux_(L,v,nelems,&(size),sizeof(t), \ |
68 | luaM_limitN(limit,t),e))) | 70 | luaM_limitN(limit,t),e))) |
@@ -744,7 +744,6 @@ typedef struct Table { | |||
744 | unsigned int alimit; /* "limit" of 'array' array */ | 744 | unsigned int alimit; /* "limit" of 'array' array */ |
745 | TValue *array; /* array part */ | 745 | TValue *array; /* array part */ |
746 | Node *node; | 746 | Node *node; |
747 | Node *lastfree; /* any free position is before this position */ | ||
748 | struct Table *metatable; | 747 | struct Table *metatable; |
749 | GCObject *gclist; | 748 | GCObject *gclist; |
750 | } Table; | 749 | } Table; |
@@ -40,6 +40,27 @@ | |||
40 | 40 | ||
41 | 41 | ||
42 | /* | 42 | /* |
43 | ** Only tables with hash parts larget than LIMFORLAST has a 'lastfree' | ||
44 | ** field that optimizes finding a free slot. Smaller tables do a | ||
45 | ** complete search when looking for a free slot. | ||
46 | */ | ||
47 | #define LLIMFORLAST 2 /* log2 of LIMTFORLAST */ | ||
48 | #define LIMFORLAST twoto(LLIMFORLAST) | ||
49 | |||
50 | /* | ||
51 | ** Union to store an int field ensuring that what follows it in | ||
52 | ** memory is properly aligned to store a TValue. | ||
53 | */ | ||
54 | typedef union { | ||
55 | int lastfree; | ||
56 | char padding[offsetof(struct { int i; TValue v; }, v)]; | ||
57 | } Limbox; | ||
58 | |||
59 | #define haslastfree(t) ((t)->lsizenode > LLIMFORLAST) | ||
60 | #define getlastfree(t) (&((cast(Limbox *, (t)->node) - 1)->lastfree)) | ||
61 | |||
62 | |||
63 | /* | ||
43 | ** MAXABITS is the largest integer such that MAXASIZE fits in an | 64 | ** MAXABITS is the largest integer such that MAXASIZE fits in an |
44 | ** unsigned int. | 65 | ** unsigned int. |
45 | */ | 66 | */ |
@@ -367,8 +388,15 @@ int luaH_next (lua_State *L, Table *t, StkId key) { | |||
367 | 388 | ||
368 | 389 | ||
369 | static void freehash (lua_State *L, Table *t) { | 390 | static void freehash (lua_State *L, Table *t) { |
370 | if (!isdummy(t)) | 391 | if (!isdummy(t)) { |
371 | luaM_freearray(L, t->node, cast_sizet(sizenode(t))); | 392 | size_t bsize = sizenode(t) * sizeof(Node); /* 'node' size in bytes */ |
393 | char *arr = cast_charp(t->node); | ||
394 | if (haslastfree(t)) { | ||
395 | bsize += sizeof(Limbox); | ||
396 | arr -= sizeof(Limbox); | ||
397 | } | ||
398 | luaM_freearray(L, arr, bsize); | ||
399 | } | ||
372 | } | 400 | } |
373 | 401 | ||
374 | 402 | ||
@@ -479,7 +507,7 @@ static void setnodevector (lua_State *L, Table *t, unsigned int size) { | |||
479 | if (size == 0) { /* no elements to hash part? */ | 507 | if (size == 0) { /* no elements to hash part? */ |
480 | t->node = cast(Node *, dummynode); /* use common 'dummynode' */ | 508 | t->node = cast(Node *, dummynode); /* use common 'dummynode' */ |
481 | t->lsizenode = 0; | 509 | t->lsizenode = 0; |
482 | t->lastfree = NULL; /* signal that it is using dummy node */ | 510 | setdummy(t); /* signal that it is using dummy node */ |
483 | } | 511 | } |
484 | else { | 512 | else { |
485 | int i; | 513 | int i; |
@@ -487,15 +515,22 @@ static void setnodevector (lua_State *L, Table *t, unsigned int size) { | |||
487 | if (lsize > MAXHBITS || (1u << lsize) > MAXHSIZE) | 515 | if (lsize > MAXHBITS || (1u << lsize) > MAXHSIZE) |
488 | luaG_runerror(L, "table overflow"); | 516 | luaG_runerror(L, "table overflow"); |
489 | size = twoto(lsize); | 517 | size = twoto(lsize); |
490 | t->node = luaM_newvector(L, size, Node); | 518 | if (lsize <= LLIMFORLAST) /* no 'lastfree' field? */ |
519 | t->node = luaM_newvector(L, size, Node); | ||
520 | else { | ||
521 | size_t bsize = size * sizeof(Node) + sizeof(Limbox); | ||
522 | char *node = luaM_newblock(L, bsize); | ||
523 | t->node = cast(Node *, node + sizeof(Limbox)); | ||
524 | *getlastfree(t) = size; /* all positions are free */ | ||
525 | } | ||
526 | t->lsizenode = cast_byte(lsize); | ||
527 | setnodummy(t); | ||
491 | for (i = 0; i < cast_int(size); i++) { | 528 | for (i = 0; i < cast_int(size); i++) { |
492 | Node *n = gnode(t, i); | 529 | Node *n = gnode(t, i); |
493 | gnext(n) = 0; | 530 | gnext(n) = 0; |
494 | setnilkey(n); | 531 | setnilkey(n); |
495 | setempty(gval(n)); | 532 | setempty(gval(n)); |
496 | } | 533 | } |
497 | t->lsizenode = cast_byte(lsize); | ||
498 | t->lastfree = gnode(t, size); /* all positions are free */ | ||
499 | } | 534 | } |
500 | } | 535 | } |
501 | 536 | ||
@@ -520,18 +555,21 @@ static void reinsert (lua_State *L, Table *ot, Table *t) { | |||
520 | 555 | ||
521 | 556 | ||
522 | /* | 557 | /* |
523 | ** Exchange the hash part of 't1' and 't2'. | 558 | ** Exchange the hash part of 't1' and 't2'. (In 'flags', only the |
559 | ** dummy bit must be exchanged: The 'isrealasize' is not related | ||
560 | ** to the hash part, and the metamethod bits do not change during | ||
561 | ** a resize, so the "real" table can keep their values.) | ||
524 | */ | 562 | */ |
525 | static void exchangehashpart (Table *t1, Table *t2) { | 563 | static void exchangehashpart (Table *t1, Table *t2) { |
526 | lu_byte lsizenode = t1->lsizenode; | 564 | lu_byte lsizenode = t1->lsizenode; |
527 | Node *node = t1->node; | 565 | Node *node = t1->node; |
528 | Node *lastfree = t1->lastfree; | 566 | int bitdummy1 = t1->flags & BITDUMMY; |
529 | t1->lsizenode = t2->lsizenode; | 567 | t1->lsizenode = t2->lsizenode; |
530 | t1->node = t2->node; | 568 | t1->node = t2->node; |
531 | t1->lastfree = t2->lastfree; | 569 | t1->flags = (t1->flags & NOTBITDUMMY) | (t2->flags & BITDUMMY); |
532 | t2->lsizenode = lsizenode; | 570 | t2->lsizenode = lsizenode; |
533 | t2->node = node; | 571 | t2->node = node; |
534 | t2->lastfree = lastfree; | 572 | t2->flags = (t2->flags & NOTBITDUMMY) | bitdummy1; |
535 | } | 573 | } |
536 | 574 | ||
537 | 575 | ||
@@ -555,6 +593,7 @@ void luaH_resize (lua_State *L, Table *t, unsigned int newasize, | |||
555 | unsigned int oldasize = setlimittosize(t); | 593 | unsigned int oldasize = setlimittosize(t); |
556 | TValue *newarray; | 594 | TValue *newarray; |
557 | /* create new hash part with appropriate size into 'newt' */ | 595 | /* create new hash part with appropriate size into 'newt' */ |
596 | newt.flags = 0; | ||
558 | setnodevector(L, &newt, nhsize); | 597 | setnodevector(L, &newt, nhsize); |
559 | if (newasize < oldasize) { /* will array shrink? */ | 598 | if (newasize < oldasize) { /* will array shrink? */ |
560 | t->alimit = newasize; /* pretend array has new size... */ | 599 | t->alimit = newasize; /* pretend array has new size... */ |
@@ -641,11 +680,22 @@ void luaH_free (lua_State *L, Table *t) { | |||
641 | 680 | ||
642 | 681 | ||
643 | static Node *getfreepos (Table *t) { | 682 | static Node *getfreepos (Table *t) { |
644 | if (!isdummy(t)) { | 683 | if (haslastfree(t)) { /* does it have 'lastfree' information? */ |
645 | while (t->lastfree > t->node) { | 684 | /* look for a spot before 'lastfree', updating 'lastfree' */ |
646 | t->lastfree--; | 685 | while (*getlastfree(t) > 0) { |
647 | if (keyisnil(t->lastfree)) | 686 | Node *free = gnode(t, --(*getlastfree(t))); |
648 | return t->lastfree; | 687 | if (keyisnil(free)) |
688 | return free; | ||
689 | } | ||
690 | } | ||
691 | else { /* no 'lastfree' information */ | ||
692 | if (!isdummy(t)) { | ||
693 | int i = sizenode(t); | ||
694 | while (i--) { /* do a linear search */ | ||
695 | Node *free = gnode(t, i); | ||
696 | if (keyisnil(free)) | ||
697 | return free; | ||
698 | } | ||
649 | } | 699 | } |
650 | } | 700 | } |
651 | return NULL; /* could not find a free place */ | 701 | return NULL; /* could not find a free place */ |
@@ -23,8 +23,18 @@ | |||
23 | #define invalidateTMcache(t) ((t)->flags &= ~maskflags) | 23 | #define invalidateTMcache(t) ((t)->flags &= ~maskflags) |
24 | 24 | ||
25 | 25 | ||
26 | /* true when 't' is using 'dummynode' as its hash part */ | 26 | /* |
27 | #define isdummy(t) ((t)->lastfree == NULL) | 27 | ** Bit BITDUMMY set in 'flags' means the table is using the dummy node |
28 | ** for its hash part. | ||
29 | */ | ||
30 | |||
31 | #define BITDUMMY (1 << 6) | ||
32 | #define NOTBITDUMMY cast_byte(~BITDUMMY) | ||
33 | #define isdummy(t) ((t)->flags & BITDUMMY) | ||
34 | |||
35 | #define setnodummy(t) ((t)->flags &= NOTBITDUMMY) | ||
36 | #define setdummy(t) ((t)->flags |= BITDUMMY) | ||
37 | |||
28 | 38 | ||
29 | 39 | ||
30 | /* allocated size for hash nodes */ | 40 | /* allocated size for hash nodes */ |
@@ -999,9 +999,8 @@ static int table_query (lua_State *L) { | |||
999 | if (i == -1) { | 999 | if (i == -1) { |
1000 | lua_pushinteger(L, asize); | 1000 | lua_pushinteger(L, asize); |
1001 | lua_pushinteger(L, allocsizenode(t)); | 1001 | lua_pushinteger(L, allocsizenode(t)); |
1002 | lua_pushinteger(L, isdummy(t) ? 0 : t->lastfree - t->node); | ||
1003 | lua_pushinteger(L, t->alimit); | 1002 | lua_pushinteger(L, t->alimit); |
1004 | return 4; | 1003 | return 3; |
1005 | } | 1004 | } |
1006 | else if ((unsigned int)i < asize) { | 1005 | else if ((unsigned int)i < asize) { |
1007 | lua_pushinteger(L, i); | 1006 | lua_pushinteger(L, i); |
@@ -48,8 +48,8 @@ typedef enum { | |||
48 | /* | 48 | /* |
49 | ** Mask with 1 in all fast-access methods. A 1 in any of these bits | 49 | ** Mask with 1 in all fast-access methods. A 1 in any of these bits |
50 | ** in the flag of a (meta)table means the metatable does not have the | 50 | ** in the flag of a (meta)table means the metatable does not have the |
51 | ** corresponding metamethod field. (Bit 7 of the flag is used for | 51 | ** corresponding metamethod field. (Bit 6 of the flag indicates that |
52 | ** 'isrealasize'.) | 52 | ** the table is using the dummy node; bit 7 is used for 'isrealasize'.) |
53 | */ | 53 | */ |
54 | #define maskflags (~(~0u << (TM_EQ + 1))) | 54 | #define maskflags (~(~0u << (TM_EQ + 1))) |
55 | 55 | ||
diff --git a/testes/nextvar.lua b/testes/nextvar.lua index 0874e5bb..80b3d05c 100644 --- a/testes/nextvar.lua +++ b/testes/nextvar.lua | |||
@@ -210,9 +210,9 @@ assert(T.querytab(a) == 64) -- array part has 64 elements | |||
210 | a[32] = true; a[48] = true; -- binary search will find these ones | 210 | a[32] = true; a[48] = true; -- binary search will find these ones |
211 | a[51] = true -- binary search will miss this one | 211 | a[51] = true -- binary search will miss this one |
212 | assert(#a == 48) -- this will set the limit | 212 | assert(#a == 48) -- this will set the limit |
213 | assert(select(4, T.querytab(a)) == 48) -- this is the limit now | 213 | assert(select(3, T.querytab(a)) == 48) -- this is the limit now |
214 | a[50] = true -- this will set a new limit | 214 | a[50] = true -- this will set a new limit |
215 | assert(select(4, T.querytab(a)) == 50) -- this is the limit now | 215 | assert(select(3, T.querytab(a)) == 50) -- this is the limit now |
216 | -- but the size is larger (and still inside the array part) | 216 | -- but the size is larger (and still inside the array part) |
217 | assert(#a == 51) | 217 | assert(#a == 51) |
218 | 218 | ||