File: hashmap.c

package info (click to toggle)
opencryptoki 3.23.0%2Bdfsg-0.3
  • links: PTS
  • area: main
  • in suites: forky, sid, trixie
  • size: 12,604 kB
  • sloc: ansic: 214,248; sh: 2,759; makefile: 289; yacc: 242; pascal: 152; exp: 126; lex: 93; cpp: 9
file content (275 lines) | stat: -rw-r--r-- 7,256 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
/*
 * COPYRIGHT (c) International Business Machines Corp. 2021
 *
 * This program is provided under the terms of the Common Public License,
 * version 1.0 (CPL-1.0). Any use, reproduction or distribution for this
 * software constitutes recipient's acceptance of CPL-1.0 terms which can be
 * found in the file LICENSE file or at
 * https://opensource.org/licenses/cpl1.0.php
 */
#include <stdio.h>
#include <stdlib.h>

#include <pkcs11types.h>
#include "hashmap.h"

/*
 * Hash map for mechanisms.  It stores the mechanism number in a
 * linked hash map.  Note that it does not directly store the
 * mechanism number, but the mechanism number plus one.  This is done
 * to have 0 represent an empty bucket.  The structure is optimized
 * for the non-chaining case in which case the value is directly
 * stored in the root of the bucket chain.  Only further chain
 * elements are allocated separately.
 *
 * Furthermore, we use a size optimization to not pre-allocate buckets
 * when creating a new hash.  Only on first addition to the hash do we
 * create the bucket list.
 *
 * The hash is a power of 2 hash to speed up hash value computation
 * (subtraction + binary and versus modulo computation).
 */

/* Default hash size has to be a power of 2. */
#define HASH_DEFAULT_CAPA 16

static unsigned int hash(unsigned int capa, CK_ULONG value)
{
#ifdef HASHMAP_FULL_JENKINS
    /* Full Jenkins hash */
    unsigned char *key = (unsigned char *)&value;
    size_t i = 0;
    unsigned int hash = 0;

    while (i != 8) {
        hash += key[i++];
        hash += hash << 10;
        hash ^= hash >> 6;
    }
    hash += hash << 3;
    hash ^= hash >> 11;
    hash += hash << 15;
    return hash & (capa - 1);
#else
#  ifdef HASHMAP_JENKINS_MIX
    /* Jenkins hash mix function */
    value += value << 3;
    value ^= value >> 11;
    value += value << 15;
#  endif
    return value & (capa - 1);
#endif
}

struct hashmap_bucket;
struct hashmap_bucket {
    CK_ULONG key;
    union hashmap_value value;
    struct hashmap_bucket *next;
};

struct hashmap {
    struct hashmap_bucket *buckets;
    unsigned int size;
    unsigned int capa;
    freefunc_t freefunc;
};

/* Create size-optimized empty hash.  First add will expand the hash to its
 * default capacity.
 */
struct hashmap *hashmap_new(void)
{
    struct hashmap *res = malloc(sizeof(struct hashmap));
    if (!res)
        return res;
    res->buckets = NULL;
    res->size = 0;
    res->capa = 0;
    return res;
}

static void freebucketchain(struct hashmap_bucket *b, freefunc_t f)
{
    struct hashmap_bucket *n;
    
    while (b) {
        n = b->next;
        if (f)
            f(b->value);
        free(b);
        b = n;
    }
}

static void freebuckets(struct hashmap_bucket *buckets, unsigned int size,
                        freefunc_t f)
{
    unsigned int i;
    
    for (i = 0; i < size; ++i)
        freebucketchain(buckets[i].next, f);
}

void hashmap_free(struct hashmap *h, freefunc_t f)
{
    if (h) {
        if (h->buckets) {
            freebuckets(h->buckets, h->capa, f);
            free(h->buckets);
        }
        free(h);
    }
}

static int do_add(struct hashmap_bucket *buckets, unsigned int size,
                  CK_ULONG key, union hashmap_value val)
{
    unsigned int hval;
    struct hashmap_bucket *newbucket;
    
    hval = hash(size, key);
    if (buckets[hval].key) {
        newbucket = malloc(sizeof(struct hashmap_bucket));
        if (!newbucket)
            return 1;
        newbucket->next = buckets[hval].next;
        newbucket->key = key;
        newbucket->value = val;
        buckets[hval].next = newbucket;
    } else {
        buckets[hval].key = key;
        buckets[hval].value = val;
    }
    return 0;
}

static int grow(struct hashmap *h)
{
    unsigned int i;
    unsigned int newcapa;
    struct hashmap_bucket *newarr, *walk;
    
    newcapa = h->capa ? h->capa << 1 : HASH_DEFAULT_CAPA;
    if (newcapa < h->capa)
        return 1;
    newarr = calloc(newcapa, sizeof(struct hashmap_bucket));
    if (!newarr)
        return 1;
    for (i = 0; i < h->capa; ++i) {
        if (h->buckets[i].key) {
            walk = &h->buckets[i];
            while (walk) {
                if (do_add(newarr, newcapa, walk->key, walk->value)) {
                    /* Pass no free function here since the values are
                       still in the old hash buckets that remain in
                       the hash. */
                    freebuckets(newarr, newcapa, NULL);
                    free(newarr);
                    return 1;
                }
                walk = walk->next;
            }
        }
    }
    if (h->buckets) {
        /* Pass no free function here since we copied the values into
           the new bucket array. */
        freebuckets(h->buckets, h->capa, NULL);
        free(h->buckets);
    }
    h->buckets = newarr;
    h->capa = newcapa;
    return 0;
}

static struct hashmap_bucket *hashmap_findbucket(struct hashmap *h,
                                                 CK_ULONG key)
{
    unsigned int hval;
    struct hashmap_bucket *b = NULL;

    if (h->buckets) {
        hval = hash(h->capa, key + 1);
        b = &h->buckets[hval];
        while (b && b->key != key + 1)
            b = b->next;
    }
    return b;
}

int hashmap_find(struct hashmap *h, CK_ULONG key, union hashmap_value *val)
{
    struct hashmap_bucket *b = 0;

    if (!h)
        /* The non-existing hash is universal. */
        return 1;
    b = hashmap_findbucket(h, key);
    if (b && val)
        *val = b->value;
    return !!b;
}

int hashmap_add(struct hashmap *h, CK_ULONG key, union hashmap_value val,
                union hashmap_value *oldval)
{
    struct hashmap_bucket *b;

    b = hashmap_findbucket(h, key);
    if (b) {
        if (oldval)
            *oldval = b->value;
        b->value = val;
        return 0;
    }
    /* 0.75 fill factor */
    if (h->capa - (h->capa / 4) < h->size + 1) {
        if (grow(h))
            return 1;
    }
    if (do_add(h->buckets, h->capa, key + 1, val))
        return 1;
    h->size++;
    return 0;
}

int hashmap_delete(struct hashmap *h, CK_ULONG key, union hashmap_value *val)
{
    int retval = 0;
    unsigned int hval;
    struct hashmap_bucket *b, **indirect;
    
    if (h->buckets) {
        hval = hash(h->capa, key + 1);
        if (h->buckets[hval].key == key + 1) {
            if (val)
                *val = h->buckets[hval].value;
            if ((b = h->buckets[hval].next) != NULL) {
                h->buckets[hval].key = b->key;
                h->buckets[hval].value = b->value;
                h->buckets[hval].next = b->next;
                free(b);
            } else {
                h->buckets[hval].key = 0;
            }
            retval = 1;
        } else {
            b = h->buckets[hval].next;
            indirect = &h->buckets[hval].next;
            while (b && b->key != key + 1) {
                indirect = &b->next;
                b = b->next;
            }
            if (b) {
                if (val)
                    *val = b->value;
                *indirect = b->next;
                free(b);
                retval = 1;
            }
        }
    }
    h->size -= retval;
    return retval;
}