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