Use binary search to improve getNewlinesBefore/After performance
This commit is contained in:
parent
76e3c5227b
commit
ed227a6c8a
@ -1,18 +1,3 @@
|
||||
/**
|
||||
* Returns `i`th number from `base`, continuing from 0 when `max` is reached.
|
||||
* Useful for shifting `for` loop by a fixed number but going over all items.
|
||||
*/
|
||||
|
||||
function getLookupIndex(i: number, base: number, max: number): number {
|
||||
i += base;
|
||||
|
||||
if (i >= max) {
|
||||
i -= max;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whitespace around tokens.
|
||||
*/
|
||||
@ -21,16 +6,6 @@ export default class Whitespace {
|
||||
constructor(tokens) {
|
||||
this.tokens = tokens;
|
||||
this.used = {};
|
||||
|
||||
// Profiling this code shows that while generator passes over it, indexes
|
||||
// returned by `getNewlinesBefore` and `getNewlinesAfter` are always increasing.
|
||||
|
||||
// We use this implementation detail for an optimization: instead of always
|
||||
// starting to look from `this.tokens[0]`, we will start `for` loops from the
|
||||
// previous successful match. We will enumerate all tokens—but the common
|
||||
// case will be much faster.
|
||||
|
||||
this._lastFoundIndex = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -42,19 +17,11 @@ export default class Whitespace {
|
||||
let endToken;
|
||||
let tokens = this.tokens;
|
||||
|
||||
for (let j = 0; j < tokens.length; j++) {
|
||||
// optimize for forward traversal by shifting for loop index
|
||||
let i = getLookupIndex(j, this._lastFoundIndex, this.tokens.length);
|
||||
let token = tokens[i];
|
||||
|
||||
// this is the token this node starts with
|
||||
if (node.start === token.start) {
|
||||
startToken = tokens[i - 1];
|
||||
endToken = token;
|
||||
|
||||
this._lastFoundIndex = i;
|
||||
break;
|
||||
}
|
||||
let index = this.findToken(token => token.start - node.start, 0, tokens.length);
|
||||
if (typeof index === "number") {
|
||||
while (index && node.start === tokens[index - 1].start) --index;
|
||||
startToken = tokens[index - 1];
|
||||
endToken = tokens[index];
|
||||
}
|
||||
|
||||
return this.getNewlinesBetween(startToken, endToken);
|
||||
@ -69,20 +36,12 @@ export default class Whitespace {
|
||||
let endToken;
|
||||
let tokens = this.tokens;
|
||||
|
||||
for (let j = 0; j < tokens.length; j++) {
|
||||
// optimize for forward traversal by shifting for loop index
|
||||
let i = getLookupIndex(j, this._lastFoundIndex, this.tokens.length);
|
||||
let token = tokens[i];
|
||||
|
||||
// this is the token this node ends with
|
||||
if (node.end === token.end) {
|
||||
startToken = token;
|
||||
endToken = tokens[i + 1];
|
||||
if (endToken.type.label === ",") endToken = tokens[i + 2];
|
||||
|
||||
this._lastFoundIndex = i;
|
||||
break;
|
||||
}
|
||||
let index = this.findToken(token => token.end - node.end, 0, tokens.length);
|
||||
if (typeof index === "number") {
|
||||
while (index && node.end === tokens[index - 1].end) --index;
|
||||
startToken = tokens[index];
|
||||
endToken = tokens[index + 1];
|
||||
if (endToken.type.label === ",") endToken = tokens[index + 2];
|
||||
}
|
||||
|
||||
if (endToken && endToken.type.label === "eof") {
|
||||
@ -98,6 +57,18 @@ export default class Whitespace {
|
||||
}
|
||||
}
|
||||
|
||||
findToken(test, start, end) {
|
||||
const middle = (start + end) >>> 1;
|
||||
const match = test(this.tokens[middle]);
|
||||
if (match < 0 && end > middle) {
|
||||
return this.findToken(test, middle + 1, end);
|
||||
} else if (match > 0 && start < middle) {
|
||||
return this.findToken(test, start, middle);
|
||||
} else if (match === 0) {
|
||||
return middle;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Count all the newlines between two tokens.
|
||||
*/
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user