summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2024-04-30 21:58:01 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2024-04-30 21:58:01 +0000
commit42e68feabc58f5b7581116be7adc094d44008b06 (patch)
treead4c8da28e495ff4c306bc3b4cca6070dbe3e1a3
parent99e3830764a0cb61a685e5e5b3167864c3136fb8 (diff)
parentf10f8c0908a2b10af984faa51bc854443f854132 (diff)
downloadzlib-build-tools-release.tar.gz
Merge "Snap for 11784721 from b34cd2e4371556e913ad9801a924a891c74d1463 to build-tools-release" into build-tools-releasebuild-tools-release
-rw-r--r--Android.bp41
-rw-r--r--BUILD.bazel22
-rw-r--r--BUILD.gn30
-rw-r--r--CMakeLists.txt37
-rw-r--r--METADATA6
-rw-r--r--adler32_simd.c166
-rw-r--r--contrib/optimizations/chunkcopy.h75
-rw-r--r--contrib/tests/utils_unittest.cc24
-rw-r--r--examples/zpipe.c209
-rw-r--r--google/compression_utils.cc1
-rw-r--r--google/zip_reader_unittest.cc8
-rw-r--r--test/minigzip.c579
12 files changed, 1061 insertions, 137 deletions
diff --git a/Android.bp b/Android.bp
index a8ff866..a6d27d1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -35,20 +35,25 @@ cflags_shared = [
cflags_arm = [
// Even the NDK dropped non-neon support in r24.
"-DADLER32_SIMD_NEON",
- // TODO: causes `atest org.apache.harmony.tests.java.util.zip.DeflaterTest` failures.
- // "-DINFLATE_CHUNK_SIMD_NEON",
- // HWCAP_CRC32 is checked at runtime, so it's okay to turn crc32
- // acceleration on for both 32- and 64-bit.
+ // HWCAP_CRC32 is checked at runtime, so it's okay to enable crc32
+ // acceleration for both 64-bit and 32-bit (which may be armv7, at
+ // least for NDK users).
"-DCRC32_ARMV8_CRC32",
+ // TODO: DINFLATE_CHUNK_SIMD_NEON causes `atest org.apache.harmony.tests.java.util.zip.DeflaterTest` failures.
+ // "-DINFLATE_CHUNK_SIMD_NEON",
+]
+cflags_arm64 = cflags_arm + ["-DINFLATE_CHUNK_READ_64LE"]
+
+cflags_riscv64 = [
+ // TODO: test and enable these.
+ // "-DRISCV_RVV",
+ // "-DADLER32_SIMD_RVV",
]
-cflags_arm64 = cflags_arm
// The *host* x86 configuration (with *lower* CPU feature requirements).
cflags_x86 = [
// See ARMV8_OS_LINUX above.
"-DX86_NOT_WINDOWS",
- // TODO: see arm above.
- // "-DINFLATE_CHUNK_SIMD_SSE2",
// Android's host CPU feature requirements are *lower* than the
// corresponding device CPU feature requirements, so it's easier to just
// say "no SIMD for you" rather than specificially disable SSSE3.
@@ -57,16 +62,18 @@ cflags_x86 = [
// https://issuetracker.google.com/171235570
"-DCPU_NO_SIMD",
]
-// The *device* x86 configuration (with *higher* CPU feature requirements).
+cflags_x86_64 = cflags_x86 + ["-DINFLATE_CHUNK_READ_64LE"]
+
+// The additional *device* x86/x86_64 configuration. Devices have *higher* CPU
+// feature requirements than the host.
cflags_android_x86 = [
- // Android's x86/x86-64 ABI includes SSE2 and SSSE3.
+ // Android's x86 and x86-64 ABIs both include SSE2 and SSSE3.
"-UCPU_NO_SIMD",
"-DADLER32_SIMD_SSSE3",
+ // TODO: DINFLATE_CHUNK_SIMD_SSE2 causes `atest org.apache.harmony.tests.java.util.zip.DeflaterTest` failures.
+ // "-DINFLATE_CHUNK_SIMD_SSE2",
]
-// This optimization is applicable to arm64 and x86-64.
-cflags_64 = ["-DINFLATE_CHUNK_READ_64LE"]
-
libz_srcs = [
"adler32.c",
"adler32_simd.c",
@@ -121,13 +128,16 @@ cc_defaults {
cflags: cflags_arm,
},
arm64: {
- cflags: cflags_arm64 + cflags_64,
+ cflags: cflags_arm64,
+ },
+ riscv64: {
+ cflags: cflags_riscv64,
},
x86: {
cflags: cflags_x86,
},
x86_64: {
- cflags: cflags_x86 + cflags_64,
+ cflags: cflags_x86_64,
},
},
target: {
@@ -235,6 +245,9 @@ cc_library {
"//bootable/recovery/applypatch",
"//bootable/recovery/tests",
"//bootable/recovery/updater",
+ "//bootable/deprecated-ota/applypatch",
+ "//bootable/deprecated-ota/tests",
+ "//bootable/deprecated-ota/updater",
],
// We only use the shared flags here; the whole point is that this
// library behaves the same on all different architectures.
diff --git a/BUILD.bazel b/BUILD.bazel
deleted file mode 100644
index f0fc0f9..0000000
--- a/BUILD.bazel
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright (C) 2024 The Android Open Source Project
-#
-# 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.
-
-load("@rules_pkg//pkg:mappings.bzl", "pkg_files")
-
-pkg_files(
- name = "test_mappings",
- srcs = ["TEST_MAPPING"],
- prefix = package_name(),
- visibility = ["//kernel/tests/test_mappings:__pkg__"],
-)
diff --git a/BUILD.gn b/BUILD.gn
index 7fff576..f97ab45 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -441,6 +441,36 @@ executable("zlib_bench") {
configs += [ "//build/config/compiler:no_chromium_code" ]
}
+executable("minigzip") {
+ include_dirs = [ "." ]
+
+ sources = [ "test/minigzip.c" ]
+ if (!is_debug) {
+ configs -= [ "//build/config/compiler:default_optimization" ]
+ configs += [ "//build/config/compiler:optimize_speed" ]
+ }
+
+ deps = [ ":zlib" ]
+
+ configs -= [ "//build/config/compiler:chromium_code" ]
+ configs += [ "//build/config/compiler:no_chromium_code" ]
+}
+
+executable("zpipe") {
+ include_dirs = [ "." ]
+
+ sources = [ "examples/zpipe.c" ]
+ if (!is_debug) {
+ configs -= [ "//build/config/compiler:default_optimization" ]
+ configs += [ "//build/config/compiler:optimize_speed" ]
+ }
+
+ deps = [ ":zlib" ]
+
+ configs -= [ "//build/config/compiler:chromium_code" ]
+ configs += [ "//build/config/compiler:no_chromium_code" ]
+}
+
if (!is_win || target_os != "winuwp") {
executable("minizip_bin") {
include_dirs = [ "." ]
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c3f4247..66f7d04 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -26,6 +26,8 @@ option(ENABLE_SIMD_AVX512 "Enable SIMD AXV512 optimizations" OFF)
option(USE_ZLIB_RABIN_KARP_HASH "Enable bitstream compatibility with canonical zlib" OFF)
option(BUILD_UNITTESTS "Enable standalone unit tests build" OFF)
option(BUILD_MINIZIP_BIN "Enable building minzip_bin tool" OFF)
+option(BUILD_ZPIPE "Enable building zpipe tool" OFF)
+option(BUILD_MINIGZIP "Enable building minigzip tool" OFF)
if (USE_ZLIB_RABIN_KARP_HASH)
add_definitions(-DUSE_ZLIB_RABIN_KARP_ROLLING_HASH)
@@ -79,9 +81,16 @@ if (ENABLE_SIMD_OPTIMIZATIONS)
add_definitions(-DRISCV_RVV)
add_definitions(-DDEFLATE_SLIDE_HASH_RVV)
add_definitions(-DADLER32_SIMD_RVV)
- #TODO(cavalcantii): add remaining flags as we port optimizations to RVV.
- # Required by CPU features detection code.
- SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --target=riscv64-unknown-linux-gnu -march=rv64gcv")
+
+ # TODO(cavalcantii): add remaining flags as we port optimizations to RVV.
+ # chunk_copy is required for READ64 and unconditional decode of literals.
+ add_definitions(-DINFLATE_CHUNK_GENERIC)
+ add_definitions(-DINFLATE_CHUNK_READ_64LE)
+
+ # Tested with clang-17, unaligned loads are required by read64 & chunk_copy.
+ # TODO(cavalcantii): replace internal clang flags for -munaligned-access
+ # when we have a newer compiler available.
+ SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --target=riscv64-unknown-linux-gnu -march=rv64gcv -Xclang -target-feature -Xclang +unaligned-scalar-mem")
endif()
endif()
@@ -192,9 +201,14 @@ set(ZLIB_SRCS
if (ENABLE_SIMD_OPTIMIZATIONS)
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64")
message("RISCVV: Add optimizations.")
+ list(REMOVE_ITEM ZLIB_SRCS inflate.c)
list(APPEND ZLIB_PRIVATE_HDRS ${CMAKE_CURRENT_SOURCE_DIR}/adler32_simd.h)
+ list(APPEND ZLIB_PRIVATE_HDRS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/optimizations/chunkcopy.h)
list(APPEND ZLIB_PRIVATE_HDRS ${CMAKE_CURRENT_SOURCE_DIR}/cpu_features.h)
+
list(APPEND ZLIB_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/adler32_simd.c)
+ list(APPEND ZLIB_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/optimizations/inffast_chunk.c)
+ list(APPEND ZLIB_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/contrib/optimizations/inflate.c)
list(APPEND ZLIB_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/cpu_features.c)
else()
list(REMOVE_ITEM ZLIB_SRCS inflate.c)
@@ -339,7 +353,7 @@ if (BUILD_UNITTESTS)
endif()
#============================================================================
-# Minigzip tool
+# Minizip tool
#============================================================================
# TODO(cavalcantii): get it working on Windows.
if (BUILD_MINIZIP_BIN)
@@ -349,3 +363,18 @@ if (BUILD_MINIZIP_BIN)
)
target_link_libraries(minizip_bin zlib)
endif()
+
+#============================================================================
+# zpipe tool
+#============================================================================
+if (BUILD_ZPIPE)
+ add_executable(zpipe examples/zpipe.c)
+ target_link_libraries(zpipe zlib)
+endif()
+#============================================================================
+# MiniGzip tool
+#============================================================================
+if (BUILD_MINIGZIP)
+ add_executable(minigzip_bin test/minigzip.c)
+ target_link_libraries(minigzip_bin zlib)
+endif()
diff --git a/METADATA b/METADATA
index 370833b..65435cc 100644
--- a/METADATA
+++ b/METADATA
@@ -8,12 +8,12 @@ third_party {
license_type: NOTICE
last_upgrade_date {
year: 2024
- month: 3
- day: 26
+ month: 4
+ day: 10
}
identifier {
type: "Git"
value: "https://chromium.googlesource.com/chromium/src/third_party/zlib/"
- version: "30bf3a72e77abb71568fa1e6258a0a731fef9ba3"
+ version: "7d77fb7fd66d8a5640618ad32c71fdeb7d3e02df"
}
}
diff --git a/adler32_simd.c b/adler32_simd.c
index 9970ea9..b3e1f0a 100644
--- a/adler32_simd.c
+++ b/adler32_simd.c
@@ -41,9 +41,6 @@
* [2] zlib adler32_z() uses this fact to implement NMAX-block-based updates
* of the adler s1 s2 of uint32_t type (see adler32.c).
*/
-/* Copyright (C) 2023 SiFive, Inc. All rights reserved.
- * For conditions of distribution and use, see copyright notice in zlib.h
- */
#include "adler32_simd.h"
@@ -368,11 +365,10 @@ uint32_t ZLIB_INTERNAL adler32_simd_( /* NEON */
#elif defined(ADLER32_SIMD_RVV)
#include <riscv_vector.h>
-/* adler32_rvv.c - RVV version of Adler-32
- * RVV 1.0 code contributed by Alex Chiang <alex.chiang@sifive.com>
- * on https://github.com/zlib-ng/zlib-ng/pull/1532
- * Port from Simon Hosie's fork:
- * https://github.com/cloudflare/zlib/commit/40688b53c61cb9bfc36471acd2dc0800b7ebcab1
+
+/*
+ * Patch by Simon Hosie, from:
+ * https://github.com/cloudflare/zlib/pull/55
*/
uint32_t ZLIB_INTERNAL adler32_simd_( /* RVV */
@@ -380,91 +376,81 @@ uint32_t ZLIB_INTERNAL adler32_simd_( /* RVV */
const unsigned char *buf,
unsigned long len)
{
- /* split Adler-32 into component sums */
- uint32_t sum2 = (adler >> 16) & 0xffff;
- adler &= 0xffff;
-
- size_t left = len;
- size_t vl = __riscv_vsetvlmax_e8m1();
- vl = vl > 256 ? 256 : vl;
- vuint32m4_t v_buf32_accu = __riscv_vmv_v_x_u32m4(0, vl);
- vuint32m4_t v_adler32_prev_accu = __riscv_vmv_v_x_u32m4(0, vl);
- vuint16m2_t v_buf16_accu;
-
- /*
- * We accumulate 8-bit data, and to prevent overflow, we have to use a 32-bit accumulator.
- * However, adding 8-bit data into a 32-bit accumulator isn't efficient. We use 16-bit & 32-bit
- * accumulators to boost performance.
- *
- * The block_size is the largest multiple of vl that <= 256, because overflow would occur when
- * vl > 256 (255 * 256 <= UINT16_MAX).
- *
- * We accumulate 8-bit data into a 16-bit accumulator and then
- * move the data into the 32-bit accumulator at the last iteration.
+ size_t vl = __riscv_vsetvlmax_e8m2();
+ const vuint16m4_t zero16 = __riscv_vmv_v_x_u16m4(0, vl);
+ vuint16m4_t a_sum = zero16;
+ vuint32m8_t b_sum = __riscv_vmv_v_x_u32m8(0, vl);
+
+ /* Deal with the part which is not a multiple of vl first; because it's
+ * easier to zero-stuff the beginning of the checksum than it is to tweak the
+ * multipliers and sums for odd lengths afterwards.
+ */
+ size_t head = len & (vl - 1);
+ if (head > 0) {
+ vuint8m2_t zero8 = __riscv_vmv_v_x_u8m2(0, vl);
+ vuint8m2_t in = __riscv_vle8_v_u8m2(buf, vl);
+ in = __riscv_vslideup(zero8, in, vl - head, vl);
+ vuint16m4_t in16 = __riscv_vwcvtu_x(in, vl);
+ a_sum = in16;
+ buf += head;
+ }
+
+ /* We have a 32-bit accumulator, and in each iteration we add 22-times a
+ * 16-bit value, plus another 16-bit value. We periodically subtract up to
+ * 65535 times BASE to avoid overflow. b_overflow estimates how often we
+ * need to do this subtraction.
+ */
+ const int b_overflow = BASE / 23;
+ int fixup = b_overflow;
+ ssize_t iters = (len - head) / vl;
+ while (iters > 0) {
+ const vuint16m4_t a_overflow = __riscv_vrsub(a_sum, BASE, vl);
+ int batch = iters < 22 ? iters : 22;
+ iters -= batch;
+ b_sum = __riscv_vwmaccu(b_sum, batch, a_sum, vl);
+ vuint16m4_t a_batch = zero16, b_batch = zero16;
+
+ /* Do a short batch, where neither a_sum nor b_sum can overflow a 16-bit
+ * register. Then add them back into the main accumulators.
*/
- size_t block_size = (256 / vl) * vl;
- size_t nmax_limit = (NMAX / block_size);
- size_t cnt = 0;
- while (left >= block_size) {
- v_buf16_accu = __riscv_vmv_v_x_u16m2(0, vl);
- size_t subprob = block_size;
- while (subprob > 0) {
- vuint8m1_t v_buf8 = __riscv_vle8_v_u8m1(buf, vl);
- v_adler32_prev_accu = __riscv_vwaddu_wv_u32m4(v_adler32_prev_accu, v_buf16_accu, vl);
- v_buf16_accu = __riscv_vwaddu_wv_u16m2(v_buf16_accu, v_buf8, vl);
- buf += vl;
- subprob -= vl;
- }
- v_adler32_prev_accu = __riscv_vmacc_vx_u32m4(v_adler32_prev_accu, block_size / vl, v_buf32_accu, vl);
- v_buf32_accu = __riscv_vwaddu_wv_u32m4(v_buf32_accu, v_buf16_accu, vl);
- left -= block_size;
- /* do modulo once each block of NMAX size */
- if (++cnt >= nmax_limit) {
- v_adler32_prev_accu = __riscv_vremu_vx_u32m4(v_adler32_prev_accu, BASE, vl);
- cnt = 0;
- }
+ while (batch-- > 0) {
+ vuint8m2_t in8 = __riscv_vle8_v_u8m2(buf, vl);
+ buf += vl;
+ b_batch = __riscv_vadd(b_batch, a_batch, vl);
+ a_batch = __riscv_vwaddu_wv(a_batch, in8, vl);
}
- /* the left len <= 256 now, we can use 16-bit accum safely */
- v_buf16_accu = __riscv_vmv_v_x_u16m2(0, vl);
- size_t res = left;
- while (left >= vl) {
- vuint8m1_t v_buf8 = __riscv_vle8_v_u8m1(buf, vl);
- v_adler32_prev_accu = __riscv_vwaddu_wv_u32m4(v_adler32_prev_accu, v_buf16_accu, vl);
- v_buf16_accu = __riscv_vwaddu_wv_u16m2(v_buf16_accu, v_buf8, vl);
- buf += vl;
- left -= vl;
+ vbool4_t ov = __riscv_vmsgeu(a_batch, a_overflow, vl);
+ a_sum = __riscv_vadd(a_sum, a_batch, vl);
+ a_sum = __riscv_vadd_mu(ov, a_sum, a_sum, 65536 - BASE, vl);
+ b_sum = __riscv_vwaddu_wv(b_sum, b_batch, vl);
+ if (--fixup <= 0) {
+ b_sum = __riscv_vnmsac(b_sum, BASE, __riscv_vsrl(b_sum, 16, vl), vl);
+ fixup = b_overflow;
}
- v_adler32_prev_accu = __riscv_vmacc_vx_u32m4(v_adler32_prev_accu, res / vl, v_buf32_accu, vl);
- v_adler32_prev_accu = __riscv_vremu_vx_u32m4(v_adler32_prev_accu, BASE, vl);
- v_buf32_accu = __riscv_vwaddu_wv_u32m4(v_buf32_accu, v_buf16_accu, vl);
-
- vuint32m4_t v_seq = __riscv_vid_v_u32m4(vl);
- vuint32m4_t v_rev_seq = __riscv_vrsub_vx_u32m4(v_seq, vl, vl);
- vuint32m4_t v_sum32_accu = __riscv_vmul_vv_u32m4(v_buf32_accu, v_rev_seq, vl);
-
- v_sum32_accu = __riscv_vadd_vv_u32m4(v_sum32_accu, __riscv_vmul_vx_u32m4(v_adler32_prev_accu, vl, vl), vl);
-
- vuint32m1_t v_sum2_sum = __riscv_vmv_s_x_u32m1(0, vl);
- v_sum2_sum = __riscv_vredsum_vs_u32m4_u32m1(v_sum32_accu, v_sum2_sum, vl);
- uint32_t sum2_sum = __riscv_vmv_x_s_u32m1_u32(v_sum2_sum);
-
- sum2 += (sum2_sum + adler * (len - left));
-
- vuint32m1_t v_adler_sum = __riscv_vmv_s_x_u32m1(0, vl);
- v_adler_sum = __riscv_vredsum_vs_u32m4_u32m1(v_buf32_accu, v_adler_sum, vl);
- uint32_t adler_sum = __riscv_vmv_x_s_u32m1_u32(v_adler_sum);
-
- adler += adler_sum;
-
- while (left--) {
- adler += *buf++;
- sum2 += adler;
- }
-
- sum2 %= BASE;
- adler %= BASE;
-
- return adler | (sum2 << 16);
+ }
+ /* Adjust per-lane sums to have appropriate offsets from the end of the
+ * buffer.
+ */
+ const vuint16m4_t off = __riscv_vrsub(__riscv_vid_v_u16m4(vl), vl, vl);
+ vuint16m4_t bsum16 = __riscv_vncvt_x(__riscv_vremu(b_sum, BASE, vl), vl);
+ b_sum = __riscv_vadd(__riscv_vwmulu(a_sum, off, vl),
+ __riscv_vwmulu(bsum16, vl, vl), vl);
+ bsum16 = __riscv_vncvt_x(__riscv_vremu(b_sum, BASE, vl), vl);
+
+ /* And finally, do a horizontal sum across the registers for the final
+ * result.
+ */
+ uint32_t a = adler & 0xffff;
+ uint32_t b = ((adler >> 16) + a * (len % BASE)) % BASE;
+ vuint32m1_t sca = __riscv_vmv_v_x_u32m1(a, 1);
+ vuint32m1_t scb = __riscv_vmv_v_x_u32m1(b, 1);
+ sca = __riscv_vwredsumu(a_sum, sca, vl);
+ scb = __riscv_vwredsumu(bsum16, scb, vl);
+ a = __riscv_vmv_x(sca);
+ b = __riscv_vmv_x(scb);
+ a %= BASE;
+ b %= BASE;
+ return (b << 16) | a;
}
#endif /* ADLER32_SIMD_SSSE3 */
diff --git a/contrib/optimizations/chunkcopy.h b/contrib/optimizations/chunkcopy.h
index f40546d..97efff3 100644
--- a/contrib/optimizations/chunkcopy.h
+++ b/contrib/optimizations/chunkcopy.h
@@ -21,8 +21,10 @@
#if defined(__clang__) || defined(__GNUC__) || defined(__llvm__)
#define Z_BUILTIN_MEMCPY __builtin_memcpy
+#define Z_BUILTIN_MEMSET __builtin_memset
#else
#define Z_BUILTIN_MEMCPY zmemcpy
+#define Z_BUILTIN_MEMSET zmemset
#endif
#if defined(INFLATE_CHUNK_SIMD_NEON)
@@ -31,6 +33,8 @@ typedef uint8x16_t z_vec128i_t;
#elif defined(INFLATE_CHUNK_SIMD_SSE2)
#include <emmintrin.h>
typedef __m128i z_vec128i_t;
+#elif defined(INFLATE_CHUNK_GENERIC)
+typedef struct { uint8_t x[16]; } z_vec128i_t;
#else
#error chunkcopy.h inflate chunk SIMD is not defined for your build target
#endif
@@ -265,6 +269,77 @@ static inline z_vec128i_t v_load8_dup(const void* src) {
static inline void v_store_128(void* out, const z_vec128i_t vec) {
_mm_storeu_si128((__m128i*)out, vec);
}
+#elif defined(INFLATE_CHUNK_GENERIC)
+/*
+ * Default implementations for chunk-copy functions rely on memcpy() being
+ * inlined by the compiler for best performance. This is most likely to work
+ * as expected when the length argument is constant (as is the case here) and
+ * the target supports unaligned loads and stores. Since that's not always a
+ * safe assumption, this may need extra compiler arguments such as
+ * `-mno-strict-align` or `-munaligned-access`, or the availability of
+ * extensions like SIMD.
+ */
+
+/*
+ * v_load64_dup(): load *src as an unaligned 64-bit int and duplicate it in
+ * every 64-bit component of the 128-bit result (64-bit int splat).
+ */
+static inline z_vec128i_t v_load64_dup(const void* src) {
+ int64_t in;
+ Z_BUILTIN_MEMCPY(&in, src, sizeof(in));
+ z_vec128i_t out;
+ for (int i = 0; i < sizeof(out); i += sizeof(in)) {
+ Z_BUILTIN_MEMCPY((uint8_t*)&out + i, &in, sizeof(in));
+ }
+ return out;
+}
+
+/*
+ * v_load32_dup(): load *src as an unaligned 32-bit int and duplicate it in
+ * every 32-bit component of the 128-bit result (32-bit int splat).
+ */
+static inline z_vec128i_t v_load32_dup(const void* src) {
+ int32_t in;
+ Z_BUILTIN_MEMCPY(&in, src, sizeof(in));
+ z_vec128i_t out;
+ for (int i = 0; i < sizeof(out); i += sizeof(in)) {
+ Z_BUILTIN_MEMCPY((uint8_t*)&out + i, &in, sizeof(in));
+ }
+ return out;
+}
+
+/*
+ * v_load16_dup(): load *src as an unaligned 16-bit int and duplicate it in
+ * every 16-bit component of the 128-bit result (16-bit int splat).
+ */
+static inline z_vec128i_t v_load16_dup(const void* src) {
+ int16_t in;
+ Z_BUILTIN_MEMCPY(&in, src, sizeof(in));
+ z_vec128i_t out;
+ for (int i = 0; i < sizeof(out); i += sizeof(in)) {
+ Z_BUILTIN_MEMCPY((uint8_t*)&out + i, &in, sizeof(in));
+ }
+ return out;
+}
+
+/*
+ * v_load8_dup(): load the 8-bit int *src and duplicate it in every 8-bit
+ * component of the 128-bit result (8-bit int splat).
+ */
+static inline z_vec128i_t v_load8_dup(const void* src) {
+ int8_t in = *(const uint8_t*)src;
+ z_vec128i_t out;
+ Z_BUILTIN_MEMSET(&out, in, sizeof(out));
+ return out;
+}
+
+/*
+ * v_store_128(): store the 128-bit vec in a memory destination (that might
+ * not be 16-byte aligned) void* out.
+ */
+static inline void v_store_128(void* out, const z_vec128i_t vec) {
+ Z_BUILTIN_MEMCPY(out, &vec, sizeof(vec));
+}
#endif
/*
diff --git a/contrib/tests/utils_unittest.cc b/contrib/tests/utils_unittest.cc
index 0cc1081..f487a06 100644
--- a/contrib/tests/utils_unittest.cc
+++ b/contrib/tests/utils_unittest.cc
@@ -20,7 +20,8 @@
#include "zlib.h"
-void TestPayloads(size_t input_size, zlib_internal::WrapperType type) {
+void TestPayloads(size_t input_size, zlib_internal::WrapperType type,
+ const int compression_level = Z_DEFAULT_COMPRESSION) {
std::vector<unsigned char> input;
input.reserve(input_size);
for (size_t i = 1; i <= input_size; ++i)
@@ -36,7 +37,7 @@ void TestPayloads(size_t input_size, zlib_internal::WrapperType type) {
unsigned long compressed_size = static_cast<unsigned long>(compressed.size());
int result = zlib_internal::CompressHelper(
type, compressed.data(), &compressed_size, input.data(), input.size(),
- Z_DEFAULT_COMPRESSION, nullptr, nullptr);
+ compression_level, nullptr, nullptr);
ASSERT_EQ(result, Z_OK);
unsigned long decompressed_size =
@@ -67,6 +68,25 @@ TEST(ZlibTest, RawWrapper) {
TestPayloads(i, zlib_internal::WrapperType::ZRAW);
}
+TEST(ZlibTest, LargePayloads) {
+ static const size_t lengths[] = { 6000, 8000, 10'000, 15'000, 20'000, 30'000,
+ 50'000, 100'000, 150'000, 2'500'000,
+ 5'000'000, 10'000'000, 20'000'000 };
+
+ for (size_t length: lengths) {
+ TestPayloads(length, zlib_internal::WrapperType::ZLIB);
+ TestPayloads(length, zlib_internal::WrapperType::GZIP);
+ }
+}
+
+TEST(ZlibTest, CompressionLevels) {
+ static const int levels[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ for (int level: levels) {
+ TestPayloads(5'000'000, zlib_internal::WrapperType::ZLIB, level);
+ TestPayloads(5'000'000, zlib_internal::WrapperType::GZIP, level);
+ }
+}
+
TEST(ZlibTest, InflateCover) {
cover_support();
cover_wrap();
diff --git a/examples/zpipe.c b/examples/zpipe.c
new file mode 100644
index 0000000..51dec47
--- /dev/null
+++ b/examples/zpipe.c
@@ -0,0 +1,209 @@
+/* zpipe.c: example of proper use of zlib's inflate() and deflate()
+ Not copyrighted -- provided to the public domain
+ Version 1.4 11 December 2005 Mark Adler */
+
+/* Version history:
+ 1.0 30 Oct 2004 First version
+ 1.1 8 Nov 2004 Add void casting for unused return values
+ Use switch statement for inflate() return values
+ 1.2 9 Nov 2004 Add assertions to document zlib guarantees
+ 1.3 6 Apr 2005 Remove incorrect assertion in inf()
+ 1.4 11 Dec 2005 Add hack to avoid MSDOS end-of-line conversions
+ Avoid some compiler warnings for input and output buffers
+ */
+
+#if defined(_WIN32) && !defined(_CRT_NONSTDC_NO_DEPRECATE)
+# define _CRT_NONSTDC_NO_DEPRECATE
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include "zlib.h"
+
+#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
+# include <fcntl.h>
+# include <io.h>
+# define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
+#else
+# define SET_BINARY_MODE(file)
+#endif
+
+#define CHUNK 16384
+
+/* Compress from file source to file dest until EOF on source.
+ def() returns Z_OK on success, Z_MEM_ERROR if memory could not be
+ allocated for processing, Z_STREAM_ERROR if an invalid compression
+ level is supplied, Z_VERSION_ERROR if the version of zlib.h and the
+ version of the library linked do not match, or Z_ERRNO if there is
+ an error reading or writing the files. */
+int def(FILE *source, FILE *dest, int level)
+{
+ int ret, flush;
+ unsigned have;
+ z_stream strm;
+ unsigned char in[CHUNK];
+ unsigned char out[CHUNK];
+
+ /* allocate deflate state */
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ ret = deflateInit(&strm, level);
+ if (ret != Z_OK)
+ return ret;
+
+ /* compress until end of file */
+ do {
+ strm.avail_in = fread(in, 1, CHUNK, source);
+ if (ferror(source)) {
+ (void)deflateEnd(&strm);
+ return Z_ERRNO;
+ }
+ flush = feof(source) ? Z_FINISH : Z_NO_FLUSH;
+ strm.next_in = in;
+
+ /* run deflate() on input until output buffer not full, finish
+ compression if all of source has been read in */
+ do {
+ strm.avail_out = CHUNK;
+ strm.next_out = out;
+ ret = deflate(&strm, flush); /* no bad return value */
+ assert(ret != Z_STREAM_ERROR); /* state not clobbered */
+ have = CHUNK - strm.avail_out;
+ if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
+ (void)deflateEnd(&strm);
+ return Z_ERRNO;
+ }
+ } while (strm.avail_out == 0);
+ assert(strm.avail_in == 0); /* all input will be used */
+
+ /* done when last data in file processed */
+ } while (flush != Z_FINISH);
+ assert(ret == Z_STREAM_END); /* stream will be complete */
+
+ /* clean up and return */
+ (void)deflateEnd(&strm);
+ return Z_OK;
+}
+
+/* Decompress from file source to file dest until stream ends or EOF.
+ inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be
+ allocated for processing, Z_DATA_ERROR if the deflate data is
+ invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and
+ the version of the library linked do not match, or Z_ERRNO if there
+ is an error reading or writing the files. */
+int inf(FILE *source, FILE *dest)
+{
+ int ret;
+ unsigned have;
+ z_stream strm;
+ unsigned char in[CHUNK];
+ unsigned char out[CHUNK];
+
+ /* allocate inflate state */
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ strm.avail_in = 0;
+ strm.next_in = Z_NULL;
+ ret = inflateInit(&strm);
+ if (ret != Z_OK)
+ return ret;
+
+ /* decompress until deflate stream ends or end of file */
+ do {
+ strm.avail_in = fread(in, 1, CHUNK, source);
+ if (ferror(source)) {
+ (void)inflateEnd(&strm);
+ return Z_ERRNO;
+ }
+ if (strm.avail_in == 0)
+ break;
+ strm.next_in = in;
+
+ /* run inflate() on input until output buffer not full */
+ do {
+ strm.avail_out = CHUNK;
+ strm.next_out = out;
+ ret = inflate(&strm, Z_NO_FLUSH);
+ assert(ret != Z_STREAM_ERROR); /* state not clobbered */
+ switch (ret) {
+ case Z_NEED_DICT:
+ ret = Z_DATA_ERROR; /* and fall through */
+ case Z_DATA_ERROR:
+ case Z_MEM_ERROR:
+ (void)inflateEnd(&strm);
+ return ret;
+ }
+ have = CHUNK - strm.avail_out;
+ if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
+ (void)inflateEnd(&strm);
+ return Z_ERRNO;
+ }
+ } while (strm.avail_out == 0);
+
+ /* done when inflate() says it's done */
+ } while (ret != Z_STREAM_END);
+
+ /* clean up and return */
+ (void)inflateEnd(&strm);
+ return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
+}
+
+/* report a zlib or i/o error */
+void zerr(int ret)
+{
+ fputs("zpipe: ", stderr);
+ switch (ret) {
+ case Z_ERRNO:
+ if (ferror(stdin))
+ fputs("error reading stdin\n", stderr);
+ if (ferror(stdout))
+ fputs("error writing stdout\n", stderr);
+ break;
+ case Z_STREAM_ERROR:
+ fputs("invalid compression level\n", stderr);
+ break;
+ case Z_DATA_ERROR:
+ fputs("invalid or incomplete deflate data\n", stderr);
+ break;
+ case Z_MEM_ERROR:
+ fputs("out of memory\n", stderr);
+ break;
+ case Z_VERSION_ERROR:
+ fputs("zlib version mismatch!\n", stderr);
+ }
+}
+
+/* compress or decompress from stdin to stdout */
+int main(int argc, char **argv)
+{
+ int ret;
+
+ /* avoid end-of-line conversions */
+ SET_BINARY_MODE(stdin);
+ SET_BINARY_MODE(stdout);
+
+ /* do compression if no arguments */
+ if (argc == 1) {
+ ret = def(stdin, stdout, Z_DEFAULT_COMPRESSION);
+ if (ret != Z_OK)
+ zerr(ret);
+ return ret;
+ }
+
+ /* do decompression if -d specified */
+ else if (argc == 2 && strcmp(argv[1], "-d") == 0) {
+ ret = inf(stdin, stdout);
+ if (ret != Z_OK)
+ zerr(ret);
+ return ret;
+ }
+
+ /* otherwise, report usage */
+ else {
+ fputs("zpipe usage: zpipe [-d] < source > dest\n", stderr);
+ return 1;
+ }
+}
diff --git a/google/compression_utils.cc b/google/compression_utils.cc
index c2b17e4..0ba3110 100644
--- a/google/compression_utils.cc
+++ b/google/compression_utils.cc
@@ -6,7 +6,6 @@
#include "base/check_op.h"
#include "base/process/memory.h"
-#include "base/sys_byteorder.h"
#include "third_party/zlib/google/compression_utils_portable.h"
diff --git a/google/zip_reader_unittest.cc b/google/zip_reader_unittest.cc
index e6f89d7..9eb7d7d 100644
--- a/google/zip_reader_unittest.cc
+++ b/google/zip_reader_unittest.cc
@@ -72,7 +72,7 @@ class FileWrapper {
// A mock that provides methods that can be used as callbacks in asynchronous
// unzip functions. Tracks the number of calls and number of bytes reported.
// Assumes that progress callbacks will be executed in-order.
-class MockUnzipListener : public base::SupportsWeakPtr<MockUnzipListener> {
+class MockUnzipListener final {
public:
MockUnzipListener()
: success_calls_(0),
@@ -98,12 +98,18 @@ class MockUnzipListener : public base::SupportsWeakPtr<MockUnzipListener> {
int progress_calls() { return progress_calls_; }
int current_progress() { return current_progress_; }
+ base::WeakPtr<MockUnzipListener> AsWeakPtr() {
+ return weak_ptr_factory_.GetWeakPtr();
+ }
+
private:
int success_calls_;
int failure_calls_;
int progress_calls_;
int64_t current_progress_;
+
+ base::WeakPtrFactory<MockUnzipListener> weak_ptr_factory_{this};
};
class MockWriterDelegate : public zip::WriterDelegate {
diff --git a/test/minigzip.c b/test/minigzip.c
new file mode 100644
index 0000000..c72356d
--- /dev/null
+++ b/test/minigzip.c
@@ -0,0 +1,579 @@
+/* minigzip.c -- simulate gzip using the zlib compression library
+ * Copyright (C) 1995-2006, 2010, 2011, 2016 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * minigzip is a minimal implementation of the gzip utility. This is
+ * only an example of using zlib and isn't meant to replace the
+ * full-featured gzip. No attempt is made to deal with file systems
+ * limiting names to 14 or 8+3 characters, etc... Error checking is
+ * very limited. So use minigzip only for testing; use gzip for the
+ * real thing. On MSDOS, use only on file names without extension
+ * or in pipe mode.
+ */
+
+/* @(#) $Id$ */
+
+#include "zlib.h"
+#include <stdio.h>
+
+#ifdef STDC
+# include <string.h>
+# include <stdlib.h>
+#endif
+
+#ifdef USE_MMAP
+# include <sys/types.h>
+# include <sys/mman.h>
+# include <sys/stat.h>
+#endif
+
+#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
+# include <fcntl.h>
+# include <io.h>
+# ifdef UNDER_CE
+# include <stdlib.h>
+# endif
+# define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
+#else
+# define SET_BINARY_MODE(file)
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+# define snprintf _snprintf
+#endif
+
+#ifdef VMS
+# define unlink delete
+# define GZ_SUFFIX "-gz"
+#endif
+#ifdef RISCOS
+# define unlink remove
+# define GZ_SUFFIX "-gz"
+# define fileno(file) file->__file
+#endif
+#if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
+# include <unix.h> /* for fileno */
+#endif
+
+#if !defined(Z_HAVE_UNISTD_H) && !defined(_LARGEFILE64_SOURCE)
+#ifndef WIN32 /* unlink already in stdio.h for WIN32 */
+ extern int unlink(const char *);
+#endif
+#endif
+
+#if defined(UNDER_CE)
+# include <windows.h>
+# define perror(s) pwinerror(s)
+
+/* Map the Windows error number in ERROR to a locale-dependent error
+ message string and return a pointer to it. Typically, the values
+ for ERROR come from GetLastError.
+
+ The string pointed to shall not be modified by the application,
+ but may be overwritten by a subsequent call to strwinerror
+
+ The strwinerror function does not change the current setting
+ of GetLastError. */
+
+static char *strwinerror (error)
+ DWORD error;
+{
+ static char buf[1024];
+
+ wchar_t *msgbuf;
+ DWORD lasterr = GetLastError();
+ DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
+ | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ NULL,
+ error,
+ 0, /* Default language */
+ (LPVOID)&msgbuf,
+ 0,
+ NULL);
+ if (chars != 0) {
+ /* If there is an \r\n appended, zap it. */
+ if (chars >= 2
+ && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') {
+ chars -= 2;
+ msgbuf[chars] = 0;
+ }
+
+ if (chars > sizeof (buf) - 1) {
+ chars = sizeof (buf) - 1;
+ msgbuf[chars] = 0;
+ }
+
+ wcstombs(buf, msgbuf, chars + 1);
+ LocalFree(msgbuf);
+ }
+ else {
+ sprintf(buf, "unknown win32 error (%ld)", error);
+ }
+
+ SetLastError(lasterr);
+ return buf;
+}
+
+static void pwinerror (s)
+ const char *s;
+{
+ if (s && *s)
+ fprintf(stderr, "%s: %s\n", s, strwinerror(GetLastError ()));
+ else
+ fprintf(stderr, "%s\n", strwinerror(GetLastError ()));
+}
+
+#endif /* UNDER_CE */
+
+#ifndef GZ_SUFFIX
+# define GZ_SUFFIX ".gz"
+#endif
+#define SUFFIX_LEN (sizeof(GZ_SUFFIX)-1)
+
+#define BUFLEN 16384
+#define MAX_NAME_LEN 1024
+
+#ifdef MAXSEG_64K
+# define local static
+ /* Needed for systems with limitation on stack size. */
+#else
+# define local
+#endif
+
+#ifdef Z_SOLO
+/* for Z_SOLO, create simplified gz* functions using deflate and inflate */
+
+#if defined(Z_HAVE_UNISTD_H) || defined(Z_LARGE)
+# include <unistd.h> /* for unlink() */
+#endif
+
+static void *myalloc(void *q, unsigned n, unsigned m) {
+ (void)q;
+ return calloc(n, m);
+}
+
+static void myfree(void *q, void *p) {
+ (void)q;
+ free(p);
+}
+
+typedef struct gzFile_s {
+ FILE *file;
+ int write;
+ int err;
+ char *msg;
+ z_stream strm;
+} *gzFile;
+
+static gzFile gz_open(const char *path, int fd, const char *mode) {
+ gzFile gz;
+ int ret;
+
+ gz = malloc(sizeof(struct gzFile_s));
+ if (gz == NULL)
+ return NULL;
+ gz->write = strchr(mode, 'w') != NULL;
+ gz->strm.zalloc = myalloc;
+ gz->strm.zfree = myfree;
+ gz->strm.opaque = Z_NULL;
+ if (gz->write)
+ ret = deflateInit2(&(gz->strm), -1, 8, 15 + 16, 8, 0);
+ else {
+ gz->strm.next_in = 0;
+ gz->strm.avail_in = Z_NULL;
+ ret = inflateInit2(&(gz->strm), 15 + 16);
+ }
+ if (ret != Z_OK) {
+ free(gz);
+ return NULL;
+ }
+ gz->file = path == NULL ? fdopen(fd, gz->write ? "wb" : "rb") :
+ fopen(path, gz->write ? "wb" : "rb");
+ if (gz->file == NULL) {
+ gz->write ? deflateEnd(&(gz->strm)) : inflateEnd(&(gz->strm));
+ free(gz);
+ return NULL;
+ }
+ gz->err = 0;
+ gz->msg = "";
+ return gz;
+}
+
+static gzFile gzopen(const char *path, const char *mode) {
+ return gz_open(path, -1, mode);
+}
+
+static gzFile gzdopen(int fd, const char *mode) {
+ return gz_open(NULL, fd, mode);
+}
+
+static int gzwrite(gzFile gz, const void *buf, unsigned len) {
+ z_stream *strm;
+ unsigned char out[BUFLEN];
+
+ if (gz == NULL || !gz->write)
+ return 0;
+ strm = &(gz->strm);
+ strm->next_in = (void *)buf;
+ strm->avail_in = len;
+ do {
+ strm->next_out = out;
+ strm->avail_out = BUFLEN;
+ (void)deflate(strm, Z_NO_FLUSH);
+ fwrite(out, 1, BUFLEN - strm->avail_out, gz->file);
+ } while (strm->avail_out == 0);
+ return len;
+}
+
+static int gzread(gzFile gz, void *buf, unsigned len) {
+ int ret;
+ unsigned got;
+ unsigned char in[1];
+ z_stream *strm;
+
+ if (gz == NULL || gz->write)
+ return 0;
+ if (gz->err)
+ return 0;
+ strm = &(gz->strm);
+ strm->next_out = (void *)buf;
+ strm->avail_out = len;
+ do {
+ got = fread(in, 1, 1, gz->file);
+ if (got == 0)
+ break;
+ strm->next_in = in;
+ strm->avail_in = 1;
+ ret = inflate(strm, Z_NO_FLUSH);
+ if (ret == Z_DATA_ERROR) {
+ gz->err = Z_DATA_ERROR;
+ gz->msg = strm->msg;
+ return 0;
+ }
+ if (ret == Z_STREAM_END)
+ inflateReset(strm);
+ } while (strm->avail_out);
+ return len - strm->avail_out;
+}
+
+static int gzclose(gzFile gz) {
+ z_stream *strm;
+ unsigned char out[BUFLEN];
+
+ if (gz == NULL)
+ return Z_STREAM_ERROR;
+ strm = &(gz->strm);
+ if (gz->write) {
+ strm->next_in = Z_NULL;
+ strm->avail_in = 0;
+ do {
+ strm->next_out = out;
+ strm->avail_out = BUFLEN;
+ (void)deflate(strm, Z_FINISH);
+ fwrite(out, 1, BUFLEN - strm->avail_out, gz->file);
+ } while (strm->avail_out == 0);
+ deflateEnd(strm);
+ }
+ else
+ inflateEnd(strm);
+ fclose(gz->file);
+ free(gz);
+ return Z_OK;
+}
+
+static const char *gzerror(gzFile gz, int *err) {
+ *err = gz->err;
+ return gz->msg;
+}
+
+#endif
+
+static char *prog;
+
+/* ===========================================================================
+ * Display error message and exit
+ */
+static void error(const char *msg) {
+ fprintf(stderr, "%s: %s\n", prog, msg);
+ exit(1);
+}
+
+#ifdef USE_MMAP /* MMAP version, Miguel Albrecht <malbrech@eso.org> */
+
+/* Try compressing the input file at once using mmap. Return Z_OK if
+ * if success, Z_ERRNO otherwise.
+ */
+static int gz_compress_mmap(FILE *in, gzFile out) {
+ int len;
+ int err;
+ int ifd = fileno(in);
+ caddr_t buf; /* mmap'ed buffer for the entire input file */
+ off_t buf_len; /* length of the input file */
+ struct stat sb;
+
+ /* Determine the size of the file, needed for mmap: */
+ if (fstat(ifd, &sb) < 0) return Z_ERRNO;
+ buf_len = sb.st_size;
+ if (buf_len <= 0) return Z_ERRNO;
+
+ /* Now do the actual mmap: */
+ buf = mmap((caddr_t) 0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0);
+ if (buf == (caddr_t)(-1)) return Z_ERRNO;
+
+ /* Compress the whole file at once: */
+ len = gzwrite(out, (char *)buf, (unsigned)buf_len);
+
+ if (len != (int)buf_len) error(gzerror(out, &err));
+
+ munmap(buf, buf_len);
+ fclose(in);
+ if (gzclose(out) != Z_OK) error("failed gzclose");
+ return Z_OK;
+}
+#endif /* USE_MMAP */
+
+/* ===========================================================================
+ * Compress input to output then close both files.
+ */
+
+static void gz_compress(FILE *in, gzFile out) {
+ local char buf[BUFLEN];
+ int len;
+ int err;
+
+#ifdef USE_MMAP
+ /* Try first compressing with mmap. If mmap fails (minigzip used in a
+ * pipe), use the normal fread loop.
+ */
+ if (gz_compress_mmap(in, out) == Z_OK) return;
+#endif
+ for (;;) {
+ len = (int)fread(buf, 1, sizeof(buf), in);
+ if (ferror(in)) {
+ perror("fread");
+ exit(1);
+ }
+ if (len == 0) break;
+
+ if (gzwrite(out, buf, (unsigned)len) != len) error(gzerror(out, &err));
+ }
+ fclose(in);
+ if (gzclose(out) != Z_OK) error("failed gzclose");
+}
+
+/* ===========================================================================
+ * Uncompress input to output then close both files.
+ */
+static void gz_uncompress(gzFile in, FILE *out) {
+ local char buf[BUFLEN];
+ int len;
+ int err;
+
+ for (;;) {
+ len = gzread(in, buf, sizeof(buf));
+ if (len < 0) error (gzerror(in, &err));
+ if (len == 0) break;
+
+ if ((int)fwrite(buf, 1, (unsigned)len, out) != len) {
+ error("failed fwrite");
+ }
+ }
+ if (fclose(out)) error("failed fclose");
+
+ if (gzclose(in) != Z_OK) error("failed gzclose");
+}
+
+
+/* ===========================================================================
+ * Compress the given file: create a corresponding .gz file and remove the
+ * original.
+ */
+static void file_compress(char *file, char *mode) {
+ local char outfile[MAX_NAME_LEN];
+ FILE *in;
+ gzFile out;
+
+ if (strlen(file) + strlen(GZ_SUFFIX) >= sizeof(outfile)) {
+ fprintf(stderr, "%s: filename too long\n", prog);
+ exit(1);
+ }
+
+#if !defined(NO_snprintf) && !defined(NO_vsnprintf)
+ snprintf(outfile, sizeof(outfile), "%s%s", file, GZ_SUFFIX);
+#else
+ strcpy(outfile, file);
+ strcat(outfile, GZ_SUFFIX);
+#endif
+
+ in = fopen(file, "rb");
+ if (in == NULL) {
+ perror(file);
+ exit(1);
+ }
+ out = gzopen(outfile, mode);
+ if (out == NULL) {
+ fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile);
+ exit(1);
+ }
+ gz_compress(in, out);
+
+ unlink(file);
+}
+
+
+/* ===========================================================================
+ * Uncompress the given file and remove the original.
+ */
+static void file_uncompress(char *file) {
+ local char buf[MAX_NAME_LEN];
+ char *infile, *outfile;
+ FILE *out;
+ gzFile in;
+ z_size_t len = strlen(file);
+
+ if (len + strlen(GZ_SUFFIX) >= sizeof(buf)) {
+ fprintf(stderr, "%s: filename too long\n", prog);
+ exit(1);
+ }
+
+#if !defined(NO_snprintf) && !defined(NO_vsnprintf)
+ snprintf(buf, sizeof(buf), "%s", file);
+#else
+ strcpy(buf, file);
+#endif
+
+ if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) {
+ infile = file;
+ outfile = buf;
+ outfile[len-3] = '\0';
+ } else {
+ outfile = file;
+ infile = buf;
+#if !defined(NO_snprintf) && !defined(NO_vsnprintf)
+ snprintf(buf + len, sizeof(buf) - len, "%s", GZ_SUFFIX);
+#else
+ strcat(infile, GZ_SUFFIX);
+#endif
+ }
+ in = gzopen(infile, "rb");
+ if (in == NULL) {
+ fprintf(stderr, "%s: can't gzopen %s\n", prog, infile);
+ exit(1);
+ }
+ out = fopen(outfile, "wb");
+ if (out == NULL) {
+ perror(file);
+ exit(1);
+ }
+
+ gz_uncompress(in, out);
+
+ unlink(infile);
+}
+
+
+/* ===========================================================================
+ * Usage: minigzip [-c] [-d] [-f] [-h] [-r] [-1 to -9] [files...]
+ * -c : write to standard output
+ * -d : decompress
+ * -f : compress with Z_FILTERED
+ * -h : compress with Z_HUFFMAN_ONLY
+ * -r : compress with Z_RLE
+ * -1 to -9 : compression level
+ */
+
+int main(int argc, char *argv[]) {
+ int copyout = 0;
+ int uncompr = 0;
+ gzFile file;
+ char *bname, outmode[20];
+
+#if !defined(NO_snprintf) && !defined(NO_vsnprintf)
+ snprintf(outmode, sizeof(outmode), "%s", "wb6 ");
+#else
+ strcpy(outmode, "wb6 ");
+#endif
+
+ prog = argv[0];
+ bname = strrchr(argv[0], '/');
+ if (bname)
+ bname++;
+ else
+ bname = argv[0];
+ argc--, argv++;
+
+ if (!strcmp(bname, "gunzip"))
+ uncompr = 1;
+ else if (!strcmp(bname, "zcat"))
+ copyout = uncompr = 1;
+
+ while (argc > 0) {
+ if (strcmp(*argv, "-c") == 0)
+ copyout = 1;
+ else if (strcmp(*argv, "-d") == 0)
+ uncompr = 1;
+ else if (strcmp(*argv, "-f") == 0)
+ outmode[3] = 'f';
+ else if (strcmp(*argv, "-h") == 0)
+ outmode[3] = 'h';
+ else if (strcmp(*argv, "-r") == 0)
+ outmode[3] = 'R';
+ else if ((*argv)[0] == '-' && (*argv)[1] >= '1' && (*argv)[1] <= '9' &&
+ (*argv)[2] == 0)
+ outmode[2] = (*argv)[1];
+ else
+ break;
+ argc--, argv++;
+ }
+ if (outmode[3] == ' ')
+ outmode[3] = 0;
+ if (argc == 0) {
+ SET_BINARY_MODE(stdin);
+ SET_BINARY_MODE(stdout);
+ if (uncompr) {
+ file = gzdopen(fileno(stdin), "rb");
+ if (file == NULL) error("can't gzdopen stdin");
+ gz_uncompress(file, stdout);
+ } else {
+ file = gzdopen(fileno(stdout), outmode);
+ if (file == NULL) error("can't gzdopen stdout");
+ gz_compress(stdin, file);
+ }
+ } else {
+ if (copyout) {
+ SET_BINARY_MODE(stdout);
+ }
+ do {
+ if (uncompr) {
+ if (copyout) {
+ file = gzopen(*argv, "rb");
+ if (file == NULL)
+ fprintf(stderr, "%s: can't gzopen %s\n", prog, *argv);
+ else
+ gz_uncompress(file, stdout);
+ } else {
+ file_uncompress(*argv);
+ }
+ } else {
+ if (copyout) {
+ FILE * in = fopen(*argv, "rb");
+
+ if (in == NULL) {
+ perror(*argv);
+ } else {
+ file = gzdopen(fileno(stdout), outmode);
+ if (file == NULL) error("can't gzdopen stdout");
+
+ gz_compress(in, file);
+ }
+
+ } else {
+ file_compress(*argv, outmode);
+ }
+ }
+ } while (argv++, --argc);
+ }
+ return 0;
+}