Skip to content

Commit

Permalink
perf(formula): calculation speed up (#3738)
Browse files Browse the repository at this point in the history
  • Loading branch information
DR-Univer authored Oct 18, 2024
1 parent fb27675 commit cea4571
Show file tree
Hide file tree
Showing 57 changed files with 715 additions and 586 deletions.
3 changes: 2 additions & 1 deletion packages/core/src/shared/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
* @param val The number or string to be judged
* @returns Result
*/
const $blank = /\s/g;
export function isRealNum(val: string | number | boolean) {
if (val === null || val.toString().replace(/\s/g, '') === '') {
if (val === null || val.toString().replace($blank, '') === '') {
return false;
}

Expand Down
30 changes: 8 additions & 22 deletions packages/core/src/shared/r-tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,42 +89,28 @@ export class RTree {
}
}

search(search: IUnitRange): Map<string, IRTreeItem> {
search(search: IUnitRange): IRTreeItem[] {
const { unitId, sheetId: subUnitId, range } = search;
const tree = this._tree.get(unitId)?.get(subUnitId);
if (!tree) {
return new Map();
return [];
}
const result = new Map<string, IRTreeItem>();
tree.search({

return tree.search({
minX: range.startColumn,
minY: range.startRow,
maxX: range.endColumn,
maxY: range.endRow,
}).forEach((item) => {
result.set(item.id, {
unitId,
sheetId: subUnitId,
id: item.id,
range: {
startColumn: item.minX,
startRow: item.minY,
endColumn: item.maxX,
endRow: item.maxY,
},
});
});
return result;
}) as unknown as IRTreeItem[];
}

bulkSearch(searchList: IUnitRange[]): Map<string, IRTreeItem> {
const result = new Map<string, IRTreeItem>();
for (const search of searchList) {
const items = this.search(search);
items.forEach((value, key) => {
result.set(key, value);
});
items.clear();
for (const item of items) {
result.set(item.id, item);
}
}
return result;
}
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/shared/range.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ import { AbsoluteRefType, RANGE_TYPE } from '../sheets/typedef';
import { Rectangle } from './rectangle';

export function moveRangeByOffset(range: IRange, refOffsetX: number, refOffsetY: number, ignoreAbsolute = false): IRange {
if (refOffsetX === 0 && refOffsetY === 0) {
return range;
}

let newRange = { ...range };

const startAbsoluteRefType = newRange.startAbsoluteRefType || AbsoluteRefType.NONE;
Expand Down
42 changes: 21 additions & 21 deletions packages/engine-formula/src/basics/__tests__/regex.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,39 +15,39 @@
*/

import { describe, expect, it } from 'vitest';
import { isReferenceString, REFERENCE_MULTIPLE_RANGE_REGEX, REFERENCE_REGEX_COLUMN, REFERENCE_REGEX_ROW, REFERENCE_SINGLE_RANGE_REGEX, REFERENCE_TABLE_MULTIPLE_COLUMN_REGEX, REFERENCE_TABLE_SINGLE_COLUMN_REGEX } from '../regex';
import { isReferenceString, REFERENCE_TABLE_MULTIPLE_COLUMN_REGEX, REFERENCE_TABLE_SINGLE_COLUMN_REGEX, regexTestColumn, regexTestMultipleRange, regexTestRow, regexTestSingeRange } from '../regex';

describe('Test ref regex', () => {
it('Single range', () => {
expect(new RegExp(REFERENCE_SINGLE_RANGE_REGEX).test('A1')).toBe(true);
expect(new RegExp(REFERENCE_SINGLE_RANGE_REGEX).test('Sheet1!A1')).toBe(true);
expect(new RegExp(REFERENCE_SINGLE_RANGE_REGEX).test('[workbook]Sheet1!A1')).toBe(true);
expect(new RegExp(REFERENCE_SINGLE_RANGE_REGEX).test('[workbook]\'Sheet-1\'!A1')).toBe(true);
expect(new RegExp(REFERENCE_SINGLE_RANGE_REGEX).test('\'[workbook]Sheet1\'!A1')).toBe(true);
expect(regexTestSingeRange('A1')).toBe(true);
expect(regexTestSingeRange('Sheet1!A1')).toBe(true);
expect(regexTestSingeRange('[workbook]Sheet1!A1')).toBe(true);
expect(regexTestSingeRange('[workbook]\'Sheet-1\'!A1')).toBe(true);
expect(regexTestSingeRange('\'[workbook]Sheet1\'!A1')).toBe(true);
});

it('Multiple range', () => {
expect(new RegExp(REFERENCE_MULTIPLE_RANGE_REGEX).test('A1:B10')).toBe(true);
expect(new RegExp(REFERENCE_MULTIPLE_RANGE_REGEX).test('Sheet1!A1:B10')).toBe(true);
expect(new RegExp(REFERENCE_MULTIPLE_RANGE_REGEX).test('[workbook]Sheet1!A1:B10')).toBe(true);
expect(new RegExp(REFERENCE_MULTIPLE_RANGE_REGEX).test('[workbook]\'Sheet-1\'!A1:B10')).toBe(true);
expect(new RegExp(REFERENCE_MULTIPLE_RANGE_REGEX).test('\'[workbook]Sheet1\'!A1:B10')).toBe(true);
expect(regexTestMultipleRange('A1:B10')).toBe(true);
expect(regexTestMultipleRange('Sheet1!A1:B10')).toBe(true);
expect(regexTestMultipleRange('[workbook]Sheet1!A1:B10')).toBe(true);
expect(regexTestMultipleRange('[workbook]\'Sheet-1\'!A1:B10')).toBe(true);
expect(regexTestMultipleRange('\'[workbook]Sheet1\'!A1:B10')).toBe(true);
});

it('Row range', () => {
expect(new RegExp(REFERENCE_REGEX_ROW).test('1:10')).toBe(true);
expect(new RegExp(REFERENCE_REGEX_ROW).test('Sheet1!1:10')).toBe(true);
expect(new RegExp(REFERENCE_REGEX_ROW).test('[workbook]Sheet1!1:10')).toBe(true);
expect(new RegExp(REFERENCE_REGEX_ROW).test('[workbook]\'Sheet-1\'!1:10')).toBe(true);
expect(new RegExp(REFERENCE_REGEX_ROW).test('\'[workbook]Sheet1\'!1:10')).toBe(true);
expect(regexTestRow('1:10')).toBe(true);
expect(regexTestRow('Sheet1!1:10')).toBe(true);
expect(regexTestRow('[workbook]Sheet1!1:10')).toBe(true);
expect(regexTestRow('[workbook]\'Sheet-1\'!1:10')).toBe(true);
expect(regexTestRow('\'[workbook]Sheet1\'!1:10')).toBe(true);
});

it('Column range', () => {
expect(new RegExp(REFERENCE_REGEX_COLUMN).test('A:B')).toBe(true);
expect(new RegExp(REFERENCE_REGEX_COLUMN).test('Sheet1!A:B')).toBe(true);
expect(new RegExp(REFERENCE_REGEX_COLUMN).test('[workbook]Sheet1!A:B')).toBe(true);
expect(new RegExp(REFERENCE_REGEX_COLUMN).test('[workbook]\'Sheet-1\'!A:B')).toBe(true);
expect(new RegExp(REFERENCE_REGEX_COLUMN).test('\'[workbook]Sheet1\'!A:B')).toBe(true);
expect(regexTestColumn('A:B')).toBe(true);
expect(regexTestColumn('Sheet1!A:B')).toBe(true);
expect(regexTestColumn('[workbook]Sheet1!A:B')).toBe(true);
expect(regexTestColumn('[workbook]\'Sheet-1\'!A:B')).toBe(true);
expect(regexTestColumn('\'[workbook]Sheet1\'!A:B')).toBe(true);
});

it('Table single range', () => {
Expand Down
9 changes: 6 additions & 3 deletions packages/engine-formula/src/basics/cache-lru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ import { hashAlgorithm, LRUMap } from '@univerjs/core';
// export const CACHE_FORMULA_AST = new LRUMap<string, AstRootNode>(100000);

export class FormulaAstLRU<T> {
private _cache: LRUMap<number, T>;
private _cache: LRUMap<string, T>;

constructor(cacheCount: number) {
this._cache = new LRUMap<number, T>(cacheCount);
this._cache = new LRUMap<string, T>(cacheCount);
}

set(formulaString: string, node: T) {
Expand All @@ -40,6 +40,9 @@ export class FormulaAstLRU<T> {
}

private _hash(formulaString: string) {
return hashAlgorithm(formulaString);
if (formulaString.length <= 64) {
return formulaString;
}
return hashAlgorithm(formulaString).toString();
}
}
8 changes: 5 additions & 3 deletions packages/engine-formula/src/basics/object-class-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@
* limitations under the License.
*/

import { Disposable } from '@univerjs/core';

export class ObjectClassType extends Disposable {
export class ObjectClassType {
pattern: string = '';

dispose() {

}

getPattern() {
return this.pattern;
}
Expand Down
70 changes: 64 additions & 6 deletions packages/engine-formula/src/basics/regex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import { prefixToken, suffixToken } from './token';

export const UNIT_NAME_REGEX = '\\[([^\\[\\]\\/?:"<>|*\\\\]+)\\]'; // '[Book-1.xlsx]Sheet1'!$A$4 gets [Book-1.xlsx] as unitId

export const UNIT_NAME_REGEX_PRECOMPILING = new RegExp(UNIT_NAME_REGEX);

export const SHEET_NAME_REGEX = '((?![\\[\\]\\/?*\\\\]).)*!';

export const ABSOLUTE_SYMBOL = '$';
Expand All @@ -38,16 +40,28 @@ export const SIMPLE_SINGLE_RANGE_REGEX = `\\${ABSOLUTE_SYMBOL}?${COLUMN_REGEX}\\

export const REFERENCE_MULTIPLE_RANGE_REGEX = `^(${prefixToken.AT})?${UNIT_NAME_SHEET_NAME_REGEX}${SIMPLE_SINGLE_RANGE_REGEX}${RANGE_SYMBOL}${SIMPLE_SINGLE_RANGE_REGEX}$`;

export const REFERENCE_MULTIPLE_RANGE_REGEX_PRECOMPILING = new RegExp(REFERENCE_MULTIPLE_RANGE_REGEX);

export const REFERENCE_SINGLE_RANGE_REGEX = `^${UNIT_NAME_SHEET_NAME_REGEX}\\s*?${SIMPLE_SINGLE_RANGE_REGEX}(${suffixToken.POUND})?$`;

export const REFERENCE_SINGLE_RANGE_REGEX_PRECOMPILING = new RegExp(REFERENCE_SINGLE_RANGE_REGEX);

export const REFERENCE_REGEX_ROW = `^${UNIT_NAME_SHEET_NAME_REGEX}\\${ABSOLUTE_SYMBOL}?${ROW_REGEX}${RANGE_SYMBOL}\\${ABSOLUTE_SYMBOL}?${ROW_REGEX}$`;

export const REFERENCE_REGEX_ROW_PRECOMPILING = new RegExp(REFERENCE_REGEX_ROW);

export const REFERENCE_REGEX_COLUMN = `^${UNIT_NAME_SHEET_NAME_REGEX}\\${ABSOLUTE_SYMBOL}?${COLUMN_REGEX}${RANGE_SYMBOL}\\${ABSOLUTE_SYMBOL}?${COLUMN_REGEX}$`;

export const REFERENCE_REGEX_COLUMN_PRECOMPILING = new RegExp(REFERENCE_REGEX_COLUMN);

export const REFERENCE_REGEX_SINGLE_ROW = `^${UNIT_NAME_SHEET_NAME_REGEX}\\s*?\\${ABSOLUTE_SYMBOL}?${ROW_REGEX}$`;

export const REFERENCE_REGEX_SINGLE_ROW_PRECOMPILING = new RegExp(REFERENCE_REGEX_SINGLE_ROW);

export const REFERENCE_REGEX_SINGLE_COLUMN = `^${UNIT_NAME_SHEET_NAME_REGEX}\\s*?\\${ABSOLUTE_SYMBOL}?${COLUMN_REGEX}$`;

export const REFERENCE_REGEX_SINGLE_COLUMN_PRECOMPILING = new RegExp(REFERENCE_REGEX_SINGLE_COLUMN);

const TABLE_NAME_REGEX = '((?![~!@#$%^&*()_+<>?:,./;’,。、‘:“《》?~!@#¥%……()【】\\[\\]\\/\\\\]).)+';

const TABLE_TITLE_REGEX = '\\[#.+\\]\\s*?,\\s*?';
Expand All @@ -62,15 +76,59 @@ export const REFERENCE_TABLE_SINGLE_COLUMN_REGEX = `^(${UNIT_NAME_REGEX})?${TABL

export const REFERENCE_TABLE_MULTIPLE_COLUMN_REGEX = `^(${UNIT_NAME_REGEX})?${TABLE_NAME_REGEX}(\\[${TABLE_MULTIPLE_COLUMN_REGEX}\\])?$|^${TABLE_NAME_REGEX}(\\[${TABLE_TITLE_REGEX}${TABLE_MULTIPLE_COLUMN_REGEX}\\])?$`; // =Table1[[#Title],[Column1]:[Column2]] | =Table1[[Column1]:[Column2]]

export const $SUPER_TABLE_COLUMN_REGEX = '[.*?]';
export const SUPER_TABLE_COLUMN_REGEX = '[.*?]';

export const $ARRAY_VALUE_REGEX = '{.*?}';
export const SUPER_TABLE_COLUMN_REGEX_PRECOMPILING = new RegExp(SUPER_TABLE_COLUMN_REGEX, 'g');

export const ARRAY_VALUE_REGEX = '{.*?}';

export const ARRAY_VALUE_REGEX_PRECOMPILING = new RegExp(ARRAY_VALUE_REGEX, 'g');

export function regexTestSingeRange(token: string): boolean {
REFERENCE_SINGLE_RANGE_REGEX_PRECOMPILING.lastIndex = 0;
return REFERENCE_SINGLE_RANGE_REGEX_PRECOMPILING.test(token);
}

export function regexTestMultipleRange(token: string): boolean {
REFERENCE_MULTIPLE_RANGE_REGEX_PRECOMPILING.lastIndex = 0;
return REFERENCE_MULTIPLE_RANGE_REGEX_PRECOMPILING.test(token);
}

export function regexTestRow(token: string): boolean {
REFERENCE_REGEX_ROW_PRECOMPILING.lastIndex = 0;
return REFERENCE_REGEX_ROW_PRECOMPILING.test(token);
}

export function regexTestColumn(token: string): boolean {
REFERENCE_REGEX_COLUMN_PRECOMPILING.lastIndex = 0;
return REFERENCE_REGEX_COLUMN_PRECOMPILING.test(token);
}

export function regexTestSingleRow(token: string): boolean {
REFERENCE_REGEX_SINGLE_ROW_PRECOMPILING.lastIndex = 0;
return REFERENCE_REGEX_SINGLE_ROW_PRECOMPILING.test(token);
}

export function regexTestSingleColumn(token: string): boolean {
REFERENCE_REGEX_SINGLE_COLUMN_PRECOMPILING.lastIndex = 0;
return REFERENCE_REGEX_SINGLE_COLUMN_PRECOMPILING.test(token);
}

export function regexTestSuperTableColumn(token: string): boolean {
SUPER_TABLE_COLUMN_REGEX_PRECOMPILING.lastIndex = 0;
return SUPER_TABLE_COLUMN_REGEX_PRECOMPILING.test(token);
}

export function regexTestArrayValue(token: string): boolean {
ARRAY_VALUE_REGEX_PRECOMPILING.lastIndex = 0;
return ARRAY_VALUE_REGEX_PRECOMPILING.test(token);
}

export function isReferenceString(refString: string) {
return (
new RegExp(REFERENCE_SINGLE_RANGE_REGEX).test(refString) ||
new RegExp(REFERENCE_MULTIPLE_RANGE_REGEX).test(refString) ||
new RegExp(REFERENCE_REGEX_ROW).test(refString) ||
new RegExp(REFERENCE_REGEX_COLUMN).test(refString)
regexTestSingeRange(refString) ||
regexTestMultipleRange(refString) ||
regexTestRow(refString) ||
regexTestColumn(refString)
);
}
Loading

0 comments on commit cea4571

Please sign in to comment.