Skip to content

Commit

Permalink
fix: Improve performance (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
Trott committed Sep 29, 2021
1 parent 6149ec8 commit f50a175
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 50 deletions.
5 changes: 2 additions & 3 deletions .eslintrc.json
@@ -1,7 +1,6 @@
{
"ecmaFeatures": {
"modules": true,
"experimentalObjectRestSpread": true
"parserOptions": {
"ecmaVersion": 2020
},

"env": {
Expand Down
23 changes: 23 additions & 0 deletions benchmark.js
@@ -0,0 +1,23 @@
'use strict';

if (!global.BigInt) {
console.log('Benchmark requirest 10.x or newer.');
process.exit(0);
}

var assert = require('assert');
var isGlob = require('./');

function runBenchmark(length) {
var start = process.hrtime.bigint();
['(', '[', '{', '\\'].forEach(function(char) {
isGlob(char.repeat(length));
isGlob(char.repeat(length), {strict: false});
});
return process.hrtime.bigint() - start;
}

var baseline = runBenchmark(1e6);
console.log('Benchmark took ' + baseline / global.BigInt(1000000) + ' milliseconds.');

assert.ok(baseline < global.BigInt(1000000000), 'Benchmark goal is less than one second');
72 changes: 42 additions & 30 deletions index.js
Expand Up @@ -7,11 +7,16 @@

var isExtglob = require('is-extglob');
var chars = { '{': '}', '(': ')', '[': ']'};
var strictCheck = function (str) {
var strictCheck = function(str) {
if (str[0] === '!') {
return true;
}
var index = 0;
var pipeIndex = -2;
var closeSquareIndex = -2;
var closeCurlyIndex = -2;
var closeParenIndex = -2;
var backSlashIndex = -2;
while (index < str.length) {
if (str[index] === '*') {
return true;
Expand All @@ -21,51 +26,58 @@ var strictCheck = function (str) {
return true;
}

if (str[index] === '[' && str[index + 1] !== ']') {
var closeIndex = str.indexOf(']', index);
if (closeIndex > index) {
var slashIndex = str.indexOf('\\', index);
if (slashIndex === -1 || slashIndex > closeIndex) {
if (closeSquareIndex !== -1 && str[index] === '[' && str[index + 1] !== ']') {
if (closeSquareIndex < index) {
closeSquareIndex = str.indexOf(']', index);
}
if (closeSquareIndex > index) {
if (backSlashIndex === -1 || backSlashIndex > closeSquareIndex) {
return true;
}
backSlashIndex = str.indexOf('\\', index);
if (backSlashIndex === -1 || backSlashIndex > closeSquareIndex) {
return true;
}
}
}

if (str[index] === '{' && str[index + 1] !== '}') {
closeIndex = str.indexOf('}', index);
if (closeIndex > index) {
slashIndex = str.indexOf('\\', index);
if (slashIndex === -1 || slashIndex > closeIndex) {
if (closeCurlyIndex !== -1 && str[index] === '{' && str[index + 1] !== '}') {
closeCurlyIndex = str.indexOf('}', index);
if (closeCurlyIndex > index) {
backSlashIndex = str.indexOf('\\', index);
if (backSlashIndex === -1 || backSlashIndex > closeCurlyIndex) {
return true;
}
}
}

if (str[index] === '(' && str[index + 1] === '?' && /[:!=]/.test(str[index + 2]) && str[index + 3] !== ')') {
closeIndex = str.indexOf(')', index);
if (closeIndex > index) {
slashIndex = str.indexOf('\\', index);
if (slashIndex === -1 || slashIndex > closeIndex) {
if (closeParenIndex !== -1 && str[index] === '(' && str[index + 1] === '?' && /[:!=]/.test(str[index + 2]) && str[index + 3] !== ')') {
closeParenIndex = str.indexOf(')', index);
if (closeParenIndex > index) {
backSlashIndex = str.indexOf('\\', index);
if (backSlashIndex === -1 || backSlashIndex > closeParenIndex) {
return true;
}
}
}

if (str[index] === '(' && str[index + 1] !== '|') {
var pipeIndex = str.indexOf('|', index);
if (pipeIndex > index && str[pipeIndex + 1] !== ')') {
closeIndex = str.indexOf(')', pipeIndex);
if (closeIndex > pipeIndex) {
slashIndex = str.indexOf('\\', pipeIndex);
if (slashIndex === -1 || slashIndex > closeIndex) {
return true;
if (pipeIndex !== -1 && str[index] === '(' && str[index + 1] !== '|') {
if (pipeIndex < index) {
pipeIndex = str.indexOf('|', index);
}
if (pipeIndex !== -1 && str[pipeIndex + 1] !== ')') {
closeParenIndex = str.indexOf(')', pipeIndex);
if (closeParenIndex > pipeIndex) {
backSlashIndex = str.indexOf('\\', pipeIndex);
if (backSlashIndex === -1 || backSlashIndex > closeParenIndex) {
return true;
}
}
}
}

if (str[index] === '\\') {
var open = str[index+1];
var open = str[index + 1];
index += 2;
var close = chars[open];

Expand All @@ -84,9 +96,9 @@ var strictCheck = function (str) {
}
}
return false;
}
};

var relaxedCheck = function (str) {
var relaxedCheck = function(str) {
if (str[0] === '!') {
return true;
}
Expand All @@ -97,7 +109,7 @@ var relaxedCheck = function (str) {
}

if (str[index] === '\\') {
var open = str[index+1];
var open = str[index + 1];
index += 2;
var close = chars[open];

Expand All @@ -116,7 +128,7 @@ var relaxedCheck = function (str) {
}
}
return false;
}
};

module.exports = function isGlob(str, options) {
if (typeof str !== 'string' || str === '') {
Expand All @@ -135,4 +147,4 @@ module.exports = function isGlob(str, options) {
}

return check(str);
}
};
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -22,7 +22,7 @@
"node": ">=0.10.0"
},
"scripts": {
"test": "mocha"
"test": "mocha && node benchmark.js"
},
"dependencies": {
"is-extglob": "^2.1.1"
Expand Down
16 changes: 0 additions & 16 deletions test.js
Expand Up @@ -7,7 +7,6 @@

'use strict';

require('mocha');
var assert = require('assert');
var isGlob = require('./');

Expand Down Expand Up @@ -303,20 +302,5 @@ describe('isGlob', function() {
assert(isGlob('\\*(abc|xyz)/*(abc|xyz)'));
assert(isGlob('\\+(abc|xyz)/+(abc|xyz)'));
});

it('should be performant and not subject to ReDoS/exponential backtracking', function() {
if (!String.prototype.repeat) {
return;
}
// These will time out if the algorithm is inefficient.
isGlob('('.repeat(1e5));
isGlob('('.repeat(1e5), {strict: false});
isGlob('['.repeat(1e5));
isGlob('['.repeat(1e5), {strict: false});
isGlob('{'.repeat(1e5));
isGlob('{'.repeat(1e5), {strict: false});
isGlob('\\'.repeat(1e5));
isGlob('\\'.repeat(1e5), {strict: false});
});
});
});

0 comments on commit f50a175

Please sign in to comment.