tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

plhash.c (11652B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 /*
      7 * PL hash table package.
      8 */
      9 #include "plhash.h"
     10 #include "prbit.h"
     11 #include "prlog.h"
     12 #include "prmem.h"
     13 #include "prtypes.h"
     14 #include <stdlib.h>
     15 #include <string.h>
     16 
     17 /* Compute the number of buckets in ht */
     18 #define NBUCKETS(ht) (1 << (PL_HASH_BITS - (ht)->shift))
     19 
     20 /* The smallest table has 16 buckets */
     21 #define MINBUCKETSLOG2 4
     22 #define MINBUCKETS (1 << MINBUCKETSLOG2)
     23 
     24 /* Compute the maximum entries given n buckets that we will tolerate, ~90% */
     25 #define OVERLOADED(n) ((n) - ((n) >> 3))
     26 
     27 /* Compute the number of entries below which we shrink the table by half */
     28 #define UNDERLOADED(n) (((n) > MINBUCKETS) ? ((n) >> 2) : 0)
     29 
     30 /*
     31 ** Stubs for default hash allocator ops.
     32 */
     33 static void* PR_CALLBACK DefaultAllocTable(void* pool, PRSize size) {
     34  return PR_MALLOC(size);
     35 }
     36 
     37 static void PR_CALLBACK DefaultFreeTable(void* pool, void* item) {
     38  PR_Free(item);
     39 }
     40 
     41 static PLHashEntry* PR_CALLBACK DefaultAllocEntry(void* pool, const void* key) {
     42  return PR_NEW(PLHashEntry);
     43 }
     44 
     45 static void PR_CALLBACK DefaultFreeEntry(void* pool, PLHashEntry* he,
     46                                         PRUintn flag) {
     47  if (flag == HT_FREE_ENTRY) {
     48    PR_Free(he);
     49  }
     50 }
     51 
     52 static PLHashAllocOps defaultHashAllocOps = {
     53    DefaultAllocTable, DefaultFreeTable, DefaultAllocEntry, DefaultFreeEntry};
     54 
     55 PR_IMPLEMENT(PLHashTable*)
     56 PL_NewHashTable(PRUint32 n, PLHashFunction keyHash, PLHashComparator keyCompare,
     57                PLHashComparator valueCompare, const PLHashAllocOps* allocOps,
     58                void* allocPriv) {
     59  PLHashTable* ht;
     60  PRSize nb;
     61 
     62  if (n <= MINBUCKETS) {
     63    n = MINBUCKETSLOG2;
     64  } else {
     65    n = PR_CeilingLog2(n);
     66    if ((PRInt32)n < 0) {
     67      return 0;
     68    }
     69  }
     70 
     71  if (!allocOps) {
     72    allocOps = &defaultHashAllocOps;
     73  }
     74 
     75  ht = (PLHashTable*)((*allocOps->allocTable)(allocPriv, sizeof *ht));
     76  if (!ht) {
     77    return 0;
     78  }
     79  memset(ht, 0, sizeof *ht);
     80  ht->shift = PL_HASH_BITS - n;
     81  n = 1 << n;
     82  nb = n * sizeof(PLHashEntry*);
     83  ht->buckets = (PLHashEntry**)((*allocOps->allocTable)(allocPriv, nb));
     84  if (!ht->buckets) {
     85    (*allocOps->freeTable)(allocPriv, ht);
     86    return 0;
     87  }
     88  memset(ht->buckets, 0, nb);
     89 
     90  ht->keyHash = keyHash;
     91  ht->keyCompare = keyCompare;
     92  ht->valueCompare = valueCompare;
     93  ht->allocOps = allocOps;
     94  ht->allocPriv = allocPriv;
     95  return ht;
     96 }
     97 
     98 PR_IMPLEMENT(void)
     99 PL_HashTableDestroy(PLHashTable* ht) {
    100  PRUint32 i, n;
    101  PLHashEntry *he, *next;
    102  const PLHashAllocOps* allocOps = ht->allocOps;
    103  void* allocPriv = ht->allocPriv;
    104 
    105  n = NBUCKETS(ht);
    106  for (i = 0; i < n; i++) {
    107    for (he = ht->buckets[i]; he; he = next) {
    108      next = he->next;
    109      (*allocOps->freeEntry)(allocPriv, he, HT_FREE_ENTRY);
    110    }
    111  }
    112 #ifdef DEBUG
    113  memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]);
    114 #endif
    115  (*allocOps->freeTable)(allocPriv, ht->buckets);
    116 #ifdef DEBUG
    117  memset(ht, 0xDB, sizeof *ht);
    118 #endif
    119  (*allocOps->freeTable)(allocPriv, ht);
    120 }
    121 
    122 /*
    123 ** Multiplicative hash, from Knuth 6.4.
    124 */
    125 #define GOLDEN_RATIO 0x9E3779B9U /* 2/(1+sqrt(5))*(2^32) */
    126 
    127 PR_IMPLEMENT(PLHashEntry**)
    128 PL_HashTableRawLookup(PLHashTable* ht, PLHashNumber keyHash, const void* key) {
    129  PLHashEntry *he, **hep, **hep0;
    130  PLHashNumber h;
    131 
    132 #ifdef HASHMETER
    133  ht->nlookups++;
    134 #endif
    135  h = keyHash * GOLDEN_RATIO;
    136  h >>= ht->shift;
    137  hep = hep0 = &ht->buckets[h];
    138  while ((he = *hep) != 0) {
    139    if (he->keyHash == keyHash && (*ht->keyCompare)(key, he->key)) {
    140      /* Move to front of chain if not already there */
    141      if (hep != hep0) {
    142        *hep = he->next;
    143        he->next = *hep0;
    144        *hep0 = he;
    145      }
    146      return hep0;
    147    }
    148    hep = &he->next;
    149 #ifdef HASHMETER
    150    ht->nsteps++;
    151 #endif
    152  }
    153  return hep;
    154 }
    155 
    156 /*
    157 ** Same as PL_HashTableRawLookup but doesn't reorder the hash entries.
    158 */
    159 PR_IMPLEMENT(PLHashEntry**)
    160 PL_HashTableRawLookupConst(PLHashTable* ht, PLHashNumber keyHash,
    161                           const void* key) {
    162  PLHashEntry *he, **hep;
    163  PLHashNumber h;
    164 
    165 #ifdef HASHMETER
    166  ht->nlookups++;
    167 #endif
    168  h = keyHash * GOLDEN_RATIO;
    169  h >>= ht->shift;
    170  hep = &ht->buckets[h];
    171  while ((he = *hep) != 0) {
    172    if (he->keyHash == keyHash && (*ht->keyCompare)(key, he->key)) {
    173      break;
    174    }
    175    hep = &he->next;
    176 #ifdef HASHMETER
    177    ht->nsteps++;
    178 #endif
    179  }
    180  return hep;
    181 }
    182 
    183 PR_IMPLEMENT(PLHashEntry*)
    184 PL_HashTableRawAdd(PLHashTable* ht, PLHashEntry** hep, PLHashNumber keyHash,
    185                   const void* key, void* value) {
    186  PRUint32 i, n;
    187  PLHashEntry *he, *next, **oldbuckets;
    188  PRSize nb;
    189 
    190  /* Grow the table if it is overloaded */
    191  n = NBUCKETS(ht);
    192  if (ht->nentries >= OVERLOADED(n)) {
    193    oldbuckets = ht->buckets;
    194    nb = 2 * n * sizeof(PLHashEntry*);
    195    ht->buckets =
    196        (PLHashEntry**)((*ht->allocOps->allocTable)(ht->allocPriv, nb));
    197    if (!ht->buckets) {
    198      ht->buckets = oldbuckets;
    199      return 0;
    200    }
    201    memset(ht->buckets, 0, nb);
    202 #ifdef HASHMETER
    203    ht->ngrows++;
    204 #endif
    205    ht->shift--;
    206 
    207    for (i = 0; i < n; i++) {
    208      for (he = oldbuckets[i]; he; he = next) {
    209        next = he->next;
    210        hep = PL_HashTableRawLookup(ht, he->keyHash, he->key);
    211        PR_ASSERT(*hep == 0);
    212        he->next = 0;
    213        *hep = he;
    214      }
    215    }
    216 #ifdef DEBUG
    217    memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]);
    218 #endif
    219    (*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets);
    220    hep = PL_HashTableRawLookup(ht, keyHash, key);
    221  }
    222 
    223  /* Make a new key value entry */
    224  he = (*ht->allocOps->allocEntry)(ht->allocPriv, key);
    225  if (!he) {
    226    return 0;
    227  }
    228  he->keyHash = keyHash;
    229  he->key = key;
    230  he->value = value;
    231  he->next = *hep;
    232  *hep = he;
    233  ht->nentries++;
    234  return he;
    235 }
    236 
    237 PR_IMPLEMENT(PLHashEntry*)
    238 PL_HashTableAdd(PLHashTable* ht, const void* key, void* value) {
    239  PLHashNumber keyHash;
    240  PLHashEntry *he, **hep;
    241 
    242  keyHash = (*ht->keyHash)(key);
    243  hep = PL_HashTableRawLookup(ht, keyHash, key);
    244  if ((he = *hep) != 0) {
    245    /* Hit; see if values match */
    246    if ((*ht->valueCompare)(he->value, value)) {
    247      /* key,value pair is already present in table */
    248      return he;
    249    }
    250    if (he->value) {
    251      (*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_VALUE);
    252    }
    253    he->value = value;
    254    return he;
    255  }
    256  return PL_HashTableRawAdd(ht, hep, keyHash, key, value);
    257 }
    258 
    259 PR_IMPLEMENT(void)
    260 PL_HashTableRawRemove(PLHashTable* ht, PLHashEntry** hep, PLHashEntry* he) {
    261  PRUint32 i, n;
    262  PLHashEntry *next, **oldbuckets;
    263  PRSize nb;
    264 
    265  *hep = he->next;
    266  (*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_ENTRY);
    267 
    268  /* Shrink table if it's underloaded */
    269  n = NBUCKETS(ht);
    270  if (--ht->nentries < UNDERLOADED(n)) {
    271    oldbuckets = ht->buckets;
    272    nb = n * sizeof(PLHashEntry*) / 2;
    273    ht->buckets =
    274        (PLHashEntry**)((*ht->allocOps->allocTable)(ht->allocPriv, nb));
    275    if (!ht->buckets) {
    276      ht->buckets = oldbuckets;
    277      return;
    278    }
    279    memset(ht->buckets, 0, nb);
    280 #ifdef HASHMETER
    281    ht->nshrinks++;
    282 #endif
    283    ht->shift++;
    284 
    285    for (i = 0; i < n; i++) {
    286      for (he = oldbuckets[i]; he; he = next) {
    287        next = he->next;
    288        hep = PL_HashTableRawLookup(ht, he->keyHash, he->key);
    289        PR_ASSERT(*hep == 0);
    290        he->next = 0;
    291        *hep = he;
    292      }
    293    }
    294 #ifdef DEBUG
    295    memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]);
    296 #endif
    297    (*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets);
    298  }
    299 }
    300 
    301 PR_IMPLEMENT(PRBool)
    302 PL_HashTableRemove(PLHashTable* ht, const void* key) {
    303  PLHashNumber keyHash;
    304  PLHashEntry *he, **hep;
    305 
    306  keyHash = (*ht->keyHash)(key);
    307  hep = PL_HashTableRawLookup(ht, keyHash, key);
    308  if ((he = *hep) == 0) {
    309    return PR_FALSE;
    310  }
    311 
    312  /* Hit; remove element */
    313  PL_HashTableRawRemove(ht, hep, he);
    314  return PR_TRUE;
    315 }
    316 
    317 PR_IMPLEMENT(void*)
    318 PL_HashTableLookup(PLHashTable* ht, const void* key) {
    319  PLHashNumber keyHash;
    320  PLHashEntry *he, **hep;
    321 
    322  keyHash = (*ht->keyHash)(key);
    323  hep = PL_HashTableRawLookup(ht, keyHash, key);
    324  if ((he = *hep) != 0) {
    325    return he->value;
    326  }
    327  return 0;
    328 }
    329 
    330 /*
    331 ** Same as PL_HashTableLookup but doesn't reorder the hash entries.
    332 */
    333 PR_IMPLEMENT(void*)
    334 PL_HashTableLookupConst(PLHashTable* ht, const void* key) {
    335  PLHashNumber keyHash;
    336  PLHashEntry *he, **hep;
    337 
    338  keyHash = (*ht->keyHash)(key);
    339  hep = PL_HashTableRawLookupConst(ht, keyHash, key);
    340  if ((he = *hep) != 0) {
    341    return he->value;
    342  }
    343  return 0;
    344 }
    345 
    346 /*
    347 ** Iterate over the entries in the hash table calling func for each
    348 ** entry found. Stop if "f" says to (return value & PR_ENUMERATE_STOP).
    349 ** Return a count of the number of elements scanned.
    350 */
    351 PR_IMPLEMENT(int)
    352 PL_HashTableEnumerateEntries(PLHashTable* ht, PLHashEnumerator f, void* arg) {
    353  PLHashEntry *he, **hep;
    354  PRUint32 i, nbuckets;
    355  int rv, n = 0;
    356  PLHashEntry* todo = 0;
    357 
    358  nbuckets = NBUCKETS(ht);
    359  for (i = 0; i < nbuckets; i++) {
    360    hep = &ht->buckets[i];
    361    while ((he = *hep) != 0) {
    362      rv = (*f)(he, n, arg);
    363      n++;
    364      if (rv & (HT_ENUMERATE_REMOVE | HT_ENUMERATE_UNHASH)) {
    365        *hep = he->next;
    366        if (rv & HT_ENUMERATE_REMOVE) {
    367          he->next = todo;
    368          todo = he;
    369        }
    370      } else {
    371        hep = &he->next;
    372      }
    373      if (rv & HT_ENUMERATE_STOP) {
    374        goto out;
    375      }
    376    }
    377  }
    378 
    379 out:
    380  hep = &todo;
    381  while ((he = *hep) != 0) {
    382    PL_HashTableRawRemove(ht, hep, he);
    383  }
    384  return n;
    385 }
    386 
    387 #ifdef HASHMETER
    388 #  include <math.h>
    389 #  include <stdio.h>
    390 
    391 PR_IMPLEMENT(void)
    392 PL_HashTableDumpMeter(PLHashTable* ht, PLHashEnumerator dump, FILE* fp) {
    393  double mean, variance;
    394  PRUint32 nchains, nbuckets;
    395  PRUint32 i, n, maxChain, maxChainLen;
    396  PLHashEntry* he;
    397 
    398  variance = 0;
    399  nchains = 0;
    400  maxChainLen = 0;
    401  nbuckets = NBUCKETS(ht);
    402  for (i = 0; i < nbuckets; i++) {
    403    he = ht->buckets[i];
    404    if (!he) {
    405      continue;
    406    }
    407    nchains++;
    408    for (n = 0; he; he = he->next) {
    409      n++;
    410    }
    411    variance += n * n;
    412    if (n > maxChainLen) {
    413      maxChainLen = n;
    414      maxChain = i;
    415    }
    416  }
    417  mean = (double)ht->nentries / nchains;
    418  variance = fabs(variance / nchains - mean * mean);
    419 
    420  fprintf(fp, "\nHash table statistics:\n");
    421  fprintf(fp, "     number of lookups: %u\n", ht->nlookups);
    422  fprintf(fp, "     number of entries: %u\n", ht->nentries);
    423  fprintf(fp, "       number of grows: %u\n", ht->ngrows);
    424  fprintf(fp, "     number of shrinks: %u\n", ht->nshrinks);
    425  fprintf(fp, "   mean steps per hash: %g\n",
    426          (double)ht->nsteps / ht->nlookups);
    427  fprintf(fp, "mean hash chain length: %g\n", mean);
    428  fprintf(fp, "    standard deviation: %g\n", sqrt(variance));
    429  fprintf(fp, " max hash chain length: %u\n", maxChainLen);
    430  fprintf(fp, "        max hash chain: [%u]\n", maxChain);
    431 
    432  for (he = ht->buckets[maxChain], i = 0; he; he = he->next, i++)
    433    if ((*dump)(he, i, fp) != HT_ENUMERATE_NEXT) {
    434      break;
    435    }
    436 }
    437 #endif /* HASHMETER */
    438 
    439 PR_IMPLEMENT(int)
    440 PL_HashTableDump(PLHashTable* ht, PLHashEnumerator dump, FILE* fp) {
    441  int count;
    442 
    443  count = PL_HashTableEnumerateEntries(ht, dump, fp);
    444 #ifdef HASHMETER
    445  PL_HashTableDumpMeter(ht, dump, fp);
    446 #endif
    447  return count;
    448 }
    449 
    450 PR_IMPLEMENT(PLHashNumber)
    451 PL_HashString(const void* key) {
    452  PLHashNumber h;
    453  const PRUint8* s;
    454 
    455  h = 0;
    456  for (s = (const PRUint8*)key; *s; s++) {
    457    h = PR_ROTATE_LEFT32(h, 4) ^ *s;
    458  }
    459  return h;
    460 }
    461 
    462 PR_IMPLEMENT(int)
    463 PL_CompareStrings(const void* v1, const void* v2) {
    464  return strcmp((const char*)v1, (const char*)v2) == 0;
    465 }
    466 
    467 PR_IMPLEMENT(int)
    468 PL_CompareValues(const void* v1, const void* v2) { return v1 == v2; }