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-06-02 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           23023 : DefaultAllocTable(void *pool, PRSize size)
      67                 : {
      68           23023 :     return PR_MALLOC(size);
      69                 : }
      70                 : 
      71                 : static void PR_CALLBACK
      72           22801 : DefaultFreeTable(void *pool, void *item)
      73                 : {
      74           22801 :     PR_Free(item);
      75           22801 : }
      76                 : 
      77                 : static PLHashEntry * PR_CALLBACK
      78          263909 : DefaultAllocEntry(void *pool, const void *key)
      79                 : {
      80          263909 :     return PR_NEW(PLHashEntry);
      81                 : }
      82                 : 
      83                 : static void PR_CALLBACK
      84          268528 : DefaultFreeEntry(void *pool, PLHashEntry *he, PRUintn flag)
      85                 : {
      86          268528 :     if (flag == HT_FREE_ENTRY)
      87          263848 :         PR_Free(he);
      88          268528 : }
      89                 : 
      90                 : static PLHashAllocOps defaultHashAllocOps = {
      91                 :     DefaultAllocTable, DefaultFreeTable,
      92                 :     DefaultAllocEntry, DefaultFreeEntry
      93                 : };
      94                 : 
      95                 : PR_IMPLEMENT(PLHashTable *)
      96           19085 : 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           19085 :     if (n <= MINBUCKETS) {
     104            8069 :         n = MINBUCKETSLOG2;
     105                 :     } else {
     106           11016 :         n = PR_CeilingLog2(n);
     107           11016 :         if ((PRInt32)n < 0)
     108               0 :             return 0;
     109                 :     }
     110                 : 
     111           19085 :     if (!allocOps) allocOps = &defaultHashAllocOps;
     112                 : 
     113           19085 :     ht = (PLHashTable*)((*allocOps->allocTable)(allocPriv, sizeof *ht));
     114           19085 :     if (!ht)
     115               0 :         return 0;
     116           19085 :     memset(ht, 0, sizeof *ht);
     117           19085 :     ht->shift = PL_HASH_BITS - n;
     118           19085 :     n = 1 << n;
     119           19085 :     nb = n * sizeof(PLHashEntry *);
     120           19085 :     ht->buckets = (PLHashEntry**)((*allocOps->allocTable)(allocPriv, nb));
     121           19085 :     if (!ht->buckets) {
     122               0 :         (*allocOps->freeTable)(allocPriv, ht);
     123               0 :         return 0;
     124                 :     }
     125           19085 :     memset(ht->buckets, 0, nb);
     126                 : 
     127           19085 :     ht->keyHash = keyHash;
     128           19085 :     ht->keyCompare = keyCompare;
     129           19085 :     ht->valueCompare = valueCompare;
     130           19085 :     ht->allocOps = allocOps;
     131           19085 :     ht->allocPriv = allocPriv;
     132           19085 :     return ht;
     133                 : }
     134                 : 
     135                 : PR_IMPLEMENT(void)
     136           18967 : PL_HashTableDestroy(PLHashTable *ht)
     137                 : {
     138                 :     PRUint32 i, n;
     139                 :     PLHashEntry *he, *next;
     140           18967 :     const PLHashAllocOps *allocOps = ht->allocOps;
     141           18967 :     void *allocPriv = ht->allocPriv;
     142                 : 
     143           18967 :     n = NBUCKETS(ht);
     144         1679655 :     for (i = 0; i < n; i++) {
     145         2384794 :         for (he = ht->buckets[i]; he; he = next) {
     146          724106 :             next = he->next;
     147          724106 :             (*allocOps->freeEntry)(allocPriv, he, HT_FREE_ENTRY);
     148                 :         }
     149                 :     }
     150                 : #ifdef DEBUG
     151           18967 :     memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]);
     152                 : #endif
     153           18967 :     (*allocOps->freeTable)(allocPriv, ht->buckets);
     154                 : #ifdef DEBUG
     155           18967 :     memset(ht, 0xDB, sizeof *ht);
     156                 : #endif
     157           18967 :     (*allocOps->freeTable)(allocPriv, ht);
     158           18967 : }
     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       238823810 : 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       238823810 :     h = keyHash * GOLDEN_RATIO;
     175       238823810 :     h >>= ht->shift;
     176       238823810 :     hep = hep0 = &ht->buckets[h];
     177       479485423 :     while ((he = *hep) != 0) {
     178       238937017 :         if (he->keyHash == keyHash && (*ht->keyCompare)(key, he->key)) {
     179                 :             /* Move to front of chain if not already there */
     180       237098324 :             if (hep != hep0) {
     181         1117386 :                 *hep = he->next;
     182         1117386 :                 he->next = *hep0;
     183         1117386 :                 *hep0 = he;
     184                 :             }
     185       237098324 :             return hep0;
     186                 :         }
     187         1837803 :         hep = &he->next;
     188                 : #ifdef HASHMETER
     189                 :         ht->nsteps++;
     190                 : #endif
     191                 :     }
     192         1724596 :     return hep;
     193                 : }
     194                 : 
     195                 : /*
     196                 : ** Same as PL_HashTableRawLookup but doesn't reorder the hash entries.
     197                 : */
     198                 : PR_IMPLEMENT(PLHashEntry **)
     199          406316 : 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          406316 :     h = keyHash * GOLDEN_RATIO;
     209          406316 :     h >>= ht->shift;
     210          406316 :     hep = &ht->buckets[h];
     211          837918 :     while ((he = *hep) != 0) {
     212          425158 :         if (he->keyHash == keyHash && (*ht->keyCompare)(key, he->key)) {
     213          399872 :             break;
     214                 :         }
     215           25286 :         hep = &he->next;
     216                 : #ifdef HASHMETER
     217                 :         ht->nsteps++;
     218                 : #endif
     219                 :     }
     220          406316 :     return hep;
     221                 : }
     222                 : 
     223                 : PR_IMPLEMENT(PLHashEntry *)
     224          748607 : 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          748607 :     n = NBUCKETS(ht);
     233          748607 :     if (ht->nentries >= OVERLOADED(n)) {
     234            7400 :         oldbuckets = ht->buckets;
     235            7400 :         nb = 2 * n * sizeof(PLHashEntry *);
     236            7400 :         ht->buckets = (PLHashEntry**)
     237            7400 :             ((*ht->allocOps->allocTable)(ht->allocPriv, nb));
     238            7400 :         if (!ht->buckets) {
     239               0 :             ht->buckets = oldbuckets;
     240               0 :             return 0;
     241                 :         }
     242            7400 :         memset(ht->buckets, 0, nb);
     243                 : #ifdef HASHMETER
     244                 :         ht->ngrows++;
     245                 : #endif
     246            7400 :         ht->shift--;
     247                 : 
     248          726120 :         for (i = 0; i < n; i++) {
     249         1347600 :             for (he = oldbuckets[i]; he; he = next) {
     250          628880 :                 next = he->next;
     251          628880 :                 hep = PL_HashTableRawLookup(ht, he->keyHash, he->key);
     252          628880 :                 PR_ASSERT(*hep == 0);
     253          628880 :                 he->next = 0;
     254          628880 :                 *hep = he;
     255                 :             }
     256                 :         }
     257                 : #ifdef DEBUG
     258            7400 :         memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]);
     259                 : #endif
     260            7400 :         (*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets);
     261            7400 :         hep = PL_HashTableRawLookup(ht, keyHash, key);
     262                 :     }
     263                 : 
     264                 :     /* Make a new key value entry */
     265          748607 :     he = (*ht->allocOps->allocEntry)(ht->allocPriv, key);
     266          748607 :     if (!he)
     267               0 :         return 0;
     268          748607 :     he->keyHash = keyHash;
     269          748607 :     he->key = key;
     270          748607 :     he->value = value;
     271          748607 :     he->next = *hep;
     272          748607 :     *hep = he;
     273          748607 :     ht->nentries++;
     274          748607 :     return he;
     275                 : }
     276                 : 
     277                 : PR_IMPLEMENT(PLHashEntry *)
     278          753287 : PL_HashTableAdd(PLHashTable *ht, const void *key, void *value)
     279                 : {
     280                 :     PLHashNumber keyHash;
     281                 :     PLHashEntry *he, **hep;
     282                 : 
     283          753287 :     keyHash = (*ht->keyHash)(key);
     284          753287 :     hep = PL_HashTableRawLookup(ht, keyHash, key);
     285          753287 :     if ((he = *hep) != 0) {
     286                 :         /* Hit; see if values match */
     287            4680 :         if ((*ht->valueCompare)(he->value, value)) {
     288                 :             /* key,value pair is already present in table */
     289               0 :             return he;
     290                 :         }
     291            4680 :         if (he->value)
     292            4680 :             (*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_VALUE);
     293            4680 :         he->value = value;
     294            4680 :         return he;
     295                 :     }
     296          748607 :     return PL_HashTableRawAdd(ht, hep, keyHash, key, value);
     297                 : }
     298                 : 
     299                 : PR_IMPLEMENT(void)
     300           24416 : PL_HashTableRawRemove(PLHashTable *ht, PLHashEntry **hep, PLHashEntry *he)
     301                 : {
     302                 :     PRUint32 i, n;
     303                 :     PLHashEntry *next, **oldbuckets;
     304                 :     PRSize nb;
     305                 : 
     306           24416 :     *hep = he->next;
     307           24416 :     (*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_ENTRY);
     308                 : 
     309                 :     /* Shrink table if it's underloaded */
     310           24416 :     n = NBUCKETS(ht);
     311           24416 :     if (--ht->nentries < UNDERLOADED(n)) {
     312            2929 :         oldbuckets = ht->buckets;
     313            2929 :         nb = n * sizeof(PLHashEntry*) / 2;
     314            2929 :         ht->buckets = (PLHashEntry**)(
     315            2929 :             (*ht->allocOps->allocTable)(ht->allocPriv, nb));
     316            2929 :         if (!ht->buckets) {
     317               0 :             ht->buckets = oldbuckets;
     318               0 :             return;
     319                 :         }
     320            2929 :         memset(ht->buckets, 0, nb);
     321                 : #ifdef HASHMETER
     322                 :         ht->nshrinks++;
     323                 : #endif
     324            2929 :         ht->shift++;
     325                 : 
     326          142449 :         for (i = 0; i < n; i++) {
     327          147090 :             for (he = oldbuckets[i]; he; he = next) {
     328            7570 :                 next = he->next;
     329            7570 :                 hep = PL_HashTableRawLookup(ht, he->keyHash, he->key);
     330            7570 :                 PR_ASSERT(*hep == 0);
     331            7570 :                 he->next = 0;
     332            7570 :                 *hep = he;
     333                 :             }
     334                 :         }
     335                 : #ifdef DEBUG
     336            2929 :         memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]);
     337                 : #endif
     338            2929 :         (*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets);
     339                 :     }
     340                 : }
     341                 : 
     342                 : PR_IMPLEMENT(PRBool)
     343           23410 : PL_HashTableRemove(PLHashTable *ht, const void *key)
     344                 : {
     345                 :     PLHashNumber keyHash;
     346                 :     PLHashEntry *he, **hep;
     347                 : 
     348           23410 :     keyHash = (*ht->keyHash)(key);
     349           23410 :     hep = PL_HashTableRawLookup(ht, keyHash, key);
     350           23410 :     if ((he = *hep) == 0)
     351               0 :         return PR_FALSE;
     352                 : 
     353                 :     /* Hit; remove element */
     354           23410 :     PL_HashTableRawRemove(ht, hep, he);
     355           23410 :     return PR_TRUE;
     356                 : }
     357                 : 
     358                 : PR_IMPLEMENT(void *)
     359       207856821 : PL_HashTableLookup(PLHashTable *ht, const void *key)
     360                 : {
     361                 :     PLHashNumber keyHash;
     362                 :     PLHashEntry *he, **hep;
     363                 : 
     364       207856821 :     keyHash = (*ht->keyHash)(key);
     365       207856821 :     hep = PL_HashTableRawLookup(ht, keyHash, key);
     366       207856821 :     if ((he = *hep) != 0) {
     367       207524692 :         return he->value;
     368                 :     }
     369          332129 :     return 0;
     370                 : }
     371                 : 
     372                 : /*
     373                 : ** Same as PL_HashTableLookup but doesn't reorder the hash entries.
     374                 : */
     375                 : PR_IMPLEMENT(void *)
     376          406316 : PL_HashTableLookupConst(PLHashTable *ht, const void *key)
     377                 : {
     378                 :     PLHashNumber keyHash;
     379                 :     PLHashEntry *he, **hep;
     380                 : 
     381          406316 :     keyHash = (*ht->keyHash)(key);
     382          406316 :     hep = PL_HashTableRawLookupConst(ht, keyHash, key);
     383          406316 :     if ((he = *hep) != 0) {
     384          399872 :         return he->value;
     385                 :     }
     386            6444 :     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            9339 : PL_HashTableEnumerateEntries(PLHashTable *ht, PLHashEnumerator f, void *arg)
     396                 : {
     397                 :     PLHashEntry *he, **hep;
     398                 :     PRUint32 i, nbuckets;
     399            9339 :     int rv, n = 0;
     400            9339 :     PLHashEntry *todo = 0;
     401                 : 
     402            9339 :     nbuckets = NBUCKETS(ht);
     403         1203371 :     for (i = 0; i < nbuckets; i++) {
     404         1194032 :         hep = &ht->buckets[i];
     405         2990124 :         while ((he = *hep) != 0) {
     406          602060 :             rv = (*f)(he, n, arg);
     407          602060 :             n++;
     408          602060 :             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          601064 :                 hep = &he->next;
     416                 :             }
     417          602060 :             if (rv & HT_ENUMERATE_STOP) {
     418               0 :                 goto out;
     419                 :             }
     420                 :         }
     421                 :     }
     422                 : 
     423                 : out:
     424            9339 :     hep = &todo;
     425           19674 :     while ((he = *hep) != 0) {
     426             996 :         PL_HashTableRawRemove(ht, hep, he);
     427                 :     }
     428            9339 :     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       208055584 : PL_HashString(const void *key)
     495                 : {
     496                 :     PLHashNumber h;
     497                 :     const PRUint8 *s;
     498                 : 
     499       208055584 :     h = 0;
     500     -1113351470 :     for (s = (const PRUint8*)key; *s; s++)
     501     -1321407054 :         h = PR_ROTATE_LEFT32(h, 4) ^ *s;
     502       208055584 :     return h;
     503                 : }
     504                 : 
     505                 : PR_IMPLEMENT(int)
     506       207454161 : PL_CompareStrings(const void *v1, const void *v2)
     507                 : {
     508       207454161 :     return strcmp((const char*)v1, (const char*)v2) == 0;
     509                 : }
     510                 : 
     511                 : PR_IMPLEMENT(int)
     512        29809668 : PL_CompareValues(const void *v1, const void *v2)
     513                 : {
     514        29809668 :     return v1 == v2;
     515                 : }

Generated by: LCOV version 1.7