Add a new Hash module
A simple implementation of the ES6 Map proposal, internally done as a hash table, using System.addressOf() to support keys that are arbitrary objects. Should help replacing linear searches in various places around the shell. https://bugzilla.gnome.org/show_bug.cgi?id=685926
This commit is contained in:
parent
d14cf1e80b
commit
daf8be9f78
@ -28,6 +28,7 @@ nobase_dist_js_DATA = \
|
||||
misc/extensionUtils.js \
|
||||
misc/fileUtils.js \
|
||||
misc/gnomeSession.js \
|
||||
misc/hash.js \
|
||||
misc/history.js \
|
||||
misc/jsParse.js \
|
||||
misc/loginManager.js \
|
||||
|
141
js/misc/hash.js
Normal file
141
js/misc/hash.js
Normal file
@ -0,0 +1,141 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Lang = imports.lang;
|
||||
const System = imports.system;
|
||||
|
||||
const Params = imports.misc.params;
|
||||
|
||||
// This is an implementation of EcmaScript SameValue algorithm,
|
||||
// which returns true if two values are not observably distinguishable.
|
||||
// It was taken from http://wiki.ecmascript.org/doku.php?id=harmony:egal
|
||||
//
|
||||
// In the future, we may want to use the 'is' operator instead.
|
||||
function _sameValue(x, y) {
|
||||
if (x === y) {
|
||||
// 0 === -0, but they are not identical
|
||||
return x !== 0 || 1 / x === 1 / y;
|
||||
}
|
||||
|
||||
// NaN !== NaN, but they are identical.
|
||||
// NaNs are the only non-reflexive value, i.e., if x !== x,
|
||||
// then x is a NaN.
|
||||
// isNaN is broken: it converts its argument to number, so
|
||||
// isNaN("foo") => true
|
||||
return x !== x && y !== y;
|
||||
}
|
||||
|
||||
const _hashers = {
|
||||
object: function(o) { return o ? System.addressOf(o) : 'null'; },
|
||||
function: function(f) { return System.addressOf(f); },
|
||||
string: function(s) { return s; },
|
||||
number: function(n) { return String(n); },
|
||||
undefined: function() { return 'undefined'; },
|
||||
};
|
||||
|
||||
/* Map is meant to be similar in usage to ES6 Map, which is
|
||||
described at http://wiki.ecmascript.org/doku.php?id=harmony:simple_maps_and_sets,
|
||||
without requiring more than ES5 + Gjs extensions.
|
||||
|
||||
Known differences from other implementations:
|
||||
Polyfills around the web usually implement HashMaps for
|
||||
primitive values and reversed WeakMaps for object keys,
|
||||
but we want real maps with real O(1) semantics in all cases,
|
||||
and the easiest way is to have different hashers for different
|
||||
types.
|
||||
|
||||
Known differences from the ES6 specification:
|
||||
- Map is a Lang.Class, not a ES6 class, so inheritance,
|
||||
prototype, sealing, etc. work differently.
|
||||
- items(), keys() and values() don't return iterators,
|
||||
they return actual arrays, so they incur a full copy everytime
|
||||
they're called, and they don't see changes if you mutate
|
||||
the table while iterating
|
||||
(admittedly, the ES6 spec is a bit unclear on this, and
|
||||
the reference code would just blow up)
|
||||
*/
|
||||
const Map = new Lang.Class({
|
||||
Name: 'Map',
|
||||
|
||||
_init: function(iterable) {
|
||||
this._pool = { };
|
||||
|
||||
if (iterable) {
|
||||
for (let i = 0; i < iterable.length; i++) {
|
||||
let [key, value] = iterable[i];
|
||||
this.set(key, value);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_hashKey: function(key) {
|
||||
let type = typeof(key);
|
||||
return type + ':' + _hashers[type](key);
|
||||
},
|
||||
|
||||
_internalGet: function(key) {
|
||||
let hash = this._hashKey(key);
|
||||
let node = this._pool[hash];
|
||||
|
||||
if (node && _sameValue(node.key, key))
|
||||
return [true, node.value];
|
||||
else
|
||||
return [false, null];
|
||||
},
|
||||
|
||||
get: function(key) {
|
||||
return this._internalGet(key)[1];
|
||||
},
|
||||
|
||||
has: function(key) {
|
||||
return this._internalGet(key)[0];
|
||||
},
|
||||
|
||||
set: function(key, value) {
|
||||
let hash = this._hashKey(key);
|
||||
let node = this._pool[hash];
|
||||
|
||||
if (node) {
|
||||
node.key = key;
|
||||
node.value = value;
|
||||
} else {
|
||||
this._pool[hash] = { key: key, value: value };
|
||||
}
|
||||
},
|
||||
|
||||
delete: function(key) {
|
||||
let hash = this._hashKey(key);
|
||||
let node = this._pool[hash];
|
||||
|
||||
if (node && _sameValue(node.key, key)) {
|
||||
delete this._pool[hash];
|
||||
return [node.key, node.value];
|
||||
} else {
|
||||
return [null, null];
|
||||
}
|
||||
},
|
||||
|
||||
keys: function() {
|
||||
let pool = this._pool;
|
||||
return Object.getOwnPropertyNames(pool).map(function(hash) {
|
||||
return pool[hash].key;
|
||||
});
|
||||
},
|
||||
|
||||
values: function() {
|
||||
let pool = this._pool;
|
||||
return Object.getOwnPropertyNames(pool).map(function(hash) {
|
||||
return pool[hash].value;
|
||||
});
|
||||
},
|
||||
|
||||
items: function() {
|
||||
let pool = this._pool;
|
||||
return Object.getOwnPropertyNames(pool).map(function(hash) {
|
||||
return [pool[hash].key, pool[hash].value];
|
||||
});
|
||||
},
|
||||
|
||||
size: function() {
|
||||
return Object.getOwnPropertyNames(this._pool).length;
|
||||
},
|
||||
});
|
Loading…
Reference in New Issue
Block a user