~vaifrax/inkscape/bugfix170049

« back to all changes in this revision

Viewing changes to src/extension/script/js/jshash.c

  • Committer: mental
  • Date: 2006-01-16 02:36:01 UTC
  • Revision ID: mental@users.sourceforge.net-20060116023601-wkr0h7edl5veyudq
moving trunk for module inkscape

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 
2
 *
 
3
 * ***** BEGIN LICENSE BLOCK *****
 
4
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 
5
 *
 
6
 * The contents of this file are subject to the Mozilla Public License Version
 
7
 * 1.1 (the "License"); you may not use this file except in compliance with
 
8
 * the License. You may obtain a copy of the License at
 
9
 * http://www.mozilla.org/MPL/
 
10
 *
 
11
 * Software distributed under the License is distributed on an "AS IS" basis,
 
12
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 
13
 * for the specific language governing rights and limitations under the
 
14
 * License.
 
15
 *
 
16
 * The Original Code is Mozilla Communicator client code, released
 
17
 * March 31, 1998.
 
18
 *
 
19
 * The Initial Developer of the Original Code is
 
20
 * Netscape Communications Corporation.
 
21
 * Portions created by the Initial Developer are Copyright (C) 1998
 
22
 * the Initial Developer. All Rights Reserved.
 
23
 *
 
24
 * Contributor(s):
 
25
 *
 
26
 * Alternatively, the contents of this file may be used under the terms of
 
27
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 
28
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 
29
 * in which case the provisions of the GPL or the LGPL are applicable instead
 
30
 * of those above. If you wish to allow use of your version of this file only
 
31
 * under the terms of either the GPL or the LGPL, and not to allow others to
 
32
 * use your version of this file under the terms of the MPL, indicate your
 
33
 * decision by deleting the provisions above and replace them with the notice
 
34
 * and other provisions required by the GPL or the LGPL. If you do not delete
 
35
 * the provisions above, a recipient may use your version of this file under
 
36
 * the terms of any one of the MPL, the GPL or the LGPL.
 
37
 *
 
38
 * ***** END LICENSE BLOCK ***** */
 
39
 
 
40
/*
 
41
 * PR hash table package.
 
42
 */
 
43
#include "jsstddef.h"
 
44
#include <stdlib.h>
 
45
#include <string.h>
 
46
#include "jstypes.h"
 
47
#include "jsbit.h"
 
48
#include "jsutil.h" /* Added by JSIFY */
 
49
#include "jshash.h" /* Added by JSIFY */
 
50
 
 
51
/* Compute the number of buckets in ht */
 
52
#define NBUCKETS(ht)    JS_BIT(JS_HASH_BITS - (ht)->shift)
 
53
 
 
54
/* The smallest table has 16 buckets */
 
55
#define MINBUCKETSLOG2  4
 
56
#define MINBUCKETS      JS_BIT(MINBUCKETSLOG2)
 
57
 
 
58
/* Compute the maximum entries given n buckets that we will tolerate, ~90% */
 
59
#define OVERLOADED(n)   ((n) - ((n) >> 3))
 
60
 
 
61
/* Compute the number of entries below which we shrink the table by half */
 
62
#define UNDERLOADED(n)  (((n) > MINBUCKETS) ? ((n) >> 2) : 0)
 
63
 
 
64
/*
 
65
** Stubs for default hash allocator ops.
 
66
*/
 
67
static void *
 
68
DefaultAllocTable(void *pool, size_t size)
 
69
{
 
70
    return malloc(size);
 
71
}
 
72
 
 
73
static void
 
74
DefaultFreeTable(void *pool, void *item)
 
75
{
 
76
    free(item);
 
77
}
 
78
 
 
79
static JSHashEntry *
 
80
DefaultAllocEntry(void *pool, const void *key)
 
81
{
 
82
    return (JSHashEntry*) malloc(sizeof(JSHashEntry));
 
83
}
 
84
 
 
85
static void
 
86
DefaultFreeEntry(void *pool, JSHashEntry *he, uintN flag)
 
87
{
 
88
    if (flag == HT_FREE_ENTRY)
 
89
        free(he);
 
90
}
 
91
 
 
92
static JSHashAllocOps defaultHashAllocOps = {
 
93
    DefaultAllocTable, DefaultFreeTable,
 
94
    DefaultAllocEntry, DefaultFreeEntry
 
95
};
 
96
 
 
97
JS_PUBLIC_API(JSHashTable *)
 
98
JS_NewHashTable(uint32 n, JSHashFunction keyHash,
 
99
                JSHashComparator keyCompare, JSHashComparator valueCompare,
 
100
                JSHashAllocOps *allocOps, void *allocPriv)
 
101
{
 
102
    JSHashTable *ht;
 
103
    size_t nb;
 
104
 
 
105
    if (n <= MINBUCKETS) {
 
106
        n = MINBUCKETSLOG2;
 
107
    } else {
 
108
        n = JS_CeilingLog2(n);
 
109
        if ((int32)n < 0)
 
110
            return NULL;
 
111
    }
 
112
 
 
113
    if (!allocOps) allocOps = &defaultHashAllocOps;
 
114
 
 
115
    ht = (JSHashTable*) (*allocOps->allocTable)(allocPriv, sizeof *ht);
 
116
    if (!ht)
 
117
        return NULL;
 
118
    memset(ht, 0, sizeof *ht);
 
119
    ht->shift = JS_HASH_BITS - n;
 
120
    n = JS_BIT(n);
 
121
#if defined _MSC_VER && _MSC_VER <= 800
 
122
    if (n > 16000) {
 
123
        (*allocOps->freeTable)(allocPriv, ht);
 
124
        return NULL;
 
125
    }
 
126
#endif  /* WIN16 */
 
127
    nb = n * sizeof(JSHashEntry *);
 
128
    ht->buckets = (JSHashEntry**) (*allocOps->allocTable)(allocPriv, nb);
 
129
    if (!ht->buckets) {
 
130
        (*allocOps->freeTable)(allocPriv, ht);
 
131
        return NULL;
 
132
    }
 
133
    memset(ht->buckets, 0, nb);
 
134
 
 
135
    ht->keyHash = keyHash;
 
136
    ht->keyCompare = keyCompare;
 
137
    ht->valueCompare = valueCompare;
 
138
    ht->allocOps = allocOps;
 
139
    ht->allocPriv = allocPriv;
 
140
    return ht;
 
141
}
 
142
 
 
143
JS_PUBLIC_API(void)
 
144
JS_HashTableDestroy(JSHashTable *ht)
 
145
{
 
146
    uint32 i, n;
 
147
    JSHashEntry *he, **hep;
 
148
    JSHashAllocOps *allocOps = ht->allocOps;
 
149
    void *allocPriv = ht->allocPriv;
 
150
 
 
151
    n = NBUCKETS(ht);
 
152
    for (i = 0; i < n; i++) {
 
153
        hep = &ht->buckets[i];
 
154
        while ((he = *hep) != NULL) {
 
155
            *hep = he->next;
 
156
            (*allocOps->freeEntry)(allocPriv, he, HT_FREE_ENTRY);
 
157
        }
 
158
    }
 
159
#ifdef DEBUG
 
160
    memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]);
 
161
#endif
 
162
    (*allocOps->freeTable)(allocPriv, ht->buckets);
 
163
#ifdef DEBUG
 
164
    memset(ht, 0xDB, sizeof *ht);
 
165
#endif
 
166
    (*allocOps->freeTable)(allocPriv, ht);
 
167
}
 
168
 
 
169
/*
 
170
** Multiplicative hash, from Knuth 6.4.
 
171
*/
 
172
JS_PUBLIC_API(JSHashEntry **)
 
173
JS_HashTableRawLookup(JSHashTable *ht, JSHashNumber keyHash, const void *key)
 
174
{
 
175
    JSHashEntry *he, **hep, **hep0;
 
176
    JSHashNumber h;
 
177
 
 
178
#ifdef HASHMETER
 
179
    ht->nlookups++;
 
180
#endif
 
181
    h = keyHash * JS_GOLDEN_RATIO;
 
182
    h >>= ht->shift;
 
183
    hep = hep0 = &ht->buckets[h];
 
184
    while ((he = *hep) != NULL) {
 
185
        if (he->keyHash == keyHash && (*ht->keyCompare)(key, he->key)) {
 
186
            /* Move to front of chain if not already there */
 
187
            if (hep != hep0) {
 
188
                *hep = he->next;
 
189
                he->next = *hep0;
 
190
                *hep0 = he;
 
191
            }
 
192
            return hep0;
 
193
        }
 
194
        hep = &he->next;
 
195
#ifdef HASHMETER
 
196
        ht->nsteps++;
 
197
#endif
 
198
    }
 
199
    return hep;
 
200
}
 
201
 
 
202
JS_PUBLIC_API(JSHashEntry *)
 
203
JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **hep,
 
204
                   JSHashNumber keyHash, const void *key, void *value)
 
205
{
 
206
    uint32 i, n;
 
207
    JSHashEntry *he, *next, **oldbuckets;
 
208
    size_t nb;
 
209
 
 
210
    /* Grow the table if it is overloaded */
 
211
    n = NBUCKETS(ht);
 
212
    if (ht->nentries >= OVERLOADED(n)) {
 
213
        oldbuckets = ht->buckets;
 
214
#if defined _MSC_VER && _MSC_VER <= 800
 
215
        if (2 * n > 16000)
 
216
            return NULL;
 
217
#endif  /* WIN16 */
 
218
        nb = 2 * n * sizeof(JSHashEntry *);
 
219
        ht->buckets = (JSHashEntry**) (*ht->allocOps->allocTable)(ht->allocPriv, nb);
 
220
        if (!ht->buckets) {
 
221
            ht->buckets = oldbuckets;
 
222
            return NULL;
 
223
        }
 
224
        memset(ht->buckets, 0, nb);
 
225
#ifdef HASHMETER
 
226
        ht->ngrows++;
 
227
#endif
 
228
        ht->shift--;
 
229
 
 
230
        for (i = 0; i < n; i++) {
 
231
            for (he = oldbuckets[i]; he; he = next) {
 
232
                next = he->next;
 
233
                hep = JS_HashTableRawLookup(ht, he->keyHash, he->key);
 
234
                JS_ASSERT(*hep == NULL);
 
235
                he->next = NULL;
 
236
                *hep = he;
 
237
            }
 
238
        }
 
239
#ifdef DEBUG
 
240
        memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]);
 
241
#endif
 
242
        (*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets);
 
243
        hep = JS_HashTableRawLookup(ht, keyHash, key);
 
244
    }
 
245
 
 
246
    /* Make a new key value entry */
 
247
    he = (*ht->allocOps->allocEntry)(ht->allocPriv, key);
 
248
    if (!he)
 
249
        return NULL;
 
250
    he->keyHash = keyHash;
 
251
    he->key = key;
 
252
    he->value = value;
 
253
    he->next = *hep;
 
254
    *hep = he;
 
255
    ht->nentries++;
 
256
    return he;
 
257
}
 
258
 
 
259
JS_PUBLIC_API(JSHashEntry *)
 
260
JS_HashTableAdd(JSHashTable *ht, const void *key, void *value)
 
261
{
 
262
    JSHashNumber keyHash;
 
263
    JSHashEntry *he, **hep;
 
264
 
 
265
    keyHash = (*ht->keyHash)(key);
 
266
    hep = JS_HashTableRawLookup(ht, keyHash, key);
 
267
    if ((he = *hep) != NULL) {
 
268
        /* Hit; see if values match */
 
269
        if ((*ht->valueCompare)(he->value, value)) {
 
270
            /* key,value pair is already present in table */
 
271
            return he;
 
272
        }
 
273
        if (he->value)
 
274
            (*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_VALUE);
 
275
        he->value = value;
 
276
        return he;
 
277
    }
 
278
    return JS_HashTableRawAdd(ht, hep, keyHash, key, value);
 
279
}
 
280
 
 
281
JS_PUBLIC_API(void)
 
282
JS_HashTableRawRemove(JSHashTable *ht, JSHashEntry **hep, JSHashEntry *he)
 
283
{
 
284
    uint32 i, n;
 
285
    JSHashEntry *next, **oldbuckets;
 
286
    size_t nb;
 
287
 
 
288
    *hep = he->next;
 
289
    (*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_ENTRY);
 
290
 
 
291
    /* Shrink table if it's underloaded */
 
292
    n = NBUCKETS(ht);
 
293
    if (--ht->nentries < UNDERLOADED(n)) {
 
294
        oldbuckets = ht->buckets;
 
295
        nb = n * sizeof(JSHashEntry*) / 2;
 
296
        ht->buckets = (JSHashEntry**) (*ht->allocOps->allocTable)(ht->allocPriv, nb);
 
297
        if (!ht->buckets) {
 
298
            ht->buckets = oldbuckets;
 
299
            return;
 
300
        }
 
301
        memset(ht->buckets, 0, nb);
 
302
#ifdef HASHMETER
 
303
        ht->nshrinks++;
 
304
#endif
 
305
        ht->shift++;
 
306
 
 
307
        for (i = 0; i < n; i++) {
 
308
            for (he = oldbuckets[i]; he; he = next) {
 
309
                next = he->next;
 
310
                hep = JS_HashTableRawLookup(ht, he->keyHash, he->key);
 
311
                JS_ASSERT(*hep == NULL);
 
312
                he->next = NULL;
 
313
                *hep = he;
 
314
            }
 
315
        }
 
316
#ifdef DEBUG
 
317
        memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]);
 
318
#endif
 
319
        (*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets);
 
320
    }
 
321
}
 
322
 
 
323
JS_PUBLIC_API(JSBool)
 
324
JS_HashTableRemove(JSHashTable *ht, const void *key)
 
325
{
 
326
    JSHashNumber keyHash;
 
327
    JSHashEntry *he, **hep;
 
328
 
 
329
    keyHash = (*ht->keyHash)(key);
 
330
    hep = JS_HashTableRawLookup(ht, keyHash, key);
 
331
    if ((he = *hep) == NULL)
 
332
        return JS_FALSE;
 
333
 
 
334
    /* Hit; remove element */
 
335
    JS_HashTableRawRemove(ht, hep, he);
 
336
    return JS_TRUE;
 
337
}
 
338
 
 
339
JS_PUBLIC_API(void *)
 
340
JS_HashTableLookup(JSHashTable *ht, const void *key)
 
341
{
 
342
    JSHashNumber keyHash;
 
343
    JSHashEntry *he, **hep;
 
344
 
 
345
    keyHash = (*ht->keyHash)(key);
 
346
    hep = JS_HashTableRawLookup(ht, keyHash, key);
 
347
    if ((he = *hep) != NULL) {
 
348
        return he->value;
 
349
    }
 
350
    return NULL;
 
351
}
 
352
 
 
353
/*
 
354
** Iterate over the entries in the hash table calling func for each
 
355
** entry found. Stop if "f" says to (return value & JS_ENUMERATE_STOP).
 
356
** Return a count of the number of elements scanned.
 
357
*/
 
358
JS_PUBLIC_API(int)
 
359
JS_HashTableEnumerateEntries(JSHashTable *ht, JSHashEnumerator f, void *arg)
 
360
{
 
361
    JSHashEntry *he, **hep;
 
362
    uint32 i, nbuckets;
 
363
    int rv, n = 0;
 
364
    JSHashEntry *todo = NULL;
 
365
 
 
366
    nbuckets = NBUCKETS(ht);
 
367
    for (i = 0; i < nbuckets; i++) {
 
368
        hep = &ht->buckets[i];
 
369
        while ((he = *hep) != NULL) {
 
370
            rv = (*f)(he, n, arg);
 
371
            n++;
 
372
            if (rv & (HT_ENUMERATE_REMOVE | HT_ENUMERATE_UNHASH)) {
 
373
                *hep = he->next;
 
374
                if (rv & HT_ENUMERATE_REMOVE) {
 
375
                    he->next = todo;
 
376
                    todo = he;
 
377
                }
 
378
            } else {
 
379
                hep = &he->next;
 
380
            }
 
381
            if (rv & HT_ENUMERATE_STOP) {
 
382
                goto out;
 
383
            }
 
384
        }
 
385
    }
 
386
 
 
387
out:
 
388
    hep = &todo;
 
389
    while ((he = *hep) != NULL) {
 
390
        JS_HashTableRawRemove(ht, hep, he);
 
391
    }
 
392
    return n;
 
393
}
 
394
 
 
395
#ifdef HASHMETER
 
396
#include <math.h>
 
397
#include <stdio.h>
 
398
 
 
399
JS_PUBLIC_API(void)
 
400
JS_HashTableDumpMeter(JSHashTable *ht, JSHashEnumerator dump, FILE *fp)
 
401
{
 
402
    double sqsum, mean, variance, sigma;
 
403
    uint32 nchains, nbuckets, nentries;
 
404
    uint32 i, n, maxChain, maxChainLen;
 
405
    JSHashEntry *he;
 
406
 
 
407
    sqsum = 0;
 
408
    nchains = 0;
 
409
    maxChainLen = 0;
 
410
    nbuckets = NBUCKETS(ht);
 
411
    for (i = 0; i < nbuckets; i++) {
 
412
        he = ht->buckets[i];
 
413
        if (!he)
 
414
            continue;
 
415
        nchains++;
 
416
        for (n = 0; he; he = he->next)
 
417
            n++;
 
418
        sqsum += n * n;
 
419
        if (n > maxChainLen) {
 
420
            maxChainLen = n;
 
421
            maxChain = i;
 
422
        }
 
423
    }
 
424
    nentries = ht->nentries;
 
425
    mean = (double)nentries / nchains;
 
426
    variance = nchains * sqsum - nentries * nentries;
 
427
    if (variance < 0 || nchains == 1)
 
428
        variance = 0;
 
429
    else
 
430
        variance /= nchains * (nchains - 1);
 
431
    sigma = sqrt(variance);
 
432
 
 
433
    fprintf(fp, "\nHash table statistics:\n");
 
434
    fprintf(fp, "     number of lookups: %u\n", ht->nlookups);
 
435
    fprintf(fp, "     number of entries: %u\n", ht->nentries);
 
436
    fprintf(fp, "       number of grows: %u\n", ht->ngrows);
 
437
    fprintf(fp, "     number of shrinks: %u\n", ht->nshrinks);
 
438
    fprintf(fp, "   mean steps per hash: %g\n", (double)ht->nsteps
 
439
                                                / ht->nlookups);
 
440
    fprintf(fp, "mean hash chain length: %g\n", mean);
 
441
    fprintf(fp, "    standard deviation: %g\n", sigma);
 
442
    fprintf(fp, " max hash chain length: %u\n", maxChainLen);
 
443
    fprintf(fp, "        max hash chain: [%u]\n", maxChain);
 
444
 
 
445
    for (he = ht->buckets[maxChain], i = 0; he; he = he->next, i++)
 
446
        if ((*dump)(he, i, fp) != HT_ENUMERATE_NEXT)
 
447
            break;
 
448
}
 
449
#endif /* HASHMETER */
 
450
 
 
451
JS_PUBLIC_API(int)
 
452
JS_HashTableDump(JSHashTable *ht, JSHashEnumerator dump, FILE *fp)
 
453
{
 
454
    int count;
 
455
 
 
456
    count = JS_HashTableEnumerateEntries(ht, dump, fp);
 
457
#ifdef HASHMETER
 
458
    JS_HashTableDumpMeter(ht, dump, fp);
 
459
#endif
 
460
    return count;
 
461
}
 
462
 
 
463
JS_PUBLIC_API(JSHashNumber)
 
464
JS_HashString(const void *key)
 
465
{
 
466
    JSHashNumber h;
 
467
    const unsigned char *s;
 
468
 
 
469
    h = 0;
 
470
    for (s = (const unsigned char *)key; *s; s++)
 
471
        h = (h >> (JS_HASH_BITS - 4)) ^ (h << 4) ^ *s;
 
472
    return h;
 
473
}
 
474
 
 
475
JS_PUBLIC_API(int)
 
476
JS_CompareValues(const void *v1, const void *v2)
 
477
{
 
478
    return v1 == v2;
 
479
}