create-mendix-widget-gleam 2.0.14 → 2.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +5 -1
- package/src/index.mjs +32 -12
- package/template/gleam.toml +1 -1
- package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@command.cache +0 -0
- package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@command.cache_inline +0 -0
- package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@command.cache_meta +0 -0
- package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@cursor.cache +0 -0
- package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@cursor.cache_inline +0 -0
- package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@cursor.cache_meta +0 -0
- package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@event.cache +0 -0
- package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@event.cache_inline +0 -0
- package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@event.cache_meta +0 -0
- package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@internal@consts.cache +0 -0
- package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@internal@consts.cache_inline +0 -0
- package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@internal@consts.cache_meta +0 -0
- package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@stdout.cache +0 -0
- package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@stdout.cache_inline +0 -0
- package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@stdout.cache_meta +0 -0
- package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@style.cache +0 -0
- package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@style.cache_inline +0 -0
- package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@style.cache_meta +0 -0
- package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@terminal.cache +0 -0
- package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@terminal.cache_inline +0 -0
- package/tui/build/dev/javascript/etch/_gleam_artefacts/etch@terminal.cache_meta +0 -0
- package/tui/build/dev/javascript/etch/etch/command.mjs +479 -0
- package/tui/build/dev/javascript/etch/etch/cursor.mjs +164 -0
- package/tui/build/dev/javascript/etch/etch/event.mjs +2399 -0
- package/tui/build/dev/javascript/etch/etch/internal/consts.mjs +3 -0
- package/tui/build/dev/javascript/etch/etch/stdout.mjs +375 -0
- package/tui/build/dev/javascript/etch/etch/style.mjs +741 -0
- package/tui/build/dev/javascript/etch/etch/terminal.mjs +137 -0
- package/tui/build/dev/javascript/etch/gleam.mjs +1 -0
- package/tui/build/dev/javascript/etch/input/event_ffi.erl +73 -0
- package/tui/build/dev/javascript/etch/input/input_ffi.mjs +192 -0
- package/tui/build/dev/javascript/etch/input/signal_handler.erl +33 -0
- package/tui/build/dev/javascript/etch/terminal/terminal_ffi.erl +22 -0
- package/tui/build/dev/javascript/etch/terminal/terminal_ffi.mjs +37 -0
- package/tui/build/dev/javascript/etch/terminal/tty_state.erl +29 -0
- package/tui/build/dev/javascript/gleam_erlang/_gleam_artefacts/gleam@erlang@application.cache +0 -0
- package/tui/build/dev/javascript/gleam_erlang/_gleam_artefacts/gleam@erlang@application.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_erlang/_gleam_artefacts/gleam@erlang@application.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_erlang/_gleam_artefacts/gleam@erlang@atom.cache +0 -0
- package/tui/build/dev/javascript/gleam_erlang/_gleam_artefacts/gleam@erlang@atom.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_erlang/_gleam_artefacts/gleam@erlang@atom.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_erlang/_gleam_artefacts/gleam@erlang@charlist.cache +0 -0
- package/tui/build/dev/javascript/gleam_erlang/_gleam_artefacts/gleam@erlang@charlist.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_erlang/_gleam_artefacts/gleam@erlang@charlist.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_erlang/_gleam_artefacts/gleam@erlang@node.cache +0 -0
- package/tui/build/dev/javascript/gleam_erlang/_gleam_artefacts/gleam@erlang@node.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_erlang/_gleam_artefacts/gleam@erlang@node.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_erlang/_gleam_artefacts/gleam@erlang@port.cache +0 -0
- package/tui/build/dev/javascript/gleam_erlang/_gleam_artefacts/gleam@erlang@port.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_erlang/_gleam_artefacts/gleam@erlang@port.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_erlang/_gleam_artefacts/gleam@erlang@process.cache +0 -0
- package/tui/build/dev/javascript/gleam_erlang/_gleam_artefacts/gleam@erlang@process.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_erlang/_gleam_artefacts/gleam@erlang@process.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_erlang/_gleam_artefacts/gleam@erlang@reference.cache +0 -0
- package/tui/build/dev/javascript/gleam_erlang/_gleam_artefacts/gleam@erlang@reference.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_erlang/_gleam_artefacts/gleam@erlang@reference.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_erlang/gleam/erlang/application.mjs +38 -0
- package/tui/build/dev/javascript/gleam_erlang/gleam/erlang/atom.mjs +2 -0
- package/tui/build/dev/javascript/gleam_erlang/gleam/erlang/charlist.mjs +1 -0
- package/tui/build/dev/javascript/gleam_erlang/gleam/erlang/node.mjs +12 -0
- package/tui/build/dev/javascript/gleam_erlang/gleam/erlang/port.mjs +1 -0
- package/tui/build/dev/javascript/gleam_erlang/gleam/erlang/process.mjs +161 -0
- package/tui/build/dev/javascript/gleam_erlang/gleam/erlang/reference.mjs +1 -0
- package/tui/build/dev/javascript/gleam_erlang/gleam.mjs +1 -0
- package/tui/build/dev/javascript/gleam_erlang/gleam@erlang@application.erl +43 -0
- package/tui/build/dev/javascript/gleam_erlang/gleam@erlang@atom.erl +94 -0
- package/tui/build/dev/javascript/gleam_erlang/gleam@erlang@charlist.erl +42 -0
- package/tui/build/dev/javascript/gleam_erlang/gleam@erlang@node.erl +80 -0
- package/tui/build/dev/javascript/gleam_erlang/gleam@erlang@port.erl +8 -0
- package/tui/build/dev/javascript/gleam_erlang/gleam@erlang@process.erl +868 -0
- package/tui/build/dev/javascript/gleam_erlang/gleam@erlang@reference.erl +21 -0
- package/tui/build/dev/javascript/gleam_erlang/gleam_erlang_ffi.erl +164 -0
- package/tui/build/dev/javascript/gleam_javascript/_gleam_artefacts/gleam@javascript@array.cache +0 -0
- package/tui/build/dev/javascript/gleam_javascript/_gleam_artefacts/gleam@javascript@array.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_javascript/_gleam_artefacts/gleam@javascript@array.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_javascript/_gleam_artefacts/gleam@javascript@promise.cache +0 -0
- package/tui/build/dev/javascript/gleam_javascript/_gleam_artefacts/gleam@javascript@promise.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_javascript/_gleam_artefacts/gleam@javascript@promise.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_javascript/_gleam_artefacts/gleam@javascript@symbol.cache +0 -0
- package/tui/build/dev/javascript/gleam_javascript/_gleam_artefacts/gleam@javascript@symbol.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_javascript/_gleam_artefacts/gleam@javascript@symbol.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_javascript/gleam/javascript/array.mjs +24 -0
- package/tui/build/dev/javascript/gleam_javascript/gleam/javascript/promise.mjs +105 -0
- package/tui/build/dev/javascript/gleam_javascript/gleam/javascript/symbol.mjs +7 -0
- package/tui/build/dev/javascript/gleam_javascript/gleam.mjs +1 -0
- package/tui/build/dev/javascript/gleam_javascript/gleam_javascript_ffi.mjs +133 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bit_array.cache +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bit_array.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bit_array.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bool.cache +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bool.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bool.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bytes_tree.cache +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bytes_tree.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bytes_tree.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@dict.cache +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@dict.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@dict.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@dynamic.cache +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@dynamic.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@dynamic.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@dynamic@decode.cache +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@dynamic@decode.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@dynamic@decode.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@float.cache +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@float.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@float.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@function.cache +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@function.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@function.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@int.cache +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@int.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@int.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@io.cache +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@io.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@io.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@list.cache +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@list.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@list.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@option.cache +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@option.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@option.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@order.cache +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@order.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@order.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@pair.cache +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@pair.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@pair.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@result.cache +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@result.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@result.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@set.cache +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@set.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@set.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@string.cache +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@string.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@string.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@string_tree.cache +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@string_tree.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@string_tree.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@uri.cache +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@uri.cache_inline +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@uri.cache_meta +0 -0
- package/tui/build/dev/javascript/gleam_stdlib/dict.mjs +710 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam/bit_array.mjs +286 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam/bool.mjs +295 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam/bytes_tree.mjs +225 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam/dict.mjs +455 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam/dynamic/decode.mjs +993 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam/dynamic.mjs +35 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam/float.mjs +528 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam/function.mjs +6 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam/int.mjs +764 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam/io.mjs +8 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam/list.mjs +3063 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam/option.mjs +386 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam/order.mjs +166 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam/pair.mjs +96 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam/result.mjs +448 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam/set.mjs +413 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam/string.mjs +695 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam/string_tree.mjs +128 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam/uri.mjs +1151 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam.mjs +1 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam@bit_array.erl +347 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam@bool.erl +334 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam@bytes_tree.erl +211 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam@dict.erl +513 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam@dynamic.erl +105 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam@dynamic@decode.erl +1114 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam@float.erl +711 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam@function.erl +18 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam@int.erl +972 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam@io.erl +76 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam@list.erl +2735 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam@option.erl +381 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam@order.erl +188 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam@pair.erl +104 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam@result.erl +500 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam@set.erl +430 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam@string.erl +964 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam@string_tree.erl +202 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam@uri.erl +1042 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam_stdlib.erl +534 -0
- package/tui/build/dev/javascript/gleam_stdlib/gleam_stdlib.mjs +1133 -0
- package/tui/build/dev/javascript/gleam_version +1 -0
- package/tui/build/dev/javascript/prelude.mjs +1575 -0
- package/tui/build/dev/javascript/tui/_gleam_artefacts/tui.cache +0 -0
- package/tui/build/dev/javascript/tui/_gleam_artefacts/tui.cache_inline +0 -0
- package/tui/build/dev/javascript/tui/_gleam_artefacts/tui.cache_meta +0 -0
- package/tui/build/dev/javascript/tui/_gleam_artefacts/tui.cache_warnings +0 -0
- package/tui/build/dev/javascript/tui/_gleam_artefacts/tui@prompt.cache +0 -0
- package/tui/build/dev/javascript/tui/_gleam_artefacts/tui@prompt.cache_inline +0 -0
- package/tui/build/dev/javascript/tui/_gleam_artefacts/tui@prompt.cache_meta +0 -0
- package/tui/build/dev/javascript/tui/_gleam_artefacts/tui@prompt.cache_warnings +0 -0
- package/tui/build/dev/javascript/tui/gleam.mjs +1 -0
- package/tui/build/dev/javascript/tui/tui/prompt.mjs +521 -0
- package/tui/build/dev/javascript/tui/tui.mjs +334 -0
- package/tui/build/dev/javascript/tui/tui_ffi.mjs +32 -0
|
@@ -0,0 +1,710 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file uses jsdoc to annotate types.
|
|
3
|
+
* These types can be checked using the typescript compiler with "checkjs" option.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { isEqual, Result$Error, Result$Ok } from "./gleam.mjs";
|
|
7
|
+
|
|
8
|
+
// -- HASH --------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
const referenceMap = /* @__PURE__ */ new WeakMap();
|
|
11
|
+
const tempDataView = /* @__PURE__ */ new DataView(
|
|
12
|
+
/* @__PURE__ */ new ArrayBuffer(8),
|
|
13
|
+
);
|
|
14
|
+
let referenceUID = 0;
|
|
15
|
+
/**
|
|
16
|
+
* hash the object by reference using a weak map and incrementing uid
|
|
17
|
+
* @param {any} o
|
|
18
|
+
* @returns {number}
|
|
19
|
+
*/
|
|
20
|
+
function hashByReference(o) {
|
|
21
|
+
const known = referenceMap.get(o);
|
|
22
|
+
if (known !== undefined) {
|
|
23
|
+
return known;
|
|
24
|
+
}
|
|
25
|
+
const hash = referenceUID++;
|
|
26
|
+
if (referenceUID === 0x7fffffff) {
|
|
27
|
+
referenceUID = 0;
|
|
28
|
+
}
|
|
29
|
+
referenceMap.set(o, hash);
|
|
30
|
+
return hash;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* merge two hashes in an order sensitive way
|
|
35
|
+
* @param {number} a
|
|
36
|
+
* @param {number} b
|
|
37
|
+
* @returns {number}
|
|
38
|
+
*/
|
|
39
|
+
function hashMerge(a, b) {
|
|
40
|
+
return (a ^ (b + 0x9e3779b9 + (a << 6) + (a >> 2))) | 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* standard string hash popularised by Java
|
|
45
|
+
* @param {string} s
|
|
46
|
+
* @returns {number}
|
|
47
|
+
*/
|
|
48
|
+
function hashString(s) {
|
|
49
|
+
let hash = 0;
|
|
50
|
+
const len = s.length;
|
|
51
|
+
for (let i = 0; i < len; i++) {
|
|
52
|
+
hash = (Math.imul(31, hash) + s.charCodeAt(i)) | 0;
|
|
53
|
+
}
|
|
54
|
+
return hash;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* hash a number by converting to two integers and do some jumbling
|
|
59
|
+
* @param {number} n
|
|
60
|
+
* @returns {number}
|
|
61
|
+
*/
|
|
62
|
+
function hashNumber(n) {
|
|
63
|
+
tempDataView.setFloat64(0, n);
|
|
64
|
+
const i = tempDataView.getInt32(0);
|
|
65
|
+
const j = tempDataView.getInt32(4);
|
|
66
|
+
return Math.imul(0x45d9f3b, (i >> 16) ^ i) ^ j;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* hash a BigInt by converting it to a string and hashing that
|
|
71
|
+
* @param {BigInt} n
|
|
72
|
+
* @returns {number}
|
|
73
|
+
*/
|
|
74
|
+
function hashBigInt(n) {
|
|
75
|
+
return hashString(n.toString());
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* hash any js object
|
|
80
|
+
* @param {any} o
|
|
81
|
+
* @returns {number}
|
|
82
|
+
*/
|
|
83
|
+
function hashObject(o) {
|
|
84
|
+
const proto = Object.getPrototypeOf(o);
|
|
85
|
+
if (proto !== null && typeof proto.hashCode === "function") {
|
|
86
|
+
try {
|
|
87
|
+
const code = o.hashCode(o);
|
|
88
|
+
if (typeof code === "number") {
|
|
89
|
+
return code;
|
|
90
|
+
}
|
|
91
|
+
} catch {}
|
|
92
|
+
}
|
|
93
|
+
if (o instanceof Promise || o instanceof WeakSet || o instanceof WeakMap) {
|
|
94
|
+
return hashByReference(o);
|
|
95
|
+
}
|
|
96
|
+
if (o instanceof Date) {
|
|
97
|
+
return hashNumber(o.getTime());
|
|
98
|
+
}
|
|
99
|
+
let h = 0;
|
|
100
|
+
if (o instanceof ArrayBuffer) {
|
|
101
|
+
o = new Uint8Array(o);
|
|
102
|
+
}
|
|
103
|
+
if (Array.isArray(o) || o instanceof Uint8Array) {
|
|
104
|
+
for (let i = 0; i < o.length; i++) {
|
|
105
|
+
h = (Math.imul(31, h) + getHash(o[i])) | 0;
|
|
106
|
+
}
|
|
107
|
+
} else if (o instanceof Set) {
|
|
108
|
+
o.forEach((v) => {
|
|
109
|
+
h = (h + getHash(v)) | 0;
|
|
110
|
+
});
|
|
111
|
+
} else if (o instanceof Map) {
|
|
112
|
+
o.forEach((v, k) => {
|
|
113
|
+
h = (h + hashMerge(getHash(v), getHash(k))) | 0;
|
|
114
|
+
});
|
|
115
|
+
} else {
|
|
116
|
+
const keys = Object.keys(o);
|
|
117
|
+
for (let i = 0; i < keys.length; i++) {
|
|
118
|
+
const k = keys[i];
|
|
119
|
+
const v = o[k];
|
|
120
|
+
h = (h + hashMerge(getHash(v), hashString(k))) | 0;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return h;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* hash any js value
|
|
128
|
+
* @param {any} u
|
|
129
|
+
* @returns {number}
|
|
130
|
+
*/
|
|
131
|
+
export function getHash(u) {
|
|
132
|
+
if (u === null) return 0x42108422;
|
|
133
|
+
if (u === undefined) return 0x42108423;
|
|
134
|
+
if (u === true) return 0x42108421;
|
|
135
|
+
if (u === false) return 0x42108420;
|
|
136
|
+
switch (typeof u) {
|
|
137
|
+
case "number":
|
|
138
|
+
return hashNumber(u);
|
|
139
|
+
case "string":
|
|
140
|
+
return hashString(u);
|
|
141
|
+
case "bigint":
|
|
142
|
+
return hashBigInt(u);
|
|
143
|
+
case "object":
|
|
144
|
+
return hashObject(u);
|
|
145
|
+
case "symbol":
|
|
146
|
+
return hashByReference(u);
|
|
147
|
+
case "function":
|
|
148
|
+
return hashByReference(u);
|
|
149
|
+
default:
|
|
150
|
+
return 0; // should be unreachable
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// -- DICT --------------------------------------------------------------------
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* An implementation of the CHAMP data structure, an optimised HAMT.
|
|
158
|
+
*
|
|
159
|
+
* See: M. J. Steindorfer, J.J. Vinju (2015). Optimizing Hash-Array Mapped Tries for
|
|
160
|
+
Fast and Lean Immutable JVM Collections. Available: https://michael.steindorfer.name/publications/oopsla15.pdf
|
|
161
|
+
*/
|
|
162
|
+
export default class Dict {
|
|
163
|
+
constructor(size, root) {
|
|
164
|
+
this.size = size;
|
|
165
|
+
this.root = root;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/// The power-of-2 branching factor for the dict. For example, a value of `5` indicates a 32-ary tree.
|
|
170
|
+
const bits = 5;
|
|
171
|
+
const mask = (1 << bits) - 1;
|
|
172
|
+
|
|
173
|
+
/// This symbol is used internally to avoid constructing results.
|
|
174
|
+
const noElementMarker = Symbol();
|
|
175
|
+
|
|
176
|
+
/// This symbol is used to store the "generation" on a node.
|
|
177
|
+
/// Using a symbol makes the property not enumerable, which means the generation
|
|
178
|
+
/// will be ignored during equality checks.
|
|
179
|
+
const generationKey = Symbol();
|
|
180
|
+
|
|
181
|
+
// Some commonly used constants throughout the code.
|
|
182
|
+
const emptyNode = /* @__PURE__ */ newNode(0);
|
|
183
|
+
const emptyDict = /* @__PURE__ */ new Dict(0, emptyNode);
|
|
184
|
+
const errorNil = /* @__PURE__ */ Result$Error(undefined);
|
|
185
|
+
|
|
186
|
+
function makeNode(generation, datamap, nodemap, data) {
|
|
187
|
+
// The order of fields is important, as they define the order `isEqual` will
|
|
188
|
+
// compare our fields. Putting the bitmaps first means that equality can
|
|
189
|
+
// early-out if the bitmaps are not equal.
|
|
190
|
+
return {
|
|
191
|
+
// A node is a high-arity (32 in practice) hybrid tree node.
|
|
192
|
+
// Hybrid means that it stores data directly as well as pointers to child nodes.
|
|
193
|
+
//
|
|
194
|
+
// Each node contains 2 bitmaps:
|
|
195
|
+
// - The datamap has a bit set if that slot in the node contains direct data
|
|
196
|
+
// - The nodemap has a bit set if that slot in the node contains another node.
|
|
197
|
+
//
|
|
198
|
+
// Both are exclusive to on another, so datamap & nodemap == 0.
|
|
199
|
+
//
|
|
200
|
+
// Every key/hash value directly correlates to a specific bit by using a trie
|
|
201
|
+
// suffix (least significant bits first) encoding.
|
|
202
|
+
// For example, if the last 5 bits of the hash are 1101, the bit to check for
|
|
203
|
+
// that value is the 13th bit.
|
|
204
|
+
datamap,
|
|
205
|
+
nodemap,
|
|
206
|
+
// The slots itself are stored in a single contiguous array that contains
|
|
207
|
+
// both direct k/v-pairs and child nodes.
|
|
208
|
+
//
|
|
209
|
+
// The direct children come first, followed by the child nodes in _reverse order_:
|
|
210
|
+
//
|
|
211
|
+
// 7654321
|
|
212
|
+
// datamap: 1000100
|
|
213
|
+
// nodemap: 10011
|
|
214
|
+
// data: [key3, value3, key7, value7, child5, child2, child1]
|
|
215
|
+
// -------------------------> <---------------------
|
|
216
|
+
// datamap nodemap
|
|
217
|
+
//
|
|
218
|
+
// Every `1` bit in the datamap corresponds to a pair of [key, value] entries,
|
|
219
|
+
// and every `1` bit in the nodemap corresponds to a child node entry.
|
|
220
|
+
//
|
|
221
|
+
// Children are stored in reverse order to avoid having to store or calculate an
|
|
222
|
+
// "offset" value to skip over the direct children.
|
|
223
|
+
data,
|
|
224
|
+
// The generation is used to track which nodes need to be copied during transient updates.
|
|
225
|
+
// Using a symbol here makes `isEqual` ignore this field.
|
|
226
|
+
[generationKey]: generation,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function newNode(generation) {
|
|
231
|
+
return makeNode(generation, 0, 0, []);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Copies a node and its data array if it's from another generation, making it safe
|
|
236
|
+
* to mutate the node.
|
|
237
|
+
*/
|
|
238
|
+
function copyNode(node, generation) {
|
|
239
|
+
if (node[generationKey] === generation) {
|
|
240
|
+
return node;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const newData = node.data.slice(0);
|
|
244
|
+
return makeNode(generation, node.datamap, node.nodemap, newData);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Copies a node if needed and sets a new value.
|
|
249
|
+
*/
|
|
250
|
+
function copyAndSet(node, generation, idx, val) {
|
|
251
|
+
if (node.data[idx] === val) {
|
|
252
|
+
return node;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Using copyNode is faster than a specialised implementation.
|
|
256
|
+
node = copyNode(node, generation);
|
|
257
|
+
node.data[idx] = val;
|
|
258
|
+
return node;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Copies a node if needed, and then inserts a new key-value pair.
|
|
263
|
+
*/
|
|
264
|
+
function copyAndInsertPair(node, generation, bit, idx, key, val) {
|
|
265
|
+
const data = node.data;
|
|
266
|
+
const length = data.length;
|
|
267
|
+
|
|
268
|
+
// the fastest way to insert a pair is to always copy.
|
|
269
|
+
const newData = new Array(length + 2);
|
|
270
|
+
|
|
271
|
+
let readIndex = 0;
|
|
272
|
+
let writeIndex = 0;
|
|
273
|
+
|
|
274
|
+
while (readIndex < idx) newData[writeIndex++] = data[readIndex++];
|
|
275
|
+
newData[writeIndex++] = key;
|
|
276
|
+
newData[writeIndex++] = val;
|
|
277
|
+
while (readIndex < length) newData[writeIndex++] = data[readIndex++];
|
|
278
|
+
|
|
279
|
+
return makeNode(generation, node.datamap | bit, node.nodemap, newData);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function copyAndRemovePair(node, generation, bit, idx) {
|
|
283
|
+
node = copyNode(node, generation);
|
|
284
|
+
|
|
285
|
+
const data = node.data;
|
|
286
|
+
const length = data.length;
|
|
287
|
+
for (let w = idx, r = idx + 2; r < length; ++r, ++w) {
|
|
288
|
+
data[w] = data[r];
|
|
289
|
+
}
|
|
290
|
+
data.pop();
|
|
291
|
+
data.pop();
|
|
292
|
+
|
|
293
|
+
node.datamap ^= bit;
|
|
294
|
+
return node;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export function make() {
|
|
298
|
+
return emptyDict;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export function from(iterable) {
|
|
302
|
+
let transient = toTransient(emptyDict);
|
|
303
|
+
for (const [key, value] of iterable) {
|
|
304
|
+
transient = destructiveTransientInsert(key, value, transient);
|
|
305
|
+
}
|
|
306
|
+
return fromTransient(transient);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export function size(dict) {
|
|
310
|
+
return dict.size;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export function get(dict, key) {
|
|
314
|
+
const result = lookup(dict.root, key, getHash(key));
|
|
315
|
+
return result !== noElementMarker ? Result$Ok(result) : errorNil;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export function has(dict, key) {
|
|
319
|
+
return lookup(dict.root, key, getHash(key)) !== noElementMarker;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function lookup(node, key, hash) {
|
|
323
|
+
for (let shift = 0; shift < 32; shift += bits) {
|
|
324
|
+
const data = node.data;
|
|
325
|
+
const bit = hashbit(hash, shift);
|
|
326
|
+
|
|
327
|
+
if (node.nodemap & bit) {
|
|
328
|
+
// we found our hash inside the nodemap, so we can continue our search there.
|
|
329
|
+
node = data[data.length - 1 - index(node.nodemap, bit)];
|
|
330
|
+
} else if (node.datamap & bit) {
|
|
331
|
+
// we store this hash directly!
|
|
332
|
+
//
|
|
333
|
+
// this also means that there are no other values with the same
|
|
334
|
+
// hash prefix in this dict.
|
|
335
|
+
//
|
|
336
|
+
// We still need to check if the key matches, but if it does we know for
|
|
337
|
+
// sure that this is the correct value, and if it doesn't that we don't
|
|
338
|
+
// contain the value in question.
|
|
339
|
+
const dataidx = Math.imul(index(node.datamap, bit), 2);
|
|
340
|
+
return isEqual(key, data[dataidx]) ? data[dataidx + 1] : noElementMarker;
|
|
341
|
+
} else {
|
|
342
|
+
// if the hash bit is not set in neither bitmaps, we immediately know that
|
|
343
|
+
// this key cannot be inside this dict.
|
|
344
|
+
return noElementMarker;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// our shift has exceeded 32 bits. Everything that follows is
|
|
349
|
+
// implicitely an overflow node and only contains direct children.
|
|
350
|
+
const overflow = node.data;
|
|
351
|
+
for (let i = 0; i < overflow.length; i += 2) {
|
|
352
|
+
if (isEqual(key, overflow[i])) {
|
|
353
|
+
return overflow[i + 1];
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return noElementMarker;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* We use "transient" values to allow for safer internal mutations of the data
|
|
362
|
+
* structure. This is an optimisation only. No mutable API is exposed to the user.
|
|
363
|
+
*
|
|
364
|
+
* Transients are to be treated as having a linear (single-use, think rust) type.
|
|
365
|
+
* A transient value becomes invalid as soon as it's passed to one of the functions.
|
|
366
|
+
*
|
|
367
|
+
* Internally, we track a "generation" value on each node. If the generation
|
|
368
|
+
* doesn't match the one for the current transient, we have to copy - the node
|
|
369
|
+
* could still be referenced by another dict instance!
|
|
370
|
+
* After that, no other references than the transient one exists, so it's safe
|
|
371
|
+
* to mutate in place.
|
|
372
|
+
*/
|
|
373
|
+
export function toTransient(dict) {
|
|
374
|
+
return {
|
|
375
|
+
generation: nextGeneration(dict),
|
|
376
|
+
root: dict.root,
|
|
377
|
+
size: dict.size,
|
|
378
|
+
dict: dict,
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Consume a transient, producing a normal Dict again.
|
|
384
|
+
*/
|
|
385
|
+
export function fromTransient(transient) {
|
|
386
|
+
if (transient.root === transient.dict.root) {
|
|
387
|
+
return transient.dict;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return new Dict(transient.size, transient.root);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Find and allocate the next generation id.
|
|
395
|
+
*
|
|
396
|
+
* @template K,V
|
|
397
|
+
* @param {Dict<K,V>} dict
|
|
398
|
+
* @returns {number}
|
|
399
|
+
*/
|
|
400
|
+
function nextGeneration(dict) {
|
|
401
|
+
const root = dict.root;
|
|
402
|
+
if (root[generationKey] < Number.MAX_SAFE_INTEGER) {
|
|
403
|
+
return root[generationKey] + 1;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// we have reached MAX_SAFE_INTEGER generations -
|
|
407
|
+
// at this point, we have to walk the dictionary once to reset the counter
|
|
408
|
+
// on every node. This is safe since it's part of the contract for transient
|
|
409
|
+
// that only one of them exists at any given time.
|
|
410
|
+
//
|
|
411
|
+
const queue = [root];
|
|
412
|
+
while (queue.length) {
|
|
413
|
+
// order doesn't matter, so we can use push/pop for faster array usage.
|
|
414
|
+
const node = queue.pop();
|
|
415
|
+
|
|
416
|
+
// reset the generation to 0
|
|
417
|
+
node[generationKey] = 0;
|
|
418
|
+
|
|
419
|
+
// queue all other referenced nodes
|
|
420
|
+
// We need to query the length from the nodemap, as we don't know if this
|
|
421
|
+
// is an overflow node or not! if it is, it will never have datamap set!
|
|
422
|
+
const nodeStart = data.length - popcount(node.nodemap);
|
|
423
|
+
for (let i = nodeStart; i < node.data.length; ++i) {
|
|
424
|
+
queue.push(node.data[i]);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
return 1;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/// Insert is the second-most performance-sensitive operation.
|
|
432
|
+
/// We use a global "transient" value here to avoid doing a memory allocation.
|
|
433
|
+
const globalTransient = /* @__PURE__ */ toTransient(emptyDict);
|
|
434
|
+
|
|
435
|
+
export function insert(dict, key, value) {
|
|
436
|
+
globalTransient.generation = nextGeneration(dict);
|
|
437
|
+
globalTransient.size = dict.size;
|
|
438
|
+
|
|
439
|
+
const hash = getHash(key);
|
|
440
|
+
const root = insertIntoNode(globalTransient, dict.root, key, value, hash, 0);
|
|
441
|
+
if (root === dict.root) {
|
|
442
|
+
return dict;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return new Dict(globalTransient.size, root);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Consume a transient, writing a new key/value pair into the dictionary it
|
|
450
|
+
* represents. If the key already exists, it will be overwritten.
|
|
451
|
+
*
|
|
452
|
+
* Returns a new transient.
|
|
453
|
+
*/
|
|
454
|
+
export function destructiveTransientInsert(key, value, transient) {
|
|
455
|
+
const hash = getHash(key);
|
|
456
|
+
transient.root = insertIntoNode(transient, transient.root, key, value, hash, 0);
|
|
457
|
+
return transient;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Consume a transient, writing a new key/value pair if the key doesn't exist or updating
|
|
462
|
+
* the existing value with a function if it does.
|
|
463
|
+
*
|
|
464
|
+
* Returns a new transient.
|
|
465
|
+
*/
|
|
466
|
+
export function destructiveTransientUpdateWith(key, fun, value, transient) {
|
|
467
|
+
const hash = getHash(key);
|
|
468
|
+
|
|
469
|
+
const existing = lookup(transient.root, key, hash);
|
|
470
|
+
if (existing !== noElementMarker) {
|
|
471
|
+
value = fun(existing);
|
|
472
|
+
}
|
|
473
|
+
transient.root = insertIntoNode(transient, transient.root, key, value, hash, 0);
|
|
474
|
+
return transient;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function insertIntoNode(transient, node, key, value, hash, shift) {
|
|
478
|
+
const data = node.data;
|
|
479
|
+
const generation = transient.generation;
|
|
480
|
+
|
|
481
|
+
// 1. Overflow Node
|
|
482
|
+
// overflow nodes only contain key/value-pairs. we walk the data linearly trying to find a match.
|
|
483
|
+
if (shift > 32) {
|
|
484
|
+
for (let i = 0; i < data.length; i += 2) {
|
|
485
|
+
if (isEqual(key, data[i])) {
|
|
486
|
+
return copyAndSet(node, generation, i + 1, value);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
transient.size += 1;
|
|
491
|
+
return copyAndInsertPair(node, generation, 0, data.length, key, value);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const bit = hashbit(hash, shift);
|
|
495
|
+
|
|
496
|
+
// 2. Child Node
|
|
497
|
+
// We have to check first if there is already a child node we have to traverse to.
|
|
498
|
+
if (node.nodemap & bit) {
|
|
499
|
+
const nodeidx = data.length - 1 - index(node.nodemap, bit);
|
|
500
|
+
|
|
501
|
+
let child = data[nodeidx];
|
|
502
|
+
child = insertIntoNode(transient, child, key, value, hash, shift + bits);
|
|
503
|
+
return copyAndSet(node, generation, nodeidx, child);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// 3. New Data Node
|
|
507
|
+
// No child node and no data node exists yet, so we can potentially just insert a new value.
|
|
508
|
+
const dataidx = Math.imul(index(node.datamap, bit), 2);
|
|
509
|
+
if ((node.datamap & bit) === 0) {
|
|
510
|
+
transient.size += 1;
|
|
511
|
+
return copyAndInsertPair(node, generation, bit, dataidx, key, value);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// 4. Existing Data Node
|
|
515
|
+
// We have a match that we can update, or remove.
|
|
516
|
+
if (isEqual(key, data[dataidx])) {
|
|
517
|
+
return copyAndSet(node, generation, dataidx + 1, value);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// 5. Collision
|
|
521
|
+
// There is no child node, but a data node with the same hash, but with a different key.
|
|
522
|
+
// To resolve this, we push both nodes down one level.
|
|
523
|
+
const childShift = shift + bits;
|
|
524
|
+
|
|
525
|
+
let child = emptyNode;
|
|
526
|
+
child = insertIntoNode(transient, child, key, value, hash, childShift);
|
|
527
|
+
|
|
528
|
+
const key2 = data[dataidx];
|
|
529
|
+
const value2 = data[dataidx + 1];
|
|
530
|
+
const hash2 = getHash(key2);
|
|
531
|
+
child = insertIntoNode(transient, child, key2, value2, hash2, childShift);
|
|
532
|
+
|
|
533
|
+
// we inserted 2 elements, but implicitely deleted the one we pushed down from the datamap.
|
|
534
|
+
transient.size -= 1;
|
|
535
|
+
|
|
536
|
+
// remove the old data pair, and insert the new child node.
|
|
537
|
+
const length = data.length;
|
|
538
|
+
const nodeidx = length - 1 - index(node.nodemap, bit);
|
|
539
|
+
|
|
540
|
+
// writing these loops in javascript instead of a combination of splices
|
|
541
|
+
// turns out to be faster. Copying always turned out to be faster.
|
|
542
|
+
const newData = new Array(length - 1);
|
|
543
|
+
|
|
544
|
+
let readIndex = 0;
|
|
545
|
+
let writeIndex = 0;
|
|
546
|
+
|
|
547
|
+
// [0..dataidx, skip 2 elements, ..nodeidx, newChild, ..rest]
|
|
548
|
+
while (readIndex < dataidx) newData[writeIndex++] = data[readIndex++];
|
|
549
|
+
readIndex += 2;
|
|
550
|
+
while (readIndex <= nodeidx) newData[writeIndex++] = data[readIndex++];
|
|
551
|
+
newData[writeIndex++] = child;
|
|
552
|
+
while (readIndex < length) newData[writeIndex++] = data[readIndex++];
|
|
553
|
+
|
|
554
|
+
return makeNode(generation, node.datamap ^ bit, node.nodemap | bit, newData);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Consume a transient, removing a key if it exists.
|
|
559
|
+
* Returns a new transient.
|
|
560
|
+
*/
|
|
561
|
+
export function destructiveTransientDelete(key, transient) {
|
|
562
|
+
const hash = getHash(key);
|
|
563
|
+
transient.root = deleteFromNode(transient, transient.root, key, hash, 0);
|
|
564
|
+
return transient;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
function deleteFromNode(transient, node, key, hash, shift) {
|
|
568
|
+
const data = node.data;
|
|
569
|
+
const generation = transient.generation;
|
|
570
|
+
|
|
571
|
+
// 1. Overflow Node
|
|
572
|
+
// overflow nodes only contain key/value-pairs. we walk the data linearly trying to find a match.
|
|
573
|
+
if (shift > 32) {
|
|
574
|
+
for (let i = 0; i < data.length; i += 2) {
|
|
575
|
+
if (isEqual(key, data[i])) {
|
|
576
|
+
transient.size -= 1;
|
|
577
|
+
return copyAndRemovePair(node, generation, 0, i);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
return node;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
const bit = hashbit(hash, shift);
|
|
585
|
+
const dataidx = Math.imul(index(node.datamap, bit), 2);
|
|
586
|
+
|
|
587
|
+
// 2. Child Node
|
|
588
|
+
// We have to check first if there is already a child node we have to traverse to.
|
|
589
|
+
if ((node.nodemap & bit) !== 0) {
|
|
590
|
+
const nodeidx = data.length - 1 - index(node.nodemap, bit);
|
|
591
|
+
|
|
592
|
+
let child = data[nodeidx];
|
|
593
|
+
child = deleteFromNode(transient, child, key, hash, shift + bits);
|
|
594
|
+
|
|
595
|
+
// the node did change, so let's copy to incorporate that change.
|
|
596
|
+
if (child.nodemap !== 0 || child.data.length > 2) {
|
|
597
|
+
return copyAndSet(node, generation, nodeidx, child);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// this node only has a single data (k/v-pair) child.
|
|
601
|
+
// to restore the CHAMP invariant, we "pull" that pair up into ourselves.
|
|
602
|
+
// this ensures that every tree stays in its single optimal representation,
|
|
603
|
+
// and allows dicts to be structurally compared.
|
|
604
|
+
const length = data.length;
|
|
605
|
+
const newData = new Array(length + 1);
|
|
606
|
+
|
|
607
|
+
let readIndex = 0;
|
|
608
|
+
let writeIndex = 0;
|
|
609
|
+
|
|
610
|
+
while (readIndex < dataidx) newData[writeIndex++] = data[readIndex++];
|
|
611
|
+
newData[writeIndex++] = child.data[0];
|
|
612
|
+
newData[writeIndex++] = child.data[1];
|
|
613
|
+
while (readIndex < nodeidx) newData[writeIndex++] = data[readIndex++];
|
|
614
|
+
readIndex++;
|
|
615
|
+
while (readIndex < length) newData[writeIndex++] = data[readIndex++];
|
|
616
|
+
|
|
617
|
+
return makeNode(generation, node.datamap | bit, node.nodemap ^ bit, newData);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// 3. Data Node
|
|
621
|
+
// There is no data entry here, or it is a prefix for a different key
|
|
622
|
+
if ((node.datamap & bit) === 0 || !isEqual(key, data[dataidx])) {
|
|
623
|
+
return node;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// we found a data entry that we can delete.
|
|
627
|
+
transient.size -= 1;
|
|
628
|
+
return copyAndRemovePair(node, generation, bit, dataidx);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
export function map(dict, fun) {
|
|
632
|
+
// map can never modify the structure, so we can walk the dictionary directly,
|
|
633
|
+
// but still move to a new generation to make sure we get a new copy of every node.
|
|
634
|
+
const generation = nextGeneration(dict);
|
|
635
|
+
const root = copyNode(dict.root, generation);
|
|
636
|
+
const queue = [root];
|
|
637
|
+
|
|
638
|
+
while (queue.length) {
|
|
639
|
+
// order doesn't matter, so we can use push/pop for faster array usage.
|
|
640
|
+
const node = queue.pop();
|
|
641
|
+
const data = node.data;
|
|
642
|
+
// every node contains popcount(datamap) direct entries
|
|
643
|
+
// We need to query the length from the nodemap, as we don't know if this
|
|
644
|
+
// is an overflow node or not! if it is, it will never have datamap set!
|
|
645
|
+
const edgesStart = data.length - popcount(node.nodemap);
|
|
646
|
+
for (let i = 0; i < edgesStart; i += 2) {
|
|
647
|
+
// we copied the node while queueing it, so direct mutation here is safe.
|
|
648
|
+
data[i + 1] = fun(data[i], data[i + 1]);
|
|
649
|
+
}
|
|
650
|
+
// the remaining entries are other nodes we can queue
|
|
651
|
+
for (let i = edgesStart; i < data.length; ++i) {
|
|
652
|
+
// copy the node first to make it safe to mutate
|
|
653
|
+
data[i] = copyNode(data[i], generation);
|
|
654
|
+
queue.push(data[i]);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
return new Dict(dict.size, root);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
export function fold(dict, state, fun) {
|
|
662
|
+
const queue = [dict.root];
|
|
663
|
+
|
|
664
|
+
while (queue.length) {
|
|
665
|
+
// order doesn't matter, so we can use push/pop for faster array usage.
|
|
666
|
+
const node = queue.pop();
|
|
667
|
+
const data = node.data;
|
|
668
|
+
// every node contains popcount(datamap) direct entries
|
|
669
|
+
// We need to query the length from the nodemap, as we don't know if this
|
|
670
|
+
// is an overflow node or not! if it is, it will never have datamap set!
|
|
671
|
+
const edgesStart = data.length - popcount(node.nodemap);
|
|
672
|
+
for (let i = 0; i < edgesStart; i += 2) {
|
|
673
|
+
state = fun(state, data[i], data[i + 1]);
|
|
674
|
+
}
|
|
675
|
+
// the remaining entries are child nodes we can queue.
|
|
676
|
+
for (let i = edgesStart; i < data.length; ++i) {
|
|
677
|
+
queue.push(data[i]);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
return state;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* How many `1` bits are set in a 32-bit integer.
|
|
686
|
+
*/
|
|
687
|
+
function popcount(n) {
|
|
688
|
+
n -= (n >>> 1) & 0x55555555;
|
|
689
|
+
n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);
|
|
690
|
+
return Math.imul((n + (n >>> 4)) & 0x0f0f0f0f, 0x01010101) >>> 24;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* Given a population bitmap and a bit selected from that map, returns
|
|
695
|
+
* how many less significant 1 bits there are.
|
|
696
|
+
*
|
|
697
|
+
* For example, index(10101, 100) returns 1, since there is a single less
|
|
698
|
+
* significant `1` bit. This translates to the 0-based "index" of that bit.
|
|
699
|
+
*/
|
|
700
|
+
function index(bitmap, bit) {
|
|
701
|
+
return popcount(bitmap & (bit - 1));
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/**
|
|
705
|
+
* Extracts a single slice of the hash, and returns a bitmask for the resulting value.
|
|
706
|
+
* For example, if the slice returns 5, this function returns 10000 = 1 << 5.
|
|
707
|
+
*/
|
|
708
|
+
function hashbit(hash, shift) {
|
|
709
|
+
return 1 << ((hash >>> shift) & mask);
|
|
710
|
+
}
|