pluotsorbet/vm/hashtable.ts

169 строки
4.4 KiB
TypeScript

/**
* Port of Java java.util.Hashtable.
*/
module J2ME {
import assert = Debug.assert;
export class TypedArrayHashtableEntry {
hash: number = 0;
value: any = null;
next: TypedArrayHashtableEntry = null;
key: Uint8Array = null;
}
function arrayEqual(a: Uint8Array, b: Uint8Array): boolean {
if (a === b) {
return true;
}
if (a.length !== b.length) {
return false;
}
for (var i = 0; i < a.length; i++) {
if (a[i] !== b[i]) {
return false;
}
}
return true;
}
function arrayRangeEqual(a: Uint8Array, offset: number, length: number, b: Uint8Array): boolean {
if (length !== b.length) {
return false;
}
var j = offset;
for (var i = 0; i < length; j++, i++) {
if (a[j] !== b[i]) {
return false;
}
}
return true;
}
function arrayHash(array: Uint8Array): number {
return arrayRangeHash(array, 0, array.length);
}
function arrayRangeHash(array: Uint8Array, offset: number, length: number): number {
var h = 0;
var l = offset + length;
for (var i = offset; i < l; i++) {
h = (Math.imul(31, h)|0 + array[i]|0);
}
return h;
}
function nullArray(capacity) {
var array = new Array(capacity);
for (var i = 0; i < capacity; i++) {
array[i] = null;
}
return array;
}
export class TypedArrayHashtable {
table: TypedArrayHashtableEntry []
count: number = 0;
private threshold: number;
private static loadFactorPercent = 75;
constructor(initialCapacity: number) {
release || assert(initialCapacity >= 0, "bad initialCapacity in Uint8Hashtable constructor");
if (initialCapacity == 0) {
initialCapacity = 1;
}
this.table = nullArray(initialCapacity);
this.threshold = ((initialCapacity * TypedArrayHashtable.loadFactorPercent) / 100) | 0;
}
contains(key: Uint8Array) {
var table = this.table;
var hash = arrayHash(key);
var index = (hash & 0x7FFFFFFF) % table.length;
for (var e = table[index]; e !== null; e = e.next) {
if ((e.hash === hash) && arrayEqual(e.key, key)) {
return true;
}
}
return false;
}
getByRange(key: Uint8Array, offset: number, length: number) {
var table = this.table;
var hash = arrayRangeHash(key, offset, length);
var index = (hash & 0x7FFFFFFF) % table.length;
for (var e = table[index]; e !== null; e = e.next) {
if ((e.hash === hash) && arrayRangeEqual(key, offset, length, e.key)) {
return e.value;
}
}
return null;
}
get(key: Uint8Array) {
var table = this.table;
var hash = arrayHash(key);
var index = (hash & 0x7FFFFFFF) % table.length;
for (var e = table[index]; e !== null; e = e.next) {
if ((e.hash === hash) && arrayEqual(e.key, key)) {
return e.value;
}
}
return null;
}
put(key: Uint8Array, value: any) {
// Make sure the value is not null
release || assert(value !== null, "bad value in Uin8Hashtable put");
// Makes sure the key is not already in the hashtable.
var table = this.table;
var hash = arrayHash(key);
var index = (hash & 0x7FFFFFFF) % table.length;
for (var e = table[index]; e !== null; e = e.next) {
if ((e.hash === hash) && arrayEqual(e.key, key)) {
var old = e.value;
e.value = value;
return old;
}
}
if (this.count >= this.threshold) {
// Rehash the table if the threshold is exceeded
this.rehash();
return this.put(key, value);
}
// Creates the new entry.
var e = new TypedArrayHashtableEntry();
e.hash = hash;
e.key = key;
e.value = value;
e.next = table[index];
table[index] = e;
this.count++;
return null;
}
rehash() {
var oldCapacity = this.table.length;
var oldTable = this.table;
var newCapacity = oldCapacity * 2 + 1;
var newTable = nullArray(newCapacity);
this.threshold = ((newCapacity * TypedArrayHashtable.loadFactorPercent) / 100) | 0;
this.table = newTable;
for (var i = oldCapacity; i-- > 0;) {
for (var old = oldTable[i]; old !== null;) {
var e = old;
old = old.next;
var index = (e.hash & 0x7FFFFFFF) % newCapacity;
e.next = newTable[index];
newTable[index] = e;
}
}
}
}
}