├── documentation
├── hamt-logo.ai
└── hamt-logo.png
├── .travis.yml
├── .editorconfig
├── .gitignore
├── gulpfile.js
├── tests
├── fold.js
├── forEach.js
├── keys.js
├── has.js
├── values.js
├── tryGet.js
├── modify.js
├── entries.js
├── isEmpty.js
├── count.js
├── set.js
├── mutation.js
└── remove.js
├── CHANGELOG.md
├── package.json
├── LICENSE
├── console.html
├── README.md
├── lib
└── hamt.js
├── hamt.js
└── hamt.js.map
/documentation/hamt-logo.ai:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattbierner/hamt_plus/HEAD/documentation/hamt-logo.ai
--------------------------------------------------------------------------------
/documentation/hamt-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattbierner/hamt_plus/HEAD/documentation/hamt-logo.png
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "6"
4 | before_script:
5 | - npm install -g gulp
6 | script: gulp
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*.js]
4 | indent_style = space
5 | indent_size = 4
6 |
7 | [*.json]
8 | indent_style = space
9 | indent_size = 4
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Project Files #
2 | #################
3 | .project
4 | node_modules
5 |
6 | # OS Files #
7 | ############
8 | .DS_Store*
9 | ehthumbs.db
10 | Icon?
11 | Thumbs.db
12 |
13 | # Temp Files #
14 | ##############
15 | *~
16 | *.tmp*
17 | *.orig
18 | *.cache
19 | *.log
20 |
21 |
22 | # Jekyll #
23 | ##########
24 | _site/
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var babel = require("gulp-babel");
2 | var gulp = require("gulp");
3 | var sourcemaps = require("gulp-sourcemaps");
4 | var plumber = require('gulp-plumber');
5 |
6 | gulp.task("default", () =>
7 | gulp.src("lib/*.js")
8 | .pipe(plumber())
9 | .pipe(sourcemaps.init())
10 | .pipe(babel({
11 | presets: ['es2015']
12 | }))
13 | .pipe(sourcemaps.write("."))
14 | .pipe(gulp.dest("./")));
15 |
16 |
--------------------------------------------------------------------------------
/tests/fold.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const hamt = require('../hamt');
3 | const assert = require('chai').assert;
4 |
5 | describe('fold', () => {
6 | it('should handle large folds correctly', () => {
7 | let h = hamt.make();
8 |
9 | let sum = 0;
10 | for (let i = 0; i < 20000; ++i) {
11 | h = h.set(i + '', i);
12 | sum += i;
13 | }
14 |
15 | assert.strictEqual(sum, h.fold((p, c) => p + c, 0));
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # ChangeLog #
2 |
3 | ## 1.0.2 - March 27, 2017
4 | * Fixed mutation splice out. Thanks @wshager!
5 |
6 | ## 1.0.1 - January 27, 2016
7 | * Fixed bug when array nodes are packed during removal.
8 |
9 | ## 1.0.0 - January 26, 2016
10 | * Refork from Hamt 2.1
11 | * Duplicates existing Hamt 2.x API.
12 | * Added constant time size queries.
13 |
14 | ## 0.0.6 - September 27, 2014
15 | * Fixed collision nodes on unexpanded branch not being expanded on insertions
16 | further down branch. Thanks raymond-w-ko for reporting this and providing test
17 | data for `hamt`.
18 |
19 | ## 0.0.5 - Aug 22, 2014
20 | * Fixed `CollisionNode` updates not using node values.
21 |
22 | ## 0.0.4 - Aug 21, 2014
23 | * Fixed `CollisionNode` not having an `edit` member.
24 |
25 | ## 0.0.3 - Aug 19, 2014
26 | * Revert `fold` to fix performance degradation.
27 |
28 | ## 0.0.0 - Aug 18, 2014
29 | * Initial release.
30 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hamt_plus",
3 | "version": "1.0.2",
4 | "description": "Fork of HAMT that adds transient mutation and support for custom key types",
5 | "keywords": [
6 | "trie",
7 | "hamt",
8 | "map",
9 | "persistent",
10 | "data structure",
11 | "hash"
12 | ],
13 | "author": "Matt Bierner",
14 | "license": "MIT",
15 | "repository": {
16 | "type": "git",
17 | "url": "https://github.com/mattbierner/hamt_plus.git"
18 | },
19 | "main": "hamt.js",
20 | "files": [
21 | "hamt.js",
22 | "hamt.js.map"
23 | ],
24 | "dependencies": {},
25 | "devDependencies": {
26 | "babel-preset-es2015": "^6.24.0",
27 | "gulp": "^3.9.0",
28 | "gulp-babel": "^6.1.2",
29 | "gulp-plumber": "^1.1.0",
30 | "gulp-sourcemaps": "^2.4.0",
31 | "chai": "*",
32 | "mocha": "*"
33 | },
34 | "scripts": {
35 | "compile": "gulp",
36 | "test": "mocha tests"
37 | }
38 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (C) 2016 Matt Bierner
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/tests/forEach.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const hamt = require('../hamt');
3 | const assert = require('chai').assert;
4 |
5 | describe('forEach', () => {
6 | it('should noop for empty map', () => {
7 | hamt.forEach(() => {
8 | assert.ok(false);
9 | }, hamt.make());
10 | });
11 |
12 |
13 | it('should be invoked for every element in map', () => {
14 | let h = hamt.make().set('a', 3).set('b', 5);
15 |
16 | let foundSum = 0;
17 | h.forEach(x => {
18 | foundSum += x;
19 | });
20 |
21 | assert.strictEqual(8, foundSum);
22 | });
23 |
24 | it('should handle large map correctly', () => {
25 | let h = hamt.make();
26 |
27 | let sum = 0;
28 | for (let i = 0; i < 20000; ++i) {
29 | h = h.set(i + '', i);
30 | sum += i;
31 | }
32 |
33 | let foundSum = 0;
34 | h.forEach(x => {
35 | foundSum += x;
36 | });
37 |
38 | assert.strictEqual(sum, foundSum);
39 | });
40 | });
41 |
42 |
--------------------------------------------------------------------------------
/console.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/tests/keys.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const hamt = require('../hamt');
3 | const assert = require('chai').assert;
4 |
5 | describe('keys', () => {
6 | it('should return empty for empty map', () => {
7 | assert.deepEqual([], Array.from(hamt.keys(hamt.make())));
8 | });
9 |
10 | it('should return single key for single element map', () => {
11 | assert.deepEqual(['a'], Array.from(hamt.keys(hamt.make().set('a', 5))));
12 | assert.deepEqual(['b'], Array.from(hamt.make().set('b', 5).keys()));
13 | });
14 |
15 | it('should return all keys for collision', () => {
16 | const h1 = hamt.make()
17 | .setHash(0, 'a', 3)
18 | .setHash(0, 'b', 5);
19 |
20 | assert.sameMembers(['a', 'b'], Array.from(h1.keys()));
21 | });
22 |
23 | it('return correct keys while items are added', () => {
24 | const insert = [
25 | "n", "U", "p", "^", "h", "w", "W", "x", "S", "f", "H", "m", "g",
26 | "l", "b", "_", "V", "Z", "G", "o", "F", "Q", "a", "k", "j", "r",
27 | "B", "A", "y", "\\", "R", "D", "i", "c", "]", "C", "[", "e", "s",
28 | "t", "J", "E", "q", "v", "M", "T", "N", "L", "K", "Y", "d", "P",
29 | "u", "I", "O", "`", "X"];
30 |
31 | let h = hamt.make();
32 | insert.forEach(x => {
33 | h = h.set(x, x);
34 | });
35 |
36 | assert.sameMembers(insert, Array.from(hamt.keys(h)));
37 | });
38 | });
39 |
40 |
--------------------------------------------------------------------------------
/tests/has.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const hamt = require('../hamt');
3 | const assert = require('chai').assert;
4 |
5 | describe('has', () => {
6 | it('should return false for empty map', () => {
7 | assert.strictEqual(false, hamt.has('a', hamt.make()));
8 | assert.strictEqual(false, hamt.has('b', hamt.make()));
9 | });
10 |
11 | it('should return if entry exists for single map', () => {
12 | var h1 = hamt.make().set('a', 3);
13 |
14 | assert.strictEqual(true, h1.has('a'));
15 | assert.strictEqual(false, h1.has('b'));
16 | });
17 |
18 | it('should not depend on stored value', () => {
19 | assert.strictEqual(true, hamt.has('a', hamt.make().set('a', 3)));
20 | assert.strictEqual(true, hamt.has('a', hamt.make().set('a', false)));
21 | assert.strictEqual(true, hamt.has('a', hamt.make().set('a', null)));
22 | assert.strictEqual(true, hamt.has('a', hamt.make().set('a', undefined)));
23 | });
24 |
25 | it('should return if entry exists in map', () => {
26 | var h = hamt.make();
27 | for (let i = 'A'.charCodeAt(0); i < 'z'.charCodeAt(0); i += 2) {
28 | h = h.set(String.fromCharCode(i), i);
29 | }
30 |
31 | for (let i = 'A'.charCodeAt(0); i < 'z'.charCodeAt(0);) {
32 | assert.strictEqual(true, hamt.has(String.fromCharCode(i++), h));
33 | assert.strictEqual(false, hamt.has(String.fromCharCode(i++), h));
34 | }
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/tests/values.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const hamt = require('../hamt');
3 | const assert = require('chai').assert;
4 |
5 | describe('values', () => {
6 | it('should return empty for empty map', () => {
7 | assert.deepEqual([], Array.from(hamt.values(hamt.make())));
8 | });
9 |
10 | it('should return single key for single element map', () => {
11 | assert.deepEqual([3], Array.from(hamt.values(hamt.make().set('a', 3))));
12 | assert.deepEqual([5], Array.from(hamt.make().set('b', 5).values()));
13 | });
14 |
15 | it('should return all values for collision', () => {
16 | const h1 = hamt.make()
17 | .setHash(0, 'a', 3)
18 | .setHash(0, 'b', 5);
19 |
20 | assert.sameMembers([5, 3], Array.from(h1.values()));
21 | });
22 |
23 | it('should return duplicate values', () => {
24 | const h = hamt.make().set('b', 3).set('a', 3);
25 | assert.deepEqual([3, 3], Array.from(hamt.values(h)));
26 | });
27 |
28 | it('return correct values while items are added', () => {
29 | const insert = [
30 | "n", "U", "p", "^", "h", "w", "W", "x", "S", "f", "H", "m", "g",
31 | "l", "b", "_", "V", "Z", "G", "o", "F", "Q", "a", "k", "j", "r",
32 | "B", "A", "y", "\\", "R", "D", "i", "c", "]", "C", "[", "e", "s",
33 | "t", "J", "E", "q", "v", "M", "T", "N", "L", "K", "Y", "d", "P",
34 | "u", "I", "O", "`", "X"];
35 |
36 | let h = hamt.make();
37 | insert.forEach(x => {
38 | h = h.set(x, x);
39 | });
40 |
41 | assert.sameMembers(insert, Array.from(hamt.values(h)));
42 | });
43 | });
44 |
45 |
--------------------------------------------------------------------------------
/tests/tryGet.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const hamt = require('../hamt');
3 | const assert = require('chai').assert;
4 |
5 | describe('tryGet', () => {
6 | it('should return default for empty map', () => {
7 | assert.strictEqual(10, hamt.tryGet(10, 'a', hamt.make()));
8 | assert.strictEqual(10, hamt.make().tryGet(10, 'a'));
9 |
10 | assert.strictEqual(10, hamt.tryGet(10, 'b', hamt.make()));
11 | assert.strictEqual(false, hamt.tryGet(false, 'a', hamt.make()));
12 |
13 | const a = {};
14 | assert.strictEqual(a, hamt.tryGet(a, 'b', hamt.make()));
15 |
16 | });
17 |
18 | it('should return default for non-existant value', () => {
19 | var h1 = hamt.make().set('a', 3);
20 |
21 | assert.strictEqual(3, hamt.tryGet(10, 'a', h1));
22 | assert.strictEqual(10, hamt.tryGet(10, 'b', h1));
23 | });
24 |
25 | it('should uesd custom keyEq', () => {
26 | var h1 = hamt.make({
27 | keyEq: (x, y) => x.value === y.value
28 | })
29 | .set({value: 'a'}, 3)
30 | .set({value: 'b'}, 5);
31 |
32 | assert.strictEqual(3, hamt.get({value: 'a'}, h1));
33 | assert.strictEqual(undefined, hamt.get('a', h1));
34 | assert.strictEqual(5, hamt.get({value: 'b'}, h1));
35 | assert.strictEqual(undefined, hamt.get('b', h1));
36 | });
37 |
38 | it('should work on array nodes correctly', () => {
39 | const insert = [
40 | "n", "U", "p", "^", "h", "w", "W", "x", "S", "f", "H", "m", "g",
41 | "l", "b", "_", "V", "Z", "G", "o", "F", "Q", "a", "k", "j", "r",
42 | "B", "A", "y", "\\", "R", "D", "i", "c", "]", "C", "[", "e", "s",
43 | "t", "J", "E", "q", "v", "M", "T", "N", "L", "K", "Y", "d", "P",
44 | "u", "I", "O", "`", "X"];
45 |
46 | let h = hamt.make();
47 | for (let i = 0; i < insert.length; ++i) {
48 | h = h.setHash(i, insert[i], insert[i]);
49 | }
50 |
51 | assert.strictEqual(insert.length, h.count());
52 |
53 | for (let i = 0; i < insert.length; ++i) {
54 | const x = insert[i];
55 | assert.strictEqual(x, h.tryGetHash(null, i, x));
56 | assert.strictEqual(null, h.tryGetHash(null, i, x + x));
57 | }
58 | });
59 | });
60 |
--------------------------------------------------------------------------------
/tests/modify.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const hamt = require('../hamt');
3 | const assert = require('chai').assert;
4 |
5 | describe('modify', () => {
6 | it('should update entry in single map', () => {
7 | const h = hamt.set('a', 3, hamt.make());
8 | const h1 = hamt.modify(x => x * 2, 'a', h);
9 |
10 | assert.strictEqual(6, hamt.get('a', h1));
11 | assert.strictEqual(3, hamt.get('a', h));
12 | });
13 |
14 | it('should work on with method calls', () => {
15 | const h = hamt.make().set('a', 3);
16 | const h1 = h.modify('a', x => x * 2);
17 |
18 | assert.strictEqual(6, h1.get('a'));
19 | assert.strictEqual(3, h.get('a'));
20 | });
21 |
22 |
23 | it('should insert into empty map', () => {
24 | const h = hamt.make().modify('a', _ => 10);
25 | assert.strictEqual(10, hamt.get('a', h));
26 | });
27 |
28 |
29 | it('should call `f` with zero args on insert', () => {
30 | const h = hamt.modify(function(x) {
31 | assert.strictEqual(0, arguments.length);
32 | assert.strictEqual(undefined, x);
33 | }, 'a', hamt.make());
34 | });
35 |
36 | it('should insert if no value in map matches', () => {
37 | const h = hamt.make().set('a', 3).set('b', 5);
38 | const h1 = hamt.modify(_ => 10, 'c', h);
39 |
40 | assert.strictEqual(3, hamt.get('a', h1));
41 | assert.strictEqual(5, hamt.get('b', h1));
42 | assert.strictEqual(10, hamt.get('c', h1));
43 |
44 | assert.strictEqual(3, hamt.get('a', h));
45 | assert.strictEqual(5, hamt.get('b', h));
46 | assert.strictEqual(undefined, hamt.get('c', h));
47 | });
48 |
49 | it('should modify collision values correctly', () => {
50 | const h1 = hamt.make()
51 | .modifyHash(0, 'a', () => 3)
52 | .modifyHash(0, 'b', () => 5);
53 |
54 | const h3 = h1.modifyHash(0, 'a', x => x * 2);
55 | assert.strictEqual(6, h3.getHash(0, 'a'));
56 | assert.strictEqual(5, h3.getHash(0, 'b'));
57 |
58 | const h4 = h3.modifyHash(0, 'b', x => x * 2);
59 | assert.strictEqual(6, h4.getHash(0, 'a'));
60 | assert.strictEqual(10, h4.getHash(0, 'b'));
61 |
62 | // Non existant
63 | const h5 = h4.modifyHash(0, 'c', _ => 100);
64 | assert.strictEqual(6, h5.getHash(0, 'a'));
65 | assert.strictEqual(10, h5.getHash(0, 'b'));
66 | assert.strictEqual(100, h5.getHash(0, 'c'));
67 | });
68 | });
69 |
--------------------------------------------------------------------------------
/tests/entries.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const hamt = require('../hamt');
3 | const assert = require('chai').assert;
4 |
5 | describe('entries', () => {
6 | it('should be empty for empty map', () => {
7 | const h = hamt.make();
8 | const it = hamt.entries(h);
9 |
10 | let v = it.next();
11 | assert.ok(v.done);
12 |
13 | for (let x of h)
14 | assert.isFalse(true);
15 | });
16 |
17 | it('should visit single child', () => {
18 | const h = hamt.make().set('a', 3);
19 | const it = hamt.entries(h);
20 | let v = it.next();
21 |
22 | assert.deepEqual(['a', 3], v.value);
23 | assert.notOk(v.done);
24 | v = it.next();
25 | assert.ok(v.done);
26 |
27 | assert.deepEqual([['a', 3]], Array.from(h));
28 | });
29 |
30 | it('should visit all children', () => {
31 | const h = hamt.make()
32 | .set('a', 3)
33 | .set('b', 5)
34 | .set('c', 7);
35 |
36 | const expected = [['a', 3], ['b', 5], ['c', 7]];
37 |
38 | {
39 | const it = h.entries();
40 | const results = [];
41 | let v;
42 | for (var i = 0; i < h.count(); ++i) {
43 | v = it.next();
44 | assert.notOk(v.done);
45 | results.push(v.value)
46 | }
47 | v = it.next();
48 | assert.isTrue(v.done);
49 | assert.deepEqual(expected, results);
50 | }
51 | assert.deepEqual(expected, Array.from(h));
52 | });
53 |
54 | it('should handle collisions', () => {
55 | const h = hamt.make()
56 | .setHash(0, 'a', 3)
57 | .setHash(0, 'b', 5)
58 | .setHash(0, 'c', 7)
59 |
60 | const expected = [['b', 5], ['a', 3], ['c', 7]];
61 |
62 | {
63 | const it = h.entries();
64 | const results = [];
65 | let v;
66 | for (var i = 0; i < h.count(); ++i) {
67 | v = it.next();
68 | assert.notOk(v.done);
69 | results.push(v.value)
70 | }
71 | v = it.next();
72 | assert.isTrue(v.done);
73 | assert.deepEqual(expected, results);
74 | }
75 | assert.deepEqual(expected, Array.from(h));
76 | });
77 |
78 | it('should handle large map correctly', () => {
79 | let h = hamt.make();
80 |
81 | let sum = 0;
82 | for (let i = 0; i < 20000; ++i) {
83 | h = h.set(i + '', i);
84 | sum += i;
85 | }
86 |
87 | let foundSum = 0;
88 | for (let x of h)
89 | foundSum += x[1];
90 | assert.strictEqual(sum, foundSum);
91 | });
92 | });
93 |
94 |
--------------------------------------------------------------------------------
/tests/isEmpty.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const hamt = require('../hamt');
3 | const assert = require('chai').assert;
4 |
5 | describe('isEmpty', () => {
6 | it('should return true for empty', () => {
7 | assert.isTrue(hamt.isEmpty(hamt.make()));
8 | assert.isTrue(hamt.make().isEmpty());
9 | });
10 |
11 | it('should false for single element map', () => {
12 | assert.isFalse(hamt.isEmpty(hamt.make().set('a', 4)));
13 | assert.isFalse(hamt.make().set('a', 4).isEmpty());
14 | });
15 |
16 | it('should return false for collision', () => {
17 | const h1 = hamt.make()
18 | .setHash(0, 'a', 3)
19 | .setHash(0, 'b', 5);
20 |
21 | assert.isFalse(hamt.isEmpty(h1));
22 | assert.isFalse(h1.isEmpty());
23 | });
24 |
25 | it('should handle remove correctly', () => {
26 | var h1 = hamt.make()
27 | .set('a', 3)
28 | .set('b', 5);
29 |
30 | var h2 = h1.delete('a');
31 | var h3 = h2.delete('b');
32 |
33 | assert.isFalse(hamt.isEmpty(h1));
34 | assert.isFalse(hamt.isEmpty(h2));
35 | assert.isTrue(hamt.isEmpty(h3));
36 | });
37 |
38 | it('should handle remove collision correctly', () => {
39 | const h1 = hamt.make()
40 | .setHash(0, 'a', 3)
41 | .setHash(0, 'b', 5);
42 |
43 | var h2 = h1.deleteHash(0, 'a');
44 | var h3 = h2.deleteHash(0, 'b');
45 |
46 | assert.isFalse(hamt.isEmpty(h1));
47 | assert.isFalse(hamt.isEmpty(h2));
48 | assert.isTrue(hamt.isEmpty(h3));
49 | });
50 |
51 | it('return correct counts while items are added and removed', () => {
52 | const insert = [
53 | "n", "U", "p", "^", "h", "w", "W", "x", "S", "f", "H", "m", "g",
54 | "l", "b", "_", "V", "Z", "G", "o", "F", "Q", "a", "k", "j", "r",
55 | "B", "A", "y", "\\", "R", "D", "i", "c", "]", "C", "[", "e", "s",
56 | "t", "J", "E", "q", "v", "M", "T", "N", "L", "K", "Y", "d", "P",
57 | "u", "I", "O", "`", "X"];
58 |
59 | const remove = [
60 | "w", "m", "Q", "R", "i", "K", "P", "Y", "D", "g", "y", "L",
61 | "b", "[", "a", "t", "j", "W", "J", "G", "q", "r", "p", "U",
62 | "v", "h", "S", "_", "d", "x", "I", "F", "f", "n", "B", "\\",
63 | "k", "V", "N", "l", "X", "A", "]", "s", "Z", "O", "^", "o",
64 | "`", "H", "E", "e", "M", "u", "T", "c", "C"];
65 |
66 | let h = hamt.make();
67 |
68 | for (let i = 0; i < insert.length; ++i) {
69 | const x = insert[i];
70 | h = h.set(x, x);
71 | assert.isFalse(hamt.isEmpty(h));
72 | }
73 |
74 | for (let i = 0; i < remove.length; ++i) {
75 | assert.isFalse(hamt.isEmpty(h));
76 | h = h.remove(remove[i]);
77 | }
78 |
79 | assert.isTrue(hamt.isEmpty(h));
80 | });
81 | });
82 |
83 |
--------------------------------------------------------------------------------
/tests/count.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const hamt = require('../hamt');
3 | const assert = require('chai').assert;
4 |
5 | describe('count', () => {
6 | it('should return zero for empty map', () => {
7 | assert.strictEqual(0, hamt.count(hamt.make()));
8 | assert.strictEqual(0, hamt.make().count());
9 | assert.strictEqual(0, hamt.make().size);
10 | });
11 |
12 | it('should return 1 for single element map', () => {
13 | assert.strictEqual(1, hamt.count(hamt.make().set('a', 5)));
14 | assert.strictEqual(1, hamt.make().set('b', 5).count());
15 | assert.strictEqual(1, hamt.make().set('b', 5).size);
16 | });
17 |
18 | it('should handle counts on collisions correctly', () => {
19 | const h1 = hamt.make()
20 | .setHash(0, 'a', 3)
21 | .setHash(0, 'b', 5);
22 |
23 | assert.strictEqual(2, hamt.count(h1));
24 | });
25 |
26 | it('return correct counts while items are added and removed', () => {
27 | const insert = [
28 | "n", "U", "p", "^", "h", "w", "W", "x", "S", "f", "H", "m", "g",
29 | "l", "b", "_", "V", "Z", "G", "o", "F", "Q", "a", "k", "j", "r",
30 | "B", "A", "y", "\\", "R", "D", "i", "c", "]", "C", "[", "e", "s",
31 | "t", "J", "E", "q", "v", "M", "T", "N", "L", "K", "Y", "d", "P",
32 | "u", "I", "O", "`", "X"];
33 |
34 | const remove = [
35 | "w", "m", "Q", "R", "i", "K", "P", "Y", "D", "g", "y", "L",
36 | "b", "[", "a", "t", "j", "W", "J", "G", "q", "r", "p", "U",
37 | "v", "h", "S", "_", "d", "x", "I", "F", "f", "n", "B", "\\",
38 | "k", "V", "N", "l", "X", "A", "]", "s", "Z", "O", "^", "o",
39 | "`", "H", "E", "e", "M", "u", "T", "c", "C"];
40 |
41 | let h = hamt.make();
42 |
43 | for (let i = 0; i < insert.length; ++i) {
44 | const x = insert[i];
45 | h = h.set(x, x);
46 | assert.strictEqual(i + 1, hamt.count(h));
47 | }
48 |
49 | for (let i = 0; i < remove.length; ++i) {
50 | h = h.remove(remove[i]);
51 | assert.strictEqual(remove.length - i - 1, hamt.count(h));
52 | }
53 | });
54 |
55 | it('should work on array nodes correctly', () => {
56 | const insert = [
57 | "n", "U", "p", "^", "h", "w", "W", "x", "S", "f", "H", "m", "g",
58 | "l", "b", "_", "V", "Z", "G", "o", "F", "Q", "a", "k", "j", "r",
59 | "B", "A", "y", "\\", "R", "D", "i", "c", "]", "C", "[", "e", "s",
60 | "t", "J", "E", "q", "v", "M", "T", "N", "L", "K", "Y", "d", "P",
61 | "u", "I", "O", "`", "X"];
62 |
63 | let h = hamt.make();
64 | for (let i = 0; i < insert.length; ++i) {
65 | h = h.setHash(i, insert[i], insert[i]);
66 | }
67 |
68 | assert.strictEqual(insert.length, h.count());
69 |
70 | for (let i = 0; i < insert.length; ++i) {
71 | h = h.removeHash(i, insert[i]);
72 | assert.strictEqual(insert.length - i - 1, h.count());
73 | }
74 | });
75 | });
76 |
77 |
--------------------------------------------------------------------------------
/tests/set.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const hamt = require('../hamt');
3 | const assert = require('chai').assert;
4 |
5 | describe('set', () => {
6 | it('should add entry to empty map', () => {
7 | assert.strictEqual(3, hamt.set('a', 3, hamt.make()).get('a'));
8 | assert.strictEqual(3, hamt.make().set('a', 3).get('a'));
9 | });
10 |
11 | it('should add entry to existing map', () => {
12 | const h = hamt.make().set('a', 3);
13 | const h1 = h.set('b', 5);
14 |
15 | assert.strictEqual(3, hamt.get('a', h1));
16 | assert.strictEqual(5, hamt.get('b', h1));
17 |
18 | assert.strictEqual(3, hamt.get('a', h));
19 | assert.strictEqual(undefined, hamt.get('b', h));
20 | });
21 |
22 |
23 | it('should overwrite entry in existing map', () => {
24 | const h1 = hamt.make()
25 | .set('a', 3)
26 | .set('b', 5)
27 | .set('c', 10);
28 | const h2 = h1.set('b', 4);
29 |
30 | assert.strictEqual(3, hamt.get('a', h2));
31 | assert.strictEqual(4, hamt.get('b', h2));
32 | assert.strictEqual(10, hamt.get('c', h2));
33 |
34 | assert.strictEqual(3, hamt.get('a', h1));
35 | assert.strictEqual(5, hamt.get('b', h1));
36 | assert.strictEqual(10, hamt.get('c', h1));
37 | });
38 |
39 | it('should handle collisions correctly', () => {
40 | const h1 = hamt.make().setHash(0, 'a', 3);
41 | const h2 = h1.setHash(0, 'b', 5);
42 |
43 | assert.strictEqual(3, h2.getHash(0, 'a'));
44 | assert.strictEqual(5, h2.getHash(0, 'b'));
45 | });
46 |
47 | it('should add to collisions correctly', () => {
48 | const h1 = hamt.make().setHash(0, 'a', 3);
49 | const h2 = h1.setHash(0, 'b', 5);
50 | const h3 = h2.setHash(1, 'c', 7);
51 |
52 | assert.strictEqual(3, h3.getHash(0, 'a'));
53 | assert.strictEqual(5, h3.getHash(0, 'b'));
54 | assert.strictEqual(7, h3.getHash(1, 'c'));
55 | });
56 |
57 | it('should use custom hash function', () => {
58 | const hash = () => 0;
59 | const h = hamt.make({ hash: hash })
60 | .set('a', 3)
61 | .set('b', 5);
62 |
63 | assert.strictEqual(3, h.getHash(0, 'a'));
64 | assert.strictEqual(5, h.getHash(0, 'b'));
65 | });
66 |
67 | it('should use custom key function', () => {
68 | const keyEq = (x, y) => x.value === y.value;
69 | const h = hamt.make({ keyEq: keyEq })
70 | .set({ value: 'a' }, 3)
71 | .set({ value: 'b' }, 5)
72 | .set({ value: 'a' }, 7);
73 |
74 | assert.strictEqual(2, h.count());
75 | assert.strictEqual(7, h.get({ value: 'a' }));
76 | assert.strictEqual(undefined, h.get('a'));
77 | assert.strictEqual(5, h.get({ value: 'b' }));
78 | assert.strictEqual(undefined, h.get('b'));
79 | });
80 |
81 | it('should set values correctly from list with no order', () => {
82 | const arr = [
83 | "n", "U", "p", "^", "h", "w", "W", "x", "S", "f", "H", "m", "g",
84 | "l", "b", "_", "V", "Z", "G", "o", "F", "Q", "a", "k", "j", "r",
85 | "B", "A", "y", "\\", "R", "D", "i", "c", "]", "C", "[", "e", "s",
86 | "t", "J", "E", "q", "v", "M", "T", "N", "L", "K", "Y", "d", "P",
87 | "u", "I", "O", "`", "X"];
88 |
89 | let h = hamt.make();
90 | arr.forEach(function(x) {
91 | h = h.set(x, x);
92 | });
93 |
94 | arr.forEach(function(x) {
95 | assert.strictEqual(x, hamt.get(x, h));
96 | });
97 | });
98 |
99 | it('should set values correctly from an ordered list', () => {
100 | let h = hamt.make();
101 | for (let i = 'A'.charCodeAt(0); i < 'z'.charCodeAt(0); ++i) {
102 | h = h.set(String.fromCharCode(i), i);
103 | }
104 |
105 | for (let i = 'A'.charCodeAt(0); i < 'z'.charCodeAt(0); ++i) {
106 | assert.strictEqual(i, hamt.get(String.fromCharCode(i), h));
107 | }
108 | });
109 |
110 | it('should not mutate map if value is same as stored', () => {
111 | {
112 | const h = hamt.make().set('a', 3);
113 | const h1 = h.set('a', 3);
114 | assert.strictEqual(h1, h);
115 | }
116 | {
117 | const h = hamt.make()
118 | .set('a', 3)
119 | .set('b', 5)
120 | .set('c', 10);
121 | const h1 = h
122 | .set('a', 3)
123 | .set('b', 5)
124 | .set('c', 10);
125 | assert.strictEqual(h1, h);
126 | }
127 | });
128 |
129 | it('should not mutate map if value in collision is same as stored', () => {
130 | const h = hamt.make().setHash(0, 'a', 3).setHash(0, 'b', 3);
131 | const h1 = h.setHash(0, 'a', 3);
132 | assert.strictEqual(h1, h);
133 | });
134 | });
135 |
--------------------------------------------------------------------------------
/tests/mutation.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const hamt = require('../hamt');
3 | const assert = require('chai').assert;
4 |
5 | describe('mutate', () => {
6 | it('should insert single element into empty map', () => {
7 | const h1 = hamt.mutate(function (m) {
8 | hamt.set('a', 3, m);
9 | }, hamt.make());
10 |
11 | assert.strictEqual(1, h1.count());
12 | assert.strictEqual(3, h1.get('a'));
13 | });
14 |
15 | it('should insert elements as normal into empty map', () => {
16 | const h1 = hamt.mutate(function (m) {
17 | hamt.set('a', 3, m);
18 | hamt.set('b', 5, m);
19 | }, hamt.make());
20 |
21 | assert.strictEqual(2, h1.size);
22 | assert.strictEqual(3, h1.get('a'));
23 | assert.strictEqual(5, h1.get('b'));
24 | });
25 |
26 | it('should allow for nested mutations', () => {
27 | let h1 = hamt.make().beginMutation().beginMutation();
28 | h1.set('a', 3);
29 | h1.set('b', 5);
30 | h1.endMutation();
31 |
32 | assert.strictEqual(2, h1.size);
33 | assert.strictEqual(3, h1.get('a'));
34 | assert.strictEqual(5, h1.get('b'));
35 |
36 | h1.set('a', 30);
37 | assert.strictEqual(2, h1.size);
38 | assert.strictEqual(30, h1.get('a'));
39 | assert.strictEqual(5, h1.get('b'));
40 |
41 | h1.endMutation();
42 |
43 | h1.set('a', 300);
44 | assert.strictEqual(2, h1.size);
45 | assert.strictEqual(30, h1.get('a'));
46 | assert.strictEqual(5, h1.get('b'));
47 |
48 | });
49 |
50 | it('should not leak mutation to values before scope', () => {
51 | const h = hamt.set('a', 100, hamt.make());
52 |
53 | const h1 = hamt.mutate(function (m) {
54 | hamt.set('a', 3, m);
55 | hamt.set('b', 5, m);
56 | }, h);
57 |
58 | assert.strictEqual(1, h.size);
59 | assert.strictEqual(100, hamt.get('a', h));
60 | assert.strictEqual(undefined, hamt.get('b', h));
61 |
62 | assert.strictEqual(2, h1.size);
63 | assert.strictEqual(3, hamt.get('a', h1));
64 | assert.strictEqual(5, hamt.get('b', h1));
65 | });
66 |
67 | it('should not leak mutation to values after scope', () => {
68 | const h = hamt.mutate(function (m) {
69 | hamt.set('a', 3, m);
70 | hamt.set('b', 5, m);
71 | }, hamt.make());
72 |
73 | const h1 = hamt.set('a', 100, h);
74 |
75 | assert.strictEqual(2, h.size);
76 | assert.strictEqual(3, hamt.get('a', h));
77 | assert.strictEqual(5, hamt.get('b', h));
78 |
79 | assert.strictEqual(2, h1.size);
80 | assert.strictEqual(100, hamt.get('a', h1));
81 | assert.strictEqual(5, hamt.get('b', h1));
82 | });
83 |
84 | it('should not effect other mutations', () => {
85 | const h = hamt.set('a', 100, hamt.make());
86 |
87 | const h1 = hamt.mutate(function (m) {
88 | hamt.set('a', 3, m);
89 | hamt.set('b', 5, m);
90 | }, h);
91 |
92 | const h2 = hamt.mutate(function (m) {
93 | hamt.set('a', 30, m);
94 | hamt.set('b', 50, m);
95 | }, h1);
96 |
97 | assert.strictEqual(1, h.size);
98 | assert.strictEqual(100, hamt.get('a', h));
99 | assert.strictEqual(undefined, hamt.get('b', h));
100 |
101 | assert.strictEqual(2, h1.size);
102 | assert.strictEqual(3, hamt.get('a', h1));
103 | assert.strictEqual(5, hamt.get('b', h1));
104 |
105 | assert.strictEqual(2, h2.size);
106 | assert.strictEqual(50, hamt.get('b', h2));
107 | assert.strictEqual(30, hamt.get('a', h2));
108 | });
109 |
110 | it('should handle many insertions correctly', () => {
111 | const insert = [
112 | "The", "Time", "Traveller", "for", "so", "it", "will", "be",
113 | "convenient", "to", "speak", "of", "him", "was", "expounding",
114 | "a", "recondite", "matter", "us", "His", "grey", "eyes",
115 | "shone", "twinkled", "his", "usually", "pale",
116 | "face", "flushed", "animated"];
117 |
118 | const h = hamt.mutate(h =>
119 | insert.forEach((x, i) => {
120 | h.set(x, x);
121 | assert.strictEqual(i + 1, h.count());
122 | }),
123 | hamt.empty);
124 |
125 | assert.strictEqual(insert.length, h.size);
126 | insert.forEach(x =>
127 | assert.strictEqual(x, h.get(x)));
128 |
129 | const h1 = hamt.mutate(h => {
130 | insert.forEach((x, i) => {
131 | h.set(x + x, x);
132 | assert.strictEqual(insert.length + i + 1, h.count());
133 | });
134 | }, h);
135 |
136 | assert.strictEqual(insert.length, h.size);
137 | insert.forEach(x =>
138 | assert.strictEqual(x, h.get(x)));
139 |
140 | assert.strictEqual(insert.length * 2, h1.size);
141 | insert.forEach(x => {
142 | assert.strictEqual(x, h1.get(x));
143 | assert.strictEqual(x, h1.get(x + x))
144 | });
145 | });
146 |
147 | it('should handle many removals correctly', () => {
148 | const insert = [
149 | "The", "Time", "Traveller", "for", "so", "it", "will", "be",
150 | "convenient", "to", "speak", "of", "him", "was", "expounding",
151 | "a", "recondite", "matter", "us", "His", "grey", "eyes",
152 | "shone", "twinkled", "his", "usually", "pale",
153 | "face", "flushed", "animated"];
154 |
155 | const h = hamt.mutate(h => {
156 | insert.forEach((x, i) => {
157 | h.set(x, x);
158 | assert.strictEqual(i + 1, h.count());
159 | })
160 | insert.forEach((x, i) => {
161 | h.delete(x);
162 | assert.strictEqual(insert.length - i - 1, h.count());
163 | })
164 | }, hamt.empty);
165 |
166 | assert.strictEqual(0, h.count());
167 | });
168 |
169 | });
170 |
--------------------------------------------------------------------------------
/tests/remove.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const hamt = require('../hamt');
3 | const assert = require('chai').assert;
4 |
5 | describe('remove', () => {
6 | it('should noop on empty', () => {
7 | assert.strictEqual(0, hamt.count(hamt.remove('a', hamt.make())));
8 | assert.strictEqual(0, hamt.count(hamt.remove('b', hamt.make())));
9 | });
10 |
11 | it('should remove value from single map', () => {
12 | const h1 = hamt.make().set('a', 3);
13 | const h2 = h1.remove('a');
14 |
15 | assert.strictEqual(0, hamt.count(h2));
16 | assert.strictEqual(undefined, hamt.get('a', h2));
17 |
18 | assert.strictEqual(1, hamt.count(h1));
19 | assert.strictEqual(3, hamt.get('a', h1));
20 | });
21 |
22 | it('should only remove a single entry', () => {
23 | const h1 = hamt.make()
24 | .set('a', 3)
25 | .set('b', 5);
26 | const h2 = hamt.remove('a', h1);
27 |
28 | assert.strictEqual(1, hamt.count(h2));
29 | assert.strictEqual(undefined, hamt.get('a', h2));
30 | assert.strictEqual(5, hamt.get('b', h2));
31 |
32 | assert.strictEqual(2, hamt.count(h1));
33 | assert.strictEqual(3, hamt.get('a', h1));
34 | assert.strictEqual(5, hamt.get('b', h1));
35 | });
36 |
37 | it('should remove collisions correctly a single entry', () => {
38 | const h1 = hamt.make()
39 | .setHash(0, 'a', 3)
40 | .setHash(0, 'b', 5);
41 |
42 | const h2 = h1.deleteHash(0, 'a');
43 |
44 | assert.strictEqual(1, hamt.count(h2));
45 | assert.strictEqual(undefined, h2.getHash(0, 'a'));
46 | assert.strictEqual(5, h2.getHash(0, 'b'));
47 |
48 | assert.strictEqual(2, hamt.count(h1));
49 | assert.strictEqual(3, h1.getHash(0, 'a'));
50 | assert.strictEqual(5, h1.getHash(0, 'b'));
51 | });
52 |
53 | it('should not remove for a collision that does not match key', () => {
54 | const h1 = hamt.make()
55 | .setHash(0, 'a', 3)
56 | .setHash(0, 'b', 5);
57 |
58 | const h2 = h1.removeHash(0, 'c');
59 |
60 | assert.strictEqual(3, h2.getHash(0, 'a'));
61 | assert.strictEqual(5, h2.getHash(0, 'b'));
62 | assert.strictEqual(undefined, h2.getHash(0, 'c'));
63 | assert.strictEqual(h1, h2);
64 | });
65 |
66 | it('should use custom hash function', () => {
67 | const hash = () => 0;
68 | const h1 = hamt.make({
69 | hash: hash
70 | })
71 | .setHash(0, 'a', 3)
72 | .setHash(0, 'b', 5);
73 |
74 | const h2 = h1.delete('a');
75 | assert.strictEqual(undefined, h2.getHash(0, 'a'));
76 | assert.strictEqual(5, h2.getHash(0, 'b'));
77 | });
78 |
79 | it('should remove correctly from large set', () => {
80 | const insert = [
81 | "n", "U", "p", "^", "h", "w", "W", "x", "S", "f", "H", "m", "g",
82 | "l", "b", "_", "V", "Z", "G", "o", "F", "Q", "a", "k", "j", "r",
83 | "B", "A", "y", "\\", "R", "D", "i", "c", "]", "C", "[", "e", "s",
84 | "t", "J", "E", "q", "v", "M", "T", "N", "L", "K", "Y", "d", "P",
85 | "u", "I", "O", "`", "X"
86 | ];
87 |
88 | const remove = [
89 | "w", "m", "Q", "R", "i", "K", "P", "Y", "D", "g", "y", "L",
90 | "b", "[", "a", "t", "j", "W", "J", "G", "q", "r", "p", "U",
91 | "v", "h", "S", "_", "d", "x", "I", "F", "f", "n", "B", "\\",
92 | "k", "V", "N", "l", "X", "A", "]", "s", "Z", "O", "^", "o",
93 | "`", "H", "E", "e", "M", "u", "T", "c", "C"
94 | ];
95 |
96 | let h = hamt.make();
97 | insert.forEach(function(x) {
98 | h = hamt.set(x, x, h);
99 | });
100 |
101 | for (let i = 0; i < remove.length; ++i) {
102 | h = hamt.remove(remove[i], h);
103 |
104 | for (let g = 0; g <= i; ++g) {
105 | assert.strictEqual(
106 | hamt.get(remove[g], h),
107 | undefined);
108 | }
109 | for (let g = i + 1; g < remove.length; ++g) {
110 | assert.strictEqual(
111 | hamt.get(remove[g], h),
112 | remove[g]);
113 | }
114 | }
115 | });
116 |
117 | it('should not mutate for noop remove', () => {
118 | const h1 = hamt.make()
119 | .set('a', 3)
120 | .set('b', 5);
121 | const h2 = hamt.remove('none', h1);
122 |
123 | assert.strictEqual(h1, h2);
124 | });
125 |
126 | it('should when all elements are removed', () => {
127 | const keys = ['hiymmhdhq', 'hzyghyg', 'hzieut', 'mjnaup', 'tinjxpys', 'kwpcqm',
128 | 'vxeusxcg', 'faybuua', 'lycfxflwft', 'tnwtzj', 'lrvycc', 'flaqdhqkj',
129 | 'ngkmhejrm', 'jkqotnew', 'tvnxhguhn', 'frisdgmgwk', 'xqhakqug', 'cncoahk',
130 | 'zczoqcfqy', 'czlfnbl', 'comrfarx', 'xkxedunf', 'szzmuwuuu', 'mcqhmf',
131 | 'zqyjwwjba', 'kqoxvzky', 'mihnuv', 'shydgsfmpp', 'rdokftl', 'hzkpejjor',
132 | 'uuwfazpud', 'wauyyr', 'nhfzckr', 'kmfdpcdgwi', 'twhbuhpgp', 'eyzbrtjwa',
133 | 'aqdohkac', 'mteeptl', 'lmyxutoqg', 'ijqumqzsq', 'qwpqnsp', 'yklnknl',
134 | 'byjxqzl', 'ryptefqr', 'fhplnoi', 'uvflmypxsa', 'xsenqm', 'kpquygdx',
135 | 'ztsfcuy', 'xjrtyl', 'elzgmbcsfs', 'tksobwth', 'nxfktmbn', 'qsiqzdl',
136 | 'ztfxghd', 'blekwtpzg', 'ogtwty', 'jvwzhjwmnl', 'xiqset', 'yaeazzw',
137 | 'megtbspnvy', 'afjowwuv', 'ysaldydgvx', 'vrejaghyy', 'ogwjrroeiu', 'alvdrg',
138 | 'lytqpgdnt', 'yaiedb', 'czrtsqh', 'bfvxsoxvql', 'vpfaam', 'kbiyel', 'vwwubrdqx',
139 | 'iwhibpcqm', 'jrhkkzw', 'ajezbycwg', 'asubyu', 'ctdjnchw', 'gbzbbmdug', 'njxcfr',
140 | 'mddyfqb', 'xgtthksh', 'cocjuvwrjm', 'xnwuoczjnh', 'nqehrflx', 'szyfto',
141 | 'vpmynbgdo', 'zourijqabw', 'olujjqqkxy', 'rifpiaoqrj', 'ahurel', 'rqfdytylz',
142 | 'ymgpnp', 'qevprue', 'sjttddstx', 'uqjuyyu', 'mkwxsgg', 'aesdxulaw', 'nwtfbe',
143 | 'duyksei'
144 | ];
145 | const order = [62, 6, 52, 79, 89, 1, 94, 16, 10, 21, 70, 99, 7, 81, 63,
146 | 73, 53, 50, 25, 26, 78, 29, 28, 13, 35, 77, 84, 51, 15, 36, 96, 92, 69,
147 | 18, 88, 12, 20, 58, 14, 48, 17, 4, 82, 76, 42, 5, 9, 86, 87, 43, 61, 2,
148 | 66, 41, 68, 56, 54, 74, 22, 34, 3, 57, 31, 72, 91, 95, 23, 90, 32, 71,
149 | 85, 80, 60, 11, 0, 55, 46, 40, 37, 67, 75, 64, 97, 27, 49, 93, 47, 38,
150 | 83, 59, 65, 30, 98, 8, 44, 24, 19, 39, 45, 33
151 | ];
152 |
153 | let h = hamt.empty;
154 | keys.forEach(function(x) {
155 | h = hamt.set(x, x, h);
156 | });
157 |
158 | order.forEach(function(x) {
159 | h = hamt.remove(keys[x], h);
160 | });
161 | });
162 | });
163 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |

3 |
4 |
5 | Fork of the [Hamt][hamt] ([hash array mapped trie][hash-array-mapped-trie]) library. This fork adds a few important features in exchange for very slightly degraded performance:
6 |
7 | * Transient mutation. This allows efficient mass operations, while retaining the safety of a persistent data structure.
8 | * Supports using a custom key comparision function.
9 | * Supports using a custom hash function.
10 |
11 | The Hamt+ Api is a superset of Hamt's Api. Hamt+ supports any key type using the custom hash and key comparision functions.
12 |
13 | ## Install
14 | Source code is in `hamt.js` and generated from `lib/hamt.js`. The library supports node, AMD, and use as a global.
15 |
16 | ### Node
17 | ``` sh
18 | $ npm install hamt
19 | ```
20 |
21 | ``` javascript
22 | var hamt = require('hamt_plus');
23 |
24 | var h = hamt.empty.set('key', 'value');
25 |
26 | ...
27 | ```
28 |
29 |
30 | ### AMD
31 | ``` javascript
32 | requirejs.config({
33 | paths: {
34 | 'hamt': 'path/to/hamt_plus/'
35 | }
36 | });
37 |
38 | require(['hamt'], function(hamt) {
39 | var h = hamt.empty.set('key', 'value');
40 | ...
41 | });
42 | ```
43 |
44 |
45 | # Usage
46 | Hamt+ provides a method chaining interface and free functions for updating and querying the map. Both APIs provide identical functionality, but the free functions are designed for binding and composition, while the method chaining API is more legible and more Javascripty.
47 |
48 | HAMTs are is persistent, so operations always return a modified copy of the map instead of altering the original.
49 |
50 | ## Custom Hash Values
51 | Most update and lookup methods have two versions: one that takes a key and uses an internal hash function to compute its hash, and a version that takes a custom computed hash value.
52 |
53 |
54 | ``` javascript
55 | var h = hamt.empty.set('key', 'value');
56 | var h2 = hamt.empty.setHash(5, 'key', 'value');
57 |
58 |
59 | h.get('key') === 'value'
60 | h2.getHash(5, 'key') === 'value'
61 | ```
62 |
63 | If using a custom hash, you must only use the `*Hash` variant of functions to interact with the map.
64 |
65 |
66 | ``` javascript
67 | // Because the internally computed hash of `key` is not `5`, a direct
68 | // look will not work.
69 | h2.get('key') === undefined
70 |
71 | // You must use `getHash` with the same hash value originally passed in.
72 | h2.getHash(5, 'key') === 'value'
73 | ```
74 |
75 |
76 | ## API
77 |
78 | #### `hamt.make(config)`
79 | Create a new, empty map.
80 |
81 | * `config` – Optional. Holds the custom hash and key compare functions: `{ hash: myHashFunction, keyEq: myKeyCompareFunction }`
82 |
83 | ```js
84 | const Vec2 = (x, y) => ({ x: x, y: y });
85 |
86 | const vecMap = hamt.make({
87 | hash: (value) => hamt.hash(value.x + ',' + value.y),
88 | keyEq: (a, b) => a.x === b.x && a.y === b.y
89 | });
90 |
91 | vecMap = vecMap.set(Vec2(1, 2), 'value');
92 |
93 | vecMap.get(Vec2(1, 2)) === 'value'
94 | ```
95 |
96 | #### `hamt.empty`
97 | An empty map.
98 |
99 | Uses default key compare function and hash functions.
100 |
101 | ----
102 |
103 | #### `hamt.isEmpty(map)`
104 | #### `map.isEmpty()`
105 | Is a map empty?
106 |
107 | This is the correct method to check if a map is empty. Direct comparisons to `hamt.empty` will not work.
108 |
109 | ----
110 |
111 | #### `hamt.get(key, map)`
112 | #### `map.get(key)`
113 | Lookup the value for `key` in `map`.
114 |
115 | * `key` - String key.
116 | * `map` - Hamt map.
117 |
118 | ``` javascript
119 | var h = hamt.empty.set('key', 'value');
120 |
121 | h.get('key') === 'value'
122 | hamt.get('key', k) === 'value'
123 |
124 | h.get('no such key') === undefined
125 | ```
126 |
127 | ----
128 |
129 | #### `hamt.getHash(hash, key, map)`
130 | #### `map.getHash(hash, key)`
131 | Same as `get` but uses a custom hash value.
132 |
133 | ----
134 |
135 | #### `hamt.tryGet(alt, key, map)`
136 | #### `map.tryGet(alt, key)`
137 | Same as `get` but returns `alt` if no value for `key` exists.
138 |
139 | * `alt` - Value returned if no such key exists in the map.
140 | * `key` - String key.
141 | * `map` - Hamt map.
142 |
143 | ----
144 |
145 | #### `hamt.has(key, map)`
146 | #### `map.has(key)`
147 | Does an entry for `key` exist in `map`?
148 |
149 | * `key` - String key.
150 | * `map` - Hamt map.
151 |
152 | ``` javascript
153 | var h = hamt.empty.set('key', 'value');
154 |
155 | h.has('key') === true
156 | h.has('no such key') === false
157 | ```
158 |
159 | ----
160 |
161 | #### `hamt.tryGetHash(alt, hash, key, map)`
162 | #### `map.tryGetHash(alt, hash, key)`
163 | Same as `tryGet` but uses a custom hash value.
164 |
165 | ----
166 |
167 | #### `hamt.set(key, value, map)`
168 | #### `map.set(key, value)`
169 | Set the value for `key` in `map`.
170 |
171 | * `value` - Value to store. Hamt supports all value types, including: literals, objects, falsy values, null, and undefined. Keep in mind that only the map data structure itself is guaranteed to be immutable. Using immutable values is recommended but not required.
172 | * `key` - String key.
173 | * `map` - Hamt map.
174 |
175 | Returns a new map with the value set. Does not alter the original.
176 |
177 | ``` javascript
178 | var h = hamt.empty
179 | .set('key', 'value');
180 | .set('key2', 'value2');
181 |
182 | var h2 = h.set('key3', 'value3');
183 |
184 | h2.get('key') === 'value'
185 | h2.get('key2') === 'value2'
186 | h2.get('key3') === 'value3'
187 |
188 | // original `h` was not modified
189 | h.get('key') === 'value'
190 | h.get('key2') === 'value2'
191 | h.get('key3') === undefined
192 | ```
193 |
194 | ----
195 |
196 | #### `hamt.setHash(hash, key, value, map)`
197 | #### `map.setHash(hash, key, value)`
198 | Same as `set` but uses a custom hash value.
199 |
200 | ----
201 |
202 | #### `hamt.modify(f, key, map)`
203 | #### `map.modify(key, f)`
204 | Update the value stored for `key` in `map`.
205 |
206 | * `f` - Function mapping the current value to the new value. If no current value exists, the function is invoked with no arguments.
207 | * `key` - String key.
208 | * `map` - Hamt map.
209 |
210 | Returns a new map with the modified value. Does not alter the original.
211 |
212 | ``` javascript
213 | var h = hamt.empty
214 | .set('i', 2);
215 |
216 | var h2 = h.modify('i', x => x * x);
217 |
218 | h2.get('i') === 4
219 | h.get('i') === 2
220 | h2.count() === 1
221 | h.count() === 1
222 |
223 | // Operate on value that does not exist
224 | var h3 = h.modify('new', x => {
225 | if (x === undefined) {
226 | return 10;
227 | }
228 | return -x;
229 | });
230 |
231 | h3.get('new') === 10
232 | h3.count() === 2
233 | ```
234 |
235 | ----
236 |
237 | #### `hamt.modifyHash(f, hash, key, map)`
238 | #### `map.modifyHash(hash, key, f)`
239 | Same as `modify` but uses a custom hash value.
240 |
241 | ----
242 |
243 | #### `hamt.remove(key, map)`
244 | #### `map.remove(key)`
245 | #### `map.delete(key)`
246 | Remove `key` from `map`.
247 |
248 | * `key` - String key.
249 | * `map` - Hamt map.
250 |
251 | Returns a new map with the value removed. Does not alter the original.
252 |
253 | ``` javascript
254 | var h = hamt.empty
255 | .set('a', 1)
256 | .set('b', 2)
257 | .set('c', 3);
258 |
259 | var h2 = h.remove('b');
260 |
261 | h2.count() === 2;
262 | h2.get('a') === 1
263 | h2.get('b') === undefined
264 | h2.get('c') === 3
265 | ```
266 |
267 | ----
268 |
269 | #### `hamt.removeHash(hash, key, map)`
270 | #### `map.removeHash(hash, key)`
271 | #### `map.deleteHash(hash, key)`
272 | Same as `remove` but uses a custom hash value.
273 |
274 | ----
275 |
276 | #### `hamt.count(map)`
277 | #### `map.count()`
278 | #### `map.size`
279 | Get number of elements in `map`.
280 |
281 | * `map` - Hamt map.
282 |
283 |
284 | ``` javascript
285 | hamt.empty.count() === 0;
286 | hamt.empty.set('a', 3).count() === 1;
287 | hamt.empty.set('a', 3).set('b', 3).count() === 2;
288 | ```
289 |
290 | ----
291 |
292 | #### `hamt.fold(f, z, map)`
293 | #### `map.fold(f, z)`
294 | Fold over the map, accumulating result value.
295 |
296 | * `f` - Function invoked with accumulated value, current value, and current key.
297 | * `z` - Initial value.
298 | * `map` - Hamt map.
299 |
300 | Order is not guaranteed.
301 |
302 | ``` javascript
303 | var max = hamt.fold.bind(null,
304 | (acc, value, key) => Math.max(acc, value),
305 | 0);
306 |
307 | max(hamt.empty.set('key', 3).set('key', 4)) === 4;
308 | ```
309 |
310 | ----
311 |
312 | #### `hamt.entries(map)`
313 | #### `map.entries()`
314 | Get an Javascript iterator to all key value pairs in `map`.
315 |
316 | * `map` - Hamt map.
317 |
318 | Order is not guaranteed.
319 |
320 | ``` javascript
321 | Array.from(hamt.empty.entries()) === [];
322 | Array.from(hamt.empty.set('a', 3).entries()) === [['a', 3]];
323 | Array.from(hamt.empty.set('a', 3).set('b', 3).entries()) === [['a', 3], ['b', 3]];
324 | ```
325 |
326 | You can also iterated directly over a map with ES6:
327 |
328 | ```javascript
329 | const h = hamt.empty.set('a', 3).set('b', 3);
330 |
331 | for (let [key, value] of h)
332 | ...
333 |
334 | Array.from(h) === [['a', 3], ['b', 3]];
335 | ```
336 |
337 | ----
338 |
339 | #### `hamt.key(map)`
340 | #### `map.keys()`
341 | Get an Javascript iterator to all keys in `map`.
342 |
343 | * `map` - Hamt map.
344 |
345 | Order is not guaranteed.
346 |
347 | ``` javascript
348 | Array.from(hamt.empty.keys()) === [];
349 | Array.from(hamt.empty.set('a', 3).keys()) === ['a'];
350 | Array.from(hamt.empty.set('a', 3).set('b', 3).keys()) === ['a', 'b'];
351 | ```
352 |
353 | ----
354 |
355 | #### `hamt.values(map)`
356 | #### `map.values()`
357 | Get an Javascript iterator to all values in `map`.
358 |
359 | * `map` - Hamt map.
360 |
361 | Order is not guaranteed. Duplicate entries may exist.
362 |
363 | ``` javascript
364 | Array.from(hamt.empty.values()) === [];
365 | Array.from(hamt.empty.set('a', 3).values()) === [3];
366 | Array.from(hamt.empty.set('a', 3).values('b', 3).values()) === [3, 3];
367 | ```
368 |
369 | ----
370 |
371 | #### `hamt.forEach(f, map)`
372 | #### `map.forEach(f)`
373 | Invoke function `f` for each value in the map.
374 |
375 | * `f` - Function invoked with `(value, key, map)`.
376 | * `map` - Hamt map.
377 |
378 | Order is not guaranteed.
379 |
380 | ----
381 |
382 | #### `hamt.beginMutation(map)`
383 | #### `map.beginMutation()`
384 | Start the mutation of `map`. The number of calls to `beginMutation` is counted, but mutation itself is binary: the map is either mutable or immutable. Mutation cannot leak before the first call to `beginMutation` or after the matching call to `endMutation.`
385 |
386 | ----
387 |
388 | #### `hamt.endMutation(map)`
389 | #### `map.endMutation()`
390 | End the mutation of `map`.
391 |
392 | ----
393 |
394 | #### `hamt.mutate(f, map)`
395 | #### `map.mutate(f)`
396 | Mutate `map` within the context of function `f`.
397 |
398 | ```js
399 | const insert = ['a', 'b', 'c'];
400 |
401 | const h = hamt.mutate(h =>
402 | // any operations within this block may mutate `h` internally.
403 | insert.forEach((x, i) => {
404 | h.set(x, i);
405 | }),
406 | hamt.empty);
407 |
408 | h.count() === 3;
409 | h.get('b') === 2;
410 | ```
411 |
412 | ## Development
413 | Any contributions to Hamt+ are welcome. Feel free to open an [issues](https://github.com/mattbierner/hamt_plus/issues) if you run into problems or have a suggested improvement.
414 |
415 | To develop Hamt, fork the repo and install the development node packages:
416 |
417 | ```bash
418 | cd hamt_plus
419 | $ npm install
420 | ```
421 |
422 | The source is written in ES6 and lives in `lib/hamt.js`. Gulp and Bable are used to translate the ES6 code to an ES5 distribution found in `hamt.js`. To start the compiler:
423 |
424 | ```bash
425 | $ gulp default
426 | ```
427 |
428 | Tests are written in Mocha and found in `tests/*`. To run the tests:
429 |
430 | ```js
431 | $ mocha tests
432 | ```
433 |
434 | [hamt]: https://github.com/mattbierner/hamt
435 | [benchmarks]: http://github.com/mattbierner/js-hashtrie-benchmark
436 | [pdata]: https://github.com/exclipy/pdata
437 | [hash-array-mapped-trie]: http://en.wikipedia.org/wiki/Hash_array_mapped_trie
438 | [persistent]: http://en.wikipedia.org/wiki/Persistent_data_structure
439 |
--------------------------------------------------------------------------------
/lib/hamt.js:
--------------------------------------------------------------------------------
1 | /**
2 | @fileOverview Hash Array Mapped Trie.
3 |
4 | Code based on: https://github.com/exclipy/pdata
5 | */
6 | const hamt = {}; // export
7 |
8 | /* Configuration
9 | ******************************************************************************/
10 | const SIZE = 5;
11 |
12 | const BUCKET_SIZE = Math.pow(2, SIZE);
13 |
14 | const MASK = BUCKET_SIZE - 1;
15 |
16 | const MAX_INDEX_NODE = BUCKET_SIZE / 2;
17 |
18 | const MIN_ARRAY_NODE = BUCKET_SIZE / 4;
19 |
20 | /*
21 | ******************************************************************************/
22 | const nothing = ({});
23 |
24 | const constant = x => () => x;
25 |
26 | /**
27 | Get 32 bit hash of string.
28 |
29 | Based on:
30 | http://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript-jquery
31 | */
32 | const hash = hamt.hash = str => {
33 | const type = typeof str;
34 | if (type === 'number')
35 | return str;
36 | if (type !== 'string')
37 | str += '';
38 |
39 | let hash = 0;
40 | for (let i = 0, len = str.length; i < len; ++i) {
41 | const c = str.charCodeAt(i);
42 | hash = (((hash << 5) - hash) + c) | 0;
43 | }
44 | return hash;
45 | };
46 |
47 | /* Bit Ops
48 | ******************************************************************************/
49 | /**
50 | Hamming weight.
51 |
52 | Taken from: http://jsperf.com/hamming-weight
53 | */
54 | const popcount = (x) => {
55 | x -= ((x >> 1) & 0x55555555);
56 | x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
57 | x = (x + (x >> 4)) & 0x0f0f0f0f;
58 | x += (x >> 8);
59 | x += (x >> 16);
60 | return (x & 0x7f);
61 | };
62 |
63 | const hashFragment = (shift, h) =>
64 | (h >>> shift) & MASK;
65 |
66 | const toBitmap = x =>
67 | 1 << x;
68 |
69 | const fromBitmap = (bitmap, bit) =>
70 | popcount(bitmap & (bit - 1));
71 |
72 | /* Array Ops
73 | ******************************************************************************/
74 | /**
75 | Set a value in an array.
76 |
77 | @param mutate Should the input array be mutated?
78 | @param at Index to change.
79 | @param v New value
80 | @param arr Array.
81 | */
82 | const arrayUpdate = (mutate, at, v, arr) => {
83 | let out = arr;
84 | if (!mutate) {
85 | const len = arr.length;
86 | out = new Array(len);
87 | for (let i = 0; i < len; ++i)
88 | out[i] = arr[i];
89 | }
90 | out[at] = v;
91 | return out;
92 | };
93 |
94 | /**
95 | Remove a value from an array.
96 |
97 | @param mutate Should the input array be mutated?
98 | @param at Index to remove.
99 | @param arr Array.
100 | */
101 | const arraySpliceOut = (mutate, at, arr) => {
102 | const newLen = arr.length - 1;
103 | let i = 0;
104 | let g = 0;
105 | let out = arr;
106 | if (mutate) {
107 | i = g = at;
108 | } else {
109 | out = new Array(newLen);
110 | while (i < at)
111 | out[g++] = arr[i++];
112 | }
113 | ++i;
114 | while (i <= newLen)
115 | out[g++] = arr[i++];
116 | if (mutate) {
117 | out.length = newLen;
118 | }
119 | return out;
120 | };
121 |
122 | /**
123 | Insert a value into an array.
124 |
125 | @param mutate Should the input array be mutated?
126 | @param at Index to insert at.
127 | @param v Value to insert,
128 | @param arr Array.
129 | */
130 | const arraySpliceIn = (mutate, at, v, arr) => {
131 | const len = arr.length;
132 | if (mutate) {
133 | let i = len;
134 | while (i >= at)
135 | arr[i--] = arr[i];
136 | arr[at] = v;
137 | return arr;
138 | }
139 | let i = 0, g = 0;
140 | const out = new Array(len + 1);
141 | while (i < at)
142 | out[g++] = arr[i++];
143 | out[at] = v;
144 | while (i < len)
145 | out[++g] = arr[i++];
146 | return out;
147 | };
148 |
149 | /* Node Structures
150 | ******************************************************************************/
151 | const LEAF = 1;
152 | const COLLISION = 2;
153 | const INDEX = 3;
154 | const ARRAY = 4;
155 |
156 | /**
157 | Empty node.
158 | */
159 | const empty = ({
160 | __hamt_isEmpty: true
161 | });
162 |
163 | const isEmptyNode = x =>
164 | x === empty || (x && x.__hamt_isEmpty);
165 |
166 | /**
167 | Leaf holding a value.
168 |
169 | @member edit Edit of the node.
170 | @member hash Hash of key.
171 | @member key Key.
172 | @member value Value stored.
173 | */
174 | const Leaf = (edit, hash, key, value) => ({
175 | type: LEAF,
176 | edit: edit,
177 | hash: hash,
178 | key: key,
179 | value: value,
180 | _modify: Leaf__modify
181 | });
182 |
183 | /**
184 | Leaf holding multiple values with the same hash but different keys.
185 |
186 | @member edit Edit of the node.
187 | @member hash Hash of key.
188 | @member children Array of collision children node.
189 | */
190 | const Collision = (edit, hash, children) => ({
191 | type: COLLISION,
192 | edit: edit,
193 | hash: hash,
194 | children: children,
195 | _modify: Collision__modify
196 | });
197 |
198 | /**
199 | Internal node with a sparse set of children.
200 |
201 | Uses a bitmap and array to pack children.
202 |
203 | @member edit Edit of the node.
204 | @member mask Bitmap that encode the positions of children in the array.
205 | @member children Array of child nodes.
206 | */
207 | const IndexedNode = (edit, mask, children) => ({
208 | type: INDEX,
209 | edit: edit,
210 | mask: mask,
211 | children: children,
212 | _modify: IndexedNode__modify
213 | });
214 |
215 | /**
216 | Internal node with many children.
217 |
218 | @member edit Edit of the node.
219 | @member size Number of children.
220 | @member children Array of child nodes.
221 | */
222 | const ArrayNode = (edit, size, children) => ({
223 | type: ARRAY,
224 | edit: edit,
225 | size: size,
226 | children: children,
227 | _modify: ArrayNode__modify
228 | });
229 |
230 | /**
231 | Is `node` a leaf node?
232 | */
233 | const isLeaf = node =>
234 | (node === empty || node.type === LEAF || node.type === COLLISION);
235 |
236 | /* Internal node operations.
237 | ******************************************************************************/
238 | /**
239 | Expand an indexed node into an array node.
240 |
241 | @param edit Current edit.
242 | @param frag Index of added child.
243 | @param child Added child.
244 | @param mask Index node mask before child added.
245 | @param subNodes Index node children before child added.
246 | */
247 | const expand = (edit, frag, child, bitmap, subNodes) => {
248 | const arr = [];
249 | let bit = bitmap;
250 | let count = 0;
251 | for (let i = 0; bit; ++i) {
252 | if (bit & 1)
253 | arr[i] = subNodes[count++];
254 | bit >>>= 1;
255 | }
256 | arr[frag] = child;
257 | return ArrayNode(edit, count + 1, arr);
258 | };
259 |
260 | /**
261 | Collapse an array node into a indexed node.
262 |
263 | @param edit Current edit.
264 | @param count Number of elements in new array.
265 | @param removed Index of removed element.
266 | @param elements Array node children before remove.
267 | */
268 | const pack = (edit, count, removed, elements) => {
269 | const children = new Array(count - 1);
270 | let g = 0;
271 | let bitmap = 0;
272 | for (let i = 0, len = elements.length; i < len; ++i) {
273 | if (i !== removed) {
274 | const elem = elements[i];
275 | if (elem && !isEmptyNode(elem)) {
276 | children[g++] = elem;
277 | bitmap |= 1 << i;
278 | }
279 | }
280 | }
281 | return IndexedNode(edit, bitmap, children);
282 | };
283 |
284 | /**
285 | Merge two leaf nodes.
286 |
287 | @param shift Current shift.
288 | @param h1 Node 1 hash.
289 | @param n1 Node 1.
290 | @param h2 Node 2 hash.
291 | @param n2 Node 2.
292 | */
293 | const mergeLeaves = (edit, shift, h1, n1, h2, n2) => {
294 | if (h1 === h2)
295 | return Collision(edit, h1, [n2, n1]);
296 |
297 | const subH1 = hashFragment(shift, h1);
298 | const subH2 = hashFragment(shift, h2);
299 | return IndexedNode(edit, toBitmap(subH1) | toBitmap(subH2),
300 | subH1 === subH2
301 | ? [mergeLeaves(edit, shift + SIZE, h1, n1, h2, n2)]
302 | : subH1 < subH2 ? [n1, n2] : [n2, n1]);
303 | };
304 |
305 | /**
306 | Update an entry in a collision list.
307 |
308 | @param mutate Should mutation be used?
309 | @param edit Current edit.
310 | @param keyEq Key compare function.
311 | @param hash Hash of collision.
312 | @param list Collision list.
313 | @param f Update function.
314 | @param k Key to update.
315 | @param size Size ref.
316 | */
317 | const updateCollisionList = (mutate, edit, keyEq, h, list, f, k, size) => {
318 | const len = list.length;
319 | for (let i = 0; i < len; ++i) {
320 | const child = list[i];
321 | if (keyEq(k, child.key)) {
322 | const value = child.value;
323 | const newValue = f(value);
324 | if (newValue === value)
325 | return list;
326 |
327 | if (newValue === nothing) {
328 | --size.value;
329 | return arraySpliceOut(mutate, i, list);
330 | }
331 | return arrayUpdate(mutate, i, Leaf(edit, h, k, newValue), list);
332 | }
333 | }
334 |
335 | const newValue = f();
336 | if (newValue === nothing)
337 | return list;
338 | ++size.value;
339 | return arrayUpdate(mutate, len, Leaf(edit, h, k, newValue), list);
340 | };
341 |
342 | const canEditNode = (edit, node) => edit === node.edit;
343 |
344 | /* Editing
345 | ******************************************************************************/
346 | const Leaf__modify = function (edit, keyEq, shift, f, h, k, size) {
347 | if (keyEq(k, this.key)) {
348 | const v = f(this.value);
349 | if (v === this.value)
350 | return this;
351 | else if (v === nothing) {
352 | --size.value;
353 | return empty;
354 | }
355 | if (canEditNode(edit, this)) {
356 | this.value = v;
357 | return this;
358 | }
359 | return Leaf(edit, h, k, v);
360 | }
361 | const v = f();
362 | if (v === nothing)
363 | return this;
364 | ++size.value;
365 | return mergeLeaves(edit, shift, this.hash, this, h, Leaf(edit, h, k, v));
366 | };
367 |
368 | const Collision__modify = function (edit, keyEq, shift, f, h, k, size) {
369 | if (h === this.hash) {
370 | const canEdit = canEditNode(edit, this);
371 | const list = updateCollisionList(canEdit, edit, keyEq, this.hash, this.children, f, k, size);
372 | if (list === this.children)
373 | return this;
374 |
375 | return list.length > 1
376 | ? Collision(edit, this.hash, list)
377 | : list[0]; // collapse single element collision list
378 | }
379 | const v = f();
380 | if (v === nothing)
381 | return this;
382 | ++size.value;
383 | return mergeLeaves(edit, shift, this.hash, this, h, Leaf(edit, h, k, v));
384 | };
385 |
386 | const IndexedNode__modify = function (edit, keyEq, shift, f, h, k, size) {
387 | const mask = this.mask;
388 | const children = this.children;
389 | const frag = hashFragment(shift, h);
390 | const bit = toBitmap(frag);
391 | const indx = fromBitmap(mask, bit);
392 | const exists = mask & bit;
393 | const current = exists ? children[indx] : empty;
394 | const child = current._modify(edit, keyEq, shift + SIZE, f, h, k, size);
395 |
396 | if (current === child)
397 | return this;
398 |
399 | const canEdit = canEditNode(edit, this);
400 | let bitmap = mask;
401 | let newChildren;
402 | if (exists && isEmptyNode(child)) { // remove
403 | bitmap &= ~bit;
404 | if (!bitmap)
405 | return empty;
406 | if (children.length <= 2 && isLeaf(children[indx ^ 1]))
407 | return children[indx ^ 1] // collapse
408 |
409 | newChildren = arraySpliceOut(canEdit, indx, children);
410 | } else if (!exists && !isEmptyNode(child)) { // add
411 | if (children.length >= MAX_INDEX_NODE)
412 | return expand(edit, frag, child, mask, children);
413 |
414 | bitmap |= bit;
415 | newChildren = arraySpliceIn(canEdit, indx, child, children);
416 | } else { // modify
417 | newChildren = arrayUpdate(canEdit, indx, child, children);
418 | }
419 |
420 | if (canEdit) {
421 | this.mask = bitmap;
422 | this.children = newChildren;
423 | return this;
424 | }
425 | return IndexedNode(edit, bitmap, newChildren);
426 | };
427 |
428 | const ArrayNode__modify = function (edit, keyEq, shift, f, h, k, size) {
429 | let count = this.size;
430 | const children = this.children;
431 | const frag = hashFragment(shift, h);
432 | const child = children[frag];
433 | const newChild = (child || empty)._modify(edit, keyEq, shift + SIZE, f, h, k, size);
434 |
435 | if (child === newChild)
436 | return this;
437 |
438 | const canEdit = canEditNode(edit, this);
439 | let newChildren;
440 | if (isEmptyNode(child) && !isEmptyNode(newChild)) { // add
441 | ++count;
442 | newChildren = arrayUpdate(canEdit, frag, newChild, children);
443 | } else if (!isEmptyNode(child) && isEmptyNode(newChild)) { // remove
444 | --count;
445 | if (count <= MIN_ARRAY_NODE)
446 | return pack(edit, count, frag, children);
447 | newChildren = arrayUpdate(canEdit, frag, empty, children);
448 | } else { // modify
449 | newChildren = arrayUpdate(canEdit, frag, newChild, children);
450 | }
451 |
452 | if (canEdit) {
453 | this.size = count;
454 | this.children = newChildren;
455 | return this;
456 | }
457 | return ArrayNode(edit, count, newChildren);
458 | };
459 |
460 | empty._modify = (edit, keyEq, shift, f, h, k, size) => {
461 | const v = f();
462 | if (v === nothing)
463 | return empty;
464 | ++size.value;
465 | return Leaf(edit, h, k, v);
466 | };
467 |
468 | /*
469 | ******************************************************************************/
470 | function Map(editable, edit, config, root, size) {
471 | this._editable = editable;
472 | this._edit = edit;
473 | this._config = config;
474 | this._root = root;
475 | this._size = size;
476 | };
477 |
478 | Map.prototype.setTree = function (newRoot, newSize) {
479 | if (this._editable) {
480 | this._root = newRoot;
481 | this._size = newSize;
482 | return this;
483 | }
484 | return newRoot === this._root
485 | ? this
486 | : new Map(this._editable, this._edit, this._config, newRoot, newSize);
487 | };
488 |
489 | /* Queries
490 | ******************************************************************************/
491 | /**
492 | Lookup the value for `key` in `map` using a custom `hash`.
493 |
494 | Returns the value or `alt` if none.
495 | */
496 | const tryGetHash = hamt.tryGetHash = (alt, hash, key, map) => {
497 | let node = map._root;
498 | let shift = 0;
499 | const keyEq = map._config.keyEq;
500 | while (true) switch (node.type) {
501 | case LEAF:
502 | {
503 | return keyEq(key, node.key) ? node.value : alt;
504 | }
505 | case COLLISION:
506 | {
507 | if (hash === node.hash) {
508 | const children = node.children;
509 | for (let i = 0, len = children.length; i < len; ++i) {
510 | const child = children[i];
511 | if (keyEq(key, child.key))
512 | return child.value;
513 | }
514 | }
515 | return alt;
516 | }
517 | case INDEX:
518 | {
519 | const frag = hashFragment(shift, hash);
520 | const bit = toBitmap(frag);
521 | if (node.mask & bit) {
522 | node = node.children[fromBitmap(node.mask, bit)]
523 | shift += SIZE;
524 | break;
525 | }
526 | return alt;
527 | }
528 | case ARRAY:
529 | {
530 | node = node.children[hashFragment(shift, hash)];
531 | if (node) {
532 | shift += SIZE;
533 | break;
534 | }
535 | return alt;
536 | }
537 | default:
538 | return alt;
539 | }
540 | };
541 |
542 | Map.prototype.tryGetHash = function (alt, hash, key) {
543 | return tryGetHash(alt, hash, key, this);
544 | };
545 |
546 | /**
547 | Lookup the value for `key` in `map` using internal hash function.
548 |
549 | @see `tryGetHash`
550 | */
551 | const tryGet = hamt.tryGet = (alt, key, map) =>
552 | tryGetHash(alt, map._config.hash(key), key, map);
553 |
554 | Map.prototype.tryGet = function (alt, key) {
555 | return tryGet(alt, key, this);
556 | };
557 |
558 | /**
559 | Lookup the value for `key` in `map` using a custom `hash`.
560 |
561 | Returns the value or `undefined` if none.
562 | */
563 | const getHash = hamt.getHash = (hash, key, map) =>
564 | tryGetHash(undefined, hash, key, map);
565 |
566 | Map.prototype.getHash = function (hash, key) {
567 | return getHash(hash, key, this);
568 | };
569 |
570 | /**
571 | Lookup the value for `key` in `map` using internal hash function.
572 |
573 | @see `get`
574 | */
575 | const get = hamt.get = (key, map) =>
576 | tryGetHash(undefined, map._config.hash(key), key, map);
577 |
578 | Map.prototype.get = function (key, alt) {
579 | return tryGet(alt, key, this);
580 | };
581 |
582 | /**
583 | Does an entry exist for `key` in `map`? Uses custom `hash`.
584 | */
585 | const hasHash = hamt.has = (hash, key, map) =>
586 | tryGetHash(nothing, hash, key, map) !== nothing;
587 |
588 | Map.prototype.hasHash = function (hash, key) {
589 | return hasHash(hash, key, this);
590 | };
591 |
592 | /**
593 | Does an entry exist for `key` in `map`? Uses internal hash function.
594 | */
595 | const has = hamt.has = (key, map) =>
596 | hasHash(map._config.hash(key), key, map);
597 |
598 | Map.prototype.has = function (key) {
599 | return has(key, this);
600 | };
601 |
602 | const defKeyCompare = (x, y) => x === y;
603 |
604 | /**
605 | Create an empty map.
606 |
607 | @param config Configuration.
608 | */
609 | hamt.make = (config) =>
610 | new Map(0, 0, {
611 | keyEq: (config && config.keyEq) || defKeyCompare,
612 | hash: (config && config.hash) || hash
613 | }, empty, 0);
614 |
615 | /**
616 | Empty map.
617 | */
618 | hamt.empty = hamt.make();
619 |
620 | /**
621 | Does `map` contain any elements?
622 | */
623 | const isEmpty = hamt.isEmpty = (map) =>
624 | map && !!isEmptyNode(map._root);
625 |
626 | Map.prototype.isEmpty = function () {
627 | return isEmpty(this);
628 | };
629 |
630 | /* Updates
631 | ******************************************************************************/
632 | /**
633 | Alter the value stored for `key` in `map` using function `f` using
634 | custom hash.
635 |
636 | `f` is invoked with the current value for `k` if it exists,
637 | or no arguments if no such value exists. `modify` will always either
638 | update or insert a value into the map.
639 |
640 | Returns a map with the modified value. Does not alter `map`.
641 | */
642 | const modifyHash = hamt.modifyHash = (f, hash, key, map) => {
643 | const size = { value: map._size };
644 | const newRoot = map._root._modify(
645 | map._editable ? map._edit : NaN,
646 | map._config.keyEq,
647 | 0,
648 | f,
649 | hash,
650 | key,
651 | size);
652 | return map.setTree(newRoot, size.value);
653 | };
654 |
655 | Map.prototype.modifyHash = function (hash, key, f) {
656 | return modifyHash(f, hash, key, this);
657 | };
658 |
659 | /**
660 | Alter the value stored for `key` in `map` using function `f` using
661 | internal hash function.
662 |
663 | @see `modifyHash`
664 | */
665 | const modify = hamt.modify = (f, key, map) =>
666 | modifyHash(f, map._config.hash(key), key, map);
667 |
668 | Map.prototype.modify = function (key, f) {
669 | return modify(f, key, this);
670 | };
671 |
672 | /**
673 | Store `value` for `key` in `map` using custom `hash`.
674 |
675 | Returns a map with the modified value. Does not alter `map`.
676 | */
677 | const setHash = hamt.setHash = (hash, key, value, map) =>
678 | modifyHash(constant(value), hash, key, map);
679 |
680 | Map.prototype.setHash = function (hash, key, value) {
681 | return setHash(hash, key, value, this);
682 | };
683 |
684 | /**
685 | Store `value` for `key` in `map` using internal hash function.
686 |
687 | @see `setHash`
688 | */
689 | const set = hamt.set = (key, value, map) =>
690 | setHash(map._config.hash(key), key, value, map);
691 |
692 | Map.prototype.set = function (key, value) {
693 | return set(key, value, this);
694 | };
695 |
696 | /**
697 | Remove the entry for `key` in `map`.
698 |
699 | Returns a map with the value removed. Does not alter `map`.
700 | */
701 | const del = constant(nothing);
702 | const removeHash = hamt.removeHash = (hash, key, map) =>
703 | modifyHash(del, hash, key, map);
704 |
705 | Map.prototype.removeHash = Map.prototype.deleteHash = function (hash, key) {
706 | return removeHash(hash, key, this);
707 | };
708 |
709 | /**
710 | Remove the entry for `key` in `map` using internal hash function.
711 |
712 | @see `removeHash`
713 | */
714 | const remove = hamt.remove = (key, map) =>
715 | removeHash(map._config.hash(key), key, map);
716 |
717 | Map.prototype.remove = Map.prototype.delete = function (key) {
718 | return remove(key, this);
719 | };
720 |
721 | /* Mutation
722 | ******************************************************************************/
723 | /**
724 | Mark `map` as mutable.
725 | */
726 | const beginMutation = hamt.beginMutation = (map) =>
727 | new Map(
728 | map._editable + 1,
729 | map._edit + 1,
730 | map._config,
731 | map._root,
732 | map._size);
733 |
734 | Map.prototype.beginMutation = function () {
735 | return beginMutation(this);
736 | };
737 |
738 | /**
739 | Mark `map` as immutable.
740 | */
741 | const endMutation = hamt.endMutation = (map) => {
742 | map._editable = map._editable && map._editable - 1;
743 | return map;
744 | };
745 |
746 | Map.prototype.endMutation = function () {
747 | return endMutation(this);
748 | };
749 |
750 | /**
751 | Mutate `map` within the context of `f`.
752 | @param f
753 | @param map HAMT
754 | */
755 | const mutate = hamt.mutate = (f, map) => {
756 | const transient = beginMutation(map);
757 | f(transient);
758 | return endMutation(transient);
759 | };
760 |
761 | Map.prototype.mutate = function (f) {
762 | return mutate(f, this);
763 | };
764 |
765 | /* Traversal
766 | ******************************************************************************/
767 | /**
768 | Apply a continuation.
769 | */
770 | const appk = k =>
771 | k && lazyVisitChildren(k[0], k[1], k[2], k[3], k[4]);
772 |
773 | /**
774 | Recursively visit all values stored in an array of nodes lazily.
775 | */
776 | var lazyVisitChildren = (len, children, i, f, k) => {
777 | while (i < len) {
778 | const child = children[i++];
779 | if (child && !isEmptyNode(child))
780 | return lazyVisit(child, f, [len, children, i, f, k]);
781 | }
782 | return appk(k);
783 | };
784 |
785 | /**
786 | Recursively visit all values stored in `node` lazily.
787 | */
788 | const lazyVisit = (node, f, k) => {
789 | switch (node.type) {
790 | case LEAF:
791 | return {
792 | value: f(node),
793 | rest: k
794 | };
795 |
796 | case COLLISION:
797 | case ARRAY:
798 | case INDEX:
799 | const children = node.children;
800 | return lazyVisitChildren(children.length, children, 0, f, k);
801 |
802 | default:
803 | return appk(k);
804 | }
805 | };
806 |
807 | const DONE = {
808 | done: true
809 | };
810 |
811 | /**
812 | Javascript iterator over a map.
813 | */
814 | function MapIterator(v) {
815 | this.v = v;
816 | };
817 |
818 | MapIterator.prototype.next = function () {
819 | if (!this.v)
820 | return DONE;
821 | const v0 = this.v;
822 | this.v = appk(v0.rest);
823 | return v0;
824 | };
825 |
826 | MapIterator.prototype[Symbol.iterator] = function () {
827 | return this;
828 | };
829 |
830 | /**
831 | Lazily visit each value in map with function `f`.
832 | */
833 | const visit = (map, f) =>
834 | new MapIterator(lazyVisit(map._root, f));
835 |
836 | /**
837 | Get a Javascsript iterator of `map`.
838 |
839 | Iterates over `[key, value]` arrays.
840 | */
841 | const buildPairs = (x) => [x.key, x.value];
842 | const entries = hamt.entries = (map) =>
843 | visit(map, buildPairs);
844 |
845 | Map.prototype.entries = Map.prototype[Symbol.iterator] = function () {
846 | return entries(this);
847 | };
848 |
849 | /**
850 | Get array of all keys in `map`.
851 |
852 | Order is not guaranteed.
853 | */
854 | const buildKeys = (x) => x.key;
855 | const keys = hamt.keys = (map) =>
856 | visit(map, buildKeys);
857 |
858 | Map.prototype.keys = function () {
859 | return keys(this);
860 | }
861 |
862 | /**
863 | Get array of all values in `map`.
864 |
865 | Order is not guaranteed, duplicates are preserved.
866 | */
867 | const buildValues = x => x.value;
868 | const values = hamt.values = Map.prototype.values = map =>
869 | visit(map, buildValues);
870 |
871 | Map.prototype.values = function () {
872 | return values(this);
873 | };
874 |
875 | /* Fold
876 | ******************************************************************************/
877 | /**
878 | Visit every entry in the map, aggregating data.
879 |
880 | Order of nodes is not guaranteed.
881 |
882 | @param f Function mapping accumulated value, value, and key to new value.
883 | @param z Starting value.
884 | @param m HAMT
885 | */
886 | const fold = hamt.fold = (f, z, m) => {
887 | const root = m._root;
888 | if (root.type === LEAF)
889 | return f(z, root.value, root.key);
890 |
891 | const toVisit = [root.children];
892 | let children;
893 | while (children = toVisit.pop()) {
894 | for (let i = 0, len = children.length; i < len;) {
895 | const child = children[i++];
896 | if (child && child.type) {
897 | if (child.type === LEAF)
898 | z = f(z, child.value, child.key);
899 | else
900 | toVisit.push(child.children);
901 | }
902 | }
903 | }
904 | return z;
905 | };
906 |
907 | Map.prototype.fold = function (f, z) {
908 | return fold(f, z, this);
909 | };
910 |
911 | /**
912 | Visit every entry in the map, aggregating data.
913 |
914 | Order of nodes is not guaranteed.
915 |
916 | @param f Function invoked with value and key
917 | @param map HAMT
918 | */
919 | const forEach = hamt.forEach = (f, map) =>
920 | fold((_, value, key) => f(value, key, map), null, map);
921 |
922 | Map.prototype.forEach = function (f) {
923 | return forEach(f, this);
924 | };
925 |
926 | /* Aggregate
927 | ******************************************************************************/
928 | /**
929 | Get the number of entries in `map`.
930 | */
931 | const count = hamt.count = map =>
932 | map._size;
933 |
934 | Map.prototype.count = function () {
935 | return count(this);
936 | };
937 |
938 | Object.defineProperty(Map.prototype, 'size', {
939 | get: Map.prototype.count
940 | });
941 |
942 | /* Export
943 | ******************************************************************************/
944 | if (typeof module !== 'undefined' && module.exports) {
945 | module.exports = hamt;
946 | } else if (typeof define === 'function' && define.amd) {
947 | define('hamt', [], () => hamt);
948 | } else {
949 | this.hamt = hamt;
950 | }
951 |
--------------------------------------------------------------------------------
/hamt.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
4 |
5 | /**
6 | @fileOverview Hash Array Mapped Trie.
7 |
8 | Code based on: https://github.com/exclipy/pdata
9 | */
10 | var hamt = {}; // export
11 |
12 | /* Configuration
13 | ******************************************************************************/
14 | var SIZE = 5;
15 |
16 | var BUCKET_SIZE = Math.pow(2, SIZE);
17 |
18 | var MASK = BUCKET_SIZE - 1;
19 |
20 | var MAX_INDEX_NODE = BUCKET_SIZE / 2;
21 |
22 | var MIN_ARRAY_NODE = BUCKET_SIZE / 4;
23 |
24 | /*
25 | ******************************************************************************/
26 | var nothing = {};
27 |
28 | var constant = function constant(x) {
29 | return function () {
30 | return x;
31 | };
32 | };
33 |
34 | /**
35 | Get 32 bit hash of string.
36 |
37 | Based on:
38 | http://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript-jquery
39 | */
40 | var hash = hamt.hash = function (str) {
41 | var type = typeof str === 'undefined' ? 'undefined' : _typeof(str);
42 | if (type === 'number') return str;
43 | if (type !== 'string') str += '';
44 |
45 | var hash = 0;
46 | for (var i = 0, len = str.length; i < len; ++i) {
47 | var c = str.charCodeAt(i);
48 | hash = (hash << 5) - hash + c | 0;
49 | }
50 | return hash;
51 | };
52 |
53 | /* Bit Ops
54 | ******************************************************************************/
55 | /**
56 | Hamming weight.
57 |
58 | Taken from: http://jsperf.com/hamming-weight
59 | */
60 | var popcount = function popcount(x) {
61 | x -= x >> 1 & 0x55555555;
62 | x = (x & 0x33333333) + (x >> 2 & 0x33333333);
63 | x = x + (x >> 4) & 0x0f0f0f0f;
64 | x += x >> 8;
65 | x += x >> 16;
66 | return x & 0x7f;
67 | };
68 |
69 | var hashFragment = function hashFragment(shift, h) {
70 | return h >>> shift & MASK;
71 | };
72 |
73 | var toBitmap = function toBitmap(x) {
74 | return 1 << x;
75 | };
76 |
77 | var fromBitmap = function fromBitmap(bitmap, bit) {
78 | return popcount(bitmap & bit - 1);
79 | };
80 |
81 | /* Array Ops
82 | ******************************************************************************/
83 | /**
84 | Set a value in an array.
85 |
86 | @param mutate Should the input array be mutated?
87 | @param at Index to change.
88 | @param v New value
89 | @param arr Array.
90 | */
91 | var arrayUpdate = function arrayUpdate(mutate, at, v, arr) {
92 | var out = arr;
93 | if (!mutate) {
94 | var len = arr.length;
95 | out = new Array(len);
96 | for (var i = 0; i < len; ++i) {
97 | out[i] = arr[i];
98 | }
99 | }
100 | out[at] = v;
101 | return out;
102 | };
103 |
104 | /**
105 | Remove a value from an array.
106 |
107 | @param mutate Should the input array be mutated?
108 | @param at Index to remove.
109 | @param arr Array.
110 | */
111 | var arraySpliceOut = function arraySpliceOut(mutate, at, arr) {
112 | var newLen = arr.length - 1;
113 | var i = 0;
114 | var g = 0;
115 | var out = arr;
116 | if (mutate) {
117 | i = g = at;
118 | } else {
119 | out = new Array(newLen);
120 | while (i < at) {
121 | out[g++] = arr[i++];
122 | }
123 | }
124 | ++i;
125 | while (i <= newLen) {
126 | out[g++] = arr[i++];
127 | }if (mutate) {
128 | out.length = newLen;
129 | }
130 | return out;
131 | };
132 |
133 | /**
134 | Insert a value into an array.
135 |
136 | @param mutate Should the input array be mutated?
137 | @param at Index to insert at.
138 | @param v Value to insert,
139 | @param arr Array.
140 | */
141 | var arraySpliceIn = function arraySpliceIn(mutate, at, v, arr) {
142 | var len = arr.length;
143 | if (mutate) {
144 | var _i = len;
145 | while (_i >= at) {
146 | arr[_i--] = arr[_i];
147 | }arr[at] = v;
148 | return arr;
149 | }
150 | var i = 0,
151 | g = 0;
152 | var out = new Array(len + 1);
153 | while (i < at) {
154 | out[g++] = arr[i++];
155 | }out[at] = v;
156 | while (i < len) {
157 | out[++g] = arr[i++];
158 | }return out;
159 | };
160 |
161 | /* Node Structures
162 | ******************************************************************************/
163 | var LEAF = 1;
164 | var COLLISION = 2;
165 | var INDEX = 3;
166 | var ARRAY = 4;
167 |
168 | /**
169 | Empty node.
170 | */
171 | var empty = {
172 | __hamt_isEmpty: true
173 | };
174 |
175 | var isEmptyNode = function isEmptyNode(x) {
176 | return x === empty || x && x.__hamt_isEmpty;
177 | };
178 |
179 | /**
180 | Leaf holding a value.
181 |
182 | @member edit Edit of the node.
183 | @member hash Hash of key.
184 | @member key Key.
185 | @member value Value stored.
186 | */
187 | var Leaf = function Leaf(edit, hash, key, value) {
188 | return {
189 | type: LEAF,
190 | edit: edit,
191 | hash: hash,
192 | key: key,
193 | value: value,
194 | _modify: Leaf__modify
195 | };
196 | };
197 |
198 | /**
199 | Leaf holding multiple values with the same hash but different keys.
200 |
201 | @member edit Edit of the node.
202 | @member hash Hash of key.
203 | @member children Array of collision children node.
204 | */
205 | var Collision = function Collision(edit, hash, children) {
206 | return {
207 | type: COLLISION,
208 | edit: edit,
209 | hash: hash,
210 | children: children,
211 | _modify: Collision__modify
212 | };
213 | };
214 |
215 | /**
216 | Internal node with a sparse set of children.
217 |
218 | Uses a bitmap and array to pack children.
219 |
220 | @member edit Edit of the node.
221 | @member mask Bitmap that encode the positions of children in the array.
222 | @member children Array of child nodes.
223 | */
224 | var IndexedNode = function IndexedNode(edit, mask, children) {
225 | return {
226 | type: INDEX,
227 | edit: edit,
228 | mask: mask,
229 | children: children,
230 | _modify: IndexedNode__modify
231 | };
232 | };
233 |
234 | /**
235 | Internal node with many children.
236 |
237 | @member edit Edit of the node.
238 | @member size Number of children.
239 | @member children Array of child nodes.
240 | */
241 | var ArrayNode = function ArrayNode(edit, size, children) {
242 | return {
243 | type: ARRAY,
244 | edit: edit,
245 | size: size,
246 | children: children,
247 | _modify: ArrayNode__modify
248 | };
249 | };
250 |
251 | /**
252 | Is `node` a leaf node?
253 | */
254 | var isLeaf = function isLeaf(node) {
255 | return node === empty || node.type === LEAF || node.type === COLLISION;
256 | };
257 |
258 | /* Internal node operations.
259 | ******************************************************************************/
260 | /**
261 | Expand an indexed node into an array node.
262 |
263 | @param edit Current edit.
264 | @param frag Index of added child.
265 | @param child Added child.
266 | @param mask Index node mask before child added.
267 | @param subNodes Index node children before child added.
268 | */
269 | var expand = function expand(edit, frag, child, bitmap, subNodes) {
270 | var arr = [];
271 | var bit = bitmap;
272 | var count = 0;
273 | for (var i = 0; bit; ++i) {
274 | if (bit & 1) arr[i] = subNodes[count++];
275 | bit >>>= 1;
276 | }
277 | arr[frag] = child;
278 | return ArrayNode(edit, count + 1, arr);
279 | };
280 |
281 | /**
282 | Collapse an array node into a indexed node.
283 |
284 | @param edit Current edit.
285 | @param count Number of elements in new array.
286 | @param removed Index of removed element.
287 | @param elements Array node children before remove.
288 | */
289 | var pack = function pack(edit, count, removed, elements) {
290 | var children = new Array(count - 1);
291 | var g = 0;
292 | var bitmap = 0;
293 | for (var i = 0, len = elements.length; i < len; ++i) {
294 | if (i !== removed) {
295 | var elem = elements[i];
296 | if (elem && !isEmptyNode(elem)) {
297 | children[g++] = elem;
298 | bitmap |= 1 << i;
299 | }
300 | }
301 | }
302 | return IndexedNode(edit, bitmap, children);
303 | };
304 |
305 | /**
306 | Merge two leaf nodes.
307 |
308 | @param shift Current shift.
309 | @param h1 Node 1 hash.
310 | @param n1 Node 1.
311 | @param h2 Node 2 hash.
312 | @param n2 Node 2.
313 | */
314 | var mergeLeaves = function mergeLeaves(edit, shift, h1, n1, h2, n2) {
315 | if (h1 === h2) return Collision(edit, h1, [n2, n1]);
316 |
317 | var subH1 = hashFragment(shift, h1);
318 | var subH2 = hashFragment(shift, h2);
319 | return IndexedNode(edit, toBitmap(subH1) | toBitmap(subH2), subH1 === subH2 ? [mergeLeaves(edit, shift + SIZE, h1, n1, h2, n2)] : subH1 < subH2 ? [n1, n2] : [n2, n1]);
320 | };
321 |
322 | /**
323 | Update an entry in a collision list.
324 |
325 | @param mutate Should mutation be used?
326 | @param edit Current edit.
327 | @param keyEq Key compare function.
328 | @param hash Hash of collision.
329 | @param list Collision list.
330 | @param f Update function.
331 | @param k Key to update.
332 | @param size Size ref.
333 | */
334 | var updateCollisionList = function updateCollisionList(mutate, edit, keyEq, h, list, f, k, size) {
335 | var len = list.length;
336 | for (var i = 0; i < len; ++i) {
337 | var child = list[i];
338 | if (keyEq(k, child.key)) {
339 | var value = child.value;
340 | var _newValue = f(value);
341 | if (_newValue === value) return list;
342 |
343 | if (_newValue === nothing) {
344 | --size.value;
345 | return arraySpliceOut(mutate, i, list);
346 | }
347 | return arrayUpdate(mutate, i, Leaf(edit, h, k, _newValue), list);
348 | }
349 | }
350 |
351 | var newValue = f();
352 | if (newValue === nothing) return list;
353 | ++size.value;
354 | return arrayUpdate(mutate, len, Leaf(edit, h, k, newValue), list);
355 | };
356 |
357 | var canEditNode = function canEditNode(edit, node) {
358 | return edit === node.edit;
359 | };
360 |
361 | /* Editing
362 | ******************************************************************************/
363 | var Leaf__modify = function Leaf__modify(edit, keyEq, shift, f, h, k, size) {
364 | if (keyEq(k, this.key)) {
365 | var _v = f(this.value);
366 | if (_v === this.value) return this;else if (_v === nothing) {
367 | --size.value;
368 | return empty;
369 | }
370 | if (canEditNode(edit, this)) {
371 | this.value = _v;
372 | return this;
373 | }
374 | return Leaf(edit, h, k, _v);
375 | }
376 | var v = f();
377 | if (v === nothing) return this;
378 | ++size.value;
379 | return mergeLeaves(edit, shift, this.hash, this, h, Leaf(edit, h, k, v));
380 | };
381 |
382 | var Collision__modify = function Collision__modify(edit, keyEq, shift, f, h, k, size) {
383 | if (h === this.hash) {
384 | var canEdit = canEditNode(edit, this);
385 | var list = updateCollisionList(canEdit, edit, keyEq, this.hash, this.children, f, k, size);
386 | if (list === this.children) return this;
387 |
388 | return list.length > 1 ? Collision(edit, this.hash, list) : list[0]; // collapse single element collision list
389 | }
390 | var v = f();
391 | if (v === nothing) return this;
392 | ++size.value;
393 | return mergeLeaves(edit, shift, this.hash, this, h, Leaf(edit, h, k, v));
394 | };
395 |
396 | var IndexedNode__modify = function IndexedNode__modify(edit, keyEq, shift, f, h, k, size) {
397 | var mask = this.mask;
398 | var children = this.children;
399 | var frag = hashFragment(shift, h);
400 | var bit = toBitmap(frag);
401 | var indx = fromBitmap(mask, bit);
402 | var exists = mask & bit;
403 | var current = exists ? children[indx] : empty;
404 | var child = current._modify(edit, keyEq, shift + SIZE, f, h, k, size);
405 |
406 | if (current === child) return this;
407 |
408 | var canEdit = canEditNode(edit, this);
409 | var bitmap = mask;
410 | var newChildren = void 0;
411 | if (exists && isEmptyNode(child)) {
412 | // remove
413 | bitmap &= ~bit;
414 | if (!bitmap) return empty;
415 | if (children.length <= 2 && isLeaf(children[indx ^ 1])) return children[indx ^ 1]; // collapse
416 |
417 | newChildren = arraySpliceOut(canEdit, indx, children);
418 | } else if (!exists && !isEmptyNode(child)) {
419 | // add
420 | if (children.length >= MAX_INDEX_NODE) return expand(edit, frag, child, mask, children);
421 |
422 | bitmap |= bit;
423 | newChildren = arraySpliceIn(canEdit, indx, child, children);
424 | } else {
425 | // modify
426 | newChildren = arrayUpdate(canEdit, indx, child, children);
427 | }
428 |
429 | if (canEdit) {
430 | this.mask = bitmap;
431 | this.children = newChildren;
432 | return this;
433 | }
434 | return IndexedNode(edit, bitmap, newChildren);
435 | };
436 |
437 | var ArrayNode__modify = function ArrayNode__modify(edit, keyEq, shift, f, h, k, size) {
438 | var count = this.size;
439 | var children = this.children;
440 | var frag = hashFragment(shift, h);
441 | var child = children[frag];
442 | var newChild = (child || empty)._modify(edit, keyEq, shift + SIZE, f, h, k, size);
443 |
444 | if (child === newChild) return this;
445 |
446 | var canEdit = canEditNode(edit, this);
447 | var newChildren = void 0;
448 | if (isEmptyNode(child) && !isEmptyNode(newChild)) {
449 | // add
450 | ++count;
451 | newChildren = arrayUpdate(canEdit, frag, newChild, children);
452 | } else if (!isEmptyNode(child) && isEmptyNode(newChild)) {
453 | // remove
454 | --count;
455 | if (count <= MIN_ARRAY_NODE) return pack(edit, count, frag, children);
456 | newChildren = arrayUpdate(canEdit, frag, empty, children);
457 | } else {
458 | // modify
459 | newChildren = arrayUpdate(canEdit, frag, newChild, children);
460 | }
461 |
462 | if (canEdit) {
463 | this.size = count;
464 | this.children = newChildren;
465 | return this;
466 | }
467 | return ArrayNode(edit, count, newChildren);
468 | };
469 |
470 | empty._modify = function (edit, keyEq, shift, f, h, k, size) {
471 | var v = f();
472 | if (v === nothing) return empty;
473 | ++size.value;
474 | return Leaf(edit, h, k, v);
475 | };
476 |
477 | /*
478 | ******************************************************************************/
479 | function Map(editable, edit, config, root, size) {
480 | this._editable = editable;
481 | this._edit = edit;
482 | this._config = config;
483 | this._root = root;
484 | this._size = size;
485 | };
486 |
487 | Map.prototype.setTree = function (newRoot, newSize) {
488 | if (this._editable) {
489 | this._root = newRoot;
490 | this._size = newSize;
491 | return this;
492 | }
493 | return newRoot === this._root ? this : new Map(this._editable, this._edit, this._config, newRoot, newSize);
494 | };
495 |
496 | /* Queries
497 | ******************************************************************************/
498 | /**
499 | Lookup the value for `key` in `map` using a custom `hash`.
500 |
501 | Returns the value or `alt` if none.
502 | */
503 | var tryGetHash = hamt.tryGetHash = function (alt, hash, key, map) {
504 | var node = map._root;
505 | var shift = 0;
506 | var keyEq = map._config.keyEq;
507 | while (true) {
508 | switch (node.type) {
509 | case LEAF:
510 | {
511 | return keyEq(key, node.key) ? node.value : alt;
512 | }
513 | case COLLISION:
514 | {
515 | if (hash === node.hash) {
516 | var children = node.children;
517 | for (var i = 0, len = children.length; i < len; ++i) {
518 | var child = children[i];
519 | if (keyEq(key, child.key)) return child.value;
520 | }
521 | }
522 | return alt;
523 | }
524 | case INDEX:
525 | {
526 | var frag = hashFragment(shift, hash);
527 | var bit = toBitmap(frag);
528 | if (node.mask & bit) {
529 | node = node.children[fromBitmap(node.mask, bit)];
530 | shift += SIZE;
531 | break;
532 | }
533 | return alt;
534 | }
535 | case ARRAY:
536 | {
537 | node = node.children[hashFragment(shift, hash)];
538 | if (node) {
539 | shift += SIZE;
540 | break;
541 | }
542 | return alt;
543 | }
544 | default:
545 | return alt;
546 | }
547 | }
548 | };
549 |
550 | Map.prototype.tryGetHash = function (alt, hash, key) {
551 | return tryGetHash(alt, hash, key, this);
552 | };
553 |
554 | /**
555 | Lookup the value for `key` in `map` using internal hash function.
556 |
557 | @see `tryGetHash`
558 | */
559 | var tryGet = hamt.tryGet = function (alt, key, map) {
560 | return tryGetHash(alt, map._config.hash(key), key, map);
561 | };
562 |
563 | Map.prototype.tryGet = function (alt, key) {
564 | return tryGet(alt, key, this);
565 | };
566 |
567 | /**
568 | Lookup the value for `key` in `map` using a custom `hash`.
569 |
570 | Returns the value or `undefined` if none.
571 | */
572 | var getHash = hamt.getHash = function (hash, key, map) {
573 | return tryGetHash(undefined, hash, key, map);
574 | };
575 |
576 | Map.prototype.getHash = function (hash, key) {
577 | return getHash(hash, key, this);
578 | };
579 |
580 | /**
581 | Lookup the value for `key` in `map` using internal hash function.
582 |
583 | @see `get`
584 | */
585 | var get = hamt.get = function (key, map) {
586 | return tryGetHash(undefined, map._config.hash(key), key, map);
587 | };
588 |
589 | Map.prototype.get = function (key, alt) {
590 | return tryGet(alt, key, this);
591 | };
592 |
593 | /**
594 | Does an entry exist for `key` in `map`? Uses custom `hash`.
595 | */
596 | var hasHash = hamt.has = function (hash, key, map) {
597 | return tryGetHash(nothing, hash, key, map) !== nothing;
598 | };
599 |
600 | Map.prototype.hasHash = function (hash, key) {
601 | return hasHash(hash, key, this);
602 | };
603 |
604 | /**
605 | Does an entry exist for `key` in `map`? Uses internal hash function.
606 | */
607 | var has = hamt.has = function (key, map) {
608 | return hasHash(map._config.hash(key), key, map);
609 | };
610 |
611 | Map.prototype.has = function (key) {
612 | return has(key, this);
613 | };
614 |
615 | var defKeyCompare = function defKeyCompare(x, y) {
616 | return x === y;
617 | };
618 |
619 | /**
620 | Create an empty map.
621 |
622 | @param config Configuration.
623 | */
624 | hamt.make = function (config) {
625 | return new Map(0, 0, {
626 | keyEq: config && config.keyEq || defKeyCompare,
627 | hash: config && config.hash || hash
628 | }, empty, 0);
629 | };
630 |
631 | /**
632 | Empty map.
633 | */
634 | hamt.empty = hamt.make();
635 |
636 | /**
637 | Does `map` contain any elements?
638 | */
639 | var isEmpty = hamt.isEmpty = function (map) {
640 | return map && !!isEmptyNode(map._root);
641 | };
642 |
643 | Map.prototype.isEmpty = function () {
644 | return isEmpty(this);
645 | };
646 |
647 | /* Updates
648 | ******************************************************************************/
649 | /**
650 | Alter the value stored for `key` in `map` using function `f` using
651 | custom hash.
652 |
653 | `f` is invoked with the current value for `k` if it exists,
654 | or no arguments if no such value exists. `modify` will always either
655 | update or insert a value into the map.
656 |
657 | Returns a map with the modified value. Does not alter `map`.
658 | */
659 | var modifyHash = hamt.modifyHash = function (f, hash, key, map) {
660 | var size = { value: map._size };
661 | var newRoot = map._root._modify(map._editable ? map._edit : NaN, map._config.keyEq, 0, f, hash, key, size);
662 | return map.setTree(newRoot, size.value);
663 | };
664 |
665 | Map.prototype.modifyHash = function (hash, key, f) {
666 | return modifyHash(f, hash, key, this);
667 | };
668 |
669 | /**
670 | Alter the value stored for `key` in `map` using function `f` using
671 | internal hash function.
672 |
673 | @see `modifyHash`
674 | */
675 | var modify = hamt.modify = function (f, key, map) {
676 | return modifyHash(f, map._config.hash(key), key, map);
677 | };
678 |
679 | Map.prototype.modify = function (key, f) {
680 | return modify(f, key, this);
681 | };
682 |
683 | /**
684 | Store `value` for `key` in `map` using custom `hash`.
685 |
686 | Returns a map with the modified value. Does not alter `map`.
687 | */
688 | var setHash = hamt.setHash = function (hash, key, value, map) {
689 | return modifyHash(constant(value), hash, key, map);
690 | };
691 |
692 | Map.prototype.setHash = function (hash, key, value) {
693 | return setHash(hash, key, value, this);
694 | };
695 |
696 | /**
697 | Store `value` for `key` in `map` using internal hash function.
698 |
699 | @see `setHash`
700 | */
701 | var set = hamt.set = function (key, value, map) {
702 | return setHash(map._config.hash(key), key, value, map);
703 | };
704 |
705 | Map.prototype.set = function (key, value) {
706 | return set(key, value, this);
707 | };
708 |
709 | /**
710 | Remove the entry for `key` in `map`.
711 |
712 | Returns a map with the value removed. Does not alter `map`.
713 | */
714 | var del = constant(nothing);
715 | var removeHash = hamt.removeHash = function (hash, key, map) {
716 | return modifyHash(del, hash, key, map);
717 | };
718 |
719 | Map.prototype.removeHash = Map.prototype.deleteHash = function (hash, key) {
720 | return removeHash(hash, key, this);
721 | };
722 |
723 | /**
724 | Remove the entry for `key` in `map` using internal hash function.
725 |
726 | @see `removeHash`
727 | */
728 | var remove = hamt.remove = function (key, map) {
729 | return removeHash(map._config.hash(key), key, map);
730 | };
731 |
732 | Map.prototype.remove = Map.prototype.delete = function (key) {
733 | return remove(key, this);
734 | };
735 |
736 | /* Mutation
737 | ******************************************************************************/
738 | /**
739 | Mark `map` as mutable.
740 | */
741 | var beginMutation = hamt.beginMutation = function (map) {
742 | return new Map(map._editable + 1, map._edit + 1, map._config, map._root, map._size);
743 | };
744 |
745 | Map.prototype.beginMutation = function () {
746 | return beginMutation(this);
747 | };
748 |
749 | /**
750 | Mark `map` as immutable.
751 | */
752 | var endMutation = hamt.endMutation = function (map) {
753 | map._editable = map._editable && map._editable - 1;
754 | return map;
755 | };
756 |
757 | Map.prototype.endMutation = function () {
758 | return endMutation(this);
759 | };
760 |
761 | /**
762 | Mutate `map` within the context of `f`.
763 | @param f
764 | @param map HAMT
765 | */
766 | var mutate = hamt.mutate = function (f, map) {
767 | var transient = beginMutation(map);
768 | f(transient);
769 | return endMutation(transient);
770 | };
771 |
772 | Map.prototype.mutate = function (f) {
773 | return mutate(f, this);
774 | };
775 |
776 | /* Traversal
777 | ******************************************************************************/
778 | /**
779 | Apply a continuation.
780 | */
781 | var appk = function appk(k) {
782 | return k && lazyVisitChildren(k[0], k[1], k[2], k[3], k[4]);
783 | };
784 |
785 | /**
786 | Recursively visit all values stored in an array of nodes lazily.
787 | */
788 | var lazyVisitChildren = function lazyVisitChildren(len, children, i, f, k) {
789 | while (i < len) {
790 | var child = children[i++];
791 | if (child && !isEmptyNode(child)) return lazyVisit(child, f, [len, children, i, f, k]);
792 | }
793 | return appk(k);
794 | };
795 |
796 | /**
797 | Recursively visit all values stored in `node` lazily.
798 | */
799 | var lazyVisit = function lazyVisit(node, f, k) {
800 | switch (node.type) {
801 | case LEAF:
802 | return {
803 | value: f(node),
804 | rest: k
805 | };
806 |
807 | case COLLISION:
808 | case ARRAY:
809 | case INDEX:
810 | var children = node.children;
811 | return lazyVisitChildren(children.length, children, 0, f, k);
812 |
813 | default:
814 | return appk(k);
815 | }
816 | };
817 |
818 | var DONE = {
819 | done: true
820 | };
821 |
822 | /**
823 | Javascript iterator over a map.
824 | */
825 | function MapIterator(v) {
826 | this.v = v;
827 | };
828 |
829 | MapIterator.prototype.next = function () {
830 | if (!this.v) return DONE;
831 | var v0 = this.v;
832 | this.v = appk(v0.rest);
833 | return v0;
834 | };
835 |
836 | MapIterator.prototype[Symbol.iterator] = function () {
837 | return this;
838 | };
839 |
840 | /**
841 | Lazily visit each value in map with function `f`.
842 | */
843 | var visit = function visit(map, f) {
844 | return new MapIterator(lazyVisit(map._root, f));
845 | };
846 |
847 | /**
848 | Get a Javascsript iterator of `map`.
849 |
850 | Iterates over `[key, value]` arrays.
851 | */
852 | var buildPairs = function buildPairs(x) {
853 | return [x.key, x.value];
854 | };
855 | var entries = hamt.entries = function (map) {
856 | return visit(map, buildPairs);
857 | };
858 |
859 | Map.prototype.entries = Map.prototype[Symbol.iterator] = function () {
860 | return entries(this);
861 | };
862 |
863 | /**
864 | Get array of all keys in `map`.
865 |
866 | Order is not guaranteed.
867 | */
868 | var buildKeys = function buildKeys(x) {
869 | return x.key;
870 | };
871 | var keys = hamt.keys = function (map) {
872 | return visit(map, buildKeys);
873 | };
874 |
875 | Map.prototype.keys = function () {
876 | return keys(this);
877 | };
878 |
879 | /**
880 | Get array of all values in `map`.
881 |
882 | Order is not guaranteed, duplicates are preserved.
883 | */
884 | var buildValues = function buildValues(x) {
885 | return x.value;
886 | };
887 | var values = hamt.values = Map.prototype.values = function (map) {
888 | return visit(map, buildValues);
889 | };
890 |
891 | Map.prototype.values = function () {
892 | return values(this);
893 | };
894 |
895 | /* Fold
896 | ******************************************************************************/
897 | /**
898 | Visit every entry in the map, aggregating data.
899 |
900 | Order of nodes is not guaranteed.
901 |
902 | @param f Function mapping accumulated value, value, and key to new value.
903 | @param z Starting value.
904 | @param m HAMT
905 | */
906 | var fold = hamt.fold = function (f, z, m) {
907 | var root = m._root;
908 | if (root.type === LEAF) return f(z, root.value, root.key);
909 |
910 | var toVisit = [root.children];
911 | var children = void 0;
912 | while (children = toVisit.pop()) {
913 | for (var i = 0, len = children.length; i < len;) {
914 | var child = children[i++];
915 | if (child && child.type) {
916 | if (child.type === LEAF) z = f(z, child.value, child.key);else toVisit.push(child.children);
917 | }
918 | }
919 | }
920 | return z;
921 | };
922 |
923 | Map.prototype.fold = function (f, z) {
924 | return fold(f, z, this);
925 | };
926 |
927 | /**
928 | Visit every entry in the map, aggregating data.
929 |
930 | Order of nodes is not guaranteed.
931 |
932 | @param f Function invoked with value and key
933 | @param map HAMT
934 | */
935 | var forEach = hamt.forEach = function (f, map) {
936 | return fold(function (_, value, key) {
937 | return f(value, key, map);
938 | }, null, map);
939 | };
940 |
941 | Map.prototype.forEach = function (f) {
942 | return forEach(f, this);
943 | };
944 |
945 | /* Aggregate
946 | ******************************************************************************/
947 | /**
948 | Get the number of entries in `map`.
949 | */
950 | var count = hamt.count = function (map) {
951 | return map._size;
952 | };
953 |
954 | Map.prototype.count = function () {
955 | return count(this);
956 | };
957 |
958 | Object.defineProperty(Map.prototype, 'size', {
959 | get: Map.prototype.count
960 | });
961 |
962 | /* Export
963 | ******************************************************************************/
964 | if (typeof module !== 'undefined' && module.exports) {
965 | module.exports = hamt;
966 | } else if (typeof define === 'function' && define.amd) {
967 | define('hamt', [], function () {
968 | return hamt;
969 | });
970 | } else {
971 | undefined.hamt = hamt;
972 | }
973 | //# sourceMappingURL=hamt.js.map
974 |
--------------------------------------------------------------------------------
/hamt.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["hamt.js"],"names":["hamt","SIZE","BUCKET_SIZE","Math","pow","MASK","MAX_INDEX_NODE","MIN_ARRAY_NODE","nothing","constant","x","hash","type","str","i","len","length","c","charCodeAt","popcount","hashFragment","shift","h","toBitmap","fromBitmap","bitmap","bit","arrayUpdate","mutate","at","v","arr","out","Array","arraySpliceOut","newLen","g","arraySpliceIn","LEAF","COLLISION","INDEX","ARRAY","empty","__hamt_isEmpty","isEmptyNode","Leaf","edit","key","value","_modify","Leaf__modify","Collision","children","Collision__modify","IndexedNode","mask","IndexedNode__modify","ArrayNode","size","ArrayNode__modify","isLeaf","node","expand","frag","child","subNodes","count","pack","removed","elements","elem","mergeLeaves","h1","n1","h2","n2","subH1","subH2","updateCollisionList","keyEq","list","f","k","newValue","canEditNode","canEdit","indx","exists","current","newChildren","newChild","Map","editable","config","root","_editable","_edit","_config","_root","_size","prototype","setTree","newRoot","newSize","tryGetHash","alt","map","tryGet","getHash","undefined","get","hasHash","has","defKeyCompare","y","make","isEmpty","modifyHash","NaN","modify","setHash","set","del","removeHash","deleteHash","remove","delete","beginMutation","endMutation","transient","appk","lazyVisitChildren","lazyVisit","rest","DONE","done","MapIterator","next","v0","Symbol","iterator","visit","buildPairs","entries","buildKeys","keys","buildValues","values","fold","z","m","toVisit","pop","push","forEach","_","Object","defineProperty","module","exports","define","amd"],"mappings":"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","file":"hamt.js","sourcesContent":["/**\n @fileOverview Hash Array Mapped Trie.\n\n Code based on: https://github.com/exclipy/pdata\n*/\nconst hamt = {}; // export\n\n/* Configuration\n ******************************************************************************/\nconst SIZE = 5;\n\nconst BUCKET_SIZE = Math.pow(2, SIZE);\n\nconst MASK = BUCKET_SIZE - 1;\n\nconst MAX_INDEX_NODE = BUCKET_SIZE / 2;\n\nconst MIN_ARRAY_NODE = BUCKET_SIZE / 4;\n\n/*\n ******************************************************************************/\nconst nothing = ({});\n\nconst constant = x => () => x;\n\n/**\n Get 32 bit hash of string.\n\n Based on:\n http://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript-jquery\n*/\nconst hash = hamt.hash = str => {\n const type = typeof str;\n if (type === 'number')\n return str;\n if (type !== 'string')\n str += '';\n\n let hash = 0;\n for (let i = 0, len = str.length; i < len; ++i) {\n const c = str.charCodeAt(i);\n hash = (((hash << 5) - hash) + c) | 0;\n }\n return hash;\n};\n\n/* Bit Ops\n ******************************************************************************/\n/**\n Hamming weight.\n\n Taken from: http://jsperf.com/hamming-weight\n*/\nconst popcount = (x) => {\n x -= ((x >> 1) & 0x55555555);\n x = (x & 0x33333333) + ((x >> 2) & 0x33333333);\n x = (x + (x >> 4)) & 0x0f0f0f0f;\n x += (x >> 8);\n x += (x >> 16);\n return (x & 0x7f);\n};\n\nconst hashFragment = (shift, h) =>\n (h >>> shift) & MASK;\n\nconst toBitmap = x =>\n 1 << x;\n\nconst fromBitmap = (bitmap, bit) =>\n popcount(bitmap & (bit - 1));\n\n/* Array Ops\n ******************************************************************************/\n/**\n Set a value in an array.\n\n @param mutate Should the input array be mutated?\n @param at Index to change.\n @param v New value\n @param arr Array.\n*/\nconst arrayUpdate = (mutate, at, v, arr) => {\n let out = arr;\n if (!mutate) {\n const len = arr.length;\n out = new Array(len);\n for (let i = 0; i < len; ++i)\n out[i] = arr[i];\n }\n out[at] = v;\n return out;\n};\n\n/**\n Remove a value from an array.\n\n @param mutate Should the input array be mutated?\n @param at Index to remove.\n @param arr Array.\n*/\nconst arraySpliceOut = (mutate, at, arr) => {\n const newLen = arr.length - 1;\n let i = 0;\n let g = 0;\n let out = arr;\n if (mutate) {\n i = g = at;\n } else {\n out = new Array(newLen);\n while (i < at)\n out[g++] = arr[i++];\n }\n ++i;\n while (i <= newLen)\n out[g++] = arr[i++];\n if (mutate) {\n out.length = newLen;\n }\n return out;\n};\n\n/**\n Insert a value into an array.\n\n @param mutate Should the input array be mutated?\n @param at Index to insert at.\n @param v Value to insert,\n @param arr Array.\n*/\nconst arraySpliceIn = (mutate, at, v, arr) => {\n const len = arr.length;\n if (mutate) {\n let i = len;\n while (i >= at)\n arr[i--] = arr[i];\n arr[at] = v;\n return arr;\n }\n let i = 0, g = 0;\n const out = new Array(len + 1);\n while (i < at)\n out[g++] = arr[i++];\n out[at] = v;\n while (i < len)\n out[++g] = arr[i++];\n return out;\n};\n\n/* Node Structures\n ******************************************************************************/\nconst LEAF = 1;\nconst COLLISION = 2;\nconst INDEX = 3;\nconst ARRAY = 4;\n\n/**\n Empty node.\n*/\nconst empty = ({\n __hamt_isEmpty: true\n});\n\nconst isEmptyNode = x =>\n x === empty || (x && x.__hamt_isEmpty);\n\n/**\n Leaf holding a value.\n\n @member edit Edit of the node.\n @member hash Hash of key.\n @member key Key.\n @member value Value stored.\n*/\nconst Leaf = (edit, hash, key, value) => ({\n type: LEAF,\n edit: edit,\n hash: hash,\n key: key,\n value: value,\n _modify: Leaf__modify\n});\n\n/**\n Leaf holding multiple values with the same hash but different keys.\n\n @member edit Edit of the node.\n @member hash Hash of key.\n @member children Array of collision children node.\n*/\nconst Collision = (edit, hash, children) => ({\n type: COLLISION,\n edit: edit,\n hash: hash,\n children: children,\n _modify: Collision__modify\n});\n\n/**\n Internal node with a sparse set of children.\n\n Uses a bitmap and array to pack children.\n\n @member edit Edit of the node.\n @member mask Bitmap that encode the positions of children in the array.\n @member children Array of child nodes.\n*/\nconst IndexedNode = (edit, mask, children) => ({\n type: INDEX,\n edit: edit,\n mask: mask,\n children: children,\n _modify: IndexedNode__modify\n});\n\n/**\n Internal node with many children.\n\n @member edit Edit of the node.\n @member size Number of children.\n @member children Array of child nodes.\n*/\nconst ArrayNode = (edit, size, children) => ({\n type: ARRAY,\n edit: edit,\n size: size,\n children: children,\n _modify: ArrayNode__modify\n});\n\n/**\n Is `node` a leaf node?\n*/\nconst isLeaf = node =>\n (node === empty || node.type === LEAF || node.type === COLLISION);\n\n/* Internal node operations.\n ******************************************************************************/\n/**\n Expand an indexed node into an array node.\n\n @param edit Current edit.\n @param frag Index of added child.\n @param child Added child.\n @param mask Index node mask before child added.\n @param subNodes Index node children before child added.\n*/\nconst expand = (edit, frag, child, bitmap, subNodes) => {\n const arr = [];\n let bit = bitmap;\n let count = 0;\n for (let i = 0; bit; ++i) {\n if (bit & 1)\n arr[i] = subNodes[count++];\n bit >>>= 1;\n }\n arr[frag] = child;\n return ArrayNode(edit, count + 1, arr);\n};\n\n/**\n Collapse an array node into a indexed node.\n\n @param edit Current edit.\n @param count Number of elements in new array.\n @param removed Index of removed element.\n @param elements Array node children before remove.\n*/\nconst pack = (edit, count, removed, elements) => {\n const children = new Array(count - 1);\n let g = 0;\n let bitmap = 0;\n for (let i = 0, len = elements.length; i < len; ++i) {\n if (i !== removed) {\n const elem = elements[i];\n if (elem && !isEmptyNode(elem)) {\n children[g++] = elem;\n bitmap |= 1 << i;\n }\n }\n }\n return IndexedNode(edit, bitmap, children);\n};\n\n/**\n Merge two leaf nodes.\n\n @param shift Current shift.\n @param h1 Node 1 hash.\n @param n1 Node 1.\n @param h2 Node 2 hash.\n @param n2 Node 2.\n*/\nconst mergeLeaves = (edit, shift, h1, n1, h2, n2) => {\n if (h1 === h2)\n return Collision(edit, h1, [n2, n1]);\n\n const subH1 = hashFragment(shift, h1);\n const subH2 = hashFragment(shift, h2);\n return IndexedNode(edit, toBitmap(subH1) | toBitmap(subH2),\n subH1 === subH2\n ? [mergeLeaves(edit, shift + SIZE, h1, n1, h2, n2)]\n : subH1 < subH2 ? [n1, n2] : [n2, n1]);\n};\n\n/**\n Update an entry in a collision list.\n\n @param mutate Should mutation be used?\n @param edit Current edit.\n @param keyEq Key compare function.\n @param hash Hash of collision.\n @param list Collision list.\n @param f Update function.\n @param k Key to update.\n @param size Size ref.\n*/\nconst updateCollisionList = (mutate, edit, keyEq, h, list, f, k, size) => {\n const len = list.length;\n for (let i = 0; i < len; ++i) {\n const child = list[i];\n if (keyEq(k, child.key)) {\n const value = child.value;\n const newValue = f(value);\n if (newValue === value)\n return list;\n\n if (newValue === nothing) {\n --size.value;\n return arraySpliceOut(mutate, i, list);\n }\n return arrayUpdate(mutate, i, Leaf(edit, h, k, newValue), list);\n }\n }\n\n const newValue = f();\n if (newValue === nothing)\n return list;\n ++size.value;\n return arrayUpdate(mutate, len, Leaf(edit, h, k, newValue), list);\n};\n\nconst canEditNode = (edit, node) => edit === node.edit;\n\n/* Editing\n ******************************************************************************/\nconst Leaf__modify = function (edit, keyEq, shift, f, h, k, size) {\n if (keyEq(k, this.key)) {\n const v = f(this.value);\n if (v === this.value)\n return this;\n else if (v === nothing) {\n --size.value;\n return empty;\n }\n if (canEditNode(edit, this)) {\n this.value = v;\n return this;\n }\n return Leaf(edit, h, k, v);\n }\n const v = f();\n if (v === nothing)\n return this;\n ++size.value;\n return mergeLeaves(edit, shift, this.hash, this, h, Leaf(edit, h, k, v));\n};\n\nconst Collision__modify = function (edit, keyEq, shift, f, h, k, size) {\n if (h === this.hash) {\n const canEdit = canEditNode(edit, this);\n const list = updateCollisionList(canEdit, edit, keyEq, this.hash, this.children, f, k, size);\n if (list === this.children)\n return this;\n\n return list.length > 1\n ? Collision(edit, this.hash, list)\n : list[0]; // collapse single element collision list\n }\n const v = f();\n if (v === nothing)\n return this;\n ++size.value;\n return mergeLeaves(edit, shift, this.hash, this, h, Leaf(edit, h, k, v));\n};\n\nconst IndexedNode__modify = function (edit, keyEq, shift, f, h, k, size) {\n const mask = this.mask;\n const children = this.children;\n const frag = hashFragment(shift, h);\n const bit = toBitmap(frag);\n const indx = fromBitmap(mask, bit);\n const exists = mask & bit;\n const current = exists ? children[indx] : empty;\n const child = current._modify(edit, keyEq, shift + SIZE, f, h, k, size);\n\n if (current === child)\n return this;\n\n const canEdit = canEditNode(edit, this);\n let bitmap = mask;\n let newChildren;\n if (exists && isEmptyNode(child)) { // remove\n bitmap &= ~bit;\n if (!bitmap)\n return empty;\n if (children.length <= 2 && isLeaf(children[indx ^ 1]))\n return children[indx ^ 1] // collapse\n\n newChildren = arraySpliceOut(canEdit, indx, children);\n } else if (!exists && !isEmptyNode(child)) { // add\n if (children.length >= MAX_INDEX_NODE)\n return expand(edit, frag, child, mask, children);\n\n bitmap |= bit;\n newChildren = arraySpliceIn(canEdit, indx, child, children);\n } else { // modify\n newChildren = arrayUpdate(canEdit, indx, child, children);\n }\n\n if (canEdit) {\n this.mask = bitmap;\n this.children = newChildren;\n return this;\n }\n return IndexedNode(edit, bitmap, newChildren);\n};\n\nconst ArrayNode__modify = function (edit, keyEq, shift, f, h, k, size) {\n let count = this.size;\n const children = this.children;\n const frag = hashFragment(shift, h);\n const child = children[frag];\n const newChild = (child || empty)._modify(edit, keyEq, shift + SIZE, f, h, k, size);\n\n if (child === newChild)\n return this;\n\n const canEdit = canEditNode(edit, this);\n let newChildren;\n if (isEmptyNode(child) && !isEmptyNode(newChild)) { // add\n ++count;\n newChildren = arrayUpdate(canEdit, frag, newChild, children);\n } else if (!isEmptyNode(child) && isEmptyNode(newChild)) { // remove\n --count;\n if (count <= MIN_ARRAY_NODE)\n return pack(edit, count, frag, children);\n newChildren = arrayUpdate(canEdit, frag, empty, children);\n } else { // modify\n newChildren = arrayUpdate(canEdit, frag, newChild, children);\n }\n\n if (canEdit) {\n this.size = count;\n this.children = newChildren;\n return this;\n }\n return ArrayNode(edit, count, newChildren);\n};\n\nempty._modify = (edit, keyEq, shift, f, h, k, size) => {\n const v = f();\n if (v === nothing)\n return empty;\n ++size.value;\n return Leaf(edit, h, k, v);\n};\n\n/*\n ******************************************************************************/\nfunction Map(editable, edit, config, root, size) {\n this._editable = editable;\n this._edit = edit;\n this._config = config;\n this._root = root;\n this._size = size;\n};\n\nMap.prototype.setTree = function (newRoot, newSize) {\n if (this._editable) {\n this._root = newRoot;\n this._size = newSize;\n return this;\n }\n return newRoot === this._root\n ? this\n : new Map(this._editable, this._edit, this._config, newRoot, newSize);\n};\n\n/* Queries\n ******************************************************************************/\n/**\n Lookup the value for `key` in `map` using a custom `hash`.\n\n Returns the value or `alt` if none.\n*/\nconst tryGetHash = hamt.tryGetHash = (alt, hash, key, map) => {\n let node = map._root;\n let shift = 0;\n const keyEq = map._config.keyEq;\n while (true) switch (node.type) {\n case LEAF:\n {\n return keyEq(key, node.key) ? node.value : alt;\n }\n case COLLISION:\n {\n if (hash === node.hash) {\n const children = node.children;\n for (let i = 0, len = children.length; i < len; ++i) {\n const child = children[i];\n if (keyEq(key, child.key))\n return child.value;\n }\n }\n return alt;\n }\n case INDEX:\n {\n const frag = hashFragment(shift, hash);\n const bit = toBitmap(frag);\n if (node.mask & bit) {\n node = node.children[fromBitmap(node.mask, bit)]\n shift += SIZE;\n break;\n }\n return alt;\n }\n case ARRAY:\n {\n node = node.children[hashFragment(shift, hash)];\n if (node) {\n shift += SIZE;\n break;\n }\n return alt;\n }\n default:\n return alt;\n }\n};\n\nMap.prototype.tryGetHash = function (alt, hash, key) {\n return tryGetHash(alt, hash, key, this);\n};\n\n/**\n Lookup the value for `key` in `map` using internal hash function.\n\n @see `tryGetHash`\n*/\nconst tryGet = hamt.tryGet = (alt, key, map) =>\n tryGetHash(alt, map._config.hash(key), key, map);\n\nMap.prototype.tryGet = function (alt, key) {\n return tryGet(alt, key, this);\n};\n\n/**\n Lookup the value for `key` in `map` using a custom `hash`.\n\n Returns the value or `undefined` if none.\n*/\nconst getHash = hamt.getHash = (hash, key, map) =>\n tryGetHash(undefined, hash, key, map);\n\nMap.prototype.getHash = function (hash, key) {\n return getHash(hash, key, this);\n};\n\n/**\n Lookup the value for `key` in `map` using internal hash function.\n\n @see `get`\n*/\nconst get = hamt.get = (key, map) =>\n tryGetHash(undefined, map._config.hash(key), key, map);\n\nMap.prototype.get = function (key, alt) {\n return tryGet(alt, key, this);\n};\n\n/**\n Does an entry exist for `key` in `map`? Uses custom `hash`.\n*/\nconst hasHash = hamt.has = (hash, key, map) =>\n tryGetHash(nothing, hash, key, map) !== nothing;\n\nMap.prototype.hasHash = function (hash, key) {\n return hasHash(hash, key, this);\n};\n\n/**\n Does an entry exist for `key` in `map`? Uses internal hash function.\n*/\nconst has = hamt.has = (key, map) =>\n hasHash(map._config.hash(key), key, map);\n\nMap.prototype.has = function (key) {\n return has(key, this);\n};\n\nconst defKeyCompare = (x, y) => x === y;\n\n/**\n Create an empty map.\n\n @param config Configuration.\n*/\nhamt.make = (config) =>\n new Map(0, 0, {\n keyEq: (config && config.keyEq) || defKeyCompare,\n hash: (config && config.hash) || hash\n }, empty, 0);\n\n/**\n Empty map.\n*/\nhamt.empty = hamt.make();\n\n/**\n Does `map` contain any elements?\n*/\nconst isEmpty = hamt.isEmpty = (map) =>\n map && !!isEmptyNode(map._root);\n\nMap.prototype.isEmpty = function () {\n return isEmpty(this);\n};\n\n/* Updates\n ******************************************************************************/\n/**\n Alter the value stored for `key` in `map` using function `f` using\n custom hash.\n\n `f` is invoked with the current value for `k` if it exists,\n or no arguments if no such value exists. `modify` will always either\n update or insert a value into the map.\n\n Returns a map with the modified value. Does not alter `map`.\n*/\nconst modifyHash = hamt.modifyHash = (f, hash, key, map) => {\n const size = { value: map._size };\n const newRoot = map._root._modify(\n map._editable ? map._edit : NaN,\n map._config.keyEq,\n 0,\n f,\n hash,\n key,\n size);\n return map.setTree(newRoot, size.value);\n};\n\nMap.prototype.modifyHash = function (hash, key, f) {\n return modifyHash(f, hash, key, this);\n};\n\n/**\n Alter the value stored for `key` in `map` using function `f` using\n internal hash function.\n\n @see `modifyHash`\n*/\nconst modify = hamt.modify = (f, key, map) =>\n modifyHash(f, map._config.hash(key), key, map);\n\nMap.prototype.modify = function (key, f) {\n return modify(f, key, this);\n};\n\n/**\n Store `value` for `key` in `map` using custom `hash`.\n\n Returns a map with the modified value. Does not alter `map`.\n*/\nconst setHash = hamt.setHash = (hash, key, value, map) =>\n modifyHash(constant(value), hash, key, map);\n\nMap.prototype.setHash = function (hash, key, value) {\n return setHash(hash, key, value, this);\n};\n\n/**\n Store `value` for `key` in `map` using internal hash function.\n\n @see `setHash`\n*/\nconst set = hamt.set = (key, value, map) =>\n setHash(map._config.hash(key), key, value, map);\n\nMap.prototype.set = function (key, value) {\n return set(key, value, this);\n};\n\n/**\n Remove the entry for `key` in `map`.\n\n Returns a map with the value removed. Does not alter `map`.\n*/\nconst del = constant(nothing);\nconst removeHash = hamt.removeHash = (hash, key, map) =>\n modifyHash(del, hash, key, map);\n\nMap.prototype.removeHash = Map.prototype.deleteHash = function (hash, key) {\n return removeHash(hash, key, this);\n};\n\n/**\n Remove the entry for `key` in `map` using internal hash function.\n\n @see `removeHash`\n*/\nconst remove = hamt.remove = (key, map) =>\n removeHash(map._config.hash(key), key, map);\n\nMap.prototype.remove = Map.prototype.delete = function (key) {\n return remove(key, this);\n};\n\n/* Mutation\n ******************************************************************************/\n/**\n Mark `map` as mutable.\n */\nconst beginMutation = hamt.beginMutation = (map) =>\n new Map(\n map._editable + 1,\n map._edit + 1,\n map._config,\n map._root,\n map._size);\n\nMap.prototype.beginMutation = function () {\n return beginMutation(this);\n};\n\n/**\n Mark `map` as immutable.\n */\nconst endMutation = hamt.endMutation = (map) => {\n map._editable = map._editable && map._editable - 1;\n return map;\n};\n\nMap.prototype.endMutation = function () {\n return endMutation(this);\n};\n\n/**\n Mutate `map` within the context of `f`.\n @param f\n @param map HAMT\n*/\nconst mutate = hamt.mutate = (f, map) => {\n const transient = beginMutation(map);\n f(transient);\n return endMutation(transient);\n};\n\nMap.prototype.mutate = function (f) {\n return mutate(f, this);\n};\n\n/* Traversal\n ******************************************************************************/\n/**\n Apply a continuation.\n*/\nconst appk = k =>\n k && lazyVisitChildren(k[0], k[1], k[2], k[3], k[4]);\n\n/**\n Recursively visit all values stored in an array of nodes lazily.\n*/\nvar lazyVisitChildren = (len, children, i, f, k) => {\n while (i < len) {\n const child = children[i++];\n if (child && !isEmptyNode(child))\n return lazyVisit(child, f, [len, children, i, f, k]);\n }\n return appk(k);\n};\n\n/**\n Recursively visit all values stored in `node` lazily.\n*/\nconst lazyVisit = (node, f, k) => {\n switch (node.type) {\n case LEAF:\n return {\n value: f(node),\n rest: k\n };\n\n case COLLISION:\n case ARRAY:\n case INDEX:\n const children = node.children;\n return lazyVisitChildren(children.length, children, 0, f, k);\n\n default:\n return appk(k);\n }\n};\n\nconst DONE = {\n done: true\n};\n\n/**\n Javascript iterator over a map.\n*/\nfunction MapIterator(v) {\n this.v = v;\n};\n\nMapIterator.prototype.next = function () {\n if (!this.v)\n return DONE;\n const v0 = this.v;\n this.v = appk(v0.rest);\n return v0;\n};\n\nMapIterator.prototype[Symbol.iterator] = function () {\n return this;\n};\n\n/**\n Lazily visit each value in map with function `f`.\n*/\nconst visit = (map, f) =>\n new MapIterator(lazyVisit(map._root, f));\n\n/**\n Get a Javascsript iterator of `map`.\n\n Iterates over `[key, value]` arrays.\n*/\nconst buildPairs = (x) => [x.key, x.value];\nconst entries = hamt.entries = (map) =>\n visit(map, buildPairs);\n\nMap.prototype.entries = Map.prototype[Symbol.iterator] = function () {\n return entries(this);\n};\n\n/**\n Get array of all keys in `map`.\n\n Order is not guaranteed.\n*/\nconst buildKeys = (x) => x.key;\nconst keys = hamt.keys = (map) =>\n visit(map, buildKeys);\n\nMap.prototype.keys = function () {\n return keys(this);\n}\n\n/**\n Get array of all values in `map`.\n\n Order is not guaranteed, duplicates are preserved.\n*/\nconst buildValues = x => x.value;\nconst values = hamt.values = Map.prototype.values = map =>\n visit(map, buildValues);\n\nMap.prototype.values = function () {\n return values(this);\n};\n\n/* Fold\n ******************************************************************************/\n/**\n Visit every entry in the map, aggregating data.\n\n Order of nodes is not guaranteed.\n\n @param f Function mapping accumulated value, value, and key to new value.\n @param z Starting value.\n @param m HAMT\n*/\nconst fold = hamt.fold = (f, z, m) => {\n const root = m._root;\n if (root.type === LEAF)\n return f(z, root.value, root.key);\n\n const toVisit = [root.children];\n let children;\n while (children = toVisit.pop()) {\n for (let i = 0, len = children.length; i < len;) {\n const child = children[i++];\n if (child && child.type) {\n if (child.type === LEAF)\n z = f(z, child.value, child.key);\n else\n toVisit.push(child.children);\n }\n }\n }\n return z;\n};\n\nMap.prototype.fold = function (f, z) {\n return fold(f, z, this);\n};\n\n/**\n Visit every entry in the map, aggregating data.\n\n Order of nodes is not guaranteed.\n\n @param f Function invoked with value and key\n @param map HAMT\n*/\nconst forEach = hamt.forEach = (f, map) =>\n fold((_, value, key) => f(value, key, map), null, map);\n\nMap.prototype.forEach = function (f) {\n return forEach(f, this);\n};\n\n/* Aggregate\n ******************************************************************************/\n/**\n Get the number of entries in `map`.\n*/\nconst count = hamt.count = map =>\n map._size;\n\nMap.prototype.count = function () {\n return count(this);\n};\n\nObject.defineProperty(Map.prototype, 'size', {\n get: Map.prototype.count\n});\n\n/* Export\n ******************************************************************************/\nif (typeof module !== 'undefined' && module.exports) {\n module.exports = hamt;\n} else if (typeof define === 'function' && define.amd) {\n define('hamt', [], () => hamt);\n} else {\n this.hamt = hamt;\n}\n"]}
--------------------------------------------------------------------------------