зеркало из https://github.com/mozilla/pjs.git
173 строки
4.3 KiB
C++
173 строки
4.3 KiB
C++
// -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
//
|
|
// The contents of this file are subject to the Netscape 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/NPL/
|
|
//
|
|
// Software distributed under the License is distributed on an "AS
|
|
// IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
|
|
// implied. See the License for the specific language governing
|
|
// rights and limitations under the License.
|
|
//
|
|
// The Original Code is the JavaScript 2 Prototype.
|
|
//
|
|
// The Initial Developer of the Original Code is Netscape
|
|
// Communications Corporation. Portions created by Netscape are
|
|
// Copyright (C) 1998 Netscape Communications Corporation. All
|
|
// Rights Reserved.
|
|
|
|
#include "hash.h"
|
|
#include <new>
|
|
namespace JS = JavaScript;
|
|
|
|
|
|
//
|
|
// Hash Codes
|
|
//
|
|
|
|
|
|
// General-purpose null-terminated C string hash function
|
|
JS::HashNumber JS::hashString(const char *s)
|
|
{
|
|
HashNumber h = 0;
|
|
uchar ch;
|
|
|
|
while ((ch = (uchar)*s++) != 0)
|
|
h = (h >> 28) ^ (h << 4) ^ ch;
|
|
return h;
|
|
}
|
|
|
|
|
|
// General-purpose String hash function
|
|
JS::HashNumber JS::hashString(const String &s)
|
|
{
|
|
HashNumber h = 0;
|
|
String::const_iterator p = s.begin();
|
|
String::size_type n = s.size();
|
|
|
|
if (n < 16)
|
|
// Hash every character in a short string.
|
|
while (n--)
|
|
h = (h >> 28) ^ (h << 4) ^ *p++;
|
|
else
|
|
// Sample a la java.lang.String.hash().
|
|
for (String::size_type m = n / 8; n >= m; p += m, n -= m)
|
|
h = (h >> 28) ^ (h << 4) ^ *p;
|
|
return h;
|
|
}
|
|
|
|
|
|
//
|
|
// Hash Tables
|
|
//
|
|
|
|
|
|
const uint minLgNBuckets = 4;
|
|
|
|
|
|
JS::GenericHashTableIterator::GenericHashTableIterator(GenericHashTable &ht):
|
|
ht(ht), entry(0), nextBucket(ht.buckets)
|
|
{
|
|
DEBUG_ONLY(++ht.nReferences);
|
|
operator++();
|
|
}
|
|
|
|
|
|
|
|
JS::GenericHashTableIterator &
|
|
JS::GenericHashTableIterator::operator++()
|
|
{
|
|
GenericHashEntry *e = entry;
|
|
|
|
if (e) {
|
|
backpointer = &e->next;
|
|
e = e->next;
|
|
}
|
|
if (!e) {
|
|
GenericHashEntry **const bucketsEnd = ht.bucketsEnd;
|
|
GenericHashEntry **bucket = nextBucket;
|
|
|
|
while (bucket != bucketsEnd) {
|
|
e = *bucket++;
|
|
if (e) {
|
|
backpointer = bucket-1;
|
|
break;
|
|
}
|
|
}
|
|
nextBucket = bucket;
|
|
}
|
|
entry = e;
|
|
return *this;
|
|
}
|
|
|
|
|
|
JS::GenericHashTable::GenericHashTable(uint32 nEntriesDefault):
|
|
nEntries(0)
|
|
{
|
|
DEBUG_ONLY(nReferences = 0);
|
|
|
|
uint lgNBuckets = ceilingLog2(nEntriesDefault);
|
|
if (lgNBuckets < minLgNBuckets)
|
|
lgNBuckets = minLgNBuckets;
|
|
defaultLgNBuckets = lgNBuckets;
|
|
|
|
recomputeMinMaxNEntries(lgNBuckets);
|
|
uint32 nBuckets = JS_BIT(lgNBuckets);
|
|
buckets = new GenericHashEntry*[nBuckets];
|
|
// No exceptions after this point unless buckets is deleted.
|
|
|
|
bucketsEnd = buckets + nBuckets;
|
|
zero(buckets, bucketsEnd);
|
|
}
|
|
|
|
|
|
// Initialize shift, minNEntries, and maxNEntries based on the lg2 of the
|
|
// number of buckets.
|
|
void JS::GenericHashTable::recomputeMinMaxNEntries(uint lgNBuckets)
|
|
{
|
|
uint32 nBuckets = JS_BIT(lgNBuckets);
|
|
shift = 32 - lgNBuckets;
|
|
maxNEntries = nBuckets; // Maximum ratio is 100%
|
|
minNEntries = lgNBuckets <= defaultLgNBuckets ? 0 : 3*(nBuckets>>3); // Minimum ratio is 37.5%
|
|
}
|
|
|
|
|
|
// Rehash the table. This method cannot throw out-of-memory exceptions, so it is
|
|
// safe to call from a destructor.
|
|
void JS::GenericHashTable::rehash()
|
|
{
|
|
uint32 newLgNBuckets = ceilingLog2(nEntries);
|
|
if (newLgNBuckets < defaultLgNBuckets)
|
|
newLgNBuckets = defaultLgNBuckets;
|
|
uint32 newNBuckets = JS_BIT(newLgNBuckets);
|
|
try {
|
|
GenericHashEntry **newBuckets = new GenericHashEntry*[newNBuckets];
|
|
// No exceptions after this point.
|
|
|
|
GenericHashEntry **newBucketsEnd = newBuckets + newNBuckets;
|
|
zero(newBuckets, newBucketsEnd);
|
|
recomputeMinMaxNEntries(newLgNBuckets);
|
|
GenericHashEntry **be = bucketsEnd;
|
|
for (GenericHashEntry **b = buckets; b != be; b++) {
|
|
GenericHashEntry *e = *b;
|
|
while (e) {
|
|
GenericHashEntry *next = e->next;
|
|
// Place e in the new set of buckets.
|
|
GenericHashEntry **nb = newBuckets + (e->keyHash*goldenRatio >> shift);
|
|
e->next = *nb;
|
|
*nb = e;
|
|
e = next;
|
|
}
|
|
}
|
|
buckets = newBuckets;
|
|
bucketsEnd = newBucketsEnd;
|
|
} catch (std::bad_alloc) {
|
|
// Out of memory. Ignore the error and just relax the resizing boundaries.
|
|
if (buckets + JS_BIT(newLgNBuckets) > bucketsEnd)
|
|
maxNEntries >>= 1;
|
|
else
|
|
minNEntries <<= 1;
|
|
}
|
|
}
|