Skip to content

Commit 8a47ae7

Browse files
committed
col-cache: optimize for performance
1 parent a682091 commit 8a47ae7

File tree

1 file changed

+82
-22
lines changed

1 file changed

+82
-22
lines changed

lib/utils/col-cache.js

Lines changed: 82 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const colCache = {
2929
'Y',
3030
'Z',
3131
],
32+
_l2nFill: 0,
3233
_l2n: {},
3334
_n2l: [],
3435
_level(n) {
@@ -48,18 +49,20 @@ const colCache = {
4849
let l3;
4950
let n = 1;
5051
if (level >= 4) {
51-
throw new Error(`Out of bounds. Excel supports columns from 1 to 16384`);
52+
throw new Error('Out of bounds. Excel supports columns from 1 to 16384');
5253
}
53-
if (level >= 1) {
54+
if (this._l2nFill < 1 && level >= 1) {
5455
while (n <= 26) {
5556
c = this._dictionary[n - 1];
5657
this._n2l[n] = c;
5758
this._l2n[c] = n;
5859
n++;
5960
}
61+
this._l2nFill = 1;
6062
}
61-
if (level >= 2) {
62-
while (n <= 26 + 26 * 26) {
63+
if (this._l2nFill < 2 && level >= 2) {
64+
n = 27;
65+
while (n <= 26 + (26 * 26)) {
6366
v = n - (26 + 1);
6467
l1 = v % 26;
6568
l2 = Math.floor(v / 26);
@@ -68,10 +71,12 @@ const colCache = {
6871
this._l2n[c] = n;
6972
n++;
7073
}
74+
this._l2nFill = 2;
7175
}
72-
if (level >= 3) {
76+
if (this._l2nFill < 3 && level >= 3) {
77+
n = 26 + (26 * 26) + 1;
7378
while (n <= 16384) {
74-
v = n - (26 * 26 + 26 + 1);
79+
v = n - ((26 * 26) + 26 + 1);
7580
l1 = v % 26;
7681
l2 = Math.floor(v / 26) % 26;
7782
l3 = Math.floor(v / (26 * 26));
@@ -80,6 +85,7 @@ const colCache = {
8085
this._l2n[c] = n;
8186
n++;
8287
}
88+
this._l2nFill = 3;
8389
}
8490
},
8591
l2n(l) {
@@ -107,41 +113,95 @@ const colCache = {
107113

108114
// check if value looks like an address
109115
validateAddress(value) {
110-
if (!value.match(/^[A-Z]+\d+$/)) {
116+
// if (!value.match(/^[A-Z]+\d+$/))
117+
// throw new Error(`Invalid Address: ${value}`);
118+
let isValid = true;
119+
let hasRow = false;
120+
let hasCol = false;
121+
// 65 = 'A'.charCodeAt(0)
122+
// 90 = 'Z'.charCodeAt(0)
123+
// 48 = '0'.charCodeAt(0)
124+
// 57 = '9'.charCodeAt(0)
125+
for (let i = 0, char; i < value.length; i++) {
126+
if (!isValid) {
127+
break;
128+
}
129+
char = value.charCodeAt(i);
130+
if (!hasRow) {
131+
if (char >= 65 && char <= 90) {
132+
hasCol = true;
133+
} else if (char >= 48 && char <= 57) {
134+
if (!hasCol) {
135+
isValid = false;
136+
break;
137+
}
138+
hasRow = true;
139+
} else {
140+
isValid = false;
141+
break;
142+
}
143+
} else if (!(char >= 48 && char <= 57)) {
144+
isValid = false;
145+
break;
146+
}
147+
}
148+
if (!isValid || !hasRow || !hasCol) {
111149
throw new Error(`Invalid Address: ${value}`);
112150
}
113151
return true;
114152
},
115153

116154
// convert address string into structure
117155
decodeAddress(value) {
118-
const addr = this._hash[value];
156+
const addr = value.length < 5 && this._hash[value];
119157
if (addr) {
120158
return addr;
121159
}
122-
const matchCol = value.match(/[A-Z]+/);
123-
let col;
124-
let colNumber;
125-
if (matchCol) {
126-
col = matchCol[0];
127-
colNumber = this.l2n(col);
160+
let hasCol = false;
161+
let col = '';
162+
let colNumber = 0;
163+
let hasRow = false;
164+
let row = '';
165+
let rowNumber = 0;
166+
for (let i = 0, char; i < value.length; i++) {
167+
char = value.charCodeAt(i);
168+
// col should before row
169+
if (!hasRow && char >= 65 && char <= 90) {
170+
// 65 = 'A'.charCodeAt(0)
171+
// 90 = 'Z'.charCodeAt(0)
172+
hasCol = true;
173+
col += value[i];
174+
// colNumber starts from 1
175+
colNumber = (colNumber * 26) + char - 64;
176+
} else if (char >= 48 && char <= 57) {
177+
// 48 = '0'.charCodeAt(0)
178+
// 57 = '9'.charCodeAt(0)
179+
hasRow = true;
180+
row += value[i];
181+
// rowNumber starts from 0
182+
rowNumber = (rowNumber * 10) + char - 48;
183+
} else if (hasRow && hasCol && char !== 36) {
184+
// 36 = '$'.charCodeAt(0)
185+
break;
186+
}
187+
}
188+
if (!hasCol) {
189+
colNumber = undefined;
190+
} else if (colNumber > 16384) {
191+
throw new Error(`Out of bounds. Invalid column letter: ${col}`);
128192
}
129-
const matchRow = value.match(/\d+/);
130-
let row;
131-
let rowNumber;
132-
if (matchRow) {
133-
row = matchRow[0];
134-
rowNumber = parseInt(row, 10);
193+
if (!hasRow) {
194+
rowNumber = undefined;
135195
}
136196

137197
// in case $row$col
138-
value = (col || '') + (row || '');
198+
value = col + row;
139199

140200
const address = {
141201
address: value,
142202
col: colNumber,
143203
row: rowNumber,
144-
$col$row: `$${col || ''}$${row || ''}`,
204+
$col$row: `$${col}$${row}`,
145205
};
146206

147207
// mem fix - cache only the tl 100x100 square

0 commit comments

Comments
 (0)