Skip to content

Commit

Permalink
perf(ZipOpenFS): replace FILE_PARTS_REGEX with indexOf implementation (
Browse files Browse the repository at this point in the history
…#2796)

* perf(ZipOpenFS): replace FILE_PARTS_REGEX with indexOf implementation

* Fixes edge case

* Update ZipOpenFS.ts

Co-authored-by: Maël Nison <nison.mael@gmail.com>
  • Loading branch information
paul-soporan and arcanis committed Apr 27, 2021
1 parent 9792a9a commit 30e1d3c
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 5 deletions.
36 changes: 36 additions & 0 deletions .yarn/versions/f01fa2ef.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
releases:
"@yarnpkg/cli": patch
"@yarnpkg/core": patch
"@yarnpkg/fslib": patch
"@yarnpkg/plugin-pnp": patch
"@yarnpkg/pnp": patch
vscode-zipfs: patch

declined:
- "@yarnpkg/esbuild-plugin-pnp"
- "@yarnpkg/plugin-compat"
- "@yarnpkg/plugin-constraints"
- "@yarnpkg/plugin-dlx"
- "@yarnpkg/plugin-essentials"
- "@yarnpkg/plugin-exec"
- "@yarnpkg/plugin-file"
- "@yarnpkg/plugin-git"
- "@yarnpkg/plugin-github"
- "@yarnpkg/plugin-http"
- "@yarnpkg/plugin-init"
- "@yarnpkg/plugin-interactive-tools"
- "@yarnpkg/plugin-link"
- "@yarnpkg/plugin-node-modules"
- "@yarnpkg/plugin-npm"
- "@yarnpkg/plugin-npm-cli"
- "@yarnpkg/plugin-pack"
- "@yarnpkg/plugin-patch"
- "@yarnpkg/plugin-stage"
- "@yarnpkg/plugin-typescript"
- "@yarnpkg/plugin-version"
- "@yarnpkg/plugin-workspace-tools"
- "@yarnpkg/builder"
- "@yarnpkg/doctor"
- "@yarnpkg/json-proxy"
- "@yarnpkg/pnpify"
- "@yarnpkg/shell"
33 changes: 28 additions & 5 deletions packages/yarnpkg-fslib/sources/ZipOpenFS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,34 @@ import {NodeFS}
import {ZipFS} from './ZipFS';
import {watchFile, unwatchFile, unwatchAllFiles} from './algorithms/watchFile';
import * as errors from './errors';
import {Filename, FSPath, PortablePath} from './path';
import {Filename, FSPath, PortablePath, ppath} from './path';

const ZIP_FD = 0x80000000;

const FILE_PARTS_REGEX = /.*?(?<!\/)\.zip(?=\/|$)/;
const DOT_ZIP = `.zip`;

/**
* Extracts the archive part (ending in the first `.zip`) from a path.
*
* The indexOf-based implementation is ~3.7x faster than a RegExp-based implementation.
*/
export const getArchivePart = (path: string) => {
const idx = path.indexOf(DOT_ZIP);
if (idx <= 0)
return null;

// Disallow files named ".zip"
if (path[idx - 1] === ppath.sep)
return null;

const nextCharIdx = idx + DOT_ZIP.length;

// The path either has to end in ".zip" or contain an archive subpath (".zip/...")
if (path.length > nextCharIdx && path[nextCharIdx] !== ppath.sep)
return null;

return path.slice(0, nextCharIdx) as PortablePath;
};

export type ZipOpenFSOptions = {
baseFs?: FakeFS<PortablePath>,
Expand Down Expand Up @@ -851,11 +874,11 @@ export class ZipOpenFS extends BasePortableFakeFS {
let filePath = `` as PortablePath;

while (true) {
const parts = FILE_PARTS_REGEX.exec(p.substr(filePath.length));
if (!parts)
const archivePart = getArchivePart(p.substr(filePath.length));
if (!archivePart)
return null;

filePath = this.pathUtils.join(filePath, parts[0] as PortablePath);
filePath = this.pathUtils.join(filePath, archivePart);

if (this.isZip.has(filePath) === false) {
if (this.notZip.has(filePath))
Expand Down
22 changes: 22 additions & 0 deletions packages/yarnpkg-fslib/tests/ZipOpenFS.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {getLibzipSync} from '@yarnpkg/libzip';

import {getArchivePart} from '../sources/ZipOpenFS';
import {ppath, npath, Filename} from '../sources/path';
import {ZipOpenFS} from '../sources';

Expand All @@ -17,6 +18,27 @@ const ZIP_DIR2 = ppath.join(
export const ZIP_FILE1 = ppath.join(ZIP_DIR1, `foo.txt` as Filename);
const ZIP_FILE2 = ppath.join(ZIP_DIR2, `foo.txt` as Filename);

describe(`getArchivePart`, () => {
const tests = [
[`.zip`, null],
[`foo`, null],
[`foo.zip`, `foo.zip`],
[`foo.zip/bar`, `foo.zip`],
[`foo.zip/bar/baz`, `foo.zip`],
[`/a/b/c/foo.zip`, `/a/b/c/foo.zip`],
[`./a/b/c/foo.zip`, `./a/b/c/foo.zip`],
[`./a/b/c/.zip`, null],
[`./a/b/c/foo.zipp`, null],
[`./a/b/c/foo.zip/bar/baz/qux.zip`, `./a/b/c/foo.zip`],
] as const;

for (const [path, result] of tests) {
test(`getArchivePart(${JSON.stringify(path)}) === ${JSON.stringify(result)}`, () => {
expect(getArchivePart(path)).toStrictEqual(result);
});
}
});

describe(`ZipOpenFS`, () => {
it(`can read from a zip file`, () => {
const fs = new ZipOpenFS({libzip: getLibzipSync()});
Expand Down

0 comments on commit 30e1d3c

Please sign in to comment.