Annotation of embedaddon/dhcp/omapip/hash.c, revision 1.1

1.1     ! misho       1: /* hash.c
        !             2: 
        !             3:    Routines for manipulating hash tables... */
        !             4: 
        !             5: /*
        !             6:  * Copyright (c) 2009-2010 by Internet Systems Consortium, Inc. ("ISC")
        !             7:  * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC")
        !             8:  * Copyright (c) 1995-2003 by Internet Software Consortium
        !             9:  *
        !            10:  * Permission to use, copy, modify, and distribute this software for any
        !            11:  * purpose with or without fee is hereby granted, provided that the above
        !            12:  * copyright notice and this permission notice appear in all copies.
        !            13:  *
        !            14:  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
        !            15:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        !            16:  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
        !            17:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        !            18:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
        !            19:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
        !            20:  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
        !            21:  *
        !            22:  *   Internet Systems Consortium, Inc.
        !            23:  *   950 Charter Street
        !            24:  *   Redwood City, CA 94063
        !            25:  *   <info@isc.org>
        !            26:  *   https://www.isc.org/
        !            27:  *
        !            28:  * This software has been written for Internet Systems Consortium
        !            29:  * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
        !            30:  * To learn more about Internet Systems Consortium, see
        !            31:  * ``https://www.isc.org/''.  To learn more about Vixie Enterprises,
        !            32:  * see ``http://www.vix.com''.   To learn more about Nominum, Inc., see
        !            33:  * ``http://www.nominum.com''.
        !            34:  */
        !            35: 
        !            36: #include "dhcpd.h"
        !            37: 
        !            38: #include <omapip/omapip_p.h>
        !            39: #include <limits.h>
        !            40: #include <ctype.h>
        !            41: 
        !            42: static unsigned
        !            43: find_length(const void *key,
        !            44:            unsigned (*do_hash)(const void *, unsigned, unsigned))
        !            45: {
        !            46:        if (do_hash == do_case_hash || do_hash == do_string_hash)
        !            47:                return strlen((const char *)key);
        !            48:        if (do_hash == do_number_hash)
        !            49:                return sizeof(unsigned);
        !            50:        if (do_hash == do_ip4_hash)
        !            51:                return 4;
        !            52: 
        !            53:        log_debug("Unexpected hash function at %s:%d.", MDL);
        !            54:        /*
        !            55:         * If we get a hash function we don't specifically expect
        !            56:         * return a length of 0, this covers the case where a client
        !            57:         * id has a length of 0.
        !            58:         */
        !            59:        return 0;
        !            60: }
        !            61: 
        !            62: int new_hash_table (tp, count, file, line)
        !            63:        struct hash_table **tp;
        !            64:        unsigned count;
        !            65:        const char *file;
        !            66:        int line;
        !            67: {
        !            68:        struct hash_table *rval;
        !            69:        unsigned extra;
        !            70: 
        !            71:        if (!tp) {
        !            72:                log_error ("%s(%d): new_hash_table called with null pointer.",
        !            73:                           file, line);
        !            74: #if defined (POINTER_DEBUG)
        !            75:                abort ();
        !            76: #endif
        !            77:                return 0;
        !            78:        }
        !            79:        if (*tp) {
        !            80:                log_error ("%s(%d): non-null target for new_hash_table.",
        !            81:                           file, line);
        !            82: #if defined (POINTER_DEBUG)
        !            83:                abort ();
        !            84: #endif
        !            85:        }
        !            86: 
        !            87:        /* There is one hash bucket in the structure.  Allocate extra
        !            88:         * memory beyond the end of the structure to fulfill the requested
        !            89:         * count ("count - 1").  Do not let there be less than one.
        !            90:         */
        !            91:        if (count <= 1)
        !            92:                extra = 0;
        !            93:        else
        !            94:                extra = count - 1;
        !            95: 
        !            96:        rval = dmalloc(sizeof(struct hash_table) +
        !            97:                       (extra * sizeof(struct hash_bucket *)), file, line);
        !            98:        if (!rval)
        !            99:                return 0;
        !           100:        rval -> hash_count = count;
        !           101:        *tp = rval;
        !           102:        return 1;
        !           103: }
        !           104: 
        !           105: void free_hash_table (tp, file, line)
        !           106:        struct hash_table **tp;
        !           107:        const char *file;
        !           108:        int line;
        !           109: {
        !           110:        struct hash_table *ptr = *tp;
        !           111: 
        !           112: #if defined (DEBUG_MEMORY_LEAKAGE) || \
        !           113:                defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
        !           114:        int i;
        !           115:        struct hash_bucket *hbc, *hbn = (struct hash_bucket *)0;
        !           116: 
        !           117:        for (i = 0; i < ptr -> hash_count; i++) {
        !           118:            for (hbc = ptr -> buckets [i]; hbc; hbc = hbn) {
        !           119:                hbn = hbc -> next;
        !           120:                if (ptr -> dereferencer && hbc -> value)
        !           121:                    (*ptr -> dereferencer) (&hbc -> value, MDL);
        !           122:            }
        !           123:            for (hbc = ptr -> buckets [i]; hbc; hbc = hbn) {
        !           124:                hbn = hbc -> next;
        !           125:                free_hash_bucket (hbc, MDL);
        !           126:            }
        !           127:            ptr -> buckets [i] = (struct hash_bucket *)0;
        !           128:        }
        !           129: #endif
        !           130: 
        !           131:        dfree((void *)ptr, MDL);
        !           132:        *tp = (struct hash_table *)0;
        !           133: }
        !           134: 
        !           135: struct hash_bucket *free_hash_buckets;
        !           136: 
        !           137: #if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
        !           138: struct hash_bucket *hash_bucket_hunks;
        !           139: 
        !           140: void relinquish_hash_bucket_hunks ()
        !           141: {
        !           142:        struct hash_bucket *c, *n, **p;
        !           143: 
        !           144:        /* Account for all the hash buckets on the free list. */
        !           145:        p = &free_hash_buckets;
        !           146:        for (c = free_hash_buckets; c; c = c -> next) {
        !           147:                for (n = hash_bucket_hunks; n; n = n -> next) {
        !           148:                        if (c > n && c < n + 127) {
        !           149:                                *p = c -> next;
        !           150:                                n -> len++;
        !           151:                                break;
        !           152:                        }
        !           153:                }
        !           154:                /* If we didn't delete the hash bucket from the free list,
        !           155:                   advance the pointer. */
        !           156:                if (!n)
        !           157:                        p = &c -> next;
        !           158:        }
        !           159:                
        !           160:        for (c = hash_bucket_hunks; c; c = n) {
        !           161:                n = c -> next;
        !           162:                if (c -> len != 126) {
        !           163:                        log_info ("hashbucket %lx hash_buckets %d free %u",
        !           164:                                  (unsigned long)c, 127, c -> len);
        !           165:                }
        !           166:                dfree (c, MDL);
        !           167:        }
        !           168: }
        !           169: #endif
        !           170: 
        !           171: struct hash_bucket *new_hash_bucket (file, line)
        !           172:        const char *file;
        !           173:        int line;
        !           174: {
        !           175:        struct hash_bucket *rval;
        !           176:        int i = 0;
        !           177:        if (!free_hash_buckets) {
        !           178:                rval = dmalloc (127 * sizeof (struct hash_bucket),
        !           179:                                file, line);
        !           180:                if (!rval)
        !           181:                        return rval;
        !           182: # if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
        !           183:                rval -> next = hash_bucket_hunks;
        !           184:                hash_bucket_hunks = rval;
        !           185:                hash_bucket_hunks -> len = 0;
        !           186:                i++;
        !           187:                rval++;
        !           188: #endif
        !           189:                for (; i < 127; i++) {
        !           190:                        rval -> next = free_hash_buckets;
        !           191:                        free_hash_buckets = rval;
        !           192:                        rval++;
        !           193:                }
        !           194:        }
        !           195:        rval = free_hash_buckets;
        !           196:        free_hash_buckets = rval -> next;
        !           197:        return rval;
        !           198: }
        !           199: 
        !           200: void free_hash_bucket (ptr, file, line)
        !           201:        struct hash_bucket *ptr;
        !           202:        const char *file;
        !           203:        int line;
        !           204: {
        !           205: #if defined (DEBUG_MALLOC_POOL)
        !           206:        struct hash_bucket *hp;
        !           207: 
        !           208:        for (hp = free_hash_buckets; hp; hp = hp -> next) {
        !           209:                if (hp == ptr) {
        !           210:                        log_error ("hash bucket freed twice!");
        !           211:                        abort ();
        !           212:                }
        !           213:        }
        !           214: #endif
        !           215:        ptr -> next = free_hash_buckets;
        !           216:        free_hash_buckets = ptr;
        !           217: }
        !           218: 
        !           219: int new_hash(struct hash_table **rp,
        !           220:             hash_reference referencer,
        !           221:             hash_dereference dereferencer,
        !           222:             unsigned hsize,
        !           223:             unsigned (*hasher)(const void *, unsigned, unsigned),
        !           224:             const char *file, int line)
        !           225: {
        !           226:        if (hsize == 0)
        !           227:                hsize = DEFAULT_HASH_SIZE;
        !           228: 
        !           229:        if (!new_hash_table (rp, hsize, file, line))
        !           230:                return 0;
        !           231: 
        !           232:        memset ((*rp)->buckets, 0, hsize * sizeof(struct hash_bucket *));
        !           233: 
        !           234:        (*rp)->referencer = referencer;
        !           235:        (*rp)->dereferencer = dereferencer;
        !           236:        (*rp)->do_hash = hasher;
        !           237: 
        !           238:        if (hasher == do_case_hash)
        !           239:                (*rp)->cmp = casecmp;
        !           240:        else
        !           241:                (*rp)->cmp = memcmp;
        !           242: 
        !           243:        return 1;
        !           244: }
        !           245: 
        !           246: unsigned
        !           247: do_case_hash(const void *name, unsigned len, unsigned size)
        !           248: {
        !           249:        register unsigned accum = 0;
        !           250:        register const unsigned char *s = name;
        !           251:        int i = len;
        !           252:        register unsigned c;
        !           253: 
        !           254:        while (i--) {
        !           255:                /* Make the hash case-insensitive. */
        !           256:                c = *s++;
        !           257:                if (isascii(c))
        !           258:                        c = tolower(c);
        !           259: 
        !           260:                /* Add the character in... */
        !           261:                accum = (accum << 1) + c;
        !           262: 
        !           263:                /* Add carry back in... */
        !           264:                while (accum > 65535) {
        !           265:                        accum = (accum & 65535) + (accum >> 16);
        !           266:                }
        !           267: 
        !           268:        }
        !           269:        return accum % size;
        !           270: }
        !           271: 
        !           272: unsigned
        !           273: do_string_hash(const void *name, unsigned len, unsigned size)
        !           274: {
        !           275:        register unsigned accum = 0;
        !           276:        register const unsigned char *s = (const unsigned char *)name;
        !           277:        int i = len;
        !           278: 
        !           279:        while (i--) {
        !           280:                /* Add the character in... */
        !           281:                accum = (accum << 1) + *s++;
        !           282: 
        !           283:                /* Add carry back in... */
        !           284:                while (accum > 65535) {
        !           285:                        accum = (accum & 65535) + (accum >> 16);
        !           286:                }
        !           287:        }
        !           288:        return accum % size;
        !           289: }
        !           290: 
        !           291: /* Client identifiers are generally 32-bits of ordinary
        !           292:  * non-randomness followed by 24-bits of unordinary randomness.
        !           293:  * So, end-align in 24-bit chunks, and xor any preceding data
        !           294:  * just to mix it up a little.
        !           295:  */
        !           296: unsigned
        !           297: do_id_hash(const void *name, unsigned len, unsigned size)
        !           298: {
        !           299:        register unsigned accum = 0;
        !           300:        register const unsigned char *s = (const unsigned char *)name;
        !           301:        const unsigned char *end = s + len;
        !           302: 
        !           303:        if (len == 0)
        !           304:                return 0;
        !           305: 
        !           306:        /*
        !           307:         * The switch handles our starting conditions, then we hash the
        !           308:         * remaining bytes in groups of 3
        !           309:         */
        !           310:           
        !           311:        switch (len % 3) {
        !           312:        case 0:
        !           313:                break;
        !           314:        case 2:
        !           315:                accum ^= *s++ << 8;
        !           316:        case 1:
        !           317:                accum ^= *s++;
        !           318:                break;
        !           319:        }
        !           320: 
        !           321:        while (s < end) {
        !           322:                accum ^= *s++ << 16;
        !           323:                accum ^= *s++ << 8;
        !           324:                accum ^= *s++;
        !           325:        }
        !           326: 
        !           327:        return accum % size;
        !           328: }
        !           329: 
        !           330: unsigned
        !           331: do_number_hash(const void *key, unsigned len, unsigned size)
        !           332: {
        !           333:        register unsigned number = *((const unsigned *)key);
        !           334: 
        !           335:        return number % size;
        !           336: }
        !           337: 
        !           338: unsigned
        !           339: do_ip4_hash(const void *key, unsigned len, unsigned size)
        !           340: {
        !           341:        u_int32_t number;
        !           342: 
        !           343:        memcpy(&number, key, 4);
        !           344: 
        !           345:        number = ntohl(number);
        !           346: 
        !           347:        return number % size;
        !           348: }
        !           349: 
        !           350: unsigned char *
        !           351: hash_report(struct hash_table *table)
        !           352: {
        !           353:        static unsigned char retbuf[sizeof("Contents/Size (%): "
        !           354:                                           "2147483647/2147483647 "
        !           355:                                           "(2147483647%). "
        !           356:                                           "Min/max: 2147483647/2147483647")];
        !           357:        unsigned curlen, pct, contents=0, minlen=UINT_MAX, maxlen=0;
        !           358:        unsigned i;
        !           359:        struct hash_bucket *bp;
        !           360: 
        !           361:        if (table == NULL)
        !           362:                return (unsigned char *) "No table.";
        !           363: 
        !           364:        if (table->hash_count == 0)
        !           365:                return (unsigned char *) "Invalid hash table.";
        !           366: 
        !           367:        for (i = 0 ; i < table->hash_count ; i++) {
        !           368:                curlen = 0;
        !           369: 
        !           370:                bp = table->buckets[i];
        !           371:                while (bp != NULL) {
        !           372:                        curlen++;
        !           373:                        bp = bp->next;
        !           374:                }
        !           375: 
        !           376:                if (curlen < minlen)
        !           377:                        minlen = curlen;
        !           378:                if (curlen > maxlen)
        !           379:                        maxlen = curlen;
        !           380: 
        !           381:                contents += curlen;
        !           382:        }
        !           383: 
        !           384:        if (contents >= (UINT_MAX / 100))
        !           385:                pct = contents / ((table->hash_count / 100) + 1);
        !           386:        else
        !           387:                pct = (contents * 100) / table->hash_count;
        !           388: 
        !           389:        if (contents > 2147483647 ||
        !           390:            table->hash_count > 2147483647 ||
        !           391:            pct > 2147483647 ||
        !           392:            minlen > 2147483647 ||
        !           393:            maxlen > 2147483647)
        !           394:                return (unsigned char *) "Report out of range for display.";
        !           395: 
        !           396:        sprintf((char *)retbuf, 
        !           397:                "Contents/Size (%%): %u/%u (%u%%). Min/max: %u/%u",
        !           398:                contents, table->hash_count, pct, minlen, maxlen);
        !           399: 
        !           400:        return retbuf;
        !           401: }
        !           402: 
        !           403: void add_hash (table, key, len, pointer, file, line)
        !           404:        struct hash_table *table;
        !           405:        unsigned len;
        !           406:        const void *key;
        !           407:        hashed_object_t *pointer;
        !           408:        const char *file;
        !           409:        int line;
        !           410: {
        !           411:        int hashno;
        !           412:        struct hash_bucket *bp;
        !           413:        void *foo;
        !           414: 
        !           415:        if (!table)
        !           416:                return;
        !           417: 
        !           418:        if (!len)
        !           419:                len = find_length(key, table->do_hash);
        !           420: 
        !           421:        hashno = (*table->do_hash)(key, len, table->hash_count);
        !           422:        bp = new_hash_bucket (file, line);
        !           423: 
        !           424:        if (!bp) {
        !           425:                log_error ("Can't add entry to hash table: no memory.");
        !           426:                return;
        !           427:        }
        !           428:        bp -> name = key;
        !           429:        if (table -> referencer) {
        !           430:                foo = &bp -> value;
        !           431:                (*(table -> referencer)) (foo, pointer, file, line);
        !           432:        } else
        !           433:                bp -> value = pointer;
        !           434:        bp -> next = table -> buckets [hashno];
        !           435:        bp -> len = len;
        !           436:        table -> buckets [hashno] = bp;
        !           437: }
        !           438: 
        !           439: void delete_hash_entry (table, key, len, file, line)
        !           440:        struct hash_table *table;
        !           441:        unsigned len;
        !           442:        const void *key;
        !           443:        const char *file;
        !           444:        int line;
        !           445: {
        !           446:        int hashno;
        !           447:        struct hash_bucket *bp, *pbp = (struct hash_bucket *)0;
        !           448:        void *foo;
        !           449: 
        !           450:        if (!table)
        !           451:                return;
        !           452: 
        !           453:        if (!len)
        !           454:                len = find_length(key, table->do_hash);
        !           455: 
        !           456:        hashno = (*table->do_hash)(key, len, table->hash_count);
        !           457: 
        !           458:        /* Go through the list looking for an entry that matches;
        !           459:           if we find it, delete it. */
        !           460:        for (bp = table -> buckets [hashno]; bp; bp = bp -> next) {
        !           461:                if ((!bp -> len &&
        !           462:                     !strcmp ((const char *)bp->name, key)) ||
        !           463:                    (bp -> len == len &&
        !           464:                     !(table -> cmp)(bp->name, key, len))) {
        !           465:                        if (pbp) {
        !           466:                                pbp -> next = bp -> next;
        !           467:                        } else {
        !           468:                                table -> buckets [hashno] = bp -> next;
        !           469:                        }
        !           470:                        if (bp -> value && table -> dereferencer) {
        !           471:                                foo = &bp -> value;
        !           472:                                (*(table -> dereferencer)) (foo, file, line);
        !           473:                        }
        !           474:                        free_hash_bucket (bp, file, line);
        !           475:                        break;
        !           476:                }
        !           477:                pbp = bp;       /* jwg, 9/6/96 - nice catch! */
        !           478:        }
        !           479: }
        !           480: 
        !           481: int hash_lookup (vp, table, key, len, file, line)
        !           482:        hashed_object_t **vp;
        !           483:        struct hash_table *table;
        !           484:        const void *key;
        !           485:        unsigned len;
        !           486:        const char *file;
        !           487:        int line;
        !           488: {
        !           489:        int hashno;
        !           490:        struct hash_bucket *bp;
        !           491: 
        !           492:        if (!table)
        !           493:                return 0;
        !           494:        if (!len)
        !           495:                len = find_length(key, table->do_hash);
        !           496: 
        !           497:        if (*vp != NULL) {
        !           498:                log_fatal("Internal inconsistency: storage value has not been "
        !           499:                          "initialized to zero (from %s:%d).", file, line);
        !           500:        }
        !           501: 
        !           502:        hashno = (*table->do_hash)(key, len, table->hash_count);
        !           503: 
        !           504:        for (bp = table -> buckets [hashno]; bp; bp = bp -> next) {
        !           505:                if (len == bp -> len
        !           506:                    && !(*table->cmp)(bp->name, key, len)) {
        !           507:                        if (table -> referencer)
        !           508:                                (*table -> referencer) (vp, bp -> value,
        !           509:                                                        file, line);
        !           510:                        else
        !           511:                                *vp = bp -> value;
        !           512:                        return 1;
        !           513:                }
        !           514:        }
        !           515:        return 0;
        !           516: }
        !           517: 
        !           518: int hash_foreach (struct hash_table *table, hash_foreach_func func)
        !           519: {
        !           520:        int i;
        !           521:        struct hash_bucket *bp, *next;
        !           522:        int count = 0;
        !           523: 
        !           524:        if (!table)
        !           525:                return 0;
        !           526: 
        !           527:        for (i = 0; i < table -> hash_count; i++) {
        !           528:                bp = table -> buckets [i];
        !           529:                while (bp) {
        !           530:                        next = bp -> next;
        !           531:                        if ((*func)(bp->name, bp->len, bp->value)
        !           532:                                                        != ISC_R_SUCCESS)
        !           533:                                return count;
        !           534:                        bp = next;
        !           535:                        count++;
        !           536:                }
        !           537:        }
        !           538:        return count;
        !           539: }
        !           540: 
        !           541: int casecmp (const void *v1, const void *v2, size_t len)
        !           542: {
        !           543:        size_t i;
        !           544:        const unsigned char *s = v1;
        !           545:        const unsigned char *t = v2;
        !           546:        
        !           547:        for (i = 0; i < len; i++)
        !           548:        {
        !           549:                int c1, c2;
        !           550:                if (isascii(s[i]))
        !           551:                        c1 = tolower(s[i]);
        !           552:                else
        !           553:                        c1 = s[i];
        !           554: 
        !           555:                if (isascii(t[i]))
        !           556:                        c2 = tolower(t[i]);
        !           557:                else
        !           558:                        c2 = t[i];
        !           559: 
        !           560:                if (c1 < c2)
        !           561:                        return -1;
        !           562:                if (c1 > c2)
        !           563:                        return 1;
        !           564:        }
        !           565:        return 0;
        !           566: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>