xpcom/glue/pldhash.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/xpcom/glue/pldhash.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,586 @@
     1.4 +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#ifndef pldhash_h___
    1.10 +#define pldhash_h___
    1.11 +/*
    1.12 + * Double hashing, a la Knuth 6.
    1.13 + */
    1.14 +#include "mozilla/fallible.h"
    1.15 +#include "mozilla/MemoryReporting.h"
    1.16 +#include "mozilla/Types.h"
    1.17 +#include "nscore.h"
    1.18 +
    1.19 +#if defined(__GNUC__) && defined(__i386__)
    1.20 +#define PL_DHASH_FASTCALL __attribute__ ((regparm (3),stdcall))
    1.21 +#elif defined(XP_WIN)
    1.22 +#define PL_DHASH_FASTCALL __fastcall
    1.23 +#else
    1.24 +#define PL_DHASH_FASTCALL
    1.25 +#endif
    1.26 +
    1.27 +/*
    1.28 + * Table size limit; do not exceed.  The max capacity used to be 1<<23 but that
    1.29 + * occasionally that wasn't enough.  Making it much bigger than 1<<26 probably
    1.30 + * isn't worthwhile -- tables that big are kind of ridiculous.  Also, the
    1.31 + * growth operation will (deliberately) fail if |capacity * entrySize|
    1.32 + * overflows a uint32_t, and entrySize is always at least 8 bytes.
    1.33 + */
    1.34 +#undef PL_DHASH_MAX_SIZE
    1.35 +#define PL_DHASH_MAX_SIZE     ((uint32_t)1 << 26)
    1.36 +
    1.37 +/* Minimum table size, or gross entry count (net is at most .75 loaded). */
    1.38 +#ifndef PL_DHASH_MIN_SIZE
    1.39 +#define PL_DHASH_MIN_SIZE 16
    1.40 +#elif (PL_DHASH_MIN_SIZE & (PL_DHASH_MIN_SIZE - 1)) != 0
    1.41 +#error "PL_DHASH_MIN_SIZE must be a power of two!"
    1.42 +#endif
    1.43 +
    1.44 +/*
    1.45 + * Multiplicative hash uses an unsigned 32 bit integer and the golden ratio,
    1.46 + * expressed as a fixed-point 32-bit fraction.
    1.47 + */
    1.48 +#define PL_DHASH_BITS           32
    1.49 +#define PL_DHASH_GOLDEN_RATIO   0x9E3779B9U
    1.50 +
    1.51 +/* Primitive and forward-struct typedefs. */
    1.52 +typedef uint32_t                PLDHashNumber;
    1.53 +typedef struct PLDHashEntryHdr  PLDHashEntryHdr;
    1.54 +typedef struct PLDHashEntryStub PLDHashEntryStub;
    1.55 +typedef struct PLDHashTable     PLDHashTable;
    1.56 +typedef struct PLDHashTableOps  PLDHashTableOps;
    1.57 +
    1.58 +/*
    1.59 + * Table entry header structure.
    1.60 + *
    1.61 + * In order to allow in-line allocation of key and value, we do not declare
    1.62 + * either here.  Instead, the API uses const void *key as a formal parameter.
    1.63 + * The key need not be stored in the entry; it may be part of the value, but
    1.64 + * need not be stored at all.
    1.65 + *
    1.66 + * Callback types are defined below and grouped into the PLDHashTableOps
    1.67 + * structure, for single static initialization per hash table sub-type.
    1.68 + *
    1.69 + * Each hash table sub-type should nest the PLDHashEntryHdr structure at the
    1.70 + * front of its particular entry type.  The keyHash member contains the result
    1.71 + * of multiplying the hash code returned from the hashKey callback (see below)
    1.72 + * by PL_DHASH_GOLDEN_RATIO, then constraining the result to avoid the magic 0
    1.73 + * and 1 values.  The stored keyHash value is table size invariant, and it is
    1.74 + * maintained automatically by PL_DHashTableOperate -- users should never set
    1.75 + * it, and its only uses should be via the entry macros below.
    1.76 + *
    1.77 + * The PL_DHASH_ENTRY_IS_LIVE function tests whether entry is neither free nor
    1.78 + * removed.  An entry may be either busy or free; if busy, it may be live or
    1.79 + * removed.  Consumers of this API should not access members of entries that
    1.80 + * are not live.
    1.81 + *
    1.82 + * However, use PL_DHASH_ENTRY_IS_BUSY for faster liveness testing of entries
    1.83 + * returned by PL_DHashTableOperate, as PL_DHashTableOperate never returns a
    1.84 + * non-live, busy (i.e., removed) entry pointer to its caller.  See below for
    1.85 + * more details on PL_DHashTableOperate's calling rules.
    1.86 + */
    1.87 +struct PLDHashEntryHdr {
    1.88 +    PLDHashNumber       keyHash;        /* every entry must begin like this */
    1.89 +};
    1.90 +
    1.91 +MOZ_ALWAYS_INLINE bool
    1.92 +PL_DHASH_ENTRY_IS_FREE(PLDHashEntryHdr* entry)
    1.93 +{
    1.94 +    return entry->keyHash == 0;
    1.95 +}
    1.96 +
    1.97 +MOZ_ALWAYS_INLINE bool
    1.98 +PL_DHASH_ENTRY_IS_BUSY(PLDHashEntryHdr* entry)
    1.99 +{
   1.100 +    return !PL_DHASH_ENTRY_IS_FREE(entry);
   1.101 +}
   1.102 +
   1.103 +MOZ_ALWAYS_INLINE bool
   1.104 +PL_DHASH_ENTRY_IS_LIVE(PLDHashEntryHdr* entry)
   1.105 +{
   1.106 +    return entry->keyHash >= 2;
   1.107 +}
   1.108 +
   1.109 +/*
   1.110 + * A PLDHashTable is currently 8 words (without the PL_DHASHMETER overhead)
   1.111 + * on most architectures, and may be allocated on the stack or within another
   1.112 + * structure or class (see below for the Init and Finish functions to use).
   1.113 + *
   1.114 + * To decide whether to use double hashing vs. chaining, we need to develop a
   1.115 + * trade-off relation, as follows:
   1.116 + *
   1.117 + * Let alpha be the load factor, esize the entry size in words, count the
   1.118 + * entry count, and pow2 the power-of-two table size in entries.
   1.119 + *
   1.120 + *   (PLDHashTable overhead)    > (PLHashTable overhead)
   1.121 + *   (unused table entry space) > (malloc and .next overhead per entry) +
   1.122 + *                                (buckets overhead)
   1.123 + *   (1 - alpha) * esize * pow2 > 2 * count + pow2
   1.124 + *
   1.125 + * Notice that alpha is by definition (count / pow2):
   1.126 + *
   1.127 + *   (1 - alpha) * esize * pow2 > 2 * alpha * pow2 + pow2
   1.128 + *   (1 - alpha) * esize        > 2 * alpha + 1
   1.129 + *
   1.130 + *   esize > (1 + 2 * alpha) / (1 - alpha)
   1.131 + *
   1.132 + * This assumes both tables must keep keyHash, key, and value for each entry,
   1.133 + * where key and value point to separately allocated strings or structures.
   1.134 + * If key and value can be combined into one pointer, then the trade-off is:
   1.135 + *
   1.136 + *   esize > (1 + 3 * alpha) / (1 - alpha)
   1.137 + *
   1.138 + * If the entry value can be a subtype of PLDHashEntryHdr, rather than a type
   1.139 + * that must be allocated separately and referenced by an entry.value pointer
   1.140 + * member, and provided key's allocation can be fused with its entry's, then
   1.141 + * k (the words wasted per entry with chaining) is 4.
   1.142 + *
   1.143 + * To see these curves, feed gnuplot input like so:
   1.144 + *
   1.145 + *   gnuplot> f(x,k) = (1 + k * x) / (1 - x)
   1.146 + *   gnuplot> plot [0:.75] f(x,2), f(x,3), f(x,4)
   1.147 + *
   1.148 + * For k of 2 and a well-loaded table (alpha > .5), esize must be more than 4
   1.149 + * words for chaining to be more space-efficient than double hashing.
   1.150 + *
   1.151 + * Solving for alpha helps us decide when to shrink an underloaded table:
   1.152 + *
   1.153 + *   esize                     > (1 + k * alpha) / (1 - alpha)
   1.154 + *   esize - alpha * esize     > 1 + k * alpha
   1.155 + *   esize - 1                 > (k + esize) * alpha
   1.156 + *   (esize - 1) / (k + esize) > alpha
   1.157 + *
   1.158 + *   alpha < (esize - 1) / (esize + k)
   1.159 + *
   1.160 + * Therefore double hashing should keep alpha >= (esize - 1) / (esize + k),
   1.161 + * assuming esize is not too large (in which case, chaining should probably be
   1.162 + * used for any alpha).  For esize=2 and k=3, we want alpha >= .2; for esize=3
   1.163 + * and k=2, we want alpha >= .4.  For k=4, esize could be 6, and alpha >= .5
   1.164 + * would still obtain.
   1.165 + *
   1.166 + * The current implementation uses a configurable lower bound on alpha, which
   1.167 + * defaults to .25, when deciding to shrink the table (while still respecting
   1.168 + * PL_DHASH_MIN_SIZE).
   1.169 + *
   1.170 + * Note a qualitative difference between chaining and double hashing: under
   1.171 + * chaining, entry addresses are stable across table shrinks and grows.  With
   1.172 + * double hashing, you can't safely hold an entry pointer and use it after an
   1.173 + * ADD or REMOVE operation, unless you sample table->generation before adding
   1.174 + * or removing, and compare the sample after, dereferencing the entry pointer
   1.175 + * only if table->generation has not changed.
   1.176 + *
   1.177 + * The moral of this story: there is no one-size-fits-all hash table scheme,
   1.178 + * but for small table entry size, and assuming entry address stability is not
   1.179 + * required, double hashing wins.
   1.180 + */
   1.181 +struct PLDHashTable {
   1.182 +    const PLDHashTableOps *ops;         /* virtual operations, see below */
   1.183 +    void                *data;          /* ops- and instance-specific data */
   1.184 +    int16_t             hashShift;      /* multiplicative hash shift */
   1.185 +    /*
   1.186 +     * |recursionLevel| is only used in debug builds, but is present in opt
   1.187 +     * builds to avoid binary compatibility problems when mixing DEBUG and
   1.188 +     * non-DEBUG components.  (Actually, even if it were removed,
   1.189 +     * sizeof(PLDHashTable) wouldn't change, due to struct padding.)
   1.190 +     */
   1.191 +    uint16_t            recursionLevel; /* used to detect unsafe re-entry */
   1.192 +    uint32_t            entrySize;      /* number of bytes in an entry */
   1.193 +    uint32_t            entryCount;     /* number of entries in table */
   1.194 +    uint32_t            removedCount;   /* removed entry sentinels in table */
   1.195 +    uint32_t            generation;     /* entry storage generation number */
   1.196 +    char                *entryStore;    /* entry storage */
   1.197 +#ifdef PL_DHASHMETER
   1.198 +    struct PLDHashStats {
   1.199 +        uint32_t        searches;       /* total number of table searches */
   1.200 +        uint32_t        steps;          /* hash chain links traversed */
   1.201 +        uint32_t        hits;           /* searches that found key */
   1.202 +        uint32_t        misses;         /* searches that didn't find key */
   1.203 +        uint32_t        lookups;        /* number of PL_DHASH_LOOKUPs */
   1.204 +        uint32_t        addMisses;      /* adds that miss, and do work */
   1.205 +        uint32_t        addOverRemoved; /* adds that recycled a removed entry */
   1.206 +        uint32_t        addHits;        /* adds that hit an existing entry */
   1.207 +        uint32_t        addFailures;    /* out-of-memory during add growth */
   1.208 +        uint32_t        removeHits;     /* removes that hit, and do work */
   1.209 +        uint32_t        removeMisses;   /* useless removes that miss */
   1.210 +        uint32_t        removeFrees;    /* removes that freed entry directly */
   1.211 +        uint32_t        removeEnums;    /* removes done by Enumerate */
   1.212 +        uint32_t        grows;          /* table expansions */
   1.213 +        uint32_t        shrinks;        /* table contractions */
   1.214 +        uint32_t        compresses;     /* table compressions */
   1.215 +        uint32_t        enumShrinks;    /* contractions after Enumerate */
   1.216 +    } stats;
   1.217 +#endif
   1.218 +};
   1.219 +
   1.220 +/*
   1.221 + * Size in entries (gross, not net of free and removed sentinels) for table.
   1.222 + * We store hashShift rather than sizeLog2 to optimize the collision-free case
   1.223 + * in SearchTable.
   1.224 + */
   1.225 +#define PL_DHASH_TABLE_SIZE(table) \
   1.226 +    ((uint32_t)1 << (PL_DHASH_BITS - (table)->hashShift))
   1.227 +
   1.228 +/*
   1.229 + * Table space at entryStore is allocated and freed using these callbacks.
   1.230 + * The allocator should return null on error only (not if called with nbytes
   1.231 + * equal to 0; but note that pldhash.c code will never call with 0 nbytes).
   1.232 + */
   1.233 +typedef void *
   1.234 +(* PLDHashAllocTable)(PLDHashTable *table, uint32_t nbytes);
   1.235 +
   1.236 +typedef void
   1.237 +(* PLDHashFreeTable) (PLDHashTable *table, void *ptr);
   1.238 +
   1.239 +/*
   1.240 + * Compute the hash code for a given key to be looked up, added, or removed
   1.241 + * from table.  A hash code may have any PLDHashNumber value.
   1.242 + */
   1.243 +typedef PLDHashNumber
   1.244 +(* PLDHashHashKey)   (PLDHashTable *table, const void *key);
   1.245 +
   1.246 +/*
   1.247 + * Compare the key identifying entry in table with the provided key parameter.
   1.248 + * Return true if keys match, false otherwise.
   1.249 + */
   1.250 +typedef bool
   1.251 +(* PLDHashMatchEntry)(PLDHashTable *table, const PLDHashEntryHdr *entry,
   1.252 +                      const void *key);
   1.253 +
   1.254 +/*
   1.255 + * Copy the data starting at from to the new entry storage at to.  Do not add
   1.256 + * reference counts for any strong references in the entry, however, as this
   1.257 + * is a "move" operation: the old entry storage at from will be freed without
   1.258 + * any reference-decrementing callback shortly.
   1.259 + */
   1.260 +typedef void
   1.261 +(* PLDHashMoveEntry)(PLDHashTable *table, const PLDHashEntryHdr *from,
   1.262 +                     PLDHashEntryHdr *to);
   1.263 +
   1.264 +/*
   1.265 + * Clear the entry and drop any strong references it holds.  This callback is
   1.266 + * invoked during a PL_DHASH_REMOVE operation (see below for operation codes),
   1.267 + * but only if the given key is found in the table.
   1.268 + */
   1.269 +typedef void
   1.270 +(* PLDHashClearEntry)(PLDHashTable *table, PLDHashEntryHdr *entry);
   1.271 +
   1.272 +/*
   1.273 + * Called when a table (whether allocated dynamically by itself, or nested in
   1.274 + * a larger structure, or allocated on the stack) is finished.  This callback
   1.275 + * allows table->ops-specific code to finalize table->data.
   1.276 + */
   1.277 +typedef void
   1.278 +(* PLDHashFinalize)  (PLDHashTable *table);
   1.279 +
   1.280 +/*
   1.281 + * Initialize a new entry, apart from keyHash.  This function is called when
   1.282 + * PL_DHashTableOperate's PL_DHASH_ADD case finds no existing entry for the
   1.283 + * given key, and must add a new one.  At that point, entry->keyHash is not
   1.284 + * set yet, to avoid claiming the last free entry in a severely overloaded
   1.285 + * table.
   1.286 + */
   1.287 +typedef bool
   1.288 +(* PLDHashInitEntry)(PLDHashTable *table, PLDHashEntryHdr *entry,
   1.289 +                     const void *key);
   1.290 +
   1.291 +/*
   1.292 + * Finally, the "vtable" structure for PLDHashTable.  The first eight hooks
   1.293 + * must be provided by implementations; they're called unconditionally by the
   1.294 + * generic pldhash.c code.  Hooks after these may be null.
   1.295 + *
   1.296 + * Summary of allocation-related hook usage with C++ placement new emphasis:
   1.297 + *  allocTable          Allocate raw bytes with malloc, no ctors run.
   1.298 + *  freeTable           Free raw bytes with free, no dtors run.
   1.299 + *  initEntry           Call placement new using default key-based ctor.
   1.300 + *                      Return true on success, false on error.
   1.301 + *  moveEntry           Call placement new using copy ctor, run dtor on old
   1.302 + *                      entry storage.
   1.303 + *  clearEntry          Run dtor on entry.
   1.304 + *  finalize            Stub unless table->data was initialized and needs to
   1.305 + *                      be finalized.
   1.306 + *
   1.307 + * Note the reason why initEntry is optional: the default hooks (stubs) clear
   1.308 + * entry storage:  On successful PL_DHashTableOperate(tbl, key, PL_DHASH_ADD),
   1.309 + * the returned entry pointer addresses an entry struct whose keyHash member
   1.310 + * has been set non-zero, but all other entry members are still clear (null).
   1.311 + * PL_DHASH_ADD callers can test such members to see whether the entry was
   1.312 + * newly created by the PL_DHASH_ADD call that just succeeded.  If placement
   1.313 + * new or similar initialization is required, define an initEntry hook.  Of
   1.314 + * course, the clearEntry hook must zero or null appropriately.
   1.315 + *
   1.316 + * XXX assumes 0 is null for pointer types.
   1.317 + */
   1.318 +struct PLDHashTableOps {
   1.319 +    /* Mandatory hooks.  All implementations must provide these. */
   1.320 +    PLDHashAllocTable   allocTable;
   1.321 +    PLDHashFreeTable    freeTable;
   1.322 +    PLDHashHashKey      hashKey;
   1.323 +    PLDHashMatchEntry   matchEntry;
   1.324 +    PLDHashMoveEntry    moveEntry;
   1.325 +    PLDHashClearEntry   clearEntry;
   1.326 +    PLDHashFinalize     finalize;
   1.327 +
   1.328 +    /* Optional hooks start here.  If null, these are not called. */
   1.329 +    PLDHashInitEntry    initEntry;
   1.330 +};
   1.331 +
   1.332 +/*
   1.333 + * Default implementations for the above ops.
   1.334 + */
   1.335 +NS_COM_GLUE void *
   1.336 +PL_DHashAllocTable(PLDHashTable *table, uint32_t nbytes);
   1.337 +
   1.338 +NS_COM_GLUE void
   1.339 +PL_DHashFreeTable(PLDHashTable *table, void *ptr);
   1.340 +
   1.341 +NS_COM_GLUE PLDHashNumber
   1.342 +PL_DHashStringKey(PLDHashTable *table, const void *key);
   1.343 +
   1.344 +/* A minimal entry contains a keyHash header and a void key pointer. */
   1.345 +struct PLDHashEntryStub {
   1.346 +    PLDHashEntryHdr hdr;
   1.347 +    const void      *key;
   1.348 +};
   1.349 +
   1.350 +NS_COM_GLUE PLDHashNumber
   1.351 +PL_DHashVoidPtrKeyStub(PLDHashTable *table, const void *key);
   1.352 +
   1.353 +NS_COM_GLUE bool
   1.354 +PL_DHashMatchEntryStub(PLDHashTable *table,
   1.355 +                       const PLDHashEntryHdr *entry,
   1.356 +                       const void *key);
   1.357 +
   1.358 +NS_COM_GLUE bool
   1.359 +PL_DHashMatchStringKey(PLDHashTable *table,
   1.360 +                       const PLDHashEntryHdr *entry,
   1.361 +                       const void *key);
   1.362 +
   1.363 +NS_COM_GLUE void
   1.364 +PL_DHashMoveEntryStub(PLDHashTable *table,
   1.365 +                      const PLDHashEntryHdr *from,
   1.366 +                      PLDHashEntryHdr *to);
   1.367 +
   1.368 +NS_COM_GLUE void
   1.369 +PL_DHashClearEntryStub(PLDHashTable *table, PLDHashEntryHdr *entry);
   1.370 +
   1.371 +NS_COM_GLUE void
   1.372 +PL_DHashFreeStringKey(PLDHashTable *table, PLDHashEntryHdr *entry);
   1.373 +
   1.374 +NS_COM_GLUE void
   1.375 +PL_DHashFinalizeStub(PLDHashTable *table);
   1.376 +
   1.377 +/*
   1.378 + * If you use PLDHashEntryStub or a subclass of it as your entry struct, and
   1.379 + * if your entries move via memcpy and clear via memset(0), you can use these
   1.380 + * stub operations.
   1.381 + */
   1.382 +NS_COM_GLUE const PLDHashTableOps *
   1.383 +PL_DHashGetStubOps(void);
   1.384 +
   1.385 +/*
   1.386 + * Dynamically allocate a new PLDHashTable using malloc, initialize it using
   1.387 + * PL_DHashTableInit, and return its address.  Return null on malloc failure.
   1.388 + * Note that the entry storage at table->entryStore will be allocated using
   1.389 + * the ops->allocTable callback.
   1.390 + */
   1.391 +NS_COM_GLUE PLDHashTable *
   1.392 +PL_NewDHashTable(const PLDHashTableOps *ops, void *data, uint32_t entrySize,
   1.393 +                 uint32_t capacity);
   1.394 +
   1.395 +/*
   1.396 + * Finalize table's data, free its entry storage (via table->ops->freeTable),
   1.397 + * and return the memory starting at table to the malloc heap.
   1.398 + */
   1.399 +NS_COM_GLUE void
   1.400 +PL_DHashTableDestroy(PLDHashTable *table);
   1.401 +
   1.402 +/*
   1.403 + * Initialize table with ops, data, entrySize, and capacity.  Capacity is a
   1.404 + * guess for the smallest table size at which the table will usually be less
   1.405 + * than 75% loaded (the table will grow or shrink as needed; capacity serves
   1.406 + * only to avoid inevitable early growth from PL_DHASH_MIN_SIZE).  This will
   1.407 + * crash if it can't allocate enough memory, or if entrySize or capacity are
   1.408 + * too large.
   1.409 + */
   1.410 +NS_COM_GLUE void
   1.411 +PL_DHashTableInit(PLDHashTable *table, const PLDHashTableOps *ops, void *data,
   1.412 +                  uint32_t entrySize, uint32_t capacity);
   1.413 +
   1.414 +/*
   1.415 + * Initialize table. This is the same as PL_DHashTableInit, except that it
   1.416 + * returns a boolean indicating success, rather than crashing on failure.
   1.417 + */
   1.418 +NS_COM_GLUE bool
   1.419 +PL_DHashTableInit(PLDHashTable *table, const PLDHashTableOps *ops, void *data,
   1.420 +                  uint32_t entrySize, uint32_t capacity,
   1.421 +                  const mozilla::fallible_t& ) MOZ_WARN_UNUSED_RESULT;
   1.422 +
   1.423 +/*
   1.424 + * Finalize table's data, free its entry storage using table->ops->freeTable,
   1.425 + * and leave its members unchanged from their last live values (which leaves
   1.426 + * pointers dangling).  If you want to burn cycles clearing table, it's up to
   1.427 + * your code to call memset.
   1.428 + */
   1.429 +NS_COM_GLUE void
   1.430 +PL_DHashTableFinish(PLDHashTable *table);
   1.431 +
   1.432 +/*
   1.433 + * To consolidate keyHash computation and table grow/shrink code, we use a
   1.434 + * single entry point for lookup, add, and remove operations.  The operation
   1.435 + * codes are declared here, along with codes returned by PLDHashEnumerator
   1.436 + * functions, which control PL_DHashTableEnumerate's behavior.
   1.437 + */
   1.438 +typedef enum PLDHashOperator {
   1.439 +    PL_DHASH_LOOKUP = 0,        /* lookup entry */
   1.440 +    PL_DHASH_ADD = 1,           /* add entry */
   1.441 +    PL_DHASH_REMOVE = 2,        /* remove entry, or enumerator says remove */
   1.442 +    PL_DHASH_NEXT = 0,          /* enumerator says continue */
   1.443 +    PL_DHASH_STOP = 1           /* enumerator says stop */
   1.444 +} PLDHashOperator;
   1.445 +
   1.446 +/*
   1.447 + * To lookup a key in table, call:
   1.448 + *
   1.449 + *  entry = PL_DHashTableOperate(table, key, PL_DHASH_LOOKUP);
   1.450 + *
   1.451 + * If PL_DHASH_ENTRY_IS_BUSY(entry) is true, key was found and it identifies
   1.452 + * entry.  If PL_DHASH_ENTRY_IS_FREE(entry) is true, key was not found.
   1.453 + *
   1.454 + * To add an entry identified by key to table, call:
   1.455 + *
   1.456 + *  entry = PL_DHashTableOperate(table, key, PL_DHASH_ADD);
   1.457 + *
   1.458 + * If entry is null upon return, then either the table is severely overloaded,
   1.459 + * and memory can't be allocated for entry storage via table->ops->allocTable;
   1.460 + * Or if table->ops->initEntry is non-null, the table->ops->initEntry op may
   1.461 + * have returned false.
   1.462 + *
   1.463 + * Otherwise, entry->keyHash has been set so that PL_DHASH_ENTRY_IS_BUSY(entry)
   1.464 + * is true, and it is up to the caller to initialize the key and value parts
   1.465 + * of the entry sub-type, if they have not been set already (i.e. if entry was
   1.466 + * not already in the table, and if the optional initEntry hook was not used).
   1.467 + *
   1.468 + * To remove an entry identified by key from table, call:
   1.469 + *
   1.470 + *  (void) PL_DHashTableOperate(table, key, PL_DHASH_REMOVE);
   1.471 + *
   1.472 + * If key's entry is found, it is cleared (via table->ops->clearEntry) and
   1.473 + * the entry is marked so that PL_DHASH_ENTRY_IS_FREE(entry).  This operation
   1.474 + * returns null unconditionally; you should ignore its return value.
   1.475 + */
   1.476 +NS_COM_GLUE PLDHashEntryHdr * PL_DHASH_FASTCALL
   1.477 +PL_DHashTableOperate(PLDHashTable *table, const void *key, PLDHashOperator op);
   1.478 +
   1.479 +/*
   1.480 + * Remove an entry already accessed via LOOKUP or ADD.
   1.481 + *
   1.482 + * NB: this is a "raw" or low-level routine, intended to be used only where
   1.483 + * the inefficiency of a full PL_DHashTableOperate (which rehashes in order
   1.484 + * to find the entry given its key) is not tolerable.  This function does not
   1.485 + * shrink the table if it is underloaded.  It does not update stats #ifdef
   1.486 + * PL_DHASHMETER, either.
   1.487 + */
   1.488 +NS_COM_GLUE void
   1.489 +PL_DHashTableRawRemove(PLDHashTable *table, PLDHashEntryHdr *entry);
   1.490 +
   1.491 +/*
   1.492 + * Enumerate entries in table using etor:
   1.493 + *
   1.494 + *   count = PL_DHashTableEnumerate(table, etor, arg);
   1.495 + *
   1.496 + * PL_DHashTableEnumerate calls etor like so:
   1.497 + *
   1.498 + *   op = etor(table, entry, number, arg);
   1.499 + *
   1.500 + * where number is a zero-based ordinal assigned to live entries according to
   1.501 + * their order in table->entryStore.
   1.502 + *
   1.503 + * The return value, op, is treated as a set of flags.  If op is PL_DHASH_NEXT,
   1.504 + * then continue enumerating.  If op contains PL_DHASH_REMOVE, then clear (via
   1.505 + * table->ops->clearEntry) and free entry.  Then we check whether op contains
   1.506 + * PL_DHASH_STOP; if so, stop enumerating and return the number of live entries
   1.507 + * that were enumerated so far.  Return the total number of live entries when
   1.508 + * enumeration completes normally.
   1.509 + *
   1.510 + * If etor calls PL_DHashTableOperate on table with op != PL_DHASH_LOOKUP, it
   1.511 + * must return PL_DHASH_STOP; otherwise undefined behavior results.
   1.512 + *
   1.513 + * If any enumerator returns PL_DHASH_REMOVE, table->entryStore may be shrunk
   1.514 + * or compressed after enumeration, but before PL_DHashTableEnumerate returns.
   1.515 + * Such an enumerator therefore can't safely set aside entry pointers, but an
   1.516 + * enumerator that never returns PL_DHASH_REMOVE can set pointers to entries
   1.517 + * aside, e.g., to avoid copying live entries into an array of the entry type.
   1.518 + * Copying entry pointers is cheaper, and safe so long as the caller of such a
   1.519 + * "stable" Enumerate doesn't use the set-aside pointers after any call either
   1.520 + * to PL_DHashTableOperate, or to an "unstable" form of Enumerate, which might
   1.521 + * grow or shrink entryStore.
   1.522 + *
   1.523 + * If your enumerator wants to remove certain entries, but set aside pointers
   1.524 + * to other entries that it retains, it can use PL_DHashTableRawRemove on the
   1.525 + * entries to be removed, returning PL_DHASH_NEXT to skip them.  Likewise, if
   1.526 + * you want to remove entries, but for some reason you do not want entryStore
   1.527 + * to be shrunk or compressed, you can call PL_DHashTableRawRemove safely on
   1.528 + * the entry being enumerated, rather than returning PL_DHASH_REMOVE.
   1.529 + */
   1.530 +typedef PLDHashOperator
   1.531 +(* PLDHashEnumerator)(PLDHashTable *table, PLDHashEntryHdr *hdr, uint32_t number,
   1.532 +                      void *arg);
   1.533 +
   1.534 +NS_COM_GLUE uint32_t
   1.535 +PL_DHashTableEnumerate(PLDHashTable *table, PLDHashEnumerator etor, void *arg);
   1.536 +
   1.537 +typedef size_t
   1.538 +(* PLDHashSizeOfEntryExcludingThisFun)(PLDHashEntryHdr *hdr,
   1.539 +                                       mozilla::MallocSizeOf mallocSizeOf,
   1.540 +                                       void *arg);
   1.541 +
   1.542 +/**
   1.543 + * Measure the size of the table's entry storage, and if
   1.544 + * |sizeOfEntryExcludingThis| is non-nullptr, measure the size of things
   1.545 + * pointed to by entries.  Doesn't measure |ops| because it's often shared
   1.546 + * between tables, nor |data| because it's opaque.
   1.547 + */
   1.548 +NS_COM_GLUE size_t
   1.549 +PL_DHashTableSizeOfExcludingThis(const PLDHashTable *table,
   1.550 +                                 PLDHashSizeOfEntryExcludingThisFun sizeOfEntryExcludingThis,
   1.551 +                                 mozilla::MallocSizeOf mallocSizeOf,
   1.552 +                                 void *arg = nullptr);
   1.553 +
   1.554 +/**
   1.555 + * Like PL_DHashTableSizeOfExcludingThis, but includes sizeof(*this).
   1.556 + */
   1.557 +NS_COM_GLUE size_t
   1.558 +PL_DHashTableSizeOfIncludingThis(const PLDHashTable *table,
   1.559 +                                 PLDHashSizeOfEntryExcludingThisFun sizeOfEntryExcludingThis,
   1.560 +                                 mozilla::MallocSizeOf mallocSizeOf,
   1.561 +                                 void *arg = nullptr);
   1.562 +
   1.563 +#ifdef DEBUG
   1.564 +/**
   1.565 + * Mark a table as immutable for the remainder of its lifetime.  This
   1.566 + * changes the implementation from ASSERTing one set of invariants to
   1.567 + * ASSERTing a different set.
   1.568 + *
   1.569 + * When a table is NOT marked as immutable, the table implementation
   1.570 + * asserts that the table is not mutated from its own callbacks.  It
   1.571 + * assumes the caller protects the table from being accessed on multiple
   1.572 + * threads simultaneously.
   1.573 + *
   1.574 + * When the table is marked as immutable, the re-entry assertions will
   1.575 + * no longer trigger erroneously due to multi-threaded access.  Instead,
   1.576 + * mutations will cause assertions.
   1.577 + */
   1.578 +NS_COM_GLUE void
   1.579 +PL_DHashMarkTableImmutable(PLDHashTable *table);
   1.580 +#endif
   1.581 +
   1.582 +#ifdef PL_DHASHMETER
   1.583 +#include <stdio.h>
   1.584 +
   1.585 +NS_COM_GLUE void
   1.586 +PL_DHashTableDumpMeter(PLDHashTable *table, PLDHashEnumerator dump, FILE *fp);
   1.587 +#endif
   1.588 +
   1.589 +#endif /* pldhash_h___ */

mercurial