Created
August 3, 2021 20:51
-
-
Save isaacs/3234a28dc76f0a4a43b227e49a0f8b43 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env node | |
const re = /\/+$/ | |
const batchStrings = [ | |
new Array(Math.pow(2, 20) + 1).join('/'), | |
new Array(Math.pow(2, 18) + 1).join('/'), | |
new Array(Math.pow(2, 17) + 1).join('/'), | |
new Array(Math.pow(2, 16) + 1).join('/'), | |
new Array(Math.pow(2, 15) + 1).join('/'), | |
new Array(Math.pow(2, 14) + 1).join('/'), | |
new Array(Math.pow(2, 13) + 1).join('/'), | |
new Array(Math.pow(2, 12) + 1).join('/'), | |
new Array(Math.pow(2, 11) + 1).join('/'), | |
new Array(Math.pow(2, 10) + 1).join('/'), | |
new Array(Math.pow(2, 9) + 1).join('/'), | |
new Array(Math.pow(2, 8) + 1).join('/'), | |
new Array(Math.pow(2, 7) + 1).join('/'), | |
new Array(Math.pow(2, 6) + 1).join('/'), | |
new Array(Math.pow(2, 5) + 1).join('/'), | |
new Array(Math.pow(2, 4) + 1).join('/'), | |
new Array(Math.pow(2, 3) + 1).join('/'), | |
new Array(Math.pow(2, 2) + 1).join('/'), | |
new Array(Math.pow(2, 1) + 1).join('/'), | |
'/', | |
] | |
const short = '/a/b/c/' | |
const medium = short + (new Array(1e3)).join('/') | |
const long = short + (new Array(1e6)).join('/') | |
exports.compare = { | |
redos: function() { | |
return long.replace(re, '') | |
}, | |
// endsWith_pow20: function() { | |
// let stripped = long | |
// for (const str of batchStrings) { | |
// while (stripped.endsWith(str)) | |
// stripped = stripped.slice(0, -1 * str.length) | |
// } | |
// return stripped | |
// }, | |
endsWithManual_pow20: function() { | |
let stripped = long | |
for (const str of batchStrings) { | |
while (stripped.slice(-1 * str.length) === str) | |
stripped = stripped.slice(0, -1 * str.length) | |
} | |
return stripped | |
}, | |
endsWithManual_pow20_oneSlice: function() { | |
let i = long.length | |
for (const str of batchStrings) { | |
while (i > str.length && long.substr(i - str.length, str.length) === str) | |
i -= str.length | |
} | |
return long.substr(0, i) | |
}, | |
//endsWith_pow10: function() { | |
// let stripped = long | |
// for (const str of batchStrings.slice(-11)) { | |
// while (stripped.endsWith(str)) | |
// stripped = stripped.slice(0, -1 * str.length) | |
// } | |
// return stripped | |
//}, | |
// endsWith_pow5: function() { | |
// let stripped = long | |
// for (const str of batchStrings.slice(-6)) { | |
// while (stripped.endsWith(str)) | |
// stripped = stripped.slice(0, -1 * str.length) | |
// } | |
// return stripped | |
// }, | |
// endsWith_pow2: function() { | |
// let stripped = long | |
// for (const str of batchStrings.slice(-3)) { | |
// while (stripped.endsWith(str)) | |
// stripped = stripped.slice(0, -1 * str.length) | |
// } | |
// return stripped | |
// }, | |
// endsWith_pow1: function() { | |
// let stripped = long | |
// for (const str of batchStrings.slice(-2)) { | |
// while (stripped.endsWith(str)) | |
// stripped = stripped.slice(0, -1 * str.length) | |
// } | |
// return stripped | |
// }, | |
//endsWith_single: function() { | |
// let stripped = long | |
// const str = '/' | |
// while (stripped.endsWith(str)) | |
// stripped = stripped.slice(0, -1 * str.length) | |
// return stripped | |
//}, | |
checkTail: function() { | |
let stripped = long | |
while (stripped.charAt(stripped.length - 1) === '/') | |
stripped = stripped.slice(0, -1) | |
return stripped | |
}, | |
checkTail_oneSlice: function() { | |
for (let i = long.length - 1; i >= 0; i--) { | |
if (long.charAt(i) === '/') | |
continue | |
return long.slice(0, i + 1) | |
} | |
return '' | |
}, | |
} | |
const assert = require('assert') | |
for (const [name, fn] of Object.entries(exports.compare)) { | |
assert.equal(fn(), '/a/b/c', name) | |
console.error('ok', name) | |
} | |
require('../').runMain() | |
/* | |
$ node examples/redos-slash-ending.js | |
ok redos | |
ok endsWithManual_pow20 | |
ok endsWithManual_pow20_oneSlice | |
ok checkTail | |
ok checkTail_oneSlice | |
benchmarking /Users/isaacs/dev/js/bench/examples/redos-slash-ending.js | |
Please be patient. | |
{ | |
node: '16.5.0', | |
v8: '9.1.269.38-node.20', | |
uv: '1.41.0', | |
zlib: '1.2.11', | |
brotli: '1.0.9', | |
ares: '1.17.1', | |
modules: '93', | |
nghttp2: '1.42.0', | |
napi: '8', | |
llhttp: '6.0.2', | |
openssl: '1.1.1k+quic', | |
cldr: '39.0', | |
icu: '69.1', | |
tz: '2021a', | |
unicode: '13.0', | |
ngtcp2: '0.1.0-DEV', | |
nghttp3: '0.1.0-DEV' | |
} | |
Scores: (bigger is better) | |
endsWithManual_pow20_oneSlice | |
Raw: | |
> 19.66568338249754 | |
> 18.962075848303392 | |
> 19.627085377821395 | |
> 19.743336623889437 | |
Average (mean) 19.499545308127942 | |
endsWithManual_pow20 | |
Raw: | |
> 19.607843137254903 | |
> 18.645731108930324 | |
> 19.607843137254903 | |
> 19.64636542239686 | |
Average (mean) 19.376945701459245 | |
redos | |
Raw: | |
> 1.408450704225352 | |
> 1.3280212483399734 | |
> 1.4134275618374559 | |
> 1.4194464158978 | |
Average (mean) 1.3923364825751454 | |
checkTail_oneSlice | |
Raw: | |
> 0.47058823529411764 | |
> 0.4570383912248629 | |
> 0.4522840343735866 | |
> 0.44424700133274103 | |
Average (mean) 0.45603941555632704 | |
checkTail | |
Raw: | |
> 0.09706853038245002 | |
> 0.09672115291614276 | |
> 0.0971534052268532 | |
> 0.09766578767457759 | |
Average (mean) 0.0971522190500059 | |
Winner: endsWithManual_pow20_oneSlice | |
Compared with next highest (endsWithManual_pow20), it's: | |
0.63% faster | |
1.01 times as fast | |
0 order(s) of magnitude faster | |
BASICALLY THE SAME | |
Compared with the slowest (checkTail), it's: | |
99.5% faster | |
200.71 times as fast | |
2.3 order(s) of magnitude faster | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment