Annotation of objc/HashTable.m, revision 1.1.1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.