Skip to content

Optimize Reports view to prevent memory leaks after long usage #61771

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
28 changes: 19 additions & 9 deletions src/hooks/useFastSearchFromOptions.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import deburr from 'lodash/deburr';
import {useMemo} from 'react';
import {useCallback, useEffect, useState} from 'react';
import FastSearch from '@libs/FastSearch';
import {filterUserToInvite, isSearchStringMatch} from '@libs/OptionsListUtils';
import type {Options as OptionsListType, ReportAndPersonalDetailOptions} from '@libs/OptionsListUtils';
import {filterUserToInvite, isSearchStringMatch} from '@libs/OptionsListUtils';
import type {OptionData} from '@libs/ReportUtils';
import StringUtils from '@libs/StringUtils';

type AllOrSelectiveOptions = ReportAndPersonalDetailOptions | OptionsListType;
Expand Down Expand Up @@ -34,8 +35,10 @@ function useFastSearchFromOptions(
options: ReportAndPersonalDetailOptions | OptionsListType,
{includeUserToInvite}: Options = {includeUserToInvite: false},
): (searchInput: string) => AllOrSelectiveOptions {
const findInSearchTree = useMemo(() => {
const fastSearch = FastSearch.createFastSearch([
const [fastSearch, setFastSearch] = useState<ReturnType<typeof FastSearch.createFastSearch<OptionData>> | null>(null);

useEffect(() => {
const newFastSearch = FastSearch.createFastSearch([
{
data: options.personalDetails,
toSearchableString: (option) => {
Expand Down Expand Up @@ -63,8 +66,16 @@ function useFastSearchFromOptions(
},
},
]);
setFastSearch(newFastSearch);

return () => newFastSearch.dispose();
}, [options]);

function search(searchInput: string): AllOrSelectiveOptions {
const findInSearchTree = useCallback(
(searchInput: string): AllOrSelectiveOptions => {
if (!fastSearch) {
return emptyResult;
}
const deburredInput = deburr(searchInput);
const searchWords = deburredInput.split(/\s+/);
const searchWordsSorted = StringUtils.sortStringArrayByLength(searchWords);
Expand Down Expand Up @@ -104,10 +115,9 @@ function useFastSearchFromOptions(
personalDetails,
recentReports,
};
}

return search;
}, [includeUserToInvite, options]);
},
[includeUserToInvite, options, fastSearch],
);

return findInSearchTree;
}
Expand Down
4 changes: 4 additions & 0 deletions src/libs/DynamicArrayBuffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ class DynamicArrayBuffer<T extends TypedArray> {
return this;
}

clear(): void {
this.truncate(0);
}

[Symbol.iterator](): Iterator<number> {
let index = 0;
return {
Expand Down
7 changes: 7 additions & 0 deletions src/libs/FastSearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,16 @@ function createFastSearch<T>(dataSets: Array<SearchableData<T>>) {

return resultsByDataSet.map((set) => Array.from(set));
}
function dispose(): void {
concatenatedNumericList.clear();
occurrenceToIndex.clear();
tree.disposeTree();
listOffsets.length = 0;
}

return {
search,
dispose,
};
}

Expand Down
18 changes: 13 additions & 5 deletions src/libs/SuffixUkkonenTree/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,14 @@ function makeTree(numericSearchValues: DynamicArrayBuffer<Uint8Array>) {
/ \ / \ / \ / \
[0,0,0,0, 0,0,0,0, 0,0,0,0, ................. 0,0,0,0]
*/
const transitionNodes = new Uint32Array(maxNodes * ALPHABET_SIZE);
let transitionNodes = new Uint32Array(maxNodes * ALPHABET_SIZE);

// Storing the range of the original string that each node represents:
const rangeStart = new Uint32Array(maxNodes);
const rangeEnd = new Uint32Array(maxNodes);
let rangeStart = new Uint32Array(maxNodes);
let rangeEnd = new Uint32Array(maxNodes);

const parent = new Uint32Array(maxNodes);
const suffixLink = new Uint32Array(maxNodes);
let parent = new Uint32Array(maxNodes);
let suffixLink = new Uint32Array(maxNodes);

let currentNode = 1;
let currentPosition = 1;
Expand Down Expand Up @@ -144,6 +144,13 @@ function makeTree(numericSearchValues: DynamicArrayBuffer<Uint8Array>) {
processCharacter(c);
}
}
function disposeTree() {
rangeEnd = new Uint32Array(0);
rangeStart = new Uint32Array(0);
transitionNodes = new Uint32Array(0);
parent = new Uint32Array(0);
suffixLink = new Uint32Array(0);
}

/**
* Returns all occurrences of the given (sub)string in the input string.
Expand Down Expand Up @@ -196,6 +203,7 @@ function makeTree(numericSearchValues: DynamicArrayBuffer<Uint8Array>) {
return {
build,
findSubstring,
disposeTree,
};
}

Expand Down
Loading