|
|
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: /*
25: HashTable.m
26: Copyright 1988, 1989 NeXT, Inc.
27: Written by Bertrand Serlet, Dec 88
28: Responsibility: Bertrand Serlet
29: */
30:
31: #ifdef SHLIB
32: #import "shlib.h"
33: #endif SHLIB
34:
35: #import <stdlib.h>
36: #import <stdio.h>
37: #import <string.h>
38: #import "HashTable.h"
39: #import "NXStringTable.h"
40: #import "objc-private.h"
41:
42: typedef struct {const void *key; void *value;} Pair;
43: typedef Pair *Pairs;
44: typedef struct {unsigned count; Pairs pairs;} Bucket;
45: typedef Bucket *Buckets;
46:
47: #define INITCAPA 1 /* _nbBuckets has to be a 2**N-1, and at least 1 */
48: #define PAIRSSIZE(count) ((count) * sizeof(Pair))
49: #define HASHSTR(str) (_objc_strhash (str))
50: #define HASHINT(x) ((((unsigned) x) >> 16) ^ ((unsigned) x))
51:
52: static unsigned log2 (unsigned x) { return (x<2) ? 0 : log2 (x>>1)+1; };
53:
54: static unsigned exp2m1 (unsigned x) { return (1 << x) - 1; };
55:
56: static unsigned hash (const char *keyDesc, const void *aKey, unsigned mod) {
57: switch (keyDesc[0]) {
58: case '@': return [(id) aKey hash] % mod;
59: case '%':
60: case '*': return aKey ? HASHSTR (aKey) % mod : 0;
61: default: return HASHINT (aKey) % mod;
62: };
63: };
64:
65: static unsigned isEqual (const char *keyDesc, const void *key1, const void *key2) {
66: if (key1 == key2) return YES;
67: switch (keyDesc[0]) {
68: case '@': return [(id) key1 isEqual: (id) key2];
69: case '%':
70: case '*':
71: if (! key1) return (strlen (key2) == 0);
72: if (! key2) return (strlen (key1) == 0);
73: if (((char *) key1)[0] != ((char *) key2)[0]) return NO;
74: return (strcmp (key1, key2) == 0);
75: default: return NO;
76: };
77: };
78:
79: static void freeObject (void *item) {
80: [(id) item free];
81: };
82:
83: static void noFree (void *item) {
84: };
85:
86: @implementation HashTable
87:
88: + initialize
89: {
90: [self setVersion: 1];
91: return self;
92: }
93:
94: - _initBare: (const char *) aKeyDesc : (const char *) aValueDesc : (unsigned) capacity
95: /* used for efficient implementation of rehashing: does create a semi ok table */
96: {
97: count = 0; _nbBuckets = capacity;
98: keyDesc = (aKeyDesc) ? aKeyDesc : "@";
99: valueDesc = (aValueDesc) ? aValueDesc : "@";
100: _buckets = nil;
101: return self;
102: }
103:
104: + _newBare: (const char *) aKeyDesc : (const char *) aValueDesc : (unsigned) capacity
105: /* used for efficient implementation of rehashing: does create a semi ok table */
106: {
107: return [[self allocFromZone: NXDefaultMallocZone()]
108: _initBare:aKeyDesc:aValueDesc:capacity];
109: }
110:
111: - initKeyDesc: (const char *) aKeyDesc valueDesc: (const char *) aValueDesc
112: {
113: return [self initKeyDesc: aKeyDesc valueDesc: aValueDesc capacity: INITCAPA];
114: }
115:
116: - initKeyDesc: (const char *) aKeyDesc
117: {
118: return [self initKeyDesc: aKeyDesc valueDesc: NULL];
119: }
120:
121: - init
122: {
123: return [self initKeyDesc: NULL];
124: }
125: - initKeyDesc: (const char *) aKeyDesc valueDesc: (const char *) aValueDesc capacity: (unsigned) aCapacity
126: {
127: [self _initBare: aKeyDesc: aValueDesc: exp2m1 (log2 (aCapacity)+1)];
128: _buckets = NXZoneCalloc ([self zone], _nbBuckets, sizeof(Bucket));
129: return self;
130: }
131:
132: + newKeyDesc: (const char *) aKeyDesc valueDesc: (const char *) aValueDesc capacity: (unsigned) aCapacity
133: {
134: return [[self allocFromZone: NXDefaultMallocZone()]
135: initKeyDesc:aKeyDesc valueDesc:aValueDesc capacity:aCapacity];
136: }
137:
138: + newKeyDesc: (const char *) aKeyDesc valueDesc: (const char *) aValueDesc
139: {
140: return [self newKeyDesc: aKeyDesc valueDesc: aValueDesc capacity: INITCAPA];
141: }
142:
143: + newKeyDesc: (const char *) aKeyDesc
144: {
145: return [self newKeyDesc: aKeyDesc valueDesc: NULL];
146: }
147:
148: + new
149: {
150: return [self newKeyDesc: NULL];
151: }
152:
153: - free {
154: [self freeKeys: noFree values: noFree];
155: free(_buckets);
156: return [super free];
157: }
158:
159: - freeObjects {
160: return [self
161: freeKeys: (keyDesc[0] == '@') ? freeObject : noFree
162: values: (valueDesc[0] == '@') ? freeObject : noFree
163: ];
164: }
165:
166: - freeKeys: (void (*) (void *)) keyFunc values: (void (*) (void *)) valueFunc {
167: unsigned i = _nbBuckets;
168: Buckets buckets = (Buckets) _buckets;
169: while (i--) {
170: if (buckets->count) {
171: unsigned j = buckets->count;
172: Pairs pairs = buckets->pairs;
173: while (j--) {
174: (*keyFunc) ((void *) pairs->key);
175: (*valueFunc) (pairs->value);
176: pairs++;
177: };
178: free(buckets->pairs);
179: buckets->count = 0;
180: buckets->pairs = (Pairs) nil;
181: };
182: buckets++;
183: };
184: count = 0;
185: return self;
186: };
187:
188: - empty
189: {
190: unsigned i = _nbBuckets;
191: Buckets buckets = (Buckets) _buckets;
192: while (i--) {
193: if (buckets->count) free(buckets->pairs);
194: buckets->count = 0;
195: buckets->pairs = NULL;
196: buckets++;
197: };
198: count = 0;
199: return self;
200: }
201:
202:
203: - copyFromZone:(NXZone *)zone
204: /* we enumerate the table instead of blind copy to create a hash table of the right size */
205: {
206: HashTable *new = [[[self class] allocFromZone: zone]
207: initKeyDesc: keyDesc
208: valueDesc: valueDesc
209: capacity: count];
210: NXHashState state = [self initState];
211: const void *key;
212: void *value;
213: while ([self nextState: &state key: &key value: &value])
214: [new insertKey: key value: value];
215: return new;
216: }
217:
218: - (unsigned) count
219: {
220: return count;
221: }
222:
223: - (BOOL) isKey: (const void *) aKey
224: {
225: Buckets buckets = (Buckets) _buckets;
226: unsigned index = hash (keyDesc, aKey, _nbBuckets);
227: Bucket bucket = buckets[index];
228: unsigned j = bucket.count;
229: Pairs pairs;
230: if (j == 0) return NO;
231: pairs = bucket.pairs;
232: while (j--) {
233: if (isEqual (keyDesc, aKey, pairs->key)) return YES;
234: pairs++;
235: };
236: return NO;
237: }
238:
239: - (void *) valueForKey: (const void *) aKey
240: {
241: Buckets buckets = (Buckets) _buckets;
242: unsigned index = hash (keyDesc, aKey, _nbBuckets);
243: Bucket bucket = buckets[index];
244: unsigned j = bucket.count;
245: Pairs pairs;
246: if (j == 0) return nil;
247: pairs = bucket.pairs;
248: while (j--) {
249: if (isEqual (keyDesc, aKey, pairs->key)) return pairs->value;
250: pairs++;
251: };
252: return nil;
253: }
254:
255: - (void *) _insertKeyNoRehash: (const void *) aKey value: (void *) aValue
256: {
257: Buckets buckets = (Buckets) _buckets;
258: unsigned index = hash (keyDesc, aKey, _nbBuckets);
259: Bucket bucket = buckets[index];
260: unsigned j = bucket.count;
261: Pairs pairs = bucket.pairs;
262: Pairs new;
263: while (j--) {
264: if (isEqual (keyDesc, aKey, pairs->key)) {
265: void *old = pairs->value;
266: pairs->key = aKey;
267: pairs->value = aValue;
268: return old;
269: };
270: pairs++;
271: };
272: /* we enlarge this bucket; this could be optimized by using realloc */
273: new = (Pairs) NXZoneMalloc ([self zone], PAIRSSIZE(bucket.count+1));
274: if (bucket.count) bcopy (bucket.pairs, new+1, PAIRSSIZE(bucket.count));
275: new->key = aKey; new->value = aValue;
276: if (bucket.count) free (bucket.pairs);
277: buckets[index].count++; buckets[index].pairs = new; count++;
278: return nil;
279: }
280:
281: - (void *) insertKey: (const void *) aKey value: (void *) aValue
282: {
283: void *result = [self _insertKeyNoRehash: aKey value: aValue];
284: if (result) return result; /* it was a replace */
285: if (count > _nbBuckets) {
286: /* Rehash: we create a pseudo table pointing really to the old guys,
287: extend self, copy the old pairs, and free the pseudo table */
288: HashTable * old = [[HashTable allocFromZone: [self zone]]
289: _initBare: keyDesc: valueDesc: _nbBuckets];
290: NXHashState state;
291: const void *key;
292: void *value;
293:
294: old->count = count; old->_buckets = _buckets;
295: _nbBuckets += _nbBuckets + 1;
296: count = 0;
297: _buckets = NXZoneCalloc ([self zone], _nbBuckets, sizeof(Bucket));
298: state = [old initState];
299: while ([old nextState: &state key: &key value: &value])
300: [self insertKey: key value: value];
301: #ifndef KERNEL
302: if (old->count != count)
303: _NXLogError("*** HashTable: 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");
304: #endif /* KERNEL */
305: [old free];
306: };
307: return nil;
308: }
309:
310: - (void *) removeKey: (const void *) aKey
311: {
312: Buckets buckets = (Buckets) _buckets;
313: unsigned index = hash (keyDesc, aKey, _nbBuckets);
314: Bucket bucket = buckets[index];
315: unsigned j = bucket.count;
316: Pairs pairs = bucket.pairs;
317: Pairs new;
318: while (j--) {
319: if (isEqual (keyDesc, aKey, pairs->key)) {
320: /* we shrink this bucket; this could be optimized by using realloc */
321: void *oldValue = pairs->value;
322: new = (bucket.count-1) ? (Pairs) NXZoneMalloc (
323: [self zone], PAIRSSIZE(bucket.count-1)) : 0;
324: if (bucket.count-1 != j)
325: bcopy (bucket.pairs, new, PAIRSSIZE(bucket.count-j-1));
326: if (j)
327: bcopy (bucket.pairs+bucket.count-j, new+bucket.count-j-1,PAIRSSIZE (j));
328: free (bucket.pairs);
329: count--; buckets[index].count--; buckets[index].pairs = new;
330: return oldValue;
331: };
332: pairs++;
333: };
334: return nil;
335: }
336:
337: - (NXHashState) initState {
338: NXHashState state;
339: state.i = _nbBuckets;
340: state.j = 0;
341: return state;
342: };
343:
344: - (BOOL) nextState: (NXHashState *) aState key: (const void **) aKey value: (void **) aValue
345: {
346: Buckets buckets = (Buckets) _buckets;
347: Pair pair;
348: while (aState->j == 0) {
349: if (aState->i == 0)
350: {
351: *aKey = NULL; *aValue = NULL;
352: return NO;
353: }
354: aState->i--; aState->j = buckets[aState->i].count;
355: }
356: aState->j--;
357: pair = buckets[aState->i].pairs[aState->j];
358: *aKey = pair.key; *aValue = pair.value;
359: return YES;
360: }
361:
362: #ifndef KERNEL
363: - write:(NXTypedStream *) stream
364: {
365: NXHashState state = [self initState];
366: const void *key;
367: void *value;
368: [super write: stream];
369: NXWriteTypes (stream, "i%%", &count, &keyDesc, &valueDesc);
370: while ([self nextState: &state key: &key value: &value]) {
371: NXWriteType (stream, keyDesc, &key);
372: NXWriteType (stream, valueDesc, &value);
373: };
374: return self;
375: }
376:
377: - read:(NXTypedStream *) stream
378: {
379: unsigned nb; /* we set count as 0 but read nb elements */
380: if (NXTypedStreamClassVersion (stream, "HashTable") == 0) {
381: NXReadTypes (stream, "i**", &nb, &keyDesc, &valueDesc);
382: } else {
383: [super read: stream];
384: NXReadTypes (stream, "i%%", &nb, &keyDesc, &valueDesc);
385: }
386: if (! keyDesc) exit (1);
387: if (! valueDesc) exit (2);
388: count = 0;
389: _nbBuckets = exp2m1 (log2 (nb)+1);
390: _buckets = NXZoneCalloc ([self zone], _nbBuckets, sizeof(Bucket));
391: while (nb--) {
392: const void *key;
393: void *value;
394: NXReadType (stream, keyDesc, &key);
395: NXReadType (stream, valueDesc, &value);
396: [self _insertKeyNoRehash: key value: value];
397: };
398: return self;
399: }
400: #else /* KERNEL */
401: - write:(NXTypedStream *) stream
402: {
403: return nil;
404: }
405: - read:(NXTypedStream *) stream
406: {
407: return nil;
408: }
409: #endif /* KERNEL */
410:
411: static void DebugPrint (NXStream *stream, const char *desc, const void *data) {
412: switch (desc[0]) {
413: case '@':
414: NXPrintf (stream, "%s[0x%x]", [(id) data name], (int) data);
415: return;
416: case 'i':
417: NXPrintf (stream, "%d[0x%x]", (int) data, (int) data);
418: return;
419: case '%':
420: case '*':
421: NXPrintf (stream, "\"%s\"", (char *) data);
422: return;
423: default:
424: NXPrintf (stream, "0x%x", (int) data);
425: return;
426: };
427: };
428:
429: - (void) printForDebugger:(NXStream *)stream
430: {
431: unsigned i = _nbBuckets;
432: Buckets buckets = (Buckets) _buckets;
433:
434: NXPrintf (stream, "Table [%s -> %s]: \tcount: %d\tcapacity: %d\n",
435: keyDesc, valueDesc, count, _nbBuckets);
436: while (i--) {
437: if (buckets->count) {
438: unsigned j = buckets->count;
439: Pairs pairs = buckets->pairs;
440:
441: NXPrintf (stream, "%d\t", j);
442: while (j--) {
443: DebugPrint (stream, keyDesc, pairs->key);
444: NXPrintf (stream, ": ");
445: DebugPrint (stream, valueDesc, pairs->value);
446: NXPrintf (stream, "\t");
447: pairs++;
448: };
449: NXPrintf (stream, "\n");
450: };
451: buckets++;
452: };
453: NXPrintf (stream, "\n");
454: NXFlush (stream);
455: }
456:
457: /* Obsolete (for binary compatibility only). */
458:
459: - _debugPrint: (NXStream *) stream
460: {
461: [self printForDebugger: stream];
462: return self;
463: }
464:
465: @end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.