Files
react-native-devtools-frontend/front_end/core/common/Trie.ts
T
Tim van der Lippe 445f84c23e Migrate to private class fields in core/common
R=szuend@chromium.org

Bug: 1222126
Change-Id: I5622067436b438937247fb67b3e5f9f8268989dd
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/3207875
Commit-Queue: Tim van der Lippe <tvanderlippe@chromium.org>
Auto-Submit: Tim van der Lippe <tvanderlippe@chromium.org>
Reviewed-by: Simon Zünd <szuend@chromium.org>
2021-10-08 14:36:54 +00:00

121 lines
2.9 KiB
TypeScript

// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
export class Trie {
#size!: number;
#root: number;
#edges!: {
[x: string]: number,
}[];
#isWord!: boolean[];
#wordsInSubtree!: number[];
#freeNodes!: number[];
constructor() {
this.#root = 0;
this.clear();
}
add(word: string): void {
let node: number = this.#root;
++this.#wordsInSubtree[this.#root];
for (let i = 0; i < word.length; ++i) {
const edge = word[i];
let next: number = this.#edges[node][edge];
if (!next) {
if (this.#freeNodes.length) {
next = (this.#freeNodes.pop() as number);
} else {
next = this.#size++;
this.#isWord.push(false);
this.#wordsInSubtree.push(0);
this.#edges.push(Object.create(null));
}
this.#edges[node][edge] = next;
}
++this.#wordsInSubtree[next];
node = next;
}
this.#isWord[node] = true;
}
remove(word: string): boolean {
if (!this.has(word)) {
return false;
}
let node: number = this.#root;
--this.#wordsInSubtree[this.#root];
for (let i = 0; i < word.length; ++i) {
const edge = word[i];
const next = this.#edges[node][edge];
if (!--this.#wordsInSubtree[next]) {
delete this.#edges[node][edge];
this.#freeNodes.push(next);
}
node = next;
}
this.#isWord[node] = false;
return true;
}
has(word: string): boolean {
let node: number = this.#root;
for (let i = 0; i < word.length; ++i) {
node = this.#edges[node][word[i]];
if (!node) {
return false;
}
}
return this.#isWord[node];
}
words(prefix?: string): string[] {
prefix = prefix || '';
let node: number = this.#root;
for (let i = 0; i < prefix.length; ++i) {
node = this.#edges[node][prefix[i]];
if (!node) {
return [];
}
}
const results: string[] = [];
this.dfs(node, prefix, results);
return results;
}
private dfs(node: number, prefix: string, results: string[]): void {
if (this.#isWord[node]) {
results.push(prefix);
}
const edges = this.#edges[node];
for (const edge in edges) {
this.dfs(edges[edge], prefix + edge, results);
}
}
longestPrefix(word: string, fullWordOnly: boolean): string {
let node: number = this.#root;
let wordIndex = 0;
for (let i = 0; i < word.length; ++i) {
node = this.#edges[node][word[i]];
if (!node) {
break;
}
if (!fullWordOnly || this.#isWord[node]) {
wordIndex = i + 1;
}
}
return word.substring(0, wordIndex);
}
clear(): void {
this.#size = 1;
this.#root = 0;
this.#edges = [Object.create(null)];
this.#isWord = [false];
this.#wordsInSubtree = [0];
this.#freeNodes = [];
}
}