├── .cargo └── config.toml ├── .github ├── actions │ └── setup-rust │ │ └── action.yml ├── dependabot.yml └── workflows │ ├── main.yml │ ├── publish.yml │ ├── release.yml │ └── webassembly.yml ├── .gitignore ├── .gitmodules ├── Cargo.lock.msrv ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── THANKS ├── liblzma-sys ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── build.rs ├── config.h ├── src │ ├── bindgen.rs │ ├── bindgen_wrap.rs │ ├── lib.rs │ ├── manual.rs │ └── wasm_shim.rs └── wasm-shim │ ├── assert.h │ ├── stdlib.h │ └── string.h ├── src ├── bufread.rs ├── lib.rs ├── read.rs ├── stream.rs ├── write.rs └── write │ └── auto_finish.rs ├── systest ├── Cargo.toml ├── build.rs └── src │ └── main.rs └── tests ├── drop-incomplete.rs └── xz.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target."wasm32-wasi"] 2 | runner = ["wasmtime", "--dir", "."] 3 | [target."wasm32-wasip1"] 4 | runner = ["wasmtime", "--dir", "."] 5 | [target.wasm32-unknown-emscripten] 6 | runner = "node" 7 | [target.wasm32-unknown-unknown] 8 | runner = "wasm-bindgen-test-runner" 9 | -------------------------------------------------------------------------------- /.github/actions/setup-rust/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Setup Rust' 2 | description: 'Setup Rust by rustup' 3 | 4 | inputs: 5 | channel: 6 | description: 'Rust release channel[stable|beta|nightly]' 7 | default: stable 8 | required: false 9 | target: 10 | description: 'Rust build target' 11 | default: '' 12 | required: false 13 | 14 | runs: 15 | using: 'composite' 16 | steps: 17 | - name: Install Rust toolchain 18 | shell: bash 19 | run: | 20 | rustup update --no-self-update ${{ inputs.channel }} 21 | rustup default ${{ inputs.channel }} 22 | - name: Echo rust version 23 | shell: bash 24 | run: | 25 | rustc --version 26 | - if: ${{ inputs.target != '' }} 27 | name: Install Rust target 28 | shell: bash 29 | run: | 30 | rustup target add ${{ inputs.target }} 31 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | - package-ecosystem: "cargo" 13 | directory: "/" 14 | schedule: 15 | interval: "daily" 16 | - package-ecosystem: "cargo" 17 | directory: "liblzma-sys" 18 | schedule: 19 | interval: "daily" 20 | - package-ecosystem: "cargo" 21 | directory: "systest" 22 | schedule: 23 | interval: "daily" 24 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | test: 6 | name: Test 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | include: 12 | - os: ubuntu-latest 13 | rust: '1.60' # msrv 14 | - os: ubuntu-latest 15 | rust: stable 16 | - os: ubuntu-latest 17 | rust: stable 18 | static: yes 19 | - os: ubuntu-latest 20 | rust: beta 21 | - os: ubuntu-latest 22 | rust: nightly 23 | - os: macos-latest 24 | rust: stable 25 | - os: macos-latest 26 | rust: stable 27 | static: yes 28 | - os: windows-latest 29 | rust: stable-i686-msvc 30 | - os: windows-latest 31 | rust: stable-x86_64-msvc 32 | - os: windows-latest 33 | rust: stable-x86_64-msvc 34 | static: yes 35 | - os: windows-latest 36 | rust: stable-x86_64-msvc 37 | static: yes 38 | crt_static: yes 39 | - os: windows-latest 40 | rust: stable-i686-gnu 41 | - os: windows-latest 42 | rust: stable-x86_64-gnu 43 | steps: 44 | - uses: actions/checkout@v4 45 | with: 46 | submodules: true 47 | - name: Install Rust (rustup) 48 | run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} 49 | shell: bash 50 | - name: Set crt-static 51 | if: matrix.crt_static == 'yes' 52 | run: echo RUSTFLAGS=-Ctarget-feature=+crt-static >> $GITHUB_ENV 53 | shell: bash 54 | - name: Set static 55 | if: matrix.static == 'yes' 56 | run: echo LZMA_API_STATIC=1 >> $GITHUB_ENV 57 | shell: bash 58 | - name: copy Cargo.lock for msrv check 59 | if: matrix.rust == '1.60' 60 | run: | 61 | cp Cargo.lock.msrv Cargo.lock 62 | - run: | 63 | cargo test --no-default-features 64 | cargo clean -p liblzma -p liblzma-sys 65 | - run: | 66 | cargo test --features parallel 67 | cargo clean -p liblzma -p liblzma-sys 68 | - run: | 69 | cargo test --features bindgen 70 | cargo clean -p liblzma -p liblzma-sys 71 | # thin-lto and fat-lto feature required use clang as a linker, but linux default linker is gcc. so exclude thin-lto and fat-lto. 72 | - run: | 73 | cargo test --features parallel,static 74 | cargo clean -p liblzma -p liblzma-sys 75 | if: ${{ startsWith(matrix.os, 'ubuntu') }} 76 | - run: | 77 | cargo test --all-features 78 | cargo clean -p liblzma -p liblzma-sys 79 | if: ${{ !startsWith(matrix.os, 'ubuntu') }} 80 | - run: | 81 | cargo run --manifest-path systest/Cargo.toml 82 | cargo clean -p liblzma -p liblzma-sys 83 | if: matrix.static == 'yes' 84 | - run: cargo run --manifest-path systest/Cargo.toml --features bindgen 85 | if: matrix.static == 'yes' 86 | 87 | 88 | rustfmt: 89 | name: Rustfmt 90 | runs-on: ubuntu-latest 91 | steps: 92 | - uses: actions/checkout@v4 93 | with: 94 | submodules: true 95 | - name: Install Rust 96 | run: rustup update stable && rustup default stable && rustup component add rustfmt 97 | - run: cargo fmt -- --check 98 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish Rust crate 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | publish: 10 | name: Publish crate 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | with: 15 | submodules: true 16 | - uses: ./.github/actions/setup-rust 17 | - if: startsWith(github.ref, 'refs/tags/liblzma-sys') 18 | name: Publish liblzma-sys crate 19 | run: cargo publish 20 | working-directory: liblzma-sys 21 | env: 22 | CARGO_REGISTRY_TOKEN: "${{ secrets.CRATES_IO_API_KEY }}" 23 | - if: "!startsWith(github.ref, 'refs/tags/liblzma-sys') && startsWith(github.ref, 'refs/tags/liblzma')" 24 | name: Publish liblzma crate 25 | run: cargo publish 26 | working-directory: . 27 | env: 28 | CARGO_REGISTRY_TOKEN: "${{ secrets.CRATES_IO_API_KEY }}" 29 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: GitHub Release 2 | on: 3 | push: 4 | tags: 5 | - "*" 6 | 7 | permissions: write-all 8 | 9 | jobs: 10 | create-release: 11 | name: create-release 12 | runs-on: ubuntu-22.04 13 | outputs: 14 | upload_url: ${{ steps.release.outputs.upload_url }} 15 | version: ${{ env.VERSION }} 16 | steps: 17 | - uses: actions/checkout@v4 18 | with: 19 | submodules: true 20 | - name: Get the release version from the tag 21 | shell: bash 22 | run: | 23 | # Apparently, this is the right way to get a tag name. Really? 24 | # 25 | # See: https://github.community/t5/GitHub-Actions/How-to-get-just-the-tag-name/m-p/32167/highlight/true#M1027 26 | echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV 27 | echo "version is: ${{ env.VERSION }}" 28 | - name: Create GitHub release 29 | id: release 30 | run: | 31 | gh release create ${{ env.VERSION }} --verify-tag --title ${{ env.VERSION }} --draft --generate-notes 32 | env: 33 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 34 | -------------------------------------------------------------------------------- /.github/workflows/webassembly.yml: -------------------------------------------------------------------------------- 1 | name: Build Webassembly 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | test: 6 | name: Test WebAssembly 7 | strategy: 8 | fail-fast: false 9 | matrix: 10 | rust: 11 | - stable 12 | - beta 13 | - nightly-2025-05-16 # Pin nightly rust version because emscripten target not worked 14 | target: 15 | - wasm32-unknown-emscripten 16 | - wasm32-unknown-unknown 17 | - wasm32-wasip1 18 | include: 19 | - target: wasm32-unknown-emscripten 20 | container: emscripten/emsdk:latest 21 | - target: wasm32-unknown-unknown 22 | container: ghcr.io/portable-network-archive/wasi-sdk-gh-actions:wasi-sdk-22 23 | - target: wasm32-wasip1 24 | container: ghcr.io/portable-network-archive/wasi-sdk-gh-actions:wasi-sdk-22 25 | 26 | runs-on: ubuntu-latest 27 | container: ${{ matrix.container }} 28 | steps: 29 | - uses: actions/checkout@v4 30 | with: 31 | submodules: true 32 | - uses: actions-rust-lang/setup-rust-toolchain@v1 33 | with: 34 | target: ${{ matrix.target }} 35 | toolchain: ${{ matrix.rust }} 36 | - if: ${{ startsWith(matrix.target, 'wasm32-wasi') }} 37 | name: Setup wasmtime 38 | uses: bytecodealliance/actions/wasmtime/setup@v1 39 | with: 40 | github_token: ${{ github.token }} 41 | - if: ${{ startsWith(matrix.target, 'wasm32-wasi') }} 42 | name: Run test 43 | run: | 44 | cargo test --target ${{ matrix.target }} --features wasm 45 | - if: ${{ endsWith(matrix.target, 'unknown') }} 46 | name: Run test 47 | run: | 48 | curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash 49 | cargo binstall -y --force wasm-bindgen-cli 50 | cargo test --target ${{ matrix.target }} --features wasm --release 51 | - name: Run test 52 | if: ${{ startsWith(matrix.target, 'wasm32-unknown-emscripten') }} 53 | run: | 54 | cargo test --target ${{ matrix.target }} --features wasm -- --skip standard_files 55 | env: 56 | RUSTFLAGS: "-C link-args=-sINITIAL_MEMORY=128MB" 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/windows,linux,macos,visualstudiocode,intellij+all,rust 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=windows,linux,macos,visualstudiocode,intellij+all,rust 3 | 4 | ### Intellij+all ### 5 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 6 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 7 | 8 | # User-specific stuff 9 | .idea/**/workspace.xml 10 | .idea/**/tasks.xml 11 | .idea/**/usage.statistics.xml 12 | .idea/**/dictionaries 13 | .idea/**/shelf 14 | 15 | # AWS User-specific 16 | .idea/**/aws.xml 17 | 18 | # Generated files 19 | .idea/**/contentModel.xml 20 | 21 | # Sensitive or high-churn files 22 | .idea/**/dataSources/ 23 | .idea/**/dataSources.ids 24 | .idea/**/dataSources.local.xml 25 | .idea/**/sqlDataSources.xml 26 | .idea/**/dynamic.xml 27 | .idea/**/uiDesigner.xml 28 | .idea/**/dbnavigator.xml 29 | 30 | # Gradle 31 | .idea/**/gradle.xml 32 | .idea/**/libraries 33 | 34 | # Gradle and Maven with auto-import 35 | # When using Gradle or Maven with auto-import, you should exclude module files, 36 | # since they will be recreated, and may cause churn. Uncomment if using 37 | # auto-import. 38 | # .idea/artifacts 39 | # .idea/compiler.xml 40 | # .idea/jarRepositories.xml 41 | # .idea/modules.xml 42 | # .idea/*.iml 43 | # .idea/modules 44 | # *.iml 45 | # *.ipr 46 | 47 | # CMake 48 | cmake-build-*/ 49 | 50 | # Mongo Explorer plugin 51 | .idea/**/mongoSettings.xml 52 | 53 | # File-based project format 54 | *.iws 55 | 56 | # IntelliJ 57 | out/ 58 | 59 | # mpeltonen/sbt-idea plugin 60 | .idea_modules/ 61 | 62 | # JIRA plugin 63 | atlassian-ide-plugin.xml 64 | 65 | # Cursive Clojure plugin 66 | .idea/replstate.xml 67 | 68 | # SonarLint plugin 69 | .idea/sonarlint/ 70 | 71 | # Crashlytics plugin (for Android Studio and IntelliJ) 72 | com_crashlytics_export_strings.xml 73 | crashlytics.properties 74 | crashlytics-build.properties 75 | fabric.properties 76 | 77 | # Editor-based Rest Client 78 | .idea/httpRequests 79 | 80 | # Android studio 3.1+ serialized cache file 81 | .idea/caches/build_file_checksums.ser 82 | 83 | ### Intellij+all Patch ### 84 | # Ignore everything but code style settings and run configurations 85 | # that are supposed to be shared within teams. 86 | 87 | .idea/* 88 | 89 | !.idea/codeStyles 90 | !.idea/runConfigurations 91 | 92 | ### Linux ### 93 | *~ 94 | 95 | # temporary files which can be created if a process still has a handle open of a deleted file 96 | .fuse_hidden* 97 | 98 | # KDE directory preferences 99 | .directory 100 | 101 | # Linux trash folder which might appear on any partition or disk 102 | .Trash-* 103 | 104 | # .nfs files are created when an open file is removed but is still being accessed 105 | .nfs* 106 | 107 | ### macOS ### 108 | # General 109 | .DS_Store 110 | .AppleDouble 111 | .LSOverride 112 | 113 | # Icon must end with two \r 114 | Icon 115 | 116 | 117 | # Thumbnails 118 | ._* 119 | 120 | # Files that might appear in the root of a volume 121 | .DocumentRevisions-V100 122 | .fseventsd 123 | .Spotlight-V100 124 | .TemporaryItems 125 | .Trashes 126 | .VolumeIcon.icns 127 | .com.apple.timemachine.donotpresent 128 | 129 | # Directories potentially created on remote AFP share 130 | .AppleDB 131 | .AppleDesktop 132 | Network Trash Folder 133 | Temporary Items 134 | .apdisk 135 | 136 | ### macOS Patch ### 137 | # iCloud generated files 138 | *.icloud 139 | 140 | ### Rust ### 141 | # Generated by Cargo 142 | # will have compiled files and executables 143 | debug/ 144 | target/ 145 | 146 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 147 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 148 | Cargo.lock 149 | 150 | # These are backup files generated by rustfmt 151 | **/*.rs.bk 152 | 153 | # MSVC Windows builds of rustc generate these, which store debugging information 154 | *.pdb 155 | 156 | ### VisualStudioCode ### 157 | .vscode/* 158 | !.vscode/settings.json 159 | !.vscode/tasks.json 160 | !.vscode/launch.json 161 | !.vscode/extensions.json 162 | !.vscode/*.code-snippets 163 | 164 | # Local History for Visual Studio Code 165 | .history/ 166 | 167 | # Built Visual Studio Code Extensions 168 | *.vsix 169 | 170 | ### VisualStudioCode Patch ### 171 | # Ignore all local history of files 172 | .history 173 | .ionide 174 | 175 | ### Windows ### 176 | # Windows thumbnail cache files 177 | Thumbs.db 178 | Thumbs.db:encryptable 179 | ehthumbs.db 180 | ehthumbs_vista.db 181 | 182 | # Dump file 183 | *.stackdump 184 | 185 | # Folder config file 186 | [Dd]esktop.ini 187 | 188 | # Recycle Bin used on file shares 189 | $RECYCLE.BIN/ 190 | 191 | # Windows Installer files 192 | *.cab 193 | *.msi 194 | *.msix 195 | *.msm 196 | *.msp 197 | 198 | # Windows shortcuts 199 | *.lnk 200 | 201 | # End of https://www.toptal.com/developers/gitignore/api/windows,linux,macos,visualstudiocode,intellij+all,rust 202 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "liblzma-sys/xz"] 2 | path = liblzma-sys/xz 3 | url = https://github.com/tukaani-project/xz.git 4 | branch = v5.6 5 | -------------------------------------------------------------------------------- /Cargo.lock.msrv: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.1.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "bitflags" 16 | version = "1.3.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 19 | 20 | [[package]] 21 | name = "bitflags" 22 | version = "2.9.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" 25 | 26 | [[package]] 27 | name = "bumpalo" 28 | version = "3.17.0" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" 31 | 32 | [[package]] 33 | name = "cc" 34 | version = "1.0.49" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "e450b8da92aa6f274e7c6437692f9f2ce6d701fb73bacfcf87897b3f89a4c20e" 37 | dependencies = [ 38 | "jobserver", 39 | "num_cpus", 40 | ] 41 | 42 | [[package]] 43 | name = "cfg-if" 44 | version = "0.1.10" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 47 | 48 | [[package]] 49 | name = "cfg-if" 50 | version = "1.0.0" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 53 | 54 | [[package]] 55 | name = "console_error_panic_hook" 56 | version = "0.1.7" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" 59 | dependencies = [ 60 | "cfg-if 1.0.0", 61 | "wasm-bindgen", 62 | ] 63 | 64 | [[package]] 65 | name = "ctest2" 66 | version = "0.4.10" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "7cf53ed2a9e25d98cd444fd6e73eae7de0a26a8cb9e3f998170c6901a1afa0e5" 69 | dependencies = [ 70 | "cc", 71 | "garando_syntax", 72 | "rustc_version", 73 | ] 74 | 75 | [[package]] 76 | name = "dirs" 77 | version = "2.0.2" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" 80 | dependencies = [ 81 | "cfg-if 0.1.10", 82 | "dirs-sys", 83 | ] 84 | 85 | [[package]] 86 | name = "dirs-sys" 87 | version = "0.3.7" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" 90 | dependencies = [ 91 | "libc", 92 | "redox_users", 93 | "winapi", 94 | ] 95 | 96 | [[package]] 97 | name = "env_logger" 98 | version = "0.8.4" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" 101 | dependencies = [ 102 | "log", 103 | "regex", 104 | ] 105 | 106 | [[package]] 107 | name = "garando_errors" 108 | version = "0.1.0" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "18495ec4aced5922809efe4d2862918ff0e8d75e122bde57bba9bae45965256a" 111 | dependencies = [ 112 | "garando_pos", 113 | "libc", 114 | "serde", 115 | "term", 116 | "unicode-xid", 117 | ] 118 | 119 | [[package]] 120 | name = "garando_pos" 121 | version = "0.1.0" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "0c9386fc75dca486daefbbf5a8d2ea6f379237f95c9b982776159cd66f220aaf" 124 | dependencies = [ 125 | "serde", 126 | ] 127 | 128 | [[package]] 129 | name = "garando_syntax" 130 | version = "0.1.1" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "1d8a383861d12fc78c251bbcb1ec252dd8338714ce02ab0cc393cfd02f40d32b" 133 | dependencies = [ 134 | "bitflags 1.3.2", 135 | "garando_errors", 136 | "garando_pos", 137 | "log", 138 | "serde", 139 | "serde_json", 140 | "unicode-xid", 141 | ] 142 | 143 | [[package]] 144 | name = "getrandom" 145 | version = "0.1.16" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" 148 | dependencies = [ 149 | "cfg-if 1.0.0", 150 | "libc", 151 | "wasi 0.9.0+wasi-snapshot-preview1", 152 | ] 153 | 154 | [[package]] 155 | name = "getrandom" 156 | version = "0.2.5" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" 159 | dependencies = [ 160 | "cfg-if 1.0.0", 161 | "js-sys", 162 | "libc", 163 | "wasi 0.10.2+wasi-snapshot-preview1", 164 | "wasm-bindgen", 165 | ] 166 | 167 | [[package]] 168 | name = "hermit-abi" 169 | version = "0.3.9" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 172 | 173 | [[package]] 174 | name = "itoa" 175 | version = "1.0.15" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 178 | 179 | [[package]] 180 | name = "jobserver" 181 | version = "0.1.16" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "f74e73053eaf95399bf926e48fc7a2a3ce50bd0eaaa2357d391e95b2dcdd4f10" 184 | dependencies = [ 185 | "libc", 186 | "log", 187 | "rand 0.7.3", 188 | ] 189 | 190 | [[package]] 191 | name = "js-sys" 192 | version = "0.3.77" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 195 | dependencies = [ 196 | "once_cell", 197 | "wasm-bindgen", 198 | ] 199 | 200 | [[package]] 201 | name = "libc" 202 | version = "0.2.67" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018" 205 | 206 | [[package]] 207 | name = "liblzma" 208 | version = "0.4.1" 209 | dependencies = [ 210 | "getrandom 0.2.5", 211 | "liblzma-sys", 212 | "num_cpus", 213 | "quickcheck", 214 | "rand 0.8.5", 215 | "wasm-bindgen-test", 216 | ] 217 | 218 | [[package]] 219 | name = "liblzma-sys" 220 | version = "0.4.4" 221 | dependencies = [ 222 | "cc", 223 | "libc", 224 | "pkg-config", 225 | ] 226 | 227 | [[package]] 228 | name = "libredox" 229 | version = "0.1.3" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" 232 | dependencies = [ 233 | "bitflags 2.9.0", 234 | "libc", 235 | ] 236 | 237 | [[package]] 238 | name = "log" 239 | version = "0.4.27" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 242 | 243 | [[package]] 244 | name = "memchr" 245 | version = "2.6.0" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "76fc44e2588d5b436dbc3c6cf62aef290f90dab6235744a93dfe1cc18f451e2c" 248 | 249 | [[package]] 250 | name = "num_cpus" 251 | version = "1.16.0" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" 254 | dependencies = [ 255 | "hermit-abi", 256 | "libc", 257 | ] 258 | 259 | [[package]] 260 | name = "once_cell" 261 | version = "1.21.3" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 264 | 265 | [[package]] 266 | name = "pkg-config" 267 | version = "0.3.18" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | checksum = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33" 270 | 271 | [[package]] 272 | name = "ppv-lite86" 273 | version = "0.2.10" 274 | source = "registry+https://github.com/rust-lang/crates.io-index" 275 | checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" 276 | 277 | [[package]] 278 | name = "proc-macro2" 279 | version = "1.0.94" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" 282 | dependencies = [ 283 | "unicode-ident", 284 | ] 285 | 286 | [[package]] 287 | name = "quickcheck" 288 | version = "1.0.3" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6" 291 | dependencies = [ 292 | "env_logger", 293 | "log", 294 | "rand 0.8.5", 295 | ] 296 | 297 | [[package]] 298 | name = "quote" 299 | version = "1.0.40" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 302 | dependencies = [ 303 | "proc-macro2", 304 | ] 305 | 306 | [[package]] 307 | name = "rand" 308 | version = "0.7.3" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 311 | dependencies = [ 312 | "getrandom 0.1.16", 313 | "libc", 314 | "rand_chacha 0.2.2", 315 | "rand_core 0.5.1", 316 | "rand_hc", 317 | ] 318 | 319 | [[package]] 320 | name = "rand" 321 | version = "0.8.5" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 324 | dependencies = [ 325 | "libc", 326 | "rand_chacha 0.3.1", 327 | "rand_core 0.6.4", 328 | ] 329 | 330 | [[package]] 331 | name = "rand_chacha" 332 | version = "0.2.2" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 335 | dependencies = [ 336 | "ppv-lite86", 337 | "rand_core 0.5.1", 338 | ] 339 | 340 | [[package]] 341 | name = "rand_chacha" 342 | version = "0.3.1" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 345 | dependencies = [ 346 | "ppv-lite86", 347 | "rand_core 0.6.4", 348 | ] 349 | 350 | [[package]] 351 | name = "rand_core" 352 | version = "0.5.1" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 355 | dependencies = [ 356 | "getrandom 0.1.16", 357 | ] 358 | 359 | [[package]] 360 | name = "rand_core" 361 | version = "0.6.4" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 364 | dependencies = [ 365 | "getrandom 0.2.5", 366 | ] 367 | 368 | [[package]] 369 | name = "rand_hc" 370 | version = "0.2.0" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 373 | dependencies = [ 374 | "rand_core 0.5.1", 375 | ] 376 | 377 | [[package]] 378 | name = "redox_users" 379 | version = "0.4.6" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" 382 | dependencies = [ 383 | "getrandom 0.2.5", 384 | "libredox", 385 | "thiserror", 386 | ] 387 | 388 | [[package]] 389 | name = "regex" 390 | version = "1.9.0" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "89089e897c013b3deb627116ae56a6955a72b8bed395c9526af31c9fe528b484" 393 | dependencies = [ 394 | "aho-corasick", 395 | "memchr", 396 | "regex-automata", 397 | "regex-syntax", 398 | ] 399 | 400 | [[package]] 401 | name = "regex-automata" 402 | version = "0.3.9" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" 405 | dependencies = [ 406 | "aho-corasick", 407 | "memchr", 408 | "regex-syntax", 409 | ] 410 | 411 | [[package]] 412 | name = "regex-syntax" 413 | version = "0.7.5" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" 416 | 417 | [[package]] 418 | name = "rustc_version" 419 | version = "0.4.1" 420 | source = "registry+https://github.com/rust-lang/crates.io-index" 421 | checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" 422 | dependencies = [ 423 | "semver", 424 | ] 425 | 426 | [[package]] 427 | name = "rustversion" 428 | version = "1.0.20" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" 431 | 432 | [[package]] 433 | name = "ryu" 434 | version = "1.0.20" 435 | source = "registry+https://github.com/rust-lang/crates.io-index" 436 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 437 | 438 | [[package]] 439 | name = "scoped-tls" 440 | version = "1.0.1" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" 443 | 444 | [[package]] 445 | name = "semver" 446 | version = "1.0.26" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" 449 | 450 | [[package]] 451 | name = "serde" 452 | version = "1.0.219" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 455 | dependencies = [ 456 | "serde_derive", 457 | ] 458 | 459 | [[package]] 460 | name = "serde_derive" 461 | version = "1.0.219" 462 | source = "registry+https://github.com/rust-lang/crates.io-index" 463 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 464 | dependencies = [ 465 | "proc-macro2", 466 | "quote", 467 | "syn", 468 | ] 469 | 470 | [[package]] 471 | name = "serde_json" 472 | version = "1.0.140" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" 475 | dependencies = [ 476 | "itoa", 477 | "memchr", 478 | "ryu", 479 | "serde", 480 | ] 481 | 482 | [[package]] 483 | name = "syn" 484 | version = "2.0.100" 485 | source = "registry+https://github.com/rust-lang/crates.io-index" 486 | checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" 487 | dependencies = [ 488 | "proc-macro2", 489 | "quote", 490 | "unicode-ident", 491 | ] 492 | 493 | [[package]] 494 | name = "systest" 495 | version = "0.1.0" 496 | dependencies = [ 497 | "ctest2", 498 | "libc", 499 | "liblzma-sys", 500 | ] 501 | 502 | [[package]] 503 | name = "term" 504 | version = "0.6.1" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "c0863a3345e70f61d613eab32ee046ccd1bcc5f9105fe402c61fcd0c13eeb8b5" 507 | dependencies = [ 508 | "dirs", 509 | "winapi", 510 | ] 511 | 512 | [[package]] 513 | name = "thiserror" 514 | version = "1.0.69" 515 | source = "registry+https://github.com/rust-lang/crates.io-index" 516 | checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 517 | dependencies = [ 518 | "thiserror-impl", 519 | ] 520 | 521 | [[package]] 522 | name = "thiserror-impl" 523 | version = "1.0.69" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 526 | dependencies = [ 527 | "proc-macro2", 528 | "quote", 529 | "syn", 530 | ] 531 | 532 | [[package]] 533 | name = "unicode-ident" 534 | version = "1.0.18" 535 | source = "registry+https://github.com/rust-lang/crates.io-index" 536 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 537 | 538 | [[package]] 539 | name = "unicode-xid" 540 | version = "0.2.6" 541 | source = "registry+https://github.com/rust-lang/crates.io-index" 542 | checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" 543 | 544 | [[package]] 545 | name = "wasi" 546 | version = "0.9.0+wasi-snapshot-preview1" 547 | source = "registry+https://github.com/rust-lang/crates.io-index" 548 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 549 | 550 | [[package]] 551 | name = "wasi" 552 | version = "0.10.2+wasi-snapshot-preview1" 553 | source = "registry+https://github.com/rust-lang/crates.io-index" 554 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 555 | 556 | [[package]] 557 | name = "wasm-bindgen" 558 | version = "0.2.100" 559 | source = "registry+https://github.com/rust-lang/crates.io-index" 560 | checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 561 | dependencies = [ 562 | "cfg-if 1.0.0", 563 | "once_cell", 564 | "rustversion", 565 | "wasm-bindgen-macro", 566 | ] 567 | 568 | [[package]] 569 | name = "wasm-bindgen-backend" 570 | version = "0.2.100" 571 | source = "registry+https://github.com/rust-lang/crates.io-index" 572 | checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 573 | dependencies = [ 574 | "bumpalo", 575 | "log", 576 | "proc-macro2", 577 | "quote", 578 | "syn", 579 | "wasm-bindgen-shared", 580 | ] 581 | 582 | [[package]] 583 | name = "wasm-bindgen-futures" 584 | version = "0.4.50" 585 | source = "registry+https://github.com/rust-lang/crates.io-index" 586 | checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" 587 | dependencies = [ 588 | "cfg-if 1.0.0", 589 | "js-sys", 590 | "once_cell", 591 | "wasm-bindgen", 592 | "web-sys", 593 | ] 594 | 595 | [[package]] 596 | name = "wasm-bindgen-macro" 597 | version = "0.2.100" 598 | source = "registry+https://github.com/rust-lang/crates.io-index" 599 | checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 600 | dependencies = [ 601 | "quote", 602 | "wasm-bindgen-macro-support", 603 | ] 604 | 605 | [[package]] 606 | name = "wasm-bindgen-macro-support" 607 | version = "0.2.100" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 610 | dependencies = [ 611 | "proc-macro2", 612 | "quote", 613 | "syn", 614 | "wasm-bindgen-backend", 615 | "wasm-bindgen-shared", 616 | ] 617 | 618 | [[package]] 619 | name = "wasm-bindgen-shared" 620 | version = "0.2.100" 621 | source = "registry+https://github.com/rust-lang/crates.io-index" 622 | checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 623 | dependencies = [ 624 | "unicode-ident", 625 | ] 626 | 627 | [[package]] 628 | name = "wasm-bindgen-test" 629 | version = "0.3.42" 630 | source = "registry+https://github.com/rust-lang/crates.io-index" 631 | checksum = "d9bf62a58e0780af3e852044583deee40983e5886da43a271dd772379987667b" 632 | dependencies = [ 633 | "console_error_panic_hook", 634 | "js-sys", 635 | "scoped-tls", 636 | "wasm-bindgen", 637 | "wasm-bindgen-futures", 638 | "wasm-bindgen-test-macro", 639 | ] 640 | 641 | [[package]] 642 | name = "wasm-bindgen-test-macro" 643 | version = "0.3.42" 644 | source = "registry+https://github.com/rust-lang/crates.io-index" 645 | checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" 646 | dependencies = [ 647 | "proc-macro2", 648 | "quote", 649 | "syn", 650 | ] 651 | 652 | [[package]] 653 | name = "web-sys" 654 | version = "0.3.77" 655 | source = "registry+https://github.com/rust-lang/crates.io-index" 656 | checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" 657 | dependencies = [ 658 | "js-sys", 659 | "wasm-bindgen", 660 | ] 661 | 662 | [[package]] 663 | name = "winapi" 664 | version = "0.3.9" 665 | source = "registry+https://github.com/rust-lang/crates.io-index" 666 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 667 | dependencies = [ 668 | "winapi-i686-pc-windows-gnu", 669 | "winapi-x86_64-pc-windows-gnu", 670 | ] 671 | 672 | [[package]] 673 | name = "winapi-i686-pc-windows-gnu" 674 | version = "0.4.0" 675 | source = "registry+https://github.com/rust-lang/crates.io-index" 676 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 677 | 678 | [[package]] 679 | name = "winapi-x86_64-pc-windows-gnu" 680 | version = "0.4.0" 681 | source = "registry+https://github.com/rust-lang/crates.io-index" 682 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 683 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "liblzma" 3 | version = "0.4.1" 4 | authors = ["Alex Crichton ", "Portable-Network-Archive Developers"] 5 | license = "MIT OR Apache-2.0" 6 | readme = "README.md" 7 | keywords = ["lzma", "xz", "encoding", "wasm"] 8 | repository = "https://github.com/portable-network-archive/liblzma-rs" 9 | homepage = "https://github.com/portable-network-archive/liblzma-rs" 10 | description = """ 11 | Rust bindings to liblzma providing Read/Write streams as well as low-level 12 | in-memory encoding/decoding. forked from xz2. 13 | """ 14 | categories = ["compression", "api-bindings"] 15 | edition = "2021" 16 | rust-version = "1.60" 17 | exclude = [".github/"] 18 | 19 | [workspace] 20 | members = ["systest"] 21 | 22 | [dependencies] 23 | liblzma-sys = { path = "liblzma-sys", version = "0.4.3", default-features = false } 24 | num_cpus = { version = "1.16.0", optional = true } 25 | 26 | [dev-dependencies] 27 | rand = "0.8.0" 28 | quickcheck = "1.0.1" 29 | 30 | [target.'cfg(target_arch = "wasm32")'.dev-dependencies] 31 | getrandom = { version = "0.2", features = ["js"] } 32 | 33 | [target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dev-dependencies] 34 | wasm-bindgen-test = "0.3" 35 | 36 | [features] 37 | default = ["bindgen"] 38 | static = ["liblzma-sys/static"] 39 | parallel = ["liblzma-sys/parallel", "num_cpus"] 40 | bindgen = ["liblzma-sys/bindgen"] 41 | wasm = ["liblzma-sys/wasm"] 42 | uncheck_liblzma_version = ["liblzma-sys/uncheck_liblzma_version"] # Disables version checking for the system-installed liblzma. This is not recommended under normal circumstances. 43 | 44 | # These two are for cross-language LTO. 45 | # Will only work if `clang` is used to build the C library. 46 | fat-lto = ["liblzma-sys/fat-lto"] # Enable fat-lto, will override thin-lto if specified 47 | thin-lto = ["liblzma-sys/thin-lto"] # Enable thin-lto, will fall back to fat-lto if not supported 48 | 49 | [package.metadata.docs.rs] 50 | features = ["parallel"] 51 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016-2023 Alex Crichton and Portable-Network-Archive Developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # liblzma 2 | 3 | [![CI](https://github.com/Portable-Network-Archive/liblzma-rs/actions/workflows/main.yml/badge.svg)](https://github.com/Portable-Network-Archive/liblzma-rs/actions/workflows/main.yml) 4 | [![Crates.io][crates-badge]][crates-url] 5 | 6 | [crates-badge]: https://img.shields.io/crates/v/liblzma.svg 7 | [crates-url]: https://crates.io/crates/liblzma 8 | 9 | [Documentation](https://docs.rs/liblzma) 10 | 11 | Bindings to the liblzma implementation in Rust, also provides types to 12 | read/write xz streams. 13 | 14 | **This crate is forked from [xz2](https://crates.io/crates/xz2) and `liblzma = "0.1.x"` is fully compatible with `xz2 = "0.1.7"`,** 15 | so you can migrate simply. 16 | 17 | ## Migrate from xz2 18 | 19 | ```diff 20 | # Cargo.toml 21 | [dependencies] 22 | -xz2 = "0.1.7" 23 | +liblzma = "0.1.7" 24 | ``` 25 | 26 | ```diff 27 | // *.rs 28 | -use xz2; 29 | +use liblzma; 30 | ``` 31 | 32 | ## Version 0.2.x breaking changes 33 | 34 | - XZ upgraded to 5.4 35 | - Multithreading is disabled by default. 36 | This feature is available by enabling the `parallel` feature 37 | - Support for compiling to WebAssembly 38 | 39 | ## Version 0.3.x breaking changes 40 | 41 | - XZ upgraded to 5.6 42 | 43 | ## Version 0.4.x breaking changes 44 | 45 | - XZ upgraded to 5.8 46 | - Dropped `tokio` support (If you need async I/O, use [`async-compression`](https://github.com/Nullus157/async-compression) crate with `lzma` feature flag) 47 | 48 | ## License 49 | 50 | This project is licensed under either of 51 | 52 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or 53 | http://www.apache.org/licenses/LICENSE-2.0) 54 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or 55 | http://opensource.org/licenses/MIT) 56 | 57 | at your option. 58 | 59 | ### Contribution 60 | 61 | Unless you explicitly state otherwise, any contribution intentionally submitted 62 | for inclusion in liblzma by you, as defined in the Apache-2.0 license, shall be 63 | dual licensed as above, without any additional terms or conditions. 64 | -------------------------------------------------------------------------------- /THANKS: -------------------------------------------------------------------------------- 1 | Thanks 2 | ====== 3 | 4 | Some people have helped more, some less, but nevertheless everyone's help 5 | has been important. :-) In alphabetical order: 6 | - Ayush Singh 7 | - Ivan Krivosheev 8 | - Luke Street 9 | - Nathaniel Daniel 10 | 11 | Also thanks to all the people who have participated in the liblzma-rs project. 12 | 13 | I have probably forgot to add some names to the above list. Sorry about 14 | that and thanks for your help. 15 | -------------------------------------------------------------------------------- /liblzma-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "liblzma-sys" 3 | version = "0.4.4" 4 | authors = ["Alex Crichton ", "Portable-Network-Archive Developers"] 5 | build = "build.rs" 6 | links = "lzma" 7 | license = "MIT OR Apache-2.0" 8 | readme = "README.md" 9 | keywords = ["lzma", "xz", "encoding", "wasm"] 10 | repository = "https://github.com/portable-network-archive/liblzma-rs" 11 | homepage = "https://github.com/portable-network-archive/liblzma-rs" 12 | description = """ 13 | Raw bindings to liblzma which contains an implementation of LZMA and xz stream 14 | encoding/decoding. 15 | 16 | High level Rust bindings are available in the `liblzma` crate. 17 | """ 18 | categories = ["external-ffi-bindings"] 19 | edition = "2021" 20 | rust-version = "1.60" 21 | exclude = ["xz/.github/", "xz/tests/files/*"] 22 | 23 | [dependencies] 24 | libc = "0.2.67" 25 | 26 | [build-dependencies] 27 | cc = { version = "1.0.49", features = ["parallel"] } 28 | pkg-config = "0.3.18" 29 | 30 | [features] 31 | default = ["bindgen"] 32 | static = [] 33 | bindgen = [] 34 | parallel = [] 35 | wasm = ["static", "bindgen"] 36 | uncheck_liblzma_version = [] # Disables version checking for the system-installed liblzma. This is not recommended under normal circumstances. 37 | 38 | # These two are for cross-language LTO. 39 | # Will only work if `clang` is used to build the C library. 40 | fat-lto = [] # Enable fat-lto, will override thin-lto if specified 41 | thin-lto = [] # Enable thin-lto, will fall back to fat-lto if not supported 42 | -------------------------------------------------------------------------------- /liblzma-sys/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /liblzma-sys/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /liblzma-sys/README.md: -------------------------------------------------------------------------------- 1 | # liblzma-sys 2 | 3 | [![CI](https://github.com/Portable-Network-Archive/liblzma-rs/actions/workflows/main.yml/badge.svg)](https://github.com/Portable-Network-Archive/liblzma-rs/actions/workflows/main.yml) 4 | [![Crates.io][crates-badge]][crates-url] 5 | 6 | [crates-badge]: https://img.shields.io/crates/v/liblzma-sys.svg 7 | [crates-url]: https://crates.io/crates/liblzma-sys 8 | 9 | [Documentation](https://docs.rs/liblzma-sys) 10 | 11 | Raw bindings to liblzma which contains an implementation of LZMA and xz stream 12 | encoding/decoding. 13 | 14 | High level Rust bindings are available in the [liblzma](https://crates.io/crates/liblzma) crate. 15 | 16 | **This crate is forked from [lzma-sys](https://crates.io/crates/lzma-sys) and `liblzma-sys = "0.1.x"` is fully compatible with `lzma-sys = "0.1.20"`,** 17 | so you can migrate simply. 18 | 19 | ## Migrate from lzma-sys 20 | 21 | ```diff 22 | # Cargo.toml 23 | [dependencies] 24 | -lzma-sys = "0.1.20" 25 | +liblzma-sys = "0.1.20" 26 | ``` 27 | 28 | ```diff 29 | // *.rs 30 | -use lzma_sys; 31 | +use liblzma_sys; 32 | ``` 33 | 34 | ## Version 0.2.x breaking changes 35 | 36 | - XZ upgraded to 5.4 37 | - Multithreading is disabled by default. 38 | This feature is available by enabling the `parallel` feature 39 | - Support for compiling to WebAssembly 40 | 41 | ## Version 0.3.x breaking changes 42 | 43 | - XZ upgraded to 5.6 44 | 45 | ## Version 0.4.x breaking changes 46 | 47 | - XZ upgraded to 5.8 48 | 49 | ## License 50 | 51 | This project is licensed under either of 52 | 53 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or 54 | http://www.apache.org/licenses/LICENSE-2.0) 55 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or 56 | http://opensource.org/licenses/MIT) 57 | 58 | at your option. 59 | 60 | ### Contribution 61 | 62 | Unless you explicitly state otherwise, any contribution intentionally submitted 63 | for inclusion in liblzma-sys by you, as defined in the Apache-2.0 license, shall be 64 | dual licensed as above, without any additional terms or conditions. 65 | -------------------------------------------------------------------------------- /liblzma-sys/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs; 3 | use std::path::PathBuf; 4 | 5 | const SKIP_FILENAMES: &[&str] = &["crc32_small", "crc64_small", "crc_clmul_consts_gen"]; 6 | const MIN_LIBLZMA: &str = "5.8.0"; 7 | 8 | fn main() { 9 | let target = env::var("TARGET").unwrap(); 10 | 11 | println!("cargo:rerun-if-changed=build.rs"); 12 | println!("cargo:rerun-if-env-changed=LZMA_API_STATIC"); 13 | let want_static = cfg!(feature = "static") || env::var("LZMA_API_STATIC").is_ok(); 14 | let msvc = target.contains("msvc"); 15 | 16 | // If a static link is desired, we compile from source. 17 | // If we're compiling for MSVC, pkg-config runs a risk of picking up MinGW 18 | // libraries by accident, so disable it. 19 | // 20 | // Otherwise, check the system to see if it has a lzma library already 21 | // installed that we can use. 22 | let mut config = pkg_config::Config::new(); 23 | // Skip liblzma version check if `uncheck_liblzma_version` feature flag enabled. 24 | if !cfg!(feature = "uncheck_liblzma_version") { 25 | config.atleast_version(MIN_LIBLZMA); 26 | } 27 | if !want_static && !msvc { 28 | let pkg = config.probe("liblzma"); 29 | 30 | if pkg.is_ok() { 31 | return; 32 | } 33 | } 34 | 35 | let want_parallel = cfg!(feature = "parallel"); 36 | 37 | let out_dir = env::var("OUT_DIR").unwrap(); 38 | println!("cargo:root={}", out_dir); 39 | let include_dir = env::current_dir().unwrap().join("xz/src/liblzma/api"); 40 | println!("cargo:include={}", include_dir.display()); 41 | 42 | let mut src_files = [ 43 | "xz/src/liblzma/common", 44 | "xz/src/liblzma/lzma", 45 | "xz/src/liblzma/lz", 46 | "xz/src/liblzma/check", 47 | "xz/src/liblzma/delta", 48 | "xz/src/liblzma/rangecoder", 49 | "xz/src/liblzma/simple", 50 | ] 51 | .iter() 52 | .flat_map(|dir| read_dir_files(dir)) 53 | .chain(vec![ 54 | "xz/src/common/tuklib_cpucores.c".into(), 55 | "xz/src/common/tuklib_physmem.c".into(), 56 | ]) 57 | .collect::>(); 58 | 59 | if !want_parallel { 60 | src_files = src_files 61 | .into_iter() 62 | .filter(|path| !path.file_stem().unwrap().to_str().unwrap().ends_with("mt")) 63 | .collect::>(); 64 | } 65 | 66 | // sort to make build reproducible. 67 | src_files.sort(); 68 | 69 | let mut build = cc::Build::new(); 70 | 71 | if !cfg!(debug_assertions) { 72 | build.define("NDEBUG", None); 73 | } 74 | 75 | build 76 | .files(src_files) 77 | // all C preproc defines are in `./config.h` 78 | .define("HAVE_CONFIG_H", "1") 79 | .include("xz/src/liblzma/api") 80 | .include("xz/src/liblzma/lzma") 81 | .include("xz/src/liblzma/lz") 82 | .include("xz/src/liblzma/check") 83 | .include("xz/src/liblzma/simple") 84 | .include("xz/src/liblzma/delta") 85 | .include("xz/src/liblzma/common") 86 | .include("xz/src/liblzma/rangecoder") 87 | .include("xz/src/common") 88 | .include(env::current_dir().unwrap()); 89 | 90 | if !target.ends_with("msvc") { 91 | build.flag("-std=c99"); 92 | if want_parallel { 93 | build.flag("-pthread"); 94 | } 95 | } 96 | if cfg!(feature = "fat-lto") { 97 | build.flag_if_supported("-flto"); 98 | } else if cfg!(feature = "thin-lto") { 99 | // find the first flag in `flags` that is supported and add that to. 100 | let flags = ["-flto=thin", "-flto"]; 101 | let option = flags 102 | .into_iter() 103 | .find(|flag| build.is_flag_supported(flag).unwrap_or_default()); 104 | if let Some(flag) = option { 105 | build.flag(flag); 106 | } 107 | } 108 | if want_parallel { 109 | build.define("LZMA_SYS_ENABLE_THREADS", "1"); 110 | } 111 | 112 | if let Ok(s) = env::var("CARGO_CFG_TARGET_ENDIAN") { 113 | if s == "big" { 114 | build.define("WORDS_BIGENDIAN", None); 115 | } 116 | } 117 | 118 | // List out the WASM targets that need wasm-shim. 119 | // Note that Emscripten already provides its own C standard library so 120 | // wasm32-unknown-emscripten should not be included here. 121 | let need_wasm_shim = target == "wasm32-unknown-unknown" || target.starts_with("wasm32-wasi"); 122 | if need_wasm_shim { 123 | println!("cargo:rerun-if-changed=wasm-shim/assert.h"); 124 | println!("cargo:rerun-if-changed=wasm-shim/stdlib.h"); 125 | println!("cargo:rerun-if-changed=wasm-shim/string.h"); 126 | build.include("wasm-shim/"); 127 | } 128 | 129 | build.compile("liblzma.a"); 130 | } 131 | 132 | fn read_dir_files(dir: &str) -> impl Iterator { 133 | fs::read_dir(dir) 134 | .unwrap_or_else(|_| panic!("failed to read dir {}", dir)) 135 | .filter_map(|ent| { 136 | let ent = ent.expect("failed to read entry"); 137 | 138 | if ent.file_type().unwrap().is_dir() { 139 | return None; 140 | } 141 | 142 | let path = ent.path(); 143 | 144 | if path.extension().unwrap() != "c" { 145 | return None; 146 | } 147 | 148 | { 149 | let file_stem = path.file_stem().unwrap().to_str().unwrap(); 150 | if SKIP_FILENAMES.contains(&file_stem) { 151 | return None; 152 | } 153 | if file_stem.ends_with("tablegen") { 154 | return None; 155 | } 156 | } 157 | 158 | Some(path) 159 | }) 160 | } 161 | -------------------------------------------------------------------------------- /liblzma-sys/config.h: -------------------------------------------------------------------------------- 1 | // Configuration values for `liblzma` 2 | 3 | // tell `liblzma` to look for standard C99 headers 4 | #define HAVE_STDINT_H 1 5 | #define HAVE_STDBOOL_H 1 6 | #define HAVE_STRING_H 1 7 | 8 | // enable encoders/decoders 9 | #define HAVE_DECODER_LZMA1 1 10 | #define HAVE_DECODER_LZMA2 1 11 | #define HAVE_ENCODER_LZMA1 1 12 | #define HAVE_ENCODER_LZMA2 1 13 | #define HAVE_DECODER_LZ 1 14 | #define HAVE_ENCODER_LZ 1 15 | #define HAVE_DECODER_ARM 1 16 | #define HAVE_ENCODER_ARM 1 17 | #define HAVE_DECODER_ARM64 1 18 | #define HAVE_ENCODER_ARM64 1 19 | #define HAVE_DECODER_ARMTHUMB 1 20 | #define HAVE_ENCODER_ARMTHUMB 1 21 | #define HAVE_DECODER_DELTA 1 22 | #define HAVE_ENCODER_DELTA 1 23 | #define HAVE_DECODER_IA64 1 24 | #define HAVE_ENCODER_IA64 1 25 | #define HAVE_DECODER_POWERPC 1 26 | #define HAVE_ENCODER_POWERPC 1 27 | #define HAVE_DECODER_RISCV 1 28 | #define HAVE_ENCODER_RISCV 1 29 | #define HAVE_DECODER_SPARC 1 30 | #define HAVE_ENCODER_SPARC 1 31 | #define HAVE_DECODER_X86 1 32 | #define HAVE_ENCODER_X86 1 33 | 34 | // enable checksums 35 | #define HAVE_CHECK_SHA256 1 36 | #define HAVE_CHECK_CRC64 1 37 | #define HAVE_CHECK_CRC32 1 38 | 39 | // enable other liblzma stuff 40 | #define HAVE_MF_BT2 1 41 | #define HAVE_MF_BT3 1 42 | #define HAVE_MF_BT4 1 43 | #define HAVE_MF_HC3 1 44 | #define HAVE_MF_HC4 1 45 | 46 | #if defined(MSC_VER) || defined(WIN32) || defined(WIN64) || defined(_WIN32) 47 | #if defined(LZMA_SYS_ENABLE_THREADS) 48 | // change to `MYTHREAD_WIN95` if targeting Windows XP or earlier 49 | #define MYTHREAD_VISTA 1 50 | #endif 51 | #else 52 | #define _POSIX_C_SOURCE 199506L 53 | #if defined(LZMA_SYS_ENABLE_THREADS) 54 | #define MYTHREAD_POSIX 1 55 | #endif 56 | #endif 57 | 58 | #if defined(__sun) 59 | #define HAVE_CLOCK_GETTIME 1 60 | #define HAVE_DECL_CLOCK_MONOTONIC 1 61 | #endif 62 | -------------------------------------------------------------------------------- /liblzma-sys/src/bindgen_wrap.rs: -------------------------------------------------------------------------------- 1 | pub use crate::bindgen::*; 2 | 3 | // lzma_action 4 | pub const LZMA_RUN: lzma_action = lzma_action_LZMA_RUN; 5 | pub const LZMA_SYNC_FLUSH: lzma_action = lzma_action_LZMA_SYNC_FLUSH; 6 | pub const LZMA_FULL_FLUSH: lzma_action = lzma_action_LZMA_FULL_FLUSH; 7 | pub const LZMA_FULL_BARRIER: lzma_action = lzma_action_LZMA_FULL_BARRIER; 8 | pub const LZMA_FINISH: lzma_action = lzma_action_LZMA_FINISH; 9 | 10 | // lzma_check 11 | pub const LZMA_CHECK_NONE: lzma_check = lzma_check_LZMA_CHECK_NONE; 12 | pub const LZMA_CHECK_CRC32: lzma_check = lzma_check_LZMA_CHECK_CRC32; 13 | pub const LZMA_CHECK_CRC64: lzma_check = lzma_check_LZMA_CHECK_CRC64; 14 | pub const LZMA_CHECK_SHA256: lzma_check = lzma_check_LZMA_CHECK_SHA256; 15 | 16 | // lzma_mode 17 | pub const LZMA_MODE_FAST: lzma_mode = lzma_mode_LZMA_MODE_FAST; 18 | pub const LZMA_MODE_NORMAL: lzma_mode = lzma_mode_LZMA_MODE_NORMAL; 19 | 20 | // lzma_match_finder 21 | pub const LZMA_MF_HC3: lzma_match_finder = lzma_match_finder_LZMA_MF_HC3; 22 | pub const LZMA_MF_HC4: lzma_match_finder = lzma_match_finder_LZMA_MF_HC4; 23 | pub const LZMA_MF_BT2: lzma_match_finder = lzma_match_finder_LZMA_MF_BT2; 24 | pub const LZMA_MF_BT3: lzma_match_finder = lzma_match_finder_LZMA_MF_BT3; 25 | pub const LZMA_MF_BT4: lzma_match_finder = lzma_match_finder_LZMA_MF_BT4; 26 | 27 | // macros 28 | pub const LZMA_TELL_NO_CHECK: u32 = 0x01; 29 | pub const LZMA_TELL_UNSUPPORTED_CHECK: u32 = 0x02; 30 | pub const LZMA_TELL_ANY_CHECK: u32 = 0x04; 31 | pub const LZMA_IGNORE_CHECK: u32 = 0x10; 32 | pub const LZMA_CONCATENATED: u32 = 0x08; 33 | 34 | pub const LZMA_PRESET_DEFAULT: u32 = 6; 35 | pub const LZMA_PRESET_LEVEL_MASK: u32 = 0x1f; 36 | pub const LZMA_PRESET_EXTREME: u32 = 1 << 31; 37 | 38 | pub const LZMA_DICT_SIZE_MIN: u32 = 4096; 39 | pub const LZMA_DICT_SIZE_DEFAULT: u32 = 1 << 23; 40 | 41 | pub const LZMA_BACKWARD_SIZE_MIN: lzma_vli = 4; 42 | pub const LZMA_BACKWARD_SIZE_MAX: lzma_vli = 1 << 34; 43 | 44 | // filters 45 | pub const LZMA_FILTER_X86: lzma_vli = 0x04; 46 | pub const LZMA_FILTER_POWERPC: lzma_vli = 0x05; 47 | pub const LZMA_FILTER_IA64: lzma_vli = 0x06; 48 | pub const LZMA_FILTER_ARM: lzma_vli = 0x07; 49 | pub const LZMA_FILTER_ARMTHUMB: lzma_vli = 0x08; 50 | pub const LZMA_FILTER_SPARC: lzma_vli = 0x09; 51 | pub const LZMA_FILTER_ARM64: lzma_vli = 0x0A; 52 | pub const LZMA_FILTER_DELTA: lzma_vli = 0x03; 53 | pub const LZMA_FILTER_RISCV: lzma_vli = 0x0B; 54 | pub const LZMA_FILTER_LZMA1: lzma_vli = 0x4000000000000001; 55 | pub const LZMA_FILTER_LZMA2: lzma_vli = 0x21; 56 | 57 | // status 58 | pub const LZMA_OK: lzma_ret = lzma_ret_LZMA_OK; 59 | pub const LZMA_STREAM_END: lzma_ret = lzma_ret_LZMA_STREAM_END; 60 | pub const LZMA_NO_CHECK: lzma_ret = lzma_ret_LZMA_NO_CHECK; 61 | pub const LZMA_UNSUPPORTED_CHECK: lzma_ret = lzma_ret_LZMA_UNSUPPORTED_CHECK; 62 | pub const LZMA_GET_CHECK: lzma_ret = lzma_ret_LZMA_GET_CHECK; 63 | pub const LZMA_MEM_ERROR: lzma_ret = lzma_ret_LZMA_MEM_ERROR; 64 | pub const LZMA_MEMLIMIT_ERROR: lzma_ret = lzma_ret_LZMA_MEMLIMIT_ERROR; 65 | pub const LZMA_FORMAT_ERROR: lzma_ret = lzma_ret_LZMA_FORMAT_ERROR; 66 | pub const LZMA_OPTIONS_ERROR: lzma_ret = lzma_ret_LZMA_OPTIONS_ERROR; 67 | pub const LZMA_DATA_ERROR: lzma_ret = lzma_ret_LZMA_DATA_ERROR; 68 | pub const LZMA_BUF_ERROR: lzma_ret = lzma_ret_LZMA_BUF_ERROR; 69 | pub const LZMA_PROG_ERROR: lzma_ret = lzma_ret_LZMA_PROG_ERROR; 70 | pub const LZMA_SEEK_NEEDED: lzma_ret = lzma_ret_LZMA_SEEK_NEEDED; 71 | -------------------------------------------------------------------------------- /liblzma-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc(html_root_url = "https://docs.rs/liblzma-sys/0.4.4")] 2 | #![allow(bad_style)] 3 | 4 | #[cfg(feature = "bindgen")] 5 | mod bindgen; 6 | #[cfg(feature = "bindgen")] 7 | mod bindgen_wrap; 8 | #[cfg(not(feature = "bindgen"))] 9 | mod manual; 10 | 11 | #[cfg(target_arch = "wasm32")] 12 | mod wasm_shim; 13 | 14 | #[cfg(feature = "bindgen")] 15 | pub use bindgen_wrap::*; 16 | #[cfg(not(feature = "bindgen"))] 17 | pub use manual::*; 18 | -------------------------------------------------------------------------------- /liblzma-sys/src/manual.rs: -------------------------------------------------------------------------------- 1 | use libc::{c_char, c_uchar, c_void, size_t}; 2 | use std::u64; 3 | 4 | #[cfg(target_env = "msvc")] 5 | #[doc(hidden)] 6 | pub type __enum_ty = libc::c_int; 7 | #[cfg(not(target_env = "msvc"))] 8 | #[doc(hidden)] 9 | pub type __enum_ty = libc::c_uint; 10 | pub type lzma_bool = c_uchar; 11 | pub type lzma_ret = __enum_ty; 12 | pub type lzma_action = __enum_ty; 13 | type lzma_reserved_enum = __enum_ty; 14 | pub type lzma_check = __enum_ty; 15 | pub type lzma_vli = u64; 16 | pub type lzma_mode = __enum_ty; 17 | pub type lzma_match_finder = __enum_ty; 18 | 19 | pub const LZMA_OK: lzma_ret = 0; 20 | pub const LZMA_STREAM_END: lzma_ret = 1; 21 | pub const LZMA_NO_CHECK: lzma_ret = 2; 22 | pub const LZMA_UNSUPPORTED_CHECK: lzma_ret = 3; 23 | pub const LZMA_GET_CHECK: lzma_ret = 4; 24 | pub const LZMA_MEM_ERROR: lzma_ret = 5; 25 | pub const LZMA_MEMLIMIT_ERROR: lzma_ret = 6; 26 | pub const LZMA_FORMAT_ERROR: lzma_ret = 7; 27 | pub const LZMA_OPTIONS_ERROR: lzma_ret = 8; 28 | pub const LZMA_DATA_ERROR: lzma_ret = 9; 29 | pub const LZMA_BUF_ERROR: lzma_ret = 10; 30 | pub const LZMA_PROG_ERROR: lzma_ret = 11; 31 | pub const LZMA_SEEK_NEEDED: lzma_ret = 12; 32 | 33 | pub const LZMA_RUN: lzma_action = 0; 34 | pub const LZMA_SYNC_FLUSH: lzma_action = 1; 35 | pub const LZMA_FULL_FLUSH: lzma_action = 2; 36 | pub const LZMA_FULL_BARRIER: lzma_action = 4; 37 | pub const LZMA_FINISH: lzma_action = 3; 38 | 39 | pub const LZMA_CHECK_NONE: lzma_check = 0; 40 | pub const LZMA_CHECK_CRC32: lzma_check = 1; 41 | pub const LZMA_CHECK_CRC64: lzma_check = 4; 42 | pub const LZMA_CHECK_SHA256: lzma_check = 10; 43 | 44 | pub const LZMA_MODE_FAST: lzma_mode = 1; 45 | pub const LZMA_MODE_NORMAL: lzma_mode = 2; 46 | 47 | pub const LZMA_MF_HC3: lzma_match_finder = 0x03; 48 | pub const LZMA_MF_HC4: lzma_match_finder = 0x04; 49 | pub const LZMA_MF_BT2: lzma_match_finder = 0x12; 50 | pub const LZMA_MF_BT3: lzma_match_finder = 0x13; 51 | pub const LZMA_MF_BT4: lzma_match_finder = 0x14; 52 | 53 | pub const LZMA_TELL_NO_CHECK: u32 = 0x01; 54 | pub const LZMA_TELL_UNSUPPORTED_CHECK: u32 = 0x02; 55 | pub const LZMA_TELL_ANY_CHECK: u32 = 0x04; 56 | pub const LZMA_IGNORE_CHECK: u32 = 0x10; 57 | pub const LZMA_CONCATENATED: u32 = 0x08; 58 | 59 | pub const LZMA_PRESET_DEFAULT: u32 = 6; 60 | pub const LZMA_PRESET_LEVEL_MASK: u32 = 0x1f; 61 | pub const LZMA_PRESET_EXTREME: u32 = 1 << 31; 62 | 63 | pub const LZMA_DICT_SIZE_MIN: u32 = 4096; 64 | pub const LZMA_DICT_SIZE_DEFAULT: u32 = 1 << 23; 65 | 66 | pub const LZMA_LCLP_MIN: u32 = 0; 67 | pub const LZMA_LCLP_MAX: u32 = 4; 68 | pub const LZMA_LC_DEFAULT: u32 = 3; 69 | 70 | pub const LZMA_LP_DEFAULT: u32 = 0; 71 | 72 | pub const LZMA_PB_MIN: u32 = 0; 73 | pub const LZMA_PB_MAX: u32 = 4; 74 | pub const LZMA_PB_DEFAULT: u32 = 2; 75 | 76 | pub const LZMA_BACKWARD_SIZE_MIN: lzma_vli = 4; 77 | pub const LZMA_BACKWARD_SIZE_MAX: lzma_vli = 1 << 34; 78 | 79 | pub const LZMA_VLI_MAX: lzma_vli = u64::MAX / 2; 80 | pub const LZMA_VLI_UNKNOWN: lzma_vli = u64::MAX; 81 | pub const LZMA_VLI_BYTES_MAX: usize = 9; 82 | 83 | pub const LZMA_FILTER_X86: lzma_vli = 0x04; 84 | pub const LZMA_FILTER_POWERPC: lzma_vli = 0x05; 85 | pub const LZMA_FILTER_IA64: lzma_vli = 0x06; 86 | pub const LZMA_FILTER_ARM: lzma_vli = 0x07; 87 | pub const LZMA_FILTER_ARMTHUMB: lzma_vli = 0x08; 88 | pub const LZMA_FILTER_SPARC: lzma_vli = 0x09; 89 | pub const LZMA_FILTER_ARM64: lzma_vli = 0x0A; 90 | pub const LZMA_FILTER_DELTA: lzma_vli = 0x03; 91 | pub const LZMA_FILTER_RISCV: lzma_vli = 0x0B; 92 | pub const LZMA_FILTER_LZMA1: lzma_vli = 0x4000000000000001; 93 | pub const LZMA_FILTER_LZMA2: lzma_vli = 0x21; 94 | 95 | #[repr(C)] 96 | pub struct lzma_allocator { 97 | pub alloc: Option *mut c_void>, 98 | pub free: Option, 99 | pub opaque: *mut c_void, 100 | } 101 | 102 | pub enum lzma_internal {} 103 | 104 | #[repr(C)] 105 | pub struct lzma_stream { 106 | pub next_in: *const u8, 107 | pub avail_in: size_t, 108 | pub total_in: u64, 109 | pub next_out: *mut u8, 110 | pub avail_out: size_t, 111 | pub total_out: u64, 112 | pub allocator: *const lzma_allocator, 113 | 114 | internal: *mut lzma_internal, 115 | reserved_ptr1: *mut c_void, 116 | reserved_ptr2: *mut c_void, 117 | reserved_ptr3: *mut c_void, 118 | reserved_ptr4: *mut c_void, 119 | reserved_int1: u64, 120 | reserved_int2: u64, 121 | reserved_int3: size_t, 122 | reserved_int4: size_t, 123 | reserved_enum1: lzma_reserved_enum, 124 | reserved_enum2: lzma_reserved_enum, 125 | } 126 | 127 | #[repr(C)] 128 | pub struct lzma_filter { 129 | pub id: lzma_vli, 130 | pub options: *mut c_void, 131 | } 132 | 133 | #[cfg(feature = "parallel")] 134 | #[repr(C)] 135 | pub struct lzma_mt { 136 | pub flags: u32, 137 | pub threads: u32, 138 | pub block_size: u64, 139 | pub timeout: u32, 140 | pub preset: u32, 141 | pub filters: *const lzma_filter, 142 | pub check: lzma_check, 143 | 144 | reserved_enum1: lzma_reserved_enum, 145 | reserved_enum2: lzma_reserved_enum, 146 | reserved_enum3: lzma_reserved_enum, 147 | reserved_int1: u32, 148 | reserved_int2: u32, 149 | reserved_int3: u32, 150 | reserved_int4: u32, 151 | pub memlimit_threading: u64, 152 | pub memlimit_stop: u64, 153 | reserved_int7: u64, 154 | reserved_int8: u64, 155 | reserved_ptr1: *mut c_void, 156 | reserved_ptr2: *mut c_void, 157 | reserved_ptr3: *mut c_void, 158 | reserved_ptr4: *mut c_void, 159 | } 160 | 161 | #[repr(C)] 162 | #[derive(Copy, Clone)] 163 | pub struct lzma_options_lzma { 164 | pub dict_size: u32, 165 | pub preset_dict: *const u8, 166 | pub preset_dict_size: u32, 167 | pub lc: u32, 168 | pub lp: u32, 169 | pub pb: u32, 170 | pub mode: lzma_mode, 171 | pub nice_len: u32, 172 | pub mf: lzma_match_finder, 173 | pub depth: u32, 174 | 175 | reserved_int1: u32, 176 | reserved_int2: u32, 177 | reserved_int3: u32, 178 | reserved_int4: u32, 179 | reserved_int5: u32, 180 | reserved_int6: u32, 181 | reserved_int7: u32, 182 | reserved_int8: u32, 183 | reserved_enum1: lzma_reserved_enum, 184 | reserved_enum2: lzma_reserved_enum, 185 | reserved_enum3: lzma_reserved_enum, 186 | reserved_enum4: lzma_reserved_enum, 187 | reserved_ptr1: *mut c_void, 188 | reserved_ptr2: *mut c_void, 189 | } 190 | 191 | #[repr(C)] 192 | pub struct lzma_stream_flags { 193 | pub version: u32, 194 | pub backward_size: lzma_vli, 195 | pub check: lzma_check, 196 | 197 | reserved_enum1: lzma_reserved_enum, 198 | reserved_enum2: lzma_reserved_enum, 199 | reserved_enum3: lzma_reserved_enum, 200 | reserved_enum4: lzma_reserved_enum, 201 | reserved_bool1: lzma_bool, 202 | reserved_bool2: lzma_bool, 203 | reserved_bool3: lzma_bool, 204 | reserved_bool4: lzma_bool, 205 | reserved_bool5: lzma_bool, 206 | reserved_bool6: lzma_bool, 207 | reserved_bool7: lzma_bool, 208 | reserved_bool8: lzma_bool, 209 | reserved_int1: u32, 210 | reserved_int2: u32, 211 | } 212 | 213 | #[repr(C)] 214 | pub struct lzma_options_bcj { 215 | pub start_offset: u32, 216 | } 217 | 218 | extern "C" { 219 | pub fn lzma_code(strm: *mut lzma_stream, action: lzma_action) -> lzma_ret; 220 | pub fn lzma_end(strm: *mut lzma_stream); 221 | pub fn lzma_get_progress(strm: *mut lzma_stream, progress_in: *mut u64, progress_out: *mut u64); 222 | pub fn lzma_memusage(strm: *const lzma_stream) -> u64; 223 | pub fn lzma_memlimit_get(strm: *const lzma_stream) -> u64; 224 | pub fn lzma_memlimit_set(strm: *mut lzma_stream, memlimit: u64) -> lzma_ret; 225 | 226 | pub fn lzma_easy_encoder_memusage(preset: u32) -> u64; 227 | pub fn lzma_easy_decoder_memusage(preset: u32) -> u64; 228 | pub fn lzma_easy_encoder(strm: *mut lzma_stream, preset: u32, check: lzma_check) -> lzma_ret; 229 | pub fn lzma_easy_buffer_encode( 230 | preset: u32, 231 | check: lzma_check, 232 | allocator: *const lzma_allocator, 233 | input: *const u8, 234 | in_size: size_t, 235 | out: *mut u8, 236 | out_pos: *mut size_t, 237 | out_size: size_t, 238 | ) -> lzma_ret; 239 | 240 | pub fn lzma_stream_encoder( 241 | strm: *mut lzma_stream, 242 | filters: *const lzma_filter, 243 | check: lzma_check, 244 | ) -> lzma_ret; 245 | #[cfg(feature = "parallel")] 246 | pub fn lzma_stream_encoder_mt_memusage(options: *const lzma_mt) -> u64; 247 | #[cfg(feature = "parallel")] 248 | pub fn lzma_stream_encoder_mt(strm: *mut lzma_stream, options: *const lzma_mt) -> lzma_ret; 249 | #[cfg(feature = "parallel")] 250 | pub fn lzma_stream_decoder_mt(strm: *mut lzma_stream, options: *const lzma_mt) -> lzma_ret; 251 | #[cfg(feature = "parallel")] 252 | pub fn lzma_mt_block_size(filters: *const crate::lzma_filter) -> u64; 253 | 254 | pub fn lzma_alone_encoder( 255 | strm: *mut lzma_stream, 256 | options: *const lzma_options_lzma, 257 | ) -> lzma_ret; 258 | 259 | pub fn lzma_stream_buffer_bound(uncompressed_size: size_t) -> size_t; 260 | pub fn lzma_stream_buffer_encode( 261 | filters: *mut lzma_filter, 262 | check: lzma_check, 263 | allocator: *const lzma_allocator, 264 | input: *const u8, 265 | in_size: size_t, 266 | out: *mut u8, 267 | out_pos: *mut size_t, 268 | out_size: size_t, 269 | ) -> lzma_ret; 270 | 271 | pub fn lzma_stream_decoder(strm: *mut lzma_stream, memlimit: u64, flags: u32) -> lzma_ret; 272 | pub fn lzma_auto_decoder(strm: *mut lzma_stream, memlimit: u64, flags: u32) -> lzma_ret; 273 | pub fn lzma_alone_decoder(strm: *mut lzma_stream, memlimit: u64) -> lzma_ret; 274 | pub fn lzma_lzip_decoder(strm: *mut lzma_stream, memlimit: u64, flags: u32) -> lzma_ret; 275 | pub fn lzma_stream_buffer_decode( 276 | memlimit: *mut u64, 277 | flags: u32, 278 | allocator: *const lzma_allocator, 279 | input: *const u8, 280 | in_pos: *mut size_t, 281 | in_size: size_t, 282 | out: *mut u8, 283 | out_pos: *mut size_t, 284 | out_size: size_t, 285 | ) -> lzma_ret; 286 | 287 | pub fn lzma_check_is_supported(check: lzma_check) -> lzma_bool; 288 | pub fn lzma_check_size(check: lzma_check) -> u32; 289 | 290 | pub fn lzma_crc32(buf: *const u8, size: size_t, crc: u32) -> u32; 291 | pub fn lzma_crc64(buf: *const u8, size: size_t, crc: u64) -> u64; 292 | pub fn lzma_get_check(strm: *const lzma_stream) -> lzma_check; 293 | 294 | pub fn lzma_filter_encoder_is_supported(id: lzma_vli) -> lzma_bool; 295 | pub fn lzma_filter_decoder_is_supported(id: lzma_vli) -> lzma_bool; 296 | pub fn lzma_filters_copy( 297 | src: *const lzma_filter, 298 | dest: *mut lzma_filter, 299 | allocator: *const lzma_allocator, 300 | ) -> lzma_ret; 301 | pub fn lzma_raw_encoder_memusage(filters: *const lzma_filter) -> u64; 302 | pub fn lzma_raw_decoder_memusage(filters: *const lzma_filter) -> u64; 303 | pub fn lzma_raw_encoder(strm: *mut lzma_stream, filters: *const lzma_filter) -> lzma_ret; 304 | pub fn lzma_raw_decoder(strm: *mut lzma_stream, filters: *const lzma_filter) -> lzma_ret; 305 | pub fn lzma_filters_update(strm: *mut lzma_stream, filters: *const lzma_filter) -> lzma_ret; 306 | pub fn lzma_raw_buffer_encode( 307 | filters: *const lzma_filter, 308 | allocator: *const lzma_allocator, 309 | input: *const u8, 310 | in_size: size_t, 311 | out: *mut u8, 312 | out_pos: *mut size_t, 313 | out_size: size_t, 314 | ) -> lzma_ret; 315 | pub fn lzma_raw_buffer_decode( 316 | filters: *const lzma_filter, 317 | allocator: *const lzma_allocator, 318 | input: *const u8, 319 | in_pos: *mut size_t, 320 | in_size: size_t, 321 | out: *mut u8, 322 | out_pos: *mut size_t, 323 | out_size: size_t, 324 | ) -> lzma_ret; 325 | pub fn lzma_properties_size(size: *mut u32, filter: *const lzma_filter) -> lzma_ret; 326 | pub fn lzma_properties_encode(filter: *const lzma_filter, props: *mut u8) -> lzma_ret; 327 | pub fn lzma_properties_decode( 328 | filter: *mut lzma_filter, 329 | allocator: *const lzma_allocator, 330 | props: *const u8, 331 | props_size: size_t, 332 | ) -> lzma_ret; 333 | pub fn lzma_physmem() -> u64; 334 | pub fn lzma_cputhreads() -> u32; 335 | 336 | pub fn lzma_stream_header_encode(options: *const lzma_stream_flags, out: *mut u8) -> lzma_ret; 337 | pub fn lzma_stream_footer_encode(options: *const lzma_stream_flags, out: *mut u8) -> lzma_ret; 338 | pub fn lzma_stream_header_decode(options: *mut lzma_stream_flags, input: *const u8) 339 | -> lzma_ret; 340 | pub fn lzma_stream_footer_decode(options: *mut lzma_stream_flags, input: *const u8) 341 | -> lzma_ret; 342 | pub fn lzma_stream_flags_compare( 343 | a: *const lzma_stream_flags, 344 | b: *const lzma_stream_flags, 345 | ) -> lzma_ret; 346 | 347 | pub fn lzma_version_number() -> u32; 348 | pub fn lzma_version_string() -> *const c_char; 349 | 350 | pub fn lzma_vli_encode( 351 | vli: lzma_vli, 352 | vli_pos: *mut size_t, 353 | out: *mut u8, 354 | out_pos: *mut size_t, 355 | out_size: size_t, 356 | ) -> lzma_ret; 357 | pub fn lzma_vli_decode( 358 | vli: *mut lzma_vli, 359 | vli_pos: *mut size_t, 360 | input: *const u8, 361 | in_pos: *mut size_t, 362 | in_size: size_t, 363 | ) -> lzma_ret; 364 | pub fn lzma_vli_size(vli: lzma_vli) -> u32; 365 | 366 | pub fn lzma_lzma_preset(options: *mut lzma_options_lzma, preset: u32) -> lzma_bool; 367 | pub fn lzma_mf_is_supported(mf: lzma_match_finder) -> lzma_bool; 368 | } 369 | -------------------------------------------------------------------------------- /liblzma-sys/src/wasm_shim.rs: -------------------------------------------------------------------------------- 1 | use core::ffi::{c_char, c_int, c_void}; 2 | use std::alloc::{alloc, alloc_zeroed, dealloc, Layout}; 3 | 4 | #[no_mangle] 5 | pub extern "C" fn rust_lzma_wasm_shim_malloc(size: usize) -> *mut c_void { 6 | wasm_shim_alloc::(size) 7 | } 8 | 9 | #[no_mangle] 10 | pub extern "C" fn rust_lzma_wasm_shim_calloc(nmemb: usize, size: usize) -> *mut c_void { 11 | // note: calloc expects the allocation to be zeroed 12 | wasm_shim_alloc::(nmemb * size) 13 | } 14 | 15 | #[no_mangle] 16 | pub unsafe extern "C" fn rust_lzma_wasm_shim_free(ptr: *mut c_void) { 17 | if ptr.is_null() { 18 | return; 19 | } 20 | wasm_shim_free(ptr) 21 | } 22 | 23 | #[no_mangle] 24 | pub extern "C" fn rust_lzma_wasm_shim_memcmp( 25 | str1: *const c_void, 26 | str2: *const c_void, 27 | n: usize, 28 | ) -> i32 { 29 | // Safety: function contracts requires str1 and str2 at least `n`-long. 30 | unsafe { 31 | let str1: &[u8] = core::slice::from_raw_parts(str1 as *const u8, n); 32 | let str2: &[u8] = core::slice::from_raw_parts(str2 as *const u8, n); 33 | match str1.cmp(str2) { 34 | core::cmp::Ordering::Less => -1, 35 | core::cmp::Ordering::Equal => 0, 36 | core::cmp::Ordering::Greater => 1, 37 | } 38 | } 39 | } 40 | 41 | #[no_mangle] 42 | pub unsafe extern "C" fn rust_lzma_wasm_shim_memcpy( 43 | dest: *mut c_void, 44 | src: *const c_void, 45 | n: usize, 46 | ) -> *mut c_void { 47 | core::ptr::copy_nonoverlapping(src as *const u8, dest as *mut u8, n); 48 | dest 49 | } 50 | 51 | #[no_mangle] 52 | pub unsafe extern "C" fn rust_lzma_wasm_shim_memmove( 53 | dest: *mut c_void, 54 | src: *const c_void, 55 | n: usize, 56 | ) -> *mut c_void { 57 | core::ptr::copy(src as *const u8, dest as *mut u8, n); 58 | dest 59 | } 60 | 61 | #[no_mangle] 62 | pub unsafe extern "C" fn rust_lzma_wasm_shim_memset( 63 | dest: *mut c_void, 64 | c: c_int, 65 | n: usize, 66 | ) -> *mut c_void { 67 | core::ptr::write_bytes(dest as *mut u8, c as u8, n); 68 | dest 69 | } 70 | 71 | #[no_mangle] 72 | pub unsafe extern "C" fn rust_lzma_wasm_shim_strlen(s: *const c_char) -> usize { 73 | let str = unsafe { std::ffi::CStr::from_ptr(s) }; 74 | str.to_bytes().len() 75 | } 76 | 77 | #[no_mangle] 78 | pub unsafe extern "C" fn rust_lzma_wasm_shim_memchr( 79 | s: *const c_void, 80 | c: c_int, 81 | n: usize, 82 | ) -> *mut c_void { 83 | let s_slice = unsafe { core::slice::from_raw_parts(s as *const u8, n) }; 84 | s_slice 85 | .iter() 86 | .position(|&r| r == c as u8) 87 | .map_or(core::ptr::null_mut(), |p| unsafe { 88 | s.add(p) as *mut c_void 89 | }) 90 | } 91 | 92 | const USIZE_ALIGN: usize = core::mem::align_of::(); 93 | const USIZE_SIZE: usize = core::mem::size_of::(); 94 | 95 | #[inline] 96 | fn wasm_shim_alloc(size: usize) -> *mut c_void { 97 | // in order to recover the size upon free, we store the size below the allocation 98 | // special alignment is never requested via the malloc API, 99 | // so it's not stored, and usize-alignment is used 100 | // memory layout: [size] [allocation] 101 | 102 | let full_alloc_size = size + USIZE_SIZE; 103 | 104 | unsafe { 105 | let layout = Layout::from_size_align_unchecked(full_alloc_size, USIZE_ALIGN); 106 | 107 | let ptr = if ZEROED { 108 | alloc_zeroed(layout) 109 | } else { 110 | alloc(layout) 111 | }; 112 | 113 | // SAFETY: ptr is usize-aligned and we've allocated sufficient memory 114 | ptr.cast::().write(full_alloc_size); 115 | 116 | ptr.add(USIZE_SIZE).cast() 117 | } 118 | } 119 | 120 | unsafe fn wasm_shim_free(ptr: *mut c_void) { 121 | // the layout for the allocation needs to be recovered for dealloc 122 | // - the size must be recovered from directly below the allocation 123 | // - the alignment will always by USIZE_ALIGN 124 | 125 | let alloc_ptr = ptr.sub(USIZE_SIZE); 126 | // SAFETY: the allocation routines must uphold having a valid usize below the provided pointer 127 | let full_alloc_size = alloc_ptr.cast::().read(); 128 | 129 | let layout = Layout::from_size_align_unchecked(full_alloc_size, USIZE_ALIGN); 130 | dealloc(alloc_ptr.cast(), layout); 131 | } 132 | -------------------------------------------------------------------------------- /liblzma-sys/wasm-shim/assert.h: -------------------------------------------------------------------------------- 1 | #ifndef _ASSERT_H 2 | #define _ASSERT_H 3 | 4 | #define assert(expr) 5 | 6 | #endif // _ASSERT_H 7 | -------------------------------------------------------------------------------- /liblzma-sys/wasm-shim/stdlib.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifndef _STDLIB_H 4 | #define _STDLIB_H 1 5 | 6 | void *rust_lzma_wasm_shim_malloc(size_t size); 7 | void *rust_lzma_wasm_shim_calloc(size_t nmemb, size_t size); 8 | void rust_lzma_wasm_shim_free(void *ptr); 9 | 10 | #define malloc(size) rust_lzma_wasm_shim_malloc(size) 11 | 12 | #define calloc(nmemb, size) rust_lzma_wasm_shim_calloc(nmemb, size); 13 | 14 | // Hack: Avoid replacing `allocator->free` to `allocator->rust_lzma_wasm_shim_free` in 15 | // Link: liblzma-sys/xz/src/liblzma/common/common.c:79 16 | // lzma_free(void *ptr, const lzma_allocator *allocator) 17 | // { 18 | // if (allocator != NULL && allocator->free != NULL) 19 | // allocator->free(allocator->opaque, ptr); // 20 | // else 21 | // free(ptr); 22 | // } 23 | #define free(...) _FREE_DISPATCH(__VA_ARGS__, free2, free1)(__VA_ARGS__) 24 | 25 | #define _FREE_DISPATCH(_1, _2, NAME, ...) NAME 26 | 27 | // for free 28 | #define free1(ptr) rust_lzma_wasm_shim_free(ptr) 29 | // for allocator->free 30 | #define free2(info, ptr) free(info, ptr) 31 | 32 | #endif // _STDLIB_H 33 | -------------------------------------------------------------------------------- /liblzma-sys/wasm-shim/string.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifndef _STRING_H 4 | #define _STRING_H 1 5 | 6 | int rust_lzma_wasm_shim_memcmp(const void *str1, const void *str2, size_t n); 7 | void *rust_lzma_wasm_shim_memcpy(void *restrict dest, const void *restrict src, size_t n); 8 | void *rust_lzma_wasm_shim_memmove(void *dest, const void *src, size_t n); 9 | void *rust_lzma_wasm_shim_memset(void *dest, int c, size_t n); 10 | size_t rust_lzma_wasm_shim_strlen(const char *s); 11 | void *rust_lzma_wasm_shim_memchr(const void *s, int c, size_t n); 12 | 13 | inline int memcmp(const void *str1, const void *str2, size_t n) { 14 | return rust_lzma_wasm_shim_memcmp(str1, str2, n); 15 | } 16 | 17 | inline void *memcpy(void *restrict dest, const void *restrict src, size_t n) { 18 | return rust_lzma_wasm_shim_memcpy(dest, src, n); 19 | } 20 | 21 | inline void *memmove(void *dest, const void *src, size_t n) { 22 | return rust_lzma_wasm_shim_memmove(dest, src, n); 23 | } 24 | 25 | inline void *memset(void *dest, int c, size_t n) { 26 | return rust_lzma_wasm_shim_memset(dest, c, n); 27 | } 28 | 29 | inline void *memchr(const void *s, int c, size_t n) { 30 | return rust_lzma_wasm_shim_memchr(s, c, n); 31 | } 32 | 33 | inline size_t strlen(const char *s) { 34 | return rust_lzma_wasm_shim_strlen(s); 35 | } 36 | 37 | #endif // _STRING_H 38 | -------------------------------------------------------------------------------- /src/bufread.rs: -------------------------------------------------------------------------------- 1 | //! I/O streams for wrapping `BufRead` types as encoders/decoders 2 | 3 | use std::io; 4 | use std::io::prelude::*; 5 | 6 | #[cfg(feature = "parallel")] 7 | use crate::stream::MtStreamBuilder; 8 | use crate::stream::{Action, Check, Status, Stream}; 9 | 10 | /// A xz encoder, or compressor. 11 | /// 12 | /// This structure implements a `BufRead` interface and will read uncompressed 13 | /// data from an underlying stream and emit a stream of compressed data. 14 | pub struct XzEncoder { 15 | obj: R, 16 | data: Stream, 17 | } 18 | 19 | /// A xz decoder, or decompressor. 20 | /// 21 | /// This structure implements a `BufRead` interface and takes a stream of 22 | /// compressed data as input, providing the decompressed data when read from. 23 | pub struct XzDecoder { 24 | obj: R, 25 | data: Stream, 26 | } 27 | 28 | impl XzEncoder { 29 | /// Creates a new encoder which will read uncompressed data from the given 30 | /// stream and emit the compressed stream. 31 | /// 32 | /// The `level` argument here is typically 0-9 with 6 being a good default. 33 | #[inline] 34 | pub fn new(r: R, level: u32) -> XzEncoder { 35 | let stream = Stream::new_easy_encoder(level, Check::Crc64).unwrap(); 36 | XzEncoder::new_stream(r, stream) 37 | } 38 | 39 | /// Creates a new parallel encoder which will read uncompressed data from the given 40 | /// stream and emit the compressed stream. 41 | /// 42 | /// The `level` argument here is typically 0-9 with 6 being a good default. 43 | #[cfg(feature = "parallel")] 44 | pub fn new_parallel(r: R, level: u32) -> XzEncoder { 45 | let stream = MtStreamBuilder::new() 46 | .preset(level) 47 | .check(Check::Crc64) 48 | .threads(num_cpus::get() as u32) 49 | .encoder() 50 | .unwrap(); 51 | Self::new_stream(r, stream) 52 | } 53 | 54 | /// Creates a new encoder with a custom `Stream`. 55 | /// 56 | /// The `Stream` can be pre-configured for multithreaded encoding, different 57 | /// compression options/tuning, etc. 58 | #[inline] 59 | pub fn new_stream(r: R, stream: Stream) -> XzEncoder { 60 | XzEncoder { 61 | obj: r, 62 | data: stream, 63 | } 64 | } 65 | } 66 | 67 | impl XzEncoder { 68 | /// Acquires a reference to the underlying stream 69 | #[inline] 70 | pub fn get_ref(&self) -> &R { 71 | &self.obj 72 | } 73 | 74 | /// Acquires a mutable reference to the underlying stream 75 | /// 76 | /// Note that mutation of the stream may result in surprising results if 77 | /// this encoder is continued to be used. 78 | #[inline] 79 | pub fn get_mut(&mut self) -> &mut R { 80 | &mut self.obj 81 | } 82 | 83 | /// Consumes this encoder, returning the underlying reader. 84 | #[inline] 85 | pub fn into_inner(self) -> R { 86 | self.obj 87 | } 88 | 89 | /// Returns the number of bytes produced by the compressor 90 | /// (e.g., the number of bytes read from this stream) 91 | /// 92 | /// Note that, due to buffering, this only bears any relation to 93 | /// total_in() when the compressor chooses to flush its data 94 | /// (unfortunately, this won't happen in general at the end of the 95 | /// stream, because the compressor doesn't know if there's more data 96 | /// to come). At that point, `total_out() / total_in()` would be 97 | /// the compression ratio. 98 | #[inline] 99 | pub fn total_out(&self) -> u64 { 100 | self.data.total_out() 101 | } 102 | 103 | /// Returns the number of bytes consumed by the compressor 104 | /// (e.g., the number of bytes read from the underlying stream) 105 | #[inline] 106 | pub fn total_in(&self) -> u64 { 107 | self.data.total_in() 108 | } 109 | } 110 | 111 | impl Read for XzEncoder { 112 | #[inline] 113 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 114 | loop { 115 | let (read, consumed, eof, ret); 116 | { 117 | let input = self.obj.fill_buf()?; 118 | eof = input.is_empty(); 119 | let before_out = self.data.total_out(); 120 | let before_in = self.data.total_in(); 121 | let action = if eof { Action::Finish } else { Action::Run }; 122 | ret = self.data.process(input, buf, action); 123 | read = (self.data.total_out() - before_out) as usize; 124 | consumed = (self.data.total_in() - before_in) as usize; 125 | }; 126 | self.obj.consume(consumed); 127 | 128 | ret?; 129 | 130 | // If we haven't ready any data and we haven't hit EOF yet, then we 131 | // need to keep asking for more data because if we return that 0 132 | // bytes of data have been read then it will be interpreted as EOF. 133 | if read == 0 && !eof && !buf.is_empty() { 134 | continue; 135 | } 136 | return Ok(read); 137 | } 138 | } 139 | } 140 | 141 | impl Write for XzEncoder { 142 | #[inline] 143 | fn write(&mut self, buf: &[u8]) -> io::Result { 144 | self.get_mut().write(buf) 145 | } 146 | 147 | #[inline] 148 | fn flush(&mut self) -> io::Result<()> { 149 | self.get_mut().flush() 150 | } 151 | } 152 | 153 | impl XzDecoder { 154 | /// Creates a new decoder which will decompress data read from the given 155 | /// stream. 156 | #[inline] 157 | pub fn new(r: R) -> XzDecoder { 158 | let stream = Stream::new_stream_decoder(u64::MAX, 0).unwrap(); 159 | XzDecoder::new_stream(r, stream) 160 | } 161 | 162 | /// Creates a new parallel decoder which will decompress data read from the given 163 | /// stream. 164 | #[cfg(feature = "parallel")] 165 | pub fn new_parallel(r: R) -> Self { 166 | let stream = MtStreamBuilder::new() 167 | .memlimit_stop(u64::MAX) 168 | .threads(num_cpus::get() as u32) 169 | .decoder() 170 | .unwrap(); 171 | Self::new_stream(r, stream) 172 | } 173 | 174 | /// Creates a new decoder which will decompress data read from the given 175 | /// input. All the concatenated xz streams from input will be consumed. 176 | #[inline] 177 | pub fn new_multi_decoder(r: R) -> XzDecoder { 178 | let stream = Stream::new_auto_decoder(u64::MAX, liblzma_sys::LZMA_CONCATENATED).unwrap(); 179 | XzDecoder::new_stream(r, stream) 180 | } 181 | 182 | /// Creates a new decoder with a custom `Stream`. 183 | /// 184 | /// The `Stream` can be pre-configured for various checks, different 185 | /// decompression options/tuning, etc. 186 | #[inline] 187 | pub fn new_stream(r: R, stream: Stream) -> XzDecoder { 188 | XzDecoder { 189 | obj: r, 190 | data: stream, 191 | } 192 | } 193 | } 194 | 195 | impl XzDecoder { 196 | /// Acquires a reference to the underlying stream 197 | #[inline] 198 | pub fn get_ref(&self) -> &R { 199 | &self.obj 200 | } 201 | 202 | /// Acquires a mutable reference to the underlying stream 203 | /// 204 | /// Note that mutation of the stream may result in surprising results if 205 | /// this encoder is continued to be used. 206 | #[inline] 207 | pub fn get_mut(&mut self) -> &mut R { 208 | &mut self.obj 209 | } 210 | 211 | /// Consumes this decoder, returning the underlying reader. 212 | #[inline] 213 | pub fn into_inner(self) -> R { 214 | self.obj 215 | } 216 | 217 | /// Returns the number of bytes that the decompressor has consumed. 218 | /// 219 | /// Note that this will likely be smaller than what the decompressor 220 | /// actually read from the underlying stream due to buffering. 221 | #[inline] 222 | pub fn total_in(&self) -> u64 { 223 | self.data.total_in() 224 | } 225 | 226 | /// Returns the number of bytes that the decompressor has produced. 227 | #[inline] 228 | pub fn total_out(&self) -> u64 { 229 | self.data.total_out() 230 | } 231 | } 232 | 233 | impl Read for XzDecoder { 234 | #[inline] 235 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 236 | loop { 237 | let (read, consumed, eof, ret); 238 | { 239 | let input = self.obj.fill_buf()?; 240 | eof = input.is_empty(); 241 | let before_out = self.data.total_out(); 242 | let before_in = self.data.total_in(); 243 | ret = self 244 | .data 245 | .process(input, buf, if eof { Action::Finish } else { Action::Run }); 246 | read = (self.data.total_out() - before_out) as usize; 247 | consumed = (self.data.total_in() - before_in) as usize; 248 | } 249 | self.obj.consume(consumed); 250 | 251 | let status = ret?; 252 | if read > 0 || eof || buf.is_empty() { 253 | if read == 0 && status != Status::StreamEnd && !buf.is_empty() { 254 | return Err(io::Error::new( 255 | io::ErrorKind::UnexpectedEof, 256 | "premature eof", 257 | )); 258 | } 259 | return Ok(read); 260 | } 261 | if consumed == 0 { 262 | return Err(io::Error::new( 263 | io::ErrorKind::InvalidData, 264 | "corrupt xz stream", 265 | )); 266 | } 267 | } 268 | } 269 | } 270 | 271 | impl Write for XzDecoder { 272 | #[inline] 273 | fn write(&mut self, buf: &[u8]) -> io::Result { 274 | self.get_mut().write(buf) 275 | } 276 | 277 | #[inline] 278 | fn flush(&mut self) -> io::Result<()> { 279 | self.get_mut().flush() 280 | } 281 | } 282 | 283 | #[cfg(test)] 284 | mod tests { 285 | use super::*; 286 | #[cfg(all(target_family = "wasm", target_os = "unknown"))] 287 | use wasm_bindgen_test::wasm_bindgen_test as test; 288 | 289 | #[test] 290 | fn compressed_and_trailing_data() { 291 | // Make a vector with compressed data... 292 | let mut to_compress: Vec = Vec::new(); 293 | const COMPRESSED_ORIG_SIZE: usize = 1024; 294 | for num in 0..COMPRESSED_ORIG_SIZE { 295 | to_compress.push(num as u8) 296 | } 297 | let mut encoder = XzEncoder::new(&to_compress[..], 6); 298 | 299 | let mut decoder_input = Vec::new(); 300 | encoder.read_to_end(&mut decoder_input).unwrap(); 301 | 302 | assert_eq!(encoder.total_in(), to_compress.len() as u64); 303 | assert_eq!(encoder.total_out(), decoder_input.len() as u64); 304 | 305 | // ...plus additional unrelated trailing data 306 | const ADDITIONAL_SIZE: usize = 123; 307 | let mut additional_data = Vec::new(); 308 | for num in 0..ADDITIONAL_SIZE { 309 | additional_data.push(((25 + num) % 256) as u8) 310 | } 311 | decoder_input.extend(&additional_data); 312 | 313 | // Decoder must be able to read the compressed xz stream, and keep the trailing data. 314 | let mut decoder_reader = &decoder_input[..]; 315 | { 316 | let mut decoder = XzDecoder::new(&mut decoder_reader); 317 | let mut decompressed_data = vec![0u8; to_compress.len()]; 318 | 319 | assert_eq!( 320 | decoder.read(&mut decompressed_data).unwrap(), 321 | COMPRESSED_ORIG_SIZE 322 | ); 323 | assert_eq!(decompressed_data, &to_compress[..]); 324 | assert_eq!( 325 | decoder.total_in(), 326 | (decoder_input.len() - ADDITIONAL_SIZE) as u64 327 | ); 328 | assert_eq!(decoder.total_out(), decompressed_data.len() as u64); 329 | } 330 | 331 | let mut remaining_data = Vec::new(); 332 | let nb_read = decoder_reader.read_to_end(&mut remaining_data).unwrap(); 333 | assert_eq!(nb_read, ADDITIONAL_SIZE); 334 | assert_eq!(remaining_data, &additional_data[..]); 335 | } 336 | } 337 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! LZMA/XZ encoding and decoding streams 2 | //! 3 | //! This library is a binding to liblzma currently to provide LZMA and xz 4 | //! encoding/decoding streams. I/O streams are provided in the `read`, `write`, 5 | //! and `bufread` modules (same types, different bounds). Raw in-memory 6 | //! compression/decompression is provided via the `stream` module and contains 7 | //! many of the raw APIs in liblzma. 8 | //! 9 | //! # Examples 10 | //! 11 | //! ``` 12 | //! use liblzma::read::{XzDecoder, XzEncoder}; 13 | //! use std::io::prelude::*; 14 | //! 15 | //! // Round trip some bytes from a byte source, into a compressor, into a 16 | //! // decompressor, and finally into a vector. 17 | //! let data = "Hello, World!".as_bytes(); 18 | //! let compressor = XzEncoder::new(data, 9); 19 | //! let mut decompressor = XzDecoder::new(compressor); 20 | //! 21 | //! let mut contents = String::new(); 22 | //! decompressor.read_to_string(&mut contents).unwrap(); 23 | //! assert_eq!(contents, "Hello, World!"); 24 | //! ``` 25 | //! # Static linking 26 | //! 27 | //! You can enable static-linking using the `static` feature, so that the XZ 28 | //! library is not required at runtime: 29 | //! 30 | //! ```toml 31 | //! liblzma = { version = "0.4", features = ["static"] } 32 | //! ``` 33 | //! 34 | //! # Multithreading 35 | //! 36 | //! This crate optionally can support multithreading using the `parallel` 37 | //! feature of this crate: 38 | //! 39 | //! ```toml 40 | //! liblzma = { version = "0.4", features = ["parallel"] } 41 | //! ``` 42 | //! 43 | //! # Async I/O 44 | //! 45 | //! Dropped `tokio` support since 0.4.0. 46 | //! If you need to use async I/O, use `async-compression` crate with `lzma` feature flag. 47 | //! 48 | //! ```toml 49 | //! async-compression = { version = "0.4", features = ["lzma"] } 50 | //! ``` 51 | 52 | #![doc(html_root_url = "https://docs.rs/liblzma/0.4.1")] 53 | #![deny(missing_docs)] 54 | 55 | use std::io::{self, prelude::*}; 56 | 57 | pub mod stream; 58 | 59 | pub mod bufread; 60 | pub mod read; 61 | pub mod write; 62 | 63 | /// Decompress from the given source as if using a [read::XzDecoder]. 64 | /// 65 | /// Result will be in the xz format. 66 | pub fn decode_all(source: R) -> io::Result> { 67 | let mut vec = Vec::new(); 68 | let mut r = read::XzDecoder::new(source); 69 | r.read_to_end(&mut vec)?; 70 | Ok(vec) 71 | } 72 | 73 | /// Compress from the given source as if using a [read::XzEncoder]. 74 | /// 75 | /// The input data must be in the xz format. 76 | pub fn encode_all(source: R, level: u32) -> io::Result> { 77 | let mut vec = Vec::new(); 78 | let mut r = read::XzEncoder::new(source, level); 79 | r.read_to_end(&mut vec)?; 80 | Ok(vec) 81 | } 82 | 83 | /// Compress all data from the given source as if using a [read::XzEncoder]. 84 | /// 85 | /// Compressed data will be appended to `destination`. 86 | pub fn copy_encode(source: R, mut destination: W, level: u32) -> io::Result<()> { 87 | io::copy(&mut read::XzEncoder::new(source, level), &mut destination)?; 88 | Ok(()) 89 | } 90 | 91 | /// Decompress all data from the given source as if using a [read::XzDecoder]. 92 | /// 93 | /// Decompressed data will be appended to `destination`. 94 | pub fn copy_decode(source: R, mut destination: W) -> io::Result<()> { 95 | io::copy(&mut read::XzDecoder::new(source), &mut destination)?; 96 | Ok(()) 97 | } 98 | 99 | /// Find the size in bytes of uncompressed data from xz file. 100 | #[cfg(feature = "bindgen")] 101 | pub fn uncompressed_size(mut source: R) -> io::Result { 102 | use std::mem::MaybeUninit; 103 | let mut footer = [0u8; liblzma_sys::LZMA_STREAM_HEADER_SIZE as usize]; 104 | 105 | source.seek(io::SeekFrom::End( 106 | 0 - (liblzma_sys::LZMA_STREAM_HEADER_SIZE as i64), 107 | ))?; 108 | source.read_exact(&mut footer)?; 109 | 110 | let lzma_stream_flags = unsafe { 111 | let mut lzma_stream_flags = MaybeUninit::uninit(); 112 | let ret = 113 | liblzma_sys::lzma_stream_footer_decode(lzma_stream_flags.as_mut_ptr(), footer.as_ptr()); 114 | 115 | if ret != liblzma_sys::LZMA_OK { 116 | return Err(io::Error::new( 117 | io::ErrorKind::Other, 118 | "Failed to parse lzma footer", 119 | )); 120 | } 121 | 122 | lzma_stream_flags.assume_init() 123 | }; 124 | 125 | let index_plus_footer = 126 | liblzma_sys::LZMA_STREAM_HEADER_SIZE as usize + lzma_stream_flags.backward_size as usize; 127 | 128 | source.seek(io::SeekFrom::End(0 - index_plus_footer as i64))?; 129 | 130 | let buf = source 131 | .bytes() 132 | .take(index_plus_footer) 133 | .collect::>>()?; 134 | 135 | let uncompressed_size = unsafe { 136 | let mut i: MaybeUninit<*mut liblzma_sys::lzma_index> = MaybeUninit::uninit(); 137 | let mut memlimit = u64::MAX; 138 | let mut in_pos = 0usize; 139 | 140 | let ret = liblzma_sys::lzma_index_buffer_decode( 141 | i.as_mut_ptr(), 142 | &mut memlimit, 143 | std::ptr::null(), 144 | buf.as_ptr(), 145 | &mut in_pos, 146 | buf.len(), 147 | ); 148 | 149 | if ret != liblzma_sys::LZMA_OK { 150 | return Err(io::Error::new( 151 | io::ErrorKind::Other, 152 | "Failed to parse lzma footer", 153 | )); 154 | } 155 | 156 | let i = i.assume_init(); 157 | 158 | let uncompressed_size = liblzma_sys::lzma_index_uncompressed_size(i); 159 | 160 | liblzma_sys::lzma_index_end(i, std::ptr::null()); 161 | 162 | uncompressed_size 163 | }; 164 | 165 | Ok(uncompressed_size) 166 | } 167 | 168 | #[cfg(test)] 169 | mod tests { 170 | use super::*; 171 | use quickcheck::quickcheck; 172 | #[cfg(all(target_family = "wasm", target_os = "unknown"))] 173 | use wasm_bindgen_test::wasm_bindgen_test as test; 174 | 175 | #[test] 176 | fn all() { 177 | quickcheck(test as fn(_) -> _); 178 | 179 | fn test(v: Vec) -> bool { 180 | let e = encode_all(&v[..], 6).unwrap(); 181 | let d = decode_all(&e[..]).unwrap(); 182 | v == d 183 | } 184 | } 185 | 186 | #[test] 187 | fn copy() { 188 | quickcheck(test as fn(_) -> _); 189 | 190 | fn test(v: Vec) -> bool { 191 | let mut e = Vec::new(); 192 | copy_encode(&v[..], &mut e, 6).unwrap(); 193 | let mut d = Vec::new(); 194 | copy_decode(&e[..], &mut d).unwrap(); 195 | v == d 196 | } 197 | } 198 | 199 | #[test] 200 | #[cfg(feature = "bindgen")] 201 | fn size() { 202 | quickcheck(test as fn(_) -> _); 203 | 204 | fn test(v: Vec) -> bool { 205 | let mut e = Vec::new(); 206 | copy_encode(&v[..], &mut e, 6).unwrap(); 207 | 208 | let s = super::uncompressed_size(std::io::Cursor::new(e)).unwrap(); 209 | 210 | (s as usize) == v.len() 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /src/read.rs: -------------------------------------------------------------------------------- 1 | //! Reader-based compression/decompression streams 2 | 3 | use std::io::prelude::*; 4 | use std::io::{self, BufReader}; 5 | 6 | use crate::bufread; 7 | use crate::stream::Stream; 8 | 9 | /// A compression stream which wraps an uncompressed stream of data. Compressed 10 | /// data will be read from the stream. 11 | pub struct XzEncoder { 12 | inner: bufread::XzEncoder>, 13 | } 14 | 15 | /// A decompression stream which wraps a compressed stream of data. Decompressed 16 | /// data will be read from the stream. 17 | pub struct XzDecoder { 18 | inner: bufread::XzDecoder>, 19 | } 20 | 21 | impl XzEncoder { 22 | /// Create a new compression stream which will compress at the given level 23 | /// to read compress output to the give output stream. 24 | /// 25 | /// The `level` argument here is typically 0-9 with 6 being a good default. 26 | #[inline] 27 | pub fn new(r: R, level: u32) -> XzEncoder { 28 | XzEncoder { 29 | inner: bufread::XzEncoder::new(BufReader::new(r), level), 30 | } 31 | } 32 | 33 | /// Create a new parallel compression stream which will compress at the given level 34 | /// to read compress output to the give output stream. 35 | /// 36 | /// The `level` argument here is typically 0-9 with 6 being a good default. 37 | #[cfg(feature = "parallel")] 38 | pub fn new_parallel(r: R, level: u32) -> XzEncoder { 39 | XzEncoder { 40 | inner: bufread::XzEncoder::new_parallel(BufReader::new(r), level), 41 | } 42 | } 43 | 44 | /// Creates a new encoder with a custom `Stream`. 45 | /// 46 | /// The `Stream` can be pre-configured for multithreaded encoding, different 47 | /// compression options/tuning, etc. 48 | #[inline] 49 | pub fn new_stream(r: R, stream: Stream) -> XzEncoder { 50 | XzEncoder { 51 | inner: bufread::XzEncoder::new_stream(BufReader::new(r), stream), 52 | } 53 | } 54 | 55 | /// Acquires a reference to the underlying stream 56 | #[inline] 57 | pub fn get_ref(&self) -> &R { 58 | self.inner.get_ref().get_ref() 59 | } 60 | 61 | /// Acquires a mutable reference to the underlying stream 62 | /// 63 | /// Note that mutation of the stream may result in surprising results if 64 | /// this encoder is continued to be used. 65 | #[inline] 66 | pub fn get_mut(&mut self) -> &mut R { 67 | self.inner.get_mut().get_mut() 68 | } 69 | 70 | /// Unwrap the underlying writer, finishing the compression stream. 71 | #[inline] 72 | pub fn into_inner(self) -> R { 73 | self.inner.into_inner().into_inner() 74 | } 75 | 76 | /// Returns the number of bytes produced by the compressor 77 | /// (e.g. the number of bytes read from this stream) 78 | /// 79 | /// Note that, due to buffering, this only bears any relation to 80 | /// total_in() when the compressor chooses to flush its data 81 | /// (unfortunately, this won't happen this won't happen in general 82 | /// at the end of the stream, because the compressor doesn't know 83 | /// if there's more data to come). At that point, 84 | /// `total_out() / total_in()` would be the compression ratio. 85 | #[inline] 86 | pub fn total_out(&self) -> u64 { 87 | self.inner.total_out() 88 | } 89 | 90 | /// Returns the number of bytes consumed by the compressor 91 | /// (e.g. the number of bytes read from the underlying stream) 92 | #[inline] 93 | pub fn total_in(&self) -> u64 { 94 | self.inner.total_in() 95 | } 96 | } 97 | 98 | impl Read for XzEncoder { 99 | #[inline] 100 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 101 | self.inner.read(buf) 102 | } 103 | } 104 | 105 | impl Write for XzEncoder { 106 | #[inline] 107 | fn write(&mut self, buf: &[u8]) -> io::Result { 108 | self.get_mut().write(buf) 109 | } 110 | 111 | #[inline] 112 | fn flush(&mut self) -> io::Result<()> { 113 | self.get_mut().flush() 114 | } 115 | } 116 | 117 | impl XzDecoder { 118 | /// Create a new decompression stream, which will read compressed 119 | /// data from the given input stream, and decompress one xz stream. 120 | /// It may also consume input data that follows the xz stream. 121 | /// Use [`xz::bufread::XzDecoder`] instead to process a mix of xz and non-xz data. 122 | #[inline] 123 | pub fn new(r: R) -> XzDecoder { 124 | XzDecoder { 125 | inner: bufread::XzDecoder::new(BufReader::new(r)), 126 | } 127 | } 128 | 129 | /// Create a new parallel decompression stream, which will read compressed 130 | /// data from the given input stream, and decompress one xz stream. 131 | /// It may also consume input data that follows the xz stream. 132 | /// Use [`xz::bufread::XzDecoder`] instead to process a mix of xz and non-xz data. 133 | #[cfg(feature = "parallel")] 134 | #[inline] 135 | pub fn new_parallel(r: R) -> XzDecoder { 136 | XzDecoder { 137 | inner: bufread::XzDecoder::new_parallel(BufReader::new(r)), 138 | } 139 | } 140 | 141 | /// Create a new decompression stream, which will read compressed 142 | /// data from the given input and decompress all the xz stream it contains. 143 | #[inline] 144 | pub fn new_multi_decoder(r: R) -> XzDecoder { 145 | XzDecoder { 146 | inner: bufread::XzDecoder::new_multi_decoder(BufReader::new(r)), 147 | } 148 | } 149 | 150 | /// Creates a new decoder with a custom `Stream`. 151 | /// 152 | /// The `Stream` can be pre-configured for various checks, different 153 | /// decompression options/tuning, etc. 154 | #[inline] 155 | pub fn new_stream(r: R, stream: Stream) -> XzDecoder { 156 | XzDecoder { 157 | inner: bufread::XzDecoder::new_stream(BufReader::new(r), stream), 158 | } 159 | } 160 | 161 | /// Acquires a reference to the underlying stream 162 | #[inline] 163 | pub fn get_ref(&self) -> &R { 164 | self.inner.get_ref().get_ref() 165 | } 166 | 167 | /// Acquires a mutable reference to the underlying stream 168 | /// 169 | /// Note that mutation of the stream may result in surprising results if 170 | /// this encoder is continued to be used. 171 | #[inline] 172 | pub fn get_mut(&mut self) -> &mut R { 173 | self.inner.get_mut().get_mut() 174 | } 175 | 176 | /// Unwrap the underlying writer, finishing the compression stream. 177 | #[inline] 178 | pub fn into_inner(self) -> R { 179 | self.inner.into_inner().into_inner() 180 | } 181 | 182 | /// Returns the number of bytes produced by the decompressor 183 | /// (e.g. the number of bytes read from this stream) 184 | /// 185 | /// Note that, due to buffering, this only bears any relation to 186 | /// total_in() when the decompressor reaches a sync point 187 | /// (e.g. where the original compressed stream was flushed). 188 | /// At that point, `total_in() / total_out()` is the compression ratio. 189 | #[inline] 190 | pub fn total_out(&self) -> u64 { 191 | self.inner.total_out() 192 | } 193 | 194 | /// Returns the number of bytes consumed by the decompressor 195 | /// (e.g. the number of bytes read from the underlying stream) 196 | #[inline] 197 | pub fn total_in(&self) -> u64 { 198 | self.inner.total_in() 199 | } 200 | } 201 | 202 | impl Read for XzDecoder { 203 | #[inline] 204 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 205 | self.inner.read(buf) 206 | } 207 | } 208 | 209 | impl Write for XzDecoder { 210 | #[inline] 211 | fn write(&mut self, buf: &[u8]) -> io::Result { 212 | self.get_mut().write(buf) 213 | } 214 | 215 | #[inline] 216 | fn flush(&mut self) -> io::Result<()> { 217 | self.get_mut().flush() 218 | } 219 | } 220 | 221 | #[cfg(test)] 222 | mod tests { 223 | use super::*; 224 | use crate::stream::LzmaOptions; 225 | use quickcheck::quickcheck; 226 | use rand::{thread_rng, Rng}; 227 | use std::iter; 228 | #[cfg(all(target_family = "wasm", target_os = "unknown"))] 229 | use wasm_bindgen_test::wasm_bindgen_test as test; 230 | 231 | #[test] 232 | fn smoke() { 233 | let m: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8]; 234 | let mut c = XzEncoder::new(m, 6); 235 | let mut data = vec![]; 236 | c.read_to_end(&mut data).unwrap(); 237 | let mut d = XzDecoder::new(&data[..]); 238 | let mut data2 = Vec::new(); 239 | d.read_to_end(&mut data2).unwrap(); 240 | assert_eq!(data2, m); 241 | } 242 | 243 | #[test] 244 | fn smoke2() { 245 | let m: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8]; 246 | let c = XzEncoder::new(m, 6); 247 | let mut d = XzDecoder::new(c); 248 | let mut data = vec![]; 249 | d.read_to_end(&mut data).unwrap(); 250 | assert_eq!(data, [1, 2, 3, 4, 5, 6, 7, 8]); 251 | } 252 | 253 | #[test] 254 | fn smoke3() { 255 | let m = vec![3u8; 128 * 1024 + 1]; 256 | let c = XzEncoder::new(&m[..], 6); 257 | let mut d = XzDecoder::new(c); 258 | let mut data = vec![]; 259 | d.read_to_end(&mut data).unwrap(); 260 | assert_eq!(data, &m[..]); 261 | } 262 | 263 | #[test] 264 | fn self_terminating() { 265 | let m = vec![3u8; 128 * 1024 + 1]; 266 | let mut c = XzEncoder::new(&m[..], 6); 267 | 268 | let mut result = Vec::new(); 269 | c.read_to_end(&mut result).unwrap(); 270 | 271 | let mut rng = thread_rng(); 272 | let v = iter::repeat_with(|| rng.gen::()) 273 | .take(1024) 274 | .collect::>(); 275 | for _ in 0..200 { 276 | result.extend(v.iter().map(|x| *x)); 277 | } 278 | 279 | let mut d = XzDecoder::new(&result[..]); 280 | let mut data = Vec::with_capacity(m.len()); 281 | unsafe { 282 | data.set_len(m.len()); 283 | } 284 | assert_eq!(d.read(&mut data).unwrap(), m.len()); 285 | assert_eq!(data, &m[..]); 286 | } 287 | 288 | #[test] 289 | fn zero_length_read_at_eof() { 290 | let m = Vec::new(); 291 | let mut c = XzEncoder::new(&m[..], 6); 292 | 293 | let mut result = Vec::new(); 294 | c.read_to_end(&mut result).unwrap(); 295 | 296 | let mut d = XzDecoder::new(&result[..]); 297 | let mut data = Vec::new(); 298 | assert_eq!(d.read(&mut data).unwrap(), 0); 299 | } 300 | 301 | #[test] 302 | fn zero_length_read_with_data() { 303 | let m = vec![3u8; 128 * 1024 + 1]; 304 | let mut c = XzEncoder::new(&m[..], 6); 305 | 306 | let mut result = Vec::new(); 307 | c.read_to_end(&mut result).unwrap(); 308 | 309 | let mut d = XzDecoder::new(&result[..]); 310 | let mut data = Vec::new(); 311 | assert_eq!(d.read(&mut data).unwrap(), 0); 312 | } 313 | 314 | #[test] 315 | fn qc_lzma1() { 316 | quickcheck(test as fn(_) -> _); 317 | fn test(v: Vec) -> bool { 318 | let options = LzmaOptions::new_preset(6).unwrap(); 319 | let stream = Stream::new_lzma_encoder(&options).unwrap(); 320 | let r = XzEncoder::new_stream(&v[..], stream); 321 | let stream = Stream::new_lzma_decoder(u64::MAX).unwrap(); 322 | let mut r = XzDecoder::new_stream(r, stream); 323 | let mut v2 = Vec::new(); 324 | r.read_to_end(&mut v2).unwrap(); 325 | v == v2 326 | } 327 | } 328 | 329 | #[test] 330 | fn qc() { 331 | quickcheck(test as fn(_) -> _); 332 | 333 | fn test(v: Vec) -> bool { 334 | let r = XzEncoder::new(&v[..], 6); 335 | let mut r = XzDecoder::new(r); 336 | let mut v2 = Vec::new(); 337 | r.read_to_end(&mut v2).unwrap(); 338 | v == v2 339 | } 340 | } 341 | 342 | #[cfg(feature = "parallel")] 343 | #[test] 344 | fn qc_parallel_encode() { 345 | quickcheck(test as fn(_) -> _); 346 | 347 | fn test(v: Vec) -> bool { 348 | let r = XzEncoder::new_parallel(&v[..], 6); 349 | let mut r = XzDecoder::new(r); 350 | let mut v2 = Vec::new(); 351 | r.read_to_end(&mut v2).unwrap(); 352 | v == v2 353 | } 354 | } 355 | 356 | #[cfg(feature = "parallel")] 357 | #[test] 358 | fn qc_parallel_decode() { 359 | quickcheck(test as fn(_) -> _); 360 | 361 | fn test(v: Vec) -> bool { 362 | let r = XzEncoder::new(&v[..], 6); 363 | let mut r = XzDecoder::new_parallel(r); 364 | let mut v2 = Vec::new(); 365 | r.read_to_end(&mut v2).unwrap(); 366 | v == v2 367 | } 368 | } 369 | 370 | #[test] 371 | fn two_streams() { 372 | let mut input_stream1: Vec = Vec::new(); 373 | let mut input_stream2: Vec = Vec::new(); 374 | let mut all_input: Vec = Vec::new(); 375 | 376 | // Generate input data. 377 | const STREAM1_SIZE: usize = 1024; 378 | for num in 0..STREAM1_SIZE { 379 | input_stream1.push(num as u8) 380 | } 381 | const STREAM2_SIZE: usize = 532; 382 | for num in 0..STREAM2_SIZE { 383 | input_stream2.push((num + 32) as u8) 384 | } 385 | all_input.extend(&input_stream1); 386 | all_input.extend(&input_stream2); 387 | 388 | // Make a vector with compressed data 389 | let mut decoder_input = Vec::new(); 390 | { 391 | let mut encoder = XzEncoder::new(&input_stream1[..], 6); 392 | encoder.read_to_end(&mut decoder_input).unwrap(); 393 | } 394 | { 395 | let mut encoder = XzEncoder::new(&input_stream2[..], 6); 396 | encoder.read_to_end(&mut decoder_input).unwrap(); 397 | } 398 | 399 | // Decoder must be able to read the 2 concatenated xz streams and get the same data as input. 400 | let mut decoder_reader = &decoder_input[..]; 401 | { 402 | // using `XzDecoder::new` here would fail because only 1 xz stream would be processed. 403 | let mut decoder = XzDecoder::new_multi_decoder(&mut decoder_reader); 404 | let mut decompressed_data = vec![0u8; all_input.len()]; 405 | 406 | assert_eq!( 407 | decoder.read(&mut decompressed_data).unwrap(), 408 | all_input.len() 409 | ); 410 | assert_eq!(decompressed_data, &all_input[..]); 411 | } 412 | } 413 | } 414 | -------------------------------------------------------------------------------- /src/stream.rs: -------------------------------------------------------------------------------- 1 | //! Raw in-memory LZMA streams. 2 | //! 3 | //! The `Stream` type exported by this module is the primary type which performs 4 | //! encoding/decoding of LZMA streams. Each `Stream` is either an encoder or 5 | //! decoder and processes data in a streaming fashion. 6 | 7 | use std::collections::LinkedList; 8 | use std::error; 9 | use std::fmt; 10 | use std::io; 11 | use std::mem; 12 | use std::slice; 13 | 14 | /// Representation of an in-memory LZMA encoding or decoding stream. 15 | /// 16 | /// Wraps the raw underlying `lzma_stream` type and provides the ability to 17 | /// create streams which can either decode or encode various LZMA-based formats. 18 | pub struct Stream { 19 | raw: liblzma_sys::lzma_stream, 20 | } 21 | 22 | unsafe impl Send for Stream {} 23 | unsafe impl Sync for Stream {} 24 | 25 | /// Options that can be used to configure how LZMA encoding happens. 26 | /// 27 | /// This builder is consumed by a number of other methods. 28 | pub struct LzmaOptions { 29 | raw: liblzma_sys::lzma_options_lzma, 30 | } 31 | 32 | /// Builder to create a multithreaded stream encoder. 33 | #[cfg(feature = "parallel")] 34 | pub struct MtStreamBuilder { 35 | raw: liblzma_sys::lzma_mt, 36 | filters: Option, 37 | } 38 | 39 | /// A custom chain of filters to configure an encoding stream. 40 | pub struct Filters { 41 | inner: Vec, 42 | lzma_opts: LinkedList, 43 | } 44 | 45 | /// The `action` argument for `process`, 46 | /// 47 | /// After the first use of SyncFlush, FullFlush, FullBarrier, or Finish, the 48 | /// same `action' must is used until `process` returns `Status::StreamEnd`. 49 | /// Also, the amount of input must not be modified by the application until 50 | /// `process` returns `Status::StreamEnd`. Changing the `action' or modifying 51 | /// the amount of input will make `process` return `Error::Program`. 52 | #[derive(Copy, Clone)] 53 | pub enum Action { 54 | /// Continue processing 55 | /// 56 | /// When encoding, encode as much input as possible. Some internal buffering 57 | /// will probably be done (depends on the filter chain in use), which causes 58 | /// latency: the input used won't usually be decodeable from the output of 59 | /// the same `process` call. 60 | /// 61 | /// When decoding, decode as much input as possible and produce as much 62 | /// output as possible. 63 | Run = liblzma_sys::LZMA_RUN as isize, 64 | 65 | /// Make all the input available at output 66 | /// 67 | /// Normally the encoder introduces some latency. `SyncFlush` forces all the 68 | /// buffered data to be available at output without resetting the internal 69 | /// state of the encoder. This way it is possible to use compressed stream 70 | /// for example for communication over network. 71 | /// 72 | /// Only some filters support `SyncFlush`. Trying to use `SyncFlush` with 73 | /// filters that don't support it will make `process` return 74 | /// `Error::Options`. For example, LZMA1 doesn't support `SyncFlush` but 75 | /// LZMA2 does. 76 | /// 77 | /// Using `SyncFlush` very often can dramatically reduce the compression 78 | /// ratio. With some filters (for example, LZMA2), fine-tuning the 79 | /// compression options may help mitigate this problem significantly (for 80 | /// example, match finder with LZMA2). 81 | /// 82 | /// Decoders don't support `SyncFlush`. 83 | SyncFlush = liblzma_sys::LZMA_SYNC_FLUSH as isize, 84 | 85 | /// Finish encoding of the current block. 86 | /// 87 | /// All the input data going to the current block must have been given to 88 | /// the encoder. Call `process` with `FullFlush` until it returns 89 | /// `Status::StreamEnd`. Then continue normally with `Run` or finish the 90 | /// Stream with `Finish`. 91 | /// 92 | /// This action is currently supported only by stream encoder and easy 93 | /// encoder (which uses stream encoder). If there is no unfinished block, no 94 | /// empty block is created. 95 | FullFlush = liblzma_sys::LZMA_FULL_FLUSH as isize, 96 | 97 | /// Finish encoding of the current block. 98 | /// 99 | /// This is like `FullFlush` except that this doesn't necessarily wait until 100 | /// all the input has been made available via the output buffer. That is, 101 | /// `process` might return `Status::StreamEnd` as soon as all the input has 102 | /// been consumed. 103 | /// 104 | /// `FullBarrier` is useful with a threaded encoder if one wants to split 105 | /// the .xz Stream into blocks at specific offsets but doesn't care if the 106 | /// output isn't flushed immediately. Using `FullBarrier` allows keeping the 107 | /// threads busy while `FullFlush` would make `process` wait until all the 108 | /// threads have finished until more data could be passed to the encoder. 109 | /// 110 | /// With a `Stream` initialized with the single-threaded 111 | /// `new_stream_encoder` or `new_easy_encoder`, `FullBarrier` is an alias 112 | /// for `FullFlush`. 113 | FullBarrier = liblzma_sys::LZMA_FULL_BARRIER as isize, 114 | 115 | /// Finish the current operation 116 | /// 117 | /// All the input data must have been given to the encoder (the last bytes 118 | /// can still be pending in next_in). Call `process` with `Finish` until it 119 | /// returns `Status::StreamEnd`. Once `Finish` has been used, the amount of 120 | /// input must no longer be changed by the application. 121 | /// 122 | /// When decoding, using `Finish` is optional unless the concatenated flag 123 | /// was used when the decoder was initialized. When concatenated was not 124 | /// used, the only effect of `Finish` is that the amount of input must not 125 | /// be changed just like in the encoder. 126 | Finish = liblzma_sys::LZMA_FINISH as isize, 127 | } 128 | 129 | /// Return value of a `process` operation. 130 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] 131 | pub enum Status { 132 | /// Operation completed successfully. 133 | Ok, 134 | 135 | /// End of stream was reached. 136 | /// 137 | /// When encoding, this means that a sync/full flush or `Finish` was 138 | /// completed. When decoding, this indicates that all data was decoded 139 | /// successfully. 140 | StreamEnd, 141 | 142 | /// If the TELL_ANY_CHECK flags is specified when constructing a decoder, 143 | /// this informs that the `check` method will now return the underlying 144 | /// integrity check algorithm. 145 | GetCheck, 146 | 147 | /// An error has not been encountered, but no progress is possible. 148 | /// 149 | /// Processing can be continued normally by providing more input and/or more 150 | /// output space, if possible. 151 | /// 152 | /// Typically the first call to `process` that can do no progress returns 153 | /// `Ok` instead of `MemNeeded`. Only the second consecutive call doing no 154 | /// progress will return `MemNeeded`. 155 | MemNeeded, 156 | } 157 | 158 | /// Possible error codes that can be returned from a processing operation. 159 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] 160 | pub enum Error { 161 | /// The underlying data was corrupt. 162 | Data, 163 | 164 | /// Invalid or unsupported options were specified. 165 | Options, 166 | 167 | /// File format wasn't recognized. 168 | Format, 169 | 170 | /// Memory usage limit was reached. 171 | /// 172 | /// The memory limit can be increased with `set_memlimit` 173 | MemLimit, 174 | 175 | /// Memory couldn't be allocated. 176 | Mem, 177 | 178 | /// A programming error was encountered. 179 | Program, 180 | 181 | /// The `TELL_NO_CHECK` flag was specified and no integrity check was 182 | /// available for this stream. 183 | NoCheck, 184 | 185 | /// The `TELL_UNSUPPORTED_CHECK` flag was specified and no integrity check 186 | /// isn't implemented in this build of liblzma for this stream. 187 | UnsupportedCheck, 188 | } 189 | 190 | /// Possible integrity checks that can be part of a .xz stream. 191 | #[allow(missing_docs)] // self-explanatory mostly 192 | #[derive(Copy, Clone)] 193 | pub enum Check { 194 | None = liblzma_sys::LZMA_CHECK_NONE as isize, 195 | Crc32 = liblzma_sys::LZMA_CHECK_CRC32 as isize, 196 | Crc64 = liblzma_sys::LZMA_CHECK_CRC64 as isize, 197 | Sha256 = liblzma_sys::LZMA_CHECK_SHA256 as isize, 198 | } 199 | 200 | /// Compression modes 201 | /// 202 | /// This selects the function used to analyze the data produced by the match 203 | /// finder. 204 | #[derive(Copy, Clone)] 205 | pub enum Mode { 206 | /// Fast compression. 207 | /// 208 | /// Fast mode is usually at its best when combined with a hash chain match 209 | /// finder. 210 | Fast = liblzma_sys::LZMA_MODE_FAST as isize, 211 | 212 | /// Normal compression. 213 | /// 214 | /// This is usually notably slower than fast mode. Use this together with 215 | /// binary tree match finders to expose the full potential of the LZMA1 or 216 | /// LZMA2 encoder. 217 | Normal = liblzma_sys::LZMA_MODE_NORMAL as isize, 218 | } 219 | 220 | /// Match finders 221 | /// 222 | /// Match finder has major effect on both speed and compression ratio. Usually 223 | /// hash chains are faster than binary trees. 224 | /// 225 | /// If you will use `SyncFlush` often, the hash chains may be a better choice, 226 | /// because binary trees get much higher compression ratio penalty with 227 | /// `SyncFlush`. 228 | /// 229 | /// The memory usage formulas are only rough estimates, which are closest to 230 | /// reality when dict_size is a power of two. The formulas are more complex in 231 | /// reality, and can also change a little between liblzma versions. 232 | #[derive(Copy, Clone)] 233 | pub enum MatchFinder { 234 | /// Hash Chain with 2- and 3-byte hashing 235 | HashChain3 = liblzma_sys::LZMA_MF_HC3 as isize, 236 | /// Hash Chain with 2-, 3-, and 4-byte hashing 237 | HashChain4 = liblzma_sys::LZMA_MF_HC4 as isize, 238 | 239 | /// Binary Tree with 2-byte hashing 240 | BinaryTree2 = liblzma_sys::LZMA_MF_BT2 as isize, 241 | /// Binary Tree with 2- and 3-byte hashing 242 | BinaryTree3 = liblzma_sys::LZMA_MF_BT3 as isize, 243 | /// Binary Tree with 2-, 3-, and 4-byte hashing 244 | BinaryTree4 = liblzma_sys::LZMA_MF_BT4 as isize, 245 | } 246 | 247 | /// A flag passed when initializing a decoder, causes `process` to return 248 | /// `Status::GetCheck` as soon as the integrity check is known. 249 | pub const TELL_ANY_CHECK: u32 = liblzma_sys::LZMA_TELL_ANY_CHECK; 250 | 251 | /// A flag passed when initializing a decoder, causes `process` to return 252 | /// `Error::NoCheck` if the stream being decoded has no integrity check. 253 | pub const TELL_NO_CHECK: u32 = liblzma_sys::LZMA_TELL_NO_CHECK; 254 | 255 | /// A flag passed when initializing a decoder, causes `process` to return 256 | /// `Error::UnsupportedCheck` if the stream being decoded has an integrity check 257 | /// that cannot be verified by this build of liblzma. 258 | pub const TELL_UNSUPPORTED_CHECK: u32 = liblzma_sys::LZMA_TELL_UNSUPPORTED_CHECK; 259 | 260 | /// A flag passed when initializing a decoder, causes the decoder to ignore any 261 | /// integrity checks listed. 262 | pub const IGNORE_CHECK: u32 = liblzma_sys::LZMA_TELL_UNSUPPORTED_CHECK; 263 | 264 | /// A flag passed when initializing a decoder, indicates that the stream may be 265 | /// multiple concatenated xz files. 266 | pub const CONCATENATED: u32 = liblzma_sys::LZMA_CONCATENATED; 267 | 268 | impl Stream { 269 | #[inline] 270 | unsafe fn zeroed() -> Self { 271 | Self { 272 | raw: unsafe { mem::zeroed() }, 273 | } 274 | } 275 | 276 | /// Initialize .xz stream encoder using a preset number 277 | /// 278 | /// This is intended to be used by most for encoding data. The `preset` 279 | /// argument is a number 0-9 indicating the compression level to use, and 280 | /// normally 6 is a reasonable default. 281 | /// 282 | /// The `check` argument is the integrity check to insert at the end of the 283 | /// stream. The default of `Crc64` is typically appropriate. 284 | #[inline] 285 | pub fn new_easy_encoder(preset: u32, check: Check) -> Result { 286 | let mut init = unsafe { Stream::zeroed() }; 287 | cvt(unsafe { 288 | liblzma_sys::lzma_easy_encoder(&mut init.raw, preset, check as liblzma_sys::lzma_check) 289 | })?; 290 | Ok(init) 291 | } 292 | 293 | /// Initialize .lzma encoder (legacy file format) 294 | /// 295 | /// The .lzma format is sometimes called the LZMA_Alone format, which is the 296 | /// reason for the name of this function. The .lzma format supports only the 297 | /// LZMA1 filter. There is no support for integrity checks like CRC32. 298 | /// 299 | /// Use this function if and only if you need to create files readable by 300 | /// legacy LZMA tools such as LZMA Utils 4.32.x. Moving to the .xz format 301 | /// (the `new_easy_encoder` function) is strongly recommended. 302 | /// 303 | /// The valid action values for `process` are `Run` and `Finish`. No kind 304 | /// of flushing is supported, because the file format doesn't make it 305 | /// possible. 306 | #[inline] 307 | pub fn new_lzma_encoder(options: &LzmaOptions) -> Result { 308 | let mut init = unsafe { Stream::zeroed() }; 309 | cvt(unsafe { liblzma_sys::lzma_alone_encoder(&mut init.raw, &options.raw) })?; 310 | Ok(init) 311 | } 312 | 313 | /// Initialize .xz Stream encoder using a custom filter chain 314 | /// 315 | /// This function is similar to `new_easy_encoder` but a custom filter chain 316 | /// is specified. 317 | #[inline] 318 | pub fn new_stream_encoder(filters: &Filters, check: Check) -> Result { 319 | let mut init = unsafe { Stream::zeroed() }; 320 | cvt(unsafe { 321 | liblzma_sys::lzma_stream_encoder( 322 | &mut init.raw, 323 | filters.inner.as_ptr(), 324 | check as liblzma_sys::lzma_check, 325 | ) 326 | })?; 327 | Ok(init) 328 | } 329 | 330 | /// Initialize a .xz stream decoder. 331 | /// 332 | /// The maximum memory usage can be specified along with flags such as 333 | /// `TELL_ANY_CHECK`, `TELL_NO_CHECK`, `TELL_UNSUPPORTED_CHECK`, 334 | /// `TELL_IGNORE_CHECK`, or `CONCATENATED`. 335 | #[inline] 336 | pub fn new_stream_decoder(memlimit: u64, flags: u32) -> Result { 337 | let mut init = unsafe { Self::zeroed() }; 338 | cvt(unsafe { liblzma_sys::lzma_stream_decoder(&mut init.raw, memlimit, flags) })?; 339 | Ok(init) 340 | } 341 | 342 | /// Initialize a .lzma stream decoder. 343 | /// 344 | /// The maximum memory usage can also be specified. 345 | #[inline] 346 | pub fn new_lzma_decoder(memlimit: u64) -> Result { 347 | let mut init = unsafe { Self::zeroed() }; 348 | cvt(unsafe { liblzma_sys::lzma_alone_decoder(&mut init.raw, memlimit) })?; 349 | Ok(init) 350 | } 351 | 352 | /// Initialize a decoder which will choose a stream/lzma formats depending 353 | /// on the input stream. 354 | #[inline] 355 | pub fn new_auto_decoder(memlimit: u64, flags: u32) -> Result { 356 | let mut init = unsafe { Self::zeroed() }; 357 | cvt(unsafe { liblzma_sys::lzma_auto_decoder(&mut init.raw, memlimit, flags) })?; 358 | Ok(init) 359 | } 360 | 361 | /// Initialize a .lz stream decoder. 362 | #[inline] 363 | pub fn new_lzip_decoder(memlimit: u64, flags: u32) -> Result { 364 | let mut init = unsafe { Self::zeroed() }; 365 | cvt(unsafe { liblzma_sys::lzma_lzip_decoder(&mut init.raw, memlimit, flags) })?; 366 | Ok(init) 367 | } 368 | 369 | /// Initialize a decoder stream using a custom filter chain. 370 | #[inline] 371 | pub fn new_raw_decoder(filters: &Filters) -> Result { 372 | let mut init = unsafe { Self::zeroed() }; 373 | cvt(unsafe { liblzma_sys::lzma_raw_decoder(&mut init.raw, filters.inner.as_ptr()) })?; 374 | Ok(init) 375 | } 376 | 377 | /// Initialize an encoder stream using a custom filter chain. 378 | #[inline] 379 | pub fn new_raw_encoder(filters: &Filters) -> Result { 380 | let mut init = unsafe { Self::zeroed() }; 381 | cvt(unsafe { liblzma_sys::lzma_raw_encoder(&mut init.raw, filters.inner.as_ptr()) })?; 382 | Ok(init) 383 | } 384 | 385 | /// Processes some data from input into an output buffer. 386 | /// 387 | /// This will perform the appropriate encoding or decoding operation 388 | /// depending on the kind of underlying stream. Documentation for the 389 | /// various `action` arguments can be found on the respective variants. 390 | #[inline] 391 | pub fn process( 392 | &mut self, 393 | input: &[u8], 394 | output: &mut [u8], 395 | action: Action, 396 | ) -> Result { 397 | self.raw.next_in = input.as_ptr(); 398 | self.raw.avail_in = input.len(); 399 | self.raw.next_out = output.as_mut_ptr(); 400 | self.raw.avail_out = output.len(); 401 | let action = action as liblzma_sys::lzma_action; 402 | unsafe { cvt(liblzma_sys::lzma_code(&mut self.raw, action)) } 403 | } 404 | 405 | /// Performs the same data as `process`, but places output data in a `Vec`. 406 | /// 407 | /// This function will use the extra capacity of `output` as a destination 408 | /// for bytes to be placed. The length of `output` will automatically get 409 | /// updated after the operation has completed. 410 | #[inline] 411 | pub fn process_vec( 412 | &mut self, 413 | input: &[u8], 414 | output: &mut Vec, 415 | action: Action, 416 | ) -> Result { 417 | let cap = output.capacity(); 418 | let len = output.len(); 419 | 420 | unsafe { 421 | let before = self.total_out(); 422 | let ret = { 423 | let ptr = output.as_mut_ptr().add(len); 424 | let out = slice::from_raw_parts_mut(ptr, cap - len); 425 | self.process(input, out, action) 426 | }; 427 | output.set_len((self.total_out() - before) as usize + len); 428 | ret 429 | } 430 | } 431 | 432 | /// Returns the total amount of input bytes consumed by this stream. 433 | #[inline] 434 | pub fn total_in(&self) -> u64 { 435 | self.raw.total_in 436 | } 437 | 438 | /// Returns the total amount of bytes produced by this stream. 439 | #[inline] 440 | pub fn total_out(&self) -> u64 { 441 | self.raw.total_out 442 | } 443 | 444 | /// Get the current memory usage limit. 445 | /// 446 | /// This is only supported if the underlying stream supports a memlimit. 447 | #[inline] 448 | pub fn memlimit(&self) -> u64 { 449 | unsafe { liblzma_sys::lzma_memlimit_get(&self.raw) } 450 | } 451 | 452 | /// Set the current memory usage limit. 453 | /// 454 | /// This can return `Error::MemLimit` if the new limit is too small or 455 | /// `Error::Program` if this stream doesn't take a memory limit. 456 | #[inline] 457 | pub fn set_memlimit(&mut self, limit: u64) -> Result<(), Error> { 458 | cvt(unsafe { liblzma_sys::lzma_memlimit_set(&mut self.raw, limit) }).map(|_| ()) 459 | } 460 | } 461 | 462 | impl LzmaOptions { 463 | /// Creates a new blank set of options. 464 | #[inline] 465 | pub fn new() -> LzmaOptions { 466 | LzmaOptions { 467 | raw: unsafe { mem::zeroed() }, 468 | } 469 | } 470 | 471 | /// Creates a new blank set of options for encoding. 472 | /// 473 | /// The `preset` argument is the compression level to use, typically in the 474 | /// range of 0-9. 475 | #[inline] 476 | pub fn new_preset(preset: u32) -> Result { 477 | unsafe { 478 | let mut options = Self::new(); 479 | let ret = liblzma_sys::lzma_lzma_preset(&mut options.raw, preset); 480 | if ret != 0 { 481 | Err(Error::Program) 482 | } else { 483 | Ok(options) 484 | } 485 | } 486 | } 487 | 488 | /// Configures the dictionary size, in bytes 489 | /// 490 | /// Dictionary size indicates how many bytes of the recently processed 491 | /// uncompressed data is kept in memory. 492 | /// 493 | /// The minimum dictionary size is 4096 bytes and the default is 2^23, 8MB. 494 | #[inline] 495 | pub fn dict_size(&mut self, size: u32) -> &mut LzmaOptions { 496 | self.raw.dict_size = size; 497 | self 498 | } 499 | 500 | /// Configures the number of literal context bits. 501 | /// 502 | /// How many of the highest bits of the previous uncompressed eight-bit byte 503 | /// (also known as `literal') are taken into account when predicting the 504 | /// bits of the next literal. 505 | /// 506 | /// The maximum value to this is 4 and the default is 3. It is not currently 507 | /// supported if this plus `literal_position_bits` is greater than 4. 508 | #[inline] 509 | pub fn literal_context_bits(&mut self, bits: u32) -> &mut LzmaOptions { 510 | self.raw.lc = bits; 511 | self 512 | } 513 | 514 | /// Configures the number of literal position bits. 515 | /// 516 | /// This affects what kind of alignment in the uncompressed data is assumed 517 | /// when encoding literals. A literal is a single 8-bit byte. See 518 | /// `position_bits` for more information about alignment. 519 | /// 520 | /// The default for this is 0. 521 | #[inline] 522 | pub fn literal_position_bits(&mut self, bits: u32) -> &mut LzmaOptions { 523 | self.raw.lp = bits; 524 | self 525 | } 526 | 527 | /// Configures the number of position bits. 528 | /// 529 | /// Position bits affects what kind of alignment in the uncompressed data is 530 | /// assumed in general. The default of 2 means four-byte alignment (2^ pb 531 | /// =2^2=4), which is often a good choice when there's no better guess. 532 | /// 533 | /// When the alignment is known, setting pb accordingly may reduce the file 534 | /// size a little. E.g. with text files having one-byte alignment (US-ASCII, 535 | /// ISO-8859-*, UTF-8), setting pb=0 can improve compression slightly. For 536 | /// UTF-16 text, pb=1 is a good choice. If the alignment is an odd number 537 | /// like 3 bytes, pb=0 might be the best choice. 538 | /// 539 | /// Even though the assumed alignment can be adjusted with pb and lp, LZMA1 540 | /// and LZMA2 still slightly favor 16-byte alignment. It might be worth 541 | /// taking into account when designing file formats that are likely to be 542 | /// often compressed with LZMA1 or LZMA2. 543 | #[inline] 544 | pub fn position_bits(&mut self, bits: u32) -> &mut LzmaOptions { 545 | self.raw.pb = bits; 546 | self 547 | } 548 | 549 | /// Configures the compression mode. 550 | #[inline] 551 | pub fn mode(&mut self, mode: Mode) -> &mut LzmaOptions { 552 | self.raw.mode = mode as liblzma_sys::lzma_mode; 553 | self 554 | } 555 | 556 | /// Configures the nice length of a match. 557 | /// 558 | /// This determines how many bytes the encoder compares from the match 559 | /// candidates when looking for the best match. Once a match of at least 560 | /// `nice_len` bytes long is found, the encoder stops looking for better 561 | /// candidates and encodes the match. (Naturally, if the found match is 562 | /// actually longer than `nice_len`, the actual length is encoded; it's not 563 | /// truncated to `nice_len`.) 564 | /// 565 | /// Bigger values usually increase the compression ratio and compression 566 | /// time. For most files, 32 to 128 is a good value, which gives very good 567 | /// compression ratio at good speed. 568 | /// 569 | /// The exact minimum value depends on the match finder. The maximum is 273, 570 | /// which is the maximum length of a match that LZMA1 and LZMA2 can encode. 571 | #[inline] 572 | pub fn nice_len(&mut self, len: u32) -> &mut LzmaOptions { 573 | self.raw.nice_len = len; 574 | self 575 | } 576 | 577 | /// Configures the match finder ID. 578 | #[inline] 579 | pub fn match_finder(&mut self, mf: MatchFinder) -> &mut LzmaOptions { 580 | self.raw.mf = mf as liblzma_sys::lzma_match_finder; 581 | self 582 | } 583 | 584 | /// Maximum search depth in the match finder. 585 | /// 586 | /// For every input byte, match finder searches through the hash chain or 587 | /// binary tree in a loop, each iteration going one step deeper in the chain 588 | /// or tree. The searching stops if 589 | /// 590 | /// - a match of at least `nice_len` bytes long is found; 591 | /// - all match candidates from the hash chain or binary tree have 592 | /// been checked; or 593 | /// - maximum search depth is reached. 594 | /// 595 | /// Maximum search depth is needed to prevent the match finder from wasting 596 | /// too much time in case there are lots of short match candidates. On the 597 | /// other hand, stopping the search before all candidates have been checked 598 | /// can reduce compression ratio. 599 | /// 600 | /// Setting depth to zero tells liblzma to use an automatic default value, 601 | /// that depends on the selected match finder and nice_len. The default is 602 | /// in the range [4, 200] or so (it may vary between liblzma versions). 603 | /// 604 | /// Using a bigger depth value than the default can increase compression 605 | /// ratio in some cases. There is no strict maximum value, but high values 606 | /// (thousands or millions) should be used with care: the encoder could 607 | /// remain fast enough with typical input, but malicious input could cause 608 | /// the match finder to slow down dramatically, possibly creating a denial 609 | /// of service attack. 610 | #[inline] 611 | pub fn depth(&mut self, depth: u32) -> &mut LzmaOptions { 612 | self.raw.depth = depth; 613 | self 614 | } 615 | } 616 | 617 | impl Check { 618 | /// Test if this check is supported in this build of liblzma. 619 | #[inline] 620 | pub fn is_supported(&self) -> bool { 621 | let ret = unsafe { liblzma_sys::lzma_check_is_supported(*self as liblzma_sys::lzma_check) }; 622 | ret != 0 623 | } 624 | } 625 | 626 | impl MatchFinder { 627 | /// Test if this match finder is supported in this build of liblzma. 628 | #[inline] 629 | pub fn is_supported(&self) -> bool { 630 | let ret = 631 | unsafe { liblzma_sys::lzma_mf_is_supported(*self as liblzma_sys::lzma_match_finder) }; 632 | ret != 0 633 | } 634 | } 635 | 636 | impl Filters { 637 | /// Creates a new filter chain with no filters. 638 | #[inline] 639 | pub fn new() -> Filters { 640 | Filters { 641 | inner: vec![liblzma_sys::lzma_filter { 642 | id: liblzma_sys::LZMA_VLI_UNKNOWN, 643 | options: std::ptr::null_mut(), 644 | }], 645 | lzma_opts: LinkedList::new(), 646 | } 647 | } 648 | 649 | /// Add an LZMA1 filter. 650 | /// 651 | /// LZMA1 is the very same thing as what was called just LZMA in LZMA Utils, 652 | /// 7-Zip, and LZMA SDK. It's called LZMA1 here to prevent developers from 653 | /// accidentally using LZMA when they actually want LZMA2. 654 | /// 655 | /// LZMA1 shouldn't be used for new applications unless you _really_ know 656 | /// what you are doing. LZMA2 is almost always a better choice. 657 | #[inline] 658 | pub fn lzma1(&mut self, opts: &LzmaOptions) -> &mut Filters { 659 | self.lzma_opts.push_back(opts.raw); 660 | let ptr = self.lzma_opts.back().unwrap() as *const _ as *mut _; 661 | self.push(liblzma_sys::lzma_filter { 662 | id: liblzma_sys::LZMA_FILTER_LZMA1, 663 | options: ptr, 664 | }) 665 | } 666 | 667 | /// Add an LZMA1 filter with properties. 668 | #[inline] 669 | pub fn lzma1_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> { 670 | let filter = liblzma_sys::lzma_filter { 671 | id: liblzma_sys::LZMA_FILTER_LZMA1, 672 | options: std::ptr::null_mut(), 673 | }; 674 | self.push_with_properties(filter, properties) 675 | } 676 | 677 | /// Add an LZMA2 filter. 678 | /// 679 | /// Usually you want this instead of LZMA1. Compared to LZMA1, LZMA2 adds 680 | /// support for `SyncFlush`, uncompressed chunks (smaller expansion when 681 | /// trying to compress uncompressible data), possibility to change 682 | /// `literal_context_bits`/`literal_position_bits`/`position_bits` in the 683 | /// middle of encoding, and some other internal improvements. 684 | #[inline] 685 | pub fn lzma2(&mut self, opts: &LzmaOptions) -> &mut Filters { 686 | self.lzma_opts.push_back(opts.raw); 687 | let ptr = self.lzma_opts.back().unwrap() as *const _ as *mut _; 688 | self.push(liblzma_sys::lzma_filter { 689 | id: liblzma_sys::LZMA_FILTER_LZMA2, 690 | options: ptr, 691 | }) 692 | } 693 | 694 | /// Add an LZMA2 filter with properties. 695 | #[inline] 696 | pub fn lzma2_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> { 697 | let filter = liblzma_sys::lzma_filter { 698 | id: liblzma_sys::LZMA_FILTER_LZMA2, 699 | options: std::ptr::null_mut(), 700 | }; 701 | self.push_with_properties(filter, properties) 702 | } 703 | 704 | /// Add a DELTA filter. 705 | /// 706 | /// # Examples 707 | /// ``` 708 | /// use liblzma::stream::{Filters, LzmaOptions}; 709 | /// 710 | /// let dict_size = 0x40000; 711 | /// let mut opts = LzmaOptions::new_preset(6).unwrap(); 712 | /// opts.dict_size(dict_size); 713 | /// let mut filters = Filters::new(); 714 | /// filters.delta(); 715 | /// filters.lzma2(&opts); 716 | /// ``` 717 | #[inline] 718 | pub fn delta(&mut self) -> &mut Filters { 719 | self.push(liblzma_sys::lzma_filter { 720 | id: liblzma_sys::LZMA_FILTER_DELTA, 721 | options: std::ptr::null_mut(), 722 | }) 723 | } 724 | 725 | /// Add a DELTA filter with properties. 726 | /// 727 | /// # Examples 728 | /// ``` 729 | /// use liblzma::stream::{Filters, LzmaOptions}; 730 | /// 731 | /// let mut filters = Filters::new(); 732 | /// filters.delta_properties(&[0x00]).unwrap(); 733 | /// ``` 734 | #[inline] 735 | pub fn delta_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> { 736 | let filter = liblzma_sys::lzma_filter { 737 | id: liblzma_sys::LZMA_FILTER_DELTA, 738 | options: std::ptr::null_mut(), 739 | }; 740 | self.push_with_properties(filter, properties) 741 | } 742 | 743 | /// Add a filter for x86 binaries. 744 | /// 745 | /// # Examples 746 | /// ``` 747 | /// use liblzma::stream::{Filters, LzmaOptions}; 748 | /// 749 | /// let dict_size = 0x40000; 750 | /// let mut opts = LzmaOptions::new_preset(6).unwrap(); 751 | /// opts.dict_size(dict_size); 752 | /// let mut filters = Filters::new(); 753 | /// filters.x86(); 754 | /// filters.lzma2(&opts); 755 | /// ``` 756 | #[inline] 757 | pub fn x86(&mut self) -> &mut Filters { 758 | self.push(liblzma_sys::lzma_filter { 759 | id: liblzma_sys::LZMA_FILTER_X86, 760 | options: std::ptr::null_mut(), 761 | }) 762 | } 763 | 764 | /// Add a filter for x86 binaries with properties. 765 | /// 766 | /// # Examples 767 | /// ``` 768 | /// use liblzma::stream::{Filters, LzmaOptions}; 769 | /// 770 | /// let mut filters = Filters::new(); 771 | /// filters.x86_properties(&[0x00, 0x00, 0x00, 0x00]).unwrap(); 772 | /// ``` 773 | #[inline] 774 | pub fn x86_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> { 775 | let filter = liblzma_sys::lzma_filter { 776 | id: liblzma_sys::LZMA_FILTER_X86, 777 | options: std::ptr::null_mut(), 778 | }; 779 | self.push_with_properties(filter, properties) 780 | } 781 | 782 | /// Add a filter for PowerPC binaries. 783 | /// 784 | /// # Examples 785 | /// ``` 786 | /// use liblzma::stream::{Filters, LzmaOptions}; 787 | /// 788 | /// let dict_size = 0x40000; 789 | /// let mut opts = LzmaOptions::new_preset(6).unwrap(); 790 | /// opts.dict_size(dict_size); 791 | /// let mut filters = Filters::new(); 792 | /// filters.powerpc(); 793 | /// filters.lzma2(&opts); 794 | /// ``` 795 | #[inline] 796 | pub fn powerpc(&mut self) -> &mut Filters { 797 | self.push(liblzma_sys::lzma_filter { 798 | id: liblzma_sys::LZMA_FILTER_POWERPC, 799 | options: std::ptr::null_mut(), 800 | }) 801 | } 802 | 803 | /// Add a filter for PowerPC binaries with properties. 804 | /// 805 | /// # Examples 806 | /// ``` 807 | /// use liblzma::stream::{Filters, LzmaOptions}; 808 | /// 809 | /// let mut filters = Filters::new(); 810 | /// filters.powerpc_properties(&[0x00, 0x00, 0x00, 0x00]).unwrap(); 811 | /// ``` 812 | #[inline] 813 | pub fn powerpc_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> { 814 | let filter = liblzma_sys::lzma_filter { 815 | id: liblzma_sys::LZMA_FILTER_POWERPC, 816 | options: std::ptr::null_mut(), 817 | }; 818 | self.push_with_properties(filter, properties) 819 | } 820 | 821 | /// Add a filter for IA-64 (itanium) binaries. 822 | /// 823 | /// # Examples 824 | /// ``` 825 | /// use liblzma::stream::{Filters, LzmaOptions}; 826 | /// 827 | /// let dict_size = 0x40000; 828 | /// let mut opts = LzmaOptions::new_preset(6).unwrap(); 829 | /// opts.dict_size(dict_size); 830 | /// let mut filters = Filters::new(); 831 | /// filters.ia64(); 832 | /// filters.lzma2(&opts); 833 | /// ``` 834 | #[inline] 835 | pub fn ia64(&mut self) -> &mut Filters { 836 | self.push(liblzma_sys::lzma_filter { 837 | id: liblzma_sys::LZMA_FILTER_IA64, 838 | options: std::ptr::null_mut(), 839 | }) 840 | } 841 | 842 | /// Add a filter for IA-64 (itanium) binaries with properties. 843 | /// 844 | /// # Examples 845 | /// ``` 846 | /// use liblzma::stream::{Filters, LzmaOptions}; 847 | /// 848 | /// let mut filters = Filters::new(); 849 | /// filters.ia64_properties(&[0x00, 0x00, 0x00, 0x00]).unwrap(); 850 | /// ``` 851 | #[inline] 852 | pub fn ia64_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> { 853 | let filter = liblzma_sys::lzma_filter { 854 | id: liblzma_sys::LZMA_FILTER_IA64, 855 | options: std::ptr::null_mut(), 856 | }; 857 | self.push_with_properties(filter, properties) 858 | } 859 | 860 | /// Add a filter for ARM binaries. 861 | /// 862 | /// # Examples 863 | /// ``` 864 | /// use liblzma::stream::{Filters, LzmaOptions}; 865 | /// 866 | /// let dict_size = 0x40000; 867 | /// let mut opts = LzmaOptions::new_preset(6).unwrap(); 868 | /// opts.dict_size(dict_size); 869 | /// let mut filters = Filters::new(); 870 | /// filters.arm(); 871 | /// filters.lzma2(&opts); 872 | /// ``` 873 | #[inline] 874 | pub fn arm(&mut self) -> &mut Filters { 875 | self.push(liblzma_sys::lzma_filter { 876 | id: liblzma_sys::LZMA_FILTER_ARM, 877 | options: std::ptr::null_mut(), 878 | }) 879 | } 880 | 881 | /// Add a filter for ARM binaries with properties. 882 | /// 883 | /// # Examples 884 | /// ``` 885 | /// use liblzma::stream::{Filters, LzmaOptions}; 886 | /// 887 | /// let mut filters = Filters::new(); 888 | /// filters.arm_properties(&[0x00, 0x00, 0x00, 0x00]).unwrap(); 889 | /// ``` 890 | #[inline] 891 | pub fn arm_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> { 892 | let filter = liblzma_sys::lzma_filter { 893 | id: liblzma_sys::LZMA_FILTER_ARM, 894 | options: std::ptr::null_mut(), 895 | }; 896 | self.push_with_properties(filter, properties) 897 | } 898 | 899 | /// Add a filter for ARM64 binaries. 900 | /// 901 | /// # Examples 902 | /// ``` 903 | /// use liblzma::stream::{Filters, LzmaOptions}; 904 | /// 905 | /// let dict_size = 0x40000; 906 | /// let mut opts = LzmaOptions::new_preset(6).unwrap(); 907 | /// opts.dict_size(dict_size); 908 | /// let mut filters = Filters::new(); 909 | /// filters.arm64(); 910 | /// filters.lzma2(&opts); 911 | /// ``` 912 | #[inline] 913 | pub fn arm64(&mut self) -> &mut Filters { 914 | self.push(liblzma_sys::lzma_filter { 915 | id: liblzma_sys::LZMA_FILTER_ARM64, 916 | options: std::ptr::null_mut(), 917 | }) 918 | } 919 | 920 | /// Add a filter for ARM64 binaries with properties. 921 | /// 922 | /// # Examples 923 | /// ``` 924 | /// use liblzma::stream::{Filters, LzmaOptions}; 925 | /// 926 | /// let mut filters = Filters::new(); 927 | /// filters.arm64_properties(&[0x00, 0x00, 0x00, 0x00]).unwrap(); 928 | /// ``` 929 | #[inline] 930 | pub fn arm64_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> { 931 | let filter = liblzma_sys::lzma_filter { 932 | id: liblzma_sys::LZMA_FILTER_ARM64, 933 | options: std::ptr::null_mut(), 934 | }; 935 | self.push_with_properties(filter, properties) 936 | } 937 | 938 | /// Add a filter for RISCV binaries. 939 | /// 940 | /// # Examples 941 | /// ``` 942 | /// use liblzma::stream::{Filters, LzmaOptions}; 943 | /// 944 | /// let dict_size = 0x40000; 945 | /// let mut opts = LzmaOptions::new_preset(6).unwrap(); 946 | /// opts.dict_size(dict_size); 947 | /// let mut filters = Filters::new(); 948 | /// filters.riscv(); 949 | /// filters.lzma2(&opts); 950 | /// ``` 951 | #[inline] 952 | pub fn riscv(&mut self) -> &mut Filters { 953 | self.push(liblzma_sys::lzma_filter { 954 | id: liblzma_sys::LZMA_FILTER_RISCV, 955 | options: std::ptr::null_mut(), 956 | }) 957 | } 958 | 959 | /// Add a filter for RISCV binaries with properties. 960 | /// 961 | /// # Examples 962 | /// ``` 963 | /// use liblzma::stream::{Filters, LzmaOptions}; 964 | /// 965 | /// let mut filters = Filters::new(); 966 | /// filters.riscv_properties(&[0x00, 0x00, 0x00, 0x00]).unwrap(); 967 | /// ``` 968 | #[inline] 969 | pub fn riscv_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> { 970 | let filter = liblzma_sys::lzma_filter { 971 | id: liblzma_sys::LZMA_FILTER_RISCV, 972 | options: std::ptr::null_mut(), 973 | }; 974 | self.push_with_properties(filter, properties) 975 | } 976 | 977 | /// Add a filter for ARM-Thumb binaries. 978 | /// 979 | /// # Examples 980 | /// ``` 981 | /// use liblzma::stream::{Filters, LzmaOptions}; 982 | /// 983 | /// let dict_size = 0x40000; 984 | /// let mut opts = LzmaOptions::new_preset(6).unwrap(); 985 | /// opts.dict_size(dict_size); 986 | /// let mut filters = Filters::new(); 987 | /// filters.arm_thumb(); 988 | /// filters.lzma2(&opts); 989 | /// ``` 990 | #[inline] 991 | pub fn arm_thumb(&mut self) -> &mut Filters { 992 | self.push(liblzma_sys::lzma_filter { 993 | id: liblzma_sys::LZMA_FILTER_ARMTHUMB, 994 | options: std::ptr::null_mut(), 995 | }) 996 | } 997 | 998 | /// Add a filter for ARM-Thumb binaries with properties. 999 | /// 1000 | /// # Examples 1001 | /// ``` 1002 | /// use liblzma::stream::{Filters, LzmaOptions}; 1003 | /// 1004 | /// let mut filters = Filters::new(); 1005 | /// filters.arm_thumb_properties(&[0x00, 0x00, 0x00, 0x00]).unwrap(); 1006 | /// ``` 1007 | #[inline] 1008 | pub fn arm_thumb_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> { 1009 | let filter = liblzma_sys::lzma_filter { 1010 | id: liblzma_sys::LZMA_FILTER_ARMTHUMB, 1011 | options: std::ptr::null_mut(), 1012 | }; 1013 | self.push_with_properties(filter, properties) 1014 | } 1015 | 1016 | /// Add a filter for SPARC binaries. 1017 | /// 1018 | /// # Examples 1019 | /// ``` 1020 | /// use liblzma::stream::{Filters, LzmaOptions}; 1021 | /// 1022 | /// let dict_size = 0x40000; 1023 | /// let mut opts = LzmaOptions::new_preset(6).unwrap(); 1024 | /// opts.dict_size(dict_size); 1025 | /// let mut filters = Filters::new(); 1026 | /// filters.sparc(); 1027 | /// filters.lzma2(&opts); 1028 | /// ``` 1029 | #[inline] 1030 | pub fn sparc(&mut self) -> &mut Filters { 1031 | self.push(liblzma_sys::lzma_filter { 1032 | id: liblzma_sys::LZMA_FILTER_SPARC, 1033 | options: std::ptr::null_mut(), 1034 | }) 1035 | } 1036 | 1037 | /// Add a filter for SPARC binaries with properties. 1038 | /// 1039 | /// # Examples 1040 | /// ``` 1041 | /// use liblzma::stream::{Filters, LzmaOptions}; 1042 | /// 1043 | /// let mut filters = Filters::new(); 1044 | /// filters.sparc_properties(&[0x00, 0x00, 0x00, 0x00]).unwrap(); 1045 | /// ``` 1046 | #[inline] 1047 | pub fn sparc_properties(&mut self, properties: &[u8]) -> Result<&mut Filters, Error> { 1048 | let filter = liblzma_sys::lzma_filter { 1049 | id: liblzma_sys::LZMA_FILTER_SPARC, 1050 | options: std::ptr::null_mut(), 1051 | }; 1052 | self.push_with_properties(filter, properties) 1053 | } 1054 | 1055 | #[inline] 1056 | fn push(&mut self, filter: liblzma_sys::lzma_filter) -> &mut Filters { 1057 | let pos = self.inner.len() - 1; 1058 | self.inner.insert(pos, filter); 1059 | self 1060 | } 1061 | 1062 | #[inline] 1063 | fn push_with_properties( 1064 | &mut self, 1065 | mut filter: liblzma_sys::lzma_filter, 1066 | properties: &[u8], 1067 | ) -> Result<&mut Filters, Error> { 1068 | cvt(unsafe { 1069 | liblzma_sys::lzma_properties_decode( 1070 | &mut filter, 1071 | std::ptr::null(), 1072 | properties.as_ptr(), 1073 | properties.len(), 1074 | ) 1075 | })?; 1076 | let pos = self.inner.len() - 1; 1077 | self.inner.insert(pos, filter); 1078 | Ok(self) 1079 | } 1080 | 1081 | /// recommend a Block size for multithreaded encoding 1082 | /// 1083 | /// # Examples 1084 | /// ``` 1085 | /// use liblzma::stream::{Filters, LzmaOptions}; 1086 | /// 1087 | /// let dict_size = 0x40000; 1088 | /// let mut opts = LzmaOptions::new_preset(6).unwrap(); 1089 | /// opts.dict_size(dict_size); 1090 | /// let mut filters = Filters::new(); 1091 | /// filters.lzma2(&opts); 1092 | /// assert_eq!(filters.mt_block_size(), 1 << 20); 1093 | /// ``` 1094 | #[cfg(feature = "parallel")] 1095 | #[inline] 1096 | pub fn mt_block_size(&self) -> u64 { 1097 | unsafe { liblzma_sys::lzma_mt_block_size(self.inner.as_ptr()) } 1098 | } 1099 | } 1100 | 1101 | #[cfg(feature = "parallel")] 1102 | impl MtStreamBuilder { 1103 | /// Creates a new blank builder to create a multithreaded encoding `Stream`. 1104 | #[inline] 1105 | pub fn new() -> Self { 1106 | let mut init = Self { 1107 | raw: unsafe { mem::zeroed() }, 1108 | filters: None, 1109 | }; 1110 | init.raw.threads = 1; 1111 | init 1112 | } 1113 | 1114 | /// Configures the number of worker threads to use 1115 | #[inline] 1116 | pub fn threads(&mut self, threads: u32) -> &mut Self { 1117 | self.raw.threads = threads; 1118 | self 1119 | } 1120 | 1121 | /// Configures the maximum uncompressed size of a block 1122 | /// 1123 | /// The encoder will start a new .xz block every `block_size` bytes. 1124 | /// Using `FullFlush` or `FullBarrier` with `process` the caller may tell 1125 | /// liblzma to start a new block earlier. 1126 | /// 1127 | /// With LZMA2, a recommended block size is 2-4 times the LZMA2 dictionary 1128 | /// size. With very small dictionaries, it is recommended to use at least 1 1129 | /// MiB block size for good compression ratio, even if this is more than 1130 | /// four times the dictionary size. Note that these are only recommendations 1131 | /// for typical use cases; feel free to use other values. Just keep in mind 1132 | /// that using a block size less than the LZMA2 dictionary size is waste of 1133 | /// RAM. 1134 | /// 1135 | /// Set this to 0 to let liblzma choose the block size depending on the 1136 | /// compression options. For LZMA2 it will be 3*`dict_size` or 1 MiB, 1137 | /// whichever is more. 1138 | /// 1139 | /// For each thread, about 3 * `block_size` bytes of memory will be 1140 | /// allocated. This may change in later liblzma versions. If so, the memory 1141 | /// usage will probably be reduced, not increased. 1142 | #[inline] 1143 | pub fn block_size(&mut self, block_size: u64) -> &mut Self { 1144 | self.raw.block_size = block_size; 1145 | self 1146 | } 1147 | 1148 | /// Timeout to allow `process` to return early 1149 | /// 1150 | /// Multithreading can make liblzma to consume input and produce output in a 1151 | /// very bursty way: it may first read a lot of input to fill internal 1152 | /// buffers, then no input or output occurs for a while. 1153 | /// 1154 | /// In single-threaded mode, `process` won't return until it has either 1155 | /// consumed all the input or filled the output buffer. If this is done in 1156 | /// multithreaded mode, it may cause a call `process` to take even tens of 1157 | /// seconds, which isn't acceptable in all applications. 1158 | /// 1159 | /// To avoid very long blocking times in `process`, a timeout (in 1160 | /// milliseconds) may be set here. If `process would block longer than 1161 | /// this number of milliseconds, it will return with `Ok`. Reasonable 1162 | /// values are 100 ms or more. The xz command line tool uses 300 ms. 1163 | /// 1164 | /// If long blocking times are fine for you, set timeout to a special 1165 | /// value of 0, which will disable the timeout mechanism and will make 1166 | /// `process` block until all the input is consumed or the output 1167 | /// buffer has been filled. 1168 | #[inline] 1169 | pub fn timeout_ms(&mut self, timeout: u32) -> &mut Self { 1170 | self.raw.timeout = timeout; 1171 | self 1172 | } 1173 | 1174 | /// Compression preset (level and possible flags) 1175 | /// 1176 | /// The preset is set just like with `Stream::new_easy_encoder`. The preset 1177 | /// is ignored if filters below have been specified. 1178 | #[inline] 1179 | pub fn preset(&mut self, preset: u32) -> &mut Self { 1180 | self.raw.preset = preset; 1181 | self 1182 | } 1183 | 1184 | /// Configure a custom filter chain 1185 | #[inline] 1186 | pub fn filters(&mut self, filters: Filters) -> &mut Self { 1187 | self.raw.filters = filters.inner.as_ptr(); 1188 | self.filters = Some(filters); 1189 | self 1190 | } 1191 | 1192 | /// Configures the integrity check type 1193 | #[inline] 1194 | pub fn check(&mut self, check: Check) -> &mut Self { 1195 | self.raw.check = check as liblzma_sys::lzma_check; 1196 | self 1197 | } 1198 | 1199 | /// Memory usage limit to reduce the number of threads 1200 | #[inline] 1201 | pub fn memlimit_threading(&mut self, memlimit: u64) -> &mut Self { 1202 | self.raw.memlimit_threading = memlimit; 1203 | self 1204 | } 1205 | 1206 | /// Memory usage limit that should never be exceeded 1207 | #[inline] 1208 | pub fn memlimit_stop(&mut self, memlimit: u64) -> &mut Self { 1209 | self.raw.memlimit_stop = memlimit; 1210 | self 1211 | } 1212 | 1213 | /// Calculate approximate memory usage of multithreaded .xz encoder 1214 | #[inline] 1215 | pub fn memusage(&self) -> u64 { 1216 | unsafe { liblzma_sys::lzma_stream_encoder_mt_memusage(&self.raw) } 1217 | } 1218 | 1219 | /// Initialize multithreaded .xz stream encoder. 1220 | #[inline] 1221 | pub fn encoder(&self) -> Result { 1222 | let mut init = unsafe { Stream::zeroed() }; 1223 | cvt(unsafe { liblzma_sys::lzma_stream_encoder_mt(&mut init.raw, &self.raw) })?; 1224 | Ok(init) 1225 | } 1226 | 1227 | /// Initialize multithreaded .xz stream decoder. 1228 | #[inline] 1229 | pub fn decoder(&self) -> Result { 1230 | let mut init = unsafe { Stream::zeroed() }; 1231 | cvt(unsafe { liblzma_sys::lzma_stream_decoder_mt(&mut init.raw, &self.raw) })?; 1232 | Ok(init) 1233 | } 1234 | } 1235 | 1236 | fn cvt(rc: liblzma_sys::lzma_ret) -> Result { 1237 | match rc { 1238 | liblzma_sys::LZMA_OK => Ok(Status::Ok), 1239 | liblzma_sys::LZMA_STREAM_END => Ok(Status::StreamEnd), 1240 | liblzma_sys::LZMA_NO_CHECK => Err(Error::NoCheck), 1241 | liblzma_sys::LZMA_UNSUPPORTED_CHECK => Err(Error::UnsupportedCheck), 1242 | liblzma_sys::LZMA_GET_CHECK => Ok(Status::GetCheck), 1243 | liblzma_sys::LZMA_MEM_ERROR => Err(Error::Mem), 1244 | liblzma_sys::LZMA_MEMLIMIT_ERROR => Err(Error::MemLimit), 1245 | liblzma_sys::LZMA_FORMAT_ERROR => Err(Error::Format), 1246 | liblzma_sys::LZMA_OPTIONS_ERROR => Err(Error::Options), 1247 | liblzma_sys::LZMA_DATA_ERROR => Err(Error::Data), 1248 | liblzma_sys::LZMA_BUF_ERROR => Ok(Status::MemNeeded), 1249 | liblzma_sys::LZMA_PROG_ERROR => Err(Error::Program), 1250 | c => panic!("unknown return code: {}", c), 1251 | } 1252 | } 1253 | 1254 | impl From for io::Error { 1255 | #[inline] 1256 | fn from(e: Error) -> io::Error { 1257 | let kind = match e { 1258 | Error::Data => io::ErrorKind::InvalidData, 1259 | Error::Options => io::ErrorKind::InvalidInput, 1260 | Error::Format => io::ErrorKind::InvalidData, 1261 | Error::MemLimit => io::ErrorKind::Other, 1262 | Error::Mem => io::ErrorKind::Other, 1263 | Error::Program => io::ErrorKind::Other, 1264 | Error::NoCheck => io::ErrorKind::InvalidInput, 1265 | Error::UnsupportedCheck => io::ErrorKind::Other, 1266 | }; 1267 | 1268 | io::Error::new(kind, e) 1269 | } 1270 | } 1271 | 1272 | impl error::Error for Error {} 1273 | 1274 | impl fmt::Display for Error { 1275 | #[inline] 1276 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 1277 | match self { 1278 | Error::Data => "lzma data error", 1279 | Error::Options => "invalid options", 1280 | Error::Format => "stream/file format not recognized", 1281 | Error::MemLimit => "memory limit reached", 1282 | Error::Mem => "can't allocate memory", 1283 | Error::Program => "liblzma internal error", 1284 | Error::NoCheck => "no integrity check was available", 1285 | Error::UnsupportedCheck => "liblzma not built with check support", 1286 | } 1287 | .fmt(f) 1288 | } 1289 | } 1290 | 1291 | impl Drop for Stream { 1292 | #[inline] 1293 | fn drop(&mut self) { 1294 | unsafe { 1295 | liblzma_sys::lzma_end(&mut self.raw); 1296 | } 1297 | } 1298 | } 1299 | -------------------------------------------------------------------------------- /src/write.rs: -------------------------------------------------------------------------------- 1 | //! Writer-based compression/decompression streams 2 | 3 | mod auto_finish; 4 | 5 | use std::io; 6 | use std::io::prelude::*; 7 | 8 | #[cfg(feature = "parallel")] 9 | use crate::stream::MtStreamBuilder; 10 | use crate::stream::{Action, Check, Status, Stream}; 11 | pub use auto_finish::{AutoFinishXzDecoder, AutoFinishXzEncoder}; 12 | 13 | /// A compression stream which will have uncompressed data written to it and 14 | /// will write compressed data to an output stream. 15 | /// [XzEncoder] will no longer perform the finalization automatically in the next miner release, so you need to call [XzEncoder::finish] manually. 16 | /// If you want to automate the finalization process, please use [XzEncoder::auto_finish]. 17 | pub struct XzEncoder { 18 | data: Stream, 19 | obj: Option, 20 | buf: Vec, 21 | } 22 | 23 | /// A compression stream which will have compressed data written to it and 24 | /// will write uncompressed data to an output stream. 25 | /// [XzDecoder] will no longer perform the finalization automatically in the next miner release, so you need to call [XzDecoder::finish] manually. 26 | /// If you want to automate the finalization process, please use [XzDecoder::auto_finish]. 27 | pub struct XzDecoder { 28 | data: Stream, 29 | obj: Option, 30 | buf: Vec, 31 | } 32 | 33 | impl XzEncoder { 34 | /// Create a new compression stream which will compress at the given level 35 | /// to write compress output to the give output stream. 36 | #[inline] 37 | pub fn new(obj: W, level: u32) -> XzEncoder { 38 | let stream = Stream::new_easy_encoder(level, Check::Crc64).unwrap(); 39 | XzEncoder::new_stream(obj, stream) 40 | } 41 | /// Create a new parallel compression stream which will compress at the given level 42 | /// to write compress output to the give output stream. 43 | #[cfg(feature = "parallel")] 44 | pub fn new_parallel(obj: W, level: u32) -> XzEncoder { 45 | let stream = MtStreamBuilder::new() 46 | .preset(level) 47 | .check(Check::Crc64) 48 | .threads(num_cpus::get() as u32) 49 | .encoder() 50 | .unwrap(); 51 | Self::new_stream(obj, stream) 52 | } 53 | 54 | /// Create a new encoder which will use the specified `Stream` to encode 55 | /// (compress) data into the provided `obj`. 56 | #[inline] 57 | pub fn new_stream(obj: W, stream: Stream) -> XzEncoder { 58 | XzEncoder { 59 | data: stream, 60 | obj: Some(obj), 61 | buf: Vec::with_capacity(32 * 1024), 62 | } 63 | } 64 | 65 | /// Acquires a reference to the underlying writer. 66 | #[inline] 67 | pub fn get_ref(&self) -> &W { 68 | self.obj.as_ref().unwrap() 69 | } 70 | 71 | /// Acquires a mutable reference to the underlying writer. 72 | /// 73 | /// Note that mutating the output/input state of the stream may corrupt this 74 | /// object, so care must be taken when using this method. 75 | #[inline] 76 | pub fn get_mut(&mut self) -> &mut W { 77 | self.obj.as_mut().unwrap() 78 | } 79 | 80 | fn dump(&mut self) -> io::Result<()> { 81 | self.obj.as_mut().unwrap().write_all(&self.buf)?; 82 | self.buf.clear(); 83 | Ok(()) 84 | } 85 | 86 | /// Attempt to finish this output stream, writing out final chunks of data. 87 | /// 88 | /// Note that this function can only be used once data has finished being 89 | /// written to the output stream. After this function is called then further 90 | /// calls to `write` may result in a panic. 91 | /// 92 | /// # Panics 93 | /// 94 | /// Attempts to write data to this stream may result in a panic after this 95 | /// function is called. 96 | #[inline] 97 | pub fn try_finish(&mut self) -> io::Result<()> { 98 | loop { 99 | self.dump()?; 100 | let res = self.data.process_vec(&[], &mut self.buf, Action::Finish)?; 101 | if res == Status::StreamEnd { 102 | break; 103 | } 104 | } 105 | self.dump() 106 | } 107 | 108 | /// Consumes this encoder, finishing the compression stream. 109 | /// 110 | /// This will finish the underlying data stream and then return the contained 111 | /// writer if the finish succeeded. 112 | /// 113 | /// Note that this function may not be suitable to call in a situation where 114 | /// the underlying stream is an asynchronous I/O stream. To finish a stream 115 | /// the `try_finish` (or `shutdown`) method should be used instead. To 116 | /// re-acquire ownership of a stream it is safe to call this method after 117 | /// `try_finish` or `shutdown` has returned `Ok`. 118 | #[inline] 119 | pub fn finish(mut self) -> io::Result { 120 | self.try_finish()?; 121 | Ok(self.obj.take().unwrap()) 122 | } 123 | 124 | /// Returns the number of bytes produced by the compressor 125 | /// 126 | /// Note that, due to buffering, this only bears any relation to 127 | /// `total_in()` after a call to `flush()`. At that point, 128 | /// `total_out() / total_in()` is the compression ratio. 129 | #[inline] 130 | pub fn total_out(&self) -> u64 { 131 | self.data.total_out() 132 | } 133 | 134 | /// Returns the number of bytes consumed by the compressor 135 | /// (e.g. the number of bytes written to this stream.) 136 | #[inline] 137 | pub fn total_in(&self) -> u64 { 138 | self.data.total_in() 139 | } 140 | 141 | /// Convert to [AutoFinishXzEncoder] that impl [Drop] trait. 142 | /// [AutoFinishXzEncoder] automatically calls [XzDecoder::try_finish] method when exiting the scope. 143 | #[inline] 144 | pub fn auto_finish(self) -> AutoFinishXzEncoder { 145 | AutoFinishXzEncoder(self) 146 | } 147 | } 148 | 149 | impl Write for XzEncoder { 150 | #[inline] 151 | fn write(&mut self, data: &[u8]) -> io::Result { 152 | loop { 153 | self.dump()?; 154 | 155 | let total_in = self.total_in(); 156 | self.data.process_vec(data, &mut self.buf, Action::Run)?; 157 | let written = (self.total_in() - total_in) as usize; 158 | 159 | if written > 0 || data.is_empty() { 160 | return Ok(written); 161 | } 162 | } 163 | } 164 | 165 | #[inline] 166 | fn flush(&mut self) -> io::Result<()> { 167 | loop { 168 | self.dump()?; 169 | let status = self 170 | .data 171 | .process_vec(&[], &mut self.buf, Action::FullFlush)?; 172 | if status == Status::StreamEnd { 173 | break; 174 | } 175 | } 176 | self.obj.as_mut().unwrap().flush() 177 | } 178 | } 179 | 180 | impl Read for XzEncoder { 181 | #[inline] 182 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 183 | self.get_mut().read(buf) 184 | } 185 | } 186 | 187 | impl Drop for XzEncoder { 188 | #[inline] 189 | fn drop(&mut self) { 190 | if self.obj.is_some() { 191 | let _ = self.try_finish(); 192 | } 193 | } 194 | } 195 | 196 | impl XzDecoder { 197 | /// Creates a new decoding stream which will decode into `obj` one xz stream 198 | /// from the input written to it. 199 | #[inline] 200 | pub fn new(obj: W) -> XzDecoder { 201 | let stream = Stream::new_stream_decoder(u64::MAX, 0).unwrap(); 202 | XzDecoder::new_stream(obj, stream) 203 | } 204 | 205 | /// Creates a new parallel decoding stream which will decode into `obj` one xz stream 206 | /// from the input written to it. 207 | #[cfg(feature = "parallel")] 208 | pub fn new_parallel(obj: W) -> Self { 209 | let stream = MtStreamBuilder::new() 210 | .memlimit_stop(u64::MAX) 211 | .threads(num_cpus::get() as u32) 212 | .decoder() 213 | .unwrap(); 214 | Self::new_stream(obj, stream) 215 | } 216 | 217 | /// Creates a new decoding stream which will decode into `obj` all the xz streams 218 | /// from the input written to it. 219 | #[inline] 220 | pub fn new_multi_decoder(obj: W) -> XzDecoder { 221 | let stream = Stream::new_stream_decoder(u64::MAX, liblzma_sys::LZMA_CONCATENATED).unwrap(); 222 | XzDecoder::new_stream(obj, stream) 223 | } 224 | 225 | /// Creates a new decoding stream which will decode all input written to it 226 | /// into `obj`. 227 | /// 228 | /// A custom `stream` can be specified to configure what format this decoder 229 | /// will recognize or configure other various decoding options. 230 | #[inline] 231 | pub fn new_stream(obj: W, stream: Stream) -> XzDecoder { 232 | XzDecoder { 233 | data: stream, 234 | obj: Some(obj), 235 | buf: Vec::with_capacity(32 * 1024), 236 | } 237 | } 238 | 239 | /// Acquires a reference to the underlying writer. 240 | #[inline] 241 | pub fn get_ref(&self) -> &W { 242 | self.obj.as_ref().unwrap() 243 | } 244 | 245 | /// Acquires a mutable reference to the underlying writer. 246 | /// 247 | /// Note that mutating the output/input state of the stream may corrupt this 248 | /// object, so care must be taken when using this method. 249 | #[inline] 250 | pub fn get_mut(&mut self) -> &mut W { 251 | self.obj.as_mut().unwrap() 252 | } 253 | 254 | fn dump(&mut self) -> io::Result<()> { 255 | self.obj.as_mut().unwrap().write_all(&self.buf)?; 256 | self.buf.clear(); 257 | Ok(()) 258 | } 259 | 260 | /// Attempt to finish this output stream, writing out final chunks of data. 261 | /// 262 | /// Note that this function can only be used once data has finished being 263 | /// written to the output stream. After this function is called then further 264 | /// calls to `write` may result in a panic. 265 | /// 266 | /// # Panics 267 | /// 268 | /// Attempts to write data to this stream may result in a panic after this 269 | /// function is called. 270 | #[inline] 271 | pub fn try_finish(&mut self) -> io::Result<()> { 272 | loop { 273 | self.dump()?; 274 | let res = self.data.process_vec(&[], &mut self.buf, Action::Finish)?; 275 | 276 | // When decoding a truncated file, XZ returns LZMA_BUF_ERROR and 277 | // decodes no new data, which corresponds to this crate's MemNeeded 278 | // status. Since we're finishing, we cannot provide more data so 279 | // this is an error. 280 | // 281 | // See the 02_decompress.c example in xz-utils. 282 | if self.buf.is_empty() && res == Status::MemNeeded { 283 | let msg = "xz compressed stream is truncated or otherwise corrupt"; 284 | return Err(io::Error::new(io::ErrorKind::UnexpectedEof, msg)); 285 | } 286 | 287 | if res == Status::StreamEnd { 288 | break; 289 | } 290 | } 291 | self.dump() 292 | } 293 | 294 | /// Consumes this decoder, finishing the decompression stream. 295 | /// 296 | /// This will finish the underlying data stream and then return the contained 297 | /// writer if the finish succeeded. 298 | /// 299 | /// Note that this function may not be suitable to call in a situation where 300 | /// the underlying stream is an asynchronous I/O stream. To finish a stream 301 | /// the `try_finish` (or `shutdown`) method should be used instead. To 302 | /// re-acquire ownership of a stream it is safe to call this method after 303 | /// `try_finish` or `shutdown` has returned `Ok`. 304 | #[inline] 305 | pub fn finish(mut self) -> io::Result { 306 | self.try_finish()?; 307 | Ok(self.obj.take().unwrap()) 308 | } 309 | 310 | /// Returns the number of bytes produced by the decompressor 311 | /// 312 | /// Note that, due to buffering, this only bears any relation to 313 | /// `total_in()` after a call to `flush()`. At that point, 314 | /// `total_in() / total_out()` is the compression ratio. 315 | #[inline] 316 | pub fn total_out(&self) -> u64 { 317 | self.data.total_out() 318 | } 319 | 320 | /// Returns the number of bytes consumed by the decompressor 321 | /// (e.g. the number of bytes written to this stream.) 322 | #[inline] 323 | pub fn total_in(&self) -> u64 { 324 | self.data.total_in() 325 | } 326 | 327 | /// Convert to [AutoFinishXzDecoder] that impl [Drop] trait. 328 | /// [AutoFinishXzDecoder] automatically calls [XzDecoder::try_finish] method when exiting the scope. 329 | #[inline] 330 | pub fn auto_finish(self) -> AutoFinishXzDecoder { 331 | AutoFinishXzDecoder(self) 332 | } 333 | } 334 | 335 | impl Write for XzDecoder { 336 | #[inline] 337 | fn write(&mut self, data: &[u8]) -> io::Result { 338 | loop { 339 | self.dump()?; 340 | 341 | let before = self.total_in(); 342 | let res = self.data.process_vec(data, &mut self.buf, Action::Run)?; 343 | let written = (self.total_in() - before) as usize; 344 | 345 | if written > 0 || data.is_empty() || res == Status::StreamEnd { 346 | return Ok(written); 347 | } 348 | } 349 | } 350 | 351 | #[inline] 352 | fn flush(&mut self) -> io::Result<()> { 353 | self.dump()?; 354 | self.obj.as_mut().unwrap().flush() 355 | } 356 | } 357 | 358 | impl Read for XzDecoder { 359 | #[inline] 360 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 361 | self.get_mut().read(buf) 362 | } 363 | } 364 | 365 | impl Drop for XzDecoder { 366 | #[inline] 367 | fn drop(&mut self) { 368 | if self.obj.is_some() { 369 | let _ = self.try_finish(); 370 | } 371 | } 372 | } 373 | 374 | #[cfg(test)] 375 | mod tests { 376 | use super::*; 377 | use crate::stream::LzmaOptions; 378 | use quickcheck::quickcheck; 379 | use std::iter::repeat; 380 | #[cfg(all(target_family = "wasm", target_os = "unknown"))] 381 | use wasm_bindgen_test::wasm_bindgen_test as test; 382 | 383 | #[test] 384 | fn smoke() { 385 | let d = XzDecoder::new(Vec::new()); 386 | let mut c = XzEncoder::new(d, 6); 387 | c.write_all(b"12834").unwrap(); 388 | let s = repeat("12345").take(100000).collect::(); 389 | c.write_all(s.as_bytes()).unwrap(); 390 | let data = c.finish().unwrap().finish().unwrap(); 391 | assert_eq!(&data[0..5], b"12834"); 392 | assert_eq!(data.len(), 500005); 393 | assert_eq!(format!("12834{}", s).as_bytes(), &*data); 394 | } 395 | 396 | #[test] 397 | fn write_empty() { 398 | let d = XzDecoder::new(Vec::new()); 399 | let mut c = XzEncoder::new(d, 6); 400 | c.write(b"").unwrap(); 401 | let data = c.finish().unwrap().finish().unwrap(); 402 | assert_eq!(&data[..], b""); 403 | } 404 | 405 | #[test] 406 | fn qc_lzma1() { 407 | quickcheck(test as fn(_) -> _); 408 | 409 | fn test(v: Vec) -> bool { 410 | let stream = Stream::new_lzma_decoder(u64::MAX).unwrap(); 411 | let w = XzDecoder::new_stream(Vec::new(), stream); 412 | let options = LzmaOptions::new_preset(6).unwrap(); 413 | let stream = Stream::new_lzma_encoder(&options).unwrap(); 414 | let mut w = XzEncoder::new_stream(w, stream); 415 | w.write_all(&v).unwrap(); 416 | v == w.finish().unwrap().finish().unwrap() 417 | } 418 | } 419 | 420 | #[test] 421 | fn qc() { 422 | quickcheck(test as fn(_) -> _); 423 | 424 | fn test(v: Vec) -> bool { 425 | let w = XzDecoder::new(Vec::new()); 426 | let mut w = XzEncoder::new(w, 6); 427 | w.write_all(&v).unwrap(); 428 | v == w.finish().unwrap().finish().unwrap() 429 | } 430 | } 431 | 432 | #[cfg(feature = "parallel")] 433 | #[test] 434 | fn qc_parallel_encode() { 435 | quickcheck(test as fn(_) -> _); 436 | 437 | fn test(v: Vec) -> bool { 438 | let w = XzDecoder::new(Vec::new()); 439 | let mut w = XzEncoder::new_parallel(w, 6); 440 | w.write_all(&v).unwrap(); 441 | v == w.finish().unwrap().finish().unwrap() 442 | } 443 | } 444 | 445 | #[cfg(feature = "parallel")] 446 | #[test] 447 | fn qc_parallel_decode() { 448 | quickcheck(test as fn(_) -> _); 449 | 450 | fn test(v: Vec) -> bool { 451 | let w = XzDecoder::new_parallel(Vec::new()); 452 | let mut w = XzEncoder::new(w, 6); 453 | w.write_all(&v).unwrap(); 454 | v == w.finish().unwrap().finish().unwrap() 455 | } 456 | } 457 | } 458 | -------------------------------------------------------------------------------- /src/write/auto_finish.rs: -------------------------------------------------------------------------------- 1 | use crate::write::{XzDecoder, XzEncoder}; 2 | use std::io; 3 | use std::io::Write; 4 | 5 | /// A compression stream which will have uncompressed data written to it and 6 | /// will write compressed data to an output stream. 7 | /// [AutoFinishXzEncoder] impl [Drop] trait, so automatically calls [XzEncoder::try_finish] method when exiting the scope. 8 | /// However, it is not guaranteed that `try_finish` will complete successfully, and it is recommended to call `try_finish` manually if you want to ensure that the process is successful. 9 | pub struct AutoFinishXzEncoder(pub(super) XzEncoder); 10 | 11 | impl AutoFinishXzEncoder { 12 | /// Acquires a reference to the underlying writer. 13 | #[inline] 14 | pub fn get_ref(&self) -> &W { 15 | self.0.get_ref() 16 | } 17 | 18 | /// Acquires a mutable reference to the underlying writer. 19 | /// 20 | /// Note that mutating the output/input state of the stream may corrupt this 21 | /// object, so care must be taken when using this method. 22 | #[inline] 23 | pub fn get_mut(&mut self) -> &mut W { 24 | self.0.get_mut() 25 | } 26 | 27 | /// Attempt to finish this output stream, writing out final chunks of data. 28 | /// 29 | /// Note that this function can only be used once data has finished being 30 | /// written to the output stream. After this function is called then further 31 | /// calls to `write` may result in a panic. 32 | /// 33 | /// # Panics 34 | /// 35 | /// Attempts to write data to this stream may result in a panic after this 36 | /// function is called. 37 | #[inline] 38 | pub fn try_finish(&mut self) -> io::Result<()> { 39 | self.0.try_finish() 40 | } 41 | 42 | /// Consumes this encoder, flushing the output stream. 43 | /// 44 | /// This will flush the underlying data stream and then return the contained 45 | /// writer if the flush succeeded. 46 | /// 47 | /// Note that this function may not be suitable to call in a situation where 48 | /// the underlying stream is an asynchronous I/O stream. To finish a stream 49 | /// the `try_finish` method should be used instead. To 50 | /// re-acquire ownership of a stream it is safe to call this method after 51 | /// `try_finish` has returned `Ok`. 52 | #[inline] 53 | pub fn finish(mut self) -> io::Result { 54 | self.try_finish()?; 55 | Ok(self.0.obj.take().unwrap()) 56 | } 57 | 58 | /// Returns the number of bytes produced by the compressor 59 | /// 60 | /// Note that, due to buffering, this only bears any relation to 61 | /// `total_in()` after a call to `flush()`. At that point, 62 | /// `total_out() / total_in()` is the compression ratio. 63 | #[inline] 64 | pub fn total_out(&self) -> u64 { 65 | self.0.total_out() 66 | } 67 | 68 | /// Returns the number of bytes consumed by the compressor 69 | /// (e.g. the number of bytes written to this stream.) 70 | #[inline] 71 | pub fn total_in(&self) -> u64 { 72 | self.0.total_out() 73 | } 74 | } 75 | 76 | impl Write for AutoFinishXzEncoder { 77 | fn write(&mut self, buf: &[u8]) -> io::Result { 78 | self.0.write(buf) 79 | } 80 | 81 | fn flush(&mut self) -> io::Result<()> { 82 | self.0.flush() 83 | } 84 | } 85 | 86 | impl Drop for AutoFinishXzEncoder { 87 | #[inline] 88 | fn drop(&mut self) { 89 | if self.0.obj.is_some() { 90 | let _ = self.0.try_finish(); 91 | } 92 | } 93 | } 94 | 95 | /// A compression stream which will have compressed data written to it and 96 | /// will write uncompressed data to an output stream. 97 | /// [AutoFinishXzDecoder] impl [Drop] trait, so automatically calls [XzDecoder::try_finish] method when exiting the scope. 98 | /// However, it is not guaranteed that `try_finish` will complete successfully, and it is recommended to call `try_finish` manually if you want to ensure that the process is successful. 99 | pub struct AutoFinishXzDecoder(pub(super) XzDecoder); 100 | 101 | impl AutoFinishXzDecoder { 102 | /// Acquires a reference to the underlying writer. 103 | #[inline] 104 | pub fn get_ref(&self) -> &W { 105 | self.0.get_ref() 106 | } 107 | 108 | /// Acquires a mutable reference to the underlying writer. 109 | /// 110 | /// Note that mutating the output/input state of the stream may corrupt this 111 | /// object, so care must be taken when using this method. 112 | #[inline] 113 | pub fn get_mut(&mut self) -> &mut W { 114 | self.0.get_mut() 115 | } 116 | 117 | /// Attempt to finish this output stream, writing out final chunks of data. 118 | /// 119 | /// Note that this function can only be used once data has finished being 120 | /// written to the output stream. After this function is called then further 121 | /// calls to `write` may result in a panic. 122 | /// 123 | /// # Panics 124 | /// 125 | /// Attempts to write data to this stream may result in a panic after this 126 | /// function is called. 127 | #[inline] 128 | pub fn try_finish(&mut self) -> io::Result<()> { 129 | self.0.try_finish() 130 | } 131 | 132 | /// Consumes this decoder, flushing the output stream. 133 | /// 134 | /// This will flush the underlying data stream and then return the contained 135 | /// writer if the flush succeeded. 136 | /// 137 | /// Note that this function may not be suitable to call in a situation where 138 | /// the underlying stream is an asynchronous I/O stream. To finish a stream 139 | /// the `try_finish` method should be used instead. To 140 | /// re-acquire ownership of a stream it is safe to call this method after 141 | /// `try_finish` has returned `Ok`. 142 | #[inline] 143 | pub fn finish(mut self) -> io::Result { 144 | self.try_finish()?; 145 | Ok(self.0.obj.take().unwrap()) 146 | } 147 | 148 | /// Returns the number of bytes produced by the decompressor 149 | /// 150 | /// Note that, due to buffering, this only bears any relation to 151 | /// `total_in()` after a call to `flush()`. At that point, 152 | /// `total_in() / total_out()` is the compression ratio. 153 | #[inline] 154 | pub fn total_out(&self) -> u64 { 155 | self.0.total_out() 156 | } 157 | 158 | /// Returns the number of bytes consumed by the decompressor 159 | /// (e.g. the number of bytes written to this stream.) 160 | #[inline] 161 | pub fn total_in(&self) -> u64 { 162 | self.0.total_in() 163 | } 164 | } 165 | 166 | impl Write for AutoFinishXzDecoder { 167 | fn write(&mut self, buf: &[u8]) -> io::Result { 168 | self.0.write(buf) 169 | } 170 | 171 | fn flush(&mut self) -> io::Result<()> { 172 | self.0.flush() 173 | } 174 | } 175 | 176 | impl Drop for AutoFinishXzDecoder { 177 | #[inline] 178 | fn drop(&mut self) { 179 | if self.0.obj.is_some() { 180 | let _ = self.0.try_finish(); 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /systest/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "systest" 3 | version = "0.1.0" 4 | authors = ["Alex Crichton "] 5 | build = "build.rs" 6 | edition = "2021" 7 | publish = false 8 | 9 | [dependencies] 10 | liblzma-sys = { path = "../liblzma-sys" } 11 | libc = "0.2" 12 | 13 | [build-dependencies] 14 | ctest2 = "0.4" 15 | 16 | [features] 17 | bindgen = ["liblzma-sys/bindgen"] 18 | -------------------------------------------------------------------------------- /systest/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | fn main() { 4 | let mut cfg = ctest2::TestGenerator::new(); 5 | if let Ok(out) = env::var("DEP_LZMA_INCLUDE") { 6 | cfg.include(&out); 7 | } 8 | 9 | cfg.header("lzma.h"); 10 | cfg.type_name(|n, _s, _| n.to_string()); 11 | cfg.define("LZMA_API_STATIC", None); 12 | cfg.skip_type(|n| n == "__enum_ty"); 13 | cfg.generate("../liblzma-sys/src/lib.rs", "all.rs"); 14 | } 15 | -------------------------------------------------------------------------------- /systest/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(bad_style)] 2 | use liblzma_sys::*; 3 | 4 | include!(concat!(env!("OUT_DIR"), "/all.rs")); 5 | -------------------------------------------------------------------------------- /tests/drop-incomplete.rs: -------------------------------------------------------------------------------- 1 | use liblzma::write::XzDecoder; 2 | use std::io::prelude::*; 3 | 4 | // This is a XZ file generated by head -c10 /dev/urandom | xz -c 5 | const DATA: &'static [u8] = &[ 6 | 253, 55, 122, 88, 90, 0, 0, 4, 230, 214, 180, 70, 2, 0, 33, 1, 22, 0, 0, 0, 116, 47, 229, 163, 7 | 1, 0, 9, 7, 122, 65, 14, 253, 214, 121, 128, 230, 115, 0, 0, 0, 158, 47, 174, 196, 175, 10, 34, 8 | 254, 0, 1, 34, 10, 21, 26, 225, 103, 31, 182, 243, 125, 1, 0, 0, 0, 0, 4, 89, 90, 9 | ]; 10 | 11 | /// In this test, we drop a write::XzDecoder after supplying it a truncated input stream. 12 | /// 13 | /// The decoder should detect that it is impossible to decode more data and not 14 | /// go into an infinite loop waiting for more data. 15 | #[test] 16 | fn drop_writer_incomplete_input_no_loop() { 17 | let mut decoder = XzDecoder::new(Vec::new()); 18 | const PREFIX_LEN: usize = 50; 19 | decoder.write_all(&DATA[..PREFIX_LEN]).unwrap(); 20 | } 21 | 22 | /// Same as above, but verifying that we get an error if we manually call `finish`; 23 | #[test] 24 | fn finish_writer_incomplete_input_error() { 25 | let mut decoder = XzDecoder::new(Vec::new()); 26 | const PREFIX_LEN: usize = 50; 27 | decoder.write_all(&DATA[..PREFIX_LEN]).unwrap(); 28 | decoder 29 | .finish() 30 | .err() 31 | .expect("finish should error because of incomplete input"); 32 | } 33 | -------------------------------------------------------------------------------- /tests/xz.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::prelude::*; 3 | use std::path::Path; 4 | 5 | use liblzma::read; 6 | use liblzma::stream; 7 | use liblzma::write; 8 | 9 | #[test] 10 | fn standard_files() { 11 | for file in Path::new("liblzma-sys/xz/tests/files").read_dir().unwrap() { 12 | let file = file.unwrap(); 13 | if file.path().extension().and_then(|s| s.to_str()) != Some("xz") { 14 | continue; 15 | } 16 | 17 | let filename = file.file_name().into_string().unwrap(); 18 | 19 | // This appears to be implementation-defined how it's handled 20 | if filename.contains("unsupported-check") { 21 | continue; 22 | } 23 | 24 | println!("testing {:?}", file.path()); 25 | let mut contents = Vec::new(); 26 | File::open(&file.path()) 27 | .unwrap() 28 | .read_to_end(&mut contents) 29 | .unwrap(); 30 | if filename.starts_with("bad") || filename.starts_with("unsupported") { 31 | test_bad(&contents); 32 | } else { 33 | test_good(&contents); 34 | } 35 | } 36 | } 37 | 38 | fn test_good(data: &[u8]) { 39 | let mut ret = Vec::new(); 40 | read::XzDecoder::new_multi_decoder(data) 41 | .read_to_end(&mut ret) 42 | .unwrap(); 43 | let mut w = write::XzDecoder::new_multi_decoder(ret); 44 | w.write_all(data).unwrap(); 45 | w.finish().unwrap(); 46 | } 47 | 48 | fn test_bad(data: &[u8]) { 49 | let mut ret = Vec::new(); 50 | assert!(read::XzDecoder::new(data).read_to_end(&mut ret).is_err()); 51 | let mut w = write::XzDecoder::new(ret); 52 | assert!(w.write_all(data).is_err() || w.finish().is_err()); 53 | } 54 | 55 | fn assert_send_sync() {} 56 | 57 | #[test] 58 | fn impls_send_and_sync() { 59 | assert_send_sync::(); 60 | assert_send_sync::>(); 61 | assert_send_sync::>(); 62 | assert_send_sync::>(); 63 | assert_send_sync::>(); 64 | } 65 | --------------------------------------------------------------------------------