• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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}