diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000..96094115 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,27 @@ + +# RTK — Token-Optimized CLI + +**rtk** is a CLI proxy that filters and compresses command outputs, saving 60-90% tokens. + +## Rule + +Always prefix shell commands with `rtk`: + +```bash +# Instead of: Use: +git status rtk git status +git log -10 rtk git log -10 +cargo test rtk cargo test +docker ps rtk docker ps +kubectl get pods rtk kubectl pods +``` + +## Meta commands (use directly) + +```bash +rtk gain # Token savings dashboard +rtk gain --history # Per-command savings history +rtk discover # Find missed rtk opportunities +rtk proxy # Run raw (no filtering) but track usage +``` + diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 35484218..c6695bb2 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,13 +1,19 @@ version: 2 + updates: - - package-ecosystem: "github-actions" - directory: "/" + - package-ecosystem: "nuget" + directory: "/" schedule: interval: "weekly" - - package-ecosystem: "nuget" - directory: "/" # change to "/src/YourProject" if .csproj files are in subfolders - schedule: - interval: "weekly" - open-pull-requests-limit: 5 - # optional: target-branch: "master" + open-pull-requests-limit: 2 + + groups: + all-dependencies: + patterns: + - "*" + + ignore: + - dependency-name: "*" + update-types: + - "version-update:semver-major" diff --git a/.github/hooks/rtk-rewrite.json b/.github/hooks/rtk-rewrite.json new file mode 100644 index 00000000..eb2a5a7f --- /dev/null +++ b/.github/hooks/rtk-rewrite.json @@ -0,0 +1,12 @@ +{ + "hooks": { + "PreToolUse": [ + { + "type": "command", + "command": "rtk hook copilot", + "cwd": ".", + "timeout": 5 + } + ] + } +} diff --git a/.github/workflows/nuget-release.yml b/.github/workflows/nuget-release.yml index ccc626cc..345b82c4 100644 --- a/.github/workflows/nuget-release.yml +++ b/.github/workflows/nuget-release.yml @@ -29,7 +29,7 @@ jobs: - uses: actions/setup-dotnet@v5 with: - dotnet-version: 10.0.x + global-json-file: global.json # Determine version using C# build target - name: Determine Version diff --git a/.github/workflows/performance-benchmarks.yml b/.github/workflows/performance-benchmarks.yml index 7f5acdbb..3adf01aa 100644 --- a/.github/workflows/performance-benchmarks.yml +++ b/.github/workflows/performance-benchmarks.yml @@ -25,7 +25,7 @@ jobs: - uses: actions/setup-dotnet@v5 with: - dotnet-version: 10.0.x + global-json-file: global.json - name: Build Performance Project run: dotnet build tests/SharpCompress.Performance/SharpCompress.Performance.csproj --configuration Release diff --git a/.opencode/package-lock.json b/.opencode/package-lock.json deleted file mode 100644 index b1b08b4a..00000000 --- a/.opencode/package-lock.json +++ /dev/null @@ -1,389 +0,0 @@ -{ - "name": ".opencode", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "dependencies": { - "@kilocode/plugin": "7.1.20", - "@opencode-ai/plugin": "1.14.30" - } - }, - "node_modules/@kilocode/plugin": { - "version": "7.1.20", - "license": "MIT", - "dependencies": { - "@kilocode/sdk": "7.1.20", - "zod": "4.1.8" - } - }, - "node_modules/@kilocode/sdk": { - "version": "7.1.20", - "license": "MIT" - }, - "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", - "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", - "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", - "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", - "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", - "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", - "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@opencode-ai/plugin": { - "version": "1.14.30", - "resolved": "https://registry.npmjs.org/@opencode-ai/plugin/-/plugin-1.14.30.tgz", - "integrity": "sha512-O1y6qR349R5XJtB76Vx4TO/uvLkqNvdsgxtj2ZpWoygb3rtrkuVMiBsrcL2WfuyyaLJnPmbnkeigcdm42r09Hg==", - "license": "MIT", - "dependencies": { - "@opencode-ai/sdk": "1.14.30", - "effect": "4.0.0-beta.57", - "zod": "4.1.8" - }, - "peerDependencies": { - "@opentui/core": ">=0.1.105", - "@opentui/solid": ">=0.1.105" - }, - "peerDependenciesMeta": { - "@opentui/core": { - "optional": true - }, - "@opentui/solid": { - "optional": true - } - } - }, - "node_modules/@opencode-ai/sdk": { - "version": "1.14.30", - "resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.14.30.tgz", - "integrity": "sha512-OgPEDvALekHZIjByo/okJ699aLPn+XtsVxgZxUqE8TlzAG7TtskMGFl0fro8O0T2p+nkOT/LstnKGbECvc0+YA==", - "license": "MIT", - "dependencies": { - "cross-spawn": "7.0.6" - } - }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "license": "Apache-2.0", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/effect": { - "version": "4.0.0-beta.57", - "resolved": "https://registry.npmjs.org/effect/-/effect-4.0.0-beta.57.tgz", - "integrity": "sha512-rg32VgXnLKaPRs9tbRDaZ5jxmzNY7ojXt85gSHGUTwdlbWH5Ik+OCUY2q14TXliygPGoHwCAvNWS4bQJOqf00g==", - "license": "MIT", - "dependencies": { - "@standard-schema/spec": "^1.1.0", - "fast-check": "^4.6.0", - "find-my-way-ts": "^0.1.6", - "ini": "^6.0.0", - "kubernetes-types": "^1.30.0", - "msgpackr": "^1.11.9", - "multipasta": "^0.2.7", - "toml": "^4.1.1", - "uuid": "^13.0.0", - "yaml": "^2.8.3" - } - }, - "node_modules/fast-check": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-4.7.0.tgz", - "integrity": "sha512-NsZRtqvSSoCP0HbNjUD+r1JH8zqZalyp6gLY9e7OYs7NK9b6AHOs2baBFeBG7bVNsuoukh89x2Yg3rPsul8ziQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT", - "dependencies": { - "pure-rand": "^8.0.0" - }, - "engines": { - "node": ">=12.17.0" - } - }, - "node_modules/find-my-way-ts": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/find-my-way-ts/-/find-my-way-ts-0.1.6.tgz", - "integrity": "sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA==", - "license": "MIT" - }, - "node_modules/ini": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz", - "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==", - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/kubernetes-types": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/kubernetes-types/-/kubernetes-types-1.30.0.tgz", - "integrity": "sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q==", - "license": "Apache-2.0" - }, - "node_modules/msgpackr": { - "version": "1.11.10", - "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.10.tgz", - "integrity": "sha512-iCZNq+HszvF+fC3anCm4nBmWEnbeIAfpDs6IStAEKhQ2YSgkjzVG2FF9XJqwwQh5bH3N9OUTUt4QwVN6MLMLtA==", - "license": "MIT", - "optionalDependencies": { - "msgpackr-extract": "^3.0.2" - } - }, - "node_modules/msgpackr-extract": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz", - "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "dependencies": { - "node-gyp-build-optional-packages": "5.2.2" - }, - "bin": { - "download-msgpackr-prebuilds": "bin/download-prebuilds.js" - }, - "optionalDependencies": { - "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", - "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", - "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", - "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", - "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", - "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" - } - }, - "node_modules/multipasta": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/multipasta/-/multipasta-0.2.7.tgz", - "integrity": "sha512-KPA58d68KgGil15oDqXjkUBEBYc00XvbPj5/X+dyzeo/lWm9Nc25pQRlf1D+gv4OpK7NM0J1odrbu9JNNGvynA==", - "license": "MIT" - }, - "node_modules/node-gyp-build-optional-packages": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", - "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", - "license": "MIT", - "optional": true, - "dependencies": { - "detect-libc": "^2.0.1" - }, - "bin": { - "node-gyp-build-optional-packages": "bin.js", - "node-gyp-build-optional-packages-optional": "optional.js", - "node-gyp-build-optional-packages-test": "build-test.js" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/pure-rand": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-8.4.0.tgz", - "integrity": "sha512-IoM8YF/jY0hiugFo/wOWqfmarlE6J0wc6fDK1PhftMk7MGhVZl88sZimmqBBFomLOCSmcCCpsfj7wXASCpvK9A==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/toml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/toml/-/toml-4.1.1.tgz", - "integrity": "sha512-EBJnVBr3dTXdA89WVFoAIPUqkBjxPMwRqsfuo1r240tKFHXv3zgca4+NJib/h6TyvGF7vOawz0jGuryJCdNHrw==", - "license": "MIT", - "engines": { - "node": ">=20" - } - }, - "node_modules/uuid": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.1.tgz", - "integrity": "sha512-9ezox2roIft6ExBVTVqibSd5dc5/47Sw/uY6b4SjQUT2TzQ0tltNquWA46y4xPQmdZYqvnio22SgWd41M86+jw==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist-node/bin/uuid" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/yaml": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", - "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" - } - }, - "node_modules/zod": { - "version": "4.1.8", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - } - } -} diff --git a/.vscode/settings.json b/.vscode/settings.json index 1d13071f..96bc4d85 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,5 @@ { - "dotnet.defaultSolution": "SharpCompress.sln", + "dotnet.defaultSolution": "SharpCompress.slnx", "files.exclude": { "**/bin": true, "**/obj": true diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 9f59e69d..c17d4d04 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -7,7 +7,7 @@ "type": "process", "args": [ "build", - "${workspaceFolder}/SharpCompress.sln", + "${workspaceFolder}/SharpCompress.slnx", "/property:GenerateFullPaths=true", "/consoleloggerparameters:NoSummary;ForceNoAlign" ], @@ -23,7 +23,7 @@ "type": "process", "args": [ "build", - "${workspaceFolder}/SharpCompress.sln", + "${workspaceFolder}/SharpCompress.slnx", "-c", "Release", "/property:GenerateFullPaths=true", @@ -51,7 +51,7 @@ "type": "process", "args": [ "restore", - "${workspaceFolder}/SharpCompress.sln" + "${workspaceFolder}/SharpCompress.slnx" ], "problemMatcher": "$msCompile" }, @@ -61,7 +61,7 @@ "type": "process", "args": [ "clean", - "${workspaceFolder}/SharpCompress.sln" + "${workspaceFolder}/SharpCompress.slnx" ], "problemMatcher": "$msCompile" }, diff --git a/AGENTS.md b/AGENTS.md index ef872d0c..26c47300 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -76,7 +76,7 @@ SharpCompress is a pure C# compression library supporting multiple archive forma - Build project is in `build/` - Use `dotnet build` to build the solution - Use `dotnet test` to run tests -- Solution file: `SharpCompress.sln` +- Solution file: `SharpCompress.slnx` ### Directory Structure ``` diff --git a/Directory.Packages.props b/Directory.Packages.props index 7ed85889..7559f05e 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -6,18 +6,18 @@ - + - + - + diff --git a/SharpCompress.sln b/SharpCompress.sln deleted file mode 100644 index 3609183a..00000000 --- a/SharpCompress.sln +++ /dev/null @@ -1,64 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26430.6 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3C5BE746-03E5-4895-9988-0B57F162F86C}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0F0901FF-E8D9-426A-B5A2-17C7F47C1529}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpCompress", "src\SharpCompress\SharpCompress.csproj", "{FD19DDD8-72B2-4024-8665-0D1F7A2AA998}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpCompress.Test", "tests\SharpCompress.Test\SharpCompress.Test.csproj", "{F2B1A1EB-0FA6-40D0-8908-E13247C7226F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "build", "build\build.csproj", "{D4D613CB-5E94-47FB-85BE-B8423D20C545}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Config", "Config", "{CDB42573-7D22-4490-BA12-1B7FB99CE7FB}" - ProjectSection(SolutionItems) = preProject - Directory.Build.props = Directory.Build.props - global.json = global.json - .editorconfig = .editorconfig - .gitignore = .gitignore - Directory.Packages.props = Directory.Packages.props - NuGet.config = NuGet.config - .github\workflows\nuget-release.yml = .github\workflows\nuget-release.yml - README.md = README.md - AGENTS.md = AGENTS.md - docs\TAR_GAP_ANALYSIS.md = docs\TAR_GAP_ANALYSIS.md - docs\TAR_SPEC.md = docs\TAR_SPEC.md - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpCompress.Performance", "tests\SharpCompress.Performance\SharpCompress.Performance.csproj", "{5BDE6DBC-9E5F-4E21-AB71-F138A3E72B17}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {FD19DDD8-72B2-4024-8665-0D1F7A2AA998}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FD19DDD8-72B2-4024-8665-0D1F7A2AA998}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FD19DDD8-72B2-4024-8665-0D1F7A2AA998}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FD19DDD8-72B2-4024-8665-0D1F7A2AA998}.Release|Any CPU.Build.0 = Release|Any CPU - {F2B1A1EB-0FA6-40D0-8908-E13247C7226F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F2B1A1EB-0FA6-40D0-8908-E13247C7226F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F2B1A1EB-0FA6-40D0-8908-E13247C7226F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F2B1A1EB-0FA6-40D0-8908-E13247C7226F}.Release|Any CPU.Build.0 = Release|Any CPU - {D4D613CB-5E94-47FB-85BE-B8423D20C545}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D4D613CB-5E94-47FB-85BE-B8423D20C545}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D4D613CB-5E94-47FB-85BE-B8423D20C545}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D4D613CB-5E94-47FB-85BE-B8423D20C545}.Release|Any CPU.Build.0 = Release|Any CPU - {5BDE6DBC-9E5F-4E21-AB71-F138A3E72B17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5BDE6DBC-9E5F-4E21-AB71-F138A3E72B17}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5BDE6DBC-9E5F-4E21-AB71-F138A3E72B17}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5BDE6DBC-9E5F-4E21-AB71-F138A3E72B17}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {FD19DDD8-72B2-4024-8665-0D1F7A2AA998} = {3C5BE746-03E5-4895-9988-0B57F162F86C} - {F2B1A1EB-0FA6-40D0-8908-E13247C7226F} = {0F0901FF-E8D9-426A-B5A2-17C7F47C1529} - {5BDE6DBC-9E5F-4E21-AB71-F138A3E72B17} = {0F0901FF-E8D9-426A-B5A2-17C7F47C1529} - EndGlobalSection -EndGlobal diff --git a/SharpCompress.sln.DotSettings b/SharpCompress.sln.DotSettings deleted file mode 100644 index e2f7e87f..00000000 --- a/SharpCompress.sln.DotSettings +++ /dev/null @@ -1,138 +0,0 @@ - - DO_NOT_SHOW - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - DO_NOT_SHOW - - <?xml version="1.0" encoding="utf-16"?><Profile name="Basic Clean"><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSRemoveCodeRedundancies>True</CSRemoveCodeRedundancies><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSCodeStyleAttributes ArrangeTypeAccessModifier="False" ArrangeTypeMemberAccessModifier="False" SortModifiers="False" RemoveRedundantParentheses="False" AddMissingParentheses="False" ArrangeBraces="True" ArrangeAttributes="False" ArrangeArgumentsStyle="False" /><RemoveCodeRedundancies>True</RemoveCodeRedundancies><CSUseAutoProperty>True</CSUseAutoProperty><CSMakeAutoPropertyGetOnly>True</CSMakeAutoPropertyGetOnly><CSReformatCode>True</CSReformatCode></Profile> - - Basic Clean - True - Positional - Required - Required - Required - Required - False - True - True - False - True - False - True - True - True - True - True - True - 0 - 1 - - SEPARATE - ALWAYS_ADD - ALWAYS_ADD - ALWAYS_ADD - ALWAYS_ADD - ALWAYS_ADD - ALWAYS_ADD - False - 1 - 1 - NEVER - NEVER - False - False - NEVER - False - False - NEVER - False - - False - - LINE_BREAK - False - True - True - - - False - False - False - CHOP_IF_LONG - CHOP_ALWAYS - CHOP_IF_LONG - False - CHOP_IF_LONG - UseVar - UseVar - UseVar - - <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /> - <Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static readonly fields (private)"><ElementKinds><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /></Policy> - <Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /></Policy> - <Policy><Descriptor Staticness="Static" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Static readonly fields (not private)"><ElementKinds><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /></Policy> - <Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"><ElementKinds><Kind Name="FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /></Policy> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> - <Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> - - True - True - True - True - True - True - True - True - True - True - <SessionState ContinuousTestingIsOn="False" ContinuousTestingMode="0" FrameworkVersion="{x:Null}" IsLocked="False" Name="All tests from Solution" PlatformMonoPreference="{x:Null}" PlatformType="{x:Null}" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> - <Solution /> -</SessionState> diff --git a/SharpCompress.slnx b/SharpCompress.slnx new file mode 100644 index 00000000..0f3880c9 --- /dev/null +++ b/SharpCompress.slnx @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/Program.cs b/build/Program.cs index 1c36e0a9..4f9d313c 100644 --- a/build/Program.cs +++ b/build/Program.cs @@ -12,6 +12,7 @@ using static SimpleExec.Command; const string Clean = "clean"; const string Restore = "restore"; +const string UpdateLocks = "update-locks"; const string Build = "build"; const string Test = "test"; const string Format = "format"; @@ -66,7 +67,8 @@ Target( Run("dotnet", "csharpier check ."); } ); -Target(Restore, [CheckFormat], () => Run("dotnet", "restore")); +Target(Restore, [CheckFormat], () => Run("dotnet", "restore --locked-mode")); +Target(UpdateLocks, [CheckFormat], () => Run("dotnet", "restore --force-evaluate")); Target( Build, diff --git a/build/packages.lock.json b/build/packages.lock.json index e0b23c84..8cc0db72 100644 --- a/build/packages.lock.json +++ b/build/packages.lock.json @@ -25,12 +25,13 @@ }, "Microsoft.SourceLink.GitHub": { "type": "Direct", - "requested": "[10.0.102, )", - "resolved": "10.0.102", - "contentHash": "Oxq3RCIJSdtpIU4hLqO7XaDe/Ra3HS9Wi8rJl838SAg6Zu1iQjerA0+xXWBgUFYbgknUGCLOU0T+lzMLkvY9Qg==", + "requested": "[10.0.300, )", + "resolved": "10.0.300", + "contentHash": "QzCtLkXVb3l4IxcpvJCbzUwMLihAmLN6vVLjQGSzYSF8d2dvXxqJAZk83RV3gYnp2egz8jRMgSR2woY3vOahTA==", "dependencies": { - "Microsoft.Build.Tasks.Git": "10.0.102", - "Microsoft.SourceLink.Common": "10.0.102" + "Microsoft.Build.Tasks.Git": "10.0.300", + "Microsoft.SourceLink.Common": "10.0.300", + "System.IO.Hashing": "10.0.8" } }, "Microsoft.VisualStudio.Threading.Analyzers": { @@ -41,9 +42,9 @@ }, "PolySharp": { "type": "Direct", - "requested": "[1.15.0, )", - "resolved": "1.15.0", - "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g==" + "requested": "[1.16.0, )", + "resolved": "1.16.0", + "contentHash": "3kdIIceBPumwjw279FuiVMfVENT2cGASXJgcigdySsbX2dJB8ofUgG6i47yqF/k1qu6fvNR3csrSekZPviR6kQ==" }, "SimpleExec": { "type": "Direct", @@ -53,8 +54,11 @@ }, "Microsoft.Build.Tasks.Git": { "type": "Transitive", - "resolved": "10.0.102", - "contentHash": "0i81LYX31U6UiXz4NOLbvc++u+/mVDmOt+PskrM/MygpDxkv9THKQyRUmavBpLK6iBV0abNWnn+CQgSRz//Pwg==" + "resolved": "10.0.300", + "contentHash": "P0kaQwVZx4xIUe2FtrLyBadYNXuAljttJUPvjBYRuHhPE8L77L42KakLDkaADRiUrGspoLcMwayjrbQhYTr0zA==", + "dependencies": { + "System.IO.Hashing": "10.0.8" + } }, "Microsoft.NETFramework.ReferenceAssemblies.net461": { "type": "Transitive", @@ -63,8 +67,13 @@ }, "Microsoft.SourceLink.Common": { "type": "Transitive", - "resolved": "10.0.102", - "contentHash": "Mk1IMb9q5tahC2NltxYXFkLBtuBvfBoCQ3pIxYQWfzbCE9o1OB9SsHe0hnNGo7lWgTA/ePbFAJLWu6nLL9K17A==" + "resolved": "10.0.300", + "contentHash": "0jlkXaUGjYlWTIVPve5MftjKHnT3SlAtq9BCLV4J9IjdPrxV/+4rMlBSjfr1khG8/GC6KGojjola8E1VvWF0qQ==" + }, + "System.IO.Hashing": { + "type": "Transitive", + "resolved": "10.0.8", + "contentHash": "+dJsbPJ3FyUbTZNplFj0RCKePFizmv6ewDV46JE9q/IVH4c3xTCftHfHelLsAKf0jryIPqgMb5GpS0x7TAY3mg==" } } } diff --git a/global.json b/global.json index 512142d2..50baaf1a 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "10.0.100", - "rollForward": "latestFeature" + "version": "10.0.300", + "rollForward": "latestPatch" } } diff --git a/src/SharpCompress/Archives/AbstractWritableArchive.Async.cs b/src/SharpCompress/Archives/AbstractWritableArchive.Async.cs index a8012896..67b8dffc 100644 --- a/src/SharpCompress/Archives/AbstractWritableArchive.Async.cs +++ b/src/SharpCompress/Archives/AbstractWritableArchive.Async.cs @@ -122,6 +122,14 @@ public abstract partial class AbstractWritableArchive .ConfigureAwait(false); } + public override async ValueTask DisposeAsync() + { + await base.DisposeAsync().ConfigureAwait(false); + newEntries.Cast().ForEach(x => x.Close()); + removedEntries.Cast().ForEach(x => x.Close()); + modifiedEntries.Cast().ForEach(x => x.Close()); + } + protected abstract ValueTask SaveToAsync( Stream stream, TOptions options, diff --git a/src/SharpCompress/Archives/IAsyncArchiveExtensions.cs b/src/SharpCompress/Archives/IAsyncArchiveExtensions.cs index 4be1419f..e9fd9723 100644 --- a/src/SharpCompress/Archives/IAsyncArchiveExtensions.cs +++ b/src/SharpCompress/Archives/IAsyncArchiveExtensions.cs @@ -29,12 +29,33 @@ public static class IAsyncArchiveExtensions || archive.Type == ArchiveType.SevenZip ) { + var totalBytes = await archive.TotalUncompressedSizeAsync().ConfigureAwait(false); + var bytesRead = 0L; await using var reader = await archive .ExtractAllEntriesAsync() .ConfigureAwait(false); - await reader - .WriteAllToDirectoryAsync(destinationDirectory, options, cancellationToken) - .ConfigureAwait(false); + while (await reader.MoveToNextEntryAsync(cancellationToken).ConfigureAwait(false)) + { + cancellationToken.ThrowIfCancellationRequested(); + + await reader + .WriteEntryToDirectoryAsync( + destinationDirectory, + options, + cancellationToken + ) + .ConfigureAwait(false); + + if (reader.Entry.IsDirectory) + { + continue; + } + + bytesRead += reader.Entry.Size; + progress?.Report( + new ProgressReport(reader.Entry.Key ?? string.Empty, bytesRead, totalBytes) + ); + } } else { diff --git a/src/SharpCompress/Common/Rar/RarCryptoWrapper.cs b/src/SharpCompress/Common/Rar/RarCryptoWrapper.cs index 54ca2739..6e1ed222 100644 --- a/src/SharpCompress/Common/Rar/RarCryptoWrapper.cs +++ b/src/SharpCompress/Common/Rar/RarCryptoWrapper.cs @@ -63,9 +63,9 @@ internal sealed class RarCryptoWrapper : Stream int offset, int count, CancellationToken cancellationToken - ) => ReadAndDecryptAsync(buffer, offset, count, cancellationToken); + ) => ReadAndDecryptAsync(buffer, offset, count, cancellationToken).AsTask(); - private async Task ReadAndDecryptAsync( + private async ValueTask ReadAndDecryptAsync( byte[] buffer, int offset, int count, diff --git a/src/SharpCompress/Common/Tar/TarFilePart.cs b/src/SharpCompress/Common/Tar/TarFilePart.cs index 15b76d14..eb2f4f33 100644 --- a/src/SharpCompress/Common/Tar/TarFilePart.cs +++ b/src/SharpCompress/Common/Tar/TarFilePart.cs @@ -25,7 +25,7 @@ internal sealed class TarFilePart : FilePart if (_seekableStream != null) { _seekableStream.Position = Header.DataStartPosition ?? 0; - return new TarReadOnlySubStream(_seekableStream, Header.Size, false); + return new TarReadOnlySubStream(_seekableStream, Header.Size); } return Header.PackedStream.NotNull(); } @@ -36,14 +36,8 @@ internal sealed class TarFilePart : FilePart { if (_seekableStream != null) { - var useSyncOverAsync = false; -#if LEGACY_DOTNET - useSyncOverAsync = true; -#endif _seekableStream.Position = Header.DataStartPosition ?? 0; - return new ValueTask( - new TarReadOnlySubStream(_seekableStream, Header.Size, useSyncOverAsync) - ); + return new ValueTask(new TarReadOnlySubStream(_seekableStream, Header.Size)); } return new ValueTask(Header.PackedStream.NotNull()); } diff --git a/src/SharpCompress/Common/Tar/TarHeaderFactory.Async.cs b/src/SharpCompress/Common/Tar/TarHeaderFactory.Async.cs index 772a5665..c011abd2 100644 --- a/src/SharpCompress/Common/Tar/TarHeaderFactory.Async.cs +++ b/src/SharpCompress/Common/Tar/TarHeaderFactory.Async.cs @@ -44,15 +44,7 @@ internal static partial class TarHeaderFactory break; case StreamingMode.Streaming: { - var useSyncOverAsync = false; -#if LEGACY_DOTNET - useSyncOverAsync = true; -#endif - header.PackedStream = new TarReadOnlySubStream( - stream, - header.Size, - useSyncOverAsync - ); + header.PackedStream = new TarReadOnlySubStream(stream, header.Size); } break; default: diff --git a/src/SharpCompress/Common/Tar/TarHeaderFactory.cs b/src/SharpCompress/Common/Tar/TarHeaderFactory.cs index df71351e..c95efaef 100644 --- a/src/SharpCompress/Common/Tar/TarHeaderFactory.cs +++ b/src/SharpCompress/Common/Tar/TarHeaderFactory.cs @@ -38,11 +38,7 @@ internal static partial class TarHeaderFactory break; case StreamingMode.Streaming: { - header.PackedStream = new TarReadOnlySubStream( - stream, - header.Size, - false - ); + header.PackedStream = new TarReadOnlySubStream(stream, header.Size); } break; default: diff --git a/src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs b/src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs index 987751a3..86594f63 100644 --- a/src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs +++ b/src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Threading.Tasks; namespace SharpCompress.Common.Tar; @@ -8,9 +9,10 @@ internal class TarReadOnlySubStream : Stream private readonly Stream _stream; private bool _isDisposed; + private bool _isPositionedAtNextEntry; private long _amountRead; - public TarReadOnlySubStream(Stream stream, long bytesToRead, bool useSyncOverAsyncDispose) + public TarReadOnlySubStream(Stream stream, long bytesToRead) { _stream = stream; BytesLeftToRead = bytesToRead; @@ -27,27 +29,17 @@ internal class TarReadOnlySubStream : Stream _isDisposed = true; if (disposing) { - // Ensure we read all remaining blocks for this entry. - _stream.Skip(BytesLeftToRead); - _amountRead += BytesLeftToRead; - - // If the last block wasn't a full 512 bytes, skip the remaining padding bytes. - var bytesInLastBlock = _amountRead % 512; - - if (bytesInLastBlock != 0) + if (Utility.UseSyncOverAsyncDispose()) { - if (Utility.UseSyncOverAsyncDispose()) - { #pragma warning disable VSTHRD002 // Avoid problematic synchronous waits #pragma warning disable CA2012 - _stream.SkipAsync(512 - bytesInLastBlock).GetAwaiter().GetResult(); + AdvanceToNextHeaderAsync().GetAwaiter().GetResult(); #pragma warning restore CA2012 #pragma warning restore VSTHRD002 // Avoid problematic synchronous waits - } - else - { - _stream.Skip(512 - bytesInLastBlock); - } + } + else + { + AdvanceToNextHeader(); } } base.Dispose(disposing); @@ -63,17 +55,7 @@ internal class TarReadOnlySubStream : Stream } _isDisposed = true; - // Ensure we read all remaining blocks for this entry. - await _stream.SkipAsync(BytesLeftToRead).ConfigureAwait(false); - _amountRead += BytesLeftToRead; - - // If the last block wasn't a full 512 bytes, skip the remaining padding bytes. - var bytesInLastBlock = _amountRead % 512; - - if (bytesInLastBlock != 0) - { - await _stream.SkipAsync(512 - bytesInLastBlock).ConfigureAwait(false); - } + await AdvanceToNextHeaderAsync().ConfigureAwait(false); GC.SuppressFinalize(this); await base.DisposeAsync().ConfigureAwait(false); @@ -82,6 +64,54 @@ internal class TarReadOnlySubStream : Stream private long BytesLeftToRead { get; set; } + private void AdvanceToNextHeader() + { + if (_isPositionedAtNextEntry) + { + return; + } + + if (BytesLeftToRead > 0) + { + _stream.Skip(BytesLeftToRead); + _amountRead += BytesLeftToRead; + BytesLeftToRead = 0; + } + + // Tar entry data is padded to 512-byte blocks, so callers that read to EOF + // should still leave the shared archive stream positioned at the next header. + var bytesInLastBlock = _amountRead % 512; + if (bytesInLastBlock != 0) + { + _stream.Skip(512 - bytesInLastBlock); + } + + _isPositionedAtNextEntry = true; + } + + private async ValueTask AdvanceToNextHeaderAsync() + { + if (_isPositionedAtNextEntry) + { + return; + } + + if (BytesLeftToRead > 0) + { + await _stream.SkipAsync(BytesLeftToRead).ConfigureAwait(false); + _amountRead += BytesLeftToRead; + BytesLeftToRead = 0; + } + + var bytesInLastBlock = _amountRead % 512; + if (bytesInLastBlock != 0) + { + await _stream.SkipAsync(512 - bytesInLastBlock).ConfigureAwait(false); + } + + _isPositionedAtNextEntry = true; + } + public override bool CanRead => true; public override bool CanSeek => false; @@ -104,6 +134,11 @@ internal class TarReadOnlySubStream : Stream public override int Read(byte[] buffer, int offset, int count) { + if (BytesLeftToRead <= 0) + { + AdvanceToNextHeader(); + return 0; + } if (BytesLeftToRead < count) { count = (int)BytesLeftToRead; @@ -113,6 +148,10 @@ internal class TarReadOnlySubStream : Stream { BytesLeftToRead -= read; _amountRead += read; + if (BytesLeftToRead == 0) + { + AdvanceToNextHeader(); + } } return read; } @@ -121,6 +160,7 @@ internal class TarReadOnlySubStream : Stream { if (BytesLeftToRead <= 0) { + AdvanceToNextHeader(); return -1; } var value = _stream.ReadByte(); @@ -128,6 +168,10 @@ internal class TarReadOnlySubStream : Stream { --BytesLeftToRead; ++_amountRead; + if (BytesLeftToRead == 0) + { + AdvanceToNextHeader(); + } } return value; } @@ -139,6 +183,11 @@ internal class TarReadOnlySubStream : Stream System.Threading.CancellationToken cancellationToken ) { + if (BytesLeftToRead <= 0) + { + await AdvanceToNextHeaderAsync().ConfigureAwait(false); + return 0; + } if (BytesLeftToRead < count) { count = (int)BytesLeftToRead; @@ -150,6 +199,10 @@ internal class TarReadOnlySubStream : Stream { BytesLeftToRead -= read; _amountRead += read; + if (BytesLeftToRead == 0) + { + await AdvanceToNextHeaderAsync().ConfigureAwait(false); + } } return read; } @@ -160,6 +213,11 @@ internal class TarReadOnlySubStream : Stream System.Threading.CancellationToken cancellationToken = default ) { + if (BytesLeftToRead <= 0) + { + await AdvanceToNextHeaderAsync().ConfigureAwait(false); + return 0; + } if (BytesLeftToRead < buffer.Length) { buffer = buffer.Slice(0, (int)BytesLeftToRead); @@ -169,6 +227,10 @@ internal class TarReadOnlySubStream : Stream { BytesLeftToRead -= read; _amountRead += read; + if (BytesLeftToRead == 0) + { + await AdvanceToNextHeaderAsync().ConfigureAwait(false); + } } return read; } diff --git a/src/SharpCompress/Compressors/LZMA/LZ/LzBinTree.cs b/src/SharpCompress/Compressors/LZMA/LZ/LzBinTree.cs index bc0a8a7a..85795e74 100644 --- a/src/SharpCompress/Compressors/LZMA/LZ/LzBinTree.cs +++ b/src/SharpCompress/Compressors/LZMA/LZ/LzBinTree.cs @@ -1,6 +1,7 @@ #nullable disable using System; +using System.Buffers; using System.IO; namespace SharpCompress.Compressors.LZMA.LZ; @@ -11,8 +12,8 @@ internal sealed class BinTree : InWindow private uint _cyclicBufferSize; private uint _matchMaxLen; - private uint[] _son; - private uint[] _hash; + private uint[] _son = []; + private uint[] _hash = []; private uint _cutValue = 0xFF; private uint _hashMask; @@ -108,7 +109,12 @@ internal sealed class BinTree : InWindow var cyclicBufferSize = historySize + 1; if (_cyclicBufferSize != cyclicBufferSize) { - _son = new uint[(_cyclicBufferSize = cyclicBufferSize) * 2]; + if (_son.Length != 0) + { + ArrayPool.Shared.Return(_son); + } + _cyclicBufferSize = cyclicBufferSize; + _son = ArrayPool.Shared.Rent(checked((int)(_cyclicBufferSize * 2))); } var hs = K_BT2_HASH_SIZE; @@ -132,7 +138,27 @@ internal sealed class BinTree : InWindow } if (hs != _hashSizeSum) { - _hash = new uint[_hashSizeSum = hs]; + if (_hash.Length != 0) + { + ArrayPool.Shared.Return(_hash); + } + _hashSizeSum = hs; + _hash = ArrayPool.Shared.Rent(checked((int)_hashSizeSum)); + } + } + + public override void Dispose() + { + base.Dispose(); + if (_son.Length != 0) + { + ArrayPool.Shared.Return(_son); + _son = []; + } + if (_hash.Length != 0) + { + ArrayPool.Shared.Return(_hash); + _hash = []; } } diff --git a/src/SharpCompress/Compressors/LZMA/LZ/LzInWindow.cs b/src/SharpCompress/Compressors/LZMA/LZ/LzInWindow.cs index 9df3c27d..7ee8bc88 100644 --- a/src/SharpCompress/Compressors/LZMA/LZ/LzInWindow.cs +++ b/src/SharpCompress/Compressors/LZMA/LZ/LzInWindow.cs @@ -1,10 +1,12 @@ #nullable disable +using System; +using System.Buffers; using System.IO; namespace SharpCompress.Compressors.LZMA.LZ; -internal class InWindow +internal class InWindow : IDisposable { public byte[] _bufferBase; // pointer to buffer with data private Stream _stream; @@ -78,7 +80,22 @@ internal class InWindow } } - private void Free() => _bufferBase = null; + private void Free() + { + if (_bufferBase is null) + { + return; + } + + ArrayPool.Shared.Return(_bufferBase); + _bufferBase = null; + } + + public virtual void Dispose() + { + ReleaseStream(); + Free(); + } public void Create(uint keepSizeBefore, uint keepSizeAfter, uint keepSizeReserv) { @@ -89,7 +106,7 @@ internal class InWindow { Free(); _blockSize = blockSize; - _bufferBase = new byte[_blockSize]; + _bufferBase = ArrayPool.Shared.Rent(checked((int)_blockSize)); } _pointerToLastSafePosition = _blockSize - keepSizeAfter; _streamEndWasReached = false; diff --git a/src/SharpCompress/Compressors/LZMA/LZipStream.Async.cs b/src/SharpCompress/Compressors/LZMA/LZipStream.Async.cs index ae8e1074..2cc7fb8d 100644 --- a/src/SharpCompress/Compressors/LZMA/LZipStream.Async.cs +++ b/src/SharpCompress/Compressors/LZMA/LZipStream.Async.cs @@ -126,31 +126,40 @@ public sealed partial class LZipStream ) { // Read the header - byte[] header = new byte[6]; - var n = await stream - .ReadAsync(header, 0, header.Length, cancellationToken) - .ConfigureAwait(false); - - // TODO: Handle reading only part of the header? - - if (n != 6) + var header = ArrayPool.Shared.Rent(6); + try { - return 0; - } + var n = await stream.ReadAsync(header, 0, 6, cancellationToken).ConfigureAwait(false); - if ( - header[0] != 'L' - || header[1] != 'Z' - || header[2] != 'I' - || header[3] != 'P' - || header[4] != 1 /* version 1 */ - ) - { - return 0; + // TODO: Handle reading only part of the header? + + if (n != 6) + { + return 0; + } + + if ( + header[0] != 'L' + || header[1] != 'Z' + || header[2] != 'I' + || header[3] != 'P' + || header[4] != 1 /* version 1 */ + ) + { + return 0; + } + var basePower = header[5] & 0x1F; + var subtractionNumerator = (header[5] & 0xE0) >> 5; + if (basePower < 4 || basePower > 30) + { + return 0; + } + return (1 << basePower) - (subtractionNumerator * (1 << (basePower - 4))); + } + finally + { + ArrayPool.Shared.Return(header); } - var basePower = header[5] & 0x1F; - var subtractionNumerator = (header[5] & 0xE0) >> 5; - return (1 << basePower) - (subtractionNumerator * (1 << (basePower - 4))); } #if !LEGACY_DOTNET diff --git a/src/SharpCompress/Compressors/LZMA/Lzma2EncoderStream.cs b/src/SharpCompress/Compressors/LZMA/Lzma2EncoderStream.cs index f1ed808c..51e1b753 100644 --- a/src/SharpCompress/Compressors/LZMA/Lzma2EncoderStream.cs +++ b/src/SharpCompress/Compressors/LZMA/Lzma2EncoderStream.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -26,11 +27,12 @@ internal sealed class Lzma2EncoderStream : Stream private readonly int _dictionarySize; private readonly int _numFastBytes; private readonly byte[] _buffer; + private readonly byte[] _properties; + private readonly Encoder _encoder; private int _bufferPosition; private bool _isFirstChunk = true; private bool _isDisposed; private byte _lzmaPropertiesByte; - private bool _lzmaPropertiesKnown; /// /// Creates a new LZMA2 encoder stream. @@ -43,14 +45,23 @@ internal sealed class Lzma2EncoderStream : Stream _output = output; _dictionarySize = dictionarySize; _numFastBytes = numFastBytes; - _buffer = new byte[MAX_UNCOMPRESSED_CHUNK_SIZE]; + _buffer = ArrayPool.Shared.Rent(MAX_UNCOMPRESSED_CHUNK_SIZE); _bufferPosition = 0; + + var encoderProps = new LzmaEncoderProperties(eos: false, _dictionarySize, _numFastBytes); + _encoder = new Encoder(); + _encoder.SetCoderProperties(encoderProps.PropIDs, encoderProps.Properties); + + Span lzmaProperties = stackalloc byte[5]; + _encoder.WriteCoderProperties(lzmaProperties); + _lzmaPropertiesByte = lzmaProperties[0]; + _properties = [EncodeDictionarySize(_dictionarySize)]; } /// /// Gets the 1-byte LZMA2 properties (encoded dictionary size). /// - public byte[] Properties => [EncodeDictionarySize(_dictionarySize)]; + public byte[] Properties => _properties; public override bool CanRead => false; public override bool CanSeek => false; @@ -67,13 +78,13 @@ internal sealed class Lzma2EncoderStream : Stream { while (count > 0) { - var toCopy = Math.Min(count, _buffer.Length - _bufferPosition); + var toCopy = Math.Min(count, MAX_UNCOMPRESSED_CHUNK_SIZE - _bufferPosition); Buffer.BlockCopy(buffer, offset, _buffer, _bufferPosition, toCopy); _bufferPosition += toCopy; offset += toCopy; count -= toCopy; - if (_bufferPosition == _buffer.Length) + if (_bufferPosition == MAX_UNCOMPRESSED_CHUNK_SIZE) { FlushChunk(); } @@ -91,13 +102,13 @@ internal sealed class Lzma2EncoderStream : Stream { cancellationToken.ThrowIfCancellationRequested(); - var toCopy = Math.Min(count, _buffer.Length - _bufferPosition); + var toCopy = Math.Min(count, MAX_UNCOMPRESSED_CHUNK_SIZE - _bufferPosition); Buffer.BlockCopy(buffer, offset, _buffer, _bufferPosition, toCopy); _bufferPosition += toCopy; offset += toCopy; count -= toCopy; - if (_bufferPosition == _buffer.Length) + if (_bufferPosition == MAX_UNCOMPRESSED_CHUNK_SIZE) { await FlushChunkAsync(cancellationToken).ConfigureAwait(false); } @@ -116,13 +127,13 @@ internal sealed class Lzma2EncoderStream : Stream { cancellationToken.ThrowIfCancellationRequested(); - var toCopy = Math.Min(count, _buffer.Length - _bufferPosition); + var toCopy = Math.Min(count, MAX_UNCOMPRESSED_CHUNK_SIZE - _bufferPosition); buffer.Slice(offset, toCopy).Span.CopyTo(_buffer.AsSpan(_bufferPosition, toCopy)); _bufferPosition += toCopy; offset += toCopy; count -= toCopy; - if (_bufferPosition == _buffer.Length) + if (_bufferPosition == MAX_UNCOMPRESSED_CHUNK_SIZE) { await FlushChunkAsync(cancellationToken).ConfigureAwait(false); } @@ -153,6 +164,8 @@ internal sealed class Lzma2EncoderStream : Stream // Write LZMA2 end marker _output.WriteByte(0x00); + _encoder.Dispose(); + ArrayPool.Shared.Return(_buffer); } base.Dispose(disposing); } @@ -179,6 +192,9 @@ internal sealed class Lzma2EncoderStream : Stream #else await _output.WriteAsync(_endMarker, 0, 1).ConfigureAwait(false); #endif + + _encoder.Dispose(); + ArrayPool.Shared.Return(_buffer); } #if !LEGACY_DOTNET || NETSTANDARD2_1 @@ -193,14 +209,15 @@ internal sealed class Lzma2EncoderStream : Stream return; } - var uncompressedData = _buffer.AsSpan(0, _bufferPosition); + var uncompressedSize = _bufferPosition; + var uncompressedData = _buffer.AsSpan(0, uncompressedSize); _bufferPosition = 0; // Try compressing the data - byte[] compressed; + (byte[] Buffer, int Length) compressed; try { - compressed = CompressBlock(uncompressedData); + compressed = CompressBlock(_buffer, uncompressedSize); } catch (Exception ex) when (ex is not OutOfMemoryException and not StackOverflowException) { @@ -215,7 +232,7 @@ internal sealed class Lzma2EncoderStream : Stream && compressed.Length < uncompressedData.Length ) { - WriteCompressedChunk(uncompressedData.Length, compressed); + WriteCompressedChunk(uncompressedData.Length, compressed.Buffer, compressed.Length); } else { @@ -230,13 +247,14 @@ internal sealed class Lzma2EncoderStream : Stream return; } - var uncompressedData = _buffer.AsMemory(0, _bufferPosition); + var uncompressedSize = _bufferPosition; + var uncompressedData = _buffer.AsMemory(0, uncompressedSize); _bufferPosition = 0; - byte[] compressed; + (byte[] Buffer, int Length) compressed; try { - compressed = CompressBlock(uncompressedData.Span); + compressed = CompressBlock(_buffer, uncompressedSize); } catch (Exception ex) when (ex is not OutOfMemoryException and not StackOverflowException) { @@ -250,7 +268,12 @@ internal sealed class Lzma2EncoderStream : Stream && compressed.Length < uncompressedData.Length ) { - await WriteCompressedChunkAsync(uncompressedData.Length, compressed, cancellationToken) + await WriteCompressedChunkAsync( + uncompressedData.Length, + compressed.Buffer, + compressed.Length, + cancellationToken + ) .ConfigureAwait(false); } else @@ -260,45 +283,26 @@ internal sealed class Lzma2EncoderStream : Stream } } - private byte[] CompressBlock(ReadOnlySpan data) + private (byte[] Buffer, int Length) CompressBlock(byte[] data, int length) { - var encoderProps = new LzmaEncoderProperties(eos: false, _dictionarySize, _numFastBytes); - - var encoder = new Encoder(); - encoder.SetCoderProperties(encoderProps.PropIDs, encoderProps.Properties); - - // Capture the LZMA properties byte (pb/lp/lc encoding) for the chunk header - if (!_lzmaPropertiesKnown) - { - var propBytes = new byte[5]; - encoder.WriteCoderProperties(propBytes); - _lzmaPropertiesByte = propBytes[0]; - _lzmaPropertiesKnown = true; - } - - using var inputMs = new MemoryStream(data.ToArray(), writable: false); + using var inputMs = new MemoryStream(data, 0, length, writable: false); using var outputMs = new PooledMemoryStream(); - encoder.Code(inputMs, outputMs, data.Length, -1, null); + _encoder.Code(inputMs, outputMs, length, -1, null); var fullCompressed = outputMs.ToArray(); // The LZMA range encoder flush writes trailing bytes the decoder doesn't consume. // Trial-decode to find the exact byte count the decoder needs, so the LZMA2 // chunk header reports a compressed size that matches what the decoder reads. - var consumed = FindConsumedBytes(fullCompressed, data.Length); - if (consumed < fullCompressed.Length) - { - return fullCompressed.AsSpan(0, consumed).ToArray(); - } - - return fullCompressed; + var consumed = FindConsumedBytes(fullCompressed, fullCompressed.Length, length); + return (fullCompressed, consumed); } - private int FindConsumedBytes(byte[] compressedData, int uncompressedSize) + private int FindConsumedBytes(byte[] compressedData, int compressedSize, int uncompressedSize) { // Build 5-byte LZMA property header: [pb/lp/lc byte] [dictSize as LE int32] - var props = new byte[5]; + Span props = stackalloc byte[5]; props[0] = _lzmaPropertiesByte; props[1] = (byte)_dictionarySize; props[2] = (byte)(_dictionarySize >> 8); @@ -308,9 +312,8 @@ internal sealed class Lzma2EncoderStream : Stream var decoder = new Decoder(); decoder.SetDecoderProperties(props); - using var input = new MemoryStream(compressedData); - using var output = new PooledMemoryStream(); - decoder.Code(input, output, compressedData.Length, uncompressedSize, null); + using var input = new MemoryStream(compressedData, 0, compressedSize, writable: false); + decoder.Code(input, Stream.Null, compressedSize, uncompressedSize, null); return (int)input.Position; } @@ -319,10 +322,14 @@ internal sealed class Lzma2EncoderStream : Stream /// Writes a compressed LZMA2 chunk. /// Header: [control] [uncompSize_hi] [uncompSize_lo] [compSize_hi] [compSize_lo] [props?] /// - private void WriteCompressedChunk(int uncompressedSize, byte[] compressedData) + private void WriteCompressedChunk( + int uncompressedSize, + byte[] compressedData, + int compressedSize + ) { var uncompSizeMinus1 = uncompressedSize - 1; - var compSizeMinus1 = compressedData.Length - 1; + var compSizeMinus1 = compressedSize - 1; // Each chunk is compressed independently with a fresh LZMA encoder, // so we must use 0xE0 (full reset: dictionary + state + properties) every time. @@ -341,32 +348,38 @@ internal sealed class Lzma2EncoderStream : Stream // 0xE0 (>= 0xC0) requires properties byte _output.WriteByte(_lzmaPropertiesByte); - _output.Write(compressedData, 0, compressedData.Length); + _output.Write(compressedData, 0, compressedSize); } private async ValueTask WriteCompressedChunkAsync( int uncompressedSize, byte[] compressedData, + int compressedSize, CancellationToken cancellationToken = default ) { var uncompSizeMinus1 = uncompressedSize - 1; - var compSizeMinus1 = compressedData.Length - 1; + var compSizeMinus1 = compressedSize - 1; var control = (byte)(0xE0 | ((uncompSizeMinus1 >> 16) & 0x1F)); _isFirstChunk = false; - var header = new[] + var header = ArrayPool.Shared.Rent(6); + try { - control, - (byte)((uncompSizeMinus1 >> 8) & 0xFF), - (byte)(uncompSizeMinus1 & 0xFF), - (byte)((compSizeMinus1 >> 8) & 0xFF), - (byte)(compSizeMinus1 & 0xFF), - _lzmaPropertiesByte, - }; - await _output.WriteAsync(header, 0, header.Length, cancellationToken).ConfigureAwait(false); + header[0] = control; + header[1] = (byte)((uncompSizeMinus1 >> 8) & 0xFF); + header[2] = (byte)(uncompSizeMinus1 & 0xFF); + header[3] = (byte)((compSizeMinus1 >> 8) & 0xFF); + header[4] = (byte)(compSizeMinus1 & 0xFF); + header[5] = _lzmaPropertiesByte; + await _output.WriteAsync(header, 0, 6, cancellationToken).ConfigureAwait(false); + } + finally + { + ArrayPool.Shared.Return(header); + } await _output - .WriteAsync(compressedData, 0, compressedData.Length, cancellationToken) + .WriteAsync(compressedData, 0, compressedSize, cancellationToken) .ConfigureAwait(false); } @@ -426,20 +439,37 @@ internal sealed class Lzma2EncoderStream : Stream control = 0x02; } - var header = new[] + var header = ArrayPool.Shared.Rent(3); + try { - control, - (byte)((sizeMinus1 >> 8) & 0xFF), - (byte)(sizeMinus1 & 0xFF), - }; - await _output - .WriteAsync(header, 0, header.Length, cancellationToken) - .ConfigureAwait(false); + header[0] = control; + header[1] = (byte)((sizeMinus1 >> 8) & 0xFF); + header[2] = (byte)(sizeMinus1 & 0xFF); + await _output.WriteAsync(header, 0, 3, cancellationToken).ConfigureAwait(false); + } + finally + { + ArrayPool.Shared.Return(header); + } - var chunk = data.Slice(offset, chunkSize).ToArray(); +#if !LEGACY_DOTNET || NETSTANDARD2_1 await _output - .WriteAsync(chunk, 0, chunk.Length, cancellationToken) + .WriteAsync(data.Slice(offset, chunkSize), cancellationToken) .ConfigureAwait(false); +#else + var chunk = ArrayPool.Shared.Rent(chunkSize); + try + { + data.Slice(offset, chunkSize).CopyTo(chunk); + await _output + .WriteAsync(chunk, 0, chunkSize, cancellationToken) + .ConfigureAwait(false); + } + finally + { + ArrayPool.Shared.Return(chunk); + } +#endif offset += chunkSize; } } diff --git a/src/SharpCompress/Compressors/LZMA/LzmaDecoder.Async.cs b/src/SharpCompress/Compressors/LZMA/LzmaDecoder.Async.cs index 18e1475f..7b81b892 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaDecoder.Async.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaDecoder.Async.cs @@ -72,7 +72,7 @@ public partial class Decoder : IAsyncDisposable { symbol = (symbol << 1) - | await _decoders[symbol] + | await _decoders[_baseIndex + symbol] .DecodeAsync(rangeDecoder, cancellationToken) .ConfigureAwait(false); } while (symbol < 0x100); @@ -90,7 +90,7 @@ public partial class Decoder : IAsyncDisposable { var matchBit = (uint)(matchByte >> 7) & 1; matchByte <<= 1; - var bit = await _decoders[((1 + matchBit) << 8) + symbol] + var bit = await _decoders[_baseIndex + ((1 + matchBit) << 8) + symbol] .DecodeAsync(rangeDecoder, cancellationToken) .ConfigureAwait(false); symbol = (symbol << 1) | bit; @@ -100,7 +100,7 @@ public partial class Decoder : IAsyncDisposable { symbol = (symbol << 1) - | await _decoders[symbol] + | await _decoders[_baseIndex + symbol] .DecodeAsync(rangeDecoder, cancellationToken) .ConfigureAwait(false); } diff --git a/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs b/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs index 49a0e4c2..bdc189ff 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs @@ -73,14 +73,19 @@ public partial class Decoder : ICoder, ISetDecoderProperties, IDisposable private partial struct Decoder2 { private BitDecoder[] _decoders; + private int _baseIndex; - public void Create() => _decoders = new BitDecoder[0x300]; + public void Create(BitDecoder[] decoders, int baseIndex) + { + _decoders = decoders; + _baseIndex = baseIndex; + } public void Init() { for (var i = 0; i < 0x300; i++) { - _decoders[i].Init(); + _decoders[_baseIndex + i].Init(); } } @@ -89,7 +94,7 @@ public partial class Decoder : ICoder, ISetDecoderProperties, IDisposable uint symbol = 1; do { - symbol = (symbol << 1) | _decoders[symbol].Decode(rangeDecoder); + symbol = (symbol << 1) | _decoders[_baseIndex + symbol].Decode(rangeDecoder); } while (symbol < 0x100); return (byte)symbol; } @@ -101,13 +106,15 @@ public partial class Decoder : ICoder, ISetDecoderProperties, IDisposable { var matchBit = (uint)(matchByte >> 7) & 1; matchByte <<= 1; - var bit = _decoders[((1 + matchBit) << 8) + symbol].Decode(rangeDecoder); + var bit = _decoders[_baseIndex + ((1 + matchBit) << 8) + symbol] + .Decode(rangeDecoder); symbol = (symbol << 1) | bit; if (matchBit != bit) { while (symbol < 0x100) { - symbol = (symbol << 1) | _decoders[symbol].Decode(rangeDecoder); + symbol = + (symbol << 1) | _decoders[_baseIndex + symbol].Decode(rangeDecoder); } break; } @@ -117,6 +124,7 @@ public partial class Decoder : ICoder, ISetDecoderProperties, IDisposable } private Decoder2[] _coders; + private BitDecoder[] _models; private int _numPrevBits; private int _numPosBits; private uint _posMask; @@ -131,10 +139,11 @@ public partial class Decoder : ICoder, ISetDecoderProperties, IDisposable _posMask = ((uint)1 << numPosBits) - 1; _numPrevBits = numPrevBits; var numStates = (uint)1 << (_numPrevBits + _numPosBits); + _models = new BitDecoder[checked((int)(numStates * 0x300))]; _coders = new Decoder2[numStates]; for (uint i = 0; i < numStates; i++) { - _coders[i].Create(); + _coders[i].Create(_models, checked((int)(i * 0x300))); } } @@ -446,7 +455,10 @@ public partial class Decoder : ICoder, ISetDecoderProperties, IDisposable return false; } - public void SetDecoderProperties(byte[] properties) + public void SetDecoderProperties(byte[] properties) => + SetDecoderProperties(properties.AsSpan()); + + internal void SetDecoderProperties(ReadOnlySpan properties) { if (properties.Length < 1) { diff --git a/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs b/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs index 14d94707..c8d5279b 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs @@ -1,6 +1,7 @@ #nullable disable using System; +using System.Buffers; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -10,7 +11,7 @@ using SharpCompress.Compressors.LZMA.RangeCoder; namespace SharpCompress.Compressors.LZMA; -internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties +internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties, IDisposable { private enum EMatchFinderType { @@ -86,14 +87,19 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert public partial struct Encoder2 { private BitEncoder[] _encoders; + private int _baseIndex; - public void Create() => _encoders = new BitEncoder[0x300]; + public void Create(BitEncoder[] encoders, int baseIndex) + { + _encoders = encoders; + _baseIndex = baseIndex; + } public void Init() { for (var i = 0; i < 0x300; i++) { - _encoders[i].Init(); + _encoders[_baseIndex + i].Init(); } } @@ -103,7 +109,7 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert for (var i = 7; i >= 0; i--) { var bit = (uint)((symbol >> i) & 1); - _encoders[context].Encode(rangeEncoder, bit); + _encoders[_baseIndex + context].Encode(rangeEncoder, bit); context = (context << 1) | bit; } } @@ -118,7 +124,7 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert for (var i = 7; i >= 0; i--) { var bit = (uint)((symbol >> i) & 1); - await _encoders[context] + await _encoders[_baseIndex + context] .EncodeAsync(rangeEncoder, bit, cancellationToken) .ConfigureAwait(false); context = (context << 1) | bit; @@ -139,7 +145,7 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert state += ((1 + matchBit) << 8); same = (matchBit == bit); } - _encoders[state].Encode(rangeEncoder, bit); + _encoders[_baseIndex + state].Encode(rangeEncoder, bit); context = (context << 1) | bit; } } @@ -163,7 +169,7 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert state += ((1 + matchBit) << 8); same = matchBit == bit; } - await _encoders[state] + await _encoders[_baseIndex + state] .EncodeAsync(rangeEncoder, bit, cancellationToken) .ConfigureAwait(false); context = (context << 1) | bit; @@ -181,7 +187,8 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert { var matchBit = (uint)(matchByte >> i) & 1; var bit = (uint)(symbol >> i) & 1; - price += _encoders[((1 + matchBit) << 8) + context].GetPrice(bit); + price += _encoders[_baseIndex + ((1 + matchBit) << 8) + context] + .GetPrice(bit); context = (context << 1) | bit; if (matchBit != bit) { @@ -193,7 +200,7 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert for (; i >= 0; i--) { var bit = (uint)(symbol >> i) & 1; - price += _encoders[context].GetPrice(bit); + price += _encoders[_baseIndex + context].GetPrice(bit); context = (context << 1) | bit; } return price; @@ -201,6 +208,7 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert } private Encoder2[] _coders; + private BitEncoder[] _models; private int _numPrevBits; private int _numPosBits; private uint _posMask; @@ -215,13 +223,37 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert _posMask = ((uint)1 << numPosBits) - 1; _numPrevBits = numPrevBits; var numStates = (uint)1 << (_numPrevBits + _numPosBits); - _coders = new Encoder2[numStates]; + var requiredModelLength = checked((int)(numStates * 0x300)); + if (_models is null || _models.Length < requiredModelLength) + { + if (_models is not null) + { + ArrayPool.Shared.Return(_models); + } + _models = ArrayPool.Shared.Rent(requiredModelLength); + } + if (_coders is null || _coders.Length != numStates) + { + _coders = new Encoder2[numStates]; + } for (uint i = 0; i < numStates; i++) { - _coders[i].Create(); + _coders[i].Create(_models, checked((int)(i * 0x300))); } } + public void Dispose() + { + if (_models is null) + { + return; + } + + ArrayPool.Shared.Return(_models); + _models = null; + _coders = null; + } + public void Init() { var numStates = (uint)1 << (_numPrevBits + _numPosBits); @@ -423,7 +455,7 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert private const uint K_NUM_OPTS = 1 << 12; - private class Optimal + private struct Optimal { public Base.State _state; @@ -564,16 +596,19 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert public Encoder() { - for (var i = 0; i < K_NUM_OPTS; i++) - { - _optimum[i] = new Optimal(); - } for (var i = 0; i < Base.K_NUM_LEN_TO_POS_STATES; i++) { _posSlotEncoder[i] = new BitTreeEncoder(Base.K_NUM_POS_SLOT_BITS); } } + public void Dispose() + { + _literalEncoder.Dispose(); + _matchFinder?.Dispose(); + _matchFinder = null; + } + private void SetWriteEndMarkerMode(bool writeEndMarker) => _writeEndMark = writeEndMarker; private void Init() @@ -859,7 +894,7 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert do { var curAndLenPrice = price + _repMatchLenEncoder.GetPrice(repLen - 2, posState); - var optimum = _optimum[repLen]; + ref var optimum = ref _optimum[repLen]; if (curAndLenPrice < optimum._price) { optimum._price = curAndLenPrice; @@ -884,7 +919,7 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert { var distance = _matchDistances[offs + 1]; var curAndLenPrice = normalMatchPrice + GetPosLenPrice(distance, len, posState); - var optimum = _optimum[len]; + ref var optimum = ref _optimum[len]; if (curAndLenPrice < optimum._price) { optimum._price = curAndLenPrice; @@ -1039,7 +1074,7 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert .GetSubCoder(position, _matchFinder.GetIndexByte(0 - 2)) .GetPrice(!state.IsCharState(), matchByte, currentByte); - var nextOptimum = _optimum[cur + 1]; + ref var nextOptimum = ref _optimum[cur + 1]; var nextIsChar = false; if (curAnd1Price < nextOptimum._price) @@ -1105,7 +1140,7 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert } var curAndLenPrice = nextRepMatchPrice + GetRepPrice(0, lenTest2, state2, posStateNext); - var optimum = _optimum[offset]; + ref var optimum = ref _optimum[offset]; if (curAndLenPrice < optimum._price) { optimum._price = curAndLenPrice; @@ -1136,7 +1171,7 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert } var curAndLenPrice = repMatchPrice + GetRepPrice(repIndex, lenTest, state, posState); - var optimum = _optimum[cur + lenTest]; + ref var optimum = ref _optimum[cur + lenTest]; if (curAndLenPrice < optimum._price) { optimum._price = curAndLenPrice; @@ -1200,7 +1235,7 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert } var curAndLenPrice = nextRepMatchPrice + GetRepPrice(0, lenTest2, state2, posStateNext); - var optimum = _optimum[cur + offset]; + ref var optimum = ref _optimum[cur + offset]; if (curAndLenPrice < optimum._price) { optimum._price = curAndLenPrice; @@ -1249,7 +1284,7 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert var curBack = _matchDistances[offs + 1]; var curAndLenPrice = normalMatchPrice + GetPosLenPrice(curBack, lenTest, posState); - var optimum = _optimum[cur + lenTest]; + ref var optimum = ref _optimum[cur + lenTest]; if (curAndLenPrice < optimum._price) { optimum._price = curAndLenPrice; @@ -1308,7 +1343,7 @@ internal partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderPropert curAndLenPrice = nextRepMatchPrice + GetRepPrice(0, lenTest2, state2, posStateNext); - optimum = _optimum[cur + offset]; + optimum = ref _optimum[cur + offset]; if (curAndLenPrice < optimum._price) { optimum._price = curAndLenPrice; diff --git a/src/SharpCompress/Compressors/LZMA/LzmaStream.Async.cs b/src/SharpCompress/Compressors/LZMA/LzmaStream.Async.cs index 3adef73d..4ad30127 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaStream.Async.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaStream.Async.cs @@ -83,11 +83,11 @@ public partial class LzmaStream private async ValueTask DecodeChunkHeaderAsync(CancellationToken cancellationToken = default) { - var controlBuffer = new byte[1]; + var headerBuffer = GetAsyncHeaderBuffer(); await _inputStream! - .ReadExactAsync(controlBuffer, 0, 1, cancellationToken) + .ReadExactAsync(headerBuffer, 0, 1, cancellationToken) .ConfigureAwait(false); - var control = controlBuffer[0]; + var control = headerBuffer[0]; _inputPosition++; if (control == 0x00) @@ -112,26 +112,25 @@ public partial class LzmaStream _uncompressedChunk = false; _availableBytes = (control & 0x1F) << 16; - var buffer = new byte[2]; await _inputStream! - .ReadExactAsync(buffer, 0, 2, cancellationToken) + .ReadExactAsync(headerBuffer, 0, 2, cancellationToken) .ConfigureAwait(false); - _availableBytes += (buffer[0] << 8) + buffer[1] + 1; + _availableBytes += (headerBuffer[0] << 8) + headerBuffer[1] + 1; _inputPosition += 2; await _inputStream! - .ReadExactAsync(buffer, 0, 2, cancellationToken) + .ReadExactAsync(headerBuffer, 0, 2, cancellationToken) .ConfigureAwait(false); - _rangeDecoderLimit = (buffer[0] << 8) + buffer[1] + 1; + _rangeDecoderLimit = (headerBuffer[0] << 8) + headerBuffer[1] + 1; _inputPosition += 2; if (control >= 0xC0) { _needProps = false; await _inputStream! - .ReadExactAsync(controlBuffer, 0, 1, cancellationToken) + .ReadExactAsync(headerBuffer, 0, 1, cancellationToken) .ConfigureAwait(false); - Properties[0] = controlBuffer[0]; + Properties[0] = headerBuffer[0]; _inputPosition++; _decoder = new Decoder(); @@ -156,11 +155,10 @@ public partial class LzmaStream else { _uncompressedChunk = true; - var buffer = new byte[2]; await _inputStream! - .ReadExactAsync(buffer, 0, 2, cancellationToken) + .ReadExactAsync(headerBuffer, 0, 2, cancellationToken) .ConfigureAwait(false); - _availableBytes = (buffer[0] << 8) + buffer[1] + 1; + _availableBytes = (headerBuffer[0] << 8) + headerBuffer[1] + 1; _inputPosition += 2; } } @@ -436,6 +434,7 @@ public partial class LzmaStream if (_encoder != null) { _position = await _encoder.CodeAsync(null, true).ConfigureAwait(false); + _encoder.Dispose(); } if (!_leaveOpen) @@ -450,6 +449,7 @@ public partial class LzmaStream } } await _outWindow.DisposeAsync().ConfigureAwait(false); + ReturnAsyncHeaderBuffer(); #if !LEGACY_DOTNET || NETSTANDARD2_1 await base.DisposeAsync().ConfigureAwait(false); diff --git a/src/SharpCompress/Compressors/LZMA/LzmaStream.cs b/src/SharpCompress/Compressors/LZMA/LzmaStream.cs index 12f6884e..38bb2eca 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaStream.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaStream.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.Buffers.Binary; using System.IO; using System.Threading; @@ -33,6 +34,7 @@ public partial class LzmaStream : Stream, IStreamStack, IAsyncDisposable private bool _needProps = true; private readonly Encoder? _encoder; + private byte[]? _asyncHeaderBuffer; private bool _isDisposed; private LzmaStream( @@ -205,16 +207,31 @@ public partial class LzmaStream : Stream, IStreamStack, IAsyncDisposable if (_encoder != null) { _position = _encoder.Code(null, true); + _encoder.Dispose(); } if (!_leaveOpen) { _inputStream?.Dispose(); } _outWindow.Dispose(); + ReturnAsyncHeaderBuffer(); } base.Dispose(disposing); } + private byte[] GetAsyncHeaderBuffer() => _asyncHeaderBuffer ??= ArrayPool.Shared.Rent(6); + + private void ReturnAsyncHeaderBuffer() + { + if (_asyncHeaderBuffer is null) + { + return; + } + + ArrayPool.Shared.Return(_asyncHeaderBuffer); + _asyncHeaderBuffer = null; + } + public override long Length => _position + _availableBytes; public override long Position diff --git a/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.Async.cs b/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.Async.cs index cd3fc6a5..61db6eae 100644 --- a/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.Async.cs +++ b/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.Async.cs @@ -9,6 +9,8 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder; internal partial class Encoder { + private byte[] SingleByteBuffer => _singleByteBuffer ??= new byte[1]; + public async ValueTask ShiftLowAsync(CancellationToken cancellationToken = default) { if ((uint)_low < 0xFF000000 || (uint)(_low >> 32) == 1) @@ -17,7 +19,8 @@ internal partial class Encoder do { var b = (byte)(temp + (_low >> 32)); - var buffer = new[] { b }; + var buffer = SingleByteBuffer; + buffer[0] = b; await _stream.WriteAsync(buffer, 0, 1, cancellationToken).ConfigureAwait(false); temp = 0xFF; } while (--_cacheSize != 0); @@ -78,13 +81,15 @@ internal partial class Encoder internal partial class Decoder { + private byte[] SingleByteBuffer => _singleByteBuffer ??= new byte[1]; + public async ValueTask InitAsync(Stream stream, CancellationToken cancellationToken = default) { _stream = stream; _code = 0; _range = 0xFFFFFFFF; - var buffer = new byte[1]; + var buffer = SingleByteBuffer; for (var i = 0; i < 5; i++) { var read = await _stream @@ -103,7 +108,7 @@ internal partial class Decoder { while (_range < K_TOP_VALUE) { - var buffer = new byte[1]; + var buffer = SingleByteBuffer; var read = await _stream .ReadAsync(buffer, 0, 1, cancellationToken) .ConfigureAwait(false); @@ -121,7 +126,7 @@ internal partial class Decoder { if (_range < K_TOP_VALUE) { - var buffer = new byte[1]; + var buffer = SingleByteBuffer; var read = await _stream .ReadAsync(buffer, 0, 1, cancellationToken) .ConfigureAwait(false); @@ -143,7 +148,7 @@ internal partial class Decoder var range = _range; var code = _code; uint result = 0; - var buffer = new byte[1]; + var buffer = SingleByteBuffer; for (var i = numTotalBits; i > 0; i--) { range >>= 1; diff --git a/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.cs b/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.cs index e29a1c37..147146c4 100644 --- a/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.cs +++ b/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.cs @@ -15,8 +15,7 @@ internal partial class Encoder public uint _range; private uint _cacheSize; private byte _cache; - - //long StartPosition; + private byte[] _singleByteBuffer = new byte[1]; public void SetStream(Stream stream) => _stream = stream; @@ -88,6 +87,7 @@ internal partial class Decoder public Stream _stream; public long _total; + private byte[] _singleByteBuffer; public void Init(Stream stream) { @@ -180,6 +180,4 @@ internal partial class Decoder } public bool IsFinished => _code == 0; - - // ulong GetProcessedSize() {return Stream.GetProcessedSize(); } } diff --git a/src/SharpCompress/Polyfills/AsyncEnumerableExtensions.cs b/src/SharpCompress/Polyfills/AsyncEnumerableExtensions.cs index 51b6bfc8..df716ecd 100644 --- a/src/SharpCompress/Polyfills/AsyncEnumerableExtensions.cs +++ b/src/SharpCompress/Polyfills/AsyncEnumerableExtensions.cs @@ -20,7 +20,6 @@ public static class EnumerableExtensions { public static async IAsyncEnumerable ToAsyncEnumerable(this IEnumerable source) { - await Task.Yield(); foreach (var item in source) { yield return item; diff --git a/src/SharpCompress/packages.lock.json b/src/SharpCompress/packages.lock.json index 738df599..de2edfd6 100644 --- a/src/SharpCompress/packages.lock.json +++ b/src/SharpCompress/packages.lock.json @@ -22,12 +22,13 @@ }, "Microsoft.SourceLink.GitHub": { "type": "Direct", - "requested": "[10.0.102, )", - "resolved": "10.0.102", - "contentHash": "Oxq3RCIJSdtpIU4hLqO7XaDe/Ra3HS9Wi8rJl838SAg6Zu1iQjerA0+xXWBgUFYbgknUGCLOU0T+lzMLkvY9Qg==", + "requested": "[10.0.300, )", + "resolved": "10.0.300", + "contentHash": "QzCtLkXVb3l4IxcpvJCbzUwMLihAmLN6vVLjQGSzYSF8d2dvXxqJAZk83RV3gYnp2egz8jRMgSR2woY3vOahTA==", "dependencies": { - "Microsoft.Build.Tasks.Git": "10.0.102", - "Microsoft.SourceLink.Common": "10.0.102" + "Microsoft.Build.Tasks.Git": "10.0.300", + "Microsoft.SourceLink.Common": "10.0.300", + "System.IO.Hashing": "10.0.8" } }, "Microsoft.VisualStudio.Threading.Analyzers": { @@ -38,9 +39,9 @@ }, "PolySharp": { "type": "Direct", - "requested": "[1.15.0, )", - "resolved": "1.15.0", - "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g==" + "requested": "[1.16.0, )", + "resolved": "1.16.0", + "contentHash": "3kdIIceBPumwjw279FuiVMfVENT2cGASXJgcigdySsbX2dJB8ofUgG6i47yqF/k1qu6fvNR3csrSekZPviR6kQ==" }, "System.Text.Encoding.CodePages": { "type": "Direct", @@ -54,8 +55,11 @@ }, "Microsoft.Build.Tasks.Git": { "type": "Transitive", - "resolved": "10.0.102", - "contentHash": "0i81LYX31U6UiXz4NOLbvc++u+/mVDmOt+PskrM/MygpDxkv9THKQyRUmavBpLK6iBV0abNWnn+CQgSRz//Pwg==" + "resolved": "10.0.300", + "contentHash": "P0kaQwVZx4xIUe2FtrLyBadYNXuAljttJUPvjBYRuHhPE8L77L42KakLDkaADRiUrGspoLcMwayjrbQhYTr0zA==", + "dependencies": { + "System.IO.Hashing": "10.0.8" + } }, "Microsoft.NETFramework.ReferenceAssemblies.net48": { "type": "Transitive", @@ -64,33 +68,42 @@ }, "Microsoft.SourceLink.Common": { "type": "Transitive", - "resolved": "10.0.102", - "contentHash": "Mk1IMb9q5tahC2NltxYXFkLBtuBvfBoCQ3pIxYQWfzbCE9o1OB9SsHe0hnNGo7lWgTA/ePbFAJLWu6nLL9K17A==" + "resolved": "10.0.300", + "contentHash": "0jlkXaUGjYlWTIVPve5MftjKHnT3SlAtq9BCLV4J9IjdPrxV/+4rMlBSjfr1khG8/GC6KGojjola8E1VvWF0qQ==" }, "System.Buffers": { "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + "resolved": "4.6.1", + "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" + }, + "System.IO.Hashing": { + "type": "Transitive", + "resolved": "10.0.8", + "contentHash": "+dJsbPJ3FyUbTZNplFj0RCKePFizmv6ewDV46JE9q/IVH4c3xTCftHfHelLsAKf0jryIPqgMb5GpS0x7TAY3mg==", + "dependencies": { + "System.Buffers": "4.6.1", + "System.Memory": "4.6.3" + } }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "resolved": "4.6.3", + "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.5.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" + "System.Buffers": "4.6.1", + "System.Numerics.Vectors": "4.6.1", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" } }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + "resolved": "4.6.1", + "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "6.1.2", + "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" }, "System.Threading.Tasks.Extensions": { "type": "Transitive", @@ -122,12 +135,13 @@ }, "Microsoft.SourceLink.GitHub": { "type": "Direct", - "requested": "[10.0.102, )", - "resolved": "10.0.102", - "contentHash": "Oxq3RCIJSdtpIU4hLqO7XaDe/Ra3HS9Wi8rJl838SAg6Zu1iQjerA0+xXWBgUFYbgknUGCLOU0T+lzMLkvY9Qg==", + "requested": "[10.0.300, )", + "resolved": "10.0.300", + "contentHash": "QzCtLkXVb3l4IxcpvJCbzUwMLihAmLN6vVLjQGSzYSF8d2dvXxqJAZk83RV3gYnp2egz8jRMgSR2woY3vOahTA==", "dependencies": { - "Microsoft.Build.Tasks.Git": "10.0.102", - "Microsoft.SourceLink.Common": "10.0.102" + "Microsoft.Build.Tasks.Git": "10.0.300", + "Microsoft.SourceLink.Common": "10.0.300", + "System.IO.Hashing": "10.0.8" } }, "Microsoft.VisualStudio.Threading.Analyzers": { @@ -147,9 +161,9 @@ }, "PolySharp": { "type": "Direct", - "requested": "[1.15.0, )", - "resolved": "1.15.0", - "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g==" + "requested": "[1.16.0, )", + "resolved": "1.16.0", + "contentHash": "3kdIIceBPumwjw279FuiVMfVENT2cGASXJgcigdySsbX2dJB8ofUgG6i47yqF/k1qu6fvNR3csrSekZPviR6kQ==" }, "System.Text.Encoding.CodePages": { "type": "Direct", @@ -163,8 +177,11 @@ }, "Microsoft.Build.Tasks.Git": { "type": "Transitive", - "resolved": "10.0.102", - "contentHash": "0i81LYX31U6UiXz4NOLbvc++u+/mVDmOt+PskrM/MygpDxkv9THKQyRUmavBpLK6iBV0abNWnn+CQgSRz//Pwg==" + "resolved": "10.0.300", + "contentHash": "P0kaQwVZx4xIUe2FtrLyBadYNXuAljttJUPvjBYRuHhPE8L77L42KakLDkaADRiUrGspoLcMwayjrbQhYTr0zA==", + "dependencies": { + "System.IO.Hashing": "10.0.8" + } }, "Microsoft.NETCore.Platforms": { "type": "Transitive", @@ -178,33 +195,42 @@ }, "Microsoft.SourceLink.Common": { "type": "Transitive", - "resolved": "10.0.102", - "contentHash": "Mk1IMb9q5tahC2NltxYXFkLBtuBvfBoCQ3pIxYQWfzbCE9o1OB9SsHe0hnNGo7lWgTA/ePbFAJLWu6nLL9K17A==" + "resolved": "10.0.300", + "contentHash": "0jlkXaUGjYlWTIVPve5MftjKHnT3SlAtq9BCLV4J9IjdPrxV/+4rMlBSjfr1khG8/GC6KGojjola8E1VvWF0qQ==" }, "System.Buffers": { "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + "resolved": "4.6.1", + "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" + }, + "System.IO.Hashing": { + "type": "Transitive", + "resolved": "10.0.8", + "contentHash": "+dJsbPJ3FyUbTZNplFj0RCKePFizmv6ewDV46JE9q/IVH4c3xTCftHfHelLsAKf0jryIPqgMb5GpS0x7TAY3mg==", + "dependencies": { + "System.Buffers": "4.6.1", + "System.Memory": "4.6.3" + } }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "resolved": "4.6.3", + "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.4.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" + "System.Buffers": "4.6.1", + "System.Numerics.Vectors": "4.6.1", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" } }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + "resolved": "4.6.1", + "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "6.1.2", + "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" }, "System.Threading.Tasks.Extensions": { "type": "Transitive", @@ -233,12 +259,13 @@ }, "Microsoft.SourceLink.GitHub": { "type": "Direct", - "requested": "[10.0.102, )", - "resolved": "10.0.102", - "contentHash": "Oxq3RCIJSdtpIU4hLqO7XaDe/Ra3HS9Wi8rJl838SAg6Zu1iQjerA0+xXWBgUFYbgknUGCLOU0T+lzMLkvY9Qg==", + "requested": "[10.0.300, )", + "resolved": "10.0.300", + "contentHash": "QzCtLkXVb3l4IxcpvJCbzUwMLihAmLN6vVLjQGSzYSF8d2dvXxqJAZk83RV3gYnp2egz8jRMgSR2woY3vOahTA==", "dependencies": { - "Microsoft.Build.Tasks.Git": "10.0.102", - "Microsoft.SourceLink.Common": "10.0.102" + "Microsoft.Build.Tasks.Git": "10.0.300", + "Microsoft.SourceLink.Common": "10.0.300", + "System.IO.Hashing": "10.0.8" } }, "Microsoft.VisualStudio.Threading.Analyzers": { @@ -249,9 +276,9 @@ }, "PolySharp": { "type": "Direct", - "requested": "[1.15.0, )", - "resolved": "1.15.0", - "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g==" + "requested": "[1.16.0, )", + "resolved": "1.16.0", + "contentHash": "3kdIIceBPumwjw279FuiVMfVENT2cGASXJgcigdySsbX2dJB8ofUgG6i47yqF/k1qu6fvNR3csrSekZPviR6kQ==" }, "System.Text.Encoding.CodePages": { "type": "Direct", @@ -264,8 +291,11 @@ }, "Microsoft.Build.Tasks.Git": { "type": "Transitive", - "resolved": "10.0.102", - "contentHash": "0i81LYX31U6UiXz4NOLbvc++u+/mVDmOt+PskrM/MygpDxkv9THKQyRUmavBpLK6iBV0abNWnn+CQgSRz//Pwg==" + "resolved": "10.0.300", + "contentHash": "P0kaQwVZx4xIUe2FtrLyBadYNXuAljttJUPvjBYRuHhPE8L77L42KakLDkaADRiUrGspoLcMwayjrbQhYTr0zA==", + "dependencies": { + "System.IO.Hashing": "10.0.8" + } }, "Microsoft.NETFramework.ReferenceAssemblies.net461": { "type": "Transitive", @@ -274,8 +304,13 @@ }, "Microsoft.SourceLink.Common": { "type": "Transitive", - "resolved": "10.0.102", - "contentHash": "Mk1IMb9q5tahC2NltxYXFkLBtuBvfBoCQ3pIxYQWfzbCE9o1OB9SsHe0hnNGo7lWgTA/ePbFAJLWu6nLL9K17A==" + "resolved": "10.0.300", + "contentHash": "0jlkXaUGjYlWTIVPve5MftjKHnT3SlAtq9BCLV4J9IjdPrxV/+4rMlBSjfr1khG8/GC6KGojjola8E1VvWF0qQ==" + }, + "System.IO.Hashing": { + "type": "Transitive", + "resolved": "10.0.8", + "contentHash": "+dJsbPJ3FyUbTZNplFj0RCKePFizmv6ewDV46JE9q/IVH4c3xTCftHfHelLsAKf0jryIPqgMb5GpS0x7TAY3mg==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", @@ -301,12 +336,13 @@ }, "Microsoft.SourceLink.GitHub": { "type": "Direct", - "requested": "[10.0.102, )", - "resolved": "10.0.102", - "contentHash": "Oxq3RCIJSdtpIU4hLqO7XaDe/Ra3HS9Wi8rJl838SAg6Zu1iQjerA0+xXWBgUFYbgknUGCLOU0T+lzMLkvY9Qg==", + "requested": "[10.0.300, )", + "resolved": "10.0.300", + "contentHash": "QzCtLkXVb3l4IxcpvJCbzUwMLihAmLN6vVLjQGSzYSF8d2dvXxqJAZk83RV3gYnp2egz8jRMgSR2woY3vOahTA==", "dependencies": { - "Microsoft.Build.Tasks.Git": "10.0.102", - "Microsoft.SourceLink.Common": "10.0.102" + "Microsoft.Build.Tasks.Git": "10.0.300", + "Microsoft.SourceLink.Common": "10.0.300", + "System.IO.Hashing": "10.0.8" } }, "Microsoft.VisualStudio.Threading.Analyzers": { @@ -317,14 +353,17 @@ }, "PolySharp": { "type": "Direct", - "requested": "[1.15.0, )", - "resolved": "1.15.0", - "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g==" + "requested": "[1.16.0, )", + "resolved": "1.16.0", + "contentHash": "3kdIIceBPumwjw279FuiVMfVENT2cGASXJgcigdySsbX2dJB8ofUgG6i47yqF/k1qu6fvNR3csrSekZPviR6kQ==" }, "Microsoft.Build.Tasks.Git": { "type": "Transitive", - "resolved": "10.0.102", - "contentHash": "0i81LYX31U6UiXz4NOLbvc++u+/mVDmOt+PskrM/MygpDxkv9THKQyRUmavBpLK6iBV0abNWnn+CQgSRz//Pwg==" + "resolved": "10.0.300", + "contentHash": "P0kaQwVZx4xIUe2FtrLyBadYNXuAljttJUPvjBYRuHhPE8L77L42KakLDkaADRiUrGspoLcMwayjrbQhYTr0zA==", + "dependencies": { + "System.IO.Hashing": "10.0.8" + } }, "Microsoft.NETFramework.ReferenceAssemblies.net461": { "type": "Transitive", @@ -333,8 +372,13 @@ }, "Microsoft.SourceLink.Common": { "type": "Transitive", - "resolved": "10.0.102", - "contentHash": "Mk1IMb9q5tahC2NltxYXFkLBtuBvfBoCQ3pIxYQWfzbCE9o1OB9SsHe0hnNGo7lWgTA/ePbFAJLWu6nLL9K17A==" + "resolved": "10.0.300", + "contentHash": "0jlkXaUGjYlWTIVPve5MftjKHnT3SlAtq9BCLV4J9IjdPrxV/+4rMlBSjfr1khG8/GC6KGojjola8E1VvWF0qQ==" + }, + "System.IO.Hashing": { + "type": "Transitive", + "resolved": "10.0.8", + "contentHash": "+dJsbPJ3FyUbTZNplFj0RCKePFizmv6ewDV46JE9q/IVH4c3xTCftHfHelLsAKf0jryIPqgMb5GpS0x7TAY3mg==" } }, "net6.0": { @@ -349,12 +393,13 @@ }, "Microsoft.SourceLink.GitHub": { "type": "Direct", - "requested": "[10.0.102, )", - "resolved": "10.0.102", - "contentHash": "Oxq3RCIJSdtpIU4hLqO7XaDe/Ra3HS9Wi8rJl838SAg6Zu1iQjerA0+xXWBgUFYbgknUGCLOU0T+lzMLkvY9Qg==", + "requested": "[10.0.300, )", + "resolved": "10.0.300", + "contentHash": "QzCtLkXVb3l4IxcpvJCbzUwMLihAmLN6vVLjQGSzYSF8d2dvXxqJAZk83RV3gYnp2egz8jRMgSR2woY3vOahTA==", "dependencies": { - "Microsoft.Build.Tasks.Git": "10.0.102", - "Microsoft.SourceLink.Common": "10.0.102" + "Microsoft.Build.Tasks.Git": "10.0.300", + "Microsoft.SourceLink.Common": "10.0.300", + "System.IO.Hashing": "10.0.8" } }, "Microsoft.VisualStudio.Threading.Analyzers": { @@ -365,14 +410,17 @@ }, "PolySharp": { "type": "Direct", - "requested": "[1.15.0, )", - "resolved": "1.15.0", - "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g==" + "requested": "[1.16.0, )", + "resolved": "1.16.0", + "contentHash": "3kdIIceBPumwjw279FuiVMfVENT2cGASXJgcigdySsbX2dJB8ofUgG6i47yqF/k1qu6fvNR3csrSekZPviR6kQ==" }, "Microsoft.Build.Tasks.Git": { "type": "Transitive", - "resolved": "10.0.102", - "contentHash": "0i81LYX31U6UiXz4NOLbvc++u+/mVDmOt+PskrM/MygpDxkv9THKQyRUmavBpLK6iBV0abNWnn+CQgSRz//Pwg==" + "resolved": "10.0.300", + "contentHash": "P0kaQwVZx4xIUe2FtrLyBadYNXuAljttJUPvjBYRuHhPE8L77L42KakLDkaADRiUrGspoLcMwayjrbQhYTr0zA==", + "dependencies": { + "System.IO.Hashing": "10.0.8" + } }, "Microsoft.NETFramework.ReferenceAssemblies.net461": { "type": "Transitive", @@ -381,8 +429,13 @@ }, "Microsoft.SourceLink.Common": { "type": "Transitive", - "resolved": "10.0.102", - "contentHash": "Mk1IMb9q5tahC2NltxYXFkLBtuBvfBoCQ3pIxYQWfzbCE9o1OB9SsHe0hnNGo7lWgTA/ePbFAJLWu6nLL9K17A==" + "resolved": "10.0.300", + "contentHash": "0jlkXaUGjYlWTIVPve5MftjKHnT3SlAtq9BCLV4J9IjdPrxV/+4rMlBSjfr1khG8/GC6KGojjola8E1VvWF0qQ==" + }, + "System.IO.Hashing": { + "type": "Transitive", + "resolved": "10.0.8", + "contentHash": "+dJsbPJ3FyUbTZNplFj0RCKePFizmv6ewDV46JE9q/IVH4c3xTCftHfHelLsAKf0jryIPqgMb5GpS0x7TAY3mg==" } }, "net8.0": { @@ -403,12 +456,13 @@ }, "Microsoft.SourceLink.GitHub": { "type": "Direct", - "requested": "[10.0.102, )", - "resolved": "10.0.102", - "contentHash": "Oxq3RCIJSdtpIU4hLqO7XaDe/Ra3HS9Wi8rJl838SAg6Zu1iQjerA0+xXWBgUFYbgknUGCLOU0T+lzMLkvY9Qg==", + "requested": "[10.0.300, )", + "resolved": "10.0.300", + "contentHash": "QzCtLkXVb3l4IxcpvJCbzUwMLihAmLN6vVLjQGSzYSF8d2dvXxqJAZk83RV3gYnp2egz8jRMgSR2woY3vOahTA==", "dependencies": { - "Microsoft.Build.Tasks.Git": "10.0.102", - "Microsoft.SourceLink.Common": "10.0.102" + "Microsoft.Build.Tasks.Git": "10.0.300", + "Microsoft.SourceLink.Common": "10.0.300", + "System.IO.Hashing": "10.0.8" } }, "Microsoft.VisualStudio.Threading.Analyzers": { @@ -419,14 +473,17 @@ }, "PolySharp": { "type": "Direct", - "requested": "[1.15.0, )", - "resolved": "1.15.0", - "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g==" + "requested": "[1.16.0, )", + "resolved": "1.16.0", + "contentHash": "3kdIIceBPumwjw279FuiVMfVENT2cGASXJgcigdySsbX2dJB8ofUgG6i47yqF/k1qu6fvNR3csrSekZPviR6kQ==" }, "Microsoft.Build.Tasks.Git": { "type": "Transitive", - "resolved": "10.0.102", - "contentHash": "0i81LYX31U6UiXz4NOLbvc++u+/mVDmOt+PskrM/MygpDxkv9THKQyRUmavBpLK6iBV0abNWnn+CQgSRz//Pwg==" + "resolved": "10.0.300", + "contentHash": "P0kaQwVZx4xIUe2FtrLyBadYNXuAljttJUPvjBYRuHhPE8L77L42KakLDkaADRiUrGspoLcMwayjrbQhYTr0zA==", + "dependencies": { + "System.IO.Hashing": "10.0.8" + } }, "Microsoft.NETFramework.ReferenceAssemblies.net461": { "type": "Transitive", @@ -435,8 +492,13 @@ }, "Microsoft.SourceLink.Common": { "type": "Transitive", - "resolved": "10.0.102", - "contentHash": "Mk1IMb9q5tahC2NltxYXFkLBtuBvfBoCQ3pIxYQWfzbCE9o1OB9SsHe0hnNGo7lWgTA/ePbFAJLWu6nLL9K17A==" + "resolved": "10.0.300", + "contentHash": "0jlkXaUGjYlWTIVPve5MftjKHnT3SlAtq9BCLV4J9IjdPrxV/+4rMlBSjfr1khG8/GC6KGojjola8E1VvWF0qQ==" + }, + "System.IO.Hashing": { + "type": "Transitive", + "resolved": "10.0.8", + "contentHash": "+dJsbPJ3FyUbTZNplFj0RCKePFizmv6ewDV46JE9q/IVH4c3xTCftHfHelLsAKf0jryIPqgMb5GpS0x7TAY3mg==" } } } diff --git a/tests/SharpCompress.AotSmoke/SharpCompress.AotSmoke.csproj b/tests/SharpCompress.AotSmoke/SharpCompress.AotSmoke.csproj index c6f6314b..aed0b97d 100644 --- a/tests/SharpCompress.AotSmoke/SharpCompress.AotSmoke.csproj +++ b/tests/SharpCompress.AotSmoke/SharpCompress.AotSmoke.csproj @@ -2,6 +2,7 @@ Exe net10.0 + linux-x64 true true full diff --git a/tests/SharpCompress.AotSmoke/packages.lock.json b/tests/SharpCompress.AotSmoke/packages.lock.json index 74a5d802..bfbb6d92 100644 --- a/tests/SharpCompress.AotSmoke/packages.lock.json +++ b/tests/SharpCompress.AotSmoke/packages.lock.json @@ -4,15 +4,15 @@ "net10.0": { "Microsoft.DotNet.ILCompiler": { "type": "Direct", - "requested": "[10.0.6, )", - "resolved": "10.0.6", - "contentHash": "nBOzxOys8OeyJ+Nsi/uYlI/5TSsvwjaM/p5m4dTL6khCLx9UuP3b2ec3HeuBw/+F7hHCAZG1yFx8VBeoRAX+EQ==" + "requested": "[10.0.8, )", + "resolved": "10.0.8", + "contentHash": "RJxitcN5CCyZDcPNXKLsecwKvACzmy8C1z8hGM9+hFcnPhv1jDysJFFIeUHIPWaZ6wDAfYtZcgKEtegvL2Nz8A==" }, "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[10.0.6, )", - "resolved": "10.0.6", - "contentHash": "QKuvS0LWX4fjFqeDkyM7Kqt8P3wYTiPD4nwU+9y59n0sCiG714fxDgbbN82vDnzq89AF/PiHl92TP2C4aFDUQA==" + "requested": "[10.0.8, )", + "resolved": "10.0.8", + "contentHash": "dVbSXGIFNR5nZcv2tOLoWI+a9T4jtFd77IYjuND+QVe360qWgAF7H0WtoopYhRw/+SgpGUTyrkrh+65+ClNnfw==" }, "Microsoft.NETFramework.ReferenceAssemblies": { "type": "Direct", @@ -25,12 +25,13 @@ }, "Microsoft.SourceLink.GitHub": { "type": "Direct", - "requested": "[10.0.102, )", - "resolved": "10.0.102", - "contentHash": "Oxq3RCIJSdtpIU4hLqO7XaDe/Ra3HS9Wi8rJl838SAg6Zu1iQjerA0+xXWBgUFYbgknUGCLOU0T+lzMLkvY9Qg==", + "requested": "[10.0.300, )", + "resolved": "10.0.300", + "contentHash": "QzCtLkXVb3l4IxcpvJCbzUwMLihAmLN6vVLjQGSzYSF8d2dvXxqJAZk83RV3gYnp2egz8jRMgSR2woY3vOahTA==", "dependencies": { - "Microsoft.Build.Tasks.Git": "10.0.102", - "Microsoft.SourceLink.Common": "10.0.102" + "Microsoft.Build.Tasks.Git": "10.0.300", + "Microsoft.SourceLink.Common": "10.0.300", + "System.IO.Hashing": "10.0.8" } }, "Microsoft.VisualStudio.Threading.Analyzers": { @@ -41,14 +42,17 @@ }, "PolySharp": { "type": "Direct", - "requested": "[1.15.0, )", - "resolved": "1.15.0", - "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g==" + "requested": "[1.16.0, )", + "resolved": "1.16.0", + "contentHash": "3kdIIceBPumwjw279FuiVMfVENT2cGASXJgcigdySsbX2dJB8ofUgG6i47yqF/k1qu6fvNR3csrSekZPviR6kQ==" }, "Microsoft.Build.Tasks.Git": { "type": "Transitive", - "resolved": "10.0.102", - "contentHash": "0i81LYX31U6UiXz4NOLbvc++u+/mVDmOt+PskrM/MygpDxkv9THKQyRUmavBpLK6iBV0abNWnn+CQgSRz//Pwg==" + "resolved": "10.0.300", + "contentHash": "P0kaQwVZx4xIUe2FtrLyBadYNXuAljttJUPvjBYRuHhPE8L77L42KakLDkaADRiUrGspoLcMwayjrbQhYTr0zA==", + "dependencies": { + "System.IO.Hashing": "10.0.8" + } }, "Microsoft.NETFramework.ReferenceAssemblies.net461": { "type": "Transitive", @@ -57,27 +61,32 @@ }, "Microsoft.SourceLink.Common": { "type": "Transitive", - "resolved": "10.0.102", - "contentHash": "Mk1IMb9q5tahC2NltxYXFkLBtuBvfBoCQ3pIxYQWfzbCE9o1OB9SsHe0hnNGo7lWgTA/ePbFAJLWu6nLL9K17A==" + "resolved": "10.0.300", + "contentHash": "0jlkXaUGjYlWTIVPve5MftjKHnT3SlAtq9BCLV4J9IjdPrxV/+4rMlBSjfr1khG8/GC6KGojjola8E1VvWF0qQ==" + }, + "System.IO.Hashing": { + "type": "Transitive", + "resolved": "10.0.8", + "contentHash": "+dJsbPJ3FyUbTZNplFj0RCKePFizmv6ewDV46JE9q/IVH4c3xTCftHfHelLsAKf0jryIPqgMb5GpS0x7TAY3mg==" }, "sharpcompress": { "type": "Project" } }, - "net10.0/osx-arm64": { + "net10.0/linux-x64": { "Microsoft.DotNet.ILCompiler": { "type": "Direct", - "requested": "[10.0.6, )", - "resolved": "10.0.6", - "contentHash": "nBOzxOys8OeyJ+Nsi/uYlI/5TSsvwjaM/p5m4dTL6khCLx9UuP3b2ec3HeuBw/+F7hHCAZG1yFx8VBeoRAX+EQ==", + "requested": "[10.0.8, )", + "resolved": "10.0.8", + "contentHash": "RJxitcN5CCyZDcPNXKLsecwKvACzmy8C1z8hGM9+hFcnPhv1jDysJFFIeUHIPWaZ6wDAfYtZcgKEtegvL2Nz8A==", "dependencies": { - "runtime.osx-arm64.Microsoft.DotNet.ILCompiler": "10.0.6" + "runtime.linux-x64.Microsoft.DotNet.ILCompiler": "10.0.8" } }, - "runtime.osx-arm64.Microsoft.DotNet.ILCompiler": { + "runtime.linux-x64.Microsoft.DotNet.ILCompiler": { "type": "Transitive", - "resolved": "10.0.6", - "contentHash": "+yovwOAlIpfIcH+ZWmLYXWTSWYJ93wcQxF/RVk+X4MXgLASeosCJYVLqP20g0cufKjoRqvCmnklR6y9Su3ORtA==" + "resolved": "10.0.8", + "contentHash": "0jxyi69frgaqADCnEpHE+f65NoiRTAjfjvNDMOxWV77BumQ56eMDL4ECw29DcJTqwaYJQ92PqDS6y6CiLf7kgw==" } } } diff --git a/tests/SharpCompress.Performance/Program.cs b/tests/SharpCompress.Performance/Program.cs index 35e257b8..ab940ed6 100644 --- a/tests/SharpCompress.Performance/Program.cs +++ b/tests/SharpCompress.Performance/Program.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Running; @@ -8,12 +9,12 @@ namespace SharpCompress.Performance; public class Program { - public static void Main(string[] args) + public static async Task Main(string[] args) { // Check if profiling mode is requested if (args.Length > 0 && args[0].Equals("--profile", StringComparison.OrdinalIgnoreCase)) { - RunWithProfiler(args); + await RunWithProfiler(args); return; } @@ -29,7 +30,7 @@ public class Program BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, config); } - private static void RunWithProfiler(string[] args) + private static async Task RunWithProfiler(string[] args) { var profileType = "cpu"; // Default to CPU profiling var outputPath = "./profiler-snapshots"; @@ -59,16 +60,11 @@ public class Program Console.WriteLine(); // Run a sample benchmark with profiling - RunSampleBenchmarkWithProfiler(profileType, outputPath); + await RunSampleBenchmarkWithProfiler(profileType, outputPath); } - private static void RunSampleBenchmarkWithProfiler(string profileType, string outputPath) + private static async Task RunSampleBenchmarkWithProfiler(string profileType, string outputPath) { - Console.WriteLine("Running sample benchmark with profiler..."); - Console.WriteLine("Note: JetBrains profiler requires the profiler tools to be installed."); - Console.WriteLine("Install from: https://www.jetbrains.com/profiler/"); - Console.WriteLine(); - try { IDisposable? profiler = null; @@ -85,13 +81,13 @@ public class Program using (profiler) { // Run a simple benchmark iteration - var zipBenchmark = new Benchmarks.ZipBenchmarks(); + var zipBenchmark = new Benchmarks.SevenZipBenchmarks(); zipBenchmark.Setup(); Console.WriteLine("Running benchmark iterations..."); - for (int i = 0; i < 10; i++) + for (int i = 0; i < 100; i++) { - zipBenchmark.ZipExtractArchiveApi(); + await zipBenchmark.SevenZipLzma2ExtractAsync_Reader(); if (i % 3 == 0) { Console.Write("."); diff --git a/tests/SharpCompress.Performance/packages.lock.json b/tests/SharpCompress.Performance/packages.lock.json index 4e1d3a33..739ca4c7 100644 --- a/tests/SharpCompress.Performance/packages.lock.json +++ b/tests/SharpCompress.Performance/packages.lock.json @@ -41,12 +41,13 @@ }, "Microsoft.SourceLink.GitHub": { "type": "Direct", - "requested": "[10.0.102, )", - "resolved": "10.0.102", - "contentHash": "Oxq3RCIJSdtpIU4hLqO7XaDe/Ra3HS9Wi8rJl838SAg6Zu1iQjerA0+xXWBgUFYbgknUGCLOU0T+lzMLkvY9Qg==", + "requested": "[10.0.300, )", + "resolved": "10.0.300", + "contentHash": "QzCtLkXVb3l4IxcpvJCbzUwMLihAmLN6vVLjQGSzYSF8d2dvXxqJAZk83RV3gYnp2egz8jRMgSR2woY3vOahTA==", "dependencies": { - "Microsoft.Build.Tasks.Git": "10.0.102", - "Microsoft.SourceLink.Common": "10.0.102" + "Microsoft.Build.Tasks.Git": "10.0.300", + "Microsoft.SourceLink.Common": "10.0.300", + "System.IO.Hashing": "10.0.8" } }, "Microsoft.VisualStudio.Threading.Analyzers": { @@ -57,9 +58,9 @@ }, "PolySharp": { "type": "Direct", - "requested": "[1.15.0, )", - "resolved": "1.15.0", - "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g==" + "requested": "[1.16.0, )", + "resolved": "1.16.0", + "contentHash": "3kdIIceBPumwjw279FuiVMfVENT2cGASXJgcigdySsbX2dJB8ofUgG6i47yqF/k1qu6fvNR3csrSekZPviR6kQ==" }, "BenchmarkDotNet.Annotations": { "type": "Transitive", @@ -104,8 +105,11 @@ }, "Microsoft.Build.Tasks.Git": { "type": "Transitive", - "resolved": "10.0.102", - "contentHash": "0i81LYX31U6UiXz4NOLbvc++u+/mVDmOt+PskrM/MygpDxkv9THKQyRUmavBpLK6iBV0abNWnn+CQgSRz//Pwg==" + "resolved": "10.0.300", + "contentHash": "P0kaQwVZx4xIUe2FtrLyBadYNXuAljttJUPvjBYRuHhPE8L77L42KakLDkaADRiUrGspoLcMwayjrbQhYTr0zA==", + "dependencies": { + "System.IO.Hashing": "10.0.8" + } }, "Microsoft.CodeAnalysis.Analyzers": { "type": "Transitive", @@ -209,8 +213,8 @@ }, "Microsoft.SourceLink.Common": { "type": "Transitive", - "resolved": "10.0.102", - "contentHash": "Mk1IMb9q5tahC2NltxYXFkLBtuBvfBoCQ3pIxYQWfzbCE9o1OB9SsHe0hnNGo7lWgTA/ePbFAJLWu6nLL9K17A==" + "resolved": "10.0.300", + "contentHash": "0jlkXaUGjYlWTIVPve5MftjKHnT3SlAtq9BCLV4J9IjdPrxV/+4rMlBSjfr1khG8/GC6KGojjola8E1VvWF0qQ==" }, "Perfolizer": { "type": "Transitive", @@ -230,6 +234,11 @@ "resolved": "9.0.5", "contentHash": "cuzLM2MWutf9ZBEMPYYfd0DXwYdvntp7VCT6a/wvbKCa2ZuvGmW74xi+YBa2mrfEieAXqM4TNKlMmSnfAfpUoQ==" }, + "System.IO.Hashing": { + "type": "Transitive", + "resolved": "10.0.8", + "contentHash": "+dJsbPJ3FyUbTZNplFj0RCKePFizmv6ewDV46JE9q/IVH4c3xTCftHfHelLsAKf0jryIPqgMb5GpS0x7TAY3mg==" + }, "System.Management": { "type": "Transitive", "resolved": "9.0.5", diff --git a/tests/SharpCompress.Test/GZip/GZipArchiveAsyncTests.cs b/tests/SharpCompress.Test/GZip/GZipArchiveAsyncTests.cs index e8ce2a9d..d7812b57 100644 --- a/tests/SharpCompress.Test/GZip/GZipArchiveAsyncTests.cs +++ b/tests/SharpCompress.Test/GZip/GZipArchiveAsyncTests.cs @@ -221,4 +221,23 @@ public class GZipArchiveAsyncTests : ArchiveTests Path.Combine(scratchPath2, "Tar.tar") ); } + + [Fact] + public async ValueTask GZip_Async_Dispose_Closes_New_Entry_Stream() + { + var entryStream = new TestStream(new MemoryStream(new byte[] { 1, 2, 3 })); + + await using (var archive = await GZipArchive.CreateAsyncArchive()) + { + await archive.AddEntryAsync( + "test.bin", + entryStream, + closeStream: true, + size: entryStream.Length + ); + await archive.SaveToAsync(new MemoryStream(), new GZipWriterOptions()); + } + + Assert.True(entryStream.IsDisposed); + } } diff --git a/tests/SharpCompress.Test/SevenZip/SevenZipArchiveAsyncTests.cs b/tests/SharpCompress.Test/SevenZip/SevenZipArchiveAsyncTests.cs index b4f3c855..d28b6d54 100644 --- a/tests/SharpCompress.Test/SevenZip/SevenZipArchiveAsyncTests.cs +++ b/tests/SharpCompress.Test/SevenZip/SevenZipArchiveAsyncTests.cs @@ -1,9 +1,11 @@ +using System; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using SharpCompress.Archives; using SharpCompress.Archives.SevenZip; +using SharpCompress.Common; using SharpCompress.Readers; using SharpCompress.Test.Mocks; using Xunit; @@ -141,6 +143,29 @@ public class SevenZipArchiveAsyncTests : ArchiveTests VerifyFiles(); } + [Fact] + public async Task SevenZipArchive_Solid_WriteToDirectoryAsync_WithProgress() + { + var progressReports = new System.Collections.Generic.List(); + var progress = new SynchronousProgress(report => + progressReports.Add(report) + ); + var testArchive = Path.Combine(TEST_ARCHIVES_PATH, "7Zip.solid.7z"); +#if NETFRAMEWORK + using var stream = File.OpenRead(testArchive); +#else + await using var stream = File.OpenRead(testArchive); +#endif + await using var archive = await ArchiveFactory.OpenAsyncArchive( + new AsyncOnlyStream(stream) + ); + + await archive.WriteToDirectoryAsync(SCRATCH_FILES_PATH, progress: progress); + + VerifyFiles(); + Assert.True(progressReports.Count > 0, "Progress reports should be generated"); + } + [Fact] public async Task SevenZipArchive_BZip2_AsyncStreamExtraction() { @@ -336,4 +361,13 @@ public class SevenZipArchiveAsyncTests : ArchiveTests // The critical check: within a single folder, the stream should NEVER be recreated Assert.Equal(0, streamRecreationsWithinFolder); // Folder stream should remain the same for all entries in the same folder } + + private sealed class SynchronousProgress : IProgress + { + private readonly Action _handler; + + public SynchronousProgress(Action handler) => _handler = handler; + + public void Report(T value) => _handler(value); + } } diff --git a/tests/SharpCompress.Test/SharpCompress.Test.csproj b/tests/SharpCompress.Test/SharpCompress.Test.csproj index 9d56d13f..68ff64ae 100644 --- a/tests/SharpCompress.Test/SharpCompress.Test.csproj +++ b/tests/SharpCompress.Test/SharpCompress.Test.csproj @@ -9,6 +9,8 @@ $(DefineConstants);LEGACY_DOTNET + AnyCPU + false $(DefineConstants);WINDOWS diff --git a/tests/SharpCompress.Test/Tar/TarArchiveAsyncTests.cs b/tests/SharpCompress.Test/Tar/TarArchiveAsyncTests.cs index 684f05c2..28947e97 100644 --- a/tests/SharpCompress.Test/Tar/TarArchiveAsyncTests.cs +++ b/tests/SharpCompress.Test/Tar/TarArchiveAsyncTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; @@ -179,6 +180,28 @@ public class TarArchiveAsyncTests : ArchiveTests CompareArchivesByPath(unmodified, scratchPath); } + [Fact] + public async ValueTask Tar_Async_Dispose_Closes_New_Entry_Stream() + { + var entryStream = new TestStream(new MemoryStream(Encoding.UTF8.GetBytes("test"))); + + await using (var archive = await TarArchive.CreateAsyncArchive()) + { + await archive.AddEntryAsync( + "test.txt", + entryStream, + closeStream: true, + size: entryStream.Length + ); + await archive.SaveToAsync( + new MemoryStream(), + new TarWriterOptions(CompressionType.None, true) + ); + } + + Assert.True(entryStream.IsDisposed); + } + [Fact] public async ValueTask Tar_Random_Write_Add_Async() { @@ -394,4 +417,65 @@ public class TarArchiveAsyncTests : ArchiveTests Assert.Equal(5100, localOverrideLink.GroupId); Assert.Equal(Convert.ToInt64("777", 8), localOverrideLink.Mode); } + + [Fact] + public async ValueTask Tar_Read_One_At_A_Time_Without_Disposing_Entry_Stream_Async() + { + var archiveEncoding = new ArchiveEncoding { Default = Encoding.UTF8 }; + var tarWriterOptions = new TarWriterOptions(CompressionType.None, true) + { + ArchiveEncoding = archiveEncoding, + }; + var testBytes = Encoding.UTF8.GetBytes("This is a test."); + + using var memoryStream = new MemoryStream(); + using (var tarWriter = new TarWriter(memoryStream, tarWriterOptions)) + using (var testFileStream = new MemoryStream(testBytes)) + { + await tarWriter.WriteAsync("file0.txt", testFileStream, null); + testFileStream.Position = 0; + await tarWriter.WriteAsync("file1.txt", testFileStream, null); + tarWriter.WriteDirectory("folder0", null); + testFileStream.Position = 0; + await tarWriter.WriteAsync("folder0/file_in_folder0.txt", testFileStream, null); + } + + memoryStream.Position = 0; + + var entryKeys = new List(); + var openEntryStreams = new List(); + + await using ( + var archive = await TarArchive.OpenAsyncArchive( + new AsyncOnlyStream(memoryStream), + ReaderOptions.ForExternalStream + ) + ) + { + await foreach (var entry in archive.EntriesAsync) + { + entryKeys.Add(entry.Key); + if (entry.IsDirectory) + { + continue; + } + + var tarEntryStream = await entry.OpenEntryStreamAsync(); + openEntryStreams.Add(tarEntryStream); + + using var testFileStream = new MemoryStream(); + await tarEntryStream.CopyToAsync(testFileStream); + Assert.Equal(testBytes.Length, testFileStream.Length); + } + + Assert.Equal(4, await archive.EntriesAsync.CountAsync()); + } + + openEntryStreams.ForEach(stream => stream.Dispose()); + + Assert.Equal( + ["file0.txt", "file1.txt", "folder0/", "folder0/file_in_folder0.txt"], + entryKeys + ); + } } diff --git a/tests/SharpCompress.Test/Tar/TarArchiveTests.cs b/tests/SharpCompress.Test/Tar/TarArchiveTests.cs index 1ea5b3f8..7959967e 100644 --- a/tests/SharpCompress.Test/Tar/TarArchiveTests.cs +++ b/tests/SharpCompress.Test/Tar/TarArchiveTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; @@ -421,6 +422,62 @@ public class TarArchiveTests : ArchiveTests Assert.Equal(2, numberOfEntries); } + [Fact] + public void Tar_Read_One_At_A_Time_Without_Disposing_Entry_Stream() + { + var archiveEncoding = new ArchiveEncoding { Default = Encoding.UTF8 }; + var tarWriterOptions = new TarWriterOptions(CompressionType.None, true) + { + ArchiveEncoding = archiveEncoding, + }; + var testBytes = Encoding.UTF8.GetBytes("This is a test."); + + using var memoryStream = new MemoryStream(); + using (var tarWriter = new TarWriter(memoryStream, tarWriterOptions)) + using (var testFileStream = new MemoryStream(testBytes)) + { + tarWriter.Write("file0.txt", testFileStream); + testFileStream.Position = 0; + tarWriter.Write("file1.txt", testFileStream); + tarWriter.WriteDirectory("folder0", null); + testFileStream.Position = 0; + tarWriter.Write("folder0/file_in_folder0.txt", testFileStream); + } + + memoryStream.Position = 0; + + var entryKeys = new List(); + var openEntryStreams = new List(); + + using (var archive = ArchiveFactory.OpenArchive(memoryStream)) + { + foreach (var entry in archive.Entries) + { + entryKeys.Add(entry.Key); + if (entry.IsDirectory) + { + continue; + } + + var tarEntryStream = entry.OpenEntryStream(); + openEntryStreams.Add(tarEntryStream); + + using var testFileStream = new MemoryStream(); + tarEntryStream.CopyTo(testFileStream); + Assert.Equal(testBytes.Length, testFileStream.Length); + } + + Assert.Equal(4, archive.Entries.Count()); + } + + openEntryStreams.ForEach(stream => stream.Dispose()); + + Assert.Equal( + ["file0.txt", "file1.txt", "folder0/", "folder0/file_in_folder0.txt"], + entryKeys + ); + } + [Fact] public void Tar_Detect_Test() { diff --git a/tests/SharpCompress.Test/Tar/TarReaderAsyncTests.cs b/tests/SharpCompress.Test/Tar/TarReaderAsyncTests.cs index 5d096c96..d0104cbb 100644 --- a/tests/SharpCompress.Test/Tar/TarReaderAsyncTests.cs +++ b/tests/SharpCompress.Test/Tar/TarReaderAsyncTests.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Text; using System.Threading.Tasks; using SharpCompress.Common; using SharpCompress.Common.Tar; @@ -8,6 +9,7 @@ using SharpCompress.Factories; using SharpCompress.Readers; using SharpCompress.Readers.Tar; using SharpCompress.Test.Mocks; +using SharpCompress.Writers.Tar; using Xunit; namespace SharpCompress.Test.Tar; @@ -347,6 +349,62 @@ public class TarReaderAsyncTests : ReaderTests ); } + [Fact] + public async ValueTask Tar_Read_One_At_A_Time_Without_Disposing_Entry_Stream_Async() + { + var archiveEncoding = new ArchiveEncoding { Default = Encoding.UTF8 }; + var tarWriterOptions = new TarWriterOptions(CompressionType.None, true) + { + ArchiveEncoding = archiveEncoding, + }; + var testBytes = Encoding.UTF8.GetBytes("This is a test."); + + using var memoryStream = new MemoryStream(); + using (var tarWriter = new TarWriter(memoryStream, tarWriterOptions)) + using (var testFileStream = new MemoryStream(testBytes)) + { + await tarWriter.WriteAsync("file0.txt", testFileStream, null); + testFileStream.Position = 0; + await tarWriter.WriteAsync("file1.txt", testFileStream, null); + tarWriter.WriteDirectory("folder0", null); + testFileStream.Position = 0; + await tarWriter.WriteAsync("folder0/file_in_folder0.txt", testFileStream, null); + } + + memoryStream.Position = 0; + + var entryKeys = new List(); + var openEntryStreams = new List(); + + await using ( + var reader = await TarReader.OpenAsyncReader(new AsyncOnlyStream(memoryStream)) + ) + { + while (await reader.MoveToNextEntryAsync()) + { + entryKeys.Add(reader.Entry.Key); + if (reader.Entry.IsDirectory) + { + continue; + } + + var entryStream = await reader.OpenEntryStreamAsync(); + openEntryStreams.Add(entryStream); + + using var testFileStream = new MemoryStream(); + await entryStream.CopyToAsync(testFileStream); + Assert.Equal(testBytes.Length, testFileStream.Length); + } + } + + openEntryStreams.ForEach(stream => stream.Dispose()); + + Assert.Equal( + ["file0.txt", "file1.txt", "folder0/", "folder0/file_in_folder0.txt"], + entryKeys + ); + } + [Fact] public async ValueTask Tar_Corrupted_Async() { diff --git a/tests/SharpCompress.Test/Tar/TarReaderTests.cs b/tests/SharpCompress.Test/Tar/TarReaderTests.cs index 34a695c8..39c1ba15 100644 --- a/tests/SharpCompress.Test/Tar/TarReaderTests.cs +++ b/tests/SharpCompress.Test/Tar/TarReaderTests.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Text; using SharpCompress.Common; using SharpCompress.Common.Tar; using SharpCompress.Compressors.BZip2; @@ -8,6 +9,7 @@ using SharpCompress.Factories; using SharpCompress.Readers; using SharpCompress.Readers.Tar; using SharpCompress.Test.Mocks; +using SharpCompress.Writers.Tar; using Xunit; namespace SharpCompress.Test.Tar; @@ -379,6 +381,60 @@ public class TarReaderTests : ReaderTests Assert.Throws(() => reader.MoveToNextEntry()); } + [Fact] + public void Tar_Read_One_At_A_Time_Without_Disposing_Entry_Stream() + { + var archiveEncoding = new ArchiveEncoding { Default = Encoding.UTF8 }; + var tarWriterOptions = new TarWriterOptions(CompressionType.None, true) + { + ArchiveEncoding = archiveEncoding, + }; + var testBytes = Encoding.UTF8.GetBytes("This is a test."); + + using var memoryStream = new MemoryStream(); + using (var tarWriter = new TarWriter(memoryStream, tarWriterOptions)) + using (var testFileStream = new MemoryStream(testBytes)) + { + tarWriter.Write("file0.txt", testFileStream, null); + testFileStream.Position = 0; + tarWriter.Write("file1.txt", testFileStream, null); + tarWriter.WriteDirectory("folder0", null); + testFileStream.Position = 0; + tarWriter.Write("folder0/file_in_folder0.txt", testFileStream, null); + } + + memoryStream.Position = 0; + + var entryKeys = new List(); + var openEntryStreams = new List(); + + using (var reader = TarReader.OpenReader(memoryStream)) + { + while (reader.MoveToNextEntry()) + { + entryKeys.Add(reader.Entry.Key); + if (reader.Entry.IsDirectory) + { + continue; + } + + var entryStream = reader.OpenEntryStream(); + openEntryStreams.Add(entryStream); + + using var testFileStream = new MemoryStream(); + entryStream.CopyTo(testFileStream); + Assert.Equal(testBytes.Length, testFileStream.Length); + } + } + + openEntryStreams.ForEach(stream => stream.Dispose()); + + Assert.Equal( + ["file0.txt", "file1.txt", "folder0/", "folder0/file_in_folder0.txt"], + entryKeys + ); + } + [Fact] public void Tar_Corrupted() { diff --git a/tests/SharpCompress.Test/Zip/ZipArchiveAsyncTests.cs b/tests/SharpCompress.Test/Zip/ZipArchiveAsyncTests.cs index 86b97c05..420ce481 100644 --- a/tests/SharpCompress.Test/Zip/ZipArchiveAsyncTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipArchiveAsyncTests.cs @@ -186,6 +186,28 @@ public class ZipArchiveAsyncTests : ArchiveTests CompareArchivesByPath(unmodified, scratchPath); } + [Fact] + public async ValueTask Zip_Async_Dispose_Closes_New_Entry_Stream() + { + var entryStream = new TestStream(new MemoryStream(Encoding.UTF8.GetBytes("test"))); + + await using (var archive = await ZipArchive.CreateAsyncArchive()) + { + await archive.AddEntryAsync( + "test.txt", + entryStream, + closeStream: true, + size: entryStream.Length + ); + await archive.SaveToAsync( + new MemoryStream(), + new ZipWriterOptions(CompressionType.Deflate) + ); + } + + Assert.True(entryStream.IsDisposed); + } + [Fact] public async ValueTask Zip_Deflate_Entry_Stream_Async() { diff --git a/tests/SharpCompress.Test/packages.lock.json b/tests/SharpCompress.Test/packages.lock.json index 9f666ea6..2f685ff1 100644 --- a/tests/SharpCompress.Test/packages.lock.json +++ b/tests/SharpCompress.Test/packages.lock.json @@ -13,11 +13,11 @@ }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[18.5.1, )", - "resolved": "18.5.1", - "contentHash": "SfqVaLiIqAbRWuPg5BP4QFwBIirQj/YIL8Dhxl6zntBKbXp0cQykoV480SmwG+yRMiWptxEI6NbHQuGSZ8b97w==", + "requested": "[18.6.0, )", + "resolved": "18.6.0", + "contentHash": "kAIBt0MsYR0o2RULmlW5BhQ1ha50aGEgLKG4f1p0kePBGLJCprqs3S+NxRrYN8UH7mSQRPKpeiH9mwPMEKUObQ==", "dependencies": { - "Microsoft.CodeCoverage": "18.5.1" + "Microsoft.CodeCoverage": "18.6.0" } }, "Microsoft.NETFramework.ReferenceAssemblies": { @@ -31,12 +31,13 @@ }, "Microsoft.SourceLink.GitHub": { "type": "Direct", - "requested": "[10.0.102, )", - "resolved": "10.0.102", - "contentHash": "Oxq3RCIJSdtpIU4hLqO7XaDe/Ra3HS9Wi8rJl838SAg6Zu1iQjerA0+xXWBgUFYbgknUGCLOU0T+lzMLkvY9Qg==", + "requested": "[10.0.300, )", + "resolved": "10.0.300", + "contentHash": "QzCtLkXVb3l4IxcpvJCbzUwMLihAmLN6vVLjQGSzYSF8d2dvXxqJAZk83RV3gYnp2egz8jRMgSR2woY3vOahTA==", "dependencies": { - "Microsoft.Build.Tasks.Git": "10.0.102", - "Microsoft.SourceLink.Common": "10.0.102" + "Microsoft.Build.Tasks.Git": "10.0.300", + "Microsoft.SourceLink.Common": "10.0.300", + "System.IO.Hashing": "10.0.8" } }, "Microsoft.VisualStudio.Threading.Analyzers": { @@ -47,9 +48,9 @@ }, "PolySharp": { "type": "Direct", - "requested": "[1.15.0, )", - "resolved": "1.15.0", - "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g==" + "requested": "[1.16.0, )", + "resolved": "1.16.0", + "contentHash": "3kdIIceBPumwjw279FuiVMfVENT2cGASXJgcigdySsbX2dJB8ofUgG6i47yqF/k1qu6fvNR3csrSekZPviR6kQ==" }, "xunit.runner.visualstudio": { "type": "Direct", @@ -79,13 +80,16 @@ }, "Microsoft.Build.Tasks.Git": { "type": "Transitive", - "resolved": "10.0.102", - "contentHash": "0i81LYX31U6UiXz4NOLbvc++u+/mVDmOt+PskrM/MygpDxkv9THKQyRUmavBpLK6iBV0abNWnn+CQgSRz//Pwg==" + "resolved": "10.0.300", + "contentHash": "P0kaQwVZx4xIUe2FtrLyBadYNXuAljttJUPvjBYRuHhPE8L77L42KakLDkaADRiUrGspoLcMwayjrbQhYTr0zA==", + "dependencies": { + "System.IO.Hashing": "10.0.8" + } }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "18.5.1", - "contentHash": "vMFDR1ZjqzzgKmM0zrPie7Gv9Y+ZppjODB5Quzu9Eq0TlIusUfUCYFPEawO91zQuqwzvdFbJSU7WHNtjStffJQ==" + "resolved": "18.6.0", + "contentHash": "bkmCXn/65Cd0LdO2zTb/ValGAJ1H8y/CgYOiBb3jsDyHI3Y1ljKx6RBvhvn3e5D/4R4I00RRwLf+Bd2Sn6bJjA==" }, "Microsoft.NETFramework.ReferenceAssemblies.net48": { "type": "Transitive", @@ -94,8 +98,8 @@ }, "Microsoft.SourceLink.Common": { "type": "Transitive", - "resolved": "10.0.102", - "contentHash": "Mk1IMb9q5tahC2NltxYXFkLBtuBvfBoCQ3pIxYQWfzbCE9o1OB9SsHe0hnNGo7lWgTA/ePbFAJLWu6nLL9K17A==" + "resolved": "10.0.300", + "contentHash": "0jlkXaUGjYlWTIVPve5MftjKHnT3SlAtq9BCLV4J9IjdPrxV/+4rMlBSjfr1khG8/GC6KGojjola8E1VvWF0qQ==" }, "Microsoft.Testing.Extensions.Telemetry": { "type": "Transitive", @@ -147,8 +151,8 @@ }, "System.Buffers": { "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + "resolved": "4.6.1", + "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" }, "System.Collections.Immutable": { "type": "Transitive", @@ -168,20 +172,29 @@ "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, + "System.IO.Hashing": { + "type": "Transitive", + "resolved": "10.0.8", + "contentHash": "+dJsbPJ3FyUbTZNplFj0RCKePFizmv6ewDV46JE9q/IVH4c3xTCftHfHelLsAKf0jryIPqgMb5GpS0x7TAY3mg==", + "dependencies": { + "System.Buffers": "4.6.1", + "System.Memory": "4.6.3" + } + }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "resolved": "4.6.3", + "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.5.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" + "System.Buffers": "4.6.1", + "System.Numerics.Vectors": "4.6.1", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" } }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + "resolved": "4.6.1", + "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" }, "System.Reflection.Metadata": { "type": "Transitive", @@ -193,8 +206,8 @@ }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "6.1.2", + "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" }, "System.Security.AccessControl": { "type": "Transitive", @@ -324,12 +337,12 @@ }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[18.5.1, )", - "resolved": "18.5.1", - "contentHash": "SfqVaLiIqAbRWuPg5BP4QFwBIirQj/YIL8Dhxl6zntBKbXp0cQykoV480SmwG+yRMiWptxEI6NbHQuGSZ8b97w==", + "requested": "[18.6.0, )", + "resolved": "18.6.0", + "contentHash": "kAIBt0MsYR0o2RULmlW5BhQ1ha50aGEgLKG4f1p0kePBGLJCprqs3S+NxRrYN8UH7mSQRPKpeiH9mwPMEKUObQ==", "dependencies": { - "Microsoft.CodeCoverage": "18.5.1", - "Microsoft.TestPlatform.TestHost": "18.5.1" + "Microsoft.CodeCoverage": "18.6.0", + "Microsoft.TestPlatform.TestHost": "18.6.0" } }, "Microsoft.NETFramework.ReferenceAssemblies": { @@ -343,12 +356,13 @@ }, "Microsoft.SourceLink.GitHub": { "type": "Direct", - "requested": "[10.0.102, )", - "resolved": "10.0.102", - "contentHash": "Oxq3RCIJSdtpIU4hLqO7XaDe/Ra3HS9Wi8rJl838SAg6Zu1iQjerA0+xXWBgUFYbgknUGCLOU0T+lzMLkvY9Qg==", + "requested": "[10.0.300, )", + "resolved": "10.0.300", + "contentHash": "QzCtLkXVb3l4IxcpvJCbzUwMLihAmLN6vVLjQGSzYSF8d2dvXxqJAZk83RV3gYnp2egz8jRMgSR2woY3vOahTA==", "dependencies": { - "Microsoft.Build.Tasks.Git": "10.0.102", - "Microsoft.SourceLink.Common": "10.0.102" + "Microsoft.Build.Tasks.Git": "10.0.300", + "Microsoft.SourceLink.Common": "10.0.300", + "System.IO.Hashing": "10.0.8" } }, "Microsoft.VisualStudio.Threading.Analyzers": { @@ -359,9 +373,9 @@ }, "PolySharp": { "type": "Direct", - "requested": "[1.15.0, )", - "resolved": "1.15.0", - "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g==" + "requested": "[1.16.0, )", + "resolved": "1.16.0", + "contentHash": "3kdIIceBPumwjw279FuiVMfVENT2cGASXJgcigdySsbX2dJB8ofUgG6i47yqF/k1qu6fvNR3csrSekZPviR6kQ==" }, "xunit.runner.visualstudio": { "type": "Direct", @@ -385,13 +399,16 @@ }, "Microsoft.Build.Tasks.Git": { "type": "Transitive", - "resolved": "10.0.102", - "contentHash": "0i81LYX31U6UiXz4NOLbvc++u+/mVDmOt+PskrM/MygpDxkv9THKQyRUmavBpLK6iBV0abNWnn+CQgSRz//Pwg==" + "resolved": "10.0.300", + "contentHash": "P0kaQwVZx4xIUe2FtrLyBadYNXuAljttJUPvjBYRuHhPE8L77L42KakLDkaADRiUrGspoLcMwayjrbQhYTr0zA==", + "dependencies": { + "System.IO.Hashing": "10.0.8" + } }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "18.5.1", - "contentHash": "vMFDR1ZjqzzgKmM0zrPie7Gv9Y+ZppjODB5Quzu9Eq0TlIusUfUCYFPEawO91zQuqwzvdFbJSU7WHNtjStffJQ==" + "resolved": "18.6.0", + "contentHash": "bkmCXn/65Cd0LdO2zTb/ValGAJ1H8y/CgYOiBb3jsDyHI3Y1ljKx6RBvhvn3e5D/4R4I00RRwLf+Bd2Sn6bJjA==" }, "Microsoft.NETFramework.ReferenceAssemblies.net461": { "type": "Transitive", @@ -400,8 +417,8 @@ }, "Microsoft.SourceLink.Common": { "type": "Transitive", - "resolved": "10.0.102", - "contentHash": "Mk1IMb9q5tahC2NltxYXFkLBtuBvfBoCQ3pIxYQWfzbCE9o1OB9SsHe0hnNGo7lWgTA/ePbFAJLWu6nLL9K17A==" + "resolved": "10.0.300", + "contentHash": "0jlkXaUGjYlWTIVPve5MftjKHnT3SlAtq9BCLV4J9IjdPrxV/+4rMlBSjfr1khG8/GC6KGojjola8E1VvWF0qQ==" }, "Microsoft.Testing.Extensions.Telemetry": { "type": "Transitive", @@ -435,15 +452,15 @@ }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "18.5.1", - "contentHash": "KNZd+M0S0rz5eNAln0pbZX+A/RbokYZCbGKx4fN4CkhtWhkz6nSJDO+9LGYjRE4d0WPVriJ2JnVubkjt3+PpMg==" + "resolved": "18.6.0", + "contentHash": "gQTW4BIfM2ZLxixo9ITXoulLKjn20FiiHtqTsx9PENqTrX7368ZeJ5L0QZJyReXDWORPRV8jXwZR6Aar8JOyaA==" }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "18.5.1", - "contentHash": "RM+3JNHEoHOCFXzVntUcIiYxzPjzBN0N8wto6HYXi76YyBTZ/3CeRL8U+Pk5zx3AUrOmHxDvKJwGUCdElU9bJg==", + "resolved": "18.6.0", + "contentHash": "em1eLz5Q46+hsCtAXdXggWAPd9gQyT4ngdsQ7k1eWvQgpsjtS/wAOJ/5TteieFdiAvrEq1iVn00LtusAxRaVmQ==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "18.5.1", + "Microsoft.TestPlatform.ObjectModel": "18.6.0", "Newtonsoft.Json": "13.0.3" } }, @@ -457,6 +474,11 @@ "resolved": "13.0.3", "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" }, + "System.IO.Hashing": { + "type": "Transitive", + "resolved": "10.0.8", + "contentHash": "+dJsbPJ3FyUbTZNplFj0RCKePFizmv6ewDV46JE9q/IVH4c3xTCftHfHelLsAKf0jryIPqgMb5GpS0x7TAY3mg==" + }, "xunit.analyzers": { "type": "Transitive", "resolved": "1.27.0",