aboutsummaryrefslogtreecommitdiffstats
path: root/mps/code/table.c
diff options
context:
space:
mode:
authorRichard Brooksby2012-09-06 17:17:18 +0100
committerRichard Brooksby2012-09-06 17:17:18 +0100
commit858e4ac0ac8ee684f48f0edd9d80ae28b17aee53 (patch)
tree5034519c869b370df2c87394c03f7f30e78945b9 /mps/code/table.c
parent383335816d888b5f28fe7b034106dc2056f56620 (diff)
downloademacs-858e4ac0ac8ee684f48f0edd9d80ae28b17aee53.tar.gz
emacs-858e4ac0ac8ee684f48f0edd9d80ae28b17aee53.zip
Partial merge of branch/2012-07-23/cet-transform, excluding cet-specific parts.
Copied from Perforce Change: 179309 ServerID: perforce.ravenbrook.com
Diffstat (limited to 'mps/code/table.c')
-rw-r--r--mps/code/table.c327
1 files changed, 209 insertions, 118 deletions
diff --git a/mps/code/table.c b/mps/code/table.c
index e141bf1d8e2..7e6b1ec7149 100644
--- a/mps/code/table.c
+++ b/mps/code/table.c
@@ -8,60 +8,75 @@
8 */ 8 */
9 9
10#include "table.h" 10#include "table.h"
11#include "mpmtypes.h" 11#include "mpm.h"
12 12
13#include <stddef.h> 13#include <stddef.h>
14#include <stdlib.h>
15#include <assert.h>
16#include <stdio.h>
17#include "mpstd.h"
18 14
19typedef unsigned long ulong;
20 15
16SRCID(table, "$Id$");
21 17
22#define tableUNUSED ((Word)0x2AB7E040)
23#define tableDELETED ((Word)0x2AB7EDE7)
24#define tableACTIVE ((Word)0x2AB7EAC2)
25 18
19/* TableHash -- return a hash value from an address
20 *
21 * This uses a single cycle of an MLCG, more commonly seen as a
22 * pseudorandom number generator. It works extremely well as a
23 * hash function.
24 *
25 * (In particular, it is substantially better than simply doing this:
26 * seed = (unsigned long)addr * 48271;
27 * Tested by RHSK 2010-12-28.)
28 *
29 * This MLCG is a full period generator: it cycles through every
30 * number from 1 to m-1 before repeating. Therefore, no two numbers
31 * in that range hash to the same value. Furthermore, it has prime
32 * modulus, which tends to avoid recurring patterns in the low-order
33 * bits, which is good because the hash will be used modulus the
34 * number of slots in the table.
35 *
36 * Of course it's only a 31-bit cycle, so we start by losing the top
37 * bit of the address, but that's hardly a great problem.
38 *
39 * The implementation is quite subtle. See rnd() in testlib.c, where
40 * it has been exhaustively (ie: totally) tested. RHSK 2010-12-28.
41 *
42 * TODO: Check how this works out on 64-bit. RB 2012-09-04
43 */
44
45#define R_m 2147483647UL
46#define R_a 48271UL
26 47
27typedef struct TableEntryStruct *TableEntry; 48typedef Word Hash;
28typedef struct TableEntryStruct {
29 Word status;
30 Word key;
31 void *value;
32} TableEntryStruct;
33
34
35typedef struct TableStruct {
36 size_t length;
37 size_t count;
38 size_t limit;
39 TableEntry array;
40} TableStruct;
41
42 49
50static Hash TableHash(Word key)
51{
52 Hash seed = (Hash)(key & 0x7FFFFFFF);
53 /* requires m == 2^31-1, a < 2^16 */
54 Hash bot = R_a * (seed & 0x7FFF);
55 Hash top = R_a * (seed >> 15);
56 seed = bot + ((top & 0xFFFF) << 15) + (top >> 16);
57 if(seed > R_m)
58 seed -= R_m;
59 return seed;
60}
43 61
44/* sizeFloorLog2 -- logarithm base 2 */
45 62
46static size_t sizeFloorLog2(size_t size) 63Bool TableCheck(Table table)
47{ 64{
48 size_t l = 0; 65 CHECKS(Table, table);
49 66 CHECKL(table->count <= table->length);
50 assert(size != 0); 67 CHECKL(table->length == 0 || table->array != NULL);
51 while(size > 1) { 68 CHECKL(FUNCHECK(table->alloc));
52 ++l; 69 CHECKL(FUNCHECK(table->free));
53 size >>= 1; 70 /* can't check allocClosure -- it could be anything */
54 } 71 CHECKL(table->unusedKey != table->deletedKey);
55 return l; 72 return TRUE;
56} 73}
57 74
58 75
59/* TableHash -- table hashing function */ 76static Bool entryIsActive(Table table, TableEntry entry)
60
61static ulong TableHash(Word key)
62{ 77{
63 /* Shift some randomness into the low bits. */ 78 return !(entry->key == table->unusedKey ||
64 return (ulong)((key >> 10) + key); 79 entry->key == table->deletedKey);
65} 80}
66 81
67 82
@@ -72,69 +87,126 @@ static ulong TableHash(Word key)
72 * that all the items still fit in after growing the table. 87 * that all the items still fit in after growing the table.
73 */ 88 */
74 89
75static TableEntry TableFind(Table table, Word key, int skip_deleted) 90static TableEntry TableFind(Table table, Word key, Bool skip_deleted)
76{ 91{
77 ulong hash; 92 Hash hash;
78 size_t i, mask = table->length - 1; 93 Index i;
79 94 Word mask;
95
96 /* .find.visit: Ensure the length is a power of two so that the stride
97 is coprime and so visits all entries in the array eventually. */
98 AVER(WordIsP2(table->length)); /* .find.visit */
99
100 mask = table->length - 1;
80 hash = TableHash(key) & mask; 101 hash = TableHash(key) & mask;
81 i = hash; 102 i = hash;
82 do { 103 do {
83 switch (table->array[i].status) { 104 Word k = table->array[i].key;
84 case tableACTIVE: 105 if (k == key ||
85 if (table->array[i].key == key) 106 k == table->unusedKey ||
86 return &table->array[i]; 107 (!skip_deleted && key == table->deletedKey))
87 break;
88 case tableDELETED:
89 if (!skip_deleted)
90 return &table->array[i];
91 break;
92 case tableUNUSED:
93 return &table->array[i]; 108 return &table->array[i];
94 break; 109 i = (i + (hash | 1)) & mask; /* .find.visit */
95 }
96 i = (i + 1) & mask;
97 } while(i != hash); 110 } while(i != hash);
98 111
99 return NULL; 112 return NULL;
100} 113}
101 114
102 115
103/* TableGrow -- doubles the size of the table */ 116/* TableGrow -- increase the capacity of the table
117 *
118 * Ensure the transform's hashtable can accommodate N entries (filled
119 * slots), without becoming cramped. If necessary, resize the
120 * hashtable by allocating a new one and rehashing all old entries.
121 * If insufficient memory, return error without modifying table.
122 *
123 * .hash.spacefraction: As with all closed hash tables, we must choose
124 * an appropriate proportion of slots to remain free. More free slots
125 * help avoid large-sized contiguous clumps of full cells and their
126 * associated linear search costs.
127 *
128 * .hash.initial: Any reasonable number.
129 *
130 * .hash.growth: A compromise between space inefficency (growing bigger
131 * than required) and time inefficiency (growing too slowly, with all
132 * the rehash costs at every step). A factor of 2 means that at the
133 * point of growing to a size X table, hash-work equivalent to filling
134 * a size-X table has already been done. So we do at most 2x the
135 * hash-work we would have done if we had been able to guess the right
136 * table size initially.
137 *
138 * Numbers of slots maintain this relation:
139 * occupancy <= capacity < enough <= cSlots
140 */
141
142#define SPACEFRACTION 0.75 /* .hash.spacefraction */
104 143
105static Res TableGrow(Table table) 144Res TableGrow(Table table, Count extraCapacity)
106{ 145{
107 TableEntry oldArray, newArray; 146 TableEntry oldArray, newArray;
108 size_t i, oldLength, newLength; 147 Count oldLength, newLength;
148 Count required, minimum;
149 Count i, found;
150
151 required = table->count + extraCapacity;
152 if (required < table->count) /* overflow? */
153 return ResLIMIT;
154
155 /* Calculate the minimum table length that would allow for the required
156 capacity without growing again. */
157 minimum = (Count)(required / SPACEFRACTION);
158 if (minimum < required) /* overflow? */
159 return ResLIMIT;
109 160
161 /* Double the table length until it's larger than the minimum */
110 oldLength = table->length; 162 oldLength = table->length;
163 newLength = oldLength;
164 while(newLength < minimum) {
165 Count doubled = newLength > 0 ? newLength * 2 : 1; /* .hash.growth */
166 if (doubled <= newLength) /* overflow? */
167 return ResLIMIT;
168 newLength = doubled;
169 }
170
171 if (newLength == oldLength) /* already enough space? */
172 return ResOK;
173
174 /* TODO: An event would be good here */
175
111 oldArray = table->array; 176 oldArray = table->array;
112 newLength = oldLength * 2; 177 newArray = table->alloc(table->allocClosure,
113 newArray = malloc(sizeof(TableEntryStruct) * newLength); 178 sizeof(TableEntryStruct) * newLength);
114 if(newArray == NULL) return ResMEMORY; 179 if(newArray == NULL)
180 return ResMEMORY;
115 181
116 for(i = 0; i < newLength; ++i) { 182 for(i = 0; i < newLength; ++i) {
117 newArray[i].key = 0; 183 newArray[i].key = table->unusedKey;
118 newArray[i].value = NULL; 184 newArray[i].value = NULL;
119 newArray[i].status = tableUNUSED;
120 } 185 }
121 186
122 table->length = newLength; 187 table->length = newLength;
123 table->array = newArray; 188 table->array = newArray;
124 table->limit *= 2;
125 189
190 found = 0;
126 for(i = 0; i < oldLength; ++i) { 191 for(i = 0; i < oldLength; ++i) {
127 if (oldArray[i].status == tableACTIVE) { 192 if (entryIsActive(table, &oldArray[i])) {
128 TableEntry entry; 193 TableEntry entry;
129 entry = TableFind(table, oldArray[i].key, 0 /* none deleted */); 194 entry = TableFind(table, oldArray[i].key, FALSE /* none deleted */);
130 assert(entry != NULL); 195 AVER(entry != NULL);
131 assert(entry->status == tableUNUSED); 196 AVER(entry->key == table->unusedKey);
132 entry->key = oldArray[i].key; 197 entry->key = oldArray[i].key;
133 entry->value = oldArray[i].value; 198 entry->value = oldArray[i].value;
134 entry->status = tableACTIVE; 199 ++found;
135 } 200 }
136 } 201 }
137 free(oldArray); 202 AVER(found == table->count);
203
204 if (oldLength > 0) {
205 AVER(oldArray != NULL);
206 table->free(table->allocClosure,
207 oldArray,
208 sizeof(TableEntryStruct) * oldLength);
209 }
138 210
139 return ResOK; 211 return ResOK;
140} 212}
@@ -142,35 +214,44 @@ static Res TableGrow(Table table)
142 214
143/* TableCreate -- makes a new table */ 215/* TableCreate -- makes a new table */
144 216
145extern Res TableCreate(Table *tableReturn, size_t length) 217extern Res TableCreate(Table *tableReturn,
218 Count length,
219 TableAllocMethod tableAlloc,
220 TableFreeMethod tableFree,
221 void *allocClosure,
222 Word unusedKey,
223 Word deletedKey)
146{ 224{
147 Table table; 225 Table table;
148 size_t i; 226 Res res;
227
228 AVER(tableReturn != NULL);
229 AVER(FUNCHECK(tableAlloc));
230 AVER(FUNCHECK(tableFree));
231 AVER(unusedKey != deletedKey);
149 232
150 assert(tableReturn != NULL); 233 table = tableAlloc(allocClosure, sizeof(TableStruct));
234 if(table == NULL)
235 return ResMEMORY;
151 236
152 table = malloc(sizeof(TableStruct)); 237 table->length = 0;
153 if(table == NULL) goto failMallocTable;
154 if (length < 2) length = 2;
155 /* Table size is length rounded up to the next power of 2. */
156 table->length = (size_t)1 << (sizeFloorLog2(length-1) + 1);
157 table->count = 0; 238 table->count = 0;
158 table->limit = (size_t)(.5 * length); 239 table->array = NULL;
159 table->array = malloc(sizeof(TableEntryStruct) * length); 240 table->alloc = tableAlloc;
160 if(table->array == NULL) goto failMallocArray; 241 table->free = tableFree;
161 for(i = 0; i < length; ++i) { 242 table->allocClosure = allocClosure;
162 table->array[i].key = 0; 243 table->unusedKey = unusedKey;
163 table->array[i].value = NULL; 244 table->deletedKey = deletedKey;
164 table->array[i].status = tableUNUSED; 245 table->sig = TableSig;
165 } 246
247 AVERT(Table, table);
248
249 res = TableGrow(table, length);
250 if (res != ResOK)
251 return res;
166 252
167 *tableReturn = table; 253 *tableReturn = table;
168 return ResOK; 254 return ResOK;
169
170failMallocArray:
171 free(table);
172failMallocTable:
173 return ResMEMORY;
174} 255}
175 256
176 257
@@ -178,9 +259,15 @@ failMallocTable:
178 259
179extern void TableDestroy(Table table) 260extern void TableDestroy(Table table)
180{ 261{
181 assert(table != NULL); 262 AVER(table != NULL);
182 free(table->array); 263 if (table->length > 0) {
183 free(table); 264 AVER(table->array != NULL);
265 table->free(table->allocClosure,
266 table->array,
267 sizeof(TableEntryStruct) * table->length);
268 }
269 table->sig = SigInvalid;
270 table->free(table->allocClosure, table, sizeof(TableStruct));
184} 271}
185 272
186 273
@@ -188,9 +275,9 @@ extern void TableDestroy(Table table)
188 275
189extern Bool TableLookup(void **valueReturn, Table table, Word key) 276extern Bool TableLookup(void **valueReturn, Table table, Word key)
190{ 277{
191 TableEntry entry = TableFind(table, key, 1 /* skip deleted */); 278 TableEntry entry = TableFind(table, key, TRUE /* skip deleted */);
192 279
193 if(entry == NULL || entry->status != tableACTIVE) 280 if(entry == NULL || !entryIsActive(table, entry))
194 return FALSE; 281 return FALSE;
195 *valueReturn = entry->value; 282 *valueReturn = entry->value;
196 return TRUE; 283 return TRUE;
@@ -202,24 +289,26 @@ extern Bool TableLookup(void **valueReturn, Table table, Word key)
202extern Res TableDefine(Table table, Word key, void *value) 289extern Res TableDefine(Table table, Word key, void *value)
203{ 290{
204 TableEntry entry; 291 TableEntry entry;
292
293 AVER(key != table->unusedKey);
294 AVER(key != table->deletedKey);
205 295
206 if (table->count >= table->limit) { 296 if (table->count >= table->length * SPACEFRACTION) {
207 Res res = TableGrow(table); 297 Res res = TableGrow(table, 1);
208 if(res != ResOK) return res; 298 if(res != ResOK) return res;
209 entry = TableFind(table, key, 0 /* no deletions yet */); 299 entry = TableFind(table, key, FALSE /* no deletions yet */);
210 assert(entry != NULL); 300 AVER(entry != NULL);
211 if (entry->status == tableACTIVE) 301 if (entryIsActive(table, entry))
212 return ResFAIL; 302 return ResFAIL;
213 } else { 303 } else {
214 entry = TableFind(table, key, 1 /* skip deleted */); 304 entry = TableFind(table, key, TRUE /* skip deleted */);
215 if (entry != NULL && entry->status == tableACTIVE) 305 if (entry != NULL && entryIsActive(table, entry))
216 return ResFAIL; 306 return ResFAIL;
217 /* Search again to find the best slot, deletions included. */ 307 /* Search again to find the best slot, deletions included. */
218 entry = TableFind(table, key, 0 /* don't skip deleted */); 308 entry = TableFind(table, key, FALSE /* don't skip deleted */);
219 assert(entry != NULL); 309 AVER(entry != NULL);
220 } 310 }
221 311
222 entry->status = tableACTIVE;
223 entry->key = key; 312 entry->key = key;
224 entry->value = value; 313 entry->value = value;
225 ++table->count; 314 ++table->count;
@@ -232,11 +321,11 @@ extern Res TableDefine(Table table, Word key, void *value)
232 321
233extern Res TableRedefine(Table table, Word key, void *value) 322extern Res TableRedefine(Table table, Word key, void *value)
234{ 323{
235 TableEntry entry = TableFind(table, key, 1 /* skip deletions */); 324 TableEntry entry = TableFind(table, key, TRUE /* skip deletions */);
236 325
237 if (entry == NULL || entry->status != tableACTIVE) 326 if (entry == NULL || !entryIsActive(table, entry))
238 return ResFAIL; 327 return ResFAIL;
239 assert(entry->key == key); 328 AVER(entry->key == key);
240 entry->value = value; 329 entry->value = value;
241 return ResOK; 330 return ResOK;
242} 331}
@@ -246,11 +335,11 @@ extern Res TableRedefine(Table table, Word key, void *value)
246 335
247extern Res TableRemove(Table table, Word key) 336extern Res TableRemove(Table table, Word key)
248{ 337{
249 TableEntry entry = TableFind(table, key, 1); 338 TableEntry entry = TableFind(table, key, TRUE);
250 339
251 if (entry == NULL || entry->status != tableACTIVE) 340 if (entry == NULL || !entryIsActive(table, entry))
252 return ResFAIL; 341 return ResFAIL;
253 entry->status = tableDELETED; 342 entry->key = table->deletedKey;
254 --table->count; 343 --table->count;
255 return ResOK; 344 return ResOK;
256} 345}
@@ -258,18 +347,20 @@ extern Res TableRemove(Table table, Word key)
258 347
259/* TableMap -- apply a function to all the mappings */ 348/* TableMap -- apply a function to all the mappings */
260 349
261extern void TableMap(Table table, void(*fun)(Word key, void*value)) 350extern void TableMap(Table table,
351 void (*fun)(void *closure, Word key, void*value),
352 void *closure)
262{ 353{
263 size_t i; 354 Index i;
264 for (i = 0; i < table->length; i++) 355 for (i = 0; i < table->length; i++)
265 if (table->array[i].status == tableACTIVE) 356 if (entryIsActive(table, &table->array[i]))
266 (*fun)(table->array[i].key, table->array[i].value); 357 (*fun)(closure, table->array[i].key, table->array[i].value);
267} 358}
268 359
269 360
270/* TableCount -- count the number of mappings in the table */ 361/* TableCount -- count the number of mappings in the table */
271 362
272extern size_t TableCount(Table table) 363extern Count TableCount(Table table)
273{ 364{
274 return table->count; 365 return table->count;
275} 366}