Annotation of objc/hashtable.m, revision 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 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

unix.superglobalmegacorp.com

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