jsdhash.c
来自「一个基于alice开发的机器人」· C语言 代码 · 共 764 行 · 第 1/2 页
C
764 行
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla JavaScript code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1999-2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Brendan Eich <brendan@mozilla.org> (Original Author)
* Chris Waterson <waterson@netscape.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*
* Double hashing implementation.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "jsbit.h"
#include "jsdhash.h"
#include "jsutil.h" /* for JS_ASSERT */
#ifdef JS_DHASHMETER
# if defined MOZILLA_CLIENT && defined DEBUG_XXXbrendan
# include "nsTraceMalloc.h"
# endif
# define METER(x) x
#else
# define METER(x) /* nothing */
#endif
JS_PUBLIC_API(void *)
JS_DHashAllocTable(JSDHashTable *table, uint32 nbytes)
{
return malloc(nbytes);
}
JS_PUBLIC_API(void)
JS_DHashFreeTable(JSDHashTable *table, void *ptr)
{
free(ptr);
}
JS_PUBLIC_API(JSDHashNumber)
JS_DHashStringKey(JSDHashTable *table, const void *key)
{
JSDHashNumber h;
const unsigned char *s;
h = 0;
for (s = key; *s != '\0'; s++)
h = (h >> (JS_DHASH_BITS - 4)) ^ (h << 4) ^ *s;
return h;
}
JS_PUBLIC_API(const void *)
JS_DHashGetKeyStub(JSDHashTable *table, JSDHashEntryHdr *entry)
{
JSDHashEntryStub *stub = (JSDHashEntryStub *)entry;
return stub->key;
}
JS_PUBLIC_API(JSDHashNumber)
JS_DHashVoidPtrKeyStub(JSDHashTable *table, const void *key)
{
return (JSDHashNumber)key >> 2;
}
JS_PUBLIC_API(JSBool)
JS_DHashMatchEntryStub(JSDHashTable *table,
const JSDHashEntryHdr *entry,
const void *key)
{
const JSDHashEntryStub *stub = (const JSDHashEntryStub *)entry;
return stub->key == key;
}
JS_PUBLIC_API(JSBool)
JS_DHashMatchStringKey(JSDHashTable *table,
const JSDHashEntryHdr *entry,
const void *key)
{
const JSDHashEntryStub *stub = (const JSDHashEntryStub *)entry;
/* XXX tolerate null keys on account of sloppy Mozilla callers. */
return stub->key == key ||
(stub->key && key && strcmp(stub->key, key) == 0);
}
JS_PUBLIC_API(void)
JS_DHashMoveEntryStub(JSDHashTable *table,
const JSDHashEntryHdr *from,
JSDHashEntryHdr *to)
{
memcpy(to, from, table->entrySize);
}
JS_PUBLIC_API(void)
JS_DHashClearEntryStub(JSDHashTable *table, JSDHashEntryHdr *entry)
{
memset(entry, 0, table->entrySize);
}
JS_PUBLIC_API(void)
JS_DHashFreeStringKey(JSDHashTable *table, JSDHashEntryHdr *entry)
{
const JSDHashEntryStub *stub = (const JSDHashEntryStub *)entry;
free((void *) stub->key);
memset(entry, 0, table->entrySize);
}
JS_PUBLIC_API(void)
JS_DHashFinalizeStub(JSDHashTable *table)
{
}
static const JSDHashTableOps stub_ops = {
JS_DHashAllocTable,
JS_DHashFreeTable,
JS_DHashGetKeyStub,
JS_DHashVoidPtrKeyStub,
JS_DHashMatchEntryStub,
JS_DHashMoveEntryStub,
JS_DHashClearEntryStub,
JS_DHashFinalizeStub,
NULL
};
JS_PUBLIC_API(const JSDHashTableOps *)
JS_DHashGetStubOps(void)
{
return &stub_ops;
}
JS_PUBLIC_API(JSDHashTable *)
JS_NewDHashTable(const JSDHashTableOps *ops, void *data, uint32 entrySize,
uint32 capacity)
{
JSDHashTable *table;
table = (JSDHashTable *) malloc(sizeof *table);
if (!table)
return NULL;
if (!JS_DHashTableInit(table, ops, data, entrySize, capacity)) {
free(table);
return NULL;
}
return table;
}
JS_PUBLIC_API(void)
JS_DHashTableDestroy(JSDHashTable *table)
{
JS_DHashTableFinish(table);
free(table);
}
JS_PUBLIC_API(JSBool)
JS_DHashTableInit(JSDHashTable *table, const JSDHashTableOps *ops, void *data,
uint32 entrySize, uint32 capacity)
{
int log2;
uint32 nbytes;
#ifdef DEBUG
if (entrySize > 10 * sizeof(void *)) {
fprintf(stderr,
"jsdhash: for the table at address %p, the given entrySize"
" of %lu %s favors chaining over double hashing.\n",
(void *)table,
(unsigned long) entrySize,
(entrySize > 16 * sizeof(void*)) ? "definitely" : "probably");
}
#endif
table->ops = ops;
table->data = data;
if (capacity < JS_DHASH_MIN_SIZE)
capacity = JS_DHASH_MIN_SIZE;
log2 = JS_CeilingLog2(capacity);
capacity = JS_BIT(log2);
if (capacity >= JS_DHASH_SIZE_LIMIT)
return JS_FALSE;
table->hashShift = JS_DHASH_BITS - log2;
table->maxAlphaFrac = 0xC0; /* .75 */
table->minAlphaFrac = 0x40; /* .25 */
table->entrySize = entrySize;
table->entryCount = table->removedCount = 0;
table->generation = 0;
nbytes = capacity * entrySize;
table->entryStore = ops->allocTable(table, nbytes);
if (!table->entryStore)
return JS_FALSE;
memset(table->entryStore, 0, nbytes);
METER(memset(&table->stats, 0, sizeof table->stats));
return JS_TRUE;
}
/*
* Compute max and min load numbers (entry counts) from table params.
*/
#define MAX_LOAD(table, size) (((table)->maxAlphaFrac * (size)) >> 8)
#define MIN_LOAD(table, size) (((table)->minAlphaFrac * (size)) >> 8)
JS_PUBLIC_API(void)
JS_DHashTableSetAlphaBounds(JSDHashTable *table,
float maxAlpha,
float minAlpha)
{
uint32 size;
/*
* Reject obviously insane bounds, rather than trying to guess what the
* buggy caller intended.
*/
JS_ASSERT(0.5 <= maxAlpha && maxAlpha < 1 && 0 <= minAlpha);
if (maxAlpha < 0.5 || 1 <= maxAlpha || minAlpha < 0)
return;
/*
* Ensure that at least one entry will always be free. If maxAlpha at
* minimum size leaves no entries free, reduce maxAlpha based on minimum
* size and the precision limit of maxAlphaFrac's fixed point format.
*/
JS_ASSERT(JS_DHASH_MIN_SIZE - (maxAlpha * JS_DHASH_MIN_SIZE) >= 1);
if (JS_DHASH_MIN_SIZE - (maxAlpha * JS_DHASH_MIN_SIZE) < 1) {
maxAlpha = (float)
(JS_DHASH_MIN_SIZE - JS_MAX(JS_DHASH_MIN_SIZE / 256, 1))
/ JS_DHASH_MIN_SIZE;
}
/*
* Ensure that minAlpha is strictly less than half maxAlpha. Take care
* not to truncate an entry's worth of alpha when storing in minAlphaFrac
* (8-bit fixed point format).
*/
JS_ASSERT(minAlpha < maxAlpha / 2);
if (minAlpha >= maxAlpha / 2) {
size = JS_DHASH_TABLE_SIZE(table);
minAlpha = (size * maxAlpha - JS_MAX(size / 256, 1)) / (2 * size);
}
table->maxAlphaFrac = (uint8)(maxAlpha * 256);
table->minAlphaFrac = (uint8)(minAlpha * 256);
}
/*
* Double hashing needs the second hash code to be relatively prime to table
* size, so we simply make hash2 odd.
*/
#define HASH1(hash0, shift) ((hash0) >> (shift))
#define HASH2(hash0,log2,shift) ((((hash0) << (log2)) >> (shift)) | 1)
/*
* Reserve keyHash 0 for free entries and 1 for removed-entry sentinels. Note
* that a removed-entry sentinel need be stored only if the removed entry had
* a colliding entry added after it. Therefore we can use 1 as the collision
* flag in addition to the removed-entry sentinel value. Multiplicative hash
* uses the high order bits of keyHash, so this least-significant reservation
* should not hurt the hash function's effectiveness much.
*
* If you change any of these magic numbers, also update JS_DHASH_ENTRY_IS_LIVE
* in jsdhash.h. It used to be private to jsdhash.c, but then became public to
* assist iterator writers who inspect table->entryStore directly.
*/
#define COLLISION_FLAG ((JSDHashNumber) 1)
#define MARK_ENTRY_FREE(entry) ((entry)->keyHash = 0)
#define MARK_ENTRY_REMOVED(entry) ((entry)->keyHash = 1)
#define ENTRY_IS_REMOVED(entry) ((entry)->keyHash == 1)
#define ENTRY_IS_LIVE(entry) JS_DHASH_ENTRY_IS_LIVE(entry)
#define ENSURE_LIVE_KEYHASH(hash0) if (hash0 < 2) hash0 -= 2; else (void)0
/* Match an entry's keyHash against an unstored one computed from a key. */
#define MATCH_ENTRY_KEYHASH(entry,hash0) \
(((entry)->keyHash & ~COLLISION_FLAG) == (hash0))
/* Compute the address of the indexed entry in table. */
#define ADDRESS_ENTRY(table, index) \
((JSDHashEntryHdr *)((table)->entryStore + (index) * (table)->entrySize))
JS_PUBLIC_API(void)
JS_DHashTableFinish(JSDHashTable *table)
{
char *entryAddr, *entryLimit;
uint32 entrySize;
JSDHashEntryHdr *entry;
#ifdef DEBUG_XXXbrendan
static FILE *dumpfp = NULL;
if (!dumpfp) dumpfp = fopen("/tmp/jsdhash.bigdump", "w");
if (dumpfp) {
#ifdef MOZILLA_CLIENT
NS_TraceStack(1, dumpfp);
#endif
JS_DHashTableDumpMeter(table, NULL, dumpfp);
fputc('\n', dumpfp);
}
#endif
/* Call finalize before clearing entries, so it can enumerate them. */
table->ops->finalize(table);
/* Clear any remaining live entries. */
entryAddr = table->entryStore;
entrySize = table->entrySize;
entryLimit = entryAddr + JS_DHASH_TABLE_SIZE(table) * entrySize;
while (entryAddr < entryLimit) {
entry = (JSDHashEntryHdr *)entryAddr;
if (ENTRY_IS_LIVE(entry)) {
METER(table->stats.removeEnums++);
table->ops->clearEntry(table, entry);
}
entryAddr += entrySize;
}
/* Free entry storage last. */
table->ops->freeTable(table, table->entryStore);
}
static JSDHashEntryHdr *
SearchTable(JSDHashTable *table, const void *key, JSDHashNumber keyHash,
JSDHashOperator op)
{
JSDHashNumber hash1, hash2;
int hashShift, sizeLog2;
JSDHashEntryHdr *entry, *firstRemoved;
JSDHashMatchEntry matchEntry;
uint32 sizeMask;
METER(table->stats.searches++);
JS_ASSERT(!(keyHash & COLLISION_FLAG));
/* Compute the primary hash address. */
hashShift = table->hashShift;
hash1 = HASH1(keyHash, hashShift);
entry = ADDRESS_ENTRY(table, hash1);
/* Miss: return space for a new entry. */
if (JS_DHASH_ENTRY_IS_FREE(entry)) {
METER(table->stats.misses++);
return entry;
}
/* Hit: return entry. */
matchEntry = table->ops->matchEntry;
if (MATCH_ENTRY_KEYHASH(entry, keyHash) && matchEntry(table, entry, key)) {
METER(table->stats.hits++);
return entry;
}
/* Collision: double hash. */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?