|
|
1.1 root 1: /*
2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7: * Reserved. This file contains Original Code and/or Modifications of
8: * Original Code as defined in and that are subject to the Apple Public
9: * Source License Version 1.0 (the 'License'). You may not use this file
10: * except in compliance with the License. Please obtain a copy of the
11: * License at http://www.apple.com/publicsource and read it before using
12: * this file.
13: *
14: * The Original Code and all software distributed under the License are
15: * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19: * License for the specific language governing rights and limitations
20: * under the License."
21: *
22: * @APPLE_LICENSE_HEADER_END@
23: */
24: /* maptable.m
25: Copyright 1990 NeXT, Inc.
26: Created by Bertrand Serlet, August 1990
27: */
28:
29: #ifdef SHLIB
30: #import "shlib.h"
31: #endif SHLIB
32:
33: #import "objc-private.h"
34: #import "maptable.h"
35:
36: #import <string.h>
37: #import <stdlib.h>
38: #import <stdio.h>
39: #import "Object.h"
40: #import "hashtable.h"
41:
42: /****** Macros and utilities ****************************/
43:
44: #if DEBUG
45: #define INLINE
46: #else
47: #define INLINE inline
48: #endif
49:
50: typedef struct _MapPair {
51: const void *key;
52: const void *value;
53: } MapPair;
54:
55: static unsigned log2(unsigned x) { return (x<2) ? 0 : log2(x>>1)+1; };
56:
57: static INLINE unsigned exp2m1(unsigned x) { return (1 << x) - 1; };
58:
59: /* iff necessary this modulo can be optimized since the nbBuckets is of the form 2**n-1 */
60: static INLINE unsigned bucketOf(NXMapTable *table, const void *key) {
61: unsigned hash = (table->prototype->hash)(table, key);
62: unsigned xored = (hash & 0xffff) ^ (hash >> 16);
63: return ((xored * 65521) + hash) % table->nbBuckets;
64: }
65:
66: static INLINE int isEqual(NXMapTable *table, const void *key1, const void *key2) {
67: return (key1 == key2) ? 1 : (table->prototype->isEqual)(table, key1, key2);
68: }
69:
70: static INLINE unsigned nextIndex(NXMapTable *table, unsigned index) {
71: return (index+1 >= table->nbBuckets) ? 0 : index+1;
72: }
73:
74: static INLINE void *allocBuckets(NXZone *zone, unsigned nb) {
75: MapPair *pairs = NXZoneMalloc(zone, (nb * sizeof(MapPair)));
76: MapPair *pair = pairs;
77: while (nb--) { pair->key = NX_MAPNOTAKEY; pair->value = NULL; pair++; }
78: return pairs;
79: }
80:
81: /***** Global data and bootstrap **********************/
82:
83: static unsigned hashPrototype(const void *info, const void *data) {
84: NXMapTablePrototype *proto = (NXMapTablePrototype *) data;
85: return NXPtrHash(info, proto->hash) ^ NXPtrHash(info, proto->isEqual) ^ NXPtrHash(info, proto->free) ^ (unsigned) proto->style;
86: }
87:
88: static int isEqualPrototype(const void *info, const void *data1, const void *data2) {
89: NXMapTablePrototype *proto1 = (NXMapTablePrototype *) data1;
90: NXMapTablePrototype *proto2 = (NXMapTablePrototype *) data2;
91: return (proto1->hash == proto2->hash) && (proto1->isEqual == proto2->isEqual) && (proto1->free == proto2->free) && (proto1->style == proto2->style);
92: }
93:
94: static NXHashTablePrototype protoPrototype = {
95: hashPrototype, isEqualPrototype, NXNoEffectFree, 0
96: };
97:
98: static NXHashTable *prototypes = NULL;
99: /* table of all prototypes */
100:
101: /**** Fundamentals Operations **************/
102:
103: NXMapTable *NXCreateMapTableFromZone(NXMapTablePrototype prototype, unsigned capacity, NXZone *zone) {
104: NXMapTable *table = NXZoneMalloc(zone, sizeof(NXMapTable));
105: NXMapTablePrototype *proto;
106: if (! prototypes) prototypes = NXCreateHashTable(protoPrototype, 0, NULL);
107: if (! prototype.hash || ! prototype.isEqual || ! prototype.free || prototype.style) {
108: _NXLogError("*** NXCreateMapTable: invalid creation parameters\n");
109: return NULL;
110: }
111: proto = NXHashGet(prototypes, &prototype);
112: if (! proto) {
113: proto = malloc(sizeof(NXMapTablePrototype));
114: *proto = prototype;
115: (void)NXHashInsert(prototypes, proto);
116: }
117: table->prototype = proto; table->count = 0;
118: table->nbBuckets = exp2m1(log2(capacity)+1);
119: table->buckets = allocBuckets(zone, table->nbBuckets);
120: return table;
121: }
122:
123: NXMapTable *NXCreateMapTable(NXMapTablePrototype prototype, unsigned capacity) {
124: return NXCreateMapTableFromZone(prototype, capacity, NXDefaultMallocZone());
125: }
126:
127: void NXFreeMapTable(NXMapTable *table) {
128: NXResetMapTable(table);
129: free(table->buckets);
130: free(table);
131: }
132:
133: void NXResetMapTable(NXMapTable *table) {
134: MapPair *pairs = table->buckets;
135: void (*freeProc)(struct _NXMapTable *, void *, void *) = table->prototype->free;
136: unsigned index = table->nbBuckets;
137: while (index--) {
138: if (pairs->key != NX_MAPNOTAKEY) {
139: freeProc(table, (void *)pairs->key, (void *)pairs->value);
140: pairs->key = NX_MAPNOTAKEY; pairs->value = NULL;
141: }
142: pairs++;
143: }
144: table->count = 0;
145: }
146:
147: BOOL NXCompareMapTables(NXMapTable *table1, NXMapTable *table2) {
148: if (table1 == table2) return YES;
149: if (table1->count != table2->count) return NO;
150: else {
151: void *key;
152: void *value;
153: NXMapState state = NXInitMapState(table1);
154: while (NXNextMapState(table1, &state, &key, &value)) {
155: if (NXMapMember(table2, key, &value) == NX_MAPNOTAKEY) return NO;
156: }
157: return YES;
158: }
159: }
160:
161: unsigned NXCountMapTable(NXMapTable *table) { return table->count; }
162:
163: static int mapSearch = 0;
164: static int mapSearchHit = 0;
165: static int mapSearchLoop = 0;
166:
167: static INLINE void *_NXMapMember(NXMapTable *table, const void *key, void **value) {
168: MapPair *pairs = table->buckets;
169: unsigned index = bucketOf(table, key);
170: MapPair *pair = pairs + index;
171: if (pair->key == NX_MAPNOTAKEY) return NX_MAPNOTAKEY;
172: mapSearch ++;
173: if (isEqual(table, pair->key, key)) {
174: *value = (void *)pair->value;
175: mapSearchHit ++;
176: return (void *)pair->key;
177: } else {
178: unsigned index2 = index;
179: while ((index2 = nextIndex(table, index2)) != index) {
180: mapSearchLoop ++;
181: pair = pairs + index2;
182: if (pair->key == NX_MAPNOTAKEY) return NX_MAPNOTAKEY;
183: if (isEqual(table, pair->key, key)) {
184: *value = (void *)pair->value;
185: return (void *)pair->key;
186: }
187: }
188: return NX_MAPNOTAKEY;
189: }
190: }
191:
192: void *NXMapMember(NXMapTable *table, const void *key, void **value) {
193: return _NXMapMember(table, key, value);
194: }
195:
196: void *NXMapGet(NXMapTable *table, const void *key) {
197: void *value;
198: return (_NXMapMember(table, key, &value) != NX_MAPNOTAKEY) ? value : NULL;
199: }
200:
201: static int mapRehash = 0;
202: static int mapRehashSum = 0;
203:
204: static void _NXMapRehash(NXMapTable *table) {
205: MapPair *pairs = table->buckets;
206: MapPair *pair = pairs;
207: unsigned index = table->nbBuckets;
208: unsigned oldCount = table->count;
209: table->nbBuckets += table->nbBuckets + 1; /* 2 times + 1 */
210: table->count = 0;
211: table->buckets = allocBuckets(NXZoneFromPtr(table), table->nbBuckets);
212: mapRehash ++;
213: mapRehashSum += table->count;
214: while (index--) {
215: if (pair->key != NX_MAPNOTAKEY) {
216: (void)NXMapInsert(table, pair->key, pair->value);
217: }
218: pair++;
219: }
220: if (oldCount != table->count)
221: _NXLogError("*** maptable: count differs after rehashing; probably indicates a broken invariant: there are x and y such as isEqual(x, y) is TRUE but hash(x) != hash (y)\n");
222: free(pairs);
223: }
224:
225: static int mapInsert = 0;
226: static int mapInsertHit = 0;
227: static int mapInsertLoop = 0;
228:
229: void *NXMapInsert(NXMapTable *table, const void *key, const void *value) {
230: MapPair *pairs = table->buckets;
231: unsigned index = bucketOf(table, key);
232: MapPair *pair = pairs + index;
233: if (key == NX_MAPNOTAKEY) {
234: _NXLogError("*** NXMapInsert: invalid key: -1\n");
235: return NULL;
236: }
237: mapInsert ++;
238: if (pair->key == NX_MAPNOTAKEY) {
239: mapInsertHit ++;
240: pair->key = key; pair->value = value;
241: table->count++;
242: return NULL;
243: }
244: if (isEqual(table, pair->key, key)) {
245: const void *old = pair->value;
246: mapInsertHit ++;
247: if (old != value) pair->value = value;/* avoid writing unless needed! */
248: return (void *)old;
249: } else if (table->count == table->nbBuckets) {
250: /* no room: rehash and retry */
251: _NXMapRehash(table);
252: return NXMapInsert(table, key, value);
253: } else {
254: unsigned index2 = index;
255: while ((index2 = nextIndex(table, index2)) != index) {
256: mapInsertLoop ++;
257: pair = pairs + index2;
258: if (pair->key == NX_MAPNOTAKEY) {
259: #if INSERT_TAIL
260: pair->key = key; pair->value = value;
261: #else
262: MapPair current = {key, value};
263: index2 = index;
264: while (current.key != NX_MAPNOTAKEY) {
265: MapPair temp;
266: pair = pairs + index2;
267: temp = *pair;
268: *pair = current;
269: current = temp;
270: index2 = nextIndex(table, index2);
271: }
272: #endif
273: table->count++;
274: if (table->count * 4 > table->nbBuckets * 3) _NXMapRehash(table);
275: return NULL;
276: }
277: if (isEqual(table, pair->key, key)) {
278: const void *old = pair->value;
279: if (old != value) pair->value = value;/* avoid writing unless needed! */
280: return (void *)old;
281: }
282: }
283: /* no room: can't happen! */
284: _NXLogError("**** NXMapInsert: bug\n");
285: return NULL;
286: }
287: }
288:
289: static int mapRemove = 0;
290:
291: void *NXMapRemove(NXMapTable *table, const void *key) {
292: MapPair *pairs = table->buckets;
293: unsigned index = bucketOf(table, key);
294: MapPair *pair = pairs + index;
295: unsigned chain = 1; /* number of non-nil pairs in a row */
296: int found = 0;
297: const void *old = NULL;
298: if (pair->key == NX_MAPNOTAKEY) return NULL;
299: mapRemove ++;
300: /* compute chain */
301: {
302: unsigned index2 = index;
303: if (isEqual(table, pair->key, key)) {found ++; old = pair->value; }
304: while ((index2 = nextIndex(table, index2)) != index) {
305: pair = pairs + index2;
306: if (pair->key == NX_MAPNOTAKEY) break;
307: if (isEqual(table, pair->key, key)) {found ++; old = pair->value; }
308: chain++;
309: }
310: }
311: if (! found) return NULL;
312: if (found != 1) _NXLogError("**** NXMapRemove: incorrect table\n");
313: /* remove then reinsert */
314: {
315: MapPair buffer[16];
316: MapPair *aux = (chain > 16) ? malloc(sizeof(MapPair)*(chain-1)) : buffer;
317: int auxnb = 0;
318: int nb = chain;
319: unsigned index2 = index;
320: while (nb--) {
321: pair = pairs + index2;
322: if (! isEqual(table, pair->key, key)) aux[auxnb++] = *pair;
323: pair->key = NX_MAPNOTAKEY; pair->value = NULL;
324: index2 = nextIndex(table, index2);
325: }
326: table->count -= chain;
327: if (auxnb != chain-1) _NXLogError("**** NXMapRemove: bug\n");
328: while (auxnb--) NXMapInsert(table, aux[auxnb].key, aux[auxnb].value);
329: if (chain > 16) free(aux);
330: }
331: return (void *)old;
332: }
333:
334: NXMapState NXInitMapState(NXMapTable *table) {
335: NXMapState state;
336: state.index = table->nbBuckets;
337: return state;
338: }
339:
340: int NXNextMapState(NXMapTable *table, NXMapState *state, const void **key, const void **value) {
341: MapPair *pairs = table->buckets;
342: while (state->index--) {
343: MapPair *pair = pairs + state->index;
344: if (pair->key != NX_MAPNOTAKEY) {
345: *key = pair->key; *value = pair->value;
346: return YES;
347: }
348: }
349: return NO;
350: }
351:
352: /**** Conveniences *************************************/
353:
354: unsigned _mapPtrHash(NXMapTable *table, const void *key) {
355: return (((unsigned) key) >> 16) ^ ((unsigned) key);
356: }
357:
358: unsigned _mapStrHash(NXMapTable *table, const void *key) {
359: unsigned hash = 0;
360: unsigned char *s = (unsigned char *)key;
361: /* unsigned to avoid a sign-extend */
362: /* unroll the loop */
363: if (s) for (; ; ) {
364: if (*s == '\0') break;
365: hash ^= *s++;
366: if (*s == '\0') break;
367: hash ^= *s++ << 8;
368: if (*s == '\0') break;
369: hash ^= *s++ << 16;
370: if (*s == '\0') break;
371: hash ^= *s++ << 24;
372: }
373: return hash;
374: }
375:
376: unsigned _mapObjectHash(NXMapTable *table, const void *key) {
377: return [(id)key hash];
378: }
379:
380: int _mapPtrIsEqual(NXMapTable *table, const void *key1, const void *key2) {
381: return key1 == key2;
382: }
383:
384: int _mapStrIsEqual(NXMapTable *table, const void *key1, const void *key2) {
385: if (key1 == key2) return YES;
386: if (! key1) return ! strlen ((char *) key2);
387: if (! key2) return ! strlen ((char *) key1);
388: if (((char *) key1)[0] != ((char *) key2)[0]) return NO;
389: return (strcmp((char *) key1, (char *) key2)) ? NO : YES;
390: }
391:
392: int _mapObjectIsEqual(NXMapTable *table, const void *key1, const void *key2) {
393: return [(id)key1 isEqual:(id)key2];
394: }
395:
396: void _mapNoFree(NXMapTable *table, void *key, void *value) {}
397:
398: void _mapObjectFree(NXMapTable *table, void *key, void *value) {
399: [(id)key free];
400: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.