zopfli-0.8.1/.cargo_vcs_info.json 0000644 00000000136 00000000001 0012345 0 ustar {
"git": {
"sha1": "95946a0e94e88a882b3eb80d2eaa576904096d96"
},
"path_in_vcs": ""
} zopfli-0.8.1/CONTRIBUTORS 0000644 0000000 0000000 00000000211 10461020230 0013007 0 ustar 0000000 0000000 Mark Adler
Jyrki Alakuijala
Frédéric Kayser
Jeffrey Lim
Daniel Reed
Huzaifa Sidhpurwala
Péter Szabó
Lode Vandevenne
Derek Buitenhuis
zopfli-0.8.1/COPYING 0000644 0000000 0000000 00000026115 10461020230 0012175 0 ustar 0000000 0000000 Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2011 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
zopfli-0.8.1/Cargo.lock 0000644 00000026440 00000000001 0010326 0 ustar # This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "autocfg"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "bit-set"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
dependencies = [
"bit-vec",
]
[[package]]
name = "bit-vec"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
[[package]]
name = "bitflags"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]]
name = "bumpalo"
version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "crc32fast"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa"
dependencies = [
"cfg-if",
]
[[package]]
name = "errno"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "fastrand"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "getrandom"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "libm"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
[[package]]
name = "linux-raw-sys"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]]
name = "lockfree-object-pool"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a69c0481fc2424cb55795de7da41add33372ea75a94f9b6588ab6a2826dfebc"
[[package]]
name = "log"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "miniz_oxide"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae"
dependencies = [
"adler",
]
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
"libm",
]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "proptest"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf"
dependencies = [
"bit-set",
"bit-vec",
"bitflags",
"lazy_static",
"num-traits",
"rand",
"rand_chacha",
"rand_xorshift",
"regex-syntax",
"rusty-fork",
"tempfile",
"unarray",
]
[[package]]
name = "proptest-derive"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cf16337405ca084e9c78985114633b6827711d22b9e6ef6c6c0d665eb3f0b6e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "quick-error"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quote"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "rand_xorshift"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
dependencies = [
"rand_core",
]
[[package]]
name = "regex-syntax"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
[[package]]
name = "rustix"
version = "0.38.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "rusty-fork"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f"
dependencies = [
"fnv",
"quick-error",
"tempfile",
"wait-timeout",
]
[[package]]
name = "simd-adler32"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tempfile"
version = "3.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
dependencies = [
"cfg-if",
"fastrand",
"rustix",
"windows-sys",
]
[[package]]
name = "unarray"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "wait-timeout"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
dependencies = [
"libc",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
[[package]]
name = "windows_i686_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
[[package]]
name = "windows_i686_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
[[package]]
name = "zopfli"
version = "0.8.1"
dependencies = [
"bumpalo",
"crc32fast",
"lockfree-object-pool",
"log",
"miniz_oxide",
"once_cell",
"proptest",
"proptest-derive",
"simd-adler32",
]
zopfli-0.8.1/Cargo.toml 0000644 00000004116 00000000001 0010345 0 ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.73.0"
name = "zopfli"
version = "0.8.1"
build = false
exclude = [
".github/*",
".gitignore",
"Makefile",
"benchmark-builds/*",
"rustfmt.toml",
"test/*",
]
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "A Rust implementation of the Zopfli compression algorithm."
homepage = "https://github.com/zopfli-rs/zopfli"
readme = "README.md"
keywords = ["compression"]
categories = [
"compression",
"no-std",
]
license = "Apache-2.0"
repository = "https://github.com/zopfli-rs/zopfli"
[package.metadata.docs.rs]
cargo-args = ["--all-features"]
[profile.release]
debug = 2
[lib]
name = "zopfli"
path = "src/lib.rs"
[[bin]]
name = "zopfli"
path = "src/main.rs"
required-features = [
"std",
"gzip",
"zlib",
]
[dependencies.bumpalo]
version = "3.16.0"
[dependencies.crc32fast]
version = "1.4.0"
optional = true
default-features = false
[dependencies.lockfree-object-pool]
version = "0.1.5"
optional = true
[dependencies.log]
version = "0.4.21"
optional = true
[dependencies.once_cell]
version = "1.19.0"
optional = true
[dependencies.simd-adler32]
version = "0.3.7"
optional = true
default-features = false
[dev-dependencies.miniz_oxide]
version = "0.7.3"
[dev-dependencies.proptest]
version = "1.4.0"
[dev-dependencies.proptest-derive]
version = "0.4.0"
[features]
default = [
"std",
"gzip",
"zlib",
]
gzip = ["dep:crc32fast"]
nightly = ["crc32fast?/nightly"]
std = [
"dep:log",
"dep:lockfree-object-pool",
"dep:once_cell",
"crc32fast?/std",
"simd-adler32?/std",
]
zlib = ["dep:simd-adler32"]
zopfli-0.8.1/Cargo.toml.orig 0000644 0000000 0000000 00000003103 10461020230 0014021 0 ustar 0000000 0000000 # The binary target must be set in a single line like this to make it easily
# removable by the CI "Check effective no_std compatibility" step
bin = [{ name = "zopfli", required-features = ["std", "gzip", "zlib"] }]
[package]
name = "zopfli"
version = "0.8.1"
description = "A Rust implementation of the Zopfli compression algorithm."
license = "Apache-2.0"
keywords = ["compression"]
homepage = "https://github.com/zopfli-rs/zopfli"
repository = "https://github.com/zopfli-rs/zopfli"
readme = "README.md"
categories = ["compression", "no-std"]
exclude = [
".github/*",
".gitignore",
"Makefile",
"benchmark-builds/*",
"rustfmt.toml",
"test/*",
]
edition = "2021"
rust-version = "1.73.0"
[dependencies]
crc32fast = { version = "1.4.0", default-features = false, optional = true }
simd-adler32 = { version = "0.3.7", default-features = false, optional = true }
bumpalo = "3.16.0"
log = { version = "0.4.21", optional = true }
lockfree-object-pool = { version = "0.1.5", optional = true }
once_cell = { version = "1.19.0", optional = true }
[dev-dependencies]
proptest = "1.4.0"
proptest-derive = "0.4.0"
miniz_oxide = "0.7.3"
[features]
default = ["std", "gzip", "zlib"]
gzip = ["dep:crc32fast"]
zlib = ["dep:simd-adler32"]
std = [
"dep:log",
"dep:lockfree-object-pool",
"dep:once_cell",
"crc32fast?/std",
"simd-adler32?/std",
]
nightly = ["crc32fast?/nightly"]
[profile.release]
debug = true
# docs.rs uses a nightly toolchain, so it can leverage unstable rustdoc features.
# Reference: https://docs.rs/about/builds
[package.metadata.docs.rs]
cargo-args = ["--all-features"]
zopfli-0.8.1/README.md 0000644 0000000 0000000 00000004276 10461020230 0012425 0 ustar 0000000 0000000
Zopfli in Rust
This is a reimplementation of the [Zopfli](https://github.com/google/zopfli) compression tool in Rust.
Carol Nichols started the Rust implementation as an experiment in incrementally rewriting a C library in Rust, keeping the project compiling at every step. For more information about that experiment, see [the slides for a talk she gave about it](https://github.com/carols10cents/rust-out-your-c-talk) and [the repo as it was for the experiment](https://github.com/carols10cents/zopfli).
The minimum supported Rust version (MSRV) for this crate is 1.73. Bumping this version is not considered a breaking change for semantic versioning purposes. We will try to do it only when we estimate that such a bump would not cause widespread inconvenience or breakage.
## How to build
To build the code, run:
```
$ cargo build --release
```
and the executable will be in `target/release/zopfli`.
This should work on stable or beta Rust.
You can also run `make zopfli`, which will run `cargo build` and then symlink `target/release/zopfli` to just `zopfli` in the project root; this is what the C library does and it was useful for scripting purposes during the rewrite process to keep the command and resulting artifacts the same.
## Running the tests
There are some unit tests, mostly around the boundary package merge algorithm implementation in katajainen.rs, and a property-based test for compression reversibility. These tests can be run with:
```
$ cargo test
```
Golden master tests, to check that compressed files are exactly the same as the C implementation would generate, can be run using:
```
$ ./test/run.sh
```
and then checking that git reports no changes to the files in `test/results`.
Or you can run `make test`, which will run `cargo test` followed by `./test/run.sh`; it will fail if there are any mismatches.
zopfli-0.8.1/src/blocksplitter.rs 0000644 0000000 0000000 00000017712 10461020230 0015163 0 ustar 0000000 0000000 use alloc::vec::Vec;
#[cfg(feature = "std")]
use log::{debug, log_enabled};
use crate::{cache::NoCache, deflate::calculate_block_size_auto_type, lz77::Lz77Store};
/// Finds minimum of function `f(i)` where `i` is of type `usize`, `f(i)` is of type
/// `f64`, `i` is in range `start-end` (excluding `end`).
/// Returns the index to the minimum and the minimum value.
fn find_minimum f64>(f: F, start: usize, end: usize) -> (usize, f64) {
if end - start < 1024 {
let mut best = f64::INFINITY;
let mut result = start;
for i in start..end {
let v = f(i);
if v < best {
best = v;
result = i;
}
}
(result, best)
} else {
/* Try to find minimum faster by recursively checking multiple points. */
let mut start = start;
let mut end = end;
const NUM: usize = 9; /* Good value: 9. ?!?!?!?! */
let mut p = [0; NUM];
let mut vp = [0.0; NUM];
let mut lastbest = f64::INFINITY;
let mut pos = start;
while end - start > NUM {
let mut besti = 0;
let mut best = f64::INFINITY;
let multiplier = (end - start) / (NUM + 1);
for i in 0..NUM {
p[i] = start + (i + 1) * multiplier;
vp[i] = f(p[i]);
if vp[i] < best {
best = vp[i];
besti = i;
}
}
if best > lastbest {
break;
}
start = if besti == 0 { start } else { p[besti - 1] };
end = if besti == NUM - 1 { end } else { p[besti + 1] };
pos = p[besti];
lastbest = best;
}
(pos, lastbest)
}
}
/// Returns estimated cost of a block in bits. It includes the size to encode the
/// tree and the size to encode all literal, length and distance symbols and their
/// extra bits.
///
/// litlens: lz77 lit/lengths
/// dists: ll77 distances
/// lstart: start of block
/// lend: end of block (not inclusive)
fn estimate_cost(lz77: &Lz77Store, lstart: usize, lend: usize) -> f64 {
calculate_block_size_auto_type(lz77, lstart, lend)
}
/// Finds next block to try to split, the largest of the available ones.
/// The largest is chosen to make sure that if only a limited amount of blocks is
/// requested, their sizes are spread evenly.
/// lz77size: the size of the LL77 data, which is the size of the done array here.
/// done: array indicating which blocks starting at that position are no longer
/// splittable (splitting them increases rather than decreases cost).
/// splitpoints: the splitpoints found so far.
/// npoints: the amount of splitpoints found so far.
/// lstart: output variable, giving start of block.
/// lend: output variable, giving end of block.
/// returns 1 if a block was found, 0 if no block found (all are done).
fn find_largest_splittable_block(
lz77size: usize,
done: &[u8],
splitpoints: &[usize],
) -> Option<(usize, usize)> {
let mut longest = 0;
let mut found = None;
let mut last = 0;
for &item in splitpoints.iter() {
if done[last] == 0 && item - last > longest {
found = Some((last, item));
longest = item - last;
}
last = item;
}
let end = lz77size - 1;
if done[last] == 0 && end - last > longest {
found = Some((last, end));
}
found
}
/// Prints the block split points as decimal and hex values in the terminal.
#[inline]
fn print_block_split_points(lz77: &Lz77Store, lz77splitpoints: &[usize]) {
if !log_enabled!(log::Level::Debug) {
return;
}
let nlz77points = lz77splitpoints.len();
let mut splitpoints = Vec::with_capacity(nlz77points);
/* The input is given as lz77 indices, but we want to see the uncompressed
index values. */
let mut pos = 0;
if nlz77points > 0 {
for (i, item) in lz77.litlens.iter().enumerate() {
let length = item.size();
if lz77splitpoints[splitpoints.len()] == i {
splitpoints.push(pos);
if splitpoints.len() == nlz77points {
break;
}
}
pos += length;
}
}
debug_assert_eq!(splitpoints.len(), nlz77points);
debug!(
"block split points: {} (hex: {})",
splitpoints
.iter()
.map(|&sp| format!("{}", sp))
.collect::>()
.join(" "),
splitpoints
.iter()
.map(|&sp| format!("{:x}", sp))
.collect::>()
.join(" ")
);
}
/// Does blocksplitting on LZ77 data.
/// The output splitpoints are indices in the LZ77 data.
/// maxblocks: set a limit to the amount of blocks. Set to 0 to mean no limit.
pub fn blocksplit_lz77(lz77: &Lz77Store, maxblocks: u16, splitpoints: &mut Vec) {
if lz77.size() < 10 {
return; /* This code fails on tiny files. */
}
let mut numblocks = 1u32;
let mut done = vec![0; lz77.size()];
let mut lstart = 0;
let mut lend = lz77.size();
while maxblocks != 0 && numblocks < maxblocks as u32 {
debug_assert!(lstart < lend);
let find_minimum_result = find_minimum(
|i| estimate_cost(lz77, lstart, i) + estimate_cost(lz77, i, lend),
lstart + 1,
lend,
);
let llpos = find_minimum_result.0;
let splitcost = find_minimum_result.1;
debug_assert!(llpos > lstart);
debug_assert!(llpos < lend);
let origcost = estimate_cost(lz77, lstart, lend);
if splitcost > origcost || llpos == lstart + 1 || llpos == lend {
done[lstart] = 1;
} else {
splitpoints.push(llpos);
splitpoints.sort();
numblocks += 1;
}
// If `find_largest_splittable_block` returns `None`, no further split will
// likely reduce compression.
let is_finished = find_largest_splittable_block(lz77.size(), &done, splitpoints).map_or(
true,
|(start, end)| {
lstart = start;
lend = end;
lend - lstart < 10
},
);
if is_finished {
break;
}
}
print_block_split_points(lz77, splitpoints);
}
/// Does blocksplitting on uncompressed data.
/// The output splitpoints are indices in the uncompressed bytes.
///
/// options: general program options.
/// in: uncompressed input data
/// instart: where to start splitting
/// inend: where to end splitting (not inclusive)
/// maxblocks: maximum amount of blocks to split into, or 0 for no limit
/// splitpoints: dynamic array to put the resulting split point coordinates into.
/// The coordinates are indices in the input array.
/// npoints: pointer to amount of splitpoints, for the dynamic array. The amount of
/// blocks is the amount of splitpoitns + 1.
pub fn blocksplit(
in_data: &[u8],
instart: usize,
inend: usize,
maxblocks: u16,
splitpoints: &mut Vec,
) {
splitpoints.clear();
let mut store = Lz77Store::new();
/* Unintuitively, Using a simple LZ77 method here instead of lz77_optimal
results in better blocks. */
{
store.greedy(&mut NoCache, in_data, instart, inend);
}
let mut lz77splitpoints = Vec::with_capacity(maxblocks as usize);
blocksplit_lz77(&store, maxblocks, &mut lz77splitpoints);
let nlz77points = lz77splitpoints.len();
/* Convert LZ77 positions to positions in the uncompressed input. */
let mut pos = instart;
if nlz77points > 0 {
for (i, item) in store.litlens.iter().enumerate() {
let length = item.size();
if lz77splitpoints[splitpoints.len()] == i {
splitpoints.push(pos);
if splitpoints.len() == nlz77points {
break;
}
}
pos += length;
}
}
debug_assert_eq!(splitpoints.len(), nlz77points);
}
zopfli-0.8.1/src/cache.rs 0000644 0000000 0000000 00000017225 10461020230 0013344 0 ustar 0000000 0000000 use alloc::vec::Vec;
use core::cmp;
use crate::{
lz77::LongestMatch,
util::{ZOPFLI_CACHE_LENGTH, ZOPFLI_MAX_MATCH, ZOPFLI_MIN_MATCH},
};
// Cache used by ZopfliFindLongestMatch to remember previously found length/dist
// values.
// This is needed because the squeeze runs will ask these values multiple times for
// the same position.
// Uses large amounts of memory, since it has to remember the distance belonging
// to every possible shorter-than-the-best length (the so called "sublen" array).
pub struct ZopfliLongestMatchCache {
length: Vec,
dist: Vec,
sublen: Vec,
}
impl ZopfliLongestMatchCache {
pub fn new(blocksize: usize) -> ZopfliLongestMatchCache {
ZopfliLongestMatchCache {
/* length > 0 and dist 0 is invalid combination, which indicates on purpose
that this cache value is not filled in yet. */
length: vec![1; blocksize],
dist: vec![0; blocksize],
/* Rather large amount of memory. */
sublen: vec![0; ZOPFLI_CACHE_LENGTH * blocksize * 3],
}
}
fn length_at(&self, pos: usize) -> u16 {
self.length[pos]
}
fn dist_at(&self, pos: usize) -> u16 {
self.dist[pos]
}
fn store_length_at(&mut self, pos: usize, val: u16) {
self.length[pos] = val;
}
fn store_dist_at(&mut self, pos: usize, val: u16) {
self.dist[pos] = val;
}
/// Returns the length up to which could be stored in the cache.
fn max_sublen(&self, pos: usize) -> u32 {
let start = ZOPFLI_CACHE_LENGTH * pos * 3;
if self.sublen[start + 1] == 0 && self.sublen[start + 2] == 0 {
return 0; // No sublen cached.
}
self.sublen[start + ((ZOPFLI_CACHE_LENGTH - 1) * 3)] as u32 + 3
}
/// Stores sublen array in the cache.
fn store_sublen(&mut self, sublen: &[u16], pos: usize, length: usize) {
if length < 3 {
return;
}
let start = ZOPFLI_CACHE_LENGTH * pos * 3;
let mut i = 3;
let mut j = 0;
let mut bestlength = 0;
while i <= length {
if i == length || sublen[i] != sublen[i + 1] {
self.sublen[start + (j * 3)] = (i - 3) as u8;
self.sublen[start + (j * 3 + 1)] = sublen[i].wrapping_rem(256) as u8;
self.sublen[start + (j * 3 + 2)] = (sublen[i] >> 8).wrapping_rem(256) as u8;
bestlength = i as u32;
j += 1;
if j >= ZOPFLI_CACHE_LENGTH {
break;
}
}
i += 1;
}
if j < ZOPFLI_CACHE_LENGTH {
debug_assert_eq!(bestlength, length as u32);
self.sublen[start + ((ZOPFLI_CACHE_LENGTH - 1) * 3)] = (bestlength - 3) as u8;
} else {
debug_assert!(bestlength <= length as u32);
}
debug_assert_eq!(bestlength, self.max_sublen(pos));
}
/// Extracts sublen array from the cache.
fn fetch_sublen(&self, pos: usize, length: usize, sublen: &mut [u16]) {
if length < 3 {
return;
}
let start = ZOPFLI_CACHE_LENGTH * pos * 3;
let maxlength = self.max_sublen(pos) as usize;
let mut prevlength = 0;
for j in 0..ZOPFLI_CACHE_LENGTH {
let length = self.sublen[start + (j * 3)] as usize + 3;
let dist = self.sublen[start + (j * 3 + 1)] as u16
+ 256 * self.sublen[start + (j * 3 + 2)] as u16;
let mut i = prevlength;
while i <= length {
sublen[i] = dist;
i += 1;
}
if length == maxlength {
break;
}
prevlength = length + 1;
}
}
}
pub trait Cache {
fn try_get(
&self,
pos: usize,
limit: usize,
sublen: &mut Option<&mut [u16]>,
blockstart: usize,
) -> LongestMatch;
fn store(
&mut self,
pos: usize,
limit: usize,
sublen: &mut Option<&mut [u16]>,
distance: u16,
length: u16,
blockstart: usize,
);
}
pub struct NoCache;
impl Cache for NoCache {
fn try_get(
&self,
_: usize,
limit: usize,
_: &mut Option<&mut [u16]>,
_: usize,
) -> LongestMatch {
LongestMatch::new(limit)
}
fn store(&mut self, _: usize, _: usize, _: &mut Option<&mut [u16]>, _: u16, _: u16, _: usize) {
// Nowhere to store
}
}
impl Cache for ZopfliLongestMatchCache {
fn try_get(
&self,
pos: usize,
mut limit: usize,
sublen: &mut Option<&mut [u16]>,
blockstart: usize,
) -> LongestMatch {
let mut longest_match = LongestMatch::new(limit);
/* The LMC cache starts at the beginning of the block rather than the
beginning of the whole array. */
let lmcpos = pos - blockstart;
/* Length > 0 and dist 0 is invalid combination, which indicates on purpose
that this cache value is not filled in yet. */
let length_lmcpos = self.length_at(lmcpos);
let dist_lmcpos = self.dist_at(lmcpos);
let cache_available = length_lmcpos == 0 || dist_lmcpos != 0;
let max_sublen = self.max_sublen(lmcpos);
let limit_ok_for_cache = limit == ZOPFLI_MAX_MATCH
|| length_lmcpos <= limit as u16
|| (sublen.is_some() && max_sublen >= limit as u32);
if limit_ok_for_cache && cache_available {
if sublen.is_none() || length_lmcpos as u32 <= max_sublen {
let length = cmp::min(length_lmcpos, limit as u16);
let distance;
if let Some(ref mut subl) = *sublen {
self.fetch_sublen(lmcpos, length as usize, subl);
distance = subl[length as usize];
if limit == ZOPFLI_MAX_MATCH && length >= ZOPFLI_MIN_MATCH as u16 {
debug_assert_eq!(subl[length as usize], dist_lmcpos);
}
} else {
distance = dist_lmcpos;
}
longest_match.distance = distance;
longest_match.length = length;
longest_match.from_cache = true;
return longest_match;
}
/* Can't use much of the cache, since the "sublens" need to be calculated,
but at least we already know when to stop. */
limit = length_lmcpos as usize;
longest_match.limit = limit;
}
longest_match
}
fn store(
&mut self,
pos: usize,
limit: usize,
sublen: &mut Option<&mut [u16]>,
distance: u16,
length: u16,
blockstart: usize,
) {
if let Some(ref mut subl) = *sublen {
/* Length > 0 and dist 0 is invalid combination, which indicates on purpose
that this cache value is not filled in yet. */
let lmcpos = pos - blockstart;
let cache_available = self.length_at(lmcpos) == 0 || self.dist_at(lmcpos) != 0;
if limit == ZOPFLI_MAX_MATCH && !cache_available {
debug_assert!(self.length_at(lmcpos) == 1 && self.dist_at(lmcpos) == 0);
if length < ZOPFLI_MIN_MATCH as u16 {
self.store_dist_at(lmcpos, 0);
self.store_length_at(lmcpos, 0);
} else {
self.store_dist_at(lmcpos, distance);
self.store_length_at(lmcpos, length);
}
debug_assert!(!(self.length_at(lmcpos) == 1 && self.dist_at(lmcpos) == 0));
self.store_sublen(subl, lmcpos, length as usize);
}
}
}
}
zopfli-0.8.1/src/deflate.rs 0000644 0000000 0000000 00000132277 10461020230 0013712 0 ustar 0000000 0000000 use alloc::vec::Vec;
use core::{cmp, iter};
#[cfg(feature = "std")]
use log::{debug, log_enabled};
use crate::{
blocksplitter::{blocksplit, blocksplit_lz77},
cache::ZopfliLongestMatchCache,
iter::ToFlagLastIterator,
katajainen::length_limited_code_lengths,
lz77::{LitLen, Lz77Store},
squeeze::{lz77_optimal, lz77_optimal_fixed},
symbols::{
get_dist_extra_bits, get_dist_extra_bits_value, get_dist_symbol,
get_dist_symbol_extra_bits, get_length_extra_bits, get_length_extra_bits_value,
get_length_symbol, get_length_symbol_extra_bits,
},
tree::lengths_to_symbols,
util::{ZOPFLI_NUM_D, ZOPFLI_NUM_LL, ZOPFLI_WINDOW_SIZE},
Error, Options, Write,
};
/// A DEFLATE encoder powered by the Zopfli algorithm that compresses data written
/// to it to the specified sink. Most users will find using [`compress`](crate::compress)
/// easier and more performant.
///
/// The data will be compressed as soon as possible, without trying to fill a
/// backreference window. As a consequence, frequent short writes may cause more
/// DEFLATE blocks to be emitted with less optimal Huffman trees, which can hurt
/// compression and runtime. If they are a concern, short writes can be conveniently
/// dealt with by wrapping this encoder with a [`BufWriter`](std::io::BufWriter), as done
/// by the [`new_buffered`](DeflateEncoder::new_buffered) method. An adequate write size
/// would be >32 KiB, which allows the second complete chunk to leverage a full-sized
/// backreference window.
pub struct DeflateEncoder {
options: Options,
btype: BlockType,
have_chunk: bool,
chunk_start: usize,
window_and_chunk: Vec,
bitwise_writer: Option>,
}
impl DeflateEncoder {
/// Creates a new Zopfli DEFLATE encoder that will operate according to the
/// specified options.
pub fn new(options: Options, btype: BlockType, sink: W) -> Self {
DeflateEncoder {
options,
btype,
have_chunk: false,
chunk_start: 0,
window_and_chunk: Vec::with_capacity(ZOPFLI_WINDOW_SIZE),
bitwise_writer: Some(BitwiseWriter::new(sink)),
}
}
/// Creates a new Zopfli DEFLATE encoder that operates according to the
/// specified options and is wrapped with a buffer to guarantee that
/// data is compressed in large chunks, which is necessary for decent
/// performance and good compression ratio.
#[cfg(feature = "std")]
pub fn new_buffered(options: Options, btype: BlockType, sink: W) -> std::io::BufWriter {
std::io::BufWriter::with_capacity(
crate::util::ZOPFLI_MASTER_BLOCK_SIZE,
Self::new(options, btype, sink),
)
}
/// Encodes any pending chunks of data and writes them to the sink,
/// consuming the encoder and returning the wrapped sink. The sink
/// will have received a complete DEFLATE stream when this method
/// returns.
///
/// The encoder is automatically [`finish`](Self::finish)ed when
/// dropped, but explicitly finishing it with this method allows
/// handling I/O errors.
pub fn finish(mut self) -> Result {
self._finish().map(|sink| sink.unwrap())
}
/// Compresses the chunk stored at `window_and_chunk`. This includes
/// a rolling window of the last `ZOPFLI_WINDOW_SIZE` data bytes, if
/// available.
#[inline]
fn compress_chunk(&mut self, is_last: bool) -> Result<(), Error> {
deflate_part(
&self.options,
self.btype,
is_last,
&self.window_and_chunk,
self.chunk_start,
self.window_and_chunk.len(),
self.bitwise_writer.as_mut().unwrap(),
)
}
/// Sets the next chunk that will be compressed by the next
/// call to `compress_chunk` and updates the rolling data window
/// accordingly.
fn set_chunk(&mut self, chunk: &[u8]) {
// Remove bytes exceeding the window size. Start with the
// oldest bytes, which are at the beginning of the buffer.
// The buffer length is then the position where the chunk
// we've just received starts
self.window_and_chunk.drain(
..self
.window_and_chunk
.len()
.saturating_sub(ZOPFLI_WINDOW_SIZE),
);
self.chunk_start = self.window_and_chunk.len();
self.window_and_chunk.extend_from_slice(chunk);
self.have_chunk = true;
}
/// Encodes the last chunk and finishes any partial bits.
/// The encoder will be unusable for further compression
/// after this method returns. This is intended to be an
/// implementation detail of the `Drop` trait and
/// [`finish`](Self::finish) method.
fn _finish(&mut self) -> Result