UNPKG

3.44 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const isWeakKey = thing => typeof thing === "object" && thing !== null;
9
10/**
11 * @template {any[]} T
12 * @template V
13 */
14class WeakTupleMap {
15 constructor() {
16 /** @private */
17 this.f = 0;
18 /** @private @type {any} */
19 this.v = undefined;
20 /** @private @type {Map<object, WeakTupleMap<T, V>> | undefined} */
21 this.m = undefined;
22 /** @private @type {WeakMap<object, WeakTupleMap<T, V>> | undefined} */
23 this.w = undefined;
24 }
25
26 /**
27 * @param {[...T, V]} args tuple
28 * @returns {void}
29 */
30 set(...args) {
31 /** @type {WeakTupleMap<T, V>} */
32 let node = this;
33 for (let i = 0; i < args.length - 1; i++) {
34 node = node._get(args[i]);
35 }
36 node._setValue(args[args.length - 1]);
37 }
38
39 /**
40 * @param {T} args tuple
41 * @returns {boolean} true, if the tuple is in the Set
42 */
43 has(...args) {
44 /** @type {WeakTupleMap<T, V>} */
45 let node = this;
46 for (let i = 0; i < args.length; i++) {
47 node = node._peek(args[i]);
48 if (node === undefined) return false;
49 }
50 return node._hasValue();
51 }
52
53 /**
54 * @param {T} args tuple
55 * @returns {V} the value
56 */
57 get(...args) {
58 /** @type {WeakTupleMap<T, V>} */
59 let node = this;
60 for (let i = 0; i < args.length; i++) {
61 node = node._peek(args[i]);
62 if (node === undefined) return undefined;
63 }
64 return node._getValue();
65 }
66
67 /**
68 * @param {[...T, function(): V]} args tuple
69 * @returns {V} the value
70 */
71 provide(...args) {
72 /** @type {WeakTupleMap<T, V>} */
73 let node = this;
74 for (let i = 0; i < args.length - 1; i++) {
75 node = node._get(args[i]);
76 }
77 if (node._hasValue()) return node._getValue();
78 const fn = args[args.length - 1];
79 const newValue = fn(...args.slice(0, -1));
80 node._setValue(newValue);
81 return newValue;
82 }
83
84 /**
85 * @param {T} args tuple
86 * @returns {void}
87 */
88 delete(...args) {
89 /** @type {WeakTupleMap<T, V>} */
90 let node = this;
91 for (let i = 0; i < args.length; i++) {
92 node = node._peek(args[i]);
93 if (node === undefined) return;
94 }
95 node._deleteValue();
96 }
97
98 /**
99 * @returns {void}
100 */
101 clear() {
102 this.f = 0;
103 this.v = undefined;
104 this.w = undefined;
105 this.m = undefined;
106 }
107
108 _getValue() {
109 return this.v;
110 }
111
112 _hasValue() {
113 return (this.f & 1) === 1;
114 }
115
116 _setValue(v) {
117 this.f |= 1;
118 this.v = v;
119 }
120
121 _deleteValue() {
122 this.f &= 6;
123 this.v = undefined;
124 }
125
126 _peek(thing) {
127 if (isWeakKey(thing)) {
128 if ((this.f & 4) !== 4) return undefined;
129 return this.w.get(thing);
130 } else {
131 if ((this.f & 2) !== 2) return undefined;
132 return this.m.get(thing);
133 }
134 }
135
136 _get(thing) {
137 if (isWeakKey(thing)) {
138 if ((this.f & 4) !== 4) {
139 const newMap = new WeakMap();
140 this.f |= 4;
141 const newNode = new WeakTupleMap();
142 (this.w = newMap).set(thing, newNode);
143 return newNode;
144 }
145 const entry = this.w.get(thing);
146 if (entry !== undefined) {
147 return entry;
148 }
149 const newNode = new WeakTupleMap();
150 this.w.set(thing, newNode);
151 return newNode;
152 } else {
153 if ((this.f & 2) !== 2) {
154 const newMap = new Map();
155 this.f |= 2;
156 const newNode = new WeakTupleMap();
157 (this.m = newMap).set(thing, newNode);
158 return newNode;
159 }
160 const entry = this.m.get(thing);
161 if (entry !== undefined) {
162 return entry;
163 }
164 const newNode = new WeakTupleMap();
165 this.m.set(thing, newNode);
166 return newNode;
167 }
168 }
169}
170
171module.exports = WeakTupleMap;