You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

431 lines
8.9 KiB

  1. 'use strict';
  2. import { Iterables } from './iterable';
  3. import { Strings } from './string';
  4. // Code stolen from https://github.com/Microsoft/vscode/blob/b3e6d5bb039a4a9362b52a2c8726267ca68cf64e/src/vs/base/common/map.ts#L352
  5. const FIN = { done: true, value: undefined };
  6. // eslint-disable-next-line @typescript-eslint/interface-name-prefix
  7. export interface IKeyIterator {
  8. reset(key: string): this;
  9. next(): this;
  10. hasNext(): boolean;
  11. cmp(a: string): number;
  12. value(): string;
  13. }
  14. export class StringIterator implements IKeyIterator {
  15. private _value: string = '';
  16. private _pos: number = 0;
  17. reset(key: string): this {
  18. this._value = key;
  19. this._pos = 0;
  20. return this;
  21. }
  22. next(): this {
  23. this._pos += 1;
  24. return this;
  25. }
  26. hasNext(): boolean {
  27. return this._pos < this._value.length - 1;
  28. }
  29. cmp(a: string): number {
  30. const aCode = a.charCodeAt(0);
  31. const thisCode = this._value.charCodeAt(this._pos);
  32. return aCode - thisCode;
  33. }
  34. value(): string {
  35. return this._value[this._pos];
  36. }
  37. }
  38. export class PathIterator implements IKeyIterator {
  39. private _value!: string;
  40. private _from!: number;
  41. private _to!: number;
  42. reset(key: string): this {
  43. this._value = key.replace(/\\$|\/$/, '');
  44. this._from = 0;
  45. this._to = 0;
  46. return this.next();
  47. }
  48. hasNext(): boolean {
  49. return this._to < this._value.length;
  50. }
  51. next(): this {
  52. // this._data = key.split(/[\\/]/).filter(s => !!s);
  53. this._from = this._to;
  54. let justSeps = true;
  55. for (; this._to < this._value.length; this._to++) {
  56. const ch = this._value.charCodeAt(this._to);
  57. if (ch === Strings.CharCode.Slash || ch === Strings.CharCode.Backslash) {
  58. if (justSeps) {
  59. this._from++;
  60. } else {
  61. break;
  62. }
  63. } else {
  64. justSeps = false;
  65. }
  66. }
  67. return this;
  68. }
  69. cmp(a: string): number {
  70. let aPos = 0;
  71. const aLen = a.length;
  72. let thisPos = this._from;
  73. while (aPos < aLen && thisPos < this._to) {
  74. const cmp = a.charCodeAt(aPos) - this._value.charCodeAt(thisPos);
  75. if (cmp !== 0) {
  76. return cmp;
  77. }
  78. aPos += 1;
  79. thisPos += 1;
  80. }
  81. if (aLen === this._to - this._from) {
  82. return 0;
  83. }
  84. if (aPos < aLen) {
  85. return -1;
  86. }
  87. return 1;
  88. }
  89. value(): string {
  90. return this._value.substring(this._from, this._to);
  91. }
  92. }
  93. class TernarySearchTreeNode<E> {
  94. segment!: string;
  95. value: E | undefined;
  96. key!: string;
  97. left: TernarySearchTreeNode<E> | undefined;
  98. mid: TernarySearchTreeNode<E> | undefined;
  99. right: TernarySearchTreeNode<E> | undefined;
  100. isEmpty(): boolean {
  101. return !this.left && !this.mid && !this.right && !this.value;
  102. }
  103. }
  104. export class TernarySearchTree<E> {
  105. static forPaths<E>(): TernarySearchTree<E> {
  106. return new TernarySearchTree<E>(new PathIterator());
  107. }
  108. static forStrings<E>(): TernarySearchTree<E> {
  109. return new TernarySearchTree<E>(new StringIterator());
  110. }
  111. private _iter: IKeyIterator;
  112. private _root: TernarySearchTreeNode<E> | undefined;
  113. constructor(segments: IKeyIterator) {
  114. this._iter = segments;
  115. }
  116. clear(): void {
  117. this._root = undefined;
  118. }
  119. set(key: string, element: E): E | undefined {
  120. const iter = this._iter.reset(key);
  121. let node: TernarySearchTreeNode<E>;
  122. if (!this._root) {
  123. this._root = new TernarySearchTreeNode<E>();
  124. this._root.segment = iter.value();
  125. }
  126. node = this._root;
  127. while (true) {
  128. const val = iter.cmp(node.segment);
  129. if (val > 0) {
  130. // left
  131. if (!node.left) {
  132. node.left = new TernarySearchTreeNode<E>();
  133. node.left.segment = iter.value();
  134. }
  135. node = node.left;
  136. } else if (val < 0) {
  137. // right
  138. if (!node.right) {
  139. node.right = new TernarySearchTreeNode<E>();
  140. node.right.segment = iter.value();
  141. }
  142. node = node.right;
  143. } else if (iter.hasNext()) {
  144. // mid
  145. iter.next();
  146. if (!node.mid) {
  147. node.mid = new TernarySearchTreeNode<E>();
  148. node.mid.segment = iter.value();
  149. }
  150. node = node.mid;
  151. } else {
  152. break;
  153. }
  154. }
  155. const oldElement = node.value;
  156. node.value = element;
  157. node.key = key;
  158. return oldElement;
  159. }
  160. get(key: string): E | undefined {
  161. const iter = this._iter.reset(key);
  162. let node = this._root;
  163. while (node) {
  164. const val = iter.cmp(node.segment);
  165. if (val > 0) {
  166. // left
  167. node = node.left;
  168. } else if (val < 0) {
  169. // right
  170. node = node.right;
  171. } else if (iter.hasNext()) {
  172. // mid
  173. iter.next();
  174. node = node.mid;
  175. } else {
  176. break;
  177. }
  178. }
  179. return node ? node.value : undefined;
  180. }
  181. delete(key: string): void {
  182. const iter = this._iter.reset(key);
  183. const stack: [-1 | 0 | 1, TernarySearchTreeNode<E>][] = [];
  184. let node = this._root;
  185. // find and unset node
  186. while (node) {
  187. const val = iter.cmp(node.segment);
  188. if (val > 0) {
  189. // left
  190. stack.push([1, node]);
  191. node = node.left;
  192. } else if (val < 0) {
  193. // right
  194. stack.push([-1, node]);
  195. node = node.right;
  196. } else if (iter.hasNext()) {
  197. // mid
  198. iter.next();
  199. stack.push([0, node]);
  200. node = node.mid;
  201. } else {
  202. // remove element
  203. node.value = undefined;
  204. // clean up empty nodes
  205. while (stack.length > 0 && node.isEmpty()) {
  206. const [dir, parent] = stack.pop()!;
  207. switch (dir) {
  208. case 1:
  209. parent.left = undefined;
  210. break;
  211. case 0:
  212. parent.mid = undefined;
  213. break;
  214. case -1:
  215. parent.right = undefined;
  216. break;
  217. }
  218. node = parent;
  219. }
  220. break;
  221. }
  222. }
  223. }
  224. findSubstr(key: string): E | undefined {
  225. const iter = this._iter.reset(key);
  226. let node = this._root;
  227. let candidate: E | undefined;
  228. while (node) {
  229. const val = iter.cmp(node.segment);
  230. if (val > 0) {
  231. // left
  232. node = node.left;
  233. } else if (val < 0) {
  234. // right
  235. node = node.right;
  236. } else if (iter.hasNext()) {
  237. // mid
  238. iter.next();
  239. candidate = node.value || candidate;
  240. node = node.mid;
  241. } else {
  242. break;
  243. }
  244. }
  245. return (node && node.value) || candidate;
  246. }
  247. findSuperstr(key: string, limit: boolean = false): Iterable<E> | undefined {
  248. const iter = this._iter.reset(key);
  249. let node = this._root;
  250. while (node) {
  251. const val = iter.cmp(node.segment);
  252. if (val > 0) {
  253. // left
  254. node = node.left;
  255. } else if (val < 0) {
  256. // right
  257. node = node.right;
  258. } else if (iter.hasNext()) {
  259. // mid
  260. iter.next();
  261. node = node.mid;
  262. } else {
  263. // collect
  264. if (!node.mid) {
  265. return undefined;
  266. }
  267. node = node.mid;
  268. return {
  269. // eslint-disable-next-line no-loop-func
  270. [Symbol.iterator]: () => this._nodeIterator(node!, limit)
  271. };
  272. }
  273. }
  274. return undefined;
  275. }
  276. private _nodeIterator(node: TernarySearchTreeNode<E>, limit: boolean = false): Iterator<E> {
  277. let res: { done: false; value: E };
  278. let idx: number;
  279. let data: E[];
  280. const next = (): IteratorResult<E> => {
  281. if (!data) {
  282. // lazy till first invocation
  283. data = [];
  284. idx = 0;
  285. this._forEach(node, value => data.push(value), limit);
  286. }
  287. if (idx >= data.length) {
  288. return (FIN as unknown) as IteratorResult<E>;
  289. }
  290. if (!res) {
  291. res = { done: false, value: data[idx++] };
  292. } else {
  293. res.value = data[idx++];
  294. }
  295. return res;
  296. };
  297. return { next: next };
  298. }
  299. forEach(callback: (value: E, index: string) => any) {
  300. this._forEach(this._root, callback);
  301. }
  302. private _forEach(
  303. node: TernarySearchTreeNode<E> | undefined,
  304. callback: (value: E, index: string) => any,
  305. limit: boolean = false
  306. ) {
  307. if (node === undefined) return;
  308. // left
  309. this._forEach(node.left, callback, limit);
  310. // node
  311. if (node.value) {
  312. callback(node.value, node.key);
  313. }
  314. if (!limit) {
  315. // mid
  316. this._forEach(node.mid, callback, limit);
  317. }
  318. // right
  319. this._forEach(node.right, callback, limit);
  320. }
  321. any(): boolean {
  322. return this._root !== undefined && !this._root.isEmpty();
  323. }
  324. count(predicate?: (entry: E) => boolean): number {
  325. if (this._root === undefined || this._root.isEmpty()) return 0;
  326. return Iterables.count(this.entries(), predicate === undefined ? undefined : ([e]) => predicate(e));
  327. }
  328. entries(): Iterable<[E, string]> {
  329. return this._iterator(this._root);
  330. }
  331. values(): Iterable<E> {
  332. return Iterables.map(this.entries(), ([e]) => e);
  333. }
  334. highlander(): [E, string] | undefined {
  335. if (this._root === undefined || this._root.isEmpty()) return undefined;
  336. const entries = this.entries() as IterableIterator<[E, string]>;
  337. let count = 0;
  338. let next: IteratorResult<[E, string]>;
  339. let value: [E, string] | undefined;
  340. while (true) {
  341. next = entries.next();
  342. if (next.done) break;
  343. value = next.value;
  344. count++;
  345. if (count > 1) return undefined;
  346. }
  347. return value;
  348. }
  349. some(predicate: (entry: E) => boolean): boolean {
  350. if (this._root === undefined || this._root.isEmpty()) return false;
  351. return Iterables.some(this.entries(), ([e]) => predicate(e));
  352. }
  353. private *_iterator(node: TernarySearchTreeNode<E> | undefined): IterableIterator<[E, string]> {
  354. if (node !== undefined) {
  355. // left
  356. yield* this._iterator(node.left);
  357. // node
  358. if (node.value) {
  359. yield [node.value, node.key];
  360. }
  361. // mid
  362. yield* this._iterator(node.mid);
  363. // right
  364. yield* this._iterator(node.right);
  365. }
  366. }
  367. }