summaryrefslogtreecommitdiff
path: root/js/src/Ice/HashMap.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/Ice/HashMap.js')
-rw-r--r--js/src/Ice/HashMap.js417
1 files changed, 417 insertions, 0 deletions
diff --git a/js/src/Ice/HashMap.js b/js/src/Ice/HashMap.js
new file mode 100644
index 00000000000..aa09448c85b
--- /dev/null
+++ b/js/src/Ice/HashMap.js
@@ -0,0 +1,417 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+(function(global){
+ require("Ice/Class");
+ require("Ice/StringUtil");
+
+ var Slice = global.Slice || {};
+ var Ice = global.Ice || {};
+
+ var StringUtil = Ice.StringUtil;
+
+ function setInternal(map, key, value, hash, index)
+ {
+ //
+ // Search for an entry with the same key.
+ //
+ for(var e = map._table[index]; e !== null; e = e._nextInBucket)
+ {
+ if(e._hash === hash && map.keysEqual(key, e._key))
+ {
+ //
+ // Found a match, update the value.
+ //
+ e._value = value;
+ return undefined;
+ }
+ }
+
+ //
+ // No match found, add a new entry.
+ //
+ map.add(key, value, hash, index);
+ return undefined;
+ }
+
+ var HashMap = Ice.Class({
+ __init__: function(h)
+ {
+ this._size = 0;
+ this._head = null;
+ this._initialCapacity = 32;
+ this._loadFactor = 0.75;
+ this._table = [];
+ this._keyComparator = function(k1, k2) { return k1 === k2; };
+ this._valueComparator = function(k1, k2) { return k1 === k2; };
+
+ var i, length;
+ if(h === undefined || h === null || h._size === 0)
+ {
+ this._threshold = this._initialCapacity * this._loadFactor;
+ for(i = 0; i < this._initialCapacity; i++)
+ {
+ this._table[i] = null;
+ }
+ }
+ else
+ {
+ this._threshold = h._threshold;
+ this._keyComparator = h._keyComparator;
+ this._valueComparator = h._valueComparator;
+ length = h._table.length;
+ this._table.length = length;
+ for(i = 0; i < length; i++)
+ {
+ this._table[i] = null;
+ }
+ this.merge(h);
+ }
+ },
+ set: function(key, value)
+ {
+ var hash = this.computeHash(key);
+
+ var index = this.hashIndex(hash, this._table.length);
+
+ return setInternal(this, key, value, hash, index);
+ },
+ get: function(key)
+ {
+ var e = this.findEntry(key, this.computeHash(key));
+ return e !== undefined ? e._value : undefined;
+ },
+ has: function(key)
+ {
+ return this.findEntry(key, this.computeHash(key)) !== undefined;
+ },
+ delete: function(key)
+ {
+ var hash = this.computeHash(key);
+
+ var index = this.hashIndex(hash, this._table.length);
+
+ //
+ // Search for an entry with the same key.
+ //
+ var prev = null;
+ for(var e = this._table[index]; e !== null; e = e._nextInBucket)
+ {
+ if(e._hash === hash && this.keysEqual(key, e._key))
+ {
+ //
+ // Found a match.
+ //
+ this._size--;
+
+ //
+ // Remove from bucket.
+ //
+ if(prev !== null)
+ {
+ prev._nextInBucket = e._nextInBucket;
+ }
+ else
+ {
+ this._table[index] = e._nextInBucket;
+ }
+
+ //
+ // Unlink the entry.
+ //
+ if(e._prev !== null)
+ {
+ e._prev._next = e._next;
+ }
+ if(e._next !== null)
+ {
+ e._next._prev = e._prev;
+ }
+
+ if(this._head === e)
+ {
+ this._head = e._next;
+ }
+
+ return e._value;
+ }
+
+ prev = e;
+ }
+
+ return undefined;
+ },
+ clear: function()
+ {
+ for(var i = 0; i < this._table.length; ++i)
+ {
+ this._table[i] = null;
+ }
+ this._head = null;
+ this._size = 0;
+ },
+ forEach: function(fn, obj)
+ {
+ obj = obj === undefined ? fn : obj;
+ for(var e = this._head; e !== null; e = e._next)
+ {
+ fn.call(obj, e._key, e._value);
+ }
+ },
+ equals: function(other)
+ {
+ if(other === null || !(other instanceof HashMap) || this._size !== other._size)
+ {
+ return false;
+ }
+
+ for(var e = this._head; e !== null; e = e._next)
+ {
+ var oe = other.findEntry(e._key, e._hash);
+ if(oe === undefined || !this.valuesEqual(e._value, oe._value))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ },
+ clone: function()
+ {
+ return new HashMap(this);
+ },
+ merge: function(from)
+ {
+ for(var e = from._head; e !== null; e = e._next)
+ {
+ setInternal(this, e._key, e._value, e._hash, this.hashIndex(e._hash, this._table.length));
+ }
+ },
+ add: function(key, value, hash, index)
+ {
+ //
+ // Create a new table entry.
+ //
+ /*
+ var e =
+ {
+ key: key,
+ value: value,
+ prev: null,
+ next: null,
+ _hash: hash
+ }
+ */
+ var e = Object.create(null, {
+ "key": {
+ enumerable: true,
+ get: function() { return this._key; }
+ },
+ "value": {
+ enumerable: true,
+ get: function() { return this._value; }
+ },
+ "next": {
+ enumerable: true,
+ get: function() { return this._next; }
+ },
+ "_key": {
+ enumerable: false,
+ writable: true,
+ value: key
+ },
+ "_value": {
+ enumerable: false,
+ writable: true,
+ value: value
+ },
+ "_prev": {
+ enumerable: false,
+ writable: true,
+ value: null
+ },
+ "_next": {
+ enumerable: false,
+ writable: true,
+ value: null
+ },
+ "_nextInBucket": {
+ enumerable: false,
+ writable: true,
+ value: null
+ },
+ "_hash": {
+ enumerable: false,
+ writable: true,
+ value: hash
+ }
+ });
+ e._nextInBucket = this._table[index];
+ this._table[index] = e;
+
+ e._next = this._head;
+ if(this._head !== null)
+ {
+ this._head._prev = e;
+ }
+ this._head = e;
+
+ this._size++;
+ if(this._size >= this._threshold)
+ {
+ this.resize(this._table.length * 2);
+ }
+ },
+ resize: function(capacity)
+ {
+ var oldTable = this._table;
+
+ var newTable = [];
+ for(var i = 0; i < capacity; i++)
+ {
+ newTable[i] = null;
+ }
+
+ //
+ // Re-assign all entries to buckets.
+ //
+ for(var e = this._head; e !== null; e = e._next)
+ {
+ var index = this.hashIndex(e._hash, capacity);
+ e._nextInBucket = newTable[index];
+ newTable[index] = e;
+ }
+
+ this._table = newTable;
+ this._threshold = (capacity * this._loadFactor);
+ },
+ findEntry: function(key, hash)
+ {
+ var index = this.hashIndex(hash, this._table.length);
+
+ //
+ // Search for an entry with the same key.
+ //
+ for(var e = this._table[index]; e !== null; e = e._nextInBucket)
+ {
+ if(e._hash === hash && this.keysEqual(key, e._key))
+ {
+ return e;
+ }
+ }
+
+ return undefined;
+ },
+ hashIndex: function(hash, len)
+ {
+ return hash & (len - 1);
+ },
+ computeHash: function(v)
+ {
+ if(typeof(v.hashCode) === "function")
+ {
+ return v.hashCode();
+ }
+
+ var hash = 0;
+ var type = typeof(v);
+ if(type === "string" || v instanceof String)
+ {
+ hash = StringUtil.hashCode(v);
+ }
+ else if(type === "number" || v instanceof Number)
+ {
+ hash = v.toFixed(0);
+ }
+ else if(type === "boolean" || v instanceof Boolean)
+ {
+ hash = v ? 1 : 0;
+ }
+ else if(v !== null)
+ {
+ throw "cannot compute hash for value of type " + type;
+ }
+ return hash;
+ },
+ keysEqual: function(k1, k2)
+ {
+ return this._keyComparator.call(this._keyComparator, k1, k2);
+ },
+ valuesEqual: function(v1, v2)
+ {
+ return this._valueComparator.call(this._valueComparator, v1, v2);
+ }
+ });
+
+ var prototype = HashMap.prototype;
+
+ Object.defineProperty(prototype, "size", {
+ get: function() { return this._size; }
+ });
+
+ Object.defineProperty(prototype, "entries", {
+ get: function() { return this._head; }
+ });
+
+ Object.defineProperty(prototype, "keyComparator", {
+ get: function() { return this._keyComparator; },
+ set: function(fn) { this._keyComparator = fn; }
+ });
+
+ Object.defineProperty(prototype, "valueComparator", {
+ get: function() { return this._valueComparator; },
+ set: function(fn) { this._valueComparator = fn; }
+ });
+
+ Object.defineProperty(HashMap, "compareEquals", {
+ get: function() { return function(o1, o2) { return o1.equals(o2); }; }
+ });
+
+ Slice.defineDictionary = function(module, name, helperName, keyHelper, valueHelper, fixed, useEquals, valueType)
+ {
+ if(useEquals)
+ {
+ //
+ // Define a constructor function for a dictionary whose key type requires
+ // comparison using an equals() method instead of the native comparison
+ // operators.
+ //
+ module[name] = function(h)
+ {
+ var r = new HashMap(h);
+ r.keyComparator = HashMap.compareEquals;
+ return r;
+ };
+ }
+ else
+ {
+ module[name] = HashMap;
+ }
+
+ var helper = null;
+ Object.defineProperty(module, helperName,
+ {
+ get: function()
+ {
+ if(helper === null)
+ {
+ /*jshint -W061 */
+ helper = Ice.StreamHelpers.generateDictHelper(eval(keyHelper), eval(valueHelper), fixed,
+ eval(valueType), module[name]);
+ /*jshint +W061 */
+ }
+ return helper;
+ }
+ });
+ };
+
+ Ice.HashMap = HashMap;
+ global.Slice = Slice;
+ global.Ice = Ice;
+}(typeof (global) === "undefined" ? window : global));