list-toolkit 1.0.2 → 2.1.0
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 +89 -419
- package/cjs/cache/cache-fifo.js +37 -0
- package/cjs/cache/cache-lfu.js +76 -0
- package/cjs/cache/cache-lru.js +100 -0
- package/cjs/cache/cache-random.js +77 -0
- package/cjs/cache/decorator.js +47 -0
- package/cjs/cache.js +28 -0
- package/cjs/ext-list.js +22 -0
- package/cjs/ext-slist.js +22 -0
- package/cjs/ext-value-list.js +22 -0
- package/cjs/ext-value-slist.js +22 -0
- package/cjs/{MinHeap.js → heap/min-heap.js} +68 -82
- package/cjs/heap.js +22 -0
- package/cjs/list/basics.js +88 -0
- package/cjs/list/core.js +305 -0
- package/cjs/list/ext-value.js +89 -0
- package/cjs/list/ext.js +356 -0
- package/cjs/list/nodes.js +240 -0
- package/cjs/list/ptr.js +61 -0
- package/cjs/list/value.js +100 -0
- package/cjs/list-helpers.js +91 -0
- package/cjs/list-utils.js +141 -0
- package/cjs/list.js +22 -0
- package/cjs/meta-utils.js +167 -0
- package/cjs/nt-utils.js +132 -0
- package/cjs/queue.js +58 -0
- package/cjs/slist/basics.js +71 -0
- package/cjs/slist/core.js +362 -0
- package/cjs/slist/ext-value.js +83 -0
- package/cjs/slist/ext.js +336 -0
- package/cjs/slist/nodes.js +276 -0
- package/cjs/slist/ptr.js +87 -0
- package/cjs/slist/value.js +91 -0
- package/cjs/slist.js +22 -0
- package/cjs/stack.js +55 -0
- package/cjs/tree/splay-tree.js +362 -0
- package/cjs/value-list.js +22 -0
- package/cjs/value-slist.js +22 -0
- package/package.json +7 -7
- package/src/cache/cache-fifo.js +27 -0
- package/src/cache/cache-lfu.js +63 -0
- package/src/cache/cache-lru.js +87 -0
- package/src/cache/cache-random.js +73 -0
- package/src/cache/decorator.js +45 -0
- package/src/cache.js +9 -0
- package/src/ext-list.js +6 -0
- package/src/ext-slist.js +6 -0
- package/src/ext-value-list.js +6 -0
- package/src/ext-value-slist.js +6 -0
- package/src/{MinHeap.js → heap/min-heap.js} +73 -85
- package/src/heap.js +6 -0
- package/src/list/basics.js +64 -0
- package/src/list/core.js +314 -0
- package/src/list/ext-value.js +81 -0
- package/src/list/ext.js +370 -0
- package/src/list/nodes.js +262 -0
- package/src/list/ptr.js +58 -0
- package/src/list/value.js +88 -0
- package/src/list-helpers.js +80 -0
- package/src/list-utils.js +140 -0
- package/src/list.js +6 -0
- package/src/meta-utils.js +147 -0
- package/src/nt-utils.js +85 -0
- package/src/queue.js +52 -0
- package/src/slist/basics.js +47 -0
- package/src/slist/core.js +364 -0
- package/src/slist/ext-value.js +74 -0
- package/src/slist/ext.js +331 -0
- package/src/slist/nodes.js +290 -0
- package/src/slist/ptr.js +77 -0
- package/src/slist/value.js +75 -0
- package/src/slist.js +6 -0
- package/src/stack.js +52 -0
- package/src/tree/splay-tree.js +378 -0
- package/src/value-list.js +6 -0
- package/src/value-slist.js +6 -0
- package/cjs/Cache.js +0 -71
- package/cjs/List.js +0 -294
- package/cjs/ListHead.js +0 -309
- package/cjs/SList.js +0 -342
- package/cjs/SListHead.js +0 -367
- package/cjs/utils.js +0 -43
- package/src/Cache.js +0 -61
- package/src/List.js +0 -303
- package/src/ListHead.js +0 -304
- package/src/SList.js +0 -330
- package/src/SListHead.js +0 -354
- package/src/utils.js +0 -35
package/src/list/ext.js
ADDED
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import {ExtListBase, PtrBase} from './nodes.js';
|
|
4
|
+
import {pop, extract, splice, append} from './basics.js';
|
|
5
|
+
import {addAliases, normalizeIterator} from '../meta-utils.js';
|
|
6
|
+
|
|
7
|
+
export class Ptr extends PtrBase {
|
|
8
|
+
constructor(list, node) {
|
|
9
|
+
super(list, node, ExtList);
|
|
10
|
+
this.node ||= this.list.head;
|
|
11
|
+
}
|
|
12
|
+
clone() {
|
|
13
|
+
return new Ptr(this);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class ExtList extends ExtListBase {
|
|
18
|
+
makePtr(node) {
|
|
19
|
+
if (node && !this.isNodeLike(node)) throw new Error('"node" is not a compatible node');
|
|
20
|
+
node ||= this.head;
|
|
21
|
+
return node ? new Ptr(this, node) : null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
makePtrFromPrev(prev) {
|
|
25
|
+
if (prev && !this.isNodeLike(prev)) throw new Error('"prev" is not a compatible node');
|
|
26
|
+
return new Ptr(this, prev ? prev[this.nextName] : this.front);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Ptr API
|
|
30
|
+
|
|
31
|
+
removeCurrent() {
|
|
32
|
+
if (!this.head) return null;
|
|
33
|
+
if (this.head[this.nextName] === this.head) {
|
|
34
|
+
const node = this.head;
|
|
35
|
+
this.head = null;
|
|
36
|
+
return node;
|
|
37
|
+
}
|
|
38
|
+
const result = pop(this, this.head);
|
|
39
|
+
this.head = result.rest;
|
|
40
|
+
return result.extracted;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
removeNodeBefore() {
|
|
44
|
+
return this.head ? this.removeNode(this.head[this.prevName]) : null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
removeNodeAfter() {
|
|
48
|
+
return this.head ? this.removeNode(this.head[this.nextName]) : null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
addBefore(value) {
|
|
52
|
+
const node = this.adoptValue(value);
|
|
53
|
+
if (this.head) {
|
|
54
|
+
splice(this, this.head[this.prevName], node);
|
|
55
|
+
} else {
|
|
56
|
+
this.head = node;
|
|
57
|
+
}
|
|
58
|
+
return this.makePtr(node);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
addAfter(value) {
|
|
62
|
+
const node = this.adoptValue(value);
|
|
63
|
+
if (this.head) {
|
|
64
|
+
splice(this, this.head, node);
|
|
65
|
+
} else {
|
|
66
|
+
this.head = node;
|
|
67
|
+
}
|
|
68
|
+
return this.makePtr(node);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
addNodeBefore(nodeOrPtr) {
|
|
72
|
+
const node = this.adoptNode(nodeOrPtr);
|
|
73
|
+
if (this.head) {
|
|
74
|
+
splice(this, this.head[this.prevName], node);
|
|
75
|
+
} else {
|
|
76
|
+
this.head = node;
|
|
77
|
+
}
|
|
78
|
+
return this.makePtr(node);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
addNodeAfter(nodeOrPtr) {
|
|
82
|
+
const node = this.adoptNode(nodeOrPtr);
|
|
83
|
+
if (this.head) {
|
|
84
|
+
splice(this, this.head, node);
|
|
85
|
+
} else {
|
|
86
|
+
this.head = node;
|
|
87
|
+
}
|
|
88
|
+
return this.makePtr(node);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
insertBefore(extList) {
|
|
92
|
+
if (!this.isCompatible(extList)) throw new Error('Incompatible lists');
|
|
93
|
+
|
|
94
|
+
const head = extList.head;
|
|
95
|
+
if (head) {
|
|
96
|
+
splice(this, this.head[this.prevName], head);
|
|
97
|
+
extList.head = null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return this.makePtr(head);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
insertAfter(extList) {
|
|
104
|
+
if (!this.isCompatible(extList)) throw new Error('Incompatible lists');
|
|
105
|
+
|
|
106
|
+
const head = extList.head;
|
|
107
|
+
if (head) {
|
|
108
|
+
splice(this, this.head, head);
|
|
109
|
+
extList.head = null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return this.makePtr(head);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
moveBefore(nodeOrPtr) {
|
|
116
|
+
const node = this.normalizeNode(nodeOrPtr);
|
|
117
|
+
|
|
118
|
+
if (this.head === node) {
|
|
119
|
+
this.head = this.head[this.nextName];
|
|
120
|
+
return this;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (this.head) {
|
|
124
|
+
append(this, this.head[this.prevName], {from: node});
|
|
125
|
+
} else {
|
|
126
|
+
pop(this, node);
|
|
127
|
+
this.head = node;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return this.makePtr(node);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
moveAfter(nodeOrPtr) {
|
|
134
|
+
const node = this.normalizeNode(nodeOrPtr);
|
|
135
|
+
|
|
136
|
+
if (this.head === node) {
|
|
137
|
+
this.head = this.head[this.prevName];
|
|
138
|
+
return this;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (this.head) {
|
|
142
|
+
append(this, this.head, {from: node});
|
|
143
|
+
} else {
|
|
144
|
+
pop(this, node);
|
|
145
|
+
this.head = node;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return this.makePtr(node);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// List API
|
|
152
|
+
|
|
153
|
+
clear(drop) {
|
|
154
|
+
if (drop) {
|
|
155
|
+
for (const current of this.getNodeIterator()) {
|
|
156
|
+
current[this.nextName] = current[this.prevName] = current; // make stand-alone
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
this.head = null;
|
|
160
|
+
return this;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
removeNode(nodeOrPtr) {
|
|
164
|
+
if (!this.head) return null;
|
|
165
|
+
|
|
166
|
+
const node = this.normalizeNode(nodeOrPtr);
|
|
167
|
+
|
|
168
|
+
if (this.head === node) {
|
|
169
|
+
// remove head
|
|
170
|
+
if (this.head[this.nextName] === this.head) {
|
|
171
|
+
// single node
|
|
172
|
+
this.head = null;
|
|
173
|
+
return node;
|
|
174
|
+
}
|
|
175
|
+
this.head = this.head[this.nextName];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return pop(this, node).extracted;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
removeRange(range, drop) {
|
|
182
|
+
return this.extractRange(range).clear(drop);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
extractRange(range = {}) {
|
|
186
|
+
range = this.normalizeRange(range);
|
|
187
|
+
const {from = this.head, to = from} = range;
|
|
188
|
+
|
|
189
|
+
const extracted = this.make();
|
|
190
|
+
if (!this.head) return extracted;
|
|
191
|
+
if (this.head === from || this.head === to) this.head = to[this.nextName];
|
|
192
|
+
if (this.head === from) this.head = null;
|
|
193
|
+
extracted.head = extract(this, {from, to}).extracted;
|
|
194
|
+
|
|
195
|
+
return extracted;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
extractBy(condition) {
|
|
199
|
+
const extracted = this.make();
|
|
200
|
+
if (this.isEmpty) return extracted;
|
|
201
|
+
|
|
202
|
+
const rest = this.make();
|
|
203
|
+
for (const current of this.getNodeIterator()) {
|
|
204
|
+
current[this.nextName] = current[this.prevName] = current; // make stand-alone
|
|
205
|
+
(condition(current) ? extracted : rest).addBefore(current);
|
|
206
|
+
}
|
|
207
|
+
this.head = rest.head;
|
|
208
|
+
|
|
209
|
+
return extracted;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
reverse() {
|
|
213
|
+
if (this.isOneOrEmpty) return this;
|
|
214
|
+
let current = this.head;
|
|
215
|
+
do {
|
|
216
|
+
const next = current[this.nextName];
|
|
217
|
+
current[this.nextName] = current[this.prevName];
|
|
218
|
+
current[this.prevName] = next;
|
|
219
|
+
current = next;
|
|
220
|
+
} while (current !== this.head);
|
|
221
|
+
this.head = this.head[this.nextName];
|
|
222
|
+
return this;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
sort(lessFn) {
|
|
226
|
+
if (this.isOneOrEmpty) return this;
|
|
227
|
+
|
|
228
|
+
const left = this.make(),
|
|
229
|
+
right = this.make();
|
|
230
|
+
|
|
231
|
+
// split into two sublists
|
|
232
|
+
let isLeft = true;
|
|
233
|
+
for (const current of this.getNodeIterator()) {
|
|
234
|
+
current[this.nextName] = current[this.prevName] = current; // make stand-alone
|
|
235
|
+
if (isLeft) {
|
|
236
|
+
left.addNodeAfter(current);
|
|
237
|
+
left.next();
|
|
238
|
+
} else {
|
|
239
|
+
right.addNodeAfter(current);
|
|
240
|
+
right.next();
|
|
241
|
+
}
|
|
242
|
+
isLeft = !isLeft;
|
|
243
|
+
}
|
|
244
|
+
this.clear();
|
|
245
|
+
// the list is empty now
|
|
246
|
+
|
|
247
|
+
// sort sublists
|
|
248
|
+
left.next().sort(lessFn);
|
|
249
|
+
right.next().sort(lessFn);
|
|
250
|
+
|
|
251
|
+
// merge sublists
|
|
252
|
+
while (!left.isEmpty && !right.isEmpty) {
|
|
253
|
+
this.addNodeAfter((lessFn(left.head, right.head) ? left : right).removeCurrent());
|
|
254
|
+
this.next();
|
|
255
|
+
}
|
|
256
|
+
if (!left.isEmpty) {
|
|
257
|
+
const last = left.head[left.prevName];
|
|
258
|
+
this.insertAfter(left);
|
|
259
|
+
this.head = last;
|
|
260
|
+
}
|
|
261
|
+
if (!right.isEmpty) {
|
|
262
|
+
const last = right.head[right.prevName];
|
|
263
|
+
this.insertAfter(right);
|
|
264
|
+
this.head = last;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return this.next();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// iterators
|
|
271
|
+
|
|
272
|
+
[Symbol.iterator]() {
|
|
273
|
+
let current = this.head,
|
|
274
|
+
readyToStop = this.isEmpty;
|
|
275
|
+
return normalizeIterator({
|
|
276
|
+
next: () => {
|
|
277
|
+
if (readyToStop && current === this.head) return {done: true};
|
|
278
|
+
readyToStop = true;
|
|
279
|
+
const value = current;
|
|
280
|
+
current = current[this.nextName];
|
|
281
|
+
return {value};
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
getNodeIterator(range = {}) {
|
|
287
|
+
range = this.normalizeRange(range);
|
|
288
|
+
const {from, to} = range;
|
|
289
|
+
return {
|
|
290
|
+
[Symbol.iterator]: () => {
|
|
291
|
+
let readyToStop = this.isEmpty,
|
|
292
|
+
current = readyToStop ? null : from || this.head;
|
|
293
|
+
const stop = readyToStop ? null : to ? to[this.nextName] : this.head;
|
|
294
|
+
return normalizeIterator({
|
|
295
|
+
next: () => {
|
|
296
|
+
if (readyToStop && current === stop) return {done: true};
|
|
297
|
+
readyToStop = true;
|
|
298
|
+
const value = current;
|
|
299
|
+
current = current[this.nextName];
|
|
300
|
+
return {value};
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
getPtrIterator(range) {
|
|
308
|
+
return mapIterator(this.getNodeIterator(range), node => new Ptr(this, node));
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
getReverseNodeIterator(range = {}) {
|
|
312
|
+
range = this.normalizeRange(range);
|
|
313
|
+
const {from, to} = range;
|
|
314
|
+
return {
|
|
315
|
+
[Symbol.iterator]: () => {
|
|
316
|
+
let readyToStop = this.isEmpty,
|
|
317
|
+
current = readyToStop ? null : to || this.head[this.prevName];
|
|
318
|
+
const stop = readyToStop ? null : from ? from[this.prevName] : this.head[this.prevName];
|
|
319
|
+
return normalizeIterator({
|
|
320
|
+
next: () => {
|
|
321
|
+
if (readyToStop && current === stop) return {done: true};
|
|
322
|
+
readyToStop = true;
|
|
323
|
+
const value = current;
|
|
324
|
+
current = current[this.prevName];
|
|
325
|
+
return {value};
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
getReversePtrIterator(range) {
|
|
333
|
+
return mapIterator(this.getReverseNodeIterator(range), node => new Ptr(this, node));
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// meta helpers
|
|
337
|
+
|
|
338
|
+
clone() {
|
|
339
|
+
return new ExtList(this);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
make(head = null) {
|
|
343
|
+
return new ExtList(head, this);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
makeFrom(values) {
|
|
347
|
+
return ExtList.from(values, this);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
static from(values, options) {
|
|
351
|
+
const list = new ExtList(null, options);
|
|
352
|
+
for (const value of values) {
|
|
353
|
+
list.addNodeAfter(value);
|
|
354
|
+
list.next();
|
|
355
|
+
}
|
|
356
|
+
return list.next();
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
ExtList.Ptr = Ptr;
|
|
361
|
+
|
|
362
|
+
addAliases(ExtList.prototype, {
|
|
363
|
+
addAfter: 'add',
|
|
364
|
+
removeNodeBefore: 'removeBefore',
|
|
365
|
+
removeNodeAfter: 'removeAfter',
|
|
366
|
+
getNodeIterator: 'getIterator',
|
|
367
|
+
getReverseNodeIterator: 'getReverseIterator'
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
export default ExtList;
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import {isRangeLike, normalizeNode, normalizeRange} from '../list-helpers.js';
|
|
4
|
+
import {addAlias, copyDescriptors} from '../meta-utils.js';
|
|
5
|
+
|
|
6
|
+
export const isNodeLike = ({nextName, prevName}, node) => node && node[prevName] && node[nextName];
|
|
7
|
+
export const isStandAlone = ({nextName, prevName}, node) => node && node[prevName] === node && node[nextName] === node;
|
|
8
|
+
export const isCompatible = (options1, options2) => options1.nextName === options2.nextName && options1.prevName === options2.prevName;
|
|
9
|
+
|
|
10
|
+
export class Node {
|
|
11
|
+
constructor({nextName = 'next', prevName = 'prev'} = {}) {
|
|
12
|
+
this.nextName = nextName;
|
|
13
|
+
this.prevName = prevName;
|
|
14
|
+
this[nextName] = this[prevName] = this;
|
|
15
|
+
}
|
|
16
|
+
get isStandAlone() {
|
|
17
|
+
return this[this.nextName] === this;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class HeadNode extends Node {
|
|
22
|
+
isNodeLike(node) {
|
|
23
|
+
if (!node) return false;
|
|
24
|
+
const next = node[this.nextName];
|
|
25
|
+
if (!next || typeof next != 'object') return false;
|
|
26
|
+
const prev = node[this.prevName];
|
|
27
|
+
return prev && typeof prev == 'object';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
isCompatibleNames({nextName, prevName}) {
|
|
31
|
+
return this.nextName === nextName && this.prevName === prevName;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
isCompatible(list) {
|
|
35
|
+
return list === this || (list instanceof HeadNode && this.nextName === list.nextName && this.prevName === list.prevName);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
isCompatiblePtr(ptr) {
|
|
39
|
+
return (
|
|
40
|
+
ptr instanceof PtrBase &&
|
|
41
|
+
(ptr.list === this || (ptr.list instanceof HeadNode && this.nextName === ptr.list.nextName && this.prevName === ptr.list.prevName))
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
isCompatibleRange(range) {
|
|
46
|
+
return isRangeLike(this, range);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
get isEmpty() {
|
|
50
|
+
return this[this.nextName] === this;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
get isOne() {
|
|
54
|
+
return this[this.nextName] !== this && this[this.nextName][this.nextName] === this;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
get isOneOrEmpty() {
|
|
58
|
+
return this[this.nextName][this.nextName] === this;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
get head() {
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
get front() {
|
|
66
|
+
return this[this.nextName];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
get back() {
|
|
70
|
+
return this[this.prevName];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
get range() {
|
|
74
|
+
return this[this.nextName] === this ? null : {from: this[this.nextName], to: this[this.prevName], list: this};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
getLength() {
|
|
78
|
+
let n = 0;
|
|
79
|
+
const nextName = this.nextName;
|
|
80
|
+
for (let p = this[nextName]; p !== this; ++n, p = p[nextName]);
|
|
81
|
+
return n;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
adoptNode(nodeOrPtr) {
|
|
85
|
+
const node = nodeOrPtr instanceof PtrBase ? nodeOrPtr.node : nodeOrPtr;
|
|
86
|
+
if (node[this.nextName] || node[this.prevName]) {
|
|
87
|
+
if (node[this.nextName] === node && node[this.prevName] === node) return node;
|
|
88
|
+
throw new Error('node is already a part of a list, or there is a name clash');
|
|
89
|
+
}
|
|
90
|
+
node[this.nextName] = node[this.prevName] = node;
|
|
91
|
+
if (nodeOrPtr instanceof PtrBase) nodeOrPtr.list = this;
|
|
92
|
+
return node;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
normalizeNode(nodeOrPtr) {
|
|
96
|
+
const node = normalizeNode(this, nodeOrPtr, PtrBase);
|
|
97
|
+
if (nodeOrPtr instanceof PtrBase) nodeOrPtr.list = this;
|
|
98
|
+
return node;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
normalizeRange(range) {
|
|
102
|
+
return normalizeRange(this, range, PtrBase);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
addAlias(HeadNode.prototype, 'adoptNode', 'adoptValue');
|
|
107
|
+
|
|
108
|
+
export class ValueNode extends Node {
|
|
109
|
+
constructor(value, options) {
|
|
110
|
+
super(options);
|
|
111
|
+
this.value = value;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export class PtrBase {
|
|
116
|
+
constructor(list, node, ListClass) {
|
|
117
|
+
if (list instanceof PtrBase) {
|
|
118
|
+
this.list = list.list;
|
|
119
|
+
this.node = list.node;
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (!(list instanceof ListClass)) throw new Error('"list" is not a compatible list');
|
|
123
|
+
if (node instanceof PtrBase) {
|
|
124
|
+
if (list !== node.list) throw new Error('Node specified by a pointer must belong to the same list');
|
|
125
|
+
this.list = list;
|
|
126
|
+
this.node = node.node;
|
|
127
|
+
} else {
|
|
128
|
+
this.list = list;
|
|
129
|
+
this.node = node;
|
|
130
|
+
}
|
|
131
|
+
if (this.node && !isNodeLike(this.list, this.node)) throw new Error('"node" is not a compatible node');
|
|
132
|
+
if (!this.node) this.node = this.list.front;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
get nextNode() {
|
|
136
|
+
return this.node[this.list.nextName];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
get prevNode() {
|
|
140
|
+
return this.node[this.list.prevName];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
isPrevNodeValid() {
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
next() {
|
|
148
|
+
this.node = this.node[this.list.nextName];
|
|
149
|
+
return this;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
prev() {
|
|
153
|
+
this.node = this.node[this.list.prevName];
|
|
154
|
+
return this;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export class ExtListBase {
|
|
159
|
+
constructor(head = null, {nextName = 'next', prevName = 'prev'} = {}) {
|
|
160
|
+
if (head instanceof ExtListBase) {
|
|
161
|
+
this.nextName = head.nextName;
|
|
162
|
+
this.prevName = head.prevName;
|
|
163
|
+
this.attach(head.head);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (head instanceof PtrBase) {
|
|
167
|
+
this.nextName = head.list.nextName;
|
|
168
|
+
this.prevName = head.list.prevName;
|
|
169
|
+
this.attach(head.node);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
this.nextName = nextName;
|
|
173
|
+
this.prevName = prevName;
|
|
174
|
+
this.attach(head);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
isCompatible(list) {
|
|
178
|
+
return list === this || (list instanceof ExtListBase && this.nextName === list.nextName && this.prevName === list.prevName);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
isCompatiblePtr(ptr) {
|
|
182
|
+
return (
|
|
183
|
+
ptr instanceof PtrBase &&
|
|
184
|
+
(ptr.list === this || (ptr.list instanceof ExtListBase && this.nextName === ptr.list.nextName && this.prevName === ptr.list.prevName))
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
get isEmpty() {
|
|
189
|
+
return !this.head;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
get isOne() {
|
|
193
|
+
return this.head && this.head[this.nextName] === this.head;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
get isOneOrEmpty() {
|
|
197
|
+
return !this.head || this.head[this.nextName] === this.head;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
get front() {
|
|
201
|
+
return this.head;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
get back() {
|
|
205
|
+
return this.head?.[this.prevName];
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
get range() {
|
|
209
|
+
return this.head ? {from: this.head, to: this.head[this.prevName], list: this.head} : null;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
getLength() {
|
|
213
|
+
if (!this.head) return 0;
|
|
214
|
+
|
|
215
|
+
let n = 0,
|
|
216
|
+
current = this.head;
|
|
217
|
+
do {
|
|
218
|
+
current = current[this.nextName];
|
|
219
|
+
++n;
|
|
220
|
+
} while (current !== this.head);
|
|
221
|
+
|
|
222
|
+
return n;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
attach(head = null) {
|
|
226
|
+
const oldHead = this.head;
|
|
227
|
+
if (head instanceof PtrBase) {
|
|
228
|
+
if (!this.isCompatible(head.list)) throw new Error('Incompatible lists');
|
|
229
|
+
this.head = head.node;
|
|
230
|
+
} else {
|
|
231
|
+
if (head && !this.isNodeLike(head)) throw new Error('"head" is not a compatible node');
|
|
232
|
+
this.head = head;
|
|
233
|
+
}
|
|
234
|
+
return oldHead;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
detach() {
|
|
238
|
+
const oldHead = this.head;
|
|
239
|
+
this.head = null;
|
|
240
|
+
return oldHead;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
next() {
|
|
244
|
+
if (this.head) this.head = this.head[this.nextName];
|
|
245
|
+
return this;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
prev() {
|
|
249
|
+
if (this.head) this.head = this.head[this.prevName];
|
|
250
|
+
return this;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
copyDescriptors(ExtListBase.prototype, HeadNode.prototype, [
|
|
255
|
+
'isNodeLike',
|
|
256
|
+
'isCompatibleNames',
|
|
257
|
+
'isCompatibleRange',
|
|
258
|
+
'normalizeNode',
|
|
259
|
+
'normalizeRange',
|
|
260
|
+
'adoptNode',
|
|
261
|
+
'adoptValue'
|
|
262
|
+
]);
|
package/src/list/ptr.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import {HeadNode, PtrBase} from './nodes.js';
|
|
4
|
+
import {pop, splice} from './basics.js';
|
|
5
|
+
|
|
6
|
+
export class Ptr extends PtrBase {
|
|
7
|
+
constructor(list, node) {
|
|
8
|
+
super(list, node, HeadNode);
|
|
9
|
+
}
|
|
10
|
+
get isHead() {
|
|
11
|
+
return this.node === this.list;
|
|
12
|
+
}
|
|
13
|
+
clone() {
|
|
14
|
+
return new Ptr(this);
|
|
15
|
+
}
|
|
16
|
+
removeCurrent() {
|
|
17
|
+
if (this.node === this.list) return null;
|
|
18
|
+
const node = this.node;
|
|
19
|
+
this.node = node[this.list.nextName];
|
|
20
|
+
return pop(this.list, node).extracted;
|
|
21
|
+
}
|
|
22
|
+
addBefore(value) {
|
|
23
|
+
const node = this.list.adoptValue(value);
|
|
24
|
+
splice(this.list, this.node[this.list.prevName], node);
|
|
25
|
+
return this.list.makePtr(node);
|
|
26
|
+
}
|
|
27
|
+
addNodeBefore(node) {
|
|
28
|
+
node = this.list.adoptNode(node);
|
|
29
|
+
splice(this.list, this.node[this.list.prevName], node);
|
|
30
|
+
return this.list.makePtr(node);
|
|
31
|
+
}
|
|
32
|
+
addAfter(value) {
|
|
33
|
+
const node = this.list.adoptValue(value);
|
|
34
|
+
splice(this.list, this.node, node);
|
|
35
|
+
return this.list.makePtr(node);
|
|
36
|
+
}
|
|
37
|
+
addNodeAfter(node) {
|
|
38
|
+
node = this.list.adoptNode(node);
|
|
39
|
+
splice(this.list, this.node, node);
|
|
40
|
+
return this.list.makePtr(node);
|
|
41
|
+
}
|
|
42
|
+
insertBefore(list) {
|
|
43
|
+
if (!this.list.isCompatible(list)) throw new Error('Incompatible lists');
|
|
44
|
+
if (list.isEmpty) return null;
|
|
45
|
+
const head = pop(list, list).rest;
|
|
46
|
+
splice(this.list, this.node[this.list.prevName], head);
|
|
47
|
+
return this.list.makePtr(head);
|
|
48
|
+
}
|
|
49
|
+
insertAfter(list) {
|
|
50
|
+
if (!this.list.isCompatible(list)) throw new Error('Incompatible lists');
|
|
51
|
+
if (list.isEmpty) return null;
|
|
52
|
+
const head = pop(list, list).rest;
|
|
53
|
+
splice(this.list, this.node, head);
|
|
54
|
+
return this.list.makePtr(head);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export default Ptr;
|