|
|
|
|
|
*/ |
|
/* |
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
|
* contributor license agreements. See the NOTICE file distributed with |
|
* this work for additional information regarding copyright ownership. |
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|
* (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.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, software |
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
* See the License for the specific language governing permissions and |
|
* limitations under the License. |
|
*/ |
|
|
|
package com.sun.org.apache.xerces.internal.util; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public class SymbolHash { |
|
|
|
// |
|
// Constants |
|
// |
|
|
|
|
|
protected static final int TABLE_SIZE = 101; |
|
|
|
|
|
protected static final int MAX_HASH_COLLISIONS = 40; |
|
|
|
protected static final int MULTIPLIERS_SIZE = 1 << 5; |
|
protected static final int MULTIPLIERS_MASK = MULTIPLIERS_SIZE - 1; |
|
|
|
// |
|
// Data |
|
// |
|
|
|
|
|
protected int fTableSize; |
|
|
|
|
|
protected Entry[] fBuckets; |
|
|
|
|
|
protected int fNum = 0; |
|
|
|
|
|
|
|
|
|
*/ |
|
protected int[] fHashMultipliers; |
|
|
|
// |
|
// Constructors |
|
// |
|
|
|
|
|
public SymbolHash() { |
|
this(TABLE_SIZE); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public SymbolHash(int size) { |
|
fTableSize = size; |
|
fBuckets = new Entry[fTableSize]; |
|
} |
|
|
|
// |
|
// Public methods |
|
// |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void put(Object key, Object value) { |
|
|
|
|
|
int collisionCount = 0; |
|
final int hash = hash(key); |
|
int bucket = hash % fTableSize; |
|
for (Entry entry = fBuckets[bucket]; entry != null; entry = entry.next) { |
|
if (key.equals(entry.key)) { |
|
|
|
entry.value = value; |
|
return; |
|
} |
|
++collisionCount; |
|
} |
|
|
|
if (fNum >= fTableSize) { |
|
// Rehash the table if the number of entries |
|
|
|
rehash(); |
|
bucket = hash % fTableSize; |
|
} |
|
else if (collisionCount >= MAX_HASH_COLLISIONS && key instanceof String) { |
|
// Select a new hash function and rehash the table if |
|
|
|
rebalance(); |
|
bucket = hash(key) % fTableSize; |
|
} |
|
|
|
|
|
Entry entry = new Entry(key, value, fBuckets[bucket]); |
|
fBuckets[bucket] = entry; |
|
++fNum; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Object get(Object key) { |
|
int bucket = hash(key) % fTableSize; |
|
Entry entry = search(key, bucket); |
|
if (entry != null) { |
|
return entry.value; |
|
} |
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public int getLength() { |
|
return fNum; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public int getValues(Object[] elements, int from) { |
|
for (int i=0, j=0; i<fTableSize && j<fNum; i++) { |
|
for (Entry entry = fBuckets[i]; entry != null; entry = entry.next) { |
|
elements[from+j] = entry.value; |
|
j++; |
|
} |
|
} |
|
return fNum; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public Object[] getEntries() { |
|
Object[] entries = new Object[fNum << 1]; |
|
for (int i=0, j=0; i<fTableSize && j<fNum << 1; i++) { |
|
for (Entry entry = fBuckets[i]; entry != null; entry = entry.next) { |
|
entries[j] = entry.key; |
|
entries[++j] = entry.value; |
|
j++; |
|
} |
|
} |
|
return entries; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public SymbolHash makeClone() { |
|
SymbolHash newTable = new SymbolHash(fTableSize); |
|
newTable.fNum = fNum; |
|
newTable.fHashMultipliers = fHashMultipliers != null ? fHashMultipliers.clone() : null; |
|
for (int i = 0; i < fTableSize; i++) { |
|
if (fBuckets[i] != null) { |
|
newTable.fBuckets[i] = fBuckets[i].makeClone(); |
|
} |
|
} |
|
return newTable; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public void clear() { |
|
for (int i=0; i<fTableSize; i++) { |
|
fBuckets[i] = null; |
|
} |
|
fNum = 0; |
|
fHashMultipliers = null; |
|
} // clear(): void |
|
|
|
protected Entry search(Object key, int bucket) { |
|
|
|
for (Entry entry = fBuckets[bucket]; entry != null; entry = entry.next) { |
|
if (key.equals(entry.key)) |
|
return entry; |
|
} |
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected int hash(Object key) { |
|
if (fHashMultipliers == null || !(key instanceof String)) { |
|
return key.hashCode() & 0x7FFFFFFF; |
|
} |
|
return hash0((String) key); |
|
} // hash(Object):int |
|
|
|
private int hash0(String symbol) { |
|
int code = 0; |
|
final int length = symbol.length(); |
|
final int[] multipliers = fHashMultipliers; |
|
for (int i = 0; i < length; ++i) { |
|
code = code * multipliers[i & MULTIPLIERS_MASK] + symbol.charAt(i); |
|
} |
|
return code & 0x7FFFFFFF; |
|
} // hash0(String):int |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void rehash() { |
|
rehashCommon((fBuckets.length << 1) + 1); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void rebalance() { |
|
if (fHashMultipliers == null) { |
|
fHashMultipliers = new int[MULTIPLIERS_SIZE]; |
|
} |
|
PrimeNumberSequenceGenerator.generateSequence(fHashMultipliers); |
|
rehashCommon(fBuckets.length); |
|
} |
|
|
|
private void rehashCommon(final int newCapacity) { |
|
|
|
final int oldCapacity = fBuckets.length; |
|
final Entry[] oldTable = fBuckets; |
|
|
|
final Entry[] newTable = new Entry[newCapacity]; |
|
|
|
fBuckets = newTable; |
|
fTableSize = fBuckets.length; |
|
|
|
for (int i = oldCapacity; i-- > 0;) { |
|
for (Entry old = oldTable[i]; old != null; ) { |
|
Entry e = old; |
|
old = old.next; |
|
|
|
int index = hash(e.key) % newCapacity; |
|
e.next = newTable[index]; |
|
newTable[index] = e; |
|
} |
|
} |
|
} |
|
|
|
// |
|
// Classes |
|
// |
|
|
|
|
|
|
|
|
|
*/ |
|
protected static final class Entry { |
|
|
|
public Object key; |
|
public Object value; |
|
|
|
public Entry next; |
|
|
|
public Entry() { |
|
key = null; |
|
value = null; |
|
next = null; |
|
} |
|
|
|
public Entry(Object key, Object value, Entry next) { |
|
this.key = key; |
|
this.value = value; |
|
this.next = next; |
|
} |
|
|
|
public Entry makeClone() { |
|
Entry entry = new Entry(); |
|
entry.key = key; |
|
entry.value = value; |
|
if (next != null) |
|
entry.next = next.makeClone(); |
|
return entry; |
|
} |
|
} // entry |
|
|
|
} // class SymbolHash |