solarite 0.2.2 → 0.2.3
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/package.json +3 -2
- package/src/unused/FastLookupArray.js +0 -54
- package/src/unused/Hashes.js +0 -339
- package/src/unused/InUse.test.js +0 -92
- package/src/unused/InUseMap.js +0 -98
- package/src/unused/LinkedList.js +0 -117
- package/src/unused/LinkedList.test.js +0 -115
- package/src/unused/NodeGroupManager.js +0 -444
- package/src/unused/Perf.js +0 -47
- package/src/unused/Template.js +0 -108
- package/src/unused/onConnect.js +0 -79
- package/src/unused/watch.js +0 -302
- package/src/unused/watch2.js +0 -439
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "solarite",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Solarite is a small, fast, compilation-free JavaScript web component library that closely follows modern web standards.",
|
|
5
5
|
"main": "dist/Solarite.js",
|
|
6
6
|
"files": [
|
|
7
7
|
"readme.md",
|
|
8
8
|
"dist",
|
|
9
|
-
"src"
|
|
9
|
+
"src/solarite",
|
|
10
|
+
"src/util"
|
|
10
11
|
],
|
|
11
12
|
"scripts": {
|
|
12
13
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* partialUpdate test is 20% slower when using this, even before I override any methods!*/
|
|
3
|
-
export default class FastLookupArray extends Array {
|
|
4
|
-
constructor() {
|
|
5
|
-
super(...arguments)
|
|
6
|
-
this.indexMap = new Map(); // You can replace with WeakMap() but ensure only objects are added.
|
|
7
|
-
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
push(item) { // doesn't support pushing multiple
|
|
11
|
-
if (!this.indexMap.has(item)) {
|
|
12
|
-
this[this.length] = item;
|
|
13
|
-
this.indexMap.set(item, this.length - 1);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
indexOf(item) {
|
|
18
|
-
return this.indexMap.get(item) || -1;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
remove(item) {
|
|
22
|
-
const index = this.indexOf(item);
|
|
23
|
-
if (index !== -1) {
|
|
24
|
-
this.splice(index, 1);
|
|
25
|
-
this.indexMap.delete(item);
|
|
26
|
-
|
|
27
|
-
// Adjust the map indices for the items after the removed one
|
|
28
|
-
for (let i = index; i < this.length; i++)
|
|
29
|
-
this.indexMap.set(this[i], i);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
insertBefore(referenceItem, newItem) {
|
|
34
|
-
const refIndex = this.indexOf(referenceItem);
|
|
35
|
-
if (refIndex !== -1) {
|
|
36
|
-
this.splice(refIndex, 0, newItem);
|
|
37
|
-
this.indexMap.set(newItem, refIndex);
|
|
38
|
-
|
|
39
|
-
// Adjust the map indices for the items after the inserted one
|
|
40
|
-
for (let i = refIndex + 1; i < this.length; i++)
|
|
41
|
-
this.indexMap.set(this[i], i);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
set(index, item) {
|
|
46
|
-
const currentItem = this[index];
|
|
47
|
-
if (currentItem !== undefined)
|
|
48
|
-
this.indexMap.delete(currentItem);
|
|
49
|
-
this[index] = item;
|
|
50
|
-
this.indexMap.set(item, index);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// TODO: Override splice?
|
|
54
|
-
}
|
package/src/unused/Hashes.js
DELETED
|
@@ -1,339 +0,0 @@
|
|
|
1
|
-
import {getObjectId} from "../solarite/hash.js";
|
|
2
|
-
import Template from "../solarite/Template.js";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Convert obj to a non-cyclic form that can be given to JSON.serialize.
|
|
6
|
-
* This is useful for creating a string that represents a hash of all the deep values of an object.
|
|
7
|
-
*
|
|
8
|
-
* One instance of a node always has the same hash, even if its attributes/children have changed.
|
|
9
|
-
*
|
|
10
|
-
* TODO: Could memoize be faster if given an old version of the same object, and it only finds the differences?
|
|
11
|
-
* Or if we inline getObjectId()
|
|
12
|
-
*
|
|
13
|
-
* @param obj {Object|Array|function|Node}
|
|
14
|
-
* @param seenMap Used internally.
|
|
15
|
-
* @param idCounter Used internally.
|
|
16
|
-
* @returns {{}|*|string} */
|
|
17
|
-
export function memoize(obj, seenMap = new Map(), idCounter = {value: 0}) {
|
|
18
|
-
|
|
19
|
-
// If obj is a Node, Element, or Window, we treat it specially
|
|
20
|
-
if (obj?.nodeType) { // Benchmarking shows this is faster than "instanceof Node"
|
|
21
|
-
let nodeId = getObjectId(obj);
|
|
22
|
-
return `&N:${nodeId}`;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// TODO: Should I instead use id's to keep track of functions?
|
|
26
|
-
let type = typeof obj
|
|
27
|
-
|
|
28
|
-
if (obj && type === 'object') {
|
|
29
|
-
if (seenMap.has(obj))
|
|
30
|
-
return `&O:${seenMap.get(obj)}`;
|
|
31
|
-
|
|
32
|
-
else {
|
|
33
|
-
seenMap.set(obj, idCounter.value++);
|
|
34
|
-
if (typeof obj.memoize === 'function')
|
|
35
|
-
return obj.memoize();
|
|
36
|
-
|
|
37
|
-
// We iterate through the keys of an object because an object may have changed since the last time we saw it.
|
|
38
|
-
const serializedObj = {};
|
|
39
|
-
for (let key in obj)
|
|
40
|
-
serializedObj[key] = memoize(obj[key], seenMap, idCounter);
|
|
41
|
-
return serializedObj;
|
|
42
|
-
}
|
|
43
|
-
} else if (type === 'function')
|
|
44
|
-
//return `&F:${obj}` // String version of function. This version fails the loop.eventBindings test.
|
|
45
|
-
return `&F:${getObjectId(obj)}`; // Memory reference to function. this makes it twice as slow!
|
|
46
|
-
|
|
47
|
-
else
|
|
48
|
-
return obj;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
function fnv1a(...data) {
|
|
53
|
-
let hash = 0x811c9dc5; // FNV offset basis
|
|
54
|
-
for (let i = 0; i < data.length; i++) {
|
|
55
|
-
hash ^= data[i]; // XOR
|
|
56
|
-
hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24); // FNV prime multiplication
|
|
57
|
-
}
|
|
58
|
-
return hash >>> 0; // Convert to 32-bit unsigned integer
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const FNV_PRIME_64 = BigInt("1099511628211");
|
|
62
|
-
const OFFSET_BASIS_64 = BigInt("14695981039346656037");
|
|
63
|
-
function fnv1a_64bit(...data) {
|
|
64
|
-
let hash = OFFSET_BASIS_64;
|
|
65
|
-
for (let i = 0; i < data.length; i++) {
|
|
66
|
-
hash ^= data[i]; // XOR
|
|
67
|
-
// hash = BigInt.asUintN(64, hash*FNV_PRIME_64);
|
|
68
|
-
}
|
|
69
|
-
return hash;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
// Faster than memoize
|
|
74
|
-
export function hashObject(obj) {
|
|
75
|
-
let combinedData = (0);
|
|
76
|
-
if (obj === null)
|
|
77
|
-
return combinedData;
|
|
78
|
-
|
|
79
|
-
if (Array.isArray(obj)) {
|
|
80
|
-
for (let i=(0); i<obj.length; i++) {
|
|
81
|
-
let v = hashObject(obj[i])
|
|
82
|
-
combinedData = fnv1a(combinedData, i, v);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
else if (typeof obj === 'object') {
|
|
87
|
-
let i = (0);
|
|
88
|
-
for (const key in obj) {
|
|
89
|
-
let k = getObjectId3(key)
|
|
90
|
-
let v = hashObject(obj[key])
|
|
91
|
-
combinedData = fnv1a(combinedData, i, k, v);
|
|
92
|
-
i++
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
else if (typeof obj === 'number')
|
|
96
|
-
return (obj)
|
|
97
|
-
else if (typeof obj === 'string')
|
|
98
|
-
return getObjectId3(obj)
|
|
99
|
-
else
|
|
100
|
-
return getObjectId2(obj);
|
|
101
|
-
|
|
102
|
-
return combinedData;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// TODO: Use this number instead as the high bits for getObjectId2?
|
|
106
|
-
let lastObjectId2 = (1_010_101_101); // Big enough to not easily not collide with the space of unhashed numbers.
|
|
107
|
-
let objectIds2 = new Map();
|
|
108
|
-
|
|
109
|
-
export function getObjectId2(obj) {
|
|
110
|
-
let result = objectIds2.get(obj);
|
|
111
|
-
if (!result) {
|
|
112
|
-
result = (lastObjectId2++); // convert to string, store in result, then add 1 to lastObjectId.
|
|
113
|
-
objectIds2.set(obj, result)
|
|
114
|
-
}
|
|
115
|
-
return result;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
let objectIds3 = {};
|
|
120
|
-
|
|
121
|
-
export function getObjectId3(obj) {
|
|
122
|
-
// if (typeof obj === 'string') {
|
|
123
|
-
// let l = obj.length;
|
|
124
|
-
// if (l < 512) { // Faster than getObjectId3. 512 was picked randomly and has not been benchmarked.
|
|
125
|
-
// let result = 1;
|
|
126
|
-
// for (let i=0; i < l; i++)
|
|
127
|
-
// result ^= obj.charCodeAt(i) << (i%16);
|
|
128
|
-
// return result
|
|
129
|
-
// }
|
|
130
|
-
// }
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
let result = objectIds3[obj];
|
|
134
|
-
if (!result) {
|
|
135
|
-
result = (lastObjectId2++); // convert to string, store in result, then add 1 to lastObjectId.
|
|
136
|
-
objectIds3[obj] = result
|
|
137
|
-
}
|
|
138
|
-
return result;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
let hash64Cache = new WeakMap();
|
|
143
|
-
export {hash64Cache};
|
|
144
|
-
|
|
145
|
-
// 25% slower than 32-bit version but still faster than memoize+JSON.stringify()
|
|
146
|
-
// TODO: hardcoded hash values for true and false?
|
|
147
|
-
export function hashObject64(obj) {
|
|
148
|
-
let resultLow = 0;
|
|
149
|
-
let resultHigh = 0;
|
|
150
|
-
|
|
151
|
-
if (obj instanceof Template)
|
|
152
|
-
obj = [obj.htmlId, ...obj.exprs]
|
|
153
|
-
|
|
154
|
-
if (obj === null)
|
|
155
|
-
return [0, 0];
|
|
156
|
-
|
|
157
|
-
else if (Array.isArray(obj)) {
|
|
158
|
-
let result
|
|
159
|
-
// = hash64Cache.get(obj);
|
|
160
|
-
// if (!result) {
|
|
161
|
-
for (let i = (0); i < obj.length; i++) {
|
|
162
|
-
let [vLow, vHigh] = hashObject64(obj[i])
|
|
163
|
-
resultLow = fnv1a(resultLow, i, vLow)
|
|
164
|
-
resultHigh = fnv1a(resultHigh, vHigh)
|
|
165
|
-
}
|
|
166
|
-
result = [resultLow, resultHigh]
|
|
167
|
-
// hash64Cache.set(obj, result)
|
|
168
|
-
//}
|
|
169
|
-
return result;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
else if (typeof obj === 'object') {
|
|
173
|
-
let result
|
|
174
|
-
// = hash64Cache.get(obj);
|
|
175
|
-
// if (!result) {
|
|
176
|
-
let i = (0);
|
|
177
|
-
for (const key in obj) {
|
|
178
|
-
let k = getObjectId3(key)
|
|
179
|
-
let [vLow, vHigh] = hashObject64(obj[key])
|
|
180
|
-
resultLow = fnv1a(resultLow, i, vLow)
|
|
181
|
-
resultHigh = fnv1a(resultHigh, k, vHigh)
|
|
182
|
-
i++
|
|
183
|
-
}
|
|
184
|
-
result = [resultLow, resultHigh]
|
|
185
|
-
// hash64Cache.set(obj, result)
|
|
186
|
-
//}
|
|
187
|
-
|
|
188
|
-
return result;
|
|
189
|
-
}
|
|
190
|
-
else if (typeof obj === 'number')
|
|
191
|
-
return [0, obj]
|
|
192
|
-
else if (typeof obj === 'string')
|
|
193
|
-
return [0, getObjectId3(obj)]
|
|
194
|
-
else
|
|
195
|
-
return [0, getObjectId2(obj)];
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
// Perforamnce varies greatly.
|
|
202
|
-
export function memoize2(obj) {
|
|
203
|
-
|
|
204
|
-
if (obj === null)
|
|
205
|
-
return 'null';
|
|
206
|
-
|
|
207
|
-
switch (obj?.constructor) {
|
|
208
|
-
case Function:
|
|
209
|
-
case Node:
|
|
210
|
-
return getObjectId(obj);
|
|
211
|
-
case Array:
|
|
212
|
-
return '[' + obj.map(item => memoize2(item)) + ']'
|
|
213
|
-
case Object:
|
|
214
|
-
let result = '{';
|
|
215
|
-
for (let key in obj) {
|
|
216
|
-
//key = key.match(/[^A-Z0-9_]/i) ? JSON.stringify(key) : key;
|
|
217
|
-
key = (key.includes(':')) ? JSON.stringify(key) : key;
|
|
218
|
-
result += `${key}:${memoize2(obj[key])}`
|
|
219
|
-
}
|
|
220
|
-
return result +'}';
|
|
221
|
-
default:
|
|
222
|
-
return JSON.stringify(obj);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// as fast as memoize2, or faster.
|
|
227
|
-
export function memoize3(obj) {
|
|
228
|
-
|
|
229
|
-
if (obj === null)
|
|
230
|
-
return 'null';
|
|
231
|
-
|
|
232
|
-
let type = typeof obj;
|
|
233
|
-
if (Array.isArray(obj))
|
|
234
|
-
return '[' + obj.map(item => memoize2(item)) + ']'
|
|
235
|
-
if (obj instanceof Node || type === 'function')
|
|
236
|
-
return getObjectId(obj);
|
|
237
|
-
if (type=== 'object') {
|
|
238
|
-
let result = '{';
|
|
239
|
-
for (let key in obj) {
|
|
240
|
-
//key = key.match(/[^A-Z0-9_]/i) ? JSON.stringify(key) : key;
|
|
241
|
-
key = (key.includes(':')) ? JSON.stringify(key) : key;
|
|
242
|
-
result += `${key}:${memoize2(obj[key])}`
|
|
243
|
-
}
|
|
244
|
-
return result +'}';
|
|
245
|
-
}
|
|
246
|
-
return JSON.stringify(obj);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
// as fast as memoize2, or faster.
|
|
253
|
-
export function memoize4(obj, seenMap=new Map(), idCounter={value: 0}) {
|
|
254
|
-
let result;
|
|
255
|
-
if (obj instanceof Template) {
|
|
256
|
-
if (obj.exprs.length === 1)
|
|
257
|
-
return obj.htmlId + '\f' + memoize4(obj.exprs[0]) // \f is the "Form feed" control character, unlikely to be usedin regular text.
|
|
258
|
-
|
|
259
|
-
let exprLength = obj.exprs.length;
|
|
260
|
-
result = new Array(exprLength + 1);
|
|
261
|
-
result[0] = obj.htmlId;
|
|
262
|
-
let exprs = obj.exprs;
|
|
263
|
-
for (let i = 0; i < exprLength; i++)
|
|
264
|
-
result[i + 1] = memoize4(exprs[i])
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
else if (obj === null)
|
|
268
|
-
result = 'null';
|
|
269
|
-
|
|
270
|
-
else if (Array.isArray(obj)) {
|
|
271
|
-
if (seenMap.has(obj))
|
|
272
|
-
result = `&A${seenMap.get(obj)}`;
|
|
273
|
-
else {
|
|
274
|
-
seenMap.set(obj, idCounter.value++);
|
|
275
|
-
result = '[' + obj.map(item => memoize2(item)) + ']'
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
else if (obj instanceof Node || typeof obj === 'function')
|
|
279
|
-
result = getObjectId(obj);
|
|
280
|
-
|
|
281
|
-
else if (typeof obj=== 'object') {
|
|
282
|
-
if (seenMap.has(obj))
|
|
283
|
-
result = `&O${seenMap.get(obj)}`;
|
|
284
|
-
|
|
285
|
-
else {
|
|
286
|
-
seenMap.set(obj, idCounter.value++);
|
|
287
|
-
|
|
288
|
-
// // String contact approach.
|
|
289
|
-
// result = '{';
|
|
290
|
-
// for (let key in obj) {
|
|
291
|
-
// //key = key.match(/[^A-Z0-9_]/i) ? JSON.stringify(key) : key;
|
|
292
|
-
// key = (key.includes(':')) ? JSON.stringify(key) : key;
|
|
293
|
-
// result += `${key}:${memoize2(obj[key])}`
|
|
294
|
-
// }
|
|
295
|
-
// result = result + '}';
|
|
296
|
-
|
|
297
|
-
// Buffer approach. Seems slightly faster.
|
|
298
|
-
let keys = Object.keys(obj);
|
|
299
|
-
let buffer = new Array(keys.length) + 2;
|
|
300
|
-
buffer[0] = '{'
|
|
301
|
-
buffer[buffer.length-1] = '}'
|
|
302
|
-
let idx = 1;
|
|
303
|
-
for (let key of keys) {
|
|
304
|
-
//key = key.match(/[^A-Z0-9_]/i) ? JSON.stringify(key) : key;
|
|
305
|
-
key = (key.includes(':')) ? JSON.stringify(key) : key;
|
|
306
|
-
buffer[idx] = `${key}:${memoize2(obj[key])},`
|
|
307
|
-
idx++;
|
|
308
|
-
}
|
|
309
|
-
result = buffer.join('');
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
else
|
|
313
|
-
result = JSON.stringify(obj);
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
return result;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
// Takes half as long as just calling JSON.serialize!
|
|
324
|
-
function containsFunction(obj) {
|
|
325
|
-
// Base case: if the object itself is a function
|
|
326
|
-
let type = typeof obj;
|
|
327
|
-
if (type === 'function') {
|
|
328
|
-
return true;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// If the object is an object or array, recursively search its properties
|
|
332
|
-
if (type === 'object' && obj !== null)
|
|
333
|
-
for (let key in obj)
|
|
334
|
-
if (containsFunction(obj[key]))
|
|
335
|
-
return true;
|
|
336
|
-
|
|
337
|
-
// If none of the properties are functions, return false
|
|
338
|
-
return false;
|
|
339
|
-
}
|
package/src/unused/InUse.test.js
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import {InUseArray, InUseMap} from "./InUseMap.js";
|
|
2
|
-
import Testimony, {assert} from "../../tests/Testimony.js";
|
|
3
|
-
|
|
4
|
-
Testimony.test('InUseArray.constructor', () => {
|
|
5
|
-
const arr = new InUseArray();
|
|
6
|
-
assert.eq(arr.count, 0, 'Initial count should be 0');
|
|
7
|
-
assert.eq(arr.length, 0, 'Initial length should be 0');
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
Testimony.test('InUseArray.use - with no value', () => {
|
|
11
|
-
const arr = new InUseArray(1, 2, 3);
|
|
12
|
-
const val = arr.use();
|
|
13
|
-
assert.eq(val, 1, 'Should return the first value');
|
|
14
|
-
assert.eq(arr.count, 1, 'Count should be incremented');
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
Testimony.test('InUseArray.use - with specific value', () => {
|
|
18
|
-
const arr = new InUseArray(1, 2, 3);
|
|
19
|
-
arr.count = 1; // Mark the first element as in-use
|
|
20
|
-
const val = arr.use(3);
|
|
21
|
-
assert.eq(val, 3, 'Should return the specified value');
|
|
22
|
-
assert.eq(arr.count, 2, 'Count should be incremented');
|
|
23
|
-
assert.eq(arr[1], 3, 'Value should be moved to the in-use section');
|
|
24
|
-
assert.eq(arr[2], 2, 'Remaining value should be in the available section');
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
Testimony.test('InUseArray.use', () => {
|
|
28
|
-
const arr = new InUseArray(1, 2, 3);
|
|
29
|
-
let val = arr.use(4);
|
|
30
|
-
assert.eq(val, undefined,'Should return undefined');
|
|
31
|
-
assert.eq(arr.use(), 1);
|
|
32
|
-
assert.eq(arr.use(3), 3);
|
|
33
|
-
assert.eq(arr.use(), 2);
|
|
34
|
-
assert.eq(arr.use(), undefined);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
Testimony.test('InUseArray.use - all in use', () => {
|
|
38
|
-
const arr = new InUseArray(1, 2, 3);
|
|
39
|
-
arr.count = 3;
|
|
40
|
-
const val = arr.use();
|
|
41
|
-
assert.eq(val, undefined, 'Should return undefined');
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
Testimony.test('InUseArray.free', () => {
|
|
45
|
-
const arr = new InUseArray(1, 2, 3);
|
|
46
|
-
arr.count = 3;
|
|
47
|
-
arr.free();
|
|
48
|
-
assert.eq(arr.count, 0, 'Count should be reset to 0');
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
Testimony.test('InUseArray.reset', () => {
|
|
52
|
-
const arr = new InUseArray(1, 2, 3);
|
|
53
|
-
arr.reset();
|
|
54
|
-
assert.eq(arr.length, 0, 'Array should be empty');
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
Testimony.test('InUseMap.add', () => {
|
|
61
|
-
const map = new InUseMap();
|
|
62
|
-
map.add('key', 'value');
|
|
63
|
-
assert.eq(map.data['key'][0], 'value', 'Value should be added to the map');
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
Testimony.test('InUseMap.getAll', () => {
|
|
67
|
-
const map = new InUseMap();
|
|
68
|
-
map.add('key', 'value');
|
|
69
|
-
const values = map.getAll('key');
|
|
70
|
-
assert.eq(values.length, 1, 'Should return an array with one value');
|
|
71
|
-
assert.eq(values[0], 'value', 'Should return the correct value');
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
Testimony.test('InUseMap.use', () => {
|
|
75
|
-
const map = new InUseMap();
|
|
76
|
-
map.add('key', 'value');
|
|
77
|
-
let value = map.use('key');
|
|
78
|
-
assert.eq(value, 'value', 'Should return and mark the value as in-use');
|
|
79
|
-
value = map.use('key');
|
|
80
|
-
assert.eq(value, undefined, 'Should return and mark the value as in-use');
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
Testimony.test('InUseMap.hasValue', () => {
|
|
84
|
-
const map = new InUseMap();
|
|
85
|
-
map.add('key1', 'value1');
|
|
86
|
-
map.add('key2', 'value2');
|
|
87
|
-
const keys = map.hasValue('value1');
|
|
88
|
-
assert.eq(keys.length, 1, 'Should return an array with one key');
|
|
89
|
-
assert.eq(keys[0], 'key1', 'Should return the correct key');
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
|
package/src/unused/InUseMap.js
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* An array that keeps track of how many elements are in-use.
|
|
6
|
-
* In-use elements are always at the beginning of the array. */
|
|
7
|
-
export class InUseArray extends Array {
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @type {int} Everything before this number is in-use. */
|
|
11
|
-
count = 0;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Get a value and mark it as in-use.
|
|
16
|
-
* @param val {undefined|*} Optional. If set, find this specific value. Otherwise find any value.
|
|
17
|
-
* @return {undefined|*} The value that is now marked as in-use. */
|
|
18
|
-
use(val=undefined) {
|
|
19
|
-
if (val) {
|
|
20
|
-
// If val doesn't exist or is already in-use, return undefined.
|
|
21
|
-
let index = this.indexOf(val);
|
|
22
|
-
if (index === -1 || index < this.count)
|
|
23
|
-
return undefined;
|
|
24
|
-
|
|
25
|
-
// Swap the first available with val.
|
|
26
|
-
this[index] = this[this.count];
|
|
27
|
-
this[this.count] = val;
|
|
28
|
-
this.count++;
|
|
29
|
-
return val;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Try to get last available.
|
|
33
|
-
else {
|
|
34
|
-
if (this.count >= this.length)
|
|
35
|
-
return undefined;
|
|
36
|
-
let result = this[this.count];
|
|
37
|
-
this.count++;
|
|
38
|
-
return result;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
free() {
|
|
43
|
-
this.count = 0;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
reset() {
|
|
47
|
-
this.length = 0;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
*
|
|
53
|
-
*/
|
|
54
|
-
export class InUseMap {
|
|
55
|
-
|
|
56
|
-
/** @type {Object<string, InUseArray>} */
|
|
57
|
-
data = {};
|
|
58
|
-
|
|
59
|
-
// Set a new value for a key
|
|
60
|
-
add(key, value) {
|
|
61
|
-
let data = this.data;
|
|
62
|
-
let array = data[key]
|
|
63
|
-
if (!array) {
|
|
64
|
-
array = new InUseArray();
|
|
65
|
-
data[key] = array;
|
|
66
|
-
}
|
|
67
|
-
array.push(value);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Get all values for a key
|
|
71
|
-
getAll(key) {
|
|
72
|
-
return this.data[key] || [];
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Find a value matching the given key, mark it as inUse, and return it.
|
|
76
|
-
use(key, val=undefined) {
|
|
77
|
-
return this.data[key]?.use(val) || undefined
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// temporary
|
|
81
|
-
delete(key, val=undefined) {
|
|
82
|
-
return this.data[key]?.use(val) || undefined
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
freeAll() {
|
|
86
|
-
for (let key in this.data)
|
|
87
|
-
this.data[key].free();
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
hasValue(val) {
|
|
91
|
-
let data = this.data;
|
|
92
|
-
let names = [];
|
|
93
|
-
for (let name in data)
|
|
94
|
-
if (data[name].includes(val))
|
|
95
|
-
names.push(name)
|
|
96
|
-
return names;
|
|
97
|
-
}
|
|
98
|
-
}
|
package/src/unused/LinkedList.js
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
export default class LinkedList {
|
|
2
|
-
|
|
3
|
-
constructor(input) {
|
|
4
|
-
this.head = null;
|
|
5
|
-
this.tail = null;
|
|
6
|
-
this.map = new Map();
|
|
7
|
-
|
|
8
|
-
if (Array.isArray(input)) {
|
|
9
|
-
input.forEach(val => this.push(val));
|
|
10
|
-
} else if (input instanceof LinkedList) {
|
|
11
|
-
for (let val of input) {
|
|
12
|
-
this.push(val);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
push(value) {
|
|
18
|
-
if (!this.head) {
|
|
19
|
-
this.head = value;
|
|
20
|
-
this.tail = value;
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
this.map.set(value, { prev: this.tail, next: null });
|
|
25
|
-
this.map.set(this.tail, { ...this.map.get(this.tail), next: value });
|
|
26
|
-
this.tail = value;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
unshift(value) {
|
|
30
|
-
if (!this.head) {
|
|
31
|
-
this.push(value);
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
this.map.set(value, { prev: null, next: this.head });
|
|
36
|
-
this.map.set(this.head, { ...this.map.get(this.head), prev: value });
|
|
37
|
-
this.head = value;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
shift() {
|
|
41
|
-
if (!this.head) return;
|
|
42
|
-
|
|
43
|
-
const secondNode = this.map.get(this.head).next;
|
|
44
|
-
|
|
45
|
-
if (secondNode) {
|
|
46
|
-
this.map.set(secondNode, { ...this.map.get(secondNode), prev: null });
|
|
47
|
-
} else {
|
|
48
|
-
this.tail = null;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
this.head = secondNode;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
pop() {
|
|
55
|
-
if (!this.tail) return;
|
|
56
|
-
|
|
57
|
-
const penultimate = this.map.get(this.tail).prev;
|
|
58
|
-
|
|
59
|
-
if (penultimate) {
|
|
60
|
-
this.map.set(penultimate, { ...this.map.get(penultimate), next: null });
|
|
61
|
-
} else {
|
|
62
|
-
this.head = null;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
this.tail = penultimate;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
*[Symbol.iterator]() {
|
|
69
|
-
let currentNode = this.head;
|
|
70
|
-
|
|
71
|
-
while (currentNode) {
|
|
72
|
-
yield currentNode;
|
|
73
|
-
const mapData = this.map.get(currentNode);
|
|
74
|
-
currentNode = mapData ? mapData.next : null;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
slice(start, end) {
|
|
79
|
-
const newList = new LinkedList();
|
|
80
|
-
let currentNode = start;
|
|
81
|
-
|
|
82
|
-
while (currentNode && currentNode !== end) {
|
|
83
|
-
newList.push(currentNode);
|
|
84
|
-
currentNode = this.map.get(currentNode).next;
|
|
85
|
-
}
|
|
86
|
-
if (end) {
|
|
87
|
-
newList.push(end);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return newList;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
splice(start, end, newList) {
|
|
95
|
-
// TODO: assert items in newList aren't already in our list.
|
|
96
|
-
|
|
97
|
-
const firstNewNode = newList instanceof LinkedList ? newList.head : newList[0];
|
|
98
|
-
const lastNewNode = newList instanceof LinkedList ? newList.tail : newList[newList.length - 1];
|
|
99
|
-
|
|
100
|
-
if (firstNewNode) {
|
|
101
|
-
this.map.set(start, { ...this.map.get(start), next: firstNewNode });
|
|
102
|
-
this.map.set(firstNewNode, { ...this.map.get(firstNewNode), prev: start });
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const afterEnd = end ? this.map.get(end).next : null;
|
|
106
|
-
if (lastNewNode) {
|
|
107
|
-
this.map.set(lastNewNode, { ...this.map.get(lastNewNode), next: afterEnd });
|
|
108
|
-
if (afterEnd) {
|
|
109
|
-
this.map.set(afterEnd, { ...this.map.get(afterEnd), prev: lastNewNode });
|
|
110
|
-
} else {
|
|
111
|
-
this.tail = lastNewNode;
|
|
112
|
-
}
|
|
113
|
-
} else if (afterEnd) {
|
|
114
|
-
this.map.set(afterEnd, { ...this.map.get(afterEnd), prev: start });
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|