TouchDevelop/rt/hashtable.ts

205 строки
6.2 KiB
TypeScript

///<reference path='refs.ts'/>
module TDev {
export class HashtableEntry
{
public value:any;
constructor(public key:any, public next:HashtableEntry) {
}
}
export class Hashtable
{
static primeSizes = [13, 29, 59, 127, 257, 521, 1049, 2099, 4201, 8419, 16843, 33703, 67409, 134837,
269683, 539389, 1078787, 2157587, 4315183, 8630387, 17260781, 34521589, 69043189, 138086407,
276172823, 552345671, 1104691373, 2209382761];
private entries:HashtableEntry[];
private entryCount = 0;
constructor(private getHashCode:(v:any)=>number, private equals:(a:any, b:any)=>boolean, private initialSize = 13) {
}
public count() { return this.entryCount; }
private deleteKey(k: any): HashtableEntry {
if (!this.entries) return null;
var h = (this.getHashCode(k) & 0x7fffffff) % this.entries.length;
var e0 = this.entries[h];
if (!e0) return null;
// the first element in the bucket
if (this.equals(k, e0.key)) {
this.entries[h] = e0.next;
e0.next = null;
this.entryCount--;
return e0;
}
// now we want to find the element _before_ the one with key
var e = e0;
while (e && e.next && !this.equals(k, e.next.key)) e = e.next;
if (!e.next) return null;
var found = e.next;
e.next = found.next;
found.next = null;
this.entryCount--;
return found;
}
public remove(k: any): HashtableEntry {
return this.deleteKey(k);
}
private lookup(k:any, addNew = false)
{
if (!this.entries) {
if (!addNew) return null;
this.entries = [];
for (var i = 0; i < this.initialSize; ++i)
this.entries[i] = null;
}
var h = (this.getHashCode(k) & 0x7fffffff) % this.entries.length;
var e0 = this.entries[h];
var e = e0;
while (e && !this.equals(k, e.key)) e = e.next;
if (!e && addNew) {
e = new HashtableEntry(k, e0);
this.entryCount++;
this.entries[h] = e;
if (this.entryCount > this.entries.length + this.entries.length) {
this.rehash();
}
}
return e;
}
private sizeAtLeast(n:number)
{
for (var i = 0; i < Hashtable.primeSizes.length; ++i)
if (Hashtable.primeSizes[i] > n)
return Hashtable.primeSizes[i];
return 0;
}
private rehash()
{
var size = this.sizeAtLeast(this.entries.length);
if (size == 0) return; // over 4G entries?
var oldEntries = this.entries;
this.entries = [];
for (var i = 0; i < size; ++i)
this.entries[i] = null;
for (var i = 0; i < oldEntries.length; ++i) {
var next:any;
for (var e = oldEntries[i]; e; e = next) {
next = e.next;
var h = (this.getHashCode(e.key) & 0x7fffffff) % this.entries.length;
e.next = this.entries[h];
this.entries[h] = e;
}
}
}
private forEachEntry(f:(e:HashtableEntry)=>void)
{
if (!this.entries) return;
for (var i = 0; i < this.entries.length; ++i)
for (var e = this.entries[i]; e; e = e.next)
f(e);
}
public forEach(f: (k, v) => void )
{
this.forEachEntry(e => f(e.key, e.value));
}
private mapEntries(f:(e:HashtableEntry)=>any)
{
var res = []
if (this.entries) {
for (var i = 0; i < this.entries.length; ++i)
for (var e = this.entries[i]; e; e = e.next)
res.push(f(e));
}
return res;
}
public keys() { return this.mapEntries((e) => e.key); }
public pairs() { return this.mapEntries((e) => { return { key: e.key, value: e.value } }); }
public filteredValues(filter:(v:any)=>boolean) {
var res = []
if (this.entries) {
for (var i = 0; i < this.entries.length; ++i)
for (var e = this.entries[i]; e; e = e.next)
{
var val = e.value;
if(filter(val))
res.push(val);
}
}
return res;
}
public countFiltered(filter:(v:any)=>boolean):number
{
var count = 0
if (this.entries) {
for (var i = 0; i < this.entries.length; ++i)
for (var e = this.entries[i]; e; e = e.next)
{
var val = e.value;
if(filter(val))
count = count + 1;
}
}
return count;
}
public set(k:any, v:any)
{
var e = this.lookup(k, true);
e.value = v;
}
public get(k:any)
{
var e = this.lookup(k);
if (!e) return undefined;
return e.value;
}
public clear()
{
this.entries = null
this.entryCount = 0
}
static stringHash(s:string) : number
{
var res = 5381;
for (var i = 0; i < s.length; ++i)
res = (((res + (res << 5)) | 0) + s.charCodeAt(i)) | 0;
return res;
}
static stringEq(a:string, b:string) { return a == b; }
static forStrings() { return new Hashtable(Hashtable.stringHash, Hashtable.stringEq); }
static jsonHash(v:any) { return Hashtable.stringHash(JSON.stringify(v)); }
static jsonEq(a:any, b:any) { return (a == b) || JSON.stringify(a) == JSON.stringify(b); }
static forJson() { return new Hashtable(Hashtable.jsonHash, Hashtable.jsonEq); }
}
}