Annotation of embedaddon/strongswan/src/libstrongswan/collections/hashtable.c, revision 1.1.1.2

1.1       misho       1: /*
1.1.1.2 ! misho       2:  * Copyright (C) 2008-2020 Tobias Brunner
1.1       misho       3:  * HSR Hochschule fuer Technik Rapperswil
                      4:  *
                      5:  * This program is free software; you can redistribute it and/or modify it
                      6:  * under the terms of the GNU General Public License as published by the
                      7:  * Free Software Foundation; either version 2 of the License, or (at your
                      8:  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
                      9:  *
                     10:  * This program is distributed in the hope that it will be useful, but
                     11:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
                     12:  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
                     13:  * for more details.
                     14:  */
                     15: 
                     16: #include "hashtable.h"
1.1.1.2 ! misho      17: #include "hashtable_profiler.h"
1.1       misho      18: 
                     19: #include <utils/chunk.h>
1.1.1.2 ! misho      20: #include <utils/debug.h>
1.1       misho      21: 
1.1.1.2 ! misho      22: /** The minimum size of the hash table (MUST be a power of 2) */
        !            23: #define MIN_SIZE 8
        !            24: /** The maximum size of the hash table (MUST be a power of 2) */
        !            25: #define MAX_SIZE (1 << 30)
        !            26: 
        !            27: /** Determine the capacity/maximum load of the table (higher values cause
        !            28:  * more collisions, lower values increase the memory overhead) */
        !            29: #define CAPACITY(size) (size / 3 * 2)
        !            30: /** Factor for the new table size based on the number of items when resizing,
        !            31:  * with the above load factor this results in doubling the size when growing */
        !            32: #define RESIZE_FACTOR 3
        !            33: 
        !            34: /**
        !            35:  * A note about these parameters:
        !            36:  *
        !            37:  * The maximum number of items that can be stored in this implementation
        !            38:  * is MAX_COUNT = CAPACITY(MAX_SIZE).
        !            39:  * Since we use u_int throughout, MAX_COUNT * RESIZE_FACTOR must not overflow
        !            40:  * this type.
        !            41:  */
        !            42: #if (UINT_MAX / RESIZE_FACTOR < CAPACITY(MAX_SIZE))
        !            43:        #error Hahstable parameters invalid!
        !            44: #endif
1.1       misho      45: 
                     46: typedef struct pair_t pair_t;
                     47: 
                     48: /**
                     49:  * This pair holds a pointer to the key and value it represents.
                     50:  */
                     51: struct pair_t {
1.1.1.2 ! misho      52: 
1.1       misho      53:        /**
                     54:         * Key of a hash table item.
                     55:         */
                     56:        const void *key;
                     57: 
                     58:        /**
                     59:         * Value of a hash table item.
                     60:         */
                     61:        void *value;
                     62: 
                     63:        /**
                     64:         * Cached hash (used in case of a resize).
                     65:         */
                     66:        u_int hash;
                     67: };
                     68: 
                     69: typedef struct private_hashtable_t private_hashtable_t;
                     70: 
                     71: /**
                     72:  * Private data of a hashtable_t object.
                     73:  *
                     74:  */
                     75: struct private_hashtable_t {
1.1.1.2 ! misho      76: 
1.1       misho      77:        /**
                     78:         * Public part of hash table.
                     79:         */
                     80:        hashtable_t public;
                     81: 
                     82:        /**
                     83:         * The number of items in the hash table.
                     84:         */
                     85:        u_int count;
                     86: 
                     87:        /**
1.1.1.2 ! misho      88:         * The current size of the hash table (always a power of 2).
1.1       misho      89:         */
1.1.1.2 ! misho      90:        u_int size;
1.1       misho      91: 
                     92:        /**
1.1.1.2 ! misho      93:         * The current mask to calculate the row index (size - 1).
1.1       misho      94:         */
                     95:        u_int mask;
                     96: 
                     97:        /**
1.1.1.2 ! misho      98:         * All items in the order they were inserted (removed items are marked by
        !            99:         * setting the key to NULL until resized).
        !           100:         */
        !           101:        pair_t *items;
        !           102: 
        !           103:        /**
        !           104:         * Number of available slots in the array above and the table in general,
        !           105:         * is set to CAPACITY(size) when the hash table is initialized.
1.1       misho     106:         */
1.1.1.2 ! misho     107:        u_int capacity;
1.1       misho     108: 
                    109:        /**
1.1.1.2 ! misho     110:         * Number of used slots in the array above.
1.1       misho     111:         */
1.1.1.2 ! misho     112:        u_int items_count;
        !           113: 
        !           114:        /**
        !           115:         * Hash table with indices into the array above.  The type depends on the
        !           116:         * current capacity.
        !           117:         */
        !           118:        void *table;
1.1       misho     119: 
                    120:        /**
                    121:         * The hashing function.
                    122:         */
                    123:        hashtable_hash_t hash;
                    124: 
                    125:        /**
                    126:         * The equality function.
                    127:         */
                    128:        hashtable_equals_t equals;
1.1.1.2 ! misho     129: 
        !           130:        /**
        !           131:         * Profiling data
        !           132:         */
        !           133:        hashtable_profile_t profile;
1.1       misho     134: };
                    135: 
                    136: typedef struct private_enumerator_t private_enumerator_t;
                    137: 
                    138: /**
1.1.1.2 ! misho     139:  * Hash table enumerator implementation
1.1       misho     140:  */
                    141: struct private_enumerator_t {
                    142: 
                    143:        /**
1.1.1.2 ! misho     144:         * Implements enumerator interface
1.1       misho     145:         */
                    146:        enumerator_t enumerator;
                    147: 
                    148:        /**
1.1.1.2 ! misho     149:         * Associated hash table
1.1       misho     150:         */
                    151:        private_hashtable_t *table;
                    152: 
                    153:        /**
1.1.1.2 ! misho     154:         * Current index
1.1       misho     155:         */
1.1.1.2 ! misho     156:        u_int index;
1.1       misho     157: };
                    158: 
                    159: /*
1.1.1.2 ! misho     160:  * Described in header
1.1       misho     161:  */
                    162: u_int hashtable_hash_ptr(const void *key)
                    163: {
                    164:        return chunk_hash(chunk_from_thing(key));
                    165: }
                    166: 
                    167: /*
1.1.1.2 ! misho     168:  * Described in header
1.1       misho     169:  */
                    170: u_int hashtable_hash_str(const void *key)
                    171: {
                    172:        return chunk_hash(chunk_from_str((char*)key));
                    173: }
                    174: 
                    175: /*
1.1.1.2 ! misho     176:  * Described in header
1.1       misho     177:  */
                    178: bool hashtable_equals_ptr(const void *key, const void *other_key)
                    179: {
                    180:        return key == other_key;
                    181: }
                    182: 
                    183: /*
1.1.1.2 ! misho     184:  * Described in header
1.1       misho     185:  */
                    186: bool hashtable_equals_str(const void *key, const void *other_key)
                    187: {
                    188:        return streq(key, other_key);
                    189: }
                    190: 
                    191: /**
1.1.1.2 ! misho     192:  * Returns the index stored in the given bucket. If the bucket is empty,
        !           193:  * 0 is returned.
        !           194:  */
        !           195: static inline u_int get_index(private_hashtable_t *this, u_int row)
        !           196: {
        !           197:        if (this->capacity <= 0xff)
        !           198:        {
        !           199:                return ((uint8_t*)this->table)[row];
        !           200:        }
        !           201:        else if (this->capacity <= 0xffff)
        !           202:        {
        !           203:                return ((uint16_t*)this->table)[row];
        !           204:        }
        !           205:        return ((u_int*)this->table)[row];
        !           206: }
        !           207: 
        !           208: /**
        !           209:  * Set the index stored in the given bucket. Set to 0 to clear a bucket.
        !           210:  */
        !           211: static inline void set_index(private_hashtable_t *this, u_int row, u_int index)
        !           212: {
        !           213:        if (this->capacity <= 0xff)
        !           214:        {
        !           215:                ((uint8_t*)this->table)[row] = index;
        !           216:        }
        !           217:        else if (this->capacity <= 0xffff)
        !           218:        {
        !           219:                ((uint16_t*)this->table)[row] = index;
        !           220:        }
        !           221:        else
        !           222:        {
        !           223:                ((u_int*)this->table)[row] = index;
        !           224:        }
        !           225: }
        !           226: 
        !           227: /**
1.1       misho     228:  * This function returns the next-highest power of two for the given number.
                    229:  * The algorithm works by setting all bits on the right-hand side of the most
                    230:  * significant 1 to 1 and then increments the whole number so it rolls over
                    231:  * to the nearest power of two. Note: returns 0 for n == 0
1.1.1.2 ! misho     232:  *
        !           233:  * Also used by hashlist_t.
1.1       misho     234:  */
1.1.1.2 ! misho     235: u_int hashtable_get_nearest_powerof2(u_int n)
1.1       misho     236: {
                    237:        u_int i;
                    238: 
                    239:        --n;
                    240:        for (i = 1; i < sizeof(u_int) * 8; i <<= 1)
                    241:        {
                    242:                n |= n >> i;
                    243:        }
                    244:        return ++n;
                    245: }
                    246: 
                    247: /**
1.1.1.2 ! misho     248:  * Init hash table to the given size
1.1       misho     249:  */
1.1.1.2 ! misho     250: static void init_hashtable(private_hashtable_t *this, u_int size)
1.1       misho     251: {
1.1.1.2 ! misho     252:        u_int index_size = sizeof(u_int);
        !           253: 
        !           254:        this->size = max(MIN_SIZE, min(size, MAX_SIZE));
        !           255:        this->size = hashtable_get_nearest_powerof2(this->size);
        !           256:        this->mask = this->size - 1;
        !           257:        profile_size(&this->profile, this->size);
        !           258: 
        !           259:        this->capacity = CAPACITY(this->size);
        !           260:        this->items = calloc(this->capacity, sizeof(pair_t));
        !           261:        this->items_count = 0;
        !           262: 
        !           263:        if (this->capacity <= 0xff)
        !           264:        {
        !           265:                index_size = sizeof(uint8_t);
        !           266:        }
        !           267:        else if (this->capacity <= 0xffff)
        !           268:        {
        !           269:                index_size = sizeof(uint16_t);
        !           270:        }
        !           271:        this->table = calloc(this->size, index_size);
        !           272: }
1.1       misho     273: 
1.1.1.2 ! misho     274: /**
        !           275:  * Calculate the next bucket using quadratic probing (the sequence is h(k) + 1,
        !           276:  * h(k) + 3, h(k) + 6, h(k) + 10, ...).
        !           277:  */
        !           278: static inline u_int get_next(private_hashtable_t *this, u_int row, u_int *p)
        !           279: {
        !           280:        *p += 1;
        !           281:        return (row + *p) & this->mask;
1.1       misho     282: }
                    283: 
                    284: /**
1.1.1.2 ! misho     285:  * Find the pair with the given key, optionally returns the hash and first empty
        !           286:  * or previously used row if the key is not found.
1.1       misho     287:  */
1.1.1.2 ! misho     288: static inline pair_t *find_key(private_hashtable_t *this, const void *key,
        !           289:                                                                u_int *out_hash, u_int *out_row)
1.1       misho     290: {
1.1.1.2 ! misho     291:        pair_t *pair;
        !           292:        u_int hash, row, p = 0, removed, index;
        !           293:        bool found_removed = FALSE;
1.1       misho     294: 
1.1.1.2 ! misho     295:        if (!this->count && !out_hash && !out_row)
1.1       misho     296:        {
1.1.1.2 ! misho     297:                return NULL;
1.1       misho     298:        }
                    299: 
1.1.1.2 ! misho     300:        lookup_start();
1.1       misho     301: 
1.1.1.2 ! misho     302:        hash = this->hash(key);
        !           303:        row = hash & this->mask;
        !           304:        index = get_index(this, row);
        !           305:        while (index)
1.1       misho     306:        {
1.1.1.2 ! misho     307:                lookup_probing();
        !           308:                pair = &this->items[index-1];
1.1       misho     309: 
1.1.1.2 ! misho     310:                if (!pair->key)
        !           311:                {
        !           312:                        if (!found_removed && out_row)
        !           313:                        {
        !           314:                                removed = row;
        !           315:                                found_removed = TRUE;
        !           316:                        }
1.1       misho     317:                }
1.1.1.2 ! misho     318:                else if (pair->hash == hash && this->equals(key, pair->key))
        !           319:                {
        !           320:                        lookup_success(&this->profile);
        !           321:                        return pair;
        !           322:                }
        !           323:                row = get_next(this, row, &p);
        !           324:                index = get_index(this, row);
        !           325:        }
        !           326:        if (out_hash)
        !           327:        {
        !           328:                *out_hash = hash;
        !           329:        }
        !           330:        if (out_row)
        !           331:        {
        !           332:                *out_row = found_removed ? removed : row;
1.1       misho     333:        }
1.1.1.2 ! misho     334:        lookup_failure(&this->profile);
        !           335:        return NULL;
1.1       misho     336: }
                    337: 
1.1.1.2 ! misho     338: /**
        !           339:  * Helper to insert a new item into the table and items array,
        !           340:  * returns its new index into the latter.
        !           341:  */
        !           342: static inline u_int insert_item(private_hashtable_t *this, u_int row)
1.1       misho     343: {
1.1.1.2 ! misho     344:        u_int index = this->items_count++;
1.1       misho     345: 
1.1.1.2 ! misho     346:        /* we use 0 to mark unused buckets, so increase the index */
        !           347:        set_index(this, row, index + 1);
        !           348:        return index;
        !           349: }
        !           350: 
        !           351: /**
        !           352:  * Resize the hash table to the given size and rehash all the elements,
        !           353:  * size may be smaller or even the same (e.g. if it's necessary to clear
        !           354:  * previously used buckets).
        !           355:  */
        !           356: static bool rehash(private_hashtable_t *this, u_int size)
        !           357: {
        !           358:        pair_t *old_items, *pair;
        !           359:        u_int old_count, i, p, row, index;
        !           360: 
        !           361:        if (size > MAX_SIZE)
        !           362:        {
        !           363:                return FALSE;
1.1       misho     364:        }
1.1.1.2 ! misho     365: 
        !           366:        old_items = this->items;
        !           367:        old_count = this->items_count;
        !           368:        free(this->table);
        !           369:        init_hashtable(this, size);
        !           370: 
        !           371:        /* no need to do anything if the table is empty and we are just cleaning
        !           372:         * up previously used items */
        !           373:        if (this->count)
1.1       misho     374:        {
1.1.1.2 ! misho     375:                for (i = 0; i < old_count; i++)
        !           376:                {
        !           377:                        pair = &old_items[i];
        !           378: 
        !           379:                        if (pair->key)
        !           380:                        {
        !           381:                                row = pair->hash & this->mask;
        !           382:                                index = get_index(this, row);
        !           383:                                for (p = 0; index;)
        !           384:                                {
        !           385:                                        row = get_next(this, row, &p);
        !           386:                                        index = get_index(this, row);
        !           387:                                }
        !           388:                                index = insert_item(this, row);
        !           389:                                this->items[index] = *pair;
        !           390:                        }
        !           391:                }
1.1       misho     392:        }
1.1.1.2 ! misho     393:        free(old_items);
        !           394:        return TRUE;
1.1       misho     395: }
                    396: 
1.1.1.2 ! misho     397: METHOD(hashtable_t, put, void*,
        !           398:        private_hashtable_t *this, const void *key, void *value)
1.1       misho     399: {
1.1.1.2 ! misho     400:        void *old_value = NULL;
1.1       misho     401:        pair_t *pair;
1.1.1.2 ! misho     402:        u_int index, hash = 0, row = 0;
1.1       misho     403: 
1.1.1.2 ! misho     404:        if (this->items_count >= this->capacity &&
        !           405:                !rehash(this, this->count * RESIZE_FACTOR))
        !           406:        {
        !           407:                DBG1(DBG_LIB, "!!! FAILED TO RESIZE HASHTABLE TO %u !!!",
        !           408:                         this->count * RESIZE_FACTOR);
1.1       misho     409:                return NULL;
                    410:        }
1.1.1.2 ! misho     411:        pair = find_key(this, key, &hash, &row);
        !           412:        if (pair)
1.1       misho     413:        {
1.1.1.2 ! misho     414:                old_value = pair->value;
        !           415:                pair->value = value;
        !           416:                pair->key = key;
        !           417:                return old_value;
1.1       misho     418:        }
1.1.1.2 ! misho     419:        index = insert_item(this, row);
        !           420:        this->items[index] = (pair_t){
        !           421:                .hash = hash,
        !           422:                .key = key,
        !           423:                .value = value,
        !           424:        };
        !           425:        this->count++;
        !           426:        profile_count(&this->profile, this->count);
        !           427:        return NULL;
1.1       misho     428: }
                    429: 
                    430: METHOD(hashtable_t, get, void*,
                    431:        private_hashtable_t *this, const void *key)
                    432: {
1.1.1.2 ! misho     433:        pair_t *pair = find_key(this, key, NULL, NULL);
        !           434:        return pair ? pair->value : NULL;
1.1       misho     435: }
                    436: 
1.1.1.2 ! misho     437: /**
        !           438:  * Remove the given item from the table, returns the currently stored value.
        !           439:  */
        !           440: static void *remove_internal(private_hashtable_t *this, pair_t *pair)
1.1       misho     441: {
1.1.1.2 ! misho     442:        void *value = NULL;
        !           443: 
        !           444:        if (pair)
        !           445:        {       /* this does not decrease the item count as we keep the previously
        !           446:                 * used items until the table is rehashed/resized */
        !           447:                value = pair->value;
        !           448:                pair->key = NULL;
        !           449:                this->count--;
        !           450:        }
        !           451:        return value;
1.1       misho     452: }
                    453: 
                    454: METHOD(hashtable_t, remove_, void*,
                    455:        private_hashtable_t *this, const void *key)
                    456: {
1.1.1.2 ! misho     457:        pair_t *pair = find_key(this, key, NULL, NULL);
        !           458:        return remove_internal(this, pair);
1.1       misho     459: }
                    460: 
                    461: METHOD(hashtable_t, remove_at, void,
                    462:        private_hashtable_t *this, private_enumerator_t *enumerator)
                    463: {
1.1.1.2 ! misho     464:        if (enumerator->table == this && enumerator->index)
        !           465:        {       /* the index is already advanced by one */
        !           466:                u_int index = enumerator->index - 1;
        !           467:                remove_internal(this, &this->items[index]);
1.1       misho     468:        }
                    469: }
                    470: 
                    471: METHOD(hashtable_t, get_count, u_int,
                    472:        private_hashtable_t *this)
                    473: {
                    474:        return this->count;
                    475: }
                    476: 
                    477: METHOD(enumerator_t, enumerate, bool,
                    478:        private_enumerator_t *this, va_list args)
                    479: {
                    480:        const void **key;
                    481:        void **value;
1.1.1.2 ! misho     482:        pair_t *pair;
1.1       misho     483: 
                    484:        VA_ARGS_VGET(args, key, value);
                    485: 
1.1.1.2 ! misho     486:        while (this->index < this->table->items_count)
1.1       misho     487:        {
1.1.1.2 ! misho     488:                pair = &this->table->items[this->index++];
        !           489:                if (pair->key)
1.1       misho     490:                {
                    491:                        if (key)
                    492:                        {
1.1.1.2 ! misho     493:                                *key = pair->key;
1.1       misho     494:                        }
                    495:                        if (value)
                    496:                        {
1.1.1.2 ! misho     497:                                *value = pair->value;
1.1       misho     498:                        }
                    499:                        return TRUE;
                    500:                }
                    501:        }
                    502:        return FALSE;
                    503: }
                    504: 
                    505: METHOD(hashtable_t, create_enumerator, enumerator_t*,
                    506:        private_hashtable_t *this)
                    507: {
                    508:        private_enumerator_t *enumerator;
                    509: 
                    510:        INIT(enumerator,
                    511:                .enumerator = {
                    512:                        .enumerate = enumerator_enumerate_default,
                    513:                        .venumerate = _enumerate,
                    514:                        .destroy = (void*)free,
                    515:                },
                    516:                .table = this,
                    517:        );
                    518:        return &enumerator->enumerator;
                    519: }
                    520: 
                    521: static void destroy_internal(private_hashtable_t *this,
                    522:                                                         void (*fn)(void*,const void*))
                    523: {
1.1.1.2 ! misho     524:        pair_t *pair;
        !           525:        u_int i;
1.1       misho     526: 
1.1.1.2 ! misho     527:        profiler_cleanup(&this->profile, this->count, this->size);
        !           528: 
        !           529:        if (fn)
1.1       misho     530:        {
1.1.1.2 ! misho     531:                for (i = 0; i < this->items_count; i++)
1.1       misho     532:                {
1.1.1.2 ! misho     533:                        pair = &this->items[i];
        !           534:                        if (pair->key)
1.1       misho     535:                        {
                    536:                                fn(pair->value, pair->key);
                    537:                        }
                    538:                }
                    539:        }
1.1.1.2 ! misho     540:        free(this->items);
1.1       misho     541:        free(this->table);
                    542:        free(this);
                    543: }
                    544: 
                    545: METHOD(hashtable_t, destroy, void,
                    546:        private_hashtable_t *this)
                    547: {
                    548:        destroy_internal(this, NULL);
                    549: }
                    550: 
                    551: METHOD(hashtable_t, destroy_function, void,
                    552:        private_hashtable_t *this, void (*fn)(void*,const void*))
                    553: {
                    554:        destroy_internal(this, fn);
                    555: }
                    556: 
                    557: /*
                    558:  * Described in header.
                    559:  */
                    560: hashtable_t *hashtable_create(hashtable_hash_t hash, hashtable_equals_t equals,
1.1.1.2 ! misho     561:                                                          u_int size)
1.1       misho     562: {
                    563:        private_hashtable_t *this;
                    564: 
                    565:        INIT(this,
                    566:                .public = {
                    567:                        .put = _put,
                    568:                        .get = _get,
                    569:                        .remove = _remove_,
                    570:                        .remove_at = (void*)_remove_at,
                    571:                        .get_count = _get_count,
                    572:                        .create_enumerator = _create_enumerator,
                    573:                        .destroy = _destroy,
                    574:                        .destroy_function = _destroy_function,
                    575:                },
                    576:                .hash = hash,
                    577:                .equals = equals,
                    578:        );
                    579: 
1.1.1.2 ! misho     580:        init_hashtable(this, size);
        !           581: 
        !           582:        profiler_init(&this->profile, 2);
1.1       misho     583: 
                    584:        return &this->public;
                    585: }

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