|
|
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 1989 NeXT, Inc.
27: Created by Bertrand Serlet, Feb 89
28: */
29:
30: #ifdef SHLIB
31: #import "shlib.h"
32: #endif SHLIB
33:
34: #import "hashtable.h"
35: #import "objc-private.h"
36:
37: #ifdef KERNEL
38:
39: #else /* KERNEL */
40:
41: #import <mach/mach.h>
42: #import <mach/cthreads.h>
43:
44: #endif /* KERNEL */
45:
46: /* In order to improve efficiency, buckets contain a pointer to an array or directly the data when the array size is 1 */
47: typedef union {
48: const void *one;
49: const void **many;
50: } oneOrMany;
51: /* an optimization consists of storing directly data when count = 1 */
52:
53: typedef struct {
54: unsigned count;
55: oneOrMany elements;
56: } HashBucket;
57: /* private data structure; may change */
58:
59: /*************************************************************************
60: *
61: * Macros and utilities
62: *
63: *************************************************************************/
64:
65: static unsigned log2 (unsigned x) { return (x<2) ? 0 : log2 (x>>1)+1; };
66:
67: static unsigned exp2m1 (unsigned x) { return (1 << x) - 1; };
68:
69: #define PTRSIZE sizeof(void *)
70:
71: #define ALLOCTABLE(z) ((NXHashTable *) NXZoneMalloc (z,sizeof (NXHashTable)))
72: #define ALLOCBUCKETS(z,nb)((HashBucket *) NXZoneCalloc (z, nb, sizeof (HashBucket)))
73: #define ALLOCPAIRS(z,nb) ((const void **) NXZoneCalloc (z, nb, sizeof (void *)))
74:
75: /* iff necessary this modulo can be optimized since the nbBuckets is of the form 2**n-1 */
76: #define BUCKETOF(table, data) (((HashBucket *)table->buckets)+((*table->prototype->hash)(table->info, data) % table->nbBuckets))
77:
78: #define ISEQUAL(table, data1, data2) ((data1 == data2) || (*table->prototype->isEqual)(table->info, data1, data2))
79: /* beware of double evaluation */
80:
81: /*************************************************************************
82: *
83: * Global data and bootstrap
84: *
85: *************************************************************************/
86:
87: static unsigned hashPrototype (const void *info, const void *data) {
88: NXHashTablePrototype *proto = (NXHashTablePrototype *) data;
89:
90: return NXPtrHash(info, proto->hash) ^ NXPtrHash(info, proto->isEqual) ^ NXPtrHash(info, proto->free) ^ (unsigned) proto->style;
91: };
92:
93: static int isEqualPrototype (const void *info, const void *data1, const void *data2) {
94: NXHashTablePrototype *proto1 = (NXHashTablePrototype *) data1;
95: NXHashTablePrototype *proto2 = (NXHashTablePrototype *) data2;
96:
97: return (proto1->hash == proto2->hash) && (proto1->isEqual == proto2->isEqual) && (proto1->free == proto2->free) && (proto1->style == proto2->style);
98: };
99:
100: static NXHashTablePrototype protoPrototype = {
101: hashPrototype, isEqualPrototype, NXNoEffectFree, 0
102: };
103:
104: static NXHashTable *prototypes = NULL;
105: /* table of all prototypes */
106:
107: static void bootstrap (void) {
108: free(malloc(8));
109: prototypes = ALLOCTABLE (NXDefaultMallocZone());
110: prototypes->prototype = &protoPrototype;
111: prototypes->count = 1;
112: prototypes->nbBuckets = 1; /* has to be 1 so that the right bucket is 0 */
113: prototypes->buckets = ALLOCBUCKETS(NXDefaultMallocZone(), 1);
114: prototypes->info = NULL;
115: ((HashBucket *) prototypes->buckets)[0].count = 1;
116: ((HashBucket *) prototypes->buckets)[0].elements.one = &protoPrototype;
117: };
118:
119: /*************************************************************************
120: *
121: * On z'y va
122: *
123: *************************************************************************/
124:
125: NXHashTable *NXCreateHashTable (NXHashTablePrototype prototype, unsigned capacity, const void *info) {
126: return NXCreateHashTableFromZone(prototype, capacity, info, NXDefaultMallocZone());
127: }
128:
129: NXHashTable *NXCreateHashTableFromZone (NXHashTablePrototype prototype, unsigned capacity, const void *info, NXZone *zone) {
130: NXHashTable *table;
131: NXHashTablePrototype *proto;
132:
133: table = ALLOCTABLE(zone);
134: if (! prototypes) bootstrap ();
135: if (! prototype.hash) prototype.hash = NXPtrHash;
136: if (! prototype.isEqual) prototype.isEqual = NXPtrIsEqual;
137: if (! prototype.free) prototype.free = NXNoEffectFree;
138: if (prototype.style) {
139: _NXLogError ("*** NXCreateHashTable: invalid style\n");
140: return NULL;
141: };
142: proto = NXHashGet (prototypes, &prototype);
143: if (! proto) {
144: proto
145: = (NXHashTablePrototype *) NXZoneMalloc (NXDefaultMallocZone(),
146: sizeof (NXHashTablePrototype));
147: bcopy (&prototype, proto, sizeof (NXHashTablePrototype));
148: (void) NXHashInsert (prototypes, proto);
149: proto = NXHashGet (prototypes, &prototype);
150: if (! proto) {
151: _NXLogError ("*** NXCreateHashTable: bug\n");
152: return NULL;
153: };
154: };
155: table->prototype = proto; table->count = 0; table->info = info;
156: table->nbBuckets = exp2m1 (log2 (capacity)+1);
157: table->buckets = ALLOCBUCKETS(zone, table->nbBuckets);
158: return table;
159: }
160:
161: static void freeBucketPairs (void (*freeProc)(const void *info, void *data), HashBucket bucket, const void *info) {
162: unsigned j = bucket.count;
163: const void **pairs;
164:
165: if (j == 1) {
166: (*freeProc) (info, (void *) bucket.elements.one);
167: return;
168: };
169: pairs = bucket.elements.many;
170: while (j--) {
171: (*freeProc) (info, (void *) *pairs);
172: pairs ++;
173: };
174: free (bucket.elements.many);
175: };
176:
177: static void freeBuckets (NXHashTable *table, int freeObjects) {
178: unsigned i = table->nbBuckets;
179: HashBucket *buckets = (HashBucket *) table->buckets;
180:
181: while (i--) {
182: if (buckets->count) {
183: freeBucketPairs ((freeObjects) ? table->prototype->free : NXNoEffectFree, *buckets, table->info);
184: buckets->count = 0;
185: buckets->elements.one = NULL;
186: };
187: buckets++;
188: };
189: };
190:
191: void NXFreeHashTable (NXHashTable *table) {
192: freeBuckets (table, YES);
193: free (table->buckets);
194: free (table);
195: };
196:
197: void NXEmptyHashTable (NXHashTable *table) {
198: freeBuckets (table, NO);
199: table->count = 0;
200: }
201:
202: void NXResetHashTable (NXHashTable *table) {
203: freeBuckets (table, YES);
204: table->count = 0;
205: }
206:
207: BOOL NXIsEqualHashTable (NXHashTable *table1, NXHashTable *table2) {
208: if (table1 == table2) return YES;
209: if (NXCountHashTable (table1) != NXCountHashTable (table2)) return NO;
210: else {
211: void *data;
212: NXHashState state = NXInitHashState (table1);
213: while (NXNextHashState (table1, &state, &data)) {
214: if (! NXHashMember (table2, data)) return NO;
215: }
216: return YES;
217: }
218: }
219:
220: BOOL NXCompareHashTables (NXHashTable *table1, NXHashTable *table2) {
221: if (table1 == table2) return YES;
222: if (NXCountHashTable (table1) != NXCountHashTable (table2)) return NO;
223: else {
224: void *data;
225: NXHashState state = NXInitHashState (table1);
226: while (NXNextHashState (table1, &state, &data)) {
227: if (! NXHashMember (table2, data)) return NO;
228: }
229: return YES;
230: }
231: }
232:
233: NXHashTable *NXCopyHashTable (NXHashTable *table) {
234: NXHashTable *new;
235: NXHashState state = NXInitHashState (table);
236: void *data;
237: NXZone *zone = NXZoneFromPtr(table);
238:
239: new = ALLOCTABLE(zone);
240: new->prototype = table->prototype; new->count = 0;
241: new->info = table->info;
242: new->nbBuckets = table->nbBuckets;
243: new->buckets = ALLOCBUCKETS(zone, new->nbBuckets);
244: while (NXNextHashState (table, &state, &data))
245: (void) NXHashInsert (new, data);
246: return new;
247: }
248:
249: unsigned NXCountHashTable (NXHashTable *table) {
250: return table->count;
251: }
252:
253: int NXHashMember (NXHashTable *table, const void *data) {
254: HashBucket *bucket = BUCKETOF(table, data);
255: unsigned j = bucket->count;
256: const void **pairs;
257:
258: if (! j) return 0;
259: if (j == 1) {
260: return ISEQUAL(table, data, bucket->elements.one);
261: };
262: pairs = bucket->elements.many;
263: while (j--) {
264: /* we don't cache isEqual because lists are short */
265: if (ISEQUAL(table, data, *pairs)) return 1;
266: pairs ++;
267: };
268: return 0;
269: }
270:
271: void *NXHashGet (NXHashTable *table, const void *data) {
272: HashBucket *bucket = BUCKETOF(table, data);
273: unsigned j = bucket->count;
274: const void **pairs;
275:
276: if (! j) return NULL;
277: if (j == 1) {
278: return ISEQUAL(table, data, bucket->elements.one)
279: ? (void *) bucket->elements.one : NULL;
280: };
281: pairs = bucket->elements.many;
282: while (j--) {
283: /* we don't cache isEqual because lists are short */
284: if (ISEQUAL(table, data, *pairs)) return (void *) *pairs;
285: pairs ++;
286: };
287: return NULL;
288: }
289:
290: static void _NXHashRehash (NXHashTable *table) {
291: /* Rehash: we create a pseudo table pointing really to the old guys,
292: extend self, copy the old pairs, and free the pseudo table */
293: NXHashTable *old;
294: NXHashState state;
295: void *aux;
296: NXZone *zone = NXZoneFromPtr(table);
297:
298: old = ALLOCTABLE(zone);
299: old->prototype = table->prototype; old->count = table->count;
300: old->nbBuckets = table->nbBuckets; old->buckets = table->buckets;
301: table->nbBuckets += table->nbBuckets + 1; /* 2 times + 1 */
302: table->count = 0; table->buckets = ALLOCBUCKETS(zone, table->nbBuckets);
303: state = NXInitHashState (old);
304: while (NXNextHashState (old, &state, &aux))
305: (void) NXHashInsert (table, aux);
306: freeBuckets (old, NO);
307: if (old->count != table->count)
308: _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");
309: free (old->buckets);
310: free (old);
311: };
312:
313: void *NXHashInsert (NXHashTable *table, const void *data) {
314: HashBucket *bucket = BUCKETOF(table, data);
315: unsigned j = bucket->count;
316: const void **pairs;
317: const void **new;
318: NXZone *zone = NXZoneFromPtr(table);
319:
320: if (! j) {
321: bucket->count++; bucket->elements.one = data;
322: table->count++;
323: return NULL;
324: };
325: if (j == 1) {
326: if (ISEQUAL(table, data, bucket->elements.one)) {
327: const void *old = bucket->elements.one;
328: bucket->elements.one = data;
329: return (void *) old;
330: };
331: new = ALLOCPAIRS(zone, 2);
332: new[1] = bucket->elements.one;
333: *new = data;
334: bucket->count++; bucket->elements.many = new;
335: table->count++;
336: if (table->count > table->nbBuckets) _NXHashRehash (table);
337: return NULL;
338: };
339: pairs = bucket->elements.many;
340: while (j--) {
341: /* we don't cache isEqual because lists are short */
342: if (ISEQUAL(table, data, *pairs)) {
343: const void *old = *pairs;
344: *pairs = data;
345: return (void *) old;
346: };
347: pairs ++;
348: };
349: /* we enlarge this bucket; and put new data in front */
350: new = ALLOCPAIRS(zone, bucket->count+1);
351: if (bucket->count) bcopy (bucket->elements.many, new+1, bucket->count * PTRSIZE);
352: *new = data;
353: free (bucket->elements.many);
354: bucket->count++; bucket->elements.many = new;
355: table->count++;
356: if (table->count > table->nbBuckets) _NXHashRehash (table);
357: return NULL;
358: }
359:
360: void *NXHashInsertIfAbsent (NXHashTable *table, const void *data) {
361: HashBucket *bucket = BUCKETOF(table, data);
362: unsigned j = bucket->count;
363: const void **pairs;
364: const void **new;
365: NXZone *zone = NXZoneFromPtr(table);
366:
367: if (! j) {
368: bucket->count++; bucket->elements.one = data;
369: table->count++;
370: return (void *) data;
371: };
372: if (j == 1) {
373: if (ISEQUAL(table, data, bucket->elements.one))
374: return (void *) bucket->elements.one;
375: new = ALLOCPAIRS(zone, 2);
376: new[1] = bucket->elements.one;
377: *new = data;
378: bucket->count++; bucket->elements.many = new;
379: table->count++;
380: if (table->count > table->nbBuckets) _NXHashRehash (table);
381: return (void *) data;
382: };
383: pairs = bucket->elements.many;
384: while (j--) {
385: /* we don't cache isEqual because lists are short */
386: if (ISEQUAL(table, data, *pairs))
387: return (void *) *pairs;
388: pairs ++;
389: };
390: /* we enlarge this bucket; and put new data in front */
391: new = ALLOCPAIRS(zone, bucket->count+1);
392: if (bucket->count) bcopy (bucket->elements.many, new+1, bucket->count * PTRSIZE);
393: *new = data;
394: free (bucket->elements.many);
395: bucket->count++; bucket->elements.many = new;
396: table->count++;
397: if (table->count > table->nbBuckets) _NXHashRehash (table);
398: return (void *) data;
399: }
400:
401: void *NXHashRemove (NXHashTable *table, const void *data) {
402: HashBucket *bucket = BUCKETOF(table, data);
403: unsigned j = bucket->count;
404: const void **pairs;
405: const void **new;
406: NXZone *zone = NXZoneFromPtr(table);
407:
408: if (! j) return NULL;
409: if (j == 1) {
410: if (! ISEQUAL(table, data, bucket->elements.one)) return NULL;
411: data = bucket->elements.one;
412: table->count--; bucket->count--; bucket->elements.one = NULL;
413: return (void *) data;
414: };
415: pairs = bucket->elements.many;
416: if (j == 2) {
417: if (ISEQUAL(table, data, pairs[0])) {
418: bucket->elements.one = pairs[1]; data = pairs[0];
419: }
420: else if (ISEQUAL(table, data, pairs[1])) {
421: bucket->elements.one = pairs[0]; data = pairs[1];
422: }
423: else return NULL;
424: free (pairs);
425: table->count--; bucket->count--;
426: return (void *) data;
427: };
428: while (j--) {
429: if (ISEQUAL(table, data, *pairs)) {
430: data = *pairs;
431: /* we shrink this bucket */
432: new = (bucket->count-1)
433: ? ALLOCPAIRS(zone, bucket->count-1) : NULL;
434: if (bucket->count-1 != j)
435: bcopy (bucket->elements.many, new, PTRSIZE*(bucket->count-j-1));
436: if (j)
437: bcopy (bucket->elements.many + bucket->count-j, new+bucket->count-j-1, PTRSIZE*j);
438: free (bucket->elements.many);
439: table->count--; bucket->count--; bucket->elements.many = new;
440: return (void *) data;
441: };
442: pairs ++;
443: };
444: return NULL;
445: }
446:
447: NXHashState NXInitHashState (NXHashTable *table) {
448: NXHashState state;
449:
450: state.i = table->nbBuckets;
451: state.j = 0;
452: return state;
453: };
454:
455: int NXNextHashState (NXHashTable *table, NXHashState *state, void **data) {
456: HashBucket *buckets = (HashBucket *) table->buckets;
457:
458: while (state->j == 0) {
459: if (state->i == 0) return NO;
460: state->i--; state->j = buckets[state->i].count;
461: }
462: state->j--;
463: buckets += state->i;
464: *data = (void *) ((buckets->count == 1)
465: ? buckets->elements.one : buckets->elements.many[state->j]);
466: return YES;
467: };
468:
469: /*************************************************************************
470: *
471: * Conveniences
472: *
473: *************************************************************************/
474:
475: unsigned NXPtrHash (const void *info, const void *data) {
476: return (((unsigned) data) >> 16) ^ ((unsigned) data);
477: };
478:
479: unsigned NXStrHash (const void *info, const void *data) {
480: register unsigned hash = 0;
481: register unsigned char *s = (unsigned char *) data;
482: /* unsigned to avoid a sign-extend */
483: /* unroll the loop */
484: if (s) for (; ; ) {
485: if (*s == '\0') break;
486: hash ^= *s++;
487: if (*s == '\0') break;
488: hash ^= *s++ << 8;
489: if (*s == '\0') break;
490: hash ^= *s++ << 16;
491: if (*s == '\0') break;
492: hash ^= *s++ << 24;
493: }
494: return hash;
495: };
496:
497: int NXPtrIsEqual (const void *info, const void *data1, const void *data2) {
498: return data1 == data2;
499: };
500:
501: int NXStrIsEqual (const void *info, const void *data1, const void *data2) {
502: if (data1 == data2) return YES;
503: if (! data1) return ! strlen ((char *) data2);
504: if (! data2) return ! strlen ((char *) data1);
505: if (((char *) data1)[0] != ((char *) data2)[0]) return NO;
506: return (strcmp ((char *) data1, (char *) data2)) ? NO : YES;
507: };
508:
509: void NXNoEffectFree (const void *info, void *data) {};
510:
511: void NXReallyFree (const void *info, void *data) {
512: free (data);
513: };
514:
515: /* All the following functions are really private, made non-static only for the benefit of shlibs */
516: unsigned hashPtrStructKey (const void *info, const void *data) {
517: return NXPtrHash(info, *((void **) data));
518: };
519: int isEqualPtrStructKey (const void *info, const void *data1, const void *data2) {
520: return NXPtrIsEqual (info, *((void **) data1), *((void **) data2));
521: };
522: unsigned hashStrStructKey (const void *info, const void *data) {
523: return NXStrHash(info, *((char **) data));
524: };
525: int isEqualStrStructKey (const void *info, const void *data1, const void *data2) {
526: return NXStrIsEqual (info, *((char **) data1), *((char **) data2));
527: };
528:
529:
530: /*************************************************************************
531: *
532: * Unique strings
533: *
534: *************************************************************************/
535:
536: /* the implementation could be made faster at the expense of memory if the size of the strings were kept around */
537: static NXHashTable *uniqueStrings = NULL;
538:
539: /* this is based on most apps using a few K of strings, and an average string size of 15 using sqrt(2*dataAlloced*perChunkOverhead) */
540: #define CHUNK_SIZE 360
541:
542: static int accessUniqueString = 0;
543:
544: static char *zone = NULL;
545: static vm_size_t zoneSize = 0;
546: static mutex_t lock = NULL;
547:
548: static const char *CopyIntoReadOnly (const char *str) {
549: unsigned int len = strlen (str) + 1;
550: char *new;
551:
552: if (len > CHUNK_SIZE/2) { /* dont let big strings waste space */
553: #ifdef KERNEL
554: new = kalloc (len);
555: #else /* KERNEL */
556: new = malloc (len);
557: #endif /* KERNEL */
558: bcopy (str, new, len);
559: return new;
560: }
561:
562: if (! lock) {
563: lock = mutex_alloc ();
564: mutex_init (lock);
565: };
566:
567: mutex_lock (lock);
568: if (zoneSize < len) {
569: zoneSize = CHUNK_SIZE *((len + CHUNK_SIZE - 1) / CHUNK_SIZE);
570: /* not enough room, we try to allocate. If no room left, too bad */
571: #ifdef KERNEL
572: zone = kalloc (zoneSize);
573: #else /* KERNEL */
574: zone = malloc (zoneSize);
575: #endif /* KERNEL */
576: };
577:
578: new = zone;
579: bcopy (str, new, len);
580: zone += len;
581: zoneSize -= len;
582: mutex_unlock (lock);
583: return new;
584: };
585:
586: NXAtom NXUniqueString (const char *buffer) {
587: const char *previous;
588:
589: if (! buffer) return buffer;
590: accessUniqueString++;
591: if (! uniqueStrings)
592: uniqueStrings = NXCreateHashTable (NXStrPrototype, 0, NULL);
593: previous = (const char *) NXHashGet (uniqueStrings, buffer);
594: if (previous) return previous;
595: previous = CopyIntoReadOnly (buffer);
596: if (NXHashInsert (uniqueStrings, previous)) {
597: _NXLogError ("*** NXUniqueString: invariant broken\n");
598: return NULL;
599: };
600: return previous;
601: };
602:
603: NXAtom NXUniqueStringNoCopy (const char *string) {
604: accessUniqueString++;
605: if (! uniqueStrings)
606: uniqueStrings = NXCreateHashTable (NXStrPrototype, 0, NULL);
607: return (const char *) NXHashInsertIfAbsent (uniqueStrings, string);
608: };
609:
610: #define BUF_SIZE 256
611:
612: NXAtom NXUniqueStringWithLength (const char *buffer, int length) {
613: NXAtom atom;
614: char *nullTermStr;
615: char stackBuf[BUF_SIZE];
616:
617: if (length+1 > BUF_SIZE)
618: nullTermStr = malloc (length+1);
619: else
620: nullTermStr = stackBuf;
621: bcopy (buffer, nullTermStr, length);
622: nullTermStr[length] = '\0';
623: atom = NXUniqueString (nullTermStr);
624: if (length+1 > BUF_SIZE)
625: free (nullTermStr);
626: return atom;
627: };
628:
629: char *NXCopyStringBufferFromZone (const char *str, NXZone *zone) {
630: return strcpy ((char *) NXZoneMalloc (zone, strlen (str) + 1), str);
631: };
632:
633: char *NXCopyStringBuffer (const char *str) {
634: return NXCopyStringBufferFromZone(str, NXDefaultMallocZone());
635: };
636:
637: #if 0 /* Never used! */
638: static void uniqueStringsStatistics (void) {
639: unsigned i = uniqueStrings->nbBuckets;
640: HashBucket *buckets = (HashBucket *) uniqueStrings->buckets;
641:
642: printf ("Size: %d\tcount: %d\taccesses: %d\n", i, uniqueStrings->count, accessUniqueString);
643: while (i--) {
644: if (buckets->count) {
645: if (buckets->count == 1)
646: printf ("1\t%s\n", (char *) buckets->elements.one);
647: else {
648: int j = buckets->count;
649: char **pairs = (char **) buckets->elements.many;
650: printf ("%d\t", buckets->count);
651: while (j--) printf ("%s ", *(pairs++));
652: printf ("\n");
653: };
654: };
655: buckets++;
656: };
657: };
658: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.