UNPKG

12.3 kBJavaScriptView Raw
1"use strict";
2// *****************************************************************************
3// Copyright (C) 2020 TypeFox and others.
4//
5// This program and the accompanying materials are made available under the
6// terms of the Eclipse Public License v. 2.0 which is available at
7// http://www.eclipse.org/legal/epl-2.0.
8//
9// This Source Code may also be made available under the following Secondary
10// Licenses when the conditions for such availability set forth in the Eclipse
11// Public License v. 2.0 are satisfied: GNU General Public License, version 2
12// with the GNU Classpath Exception which is available at
13// https://www.gnu.org/software/classpath/license.html.
14//
15// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
16// *****************************************************************************
17/*---------------------------------------------------------------------------------------------
18 * Copyright (c) Microsoft Corporation. All rights reserved.
19 * Licensed under the MIT License. See License.txt in the project root for license information.
20 *--------------------------------------------------------------------------------------------*/
21Object.defineProperty(exports, "__esModule", { value: true });
22exports.TernarySearchTree = exports.UriIterator = exports.PathIterator = void 0;
23const strings_1 = require("./strings");
24class PathIterator {
25 constructor(_splitOnBackslash = true, _caseSensitive = true) {
26 this._splitOnBackslash = _splitOnBackslash;
27 this._caseSensitive = _caseSensitive;
28 }
29 reset(key) {
30 this._value = key.replace(/\\$|\/$/, '');
31 this._from = 0;
32 this._to = 0;
33 return this.next();
34 }
35 hasNext() {
36 return this._to < this._value.length;
37 }
38 next() {
39 // this._data = key.split(/[\\/]/).filter(s => !!s);
40 this._from = this._to;
41 let justSeps = true;
42 for (; this._to < this._value.length; this._to++) {
43 const ch = this._value.charCodeAt(this._to);
44 if (ch === 47 /* Slash */ || this._splitOnBackslash && ch === 92 /* Backslash */) {
45 if (justSeps) {
46 this._from++;
47 }
48 else {
49 break;
50 }
51 }
52 else {
53 justSeps = false;
54 }
55 }
56 return this;
57 }
58 cmp(a) {
59 return this._caseSensitive
60 ? (0, strings_1.compareSubstring)(a, this._value, 0, a.length, this._from, this._to)
61 : (0, strings_1.compareSubstringIgnoreCase)(a, this._value, 0, a.length, this._from, this._to);
62 }
63 value() {
64 return this._value.substring(this._from, this._to);
65 }
66}
67exports.PathIterator = PathIterator;
68class UriIterator {
69 constructor(caseSensitive) {
70 this.caseSensitive = caseSensitive;
71 this._states = [];
72 this._stateIdx = 0;
73 }
74 reset(key) {
75 this._value = key;
76 this._states = [];
77 if (this._value.scheme) {
78 this._states.push(1 /* Scheme */);
79 }
80 if (this._value.authority) {
81 this._states.push(2 /* Authority */);
82 }
83 if (this._value.path) {
84 this._pathIterator = new PathIterator(false, this.caseSensitive);
85 this._pathIterator.reset(key.path.toString());
86 if (this._pathIterator.value()) {
87 this._states.push(3 /* Path */);
88 }
89 }
90 if (this._value.query) {
91 this._states.push(4 /* Query */);
92 }
93 if (this._value.fragment) {
94 this._states.push(5 /* Fragment */);
95 }
96 this._stateIdx = 0;
97 return this;
98 }
99 next() {
100 if (this._states[this._stateIdx] === 3 /* Path */ && this._pathIterator.hasNext()) {
101 this._pathIterator.next();
102 }
103 else {
104 this._stateIdx += 1;
105 }
106 return this;
107 }
108 hasNext() {
109 return (this._states[this._stateIdx] === 3 /* Path */ && this._pathIterator.hasNext())
110 || this._stateIdx < this._states.length - 1;
111 }
112 cmp(a) {
113 if (this._states[this._stateIdx] === 1 /* Scheme */) {
114 return (0, strings_1.compareSubstringIgnoreCase)(a, this._value.scheme);
115 }
116 else if (this._states[this._stateIdx] === 2 /* Authority */) {
117 return (0, strings_1.compareSubstringIgnoreCase)(a, this._value.authority);
118 }
119 else if (this._states[this._stateIdx] === 3 /* Path */) {
120 return this._pathIterator.cmp(a);
121 }
122 else if (this._states[this._stateIdx] === 4 /* Query */) {
123 return (0, strings_1.compare)(a, this._value.query);
124 }
125 else if (this._states[this._stateIdx] === 5 /* Fragment */) {
126 return (0, strings_1.compare)(a, this._value.fragment);
127 }
128 throw new Error();
129 }
130 value() {
131 if (this._states[this._stateIdx] === 1 /* Scheme */) {
132 return this._value.scheme;
133 }
134 else if (this._states[this._stateIdx] === 2 /* Authority */) {
135 return this._value.authority;
136 }
137 else if (this._states[this._stateIdx] === 3 /* Path */) {
138 return this._pathIterator.value();
139 }
140 else if (this._states[this._stateIdx] === 4 /* Query */) {
141 return this._value.query;
142 }
143 else if (this._states[this._stateIdx] === 5 /* Fragment */) {
144 return this._value.fragment;
145 }
146 throw new Error();
147 }
148}
149exports.UriIterator = UriIterator;
150class TernarySearchTreeNode {
151 isEmpty() {
152 return !this.left && !this.mid && !this.right && !this.value;
153 }
154}
155class TernarySearchTree {
156 constructor(segments) {
157 this._iter = segments;
158 }
159 static forUris(caseSensitive) {
160 return new TernarySearchTree(new UriIterator(caseSensitive));
161 }
162 static forPaths() {
163 return new TernarySearchTree(new PathIterator());
164 }
165 clear() {
166 this._root = undefined;
167 }
168 set(key, element) {
169 const iter = this._iter.reset(key);
170 let node;
171 if (!this._root) {
172 this._root = new TernarySearchTreeNode();
173 this._root.segment = iter.value();
174 }
175 node = this._root;
176 while (true) {
177 const val = iter.cmp(node.segment);
178 if (val > 0) {
179 // left
180 if (!node.left) {
181 node.left = new TernarySearchTreeNode();
182 node.left.segment = iter.value();
183 }
184 node = node.left;
185 }
186 else if (val < 0) {
187 // right
188 if (!node.right) {
189 node.right = new TernarySearchTreeNode();
190 node.right.segment = iter.value();
191 }
192 node = node.right;
193 }
194 else if (iter.hasNext()) {
195 // mid
196 iter.next();
197 if (!node.mid) {
198 node.mid = new TernarySearchTreeNode();
199 node.mid.segment = iter.value();
200 }
201 node = node.mid;
202 }
203 else {
204 break;
205 }
206 }
207 const oldElement = node.value;
208 node.value = element;
209 node.key = key;
210 return oldElement;
211 }
212 get(key) {
213 const iter = this._iter.reset(key);
214 let node = this._root;
215 while (node) {
216 const val = iter.cmp(node.segment);
217 if (val > 0) {
218 // left
219 node = node.left;
220 }
221 else if (val < 0) {
222 // right
223 node = node.right;
224 }
225 else if (iter.hasNext()) {
226 // mid
227 iter.next();
228 node = node.mid;
229 }
230 else {
231 break;
232 }
233 }
234 return node ? node.value : undefined;
235 }
236 delete(key) {
237 const iter = this._iter.reset(key);
238 const stack = [];
239 let node = this._root;
240 // find and unset node
241 while (node) {
242 const val = iter.cmp(node.segment);
243 if (val > 0) {
244 // left
245 stack.push([1, node]);
246 node = node.left;
247 }
248 else if (val < 0) {
249 // right
250 stack.push([-1, node]);
251 node = node.right;
252 }
253 else if (iter.hasNext()) {
254 // mid
255 iter.next();
256 stack.push([0, node]);
257 node = node.mid;
258 }
259 else {
260 // remove element
261 node.value = undefined;
262 // clean up empty nodes
263 while (stack.length > 0 && node.isEmpty()) {
264 const [dir, parent] = stack.pop();
265 switch (dir) {
266 case 1:
267 parent.left = undefined;
268 break;
269 case 0:
270 parent.mid = undefined;
271 break;
272 case -1:
273 parent.right = undefined;
274 break;
275 }
276 node = parent;
277 }
278 break;
279 }
280 }
281 }
282 findSubstr(key) {
283 const iter = this._iter.reset(key);
284 let node = this._root;
285 let candidate = undefined;
286 while (node) {
287 const val = iter.cmp(node.segment);
288 if (val > 0) {
289 // left
290 node = node.left;
291 }
292 else if (val < 0) {
293 // right
294 node = node.right;
295 }
296 else if (iter.hasNext()) {
297 // mid
298 iter.next();
299 candidate = node.value || candidate;
300 node = node.mid;
301 }
302 else {
303 break;
304 }
305 }
306 return node && node.value || candidate;
307 }
308 findSuperstr(key) {
309 const iter = this._iter.reset(key);
310 let node = this._root;
311 while (node) {
312 const val = iter.cmp(node.segment);
313 if (val > 0) {
314 // left
315 node = node.left;
316 }
317 else if (val < 0) {
318 // right
319 node = node.right;
320 }
321 else if (iter.hasNext()) {
322 // mid
323 iter.next();
324 node = node.mid;
325 }
326 else {
327 // collect
328 if (!node.mid) {
329 return undefined;
330 }
331 else {
332 return this._nodeIterator(node.mid);
333 }
334 }
335 }
336 return undefined;
337 }
338 _nodeIterator(node) {
339 let res;
340 let idx;
341 let data;
342 const next = () => {
343 if (!data) {
344 // lazy till first invocation
345 data = [];
346 idx = 0;
347 this._forEach(node, value => data.push(value));
348 }
349 if (idx >= data.length) {
350 return { done: true, value: undefined };
351 }
352 if (!res) {
353 res = { done: false, value: data[idx++] };
354 }
355 else {
356 res.value = data[idx++];
357 }
358 return res;
359 };
360 return { next };
361 }
362 forEach(callback) {
363 this._forEach(this._root, callback);
364 }
365 _forEach(node, callback) {
366 if (node) {
367 // left
368 this._forEach(node.left, callback);
369 // node
370 if (node.value) {
371 // callback(node.value, this._iter.join(parts));
372 callback(node.value, node.key);
373 }
374 // mid
375 this._forEach(node.mid, callback);
376 // right
377 this._forEach(node.right, callback);
378 }
379 }
380}
381exports.TernarySearchTree = TernarySearchTree;
382//# sourceMappingURL=ternary-search-tree.js.map
\No newline at end of file