LCOV - code coverage report
Current view: directory - nsprpub/lib/ds - plhash.c (source / functions) Found Hit Coverage
Test: app.info Lines: 189 174 92.1 %
Date: 2012-04-21 Functions: 19 18 94.7 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is the Netscape Portable Runtime (NSPR).
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Netscape Communications Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 1998-2000
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *
      24                 :  * Alternatively, the contents of this file may be used under the terms of
      25                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      26                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      27                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      28                 :  * of those above. If you wish to allow use of your version of this file only
      29                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      30                 :  * use your version of this file under the terms of the MPL, indicate your
      31                 :  * decision by deleting the provisions above and replace them with the notice
      32                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      33                 :  * the provisions above, a recipient may use your version of this file under
      34                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      35                 :  *
      36                 :  * ***** END LICENSE BLOCK ***** */
      37                 : 
      38                 : /*
      39                 :  * PL hash table package.
      40                 :  */
      41                 : #include "plhash.h"
      42                 : #include "prbit.h"
      43                 : #include "prlog.h"
      44                 : #include "prmem.h"
      45                 : #include "prtypes.h"
      46                 : #include <stdlib.h>
      47                 : #include <string.h>
      48                 : 
      49                 : /* Compute the number of buckets in ht */
      50                 : #define NBUCKETS(ht)    (1 << (PL_HASH_BITS - (ht)->shift))
      51                 : 
      52                 : /* The smallest table has 16 buckets */
      53                 : #define MINBUCKETSLOG2  4
      54                 : #define MINBUCKETS      (1 << MINBUCKETSLOG2)
      55                 : 
      56                 : /* Compute the maximum entries given n buckets that we will tolerate, ~90% */
      57                 : #define OVERLOADED(n)   ((n) - ((n) >> 3))
      58                 : 
      59                 : /* Compute the number of entries below which we shrink the table by half */
      60                 : #define UNDERLOADED(n)  (((n) > MINBUCKETS) ? ((n) >> 2) : 0)
      61                 : 
      62                 : /*
      63                 : ** Stubs for default hash allocator ops.
      64                 : */
      65                 : static void * PR_CALLBACK
      66           22897 : DefaultAllocTable(void *pool, PRSize size)
      67                 : {
      68           22897 :     return PR_MALLOC(size);
      69                 : }
      70                 : 
      71                 : static void PR_CALLBACK
      72           22675 : DefaultFreeTable(void *pool, void *item)
      73                 : {
      74           22675 :     PR_Free(item);
      75           22675 : }
      76                 : 
      77                 : static PLHashEntry * PR_CALLBACK
      78          262676 : DefaultAllocEntry(void *pool, const void *key)
      79                 : {
      80          262676 :     return PR_NEW(PLHashEntry);
      81                 : }
      82                 : 
      83                 : static void PR_CALLBACK
      84          267285 : DefaultFreeEntry(void *pool, PLHashEntry *he, PRUintn flag)
      85                 : {
      86          267285 :     if (flag == HT_FREE_ENTRY)
      87          262615 :         PR_Free(he);
      88          267285 : }
      89                 : 
      90                 : static PLHashAllocOps defaultHashAllocOps = {
      91                 :     DefaultAllocTable, DefaultFreeTable,
      92                 :     DefaultAllocEntry, DefaultFreeEntry
      93                 : };
      94                 : 
      95                 : PR_IMPLEMENT(PLHashTable *)
      96           18903 : PL_NewHashTable(PRUint32 n, PLHashFunction keyHash,
      97                 :                 PLHashComparator keyCompare, PLHashComparator valueCompare,
      98                 :                 const PLHashAllocOps *allocOps, void *allocPriv)
      99                 : {
     100                 :     PLHashTable *ht;
     101                 :     PRSize nb;
     102                 : 
     103           18903 :     if (n <= MINBUCKETS) {
     104            8049 :         n = MINBUCKETSLOG2;
     105                 :     } else {
     106           10854 :         n = PR_CeilingLog2(n);
     107           10854 :         if ((PRInt32)n < 0)
     108               0 :             return 0;
     109                 :     }
     110                 : 
     111           18903 :     if (!allocOps) allocOps = &defaultHashAllocOps;
     112                 : 
     113           18903 :     ht = (PLHashTable*)((*allocOps->allocTable)(allocPriv, sizeof *ht));
     114           18903 :     if (!ht)
     115               0 :         return 0;
     116           18903 :     memset(ht, 0, sizeof *ht);
     117           18903 :     ht->shift = PL_HASH_BITS - n;
     118           18903 :     n = 1 << n;
     119           18903 :     nb = n * sizeof(PLHashEntry *);
     120           18903 :     ht->buckets = (PLHashEntry**)((*allocOps->allocTable)(allocPriv, nb));
     121           18903 :     if (!ht->buckets) {
     122               0 :         (*allocOps->freeTable)(allocPriv, ht);
     123               0 :         return 0;
     124                 :     }
     125           18903 :     memset(ht->buckets, 0, nb);
     126                 : 
     127           18903 :     ht->keyHash = keyHash;
     128           18903 :     ht->keyCompare = keyCompare;
     129           18903 :     ht->valueCompare = valueCompare;
     130           18903 :     ht->allocOps = allocOps;
     131           18903 :     ht->allocPriv = allocPriv;
     132           18903 :     return ht;
     133                 : }
     134                 : 
     135                 : PR_IMPLEMENT(void)
     136           18790 : PL_HashTableDestroy(PLHashTable *ht)
     137                 : {
     138                 :     PRUint32 i, n;
     139                 :     PLHashEntry *he, *next;
     140           18790 :     const PLHashAllocOps *allocOps = ht->allocOps;
     141           18790 :     void *allocPriv = ht->allocPriv;
     142                 : 
     143           18790 :     n = NBUCKETS(ht);
     144         1661830 :     for (i = 0; i < n; i++) {
     145         2358999 :         for (he = ht->buckets[i]; he; he = next) {
     146          715959 :             next = he->next;
     147          715959 :             (*allocOps->freeEntry)(allocPriv, he, HT_FREE_ENTRY);
     148                 :         }
     149                 :     }
     150                 : #ifdef DEBUG
     151           18790 :     memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]);
     152                 : #endif
     153           18790 :     (*allocOps->freeTable)(allocPriv, ht->buckets);
     154                 : #ifdef DEBUG
     155           18790 :     memset(ht, 0xDB, sizeof *ht);
     156                 : #endif
     157           18790 :     (*allocOps->freeTable)(allocPriv, ht);
     158           18790 : }
     159                 : 
     160                 : /*
     161                 : ** Multiplicative hash, from Knuth 6.4.
     162                 : */
     163                 : #define GOLDEN_RATIO    0x9E3779B9U  /* 2/(1+sqrt(5))*(2^32) */
     164                 : 
     165                 : PR_IMPLEMENT(PLHashEntry **)
     166       242870925 : PL_HashTableRawLookup(PLHashTable *ht, PLHashNumber keyHash, const void *key)
     167                 : {
     168                 :     PLHashEntry *he, **hep, **hep0;
     169                 :     PLHashNumber h;
     170                 : 
     171                 : #ifdef HASHMETER
     172                 :     ht->nlookups++;
     173                 : #endif
     174       242870925 :     h = keyHash * GOLDEN_RATIO;
     175       242870925 :     h >>= ht->shift;
     176       242870925 :     hep = hep0 = &ht->buckets[h];
     177       487673214 :     while ((he = *hep) != 0) {
     178       243094027 :         if (he->keyHash == keyHash && (*ht->keyCompare)(key, he->key)) {
     179                 :             /* Move to front of chain if not already there */
     180       241162663 :             if (hep != hep0) {
     181         1232276 :                 *hep = he->next;
     182         1232276 :                 he->next = *hep0;
     183         1232276 :                 *hep0 = he;
     184                 :             }
     185       241162663 :             return hep0;
     186                 :         }
     187         1931364 :         hep = &he->next;
     188                 : #ifdef HASHMETER
     189                 :         ht->nsteps++;
     190                 : #endif
     191                 :     }
     192         1708262 :     return hep;
     193                 : }
     194                 : 
     195                 : /*
     196                 : ** Same as PL_HashTableRawLookup but doesn't reorder the hash entries.
     197                 : */
     198                 : PR_IMPLEMENT(PLHashEntry **)
     199          409291 : PL_HashTableRawLookupConst(PLHashTable *ht, PLHashNumber keyHash,
     200                 :                            const void *key)
     201                 : {
     202                 :     PLHashEntry *he, **hep;
     203                 :     PLHashNumber h;
     204                 : 
     205                 : #ifdef HASHMETER
     206                 :     ht->nlookups++;
     207                 : #endif
     208          409291 :     h = keyHash * GOLDEN_RATIO;
     209          409291 :     h >>= ht->shift;
     210          409291 :     hep = &ht->buckets[h];
     211          845716 :     while ((he = *hep) != 0) {
     212          429993 :         if (he->keyHash == keyHash && (*ht->keyCompare)(key, he->key)) {
     213          402859 :             break;
     214                 :         }
     215           27134 :         hep = &he->next;
     216                 : #ifdef HASHMETER
     217                 :         ht->nsteps++;
     218                 : #endif
     219                 :     }
     220          409291 :     return hep;
     221                 : }
     222                 : 
     223                 : PR_IMPLEMENT(PLHashEntry *)
     224          740308 : PL_HashTableRawAdd(PLHashTable *ht, PLHashEntry **hep,
     225                 :                    PLHashNumber keyHash, const void *key, void *value)
     226                 : {
     227                 :     PRUint32 i, n;
     228                 :     PLHashEntry *he, *next, **oldbuckets;
     229                 :     PRSize nb;
     230                 : 
     231                 :     /* Grow the table if it is overloaded */
     232          740308 :     n = NBUCKETS(ht);
     233          740308 :     if (ht->nentries >= OVERLOADED(n)) {
     234            7321 :         oldbuckets = ht->buckets;
     235            7321 :         nb = 2 * n * sizeof(PLHashEntry *);
     236            7321 :         ht->buckets = (PLHashEntry**)
     237            7321 :             ((*ht->allocOps->allocTable)(ht->allocPriv, nb));
     238            7321 :         if (!ht->buckets) {
     239               0 :             ht->buckets = oldbuckets;
     240               0 :             return 0;
     241                 :         }
     242            7321 :         memset(ht->buckets, 0, nb);
     243                 : #ifdef HASHMETER
     244                 :         ht->ngrows++;
     245                 : #endif
     246            7321 :         ht->shift--;
     247                 : 
     248          720697 :         for (i = 0; i < n; i++) {
     249         1337580 :             for (he = oldbuckets[i]; he; he = next) {
     250          624204 :                 next = he->next;
     251          624204 :                 hep = PL_HashTableRawLookup(ht, he->keyHash, he->key);
     252          624204 :                 PR_ASSERT(*hep == 0);
     253          624204 :                 he->next = 0;
     254          624204 :                 *hep = he;
     255                 :             }
     256                 :         }
     257                 : #ifdef DEBUG
     258            7321 :         memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]);
     259                 : #endif
     260            7321 :         (*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets);
     261            7321 :         hep = PL_HashTableRawLookup(ht, keyHash, key);
     262                 :     }
     263                 : 
     264                 :     /* Make a new key value entry */
     265          740308 :     he = (*ht->allocOps->allocEntry)(ht->allocPriv, key);
     266          740308 :     if (!he)
     267               0 :         return 0;
     268          740308 :     he->keyHash = keyHash;
     269          740308 :     he->key = key;
     270          740308 :     he->value = value;
     271          740308 :     he->next = *hep;
     272          740308 :     *hep = he;
     273          740308 :     ht->nentries++;
     274          740308 :     return he;
     275                 : }
     276                 : 
     277                 : PR_IMPLEMENT(PLHashEntry *)
     278          744978 : PL_HashTableAdd(PLHashTable *ht, const void *key, void *value)
     279                 : {
     280                 :     PLHashNumber keyHash;
     281                 :     PLHashEntry *he, **hep;
     282                 : 
     283          744978 :     keyHash = (*ht->keyHash)(key);
     284          744978 :     hep = PL_HashTableRawLookup(ht, keyHash, key);
     285          744978 :     if ((he = *hep) != 0) {
     286                 :         /* Hit; see if values match */
     287            4670 :         if ((*ht->valueCompare)(he->value, value)) {
     288                 :             /* key,value pair is already present in table */
     289               0 :             return he;
     290                 :         }
     291            4670 :         if (he->value)
     292            4670 :             (*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_VALUE);
     293            4670 :         he->value = value;
     294            4670 :         return he;
     295                 :     }
     296          740308 :     return PL_HashTableRawAdd(ht, hep, keyHash, key, value);
     297                 : }
     298                 : 
     299                 : PR_IMPLEMENT(void)
     300           24280 : PL_HashTableRawRemove(PLHashTable *ht, PLHashEntry **hep, PLHashEntry *he)
     301                 : {
     302                 :     PRUint32 i, n;
     303                 :     PLHashEntry *next, **oldbuckets;
     304                 :     PRSize nb;
     305                 : 
     306           24280 :     *hep = he->next;
     307           24280 :     (*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_ENTRY);
     308                 : 
     309                 :     /* Shrink table if it's underloaded */
     310           24280 :     n = NBUCKETS(ht);
     311           24280 :     if (--ht->nentries < UNDERLOADED(n)) {
     312            2905 :         oldbuckets = ht->buckets;
     313            2905 :         nb = n * sizeof(PLHashEntry*) / 2;
     314            2905 :         ht->buckets = (PLHashEntry**)(
     315            2905 :             (*ht->allocOps->allocTable)(ht->allocPriv, nb));
     316            2905 :         if (!ht->buckets) {
     317               0 :             ht->buckets = oldbuckets;
     318               0 :             return;
     319                 :         }
     320            2905 :         memset(ht->buckets, 0, nb);
     321                 : #ifdef HASHMETER
     322                 :         ht->nshrinks++;
     323                 : #endif
     324            2905 :         ht->shift++;
     325                 : 
     326          141625 :         for (i = 0; i < n; i++) {
     327          146195 :             for (he = oldbuckets[i]; he; he = next) {
     328            7475 :                 next = he->next;
     329            7475 :                 hep = PL_HashTableRawLookup(ht, he->keyHash, he->key);
     330            7475 :                 PR_ASSERT(*hep == 0);
     331            7475 :                 he->next = 0;
     332            7475 :                 *hep = he;
     333                 :             }
     334                 :         }
     335                 : #ifdef DEBUG
     336            2905 :         memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]);
     337                 : #endif
     338            2905 :         (*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets);
     339                 :     }
     340                 : }
     341                 : 
     342                 : PR_IMPLEMENT(PRBool)
     343           23274 : PL_HashTableRemove(PLHashTable *ht, const void *key)
     344                 : {
     345                 :     PLHashNumber keyHash;
     346                 :     PLHashEntry *he, **hep;
     347                 : 
     348           23274 :     keyHash = (*ht->keyHash)(key);
     349           23274 :     hep = PL_HashTableRawLookup(ht, keyHash, key);
     350           23274 :     if ((he = *hep) == 0)
     351               0 :         return PR_FALSE;
     352                 : 
     353                 :     /* Hit; remove element */
     354           23274 :     PL_HashTableRawRemove(ht, hep, he);
     355           23274 :     return PR_TRUE;
     356                 : }
     357                 : 
     358                 : PR_IMPLEMENT(void *)
     359       212629618 : PL_HashTableLookup(PLHashTable *ht, const void *key)
     360                 : {
     361                 :     PLHashNumber keyHash;
     362                 :     PLHashEntry *he, **hep;
     363                 : 
     364       212629618 :     keyHash = (*ht->keyHash)(key);
     365       212629618 :     hep = PL_HashTableRawLookup(ht, keyHash, key);
     366       212629618 :     if ((he = *hep) != 0) {
     367       212300674 :         return he->value;
     368                 :     }
     369          328944 :     return 0;
     370                 : }
     371                 : 
     372                 : /*
     373                 : ** Same as PL_HashTableLookup but doesn't reorder the hash entries.
     374                 : */
     375                 : PR_IMPLEMENT(void *)
     376          409291 : PL_HashTableLookupConst(PLHashTable *ht, const void *key)
     377                 : {
     378                 :     PLHashNumber keyHash;
     379                 :     PLHashEntry *he, **hep;
     380                 : 
     381          409291 :     keyHash = (*ht->keyHash)(key);
     382          409291 :     hep = PL_HashTableRawLookupConst(ht, keyHash, key);
     383          409291 :     if ((he = *hep) != 0) {
     384          402859 :         return he->value;
     385                 :     }
     386            6432 :     return 0;
     387                 : }
     388                 : 
     389                 : /*
     390                 : ** Iterate over the entries in the hash table calling func for each
     391                 : ** entry found. Stop if "f" says to (return value & PR_ENUMERATE_STOP).
     392                 : ** Return a count of the number of elements scanned.
     393                 : */
     394                 : PR_IMPLEMENT(int)
     395            9262 : PL_HashTableEnumerateEntries(PLHashTable *ht, PLHashEnumerator f, void *arg)
     396                 : {
     397                 :     PLHashEntry *he, **hep;
     398                 :     PRUint32 i, nbuckets;
     399            9262 :     int rv, n = 0;
     400            9262 :     PLHashEntry *todo = 0;
     401                 : 
     402            9262 :     nbuckets = NBUCKETS(ht);
     403         1192814 :     for (i = 0; i < nbuckets; i++) {
     404         1183552 :         hep = &ht->buckets[i];
     405         2962998 :         while ((he = *hep) != 0) {
     406          595894 :             rv = (*f)(he, n, arg);
     407          595894 :             n++;
     408          595894 :             if (rv & (HT_ENUMERATE_REMOVE | HT_ENUMERATE_UNHASH)) {
     409             996 :                 *hep = he->next;
     410             996 :                 if (rv & HT_ENUMERATE_REMOVE) {
     411             996 :                     he->next = todo;
     412             996 :                     todo = he;
     413                 :                 }
     414                 :             } else {
     415          594898 :                 hep = &he->next;
     416                 :             }
     417          595894 :             if (rv & HT_ENUMERATE_STOP) {
     418               0 :                 goto out;
     419                 :             }
     420                 :         }
     421                 :     }
     422                 : 
     423                 : out:
     424            9262 :     hep = &todo;
     425           19520 :     while ((he = *hep) != 0) {
     426             996 :         PL_HashTableRawRemove(ht, hep, he);
     427                 :     }
     428            9262 :     return n;
     429                 : }
     430                 : 
     431                 : #ifdef HASHMETER
     432                 : #include <math.h>
     433                 : #include <stdio.h>
     434                 : 
     435                 : PR_IMPLEMENT(void)
     436                 : PL_HashTableDumpMeter(PLHashTable *ht, PLHashEnumerator dump, FILE *fp)
     437                 : {
     438                 :     double mean, variance;
     439                 :     PRUint32 nchains, nbuckets;
     440                 :     PRUint32 i, n, maxChain, maxChainLen;
     441                 :     PLHashEntry *he;
     442                 : 
     443                 :     variance = 0;
     444                 :     nchains = 0;
     445                 :     maxChainLen = 0;
     446                 :     nbuckets = NBUCKETS(ht);
     447                 :     for (i = 0; i < nbuckets; i++) {
     448                 :         he = ht->buckets[i];
     449                 :         if (!he)
     450                 :             continue;
     451                 :         nchains++;
     452                 :         for (n = 0; he; he = he->next)
     453                 :             n++;
     454                 :         variance += n * n;
     455                 :         if (n > maxChainLen) {
     456                 :             maxChainLen = n;
     457                 :             maxChain = i;
     458                 :         }
     459                 :     }
     460                 :     mean = (double)ht->nentries / nchains;
     461                 :     variance = fabs(variance / nchains - mean * mean);
     462                 : 
     463                 :     fprintf(fp, "\nHash table statistics:\n");
     464                 :     fprintf(fp, "     number of lookups: %u\n", ht->nlookups);
     465                 :     fprintf(fp, "     number of entries: %u\n", ht->nentries);
     466                 :     fprintf(fp, "       number of grows: %u\n", ht->ngrows);
     467                 :     fprintf(fp, "     number of shrinks: %u\n", ht->nshrinks);
     468                 :     fprintf(fp, "   mean steps per hash: %g\n", (double)ht->nsteps
     469                 :                                                 / ht->nlookups);
     470                 :     fprintf(fp, "mean hash chain length: %g\n", mean);
     471                 :     fprintf(fp, "    standard deviation: %g\n", sqrt(variance));
     472                 :     fprintf(fp, " max hash chain length: %u\n", maxChainLen);
     473                 :     fprintf(fp, "        max hash chain: [%u]\n", maxChain);
     474                 : 
     475                 :     for (he = ht->buckets[maxChain], i = 0; he; he = he->next, i++)
     476                 :         if ((*dump)(he, i, fp) != HT_ENUMERATE_NEXT)
     477                 :             break;
     478                 : }
     479                 : #endif /* HASHMETER */
     480                 : 
     481                 : PR_IMPLEMENT(int)
     482               0 : PL_HashTableDump(PLHashTable *ht, PLHashEnumerator dump, FILE *fp)
     483                 : {
     484                 :     int count;
     485                 : 
     486               0 :     count = PL_HashTableEnumerateEntries(ht, dump, fp);
     487                 : #ifdef HASHMETER
     488                 :     PL_HashTableDumpMeter(ht, dump, fp);
     489                 : #endif
     490               0 :     return count;
     491                 : }
     492                 : 
     493                 : PR_IMPLEMENT(PLHashNumber)
     494       212825471 : PL_HashString(const void *key)
     495                 : {
     496                 :     PLHashNumber h;
     497                 :     const PRUint8 *s;
     498                 : 
     499       212825471 :     h = 0;
     500     -1040983207 :     for (s = (const PRUint8*)key; *s; s++)
     501     -1253808678 :         h = PR_ROTATE_LEFT32(h, 4) ^ *s;
     502       212825471 :     return h;
     503                 : }
     504                 : 
     505                 : PR_IMPLEMENT(int)
     506       212230161 : PL_CompareStrings(const void *v1, const void *v2)
     507                 : {
     508       212230161 :     return strcmp((const char*)v1, (const char*)v2) == 0;
     509                 : }
     510                 : 
     511                 : PR_IMPLEMENT(int)
     512        29098803 : PL_CompareValues(const void *v1, const void *v2)
     513                 : {
     514        29098803 :     return v1 == v2;
     515                 : }

Generated by: LCOV version 1.7