1// Copyright (c) Microsoft. All rights reserved. Licensed under the Apache License, Version 2.0. 2// See LICENSE.txt in the project root for complete license information. 3 4///<reference path='typescript.ts' /> 5 6module TypeScript { 7 8 export class BlockIntrinsics { 9 public prototype = undefined; 10 public toString = undefined; 11 public toLocaleString = undefined; 12 public valueOf = undefined; 13 public hasOwnProperty = undefined; 14 public propertyIsEnumerable = undefined; 15 public isPrototypeOf = undefined; 16 17 constructor () { 18 // initialize the 'constructor' field 19 this["constructor"] = undefined; 20 } 21 } 22 23 export interface IHashTable { 24 getAllKeys(): string[]; 25 add(key: string, data): boolean; 26 addOrUpdate(key: string, data): boolean; 27 map(fn: (k: string, v, c) => void , context): void; 28 every(fn: (k: string, v, c) => boolean, context): boolean; 29 some(fn: (k: string, v, c) => boolean, context): boolean; 30 count(): number; 31 lookup(key: string): any; 32 } 33 34 export class StringHashTable implements IHashTable { 35 public itemCount = 0; 36 public table = <any>(<any> new BlockIntrinsics()); 37 38 public getAllKeys(): string[]{ 39 var result: string[] = []; 40 for (var k in this.table) { 41 if (this.table[k] != undefined) { 42 result[result.length] = k; 43 } 44 } 45 return result; 46 } 47 48 public add(key: string, data): boolean { 49 if (this.table[key] != undefined) { 50 return false; 51 } 52 this.table[key] = data; 53 this.itemCount++; 54 return true; 55 } 56 57 public addOrUpdate(key: string, data): boolean { 58 if (this.table[key] != undefined) { 59 this.table[key] = data; 60 return false; 61 } 62 this.table[key] = data; 63 this.itemCount++; 64 return true; 65 } 66 67 public map(fn: (k: string, v, c) => void , context) { 68 for (var k in this.table) { 69 var data = this.table[k]; 70 if (data != undefined) { 71 fn(k, this.table[k], context); 72 } 73 } 74 } 75 76 public every(fn: (k: string, v, c) => boolean, context) { 77 for (var k in this.table) { 78 var data = this.table[k]; 79 if (data != undefined) { 80 if (!fn(k, this.table[k], context)) { 81 return false; 82 } 83 } 84 } 85 return true; 86 } 87 88 public some(fn: (k: string, v, c) => boolean, context) { 89 for (var k in this.table) { 90 var data = this.table[k]; 91 if (data != undefined) { 92 if (fn(k, this.table[k], context)) { 93 return true; 94 } 95 } 96 } 97 return false; 98 } 99 100 public count(): number { return this.itemCount; } 101 102 public lookup(key: string) { 103 var data = this.table[key]; 104 if (data != undefined) { 105 return data; 106 } 107 else { 108 return (null); 109 } 110 } 111 } 112 113 // The resident table is expected to reference the same table object, whereas the 114 // transientTable may reference different objects over time 115 // REVIEW: WARNING: For performance reasons, neither the primary nor secondary table may be null 116 export class DualStringHashTable implements IHashTable { 117 118 public insertPrimary = true; 119 120 constructor (public primaryTable: IHashTable, 121 public secondaryTable: IHashTable) { } 122 123 public getAllKeys(): string[]{ 124 return this.primaryTable.getAllKeys().concat(this.secondaryTable.getAllKeys()); 125 } 126 127 public add(key: string, data): boolean { 128 if (this.insertPrimary) { 129 return this.primaryTable.add(key, data); 130 } 131 else { 132 return this.secondaryTable.add(key, data); 133 } 134 } 135 136 public addOrUpdate(key: string, data): boolean { 137 if (this.insertPrimary) { 138 return this.primaryTable.addOrUpdate(key, data); 139 } 140 else { 141 return this.secondaryTable.addOrUpdate(key, data); 142 } 143 } 144 145 public map(fn: (k: string, v, c) => void , context) { 146 this.primaryTable.map(fn, context); 147 this.secondaryTable.map(fn, context); 148 } 149 150 public every(fn: (k: string, v, c) => boolean, context) { 151 return this.primaryTable.every(fn, context) && this.secondaryTable.every(fn, context); 152 } 153 154 public some(fn: (k: string, v, c) => boolean, context) { 155 return this.primaryTable.some(fn, context) || this.secondaryTable.some(fn, context); 156 } 157 158 public count() { 159 return this.primaryTable.count() + this.secondaryTable.count(); 160 } 161 162 public lookup(key: string) { 163 var data = this.primaryTable.lookup(key); 164 if (data != undefined) { 165 return data; 166 } 167 else { 168 return this.secondaryTable.lookup(key); 169 } 170 } 171 } 172 173 export function numberHashFn(key: number): number { 174 var c2 = 0x27d4eb2d; // a prime or an odd constant 175 key = (key ^ 61) ^ (key >>> 16); 176 key = key + (key << 3); 177 key = key ^ (key >>> 4); 178 key = key * c2; 179 key = key ^ (key >>> 15); 180 return key; 181 } 182 183 export function combineHashes(key1: number, key2: number) { 184 return key2 ^ ((key1 >> 5) + key1); 185 } 186 187 export class HashEntry { 188 public next: HashEntry; 189 190 constructor (public key, public data) { } 191 } 192 193 export class HashTable { 194 public itemCount: number = 0; 195 public table = new HashEntry[]; 196 197 constructor (public size: number, public hashFn: (key) =>number, 198 public equalsFn: (key1, key2) =>boolean) { 199 for (var i: number = 0; i < this.size; i++) { 200 this.table[i] = null; 201 } 202 } 203 204 public add(key, data): boolean { 205 var current: HashEntry; 206 var entry: HashEntry = new HashEntry(key, data); 207 var val: number = this.hashFn(key); 208 val = val % this.size; 209 210 for (current = this.table[val]; current != null ; current = current.next) { 211 if (this.equalsFn(key, current.key)) { 212 return false; 213 } 214 } 215 entry.next = this.table[val]; 216 this.table[val] = entry; 217 this.itemCount++; 218 return true; 219 } 220 221 public remove(key) { 222 var current: HashEntry; 223 var val: number = this.hashFn(key); 224 val = val % this.size; 225 var result = null; 226 var prevEntry: HashEntry = null; 227 228 for (current = this.table[val]; current != null ; current = current.next) { 229 if (this.equalsFn(key, current.key)) { 230 result = current.data; 231 this.itemCount--; 232 if (prevEntry) { 233 prevEntry.next = current.next; 234 } 235 else { 236 this.table[val] = current.next; 237 } 238 break; 239 } 240 prevEntry = current; 241 } 242 return result; 243 } 244 245 public count(): number { return this.itemCount; } 246 247 public lookup(key) { 248 var current: HashEntry; 249 var val: number = this.hashFn(key); 250 val = val % this.size; 251 for (current = this.table[val]; current != null ; current = current.next) { 252 if (this.equalsFn(key, current.key)) { 253 return (current.data); 254 } 255 } 256 return (null); 257 } 258 } 259 260 // Simple Hash table with list of keys and values matching each other at the given index 261 export class SimpleHashTable { 262 private keys = []; 263 private values = []; 264 265 public lookup(key, findValue?: boolean) { 266 var searchArray = this.keys; 267 if (findValue) { 268 searchArray = this.values; 269 } 270 271 for (var i = 0; i < searchArray.length; i++) { 272 if (searchArray[i] == key) { 273 return { 274 key: this.keys[i], 275 data: this.values[i], 276 }; 277 } 278 } 279 return null; 280 } 281 282 public add(key, data): boolean { 283 var lookupData = this.lookup(key); 284 if (lookupData) { 285 return false; 286 } 287 288 this.keys[this.keys.length] = key; 289 this.values[this.values.length] = data; 290 291 return true; 292 } 293 } 294 295}