diff --git a/lib/extract.js b/lib/extract.js index f269145e..98e946ec 100644 --- a/lib/extract.js +++ b/lib/extract.js @@ -6,6 +6,7 @@ const Unpack = require('./unpack.js') const fs = require('fs') const fsm = require('fs-minipass') const path = require('path') +const stripSlash = require('./strip-trailing-slashes.js') module.exports = (opt_, files, cb) => { if (typeof opt_ === 'function') @@ -41,7 +42,7 @@ module.exports = (opt_, files, cb) => { // construct a filter that limits the file entries listed // include child entries if a dir is included const filesFilter = (opt, files) => { - const map = new Map(files.map(f => [f.replace(/\/+$/, ''), true])) + const map = new Map(files.map(f => [stripSlash(f), true])) const filter = opt.filter const mapHas = (file, r) => { @@ -55,8 +56,8 @@ const filesFilter = (opt, files) => { } opt.filter = filter - ? (file, entry) => filter(file, entry) && mapHas(file.replace(/\/+$/, '')) - : file => mapHas(file.replace(/\/+$/, '')) + ? (file, entry) => filter(file, entry) && mapHas(stripSlash(file)) + : file => mapHas(stripSlash(file)) } const extractFileSync = opt => { diff --git a/lib/list.js b/lib/list.js index 702cfea8..a0c1cf2f 100644 --- a/lib/list.js +++ b/lib/list.js @@ -9,6 +9,7 @@ const Parser = require('./parse.js') const fs = require('fs') const fsm = require('fs-minipass') const path = require('path') +const stripSlash = require('./strip-trailing-slashes.js') module.exports = (opt_, files, cb) => { if (typeof opt_ === 'function') @@ -54,7 +55,7 @@ const onentryFunction = opt => { // construct a filter that limits the file entries listed // include child entries if a dir is included const filesFilter = (opt, files) => { - const map = new Map(files.map(f => [f.replace(/\/+$/, ''), true])) + const map = new Map(files.map(f => [stripSlash(f), true])) const filter = opt.filter const mapHas = (file, r) => { @@ -68,8 +69,8 @@ const filesFilter = (opt, files) => { } opt.filter = filter - ? (file, entry) => filter(file, entry) && mapHas(file.replace(/\/+$/, '')) - : file => mapHas(file.replace(/\/+$/, '')) + ? (file, entry) => filter(file, entry) && mapHas(stripSlash(file)) + : file => mapHas(stripSlash(file)) } const listFileSync = opt => { diff --git a/lib/strip-trailing-slashes.js b/lib/strip-trailing-slashes.js new file mode 100644 index 00000000..f702ed5a --- /dev/null +++ b/lib/strip-trailing-slashes.js @@ -0,0 +1,24 @@ +// this is the only approach that was significantly faster than using +// str.replace(/\/+$/, '') for strings ending with a lot of / chars and +// containing multiple / chars. +const batchStrings = [ + '/'.repeat(1024), + '/'.repeat(512), + '/'.repeat(256), + '/'.repeat(128), + '/'.repeat(64), + '/'.repeat(32), + '/'.repeat(16), + '/'.repeat(8), + '/'.repeat(4), + '/'.repeat(2), + '/', +] + +module.exports = str => { + for (const s of batchStrings) { + while (str.length >= s.length && str.slice(-1 * s.length) === s) + str = str.slice(0, -1 * s.length) + } + return str +} diff --git a/test/strip-trailing-slashes.js b/test/strip-trailing-slashes.js new file mode 100644 index 00000000..198797bf --- /dev/null +++ b/test/strip-trailing-slashes.js @@ -0,0 +1,7 @@ +const t = require('tap') +const stripSlash = require('../lib/strip-trailing-slashes.js') +const short = '///a///b///c///' +const long = short.repeat(10) + '/'.repeat(1000000) + +t.equal(stripSlash(short), '///a///b///c') +t.equal(stripSlash(long), short.repeat(9) + '///a///b///c')