diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ec730d351..8f94b3db1a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -233,7 +233,6 @@ if(SOURCEMETA_CORE_JSONLD) endif() if(SOURCEMETA_CORE_GZIP) - find_package(LibDeflate REQUIRED) add_subdirectory(src/core/gzip) endif() diff --git a/DEPENDENCIES b/DEPENDENCIES index 0e7ded6950..13642467f1 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -8,7 +8,6 @@ pyca-cryptography https://github.com/pyca/cryptography 9747d06e83764e7f1ea4c04da wycheproof https://github.com/C2SP/wycheproof 6d7cccd0fcb1917368579adeeac10fe802f1b521 pcre2 https://github.com/PCRE2Project/pcre2 pcre2-10.48 googlebenchmark https://github.com/google/benchmark 378fe693a1ef51500db21b11ff05a8018c5f0e55 -libdeflate https://github.com/ebiggers/libdeflate v1.26 unicodetools https://github.com/unicode-org/unicodetools final-17.0-20250910 jose-cookbook https://github.com/ietf-jose/cookbook 13692b68bfc18b99557a5b1ed311fd5077bfff04 w3c-json-ld https://github.com/w3c/json-ld-api 8654ac22b6cf4f441d2fee915ae634d36b5a8067 diff --git a/cmake/FindLibDeflate.cmake b/cmake/FindLibDeflate.cmake deleted file mode 100644 index f5bbcad11e..0000000000 --- a/cmake/FindLibDeflate.cmake +++ /dev/null @@ -1,137 +0,0 @@ -if(NOT LibDeflate_FOUND) - set(LIBDEFLATE_DIR "${PROJECT_SOURCE_DIR}/vendor/libdeflate") - set(LIBDEFLATE_LIB_DIR "${LIBDEFLATE_DIR}/lib") - - set(LIBDEFLATE_SOURCES - "${LIBDEFLATE_LIB_DIR}/utils.c" - "${LIBDEFLATE_LIB_DIR}/deflate_compress.c" - "${LIBDEFLATE_LIB_DIR}/deflate_decompress.c" - "${LIBDEFLATE_LIB_DIR}/gzip_compress.c" - "${LIBDEFLATE_LIB_DIR}/gzip_decompress.c" - "${LIBDEFLATE_LIB_DIR}/adler32.c" - "${LIBDEFLATE_LIB_DIR}/crc32.c" - "${LIBDEFLATE_LIB_DIR}/zlib_compress.c" - "${LIBDEFLATE_LIB_DIR}/zlib_decompress.c") - - # Platform-specific CPU feature detection - if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64|ARM64") - list(APPEND LIBDEFLATE_SOURCES - "${LIBDEFLATE_LIB_DIR}/arm/cpu_features.c") - elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64|amd64|x86|i[3-6]86") - list(APPEND LIBDEFLATE_SOURCES - "${LIBDEFLATE_LIB_DIR}/x86/cpu_features.c") - endif() - - # Merged into the library that uses it, so that no archive, no header and - # no CMake package of our own build of it reaches an installed consumer - add_library(libdeflate OBJECT ${LIBDEFLATE_SOURCES}) - sourcemeta_add_default_options(PRIVATE libdeflate) - - # Check if the assembler supports ARM dot-product (udot) instructions. - if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64|ARM64") - include(CheckCSourceCompiles) - if(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND - CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 14) - check_c_source_compiles(" - #include - __attribute__((target(\"+dotprod\"))) - int test(void) { - uint32x4_t a = vdupq_n_u32(0); - uint8x16_t b = vdupq_n_u8(0); - uint8x16_t c = vdupq_n_u8(0); - a = vdotq_u32(a, b, c); - return (int)vgetq_lane_u32(a, 0); - } - int main(void) { return test(); } - " LIBDEFLATE_HAS_DOTPROD_ASSEMBLER) - else() - set(LIBDEFLATE_SAVED_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}") - set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -march=armv8.2-a+dotprod") - check_c_source_compiles(" - #include - int main(void) { - uint32x4_t a = vdupq_n_u32(0); - uint8x16_t b = vdupq_n_u8(0); - uint8x16_t c = vdupq_n_u8(0); - a = vdotq_u32(a, b, c); - return 0; - } - " LIBDEFLATE_HAS_DOTPROD_ASSEMBLER) - set(CMAKE_REQUIRED_FLAGS "${LIBDEFLATE_SAVED_CMAKE_REQUIRED_FLAGS}") - endif() - if(NOT LIBDEFLATE_HAS_DOTPROD_ASSEMBLER) - target_compile_definitions(libdeflate PRIVATE - LIBDEFLATE_ASSEMBLER_DOES_NOT_SUPPORT_DOTPROD) - endif() - - if(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND - CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 14) - check_c_source_compiles(" - #include - __attribute__((target(\"+crypto,+crc,+sha3\"))) - int test(void) { - uint8x16_t a = vdupq_n_u8(0); - uint8x16_t b = vdupq_n_u8(0); - uint8x16_t c = vdupq_n_u8(0); - a = veor3q_u8(a, b, c); - return (int)vgetq_lane_u8(a, 0); - } - int main(void) { return test(); } - " LIBDEFLATE_HAS_SHA3_ASSEMBLER) - else() - set(LIBDEFLATE_SAVED_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}") - set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -march=armv8.2-a+crypto+crc+sha3") - check_c_source_compiles(" - #include - int main(void) { - uint8x16_t a = vdupq_n_u8(0); - uint8x16_t b = vdupq_n_u8(0); - uint8x16_t c = vdupq_n_u8(0); - a = veor3q_u8(a, b, c); - return 0; - } - " LIBDEFLATE_HAS_SHA3_ASSEMBLER) - set(CMAKE_REQUIRED_FLAGS "${LIBDEFLATE_SAVED_CMAKE_REQUIRED_FLAGS}") - endif() - if(NOT LIBDEFLATE_HAS_SHA3_ASSEMBLER) - target_compile_definitions(libdeflate PRIVATE - LIBDEFLATE_ASSEMBLER_DOES_NOT_SUPPORT_SHA3) - endif() - endif() - - target_include_directories(libdeflate PUBLIC - "$") - - target_include_directories(libdeflate PRIVATE - "${LIBDEFLATE_LIB_DIR}") - - # Marking every entry point as visible would publish this library from the - # one it is merged into, where it can collide with a real installation of it - target_compile_definitions(libdeflate PRIVATE LIBDEFLATE_EXPORT_SYM=) - - # The processor feature detection this library performs on Linux reaches for - # interfaces that the C library only declares outside the strict ISO mode - # this project otherwise compiles C in - if(SOURCEMETA_OS_LINUX) - target_compile_definitions(libdeflate PRIVATE _GNU_SOURCE) - endif() - - if(SOURCEMETA_COMPILER_LLVM OR SOURCEMETA_COMPILER_GCC) - target_compile_options(libdeflate PRIVATE -Wno-conversion) - target_compile_options(libdeflate PRIVATE -Wno-sign-conversion) - target_compile_options(libdeflate PRIVATE -Wno-sign-compare) - target_compile_options(libdeflate PRIVATE -Wno-implicit-int-conversion) - target_compile_options(libdeflate PRIVATE -Wno-shorten-64-to-32) - target_compile_options(libdeflate PRIVATE -Wno-unused-parameter) - endif() - - if(SOURCEMETA_COMPILER_MSVC) - target_compile_options(libdeflate PRIVATE /wd4113) - target_compile_options(libdeflate PRIVATE /wd4244) - target_compile_options(libdeflate PRIVATE /wd4267) - endif() - - add_library(LibDeflate::LibDeflate ALIAS libdeflate) - - set(LibDeflate_FOUND ON) -endif() diff --git a/patches/libdeflate/0001-Let-the-build-decide-whether-symbols-are-marked-for-e.patch b/patches/libdeflate/0001-Let-the-build-decide-whether-symbols-are-marked-for-e.patch deleted file mode 100644 index 9132ba364c..0000000000 --- a/patches/libdeflate/0001-Let-the-build-decide-whether-symbols-are-marked-for-e.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Juan Cruz Viotti -Date: Sat, 5 Sep 2026 00:00:00 -0300 -Subject: [PATCH] Let the build decide whether symbols are marked for export - -This library marks every entry point with default visibility whenever -the compiler is GCC or Clang, with no way to ask for anything else. A -build that compiles this library into a larger one rather than shipping -it on its own then publishes these symbols from whatever binary they end -up in, where they collide with a real installation of this library. - -Honour a definition the build provides, leaving the existing behaviour -in place for everyone else. ---- -diff --git a/lib/lib_common.h b/lib/lib_common.h -index faedfcb03..9246ca1e5 100644 ---- a/lib/lib_common.h -+++ b/lib/lib_common.h -@@ -13,6 +13,12 @@ - # error "lib_common.h must always be included before libdeflate.h" - #endif - -+/* -+ * A build that compiles this library into a larger one does not want its -+ * symbols marked for export, as they would then leave whatever binary it -+ * ends up in. Such a build can say so by defining this itself. -+ */ -+#ifndef LIBDEFLATE_EXPORT_SYM - #if defined(LIBDEFLATE_DLL) && (defined(_WIN32) || defined(__CYGWIN__)) - # define LIBDEFLATE_EXPORT_SYM __declspec(dllexport) - #elif defined(__GNUC__) -@@ -20,6 +26,7 @@ - #else - # define LIBDEFLATE_EXPORT_SYM - #endif -+#endif - - /* - * On i386, gcc assumes that the stack is 16-byte aligned at function entry. diff --git a/src/core/gzip/CMakeLists.txt b/src/core/gzip/CMakeLists.txt index 4365ee11ab..f9516f8df6 100644 --- a/src/core/gzip/CMakeLists.txt +++ b/src/core/gzip/CMakeLists.txt @@ -4,9 +4,6 @@ sourcemeta_library(NAMESPACE sourcemeta PROJECT core NAME gzip target_link_libraries(sourcemeta_core_gzip PRIVATE sourcemeta::core::crypto) -# Way faster for full buffer decompression -target_link_libraries(sourcemeta_core_gzip PRIVATE LibDeflate::LibDeflate) - if(SOURCEMETA_CORE_INSTALL) sourcemeta_library_install(NAMESPACE sourcemeta PROJECT core NAME gzip) endif() diff --git a/src/core/gzip/bit_reader.h b/src/core/gzip/bit_reader.h deleted file mode 100644 index bbffdae05e..0000000000 --- a/src/core/gzip/bit_reader.h +++ /dev/null @@ -1,155 +0,0 @@ -#ifndef SOURCEMETA_CORE_GZIP_BIT_READER_H_ -#define SOURCEMETA_CORE_GZIP_BIT_READER_H_ - -#include - -#include // std::array -#include // assert -#include // std::size_t -#include // std::uint8_t, std::uint32_t, std::uint64_t -#include // std::istream - -namespace sourcemeta::core { - -class BitReader { -public: - BitReader(std::istream &source) : source_{&source} {} - - auto read_bits(const unsigned int count) -> std::uint32_t { - const auto value{this->peek_bits(count)}; - this->consume_bits(count); - return value; - } - - auto peek_bits(const unsigned int count) -> std::uint32_t { - // Callers in this module always pass count in [0, 32]; larger shifts - // would be undefined behaviour against the 64-bit accumulator. The - // assert documents the contract without paying a release-build cost - assert(count <= 32); - if (this->bits_available_ < count) { - this->refill_for(count); - } - const auto mask{(static_cast(1) << count) - 1}; - return static_cast(this->accumulator_ & mask); - } - - auto consume_bits(const unsigned int count) -> void { - // Consuming more bits than are buffered would underflow the unsigned - // counter. Every call site is preceded by a peek or refill that - // guarantees enough bits, so the assert documents the contract - assert(count <= this->bits_available_); - this->accumulator_ >>= count; - this->bits_available_ -= count; - } - - auto align_to_byte() -> void { - const auto trailing_bits{this->bits_available_ % 8}; - this->accumulator_ >>= trailing_bits; - this->bits_available_ -= trailing_bits; - } - - auto read_byte() -> std::uint8_t { - // Reading a byte while 1 to 7 bits are buffered would return a byte from - // ahead of them. Every call site is byte-aligned, so the assert documents - // the invariant without paying a release-build cost - assert(this->bits_available_ % 8 == 0); - if (this->bits_available_ >= 8) { - const auto value{static_cast(this->accumulator_ & 0xff)}; - this->accumulator_ >>= 8; - this->bits_available_ -= 8; - return value; - } - return this->pull_source_byte(); - } - - auto try_read_byte(std::uint8_t &byte) -> bool { - assert(this->bits_available_ % 8 == 0); - if (this->bits_available_ >= 8) { - byte = static_cast(this->accumulator_ & 0xff); - this->accumulator_ >>= 8; - this->bits_available_ -= 8; - return true; - } - if (this->buffer_position_ >= this->buffer_size_ && - !this->try_refill_buffer()) { - return false; - } - byte = this->buffer_[this->buffer_position_++]; - return true; - } - - auto read_bytes(std::uint8_t *destination, const std::size_t count) -> void { - for (std::size_t index = 0; index < count; ++index) { - destination[index] = this->read_byte(); - } - } - -private: - static constexpr std::size_t SOURCE_BUFFER_SIZE{4096}; - - auto pull_source_byte() -> std::uint8_t { - if (this->buffer_position_ >= this->buffer_size_) { - this->refill_buffer(); - } - return this->buffer_[this->buffer_position_++]; - } - - auto refill_for(const unsigned int count) -> void { - // Fast path: if 4 bytes available in the input buffer and the - // accumulator has room for 32 more bits, load 4 bytes at once. - // RFC 1951 packs bits LSB-first within each byte, so the first byte - // contributes the low 8 bits of the loaded word regardless of host - // endianness. Construct the 32-bit value explicitly to keep this - // portable on big-endian hosts - if (this->bits_available_ <= 32 && - this->buffer_position_ + 4 <= this->buffer_size_) { - const std::uint32_t four_bytes{ - static_cast(this->buffer_[this->buffer_position_]) | - (static_cast(this->buffer_[this->buffer_position_ + 1]) - << 8) | - (static_cast(this->buffer_[this->buffer_position_ + 2]) - << 16) | - (static_cast(this->buffer_[this->buffer_position_ + 3]) - << 24)}; - this->accumulator_ |= static_cast(four_bytes) - << this->bits_available_; - this->bits_available_ += 32; - this->buffer_position_ += 4; - } - while (this->bits_available_ < count) { - const auto byte{this->pull_source_byte()}; - this->accumulator_ |= static_cast(byte) - << this->bits_available_; - this->bits_available_ += 8; - } - } - - auto refill_buffer() -> void { - if (!this->try_refill_buffer()) { - throw GZIPError{"Unexpected end of source stream"}; - } - } - - auto try_refill_buffer() -> bool { - this->source_->read(reinterpret_cast(this->buffer_.data()), - SOURCE_BUFFER_SIZE); - const auto bytes_read{static_cast(this->source_->gcount())}; - if (bytes_read == 0) { - return false; - } - this->buffer_size_ = bytes_read; - this->buffer_position_ = 0; - return true; - } - - std::istream *source_; - std::uint64_t accumulator_{0}; - unsigned int bits_available_{0}; - std::array buffer_{}; - std::size_t buffer_position_{0}; - std::size_t buffer_size_{0}; -}; - -} // namespace sourcemeta::core - -#endif diff --git a/src/core/gzip/deflate.h b/src/core/gzip/deflate.h index d45ddada15..509fcb28a8 100644 --- a/src/core/gzip/deflate.h +++ b/src/core/gzip/deflate.h @@ -1,409 +1,1124 @@ #ifndef SOURCEMETA_CORE_GZIP_DEFLATE_H_ #define SOURCEMETA_CORE_GZIP_DEFLATE_H_ -#include "bit_reader.h" -#include "huffman.h" +#include "inflate.h" -#include - -#include // std::min +#include // std::sort, std::fill, std::min #include // std::array -#include // assert +#include // std::endian, std::byteswap, std::countr_zero #include // std::size_t -#include // std::uint8_t, std::uint16_t -#include // std::memcpy +#include // std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t +#include // std::memcpy +#include // std::vector namespace sourcemeta::core { +inline constexpr std::size_t DEFLATE_MAXIMUM_MATCH_LENGTH{258}; inline constexpr std::size_t DEFLATE_WINDOW_SIZE{32768}; -inline constexpr std::size_t DEFLATE_WINDOW_MASK{DEFLATE_WINDOW_SIZE - 1}; -static_assert((DEFLATE_WINDOW_SIZE & DEFLATE_WINDOW_MASK) == 0, - "DEFLATE_WINDOW_SIZE must be a power of two"); - -// RFC 1951 section 3.2.5 length codes 257-285 -inline constexpr std::array DEFLATE_LENGTH_BASE{ - {3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, - 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258}}; - -inline constexpr std::array DEFLATE_LENGTH_EXTRA{ - {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, - 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0}}; - -// RFC 1951 section 3.2.5 distance codes 0-29 -inline constexpr std::array DEFLATE_DISTANCE_BASE{ - {1, 2, 3, 4, 5, 7, 9, 13, 17, 25, - 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, - 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577}}; - -inline constexpr std::array DEFLATE_DISTANCE_EXTRA{ - {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, - 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}}; - -// RFC 1951 section 3.2.7 code-length-of-codes order -inline constexpr std::array DEFLATE_CODE_LENGTH_ORDER{ - {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}}; - -class DeflateDecoder { +inline constexpr std::size_t DEFLATE_MAXIMUM_STORED_LENGTH{65535}; +inline constexpr std::uint32_t DEFLATE_END_OF_BLOCK{256}; + +// The index of the length symbol, counting from symbol 257, for every match +// length as per RFC 1951 section 3.2.5 +inline constexpr std::array DEFLATE_LENGTH_SLOTS{ + {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, + 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, + 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28}}; + +// The distance symbol for every distance up to 256 in the first half, and for +// every larger distance indexed by its value minus one divided by 128 in the +// second half, as every symbol past 256 spans whole multiples of 128 +inline constexpr std::array DEFLATE_DISTANCE_SLOTS{ + {0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, + 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, 18, 18, 19, 19, 20, 20, + 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, + 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29}}; + +inline auto deflate_distance_slot(const std::size_t distance) -> std::size_t { + return distance <= 256 ? DEFLATE_DISTANCE_SLOTS[distance - 1] + : DEFLATE_DISTANCE_SLOTS[256 + ((distance - 1) >> 7)]; +} + +// Packs bits starting from the least significant bit of each byte as per +// RFC 1951 section 3.1.1. The destination must always have room for a whole +// word past the bytes written so far +class DeflateBitWriter { public: - DeflateDecoder(BitReader &reader) : reader_{&reader} {} + DeflateBitWriter(std::uint8_t *output) : next_{output} {} - auto decompress(std::uint8_t *output, const std::size_t output_size) - -> std::size_t { - std::size_t produced{0}; - - while (!this->stream_ended_) { - switch (this->state_) { - case State::BlockHeader: - if (this->final_block_) { - this->state_ = State::End; - } else { - this->start_block(); - } - break; - case State::StoredBlock: - this->process_stored_block(output, output_size, produced); - if (this->state_ == State::StoredBlock) { - return produced; - } - break; - case State::HuffmanBlock: - this->process_huffman_block(output, output_size, produced); - if (this->state_ == State::HuffmanBlock) { - return produced; - } - break; - case State::End: - this->reader_->align_to_byte(); - this->stream_ended_ = true; - return produced; - } - } - return produced; + // The buffer holds 64 bits, and a flush leaves at most seven of them, so + // callers flush before the bits added since the last flush exceed 57 + auto add(const std::uint32_t value, const unsigned int count) -> void { + this->buffer_ |= static_cast(value) << this->count_; + this->count_ += count; } - [[nodiscard]] auto stream_ended() const -> bool { - return this->stream_ended_; + auto flush() -> void { + std::uint64_t word{this->buffer_}; + if (std::endian::native == std::endian::big) { + word = std::byteswap(word); + } + + std::memcpy(this->next_, &word, sizeof(word)); + const auto bytes{this->count_ >> 3}; + this->next_ += bytes; + this->buffer_ >>= bytes * 8; + this->count_ -= bytes * 8; } - auto reset() -> void { - this->state_ = State::BlockHeader; - this->final_block_ = false; - this->stream_ended_ = false; - this->stored_remaining_ = 0; - this->pending_copy_length_ = 0; - this->pending_copy_distance_ = 0; - this->window_position_ = 0; - this->bytes_written_ = 0; + auto align() -> void { + this->flush(); + if (this->count_ > 0) { + this->next_ += 1; + } + + this->buffer_ = 0; + this->count_ = 0; } -private: - enum class State : std::uint8_t { - BlockHeader, - StoredBlock, - HuffmanBlock, - End, - }; - - auto start_block() -> void { - this->final_block_ = this->reader_->read_bits(1) != 0; - const auto btype{this->reader_->read_bits(2)}; - switch (btype) { - case 0: - this->start_stored_block(); - break; - case 1: - this->build_fixed_trees(); - this->state_ = State::HuffmanBlock; - break; - case 2: - this->read_dynamic_header(); - this->state_ = State::HuffmanBlock; - break; - default: - throw GZIPError{"Reserved deflate block type"}; + auto bytes(const std::uint8_t *data, const std::size_t size) -> void { + if (size > 0) { + std::memcpy(this->next_, data, size); } + + this->next_ += size; } - auto start_stored_block() -> void { - this->reader_->align_to_byte(); - this->stored_remaining_ = - static_cast(this->reader_->read_byte()) | - static_cast( - static_cast(this->reader_->read_byte()) << 8); - const auto nlen_lo{this->reader_->read_byte()}; - const auto nlen_hi{this->reader_->read_byte()}; - const std::uint16_t nlen{static_cast( - static_cast(nlen_lo) | - static_cast(static_cast(nlen_hi) << 8))}; - const std::uint16_t expected_nlen{ - static_cast(~this->stored_remaining_)}; - if (expected_nlen != nlen) { - throw GZIPError{"Stored block LEN/NLEN mismatch"}; - } - this->state_ = State::StoredBlock; + [[nodiscard]] auto pending_bits() const -> unsigned int { + return this->count_; } - auto build_fixed_trees() -> void { - std::array literal_lengths{}; - for (std::size_t index = 0; index < 144; ++index) { - literal_lengths[index] = 8; + [[nodiscard]] auto position() const -> std::uint8_t * { return this->next_; } + +private: + std::uint8_t *next_; + std::uint64_t buffer_{0}; + unsigned int count_{0}; +}; + +// Scratch space for computing length-limited codes with the package-merge +// algorithm, where every list holds at most twice as many items as symbols +class DeflateCodeBuilder { +public: + // Assigns every symbol with a nonzero frequency a codeword length of at most + // the given limit, minimizing the total encoded size. The resulting code is + // always complete, adding placeholder symbols when fewer than two are used + auto lengths(const std::uint32_t *frequencies, const std::size_t symbol_count, + const unsigned int limit, std::uint8_t *lengths) -> void { + std::fill(lengths, lengths + symbol_count, std::uint8_t{0}); + std::size_t used{0}; + for (std::size_t symbol = 0; symbol < symbol_count; ++symbol) { + if (frequencies[symbol] != 0) { + this->symbols_[used] = static_cast(symbol); + used += 1; + } + } + + if (used < 2) { + const std::size_t first{used == 1 + ? static_cast(this->symbols_[0]) + : std::size_t{0}}; + lengths[first] = 1; + lengths[first == 0 ? 1 : 0] = 1; + return; } - for (std::size_t index = 144; index < 256; ++index) { - literal_lengths[index] = 9; + + std::sort( + this->symbols_.begin(), this->symbols_.begin() + used, + [frequencies](const std::uint16_t left, const std::uint16_t right) { + return frequencies[left] != frequencies[right] + ? frequencies[left] < frequencies[right] + : left < right; + }); + + // A plain Huffman code is optimal whenever no codeword exceeds the limit, + // and building it takes linear time over the sorted frequencies + if (this->unlimited_lengths(frequencies, used, limit, lengths)) { + return; } - for (std::size_t index = 256; index < 280; ++index) { - literal_lengths[index] = 7; + + std::fill(lengths, lengths + symbol_count, std::uint8_t{0}); + + // The deepest list holds only the symbols. Every shallower list merges the + // symbols with the pairs of consecutive items of the list below it + const std::size_t deepest{limit - 1}; + for (std::size_t index = 0; index < used; ++index) { + this->weights_[deepest][index] = frequencies[this->symbols_[index]]; + this->leaves_[deepest][index] = 1; } - for (std::size_t index = 280; index < 288; ++index) { - literal_lengths[index] = 8; + + this->sizes_[deepest] = used; + for (std::size_t level = deepest; level > 0; --level) { + const std::size_t below{level}; + const std::size_t current{level - 1}; + const std::size_t packages{this->sizes_[below] / 2}; + std::size_t leaf{0}; + std::size_t package{0}; + std::size_t size{0}; + while (leaf < used || package < packages) { + const std::uint64_t package_weight{ + package < packages ? this->weights_[below][package * 2] + + this->weights_[below][(package * 2) + 1] + : 0}; + if (package >= packages || + (leaf < used && + frequencies[this->symbols_[leaf]] <= package_weight)) { + this->weights_[current][size] = frequencies[this->symbols_[leaf]]; + this->leaves_[current][size] = 1; + leaf += 1; + } else { + this->weights_[current][size] = package_weight; + this->leaves_[current][size] = 0; + package += 1; + } + + size += 1; + } + + this->sizes_[current] = size; } - this->literal_length_tree_.build(literal_lengths.data(), - literal_lengths.size()); - std::array distance_lengths{}; - for (auto &length : distance_lengths) { - length = 5; + // Selecting the cheapest items of the shallowest list determines how many + // items of every deeper list take part, and a symbol gains one bit of + // length for every list where it is selected + std::size_t selected{(used * 2) - 2}; + for (std::size_t level = 0; level < limit; ++level) { + std::size_t leaves{0}; + for (std::size_t index = 0; index < selected; ++index) { + leaves += this->leaves_[level][index]; + } + + for (std::size_t index = 0; index < leaves; ++index) { + lengths[this->symbols_[index]] += 1; + } + + selected = (selected - leaves) * 2; } - this->distance_tree_.build(distance_lengths.data(), distance_lengths.size(), - true); } - auto read_dynamic_header() -> void { - const auto hlit{this->reader_->read_bits(5) + 257}; - const auto hdist{this->reader_->read_bits(5) + 1}; - const auto hclen{this->reader_->read_bits(4) + 4}; + // Builds a Huffman code by repeatedly joining the two lightest nodes, taking + // them from either the sorted symbols or the internal nodes created so far, + // which are created in order of weight. Returns whether every codeword fits + // within the limit + auto unlimited_lengths(const std::uint32_t *frequencies, + const std::size_t used, const unsigned int limit, + std::uint8_t *lengths) -> bool { + std::size_t leaf{0}; + std::size_t internal{0}; + for (std::size_t node = 0; node + 1 < used; ++node) { + std::uint64_t weight{0}; + for (std::size_t child = 0; child < 2; ++child) { + if (leaf < used && + (internal >= node || frequencies[this->symbols_[leaf]] <= + this->internal_weights_[internal])) { + this->leaf_parents_[leaf] = static_cast(node); + weight += frequencies[this->symbols_[leaf]]; + leaf += 1; + } else { + this->internal_parents_[internal] = static_cast(node); + weight += this->internal_weights_[internal]; + internal += 1; + } + } - // RFC 1951 section 3.2.7 caps the literal/length alphabet at 286 symbols - if (hlit > 286) { - throw GZIPError{"Too many literal/length codes"}; + this->internal_weights_[node] = weight; } - std::array code_length_lengths{}; - for (std::size_t index = 0; index < hclen; ++index) { - code_length_lengths[DEFLATE_CODE_LENGTH_ORDER[index]] = - static_cast(this->reader_->read_bits(3)); + // Every internal node has a later parent, so walking backwards from the + // root assigns every depth after the depth of its parent + const std::size_t root{used - 2}; + this->internal_depths_[root] = 0; + for (std::size_t node = root; node > 0; --node) { + this->internal_depths_[node - 1] = static_cast( + this->internal_depths_[this->internal_parents_[node - 1]] + 1); + } + + for (std::size_t index = 0; index < used; ++index) { + const unsigned int depth{ + static_cast( + this->internal_depths_[this->leaf_parents_[index]]) + + 1U}; + if (depth > limit) { + return false; + } + + lengths[this->symbols_[index]] = static_cast(depth); + } + + return true; + } + +private: + static constexpr std::size_t MAXIMUM_ITEMS{INFLATE_LITERAL_LENGTH_SYMBOLS * + 2}; + + std::array symbols_{}; + std::array, + INFLATE_MAXIMUM_CODEWORD_LENGTH> + weights_{}; + std::array, + INFLATE_MAXIMUM_CODEWORD_LENGTH> + leaves_{}; + std::array sizes_{}; + std::array internal_weights_{}; + std::array internal_parents_{}; + std::array internal_depths_{}; + std::array leaf_parents_{}; +}; + +// Assigns canonical codewords as per RFC 1951 section 3.2.2, bit-reversed so +// they can be written starting from the least significant bit +inline auto deflate_canonical_codes(const std::uint8_t *lengths, + const std::size_t symbol_count, + std::uint16_t *codes) -> void { + std::array counts{}; + for (std::size_t symbol = 0; symbol < symbol_count; ++symbol) { + counts[lengths[symbol]] += 1; + } + + counts[0] = 0; + std::array next{}; + std::uint32_t code{0}; + for (unsigned int length = 1; length <= INFLATE_MAXIMUM_CODEWORD_LENGTH; + ++length) { + code = (code + counts[length - 1]) << 1; + next[length] = code; + } + + for (std::size_t symbol = 0; symbol < symbol_count; ++symbol) { + const auto length{lengths[symbol]}; + if (length != 0) { + codes[symbol] = static_cast( + inflate_reverse_bits(next[length], length)); + next[length] += 1; + } else { + codes[symbol] = 0; } + } +} + +// The fixed codes of RFC 1951 section 3.2.6 +struct DeflateFixedCodes { + std::array + literal_length_lengths{}; + std::array + literal_length_codes{}; + std::array distance_lengths{}; + std::array distance_codes{}; + + DeflateFixedCodes() { + std::fill(this->literal_length_lengths.begin(), + this->literal_length_lengths.begin() + 144, std::uint8_t{8}); + std::fill(this->literal_length_lengths.begin() + 144, + this->literal_length_lengths.begin() + 256, std::uint8_t{9}); + std::fill(this->literal_length_lengths.begin() + 256, + this->literal_length_lengths.begin() + 280, std::uint8_t{7}); + std::fill(this->literal_length_lengths.begin() + 280, + this->literal_length_lengths.end(), std::uint8_t{8}); + deflate_canonical_codes(this->literal_length_lengths.data(), + this->literal_length_lengths.size(), + this->literal_length_codes.data()); + std::fill(this->distance_lengths.begin(), this->distance_lengths.end(), + std::uint8_t{5}); + deflate_canonical_codes(this->distance_lengths.data(), + this->distance_lengths.size(), + this->distance_codes.data()); + } +}; - HuffmanDecoder code_length_tree; - code_length_tree.build(code_length_lengths.data(), - code_length_lengths.size()); +// A code length sequence compressed with the repeat symbols of RFC 1951 +// section 3.2.7 +struct DeflateCodeLengthRun { + std::array + symbols{}; + std::array + extra{}; + std::size_t size{0}; - std::array all_lengths{}; + auto push(const std::uint8_t symbol, const std::uint8_t value) -> void { + this->symbols[this->size] = symbol; + this->extra[this->size] = value; + this->size += 1; + } + + auto encode(const std::uint8_t *lengths, const std::size_t count) -> void { + this->size = 0; std::size_t index{0}; - while (index < hlit + hdist) { - const auto symbol{code_length_tree.decode(*this->reader_)}; - if (symbol < 16) { - all_lengths[index++] = static_cast(symbol); - } else if (symbol == 16) { - if (index == 0) { - throw GZIPError{"Repeat-previous code length with no previous"}; - } - const auto previous{all_lengths[index - 1]}; - const auto repeats{this->reader_->read_bits(2) + 3}; - for (std::size_t step = 0; step < repeats; ++step) { - if (index >= all_lengths.size()) { - throw GZIPError{"Code length count overflow"}; - } - all_lengths[index++] = previous; + while (index < count) { + const auto value{lengths[index]}; + std::size_t run{1}; + while (index + run < count && lengths[index + run] == value) { + run += 1; + } + + if (value == 0) { + while (run >= 11) { + const auto amount{std::min(run, std::size_t{138})}; + this->push(18, static_cast(amount - 11)); + run -= amount; + index += amount; } - } else if (symbol == 17) { - const auto repeats{this->reader_->read_bits(3) + 3}; - for (std::size_t step = 0; step < repeats; ++step) { - if (index >= all_lengths.size()) { - throw GZIPError{"Code length count overflow"}; - } - all_lengths[index++] = 0; + + if (run >= 3) { + this->push(17, static_cast(run - 3)); + index += run; + run = 0; } } else { - // The code length tree is built over a 19 symbol alphabet, so its - // decoder can never hand back anything past symbol 18 - assert(symbol == 18); - const auto repeats{this->reader_->read_bits(7) + 11}; - for (std::size_t step = 0; step < repeats; ++step) { - if (index >= all_lengths.size()) { - throw GZIPError{"Code length count overflow"}; - } - all_lengths[index++] = 0; + this->push(value, 0); + index += 1; + run -= 1; + while (run >= 3) { + const auto amount{std::min(run, std::size_t{6})}; + this->push(16, static_cast(amount - 3)); + run -= amount; + index += amount; } } + + while (run > 0) { + this->push(value, 0); + index += 1; + run -= 1; + } + } + } +}; + +inline constexpr std::array + DEFLATE_CODE_LENGTH_EXTRA_BITS{ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7}}; + +// A run of literal bytes followed by a match, where a zero length marks a +// trailing run with no match +struct DeflateSequence { + std::uint32_t literals; + std::uint16_t length; + std::uint16_t distance; +}; + +struct DeflateLevel { + unsigned int search_depth; + unsigned int nice_length; + bool lazy; +}; + +// Levels 0 and 1 use dedicated strategies, so their entries are unused +inline constexpr std::array DEFLATE_LEVELS{ + {{.search_depth = 0, .nice_length = 0, .lazy = false}, + {.search_depth = 0, .nice_length = 0, .lazy = false}, + {.search_depth = 4, .nice_length = 16, .lazy = false}, + {.search_depth = 8, .nice_length = 32, .lazy = false}, + {.search_depth = 16, .nice_length = 64, .lazy = false}, + {.search_depth = 16, .nice_length = 32, .lazy = true}, + {.search_depth = 32, .nice_length = 128, .lazy = true}, + {.search_depth = 64, .nice_length = 128, .lazy = true}, + {.search_depth = 128, .nice_length = 258, .lazy = true}, + {.search_depth = 256, .nice_length = 258, .lazy = true}, + {.search_depth = 512, .nice_length = 258, .lazy = true}, + {.search_depth = 1024, .nice_length = 258, .lazy = true}, + {.search_depth = 4096, .nice_length = 258, .lazy = true}}}; + +// Compresses a whole buffer into a raw deflate stream (RFC 1951) +class DeflateEncoder { +public: + // Symbols are accumulated for this much input before a block is emitted + // with codes tailored to it. The fastest level uses shorter blocks with a cap + // on their sequences, so its codes follow changes in the input more closely, + // and a shorter remainder is merged into the last block instead of forming a + // tiny one + static constexpr std::size_t BLOCK_INPUT_LENGTH{262144}; + static constexpr std::size_t FAST_BLOCK_INPUT_LENGTH{65535}; + static constexpr std::size_t FAST_BLOCK_SEQUENCES{8192}; + static constexpr std::size_t MINIMUM_BLOCK_LENGTH{5000}; + + // Every match covers at least four bytes and a block overshoots its input + // length by at most one match, which bounds the sequences per block + DeflateEncoder(const int level, const std::size_t size) + : level_{level}, + sequences_(((std::min(size, BLOCK_INPUT_LENGTH + MINIMUM_BLOCK_LENGTH) + + DEFLATE_MAXIMUM_MATCH_LENGTH) / + 4) + + 2), + head_(level == 0 ? 0 : HASH_SIZE), + previous_(level <= 1 ? 0 : DEFLATE_WINDOW_SIZE) {} + + // The largest possible stream for an input of the given size, including a + // whole word of room for flushing bits + static auto bound(const std::size_t size) -> std::size_t { + const std::size_t stored_chunks{(size + DEFLATE_MAXIMUM_STORED_LENGTH - 1) / + DEFLATE_MAXIMUM_STORED_LENGTH}; + // A block that is not the last covers at least as many bytes as the + // sequences it can hold, each of which spans at least four bytes + const std::size_t blocks{(size / (FAST_BLOCK_SEQUENCES * 4)) + 1}; + return size + (5 * (stored_chunks + blocks)) + 16; + } + + auto compress(const std::uint8_t *input, const std::size_t size, + std::uint8_t *output) -> std::size_t { + DeflateBitWriter writer{output}; + if (this->level_ == 0) { + this->emit_stored(writer, input, size, true); + writer.align(); + return static_cast(writer.position() - output); } - if (index != hlit + hdist) { - throw GZIPError{"Code length count overflow"}; + std::size_t position{0}; + while (true) { + const std::size_t block_start{position}; + const std::size_t block_length{this->level_ == 1 ? FAST_BLOCK_INPUT_LENGTH + : BLOCK_INPUT_LENGTH}; + const std::size_t block_end{size - position < + block_length + MINIMUM_BLOCK_LENGTH + ? size + : position + block_length}; + this->literal_length_frequencies_.fill(0); + this->distance_frequencies_.fill(0); + this->sequence_count_ = 0; + if (this->level_ == 1) { + position = this->match_fast(input, size, position, block_end); + } else { + position = this->match_chains(input, size, position, block_end); + } + + const bool final{position >= size}; + this->emit_block(writer, input + block_start, position - block_start, + final); + if (final) { + break; + } } - this->literal_length_tree_.build(all_lengths.data(), hlit); - this->distance_tree_.build(all_lengths.data() + hlit, hdist); + writer.align(); + return static_cast(writer.position() - output); } - auto process_stored_block(std::uint8_t *output, const std::size_t output_size, - std::size_t &produced) -> void { - while (this->stored_remaining_ > 0 && produced < output_size) { - const auto byte{this->reader_->read_byte()}; - this->emit(byte, output, output_size, produced); - --this->stored_remaining_; +private: + static constexpr unsigned int HASH_BITS{16}; + static constexpr std::size_t HASH_SIZE{std::size_t{1} << HASH_BITS}; + static constexpr std::size_t WINDOW_MASK{DEFLATE_WINDOW_SIZE - 1}; + + // The fastest level spreads the same table over twice as few buckets, each + // holding the two most recent positions + static constexpr unsigned int FAST_HASH_BITS{HASH_BITS - 1}; + static constexpr std::size_t FAST_NICE_LENGTH{32}; + + static auto load_32(const std::uint8_t *data) -> std::uint32_t { + std::uint32_t word{0}; + std::memcpy(&word, data, sizeof(word)); + if (std::endian::native == std::endian::big) { + word = std::byteswap(word); } - if (this->stored_remaining_ == 0) { - this->state_ = State::BlockHeader; + + return word; + } + + static auto load_64(const std::uint8_t *data) -> std::uint64_t { + std::uint64_t word{0}; + std::memcpy(&word, data, sizeof(word)); + if (std::endian::native == std::endian::big) { + word = std::byteswap(word); } + + return word; + } + + static auto hash(const std::uint32_t word) -> std::uint32_t { + return (word * 0x1e35a7bdU) >> (32 - HASH_BITS); + } + + static auto hash_fast(const std::uint32_t word) -> std::uint32_t { + return (word * 0x1e35a7bdU) >> (32 - FAST_HASH_BITS); } - auto process_huffman_block(std::uint8_t *output, - const std::size_t output_size, - std::size_t &produced) -> void { - if (this->pending_copy_length_ > 0) { - this->copy_backref(output, output_size, produced); - if (this->pending_copy_length_ > 0) { - return; + // Counts how many bytes match between two positions, up to a limit + static auto extend(const std::uint8_t *left, const std::uint8_t *right, + const std::size_t limit) -> std::size_t { + std::size_t length{0}; + while (length + 8 <= limit) { + const auto difference{load_64(left + length) ^ load_64(right + length)}; + if (difference != 0) { + return length + + (static_cast(std::countr_zero(difference)) / 8); } + + length += 8; } - while (produced < output_size) { - const auto symbol{this->literal_length_tree_.decode(*this->reader_)}; - if (symbol < 256) { - this->window_[this->window_position_] = - static_cast(symbol); - this->window_position_ = - (this->window_position_ + 1) & DEFLATE_WINDOW_MASK; - if (this->bytes_written_ < DEFLATE_WINDOW_SIZE) { - ++this->bytes_written_; + while (length < limit && left[length] == right[length]) { + length += 1; + } + + return length; + } + + auto record_literal(const std::uint8_t byte) -> void { + this->literal_length_frequencies_[byte] += 1; + this->pending_literals_ += 1; + } + + auto record_match(const std::size_t length, const std::size_t distance) + -> void { + this->literal_length_frequencies_[257 + DEFLATE_LENGTH_SLOTS[length]] += 1; + this->distance_frequencies_[deflate_distance_slot(distance)] += 1; + this->sequences_[this->sequence_count_] = { + .literals = this->pending_literals_, + .length = static_cast(length), + .distance = static_cast(distance)}; + this->sequence_count_ += 1; + this->pending_literals_ = 0; + } + + auto finish_sequences() -> void { + this->sequences_[this->sequence_count_] = { + .literals = this->pending_literals_, .length = 0, .distance = 0}; + this->sequence_count_ += 1; + this->pending_literals_ = 0; + } + + // Greedy matching against the two most recent positions sharing a hash, + // keeping the running state in locals as the tables could otherwise alias it + auto match_fast(const std::uint8_t *input, const std::size_t size, + std::size_t position, const std::size_t block_end) + -> std::size_t { + auto *const table{this->head_.data()}; + auto *const literal_length_frequencies{ + this->literal_length_frequencies_.data()}; + auto *const distance_frequencies{this->distance_frequencies_.data()}; + auto *const sequences{this->sequences_.data()}; + std::size_t sequence_count{this->sequence_count_}; + std::uint32_t literals{this->pending_literals_}; + const std::size_t hash_end{size < 8 ? 0 : size - 7}; + const std::size_t match_end{std::min(block_end, hash_end)}; + while (position < match_end && sequence_count < FAST_BLOCK_SEQUENCES) { + const auto word{load_32(input + position)}; + const std::size_t bucket{static_cast(hash_fast(word)) * 2}; + const auto current{static_cast(position)}; + const auto first{table[bucket]}; + const auto second{table[bucket + 1]}; + table[bucket + 1] = first; + table[bucket] = current; + const std::size_t limit{ + std::min(DEFLATE_MAXIMUM_MATCH_LENGTH, size - position)}; + std::size_t best_length{0}; + std::size_t best_distance{0}; + // Positions are stored modulo 2^32 and the second candidate is always + // older than the first one, so it is only worth checking when the first + // one is still within the window + const std::uint32_t first_distance{current - first}; + if (first_distance - 1 < DEFLATE_WINDOW_SIZE) { + const auto *const match{input + position - first_distance}; + if (load_32(match) == word) { + best_length = 4 + extend(input + position + 4, match + 4, limit - 4); + best_distance = first_distance; } - output[produced++] = static_cast(symbol); - } else if (symbol == 256) { - this->state_ = State::BlockHeader; - return; - } else if (symbol <= 285) { - const auto length_index{static_cast(symbol - 257)}; + + const std::uint32_t second_distance{current - second}; + if (best_length < std::min(FAST_NICE_LENGTH, limit) && + second_distance - 1 < DEFLATE_WINDOW_SIZE) { + const auto *const other{input + position - second_distance}; + if (load_32(other) == word && + other[best_length] == input[position + best_length]) { + const std::size_t length{ + 4 + extend(input + position + 4, other + 4, limit - 4)}; + if (length > best_length) { + best_length = length; + best_distance = second_distance; + } + } + } + } + + if (best_length == 0) { + literal_length_frequencies[input[position]] += 1; + literals += 1; + position += 1; + continue; + } + + literal_length_frequencies[257 + DEFLATE_LENGTH_SLOTS[best_length]] += 1; + distance_frequencies[deflate_distance_slot(best_distance)] += 1; + sequences[sequence_count] = { + .literals = literals, + .length = static_cast(best_length), + .distance = static_cast(best_distance)}; + sequence_count += 1; + literals = 0; + const std::size_t end{position + best_length}; + const std::size_t insert_end{std::min(end, hash_end)}; + for (position += 1; position < insert_end; ++position) { + const std::size_t covered{ + static_cast(hash_fast(load_32(input + position))) * 2}; + table[covered + 1] = table[covered]; + table[covered] = static_cast(position); + } + + position = end; + } + + while (position < block_end && sequence_count < FAST_BLOCK_SEQUENCES) { + literal_length_frequencies[input[position]] += 1; + literals += 1; + position += 1; + } + + this->sequence_count_ = sequence_count; + this->pending_literals_ = literals; + this->finish_sequences(); + return position; + } + + auto insert(const std::uint8_t *input, const std::size_t position) -> void { + const auto slot{hash(load_32(input + position))}; + this->previous_[position & WINDOW_MASK] = this->head_[slot]; + this->head_[slot] = static_cast(position); + } + + // Walks the hash chain from the most recent position, stopping once the + // chain no longer moves further back in the window + auto find(const std::uint8_t *input, const std::size_t size, + const std::size_t position, std::size_t &best_distance) + -> std::size_t { + const auto &settings{ + DEFLATE_LEVELS[static_cast(this->level_)]}; + const std::size_t limit{ + std::min(DEFLATE_MAXIMUM_MATCH_LENGTH, size - position)}; + const auto word{load_32(input + position)}; + std::size_t best_length{0}; + std::size_t candidate{this->head_[hash(word)]}; + std::size_t previous_distance{0}; + for (unsigned int depth = 0; depth < settings.search_depth; ++depth) { + const std::size_t distance{ + static_cast(static_cast(position) - + static_cast(candidate))}; + if (distance <= previous_distance || distance > DEFLATE_WINDOW_SIZE || + distance > position) { + break; + } + + const auto *match{input + position - distance}; + if ((best_length == 0 || + match[best_length] == input[position + best_length]) && + load_32(match) == word) { const std::size_t length{ - static_cast(DEFLATE_LENGTH_BASE[length_index]) + - static_cast( - this->reader_->read_bits(DEFLATE_LENGTH_EXTRA[length_index]))}; - const auto distance_symbol{this->distance_tree_.decode(*this->reader_)}; - if (distance_symbol >= 30) { - throw GZIPError{"Invalid distance code"}; + 4 + extend(input + position + 4, match + 4, limit - 4)}; + if (length > best_length) { + best_length = length; + best_distance = distance; + if (length >= settings.nice_length || length == limit) { + break; + } + } + } + + previous_distance = distance; + candidate = this->previous_[candidate & WINDOW_MASK]; + } + + return best_length; + } + + auto match_chains(const std::uint8_t *input, const std::size_t size, + std::size_t position, const std::size_t block_end) + -> std::size_t { + const auto &settings{ + DEFLATE_LEVELS[static_cast(this->level_)]}; + std::size_t previous_length{0}; + std::size_t previous_distance{0}; + bool previous_available{false}; + while (position < block_end) { + if (size - position < 8) { + if (previous_available) { + if (previous_length >= 4) { + break; + } + + this->record_literal(input[position - 1]); + previous_available = false; + } + + this->record_literal(input[position]); + position += 1; + continue; + } + + std::size_t distance{0}; + std::size_t length{this->find(input, size, position, distance)}; + this->insert(input, position); + if (length > 0 && length < 4) { + length = 0; + } + + if (previous_available && previous_length >= 4 && + length <= previous_length) { + // The match found one position earlier is at least as good + const std::size_t start{position - 1}; + this->record_match(previous_length, previous_distance); + const std::size_t end{start + previous_length}; + position += 1; + while (position < end && size - position >= 8) { + this->insert(input, position); + position += 1; } - const std::size_t distance{ - static_cast(DEFLATE_DISTANCE_BASE[distance_symbol]) + - static_cast(this->reader_->read_bits( - DEFLATE_DISTANCE_EXTRA[distance_symbol]))}; - if (distance > this->bytes_written_) { - throw GZIPError{"Backref distance exceeds bytes available"}; + + position = end; + previous_available = false; + previous_length = 0; + continue; + } + + if (!settings.lazy) { + if (length < 4) { + this->record_literal(input[position]); + position += 1; + continue; } - this->pending_copy_length_ = length; - this->pending_copy_distance_ = distance; - this->copy_backref(output, output_size, produced); - if (this->pending_copy_length_ > 0) { - return; + + this->record_match(length, distance); + const std::size_t end{position + length}; + position += 1; + while (position < end && size - position >= 8) { + this->insert(input, position); + position += 1; } + + position = end; + continue; + } + + if (previous_available) { + this->record_literal(input[position - 1]); + } + + if (length >= settings.nice_length) { + this->record_match(length, distance); + const std::size_t end{position + length}; + position += 1; + while (position < end && size - position >= 8) { + this->insert(input, position); + position += 1; + } + + position = end; + previous_available = false; + previous_length = 0; + continue; + } + + previous_available = true; + previous_length = length; + previous_distance = distance; + position += 1; + } + + if (previous_available) { + if (previous_length >= 4 && position - 1 + previous_length <= size) { + const std::size_t start{position - 1}; + this->record_match(previous_length, previous_distance); + position = start + previous_length; } else { - throw GZIPError{"Invalid literal/length code"}; + this->record_literal(input[position - 1]); } } - } - auto copy_backref(std::uint8_t *output, const std::size_t output_size, - std::size_t &produced) -> void { - const auto remaining{output_size - produced}; - const auto to_copy{std::min(this->pending_copy_length_, remaining)}; + this->finish_sequences(); + return position; + } - if (this->pending_copy_distance_ >= this->pending_copy_length_) { - // Source range does not overlap with the bytes about to be written - this->copy_backref_non_overlapping(output, produced, to_copy); - } else { - // RLE-style overlap: must propagate byte by byte - this->copy_backref_overlapping(output, output_size, produced); + auto emit_stored(DeflateBitWriter &writer, const std::uint8_t *data, + std::size_t size, const bool final) -> void { + while (true) { + const std::size_t chunk{std::min(size, DEFLATE_MAXIMUM_STORED_LENGTH)}; + const bool last{final && chunk == size}; + writer.add(last ? 1U : 0U, 1); + writer.add(0, 2); + writer.align(); + const std::array header{ + {static_cast(chunk & 0xff), + static_cast((chunk >> 8) & 0xff), + static_cast(~chunk & 0xff), + static_cast((~chunk >> 8) & 0xff)}}; + writer.bytes(header.data(), header.size()); + writer.bytes(data, chunk); + data += chunk; + size -= chunk; + if (size == 0) { + break; + } } } - auto copy_backref_non_overlapping(std::uint8_t *output, std::size_t &produced, - const std::size_t to_copy) -> void { - const std::size_t source_position{(this->window_position_ + - DEFLATE_WINDOW_SIZE - - this->pending_copy_distance_) & - DEFLATE_WINDOW_MASK}; - - // Copy from circular window into linear output (one or two contiguous - // chunks depending on whether the source range wraps the window) - const std::size_t source_first{ - std::min(to_copy, DEFLATE_WINDOW_SIZE - source_position)}; - std::memcpy(output + produced, this->window_.data() + source_position, - source_first); - if (source_first < to_copy) { - std::memcpy(output + produced + source_first, this->window_.data(), - to_copy - source_first); - } - - // Mirror the freshly written bytes back into the circular window - const std::size_t dest_first{ - std::min(to_copy, DEFLATE_WINDOW_SIZE - this->window_position_)}; - std::memcpy(this->window_.data() + this->window_position_, - output + produced, dest_first); - if (dest_first < to_copy) { - std::memcpy(this->window_.data(), output + produced + dest_first, - to_copy - dest_first); - } - - produced += to_copy; - this->window_position_ = - (this->window_position_ + to_copy) & DEFLATE_WINDOW_MASK; - this->bytes_written_ = - std::min(this->bytes_written_ + to_copy, DEFLATE_WINDOW_SIZE); - this->pending_copy_length_ -= to_copy; + static auto stored_cost(const unsigned int pending_bits, + const std::size_t size) -> std::uint64_t { + const std::size_t chunks{size == 0 + ? 1 + : (size + DEFLATE_MAXIMUM_STORED_LENGTH - 1) / + DEFLATE_MAXIMUM_STORED_LENGTH}; + const std::uint64_t first_padding{(8 - ((pending_bits + 3) % 8)) % 8}; + return 3 + first_padding + 32 + ((chunks - 1) * 40) + + (static_cast(size) * 8); } - auto copy_backref_overlapping(std::uint8_t *output, - const std::size_t output_size, - std::size_t &produced) -> void { - std::size_t source_position{(this->window_position_ + DEFLATE_WINDOW_SIZE - - this->pending_copy_distance_) & - DEFLATE_WINDOW_MASK}; - while (this->pending_copy_length_ > 0 && produced < output_size) { - const auto byte{this->window_[source_position]}; - this->window_[this->window_position_] = byte; - this->window_position_ = - (this->window_position_ + 1) & DEFLATE_WINDOW_MASK; - source_position = (source_position + 1) & DEFLATE_WINDOW_MASK; - if (this->bytes_written_ < DEFLATE_WINDOW_SIZE) { - ++this->bytes_written_; + // Works on a local copy of the writer so that its state can live in + // registers instead of memory + auto emit_symbols(DeflateBitWriter &destination, const std::uint8_t *data, + const std::uint8_t *literal_length_lengths, + const std::uint16_t *literal_length_codes, + const std::uint8_t *distance_lengths, + const std::uint16_t *distance_codes) -> void { + DeflateBitWriter writer{destination}; + const auto *literal{data}; + for (std::size_t index = 0; index < this->sequence_count_; ++index) { + const auto &sequence{this->sequences_[index]}; + // Three codewords of at most 15 bits always fit next to the at most seven + // bits that a flush leaves in the buffer + std::uint32_t count{0}; + while (count + 3 <= sequence.literals) { + writer.add(literal_length_codes[literal[0]], + literal_length_lengths[literal[0]]); + writer.add(literal_length_codes[literal[1]], + literal_length_lengths[literal[1]]); + writer.add(literal_length_codes[literal[2]], + literal_length_lengths[literal[2]]); + writer.flush(); + literal += 3; + count += 3; + } + + while (count < sequence.literals) { + writer.add(literal_length_codes[*literal], + literal_length_lengths[*literal]); + writer.flush(); + literal += 1; + count += 1; } - output[produced++] = byte; - --this->pending_copy_length_; + + if (sequence.length == 0) { + continue; + } + + const std::size_t length_slot{DEFLATE_LENGTH_SLOTS[sequence.length]}; + const std::size_t length_symbol{257 + length_slot}; + writer.add(literal_length_codes[length_symbol], + literal_length_lengths[length_symbol]); + writer.add(static_cast(sequence.length - + INFLATE_LENGTH_BASE[length_slot]), + INFLATE_LENGTH_EXTRA_BITS[length_slot]); + writer.flush(); + const auto distance_slot{deflate_distance_slot(sequence.distance)}; + writer.add(distance_codes[distance_slot], + distance_lengths[distance_slot]); + writer.add(static_cast( + sequence.distance - INFLATE_DISTANCE_BASE[distance_slot]), + INFLATE_DISTANCE_EXTRA_BITS[distance_slot]); + writer.flush(); + literal += sequence.length; } + + writer.add(literal_length_codes[DEFLATE_END_OF_BLOCK], + literal_length_lengths[DEFLATE_END_OF_BLOCK]); + writer.flush(); + destination = writer; } - auto emit(const std::uint8_t byte, std::uint8_t *output, - const std::size_t output_size, std::size_t &produced) -> bool { - this->window_[this->window_position_] = byte; - this->window_position_ = (this->window_position_ + 1) & DEFLATE_WINDOW_MASK; - if (this->bytes_written_ < DEFLATE_WINDOW_SIZE) { - ++this->bytes_written_; + // Emits the block with whichever of the stored, fixed, and dynamic + // encodings of RFC 1951 section 3.2.3 is the smallest + auto emit_block(DeflateBitWriter &writer, const std::uint8_t *data, + const std::size_t size, const bool final) -> void { + auto &frequencies{this->literal_length_frequencies_}; + frequencies[DEFLATE_END_OF_BLOCK] = 1; + + std::uint64_t extra_bits{0}; + for (std::size_t slot = 0; slot < INFLATE_LENGTH_BASE.size(); ++slot) { + extra_bits += static_cast(frequencies[257 + slot]) * + INFLATE_LENGTH_EXTRA_BITS[slot]; } - if (produced < output_size) { - output[produced++] = byte; - return true; + + for (std::size_t slot = 0; slot < INFLATE_DISTANCE_BASE.size(); ++slot) { + extra_bits += + static_cast(this->distance_frequencies_[slot]) * + INFLATE_DISTANCE_EXTRA_BITS[slot]; + } + + this->code_builder_.lengths(frequencies.data(), 286, + INFLATE_MAXIMUM_CODEWORD_LENGTH, + this->literal_length_lengths_.data()); + this->code_builder_.lengths(this->distance_frequencies_.data(), 30, + INFLATE_MAXIMUM_CODEWORD_LENGTH, + this->distance_lengths_.data()); + + std::size_t literal_length_count{286}; + while (literal_length_count > 257 && + this->literal_length_lengths_[literal_length_count - 1] == 0) { + literal_length_count -= 1; + } + + std::size_t distance_count{30}; + while (distance_count > 1 && + this->distance_lengths_[distance_count - 1] == 0) { + distance_count -= 1; + } + + std::array + all_lengths{}; + std::memcpy(all_lengths.data(), this->literal_length_lengths_.data(), + literal_length_count); + std::memcpy(all_lengths.data() + literal_length_count, + this->distance_lengths_.data(), distance_count); + this->run_.encode(all_lengths.data(), + literal_length_count + distance_count); + + std::array + code_length_frequencies{}; + for (std::size_t index = 0; index < this->run_.size; ++index) { + code_length_frequencies[this->run_.symbols[index]] += 1; + } + + std::array code_length_lengths{}; + this->code_builder_.lengths(code_length_frequencies.data(), + INFLATE_CODE_LENGTH_SYMBOLS, 7, + code_length_lengths.data()); + std::size_t code_length_count{INFLATE_CODE_LENGTH_SYMBOLS}; + while ( + code_length_count > 4 && + code_length_lengths[INFLATE_CODE_LENGTH_ORDER[code_length_count - 1]] == + 0) { + code_length_count -= 1; } - return false; - } - BitReader *reader_; - State state_{State::BlockHeader}; - bool final_block_{false}; - bool stream_ended_{false}; + std::uint64_t dynamic_bits{17 + (3 * code_length_count) + extra_bits}; + for (std::size_t symbol = 0; symbol < INFLATE_CODE_LENGTH_SYMBOLS; + ++symbol) { + dynamic_bits += + static_cast(code_length_frequencies[symbol]) * + (code_length_lengths[symbol] + + DEFLATE_CODE_LENGTH_EXTRA_BITS[symbol]); + } - std::uint16_t stored_remaining_{0}; + std::uint64_t fixed_bits{3 + extra_bits}; + for (std::size_t symbol = 0; symbol < 286; ++symbol) { + dynamic_bits += static_cast(frequencies[symbol]) * + this->literal_length_lengths_[symbol]; + fixed_bits += static_cast(frequencies[symbol]) * + this->fixed_.literal_length_lengths[symbol]; + } - HuffmanDecoder literal_length_tree_{}; - HuffmanDecoder distance_tree_{}; + for (std::size_t symbol = 0; symbol < 30; ++symbol) { + dynamic_bits += + static_cast(this->distance_frequencies_[symbol]) * + this->distance_lengths_[symbol]; + fixed_bits += + static_cast(this->distance_frequencies_[symbol]) * 5; + } - std::size_t pending_copy_length_{0}; - std::size_t pending_copy_distance_{0}; + const auto stored_bits{stored_cost(writer.pending_bits(), size)}; + if (stored_bits <= fixed_bits && stored_bits <= dynamic_bits) { + this->emit_stored(writer, data, size, final); + return; + } + + if (fixed_bits <= dynamic_bits) { + writer.add(final ? 1U : 0U, 1); + writer.add(1, 2); + writer.flush(); + this->emit_symbols(writer, data, + this->fixed_.literal_length_lengths.data(), + this->fixed_.literal_length_codes.data(), + this->fixed_.distance_lengths.data(), + this->fixed_.distance_codes.data()); + return; + } + + writer.add(final ? 1U : 0U, 1); + writer.add(2, 2); + writer.add(static_cast(literal_length_count - 257), 5); + writer.add(static_cast(distance_count - 1), 5); + writer.add(static_cast(code_length_count - 4), 4); + writer.flush(); + for (std::size_t index = 0; index < code_length_count; ++index) { + writer.add(code_length_lengths[INFLATE_CODE_LENGTH_ORDER[index]], 3); + writer.flush(); + } + + std::array code_length_codes{}; + deflate_canonical_codes(code_length_lengths.data(), + INFLATE_CODE_LENGTH_SYMBOLS, + code_length_codes.data()); + for (std::size_t index = 0; index < this->run_.size; ++index) { + const auto symbol{this->run_.symbols[index]}; + writer.add(code_length_codes[symbol], code_length_lengths[symbol]); + writer.add(this->run_.extra[index], + DEFLATE_CODE_LENGTH_EXTRA_BITS[symbol]); + writer.flush(); + } + + deflate_canonical_codes(this->literal_length_lengths_.data(), + INFLATE_LITERAL_LENGTH_SYMBOLS, + this->literal_length_codes_.data()); + deflate_canonical_codes(this->distance_lengths_.data(), + INFLATE_DISTANCE_SYMBOLS, + this->distance_codes_.data()); + this->emit_symbols(writer, data, this->literal_length_lengths_.data(), + this->literal_length_codes_.data(), + this->distance_lengths_.data(), + this->distance_codes_.data()); + } - std::array window_{}; - std::size_t window_position_{0}; - // Bytes written into the sliding window since the last reset, capped at - // DEFLATE_WINDOW_SIZE. Used to reject back-references whose distance - // exceeds the data we have actually produced for the current member - std::size_t bytes_written_{0}; + int level_; + std::vector sequences_; + std::size_t sequence_count_{0}; + std::uint32_t pending_literals_{0}; + std::vector head_; + std::vector previous_; + std::array + literal_length_frequencies_{}; + std::array distance_frequencies_{}; + std::array + literal_length_lengths_{}; + std::array + literal_length_codes_{}; + std::array distance_lengths_{}; + std::array distance_codes_{}; + DeflateCodeBuilder code_builder_; + DeflateCodeLengthRun run_; + DeflateFixedCodes fixed_; }; } // namespace sourcemeta::core diff --git a/src/core/gzip/gzip.cc b/src/core/gzip/gzip.cc index 1399880b18..7943cbf98a 100644 --- a/src/core/gzip/gzip.cc +++ b/src/core/gzip/gzip.cc @@ -1,153 +1,138 @@ #include -extern "C" { -#include -} +#include "deflate.h" +#include "inflate.h" + +#include -#include // std::min -#include // std::unique_ptr +#include // std::min +#include // std::size_t +#include // std::uint8_t, std::uint32_t +#include // std::make_unique, std::make_unique_for_overwrite +#include // std::string +#include // std::string_view namespace sourcemeta::core { +namespace { + +// RFC 1951 cannot expand data by more than this factor, which bounds how far +// the size recorded in a trailer can be trusted as a first capacity guess +constexpr std::size_t MAXIMUM_EXPANSION{1032}; + +constexpr std::size_t GZIP_HEADER_SIZE{10}; +constexpr std::size_t GZIP_TRAILER_SIZE{8}; + +auto initial_capacity(const std::uint8_t *input, const std::size_t size, + const std::size_t output_hint, + const std::size_t maximum_size) -> std::size_t { + if (output_hint > 0) { + return std::min(output_hint, maximum_size); + } + + // The last four bytes of a single member input record its size modulo 2^32 + if (size >= 18) { + const auto recorded{static_cast(input[size - 4]) | + (static_cast(input[size - 3]) << 8) | + (static_cast(input[size - 2]) << 16) | + (static_cast(input[size - 1]) << 24)}; + const auto bound{size > maximum_size / MAXIMUM_EXPANSION + ? maximum_size + : size * MAXIMUM_EXPANSION}; + return std::min({recorded, bound, maximum_size}); + } + + return std::min(size, maximum_size); +} + +auto store_little_endian(std::uint8_t *destination, const std::uint32_t value) + -> void { + destination[0] = static_cast(value & 0xff); + destination[1] = static_cast((value >> 8) & 0xff); + destination[2] = static_cast((value >> 16) & 0xff); + destination[3] = static_cast((value >> 24) & 0xff); +} + +} // namespace + auto gzip(const std::uint8_t *input, const std::size_t size, const int level) -> std::string { - std::unique_ptr - compressor{libdeflate_alloc_compressor(level), - libdeflate_free_compressor}; - if (!compressor) { - throw GZIPError{"Could not allocate compressor"}; + if (level < 0 || level > 12) { + throw GZIPError{"Invalid compression level"}; } - const auto max_size{libdeflate_gzip_compress_bound(compressor.get(), size)}; + const auto encoder{std::make_unique(level, size)}; std::string output; - std::size_t actual_size{0}; - // libdeflate overwrites the whole bound, so leaving the buffer uninitialised - // avoids zero-filling multi-megabyte allocations that are immediately - // discarded output.resize_and_overwrite( - max_size, - [&](char *const buffer, const std::size_t capacity) -> std::size_t { - actual_size = libdeflate_gzip_compress(compressor.get(), input, size, - buffer, capacity); - return capacity; + GZIP_HEADER_SIZE + DeflateEncoder::bound(size) + GZIP_TRAILER_SIZE, + [&](char *const buffer, const std::size_t) -> std::size_t { + auto *const data{reinterpret_cast(buffer)}; + // RFC 1952 section 2.3.1: no optional fields, no modification time, + // and an unknown operating system + data[0] = 0x1f; + data[1] = 0x8b; + data[2] = 0x08; + data[3] = 0x00; + store_little_endian(data + 4, 0); + data[8] = level <= 1 ? 0x04 : (level >= 9 ? 0x02 : 0x00); + data[9] = 0xff; + const auto written{ + encoder->compress(input, size, data + GZIP_HEADER_SIZE)}; + auto *const trailer{data + GZIP_HEADER_SIZE + written}; + store_little_endian(trailer, + crc32(std::string_view{ + reinterpret_cast(input), size})); + store_little_endian(trailer + 4, static_cast(size)); + return GZIP_HEADER_SIZE + written + GZIP_TRAILER_SIZE; }); - if (actual_size == 0) { - throw GZIPError{"Could not compress input"}; - } - - output.resize(actual_size); return output; } auto gunzip(const std::uint8_t *input, const std::size_t size, const std::size_t output_hint, const std::size_t maximum_size) -> std::string { - std::unique_ptr - decompressor{libdeflate_alloc_decompressor(), - libdeflate_free_decompressor}; - if (!decompressor) { - throw GZIPError{"Could not allocate decompressor"}; - } - + const auto decoder{std::make_unique_for_overwrite()}; + InflateBuffers buffers{.input_next = input, + .input_end = input + size, + .input_final = true, + .output_begin = nullptr, + .output_next = nullptr, + .output_end = nullptr}; std::string output; - // Bound the very first allocation too, so a hint or the size heuristic cannot - // exceed the cap before the loop has a chance to reject the input. The size - // heuristic is guarded so that the multiplication cannot overflow before the - // clamp, since the cap is caller-controlled - std::size_t capacity{0}; - if (output_hint > 0) { - capacity = std::min(output_hint, maximum_size); - } else if (size > maximum_size / 4) { - capacity = maximum_size; - } else { - capacity = size * 4; - } - - // Decompress the first member. Every gzip stream has at least one member, so - // a failure here is a real error rather than trailing data - std::size_t total_in{0}; - std::size_t total_out{0}; - for (;;) { - std::size_t member_in{0}; - std::size_t member_out{0}; - auto result{LIBDEFLATE_BAD_DATA}; - // libdeflate writes only the decompressed bytes, so leaving the buffer - // uninitialised avoids zero-filling multi-megabyte allocations on every - // retry of the doubling loop + std::size_t capacity{ + initial_capacity(input, size, output_hint, maximum_size)}; + std::size_t position{0}; + while (true) { + // Growing keeps the bytes decoded so far, and leaving the rest + // uninitialised avoids zero-filling memory that is about to be written output.resize_and_overwrite( capacity, - [&](char *const buffer, const std::size_t buffer_size) -> std::size_t { - result = libdeflate_gzip_decompress_ex(decompressor.get(), input, - size, buffer, buffer_size, - &member_in, &member_out); - return buffer_size; - }); - - if (result == LIBDEFLATE_SUCCESS) { - total_in = member_in; - total_out = member_out; + [](char *, const std::size_t count) -> std::size_t { return count; }); + auto *const begin{reinterpret_cast(output.data())}; + buffers.output_begin = begin; + buffers.output_next = begin + position; + buffers.output_end = begin + capacity; + const auto status{decoder->decode(buffers)}; + position = static_cast(buffers.output_next - begin); + if (status == InflateStatus::Done) { break; } - if (result == LIBDEFLATE_INSUFFICIENT_SPACE) { - if (capacity >= maximum_size) { - throw GZIPError{"Decompressed output exceeds the maximum allowed size"}; - } - - // Double without overflowing: capacity is below the cap here, so doubling - // only runs when the result still fits under it - capacity = (capacity > maximum_size / 2) ? maximum_size : capacity * 2; - continue; + // The whole input is available upfront, so the decoder only ever stops + // early because the output is full + if (capacity >= maximum_size) { + throw GZIPError{"Decompressed output exceeds the maximum allowed size"}; } - throw GZIPError{"Could not decompress input"}; - } - - // RFC 1952 Section 2.2 permits concatenated members. Like gzip(1) and the - // streaming decoder, decompress every subsequent member and silently ignore - // trailing data that does not begin a new member, so `resize` here preserves - // the members already decoded - while (total_in < size) { - if (size - total_in < 2 || input[total_in] != 0x1F || - input[total_in + 1] != 0x8B) { - break; + if (capacity == 0) { + capacity = std::min(std::size_t{4096}, maximum_size); + } else { + capacity = capacity > maximum_size / 2 ? maximum_size : capacity * 2; } - - std::size_t member_in{0}; - std::size_t member_out{0}; - for (;;) { - const auto result{libdeflate_gzip_decompress_ex( - decompressor.get(), input + total_in, size - total_in, - output.data() + total_out, output.size() - total_out, &member_in, - &member_out)}; - - if (result == LIBDEFLATE_SUCCESS) { - break; - } - - if (result == LIBDEFLATE_INSUFFICIENT_SPACE) { - if (output.size() >= maximum_size) { - throw GZIPError{ - "Decompressed output exceeds the maximum allowed size"}; - } - - output.resize((output.size() > maximum_size / 2) ? maximum_size - : output.size() * 2); - continue; - } - - // A run that begins with the gzip magic but fails to decode is a corrupt - // member, not trailing data, so it is a real error - throw GZIPError{"Could not decompress input"}; - } - - total_in += member_in; - total_out += member_out; } - output.resize(total_out); + output.resize(position); return output; } diff --git a/src/core/gzip/huffman.h b/src/core/gzip/huffman.h deleted file mode 100644 index 79a6cf0774..0000000000 --- a/src/core/gzip/huffman.h +++ /dev/null @@ -1,168 +0,0 @@ -#ifndef SOURCEMETA_CORE_GZIP_HUFFMAN_H_ -#define SOURCEMETA_CORE_GZIP_HUFFMAN_H_ - -#include "bit_reader.h" - -#include - -#include // std::ranges::fill -#include // std::array -#include // assert -#include // std::size_t -#include // std::uint8_t, std::uint16_t - -namespace sourcemeta::core { - -// Maximum Huffman code length per RFC 1951 section 3.2.7 -inline constexpr unsigned int MAX_HUFFMAN_BITS{15}; - -// Largest alphabet is the literal/length alphabet (288 symbols) per RFC 1951 -// section 3.2.5 -inline constexpr std::size_t MAX_HUFFMAN_SYMBOLS{288}; - -class HuffmanDecoder { -public: - // Primary lookup table covering all codes of length <= LUT_BITS. Misses - // fall back to the canonical-puff traversal for codes of length 10..15 - static constexpr unsigned int LUT_BITS{9}; - static constexpr std::size_t LUT_SIZE{1U << LUT_BITS}; - - HuffmanDecoder() = default; - - // The fixed distance tree of RFC 1951 section 3.2.6 is intentionally - // incomplete (30 codes of length five over a 32-slot space), so the - // completeness check is suppressed for it and enforced everywhere else - auto build(const std::uint8_t *lengths, const std::size_t length_count, - const bool allow_incomplete = false) -> void { - std::ranges::fill(this->count_, std::uint16_t{0}); - std::ranges::fill(this->lut_, std::uint16_t{0}); - - for (std::size_t symbol = 0; symbol < length_count; ++symbol) { - // The fixed trees use lengths five to nine, the code length tree - // reads three bit values, and the dynamic trees copy code length - // symbols below sixteen, so no caller can supply a longer length - assert(lengths[symbol] <= MAX_HUFFMAN_BITS); - this->count_[lengths[symbol]]++; - } - - if (this->count_[0] == length_count) { - return; - } - - // Verify the alphabet is complete or single-symbol per RFC 1951 - int left{1}; - for (unsigned int bits = 1; bits <= MAX_HUFFMAN_BITS; ++bits) { - left <<= 1; - left -= this->count_[bits]; - if (left < 0) { - throw GZIPError{"Over-subscribed Huffman code"}; - } - } - - // Reject incomplete codes, matching zlib and puff. RFC 1951 sanctions - // incompleteness only for the single-code case (a tree built from one - // used code of length one), where every length is either zero or one - if (left > 0 && !allow_incomplete && - length_count != static_cast(this->count_[0]) + - static_cast(this->count_[1])) { - throw GZIPError{"Incomplete Huffman code"}; - } - - std::array offsets{}; - offsets[1] = 0; - for (unsigned int bits = 1; bits < MAX_HUFFMAN_BITS; ++bits) { - offsets[bits + 1] = - static_cast(offsets[bits] + this->count_[bits]); - } - - for (std::size_t symbol = 0; symbol < length_count; ++symbol) { - if (lengths[symbol] != 0) { - this->symbols_[offsets[lengths[symbol]]++] = - static_cast(symbol); - } - } - - // Build the fast-path LUT. Each entry packs (symbol << 4) | length; - // a value of 0 means "no short code, fall back". Codes longer than - // LUT_BITS leave their LUT entries at 0 - std::array next_code{}; - next_code[1] = 0; - for (unsigned int bits = 2; bits <= MAX_HUFFMAN_BITS; ++bits) { - next_code[bits] = (next_code[bits - 1] + this->count_[bits - 1]) << 1U; - } - - std::size_t symbol_index{0}; - for (unsigned int code_length = 1; code_length <= MAX_HUFFMAN_BITS; - ++code_length) { - for (unsigned int k = 0; k < this->count_[code_length]; ++k) { - const auto symbol{this->symbols_[symbol_index]}; - if (code_length <= LUT_BITS) { - const auto code{next_code[code_length]}; - const auto lsb_key{reverse_bits(code, code_length)}; - const auto entry{static_cast( - static_cast(code_length) | - static_cast(symbol << 4U))}; - const std::uint32_t stride{1U << code_length}; - for (std::uint32_t slot = lsb_key; slot < LUT_SIZE; slot += stride) { - this->lut_[slot] = entry; - } - } - ++next_code[code_length]; - ++symbol_index; - } - } - } - - auto decode(BitReader &reader) const -> std::uint16_t { - const auto key{reader.peek_bits(LUT_BITS)}; - const auto entry{this->lut_[key]}; - if (entry != 0) { - const unsigned int length{entry & 0xfU}; - reader.consume_bits(length); - return static_cast(entry >> 4U); - } - return this->decode_long(reader); - } - -private: - auto decode_long(BitReader &reader) const -> std::uint16_t { - std::uint32_t bits{reader.peek_bits(MAX_HUFFMAN_BITS)}; - int code{0}; - int first{0}; - int index{0}; - for (unsigned int length = 1; length <= MAX_HUFFMAN_BITS; ++length) { - code |= static_cast(bits & 1U); - bits >>= 1U; - const auto entries{static_cast(this->count_[length])}; - if (code - entries < first) { - const auto position{static_cast(index) + - static_cast(code - first)}; - reader.consume_bits(length); - return this->symbols_[position]; - } - index += entries; - first = (first + entries) << 1; - code <<= 1; - } - throw GZIPError{"Invalid Huffman code"}; - } - - static auto reverse_bits(std::uint32_t value, const unsigned int length) - -> std::uint32_t { - std::uint32_t result{0}; - for (unsigned int index = 0; index < length; ++index) { - if ((value & (1U << (length - 1 - index))) != 0U) { - result |= (1U << index); - } - } - return result; - } - - std::array count_{}; - std::array symbols_{}; - std::array lut_{}; -}; - -} // namespace sourcemeta::core - -#endif diff --git a/src/core/gzip/include/sourcemeta/core/gzip.h b/src/core/gzip/include/sourcemeta/core/gzip.h index fd7f202b3a..1c2b553f00 100644 --- a/src/core/gzip/include/sourcemeta/core/gzip.h +++ b/src/core/gzip/include/sourcemeta/core/gzip.h @@ -27,7 +27,8 @@ namespace sourcemeta::core { /// @ingroup gzip /// Compress a byte buffer using the GZIP format (RFC 1952). An optional -/// compression level from 0 to 12 trades speed for ratio. For example: +/// compression level from 0 to 12 trades speed for ratio, and any other level +/// throws. For example: /// /// ```cpp /// #include @@ -46,7 +47,9 @@ auto SOURCEMETA_CORE_GZIP_EXPORT gzip(const std::uint8_t *input, /// bounded by a maximum size (256 MiB by default) so that a highly compressed /// input cannot exhaust memory, and decompressing beyond it throws. Every /// concatenated member is decoded and any trailing data that does not begin a -/// new member is ignored, matching gzip(1). For example: +/// new member is ignored, matching gzip(1). A member starts with the two gzip +/// identification bytes and must then be valid. Decompression behaves exactly +/// like the streaming decompression of the stream buffer. For example: /// /// ```cpp /// #include diff --git a/src/core/gzip/inflate.h b/src/core/gzip/inflate.h new file mode 100644 index 0000000000..046a952839 --- /dev/null +++ b/src/core/gzip/inflate.h @@ -0,0 +1,1680 @@ +#ifndef SOURCEMETA_CORE_GZIP_INFLATE_H_ +#define SOURCEMETA_CORE_GZIP_INFLATE_H_ + +#include +#include + +#include // std::min +#include // std::array +#include // std::endian, std::byteswap, std::countl_zero +#include // std::size_t +#include // std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t +#include // std::memcpy, std::memset, std::memchr +#include // std::string_view +#include // std::cmp_greater + +namespace sourcemeta::core { + +// A decode table entry packs the symbol value in the upper 16 bits, flags in +// bits 13 to 15 and 5 to 7, the number of codeword bits in bits 8 to 12, and +// the number of codeword and extra bits to consume in the lowest 5 bits +inline constexpr std::uint32_t INFLATE_ENTRY_END_OF_BLOCK{0x20}; +inline constexpr std::uint32_t INFLATE_ENTRY_EXCEPTIONAL{0x40}; +inline constexpr std::uint32_t INFLATE_ENTRY_LITERAL{0x80}; +inline constexpr std::uint32_t INFLATE_ENTRY_SUBTABLE{0x2000}; +inline constexpr std::uint32_t INFLATE_ENTRY_INVALID_CODE{0x4000}; +inline constexpr std::uint32_t INFLATE_ENTRY_INVALID_SYMBOL{0x8000}; + +// RFC 1951 section 3.2.7 limits every codeword to 15 bits +inline constexpr unsigned int INFLATE_MAXIMUM_CODEWORD_LENGTH{15}; + +// Codes longer than the primary index continue into fixed size subtables. The +// number of subtables is bounded by the number of symbols, as every subtable +// holds at least one codeword +inline constexpr unsigned int INFLATE_LITERAL_LENGTH_TABLE_BITS{11}; +inline constexpr unsigned int INFLATE_DISTANCE_TABLE_BITS{8}; +inline constexpr unsigned int INFLATE_CODE_LENGTH_TABLE_BITS{7}; +inline constexpr std::size_t INFLATE_LITERAL_LENGTH_SYMBOLS{288}; +inline constexpr std::size_t INFLATE_DISTANCE_SYMBOLS{32}; +inline constexpr std::size_t INFLATE_CODE_LENGTH_SYMBOLS{19}; +inline constexpr std::size_t INFLATE_LITERAL_LENGTH_TABLE_SIZE{ + (std::size_t{1} << INFLATE_LITERAL_LENGTH_TABLE_BITS) + + (INFLATE_LITERAL_LENGTH_SYMBOLS * + (std::size_t{1} << (INFLATE_MAXIMUM_CODEWORD_LENGTH - + INFLATE_LITERAL_LENGTH_TABLE_BITS)))}; +inline constexpr std::size_t INFLATE_DISTANCE_TABLE_SIZE{ + (std::size_t{1} << INFLATE_DISTANCE_TABLE_BITS) + + (INFLATE_DISTANCE_SYMBOLS * + (std::size_t{1} << (INFLATE_MAXIMUM_CODEWORD_LENGTH - + INFLATE_DISTANCE_TABLE_BITS)))}; +inline constexpr std::size_t INFLATE_CODE_LENGTH_TABLE_SIZE{ + std::size_t{1} << INFLATE_CODE_LENGTH_TABLE_BITS}; + +// RFC 1951 section 3.2.5 +inline constexpr std::array INFLATE_LENGTH_BASE{ + {3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, + 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258}}; +inline constexpr std::array INFLATE_LENGTH_EXTRA_BITS{ + {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, + 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0}}; +inline constexpr std::array INFLATE_DISTANCE_BASE{ + {1, 2, 3, 4, 5, 7, 9, 13, 17, 25, + 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, + 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577}}; +inline constexpr std::array INFLATE_DISTANCE_EXTRA_BITS{ + {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, + 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}}; + +// RFC 1951 section 3.2.7 +inline constexpr std::array INFLATE_CODE_LENGTH_ORDER{ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}}; + +// The part of the decode table entry of every symbol that does not depend on +// its codeword, packing the literal byte, or the base and the number of extra +// bits of a length or a distance as per RFC 1951 section 3.2.5. Symbols 286 +// and 287 of the literal/length alphabet and symbols 30 and 31 of the distance +// alphabet never occur in compressed data as per RFC 1951 section 3.2.6 +inline constexpr std::array + INFLATE_LITERAL_LENGTH_RESULTS{ + {0x00000080U, + 0x00010080U, + 0x00020080U, + 0x00030080U, + 0x00040080U, + 0x00050080U, + 0x00060080U, + 0x00070080U, + 0x00080080U, + 0x00090080U, + 0x000A0080U, + 0x000B0080U, + 0x000C0080U, + 0x000D0080U, + 0x000E0080U, + 0x000F0080U, + 0x00100080U, + 0x00110080U, + 0x00120080U, + 0x00130080U, + 0x00140080U, + 0x00150080U, + 0x00160080U, + 0x00170080U, + 0x00180080U, + 0x00190080U, + 0x001A0080U, + 0x001B0080U, + 0x001C0080U, + 0x001D0080U, + 0x001E0080U, + 0x001F0080U, + 0x00200080U, + 0x00210080U, + 0x00220080U, + 0x00230080U, + 0x00240080U, + 0x00250080U, + 0x00260080U, + 0x00270080U, + 0x00280080U, + 0x00290080U, + 0x002A0080U, + 0x002B0080U, + 0x002C0080U, + 0x002D0080U, + 0x002E0080U, + 0x002F0080U, + 0x00300080U, + 0x00310080U, + 0x00320080U, + 0x00330080U, + 0x00340080U, + 0x00350080U, + 0x00360080U, + 0x00370080U, + 0x00380080U, + 0x00390080U, + 0x003A0080U, + 0x003B0080U, + 0x003C0080U, + 0x003D0080U, + 0x003E0080U, + 0x003F0080U, + 0x00400080U, + 0x00410080U, + 0x00420080U, + 0x00430080U, + 0x00440080U, + 0x00450080U, + 0x00460080U, + 0x00470080U, + 0x00480080U, + 0x00490080U, + 0x004A0080U, + 0x004B0080U, + 0x004C0080U, + 0x004D0080U, + 0x004E0080U, + 0x004F0080U, + 0x00500080U, + 0x00510080U, + 0x00520080U, + 0x00530080U, + 0x00540080U, + 0x00550080U, + 0x00560080U, + 0x00570080U, + 0x00580080U, + 0x00590080U, + 0x005A0080U, + 0x005B0080U, + 0x005C0080U, + 0x005D0080U, + 0x005E0080U, + 0x005F0080U, + 0x00600080U, + 0x00610080U, + 0x00620080U, + 0x00630080U, + 0x00640080U, + 0x00650080U, + 0x00660080U, + 0x00670080U, + 0x00680080U, + 0x00690080U, + 0x006A0080U, + 0x006B0080U, + 0x006C0080U, + 0x006D0080U, + 0x006E0080U, + 0x006F0080U, + 0x00700080U, + 0x00710080U, + 0x00720080U, + 0x00730080U, + 0x00740080U, + 0x00750080U, + 0x00760080U, + 0x00770080U, + 0x00780080U, + 0x00790080U, + 0x007A0080U, + 0x007B0080U, + 0x007C0080U, + 0x007D0080U, + 0x007E0080U, + 0x007F0080U, + 0x00800080U, + 0x00810080U, + 0x00820080U, + 0x00830080U, + 0x00840080U, + 0x00850080U, + 0x00860080U, + 0x00870080U, + 0x00880080U, + 0x00890080U, + 0x008A0080U, + 0x008B0080U, + 0x008C0080U, + 0x008D0080U, + 0x008E0080U, + 0x008F0080U, + 0x00900080U, + 0x00910080U, + 0x00920080U, + 0x00930080U, + 0x00940080U, + 0x00950080U, + 0x00960080U, + 0x00970080U, + 0x00980080U, + 0x00990080U, + 0x009A0080U, + 0x009B0080U, + 0x009C0080U, + 0x009D0080U, + 0x009E0080U, + 0x009F0080U, + 0x00A00080U, + 0x00A10080U, + 0x00A20080U, + 0x00A30080U, + 0x00A40080U, + 0x00A50080U, + 0x00A60080U, + 0x00A70080U, + 0x00A80080U, + 0x00A90080U, + 0x00AA0080U, + 0x00AB0080U, + 0x00AC0080U, + 0x00AD0080U, + 0x00AE0080U, + 0x00AF0080U, + 0x00B00080U, + 0x00B10080U, + 0x00B20080U, + 0x00B30080U, + 0x00B40080U, + 0x00B50080U, + 0x00B60080U, + 0x00B70080U, + 0x00B80080U, + 0x00B90080U, + 0x00BA0080U, + 0x00BB0080U, + 0x00BC0080U, + 0x00BD0080U, + 0x00BE0080U, + 0x00BF0080U, + 0x00C00080U, + 0x00C10080U, + 0x00C20080U, + 0x00C30080U, + 0x00C40080U, + 0x00C50080U, + 0x00C60080U, + 0x00C70080U, + 0x00C80080U, + 0x00C90080U, + 0x00CA0080U, + 0x00CB0080U, + 0x00CC0080U, + 0x00CD0080U, + 0x00CE0080U, + 0x00CF0080U, + 0x00D00080U, + 0x00D10080U, + 0x00D20080U, + 0x00D30080U, + 0x00D40080U, + 0x00D50080U, + 0x00D60080U, + 0x00D70080U, + 0x00D80080U, + 0x00D90080U, + 0x00DA0080U, + 0x00DB0080U, + 0x00DC0080U, + 0x00DD0080U, + 0x00DE0080U, + 0x00DF0080U, + 0x00E00080U, + 0x00E10080U, + 0x00E20080U, + 0x00E30080U, + 0x00E40080U, + 0x00E50080U, + 0x00E60080U, + 0x00E70080U, + 0x00E80080U, + 0x00E90080U, + 0x00EA0080U, + 0x00EB0080U, + 0x00EC0080U, + 0x00ED0080U, + 0x00EE0080U, + 0x00EF0080U, + 0x00F00080U, + 0x00F10080U, + 0x00F20080U, + 0x00F30080U, + 0x00F40080U, + 0x00F50080U, + 0x00F60080U, + 0x00F70080U, + 0x00F80080U, + 0x00F90080U, + 0x00FA0080U, + 0x00FB0080U, + 0x00FC0080U, + 0x00FD0080U, + 0x00FE0080U, + 0x00FF0080U, + INFLATE_ENTRY_EXCEPTIONAL | INFLATE_ENTRY_END_OF_BLOCK, + 0x00030000U, + 0x00040000U, + 0x00050000U, + 0x00060000U, + 0x00070000U, + 0x00080000U, + 0x00090000U, + 0x000A0000U, + 0x000B0100U, + 0x000D0100U, + 0x000F0100U, + 0x00110100U, + 0x00130200U, + 0x00170200U, + 0x001B0200U, + 0x001F0200U, + 0x00230300U, + 0x002B0300U, + 0x00330300U, + 0x003B0300U, + 0x00430400U, + 0x00530400U, + 0x00630400U, + 0x00730400U, + 0x00830500U, + 0x00A30500U, + 0x00C30500U, + 0x00E30500U, + 0x01020000U, + INFLATE_ENTRY_EXCEPTIONAL | INFLATE_ENTRY_INVALID_SYMBOL, + INFLATE_ENTRY_EXCEPTIONAL | INFLATE_ENTRY_INVALID_SYMBOL}}; +inline constexpr std::array + INFLATE_DISTANCE_RESULTS{ + {0x00010000U, + 0x00020000U, + 0x00030000U, + 0x00040000U, + 0x00050100U, + 0x00070100U, + 0x00090200U, + 0x000D0200U, + 0x00110300U, + 0x00190300U, + 0x00210400U, + 0x00310400U, + 0x00410500U, + 0x00610500U, + 0x00810600U, + 0x00C10600U, + 0x01010700U, + 0x01810700U, + 0x02010800U, + 0x03010800U, + 0x04010900U, + 0x06010900U, + 0x08010A00U, + 0x0C010A00U, + 0x10010B00U, + 0x18010B00U, + 0x20010C00U, + 0x30010C00U, + 0x40010D00U, + 0x60010D00U, + INFLATE_ENTRY_EXCEPTIONAL | INFLATE_ENTRY_INVALID_SYMBOL, + INFLATE_ENTRY_EXCEPTIONAL | INFLATE_ENTRY_INVALID_SYMBOL}}; +inline constexpr std::array + INFLATE_CODE_LENGTH_RESULTS{ + {0x00000000U, 0x00010000U, 0x00020000U, 0x00030000U, 0x00040000U, + 0x00050000U, 0x00060000U, 0x00070000U, 0x00080000U, 0x00090000U, + 0x000A0000U, 0x000B0000U, 0x000C0000U, 0x000D0000U, 0x000E0000U, + 0x000F0000U, 0x00100000U, 0x00110000U, 0x00120000U}}; + +inline auto inflate_reverse_bits(const std::uint32_t value, + const unsigned int length) -> std::uint32_t { + std::uint32_t result{0}; + for (unsigned int index = 0; index < length; ++index) { + result |= ((value >> index) & 1U) << (length - 1 - index); + } + + return result; +} + +// The symbol results hold the number of extra bits in bits 8 to 12, which the +// table entry replaces with the codeword length, adding both to the bits to +// consume so that decoding takes the codeword and its extra bits in one shift +inline auto inflate_make_entry(const std::uint32_t result, + const unsigned int length) -> std::uint32_t { + const std::uint32_t extra{(result >> 8) & 0x1fU}; + return (result & ~std::uint32_t{0x1f00U}) | (length << 8) | (length + extra); +} + +// Builds a decode table from a sequence of code lengths as per RFC 1951 +// section 3.2.2, returning a null pointer on success or the reason the code +// is invalid. Codewords are read starting from their most significant bit +// while the input is packed starting from the least significant bit, so every +// index is the bit reversal of the codeword +inline auto inflate_build_table(const std::uint8_t *lengths, + const std::size_t symbol_count, + const std::uint32_t *results, + const unsigned int table_bits, + std::uint32_t *table) -> const char * { + std::array counts{}; + for (std::size_t symbol = 0; symbol < symbol_count; ++symbol) { + counts[lengths[symbol]] += 1; + } + + const std::size_t used_symbols{symbol_count - counts[0]}; + int remaining_codespace{1}; + for (unsigned int length = 1; length <= INFLATE_MAXIMUM_CODEWORD_LENGTH; + ++length) { + remaining_codespace <<= 1; + remaining_codespace -= counts[length]; + if (remaining_codespace < 0) { + return "Over-subscribed Huffman code"; + } + } + + const std::size_t primary_size{std::size_t{1} << table_bits}; + if (remaining_codespace > 0) { + // RFC 1951 section 3.2.7 only sanctions an incomplete code that is empty + // or that holds a single codeword of one bit + if (used_symbols > 1 || (used_symbols == 1 && counts[1] != 1)) { + return "Incomplete Huffman code"; + } + + const std::uint32_t invalid{INFLATE_ENTRY_EXCEPTIONAL | + INFLATE_ENTRY_INVALID_CODE | + INFLATE_MAXIMUM_CODEWORD_LENGTH}; + for (std::size_t index = 0; index < primary_size; ++index) { + table[index] = invalid; + } + } + + std::array offsets{}; + for (unsigned int length = 1; length <= INFLATE_MAXIMUM_CODEWORD_LENGTH; + ++length) { + offsets[length + 1] = + static_cast(offsets[length] + counts[length]); + } + + std::array sorted{}; + for (std::size_t symbol = 0; symbol < symbol_count; ++symbol) { + if (lengths[symbol] != 0) { + sorted[offsets[lengths[symbol]]] = static_cast(symbol); + offsets[lengths[symbol]] += 1; + } + } + + // An allowed incomplete code holds at most one codeword, of one bit, whose + // reversal is zero + if (remaining_codespace > 0) { + if (used_symbols == 1) { + const std::uint32_t entry{inflate_make_entry(results[sorted[0]], 1)}; + for (std::size_t slot = 0; slot < primary_size; slot += 2) { + table[slot] = entry; + } + } + + return nullptr; + } + + // Walks the codewords in canonical order while keeping them bit reversed, + // where incrementing a reversed codeword flips its highest zero bit and + // clears every bit above it. The primary table starts as wide as the + // shortest codeword and doubles in size with every length, so that every + // shorter codeword is replicated with a single copy + unsigned int length{1}; + while (counts[length] == 0) { + length += 1; + } + + std::uint32_t codeword{0}; + std::size_t position{0}; + std::size_t table_end{std::size_t{1} << length}; + unsigned int count{counts[length]}; + while (length <= table_bits) { + while (count > 0) { + table[codeword] = inflate_make_entry(results[sorted[position]], length); + position += 1; + count -= 1; + if (codeword == table_end - 1) { + while (table_end < primary_size) { + std::memcpy(table + table_end, table, + table_end * sizeof(std::uint32_t)); + table_end <<= 1; + } + + return nullptr; + } + + const auto flipped{codeword ^ static_cast(table_end - 1)}; + const std::uint32_t bit{std::uint32_t{1} + << (31 - std::countl_zero(flipped))}; + codeword = (codeword & (bit - 1)) | bit; + } + + while (count == 0) { + length += 1; + if (length <= table_bits) { + std::memcpy(table + table_end, table, + table_end * sizeof(std::uint32_t)); + table_end <<= 1; + } + + count = counts[length]; + } + } + + // Longer codewords sharing the same first bits share a subtable, and + // canonical codewords sharing those bits are contiguous + const unsigned int subtable_bits{INFLATE_MAXIMUM_CODEWORD_LENGTH - + table_bits}; + const std::size_t subtable_size{std::size_t{1} << subtable_bits}; + std::size_t next_subtable{primary_size}; + std::uint32_t current_prefix{0xffffffffU}; + std::size_t current_subtable{0}; + while (true) { + const std::uint32_t prefix{codeword & + static_cast(primary_size - 1)}; + if (prefix != current_prefix) { + current_prefix = prefix; + current_subtable = next_subtable; + next_subtable += subtable_size; + table[prefix] = INFLATE_ENTRY_EXCEPTIONAL | INFLATE_ENTRY_SUBTABLE | + (static_cast(current_subtable) << 16) | + table_bits; + } + + const unsigned int remaining_length{length - table_bits}; + const std::uint32_t entry{ + inflate_make_entry(results[sorted[position]], remaining_length)}; + position += 1; + for (std::size_t slot = codeword >> table_bits; slot < subtable_size; + slot += std::size_t{1} << remaining_length) { + table[current_subtable + slot] = entry; + } + + const std::uint32_t last{(std::uint32_t{1} << length) - 1}; + if (codeword == last) { + return nullptr; + } + + const auto flipped{codeword ^ last}; + const std::uint32_t bit{std::uint32_t{1} + << (31 - std::countl_zero(flipped))}; + codeword = (codeword & (bit - 1)) | bit; + count -= 1; + while (count == 0) { + length += 1; + count = counts[length]; + } + } +} + +enum class InflateStatus : std::uint8_t { Done, NeedInput, OutputFull }; + +// The output buffer holds the bytes that back-references may point to before +// the next output position, and the input buffer holds the compressed bytes +// not yet read. Final input means no more compressed bytes will ever follow +struct InflateBuffers { + const std::uint8_t *input_next; + const std::uint8_t *input_end; + bool input_final; + std::uint8_t *output_begin; + std::uint8_t *output_next; + std::uint8_t *output_end; +}; + +// Decodes a sequence of gzip members (RFC 1952) holding deflate data +// (RFC 1951). Every call makes as much progress as the buffers allow and +// returns when the input is exhausted, the output is full, or the data ended +class InflateDecoder { +public: + // A dynamic block header never exceeds this many bytes, so holding at least + // this much unread input before a block header avoids suspending inside it + static constexpr std::size_t INPUT_MARGIN{1024}; + + auto decode(InflateBuffers &buffers) -> InflateStatus { + this->checksum_position_ = buffers.output_next; + this->history_start_ = + buffers.output_next - + std::min(this->member_history_, + static_cast(buffers.output_next - + buffers.output_begin)); + + while (true) { + switch (this->state_) { + case State::MemberStart: + if (!this->start_member(buffers)) { + return this->leave(buffers, InflateStatus::NeedInput); + } + + break; + case State::HeaderFixed: + if (!this->read_header_fixed(buffers)) { + return this->leave(buffers, InflateStatus::NeedInput); + } + + break; + case State::HeaderExtraLength: + if (!this->read_header_extra_length(buffers)) { + return this->leave(buffers, InflateStatus::NeedInput); + } + + break; + case State::HeaderExtra: + if (!this->skip_header_extra(buffers)) { + return this->leave(buffers, InflateStatus::NeedInput); + } + + break; + case State::HeaderName: + case State::HeaderComment: + if (!this->skip_header_string(buffers)) { + return this->leave(buffers, InflateStatus::NeedInput); + } + + break; + case State::HeaderChecksum: + if (!this->read_header_checksum(buffers)) { + return this->leave(buffers, InflateStatus::NeedInput); + } + + break; + case State::BlockHeader: + if (!this->read_block_header(buffers)) { + return this->leave(buffers, InflateStatus::NeedInput); + } + + break; + case State::StoredLength: + if (!this->read_stored_length(buffers)) { + return this->leave(buffers, InflateStatus::NeedInput); + } + + break; + case State::StoredData: { + const auto progress{this->copy_stored_data(buffers)}; + if (progress != InflateStatus::Done) { + return this->leave(buffers, progress); + } + + break; + } + case State::Huffman: { + const auto progress{this->decode_huffman(buffers)}; + if (progress != InflateStatus::Done) { + return this->leave(buffers, progress); + } + + break; + } + case State::Trailer: + if (!this->read_trailer(buffers)) { + return this->leave(buffers, InflateStatus::NeedInput); + } + + break; + case State::Done: + return this->leave(buffers, InflateStatus::Done); + } + } + } + + // Whole bytes already moved from the input into the bit buffer but not yet + // consumed. A caller that compacts its input buffer must keep them in place + // before the next input position + [[nodiscard]] auto buffered_input() const -> std::size_t { + return this->bits_available_ / 8; + } + +private: + enum class State : std::uint8_t { + MemberStart, + HeaderFixed, + HeaderExtraLength, + HeaderExtra, + HeaderName, + HeaderComment, + HeaderChecksum, + BlockHeader, + StoredLength, + StoredData, + Huffman, + Trailer, + Done + }; + + static constexpr std::uint8_t FLAG_HEADER_CHECKSUM{0x02}; + static constexpr std::uint8_t FLAG_EXTRA{0x04}; + static constexpr std::uint8_t FLAG_NAME{0x08}; + static constexpr std::uint8_t FLAG_COMMENT{0x10}; + static constexpr std::uint8_t FLAG_RESERVED{0xe0}; + + // Enough input for two word refills between the checks, and enough output + // for the longest match plus the bytes that chunked copies write past it + static constexpr std::size_t FAST_INPUT_MARGIN{32}; + static constexpr std::size_t FAST_OUTPUT_MARGIN{258 + 32}; + + // The smallest multiple of every distance below eight that is at least + // eight, so overlapping matches can continue with whole word copies + static constexpr std::array SHORT_DISTANCE_STEP{ + {0, 8, 8, 9, 8, 10, 12, 14}}; + + static auto load_word(const std::uint8_t *data) -> std::uint64_t { + std::uint64_t word{0}; + std::memcpy(&word, data, sizeof(word)); + if (std::endian::native == std::endian::big) { + word = std::byteswap(word); + } + + return word; + } + + static auto low_bits(const std::uint64_t value, const unsigned int count) + -> std::uint32_t { + return static_cast(value & + ((std::uint64_t{1} << count) - 1)); + } + + static auto available_input(const InflateBuffers &buffers) -> std::size_t { + return static_cast(buffers.input_end - buffers.input_next); + } + + static auto available_output(const InflateBuffers &buffers) -> std::size_t { + return static_cast(buffers.output_end - buffers.output_next); + } + + auto leave(InflateBuffers &buffers, const InflateStatus status) + -> InflateStatus { + this->flush_member_progress(buffers); + return status; + } + + auto flush_member_progress(const InflateBuffers &buffers) -> void { + const auto produced{static_cast(buffers.output_next - + this->checksum_position_)}; + if (produced > 0) { + this->member_checksum_ = + crc32_update(this->member_checksum_, + std::string_view{reinterpret_cast( + this->checksum_position_), + produced}); + this->member_size_ += static_cast(produced); + this->member_history_ += produced; + this->checksum_position_ = buffers.output_next; + } + } + + auto update_header_checksum(const std::uint8_t *data, const std::size_t size) + -> void { + if ((this->flags_ & FLAG_HEADER_CHECKSUM) != 0) { + this->header_checksum_ = crc32_update( + this->header_checksum_, + std::string_view{reinterpret_cast(data), size}); + } + } + + [[noreturn]] static auto unexpected_end() -> void { + throw GZIPError{"Unexpected end of source stream"}; + } + + [[nodiscard]] auto real_bits() const -> long long { + return static_cast(this->bits_available_) - + (static_cast(this->overread_) * 8); + } + + // Reports the input as truncated when the bits that led to the error were + // not all real input, as the error may only be an artifact of the padding + [[noreturn]] auto fail(const char *message, const long long bits_needed) const + -> void { + if (this->real_bits() < bits_needed) { + unexpected_end(); + } + + throw GZIPError{message}; + } + + auto check_not_overread() const -> void { + if (this->real_bits() < 0) { + unexpected_end(); + } + } + + auto consume(const unsigned int count) -> void { + this->bit_buffer_ >>= count; + this->bits_available_ -= count; + } + + // Past the end of the final input the bit buffer is padded with zero bytes, + // which are only an error if they end up consumed. Stopping short of 56 + // bits keeps at most 63 bits buffered, which the word refill relies on + auto refill_careful(InflateBuffers &buffers) -> void { + while (this->bits_available_ < 56) { + if (buffers.input_next < buffers.input_end) { + this->bit_buffer_ |= static_cast(*buffers.input_next) + << this->bits_available_; + buffers.input_next += 1; + } else if (buffers.input_final) { + this->overread_ += 1; + } else { + return; + } + + this->bits_available_ += 8; + } + } + + auto read_bits(InflateBuffers &buffers, const unsigned int count) + -> std::uint32_t { + if (this->bits_available_ < count) { + this->refill_careful(buffers); + } + + const auto value{low_bits(this->bit_buffer_, count)}; + this->consume(count); + return value; + } + + // Discards the bits up to the next byte boundary and hands the whole bytes + // still in the bit buffer back to the input + auto align_to_byte(InflateBuffers &buffers) -> void { + const auto whole_bytes{this->bits_available_ / 8}; + if (whole_bytes < this->overread_) { + unexpected_end(); + } + + buffers.input_next -= whole_bytes - this->overread_; + this->bit_buffer_ = 0; + this->bits_available_ = 0; + this->overread_ = 0; + } + + [[nodiscard]] auto header_state_after(const State completed) const -> State { + if (completed == State::HeaderFixed && (this->flags_ & FLAG_EXTRA) != 0) { + return State::HeaderExtraLength; + } + + if ((completed == State::HeaderFixed || completed == State::HeaderExtra) && + (this->flags_ & FLAG_NAME) != 0) { + return State::HeaderName; + } + + if (completed != State::HeaderComment && + (this->flags_ & FLAG_COMMENT) != 0) { + return State::HeaderComment; + } + + if ((this->flags_ & FLAG_HEADER_CHECKSUM) != 0) { + return State::HeaderChecksum; + } + + return State::BlockHeader; + } + + auto start_member(InflateBuffers &buffers) -> bool { + const auto available{available_input(buffers)}; + if (this->first_member_) { + if (available == 0) { + if (buffers.input_final) { + throw GZIPError{"Empty source stream"}; + } + + return false; + } + + if (buffers.input_next[0] != 0x1f) { + throw GZIPError{"Invalid gzip magic bytes"}; + } + + if (available < 2) { + if (buffers.input_final) { + unexpected_end(); + } + + return false; + } + + if (buffers.input_next[1] != 0x8b) { + throw GZIPError{"Invalid gzip magic bytes"}; + } + } else { + // Like gzip(1), data after a member that does not start with the + // identification bytes is ignored as trailing garbage + if (available < 2) { + if (buffers.input_final) { + this->state_ = State::Done; + return true; + } + + return false; + } + + if (buffers.input_next[0] != 0x1f || buffers.input_next[1] != 0x8b) { + this->state_ = State::Done; + return true; + } + } + + this->first_member_ = false; + this->flags_ = FLAG_HEADER_CHECKSUM; + this->header_checksum_ = 0; + this->update_header_checksum(buffers.input_next, 2); + buffers.input_next += 2; + this->member_checksum_ = 0; + this->member_size_ = 0; + this->member_history_ = 0; + this->checksum_position_ = buffers.output_next; + this->history_start_ = buffers.output_next; + this->state_ = State::HeaderFixed; + return true; + } + + auto read_header_fixed(InflateBuffers &buffers) -> bool { + const auto available{available_input(buffers)}; + if (available >= 1 && buffers.input_next[0] != 8) { + throw GZIPError{"Unsupported gzip compression method"}; + } + + if (available >= 2 && (buffers.input_next[1] & FLAG_RESERVED) != 0) { + throw GZIPError{"Reserved gzip FLG bits must be zero"}; + } + + // The compression method, flags, modification time, extra flags, and + // operating system + if (available < 8) { + if (buffers.input_final) { + unexpected_end(); + } + + return false; + } + + const std::uint8_t checksum_flag{FLAG_HEADER_CHECKSUM}; + const bool track{(buffers.input_next[1] & checksum_flag) != 0}; + this->flags_ = track ? checksum_flag : std::uint8_t{0}; + this->update_header_checksum(buffers.input_next, 8); + if (!track) { + this->header_checksum_ = 0; + } + + this->flags_ = buffers.input_next[1]; + buffers.input_next += 8; + this->state_ = this->header_state_after(State::HeaderFixed); + return true; + } + + auto read_header_extra_length(InflateBuffers &buffers) -> bool { + if (available_input(buffers) < 2) { + if (buffers.input_final) { + unexpected_end(); + } + + return false; + } + + this->skip_remaining_ = + static_cast(buffers.input_next[0]) | + (static_cast(buffers.input_next[1]) << 8); + this->update_header_checksum(buffers.input_next, 2); + buffers.input_next += 2; + this->state_ = State::HeaderExtra; + return true; + } + + auto skip_header_extra(InflateBuffers &buffers) -> bool { + const auto amount{ + std::min(this->skip_remaining_, available_input(buffers))}; + this->update_header_checksum(buffers.input_next, amount); + buffers.input_next += amount; + this->skip_remaining_ -= amount; + if (this->skip_remaining_ > 0) { + if (buffers.input_final) { + unexpected_end(); + } + + return false; + } + + this->state_ = this->header_state_after(State::HeaderExtra); + return true; + } + + // The file name and the comment are zero-terminated + auto skip_header_string(InflateBuffers &buffers) -> bool { + const auto available{available_input(buffers)}; + const auto *terminator{static_cast( + std::memchr(buffers.input_next, 0, available))}; + if (terminator == nullptr) { + this->update_header_checksum(buffers.input_next, available); + buffers.input_next += available; + if (buffers.input_final) { + unexpected_end(); + } + + return false; + } + + const auto amount{ + static_cast(terminator - buffers.input_next) + 1}; + this->update_header_checksum(buffers.input_next, amount); + buffers.input_next += amount; + this->state_ = this->header_state_after(this->state_); + return true; + } + + auto read_header_checksum(InflateBuffers &buffers) -> bool { + if (available_input(buffers) < 2) { + if (buffers.input_final) { + unexpected_end(); + } + + return false; + } + + // RFC 1952 section 2.3.1: the two least significant bytes of the CRC-32 + // of every header byte before this field + const auto stored{static_cast(buffers.input_next[0]) | + (static_cast(buffers.input_next[1]) << 8)}; + if (stored != (this->header_checksum_ & 0xffffU)) { + throw GZIPError{"FHCRC mismatch"}; + } + + buffers.input_next += 2; + this->state_ = State::BlockHeader; + return true; + } + + auto end_block(InflateBuffers &buffers) -> void { + // Checksumming the output of every block as soon as it ends reads it + // while it is still in cache + this->flush_member_progress(buffers); + if (this->final_block_) { + this->align_to_byte(buffers); + this->final_block_ = false; + this->state_ = State::Trailer; + } else { + this->state_ = State::BlockHeader; + } + } + + auto read_block_header(InflateBuffers &buffers) -> bool { + if (!buffers.input_final && available_input(buffers) < INPUT_MARGIN) { + return false; + } + + this->final_block_ = this->read_bits(buffers, 1) != 0; + const auto type{this->read_bits(buffers, 2)}; + this->check_not_overread(); + switch (type) { + case 0: + this->align_to_byte(buffers); + this->state_ = State::StoredLength; + return true; + case 1: + this->load_fixed_tables(); + this->state_ = State::Huffman; + return true; + case 2: + this->read_dynamic_header(buffers); + this->state_ = State::Huffman; + return true; + default: + throw GZIPError{"Reserved deflate block type"}; + } + } + + auto read_stored_length(InflateBuffers &buffers) -> bool { + if (available_input(buffers) < 4) { + if (buffers.input_final) { + unexpected_end(); + } + + return false; + } + + const auto length{static_cast(buffers.input_next[0]) | + (static_cast(buffers.input_next[1]) << 8)}; + const auto complement{ + static_cast(buffers.input_next[2]) | + (static_cast(buffers.input_next[3]) << 8)}; + if (length != (~complement & 0xffffU)) { + throw GZIPError{"Stored block LEN/NLEN mismatch"}; + } + + buffers.input_next += 4; + this->stored_remaining_ = length; + this->state_ = State::StoredData; + return true; + } + + auto copy_stored_data(InflateBuffers &buffers) -> InflateStatus { + while (this->stored_remaining_ > 0) { + if (available_output(buffers) == 0) { + return InflateStatus::OutputFull; + } + + const auto input{available_input(buffers)}; + if (input == 0) { + if (buffers.input_final) { + unexpected_end(); + } + + return InflateStatus::NeedInput; + } + + const auto amount{std::min( + {this->stored_remaining_, input, available_output(buffers)})}; + std::memcpy(buffers.output_next, buffers.input_next, amount); + buffers.input_next += amount; + buffers.output_next += amount; + this->stored_remaining_ -= amount; + } + + this->end_block(buffers); + return InflateStatus::Done; + } + + auto read_trailer(InflateBuffers &buffers) -> bool { + if (available_input(buffers) < 8) { + if (buffers.input_final) { + unexpected_end(); + } + + return false; + } + + this->flush_member_progress(buffers); + const auto *data{buffers.input_next}; + const auto checksum{static_cast(data[0]) | + (static_cast(data[1]) << 8) | + (static_cast(data[2]) << 16) | + (static_cast(data[3]) << 24)}; + const auto size{static_cast(data[4]) | + (static_cast(data[5]) << 8) | + (static_cast(data[6]) << 16) | + (static_cast(data[7]) << 24)}; + if (checksum != this->member_checksum_) { + throw GZIPError{"Gzip member CRC32 mismatch"}; + } + + if (size != this->member_size_) { + throw GZIPError{"Gzip member ISIZE mismatch"}; + } + + buffers.input_next += 8; + this->state_ = State::MemberStart; + return true; + } + + auto load_fixed_tables() -> void { + if (this->fixed_tables_) { + return; + } + + // RFC 1951 section 3.2.6 + std::array literal_lengths{}; + std::memset(literal_lengths.data(), 8, 144); + std::memset(literal_lengths.data() + 144, 9, 112); + std::memset(literal_lengths.data() + 256, 7, 24); + std::memset(literal_lengths.data() + 280, 8, 8); + inflate_build_table(literal_lengths.data(), literal_lengths.size(), + INFLATE_LITERAL_LENGTH_RESULTS.data(), + INFLATE_LITERAL_LENGTH_TABLE_BITS, + this->literal_length_table_.data()); + std::array distance_lengths{}; + std::memset(distance_lengths.data(), 5, distance_lengths.size()); + inflate_build_table(distance_lengths.data(), distance_lengths.size(), + INFLATE_DISTANCE_RESULTS.data(), + INFLATE_DISTANCE_TABLE_BITS, + this->distance_table_.data()); + this->fixed_tables_ = true; + } + + auto read_dynamic_header(InflateBuffers &buffers) -> void { + const auto literal_length_count{this->read_bits(buffers, 5) + 257}; + const auto distance_count{this->read_bits(buffers, 5) + 1}; + const auto code_length_count{this->read_bits(buffers, 4) + 4}; + if (literal_length_count > 286) { + this->fail("Too many literal/length codes", 0); + } + + std::array code_length_lengths{}; + for (std::size_t index = 0; index < code_length_count; ++index) { + code_length_lengths[INFLATE_CODE_LENGTH_ORDER[index]] = + static_cast(this->read_bits(buffers, 3)); + } + + const auto *code_length_error{inflate_build_table( + code_length_lengths.data(), code_length_lengths.size(), + INFLATE_CODE_LENGTH_RESULTS.data(), INFLATE_CODE_LENGTH_TABLE_BITS, + this->code_length_table_.data())}; + if (code_length_error != nullptr) { + this->fail(code_length_error, 0); + } + + std::array + lengths{}; + const std::size_t total{literal_length_count + distance_count}; + std::size_t index{0}; + while (index < total) { + if (this->bits_available_ < 14) { + this->refill_careful(buffers); + } + + const auto entry{this->code_length_table_[low_bits( + this->bit_buffer_, INFLATE_CODE_LENGTH_TABLE_BITS)]}; + if ((entry & INFLATE_ENTRY_EXCEPTIONAL) != 0) { + this->fail("Invalid Huffman code", INFLATE_CODE_LENGTH_TABLE_BITS); + } + + this->consume(entry & 0x1fU); + const auto symbol{entry >> 16}; + if (symbol < 16) { + lengths[index] = static_cast(symbol); + index += 1; + continue; + } + + std::size_t repeats{0}; + std::uint8_t value{0}; + if (symbol == 16) { + if (index == 0) { + this->fail("Repeat-previous code length with no previous", 0); + } + + value = lengths[index - 1]; + repeats = this->read_bits(buffers, 2) + 3; + } else if (symbol == 17) { + repeats = this->read_bits(buffers, 3) + 3; + } else { + repeats = this->read_bits(buffers, 7) + 11; + } + + if (index + repeats > total) { + this->fail("Code length count overflow", 0); + } + + std::memset(lengths.data() + index, value, repeats); + index += repeats; + } + + this->check_not_overread(); + const auto *literal_length_error{inflate_build_table( + lengths.data(), literal_length_count, + INFLATE_LITERAL_LENGTH_RESULTS.data(), + INFLATE_LITERAL_LENGTH_TABLE_BITS, this->literal_length_table_.data())}; + if (literal_length_error != nullptr) { + throw GZIPError{literal_length_error}; + } + + const auto *distance_error{inflate_build_table( + lengths.data() + literal_length_count, distance_count, + INFLATE_DISTANCE_RESULTS.data(), INFLATE_DISTANCE_TABLE_BITS, + this->distance_table_.data())}; + if (distance_error != nullptr) { + throw GZIPError{distance_error}; + } + + this->fixed_tables_ = false; + } + + // Copies the match byte by byte, which is always correct for overlapping + // ranges, stopping when the output fills up + auto copy_match_careful(InflateBuffers &buffers) -> InflateStatus { + const auto amount{ + std::min(this->pending_length_, available_output(buffers))}; + const std::uint8_t *source{buffers.output_next - this->pending_distance_}; + for (std::size_t index = 0; index < amount; ++index) { + buffers.output_next[index] = source[index]; + } + + buffers.output_next += amount; + this->pending_length_ -= amount; + return this->pending_length_ > 0 ? InflateStatus::OutputFull + : InflateStatus::Done; + } + + auto decode_huffman(InflateBuffers &buffers) -> InflateStatus { + if (this->pending_length_ > 0 && + this->copy_match_careful(buffers) != InflateStatus::Done) { + return InflateStatus::OutputFull; + } + + if (this->decode_huffman_fast(buffers)) { + return InflateStatus::Done; + } + + return this->decode_huffman_careful(buffers); + } + + // Returns whether the block ended + auto decode_huffman_fast(InflateBuffers &buffers) -> bool { + const auto *literal_length_table{this->literal_length_table_.data()}; + const auto *distance_table{this->distance_table_.data()}; + const auto *input_next{buffers.input_next}; + const auto *const input_limit{available_input(buffers) >= FAST_INPUT_MARGIN + ? buffers.input_end - FAST_INPUT_MARGIN + : buffers.input_next}; + auto *output_next{buffers.output_next}; + auto *const output_limit{available_output(buffers) >= FAST_OUTPUT_MARGIN + ? buffers.output_end - FAST_OUTPUT_MARGIN + : buffers.output_next}; + const auto *const history_start{this->history_start_}; + auto bit_buffer{this->bit_buffer_}; + auto bits_available{this->bits_available_}; + // Only the lowest six bits of the bit count are meaningful, which lets + // literals subtract their whole table entry instead of masking it first + bool block_ended{false}; + + // Every iteration starts with at least 56 buffered bits, enough for + // three literals from the primary table or for a whole match + if (input_next < input_limit && output_next < output_limit) { + bit_buffer |= load_word(input_next) << (bits_available & 63U); + input_next += (63U - (bits_available & 63U)) >> 3; + bits_available |= 56; + auto entry{literal_length_table[low_bits( + bit_buffer, INFLATE_LITERAL_LENGTH_TABLE_BITS)]}; + while (true) { + if ((entry & INFLATE_ENTRY_LITERAL) != 0) { + // Every literal leaves the entry of the next symbol loaded, and a + // refill only appends bits past the ones that entry was read from + bit_buffer >>= entry & 0x3fU; + bits_available -= entry; + *output_next++ = static_cast(entry >> 16); + entry = literal_length_table[low_bits( + bit_buffer, INFLATE_LITERAL_LENGTH_TABLE_BITS)]; + if ((entry & INFLATE_ENTRY_LITERAL) != 0) { + bit_buffer >>= entry & 0x3fU; + bits_available -= entry; + *output_next++ = static_cast(entry >> 16); + entry = literal_length_table[low_bits( + bit_buffer, INFLATE_LITERAL_LENGTH_TABLE_BITS)]; + if ((entry & INFLATE_ENTRY_LITERAL) != 0) { + bit_buffer >>= entry & 0x3fU; + bits_available -= entry; + *output_next++ = static_cast(entry >> 16); + entry = literal_length_table[low_bits( + bit_buffer, INFLATE_LITERAL_LENGTH_TABLE_BITS)]; + } + } + + if (input_next >= input_limit || output_next >= output_limit) { + break; + } + + bit_buffer |= load_word(input_next) << (bits_available & 63U); + input_next += (63U - (bits_available & 63U)) >> 3; + bits_available |= 56; + continue; + } + + if ((entry & INFLATE_ENTRY_SUBTABLE) != 0) { + bit_buffer >>= INFLATE_LITERAL_LENGTH_TABLE_BITS; + bits_available -= INFLATE_LITERAL_LENGTH_TABLE_BITS; + entry = literal_length_table + [(entry >> 16) + + low_bits(bit_buffer, INFLATE_MAXIMUM_CODEWORD_LENGTH - + INFLATE_LITERAL_LENGTH_TABLE_BITS)]; + } + + if ((entry & INFLATE_ENTRY_LITERAL) != 0) { + bit_buffer >>= entry & 0x3fU; + bits_available -= entry; + *output_next++ = static_cast(entry >> 16); + } else if ((entry & INFLATE_ENTRY_EXCEPTIONAL) != 0) { + if ((entry & INFLATE_ENTRY_END_OF_BLOCK) != 0) { + bit_buffer >>= entry & 0x1fU; + bits_available -= entry & 0x1fU; + block_ended = true; + break; + } + + if ((entry & INFLATE_ENTRY_INVALID_CODE) != 0) { + throw GZIPError{"Invalid Huffman code"}; + } + + throw GZIPError{"Invalid literal/length code"}; + } else { + const auto buffer_before_length{bit_buffer}; + bit_buffer >>= entry & 0x3fU; + bits_available -= entry; + const std::size_t length{ + (entry >> 16) + (low_bits(buffer_before_length, entry & 0x1fU) >> + ((entry >> 8) & 0x1fU))}; + + auto distance_entry{distance_table[low_bits( + bit_buffer, INFLATE_DISTANCE_TABLE_BITS)]}; + if ((distance_entry & INFLATE_ENTRY_EXCEPTIONAL) != 0) { + if ((distance_entry & INFLATE_ENTRY_SUBTABLE) != 0) { + bit_buffer >>= INFLATE_DISTANCE_TABLE_BITS; + bits_available -= INFLATE_DISTANCE_TABLE_BITS; + distance_entry = + distance_table[(distance_entry >> 16) + + low_bits(bit_buffer, + INFLATE_MAXIMUM_CODEWORD_LENGTH - + INFLATE_DISTANCE_TABLE_BITS)]; + } + + if ((distance_entry & INFLATE_ENTRY_INVALID_CODE) != 0) { + throw GZIPError{"Invalid Huffman code"}; + } + + if ((distance_entry & INFLATE_ENTRY_INVALID_SYMBOL) != 0) { + throw GZIPError{"Invalid distance code"}; + } + } + + const auto buffer_before_distance{bit_buffer}; + bit_buffer >>= distance_entry & 0x3fU; + bits_available -= distance_entry; + const std::size_t distance{ + (distance_entry >> 16) + + (low_bits(buffer_before_distance, distance_entry & 0x1fU) >> + ((distance_entry >> 8) & 0x1fU))}; + + if (std::cmp_greater(distance, output_next - history_start)) { + throw GZIPError{"Backref distance exceeds bytes available"}; + } + + // A match leaves too few bits for the next lookup, so refill and + // load the next entry before copying, which keeps a mispredicted + // copy loop from holding back the decoding that follows + bit_buffer |= load_word(input_next) << (bits_available & 63U); + input_next += (63U - (bits_available & 63U)) >> 3; + bits_available |= 56; + entry = literal_length_table[low_bits( + bit_buffer, INFLATE_LITERAL_LENGTH_TABLE_BITS)]; + + // Whole chunks may run past the end of the match, which the + // output margin leaves room for. Most matches fit in the chunks + // copied before the loop, so its branch is rarely taken + auto *destination{output_next}; + const auto *source{output_next - distance}; + output_next += length; + if (distance >= 16) { + std::memcpy(destination, source, 16); + std::memcpy(destination + 16, source + 16, 16); + std::memcpy(destination + 32, source + 32, 16); + destination += 48; + source += 48; + while (destination < output_next) { + std::memcpy(destination, source, 16); + destination += 16; + source += 16; + } + } else if (distance >= 8) { + std::memcpy(destination, source, 8); + std::memcpy(destination + 8, source + 8, 8); + std::memcpy(destination + 16, source + 16, 8); + std::memcpy(destination + 24, source + 24, 8); + std::memcpy(destination + 32, source + 32, 8); + destination += 40; + source += 40; + while (destination < output_next) { + std::memcpy(destination, source, 8); + destination += 8; + source += 8; + } + } else if (distance == 1) { + const std::uint64_t pattern{0x0101010101010101ULL * *source}; + std::memcpy(destination, &pattern, 8); + std::memcpy(destination + 8, &pattern, 8); + std::memcpy(destination + 16, &pattern, 8); + std::memcpy(destination + 24, &pattern, 8); + destination += 32; + while (destination < output_next) { + std::memcpy(destination, &pattern, 8); + destination += 8; + } + } else { + // Once one whole step of the repeating pattern is written, + // the rest is a word copy from that far back + const auto step{SHORT_DISTANCE_STEP[distance]}; + const auto *const expanded{destination + step}; + while (destination < expanded) { + *destination++ = *source++; + } + + while (destination < output_next) { + std::memcpy(destination, destination - step, 8); + destination += 8; + } + } + + if (input_next >= input_limit || output_next >= output_limit) { + break; + } + + continue; + } + + if (input_next >= input_limit || output_next >= output_limit) { + break; + } + + bit_buffer |= load_word(input_next) << (bits_available & 63U); + input_next += (63U - (bits_available & 63U)) >> 3; + bits_available |= 56; + entry = literal_length_table[low_bits( + bit_buffer, INFLATE_LITERAL_LENGTH_TABLE_BITS)]; + } + } + + buffers.input_next = input_next; + buffers.output_next = output_next; + this->bit_buffer_ = bit_buffer; + this->bits_available_ = bits_available & 63U; + if (block_ended) { + this->end_block(buffers); + } + + return block_ended; + } + + auto decode_huffman_careful(InflateBuffers &buffers) -> InflateStatus { + while (true) { + if (!buffers.input_final && available_input(buffers) < 8) { + return InflateStatus::NeedInput; + } + + this->refill_careful(buffers); + auto entry{this->literal_length_table_[low_bits( + this->bit_buffer_, INFLATE_LITERAL_LENGTH_TABLE_BITS)]}; + unsigned int prefix{0}; + if ((entry & INFLATE_ENTRY_SUBTABLE) != 0) { + prefix = INFLATE_LITERAL_LENGTH_TABLE_BITS; + entry = this->literal_length_table_ + [(entry >> 16) + + low_bits(this->bit_buffer_ >> prefix, + INFLATE_MAXIMUM_CODEWORD_LENGTH - + INFLATE_LITERAL_LENGTH_TABLE_BITS)]; + } + + if ((entry & INFLATE_ENTRY_INVALID_CODE) != 0) { + this->fail("Invalid Huffman code", INFLATE_MAXIMUM_CODEWORD_LENGTH); + } + + const unsigned int consumed{prefix + (entry & 0x1fU)}; + if ((entry & INFLATE_ENTRY_INVALID_SYMBOL) != 0) { + this->fail("Invalid literal/length code", consumed); + } + + if ((entry & INFLATE_ENTRY_END_OF_BLOCK) != 0) { + this->consume(consumed); + this->check_not_overread(); + this->end_block(buffers); + return InflateStatus::Done; + } + + // Nothing past the end of the block needs output space, so only a + // literal or a match makes a full output buffer suspend the decoder + if (available_output(buffers) == 0) { + return InflateStatus::OutputFull; + } + + if ((entry & INFLATE_ENTRY_LITERAL) != 0) { + this->consume(consumed); + this->check_not_overread(); + *buffers.output_next++ = static_cast(entry >> 16); + continue; + } + + const unsigned int length_codeword{prefix + ((entry >> 8) & 0x1fU)}; + const std::size_t length{(entry >> 16) + + low_bits(this->bit_buffer_ >> length_codeword, + consumed - length_codeword)}; + this->consume(consumed); + + auto distance_entry{this->distance_table_[low_bits( + this->bit_buffer_, INFLATE_DISTANCE_TABLE_BITS)]}; + unsigned int distance_prefix{0}; + if ((distance_entry & INFLATE_ENTRY_SUBTABLE) != 0) { + distance_prefix = INFLATE_DISTANCE_TABLE_BITS; + distance_entry = + this->distance_table_[(distance_entry >> 16) + + low_bits(this->bit_buffer_ >> distance_prefix, + INFLATE_MAXIMUM_CODEWORD_LENGTH - + INFLATE_DISTANCE_TABLE_BITS)]; + } + + if ((distance_entry & INFLATE_ENTRY_INVALID_CODE) != 0) { + this->fail("Invalid Huffman code", INFLATE_MAXIMUM_CODEWORD_LENGTH); + } + + const unsigned int distance_consumed{distance_prefix + + (distance_entry & 0x1fU)}; + if ((distance_entry & INFLATE_ENTRY_INVALID_SYMBOL) != 0) { + this->fail("Invalid distance code", distance_consumed); + } + + const unsigned int distance_codeword{distance_prefix + + ((distance_entry >> 8) & 0x1fU)}; + const std::size_t distance{ + (distance_entry >> 16) + + low_bits(this->bit_buffer_ >> distance_codeword, + distance_consumed - distance_codeword)}; + this->consume(distance_consumed); + this->check_not_overread(); + + if (std::cmp_greater(distance, + buffers.output_next - this->history_start_)) { + throw GZIPError{"Backref distance exceeds bytes available"}; + } + + this->pending_length_ = length; + this->pending_distance_ = distance; + if (this->copy_match_careful(buffers) != InflateStatus::Done) { + return InflateStatus::OutputFull; + } + } + } + + State state_{State::MemberStart}; + bool first_member_{true}; + bool final_block_{false}; + bool fixed_tables_{false}; + std::uint8_t flags_{0}; + std::uint64_t bit_buffer_{0}; + unsigned int bits_available_{0}; + unsigned int overread_{0}; + std::size_t skip_remaining_{0}; + std::size_t stored_remaining_{0}; + std::size_t pending_length_{0}; + std::size_t pending_distance_{0}; + std::uint32_t header_checksum_{0}; + std::uint32_t member_checksum_{0}; + std::uint32_t member_size_{0}; + std::size_t member_history_{0}; + const std::uint8_t *checksum_position_{nullptr}; + const std::uint8_t *history_start_{nullptr}; + // Every table is built before it is read, so zeroing them upfront would + // only slow down every construction + // NOLINTBEGIN(cppcoreguidelines-pro-type-member-init,hicpp-member-init) + std::array + literal_length_table_; + std::array distance_table_; + std::array code_length_table_; + // NOLINTEND(cppcoreguidelines-pro-type-member-init,hicpp-member-init) +}; + +} // namespace sourcemeta::core + +#endif diff --git a/src/core/gzip/streambuf.cc b/src/core/gzip/streambuf.cc index 15c318e3e7..1ac89100a7 100644 --- a/src/core/gzip/streambuf.cc +++ b/src/core/gzip/streambuf.cc @@ -1,152 +1,55 @@ #include -#include "bit_reader.h" -#include "deflate.h" +#include "inflate.h" -#include - -#include // std::array -#include // std::size_t, std::ptrdiff_t -#include // std::uint8_t, std::uint16_t, std::uint32_t -#include // std::istream -#include // std::string_view +#include // std::size_t +#include // std::uint8_t +#include // std::memmove +#include // std::streamsize +#include // std::istream +#include // std::vector namespace sourcemeta::core { -static constexpr std::size_t GZIP_BUFFER_SIZE{16384}; +static constexpr std::size_t GZIP_INPUT_BUFFER_SIZE{65536}; +static constexpr std::size_t GZIP_OUTPUT_BUFFER_SIZE{262144}; +// RFC 1951 section 3.2.5 caps the distance of a back-reference +static constexpr std::size_t GZIP_HISTORY_SIZE{32768}; struct GZIPStreamBuffer::Internal { // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members) std::istream &stream; - BitReader reader; - DeflateDecoder deflate; + InflateDecoder decoder; + std::vector input; + std::vector output; + std::size_t input_next{0}; + std::size_t input_end{0}; + std::size_t output_next{0}; + bool input_final{false}; bool stream_ended{false}; - bool any_member_completed{false}; - bool member_started{false}; - std::uint32_t member_crc32{0}; - std::uint32_t member_isize{0}; - std::array decompressed_buffer{}; Internal(std::istream &source) - : stream{source}, reader{source}, deflate{reader} {} -}; - -namespace { - -// Accumulates the running CRC-32 over the header bytes that the FHCRC check -// covers, computing it only when the FHCRC flag is present -class HeaderChecksum { -public: - HeaderChecksum(const bool track) : track_{track} {} - - auto feed(const std::uint8_t byte) -> void { - if (this->track_) { - const auto data{static_cast(byte)}; - this->checksum_ = - crc32_update(this->checksum_, std::string_view{&data, 1}); + : stream{source}, input(GZIP_INPUT_BUFFER_SIZE), + output(GZIP_OUTPUT_BUFFER_SIZE) {} + + // The bytes the decoder already moved into its bit buffer stay in place, as + // the decoder may hand them back to the input + auto refill() -> void { + const auto keep{this->input_next - this->decoder.buffered_input()}; + std::memmove(this->input.data(), this->input.data() + keep, + this->input_end - keep); + this->input_end -= keep; + this->input_next -= keep; + this->stream.read( + reinterpret_cast(this->input.data() + this->input_end), + static_cast(GZIP_INPUT_BUFFER_SIZE - this->input_end)); + this->input_end += static_cast(this->stream.gcount()); + if (!this->stream) { + this->input_final = true; } } - - [[nodiscard]] auto low16() const -> std::uint16_t { - return static_cast(this->checksum_ & 0xffffU); - } - -private: - bool track_; - std::uint32_t checksum_{0}; }; -auto read_header_byte(BitReader &reader, HeaderChecksum &checksum) - -> std::uint8_t { - const auto byte{reader.read_byte()}; - checksum.feed(byte); - return byte; -} - -auto parse_member_header(BitReader &reader, const std::uint8_t first_byte) - -> void { - // RFC 1952 section 2.3.1.2: FHCRC covers every header byte up to but not - // including the CRC16 itself, so feeding each byte as it is read produces - // exactly the right value. The bytes are not retained, removing an - // unbounded-memory path through FNAME and FCOMMENT - - // Caller already consumed the ID1 byte and verified it is 0x1f - const auto id2{reader.read_byte()}; - if (id2 != 0x8b) { - throw GZIPError{"Invalid gzip magic bytes"}; - } - const auto compression_method{reader.read_byte()}; - if (compression_method != 8) { - throw GZIPError{"Unsupported gzip compression method"}; - } - const auto flag_byte{reader.read_byte()}; - if ((flag_byte & 0xe0) != 0) { - throw GZIPError{"Reserved gzip FLG bits must be zero"}; - } - - HeaderChecksum checksum{(flag_byte & 0x02) != 0}; - checksum.feed(first_byte); - checksum.feed(id2); - checksum.feed(compression_method); - checksum.feed(flag_byte); - - // MTIME (4 bytes) + XFL (1 byte) + OS (1 byte) are informational - for (std::size_t index = 0; index < 6; ++index) { - read_header_byte(reader, checksum); - } - - if ((flag_byte & 0x04) != 0) { - // FEXTRA - const auto xlen_lo{read_header_byte(reader, checksum)}; - const auto xlen_hi{read_header_byte(reader, checksum)}; - const auto xlen{static_cast(xlen_lo) | - (static_cast(xlen_hi) << 8)}; - for (std::size_t index = 0; index < xlen; ++index) { - read_header_byte(reader, checksum); - } - } - - if ((flag_byte & 0x08) != 0) { - // FNAME (null-terminated) - while (read_header_byte(reader, checksum) != 0) { - } - } - - if ((flag_byte & 0x10) != 0) { - // FCOMMENT (null-terminated) - while (read_header_byte(reader, checksum) != 0) { - } - } - - if ((flag_byte & 0x02) != 0) { - // FHCRC: low 16 bits of CRC-32 over all preceding header bytes - const auto stored_lo{reader.read_byte()}; - const auto stored_hi{reader.read_byte()}; - const std::uint16_t stored{static_cast( - static_cast(stored_lo) | - static_cast(static_cast(stored_hi) - << 8))}; - if (stored != checksum.low16()) { - throw GZIPError{"FHCRC mismatch"}; - } - } -} - -// Used for members past the first, where gzip(1) tolerates trailing -// non-member data, so a header that fails to validate is reported as -// trailing garbage rather than propagated as an error -auto try_parse_member_header(BitReader &reader, const std::uint8_t first_byte) - -> bool { - try { - parse_member_header(reader, first_byte); - return true; - } catch (const GZIPError &) { - return false; - } -} - -} // namespace - GZIPStreamBuffer::GZIPStreamBuffer(std::istream &compressed_stream) : internal_{new Internal{compressed_stream}} {} @@ -156,86 +59,47 @@ auto GZIPStreamBuffer::underflow() -> int_type { if ((this->gptr() != nullptr) && this->gptr() < this->egptr()) { return traits_type::to_int_type(*this->gptr()); } - if (this->internal_->stream_ended) { - return traits_type::eof(); - } - - while (true) { - if (!this->internal_->member_started) { - std::uint8_t first_byte{0}; - if (!this->internal_->reader.try_read_byte(first_byte)) { - if (!this->internal_->any_member_completed) { - throw GZIPError{"Empty source stream"}; - } - this->internal_->stream_ended = true; - return traits_type::eof(); - } - if (this->internal_->any_member_completed) { - // gzip(1) silently ignores any trailing data after a complete member - // rather than treating it as the start of a new member. Bytes that do - // not form a valid member header end the stream without error, - // independent of the first byte value - if (first_byte != 0x1f || - !try_parse_member_header(this->internal_->reader, first_byte)) { - this->internal_->stream_ended = true; - return traits_type::eof(); - } - } else { - if (first_byte != 0x1f) { - throw GZIPError{"Invalid gzip magic bytes"}; - } - parse_member_header(this->internal_->reader, first_byte); - } - this->internal_->deflate.reset(); - this->internal_->member_started = true; - this->internal_->member_crc32 = 0; - this->internal_->member_isize = 0; + auto &internal{*this->internal_}; + while (!internal.stream_ended) { + if (GZIP_OUTPUT_BUFFER_SIZE - internal.output_next < GZIP_HISTORY_SIZE) { + std::memmove(internal.output.data(), + internal.output.data() + internal.output_next - + GZIP_HISTORY_SIZE, + GZIP_HISTORY_SIZE); + internal.output_next = GZIP_HISTORY_SIZE; } - const auto produced{this->internal_->deflate.decompress( - this->internal_->decompressed_buffer.data(), - this->internal_->decompressed_buffer.size())}; - - if (produced > 0) { - this->internal_->member_crc32 = crc32_update( - this->internal_->member_crc32, - std::string_view{reinterpret_cast( - this->internal_->decompressed_buffer.data()), - produced}); - this->internal_->member_isize += static_cast(produced); - - auto *buffer_start{reinterpret_cast( - this->internal_->decompressed_buffer.data())}; - this->setg(buffer_start, buffer_start, - buffer_start + static_cast(produced)); - return traits_type::to_int_type(*this->gptr()); + const auto produced_start{internal.output_next}; + InflateBuffers buffers{ + .input_next = internal.input.data() + internal.input_next, + .input_end = internal.input.data() + internal.input_end, + .input_final = internal.input_final, + .output_begin = internal.output.data(), + .output_next = internal.output.data() + internal.output_next, + .output_end = internal.output.data() + GZIP_OUTPUT_BUFFER_SIZE}; + const auto status{internal.decoder.decode(buffers)}; + internal.input_next = + static_cast(buffers.input_next - internal.input.data()); + internal.output_next = + static_cast(buffers.output_next - internal.output.data()); + if (status == InflateStatus::Done) { + internal.stream_ended = true; + } else if (status == InflateStatus::NeedInput) { + internal.refill(); } - if (!this->internal_->deflate.stream_ended()) { - throw GZIPError{"Deflate stream ended unexpectedly"}; - } - - std::array trailer{}; - this->internal_->reader.read_bytes(trailer.data(), trailer.size()); - const auto stored_crc32{static_cast(trailer[0]) | - (static_cast(trailer[1]) << 8) | - (static_cast(trailer[2]) << 16) | - (static_cast(trailer[3]) << 24)}; - const auto stored_isize{static_cast(trailer[4]) | - (static_cast(trailer[5]) << 8) | - (static_cast(trailer[6]) << 16) | - (static_cast(trailer[7]) << 24)}; - if (stored_crc32 != this->internal_->member_crc32) { - throw GZIPError{"Gzip member CRC32 mismatch"}; - } - if (stored_isize != this->internal_->member_isize) { - throw GZIPError{"Gzip member ISIZE mismatch"}; + if (internal.output_next > produced_start) { + auto *const start{ + reinterpret_cast(internal.output.data() + produced_start)}; + this->setg(start, start, + reinterpret_cast(internal.output.data() + + internal.output_next)); + return traits_type::to_int_type(*this->gptr()); } - - this->internal_->any_member_completed = true; - this->internal_->member_started = false; } + + return traits_type::eof(); } } // namespace sourcemeta::core diff --git a/test/gzip/CMakeLists.txt b/test/gzip/CMakeLists.txt index 74714327df..413b42b7ee 100644 --- a/test/gzip/CMakeLists.txt +++ b/test/gzip/CMakeLists.txt @@ -1,7 +1,13 @@ sourcemeta_test(NAMESPACE sourcemeta PROJECT core NAME gzip SOURCES - gzip_test.cc - gzip_error_test.cc + gzip_compress_error_test.cc + gzip_compress_format_test.cc + gzip_compress_size_test.cc + gzip_compress_test.cc + gzip_decompress_error_test.cc + gzip_decompress_limits_error_test.cc + gzip_decompress_limits_test.cc + gzip_decompress_test.cc gzip_streambuf_test.cc) target_link_libraries(sourcemeta_core_gzip_unit diff --git a/test/gzip/gzip_compress_error_test.cc b/test/gzip/gzip_compress_error_test.cc new file mode 100644 index 0000000000..c1bb3d350b --- /dev/null +++ b/test/gzip/gzip_compress_error_test.cc @@ -0,0 +1,44 @@ +#include +#include + +#include // std::uint8_t +#include // std::numeric_limits +#include // std::string + +namespace { + +auto compress_error(const int level) -> std::string { + const std::string input{"hello world"}; + try { + sourcemeta::core::gzip(reinterpret_cast(input.data()), + input.size(), level); + } catch (const sourcemeta::core::GZIPError &error) { + return error.what(); + } + + FAIL(); +} + +} // namespace + +TEST(compress_level_above_maximum) { + EXPECT_EQ(compress_error(13), "Invalid compression level"); +} + +TEST(compress_negative_level) { + EXPECT_EQ(compress_error(-2), "Invalid compression level"); +} + +TEST(compress_minus_one_level) { + EXPECT_EQ(compress_error(-1), "Invalid compression level"); +} + +TEST(compress_largest_integer_level) { + EXPECT_EQ(compress_error(std::numeric_limits::max()), + "Invalid compression level"); +} + +TEST(compress_smallest_integer_level) { + EXPECT_EQ(compress_error(std::numeric_limits::min()), + "Invalid compression level"); +} diff --git a/test/gzip/gzip_compress_format_test.cc b/test/gzip/gzip_compress_format_test.cc new file mode 100644 index 0000000000..1fe5c38bbb --- /dev/null +++ b/test/gzip/gzip_compress_format_test.cc @@ -0,0 +1,90 @@ +#include +#include + +#include // std::uint8_t +#include // std::string +#include // std::vector + +namespace { + +auto compress(const std::string &input, const int level) + -> std::vector { + const auto output{sourcemeta::core::gzip( + reinterpret_cast(input.data()), input.size(), + level)}; + return {output.cbegin(), output.cend()}; +} + +} // namespace + +TEST(compress_output_header_identifies_deflate_member) { + const auto compressed{compress("hello world", 6)}; + EXPECT_GE(compressed.size(), 18); + EXPECT_EQ(compressed.at(0), 0x1f); + EXPECT_EQ(compressed.at(1), 0x8b); + EXPECT_EQ(compressed.at(2), 0x08); + EXPECT_EQ(compressed.at(3) & 0xe0, 0); +} + +TEST(compress_output_trailer_of_empty_input) { + const auto compressed{compress("", 1)}; + const std::vector trailer{compressed.cend() - 8, + compressed.cend()}; + const std::vector expected{0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00}; + EXPECT_EQ(trailer, expected); +} + +TEST(compress_output_trailer_of_hello_world_at_level_0) { + const auto compressed{compress("hello world", 0)}; + const std::vector trailer{compressed.cend() - 8, + compressed.cend()}; + const std::vector expected{0x85, 0x11, 0x4a, 0x0d, + 0x0b, 0x00, 0x00, 0x00}; + EXPECT_EQ(trailer, expected); +} + +TEST(compress_output_trailer_of_hello_world_at_level_1) { + const auto compressed{compress("hello world", 1)}; + const std::vector trailer{compressed.cend() - 8, + compressed.cend()}; + const std::vector expected{0x85, 0x11, 0x4a, 0x0d, + 0x0b, 0x00, 0x00, 0x00}; + EXPECT_EQ(trailer, expected); +} + +TEST(compress_output_trailer_of_hello_world_at_level_12) { + const auto compressed{compress("hello world", 12)}; + const std::vector trailer{compressed.cend() - 8, + compressed.cend()}; + const std::vector expected{0x85, 0x11, 0x4a, 0x0d, + 0x0b, 0x00, 0x00, 0x00}; + EXPECT_EQ(trailer, expected); +} + +TEST(compress_output_trailer_of_crc32_check_string) { + const auto compressed{compress("123456789", 6)}; + const std::vector trailer{compressed.cend() - 8, + compressed.cend()}; + const std::vector expected{0x26, 0x39, 0xf4, 0xcb, + 0x09, 0x00, 0x00, 0x00}; + EXPECT_EQ(trailer, expected); +} + +TEST(compress_output_trailer_of_pangram) { + const auto compressed{ + compress("The quick brown fox jumps over the lazy dog", 9)}; + const std::vector trailer{compressed.cend() - 8, + compressed.cend()}; + const std::vector expected{0x39, 0xa3, 0x4f, 0x41, + 0x2b, 0x00, 0x00, 0x00}; + EXPECT_EQ(trailer, expected); +} + +TEST(compress_output_trailer_size_spans_multiple_bytes) { + const auto compressed{compress(std::string(70000, 'z'), 1)}; + const std::vector size{compressed.cend() - 4, + compressed.cend()}; + const std::vector expected{0x70, 0x11, 0x01, 0x00}; + EXPECT_EQ(size, expected); +} diff --git a/test/gzip/gzip_compress_size_test.cc b/test/gzip/gzip_compress_size_test.cc new file mode 100644 index 0000000000..56ed140b38 --- /dev/null +++ b/test/gzip/gzip_compress_size_test.cc @@ -0,0 +1,213 @@ +#include +#include + +#include // std::size_t +#include // std::uint8_t, std::uint32_t +#include // std::istream +#include // std::istreambuf_iterator +#include // std::mt19937, std::uniform_int_distribution +#include // std::istringstream +#include // std::string +#include // std::vector + +namespace { + +auto compress(const std::string &input, const int level) + -> std::vector { + const auto output{sourcemeta::core::gzip( + reinterpret_cast(input.data()), input.size(), + level)}; + return {output.cbegin(), output.cend()}; +} + +auto decompress_one_shot(const std::vector &input, + const std::size_t output_hint = 0, + const std::size_t maximum_size = 268435456) + -> std::string { + return sourcemeta::core::gunzip(input.data(), input.size(), output_hint, + maximum_size); +} + +auto decompress_stream(const std::vector &input) -> std::string { + std::istringstream stream{std::string{input.cbegin(), input.cend()}}; + sourcemeta::core::GZIPStreamBuffer buffer{stream}; + std::istream decompressed{&buffer}; + std::string result; + result.assign(std::istreambuf_iterator(decompressed), + std::istreambuf_iterator()); + return result; +} + +} // namespace + +// Both decompression mechanisms must produce the same output for every input +#define EXPECT_GZIP_DECOMPRESS(input, expected) \ + EXPECT_EQ(decompress_one_shot(input), expected); \ + EXPECT_EQ(decompress_stream(input), expected) + +TEST(compress_repeated_pangram) { + std::string input; + for (std::size_t index = 0; index < 1000; ++index) { + input += "The quick brown fox jumps over the lazy dog. "; + } + + const auto compressed{compress(input, 1)}; + EXPECT_LT(compressed.size(), input.size()); + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_higher_level_is_not_larger) { + std::string input; + for (std::size_t index = 0; index < 1000; ++index) { + input += "The quick brown fox jumps over the lazy dog. "; + } + + EXPECT_LE(compress(input, 12).size(), compress(input, 1).size()); +} + +TEST(compress_highly_compressible_zeros) { + const std::string input(65536, '\0'); + const auto compressed{compress(input, 1)}; + EXPECT_LT(compressed.size(), input.size() / 10); + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_highly_compressible_repeated_byte) { + const std::string input(65536, static_cast(0xff)); + const auto compressed{compress(input, 1)}; + EXPECT_LT(compressed.size(), input.size() / 10); + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_incompressible_input_at_level_0_barely_expands) { + // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) + std::mt19937 generator{3}; + std::uniform_int_distribution distribution{0, 255}; + std::string input; + for (std::size_t index = 0; index < 1048576; ++index) { + input.push_back(static_cast(distribution(generator))); + } + + const auto compressed{compress(input, 0)}; + EXPECT_LE(compressed.size(), input.size() + input.size() / 100); + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_incompressible_input_at_level_1_barely_expands) { + // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) + std::mt19937 generator{3}; + std::uniform_int_distribution distribution{0, 255}; + std::string input; + for (std::size_t index = 0; index < 1048576; ++index) { + input.push_back(static_cast(distribution(generator))); + } + + const auto compressed{compress(input, 1)}; + EXPECT_LE(compressed.size(), input.size() + input.size() / 100); + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_incompressible_input_at_level_6_barely_expands) { + // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) + std::mt19937 generator{3}; + std::uniform_int_distribution distribution{0, 255}; + std::string input; + for (std::size_t index = 0; index < 1048576; ++index) { + input.push_back(static_cast(distribution(generator))); + } + + const auto compressed{compress(input, 6)}; + EXPECT_LE(compressed.size(), input.size() + input.size() / 100); + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_incompressible_input_at_level_12_barely_expands) { + // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) + std::mt19937 generator{3}; + std::uniform_int_distribution distribution{0, 255}; + std::string input; + for (std::size_t index = 0; index < 1048576; ++index) { + input.push_back(static_cast(distribution(generator))); + } + + const auto compressed{compress(input, 12)}; + EXPECT_LE(compressed.size(), input.size() + input.size() / 100); + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_eight_megabytes_of_zeros_at_level_1) { + const std::string input(8388608, '\0'); + const auto compressed{compress(input, 1)}; + EXPECT_LT(compressed.size(), input.size() / 100); + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_four_megabytes_of_zeros_at_level_12) { + const std::string input(4194304, '\0'); + const auto compressed{compress(input, 12)}; + EXPECT_LT(compressed.size(), input.size() / 100); + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_alternating_bytes) { + std::string input; + for (std::size_t index = 0; index < 524288; ++index) { + input += "\x01\xfe"; + } + + const auto compressed{compress(input, 6)}; + EXPECT_LT(compressed.size(), input.size() / 100); + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_random_block_repeated_at_maximum_match_length_period) { + // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) + std::mt19937 generator{6}; + std::uniform_int_distribution distribution{0, 255}; + std::string block; + for (std::size_t index = 0; index < 258; ++index) { + block.push_back(static_cast(distribution(generator))); + } + + std::string input; + for (std::size_t index = 0; index < 1000; ++index) { + input += block; + } + + const auto compressed{compress(input, 6)}; + EXPECT_LT(compressed.size(), input.size() / 10); + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_random_block_repeated_beyond_maximum_match_length_period) { + // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) + std::mt19937 generator{6}; + std::uniform_int_distribution distribution{0, 255}; + std::string block; + for (std::size_t index = 0; index < 259; ++index) { + block.push_back(static_cast(distribution(generator))); + } + + std::string input; + for (std::size_t index = 0; index < 1000; ++index) { + input += block; + } + + const auto compressed{compress(input, 6)}; + EXPECT_LT(compressed.size(), input.size() / 10); + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_sixteen_megabytes) { + std::string input; + for (std::uint32_t index = 0; index < 4194304; ++index) { + input.push_back(static_cast(index & 0xff)); + input.push_back(static_cast((index >> 8) & 0xff)); + input.push_back(static_cast((index >> 16) & 0xff)); + input.push_back(static_cast((index >> 24) & 0xff)); + } + + const auto compressed{compress(input, 1)}; + EXPECT_LT(compressed.size(), input.size()); + EXPECT_GZIP_DECOMPRESS(compressed, input); +} diff --git a/test/gzip/gzip_compress_test.cc b/test/gzip/gzip_compress_test.cc new file mode 100644 index 0000000000..57d64de6bc --- /dev/null +++ b/test/gzip/gzip_compress_test.cc @@ -0,0 +1,499 @@ +#include +#include + +#include // std::size_t +#include // std::uint8_t, std::uint32_t +#include // std::istream +#include // std::istreambuf_iterator +#include // std::mt19937, std::uniform_int_distribution +#include // std::istringstream +#include // std::string +#include // std::move +#include // std::vector + +namespace { + +auto compress(const std::string &input, const int level) + -> std::vector { + const auto output{sourcemeta::core::gzip( + reinterpret_cast(input.data()), input.size(), + level)}; + return {output.cbegin(), output.cend()}; +} + +auto decompress_one_shot(const std::vector &input, + const std::size_t output_hint = 0, + const std::size_t maximum_size = 268435456) + -> std::string { + return sourcemeta::core::gunzip(input.data(), input.size(), output_hint, + maximum_size); +} + +auto decompress_stream(const std::vector &input) -> std::string { + std::istringstream stream{std::string{input.cbegin(), input.cend()}}; + sourcemeta::core::GZIPStreamBuffer buffer{stream}; + std::istream decompressed{&buffer}; + std::string result; + result.assign(std::istreambuf_iterator(decompressed), + std::istreambuf_iterator()); + return result; +} + +} // namespace + +// Both decompression mechanisms must produce the same output for every input +#define EXPECT_GZIP_DECOMPRESS(input, expected) \ + EXPECT_EQ(decompress_one_shot(input), expected); \ + EXPECT_EQ(decompress_stream(input), expected) + +TEST(compress_empty_input) { + const std::string input; + const auto compressed{compress(input, 1)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_hello_world) { + const std::string input{"hello world"}; + const auto compressed{compress(input, 1)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_every_byte_value) { + std::string input; + for (std::size_t index = 0; index < 256; ++index) { + input.push_back(static_cast(index)); + } + + const auto compressed{compress(input, 1)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_level_0_round_trips) { + std::string input; + for (std::size_t index = 0; index < 2000; ++index) { + input += "The quick brown fox jumps over the lazy dog. "; + } + + const auto compressed{compress(input, 0)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_level_1_round_trips) { + std::string input; + for (std::size_t index = 0; index < 2000; ++index) { + input += "The quick brown fox jumps over the lazy dog. "; + } + + const auto compressed{compress(input, 1)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_level_2_round_trips) { + std::string input; + for (std::size_t index = 0; index < 2000; ++index) { + input += "The quick brown fox jumps over the lazy dog. "; + } + + const auto compressed{compress(input, 2)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_level_3_round_trips) { + std::string input; + for (std::size_t index = 0; index < 2000; ++index) { + input += "The quick brown fox jumps over the lazy dog. "; + } + + const auto compressed{compress(input, 3)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_level_4_round_trips) { + std::string input; + for (std::size_t index = 0; index < 2000; ++index) { + input += "The quick brown fox jumps over the lazy dog. "; + } + + const auto compressed{compress(input, 4)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_level_5_round_trips) { + std::string input; + for (std::size_t index = 0; index < 2000; ++index) { + input += "The quick brown fox jumps over the lazy dog. "; + } + + const auto compressed{compress(input, 5)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_level_6_round_trips) { + std::string input; + for (std::size_t index = 0; index < 2000; ++index) { + input += "The quick brown fox jumps over the lazy dog. "; + } + + const auto compressed{compress(input, 6)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_level_7_round_trips) { + std::string input; + for (std::size_t index = 0; index < 2000; ++index) { + input += "The quick brown fox jumps over the lazy dog. "; + } + + const auto compressed{compress(input, 7)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_level_8_round_trips) { + std::string input; + for (std::size_t index = 0; index < 2000; ++index) { + input += "The quick brown fox jumps over the lazy dog. "; + } + + const auto compressed{compress(input, 8)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_level_9_round_trips) { + std::string input; + for (std::size_t index = 0; index < 2000; ++index) { + input += "The quick brown fox jumps over the lazy dog. "; + } + + const auto compressed{compress(input, 9)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_level_10_round_trips) { + std::string input; + for (std::size_t index = 0; index < 2000; ++index) { + input += "The quick brown fox jumps over the lazy dog. "; + } + + const auto compressed{compress(input, 10)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_level_11_round_trips) { + std::string input; + for (std::size_t index = 0; index < 2000; ++index) { + input += "The quick brown fox jumps over the lazy dog. "; + } + + const auto compressed{compress(input, 11)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_level_12_round_trips) { + std::string input; + for (std::size_t index = 0; index < 2000; ++index) { + input += "The quick brown fox jumps over the lazy dog. "; + } + + const auto compressed{compress(input, 12)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_single_byte) { + const std::string input{"X"}; + const auto compressed{compress(input, 1)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_two_bytes) { + const std::string input{"AB"}; + const auto compressed{compress(input, 1)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_single_zero_byte) { + const std::string input(1, '\0'); + const auto compressed{compress(input, 1)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_minimum_match_length_input) { + const std::string input{"abcabc"}; + const auto compressed{compress(input, 12)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_run_of_maximum_match_length) { + const std::string input(259, 'q'); + const auto compressed{compress(input, 6)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_run_one_past_maximum_match_length) { + const std::string input(260, 'q'); + const auto compressed{compress(input, 6)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_counter_bytes_one_below_stream_buffer_size) { + std::string input; + for (std::size_t index = 0; index < 16383; ++index) { + input.push_back(static_cast(index & 0xff)); + } + + const auto compressed{compress(input, 1)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_counter_bytes_of_stream_buffer_size) { + std::string input; + for (std::size_t index = 0; index < 16384; ++index) { + input.push_back(static_cast(index & 0xff)); + } + + const auto compressed{compress(input, 1)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_counter_bytes_one_above_stream_buffer_size) { + std::string input; + for (std::size_t index = 0; index < 16385; ++index) { + input.push_back(static_cast(index & 0xff)); + } + + const auto compressed{compress(input, 1)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_counter_bytes_of_window_size) { + std::string input; + for (std::size_t index = 0; index < 32768; ++index) { + input.push_back(static_cast(index & 0xff)); + } + + const auto compressed{compress(input, 1)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_counter_bytes_of_one_megabyte) { + std::string input; + for (std::size_t index = 0; index < 1048576; ++index) { + input.push_back(static_cast(index & 0xff)); + } + + const auto compressed{compress(input, 1)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_random_input_one_below_window_size) { + // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) + std::mt19937 generator{1}; + std::uniform_int_distribution distribution{0, 255}; + std::string input; + for (std::size_t index = 0; index < 32767; ++index) { + input.push_back(static_cast(distribution(generator))); + } + + const auto compressed{compress(input, 1)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_random_input_of_window_size) { + // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) + std::mt19937 generator{1}; + std::uniform_int_distribution distribution{0, 255}; + std::string input; + for (std::size_t index = 0; index < 32768; ++index) { + input.push_back(static_cast(distribution(generator))); + } + + const auto compressed{compress(input, 1)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_random_input_one_above_window_size) { + // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) + std::mt19937 generator{1}; + std::uniform_int_distribution distribution{0, 255}; + std::string input; + for (std::size_t index = 0; index < 32769; ++index) { + input.push_back(static_cast(distribution(generator))); + } + + const auto compressed{compress(input, 1)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_level_0_random_input_of_maximum_stored_block_length) { + // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) + std::mt19937 generator{2}; + std::uniform_int_distribution distribution{0, 255}; + std::string input; + for (std::size_t index = 0; index < 65535; ++index) { + input.push_back(static_cast(distribution(generator))); + } + + const auto compressed{compress(input, 0)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_level_0_random_input_one_above_maximum_stored_block_length) { + // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) + std::mt19937 generator{2}; + std::uniform_int_distribution distribution{0, 255}; + std::string input; + for (std::size_t index = 0; index < 65536; ++index) { + input.push_back(static_cast(distribution(generator))); + } + + const auto compressed{compress(input, 0)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_level_0_random_input_of_two_maximum_stored_blocks) { + // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) + std::mt19937 generator{2}; + std::uniform_int_distribution distribution{0, 255}; + std::string input; + for (std::size_t index = 0; index < 131070; ++index) { + input.push_back(static_cast(distribution(generator))); + } + + const auto compressed{compress(input, 0)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_incompressible_random_input) { + // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) + std::mt19937 generator{42}; + std::uniform_int_distribution distribution{0, 255}; + std::string input; + for (std::size_t index = 0; index < 65536; ++index) { + input.push_back(static_cast(distribution(generator))); + } + + const auto compressed{compress(input, 1)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_run_wrapping_the_window_many_times) { + const std::string input(200000, 'a'); + const auto compressed{compress(input, 1)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_random_block_repeated_at_window_size_distance_at_level_6) { + // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) + std::mt19937 generator{4}; + std::uniform_int_distribution distribution{0, 255}; + std::string block; + for (std::size_t index = 0; index < 32768; ++index) { + block.push_back(static_cast(distribution(generator))); + } + + const auto input{block + block + block + block}; + const auto compressed{compress(input, 6)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_random_block_repeated_at_window_size_distance_at_level_12) { + // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) + std::mt19937 generator{4}; + std::uniform_int_distribution distribution{0, 255}; + std::string block; + for (std::size_t index = 0; index < 32768; ++index) { + block.push_back(static_cast(distribution(generator))); + } + + const auto input{block + block + block + block}; + const auto compressed{compress(input, 12)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_random_block_repeated_beyond_window_size_distance) { + // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) + std::mt19937 generator{5}; + std::uniform_int_distribution distribution{0, 255}; + std::string block; + for (std::size_t index = 0; index < 32769; ++index) { + block.push_back(static_cast(distribution(generator))); + } + + const auto input{block + block + block + block}; + const auto compressed{compress(input, 12)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_runs_of_every_match_length) { + std::string input; + for (std::size_t length = 1; length <= 300; ++length) { + input.append(length, static_cast(length & 0xff)); + input.append(length, static_cast(~length & 0xff)); + } + + const auto compressed{compress(input, 9)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_fibonacci_word_at_level_1) { + std::string previous{"b"}; + std::string input{"a"}; + while (input.size() < 1048576) { + auto next{input + previous}; + previous = std::move(input); + input = std::move(next); + } + + const auto compressed{compress(input, 1)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_fibonacci_word_at_level_12) { + std::string previous{"b"}; + std::string input{"a"}; + while (input.size() < 1048576) { + auto next{input + previous}; + previous = std::move(input); + input = std::move(next); + } + + const auto compressed{compress(input, 12)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_counter_sequence) { + std::string input; + for (std::uint32_t index = 0; index < 262144; ++index) { + input.push_back(static_cast(index & 0xff)); + input.push_back(static_cast((index >> 8) & 0xff)); + input.push_back(static_cast((index >> 16) & 0xff)); + input.push_back(static_cast((index >> 24) & 0xff)); + } + + const auto compressed{compress(input, 6)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_already_compressed_input) { + std::string text; + for (std::size_t index = 0; index < 2000; ++index) { + text += "The quick brown fox jumps over the lazy dog. "; + } + + const auto inner{compress(text, 12)}; + const std::string input{inner.cbegin(), inner.cend()}; + const auto compressed{compress(input, 12)}; + EXPECT_GZIP_DECOMPRESS(compressed, input); +} + +TEST(compress_members_of_different_levels_concatenate) { + const std::string first(70000, 'f'); + const std::string second{"second"}; + auto compressed{compress(first, 0)}; + const auto second_compressed{compress(second, 12)}; + const auto empty_compressed{compress("", 6)}; + compressed.insert(compressed.end(), second_compressed.cbegin(), + second_compressed.cend()); + compressed.insert(compressed.end(), empty_compressed.cbegin(), + empty_compressed.cend()); + EXPECT_GZIP_DECOMPRESS(compressed, first + second); +} diff --git a/test/gzip/gzip_error_test.cc b/test/gzip/gzip_decompress_error_test.cc similarity index 61% rename from test/gzip/gzip_error_test.cc rename to test/gzip/gzip_decompress_error_test.cc index 38ed06ea70..faa9047106 100644 --- a/test/gzip/gzip_error_test.cc +++ b/test/gzip/gzip_decompress_error_test.cc @@ -5,18 +5,12 @@ #include // std::uint8_t #include // std::istream #include // std::istreambuf_iterator -#include // std::numeric_limits #include // std::istringstream #include // std::string #include // std::vector namespace { -auto decompress_one_shot(const std::vector &input) - -> std::string { - return sourcemeta::core::gunzip(input.data(), input.size()); -} - auto decompress_stream(const std::vector &input) -> std::string { std::istringstream stream{std::string{input.cbegin(), input.cend()}}; sourcemeta::core::GZIPStreamBuffer buffer{stream}; @@ -52,66 +46,33 @@ auto decompress_stream_error(const std::vector &input) FAIL(); } -auto compress_error(const int level) -> std::string { - const std::string input{"hello world"}; - try { - sourcemeta::core::gzip(reinterpret_cast(input.data()), - input.size(), level); - } catch (const sourcemeta::core::GZIPError &error) { - return error.what(); - } - - FAIL(); -} - } // namespace -TEST(compress_level_above_maximum) { - EXPECT_EQ(compress_error(13), "Could not allocate compressor"); -} - -TEST(compress_negative_level) { - EXPECT_EQ(compress_error(-2), "Could not allocate compressor"); -} - -TEST(compress_largest_integer_level) { - EXPECT_EQ(compress_error(std::numeric_limits::max()), - "Could not allocate compressor"); -} - -TEST(compress_smallest_integer_level) { - EXPECT_EQ(compress_error(std::numeric_limits::min()), - "Could not allocate compressor"); -} +// Both decompression mechanisms must fail with the same message for every input +#define EXPECT_GZIP_DECOMPRESS_ERROR(input, expected_message) \ + EXPECT_EQ(decompress_one_shot_error(input), expected_message); \ + EXPECT_EQ(decompress_stream_error(input), expected_message) TEST(invalid_input) { const std::vector input{ 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x67, 0x7a, 0x69, 0x70, 0x20, 0x64, 0x61, 0x74, 0x61}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Invalid gzip magic bytes"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Invalid gzip magic bytes"); } TEST(empty_input) { const std::vector input; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Empty source stream"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Empty source stream"); } TEST(single_identification_byte) { const std::vector input{0x1f}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Unexpected end of source stream"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Unexpected end of source stream"); } TEST(identification_bytes_only) { const std::vector input{0x1f, 0x8b}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Unexpected end of source stream"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Unexpected end of source stream"); } TEST(wrong_first_identification_byte) { @@ -119,9 +80,7 @@ TEST(wrong_first_identification_byte) { 0x1e, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Invalid gzip magic bytes"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Invalid gzip magic bytes"); } TEST(wrong_second_identification_byte) { @@ -129,9 +88,7 @@ TEST(wrong_second_identification_byte) { 0x1f, 0x8c, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Invalid gzip magic bytes"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Invalid gzip magic bytes"); } TEST(swapped_identification_bytes) { @@ -139,9 +96,7 @@ TEST(swapped_identification_bytes) { 0x8b, 0x1f, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Invalid gzip magic bytes"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Invalid gzip magic bytes"); } TEST(reserved_compression_method_0) { @@ -149,10 +104,7 @@ TEST(reserved_compression_method_0) { 0x1f, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), - "Unsupported gzip compression method"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Unsupported gzip compression method"); } TEST(reserved_compression_method_7) { @@ -160,10 +112,7 @@ TEST(reserved_compression_method_7) { 0x1f, 0x8b, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), - "Unsupported gzip compression method"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Unsupported gzip compression method"); } TEST(unknown_compression_method_9) { @@ -171,10 +120,7 @@ TEST(unknown_compression_method_9) { 0x1f, 0x8b, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), - "Unsupported gzip compression method"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Unsupported gzip compression method"); } TEST(reserved_flag_bit_5) { @@ -182,10 +128,7 @@ TEST(reserved_flag_bit_5) { 0x1f, 0x8b, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), - "Reserved gzip FLG bits must be zero"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Reserved gzip FLG bits must be zero"); } TEST(reserved_flag_bit_6) { @@ -193,10 +136,7 @@ TEST(reserved_flag_bit_6) { 0x1f, 0x8b, 0x08, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), - "Reserved gzip FLG bits must be zero"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Reserved gzip FLG bits must be zero"); } TEST(reserved_flag_bit_7) { @@ -204,10 +144,7 @@ TEST(reserved_flag_bit_7) { 0x1f, 0x8b, 0x08, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), - "Reserved gzip FLG bits must be zero"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Reserved gzip FLG bits must be zero"); } TEST(reserved_flag_bit_alongside_ftext) { @@ -215,27 +152,20 @@ TEST(reserved_flag_bit_alongside_ftext) { 0x1f, 0x8b, 0x08, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), - "Reserved gzip FLG bits must be zero"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Reserved gzip FLG bits must be zero"); } TEST(header_only) { const std::vector input{0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Unexpected end of source stream"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Unexpected end of source stream"); } TEST(header_and_trailer_without_blocks) { const std::vector input{0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Stored block LEN/NLEN mismatch"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Stored block LEN/NLEN mismatch"); } TEST(truncated_fextra) { @@ -243,9 +173,7 @@ TEST(truncated_fextra) { 0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x64, 0x00, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Unexpected end of source stream"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Unexpected end of source stream"); } TEST(fextra_swallowing_rest_of_member) { @@ -253,9 +181,7 @@ TEST(fextra_swallowing_rest_of_member) { 0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x00, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Unexpected end of source stream"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Unexpected end of source stream"); } TEST(unterminated_fname) { @@ -264,9 +190,7 @@ TEST(unterminated_fname) { 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Unexpected end of source stream"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Unexpected end of source stream"); } TEST(unterminated_fcomment) { @@ -275,17 +199,13 @@ TEST(unterminated_fcomment) { 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Unexpected end of source stream"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Unexpected end of source stream"); } TEST(truncated_fhcrc) { const std::vector input{0x1f, 0x8b, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x90}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Unexpected end of source stream"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Unexpected end of source stream"); } TEST(fhcrc_mismatch) { @@ -293,10 +213,7 @@ TEST(fhcrc_mismatch) { 0x1f, 0x8b, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x91, 0xc9, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - // TODO: The one-shot mechanism skips the header checksum while the stream - // mechanism verifies it, and RFC 1952 section 2.3.1.2 permits either - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream_error(input), "FHCRC mismatch"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "FHCRC mismatch"); } TEST(missing_trailer) { @@ -304,9 +221,7 @@ TEST(missing_trailer) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Unexpected end of source stream"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Unexpected end of source stream"); } TEST(trailer_missing_last_byte) { @@ -314,9 +229,7 @@ TEST(trailer_missing_last_byte) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Unexpected end of source stream"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Unexpected end of source stream"); } TEST(crc32_lowest_bit_mismatch) { @@ -324,9 +237,7 @@ TEST(crc32_lowest_bit_mismatch) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x84, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Gzip member CRC32 mismatch"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Gzip member CRC32 mismatch"); } TEST(crc32_highest_bit_mismatch) { @@ -334,9 +245,7 @@ TEST(crc32_highest_bit_mismatch) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x8d, 0x0b, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Gzip member CRC32 mismatch"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Gzip member CRC32 mismatch"); } TEST(isize_off_by_one) { @@ -344,9 +253,7 @@ TEST(isize_off_by_one) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0c, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Gzip member ISIZE mismatch"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Gzip member ISIZE mismatch"); } TEST(isize_highest_byte_mismatch) { @@ -354,18 +261,14 @@ TEST(isize_highest_byte_mismatch) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x01}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Gzip member ISIZE mismatch"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Gzip member ISIZE mismatch"); } TEST(empty_payload_with_nonzero_crc32) { const std::vector input{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Gzip member CRC32 mismatch"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Gzip member CRC32 mismatch"); } TEST(swapped_trailer_fields) { @@ -373,9 +276,7 @@ TEST(swapped_trailer_fields) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x0b, 0x00, 0x00, 0x00, 0x85, 0x11, 0x4a, 0x0d}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Gzip member CRC32 mismatch"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Gzip member CRC32 mismatch"); } TEST(truncated_compressed_blocks) { @@ -383,9 +284,17 @@ TEST(truncated_compressed_blocks) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xff, 0x01, 0x29, 0x00, 0xd6, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2c, 0x20, 0x74, 0x68, 0x69, 0x73}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Unexpected end of source stream"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Unexpected end of source stream"); +} + +TEST(truncated_fixed_block_at_stream_source_buffer_boundary) { + std::vector input{0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x63}; + for (std::size_t index = 0; index < 65525; ++index) { + input.push_back(0x60); + } + + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Unexpected end of source stream"); } TEST(truncated_stored_block_payload) { @@ -396,9 +305,7 @@ TEST(truncated_stored_block_payload) { input.push_back(0x61); } - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Unexpected end of source stream"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Unexpected end of source stream"); } TEST(stored_block_nlen_mismatch) { @@ -406,9 +313,7 @@ TEST(stored_block_nlen_mismatch) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Stored block LEN/NLEN mismatch"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Stored block LEN/NLEN mismatch"); } TEST(stored_block_nlen_equal_to_len) { @@ -437,9 +342,7 @@ TEST(stored_block_nlen_equal_to_len) { 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x3d, 0x0b, 0xc4, 0xa2, 0xff, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Stored block LEN/NLEN mismatch"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Stored block LEN/NLEN mismatch"); } TEST(stored_block_len_beyond_input) { @@ -447,9 +350,7 @@ TEST(stored_block_len_beyond_input) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x64, 0x00, 0x9b, 0xff, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0xc6, 0xc7, 0x84, 0xa6, 0x0a, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Unexpected end of source stream"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Unexpected end of source stream"); } TEST(reserved_block_type) { @@ -457,9 +358,7 @@ TEST(reserved_block_type) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x07, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Reserved deflate block type"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Reserved deflate block type"); } TEST(reserved_block_type_after_valid_block) { @@ -467,9 +366,7 @@ TEST(reserved_block_type_after_valid_block) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x05, 0x00, 0xfa, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x07, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Reserved deflate block type"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Reserved deflate block type"); } TEST(missing_final_block) { @@ -477,29 +374,23 @@ TEST(missing_final_block) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x05, 0x00, 0xfa, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x86, 0xa6, 0x10, 0x36, 0x05, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Reserved deflate block type"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Reserved deflate block type"); } TEST(fixed_block_match_before_any_output) { const std::vector input{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), - "Backref distance exceeds bytes available"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, + "Backref distance exceeds bytes available"); } TEST(fixed_block_distance_beyond_output) { const std::vector input{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x4b, 0x04, 0x42, 0x00, 0x45, 0xe5, 0x98, 0xad, 0x04, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), - "Backref distance exceeds bytes available"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, + "Backref distance exceeds bytes available"); } TEST(fixed_block_maximum_distance_one_beyond_output) { @@ -512,10 +403,8 @@ TEST(fixed_block_maximum_distance_one_beyond_output) { input.insert(input.end(), {0x03, 0xde, 0xff, 0x0f, 0x00, 0x12, 0x5c, 0x3a, 0x51, 0xff, 0x7f, 0x00, 0x00}); - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), - "Backref distance exceeds bytes available"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, + "Backref distance exceeds bytes available"); } TEST(match_into_previous_member) { @@ -525,103 +414,73 @@ TEST(match_into_previous_member) { 0x86, 0xa6, 0x10, 0x36, 0x05, 0x00, 0x00, 0x00, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0x13, 0x00, 0x86, 0xa6, 0x10, 0x36, 0x05, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), - "Backref distance exceeds bytes available"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, + "Backref distance exceeds bytes available"); } TEST(fixed_block_unassigned_distance_code) { const std::vector input{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x4b, 0x04, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Invalid Huffman code"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Invalid distance code"); } TEST(fixed_block_invalid_literal_length_symbol) { const std::vector input{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x1b, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Invalid literal/length code"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Invalid literal/length code"); } TEST(fixed_block_literal_length_symbol_286) { const std::vector input{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x4b, 0x1c, 0x03, 0x00, 0x56, 0xfa, 0xc2, 0x34, 0x03, 0x01, 0x00, 0x00}; - // TODO: The one-shot mechanism decodes literal/length symbols 286 and 287 - // as a match of length 258, but RFC 1951 section 3.2.6 states that they - // never occur in compressed data - EXPECT_EQ(decompress_one_shot(input), std::string(259, 'a')); - EXPECT_EQ(decompress_stream_error(input), "Invalid literal/length code"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Invalid literal/length code"); } TEST(fixed_block_literal_length_symbol_287) { const std::vector input{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x4b, 0x1c, 0x07, 0x00, 0x56, 0xfa, 0xc2, 0x34, 0x03, 0x01, 0x00, 0x00}; - // TODO: The one-shot mechanism decodes literal/length symbols 286 and 287 - // as a match of length 258, but RFC 1951 section 3.2.6 states that they - // never occur in compressed data - EXPECT_EQ(decompress_one_shot(input), std::string(259, 'a')); - EXPECT_EQ(decompress_stream_error(input), "Invalid literal/length code"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Invalid literal/length code"); } TEST(fixed_block_distance_code_30) { std::vector input{0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x80, 0xff, 0x7f}; - std::string expected; for (std::size_t index = 0; index < 32768; ++index) { const auto byte{static_cast((index ^ (index >> 8)) & 0xff)}; input.push_back(byte); - expected.push_back(static_cast(byte)); } input.insert(input.end(), {0x03, 0x3e, 0x00, 0x00, 0x00, 0x65, 0xd8, 0xb7, 0x34, 0x03, 0x80, 0x00, 0x00}); - expected += "\xe0\x20\x21"; - // TODO: The one-shot mechanism decodes distance codes 30 and 31 as - // distances beyond 24576, but RFC 1951 section 3.2.6 states that they never - // occur in compressed data - EXPECT_EQ(decompress_one_shot(input), expected); - EXPECT_EQ(decompress_stream_error(input), "Invalid Huffman code"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Invalid distance code"); } TEST(fixed_block_distance_code_31) { std::vector input{0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x80, 0xff, 0x7f}; - std::string expected; for (std::size_t index = 0; index < 32768; ++index) { const auto byte{static_cast((index ^ (index >> 8)) & 0xff)}; input.push_back(byte); - expected.push_back(static_cast(byte)); } input.insert(input.end(), {0x03, 0x7e, 0x00, 0x00, 0x00, 0x65, 0xd8, 0xb7, 0x34, 0x03, 0x80, 0x00, 0x00}); - expected += "\xe0\x20\x21"; - // TODO: The one-shot mechanism decodes distance codes 30 and 31 as - // distances beyond 24576, but RFC 1951 section 3.2.6 states that they never - // occur in compressed data - EXPECT_EQ(decompress_one_shot(input), expected); - EXPECT_EQ(decompress_stream_error(input), "Invalid Huffman code"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Invalid distance code"); } TEST(dynamic_block_distance_code_30) { std::vector input{0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x80, 0xff, 0x7f}; - std::string expected; for (std::size_t index = 0; index < 32768; ++index) { const auto byte{static_cast((index ^ (index >> 8)) & 0xff)}; input.push_back(byte); - expected.push_back(static_cast(byte)); } input.insert( @@ -640,12 +499,7 @@ TEST(dynamic_block_distance_code_30) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x65, 0xd8, 0xb7, 0x34, 0x03, 0x80, 0x00, 0x00}); - expected += "\xe0\x20\x21"; - // TODO: The one-shot mechanism decodes distance codes 30 and 31 as - // distances beyond 24576, but RFC 1951 section 3.2.5 only defines distance - // codes up to 29 - EXPECT_EQ(decompress_one_shot(input), expected); - EXPECT_EQ(decompress_stream_error(input), "Invalid distance code"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Invalid distance code"); } TEST(dynamic_block_287_literal_length_codes) { @@ -665,10 +519,7 @@ TEST(dynamic_block_287_literal_length_codes) { 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x43, 0xbe, 0xb7, 0xe8, 0x01, 0x00, 0x00, 0x00}; - // TODO: The one-shot mechanism accepts more literal/length codes than the - // maximum of 286 in RFC 1951 section 3.2.7 - EXPECT_EQ(decompress_one_shot(input), "a"); - EXPECT_EQ(decompress_stream_error(input), "Too many literal/length codes"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Too many literal/length codes"); } TEST(dynamic_block_288_literal_length_codes) { @@ -688,10 +539,7 @@ TEST(dynamic_block_288_literal_length_codes) { 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x43, 0xbe, 0xb7, 0xe8, 0x01, 0x00, 0x00, 0x00}; - // TODO: The one-shot mechanism accepts more literal/length codes than the - // maximum of 286 in RFC 1951 section 3.2.7 - EXPECT_EQ(decompress_one_shot(input), "a"); - EXPECT_EQ(decompress_stream_error(input), "Too many literal/length codes"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Too many literal/length codes"); } TEST(dynamic_block_unused_code_of_single_distance_code) { @@ -710,10 +558,7 @@ TEST(dynamic_block_unused_code_of_single_distance_code) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21, 0x3e, 0x77, 0x80, 0x7b, 0x4c, 0x05, 0x00, 0x00, 0x00}; - // TODO: The one-shot mechanism decodes the unused code of a single code - // alphabet from RFC 1951 section 3.2.7 as the used code - EXPECT_EQ(decompress_one_shot(input), "abbbb"); - EXPECT_EQ(decompress_stream_error(input), "Invalid Huffman code"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Invalid Huffman code"); } TEST(dynamic_block_unused_code_of_single_literal_length_code) { @@ -732,10 +577,7 @@ TEST(dynamic_block_unused_code_of_single_literal_length_code) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - // TODO: The one-shot mechanism decodes the unused code of a single code - // alphabet from RFC 1951 section 3.2.7 as the used code - EXPECT_EQ(decompress_one_shot(input), ""); - EXPECT_EQ(decompress_stream_error(input), "Invalid Huffman code"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Invalid Huffman code"); } TEST(dynamic_block_incomplete_literal_length_code) { @@ -754,9 +596,7 @@ TEST(dynamic_block_incomplete_literal_length_code) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x20, 0x43, 0xbe, 0xb7, 0xe8, 0x01, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Incomplete Huffman code"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Incomplete Huffman code"); } TEST(dynamic_block_incomplete_distance_code) { @@ -775,9 +615,7 @@ TEST(dynamic_block_incomplete_distance_code) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x31, 0x01, 0x45, 0xe5, 0x98, 0xad, 0x04, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Incomplete Huffman code"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Incomplete Huffman code"); } TEST(dynamic_block_incomplete_code_length_code) { @@ -790,9 +628,7 @@ TEST(dynamic_block_incomplete_code_length_code) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x43, 0xbe, 0xb7, 0xe8, 0x01, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Incomplete Huffman code"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Incomplete Huffman code"); } TEST(dynamic_block_over_subscribed_literal_length_code) { @@ -811,9 +647,7 @@ TEST(dynamic_block_over_subscribed_literal_length_code) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x43, 0xbe, 0xb7, 0xe8, 0x01, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Over-subscribed Huffman code"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Over-subscribed Huffman code"); } TEST(dynamic_block_over_subscribed_distance_code) { @@ -832,9 +666,7 @@ TEST(dynamic_block_over_subscribed_distance_code) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21, 0x22, 0x00, 0x00, 0x43, 0xbe, 0xb7, 0xe8, 0x01, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Over-subscribed Huffman code"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Over-subscribed Huffman code"); } TEST(dynamic_block_over_subscribed_code_length_code) { @@ -842,9 +674,7 @@ TEST(dynamic_block_over_subscribed_code_length_code) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x05, 0x00, 0x92, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Over-subscribed Huffman code"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Over-subscribed Huffman code"); } TEST(dynamic_block_repeat_previous_without_previous) { @@ -852,10 +682,8 @@ TEST(dynamic_block_repeat_previous_without_previous) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x05, 0x00, 0x02, 0xe4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), - "Repeat-previous code length with no previous"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, + "Repeat-previous code length with no previous"); } TEST(dynamic_block_repeat_zero_beyond_code_length_count) { @@ -863,9 +691,7 @@ TEST(dynamic_block_repeat_zero_beyond_code_length_count) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x05, 0x00, 0x80, 0xe4, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Code length count overflow"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Code length count overflow"); } TEST(dynamic_block_repeat_previous_beyond_code_length_count) { @@ -873,9 +699,7 @@ TEST(dynamic_block_repeat_previous_beyond_code_length_count) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x05, 0x00, 0x82, 0xe0, 0x3f, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Code length count overflow"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Code length count overflow"); } TEST(dynamic_block_code_length_overshoot) { @@ -883,9 +707,7 @@ TEST(dynamic_block_code_length_overshoot) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x05, 0x00, 0x80, 0xe4, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Code length count overflow"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Code length count overflow"); } TEST(dynamic_block_code_length_cap_overflow) { @@ -893,9 +715,7 @@ TEST(dynamic_block_code_length_cap_overflow) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xed, 0x1f, 0x80, 0xe4, 0xff, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Code length count overflow"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Code length count overflow"); } TEST(dynamic_block_repeat_previous_cap_overflow) { @@ -903,9 +723,7 @@ TEST(dynamic_block_repeat_previous_cap_overflow) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xed, 0x1f, 0x84, 0x28, 0x7f, 0xff, 0xff, 0xff, 0xff, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Code length count overflow"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Code length count overflow"); } TEST(dynamic_block_repeat_zero_cap_overflow) { @@ -913,9 +731,7 @@ TEST(dynamic_block_repeat_zero_cap_overflow) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xed, 0x1f, 0x20, 0xe5, 0xff, 0xff, 0xde, 0x7b, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Code length count overflow"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Code length count overflow"); } TEST(dynamic_block_without_end_of_block_code) { @@ -934,9 +750,7 @@ TEST(dynamic_block_without_end_of_block_code) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x65, 0xb4, 0x89, 0x40, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Unexpected end of source stream"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Unexpected end of source stream"); } TEST(second_member_crc32_mismatch) { @@ -946,9 +760,7 @@ TEST(second_member_crc32_mismatch) { 0x05, 0x00, 0x00, 0x00, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xff, 0x01, 0x05, 0x00, 0xfa, 0xff, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x42, 0x11, 0x77, 0x3a, 0x05, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Gzip member CRC32 mismatch"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Gzip member CRC32 mismatch"); } TEST(second_member_truncated) { @@ -958,9 +770,7 @@ TEST(second_member_truncated) { 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Unexpected end of source stream"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Unexpected end of source stream"); } TEST(second_member_reserved_block_type) { @@ -971,9 +781,7 @@ TEST(second_member_reserved_block_type) { 0x00, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x07, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Reserved deflate block type"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Reserved deflate block type"); } TEST(member_without_trailer_followed_by_member) { @@ -982,9 +790,7 @@ TEST(member_without_trailer_followed_by_member) { 0x00, 0xfa, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xff, 0x01, 0x05, 0x00, 0xfa, 0xff, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x43, 0x11, 0x77, 0x3a, 0x05, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Gzip member CRC32 mismatch"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Gzip member CRC32 mismatch"); } TEST(valid_member_after_corrupt_member) { @@ -994,9 +800,7 @@ TEST(valid_member_after_corrupt_member) { 0x05, 0x00, 0x00, 0x7f, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xff, 0x01, 0x05, 0x00, 0xfa, 0xff, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x43, 0x11, 0x77, 0x3a, 0x05, 0x00, 0x00, 0x00}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Gzip member ISIZE mismatch"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Gzip member ISIZE mismatch"); } TEST(trailing_header_without_blocks) { @@ -1005,9 +809,7 @@ TEST(trailing_header_without_blocks) { 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}; - // TODO: Report the same error message from both mechanisms - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream_error(input), "Unexpected end of source stream"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Unexpected end of source stream"); } TEST(trailing_identification_bytes_only) { @@ -1015,11 +817,7 @@ TEST(trailing_identification_bytes_only) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00, 0x1f, 0x8b}; - // TODO: The one-shot mechanism treats trailing data that starts with the - // gzip identification bytes as a corrupt member, while the stream mechanism - // ignores it as trailing garbage - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Unexpected end of source stream"); } TEST(trailing_identification_bytes_with_invalid_compression_method) { @@ -1028,11 +826,7 @@ TEST(trailing_identification_bytes_with_invalid_compression_method) { 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00, 0x1f, 0x8b, 0x63, 0x6f, 0x72, 0x72, 0x75, 0x70, 0x74}; - // TODO: The one-shot mechanism treats trailing data that starts with the - // gzip identification bytes as a corrupt member, while the stream mechanism - // ignores it as trailing garbage - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Unsupported gzip compression method"); } TEST(trailing_identification_bytes_with_truncated_fname) { @@ -1041,11 +835,7 @@ TEST(trailing_identification_bytes_with_truncated_fname) { 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00, 0x1f, 0x8b, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x61, 0x62, 0x63}; - // TODO: The one-shot mechanism treats trailing data that starts with the - // gzip identification bytes as a corrupt member, while the stream mechanism - // ignores it as trailing garbage - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Unexpected end of source stream"); } TEST(second_member_reserved_flag_bit) { @@ -1056,11 +846,7 @@ TEST(second_member_reserved_flag_bit) { 0x08, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - // TODO: The one-shot mechanism treats trailing data that starts with the - // gzip identification bytes as a corrupt member, while the stream mechanism - // ignores it as trailing garbage - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Reserved gzip FLG bits must be zero"); } TEST(second_member_unsupported_compression_method) { @@ -1071,11 +857,7 @@ TEST(second_member_unsupported_compression_method) { 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - // TODO: The one-shot mechanism treats trailing data that starts with the - // gzip identification bytes as a corrupt member, while the stream mechanism - // ignores it as trailing garbage - EXPECT_EQ(decompress_one_shot_error(input), "Could not decompress input"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "Unsupported gzip compression method"); } TEST(second_member_fhcrc_mismatch) { @@ -1086,78 +868,5 @@ TEST(second_member_fhcrc_mismatch) { 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x91, 0xc9, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - // TODO: The one-shot mechanism skips the header checksum of a later member - // and decodes it, while the stream mechanism ignores that member as - // trailing garbage - EXPECT_EQ(decompress_one_shot(input), "hello worldhello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); -} - -TEST(maximum_size_exceeded_by_one_byte) { - const std::vector input{ - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, - 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, - 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot_error(input, 0, 10), - "Decompressed output exceeds the maximum allowed size"); -} - -TEST(zero_maximum_size_with_non_empty_payload) { - const std::vector input{ - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, - 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, - 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot_error(input, 0, 0), - "Decompressed output exceeds the maximum allowed size"); -} - -TEST(output_hint_above_maximum_size_with_exceeding_output) { - const std::vector input{ - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, - 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, - 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot_error(input, 1048576, 10), - "Decompressed output exceeds the maximum allowed size"); -} - -TEST(growth_beyond_uneven_maximum_size) { - const std::vector input{ - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, - 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, - 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot_error(input, 3, 10), - "Decompressed output exceeds the maximum allowed size"); -} - -TEST(crafted_bomb_beyond_maximum_size) { - const std::vector input{ - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x4b, 0x1c, - 0x05, 0xa3, 0x60, 0x14, 0x8c, 0x82, 0x51, 0x30, 0x0a, 0x46, 0xc1, 0x28, - 0x18, 0x05, 0xa3, 0x60, 0x14, 0x8c, 0x82, 0x51, 0x30, 0x0a, 0x46, 0xc1, - 0x28, 0x18, 0x05, 0x00, 0xa1, 0x87, 0xcc, 0x71, 0x23, 0x11, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot_error(input, 0, 4386), - "Decompressed output exceeds the maximum allowed size"); -} - -TEST(members_totaling_one_byte_over_maximum_size) { - const std::vector input{ - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, - 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, - 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00, 0x1f, 0x8b, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, 0xf4, - 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, - 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot_error(input, 0, 21), - "Decompressed output exceeds the maximum allowed size"); -} - -TEST(second_member_alone_exceeding_maximum_size) { - const std::vector input{ - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x00, - 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, - 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, - 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, - 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot_error(input, 0, 5), - "Decompressed output exceeds the maximum allowed size"); + EXPECT_GZIP_DECOMPRESS_ERROR(input, "FHCRC mismatch"); } diff --git a/test/gzip/gzip_decompress_limits_error_test.cc b/test/gzip/gzip_decompress_limits_error_test.cc new file mode 100644 index 0000000000..4d5380da88 --- /dev/null +++ b/test/gzip/gzip_decompress_limits_error_test.cc @@ -0,0 +1,94 @@ +#include +#include + +#include // std::size_t +#include // std::uint8_t +#include // std::string +#include // std::vector + +namespace { + +auto decompress_one_shot_error(const std::vector &input, + const std::size_t output_hint = 0, + const std::size_t maximum_size = 268435456) + -> std::string { + try { + sourcemeta::core::gunzip(input.data(), input.size(), output_hint, + maximum_size); + } catch (const sourcemeta::core::GZIPError &error) { + return error.what(); + } + + FAIL(); +} + +} // namespace + +TEST(maximum_size_exceeded_by_one_byte) { + const std::vector input{ + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, + 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, + 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; + EXPECT_EQ(decompress_one_shot_error(input, 0, 10), + "Decompressed output exceeds the maximum allowed size"); +} + +TEST(zero_maximum_size_with_non_empty_payload) { + const std::vector input{ + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, + 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, + 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; + EXPECT_EQ(decompress_one_shot_error(input, 0, 0), + "Decompressed output exceeds the maximum allowed size"); +} + +TEST(output_hint_above_maximum_size_with_exceeding_output) { + const std::vector input{ + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, + 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, + 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; + EXPECT_EQ(decompress_one_shot_error(input, 1048576, 10), + "Decompressed output exceeds the maximum allowed size"); +} + +TEST(growth_beyond_uneven_maximum_size) { + const std::vector input{ + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, + 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, + 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; + EXPECT_EQ(decompress_one_shot_error(input, 3, 10), + "Decompressed output exceeds the maximum allowed size"); +} + +TEST(crafted_bomb_beyond_maximum_size) { + const std::vector input{ + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x4b, 0x1c, + 0x05, 0xa3, 0x60, 0x14, 0x8c, 0x82, 0x51, 0x30, 0x0a, 0x46, 0xc1, 0x28, + 0x18, 0x05, 0xa3, 0x60, 0x14, 0x8c, 0x82, 0x51, 0x30, 0x0a, 0x46, 0xc1, + 0x28, 0x18, 0x05, 0x00, 0xa1, 0x87, 0xcc, 0x71, 0x23, 0x11, 0x00, 0x00}; + EXPECT_EQ(decompress_one_shot_error(input, 0, 4386), + "Decompressed output exceeds the maximum allowed size"); +} + +TEST(members_totaling_one_byte_over_maximum_size) { + const std::vector input{ + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, + 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, + 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00, 0x1f, 0x8b, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, 0xf4, + 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, + 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; + EXPECT_EQ(decompress_one_shot_error(input, 0, 21), + "Decompressed output exceeds the maximum allowed size"); +} + +TEST(second_member_alone_exceeding_maximum_size) { + const std::vector input{ + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, + 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, + 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, + 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; + EXPECT_EQ(decompress_one_shot_error(input, 0, 5), + "Decompressed output exceeds the maximum allowed size"); +} diff --git a/test/gzip/gzip_decompress_limits_test.cc b/test/gzip/gzip_decompress_limits_test.cc new file mode 100644 index 0000000000..fbae604a07 --- /dev/null +++ b/test/gzip/gzip_decompress_limits_test.cc @@ -0,0 +1,177 @@ +#include +#include + +#include // std::size_t +#include // std::uint8_t +#include // std::istream +#include // std::istreambuf_iterator +#include // std::numeric_limits +#include // std::mt19937, std::uniform_int_distribution +#include // std::istringstream +#include // std::string +#include // std::vector + +namespace { + +auto compress(const std::string &input, const int level) + -> std::vector { + const auto output{sourcemeta::core::gzip( + reinterpret_cast(input.data()), input.size(), + level)}; + return {output.cbegin(), output.cend()}; +} + +auto decompress_one_shot(const std::vector &input, + const std::size_t output_hint = 0, + const std::size_t maximum_size = 268435456) + -> std::string { + return sourcemeta::core::gunzip(input.data(), input.size(), output_hint, + maximum_size); +} + +auto decompress_stream(const std::vector &input) -> std::string { + std::istringstream stream{std::string{input.cbegin(), input.cend()}}; + sourcemeta::core::GZIPStreamBuffer buffer{stream}; + std::istream decompressed{&buffer}; + std::string result; + result.assign(std::istreambuf_iterator(decompressed), + std::istreambuf_iterator()); + return result; +} + +} // namespace + +TEST(decompress_ten_thousand_single_byte_members) { + std::vector input; + for (std::size_t index = 0; index < 10000; ++index) { + input.insert(input.end(), {0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x01, 0x01, 0x00, 0xfe, 0xff, 0x61, + 0x43, 0xbe, 0xb7, 0xe8, 0x01, 0x00, 0x00, 0x00}); + } + + EXPECT_EQ(decompress_one_shot(input, 1), std::string(10000, 'a')); + EXPECT_EQ(decompress_stream(input), std::string(10000, 'a')); +} + +TEST(decompress_output_exactly_at_maximum_size) { + const std::vector input{ + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, + 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, + 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; + EXPECT_EQ(decompress_one_shot(input, 0, 11), "hello world"); + EXPECT_EQ(decompress_stream(input), "hello world"); +} + +TEST(decompress_empty_payload_with_zero_maximum_size) { + const std::vector input{ + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + EXPECT_EQ(decompress_one_shot(input, 0, 0), ""); + EXPECT_EQ(decompress_stream(input), ""); +} + +TEST(decompress_output_hint_larger_than_output) { + const std::vector input{ + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, + 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, + 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; + EXPECT_EQ(decompress_one_shot(input, 1048576), "hello world"); + EXPECT_EQ(decompress_stream(input), "hello world"); +} + +TEST(decompress_output_hint_above_maximum_size_with_fitting_output) { + const std::vector input{ + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, + 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, + 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; + EXPECT_EQ(decompress_one_shot(input, 1048576, 11), "hello world"); + EXPECT_EQ(decompress_stream(input), "hello world"); +} + +TEST(decompress_output_hint_of_one_grows_to_larger_output) { + const std::vector input{ + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x4b, 0x1c, + 0x05, 0xa3, 0x60, 0x14, 0x8c, 0x82, 0x51, 0x30, 0x0a, 0x46, 0xc1, 0x28, + 0x18, 0x05, 0xa3, 0x60, 0x14, 0x8c, 0x82, 0x51, 0x30, 0x0a, 0x46, 0xc1, + 0x28, 0x18, 0x05, 0x00, 0xa1, 0x87, 0xcc, 0x71, 0x23, 0x11, 0x00, 0x00}; + EXPECT_EQ(decompress_one_shot(input, 1), std::string(4387, 'a')); + EXPECT_EQ(decompress_stream(input), std::string(4387, 'a')); +} + +TEST(decompress_with_largest_possible_maximum_size) { + const std::vector input{ + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, + 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, + 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; + EXPECT_EQ( + decompress_one_shot(input, 1, std::numeric_limits::max()), + "hello world"); + EXPECT_EQ(decompress_stream(input), "hello world"); +} + +TEST(decompress_input_larger_than_quarter_of_maximum_size) { + const std::vector input{ + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, + 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, + 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; + EXPECT_EQ(decompress_one_shot(input, 0, 40), "hello world"); + EXPECT_EQ(decompress_stream(input), "hello world"); +} + +TEST(decompress_growth_clamps_to_uneven_maximum_size) { + const std::vector input{ + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, + 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, + 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; + EXPECT_EQ(decompress_one_shot(input, 3, 11), "hello world"); + EXPECT_EQ(decompress_stream(input), "hello world"); +} + +TEST(decompress_crafted_bomb_at_maximum_size) { + const std::vector input{ + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x4b, 0x1c, + 0x05, 0xa3, 0x60, 0x14, 0x8c, 0x82, 0x51, 0x30, 0x0a, 0x46, 0xc1, 0x28, + 0x18, 0x05, 0xa3, 0x60, 0x14, 0x8c, 0x82, 0x51, 0x30, 0x0a, 0x46, 0xc1, + 0x28, 0x18, 0x05, 0x00, 0xa1, 0x87, 0xcc, 0x71, 0x23, 0x11, 0x00, 0x00}; + EXPECT_EQ(decompress_one_shot(input, 0, 4387), std::string(4387, 'a')); + EXPECT_EQ(decompress_stream(input), std::string(4387, 'a')); +} + +TEST(decompress_members_totaling_exactly_maximum_size) { + const std::vector input{ + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, + 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, + 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00, 0x1f, 0x8b, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, 0xf4, + 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, + 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; + EXPECT_EQ(decompress_one_shot(input, 0, 22), "hello worldhello world"); + EXPECT_EQ(decompress_stream(input), "hello worldhello world"); +} + +TEST(decompress_members_growing_from_tiny_output_hint) { + const std::vector input{ + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xff, 0x01, 0x05, + 0x00, 0xfa, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x86, 0xa6, 0x10, 0x36, + 0x05, 0x00, 0x00, 0x00, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0xff, 0x01, 0x06, 0x00, 0xf9, 0xff, 0x20, 0x77, 0x6f, 0x72, 0x6c, + 0x64, 0xcb, 0x42, 0x3b, 0x4a, 0x06, 0x00, 0x00, 0x00}; + EXPECT_EQ(decompress_one_shot(input, 1, 1024), "hello world"); + EXPECT_EQ(decompress_stream(input), "hello world"); +} + +TEST(decompress_large_member_after_small_member) { + // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) + std::mt19937 generator{20}; + std::uniform_int_distribution distribution{0, 255}; + std::string large; + for (std::size_t index = 0; index < 1048576; ++index) { + large.push_back(static_cast(distribution(generator))); + } + + auto input{compress("x", 1)}; + const auto large_compressed{compress(large, 6)}; + input.insert(input.end(), large_compressed.cbegin(), large_compressed.cend()); + EXPECT_EQ(decompress_one_shot(input, 1), "x" + large); + EXPECT_EQ(decompress_stream(input), "x" + large); +} diff --git a/test/gzip/gzip_test.cc b/test/gzip/gzip_decompress_test.cc similarity index 51% rename from test/gzip/gzip_test.cc rename to test/gzip/gzip_decompress_test.cc index e01545342b..6a7b6d9b4c 100644 --- a/test/gzip/gzip_test.cc +++ b/test/gzip/gzip_decompress_test.cc @@ -2,26 +2,15 @@ #include #include // std::size_t -#include // std::uint8_t, std::uint32_t +#include // std::uint8_t #include // std::istream #include // std::istreambuf_iterator -#include // std::numeric_limits -#include // std::mt19937, std::uniform_int_distribution #include // std::istringstream #include // std::string -#include // std::move #include // std::vector namespace { -auto compress(const std::string &input, const int level) - -> std::vector { - const auto output{sourcemeta::core::gzip( - reinterpret_cast(input.data()), input.size(), - level)}; - return {output.cbegin(), output.cend()}; -} - auto decompress_one_shot(const std::vector &input, const std::size_t output_hint = 0, const std::size_t maximum_size = 268435456) @@ -42,761 +31,17 @@ auto decompress_stream(const std::vector &input) -> std::string { } // namespace -TEST(compress_empty_input) { - const std::string input; - const auto compressed{compress(input, 1)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_hello_world) { - const std::string input{"hello world"}; - const auto compressed{compress(input, 1)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_every_byte_value) { - std::string input; - for (std::size_t index = 0; index < 256; ++index) { - input.push_back(static_cast(index)); - } - - const auto compressed{compress(input, 1)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_repeated_pangram) { - std::string input; - for (std::size_t index = 0; index < 1000; ++index) { - input += "The quick brown fox jumps over the lazy dog. "; - } - - const auto compressed{compress(input, 1)}; - EXPECT_LT(compressed.size(), input.size()); - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_level_0_round_trips) { - std::string input; - for (std::size_t index = 0; index < 2000; ++index) { - input += "The quick brown fox jumps over the lazy dog. "; - } - - const auto compressed{compress(input, 0)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_level_1_round_trips) { - std::string input; - for (std::size_t index = 0; index < 2000; ++index) { - input += "The quick brown fox jumps over the lazy dog. "; - } - - const auto compressed{compress(input, 1)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_level_2_round_trips) { - std::string input; - for (std::size_t index = 0; index < 2000; ++index) { - input += "The quick brown fox jumps over the lazy dog. "; - } - - const auto compressed{compress(input, 2)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_level_3_round_trips) { - std::string input; - for (std::size_t index = 0; index < 2000; ++index) { - input += "The quick brown fox jumps over the lazy dog. "; - } - - const auto compressed{compress(input, 3)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_level_4_round_trips) { - std::string input; - for (std::size_t index = 0; index < 2000; ++index) { - input += "The quick brown fox jumps over the lazy dog. "; - } - - const auto compressed{compress(input, 4)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_level_5_round_trips) { - std::string input; - for (std::size_t index = 0; index < 2000; ++index) { - input += "The quick brown fox jumps over the lazy dog. "; - } - - const auto compressed{compress(input, 5)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_level_6_round_trips) { - std::string input; - for (std::size_t index = 0; index < 2000; ++index) { - input += "The quick brown fox jumps over the lazy dog. "; - } - - const auto compressed{compress(input, 6)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_level_7_round_trips) { - std::string input; - for (std::size_t index = 0; index < 2000; ++index) { - input += "The quick brown fox jumps over the lazy dog. "; - } - - const auto compressed{compress(input, 7)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_level_8_round_trips) { - std::string input; - for (std::size_t index = 0; index < 2000; ++index) { - input += "The quick brown fox jumps over the lazy dog. "; - } - - const auto compressed{compress(input, 8)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_level_9_round_trips) { - std::string input; - for (std::size_t index = 0; index < 2000; ++index) { - input += "The quick brown fox jumps over the lazy dog. "; - } - - const auto compressed{compress(input, 9)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_level_10_round_trips) { - std::string input; - for (std::size_t index = 0; index < 2000; ++index) { - input += "The quick brown fox jumps over the lazy dog. "; - } - - const auto compressed{compress(input, 10)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_level_11_round_trips) { - std::string input; - for (std::size_t index = 0; index < 2000; ++index) { - input += "The quick brown fox jumps over the lazy dog. "; - } - - const auto compressed{compress(input, 11)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_level_12_round_trips) { - std::string input; - for (std::size_t index = 0; index < 2000; ++index) { - input += "The quick brown fox jumps over the lazy dog. "; - } - - const auto compressed{compress(input, 12)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_higher_level_is_not_larger) { - std::string input; - for (std::size_t index = 0; index < 1000; ++index) { - input += "The quick brown fox jumps over the lazy dog. "; - } - - EXPECT_LE(compress(input, 12).size(), compress(input, 1).size()); -} - -TEST(compress_output_header_identifies_deflate_member) { - const auto compressed{compress("hello world", 6)}; - EXPECT_GE(compressed.size(), 18); - EXPECT_EQ(compressed.at(0), 0x1f); - EXPECT_EQ(compressed.at(1), 0x8b); - EXPECT_EQ(compressed.at(2), 0x08); - EXPECT_EQ(compressed.at(3) & 0xe0, 0); -} - -TEST(compress_output_trailer_of_empty_input) { - const auto compressed{compress("", 1)}; - const std::vector trailer{compressed.cend() - 8, - compressed.cend()}; - const std::vector expected{0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00}; - EXPECT_EQ(trailer, expected); -} - -TEST(compress_output_trailer_of_hello_world_at_level_0) { - const auto compressed{compress("hello world", 0)}; - const std::vector trailer{compressed.cend() - 8, - compressed.cend()}; - const std::vector expected{0x85, 0x11, 0x4a, 0x0d, - 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(trailer, expected); -} - -TEST(compress_output_trailer_of_hello_world_at_level_1) { - const auto compressed{compress("hello world", 1)}; - const std::vector trailer{compressed.cend() - 8, - compressed.cend()}; - const std::vector expected{0x85, 0x11, 0x4a, 0x0d, - 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(trailer, expected); -} - -TEST(compress_output_trailer_of_hello_world_at_level_12) { - const auto compressed{compress("hello world", 12)}; - const std::vector trailer{compressed.cend() - 8, - compressed.cend()}; - const std::vector expected{0x85, 0x11, 0x4a, 0x0d, - 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(trailer, expected); -} - -TEST(compress_output_trailer_of_crc32_check_string) { - const auto compressed{compress("123456789", 6)}; - const std::vector trailer{compressed.cend() - 8, - compressed.cend()}; - const std::vector expected{0x26, 0x39, 0xf4, 0xcb, - 0x09, 0x00, 0x00, 0x00}; - EXPECT_EQ(trailer, expected); -} - -TEST(compress_output_trailer_of_pangram) { - const auto compressed{ - compress("The quick brown fox jumps over the lazy dog", 9)}; - const std::vector trailer{compressed.cend() - 8, - compressed.cend()}; - const std::vector expected{0x39, 0xa3, 0x4f, 0x41, - 0x2b, 0x00, 0x00, 0x00}; - EXPECT_EQ(trailer, expected); -} - -TEST(compress_output_trailer_size_spans_multiple_bytes) { - const auto compressed{compress(std::string(70000, 'z'), 1)}; - const std::vector size{compressed.cend() - 4, - compressed.cend()}; - const std::vector expected{0x70, 0x11, 0x01, 0x00}; - EXPECT_EQ(size, expected); -} - -TEST(compress_single_byte) { - const std::string input{"X"}; - const auto compressed{compress(input, 1)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_two_bytes) { - const std::string input{"AB"}; - const auto compressed{compress(input, 1)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_single_zero_byte) { - const std::string input(1, '\0'); - const auto compressed{compress(input, 1)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_minimum_match_length_input) { - const std::string input{"abcabc"}; - const auto compressed{compress(input, 12)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_run_of_maximum_match_length) { - const std::string input(259, 'q'); - const auto compressed{compress(input, 6)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_run_one_past_maximum_match_length) { - const std::string input(260, 'q'); - const auto compressed{compress(input, 6)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_counter_bytes_one_below_stream_buffer_size) { - std::string input; - for (std::size_t index = 0; index < 16383; ++index) { - input.push_back(static_cast(index & 0xff)); - } - - const auto compressed{compress(input, 1)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_counter_bytes_of_stream_buffer_size) { - std::string input; - for (std::size_t index = 0; index < 16384; ++index) { - input.push_back(static_cast(index & 0xff)); - } - - const auto compressed{compress(input, 1)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_counter_bytes_one_above_stream_buffer_size) { - std::string input; - for (std::size_t index = 0; index < 16385; ++index) { - input.push_back(static_cast(index & 0xff)); - } - - const auto compressed{compress(input, 1)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_counter_bytes_of_window_size) { - std::string input; - for (std::size_t index = 0; index < 32768; ++index) { - input.push_back(static_cast(index & 0xff)); - } - - const auto compressed{compress(input, 1)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_counter_bytes_of_one_megabyte) { - std::string input; - for (std::size_t index = 0; index < 1048576; ++index) { - input.push_back(static_cast(index & 0xff)); - } - - const auto compressed{compress(input, 1)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_random_input_one_below_window_size) { - // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) - std::mt19937 generator{1}; - std::uniform_int_distribution distribution{0, 255}; - std::string input; - for (std::size_t index = 0; index < 32767; ++index) { - input.push_back(static_cast(distribution(generator))); - } - - const auto compressed{compress(input, 1)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_random_input_of_window_size) { - // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) - std::mt19937 generator{1}; - std::uniform_int_distribution distribution{0, 255}; - std::string input; - for (std::size_t index = 0; index < 32768; ++index) { - input.push_back(static_cast(distribution(generator))); - } - - const auto compressed{compress(input, 1)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_random_input_one_above_window_size) { - // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) - std::mt19937 generator{1}; - std::uniform_int_distribution distribution{0, 255}; - std::string input; - for (std::size_t index = 0; index < 32769; ++index) { - input.push_back(static_cast(distribution(generator))); - } - - const auto compressed{compress(input, 1)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_level_0_random_input_of_maximum_stored_block_length) { - // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) - std::mt19937 generator{2}; - std::uniform_int_distribution distribution{0, 255}; - std::string input; - for (std::size_t index = 0; index < 65535; ++index) { - input.push_back(static_cast(distribution(generator))); - } - - const auto compressed{compress(input, 0)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_level_0_random_input_one_above_maximum_stored_block_length) { - // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) - std::mt19937 generator{2}; - std::uniform_int_distribution distribution{0, 255}; - std::string input; - for (std::size_t index = 0; index < 65536; ++index) { - input.push_back(static_cast(distribution(generator))); - } - - const auto compressed{compress(input, 0)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_level_0_random_input_of_two_maximum_stored_blocks) { - // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) - std::mt19937 generator{2}; - std::uniform_int_distribution distribution{0, 255}; - std::string input; - for (std::size_t index = 0; index < 131070; ++index) { - input.push_back(static_cast(distribution(generator))); - } - - const auto compressed{compress(input, 0)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_highly_compressible_zeros) { - const std::string input(65536, '\0'); - const auto compressed{compress(input, 1)}; - EXPECT_LT(compressed.size(), input.size() / 10); - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_highly_compressible_repeated_byte) { - const std::string input(65536, static_cast(0xff)); - const auto compressed{compress(input, 1)}; - EXPECT_LT(compressed.size(), input.size() / 10); - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_incompressible_random_input) { - // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) - std::mt19937 generator{42}; - std::uniform_int_distribution distribution{0, 255}; - std::string input; - for (std::size_t index = 0; index < 65536; ++index) { - input.push_back(static_cast(distribution(generator))); - } - - const auto compressed{compress(input, 1)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_incompressible_input_at_level_0_barely_expands) { - // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) - std::mt19937 generator{3}; - std::uniform_int_distribution distribution{0, 255}; - std::string input; - for (std::size_t index = 0; index < 1048576; ++index) { - input.push_back(static_cast(distribution(generator))); - } - - const auto compressed{compress(input, 0)}; - EXPECT_LE(compressed.size(), input.size() + input.size() / 100); - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_incompressible_input_at_level_1_barely_expands) { - // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) - std::mt19937 generator{3}; - std::uniform_int_distribution distribution{0, 255}; - std::string input; - for (std::size_t index = 0; index < 1048576; ++index) { - input.push_back(static_cast(distribution(generator))); - } - - const auto compressed{compress(input, 1)}; - EXPECT_LE(compressed.size(), input.size() + input.size() / 100); - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_incompressible_input_at_level_6_barely_expands) { - // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) - std::mt19937 generator{3}; - std::uniform_int_distribution distribution{0, 255}; - std::string input; - for (std::size_t index = 0; index < 1048576; ++index) { - input.push_back(static_cast(distribution(generator))); - } - - const auto compressed{compress(input, 6)}; - EXPECT_LE(compressed.size(), input.size() + input.size() / 100); - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_incompressible_input_at_level_12_barely_expands) { - // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) - std::mt19937 generator{3}; - std::uniform_int_distribution distribution{0, 255}; - std::string input; - for (std::size_t index = 0; index < 1048576; ++index) { - input.push_back(static_cast(distribution(generator))); - } - - const auto compressed{compress(input, 12)}; - EXPECT_LE(compressed.size(), input.size() + input.size() / 100); - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_run_wrapping_the_window_many_times) { - const std::string input(200000, 'a'); - const auto compressed{compress(input, 1)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_eight_megabytes_of_zeros_at_level_1) { - const std::string input(8388608, '\0'); - const auto compressed{compress(input, 1)}; - EXPECT_LT(compressed.size(), input.size() / 100); - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_four_megabytes_of_zeros_at_level_12) { - const std::string input(4194304, '\0'); - const auto compressed{compress(input, 12)}; - EXPECT_LT(compressed.size(), input.size() / 100); - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_alternating_bytes) { - std::string input; - for (std::size_t index = 0; index < 524288; ++index) { - input += "\x01\xfe"; - } - - const auto compressed{compress(input, 6)}; - EXPECT_LT(compressed.size(), input.size() / 100); - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_random_block_repeated_at_window_size_distance_at_level_6) { - // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) - std::mt19937 generator{4}; - std::uniform_int_distribution distribution{0, 255}; - std::string block; - for (std::size_t index = 0; index < 32768; ++index) { - block.push_back(static_cast(distribution(generator))); - } - - const auto input{block + block + block + block}; - const auto compressed{compress(input, 6)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_random_block_repeated_at_window_size_distance_at_level_12) { - // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) - std::mt19937 generator{4}; - std::uniform_int_distribution distribution{0, 255}; - std::string block; - for (std::size_t index = 0; index < 32768; ++index) { - block.push_back(static_cast(distribution(generator))); - } - - const auto input{block + block + block + block}; - const auto compressed{compress(input, 12)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_random_block_repeated_beyond_window_size_distance) { - // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) - std::mt19937 generator{5}; - std::uniform_int_distribution distribution{0, 255}; - std::string block; - for (std::size_t index = 0; index < 32769; ++index) { - block.push_back(static_cast(distribution(generator))); - } - - const auto input{block + block + block + block}; - const auto compressed{compress(input, 12)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_random_block_repeated_at_maximum_match_length_period) { - // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) - std::mt19937 generator{6}; - std::uniform_int_distribution distribution{0, 255}; - std::string block; - for (std::size_t index = 0; index < 258; ++index) { - block.push_back(static_cast(distribution(generator))); - } - - std::string input; - for (std::size_t index = 0; index < 1000; ++index) { - input += block; - } - - const auto compressed{compress(input, 6)}; - EXPECT_LT(compressed.size(), input.size() / 10); - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_random_block_repeated_beyond_maximum_match_length_period) { - // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) - std::mt19937 generator{6}; - std::uniform_int_distribution distribution{0, 255}; - std::string block; - for (std::size_t index = 0; index < 259; ++index) { - block.push_back(static_cast(distribution(generator))); - } - - std::string input; - for (std::size_t index = 0; index < 1000; ++index) { - input += block; - } - - const auto compressed{compress(input, 6)}; - EXPECT_LT(compressed.size(), input.size() / 10); - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_runs_of_every_match_length) { - std::string input; - for (std::size_t length = 1; length <= 300; ++length) { - input.append(length, static_cast(length & 0xff)); - input.append(length, static_cast(~length & 0xff)); - } - - const auto compressed{compress(input, 9)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_fibonacci_word_at_level_1) { - std::string previous{"b"}; - std::string input{"a"}; - while (input.size() < 1048576) { - auto next{input + previous}; - previous = std::move(input); - input = std::move(next); - } - - const auto compressed{compress(input, 1)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_fibonacci_word_at_level_12) { - std::string previous{"b"}; - std::string input{"a"}; - while (input.size() < 1048576) { - auto next{input + previous}; - previous = std::move(input); - input = std::move(next); - } - - const auto compressed{compress(input, 12)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_counter_sequence) { - std::string input; - for (std::uint32_t index = 0; index < 262144; ++index) { - input.push_back(static_cast(index & 0xff)); - input.push_back(static_cast((index >> 8) & 0xff)); - input.push_back(static_cast((index >> 16) & 0xff)); - input.push_back(static_cast((index >> 24) & 0xff)); - } - - const auto compressed{compress(input, 6)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_already_compressed_input) { - std::string text; - for (std::size_t index = 0; index < 2000; ++index) { - text += "The quick brown fox jumps over the lazy dog. "; - } - - const auto inner{compress(text, 12)}; - const std::string input{inner.cbegin(), inner.cend()}; - const auto compressed{compress(input, 12)}; - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_sixteen_megabytes) { - std::string input; - for (std::uint32_t index = 0; index < 4194304; ++index) { - input.push_back(static_cast(index & 0xff)); - input.push_back(static_cast((index >> 8) & 0xff)); - input.push_back(static_cast((index >> 16) & 0xff)); - input.push_back(static_cast((index >> 24) & 0xff)); - } - - const auto compressed{compress(input, 1)}; - EXPECT_LT(compressed.size(), input.size()); - EXPECT_EQ(decompress_one_shot(compressed), input); - EXPECT_EQ(decompress_stream(compressed), input); -} - -TEST(compress_members_of_different_levels_concatenate) { - const std::string first(70000, 'f'); - const std::string second{"second"}; - auto compressed{compress(first, 0)}; - const auto second_compressed{compress(second, 12)}; - const auto empty_compressed{compress("", 6)}; - compressed.insert(compressed.end(), second_compressed.cbegin(), - second_compressed.cend()); - compressed.insert(compressed.end(), empty_compressed.cbegin(), - empty_compressed.cend()); - EXPECT_EQ(decompress_one_shot(compressed), first + second); - EXPECT_EQ(decompress_stream(compressed), first + second); -} +// Both decompression mechanisms must produce the same output for every input +#define EXPECT_GZIP_DECOMPRESS(input, expected) \ + EXPECT_EQ(decompress_one_shot(input), expected); \ + EXPECT_EQ(decompress_stream(input), expected) TEST(decompress_header_with_ftext) { const std::vector input{ 0x1f, 0x8b, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_header_with_fhcrc) { @@ -804,8 +49,7 @@ TEST(decompress_header_with_fhcrc) { 0x1f, 0x8b, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x90, 0xc9, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_header_with_fextra) { @@ -814,8 +58,7 @@ TEST(decompress_header_with_fextra) { 0x00, 0x45, 0x58, 0x54, 0x52, 0x41, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_header_with_empty_fextra) { @@ -823,8 +66,7 @@ TEST(decompress_header_with_empty_fextra) { 0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_header_with_fextra_subfields) { @@ -833,8 +75,7 @@ TEST(decompress_header_with_fextra_subfields) { 0x41, 0x42, 0x02, 0x00, 0x01, 0x02, 0x43, 0x44, 0x00, 0x00, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_header_with_fextra_of_null_bytes_before_fname) { @@ -843,8 +84,7 @@ TEST(decompress_header_with_fextra_of_null_bytes_before_fname) { 0x00, 0x00, 0x00, 0x00, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_header_with_fextra_and_fname) { @@ -853,8 +93,7 @@ TEST(decompress_header_with_fextra_and_fname) { 0x00, 0x78, 0x79, 0x7a, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_header_with_fextra_and_fcomment) { @@ -863,8 +102,7 @@ TEST(decompress_header_with_fextra_and_fcomment) { 0x00, 0x41, 0x42, 0x68, 0x69, 0x00, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_header_with_fname_and_fcomment) { @@ -873,8 +111,7 @@ TEST(decompress_header_with_fname_and_fcomment) { 0x2e, 0x74, 0x78, 0x74, 0x00, 0x63, 0x6d, 0x74, 0x00, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_header_with_fname) { @@ -883,8 +120,7 @@ TEST(decompress_header_with_fname) { 0x61, 0x74, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x00, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_header_with_empty_fname) { @@ -892,8 +128,7 @@ TEST(decompress_header_with_empty_fname) { 0x1f, 0x8b, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_header_with_fname_containing_gzip_magic) { @@ -902,8 +137,7 @@ TEST(decompress_header_with_fname_containing_gzip_magic) { 0x1f, 0x8b, 0x08, 0x1f, 0x8b, 0x00, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_header_with_latin1_fname) { @@ -912,8 +146,7 @@ TEST(decompress_header_with_latin1_fname) { 0xa9, 0xc6, 0xff, 0x00, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_header_with_fcomment) { @@ -923,8 +156,7 @@ TEST(decompress_header_with_fcomment) { 0x20, 0x74, 0x77, 0x6f, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x00, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_header_with_empty_fcomment) { @@ -932,8 +164,7 @@ TEST(decompress_header_with_empty_fcomment) { 0x1f, 0x8b, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_header_with_latin1_fcomment) { @@ -942,8 +173,7 @@ TEST(decompress_header_with_latin1_fcomment) { 0xc0, 0xc1, 0xfe, 0x00, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_header_with_every_optional_field) { @@ -953,8 +183,7 @@ TEST(decompress_header_with_every_optional_field) { 0x00, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x90, 0x3e, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_header_with_nonzero_mtime) { @@ -962,8 +191,7 @@ TEST(decompress_header_with_nonzero_mtime) { 0x1f, 0x8b, 0x08, 0x00, 0x78, 0x56, 0x34, 0x12, 0x00, 0xff, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_header_with_maximum_compression_xfl) { @@ -971,8 +199,7 @@ TEST(decompress_header_with_maximum_compression_xfl) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_header_with_fastest_compression_xfl) { @@ -980,8 +207,7 @@ TEST(decompress_header_with_fastest_compression_xfl) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xff, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_header_with_unix_os) { @@ -989,16 +215,14 @@ TEST(decompress_header_with_unix_os) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_stored_block_with_empty_final_block) { const std::vector input{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), ""); - EXPECT_EQ(decompress_stream(input), ""); + EXPECT_GZIP_DECOMPRESS(input, ""); } TEST(decompress_empty_stored_block_before_data) { @@ -1007,8 +231,7 @@ TEST(decompress_empty_stored_block_before_data) { 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_two_stored_blocks) { @@ -1017,8 +240,7 @@ TEST(decompress_two_stored_blocks) { 0x00, 0x05, 0x00, 0xfa, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x01, 0x06, 0x00, 0xf9, 0xff, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_stored_block_ignores_nonzero_bits_before_byte_boundary) { @@ -1026,8 +248,7 @@ TEST(decompress_stored_block_ignores_nonzero_bits_before_byte_boundary) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x4a, 0x04, 0xe4, 0x01, 0x00, 0xfe, 0xff, 0x62, 0x6d, 0x48, 0x83, 0x9e, 0x02, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "ab"); - EXPECT_EQ(decompress_stream(input), "ab"); + EXPECT_GZIP_DECOMPRESS(input, "ab"); } TEST(decompress_fixed_block_with_every_literal_value) { @@ -1062,16 +283,14 @@ TEST(decompress_fixed_block_with_every_literal_value) { expected.push_back(static_cast(index)); } - EXPECT_EQ(decompress_one_shot(input), expected); - EXPECT_EQ(decompress_stream(input), expected); + EXPECT_GZIP_DECOMPRESS(input, expected); } TEST(decompress_fixed_block_with_only_end_of_block) { const std::vector input{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x13, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), ""); - EXPECT_EQ(decompress_stream(input), ""); + EXPECT_GZIP_DECOMPRESS(input, ""); } TEST(decompress_fixed_block_with_repeated_word) { @@ -1079,8 +298,7 @@ TEST(decompress_fixed_block_with_repeated_word) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x13, 0xcb, 0x48, 0xcd, 0xc9, 0xc9, 0x57, 0xc8, 0xc0, 0x4e, 0x02, 0x00, 0xf6, 0xd2, 0x53, 0x38, 0x1d, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "hello hello hello hello hello"); - EXPECT_EQ(decompress_stream(input), "hello hello hello hello hello"); + EXPECT_GZIP_DECOMPRESS(input, "hello hello hello hello hello"); } TEST(decompress_fixed_block_with_boundary_match_lengths) { @@ -1089,24 +307,21 @@ TEST(decompress_fixed_block_with_boundary_match_lengths) { 0x04, 0x02, 0x04, 0x40, 0x02, 0xc8, 0x00, 0x13, 0x60, 0x01, 0x84, 0x01, 0x11, 0x60, 0xe0, 0xc1, 0x20, 0x00, 0x23, 0x00, 0x8c, 0x78, 0x30, 0x0a, 0x00, 0x9b, 0xb4, 0x10, 0xfa, 0x7a, 0x04, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), std::string(1146, 'a')); - EXPECT_EQ(decompress_stream(input), std::string(1146, 'a')); + EXPECT_GZIP_DECOMPRESS(input, std::string(1146, 'a')); } TEST(decompress_fixed_block_overlapping_match) { const std::vector input{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x8b, 0x88, 0x04, 0x43, 0x00, 0x60, 0xa5, 0xd7, 0x74, 0x07, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "XYXYXYX"); - EXPECT_EQ(decompress_stream(input), "XYXYXYX"); + EXPECT_GZIP_DECOMPRESS(input, "XYXYXYX"); } TEST(decompress_fixed_block_maximum_length_match_at_distance_one) { const std::vector input{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xab, 0x18, 0x05, 0x00, 0xad, 0x7c, 0x22, 0xf7, 0x03, 0x01, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), std::string(259, 'x')); - EXPECT_EQ(decompress_stream(input), std::string(259, 'x')); + EXPECT_GZIP_DECOMPRESS(input, std::string(259, 'x')); } TEST(decompress_fixed_block_matches_wrapping_the_window) { @@ -1136,8 +351,7 @@ TEST(decompress_fixed_block_matches_wrapping_the_window) { expected += "ab"; } - EXPECT_EQ(decompress_one_shot(input), expected); - EXPECT_EQ(decompress_stream(input), expected); + EXPECT_GZIP_DECOMPRESS(input, expected); } TEST(decompress_match_across_stored_and_fixed_blocks) { @@ -1145,8 +359,7 @@ TEST(decompress_match_across_stored_and_fixed_blocks) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x06, 0x00, 0xf9, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x03, 0x93, 0x00, 0x40, 0xa6, 0x2d, 0x01, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "hello hello"); - EXPECT_EQ(decompress_stream(input), "hello hello"); + EXPECT_GZIP_DECOMPRESS(input, "hello hello"); } TEST(decompress_match_spanning_several_blocks) { @@ -1155,16 +368,14 @@ TEST(decompress_match_spanning_several_blocks) { 0x00, 0x02, 0x00, 0xfd, 0xff, 0x61, 0x62, 0x4a, 0x4e, 0x01, 0x00, 0x02, 0x00, 0xfd, 0xff, 0x65, 0x66, 0x83, 0x90, 0x00, 0x66, 0xe9, 0xe4, 0x71, 0x0c, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "abcdefabcdef"); - EXPECT_EQ(decompress_stream(input), "abcdefabcdef"); + EXPECT_GZIP_DECOMPRESS(input, "abcdefabcdef"); } TEST(decompress_ignores_nonzero_padding_bits_after_final_block) { const std::vector input{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x4b, 0x04, 0xfc, 0x43, 0xbe, 0xb7, 0xe8, 0x01, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "a"); - EXPECT_EQ(decompress_stream(input), "a"); + EXPECT_GZIP_DECOMPRESS(input, "a"); } TEST(decompress_dynamic_block_with_literals_and_match) { @@ -1183,8 +394,7 @@ TEST(decompress_dynamic_block_with_literals_and_match) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21, 0xe2, 0x02, 0x14, 0x41, 0xc8, 0x7f, 0x07, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "ababbbb"); - EXPECT_EQ(decompress_stream(input), "ababbbb"); + EXPECT_GZIP_DECOMPRESS(input, "ababbbb"); } TEST(decompress_dynamic_block_without_distance_codes) { @@ -1203,8 +413,7 @@ TEST(decompress_dynamic_block_without_distance_codes) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x2d, 0x73, 0x07, 0xf0, 0x03, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "aaa"); - EXPECT_EQ(decompress_stream(input), "aaa"); + EXPECT_GZIP_DECOMPRESS(input, "aaa"); } TEST(decompress_dynamic_block_with_only_end_of_block) { @@ -1212,8 +421,7 @@ TEST(decompress_dynamic_block_with_only_end_of_block) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x05, 0xc0, 0x81, 0x08, 0x00, 0x00, 0x00, 0x00, 0x20, 0x7f, 0xeb, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), ""); - EXPECT_EQ(decompress_stream(input), ""); + EXPECT_GZIP_DECOMPRESS(input, ""); } TEST(decompress_dynamic_block_with_single_literal_length_code) { @@ -1232,8 +440,7 @@ TEST(decompress_dynamic_block_with_single_literal_length_code) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), ""); - EXPECT_EQ(decompress_stream(input), ""); + EXPECT_GZIP_DECOMPRESS(input, ""); } TEST(decompress_dynamic_block_with_maximum_literal_length_codes) { @@ -1253,8 +460,7 @@ TEST(decompress_dynamic_block_with_maximum_literal_length_codes) { 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa1, 0x05, 0x56, 0xfa, 0xc2, 0x34, 0x03, 0x01, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), std::string(259, 'a')); - EXPECT_EQ(decompress_stream(input), std::string(259, 'a')); + EXPECT_GZIP_DECOMPRESS(input, std::string(259, 'a')); } TEST(decompress_dynamic_block_with_maximum_distance_codes) { @@ -1274,8 +480,7 @@ TEST(decompress_dynamic_block_with_maximum_distance_codes) { 0x00, 0x00, 0x00, 0x10, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x17, 0x0b, 0xe0, 0xcc, 0xc9, 0x08, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "ababaaaa"); - EXPECT_EQ(decompress_stream(input), "ababaaaa"); + EXPECT_GZIP_DECOMPRESS(input, "ababaaaa"); } TEST(decompress_dynamic_block_with_fifteen_bit_literal_length_codes) { @@ -1295,8 +500,7 @@ TEST(decompress_dynamic_block_with_fifteen_bit_literal_length_codes) { 0x00, 0x00, 0x00, 0x3c, 0x68, 0xf7, 0xbe, 0xdf, 0xdf, 0xbf, 0xff, 0xfe, 0xf7, 0x7f, 0xff, 0xef, 0xff, 0xfb, 0xff, 0xfd, 0xff, 0xfe, 0xff, 0x66, 0xcd, 0x32, 0x19, 0x10, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "abcdefghijklmnoo"); - EXPECT_EQ(decompress_stream(input), "abcdefghijklmnoo"); + EXPECT_GZIP_DECOMPRESS(input, "abcdefghijklmnoo"); } TEST(decompress_dynamic_block_with_fifteen_bit_distance_codes) { @@ -1343,8 +547,7 @@ TEST(decompress_dynamic_block_with_fifteen_bit_distance_codes) { } expected.append({0x3f, 0x40, 0x41, 0x43, 0x44, 0x45, 0x45, 0x45, 0x45}); - EXPECT_EQ(decompress_one_shot(input), expected); - EXPECT_EQ(decompress_stream(input), expected); + EXPECT_GZIP_DECOMPRESS(input, expected); } TEST(decompress_dynamic_block_with_seven_bit_code_length_codes) { @@ -1355,8 +558,7 @@ TEST(decompress_dynamic_block_with_seven_bit_code_length_codes) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x69, 0xf7, 0xbe, 0xdf, 0x1f, 0xa6, 0x6a, 0x2a, 0x31, 0x07, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "abcdefg"); - EXPECT_EQ(decompress_stream(input), "abcdefg"); + EXPECT_GZIP_DECOMPRESS(input, "abcdefg"); } TEST(decompress_dynamic_block_repeat_previous_code_length_across_alphabets) { @@ -1364,8 +566,7 @@ TEST(decompress_dynamic_block_repeat_previous_code_length_across_alphabets) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x0d, 0x83, 0x05, 0x01, 0x00, 0x00, 0x00, 0x40, 0xb6, 0xf2, 0x7f, 0x84, 0x44, 0xfc, 0x0b, 0xbb, 0xc2, 0xf9, 0x28, 0x0a, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "abababaaba"); - EXPECT_EQ(decompress_stream(input), "abababaaba"); + EXPECT_GZIP_DECOMPRESS(input, "abababaaba"); } TEST(decompress_dynamic_block_repeat_zero_code_length_across_alphabets) { @@ -1373,8 +574,7 @@ TEST(decompress_dynamic_block_repeat_zero_code_length_across_alphabets) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x1d, 0xc3, 0x21, 0x01, 0x00, 0x00, 0x00, 0x80, 0xa0, 0xad, 0xfa, 0x7f, 0x84, 0x16, 0x40, 0xfc, 0x02, 0xbb, 0xc2, 0xf9, 0x28, 0x0a, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "abababaaba"); - EXPECT_EQ(decompress_stream(input), "abababaaba"); + EXPECT_GZIP_DECOMPRESS(input, "abababaaba"); } TEST(decompress_member_mixing_every_block_type) { @@ -1395,8 +595,7 @@ TEST(decompress_member_mixing_every_block_type) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xe0, 0x1b, 0x1b, 0xe1, 0x07, 0x15, 0x04, 0x00, 0x4a, 0xa1, 0x0b, 0x7c, 0x1c, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "stored fixed dynamic stored "); - EXPECT_EQ(decompress_stream(input), "stored fixed dynamic stored "); + EXPECT_GZIP_DECOMPRESS(input, "stored fixed dynamic stored "); } TEST(decompress_two_members) { @@ -1406,8 +605,7 @@ TEST(decompress_two_members) { 0x05, 0x00, 0x00, 0x00, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xff, 0x01, 0x06, 0x00, 0xf9, 0xff, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0xcb, 0x42, 0x3b, 0x4a, 0x06, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_three_members_with_empty_member) { @@ -1419,8 +617,7 @@ TEST(decompress_three_members_with_empty_member) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xff, 0x01, 0x03, 0x00, 0xfc, 0xff, 0x62, 0x61, 0x7a, 0x98, 0x04, 0x24, 0x78, 0x03, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "foobaz"); - EXPECT_EQ(decompress_stream(input), "foobaz"); + EXPECT_GZIP_DECOMPRESS(input, "foobaz"); } TEST(decompress_empty_member_between_members) { @@ -1432,8 +629,7 @@ TEST(decompress_empty_member_between_members) { 0x00, 0x00, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xff, 0x01, 0x05, 0x00, 0xfa, 0xff, 0x72, 0x69, 0x67, 0x68, 0x74, 0x14, 0x75, 0xca, 0xb4, 0x05, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "leftright"); - EXPECT_EQ(decompress_stream(input), "leftright"); + EXPECT_GZIP_DECOMPRESS(input, "leftright"); } TEST(decompress_members_with_different_optional_header_fields) { @@ -1446,8 +642,7 @@ TEST(decompress_members_with_different_optional_header_fields) { 0x2e, 0x74, 0x78, 0x74, 0x00, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x90, 0x3e, 0x01, 0x05, 0x00, 0xfa, 0xff, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x43, 0x11, 0x77, 0x3a, 0x05, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_run_wrapping_the_window) { @@ -1474,8 +669,7 @@ TEST(decompress_run_wrapping_the_window) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x01, 0x9b, 0x53, 0x69, 0xe0, 0x40, 0x0d, 0x03, 0x00}; - EXPECT_EQ(decompress_one_shot(input), std::string(200000, 'a')); - EXPECT_EQ(decompress_stream(input), std::string(200000, 'a')); + EXPECT_GZIP_DECOMPRESS(input, std::string(200000, 'a')); } TEST(decompress_ignores_trailing_text) { @@ -1486,8 +680,7 @@ TEST(decompress_ignores_trailing_text) { 0x00, 0x67, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x61, 0x66, 0x74, 0x65, 0x72, 0x20, 0x67, 0x7a, 0x69, 0x70, 0x20, 0x65, 0x6e, 0x64}; - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_ignores_single_trailing_identification_byte) { @@ -1495,8 +688,7 @@ TEST(decompress_ignores_single_trailing_identification_byte) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xff, 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00, 0x1f}; - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_ignores_trailing_first_identification_byte_without_second) { @@ -1505,8 +697,7 @@ TEST(decompress_ignores_trailing_first_identification_byte_without_second) { 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00, 0x1f, 0x8c, 0x6d, 0x6f, 0x72, 0x65}; - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_ignores_member_after_trailing_garbage) { @@ -1516,8 +707,7 @@ TEST(decompress_ignores_member_after_trailing_garbage) { 0x05, 0x00, 0x00, 0x00, 0x6a, 0x75, 0x6e, 0x6b, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xff, 0x01, 0x05, 0x00, 0xfa, 0xff, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x43, 0x11, 0x77, 0x3a, 0x05, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input), "hello"); - EXPECT_EQ(decompress_stream(input), "hello"); + EXPECT_GZIP_DECOMPRESS(input, "hello"); } TEST(decompress_ignores_trailing_garbage_after_empty_member) { @@ -1525,8 +715,7 @@ TEST(decompress_ignores_trailing_garbage_after_empty_member) { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x75, 0x6e, 0x6b}; - EXPECT_EQ(decompress_one_shot(input), ""); - EXPECT_EQ(decompress_stream(input), ""); + EXPECT_GZIP_DECOMPRESS(input, ""); } TEST(decompress_header_with_maximum_length_fextra) { @@ -1536,8 +725,7 @@ TEST(decompress_header_with_maximum_length_fextra) { input.insert(input.end(), {0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}); - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_header_with_one_megabyte_fname) { @@ -1548,8 +736,7 @@ TEST(decompress_header_with_one_megabyte_fname) { input.insert(input.end(), {0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}); - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_header_with_one_megabyte_fcomment) { @@ -1560,8 +747,7 @@ TEST(decompress_header_with_one_megabyte_fcomment) { input.insert(input.end(), {0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}); - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } TEST(decompress_header_with_fextra_spanning_stream_source_buffer) { @@ -1571,8 +757,40 @@ TEST(decompress_header_with_fextra_spanning_stream_source_buffer) { input.insert(input.end(), {0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}); - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); +} + +TEST(decompress_trailer_spanning_stream_source_buffer) { + std::vector input{0x1f, 0x8b, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, + 0x01, 0xed, 0xff, 0x12, 0x00}; + input.insert(input.end(), 65517, 0x00); + input.insert(input.end(), {0x1b, 0x35, 0x0a, 0xab, 0xed, 0xff, 0x00, 0x00}); + EXPECT_GZIP_DECOMPRESS(input, std::string(65517, '\0')); +} + +TEST( + decompress_second_member_identification_bytes_spanning_stream_source_buffer) { + std::vector input{0x1f, 0x8b, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, + 0x01, 0xe8, 0xff, 0x17, 0x00}; + input.insert(input.end(), 65512, 0x00); + input.insert(input.end(), + {0x13, 0x4e, 0x7c, 0xe2, 0xe8, 0xff, 0x00, 0x00, 0x1f, + 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x01, 0x0b, 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, + 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, + 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}); + EXPECT_GZIP_DECOMPRESS(input, std::string(65512, '\0') + "hello world"); +} + +TEST(decompress_fixed_block_spanning_stream_source_buffer) { + std::vector input{0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x63}; + input.insert(input.end(), 69999, 0x60); + input.insert(input.end(), + {0x00, 0x00, 0xdc, 0xc8, 0xa9, 0xa6, 0x70, 0x11, 0x01, 0x00}); + EXPECT_GZIP_DECOMPRESS(input, std::string(70000, '\0')); } TEST(decompress_stored_block_of_maximum_length) { @@ -1587,8 +805,7 @@ TEST(decompress_stored_block_of_maximum_length) { } input.insert(input.end(), {0xad, 0x58, 0x8d, 0x46, 0xff, 0xff, 0x00, 0x00}); - EXPECT_EQ(decompress_one_shot(input), expected); - EXPECT_EQ(decompress_stream(input), expected); + EXPECT_GZIP_DECOMPRESS(input, expected); } TEST(decompress_consecutive_stored_blocks_of_maximum_length) { @@ -1610,8 +827,7 @@ TEST(decompress_consecutive_stored_blocks_of_maximum_length) { } input.insert(input.end(), {0x55, 0x65, 0xe3, 0x34, 0xfe, 0xff, 0x01, 0x00}); - EXPECT_EQ(decompress_one_shot(input), expected); - EXPECT_EQ(decompress_stream(input), expected); + EXPECT_GZIP_DECOMPRESS(input, expected); } TEST(decompress_many_empty_stored_blocks_before_data) { @@ -1624,8 +840,7 @@ TEST(decompress_many_empty_stored_blocks_before_data) { input.insert(input.end(), {0x01, 0x04, 0x00, 0xfb, 0xff, 0x64, 0x61, 0x74, 0x61, 0x63, 0xf3, 0xf3, 0xad, 0x04, 0x00, 0x00, 0x00}); - EXPECT_EQ(decompress_one_shot(input), "data"); - EXPECT_EQ(decompress_stream(input), "data"); + EXPECT_GZIP_DECOMPRESS(input, "data"); } TEST(decompress_fixed_block_with_boundary_distances) { @@ -1649,8 +864,7 @@ TEST(decompress_fixed_block_with_boundary_distances) { "\x6a\x6b\x6b\x64\x65\x4b\x4c\x4d\x4d\x4e\x4f" "\x00\x01\x02\x24\x25\x26", 39}); - EXPECT_EQ(decompress_one_shot(input), expected); - EXPECT_EQ(decompress_stream(input), expected); + EXPECT_GZIP_DECOMPRESS(input, expected); } TEST(decompress_fixed_block_maximum_length_match_at_maximum_distance) { @@ -1670,8 +884,7 @@ TEST(decompress_fixed_block_maximum_length_match_at_maximum_distance) { input.insert(input.end(), {0x1b, 0xbd, 0xff, 0x1f, 0x00, 0xc8, 0x0f, 0x86, 0x20, 0x02, 0x81, 0x00, 0x00}); - EXPECT_EQ(decompress_one_shot(input), expected); - EXPECT_EQ(decompress_stream(input), expected); + EXPECT_GZIP_DECOMPRESS(input, expected); } TEST(decompress_many_single_literal_fixed_blocks) { @@ -1684,8 +897,7 @@ TEST(decompress_many_single_literal_fixed_blocks) { input.insert(input.end(), {0x03, 0x00, 0xa3, 0xa4, 0x55, 0x0d, 0x10, 0x27, 0x00, 0x00}); - EXPECT_EQ(decompress_one_shot(input), std::string(10000, 'x')); - EXPECT_EQ(decompress_stream(input), std::string(10000, 'x')); + EXPECT_GZIP_DECOMPRESS(input, std::string(10000, 'x')); } TEST(decompress_single_fixed_block_with_maximum_compression_ratio) { @@ -1700,8 +912,7 @@ TEST(decompress_single_fixed_block_with_maximum_compression_ratio) { {0x00, 0x3f, 0x79, 0xeb, 0xad, 0x03, 0x01, 0x02, 0x01}); std::string expected; expected.append(16908547, 'a'); - EXPECT_EQ(decompress_one_shot(input), expected); - EXPECT_EQ(decompress_stream(input), expected); + EXPECT_GZIP_DECOMPRESS(input, expected); } TEST(decompress_many_empty_members_before_data) { @@ -1715,20 +926,7 @@ TEST(decompress_many_empty_members_before_data) { input.insert(input.end(), {0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x01, 0x00, 0xfe, 0xff, 0x61, 0x43, 0xbe, 0xb7, 0xe8, 0x01, 0x00, 0x00, 0x00}); - EXPECT_EQ(decompress_one_shot(input), "a"); - EXPECT_EQ(decompress_stream(input), "a"); -} - -TEST(decompress_ten_thousand_single_byte_members) { - std::vector input; - for (std::size_t index = 0; index < 10000; ++index) { - input.insert(input.end(), {0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xff, 0x01, 0x01, 0x00, 0xfe, 0xff, 0x61, - 0x43, 0xbe, 0xb7, 0xe8, 0x01, 0x00, 0x00, 0x00}); - } - - EXPECT_EQ(decompress_one_shot(input, 1), std::string(10000, 'a')); - EXPECT_EQ(decompress_stream(input), std::string(10000, 'a')); + EXPECT_GZIP_DECOMPRESS(input, "a"); } TEST(decompress_ignores_trailing_zero_bytes) { @@ -1737,129 +935,5 @@ TEST(decompress_ignores_trailing_zero_bytes) { 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; input.insert(input.end(), 1024, 0x00); - EXPECT_EQ(decompress_one_shot(input), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); -} - -TEST(decompress_output_exactly_at_maximum_size) { - const std::vector input{ - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, - 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, - 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input, 0, 11), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); -} - -TEST(decompress_empty_payload_with_zero_maximum_size) { - const std::vector input{ - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x00, - 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input, 0, 0), ""); - EXPECT_EQ(decompress_stream(input), ""); -} - -TEST(decompress_output_hint_larger_than_output) { - const std::vector input{ - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, - 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, - 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input, 1048576), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); -} - -TEST(decompress_output_hint_above_maximum_size_with_fitting_output) { - const std::vector input{ - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, - 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, - 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input, 1048576, 11), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); -} - -TEST(decompress_output_hint_of_one_grows_to_larger_output) { - const std::vector input{ - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x4b, 0x1c, - 0x05, 0xa3, 0x60, 0x14, 0x8c, 0x82, 0x51, 0x30, 0x0a, 0x46, 0xc1, 0x28, - 0x18, 0x05, 0xa3, 0x60, 0x14, 0x8c, 0x82, 0x51, 0x30, 0x0a, 0x46, 0xc1, - 0x28, 0x18, 0x05, 0x00, 0xa1, 0x87, 0xcc, 0x71, 0x23, 0x11, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input, 1), std::string(4387, 'a')); - EXPECT_EQ(decompress_stream(input), std::string(4387, 'a')); -} - -TEST(decompress_with_largest_possible_maximum_size) { - const std::vector input{ - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, - 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, - 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ( - decompress_one_shot(input, 1, std::numeric_limits::max()), - "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); -} - -TEST(decompress_input_larger_than_quarter_of_maximum_size) { - const std::vector input{ - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, - 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, - 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input, 0, 40), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); -} - -TEST(decompress_growth_clamps_to_uneven_maximum_size) { - const std::vector input{ - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, - 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, - 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input, 3, 11), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); -} - -TEST(decompress_crafted_bomb_at_maximum_size) { - const std::vector input{ - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x4b, 0x1c, - 0x05, 0xa3, 0x60, 0x14, 0x8c, 0x82, 0x51, 0x30, 0x0a, 0x46, 0xc1, 0x28, - 0x18, 0x05, 0xa3, 0x60, 0x14, 0x8c, 0x82, 0x51, 0x30, 0x0a, 0x46, 0xc1, - 0x28, 0x18, 0x05, 0x00, 0xa1, 0x87, 0xcc, 0x71, 0x23, 0x11, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input, 0, 4387), std::string(4387, 'a')); - EXPECT_EQ(decompress_stream(input), std::string(4387, 'a')); -} - -TEST(decompress_members_totaling_exactly_maximum_size) { - const std::vector input{ - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, - 0x00, 0xf4, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, - 0x6c, 0x64, 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00, 0x1f, 0x8b, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x0b, 0x00, 0xf4, - 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, - 0x85, 0x11, 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input, 0, 22), "hello worldhello world"); - EXPECT_EQ(decompress_stream(input), "hello worldhello world"); -} - -TEST(decompress_members_growing_from_tiny_output_hint) { - const std::vector input{ - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xff, 0x01, 0x05, - 0x00, 0xfa, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x86, 0xa6, 0x10, 0x36, - 0x05, 0x00, 0x00, 0x00, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0xff, 0x01, 0x06, 0x00, 0xf9, 0xff, 0x20, 0x77, 0x6f, 0x72, 0x6c, - 0x64, 0xcb, 0x42, 0x3b, 0x4a, 0x06, 0x00, 0x00, 0x00}; - EXPECT_EQ(decompress_one_shot(input, 1, 1024), "hello world"); - EXPECT_EQ(decompress_stream(input), "hello world"); -} - -TEST(decompress_large_member_after_small_member) { - // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) - std::mt19937 generator{20}; - std::uniform_int_distribution distribution{0, 255}; - std::string large; - for (std::size_t index = 0; index < 1048576; ++index) { - large.push_back(static_cast(distribution(generator))); - } - - auto input{compress("x", 1)}; - const auto large_compressed{compress(large, 6)}; - input.insert(input.end(), large_compressed.cbegin(), large_compressed.cend()); - EXPECT_EQ(decompress_one_shot(input, 1), "x" + large); - EXPECT_EQ(decompress_stream(input), "x" + large); + EXPECT_GZIP_DECOMPRESS(input, "hello world"); } diff --git a/vendor/libdeflate.mask b/vendor/libdeflate.mask deleted file mode 100644 index 8647a53e8f..0000000000 --- a/vendor/libdeflate.mask +++ /dev/null @@ -1,8 +0,0 @@ -programs/ -scripts/ -.cirrus.yml -CMakeLists.txt -libdeflate-config.cmake.in -libdeflate.pc.in -NEWS.md -README.md diff --git a/vendor/libdeflate/COPYING b/vendor/libdeflate/COPYING deleted file mode 100644 index 9b620b3c19..0000000000 --- a/vendor/libdeflate/COPYING +++ /dev/null @@ -1,22 +0,0 @@ -Copyright 2016 Eric Biggers -Copyright 2024 Google LLC - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation files -(the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of the Software, -and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/libdeflate/common_defs.h b/vendor/libdeflate/common_defs.h deleted file mode 100644 index 80307b90cb..0000000000 --- a/vendor/libdeflate/common_defs.h +++ /dev/null @@ -1,735 +0,0 @@ -/* - * common_defs.h - * - * Copyright 2016 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef COMMON_DEFS_H -#define COMMON_DEFS_H - -#include "libdeflate.h" - -#include -#include /* for size_t */ -#include -#ifdef _MSC_VER -# include /* for _BitScan*() and other intrinsics */ -# include /* for _byteswap_*() */ - /* Disable MSVC warnings that are expected. */ - /* /W2 */ -# pragma warning(disable : 4146) /* unary minus on unsigned type */ - /* /W3 */ -# pragma warning(disable : 4018) /* signed/unsigned mismatch */ -# pragma warning(disable : 4244) /* possible loss of data */ -# pragma warning(disable : 4267) /* possible loss of precision */ -# pragma warning(disable : 4310) /* cast truncates constant value */ - /* /W4 */ -# pragma warning(disable : 4100) /* unreferenced formal parameter */ -# pragma warning(disable : 4127) /* conditional expression is constant */ -# pragma warning(disable : 4189) /* local variable initialized but not referenced */ -# pragma warning(disable : 4232) /* nonstandard extension used */ -# pragma warning(disable : 4245) /* conversion from 'int' to 'unsigned int' */ -# pragma warning(disable : 4295) /* array too small to include terminating null */ -#endif -#ifndef FREESTANDING -# include /* for memcpy() */ -#endif - -/* ========================================================================== */ -/* Target architecture */ -/* ========================================================================== */ - -/* If possible, define a compiler-independent ARCH_* macro. */ -#undef ARCH_X86_64 -#undef ARCH_X86_32 -#undef ARCH_ARM64 -#undef ARCH_ARM32 -#undef ARCH_RISCV -#ifdef _MSC_VER - /* Way too many things are broken in ARM64EC to pretend that it is x86_64. */ -# if defined(_M_X64) && !defined(_M_ARM64EC) -# define ARCH_X86_64 -# elif defined(_M_IX86) -# define ARCH_X86_32 -# elif defined(_M_ARM64) -# define ARCH_ARM64 -# elif defined(_M_ARM) -# define ARCH_ARM32 -# endif -#else -# if defined(__x86_64__) -# define ARCH_X86_64 -# elif defined(__i386__) -# define ARCH_X86_32 -# elif defined(__aarch64__) -# define ARCH_ARM64 -# elif defined(__arm__) -# define ARCH_ARM32 -# elif defined(__riscv) -# define ARCH_RISCV -# endif -#endif - -/* ========================================================================== */ -/* Type definitions */ -/* ========================================================================== */ - -/* Fixed-width integer types */ -typedef uint8_t u8; -typedef uint16_t u16; -typedef uint32_t u32; -typedef uint64_t u64; -typedef int8_t s8; -typedef int16_t s16; -typedef int32_t s32; -typedef int64_t s64; - -/* ssize_t, if not available in */ -#ifdef _MSC_VER -# ifdef _WIN64 - typedef long long ssize_t; -# else - typedef long ssize_t; -# endif -#endif - -/* - * Word type of the target architecture. Use 'size_t' instead of - * 'unsigned long' to account for platforms such as Windows that use 32-bit - * 'unsigned long' on 64-bit architectures. - */ -typedef size_t machine_word_t; - -/* Number of bytes in a word */ -#define WORDBYTES ((int)sizeof(machine_word_t)) - -/* Number of bits in a word */ -#define WORDBITS (8 * WORDBYTES) - -/* ========================================================================== */ -/* Optional compiler features */ -/* ========================================================================== */ - -/* Compiler version checks. Only use when absolutely necessary. */ -#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) -# define GCC_PREREQ(major, minor) \ - (__GNUC__ > (major) || \ - (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor))) -# if !GCC_PREREQ(4, 9) -# error "gcc versions older than 4.9 are no longer supported" -# endif -#else -# define GCC_PREREQ(major, minor) 0 -#endif -#ifdef __clang__ -# ifdef __apple_build_version__ -# define CLANG_PREREQ(major, minor, apple_version) \ - (__apple_build_version__ >= (apple_version)) -# else -# define CLANG_PREREQ(major, minor, apple_version) \ - (__clang_major__ > (major) || \ - (__clang_major__ == (major) && __clang_minor__ >= (minor))) -# endif -# if !CLANG_PREREQ(3, 9, 8000000) -# error "clang versions older than 3.9 are no longer supported" -# endif -#else -# define CLANG_PREREQ(major, minor, apple_version) 0 -#endif -#ifdef _MSC_VER -# define MSVC_PREREQ(version) (_MSC_VER >= (version)) -# if !MSVC_PREREQ(1928) -# error "MSVC versions older than Visual Studio 2019 v16.8 are no longer supported" -# endif -#else -# define MSVC_PREREQ(version) 0 -#endif - -/* - * __has_attribute(attribute) - check whether the compiler supports the given - * attribute (and also supports doing the check in the first place). Mostly - * useful just for clang, since gcc didn't add this macro until gcc 5. - */ -#ifndef __has_attribute -# define __has_attribute(attribute) 0 -#endif - -/* - * __has_builtin(builtin) - check whether the compiler supports the given - * builtin (and also supports doing the check in the first place). Mostly - * useful just for clang, since gcc didn't add this macro until gcc 10. - */ -#ifndef __has_builtin -# define __has_builtin(builtin) 0 -#endif - -/* inline - suggest that a function be inlined */ -#ifdef _MSC_VER -# define inline __inline -#endif /* else assume 'inline' is usable as-is */ - -/* forceinline - force a function to be inlined, if possible */ -#if defined(__GNUC__) || __has_attribute(always_inline) -# define forceinline inline __attribute__((always_inline)) -#elif defined(_MSC_VER) -# define forceinline __forceinline -#else -# define forceinline inline -#endif - -/* MAYBE_UNUSED - mark a function or variable as maybe unused */ -#if defined(__GNUC__) || __has_attribute(unused) -# define MAYBE_UNUSED __attribute__((unused)) -#else -# define MAYBE_UNUSED -#endif - -/* NORETURN - mark a function as never returning, e.g. due to calling abort() */ -#if defined(__GNUC__) || __has_attribute(noreturn) -# define NORETURN __attribute__((noreturn)) -#else -# define NORETURN -#endif - -/* likely(expr) - hint that an expression is usually true */ -#if defined(__GNUC__) || __has_builtin(__builtin_expect) -# define likely(expr) __builtin_expect(!!(expr), 1) -#else -# define likely(expr) (expr) -#endif - -/* unlikely(expr) - hint that an expression is usually false */ -#if defined(__GNUC__) || __has_builtin(__builtin_expect) -# define unlikely(expr) __builtin_expect(!!(expr), 0) -#else -# define unlikely(expr) (expr) -#endif - -/* prefetchr(addr) - prefetch into L1 cache for read */ -#undef prefetchr -#if defined(__GNUC__) || __has_builtin(__builtin_prefetch) -# define prefetchr(addr) __builtin_prefetch((addr), 0) -#elif defined(_MSC_VER) -# if defined(ARCH_X86_32) || defined(ARCH_X86_64) -# define prefetchr(addr) _mm_prefetch((addr), _MM_HINT_T0) -# elif defined(ARCH_ARM64) -# define prefetchr(addr) __prefetch2((addr), 0x00 /* prfop=PLDL1KEEP */) -# elif defined(ARCH_ARM32) -# define prefetchr(addr) __prefetch(addr) -# endif -#endif -#ifndef prefetchr -# define prefetchr(addr) -#endif - -/* prefetchw(addr) - prefetch into L1 cache for write */ -#undef prefetchw -#if defined(__GNUC__) || __has_builtin(__builtin_prefetch) -# define prefetchw(addr) __builtin_prefetch((addr), 1) -#elif defined(_MSC_VER) -# if defined(ARCH_X86_32) || defined(ARCH_X86_64) -# define prefetchw(addr) _m_prefetchw(addr) -# elif defined(ARCH_ARM64) -# define prefetchw(addr) __prefetch2((addr), 0x10 /* prfop=PSTL1KEEP */) -# elif defined(ARCH_ARM32) -# define prefetchw(addr) __prefetchw(addr) -# endif -#endif -#ifndef prefetchw -# define prefetchw(addr) -#endif - -/* - * _aligned_attribute(n) - declare that the annotated variable, or variables of - * the annotated type, must be aligned on n-byte boundaries. - */ -#undef _aligned_attribute -#if defined(__GNUC__) || __has_attribute(aligned) -# define _aligned_attribute(n) __attribute__((aligned(n))) -#elif defined(_MSC_VER) -# define _aligned_attribute(n) __declspec(align(n)) -#endif - -/* - * _target_attribute(attrs) - override the compilation target for a function. - * - * This accepts one or more comma-separated suffixes to the -m prefix jointly - * forming the name of a machine-dependent option. On gcc-like compilers, this - * enables codegen for the given targets, including arbitrary compiler-generated - * code as well as the corresponding intrinsics. On other compilers this macro - * expands to nothing, though MSVC allows intrinsics to be used anywhere anyway. - */ -#if defined(__GNUC__) || __has_attribute(target) -# define _target_attribute(attrs) __attribute__((target(attrs))) -#else -# define _target_attribute(attrs) -#endif - -/* ========================================================================== */ -/* Miscellaneous macros */ -/* ========================================================================== */ - -#define ARRAY_LEN(A) (sizeof(A) / sizeof((A)[0])) -#define MIN(a, b) ((a) <= (b) ? (a) : (b)) -#define MAX(a, b) ((a) >= (b) ? (a) : (b)) -#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) -#define STATIC_ASSERT(expr) ((void)sizeof(char[1 - 2 * !(expr)])) -#define ALIGN(n, a) (((n) + (a) - 1) & ~((a) - 1)) -#define ROUND_UP(n, d) ((d) * DIV_ROUND_UP((n), (d))) - -/* ========================================================================== */ -/* Endianness handling */ -/* ========================================================================== */ - -/* - * CPU_IS_LITTLE_ENDIAN() - 1 if the CPU is little endian, or 0 if it is big - * endian. When possible this is a compile-time macro that can be used in - * preprocessor conditionals. As a fallback, a generic method is used that - * can't be used in preprocessor conditionals but should still be optimized out. - */ -#if defined(__BYTE_ORDER__) /* gcc v4.6+ and clang */ -# define CPU_IS_LITTLE_ENDIAN() (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) -#elif defined(_MSC_VER) -# define CPU_IS_LITTLE_ENDIAN() true -#else -static forceinline bool CPU_IS_LITTLE_ENDIAN(void) -{ - union { - u32 w; - u8 b; - } u; - - u.w = 1; - return u.b; -} -#endif - -/* bswap16(v) - swap the bytes of a 16-bit integer */ -static forceinline u16 bswap16(u16 v) -{ -#if defined(__GNUC__) || __has_builtin(__builtin_bswap16) - return __builtin_bswap16(v); -#elif defined(_MSC_VER) - return _byteswap_ushort(v); -#else - return (v << 8) | (v >> 8); -#endif -} - -/* bswap32(v) - swap the bytes of a 32-bit integer */ -static forceinline u32 bswap32(u32 v) -{ -#if defined(__GNUC__) || __has_builtin(__builtin_bswap32) - return __builtin_bswap32(v); -#elif defined(_MSC_VER) - return _byteswap_ulong(v); -#else - return ((v & 0x000000FF) << 24) | - ((v & 0x0000FF00) << 8) | - ((v & 0x00FF0000) >> 8) | - ((v & 0xFF000000) >> 24); -#endif -} - -/* bswap64(v) - swap the bytes of a 64-bit integer */ -static forceinline u64 bswap64(u64 v) -{ -#if defined(__GNUC__) || __has_builtin(__builtin_bswap64) - return __builtin_bswap64(v); -#elif defined(_MSC_VER) - return _byteswap_uint64(v); -#else - return ((v & 0x00000000000000FF) << 56) | - ((v & 0x000000000000FF00) << 40) | - ((v & 0x0000000000FF0000) << 24) | - ((v & 0x00000000FF000000) << 8) | - ((v & 0x000000FF00000000) >> 8) | - ((v & 0x0000FF0000000000) >> 24) | - ((v & 0x00FF000000000000) >> 40) | - ((v & 0xFF00000000000000) >> 56); -#endif -} - -#define le16_bswap(v) (CPU_IS_LITTLE_ENDIAN() ? (v) : bswap16(v)) -#define le32_bswap(v) (CPU_IS_LITTLE_ENDIAN() ? (v) : bswap32(v)) -#define le64_bswap(v) (CPU_IS_LITTLE_ENDIAN() ? (v) : bswap64(v)) -#define be16_bswap(v) (CPU_IS_LITTLE_ENDIAN() ? bswap16(v) : (v)) -#define be32_bswap(v) (CPU_IS_LITTLE_ENDIAN() ? bswap32(v) : (v)) -#define be64_bswap(v) (CPU_IS_LITTLE_ENDIAN() ? bswap64(v) : (v)) - -/* ========================================================================== */ -/* Unaligned memory accesses */ -/* ========================================================================== */ - -/* - * UNALIGNED_ACCESS_IS_FAST() - 1 if unaligned memory accesses can be performed - * efficiently on the target platform, otherwise 0. - */ -#if (defined(__GNUC__) || defined(__clang__)) && \ - (defined(ARCH_X86_64) || defined(ARCH_X86_32) || \ - defined(__ARM_FEATURE_UNALIGNED) || \ - defined(__powerpc64__) || defined(__powerpc__) || defined(__POWERPC__) || \ - defined(__riscv_misaligned_fast) || \ - /* - * For all compilation purposes, WebAssembly behaves like any other CPU - * instruction set. Even though WebAssembly engine might be running on - * top of different actual CPU architectures, the WebAssembly spec - * itself permits unaligned access and it will be fast on most of those - * platforms, and simulated at the engine level on others, so it's - * worth treating it as a CPU architecture with fast unaligned access. - */ defined(__wasm__)) -# define UNALIGNED_ACCESS_IS_FAST 1 -#elif defined(_MSC_VER) -# define UNALIGNED_ACCESS_IS_FAST 1 -#else -# define UNALIGNED_ACCESS_IS_FAST 0 -#endif - -/* - * Implementing unaligned memory accesses using memcpy() is portable, and it - * usually gets optimized appropriately by modern compilers. I.e., each - * memcpy() of 1, 2, 4, or WORDBYTES bytes gets compiled to a load or store - * instruction, not to an actual function call. - * - * We no longer use the "packed struct" approach to unaligned accesses, as that - * is nonstandard, has unclear semantics, and doesn't receive enough testing - * (see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94994). - * - * arm32 with __ARM_FEATURE_UNALIGNED in gcc 5 and earlier is a known exception - * where memcpy() generates inefficient code - * (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67366). However, we no longer - * consider that one case important enough to maintain different code for. - * If you run into it, please just use a newer version of gcc (or use clang). - */ - -#ifdef FREESTANDING -# define MEMCOPY __builtin_memcpy -#else -# define MEMCOPY memcpy -#endif - -/* Unaligned loads and stores without endianness conversion */ - -#define DEFINE_UNALIGNED_TYPE(type) \ -static forceinline type \ -load_##type##_unaligned(const void *p) \ -{ \ - type v; \ - \ - MEMCOPY(&v, p, sizeof(v)); \ - return v; \ -} \ - \ -static forceinline void \ -store_##type##_unaligned(type v, void *p) \ -{ \ - MEMCOPY(p, &v, sizeof(v)); \ -} - -DEFINE_UNALIGNED_TYPE(u16) -DEFINE_UNALIGNED_TYPE(u32) -DEFINE_UNALIGNED_TYPE(u64) -DEFINE_UNALIGNED_TYPE(machine_word_t) - -#undef MEMCOPY - -#define load_word_unaligned load_machine_word_t_unaligned -#define store_word_unaligned store_machine_word_t_unaligned - -/* Unaligned loads with endianness conversion */ - -static forceinline u16 -get_unaligned_le16(const u8 *p) -{ - if (UNALIGNED_ACCESS_IS_FAST) - return le16_bswap(load_u16_unaligned(p)); - else - return ((u16)p[1] << 8) | p[0]; -} - -static forceinline u16 -get_unaligned_be16(const u8 *p) -{ - if (UNALIGNED_ACCESS_IS_FAST) - return be16_bswap(load_u16_unaligned(p)); - else - return ((u16)p[0] << 8) | p[1]; -} - -static forceinline u32 -get_unaligned_le32(const u8 *p) -{ - if (UNALIGNED_ACCESS_IS_FAST) - return le32_bswap(load_u32_unaligned(p)); - else - return ((u32)p[3] << 24) | ((u32)p[2] << 16) | - ((u32)p[1] << 8) | p[0]; -} - -static forceinline u32 -get_unaligned_be32(const u8 *p) -{ - if (UNALIGNED_ACCESS_IS_FAST) - return be32_bswap(load_u32_unaligned(p)); - else - return ((u32)p[0] << 24) | ((u32)p[1] << 16) | - ((u32)p[2] << 8) | p[3]; -} - -static forceinline u64 -get_unaligned_le64(const u8 *p) -{ - if (UNALIGNED_ACCESS_IS_FAST) - return le64_bswap(load_u64_unaligned(p)); - else - return ((u64)p[7] << 56) | ((u64)p[6] << 48) | - ((u64)p[5] << 40) | ((u64)p[4] << 32) | - ((u64)p[3] << 24) | ((u64)p[2] << 16) | - ((u64)p[1] << 8) | p[0]; -} - -static forceinline machine_word_t -get_unaligned_leword(const u8 *p) -{ - STATIC_ASSERT(WORDBITS == 32 || WORDBITS == 64); - if (WORDBITS == 32) - return get_unaligned_le32(p); - else - return get_unaligned_le64(p); -} - -/* Unaligned stores with endianness conversion */ - -static forceinline void -put_unaligned_le16(u16 v, u8 *p) -{ - if (UNALIGNED_ACCESS_IS_FAST) { - store_u16_unaligned(le16_bswap(v), p); - } else { - p[0] = (u8)(v >> 0); - p[1] = (u8)(v >> 8); - } -} - -static forceinline void -put_unaligned_be16(u16 v, u8 *p) -{ - if (UNALIGNED_ACCESS_IS_FAST) { - store_u16_unaligned(be16_bswap(v), p); - } else { - p[0] = (u8)(v >> 8); - p[1] = (u8)(v >> 0); - } -} - -static forceinline void -put_unaligned_le32(u32 v, u8 *p) -{ - if (UNALIGNED_ACCESS_IS_FAST) { - store_u32_unaligned(le32_bswap(v), p); - } else { - p[0] = (u8)(v >> 0); - p[1] = (u8)(v >> 8); - p[2] = (u8)(v >> 16); - p[3] = (u8)(v >> 24); - } -} - -static forceinline void -put_unaligned_be32(u32 v, u8 *p) -{ - if (UNALIGNED_ACCESS_IS_FAST) { - store_u32_unaligned(be32_bswap(v), p); - } else { - p[0] = (u8)(v >> 24); - p[1] = (u8)(v >> 16); - p[2] = (u8)(v >> 8); - p[3] = (u8)(v >> 0); - } -} - -static forceinline void -put_unaligned_le64(u64 v, u8 *p) -{ - if (UNALIGNED_ACCESS_IS_FAST) { - store_u64_unaligned(le64_bswap(v), p); - } else { - p[0] = (u8)(v >> 0); - p[1] = (u8)(v >> 8); - p[2] = (u8)(v >> 16); - p[3] = (u8)(v >> 24); - p[4] = (u8)(v >> 32); - p[5] = (u8)(v >> 40); - p[6] = (u8)(v >> 48); - p[7] = (u8)(v >> 56); - } -} - -static forceinline void -put_unaligned_leword(machine_word_t v, u8 *p) -{ - STATIC_ASSERT(WORDBITS == 32 || WORDBITS == 64); - if (WORDBITS == 32) - put_unaligned_le32(v, p); - else - put_unaligned_le64(v, p); -} - -/* ========================================================================== */ -/* Bit manipulation functions */ -/* ========================================================================== */ - -/* - * Bit Scan Reverse (BSR) - find the 0-based index (relative to the least - * significant end) of the *most* significant 1 bit in the input value. The - * input value must be nonzero! - */ - -static forceinline unsigned -bsr32(u32 v) -{ -#if defined(__GNUC__) || __has_builtin(__builtin_clz) - return 31 - __builtin_clz(v); -#elif defined(_MSC_VER) - unsigned long i; - - _BitScanReverse(&i, v); - return i; -#else - unsigned i = 0; - - while ((v >>= 1) != 0) - i++; - return i; -#endif -} - -static forceinline unsigned -bsr64(u64 v) -{ -#if defined(__GNUC__) || __has_builtin(__builtin_clzll) - return 63 - __builtin_clzll(v); -#elif defined(_MSC_VER) && defined(_WIN64) - unsigned long i; - - _BitScanReverse64(&i, v); - return i; -#else - unsigned i = 0; - - while ((v >>= 1) != 0) - i++; - return i; -#endif -} - -static forceinline unsigned -bsrw(machine_word_t v) -{ - STATIC_ASSERT(WORDBITS == 32 || WORDBITS == 64); - if (WORDBITS == 32) - return bsr32(v); - else - return bsr64(v); -} - -/* - * Bit Scan Forward (BSF) - find the 0-based index (relative to the least - * significant end) of the *least* significant 1 bit in the input value. The - * input value must be nonzero! - */ - -static forceinline unsigned -bsf32(u32 v) -{ -#if defined(__GNUC__) || __has_builtin(__builtin_ctz) - return __builtin_ctz(v); -#elif defined(_MSC_VER) - unsigned long i; - - _BitScanForward(&i, v); - return i; -#else - unsigned i = 0; - - for (; (v & 1) == 0; v >>= 1) - i++; - return i; -#endif -} - -static forceinline unsigned -bsf64(u64 v) -{ -#if defined(__GNUC__) || __has_builtin(__builtin_ctzll) - return __builtin_ctzll(v); -#elif defined(_MSC_VER) && defined(_WIN64) - unsigned long i; - - _BitScanForward64(&i, v); - return i; -#else - unsigned i = 0; - - for (; (v & 1) == 0; v >>= 1) - i++; - return i; -#endif -} - -static forceinline unsigned -bsfw(machine_word_t v) -{ - STATIC_ASSERT(WORDBITS == 32 || WORDBITS == 64); - if (WORDBITS == 32) - return bsf32(v); - else - return bsf64(v); -} - -/* - * rbit32(v): reverse the bits in a 32-bit integer. This doesn't have a - * fallback implementation; use '#ifdef rbit32' to check if this is available. - */ -#undef rbit32 -#if (defined(__GNUC__) || defined(__clang__)) && defined(ARCH_ARM32) && \ - (__ARM_ARCH >= 7 || (__ARM_ARCH == 6 && defined(__ARM_ARCH_6T2__))) -static forceinline u32 -rbit32(u32 v) -{ - __asm__("rbit %0, %1" : "=r" (v) : "r" (v)); - return v; -} -#define rbit32 rbit32 -#elif (defined(__GNUC__) || defined(__clang__)) && defined(ARCH_ARM64) -static forceinline u32 -rbit32(u32 v) -{ - __asm__("rbit %w0, %w1" : "=r" (v) : "r" (v)); - return v; -} -#define rbit32 rbit32 -#endif - -#endif /* COMMON_DEFS_H */ diff --git a/vendor/libdeflate/lib/adler32.c b/vendor/libdeflate/lib/adler32.c deleted file mode 100644 index d5f39d8f5a..0000000000 --- a/vendor/libdeflate/lib/adler32.c +++ /dev/null @@ -1,162 +0,0 @@ -/* - * adler32.c - Adler-32 checksum algorithm - * - * Copyright 2016 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "lib_common.h" - -/* The Adler-32 divisor, or "base", value */ -#define DIVISOR 65521 - -/* - * MAX_CHUNK_LEN is the most bytes that can be processed without the possibility - * of s2 overflowing when it is represented as an unsigned 32-bit integer. This - * value was computed using the following Python script: - * - * divisor = 65521 - * count = 0 - * s1 = divisor - 1 - * s2 = divisor - 1 - * while True: - * s1 += 0xFF - * s2 += s1 - * if s2 > 0xFFFFFFFF: - * break - * count += 1 - * print(count) - * - * Note that to get the correct worst-case value, we must assume that every byte - * has value 0xFF and that s1 and s2 started with the highest possible values - * modulo the divisor. - */ -#define MAX_CHUNK_LEN 5552 - -/* - * Update the Adler-32 values s1 and s2 using n bytes from p, update p to p + n, - * update n to 0, and reduce s1 and s2 mod DIVISOR. It is assumed that neither - * s1 nor s2 can overflow before the reduction at the end, i.e. n plus any bytes - * already processed after the last reduction must not exceed MAX_CHUNK_LEN. - * - * This uses only portable C code. This is used as a fallback when a vectorized - * implementation of Adler-32 (e.g. AVX2) is unavailable on the platform. - * - * Some of the vectorized implementations also use this to handle the end of the - * data when the data isn't evenly divisible by the length the vectorized code - * works on. To avoid compiler errors about target-specific option mismatches - * when this is used in that way, this is a macro rather than a function. - * - * Although this is unvectorized, this does include an optimization where the - * main loop processes four bytes at a time using a strategy similar to that - * used by vectorized implementations. This provides increased instruction- - * level parallelism compared to the traditional 's1 += *p++; s2 += s1;'. - */ -#define ADLER32_CHUNK(s1, s2, p, n) \ -do { \ - if (n >= 4) { \ - u32 s1_sum = 0; \ - u32 byte_0_sum = 0; \ - u32 byte_1_sum = 0; \ - u32 byte_2_sum = 0; \ - u32 byte_3_sum = 0; \ - \ - do { \ - s1_sum += s1; \ - s1 += p[0] + p[1] + p[2] + p[3]; \ - byte_0_sum += p[0]; \ - byte_1_sum += p[1]; \ - byte_2_sum += p[2]; \ - byte_3_sum += p[3]; \ - p += 4; \ - n -= 4; \ - } while (n >= 4); \ - s2 += (4 * (s1_sum + byte_0_sum)) + (3 * byte_1_sum) + \ - (2 * byte_2_sum) + byte_3_sum; \ - } \ - for (; n; n--, p++) { \ - s1 += *p; \ - s2 += s1; \ - } \ - s1 %= DIVISOR; \ - s2 %= DIVISOR; \ -} while (0) - -static u32 MAYBE_UNUSED -adler32_generic(u32 adler, const u8 *p, size_t len) -{ - u32 s1 = adler & 0xFFFF; - u32 s2 = adler >> 16; - - while (len) { - size_t n = MIN(len, MAX_CHUNK_LEN & ~3); - - len -= n; - ADLER32_CHUNK(s1, s2, p, n); - } - - return (s2 << 16) | s1; -} - -/* Include architecture-specific implementation(s) if available. */ -#undef DEFAULT_IMPL -#undef arch_select_adler32_func -typedef u32 (*adler32_func_t)(u32 adler, const u8 *p, size_t len); -#if defined(ARCH_ARM32) || defined(ARCH_ARM64) -# include "arm/adler32_impl.h" -#elif defined(ARCH_X86_32) || defined(ARCH_X86_64) -# include "x86/adler32_impl.h" -#endif - -#ifndef DEFAULT_IMPL -# define DEFAULT_IMPL adler32_generic -#endif - -#ifdef arch_select_adler32_func -static u32 dispatch_adler32(u32 adler, const u8 *p, size_t len); - -static volatile adler32_func_t adler32_impl = dispatch_adler32; - -/* Choose the best implementation at runtime. */ -static u32 dispatch_adler32(u32 adler, const u8 *p, size_t len) -{ - adler32_func_t f = arch_select_adler32_func(); - - if (f == NULL) - f = DEFAULT_IMPL; - - adler32_impl = f; - return f(adler, p, len); -} -#else -/* The best implementation is statically known, so call it directly. */ -#define adler32_impl DEFAULT_IMPL -#endif - -LIBDEFLATEAPI u32 -libdeflate_adler32(u32 adler, const void *buffer, size_t len) -{ - if (buffer == NULL) /* Return initial value. */ - return 1; - return adler32_impl(adler, buffer, len); -} diff --git a/vendor/libdeflate/lib/arm/adler32_impl.h b/vendor/libdeflate/lib/arm/adler32_impl.h deleted file mode 100644 index 66d2f2bf29..0000000000 --- a/vendor/libdeflate/lib/arm/adler32_impl.h +++ /dev/null @@ -1,365 +0,0 @@ -/* - * arm/adler32_impl.h - ARM implementations of Adler-32 checksum algorithm - * - * Copyright 2016 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef LIB_ARM_ADLER32_IMPL_H -#define LIB_ARM_ADLER32_IMPL_H - -#include "cpu_features.h" - -/* Regular NEON implementation */ -#if HAVE_NEON_INTRIN && CPU_IS_LITTLE_ENDIAN() -# define adler32_arm_neon adler32_arm_neon -# if HAVE_NEON_NATIVE - /* - * Use no attributes if none are needed, to support old versions of clang - * that don't accept the simd target attribute. - */ -# define ATTRIBUTES -# elif defined(ARCH_ARM32) -# define ATTRIBUTES _target_attribute("fpu=neon") -# elif defined(__clang__) -# define ATTRIBUTES _target_attribute("simd") -# else -# define ATTRIBUTES _target_attribute("+simd") -# endif -static ATTRIBUTES MAYBE_UNUSED u32 -adler32_arm_neon(u32 adler, const u8 *p, size_t len) -{ - static const u16 _aligned_attribute(16) mults[64] = { - 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, - 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, - 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, - 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, - }; - const uint16x8_t mults_a = vld1q_u16(&mults[0]); - const uint16x8_t mults_b = vld1q_u16(&mults[8]); - const uint16x8_t mults_c = vld1q_u16(&mults[16]); - const uint16x8_t mults_d = vld1q_u16(&mults[24]); - const uint16x8_t mults_e = vld1q_u16(&mults[32]); - const uint16x8_t mults_f = vld1q_u16(&mults[40]); - const uint16x8_t mults_g = vld1q_u16(&mults[48]); - const uint16x8_t mults_h = vld1q_u16(&mults[56]); - u32 s1 = adler & 0xFFFF; - u32 s2 = adler >> 16; - - /* - * If the length is large and the pointer is misaligned, align it. - * For smaller lengths, just take the misaligned load penalty. - */ - if (unlikely(len > 32768 && ((uintptr_t)p & 15))) { - do { - s1 += *p++; - s2 += s1; - len--; - } while ((uintptr_t)p & 15); - s1 %= DIVISOR; - s2 %= DIVISOR; - } - - while (len) { - /* - * Calculate the length of the next data chunk such that s1 and - * s2 are guaranteed to not exceed UINT32_MAX. - */ - size_t n = MIN(len, MAX_CHUNK_LEN & ~63); - - len -= n; - - if (n >= 64) { - uint32x4_t v_s1 = vdupq_n_u32(0); - uint32x4_t v_s2 = vdupq_n_u32(0); - /* - * v_byte_sums_* contain the sum of the bytes at index i - * across all 64-byte segments, for each index 0..63. - */ - uint16x8_t v_byte_sums_a = vdupq_n_u16(0); - uint16x8_t v_byte_sums_b = vdupq_n_u16(0); - uint16x8_t v_byte_sums_c = vdupq_n_u16(0); - uint16x8_t v_byte_sums_d = vdupq_n_u16(0); - uint16x8_t v_byte_sums_e = vdupq_n_u16(0); - uint16x8_t v_byte_sums_f = vdupq_n_u16(0); - uint16x8_t v_byte_sums_g = vdupq_n_u16(0); - uint16x8_t v_byte_sums_h = vdupq_n_u16(0); - - s2 += s1 * (n & ~63); - - do { - /* Load the next 64 data bytes. */ - const uint8x16_t data_a = vld1q_u8(p + 0); - const uint8x16_t data_b = vld1q_u8(p + 16); - const uint8x16_t data_c = vld1q_u8(p + 32); - const uint8x16_t data_d = vld1q_u8(p + 48); - uint16x8_t tmp; - - /* - * Accumulate the previous s1 counters into the - * s2 counters. The needed multiplication by 64 - * is delayed to later. - */ - v_s2 = vaddq_u32(v_s2, v_s1); - - /* - * Add the 64 data bytes to their v_byte_sums - * counters, while also accumulating the sums of - * each adjacent set of 4 bytes into v_s1. - */ - tmp = vpaddlq_u8(data_a); - v_byte_sums_a = vaddw_u8(v_byte_sums_a, - vget_low_u8(data_a)); - v_byte_sums_b = vaddw_u8(v_byte_sums_b, - vget_high_u8(data_a)); - tmp = vpadalq_u8(tmp, data_b); - v_byte_sums_c = vaddw_u8(v_byte_sums_c, - vget_low_u8(data_b)); - v_byte_sums_d = vaddw_u8(v_byte_sums_d, - vget_high_u8(data_b)); - tmp = vpadalq_u8(tmp, data_c); - v_byte_sums_e = vaddw_u8(v_byte_sums_e, - vget_low_u8(data_c)); - v_byte_sums_f = vaddw_u8(v_byte_sums_f, - vget_high_u8(data_c)); - tmp = vpadalq_u8(tmp, data_d); - v_byte_sums_g = vaddw_u8(v_byte_sums_g, - vget_low_u8(data_d)); - v_byte_sums_h = vaddw_u8(v_byte_sums_h, - vget_high_u8(data_d)); - v_s1 = vpadalq_u16(v_s1, tmp); - - p += 64; - n -= 64; - } while (n >= 64); - - /* s2 = 64*s2 + (64*bytesum0 + 63*bytesum1 + ... + 1*bytesum63) */ - #ifdef ARCH_ARM32 - # define umlal2(a, b, c) vmlal_u16((a), vget_high_u16(b), vget_high_u16(c)) - #else - # define umlal2 vmlal_high_u16 - #endif - v_s2 = vqshlq_n_u32(v_s2, 6); - v_s2 = vmlal_u16(v_s2, vget_low_u16(v_byte_sums_a), - vget_low_u16(mults_a)); - v_s2 = umlal2(v_s2, v_byte_sums_a, mults_a); - v_s2 = vmlal_u16(v_s2, vget_low_u16(v_byte_sums_b), - vget_low_u16(mults_b)); - v_s2 = umlal2(v_s2, v_byte_sums_b, mults_b); - v_s2 = vmlal_u16(v_s2, vget_low_u16(v_byte_sums_c), - vget_low_u16(mults_c)); - v_s2 = umlal2(v_s2, v_byte_sums_c, mults_c); - v_s2 = vmlal_u16(v_s2, vget_low_u16(v_byte_sums_d), - vget_low_u16(mults_d)); - v_s2 = umlal2(v_s2, v_byte_sums_d, mults_d); - v_s2 = vmlal_u16(v_s2, vget_low_u16(v_byte_sums_e), - vget_low_u16(mults_e)); - v_s2 = umlal2(v_s2, v_byte_sums_e, mults_e); - v_s2 = vmlal_u16(v_s2, vget_low_u16(v_byte_sums_f), - vget_low_u16(mults_f)); - v_s2 = umlal2(v_s2, v_byte_sums_f, mults_f); - v_s2 = vmlal_u16(v_s2, vget_low_u16(v_byte_sums_g), - vget_low_u16(mults_g)); - v_s2 = umlal2(v_s2, v_byte_sums_g, mults_g); - v_s2 = vmlal_u16(v_s2, vget_low_u16(v_byte_sums_h), - vget_low_u16(mults_h)); - v_s2 = umlal2(v_s2, v_byte_sums_h, mults_h); - #undef umlal2 - - /* Horizontal sum to finish up */ - #ifdef ARCH_ARM32 - s1 += vgetq_lane_u32(v_s1, 0) + vgetq_lane_u32(v_s1, 1) + - vgetq_lane_u32(v_s1, 2) + vgetq_lane_u32(v_s1, 3); - s2 += vgetq_lane_u32(v_s2, 0) + vgetq_lane_u32(v_s2, 1) + - vgetq_lane_u32(v_s2, 2) + vgetq_lane_u32(v_s2, 3); - #else - s1 += vaddvq_u32(v_s1); - s2 += vaddvq_u32(v_s2); - #endif - } - /* - * Process the last 0 <= n < 64 bytes of the chunk using - * scalar instructions and reduce s1 and s2 mod DIVISOR. - */ - ADLER32_CHUNK(s1, s2, p, n); - } - return (s2 << 16) | s1; -} -#undef ATTRIBUTES -#endif /* Regular NEON implementation */ - -/* NEON+dotprod implementation */ -#if HAVE_DOTPROD_INTRIN && CPU_IS_LITTLE_ENDIAN() && \ - !defined(LIBDEFLATE_ASSEMBLER_DOES_NOT_SUPPORT_DOTPROD) -# define adler32_arm_neon_dotprod adler32_arm_neon_dotprod -# ifdef __clang__ -# define ATTRIBUTES _target_attribute("dotprod") - /* - * Both gcc and binutils originally considered dotprod to depend on - * arch=armv8.2-a or later. This was fixed in gcc 13.2 by commit - * 9aac37ab8a7b ("aarch64: Remove architecture dependencies from intrinsics") - * and in binutils 2.41 by commit 205e4380c800 ("aarch64: Remove version - * dependencies from features"). Unfortunately, always using arch=armv8.2-a - * causes build errors with some compiler options because it may reduce the - * arch rather than increase it. Therefore we try to omit the arch whenever - * possible. If gcc is 14 or later, then both gcc and binutils are probably - * fixed, so we omit the arch. We also omit the arch if a feature that - * depends on armv8.2-a or later (in gcc 13.1 and earlier) is present. - */ -# elif GCC_PREREQ(14, 0) || defined(__ARM_FEATURE_JCVT) \ - || defined(__ARM_FEATURE_DOTPROD) -# define ATTRIBUTES _target_attribute("+dotprod") -# else -# define ATTRIBUTES _target_attribute("arch=armv8.2-a+dotprod") -# endif -static ATTRIBUTES u32 -adler32_arm_neon_dotprod(u32 adler, const u8 *p, size_t len) -{ - static const u8 _aligned_attribute(16) mults[64] = { - 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, - 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, - 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, - 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, - }; - const uint8x16_t mults_a = vld1q_u8(&mults[0]); - const uint8x16_t mults_b = vld1q_u8(&mults[16]); - const uint8x16_t mults_c = vld1q_u8(&mults[32]); - const uint8x16_t mults_d = vld1q_u8(&mults[48]); - const uint8x16_t ones = vdupq_n_u8(1); - u32 s1 = adler & 0xFFFF; - u32 s2 = adler >> 16; - - /* - * If the length is large and the pointer is misaligned, align it. - * For smaller lengths, just take the misaligned load penalty. - */ - if (unlikely(len > 32768 && ((uintptr_t)p & 15))) { - do { - s1 += *p++; - s2 += s1; - len--; - } while ((uintptr_t)p & 15); - s1 %= DIVISOR; - s2 %= DIVISOR; - } - - while (len) { - /* - * Calculate the length of the next data chunk such that s1 and - * s2 are guaranteed to not exceed UINT32_MAX. - */ - size_t n = MIN(len, MAX_CHUNK_LEN & ~63); - - len -= n; - - if (n >= 64) { - uint32x4_t v_s1_a = vdupq_n_u32(0); - uint32x4_t v_s1_b = vdupq_n_u32(0); - uint32x4_t v_s1_c = vdupq_n_u32(0); - uint32x4_t v_s1_d = vdupq_n_u32(0); - uint32x4_t v_s2_a = vdupq_n_u32(0); - uint32x4_t v_s2_b = vdupq_n_u32(0); - uint32x4_t v_s2_c = vdupq_n_u32(0); - uint32x4_t v_s2_d = vdupq_n_u32(0); - uint32x4_t v_s1_sums_a = vdupq_n_u32(0); - uint32x4_t v_s1_sums_b = vdupq_n_u32(0); - uint32x4_t v_s1_sums_c = vdupq_n_u32(0); - uint32x4_t v_s1_sums_d = vdupq_n_u32(0); - uint32x4_t v_s1; - uint32x4_t v_s2; - uint32x4_t v_s1_sums; - - s2 += s1 * (n & ~63); - - do { - uint8x16_t data_a = vld1q_u8(p + 0); - uint8x16_t data_b = vld1q_u8(p + 16); - uint8x16_t data_c = vld1q_u8(p + 32); - uint8x16_t data_d = vld1q_u8(p + 48); - - v_s1_sums_a = vaddq_u32(v_s1_sums_a, v_s1_a); - v_s1_a = vdotq_u32(v_s1_a, data_a, ones); - v_s2_a = vdotq_u32(v_s2_a, data_a, mults_a); - - v_s1_sums_b = vaddq_u32(v_s1_sums_b, v_s1_b); - v_s1_b = vdotq_u32(v_s1_b, data_b, ones); - v_s2_b = vdotq_u32(v_s2_b, data_b, mults_b); - - v_s1_sums_c = vaddq_u32(v_s1_sums_c, v_s1_c); - v_s1_c = vdotq_u32(v_s1_c, data_c, ones); - v_s2_c = vdotq_u32(v_s2_c, data_c, mults_c); - - v_s1_sums_d = vaddq_u32(v_s1_sums_d, v_s1_d); - v_s1_d = vdotq_u32(v_s1_d, data_d, ones); - v_s2_d = vdotq_u32(v_s2_d, data_d, mults_d); - - p += 64; - n -= 64; - } while (n >= 64); - - v_s1 = vaddq_u32(vaddq_u32(v_s1_a, v_s1_b), - vaddq_u32(v_s1_c, v_s1_d)); - v_s2 = vaddq_u32(vaddq_u32(v_s2_a, v_s2_b), - vaddq_u32(v_s2_c, v_s2_d)); - v_s1_sums = vaddq_u32(vaddq_u32(v_s1_sums_a, - v_s1_sums_b), - vaddq_u32(v_s1_sums_c, - v_s1_sums_d)); - v_s2 = vaddq_u32(v_s2, vqshlq_n_u32(v_s1_sums, 6)); - - s1 += vaddvq_u32(v_s1); - s2 += vaddvq_u32(v_s2); - } - /* - * Process the last 0 <= n < 64 bytes of the chunk using - * scalar instructions and reduce s1 and s2 mod DIVISOR. - */ - ADLER32_CHUNK(s1, s2, p, n); - } - return (s2 << 16) | s1; -} -#undef ATTRIBUTES -#endif /* NEON+dotprod implementation */ - -#if defined(adler32_arm_neon_dotprod) && defined(__ARM_FEATURE_DOTPROD) -#define DEFAULT_IMPL adler32_arm_neon_dotprod -#else -static inline adler32_func_t -arch_select_adler32_func(void) -{ - const u32 features MAYBE_UNUSED = get_arm_cpu_features(); - -#ifdef adler32_arm_neon_dotprod - if (HAVE_NEON(features) && HAVE_DOTPROD(features)) - return adler32_arm_neon_dotprod; -#endif -#ifdef adler32_arm_neon - if (HAVE_NEON(features)) - return adler32_arm_neon; -#endif - return NULL; -} -#define arch_select_adler32_func arch_select_adler32_func -#endif - -#endif /* LIB_ARM_ADLER32_IMPL_H */ diff --git a/vendor/libdeflate/lib/arm/cpu_features.c b/vendor/libdeflate/lib/arm/cpu_features.c deleted file mode 100644 index a4008ca948..0000000000 --- a/vendor/libdeflate/lib/arm/cpu_features.c +++ /dev/null @@ -1,292 +0,0 @@ -/* - * arm/cpu_features.c - feature detection for ARM CPUs - * - * Copyright 2018 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * ARM CPUs don't have a standard way for unprivileged programs to detect CPU - * features. But an OS-specific way can be used when available. - */ - -#ifdef __APPLE__ -# undef _ANSI_SOURCE -# undef _DARWIN_C_SOURCE -# define _DARWIN_C_SOURCE /* for sysctlbyname() */ -#endif - -#include "../cpu_features_common.h" /* must be included first */ -#include "cpu_features.h" - -#ifdef ARM_CPU_FEATURES_KNOWN -/* Runtime ARM CPU feature detection is supported. */ - -#ifdef __linux__ -/* - * On Linux, arm32 and arm64 CPU features can be detected by reading the - * AT_HWCAP and AT_HWCAP2 values from /proc/self/auxv. - * - * Ideally we'd use the C library function getauxval(), but it's not guaranteed - * to be available: it was only added to glibc in 2.16, and in Android it was - * added to API level 18 for arm32 and level 21 for arm64. - */ - -#include -#include -#include -#include -#include - -#define AT_HWCAP 16 -#define AT_HWCAP2 26 - -static void scan_auxv(unsigned long *hwcap, unsigned long *hwcap2) -{ - int fd; - unsigned long auxbuf[32]; - int filled = 0; - int i; - - fd = open("/proc/self/auxv", O_RDONLY | O_CLOEXEC); - if (fd < 0) - return; - - for (;;) { - do { - int ret = read(fd, &((char *)auxbuf)[filled], - sizeof(auxbuf) - filled); - if (ret <= 0) { - if (ret < 0 && errno == EINTR) - continue; - goto out; - } - filled += ret; - } while (filled < 2 * sizeof(long)); - - i = 0; - do { - unsigned long type = auxbuf[i]; - unsigned long value = auxbuf[i + 1]; - - if (type == AT_HWCAP) - *hwcap = value; - else if (type == AT_HWCAP2) - *hwcap2 = value; - i += 2; - filled -= 2 * sizeof(long); - } while (filled >= 2 * sizeof(long)); - - memmove(auxbuf, &auxbuf[i], filled); - } -out: - close(fd); -} - -static u32 query_arm_cpu_features(void) -{ - u32 features = 0; - unsigned long hwcap = 0; - unsigned long hwcap2 = 0; - - scan_auxv(&hwcap, &hwcap2); - -#ifdef ARCH_ARM32 - STATIC_ASSERT(sizeof(long) == 4); - if (hwcap & (1 << 12)) /* HWCAP_NEON */ - features |= ARM_CPU_FEATURE_NEON; -#else - STATIC_ASSERT(sizeof(long) == 8); - if (hwcap & (1 << 1)) /* HWCAP_ASIMD */ - features |= ARM_CPU_FEATURE_NEON; - if (hwcap & (1 << 4)) /* HWCAP_PMULL */ - features |= ARM_CPU_FEATURE_PMULL; - if (hwcap & (1 << 7)) /* HWCAP_CRC32 */ - features |= ARM_CPU_FEATURE_CRC32; - if (hwcap & (1 << 17)) /* HWCAP_SHA3 */ - features |= ARM_CPU_FEATURE_SHA3; - if (hwcap & (1 << 20)) /* HWCAP_ASIMDDP */ - features |= ARM_CPU_FEATURE_DOTPROD; -#endif - return features; -} - -#ifdef ARCH_ARM64 -/* - * Return whether cpu0's MIDR_EL1 identifies one of the Arm Neoverse - * V-class server cores (V1 / V2 / V3 / V3AE). MIDR_EL1 is exposed - * unprivileged via sysfs (added in Linux 4.7). Reading cpu0 only is fine - * in practice: no Neoverse V-class server SKU has shipped as part of a - * big.LITTLE cluster. Any failure (file missing, read error, parse - * failure, unrecognized CPU) returns false. - */ -static bool arm64_cpu_is_neoverse_v_class(void) -{ - int fd; - char buf[32]; - ssize_t n; - unsigned long midr; - u32 part; - - fd = open("/sys/devices/system/cpu/cpu0/regs/identification/midr_el1", - O_RDONLY | O_CLOEXEC); - if (fd < 0) - return false; - do { - n = read(fd, buf, sizeof(buf) - 1); - } while (n < 0 && errno == EINTR); - close(fd); - if (n <= 0) - return false; - buf[n] = '\0'; - midr = strtoul(buf, NULL, 0); /* sysfs prints "0x%016llx\n" */ - - /* MIDR_EL1: [31:24]=Implementer, [15:4]=PartNum. */ - if (((midr >> 24) & 0xff) != 0x41) /* Implementer must be Arm Ltd. */ - return false; - part = (midr >> 4) & 0xfff; - switch (part) { - case 0xd40: /* Neoverse V1 (e.g. AWS Graviton 3) */ - case 0xd4f: /* Neoverse V2 (e.g. AWS Graviton 4) */ - case 0xd83: /* Neoverse V3AE */ - case 0xd84: /* Neoverse V3 */ - return true; - } - return false; -} -#endif /* ARCH_ARM64 */ - -#elif defined(__APPLE__) -/* On Apple platforms, arm64 CPU features can be detected via sysctlbyname(). */ - -#include -#include -#include - -static const struct { - const char *name; - u32 feature; -} feature_sysctls[] = { - { "hw.optional.neon", ARM_CPU_FEATURE_NEON }, - { "hw.optional.AdvSIMD", ARM_CPU_FEATURE_NEON }, - { "hw.optional.arm.FEAT_PMULL", ARM_CPU_FEATURE_PMULL }, - { "hw.optional.armv8_crc32", ARM_CPU_FEATURE_CRC32 }, - { "hw.optional.armv8_2_sha3", ARM_CPU_FEATURE_SHA3 }, - { "hw.optional.arm.FEAT_SHA3", ARM_CPU_FEATURE_SHA3 }, - { "hw.optional.arm.FEAT_DotProd", ARM_CPU_FEATURE_DOTPROD }, -}; - -static u32 query_arm_cpu_features(void) -{ - u32 features = 0; - size_t i; - - for (i = 0; i < ARRAY_LEN(feature_sysctls); i++) { - const char *name = feature_sysctls[i].name; - u32 val = 0; - size_t valsize = sizeof(val); - - if (sysctlbyname(name, &val, &valsize, NULL, 0) == 0 && - valsize == sizeof(val) && val == 1) - features |= feature_sysctls[i].feature; - } - return features; -} -#elif defined(_WIN32) - -#include - -#ifndef PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE /* added in Windows SDK 20348 */ -# define PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE 43 -#endif - -static u32 query_arm_cpu_features(void) -{ - u32 features = ARM_CPU_FEATURE_NEON; - - if (IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE)) - features |= ARM_CPU_FEATURE_PMULL; - if (IsProcessorFeaturePresent(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE)) - features |= ARM_CPU_FEATURE_CRC32; - if (IsProcessorFeaturePresent(PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE)) - features |= ARM_CPU_FEATURE_DOTPROD; - - /* FIXME: detect SHA3 support too. */ - - return features; -} -#else -#error "unhandled case" -#endif - -static const struct cpu_feature arm_cpu_feature_table[] = { - {ARM_CPU_FEATURE_NEON, "neon"}, - {ARM_CPU_FEATURE_PMULL, "pmull"}, - {ARM_CPU_FEATURE_PREFER_PMULL, "prefer_pmull"}, - {ARM_CPU_FEATURE_CRC32, "crc32"}, - {ARM_CPU_FEATURE_SHA3, "sha3"}, - {ARM_CPU_FEATURE_DOTPROD, "dotprod"}, -}; - -/* - * Whether to set ARM_CPU_FEATURE_PREFER_PMULL on this CPU. This is the - * right choice on CPUs whose pmull pipes have more aggregate throughput - * than the crc32 unit -- in measured cases by a wide margin: the Apple M - * series sustains ~68 GB/s on pmull vs ~25 GB/s on crc32 (M1), and the Arm - * Neoverse V class sustains ~40 GB/s vs ~22 GB/s (Graviton 4 / V2). - * - * We detect Apple at compile time, and Neoverse V-class cores at runtime - * via MIDR_EL1 on Linux. Elsewhere we leave this unset, and the dispatcher - * picks the crc32-instruction path which is the right default for most - * other modern ARM CPUs. - */ -static bool arm_cpu_prefers_pmull(void) -{ -#if defined(__APPLE__) && TARGET_OS_OSX - return true; -#elif defined(__linux__) && defined(ARCH_ARM64) - if (arm64_cpu_is_neoverse_v_class()) - return true; -#endif -#ifdef TEST_SUPPORT__DO_NOT_USE - return true; -#endif - return false; -} - -volatile u32 libdeflate_arm_cpu_features = 0; - -void libdeflate_init_arm_cpu_features(void) -{ - u32 features = query_arm_cpu_features(); - - if (arm_cpu_prefers_pmull()) - features |= ARM_CPU_FEATURE_PREFER_PMULL; - - disable_cpu_features_for_testing(&features, arm_cpu_feature_table, - ARRAY_LEN(arm_cpu_feature_table)); - - libdeflate_arm_cpu_features = features | ARM_CPU_FEATURES_KNOWN; -} - -#endif /* ARM_CPU_FEATURES_KNOWN */ diff --git a/vendor/libdeflate/lib/arm/cpu_features.h b/vendor/libdeflate/lib/arm/cpu_features.h deleted file mode 100644 index dc9ab8ad34..0000000000 --- a/vendor/libdeflate/lib/arm/cpu_features.h +++ /dev/null @@ -1,214 +0,0 @@ -/* - * arm/cpu_features.h - feature detection for ARM CPUs - * - * Copyright 2018 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef LIB_ARM_CPU_FEATURES_H -#define LIB_ARM_CPU_FEATURES_H - -#include "../lib_common.h" - -#if defined(ARCH_ARM32) || defined(ARCH_ARM64) - -#define ARM_CPU_FEATURE_NEON (1 << 0) -#define ARM_CPU_FEATURE_PMULL (1 << 1) -/* - * PREFER_PMULL indicates that the CPU has very high pmull throughput, and so - * the 12x wide pmull-based CRC-32 implementation is likely to be faster than an - * implementation based on the crc32 instructions. - */ -#define ARM_CPU_FEATURE_PREFER_PMULL (1 << 2) -#define ARM_CPU_FEATURE_CRC32 (1 << 3) -#define ARM_CPU_FEATURE_SHA3 (1 << 4) -#define ARM_CPU_FEATURE_DOTPROD (1 << 5) - -#if !defined(FREESTANDING) && \ - (defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER)) && \ - (defined(__linux__) || \ - (defined(__APPLE__) && defined(ARCH_ARM64)) || \ - (defined(_WIN32) && defined(ARCH_ARM64))) -/* Runtime ARM CPU feature detection is supported. */ -# define ARM_CPU_FEATURES_KNOWN (1U << 31) -extern volatile u32 libdeflate_arm_cpu_features; - -void libdeflate_init_arm_cpu_features(void); - -static inline u32 get_arm_cpu_features(void) -{ - if (libdeflate_arm_cpu_features == 0) - libdeflate_init_arm_cpu_features(); - return libdeflate_arm_cpu_features; -} -#else -static inline u32 get_arm_cpu_features(void) { return 0; } -#endif - -/* NEON */ -#if defined(__ARM_NEON) || (defined(_MSC_VER) && defined(ARCH_ARM64)) -# define HAVE_NEON(features) 1 -# define HAVE_NEON_NATIVE 1 -#else -# define HAVE_NEON(features) ((features) & ARM_CPU_FEATURE_NEON) -# define HAVE_NEON_NATIVE 0 -#endif -/* - * With both gcc and clang, NEON intrinsics require that the main target has - * NEON enabled already. Exception: with gcc 6.1 and later (r230411 for arm32, - * r226563 for arm64), hardware floating point support is sufficient. - */ -#if (defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER)) && \ - (HAVE_NEON_NATIVE || (GCC_PREREQ(6, 1) && defined(__ARM_FP))) -# define HAVE_NEON_INTRIN 1 -# include -#else -# define HAVE_NEON_INTRIN 0 -#endif - -/* PMULL */ -#ifdef __ARM_FEATURE_CRYPTO -# define HAVE_PMULL(features) 1 -#else -# define HAVE_PMULL(features) ((features) & ARM_CPU_FEATURE_PMULL) -#endif -#if defined(ARCH_ARM64) && HAVE_NEON_INTRIN && \ - (GCC_PREREQ(7, 1) || defined(__clang__) || defined(_MSC_VER)) && \ - CPU_IS_LITTLE_ENDIAN() /* untested on big endian */ -# define HAVE_PMULL_INTRIN 1 - /* Work around MSVC's vmull_p64() taking poly64x1_t instead of poly64_t */ -# ifdef _MSC_VER -# define compat_vmull_p64(a, b) vmull_p64(vcreate_p64(a), vcreate_p64(b)) -# else -# define compat_vmull_p64(a, b) vmull_p64((a), (b)) -# endif -#else -# define HAVE_PMULL_INTRIN 0 -#endif - -/* CRC32 */ -#ifdef __ARM_FEATURE_CRC32 -# define HAVE_CRC32(features) 1 -#else -# define HAVE_CRC32(features) ((features) & ARM_CPU_FEATURE_CRC32) -#endif -#if defined(ARCH_ARM64) && \ - (defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER)) -# define HAVE_CRC32_INTRIN 1 -# if defined(__GNUC__) || defined(__clang__) -# include -# endif - /* - * Use an inline assembly fallback for clang 15 and earlier, which only - * defined the crc32 intrinsics when crc32 is enabled in the main target. - */ -# if defined(__clang__) && !CLANG_PREREQ(16, 0, 16000000) && \ - !defined(__ARM_FEATURE_CRC32) -# undef __crc32b -# define __crc32b(a, b) \ - ({ uint32_t res; \ - __asm__("crc32b %w0, %w1, %w2" \ - : "=r" (res) : "r" (a), "r" (b)); \ - res; }) -# undef __crc32h -# define __crc32h(a, b) \ - ({ uint32_t res; \ - __asm__("crc32h %w0, %w1, %w2" \ - : "=r" (res) : "r" (a), "r" (b)); \ - res; }) -# undef __crc32w -# define __crc32w(a, b) \ - ({ uint32_t res; \ - __asm__("crc32w %w0, %w1, %w2" \ - : "=r" (res) : "r" (a), "r" (b)); \ - res; }) -# undef __crc32d -# define __crc32d(a, b) \ - ({ uint32_t res; \ - __asm__("crc32x %w0, %w1, %2" \ - : "=r" (res) : "r" (a), "r" (b)); \ - res; }) -# pragma clang diagnostic ignored "-Wgnu-statement-expression" -# endif -#else -# define HAVE_CRC32_INTRIN 0 -#endif - -/* SHA3 (needed for the eor3 instruction) */ -#ifdef __ARM_FEATURE_SHA3 -# define HAVE_SHA3(features) 1 -#else -# define HAVE_SHA3(features) ((features) & ARM_CPU_FEATURE_SHA3) -#endif -#if defined(ARCH_ARM64) && HAVE_NEON_INTRIN && \ - (GCC_PREREQ(9, 1) /* r268049 */ || \ - CLANG_PREREQ(7, 0, 10010463) /* r338010 */) -# define HAVE_SHA3_INTRIN 1 - /* - * Use an inline assembly fallback for clang 15 and earlier, which only - * defined the sha3 intrinsics when sha3 is enabled in the main target. - */ -# if defined(__clang__) && !CLANG_PREREQ(16, 0, 16000000) && \ - !defined(__ARM_FEATURE_SHA3) -# undef veor3q_u8 -# define veor3q_u8(a, b, c) \ - ({ uint8x16_t res; \ - __asm__("eor3 %0.16b, %1.16b, %2.16b, %3.16b" \ - : "=w" (res) : "w" (a), "w" (b), "w" (c)); \ - res; }) -# pragma clang diagnostic ignored "-Wgnu-statement-expression" -# endif -#else -# define HAVE_SHA3_INTRIN 0 -#endif - -/* dotprod */ -#ifdef __ARM_FEATURE_DOTPROD -# define HAVE_DOTPROD(features) 1 -#else -# define HAVE_DOTPROD(features) ((features) & ARM_CPU_FEATURE_DOTPROD) -#endif -#if defined(ARCH_ARM64) && HAVE_NEON_INTRIN && \ - (GCC_PREREQ(8, 1) || CLANG_PREREQ(7, 0, 10010000) || defined(_MSC_VER)) -# define HAVE_DOTPROD_INTRIN 1 - /* - * Use an inline assembly fallback for clang 15 and earlier, which only - * defined the dotprod intrinsics when dotprod is enabled in the main target. - */ -# if defined(__clang__) && !CLANG_PREREQ(16, 0, 16000000) && \ - !defined(__ARM_FEATURE_DOTPROD) -# undef vdotq_u32 -# define vdotq_u32(a, b, c) \ - ({ uint32x4_t res = (a); \ - __asm__("udot %0.4s, %1.16b, %2.16b" \ - : "+w" (res) : "w" (b), "w" (c)); \ - res; }) -# pragma clang diagnostic ignored "-Wgnu-statement-expression" -# endif -#else -# define HAVE_DOTPROD_INTRIN 0 -#endif - -#endif /* ARCH_ARM32 || ARCH_ARM64 */ - -#endif /* LIB_ARM_CPU_FEATURES_H */ diff --git a/vendor/libdeflate/lib/arm/crc32_impl.h b/vendor/libdeflate/lib/arm/crc32_impl.h deleted file mode 100644 index 696af5a8a6..0000000000 --- a/vendor/libdeflate/lib/arm/crc32_impl.h +++ /dev/null @@ -1,594 +0,0 @@ -/* - * arm/crc32_impl.h - ARM implementations of the gzip CRC-32 algorithm - * - * Copyright 2022 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef LIB_ARM_CRC32_IMPL_H -#define LIB_ARM_CRC32_IMPL_H - -#include "cpu_features.h" - -/* - * crc32_arm_crc() - implementation using crc32 instructions (only) - * - * In general this implementation is straightforward. However, naive use of the - * crc32 instructions is serial: one of the two inputs to each crc32 instruction - * is the output of the previous one. To take advantage of CPUs that can - * execute multiple crc32 instructions in parallel, when possible we interleave - * the checksumming of several adjacent chunks, then combine their CRCs. - * - * However, without pmull, combining CRCs is fairly slow. So in this pmull-less - * version, we only use a large chunk length, and thus we only do chunked - * processing if there is a lot of data to checksum. This also means that a - * variable chunk length wouldn't help much, so we just support a fixed length. - */ -#if HAVE_CRC32_INTRIN -# ifdef __clang__ -# define ATTRIBUTES _target_attribute("crc") -# else -# define ATTRIBUTES _target_attribute("+crc") -# endif - -/* - * Combine the CRCs for 4 adjacent chunks of length L = CRC32_FIXED_CHUNK_LEN - * bytes each by computing: - * - * [ crc0*x^(3*8*L) + crc1*x^(2*8*L) + crc2*x^(1*8*L) + crc3 ] mod G(x) - * - * This has been optimized in several ways: - * - * - The needed multipliers (x to some power, reduced mod G(x)) were - * precomputed. - * - * - The 3 multiplications are interleaved. - * - * - The reduction mod G(x) is delayed to the end and done using __crc32d. - * Note that the use of __crc32d introduces an extra factor of x^32. To - * cancel that out along with the extra factor of x^1 that gets introduced - * because of how the 63-bit products are aligned in their 64-bit integers, - * the multipliers are actually x^(j*8*L - 33) instead of x^(j*8*L). - */ -static forceinline ATTRIBUTES u32 -combine_crcs_slow(u32 crc0, u32 crc1, u32 crc2, u32 crc3) -{ - u64 res0 = 0, res1 = 0, res2 = 0; - int i; - - /* Multiply crc{0,1,2} by CRC32_FIXED_CHUNK_MULT_{3,2,1}. */ - for (i = 0; i < 32; i++) { - if (CRC32_FIXED_CHUNK_MULT_3 & (1U << i)) - res0 ^= (u64)crc0 << i; - if (CRC32_FIXED_CHUNK_MULT_2 & (1U << i)) - res1 ^= (u64)crc1 << i; - if (CRC32_FIXED_CHUNK_MULT_1 & (1U << i)) - res2 ^= (u64)crc2 << i; - } - /* Add the different parts and reduce mod G(x). */ - return __crc32d(0, res0 ^ res1 ^ res2) ^ crc3; -} - -#define crc32_arm_crc crc32_arm_crc -static ATTRIBUTES u32 -crc32_arm_crc(u32 crc, const u8 *p, size_t len) -{ - if (len >= 64) { - const size_t align = -(uintptr_t)p & 7; - - /* Align p to the next 8-byte boundary. */ - if (align) { - if (align & 1) - crc = __crc32b(crc, *p++); - if (align & 2) { - crc = __crc32h(crc, le16_bswap(*(u16 *)p)); - p += 2; - } - if (align & 4) { - crc = __crc32w(crc, le32_bswap(*(u32 *)p)); - p += 4; - } - len -= align; - } - /* - * Interleave the processing of multiple adjacent data chunks to - * take advantage of instruction-level parallelism. - * - * Some CPUs don't prefetch the data if it's being fetched in - * multiple interleaved streams, so do explicit prefetching. - */ - while (len >= CRC32_NUM_CHUNKS * CRC32_FIXED_CHUNK_LEN) { - const u64 *wp0 = (const u64 *)p; - const u64 * const wp0_end = - (const u64 *)(p + CRC32_FIXED_CHUNK_LEN); - u32 crc1 = 0, crc2 = 0, crc3 = 0; - - STATIC_ASSERT(CRC32_NUM_CHUNKS == 4); - STATIC_ASSERT(CRC32_FIXED_CHUNK_LEN % (4 * 8) == 0); - do { - prefetchr(&wp0[64 + 0*CRC32_FIXED_CHUNK_LEN/8]); - prefetchr(&wp0[64 + 1*CRC32_FIXED_CHUNK_LEN/8]); - prefetchr(&wp0[64 + 2*CRC32_FIXED_CHUNK_LEN/8]); - prefetchr(&wp0[64 + 3*CRC32_FIXED_CHUNK_LEN/8]); - crc = __crc32d(crc, le64_bswap(wp0[0*CRC32_FIXED_CHUNK_LEN/8])); - crc1 = __crc32d(crc1, le64_bswap(wp0[1*CRC32_FIXED_CHUNK_LEN/8])); - crc2 = __crc32d(crc2, le64_bswap(wp0[2*CRC32_FIXED_CHUNK_LEN/8])); - crc3 = __crc32d(crc3, le64_bswap(wp0[3*CRC32_FIXED_CHUNK_LEN/8])); - wp0++; - crc = __crc32d(crc, le64_bswap(wp0[0*CRC32_FIXED_CHUNK_LEN/8])); - crc1 = __crc32d(crc1, le64_bswap(wp0[1*CRC32_FIXED_CHUNK_LEN/8])); - crc2 = __crc32d(crc2, le64_bswap(wp0[2*CRC32_FIXED_CHUNK_LEN/8])); - crc3 = __crc32d(crc3, le64_bswap(wp0[3*CRC32_FIXED_CHUNK_LEN/8])); - wp0++; - crc = __crc32d(crc, le64_bswap(wp0[0*CRC32_FIXED_CHUNK_LEN/8])); - crc1 = __crc32d(crc1, le64_bswap(wp0[1*CRC32_FIXED_CHUNK_LEN/8])); - crc2 = __crc32d(crc2, le64_bswap(wp0[2*CRC32_FIXED_CHUNK_LEN/8])); - crc3 = __crc32d(crc3, le64_bswap(wp0[3*CRC32_FIXED_CHUNK_LEN/8])); - wp0++; - crc = __crc32d(crc, le64_bswap(wp0[0*CRC32_FIXED_CHUNK_LEN/8])); - crc1 = __crc32d(crc1, le64_bswap(wp0[1*CRC32_FIXED_CHUNK_LEN/8])); - crc2 = __crc32d(crc2, le64_bswap(wp0[2*CRC32_FIXED_CHUNK_LEN/8])); - crc3 = __crc32d(crc3, le64_bswap(wp0[3*CRC32_FIXED_CHUNK_LEN/8])); - wp0++; - } while (wp0 != wp0_end); - crc = combine_crcs_slow(crc, crc1, crc2, crc3); - p += CRC32_NUM_CHUNKS * CRC32_FIXED_CHUNK_LEN; - len -= CRC32_NUM_CHUNKS * CRC32_FIXED_CHUNK_LEN; - } - /* - * Due to the large fixed chunk length used above, there might - * still be a lot of data left. So use a 64-byte loop here, - * instead of a loop that is less unrolled. - */ - while (len >= 64) { - crc = __crc32d(crc, le64_bswap(*(u64 *)(p + 0))); - crc = __crc32d(crc, le64_bswap(*(u64 *)(p + 8))); - crc = __crc32d(crc, le64_bswap(*(u64 *)(p + 16))); - crc = __crc32d(crc, le64_bswap(*(u64 *)(p + 24))); - crc = __crc32d(crc, le64_bswap(*(u64 *)(p + 32))); - crc = __crc32d(crc, le64_bswap(*(u64 *)(p + 40))); - crc = __crc32d(crc, le64_bswap(*(u64 *)(p + 48))); - crc = __crc32d(crc, le64_bswap(*(u64 *)(p + 56))); - p += 64; - len -= 64; - } - } - if (len & 32) { - crc = __crc32d(crc, get_unaligned_le64(p + 0)); - crc = __crc32d(crc, get_unaligned_le64(p + 8)); - crc = __crc32d(crc, get_unaligned_le64(p + 16)); - crc = __crc32d(crc, get_unaligned_le64(p + 24)); - p += 32; - } - if (len & 16) { - crc = __crc32d(crc, get_unaligned_le64(p + 0)); - crc = __crc32d(crc, get_unaligned_le64(p + 8)); - p += 16; - } - if (len & 8) { - crc = __crc32d(crc, get_unaligned_le64(p)); - p += 8; - } - if (len & 4) { - crc = __crc32w(crc, get_unaligned_le32(p)); - p += 4; - } - if (len & 2) { - crc = __crc32h(crc, get_unaligned_le16(p)); - p += 2; - } - if (len & 1) - crc = __crc32b(crc, *p); - return crc; -} -#undef ATTRIBUTES -#endif /* crc32_arm_crc() */ - -/* - * crc32_arm_crc_pmullcombine() - implementation using crc32 instructions, plus - * pmull instructions for CRC combining - * - * This is similar to crc32_arm_crc(), but it enables the use of pmull - * (carryless multiplication) instructions for the steps where the CRCs of - * adjacent data chunks are combined. As this greatly speeds up CRC - * combination, this implementation also differs from crc32_arm_crc() in that it - * uses a variable chunk length which can get fairly small. The precomputed - * multipliers needed for the selected chunk length are loaded from a table. - * - * Note that pmull is used here only for combining the CRCs of separately - * checksummed chunks, not for folding the data itself. See crc32_arm_pmull*() - * for implementations that use pmull for folding the data itself. - */ -#if HAVE_CRC32_INTRIN && HAVE_PMULL_INTRIN -# ifdef __clang__ -# define ATTRIBUTES _target_attribute("crc,aes") -# else -# define ATTRIBUTES _target_attribute("+crc,+crypto") -# endif - -/* Do carryless multiplication of two 32-bit values. */ -static forceinline ATTRIBUTES u64 -clmul_u32(u32 a, u32 b) -{ - uint64x2_t res = vreinterpretq_u64_p128( - compat_vmull_p64((poly64_t)a, (poly64_t)b)); - - return vgetq_lane_u64(res, 0); -} - -/* - * Like combine_crcs_slow(), but uses vmull_p64 to do the multiplications more - * quickly, and supports a variable chunk length. The chunk length is - * 'i * CRC32_MIN_VARIABLE_CHUNK_LEN' - * where 1 <= i < ARRAY_LEN(crc32_mults_for_chunklen). - */ -static forceinline ATTRIBUTES u32 -combine_crcs_fast(u32 crc0, u32 crc1, u32 crc2, u32 crc3, size_t i) -{ - u64 res0 = clmul_u32(crc0, crc32_mults_for_chunklen[i][0]); - u64 res1 = clmul_u32(crc1, crc32_mults_for_chunklen[i][1]); - u64 res2 = clmul_u32(crc2, crc32_mults_for_chunklen[i][2]); - - return __crc32d(0, res0 ^ res1 ^ res2) ^ crc3; -} - -#define crc32_arm_crc_pmullcombine crc32_arm_crc_pmullcombine -static ATTRIBUTES u32 -crc32_arm_crc_pmullcombine(u32 crc, const u8 *p, size_t len) -{ - const size_t align = -(uintptr_t)p & 7; - - if (len >= align + CRC32_NUM_CHUNKS * CRC32_MIN_VARIABLE_CHUNK_LEN) { - /* Align p to the next 8-byte boundary. */ - if (align) { - if (align & 1) - crc = __crc32b(crc, *p++); - if (align & 2) { - crc = __crc32h(crc, le16_bswap(*(u16 *)p)); - p += 2; - } - if (align & 4) { - crc = __crc32w(crc, le32_bswap(*(u32 *)p)); - p += 4; - } - len -= align; - } - /* - * Handle CRC32_MAX_VARIABLE_CHUNK_LEN specially, so that better - * code is generated for it. - */ - while (len >= CRC32_NUM_CHUNKS * CRC32_MAX_VARIABLE_CHUNK_LEN) { - const u64 *wp0 = (const u64 *)p; - const u64 * const wp0_end = - (const u64 *)(p + CRC32_MAX_VARIABLE_CHUNK_LEN); - u32 crc1 = 0, crc2 = 0, crc3 = 0; - - STATIC_ASSERT(CRC32_NUM_CHUNKS == 4); - STATIC_ASSERT(CRC32_MAX_VARIABLE_CHUNK_LEN % (4 * 8) == 0); - do { - prefetchr(&wp0[64 + 0*CRC32_MAX_VARIABLE_CHUNK_LEN/8]); - prefetchr(&wp0[64 + 1*CRC32_MAX_VARIABLE_CHUNK_LEN/8]); - prefetchr(&wp0[64 + 2*CRC32_MAX_VARIABLE_CHUNK_LEN/8]); - prefetchr(&wp0[64 + 3*CRC32_MAX_VARIABLE_CHUNK_LEN/8]); - crc = __crc32d(crc, le64_bswap(wp0[0*CRC32_MAX_VARIABLE_CHUNK_LEN/8])); - crc1 = __crc32d(crc1, le64_bswap(wp0[1*CRC32_MAX_VARIABLE_CHUNK_LEN/8])); - crc2 = __crc32d(crc2, le64_bswap(wp0[2*CRC32_MAX_VARIABLE_CHUNK_LEN/8])); - crc3 = __crc32d(crc3, le64_bswap(wp0[3*CRC32_MAX_VARIABLE_CHUNK_LEN/8])); - wp0++; - crc = __crc32d(crc, le64_bswap(wp0[0*CRC32_MAX_VARIABLE_CHUNK_LEN/8])); - crc1 = __crc32d(crc1, le64_bswap(wp0[1*CRC32_MAX_VARIABLE_CHUNK_LEN/8])); - crc2 = __crc32d(crc2, le64_bswap(wp0[2*CRC32_MAX_VARIABLE_CHUNK_LEN/8])); - crc3 = __crc32d(crc3, le64_bswap(wp0[3*CRC32_MAX_VARIABLE_CHUNK_LEN/8])); - wp0++; - crc = __crc32d(crc, le64_bswap(wp0[0*CRC32_MAX_VARIABLE_CHUNK_LEN/8])); - crc1 = __crc32d(crc1, le64_bswap(wp0[1*CRC32_MAX_VARIABLE_CHUNK_LEN/8])); - crc2 = __crc32d(crc2, le64_bswap(wp0[2*CRC32_MAX_VARIABLE_CHUNK_LEN/8])); - crc3 = __crc32d(crc3, le64_bswap(wp0[3*CRC32_MAX_VARIABLE_CHUNK_LEN/8])); - wp0++; - crc = __crc32d(crc, le64_bswap(wp0[0*CRC32_MAX_VARIABLE_CHUNK_LEN/8])); - crc1 = __crc32d(crc1, le64_bswap(wp0[1*CRC32_MAX_VARIABLE_CHUNK_LEN/8])); - crc2 = __crc32d(crc2, le64_bswap(wp0[2*CRC32_MAX_VARIABLE_CHUNK_LEN/8])); - crc3 = __crc32d(crc3, le64_bswap(wp0[3*CRC32_MAX_VARIABLE_CHUNK_LEN/8])); - wp0++; - } while (wp0 != wp0_end); - crc = combine_crcs_fast(crc, crc1, crc2, crc3, - ARRAY_LEN(crc32_mults_for_chunklen) - 1); - p += CRC32_NUM_CHUNKS * CRC32_MAX_VARIABLE_CHUNK_LEN; - len -= CRC32_NUM_CHUNKS * CRC32_MAX_VARIABLE_CHUNK_LEN; - } - /* Handle up to one variable-length chunk. */ - if (len >= CRC32_NUM_CHUNKS * CRC32_MIN_VARIABLE_CHUNK_LEN) { - const size_t i = len / (CRC32_NUM_CHUNKS * - CRC32_MIN_VARIABLE_CHUNK_LEN); - const size_t chunk_len = - i * CRC32_MIN_VARIABLE_CHUNK_LEN; - const u64 *wp0 = (const u64 *)(p + 0*chunk_len); - const u64 *wp1 = (const u64 *)(p + 1*chunk_len); - const u64 *wp2 = (const u64 *)(p + 2*chunk_len); - const u64 *wp3 = (const u64 *)(p + 3*chunk_len); - const u64 * const wp0_end = wp1; - u32 crc1 = 0, crc2 = 0, crc3 = 0; - - STATIC_ASSERT(CRC32_NUM_CHUNKS == 4); - STATIC_ASSERT(CRC32_MIN_VARIABLE_CHUNK_LEN % (4 * 8) == 0); - do { - prefetchr(wp0 + 64); - prefetchr(wp1 + 64); - prefetchr(wp2 + 64); - prefetchr(wp3 + 64); - crc = __crc32d(crc, le64_bswap(*wp0++)); - crc1 = __crc32d(crc1, le64_bswap(*wp1++)); - crc2 = __crc32d(crc2, le64_bswap(*wp2++)); - crc3 = __crc32d(crc3, le64_bswap(*wp3++)); - crc = __crc32d(crc, le64_bswap(*wp0++)); - crc1 = __crc32d(crc1, le64_bswap(*wp1++)); - crc2 = __crc32d(crc2, le64_bswap(*wp2++)); - crc3 = __crc32d(crc3, le64_bswap(*wp3++)); - crc = __crc32d(crc, le64_bswap(*wp0++)); - crc1 = __crc32d(crc1, le64_bswap(*wp1++)); - crc2 = __crc32d(crc2, le64_bswap(*wp2++)); - crc3 = __crc32d(crc3, le64_bswap(*wp3++)); - crc = __crc32d(crc, le64_bswap(*wp0++)); - crc1 = __crc32d(crc1, le64_bswap(*wp1++)); - crc2 = __crc32d(crc2, le64_bswap(*wp2++)); - crc3 = __crc32d(crc3, le64_bswap(*wp3++)); - } while (wp0 != wp0_end); - crc = combine_crcs_fast(crc, crc1, crc2, crc3, i); - p += CRC32_NUM_CHUNKS * chunk_len; - len -= CRC32_NUM_CHUNKS * chunk_len; - } - - while (len >= 32) { - crc = __crc32d(crc, le64_bswap(*(u64 *)(p + 0))); - crc = __crc32d(crc, le64_bswap(*(u64 *)(p + 8))); - crc = __crc32d(crc, le64_bswap(*(u64 *)(p + 16))); - crc = __crc32d(crc, le64_bswap(*(u64 *)(p + 24))); - p += 32; - len -= 32; - } - } else { - while (len >= 32) { - crc = __crc32d(crc, get_unaligned_le64(p + 0)); - crc = __crc32d(crc, get_unaligned_le64(p + 8)); - crc = __crc32d(crc, get_unaligned_le64(p + 16)); - crc = __crc32d(crc, get_unaligned_le64(p + 24)); - p += 32; - len -= 32; - } - } - if (len & 16) { - crc = __crc32d(crc, get_unaligned_le64(p + 0)); - crc = __crc32d(crc, get_unaligned_le64(p + 8)); - p += 16; - } - if (len & 8) { - crc = __crc32d(crc, get_unaligned_le64(p)); - p += 8; - } - if (len & 4) { - crc = __crc32w(crc, get_unaligned_le32(p)); - p += 4; - } - if (len & 2) { - crc = __crc32h(crc, get_unaligned_le16(p)); - p += 2; - } - if (len & 1) - crc = __crc32b(crc, *p); - return crc; -} -#undef ATTRIBUTES -#endif /* crc32_arm_crc_pmullcombine() */ - -/* - * crc32_arm_pmullx4() - implementation using "folding" with pmull instructions - * - * This implementation is intended for CPUs that support pmull instructions but - * not crc32 instructions. - */ -#if HAVE_PMULL_INTRIN -# define crc32_arm_pmullx4 crc32_arm_pmullx4 -# define SUFFIX _pmullx4 -# ifdef __clang__ - /* - * This used to use "crypto", but that stopped working with clang 16. - * Now only "aes" works. "aes" works with older versions too, so use - * that. No "+" prefix; clang 15 and earlier doesn't accept that. - */ -# define ATTRIBUTES _target_attribute("aes") -# else - /* - * With gcc, only "+crypto" works. Both the "+" prefix and the - * "crypto" (not "aes") are essential... - */ -# define ATTRIBUTES _target_attribute("+crypto") -# endif -# define ENABLE_EOR3 0 -# include "crc32_pmull_helpers.h" - -static ATTRIBUTES u32 -crc32_arm_pmullx4(u32 crc, const u8 *p, size_t len) -{ - static const u64 _aligned_attribute(16) mults[3][2] = { - { CRC32_X159_MODG, CRC32_X95_MODG }, /* 1 vecs */ - { CRC32_X543_MODG, CRC32_X479_MODG }, /* 4 vecs */ - { CRC32_X287_MODG, CRC32_X223_MODG }, /* 2 vecs */ - }; - static const u64 _aligned_attribute(16) barrett_consts[3][2] = { - { CRC32_X95_MODG, }, - { CRC32_BARRETT_CONSTANT_1, }, - { CRC32_BARRETT_CONSTANT_2, }, - }; - const poly64x2_t multipliers_1 = load_multipliers(mults[0]); - uint8x16_t v0, v1, v2, v3; - - if (len < 64 + 15) { - if (len < 16) - return crc32_slice1(crc, p, len); - v0 = veorq_u8(vld1q_u8(p), u32_to_bytevec(crc)); - p += 16; - len -= 16; - while (len >= 16) { - v0 = fold_vec(v0, vld1q_u8(p), multipliers_1); - p += 16; - len -= 16; - } - } else { - const poly64x2_t multipliers_4 = load_multipliers(mults[1]); - const poly64x2_t multipliers_2 = load_multipliers(mults[2]); - const size_t align = -(uintptr_t)p & 15; - const uint8x16_t *vp; - - v0 = veorq_u8(vld1q_u8(p), u32_to_bytevec(crc)); - p += 16; - /* Align p to the next 16-byte boundary. */ - if (align) { - v0 = fold_partial_vec(v0, p, align, multipliers_1); - p += align; - len -= align; - } - vp = (const uint8x16_t *)p; - v1 = *vp++; - v2 = *vp++; - v3 = *vp++; - while (len >= 64 + 64) { - v0 = fold_vec(v0, *vp++, multipliers_4); - v1 = fold_vec(v1, *vp++, multipliers_4); - v2 = fold_vec(v2, *vp++, multipliers_4); - v3 = fold_vec(v3, *vp++, multipliers_4); - len -= 64; - } - v0 = fold_vec(v0, v2, multipliers_2); - v1 = fold_vec(v1, v3, multipliers_2); - if (len & 32) { - v0 = fold_vec(v0, *vp++, multipliers_2); - v1 = fold_vec(v1, *vp++, multipliers_2); - } - v0 = fold_vec(v0, v1, multipliers_1); - if (len & 16) - v0 = fold_vec(v0, *vp++, multipliers_1); - p = (const u8 *)vp; - len &= 15; - } - - /* Handle any remaining partial block now before reducing to 32 bits. */ - if (len) - v0 = fold_partial_vec(v0, p, len, multipliers_1); - - /* Reduce to 32 bits, following lib/x86/crc32_pclmul_template.h */ - v0 = veorq_u8(clmul_low(v0, load_multipliers(barrett_consts[0])), - vextq_u8(v0, vdupq_n_u8(0), 8)); - v1 = clmul_low(v0, load_multipliers(barrett_consts[1])); - v1 = clmul_low(v1, load_multipliers(barrett_consts[2])); - v0 = veorq_u8(v0, v1); - return vgetq_lane_u32(vreinterpretq_u32_u8(v0), 2); -} -#undef SUFFIX -#undef ATTRIBUTES -#undef ENABLE_EOR3 -#endif /* crc32_arm_pmullx4() */ - -/* - * crc32_arm_pmullx12_crc() - large-stride implementation using "folding" with - * pmull instructions, where crc32 instructions are also available - * - * See crc32_pmull_wide.h for explanation. - */ -#if HAVE_PMULL_INTRIN && HAVE_CRC32_INTRIN -# define crc32_arm_pmullx12_crc crc32_arm_pmullx12_crc -# define SUFFIX _pmullx12_crc -# ifdef __clang__ -# define ATTRIBUTES _target_attribute("aes,crc") -# else -# define ATTRIBUTES _target_attribute("+crypto,+crc") -# endif -# define ENABLE_EOR3 0 -# include "crc32_pmull_wide.h" -#endif - -/* - * crc32_arm_pmullx12_crc_eor3() - * - * This like crc32_arm_pmullx12_crc(), but it adds the eor3 instruction (from - * the sha3 extension) for even better performance. - */ -#if HAVE_PMULL_INTRIN && HAVE_CRC32_INTRIN && HAVE_SHA3_INTRIN && \ - !defined(LIBDEFLATE_ASSEMBLER_DOES_NOT_SUPPORT_SHA3) -# define crc32_arm_pmullx12_crc_eor3 crc32_arm_pmullx12_crc_eor3 -# define SUFFIX _pmullx12_crc_eor3 -# ifdef __clang__ -# define ATTRIBUTES _target_attribute("aes,crc,sha3") - /* - * Both gcc and binutils originally considered sha3 to depend on - * arch=armv8.2-a or later. This was fixed in gcc 13.2 by commit - * 9aac37ab8a7b ("aarch64: Remove architecture dependencies from intrinsics") - * and in binutils 2.41 by commit 205e4380c800 ("aarch64: Remove version - * dependencies from features"). Unfortunately, always using arch=armv8.2-a - * causes build errors with some compiler options because it may reduce the - * arch rather than increase it. Therefore we try to omit the arch whenever - * possible. If gcc is 14 or later, then both gcc and binutils are probably - * fixed, so we omit the arch. We also omit the arch if a feature that - * depends on armv8.2-a or later (in gcc 13.1 and earlier) is present. - */ -# elif GCC_PREREQ(14, 0) || defined(__ARM_FEATURE_JCVT) \ - || defined(__ARM_FEATURE_DOTPROD) -# define ATTRIBUTES _target_attribute("+crypto,+crc,+sha3") -# else -# define ATTRIBUTES _target_attribute("arch=armv8.2-a+crypto+crc+sha3") -# endif -# define ENABLE_EOR3 1 -# include "crc32_pmull_wide.h" -#endif - -static inline crc32_func_t -arch_select_crc32_func(void) -{ - const u32 features MAYBE_UNUSED = get_arm_cpu_features(); - -#ifdef crc32_arm_pmullx12_crc_eor3 - if ((features & ARM_CPU_FEATURE_PREFER_PMULL) && - HAVE_PMULL(features) && HAVE_CRC32(features) && HAVE_SHA3(features)) - return crc32_arm_pmullx12_crc_eor3; -#endif -#ifdef crc32_arm_pmullx12_crc - if ((features & ARM_CPU_FEATURE_PREFER_PMULL) && - HAVE_PMULL(features) && HAVE_CRC32(features)) - return crc32_arm_pmullx12_crc; -#endif -#ifdef crc32_arm_crc_pmullcombine - if (HAVE_CRC32(features) && HAVE_PMULL(features)) - return crc32_arm_crc_pmullcombine; -#endif -#ifdef crc32_arm_crc - if (HAVE_CRC32(features)) - return crc32_arm_crc; -#endif -#ifdef crc32_arm_pmullx4 - if (HAVE_PMULL(features)) - return crc32_arm_pmullx4; -#endif - return NULL; -} -#define arch_select_crc32_func arch_select_crc32_func - -#endif /* LIB_ARM_CRC32_IMPL_H */ diff --git a/vendor/libdeflate/lib/arm/crc32_pmull_helpers.h b/vendor/libdeflate/lib/arm/crc32_pmull_helpers.h deleted file mode 100644 index 023b904484..0000000000 --- a/vendor/libdeflate/lib/arm/crc32_pmull_helpers.h +++ /dev/null @@ -1,156 +0,0 @@ -/* - * arm/crc32_pmull_helpers.h - helper functions for CRC-32 folding with PMULL - * - * Copyright 2022 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * This file is a "template" for instantiating helper functions for CRC folding - * with pmull instructions. It accepts the following parameters: - * - * SUFFIX: - * Name suffix to append to all instantiated functions. - * ATTRIBUTES: - * Target function attributes to use. - * ENABLE_EOR3: - * Use the eor3 instruction (from the sha3 extension). - */ - -/* Create a vector with 'a' in the first 4 bytes, and the rest zeroed out. */ -#undef u32_to_bytevec -static forceinline ATTRIBUTES uint8x16_t -ADD_SUFFIX(u32_to_bytevec)(u32 a) -{ - return vreinterpretq_u8_u32(vsetq_lane_u32(a, vdupq_n_u32(0), 0)); -} -#define u32_to_bytevec ADD_SUFFIX(u32_to_bytevec) - -/* Load two 64-bit values into a vector. */ -#undef load_multipliers -static forceinline ATTRIBUTES poly64x2_t -ADD_SUFFIX(load_multipliers)(const u64 p[2]) -{ - return vreinterpretq_p64_u64(vld1q_u64(p)); -} -#define load_multipliers ADD_SUFFIX(load_multipliers) - -/* Do carryless multiplication of the low halves of two vectors. */ -#undef clmul_low -static forceinline ATTRIBUTES uint8x16_t -ADD_SUFFIX(clmul_low)(uint8x16_t a, poly64x2_t b) -{ - return vreinterpretq_u8_p128( - compat_vmull_p64(vgetq_lane_p64(vreinterpretq_p64_u8(a), 0), - vgetq_lane_p64(b, 0))); -} -#define clmul_low ADD_SUFFIX(clmul_low) - -/* Do carryless multiplication of the high halves of two vectors. */ -#undef clmul_high -static forceinline ATTRIBUTES uint8x16_t -ADD_SUFFIX(clmul_high)(uint8x16_t a, poly64x2_t b) -{ -#ifdef __clang__ - /* - * Use inline asm to ensure that pmull2 is really used. This works - * around clang bug https://github.com/llvm/llvm-project/issues/52868. - */ - uint8x16_t res; - - __asm__("pmull2 %0.1q, %1.2d, %2.2d" : "=w" (res) : "w" (a), "w" (b)); - return res; -#else - return vreinterpretq_u8_p128(vmull_high_p64(vreinterpretq_p64_u8(a), b)); -#endif -} -#define clmul_high ADD_SUFFIX(clmul_high) - -#undef eor3 -static forceinline ATTRIBUTES uint8x16_t -ADD_SUFFIX(eor3)(uint8x16_t a, uint8x16_t b, uint8x16_t c) -{ -#if ENABLE_EOR3 - return veor3q_u8(a, b, c); -#else - return veorq_u8(veorq_u8(a, b), c); -#endif -} -#define eor3 ADD_SUFFIX(eor3) - -#undef fold_vec -static forceinline ATTRIBUTES uint8x16_t -ADD_SUFFIX(fold_vec)(uint8x16_t src, uint8x16_t dst, poly64x2_t multipliers) -{ - uint8x16_t a = clmul_low(src, multipliers); - uint8x16_t b = clmul_high(src, multipliers); - - return eor3(a, b, dst); -} -#define fold_vec ADD_SUFFIX(fold_vec) - -/* - * Given v containing a 16-byte polynomial, and a pointer 'p' that points to the - * next '1 <= len <= 15' data bytes, rearrange the concatenation of v and the - * data into vectors x0 and x1 that contain 'len' bytes and 16 bytes, - * respectively. Then fold x0 into x1 and return the result. Assumes that - * 'p + len - 16' is in-bounds. - */ -#undef fold_partial_vec -static forceinline ATTRIBUTES MAYBE_UNUSED uint8x16_t -ADD_SUFFIX(fold_partial_vec)(uint8x16_t v, const u8 *p, size_t len, - poly64x2_t multipliers_1) -{ - /* - * vqtbl1q_u8(v, shift_tab[len..len+15]) left shifts v by 16-len bytes. - * vqtbl1q_u8(v, shift_tab[len+16..len+31]) right shifts v by len bytes. - */ - static const u8 shift_tab[48] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - }; - const uint8x16_t lshift = vld1q_u8(&shift_tab[len]); - const uint8x16_t rshift = vld1q_u8(&shift_tab[len + 16]); - uint8x16_t x0, x1, bsl_mask; - - /* x0 = v left-shifted by '16 - len' bytes */ - x0 = vqtbl1q_u8(v, lshift); - - /* Create a vector of '16 - len' 0x00 bytes, then 'len' 0xff bytes. */ - bsl_mask = vreinterpretq_u8_s8( - vshrq_n_s8(vreinterpretq_s8_u8(rshift), 7)); - - /* - * x1 = the last '16 - len' bytes from v (i.e. v right-shifted by 'len' - * bytes) followed by the remaining data. - */ - x1 = vbslq_u8(bsl_mask /* 0 bits select from arg3, 1 bits from arg2 */, - vld1q_u8(p + len - 16), vqtbl1q_u8(v, rshift)); - - return fold_vec(x0, x1, multipliers_1); -} -#define fold_partial_vec ADD_SUFFIX(fold_partial_vec) diff --git a/vendor/libdeflate/lib/arm/crc32_pmull_wide.h b/vendor/libdeflate/lib/arm/crc32_pmull_wide.h deleted file mode 100644 index 300e2829e7..0000000000 --- a/vendor/libdeflate/lib/arm/crc32_pmull_wide.h +++ /dev/null @@ -1,226 +0,0 @@ -/* - * arm/crc32_pmull_wide.h - gzip CRC-32 with PMULL (extra-wide version) - * - * Copyright 2022 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * This file is a "template" for instantiating PMULL-based crc32_arm functions. - * The "parameters" are: - * - * SUFFIX: - * Name suffix to append to all instantiated functions. - * ATTRIBUTES: - * Target function attributes to use. - * ENABLE_EOR3: - * Use the eor3 instruction (from the sha3 extension). - * - * This is the extra-wide version; it uses an unusually large stride length of - * 12, and it assumes that crc32 instructions are available too. It's intended - * for powerful CPUs that support both pmull and crc32 instructions, but where - * throughput of pmull and xor (given enough instructions issued in parallel) is - * significantly higher than that of crc32, thus making the crc32 instructions - * (counterintuitively) not actually the fastest way to compute the CRC-32. The - * Apple M1 processor is an example of such a CPU. - */ - -#include "crc32_pmull_helpers.h" - -static ATTRIBUTES u32 -ADD_SUFFIX(crc32_arm)(u32 crc, const u8 *p, size_t len) -{ - uint8x16_t v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11; - - if (len < 3 * 192) { - static const u64 _aligned_attribute(16) mults[3][2] = { - { CRC32_X543_MODG, CRC32_X479_MODG }, /* 4 vecs */ - { CRC32_X287_MODG, CRC32_X223_MODG }, /* 2 vecs */ - { CRC32_X159_MODG, CRC32_X95_MODG }, /* 1 vecs */ - }; - poly64x2_t multipliers_4, multipliers_2, multipliers_1; - - if (len < 64) - goto tail; - multipliers_4 = load_multipliers(mults[0]); - multipliers_2 = load_multipliers(mults[1]); - multipliers_1 = load_multipliers(mults[2]); - /* - * Short length; don't bother aligning the pointer, and fold - * 64 bytes (4 vectors) at a time, at most. - */ - v0 = veorq_u8(vld1q_u8(p + 0), u32_to_bytevec(crc)); - v1 = vld1q_u8(p + 16); - v2 = vld1q_u8(p + 32); - v3 = vld1q_u8(p + 48); - p += 64; - len -= 64; - while (len >= 64) { - v0 = fold_vec(v0, vld1q_u8(p + 0), multipliers_4); - v1 = fold_vec(v1, vld1q_u8(p + 16), multipliers_4); - v2 = fold_vec(v2, vld1q_u8(p + 32), multipliers_4); - v3 = fold_vec(v3, vld1q_u8(p + 48), multipliers_4); - p += 64; - len -= 64; - } - v0 = fold_vec(v0, v2, multipliers_2); - v1 = fold_vec(v1, v3, multipliers_2); - if (len >= 32) { - v0 = fold_vec(v0, vld1q_u8(p + 0), multipliers_2); - v1 = fold_vec(v1, vld1q_u8(p + 16), multipliers_2); - p += 32; - len -= 32; - } - v0 = fold_vec(v0, v1, multipliers_1); - } else { - static const u64 _aligned_attribute(16) mults[4][2] = { - { CRC32_X1567_MODG, CRC32_X1503_MODG }, /* 12 vecs */ - { CRC32_X799_MODG, CRC32_X735_MODG }, /* 6 vecs */ - { CRC32_X415_MODG, CRC32_X351_MODG }, /* 3 vecs */ - { CRC32_X159_MODG, CRC32_X95_MODG }, /* 1 vecs */ - }; - const poly64x2_t multipliers_12 = load_multipliers(mults[0]); - const poly64x2_t multipliers_6 = load_multipliers(mults[1]); - const poly64x2_t multipliers_3 = load_multipliers(mults[2]); - const poly64x2_t multipliers_1 = load_multipliers(mults[3]); - const size_t align = -(uintptr_t)p & 15; - const uint8x16_t *vp; - - /* Align p to the next 16-byte boundary. */ - if (align) { - if (align & 1) - crc = __crc32b(crc, *p++); - if (align & 2) { - crc = __crc32h(crc, le16_bswap(*(u16 *)p)); - p += 2; - } - if (align & 4) { - crc = __crc32w(crc, le32_bswap(*(u32 *)p)); - p += 4; - } - if (align & 8) { - crc = __crc32d(crc, le64_bswap(*(u64 *)p)); - p += 8; - } - len -= align; - } - vp = (const uint8x16_t *)p; - v0 = veorq_u8(*vp++, u32_to_bytevec(crc)); - v1 = *vp++; - v2 = *vp++; - v3 = *vp++; - v4 = *vp++; - v5 = *vp++; - v6 = *vp++; - v7 = *vp++; - v8 = *vp++; - v9 = *vp++; - v10 = *vp++; - v11 = *vp++; - len -= 192; - /* Fold 192 bytes (12 vectors) at a time. */ - do { - v0 = fold_vec(v0, *vp++, multipliers_12); - v1 = fold_vec(v1, *vp++, multipliers_12); - v2 = fold_vec(v2, *vp++, multipliers_12); - v3 = fold_vec(v3, *vp++, multipliers_12); - v4 = fold_vec(v4, *vp++, multipliers_12); - v5 = fold_vec(v5, *vp++, multipliers_12); - v6 = fold_vec(v6, *vp++, multipliers_12); - v7 = fold_vec(v7, *vp++, multipliers_12); - v8 = fold_vec(v8, *vp++, multipliers_12); - v9 = fold_vec(v9, *vp++, multipliers_12); - v10 = fold_vec(v10, *vp++, multipliers_12); - v11 = fold_vec(v11, *vp++, multipliers_12); - len -= 192; - } while (len >= 192); - - /* - * Fewer than 192 bytes left. Fold v0-v11 down to just v0, - * while processing up to 144 more bytes. - */ - v0 = fold_vec(v0, v6, multipliers_6); - v1 = fold_vec(v1, v7, multipliers_6); - v2 = fold_vec(v2, v8, multipliers_6); - v3 = fold_vec(v3, v9, multipliers_6); - v4 = fold_vec(v4, v10, multipliers_6); - v5 = fold_vec(v5, v11, multipliers_6); - if (len >= 96) { - v0 = fold_vec(v0, *vp++, multipliers_6); - v1 = fold_vec(v1, *vp++, multipliers_6); - v2 = fold_vec(v2, *vp++, multipliers_6); - v3 = fold_vec(v3, *vp++, multipliers_6); - v4 = fold_vec(v4, *vp++, multipliers_6); - v5 = fold_vec(v5, *vp++, multipliers_6); - len -= 96; - } - v0 = fold_vec(v0, v3, multipliers_3); - v1 = fold_vec(v1, v4, multipliers_3); - v2 = fold_vec(v2, v5, multipliers_3); - if (len >= 48) { - v0 = fold_vec(v0, *vp++, multipliers_3); - v1 = fold_vec(v1, *vp++, multipliers_3); - v2 = fold_vec(v2, *vp++, multipliers_3); - len -= 48; - } - v0 = fold_vec(v0, v1, multipliers_1); - v0 = fold_vec(v0, v2, multipliers_1); - p = (const u8 *)vp; - } - /* Reduce 128 to 32 bits using crc32 instructions. */ - crc = __crc32d(0, vgetq_lane_u64(vreinterpretq_u64_u8(v0), 0)); - crc = __crc32d(crc, vgetq_lane_u64(vreinterpretq_u64_u8(v0), 1)); -tail: - /* Finish up the remainder using crc32 instructions. */ - if (len & 32) { - crc = __crc32d(crc, get_unaligned_le64(p + 0)); - crc = __crc32d(crc, get_unaligned_le64(p + 8)); - crc = __crc32d(crc, get_unaligned_le64(p + 16)); - crc = __crc32d(crc, get_unaligned_le64(p + 24)); - p += 32; - } - if (len & 16) { - crc = __crc32d(crc, get_unaligned_le64(p + 0)); - crc = __crc32d(crc, get_unaligned_le64(p + 8)); - p += 16; - } - if (len & 8) { - crc = __crc32d(crc, get_unaligned_le64(p)); - p += 8; - } - if (len & 4) { - crc = __crc32w(crc, get_unaligned_le32(p)); - p += 4; - } - if (len & 2) { - crc = __crc32h(crc, get_unaligned_le16(p)); - p += 2; - } - if (len & 1) - crc = __crc32b(crc, *p); - return crc; -} - -#undef SUFFIX -#undef ATTRIBUTES -#undef ENABLE_EOR3 diff --git a/vendor/libdeflate/lib/arm/matchfinder_impl.h b/vendor/libdeflate/lib/arm/matchfinder_impl.h deleted file mode 100644 index 79c1dbc9d3..0000000000 --- a/vendor/libdeflate/lib/arm/matchfinder_impl.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * arm/matchfinder_impl.h - ARM implementations of matchfinder functions - * - * Copyright 2016 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef LIB_ARM_MATCHFINDER_IMPL_H -#define LIB_ARM_MATCHFINDER_IMPL_H - -#include "cpu_features.h" - -#if HAVE_NEON_NATIVE -static forceinline void -matchfinder_init_neon(mf_pos_t *data, size_t size) -{ - int16x8_t *p = (int16x8_t *)data; - int16x8_t v = vdupq_n_s16(MATCHFINDER_INITVAL); - - STATIC_ASSERT(MATCHFINDER_MEM_ALIGNMENT % sizeof(*p) == 0); - STATIC_ASSERT(MATCHFINDER_SIZE_ALIGNMENT % (4 * sizeof(*p)) == 0); - STATIC_ASSERT(sizeof(mf_pos_t) == 2); - - do { - p[0] = v; - p[1] = v; - p[2] = v; - p[3] = v; - p += 4; - size -= 4 * sizeof(*p); - } while (size != 0); -} -#define matchfinder_init matchfinder_init_neon - -static forceinline void -matchfinder_rebase_neon(mf_pos_t *data, size_t size) -{ - int16x8_t *p = (int16x8_t *)data; - int16x8_t v = vdupq_n_s16((u16)-MATCHFINDER_WINDOW_SIZE); - - STATIC_ASSERT(MATCHFINDER_MEM_ALIGNMENT % sizeof(*p) == 0); - STATIC_ASSERT(MATCHFINDER_SIZE_ALIGNMENT % (4 * sizeof(*p)) == 0); - STATIC_ASSERT(sizeof(mf_pos_t) == 2); - - do { - p[0] = vqaddq_s16(p[0], v); - p[1] = vqaddq_s16(p[1], v); - p[2] = vqaddq_s16(p[2], v); - p[3] = vqaddq_s16(p[3], v); - p += 4; - size -= 4 * sizeof(*p); - } while (size != 0); -} -#define matchfinder_rebase matchfinder_rebase_neon - -#endif /* HAVE_NEON_NATIVE */ - -#endif /* LIB_ARM_MATCHFINDER_IMPL_H */ diff --git a/vendor/libdeflate/lib/bt_matchfinder.h b/vendor/libdeflate/lib/bt_matchfinder.h deleted file mode 100644 index b247d4bccf..0000000000 --- a/vendor/libdeflate/lib/bt_matchfinder.h +++ /dev/null @@ -1,342 +0,0 @@ -/* - * bt_matchfinder.h - Lempel-Ziv matchfinding with a hash table of binary trees - * - * Copyright 2016 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * ---------------------------------------------------------------------------- - * - * This is a Binary Trees (bt) based matchfinder. - * - * The main data structure is a hash table where each hash bucket contains a - * binary tree of sequences whose first 4 bytes share the same hash code. Each - * sequence is identified by its starting position in the input buffer. Each - * binary tree is always sorted such that each left child represents a sequence - * lexicographically lesser than its parent and each right child represents a - * sequence lexicographically greater than its parent. - * - * The algorithm processes the input buffer sequentially. At each byte - * position, the hash code of the first 4 bytes of the sequence beginning at - * that position (the sequence being matched against) is computed. This - * identifies the hash bucket to use for that position. Then, a new binary tree - * node is created to represent the current sequence. Then, in a single tree - * traversal, the hash bucket's binary tree is searched for matches and is - * re-rooted at the new node. - * - * Compared to the simpler algorithm that uses linked lists instead of binary - * trees (see hc_matchfinder.h), the binary tree version gains more information - * at each node visitation. Ideally, the binary tree version will examine only - * 'log(n)' nodes to find the same matches that the linked list version will - * find by examining 'n' nodes. In addition, the binary tree version can - * examine fewer bytes at each node by taking advantage of the common prefixes - * that result from the sort order, whereas the linked list version may have to - * examine up to the full length of the match at each node. - * - * However, it is not always best to use the binary tree version. It requires - * nearly twice as much memory as the linked list version, and it takes time to - * keep the binary trees sorted, even at positions where the compressor does not - * need matches. Generally, when doing fast compression on small buffers, - * binary trees are the wrong approach. They are best suited for thorough - * compression and/or large buffers. - * - * ---------------------------------------------------------------------------- - */ - -#ifndef LIB_BT_MATCHFINDER_H -#define LIB_BT_MATCHFINDER_H - -#include "matchfinder_common.h" - -#define BT_MATCHFINDER_HASH3_ORDER 16 -#define BT_MATCHFINDER_HASH3_WAYS 2 -#define BT_MATCHFINDER_HASH4_ORDER 16 - -#define BT_MATCHFINDER_TOTAL_HASH_SIZE \ - (((1UL << BT_MATCHFINDER_HASH3_ORDER) * BT_MATCHFINDER_HASH3_WAYS + \ - (1UL << BT_MATCHFINDER_HASH4_ORDER)) * sizeof(mf_pos_t)) - -/* Representation of a match found by the bt_matchfinder */ -struct lz_match { - - /* The number of bytes matched. */ - u16 length; - - /* The offset back from the current position that was matched. */ - u16 offset; -}; - -struct MATCHFINDER_ALIGNED bt_matchfinder { - - /* The hash table for finding length 3 matches */ - mf_pos_t hash3_tab[1UL << BT_MATCHFINDER_HASH3_ORDER][BT_MATCHFINDER_HASH3_WAYS]; - - /* The hash table which contains the roots of the binary trees for - * finding length 4+ matches */ - mf_pos_t hash4_tab[1UL << BT_MATCHFINDER_HASH4_ORDER]; - - /* The child node references for the binary trees. The left and right - * children of the node for the sequence with position 'pos' are - * 'child_tab[pos * 2]' and 'child_tab[pos * 2 + 1]', respectively. */ - mf_pos_t child_tab[2UL * MATCHFINDER_WINDOW_SIZE]; -}; - -/* Prepare the matchfinder for a new input buffer. */ -static forceinline void -bt_matchfinder_init(struct bt_matchfinder *mf) -{ - STATIC_ASSERT(BT_MATCHFINDER_TOTAL_HASH_SIZE % - MATCHFINDER_SIZE_ALIGNMENT == 0); - - matchfinder_init((mf_pos_t *)mf, BT_MATCHFINDER_TOTAL_HASH_SIZE); -} - -static forceinline void -bt_matchfinder_slide_window(struct bt_matchfinder *mf) -{ - STATIC_ASSERT(sizeof(*mf) % MATCHFINDER_SIZE_ALIGNMENT == 0); - - matchfinder_rebase((mf_pos_t *)mf, sizeof(*mf)); -} - -static forceinline mf_pos_t * -bt_left_child(struct bt_matchfinder *mf, s32 node) -{ - return &mf->child_tab[2 * (node & (MATCHFINDER_WINDOW_SIZE - 1)) + 0]; -} - -static forceinline mf_pos_t * -bt_right_child(struct bt_matchfinder *mf, s32 node) -{ - return &mf->child_tab[2 * (node & (MATCHFINDER_WINDOW_SIZE - 1)) + 1]; -} - -/* The minimum permissible value of 'max_len' for bt_matchfinder_get_matches() - * and bt_matchfinder_skip_byte(). There must be sufficiently many bytes - * remaining to load a 32-bit integer from the *next* position. */ -#define BT_MATCHFINDER_REQUIRED_NBYTES 5 - -/* Advance the binary tree matchfinder by one byte, optionally recording - * matches. @record_matches should be a compile-time constant. */ -static forceinline struct lz_match * -bt_matchfinder_advance_one_byte(struct bt_matchfinder * const mf, - const u8 * const in_base, - const ptrdiff_t cur_pos, - const u32 max_len, - const u32 nice_len, - const u32 max_search_depth, - u32 * const next_hashes, - struct lz_match *lz_matchptr, - const bool record_matches) -{ - const u8 *in_next = in_base + cur_pos; - u32 depth_remaining = max_search_depth; - const s32 cutoff = cur_pos - MATCHFINDER_WINDOW_SIZE; - u32 next_hashseq; - u32 hash3; - u32 hash4; - s32 cur_node; -#if BT_MATCHFINDER_HASH3_WAYS >= 2 - s32 cur_node_2; -#endif - const u8 *matchptr; - mf_pos_t *pending_lt_ptr, *pending_gt_ptr; - u32 best_lt_len, best_gt_len; - u32 len; - u32 best_len = 3; - - STATIC_ASSERT(BT_MATCHFINDER_HASH3_WAYS >= 1 && - BT_MATCHFINDER_HASH3_WAYS <= 2); - - next_hashseq = get_unaligned_le32(in_next + 1); - - hash3 = next_hashes[0]; - hash4 = next_hashes[1]; - - next_hashes[0] = lz_hash(next_hashseq & 0xFFFFFF, BT_MATCHFINDER_HASH3_ORDER); - next_hashes[1] = lz_hash(next_hashseq, BT_MATCHFINDER_HASH4_ORDER); - prefetchw(&mf->hash3_tab[next_hashes[0]]); - prefetchw(&mf->hash4_tab[next_hashes[1]]); - - cur_node = mf->hash3_tab[hash3][0]; - mf->hash3_tab[hash3][0] = cur_pos; -#if BT_MATCHFINDER_HASH3_WAYS >= 2 - cur_node_2 = mf->hash3_tab[hash3][1]; - mf->hash3_tab[hash3][1] = cur_node; -#endif - if (record_matches && cur_node > cutoff) { - u32 seq3 = load_u24_unaligned(in_next); - if (seq3 == load_u24_unaligned(&in_base[cur_node])) { - lz_matchptr->length = 3; - lz_matchptr->offset = in_next - &in_base[cur_node]; - lz_matchptr++; - } - #if BT_MATCHFINDER_HASH3_WAYS >= 2 - else if (cur_node_2 > cutoff && - seq3 == load_u24_unaligned(&in_base[cur_node_2])) - { - lz_matchptr->length = 3; - lz_matchptr->offset = in_next - &in_base[cur_node_2]; - lz_matchptr++; - } - #endif - } - - cur_node = mf->hash4_tab[hash4]; - mf->hash4_tab[hash4] = cur_pos; - - pending_lt_ptr = bt_left_child(mf, cur_pos); - pending_gt_ptr = bt_right_child(mf, cur_pos); - - if (cur_node <= cutoff) { - *pending_lt_ptr = MATCHFINDER_INITVAL; - *pending_gt_ptr = MATCHFINDER_INITVAL; - return lz_matchptr; - } - - best_lt_len = 0; - best_gt_len = 0; - len = 0; - - for (;;) { - matchptr = &in_base[cur_node]; - - if (matchptr[len] == in_next[len]) { - len = lz_extend(in_next, matchptr, len + 1, max_len); - if (!record_matches || len > best_len) { - if (record_matches) { - best_len = len; - lz_matchptr->length = len; - lz_matchptr->offset = in_next - matchptr; - lz_matchptr++; - } - if (len >= nice_len) { - *pending_lt_ptr = *bt_left_child(mf, cur_node); - *pending_gt_ptr = *bt_right_child(mf, cur_node); - return lz_matchptr; - } - } - } - - if (matchptr[len] < in_next[len]) { - *pending_lt_ptr = cur_node; - pending_lt_ptr = bt_right_child(mf, cur_node); - cur_node = *pending_lt_ptr; - best_lt_len = len; - if (best_gt_len < len) - len = best_gt_len; - } else { - *pending_gt_ptr = cur_node; - pending_gt_ptr = bt_left_child(mf, cur_node); - cur_node = *pending_gt_ptr; - best_gt_len = len; - if (best_lt_len < len) - len = best_lt_len; - } - - if (cur_node <= cutoff || !--depth_remaining) { - *pending_lt_ptr = MATCHFINDER_INITVAL; - *pending_gt_ptr = MATCHFINDER_INITVAL; - return lz_matchptr; - } - } -} - -/* - * Retrieve a list of matches with the current position. - * - * @mf - * The matchfinder structure. - * @in_base - * Pointer to the next byte in the input buffer to process _at the last - * time bt_matchfinder_init() or bt_matchfinder_slide_window() was called_. - * @cur_pos - * The current position in the input buffer relative to @in_base (the - * position of the sequence being matched against). - * @max_len - * The maximum permissible match length at this position. Must be >= - * BT_MATCHFINDER_REQUIRED_NBYTES. - * @nice_len - * Stop searching if a match of at least this length is found. - * Must be <= @max_len. - * @max_search_depth - * Limit on the number of potential matches to consider. Must be >= 1. - * @next_hashes - * The precomputed hash codes for the sequence beginning at @in_next. - * These will be used and then updated with the precomputed hashcodes for - * the sequence beginning at @in_next + 1. - * @lz_matchptr - * An array in which this function will record the matches. The recorded - * matches will be sorted by strictly increasing length and (non-strictly) - * increasing offset. The maximum number of matches that may be found is - * 'nice_len - 2'. - * - * The return value is a pointer to the next available slot in the @lz_matchptr - * array. (If no matches were found, this will be the same as @lz_matchptr.) - */ -static forceinline struct lz_match * -bt_matchfinder_get_matches(struct bt_matchfinder *mf, - const u8 *in_base, - ptrdiff_t cur_pos, - u32 max_len, - u32 nice_len, - u32 max_search_depth, - u32 next_hashes[2], - struct lz_match *lz_matchptr) -{ - return bt_matchfinder_advance_one_byte(mf, - in_base, - cur_pos, - max_len, - nice_len, - max_search_depth, - next_hashes, - lz_matchptr, - true); -} - -/* - * Advance the matchfinder, but don't record any matches. - * - * This is very similar to bt_matchfinder_get_matches() because both functions - * must do hashing and tree re-rooting. - */ -static forceinline void -bt_matchfinder_skip_byte(struct bt_matchfinder *mf, - const u8 *in_base, - ptrdiff_t cur_pos, - u32 nice_len, - u32 max_search_depth, - u32 next_hashes[2]) -{ - bt_matchfinder_advance_one_byte(mf, - in_base, - cur_pos, - nice_len, - nice_len, - max_search_depth, - next_hashes, - NULL, - false); -} - -#endif /* LIB_BT_MATCHFINDER_H */ diff --git a/vendor/libdeflate/lib/cpu_features_common.h b/vendor/libdeflate/lib/cpu_features_common.h deleted file mode 100644 index d0531d5c6f..0000000000 --- a/vendor/libdeflate/lib/cpu_features_common.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * cpu_features_common.h - code shared by all lib/$arch/cpu_features.c - * - * Copyright 2020 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef LIB_CPU_FEATURES_COMMON_H -#define LIB_CPU_FEATURES_COMMON_H - -#if defined(TEST_SUPPORT__DO_NOT_USE) && !defined(FREESTANDING) - /* for strdup() and strtok_r() */ -# undef _ANSI_SOURCE -# ifndef __APPLE__ -# undef _GNU_SOURCE -# define _GNU_SOURCE -# endif -# include -# include -# include -#endif - -#include "lib_common.h" - -struct cpu_feature { - u32 bit; - const char *name; -}; - -#if defined(TEST_SUPPORT__DO_NOT_USE) && !defined(FREESTANDING) -/* Disable any features that are listed in $LIBDEFLATE_DISABLE_CPU_FEATURES. */ -static inline void -disable_cpu_features_for_testing(u32 *features, - const struct cpu_feature *feature_table, - size_t feature_table_length) -{ - char *env_value, *strbuf, *p, *saveptr = NULL; - size_t i; - - env_value = getenv("LIBDEFLATE_DISABLE_CPU_FEATURES"); - if (!env_value) - return; - strbuf = strdup(env_value); - if (!strbuf) - abort(); - p = strtok_r(strbuf, ",", &saveptr); - while (p) { - for (i = 0; i < feature_table_length; i++) { - if (strcmp(p, feature_table[i].name) == 0) { - *features &= ~feature_table[i].bit; - break; - } - } - if (i == feature_table_length) { - fprintf(stderr, - "unrecognized feature in LIBDEFLATE_DISABLE_CPU_FEATURES: \"%s\"\n", - p); - abort(); - } - p = strtok_r(NULL, ",", &saveptr); - } - free(strbuf); -} -#else /* TEST_SUPPORT__DO_NOT_USE */ -static inline void -disable_cpu_features_for_testing(u32 *features, - const struct cpu_feature *feature_table, - size_t feature_table_length) -{ -} -#endif /* !TEST_SUPPORT__DO_NOT_USE */ - -#endif /* LIB_CPU_FEATURES_COMMON_H */ diff --git a/vendor/libdeflate/lib/crc32.c b/vendor/libdeflate/lib/crc32.c deleted file mode 100644 index a0ec0223b8..0000000000 --- a/vendor/libdeflate/lib/crc32.c +++ /dev/null @@ -1,262 +0,0 @@ -/* - * crc32.c - CRC-32 checksum algorithm for the gzip format - * - * Copyright 2016 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * High-level description of CRC - * ============================= - * - * Consider a bit sequence 'bits[1...len]'. Interpret 'bits' as the "message" - * polynomial M(x) with coefficients in GF(2) (the field of integers modulo 2), - * where the coefficient of 'x^i' is 'bits[len - i]'. Then, compute: - * - * R(x) = M(x)*x^n mod G(x) - * - * where G(x) is a selected "generator" polynomial of degree 'n'. The remainder - * R(x) is a polynomial of max degree 'n - 1'. The CRC of 'bits' is R(x) - * interpreted as a bitstring of length 'n'. - * - * CRC used in gzip - * ================ - * - * In the gzip format (RFC 1952): - * - * - The bitstring to checksum is formed from the bytes of the uncompressed - * data by concatenating the bits from the bytes in order, proceeding - * from the low-order bit to the high-order bit within each byte. - * - * - The generator polynomial G(x) is: x^32 + x^26 + x^23 + x^22 + x^16 + - * x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1. - * Consequently, the CRC length is 32 bits ("CRC-32"). - * - * - The highest order 32 coefficients of M(x)*x^n are inverted. - * - * - All 32 coefficients of R(x) are inverted. - * - * The two inversions cause added leading and trailing zero bits to affect the - * resulting CRC, whereas with a regular CRC such bits would have no effect on - * the CRC. - * - * Computation and optimizations - * ============================= - * - * We can compute R(x) through "long division", maintaining only 32 bits of - * state at any given time. Multiplication by 'x' can be implemented as - * right-shifting by 1 (assuming the polynomial<=>bitstring mapping where the - * highest order bit represents the coefficient of x^0), and both addition and - * subtraction can be implemented as bitwise exclusive OR (since we are working - * in GF(2)). Here is an unoptimized implementation: - * - * static u32 crc32_gzip(const u8 *p, size_t len) - * { - * u32 crc = 0; - * const u32 divisor = 0xEDB88320; - * - * for (size_t i = 0; i < len * 8 + 32; i++) { - * int bit; - * u32 multiple; - * - * if (i < len * 8) - * bit = (p[i / 8] >> (i % 8)) & 1; - * else - * bit = 0; // one of the 32 appended 0 bits - * - * if (i < 32) // the first 32 bits are inverted - * bit ^= 1; - * - * if (crc & 1) - * multiple = divisor; - * else - * multiple = 0; - * - * crc >>= 1; - * crc |= (u32)bit << 31; - * crc ^= multiple; - * } - * - * return ~crc; - * } - * - * In this implementation, the 32-bit integer 'crc' maintains the remainder of - * the currently processed portion of the message (with 32 zero bits appended) - * when divided by the generator polynomial. 'crc' is the representation of - * R(x), and 'divisor' is the representation of G(x) excluding the x^32 - * coefficient. For each bit to process, we multiply R(x) by 'x^1', then add - * 'x^0' if the new bit is a 1. If this causes R(x) to gain a nonzero x^32 - * term, then we subtract G(x) from R(x). - * - * We can speed this up by taking advantage of the fact that XOR is commutative - * and associative, so the order in which we combine the inputs into 'crc' is - * unimportant. And since each message bit we add doesn't affect the choice of - * 'multiple' until 32 bits later, we need not actually add each message bit - * until that point: - * - * static u32 crc32_gzip(const u8 *p, size_t len) - * { - * u32 crc = ~0; - * const u32 divisor = 0xEDB88320; - * - * for (size_t i = 0; i < len * 8; i++) { - * int bit; - * u32 multiple; - * - * bit = (p[i / 8] >> (i % 8)) & 1; - * crc ^= bit; - * if (crc & 1) - * multiple = divisor; - * else - * multiple = 0; - * crc >>= 1; - * crc ^= multiple; - * } - * - * return ~crc; - * } - * - * With the above implementation we get the effect of 32 appended 0 bits for - * free; they never affect the choice of a divisor, nor would they change the - * value of 'crc' if they were to be actually XOR'ed in. And by starting with a - * remainder of all 1 bits, we get the effect of complementing the first 32 - * message bits. - * - * The next optimization is to process the input in multi-bit units. Suppose - * that we insert the next 'n' message bits into the remainder. Then we get an - * intermediate remainder of length '32 + n' bits, and the CRC of the extra 'n' - * bits is the amount by which the low 32 bits of the remainder will change as a - * result of cancelling out those 'n' bits. Taking n=8 (one byte) and - * precomputing a table containing the CRC of each possible byte, we get - * crc32_slice1() defined below. - * - * As a further optimization, we could increase the multi-bit unit size to 16. - * However, that is inefficient because the table size explodes from 256 entries - * (1024 bytes) to 65536 entries (262144 bytes), which wastes memory and won't - * fit in L1 cache on typical processors. - * - * However, we can actually process 4 bytes at a time using 4 different tables - * with 256 entries each. Logically, we form a 64-bit intermediate remainder - * and cancel out the high 32 bits in 8-bit chunks. Bits 32-39 are cancelled - * out by the CRC of those bits, whereas bits 40-47 are be cancelled out by the - * CRC of those bits with 8 zero bits appended, and so on. - * - * In crc32_slice8(), this method is extended to 8 bytes at a time. The - * intermediate remainder (which we never actually store explicitly) is 96 bits. - * - * On CPUs that support fast carryless multiplication, CRCs can be computed even - * more quickly via "folding". See e.g. the x86 PCLMUL implementations. - */ - -#include "lib_common.h" -#include "crc32_multipliers.h" -#include "crc32_tables.h" - -/* This is the default implementation. It uses the slice-by-8 method. */ -static u32 MAYBE_UNUSED -crc32_slice8(u32 crc, const u8 *p, size_t len) -{ - const u8 * const end = p + len; - const u8 *end64; - - for (; ((uintptr_t)p & 7) && p != end; p++) - crc = (crc >> 8) ^ crc32_slice8_table[(u8)crc ^ *p]; - - end64 = p + ((end - p) & ~7); - for (; p != end64; p += 8) { - u32 v1 = le32_bswap(*(const u32 *)(p + 0)); - u32 v2 = le32_bswap(*(const u32 *)(p + 4)); - - crc = crc32_slice8_table[0x700 + (u8)((crc ^ v1) >> 0)] ^ - crc32_slice8_table[0x600 + (u8)((crc ^ v1) >> 8)] ^ - crc32_slice8_table[0x500 + (u8)((crc ^ v1) >> 16)] ^ - crc32_slice8_table[0x400 + (u8)((crc ^ v1) >> 24)] ^ - crc32_slice8_table[0x300 + (u8)(v2 >> 0)] ^ - crc32_slice8_table[0x200 + (u8)(v2 >> 8)] ^ - crc32_slice8_table[0x100 + (u8)(v2 >> 16)] ^ - crc32_slice8_table[0x000 + (u8)(v2 >> 24)]; - } - - for (; p != end; p++) - crc = (crc >> 8) ^ crc32_slice8_table[(u8)crc ^ *p]; - - return crc; -} - -/* - * This is a more lightweight generic implementation, which can be used as a - * subroutine by architecture-specific implementations to process small amounts - * of unaligned data at the beginning and/or end of the buffer. - */ -static forceinline u32 MAYBE_UNUSED -crc32_slice1(u32 crc, const u8 *p, size_t len) -{ - size_t i; - - for (i = 0; i < len; i++) - crc = (crc >> 8) ^ crc32_slice1_table[(u8)crc ^ p[i]]; - return crc; -} - -/* Include architecture-specific implementation(s) if available. */ -#undef DEFAULT_IMPL -#undef arch_select_crc32_func -typedef u32 (*crc32_func_t)(u32 crc, const u8 *p, size_t len); -#if defined(ARCH_ARM32) || defined(ARCH_ARM64) -# include "arm/crc32_impl.h" -#elif defined(ARCH_X86_32) || defined(ARCH_X86_64) -# include "x86/crc32_impl.h" -#endif - -#ifndef DEFAULT_IMPL -# define DEFAULT_IMPL crc32_slice8 -#endif - -#ifdef arch_select_crc32_func -static u32 dispatch_crc32(u32 crc, const u8 *p, size_t len); - -static volatile crc32_func_t crc32_impl = dispatch_crc32; - -/* Choose the best implementation at runtime. */ -static u32 dispatch_crc32(u32 crc, const u8 *p, size_t len) -{ - crc32_func_t f = arch_select_crc32_func(); - - if (f == NULL) - f = DEFAULT_IMPL; - - crc32_impl = f; - return f(crc, p, len); -} -#else -/* The best implementation is statically known, so call it directly. */ -#define crc32_impl DEFAULT_IMPL -#endif - -LIBDEFLATEAPI u32 -libdeflate_crc32(u32 crc, const void *p, size_t len) -{ - if (p == NULL) /* Return initial value. */ - return 0; - return ~crc32_impl(~crc, p, len); -} diff --git a/vendor/libdeflate/lib/crc32_multipliers.h b/vendor/libdeflate/lib/crc32_multipliers.h deleted file mode 100644 index 9a08745e67..0000000000 --- a/vendor/libdeflate/lib/crc32_multipliers.h +++ /dev/null @@ -1,375 +0,0 @@ -/* - * crc32_multipliers.h - constants for CRC-32 folding - * - * THIS FILE WAS GENERATED BY gen-crc32-consts.py. DO NOT EDIT. - */ - -#define CRC32_X159_MODG 0xae689191 /* x^159 mod G(x) */ -#define CRC32_X95_MODG 0xccaa009e /* x^95 mod G(x) */ - -#define CRC32_X287_MODG 0xf1da05aa /* x^287 mod G(x) */ -#define CRC32_X223_MODG 0x81256527 /* x^223 mod G(x) */ - -#define CRC32_X415_MODG 0x3db1ecdc /* x^415 mod G(x) */ -#define CRC32_X351_MODG 0xaf449247 /* x^351 mod G(x) */ - -#define CRC32_X543_MODG 0x8f352d95 /* x^543 mod G(x) */ -#define CRC32_X479_MODG 0x1d9513d7 /* x^479 mod G(x) */ - -#define CRC32_X671_MODG 0x1c279815 /* x^671 mod G(x) */ -#define CRC32_X607_MODG 0xae0b5394 /* x^607 mod G(x) */ - -#define CRC32_X799_MODG 0xdf068dc2 /* x^799 mod G(x) */ -#define CRC32_X735_MODG 0x57c54819 /* x^735 mod G(x) */ - -#define CRC32_X927_MODG 0x31f8303f /* x^927 mod G(x) */ -#define CRC32_X863_MODG 0x0cbec0ed /* x^863 mod G(x) */ - -#define CRC32_X1055_MODG 0x33fff533 /* x^1055 mod G(x) */ -#define CRC32_X991_MODG 0x910eeec1 /* x^991 mod G(x) */ - -#define CRC32_X1183_MODG 0x26b70c3d /* x^1183 mod G(x) */ -#define CRC32_X1119_MODG 0x3f41287a /* x^1119 mod G(x) */ - -#define CRC32_X1311_MODG 0xe3543be0 /* x^1311 mod G(x) */ -#define CRC32_X1247_MODG 0x9026d5b1 /* x^1247 mod G(x) */ - -#define CRC32_X1439_MODG 0x5a1bb05d /* x^1439 mod G(x) */ -#define CRC32_X1375_MODG 0xd1df2327 /* x^1375 mod G(x) */ - -#define CRC32_X1567_MODG 0x596c8d81 /* x^1567 mod G(x) */ -#define CRC32_X1503_MODG 0xf5e48c85 /* x^1503 mod G(x) */ - -#define CRC32_X1695_MODG 0x682bdd4f /* x^1695 mod G(x) */ -#define CRC32_X1631_MODG 0x3c656ced /* x^1631 mod G(x) */ - -#define CRC32_X1823_MODG 0x4a28bd43 /* x^1823 mod G(x) */ -#define CRC32_X1759_MODG 0xfe807bbd /* x^1759 mod G(x) */ - -#define CRC32_X1951_MODG 0x0077f00d /* x^1951 mod G(x) */ -#define CRC32_X1887_MODG 0x1f0c2cdd /* x^1887 mod G(x) */ - -#define CRC32_X2079_MODG 0xce3371cb /* x^2079 mod G(x) */ -#define CRC32_X2015_MODG 0xe95c1271 /* x^2015 mod G(x) */ - -#define CRC32_X2207_MODG 0xa749e894 /* x^2207 mod G(x) */ -#define CRC32_X2143_MODG 0xb918a347 /* x^2143 mod G(x) */ - -#define CRC32_X2335_MODG 0x2c538639 /* x^2335 mod G(x) */ -#define CRC32_X2271_MODG 0x71d54a59 /* x^2271 mod G(x) */ - -#define CRC32_X2463_MODG 0x32b0733c /* x^2463 mod G(x) */ -#define CRC32_X2399_MODG 0xff6f2fc2 /* x^2399 mod G(x) */ - -#define CRC32_X2591_MODG 0x0e9bd5cc /* x^2591 mod G(x) */ -#define CRC32_X2527_MODG 0xcec97417 /* x^2527 mod G(x) */ - -#define CRC32_X2719_MODG 0x76278617 /* x^2719 mod G(x) */ -#define CRC32_X2655_MODG 0x1c63267b /* x^2655 mod G(x) */ - -#define CRC32_X2847_MODG 0xc51b93e3 /* x^2847 mod G(x) */ -#define CRC32_X2783_MODG 0xf183c71b /* x^2783 mod G(x) */ - -#define CRC32_X2975_MODG 0x7eaed122 /* x^2975 mod G(x) */ -#define CRC32_X2911_MODG 0x9b9bdbd0 /* x^2911 mod G(x) */ - -#define CRC32_X3103_MODG 0x2ce423f1 /* x^3103 mod G(x) */ -#define CRC32_X3039_MODG 0xd31343ea /* x^3039 mod G(x) */ - -#define CRC32_X3231_MODG 0x8b8d8645 /* x^3231 mod G(x) */ -#define CRC32_X3167_MODG 0x4470ac44 /* x^3167 mod G(x) */ - -#define CRC32_X3359_MODG 0x4b700aa8 /* x^3359 mod G(x) */ -#define CRC32_X3295_MODG 0xeea395c4 /* x^3295 mod G(x) */ - -#define CRC32_X3487_MODG 0xeff5e99d /* x^3487 mod G(x) */ -#define CRC32_X3423_MODG 0xf9d9c7ee /* x^3423 mod G(x) */ - -#define CRC32_X3615_MODG 0xad0d2bb2 /* x^3615 mod G(x) */ -#define CRC32_X3551_MODG 0xcd669a40 /* x^3551 mod G(x) */ - -#define CRC32_X3743_MODG 0x9fb66bd3 /* x^3743 mod G(x) */ -#define CRC32_X3679_MODG 0x6d40f445 /* x^3679 mod G(x) */ - -#define CRC32_X3871_MODG 0xc2dcc467 /* x^3871 mod G(x) */ -#define CRC32_X3807_MODG 0x9ee62949 /* x^3807 mod G(x) */ - -#define CRC32_X3999_MODG 0x398e2ff2 /* x^3999 mod G(x) */ -#define CRC32_X3935_MODG 0x145575d5 /* x^3935 mod G(x) */ - -#define CRC32_X4127_MODG 0x1072db28 /* x^4127 mod G(x) */ -#define CRC32_X4063_MODG 0x0c30f51d /* x^4063 mod G(x) */ - -#define CRC32_BARRETT_CONSTANT_1 0xb4e5b025f7011641ULL /* floor(x^95 / G(x)) */ -#define CRC32_BARRETT_CONSTANT_2 0x00000001db710641ULL /* G(x) */ - -#define CRC32_NUM_CHUNKS 4 -#define CRC32_MIN_VARIABLE_CHUNK_LEN 128UL -#define CRC32_MAX_VARIABLE_CHUNK_LEN 16384UL - -/* Multipliers for implementations that use a variable chunk length */ -static const u32 crc32_mults_for_chunklen[][CRC32_NUM_CHUNKS - 1] MAYBE_UNUSED = { - { 0 /* unused row */ }, - /* chunk_len=128 */ - { 0xd31343ea /* x^3039 mod G(x) */, 0xe95c1271 /* x^2015 mod G(x) */, 0x910eeec1 /* x^991 mod G(x) */, }, - /* chunk_len=256 */ - { 0x1d6708a0 /* x^6111 mod G(x) */, 0x0c30f51d /* x^4063 mod G(x) */, 0xe95c1271 /* x^2015 mod G(x) */, }, - /* chunk_len=384 */ - { 0xdb3839f3 /* x^9183 mod G(x) */, 0x1d6708a0 /* x^6111 mod G(x) */, 0xd31343ea /* x^3039 mod G(x) */, }, - /* chunk_len=512 */ - { 0x1753ab84 /* x^12255 mod G(x) */, 0xbbf2f6d6 /* x^8159 mod G(x) */, 0x0c30f51d /* x^4063 mod G(x) */, }, - /* chunk_len=640 */ - { 0x3796455c /* x^15327 mod G(x) */, 0xb8e0e4a8 /* x^10207 mod G(x) */, 0xc352f6de /* x^5087 mod G(x) */, }, - /* chunk_len=768 */ - { 0x3954de39 /* x^18399 mod G(x) */, 0x1753ab84 /* x^12255 mod G(x) */, 0x1d6708a0 /* x^6111 mod G(x) */, }, - /* chunk_len=896 */ - { 0x632d78c5 /* x^21471 mod G(x) */, 0x3fc33de4 /* x^14303 mod G(x) */, 0x9a1b53c8 /* x^7135 mod G(x) */, }, - /* chunk_len=1024 */ - { 0xa0decef3 /* x^24543 mod G(x) */, 0x7b4aa8b7 /* x^16351 mod G(x) */, 0xbbf2f6d6 /* x^8159 mod G(x) */, }, - /* chunk_len=1152 */ - { 0xe9c09bb0 /* x^27615 mod G(x) */, 0x3954de39 /* x^18399 mod G(x) */, 0xdb3839f3 /* x^9183 mod G(x) */, }, - /* chunk_len=1280 */ - { 0xd51917a4 /* x^30687 mod G(x) */, 0xcae68461 /* x^20447 mod G(x) */, 0xb8e0e4a8 /* x^10207 mod G(x) */, }, - /* chunk_len=1408 */ - { 0x154a8a62 /* x^33759 mod G(x) */, 0x41e7589c /* x^22495 mod G(x) */, 0x3e9a43cd /* x^11231 mod G(x) */, }, - /* chunk_len=1536 */ - { 0xf196555d /* x^36831 mod G(x) */, 0xa0decef3 /* x^24543 mod G(x) */, 0x1753ab84 /* x^12255 mod G(x) */, }, - /* chunk_len=1664 */ - { 0x8eec2999 /* x^39903 mod G(x) */, 0xefb0a128 /* x^26591 mod G(x) */, 0x6044fbb0 /* x^13279 mod G(x) */, }, - /* chunk_len=1792 */ - { 0x27892abf /* x^42975 mod G(x) */, 0x48d72bb1 /* x^28639 mod G(x) */, 0x3fc33de4 /* x^14303 mod G(x) */, }, - /* chunk_len=1920 */ - { 0x77bc2419 /* x^46047 mod G(x) */, 0xd51917a4 /* x^30687 mod G(x) */, 0x3796455c /* x^15327 mod G(x) */, }, - /* chunk_len=2048 */ - { 0xcea114a5 /* x^49119 mod G(x) */, 0x68c0a2c5 /* x^32735 mod G(x) */, 0x7b4aa8b7 /* x^16351 mod G(x) */, }, - /* chunk_len=2176 */ - { 0xa1077e85 /* x^52191 mod G(x) */, 0x188cc628 /* x^34783 mod G(x) */, 0x0c21f835 /* x^17375 mod G(x) */, }, - /* chunk_len=2304 */ - { 0xc5ed75e1 /* x^55263 mod G(x) */, 0xf196555d /* x^36831 mod G(x) */, 0x3954de39 /* x^18399 mod G(x) */, }, - /* chunk_len=2432 */ - { 0xca4fba3f /* x^58335 mod G(x) */, 0x0acfa26f /* x^38879 mod G(x) */, 0x6cb21510 /* x^19423 mod G(x) */, }, - /* chunk_len=2560 */ - { 0xcf5bcdc4 /* x^61407 mod G(x) */, 0x4fae7fc0 /* x^40927 mod G(x) */, 0xcae68461 /* x^20447 mod G(x) */, }, - /* chunk_len=2688 */ - { 0xf36b9d16 /* x^64479 mod G(x) */, 0x27892abf /* x^42975 mod G(x) */, 0x632d78c5 /* x^21471 mod G(x) */, }, - /* chunk_len=2816 */ - { 0xf76fd988 /* x^67551 mod G(x) */, 0xed5c39b1 /* x^45023 mod G(x) */, 0x41e7589c /* x^22495 mod G(x) */, }, - /* chunk_len=2944 */ - { 0x6c45d92e /* x^70623 mod G(x) */, 0xff809fcd /* x^47071 mod G(x) */, 0x0c46baec /* x^23519 mod G(x) */, }, - /* chunk_len=3072 */ - { 0x6116b82b /* x^73695 mod G(x) */, 0xcea114a5 /* x^49119 mod G(x) */, 0xa0decef3 /* x^24543 mod G(x) */, }, - /* chunk_len=3200 */ - { 0x4d9899bb /* x^76767 mod G(x) */, 0x9f9d8d9c /* x^51167 mod G(x) */, 0x53deb236 /* x^25567 mod G(x) */, }, - /* chunk_len=3328 */ - { 0x3e7c93b9 /* x^79839 mod G(x) */, 0x6666b805 /* x^53215 mod G(x) */, 0xefb0a128 /* x^26591 mod G(x) */, }, - /* chunk_len=3456 */ - { 0x388b20ac /* x^82911 mod G(x) */, 0xc5ed75e1 /* x^55263 mod G(x) */, 0xe9c09bb0 /* x^27615 mod G(x) */, }, - /* chunk_len=3584 */ - { 0x0956d953 /* x^85983 mod G(x) */, 0x97fbdb14 /* x^57311 mod G(x) */, 0x48d72bb1 /* x^28639 mod G(x) */, }, - /* chunk_len=3712 */ - { 0x55cb4dfe /* x^89055 mod G(x) */, 0x1b37c832 /* x^59359 mod G(x) */, 0xc07331b3 /* x^29663 mod G(x) */, }, - /* chunk_len=3840 */ - { 0x52222fea /* x^92127 mod G(x) */, 0xcf5bcdc4 /* x^61407 mod G(x) */, 0xd51917a4 /* x^30687 mod G(x) */, }, - /* chunk_len=3968 */ - { 0x0603989b /* x^95199 mod G(x) */, 0xb03c8112 /* x^63455 mod G(x) */, 0x5e04b9a5 /* x^31711 mod G(x) */, }, - /* chunk_len=4096 */ - { 0x4470c029 /* x^98271 mod G(x) */, 0x2339d155 /* x^65503 mod G(x) */, 0x68c0a2c5 /* x^32735 mod G(x) */, }, - /* chunk_len=4224 */ - { 0xb6f35093 /* x^101343 mod G(x) */, 0xf76fd988 /* x^67551 mod G(x) */, 0x154a8a62 /* x^33759 mod G(x) */, }, - /* chunk_len=4352 */ - { 0xc46805ba /* x^104415 mod G(x) */, 0x416f9449 /* x^69599 mod G(x) */, 0x188cc628 /* x^34783 mod G(x) */, }, - /* chunk_len=4480 */ - { 0xc3876592 /* x^107487 mod G(x) */, 0x4b809189 /* x^71647 mod G(x) */, 0xc35cf6e7 /* x^35807 mod G(x) */, }, - /* chunk_len=4608 */ - { 0x5b0c98b9 /* x^110559 mod G(x) */, 0x6116b82b /* x^73695 mod G(x) */, 0xf196555d /* x^36831 mod G(x) */, }, - /* chunk_len=4736 */ - { 0x30d13e5f /* x^113631 mod G(x) */, 0x4c5a315a /* x^75743 mod G(x) */, 0x8c224466 /* x^37855 mod G(x) */, }, - /* chunk_len=4864 */ - { 0x54afca53 /* x^116703 mod G(x) */, 0xbccfa2c1 /* x^77791 mod G(x) */, 0x0acfa26f /* x^38879 mod G(x) */, }, - /* chunk_len=4992 */ - { 0x93102436 /* x^119775 mod G(x) */, 0x3e7c93b9 /* x^79839 mod G(x) */, 0x8eec2999 /* x^39903 mod G(x) */, }, - /* chunk_len=5120 */ - { 0xbd2655a8 /* x^122847 mod G(x) */, 0x3e116c9d /* x^81887 mod G(x) */, 0x4fae7fc0 /* x^40927 mod G(x) */, }, - /* chunk_len=5248 */ - { 0x70cd7f26 /* x^125919 mod G(x) */, 0x408e57f2 /* x^83935 mod G(x) */, 0x1691be45 /* x^41951 mod G(x) */, }, - /* chunk_len=5376 */ - { 0x2d546c53 /* x^128991 mod G(x) */, 0x0956d953 /* x^85983 mod G(x) */, 0x27892abf /* x^42975 mod G(x) */, }, - /* chunk_len=5504 */ - { 0xb53410a8 /* x^132063 mod G(x) */, 0x42ebf0ad /* x^88031 mod G(x) */, 0x161f3c12 /* x^43999 mod G(x) */, }, - /* chunk_len=5632 */ - { 0x67a93f75 /* x^135135 mod G(x) */, 0xcf3233e4 /* x^90079 mod G(x) */, 0xed5c39b1 /* x^45023 mod G(x) */, }, - /* chunk_len=5760 */ - { 0x9830ac33 /* x^138207 mod G(x) */, 0x52222fea /* x^92127 mod G(x) */, 0x77bc2419 /* x^46047 mod G(x) */, }, - /* chunk_len=5888 */ - { 0xb0b6fc3e /* x^141279 mod G(x) */, 0x2fde73f8 /* x^94175 mod G(x) */, 0xff809fcd /* x^47071 mod G(x) */, }, - /* chunk_len=6016 */ - { 0x84170f16 /* x^144351 mod G(x) */, 0xced90d99 /* x^96223 mod G(x) */, 0x30de0f98 /* x^48095 mod G(x) */, }, - /* chunk_len=6144 */ - { 0xd7017a0c /* x^147423 mod G(x) */, 0x4470c029 /* x^98271 mod G(x) */, 0xcea114a5 /* x^49119 mod G(x) */, }, - /* chunk_len=6272 */ - { 0xadb25de6 /* x^150495 mod G(x) */, 0x84f40beb /* x^100319 mod G(x) */, 0x2b7e0e1b /* x^50143 mod G(x) */, }, - /* chunk_len=6400 */ - { 0x8282fddc /* x^153567 mod G(x) */, 0xec855937 /* x^102367 mod G(x) */, 0x9f9d8d9c /* x^51167 mod G(x) */, }, - /* chunk_len=6528 */ - { 0x46362bee /* x^156639 mod G(x) */, 0xc46805ba /* x^104415 mod G(x) */, 0xa1077e85 /* x^52191 mod G(x) */, }, - /* chunk_len=6656 */ - { 0xb9077a01 /* x^159711 mod G(x) */, 0xdf7a24ac /* x^106463 mod G(x) */, 0x6666b805 /* x^53215 mod G(x) */, }, - /* chunk_len=6784 */ - { 0xf51d9bc6 /* x^162783 mod G(x) */, 0x2b52dc39 /* x^108511 mod G(x) */, 0x7e774cf6 /* x^54239 mod G(x) */, }, - /* chunk_len=6912 */ - { 0x4ca19a29 /* x^165855 mod G(x) */, 0x5b0c98b9 /* x^110559 mod G(x) */, 0xc5ed75e1 /* x^55263 mod G(x) */, }, - /* chunk_len=7040 */ - { 0xdc0fc3fc /* x^168927 mod G(x) */, 0xb939fcdf /* x^112607 mod G(x) */, 0x3678fed2 /* x^56287 mod G(x) */, }, - /* chunk_len=7168 */ - { 0x63c3d167 /* x^171999 mod G(x) */, 0x70f9947d /* x^114655 mod G(x) */, 0x97fbdb14 /* x^57311 mod G(x) */, }, - /* chunk_len=7296 */ - { 0x5851d254 /* x^175071 mod G(x) */, 0x54afca53 /* x^116703 mod G(x) */, 0xca4fba3f /* x^58335 mod G(x) */, }, - /* chunk_len=7424 */ - { 0xfeacf2a1 /* x^178143 mod G(x) */, 0x7a3c0a6a /* x^118751 mod G(x) */, 0x1b37c832 /* x^59359 mod G(x) */, }, - /* chunk_len=7552 */ - { 0x93b7edc8 /* x^181215 mod G(x) */, 0x1fea4d2a /* x^120799 mod G(x) */, 0x58fa96ee /* x^60383 mod G(x) */, }, - /* chunk_len=7680 */ - { 0x5539e44a /* x^184287 mod G(x) */, 0xbd2655a8 /* x^122847 mod G(x) */, 0xcf5bcdc4 /* x^61407 mod G(x) */, }, - /* chunk_len=7808 */ - { 0xde32a3d2 /* x^187359 mod G(x) */, 0x4ff61aa1 /* x^124895 mod G(x) */, 0x6a6a3694 /* x^62431 mod G(x) */, }, - /* chunk_len=7936 */ - { 0xf0baeeb6 /* x^190431 mod G(x) */, 0x7ae2f6f4 /* x^126943 mod G(x) */, 0xb03c8112 /* x^63455 mod G(x) */, }, - /* chunk_len=8064 */ - { 0xbe15887f /* x^193503 mod G(x) */, 0x2d546c53 /* x^128991 mod G(x) */, 0xf36b9d16 /* x^64479 mod G(x) */, }, - /* chunk_len=8192 */ - { 0x64f34a05 /* x^196575 mod G(x) */, 0xe0ee5efe /* x^131039 mod G(x) */, 0x2339d155 /* x^65503 mod G(x) */, }, - /* chunk_len=8320 */ - { 0x1b6d1aea /* x^199647 mod G(x) */, 0xfeafb67c /* x^133087 mod G(x) */, 0x4fb001a8 /* x^66527 mod G(x) */, }, - /* chunk_len=8448 */ - { 0x82adb0b8 /* x^202719 mod G(x) */, 0x67a93f75 /* x^135135 mod G(x) */, 0xf76fd988 /* x^67551 mod G(x) */, }, - /* chunk_len=8576 */ - { 0x694587c7 /* x^205791 mod G(x) */, 0x3b34408b /* x^137183 mod G(x) */, 0xeccb2978 /* x^68575 mod G(x) */, }, - /* chunk_len=8704 */ - { 0xd2fc57c3 /* x^208863 mod G(x) */, 0x07fcf8c6 /* x^139231 mod G(x) */, 0x416f9449 /* x^69599 mod G(x) */, }, - /* chunk_len=8832 */ - { 0x9dd6837c /* x^211935 mod G(x) */, 0xb0b6fc3e /* x^141279 mod G(x) */, 0x6c45d92e /* x^70623 mod G(x) */, }, - /* chunk_len=8960 */ - { 0x3a9d1f97 /* x^215007 mod G(x) */, 0xefd033b2 /* x^143327 mod G(x) */, 0x4b809189 /* x^71647 mod G(x) */, }, - /* chunk_len=9088 */ - { 0x1eee1d2a /* x^218079 mod G(x) */, 0xf2a6e46e /* x^145375 mod G(x) */, 0x55b4c814 /* x^72671 mod G(x) */, }, - /* chunk_len=9216 */ - { 0xb57c7728 /* x^221151 mod G(x) */, 0xd7017a0c /* x^147423 mod G(x) */, 0x6116b82b /* x^73695 mod G(x) */, }, - /* chunk_len=9344 */ - { 0xf2fc5d61 /* x^224223 mod G(x) */, 0x242aac86 /* x^149471 mod G(x) */, 0x05245cf0 /* x^74719 mod G(x) */, }, - /* chunk_len=9472 */ - { 0x26387824 /* x^227295 mod G(x) */, 0xc15c4ca5 /* x^151519 mod G(x) */, 0x4c5a315a /* x^75743 mod G(x) */, }, - /* chunk_len=9600 */ - { 0x8c151e77 /* x^230367 mod G(x) */, 0x8282fddc /* x^153567 mod G(x) */, 0x4d9899bb /* x^76767 mod G(x) */, }, - /* chunk_len=9728 */ - { 0x8ea1f680 /* x^233439 mod G(x) */, 0xf5ff6cdd /* x^155615 mod G(x) */, 0xbccfa2c1 /* x^77791 mod G(x) */, }, - /* chunk_len=9856 */ - { 0xe8cf3d2a /* x^236511 mod G(x) */, 0x338b1fb1 /* x^157663 mod G(x) */, 0xeda61f70 /* x^78815 mod G(x) */, }, - /* chunk_len=9984 */ - { 0x21f15b59 /* x^239583 mod G(x) */, 0xb9077a01 /* x^159711 mod G(x) */, 0x3e7c93b9 /* x^79839 mod G(x) */, }, - /* chunk_len=10112 */ - { 0x6f68d64a /* x^242655 mod G(x) */, 0x901b0161 /* x^161759 mod G(x) */, 0xb9fd3537 /* x^80863 mod G(x) */, }, - /* chunk_len=10240 */ - { 0x71b74d95 /* x^245727 mod G(x) */, 0xf5ddd5ad /* x^163807 mod G(x) */, 0x3e116c9d /* x^81887 mod G(x) */, }, - /* chunk_len=10368 */ - { 0x4c2e7261 /* x^248799 mod G(x) */, 0x4ca19a29 /* x^165855 mod G(x) */, 0x388b20ac /* x^82911 mod G(x) */, }, - /* chunk_len=10496 */ - { 0x8a2d38e8 /* x^251871 mod G(x) */, 0xd27ee0a1 /* x^167903 mod G(x) */, 0x408e57f2 /* x^83935 mod G(x) */, }, - /* chunk_len=10624 */ - { 0x7e58ca17 /* x^254943 mod G(x) */, 0x69dfedd2 /* x^169951 mod G(x) */, 0x3a76805e /* x^84959 mod G(x) */, }, - /* chunk_len=10752 */ - { 0xf997967f /* x^258015 mod G(x) */, 0x63c3d167 /* x^171999 mod G(x) */, 0x0956d953 /* x^85983 mod G(x) */, }, - /* chunk_len=10880 */ - { 0x48215963 /* x^261087 mod G(x) */, 0x71e1dfe0 /* x^174047 mod G(x) */, 0x42a6d410 /* x^87007 mod G(x) */, }, - /* chunk_len=11008 */ - { 0xa704b94c /* x^264159 mod G(x) */, 0x679f198a /* x^176095 mod G(x) */, 0x42ebf0ad /* x^88031 mod G(x) */, }, - /* chunk_len=11136 */ - { 0x1d699056 /* x^267231 mod G(x) */, 0xfeacf2a1 /* x^178143 mod G(x) */, 0x55cb4dfe /* x^89055 mod G(x) */, }, - /* chunk_len=11264 */ - { 0x6800bcc5 /* x^270303 mod G(x) */, 0x16024f15 /* x^180191 mod G(x) */, 0xcf3233e4 /* x^90079 mod G(x) */, }, - /* chunk_len=11392 */ - { 0x2d48e4ca /* x^273375 mod G(x) */, 0xbe61582f /* x^182239 mod G(x) */, 0x46026283 /* x^91103 mod G(x) */, }, - /* chunk_len=11520 */ - { 0x4c4c2b55 /* x^276447 mod G(x) */, 0x5539e44a /* x^184287 mod G(x) */, 0x52222fea /* x^92127 mod G(x) */, }, - /* chunk_len=11648 */ - { 0xd8ce94cb /* x^279519 mod G(x) */, 0xbc613c26 /* x^186335 mod G(x) */, 0x33776b4b /* x^93151 mod G(x) */, }, - /* chunk_len=11776 */ - { 0xd0b5a02b /* x^282591 mod G(x) */, 0x490d3cc6 /* x^188383 mod G(x) */, 0x2fde73f8 /* x^94175 mod G(x) */, }, - /* chunk_len=11904 */ - { 0xa223f7ec /* x^285663 mod G(x) */, 0xf0baeeb6 /* x^190431 mod G(x) */, 0x0603989b /* x^95199 mod G(x) */, }, - /* chunk_len=12032 */ - { 0x58de337a /* x^288735 mod G(x) */, 0x3bf3d597 /* x^192479 mod G(x) */, 0xced90d99 /* x^96223 mod G(x) */, }, - /* chunk_len=12160 */ - { 0x37f5d8f4 /* x^291807 mod G(x) */, 0x4d5b699b /* x^194527 mod G(x) */, 0xd7262e5f /* x^97247 mod G(x) */, }, - /* chunk_len=12288 */ - { 0xfa8a435d /* x^294879 mod G(x) */, 0x64f34a05 /* x^196575 mod G(x) */, 0x4470c029 /* x^98271 mod G(x) */, }, - /* chunk_len=12416 */ - { 0x238709fe /* x^297951 mod G(x) */, 0x52e7458f /* x^198623 mod G(x) */, 0x9a174cd3 /* x^99295 mod G(x) */, }, - /* chunk_len=12544 */ - { 0x9e1ba6f5 /* x^301023 mod G(x) */, 0xef0272f7 /* x^200671 mod G(x) */, 0x84f40beb /* x^100319 mod G(x) */, }, - /* chunk_len=12672 */ - { 0xcd8b57fa /* x^304095 mod G(x) */, 0x82adb0b8 /* x^202719 mod G(x) */, 0xb6f35093 /* x^101343 mod G(x) */, }, - /* chunk_len=12800 */ - { 0x0aed142f /* x^307167 mod G(x) */, 0xb1650290 /* x^204767 mod G(x) */, 0xec855937 /* x^102367 mod G(x) */, }, - /* chunk_len=12928 */ - { 0xd1f064db /* x^310239 mod G(x) */, 0x6e7340d3 /* x^206815 mod G(x) */, 0x5c28cb52 /* x^103391 mod G(x) */, }, - /* chunk_len=13056 */ - { 0x464ac895 /* x^313311 mod G(x) */, 0xd2fc57c3 /* x^208863 mod G(x) */, 0xc46805ba /* x^104415 mod G(x) */, }, - /* chunk_len=13184 */ - { 0xa0e6beea /* x^316383 mod G(x) */, 0xcfeec3d0 /* x^210911 mod G(x) */, 0x0225d214 /* x^105439 mod G(x) */, }, - /* chunk_len=13312 */ - { 0x78703ce0 /* x^319455 mod G(x) */, 0xc60f6075 /* x^212959 mod G(x) */, 0xdf7a24ac /* x^106463 mod G(x) */, }, - /* chunk_len=13440 */ - { 0xfea48165 /* x^322527 mod G(x) */, 0x3a9d1f97 /* x^215007 mod G(x) */, 0xc3876592 /* x^107487 mod G(x) */, }, - /* chunk_len=13568 */ - { 0xdb89b8db /* x^325599 mod G(x) */, 0xa6172211 /* x^217055 mod G(x) */, 0x2b52dc39 /* x^108511 mod G(x) */, }, - /* chunk_len=13696 */ - { 0x7ca03731 /* x^328671 mod G(x) */, 0x1db42849 /* x^219103 mod G(x) */, 0xc5df246e /* x^109535 mod G(x) */, }, - /* chunk_len=13824 */ - { 0x8801d0aa /* x^331743 mod G(x) */, 0xb57c7728 /* x^221151 mod G(x) */, 0x5b0c98b9 /* x^110559 mod G(x) */, }, - /* chunk_len=13952 */ - { 0xf89cd7f0 /* x^334815 mod G(x) */, 0xcc396a0b /* x^223199 mod G(x) */, 0xdb799c51 /* x^111583 mod G(x) */, }, - /* chunk_len=14080 */ - { 0x1611a808 /* x^337887 mod G(x) */, 0xaeae6105 /* x^225247 mod G(x) */, 0xb939fcdf /* x^112607 mod G(x) */, }, - /* chunk_len=14208 */ - { 0xe3cdb888 /* x^340959 mod G(x) */, 0x26387824 /* x^227295 mod G(x) */, 0x30d13e5f /* x^113631 mod G(x) */, }, - /* chunk_len=14336 */ - { 0x552a4cf6 /* x^344031 mod G(x) */, 0xee2d04bb /* x^229343 mod G(x) */, 0x70f9947d /* x^114655 mod G(x) */, }, - /* chunk_len=14464 */ - { 0x85e248e9 /* x^347103 mod G(x) */, 0x0a79663f /* x^231391 mod G(x) */, 0x53339cf7 /* x^115679 mod G(x) */, }, - /* chunk_len=14592 */ - { 0x1c61c3e9 /* x^350175 mod G(x) */, 0x8ea1f680 /* x^233439 mod G(x) */, 0x54afca53 /* x^116703 mod G(x) */, }, - /* chunk_len=14720 */ - { 0xb14cfc2b /* x^353247 mod G(x) */, 0x2e073302 /* x^235487 mod G(x) */, 0x10897992 /* x^117727 mod G(x) */, }, - /* chunk_len=14848 */ - { 0x6ec444cc /* x^356319 mod G(x) */, 0x9e819f13 /* x^237535 mod G(x) */, 0x7a3c0a6a /* x^118751 mod G(x) */, }, - /* chunk_len=14976 */ - { 0xe2fa5f80 /* x^359391 mod G(x) */, 0x21f15b59 /* x^239583 mod G(x) */, 0x93102436 /* x^119775 mod G(x) */, }, - /* chunk_len=15104 */ - { 0x6d33f4c6 /* x^362463 mod G(x) */, 0x31a27455 /* x^241631 mod G(x) */, 0x1fea4d2a /* x^120799 mod G(x) */, }, - /* chunk_len=15232 */ - { 0xb6dec609 /* x^365535 mod G(x) */, 0x4d437056 /* x^243679 mod G(x) */, 0x42eb1e2a /* x^121823 mod G(x) */, }, - /* chunk_len=15360 */ - { 0x1846c518 /* x^368607 mod G(x) */, 0x71b74d95 /* x^245727 mod G(x) */, 0xbd2655a8 /* x^122847 mod G(x) */, }, - /* chunk_len=15488 */ - { 0x9f947f8a /* x^371679 mod G(x) */, 0x2b501619 /* x^247775 mod G(x) */, 0xa4924b0e /* x^123871 mod G(x) */, }, - /* chunk_len=15616 */ - { 0xb7442f4d /* x^374751 mod G(x) */, 0xba30a5d8 /* x^249823 mod G(x) */, 0x4ff61aa1 /* x^124895 mod G(x) */, }, - /* chunk_len=15744 */ - { 0xe2c93242 /* x^377823 mod G(x) */, 0x8a2d38e8 /* x^251871 mod G(x) */, 0x70cd7f26 /* x^125919 mod G(x) */, }, - /* chunk_len=15872 */ - { 0xcd6863df /* x^380895 mod G(x) */, 0x78fd88dc /* x^253919 mod G(x) */, 0x7ae2f6f4 /* x^126943 mod G(x) */, }, - /* chunk_len=16000 */ - { 0xd512001d /* x^383967 mod G(x) */, 0xe6612dff /* x^255967 mod G(x) */, 0x5c4d0ca9 /* x^127967 mod G(x) */, }, - /* chunk_len=16128 */ - { 0x4e8d6b6c /* x^387039 mod G(x) */, 0xf997967f /* x^258015 mod G(x) */, 0x2d546c53 /* x^128991 mod G(x) */, }, - /* chunk_len=16256 */ - { 0xfa653ba1 /* x^390111 mod G(x) */, 0xc99014d4 /* x^260063 mod G(x) */, 0xa0c9fd27 /* x^130015 mod G(x) */, }, - /* chunk_len=16384 */ - { 0x49893408 /* x^393183 mod G(x) */, 0x29c2448b /* x^262111 mod G(x) */, 0xe0ee5efe /* x^131039 mod G(x) */, }, -}; - -/* Multipliers for implementations that use a large fixed chunk length */ -#define CRC32_FIXED_CHUNK_LEN 32768UL -#define CRC32_FIXED_CHUNK_MULT_1 0x29c2448b /* x^262111 mod G(x) */ -#define CRC32_FIXED_CHUNK_MULT_2 0x4b912f53 /* x^524255 mod G(x) */ -#define CRC32_FIXED_CHUNK_MULT_3 0x454c93be /* x^786399 mod G(x) */ diff --git a/vendor/libdeflate/lib/crc32_tables.h b/vendor/libdeflate/lib/crc32_tables.h deleted file mode 100644 index d6aff733d7..0000000000 --- a/vendor/libdeflate/lib/crc32_tables.h +++ /dev/null @@ -1,587 +0,0 @@ -/* - * crc32_tables.h - data tables for CRC-32 computation - * - * THIS FILE WAS GENERATED BY gen-crc32-consts.py. DO NOT EDIT. - */ - -static const u32 crc32_slice1_table[] MAYBE_UNUSED = { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, - 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, - 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, - 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, - 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, - 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, - 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, - 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, - 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, - 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, - 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, - 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, - 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, - 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, - 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, - 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, - 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, - 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, - 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, - 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, - 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, - 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, - 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, - 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, - 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, - 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, - 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, - 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, - 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, - 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, - 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, - 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, - 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, - 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, - 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, - 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, - 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, - 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, - 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, - 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, - 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, - 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, - 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, -}; - -static const u32 crc32_slice8_table[] MAYBE_UNUSED = { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, - 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, - 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, - 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, - 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, - 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, - 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, - 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, - 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, - 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, - 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, - 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, - 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, - 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, - 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, - 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, - 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, - 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, - 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, - 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, - 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, - 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, - 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, - 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, - 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, - 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, - 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, - 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, - 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, - 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, - 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, - 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, - 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, - 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, - 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, - 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, - 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, - 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, - 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, - 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, - 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, - 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, - 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, - 0x00000000, 0x191b3141, 0x32366282, 0x2b2d53c3, - 0x646cc504, 0x7d77f445, 0x565aa786, 0x4f4196c7, - 0xc8d98a08, 0xd1c2bb49, 0xfaefe88a, 0xe3f4d9cb, - 0xacb54f0c, 0xb5ae7e4d, 0x9e832d8e, 0x87981ccf, - 0x4ac21251, 0x53d92310, 0x78f470d3, 0x61ef4192, - 0x2eaed755, 0x37b5e614, 0x1c98b5d7, 0x05838496, - 0x821b9859, 0x9b00a918, 0xb02dfadb, 0xa936cb9a, - 0xe6775d5d, 0xff6c6c1c, 0xd4413fdf, 0xcd5a0e9e, - 0x958424a2, 0x8c9f15e3, 0xa7b24620, 0xbea97761, - 0xf1e8e1a6, 0xe8f3d0e7, 0xc3de8324, 0xdac5b265, - 0x5d5daeaa, 0x44469feb, 0x6f6bcc28, 0x7670fd69, - 0x39316bae, 0x202a5aef, 0x0b07092c, 0x121c386d, - 0xdf4636f3, 0xc65d07b2, 0xed705471, 0xf46b6530, - 0xbb2af3f7, 0xa231c2b6, 0x891c9175, 0x9007a034, - 0x179fbcfb, 0x0e848dba, 0x25a9de79, 0x3cb2ef38, - 0x73f379ff, 0x6ae848be, 0x41c51b7d, 0x58de2a3c, - 0xf0794f05, 0xe9627e44, 0xc24f2d87, 0xdb541cc6, - 0x94158a01, 0x8d0ebb40, 0xa623e883, 0xbf38d9c2, - 0x38a0c50d, 0x21bbf44c, 0x0a96a78f, 0x138d96ce, - 0x5ccc0009, 0x45d73148, 0x6efa628b, 0x77e153ca, - 0xbabb5d54, 0xa3a06c15, 0x888d3fd6, 0x91960e97, - 0xded79850, 0xc7cca911, 0xece1fad2, 0xf5facb93, - 0x7262d75c, 0x6b79e61d, 0x4054b5de, 0x594f849f, - 0x160e1258, 0x0f152319, 0x243870da, 0x3d23419b, - 0x65fd6ba7, 0x7ce65ae6, 0x57cb0925, 0x4ed03864, - 0x0191aea3, 0x188a9fe2, 0x33a7cc21, 0x2abcfd60, - 0xad24e1af, 0xb43fd0ee, 0x9f12832d, 0x8609b26c, - 0xc94824ab, 0xd05315ea, 0xfb7e4629, 0xe2657768, - 0x2f3f79f6, 0x362448b7, 0x1d091b74, 0x04122a35, - 0x4b53bcf2, 0x52488db3, 0x7965de70, 0x607eef31, - 0xe7e6f3fe, 0xfefdc2bf, 0xd5d0917c, 0xcccba03d, - 0x838a36fa, 0x9a9107bb, 0xb1bc5478, 0xa8a76539, - 0x3b83984b, 0x2298a90a, 0x09b5fac9, 0x10aecb88, - 0x5fef5d4f, 0x46f46c0e, 0x6dd93fcd, 0x74c20e8c, - 0xf35a1243, 0xea412302, 0xc16c70c1, 0xd8774180, - 0x9736d747, 0x8e2de606, 0xa500b5c5, 0xbc1b8484, - 0x71418a1a, 0x685abb5b, 0x4377e898, 0x5a6cd9d9, - 0x152d4f1e, 0x0c367e5f, 0x271b2d9c, 0x3e001cdd, - 0xb9980012, 0xa0833153, 0x8bae6290, 0x92b553d1, - 0xddf4c516, 0xc4eff457, 0xefc2a794, 0xf6d996d5, - 0xae07bce9, 0xb71c8da8, 0x9c31de6b, 0x852aef2a, - 0xca6b79ed, 0xd37048ac, 0xf85d1b6f, 0xe1462a2e, - 0x66de36e1, 0x7fc507a0, 0x54e85463, 0x4df36522, - 0x02b2f3e5, 0x1ba9c2a4, 0x30849167, 0x299fa026, - 0xe4c5aeb8, 0xfdde9ff9, 0xd6f3cc3a, 0xcfe8fd7b, - 0x80a96bbc, 0x99b25afd, 0xb29f093e, 0xab84387f, - 0x2c1c24b0, 0x350715f1, 0x1e2a4632, 0x07317773, - 0x4870e1b4, 0x516bd0f5, 0x7a468336, 0x635db277, - 0xcbfad74e, 0xd2e1e60f, 0xf9ccb5cc, 0xe0d7848d, - 0xaf96124a, 0xb68d230b, 0x9da070c8, 0x84bb4189, - 0x03235d46, 0x1a386c07, 0x31153fc4, 0x280e0e85, - 0x674f9842, 0x7e54a903, 0x5579fac0, 0x4c62cb81, - 0x8138c51f, 0x9823f45e, 0xb30ea79d, 0xaa1596dc, - 0xe554001b, 0xfc4f315a, 0xd7626299, 0xce7953d8, - 0x49e14f17, 0x50fa7e56, 0x7bd72d95, 0x62cc1cd4, - 0x2d8d8a13, 0x3496bb52, 0x1fbbe891, 0x06a0d9d0, - 0x5e7ef3ec, 0x4765c2ad, 0x6c48916e, 0x7553a02f, - 0x3a1236e8, 0x230907a9, 0x0824546a, 0x113f652b, - 0x96a779e4, 0x8fbc48a5, 0xa4911b66, 0xbd8a2a27, - 0xf2cbbce0, 0xebd08da1, 0xc0fdde62, 0xd9e6ef23, - 0x14bce1bd, 0x0da7d0fc, 0x268a833f, 0x3f91b27e, - 0x70d024b9, 0x69cb15f8, 0x42e6463b, 0x5bfd777a, - 0xdc656bb5, 0xc57e5af4, 0xee530937, 0xf7483876, - 0xb809aeb1, 0xa1129ff0, 0x8a3fcc33, 0x9324fd72, - 0x00000000, 0x01c26a37, 0x0384d46e, 0x0246be59, - 0x0709a8dc, 0x06cbc2eb, 0x048d7cb2, 0x054f1685, - 0x0e1351b8, 0x0fd13b8f, 0x0d9785d6, 0x0c55efe1, - 0x091af964, 0x08d89353, 0x0a9e2d0a, 0x0b5c473d, - 0x1c26a370, 0x1de4c947, 0x1fa2771e, 0x1e601d29, - 0x1b2f0bac, 0x1aed619b, 0x18abdfc2, 0x1969b5f5, - 0x1235f2c8, 0x13f798ff, 0x11b126a6, 0x10734c91, - 0x153c5a14, 0x14fe3023, 0x16b88e7a, 0x177ae44d, - 0x384d46e0, 0x398f2cd7, 0x3bc9928e, 0x3a0bf8b9, - 0x3f44ee3c, 0x3e86840b, 0x3cc03a52, 0x3d025065, - 0x365e1758, 0x379c7d6f, 0x35dac336, 0x3418a901, - 0x3157bf84, 0x3095d5b3, 0x32d36bea, 0x331101dd, - 0x246be590, 0x25a98fa7, 0x27ef31fe, 0x262d5bc9, - 0x23624d4c, 0x22a0277b, 0x20e69922, 0x2124f315, - 0x2a78b428, 0x2bbade1f, 0x29fc6046, 0x283e0a71, - 0x2d711cf4, 0x2cb376c3, 0x2ef5c89a, 0x2f37a2ad, - 0x709a8dc0, 0x7158e7f7, 0x731e59ae, 0x72dc3399, - 0x7793251c, 0x76514f2b, 0x7417f172, 0x75d59b45, - 0x7e89dc78, 0x7f4bb64f, 0x7d0d0816, 0x7ccf6221, - 0x798074a4, 0x78421e93, 0x7a04a0ca, 0x7bc6cafd, - 0x6cbc2eb0, 0x6d7e4487, 0x6f38fade, 0x6efa90e9, - 0x6bb5866c, 0x6a77ec5b, 0x68315202, 0x69f33835, - 0x62af7f08, 0x636d153f, 0x612bab66, 0x60e9c151, - 0x65a6d7d4, 0x6464bde3, 0x662203ba, 0x67e0698d, - 0x48d7cb20, 0x4915a117, 0x4b531f4e, 0x4a917579, - 0x4fde63fc, 0x4e1c09cb, 0x4c5ab792, 0x4d98dda5, - 0x46c49a98, 0x4706f0af, 0x45404ef6, 0x448224c1, - 0x41cd3244, 0x400f5873, 0x4249e62a, 0x438b8c1d, - 0x54f16850, 0x55330267, 0x5775bc3e, 0x56b7d609, - 0x53f8c08c, 0x523aaabb, 0x507c14e2, 0x51be7ed5, - 0x5ae239e8, 0x5b2053df, 0x5966ed86, 0x58a487b1, - 0x5deb9134, 0x5c29fb03, 0x5e6f455a, 0x5fad2f6d, - 0xe1351b80, 0xe0f771b7, 0xe2b1cfee, 0xe373a5d9, - 0xe63cb35c, 0xe7fed96b, 0xe5b86732, 0xe47a0d05, - 0xef264a38, 0xeee4200f, 0xeca29e56, 0xed60f461, - 0xe82fe2e4, 0xe9ed88d3, 0xebab368a, 0xea695cbd, - 0xfd13b8f0, 0xfcd1d2c7, 0xfe976c9e, 0xff5506a9, - 0xfa1a102c, 0xfbd87a1b, 0xf99ec442, 0xf85cae75, - 0xf300e948, 0xf2c2837f, 0xf0843d26, 0xf1465711, - 0xf4094194, 0xf5cb2ba3, 0xf78d95fa, 0xf64fffcd, - 0xd9785d60, 0xd8ba3757, 0xdafc890e, 0xdb3ee339, - 0xde71f5bc, 0xdfb39f8b, 0xddf521d2, 0xdc374be5, - 0xd76b0cd8, 0xd6a966ef, 0xd4efd8b6, 0xd52db281, - 0xd062a404, 0xd1a0ce33, 0xd3e6706a, 0xd2241a5d, - 0xc55efe10, 0xc49c9427, 0xc6da2a7e, 0xc7184049, - 0xc25756cc, 0xc3953cfb, 0xc1d382a2, 0xc011e895, - 0xcb4dafa8, 0xca8fc59f, 0xc8c97bc6, 0xc90b11f1, - 0xcc440774, 0xcd866d43, 0xcfc0d31a, 0xce02b92d, - 0x91af9640, 0x906dfc77, 0x922b422e, 0x93e92819, - 0x96a63e9c, 0x976454ab, 0x9522eaf2, 0x94e080c5, - 0x9fbcc7f8, 0x9e7eadcf, 0x9c381396, 0x9dfa79a1, - 0x98b56f24, 0x99770513, 0x9b31bb4a, 0x9af3d17d, - 0x8d893530, 0x8c4b5f07, 0x8e0de15e, 0x8fcf8b69, - 0x8a809dec, 0x8b42f7db, 0x89044982, 0x88c623b5, - 0x839a6488, 0x82580ebf, 0x801eb0e6, 0x81dcdad1, - 0x8493cc54, 0x8551a663, 0x8717183a, 0x86d5720d, - 0xa9e2d0a0, 0xa820ba97, 0xaa6604ce, 0xaba46ef9, - 0xaeeb787c, 0xaf29124b, 0xad6fac12, 0xacadc625, - 0xa7f18118, 0xa633eb2f, 0xa4755576, 0xa5b73f41, - 0xa0f829c4, 0xa13a43f3, 0xa37cfdaa, 0xa2be979d, - 0xb5c473d0, 0xb40619e7, 0xb640a7be, 0xb782cd89, - 0xb2cddb0c, 0xb30fb13b, 0xb1490f62, 0xb08b6555, - 0xbbd72268, 0xba15485f, 0xb853f606, 0xb9919c31, - 0xbcde8ab4, 0xbd1ce083, 0xbf5a5eda, 0xbe9834ed, - 0x00000000, 0xb8bc6765, 0xaa09c88b, 0x12b5afee, - 0x8f629757, 0x37def032, 0x256b5fdc, 0x9dd738b9, - 0xc5b428ef, 0x7d084f8a, 0x6fbde064, 0xd7018701, - 0x4ad6bfb8, 0xf26ad8dd, 0xe0df7733, 0x58631056, - 0x5019579f, 0xe8a530fa, 0xfa109f14, 0x42acf871, - 0xdf7bc0c8, 0x67c7a7ad, 0x75720843, 0xcdce6f26, - 0x95ad7f70, 0x2d111815, 0x3fa4b7fb, 0x8718d09e, - 0x1acfe827, 0xa2738f42, 0xb0c620ac, 0x087a47c9, - 0xa032af3e, 0x188ec85b, 0x0a3b67b5, 0xb28700d0, - 0x2f503869, 0x97ec5f0c, 0x8559f0e2, 0x3de59787, - 0x658687d1, 0xdd3ae0b4, 0xcf8f4f5a, 0x7733283f, - 0xeae41086, 0x525877e3, 0x40edd80d, 0xf851bf68, - 0xf02bf8a1, 0x48979fc4, 0x5a22302a, 0xe29e574f, - 0x7f496ff6, 0xc7f50893, 0xd540a77d, 0x6dfcc018, - 0x359fd04e, 0x8d23b72b, 0x9f9618c5, 0x272a7fa0, - 0xbafd4719, 0x0241207c, 0x10f48f92, 0xa848e8f7, - 0x9b14583d, 0x23a83f58, 0x311d90b6, 0x89a1f7d3, - 0x1476cf6a, 0xaccaa80f, 0xbe7f07e1, 0x06c36084, - 0x5ea070d2, 0xe61c17b7, 0xf4a9b859, 0x4c15df3c, - 0xd1c2e785, 0x697e80e0, 0x7bcb2f0e, 0xc377486b, - 0xcb0d0fa2, 0x73b168c7, 0x6104c729, 0xd9b8a04c, - 0x446f98f5, 0xfcd3ff90, 0xee66507e, 0x56da371b, - 0x0eb9274d, 0xb6054028, 0xa4b0efc6, 0x1c0c88a3, - 0x81dbb01a, 0x3967d77f, 0x2bd27891, 0x936e1ff4, - 0x3b26f703, 0x839a9066, 0x912f3f88, 0x299358ed, - 0xb4446054, 0x0cf80731, 0x1e4da8df, 0xa6f1cfba, - 0xfe92dfec, 0x462eb889, 0x549b1767, 0xec277002, - 0x71f048bb, 0xc94c2fde, 0xdbf98030, 0x6345e755, - 0x6b3fa09c, 0xd383c7f9, 0xc1366817, 0x798a0f72, - 0xe45d37cb, 0x5ce150ae, 0x4e54ff40, 0xf6e89825, - 0xae8b8873, 0x1637ef16, 0x048240f8, 0xbc3e279d, - 0x21e91f24, 0x99557841, 0x8be0d7af, 0x335cb0ca, - 0xed59b63b, 0x55e5d15e, 0x47507eb0, 0xffec19d5, - 0x623b216c, 0xda874609, 0xc832e9e7, 0x708e8e82, - 0x28ed9ed4, 0x9051f9b1, 0x82e4565f, 0x3a58313a, - 0xa78f0983, 0x1f336ee6, 0x0d86c108, 0xb53aa66d, - 0xbd40e1a4, 0x05fc86c1, 0x1749292f, 0xaff54e4a, - 0x322276f3, 0x8a9e1196, 0x982bbe78, 0x2097d91d, - 0x78f4c94b, 0xc048ae2e, 0xd2fd01c0, 0x6a4166a5, - 0xf7965e1c, 0x4f2a3979, 0x5d9f9697, 0xe523f1f2, - 0x4d6b1905, 0xf5d77e60, 0xe762d18e, 0x5fdeb6eb, - 0xc2098e52, 0x7ab5e937, 0x680046d9, 0xd0bc21bc, - 0x88df31ea, 0x3063568f, 0x22d6f961, 0x9a6a9e04, - 0x07bda6bd, 0xbf01c1d8, 0xadb46e36, 0x15080953, - 0x1d724e9a, 0xa5ce29ff, 0xb77b8611, 0x0fc7e174, - 0x9210d9cd, 0x2aacbea8, 0x38191146, 0x80a57623, - 0xd8c66675, 0x607a0110, 0x72cfaefe, 0xca73c99b, - 0x57a4f122, 0xef189647, 0xfdad39a9, 0x45115ecc, - 0x764dee06, 0xcef18963, 0xdc44268d, 0x64f841e8, - 0xf92f7951, 0x41931e34, 0x5326b1da, 0xeb9ad6bf, - 0xb3f9c6e9, 0x0b45a18c, 0x19f00e62, 0xa14c6907, - 0x3c9b51be, 0x842736db, 0x96929935, 0x2e2efe50, - 0x2654b999, 0x9ee8defc, 0x8c5d7112, 0x34e11677, - 0xa9362ece, 0x118a49ab, 0x033fe645, 0xbb838120, - 0xe3e09176, 0x5b5cf613, 0x49e959fd, 0xf1553e98, - 0x6c820621, 0xd43e6144, 0xc68bceaa, 0x7e37a9cf, - 0xd67f4138, 0x6ec3265d, 0x7c7689b3, 0xc4caeed6, - 0x591dd66f, 0xe1a1b10a, 0xf3141ee4, 0x4ba87981, - 0x13cb69d7, 0xab770eb2, 0xb9c2a15c, 0x017ec639, - 0x9ca9fe80, 0x241599e5, 0x36a0360b, 0x8e1c516e, - 0x866616a7, 0x3eda71c2, 0x2c6fde2c, 0x94d3b949, - 0x090481f0, 0xb1b8e695, 0xa30d497b, 0x1bb12e1e, - 0x43d23e48, 0xfb6e592d, 0xe9dbf6c3, 0x516791a6, - 0xccb0a91f, 0x740cce7a, 0x66b96194, 0xde0506f1, - 0x00000000, 0x3d6029b0, 0x7ac05360, 0x47a07ad0, - 0xf580a6c0, 0xc8e08f70, 0x8f40f5a0, 0xb220dc10, - 0x30704bc1, 0x0d106271, 0x4ab018a1, 0x77d03111, - 0xc5f0ed01, 0xf890c4b1, 0xbf30be61, 0x825097d1, - 0x60e09782, 0x5d80be32, 0x1a20c4e2, 0x2740ed52, - 0x95603142, 0xa80018f2, 0xefa06222, 0xd2c04b92, - 0x5090dc43, 0x6df0f5f3, 0x2a508f23, 0x1730a693, - 0xa5107a83, 0x98705333, 0xdfd029e3, 0xe2b00053, - 0xc1c12f04, 0xfca106b4, 0xbb017c64, 0x866155d4, - 0x344189c4, 0x0921a074, 0x4e81daa4, 0x73e1f314, - 0xf1b164c5, 0xccd14d75, 0x8b7137a5, 0xb6111e15, - 0x0431c205, 0x3951ebb5, 0x7ef19165, 0x4391b8d5, - 0xa121b886, 0x9c419136, 0xdbe1ebe6, 0xe681c256, - 0x54a11e46, 0x69c137f6, 0x2e614d26, 0x13016496, - 0x9151f347, 0xac31daf7, 0xeb91a027, 0xd6f18997, - 0x64d15587, 0x59b17c37, 0x1e1106e7, 0x23712f57, - 0x58f35849, 0x659371f9, 0x22330b29, 0x1f532299, - 0xad73fe89, 0x9013d739, 0xd7b3ade9, 0xead38459, - 0x68831388, 0x55e33a38, 0x124340e8, 0x2f236958, - 0x9d03b548, 0xa0639cf8, 0xe7c3e628, 0xdaa3cf98, - 0x3813cfcb, 0x0573e67b, 0x42d39cab, 0x7fb3b51b, - 0xcd93690b, 0xf0f340bb, 0xb7533a6b, 0x8a3313db, - 0x0863840a, 0x3503adba, 0x72a3d76a, 0x4fc3feda, - 0xfde322ca, 0xc0830b7a, 0x872371aa, 0xba43581a, - 0x9932774d, 0xa4525efd, 0xe3f2242d, 0xde920d9d, - 0x6cb2d18d, 0x51d2f83d, 0x167282ed, 0x2b12ab5d, - 0xa9423c8c, 0x9422153c, 0xd3826fec, 0xeee2465c, - 0x5cc29a4c, 0x61a2b3fc, 0x2602c92c, 0x1b62e09c, - 0xf9d2e0cf, 0xc4b2c97f, 0x8312b3af, 0xbe729a1f, - 0x0c52460f, 0x31326fbf, 0x7692156f, 0x4bf23cdf, - 0xc9a2ab0e, 0xf4c282be, 0xb362f86e, 0x8e02d1de, - 0x3c220dce, 0x0142247e, 0x46e25eae, 0x7b82771e, - 0xb1e6b092, 0x8c869922, 0xcb26e3f2, 0xf646ca42, - 0x44661652, 0x79063fe2, 0x3ea64532, 0x03c66c82, - 0x8196fb53, 0xbcf6d2e3, 0xfb56a833, 0xc6368183, - 0x74165d93, 0x49767423, 0x0ed60ef3, 0x33b62743, - 0xd1062710, 0xec660ea0, 0xabc67470, 0x96a65dc0, - 0x248681d0, 0x19e6a860, 0x5e46d2b0, 0x6326fb00, - 0xe1766cd1, 0xdc164561, 0x9bb63fb1, 0xa6d61601, - 0x14f6ca11, 0x2996e3a1, 0x6e369971, 0x5356b0c1, - 0x70279f96, 0x4d47b626, 0x0ae7ccf6, 0x3787e546, - 0x85a73956, 0xb8c710e6, 0xff676a36, 0xc2074386, - 0x4057d457, 0x7d37fde7, 0x3a978737, 0x07f7ae87, - 0xb5d77297, 0x88b75b27, 0xcf1721f7, 0xf2770847, - 0x10c70814, 0x2da721a4, 0x6a075b74, 0x576772c4, - 0xe547aed4, 0xd8278764, 0x9f87fdb4, 0xa2e7d404, - 0x20b743d5, 0x1dd76a65, 0x5a7710b5, 0x67173905, - 0xd537e515, 0xe857cca5, 0xaff7b675, 0x92979fc5, - 0xe915e8db, 0xd475c16b, 0x93d5bbbb, 0xaeb5920b, - 0x1c954e1b, 0x21f567ab, 0x66551d7b, 0x5b3534cb, - 0xd965a31a, 0xe4058aaa, 0xa3a5f07a, 0x9ec5d9ca, - 0x2ce505da, 0x11852c6a, 0x562556ba, 0x6b457f0a, - 0x89f57f59, 0xb49556e9, 0xf3352c39, 0xce550589, - 0x7c75d999, 0x4115f029, 0x06b58af9, 0x3bd5a349, - 0xb9853498, 0x84e51d28, 0xc34567f8, 0xfe254e48, - 0x4c059258, 0x7165bbe8, 0x36c5c138, 0x0ba5e888, - 0x28d4c7df, 0x15b4ee6f, 0x521494bf, 0x6f74bd0f, - 0xdd54611f, 0xe03448af, 0xa794327f, 0x9af41bcf, - 0x18a48c1e, 0x25c4a5ae, 0x6264df7e, 0x5f04f6ce, - 0xed242ade, 0xd044036e, 0x97e479be, 0xaa84500e, - 0x4834505d, 0x755479ed, 0x32f4033d, 0x0f942a8d, - 0xbdb4f69d, 0x80d4df2d, 0xc774a5fd, 0xfa148c4d, - 0x78441b9c, 0x4524322c, 0x028448fc, 0x3fe4614c, - 0x8dc4bd5c, 0xb0a494ec, 0xf704ee3c, 0xca64c78c, - 0x00000000, 0xcb5cd3a5, 0x4dc8a10b, 0x869472ae, - 0x9b914216, 0x50cd91b3, 0xd659e31d, 0x1d0530b8, - 0xec53826d, 0x270f51c8, 0xa19b2366, 0x6ac7f0c3, - 0x77c2c07b, 0xbc9e13de, 0x3a0a6170, 0xf156b2d5, - 0x03d6029b, 0xc88ad13e, 0x4e1ea390, 0x85427035, - 0x9847408d, 0x531b9328, 0xd58fe186, 0x1ed33223, - 0xef8580f6, 0x24d95353, 0xa24d21fd, 0x6911f258, - 0x7414c2e0, 0xbf481145, 0x39dc63eb, 0xf280b04e, - 0x07ac0536, 0xccf0d693, 0x4a64a43d, 0x81387798, - 0x9c3d4720, 0x57619485, 0xd1f5e62b, 0x1aa9358e, - 0xebff875b, 0x20a354fe, 0xa6372650, 0x6d6bf5f5, - 0x706ec54d, 0xbb3216e8, 0x3da66446, 0xf6fab7e3, - 0x047a07ad, 0xcf26d408, 0x49b2a6a6, 0x82ee7503, - 0x9feb45bb, 0x54b7961e, 0xd223e4b0, 0x197f3715, - 0xe82985c0, 0x23755665, 0xa5e124cb, 0x6ebdf76e, - 0x73b8c7d6, 0xb8e41473, 0x3e7066dd, 0xf52cb578, - 0x0f580a6c, 0xc404d9c9, 0x4290ab67, 0x89cc78c2, - 0x94c9487a, 0x5f959bdf, 0xd901e971, 0x125d3ad4, - 0xe30b8801, 0x28575ba4, 0xaec3290a, 0x659ffaaf, - 0x789aca17, 0xb3c619b2, 0x35526b1c, 0xfe0eb8b9, - 0x0c8e08f7, 0xc7d2db52, 0x4146a9fc, 0x8a1a7a59, - 0x971f4ae1, 0x5c439944, 0xdad7ebea, 0x118b384f, - 0xe0dd8a9a, 0x2b81593f, 0xad152b91, 0x6649f834, - 0x7b4cc88c, 0xb0101b29, 0x36846987, 0xfdd8ba22, - 0x08f40f5a, 0xc3a8dcff, 0x453cae51, 0x8e607df4, - 0x93654d4c, 0x58399ee9, 0xdeadec47, 0x15f13fe2, - 0xe4a78d37, 0x2ffb5e92, 0xa96f2c3c, 0x6233ff99, - 0x7f36cf21, 0xb46a1c84, 0x32fe6e2a, 0xf9a2bd8f, - 0x0b220dc1, 0xc07ede64, 0x46eaacca, 0x8db67f6f, - 0x90b34fd7, 0x5bef9c72, 0xdd7beedc, 0x16273d79, - 0xe7718fac, 0x2c2d5c09, 0xaab92ea7, 0x61e5fd02, - 0x7ce0cdba, 0xb7bc1e1f, 0x31286cb1, 0xfa74bf14, - 0x1eb014d8, 0xd5ecc77d, 0x5378b5d3, 0x98246676, - 0x852156ce, 0x4e7d856b, 0xc8e9f7c5, 0x03b52460, - 0xf2e396b5, 0x39bf4510, 0xbf2b37be, 0x7477e41b, - 0x6972d4a3, 0xa22e0706, 0x24ba75a8, 0xefe6a60d, - 0x1d661643, 0xd63ac5e6, 0x50aeb748, 0x9bf264ed, - 0x86f75455, 0x4dab87f0, 0xcb3ff55e, 0x006326fb, - 0xf135942e, 0x3a69478b, 0xbcfd3525, 0x77a1e680, - 0x6aa4d638, 0xa1f8059d, 0x276c7733, 0xec30a496, - 0x191c11ee, 0xd240c24b, 0x54d4b0e5, 0x9f886340, - 0x828d53f8, 0x49d1805d, 0xcf45f2f3, 0x04192156, - 0xf54f9383, 0x3e134026, 0xb8873288, 0x73dbe12d, - 0x6eded195, 0xa5820230, 0x2316709e, 0xe84aa33b, - 0x1aca1375, 0xd196c0d0, 0x5702b27e, 0x9c5e61db, - 0x815b5163, 0x4a0782c6, 0xcc93f068, 0x07cf23cd, - 0xf6999118, 0x3dc542bd, 0xbb513013, 0x700de3b6, - 0x6d08d30e, 0xa65400ab, 0x20c07205, 0xeb9ca1a0, - 0x11e81eb4, 0xdab4cd11, 0x5c20bfbf, 0x977c6c1a, - 0x8a795ca2, 0x41258f07, 0xc7b1fda9, 0x0ced2e0c, - 0xfdbb9cd9, 0x36e74f7c, 0xb0733dd2, 0x7b2fee77, - 0x662adecf, 0xad760d6a, 0x2be27fc4, 0xe0beac61, - 0x123e1c2f, 0xd962cf8a, 0x5ff6bd24, 0x94aa6e81, - 0x89af5e39, 0x42f38d9c, 0xc467ff32, 0x0f3b2c97, - 0xfe6d9e42, 0x35314de7, 0xb3a53f49, 0x78f9ecec, - 0x65fcdc54, 0xaea00ff1, 0x28347d5f, 0xe368aefa, - 0x16441b82, 0xdd18c827, 0x5b8cba89, 0x90d0692c, - 0x8dd55994, 0x46898a31, 0xc01df89f, 0x0b412b3a, - 0xfa1799ef, 0x314b4a4a, 0xb7df38e4, 0x7c83eb41, - 0x6186dbf9, 0xaada085c, 0x2c4e7af2, 0xe712a957, - 0x15921919, 0xdececabc, 0x585ab812, 0x93066bb7, - 0x8e035b0f, 0x455f88aa, 0xc3cbfa04, 0x089729a1, - 0xf9c19b74, 0x329d48d1, 0xb4093a7f, 0x7f55e9da, - 0x6250d962, 0xa90c0ac7, 0x2f987869, 0xe4c4abcc, - 0x00000000, 0xa6770bb4, 0x979f1129, 0x31e81a9d, - 0xf44f2413, 0x52382fa7, 0x63d0353a, 0xc5a73e8e, - 0x33ef4e67, 0x959845d3, 0xa4705f4e, 0x020754fa, - 0xc7a06a74, 0x61d761c0, 0x503f7b5d, 0xf64870e9, - 0x67de9cce, 0xc1a9977a, 0xf0418de7, 0x56368653, - 0x9391b8dd, 0x35e6b369, 0x040ea9f4, 0xa279a240, - 0x5431d2a9, 0xf246d91d, 0xc3aec380, 0x65d9c834, - 0xa07ef6ba, 0x0609fd0e, 0x37e1e793, 0x9196ec27, - 0xcfbd399c, 0x69ca3228, 0x582228b5, 0xfe552301, - 0x3bf21d8f, 0x9d85163b, 0xac6d0ca6, 0x0a1a0712, - 0xfc5277fb, 0x5a257c4f, 0x6bcd66d2, 0xcdba6d66, - 0x081d53e8, 0xae6a585c, 0x9f8242c1, 0x39f54975, - 0xa863a552, 0x0e14aee6, 0x3ffcb47b, 0x998bbfcf, - 0x5c2c8141, 0xfa5b8af5, 0xcbb39068, 0x6dc49bdc, - 0x9b8ceb35, 0x3dfbe081, 0x0c13fa1c, 0xaa64f1a8, - 0x6fc3cf26, 0xc9b4c492, 0xf85cde0f, 0x5e2bd5bb, - 0x440b7579, 0xe27c7ecd, 0xd3946450, 0x75e36fe4, - 0xb044516a, 0x16335ade, 0x27db4043, 0x81ac4bf7, - 0x77e43b1e, 0xd19330aa, 0xe07b2a37, 0x460c2183, - 0x83ab1f0d, 0x25dc14b9, 0x14340e24, 0xb2430590, - 0x23d5e9b7, 0x85a2e203, 0xb44af89e, 0x123df32a, - 0xd79acda4, 0x71edc610, 0x4005dc8d, 0xe672d739, - 0x103aa7d0, 0xb64dac64, 0x87a5b6f9, 0x21d2bd4d, - 0xe47583c3, 0x42028877, 0x73ea92ea, 0xd59d995e, - 0x8bb64ce5, 0x2dc14751, 0x1c295dcc, 0xba5e5678, - 0x7ff968f6, 0xd98e6342, 0xe86679df, 0x4e11726b, - 0xb8590282, 0x1e2e0936, 0x2fc613ab, 0x89b1181f, - 0x4c162691, 0xea612d25, 0xdb8937b8, 0x7dfe3c0c, - 0xec68d02b, 0x4a1fdb9f, 0x7bf7c102, 0xdd80cab6, - 0x1827f438, 0xbe50ff8c, 0x8fb8e511, 0x29cfeea5, - 0xdf879e4c, 0x79f095f8, 0x48188f65, 0xee6f84d1, - 0x2bc8ba5f, 0x8dbfb1eb, 0xbc57ab76, 0x1a20a0c2, - 0x8816eaf2, 0x2e61e146, 0x1f89fbdb, 0xb9fef06f, - 0x7c59cee1, 0xda2ec555, 0xebc6dfc8, 0x4db1d47c, - 0xbbf9a495, 0x1d8eaf21, 0x2c66b5bc, 0x8a11be08, - 0x4fb68086, 0xe9c18b32, 0xd82991af, 0x7e5e9a1b, - 0xefc8763c, 0x49bf7d88, 0x78576715, 0xde206ca1, - 0x1b87522f, 0xbdf0599b, 0x8c184306, 0x2a6f48b2, - 0xdc27385b, 0x7a5033ef, 0x4bb82972, 0xedcf22c6, - 0x28681c48, 0x8e1f17fc, 0xbff70d61, 0x198006d5, - 0x47abd36e, 0xe1dcd8da, 0xd034c247, 0x7643c9f3, - 0xb3e4f77d, 0x1593fcc9, 0x247be654, 0x820cede0, - 0x74449d09, 0xd23396bd, 0xe3db8c20, 0x45ac8794, - 0x800bb91a, 0x267cb2ae, 0x1794a833, 0xb1e3a387, - 0x20754fa0, 0x86024414, 0xb7ea5e89, 0x119d553d, - 0xd43a6bb3, 0x724d6007, 0x43a57a9a, 0xe5d2712e, - 0x139a01c7, 0xb5ed0a73, 0x840510ee, 0x22721b5a, - 0xe7d525d4, 0x41a22e60, 0x704a34fd, 0xd63d3f49, - 0xcc1d9f8b, 0x6a6a943f, 0x5b828ea2, 0xfdf58516, - 0x3852bb98, 0x9e25b02c, 0xafcdaab1, 0x09baa105, - 0xfff2d1ec, 0x5985da58, 0x686dc0c5, 0xce1acb71, - 0x0bbdf5ff, 0xadcafe4b, 0x9c22e4d6, 0x3a55ef62, - 0xabc30345, 0x0db408f1, 0x3c5c126c, 0x9a2b19d8, - 0x5f8c2756, 0xf9fb2ce2, 0xc813367f, 0x6e643dcb, - 0x982c4d22, 0x3e5b4696, 0x0fb35c0b, 0xa9c457bf, - 0x6c636931, 0xca146285, 0xfbfc7818, 0x5d8b73ac, - 0x03a0a617, 0xa5d7ada3, 0x943fb73e, 0x3248bc8a, - 0xf7ef8204, 0x519889b0, 0x6070932d, 0xc6079899, - 0x304fe870, 0x9638e3c4, 0xa7d0f959, 0x01a7f2ed, - 0xc400cc63, 0x6277c7d7, 0x539fdd4a, 0xf5e8d6fe, - 0x647e3ad9, 0xc209316d, 0xf3e12bf0, 0x55962044, - 0x90311eca, 0x3646157e, 0x07ae0fe3, 0xa1d90457, - 0x579174be, 0xf1e67f0a, 0xc00e6597, 0x66796e23, - 0xa3de50ad, 0x05a95b19, 0x34414184, 0x92364a30, - 0x00000000, 0xccaa009e, 0x4225077d, 0x8e8f07e3, - 0x844a0efa, 0x48e00e64, 0xc66f0987, 0x0ac50919, - 0xd3e51bb5, 0x1f4f1b2b, 0x91c01cc8, 0x5d6a1c56, - 0x57af154f, 0x9b0515d1, 0x158a1232, 0xd92012ac, - 0x7cbb312b, 0xb01131b5, 0x3e9e3656, 0xf23436c8, - 0xf8f13fd1, 0x345b3f4f, 0xbad438ac, 0x767e3832, - 0xaf5e2a9e, 0x63f42a00, 0xed7b2de3, 0x21d12d7d, - 0x2b142464, 0xe7be24fa, 0x69312319, 0xa59b2387, - 0xf9766256, 0x35dc62c8, 0xbb53652b, 0x77f965b5, - 0x7d3c6cac, 0xb1966c32, 0x3f196bd1, 0xf3b36b4f, - 0x2a9379e3, 0xe639797d, 0x68b67e9e, 0xa41c7e00, - 0xaed97719, 0x62737787, 0xecfc7064, 0x205670fa, - 0x85cd537d, 0x496753e3, 0xc7e85400, 0x0b42549e, - 0x01875d87, 0xcd2d5d19, 0x43a25afa, 0x8f085a64, - 0x562848c8, 0x9a824856, 0x140d4fb5, 0xd8a74f2b, - 0xd2624632, 0x1ec846ac, 0x9047414f, 0x5ced41d1, - 0x299dc2ed, 0xe537c273, 0x6bb8c590, 0xa712c50e, - 0xadd7cc17, 0x617dcc89, 0xeff2cb6a, 0x2358cbf4, - 0xfa78d958, 0x36d2d9c6, 0xb85dde25, 0x74f7debb, - 0x7e32d7a2, 0xb298d73c, 0x3c17d0df, 0xf0bdd041, - 0x5526f3c6, 0x998cf358, 0x1703f4bb, 0xdba9f425, - 0xd16cfd3c, 0x1dc6fda2, 0x9349fa41, 0x5fe3fadf, - 0x86c3e873, 0x4a69e8ed, 0xc4e6ef0e, 0x084cef90, - 0x0289e689, 0xce23e617, 0x40ace1f4, 0x8c06e16a, - 0xd0eba0bb, 0x1c41a025, 0x92cea7c6, 0x5e64a758, - 0x54a1ae41, 0x980baedf, 0x1684a93c, 0xda2ea9a2, - 0x030ebb0e, 0xcfa4bb90, 0x412bbc73, 0x8d81bced, - 0x8744b5f4, 0x4beeb56a, 0xc561b289, 0x09cbb217, - 0xac509190, 0x60fa910e, 0xee7596ed, 0x22df9673, - 0x281a9f6a, 0xe4b09ff4, 0x6a3f9817, 0xa6959889, - 0x7fb58a25, 0xb31f8abb, 0x3d908d58, 0xf13a8dc6, - 0xfbff84df, 0x37558441, 0xb9da83a2, 0x7570833c, - 0x533b85da, 0x9f918544, 0x111e82a7, 0xddb48239, - 0xd7718b20, 0x1bdb8bbe, 0x95548c5d, 0x59fe8cc3, - 0x80de9e6f, 0x4c749ef1, 0xc2fb9912, 0x0e51998c, - 0x04949095, 0xc83e900b, 0x46b197e8, 0x8a1b9776, - 0x2f80b4f1, 0xe32ab46f, 0x6da5b38c, 0xa10fb312, - 0xabcaba0b, 0x6760ba95, 0xe9efbd76, 0x2545bde8, - 0xfc65af44, 0x30cfafda, 0xbe40a839, 0x72eaa8a7, - 0x782fa1be, 0xb485a120, 0x3a0aa6c3, 0xf6a0a65d, - 0xaa4de78c, 0x66e7e712, 0xe868e0f1, 0x24c2e06f, - 0x2e07e976, 0xe2ade9e8, 0x6c22ee0b, 0xa088ee95, - 0x79a8fc39, 0xb502fca7, 0x3b8dfb44, 0xf727fbda, - 0xfde2f2c3, 0x3148f25d, 0xbfc7f5be, 0x736df520, - 0xd6f6d6a7, 0x1a5cd639, 0x94d3d1da, 0x5879d144, - 0x52bcd85d, 0x9e16d8c3, 0x1099df20, 0xdc33dfbe, - 0x0513cd12, 0xc9b9cd8c, 0x4736ca6f, 0x8b9ccaf1, - 0x8159c3e8, 0x4df3c376, 0xc37cc495, 0x0fd6c40b, - 0x7aa64737, 0xb60c47a9, 0x3883404a, 0xf42940d4, - 0xfeec49cd, 0x32464953, 0xbcc94eb0, 0x70634e2e, - 0xa9435c82, 0x65e95c1c, 0xeb665bff, 0x27cc5b61, - 0x2d095278, 0xe1a352e6, 0x6f2c5505, 0xa386559b, - 0x061d761c, 0xcab77682, 0x44387161, 0x889271ff, - 0x825778e6, 0x4efd7878, 0xc0727f9b, 0x0cd87f05, - 0xd5f86da9, 0x19526d37, 0x97dd6ad4, 0x5b776a4a, - 0x51b26353, 0x9d1863cd, 0x1397642e, 0xdf3d64b0, - 0x83d02561, 0x4f7a25ff, 0xc1f5221c, 0x0d5f2282, - 0x079a2b9b, 0xcb302b05, 0x45bf2ce6, 0x89152c78, - 0x50353ed4, 0x9c9f3e4a, 0x121039a9, 0xdeba3937, - 0xd47f302e, 0x18d530b0, 0x965a3753, 0x5af037cd, - 0xff6b144a, 0x33c114d4, 0xbd4e1337, 0x71e413a9, - 0x7b211ab0, 0xb78b1a2e, 0x39041dcd, 0xf5ae1d53, - 0x2c8e0fff, 0xe0240f61, 0x6eab0882, 0xa201081c, - 0xa8c40105, 0x646e019b, 0xeae10678, 0x264b06e6, -}; diff --git a/vendor/libdeflate/lib/decompress_template.h b/vendor/libdeflate/lib/decompress_template.h deleted file mode 100644 index 8c874c3644..0000000000 --- a/vendor/libdeflate/lib/decompress_template.h +++ /dev/null @@ -1,777 +0,0 @@ -/* - * decompress_template.h - * - * Copyright 2016 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * This is the actual DEFLATE decompression routine, lifted out of - * deflate_decompress.c so that it can be compiled multiple times with different - * target instruction sets. - */ - -#ifndef ATTRIBUTES -# define ATTRIBUTES -#endif -#ifndef EXTRACT_VARBITS -# define EXTRACT_VARBITS(word, count) ((word) & BITMASK(count)) -#endif -#ifndef EXTRACT_VARBITS8 -# define EXTRACT_VARBITS8(word, count) ((word) & BITMASK((u8)(count))) -#endif - -static ATTRIBUTES MAYBE_UNUSED enum libdeflate_result -FUNCNAME(struct libdeflate_decompressor * restrict d, - const void * restrict in, size_t in_nbytes, - void * restrict out, size_t out_nbytes_avail, - size_t *actual_in_nbytes_ret, size_t *actual_out_nbytes_ret) -{ - u8 *out_next = out; - u8 * const out_end = out_next + out_nbytes_avail; - u8 * const out_fastloop_end = - out_end - MIN(out_nbytes_avail, FASTLOOP_MAX_BYTES_WRITTEN); - - /* Input bitstream state; see deflate_decompress.c for documentation */ - const u8 *in_next = in; - const u8 * const in_end = in_next + in_nbytes; - const u8 * const in_fastloop_end = - in_end - MIN(in_nbytes, FASTLOOP_MAX_BYTES_READ); - bitbuf_t bitbuf = 0; - bitbuf_t saved_bitbuf; - u32 bitsleft = 0; - size_t overread_count = 0; - - bool is_final_block; - unsigned block_type; - unsigned num_litlen_syms; - unsigned num_offset_syms; - bitbuf_t litlen_tablemask; - u32 entry; - -next_block: - /* Starting to read the next block */ - ; - - STATIC_ASSERT(CAN_CONSUME(1 + 2 + 5 + 5 + 4 + 3)); - REFILL_BITS(); - - /* BFINAL: 1 bit */ - is_final_block = bitbuf & BITMASK(1); - - /* BTYPE: 2 bits */ - block_type = (bitbuf >> 1) & BITMASK(2); - - if (block_type == DEFLATE_BLOCKTYPE_DYNAMIC_HUFFMAN) { - - /* Dynamic Huffman block */ - - /* The order in which precode lengths are stored */ - static const u8 deflate_precode_lens_permutation[DEFLATE_NUM_PRECODE_SYMS] = { - 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 - }; - - unsigned num_explicit_precode_lens; - unsigned i; - - /* Read the codeword length counts. */ - - STATIC_ASSERT(DEFLATE_NUM_LITLEN_SYMS == 257 + BITMASK(5)); - num_litlen_syms = 257 + ((bitbuf >> 3) & BITMASK(5)); - - STATIC_ASSERT(DEFLATE_NUM_OFFSET_SYMS == 1 + BITMASK(5)); - num_offset_syms = 1 + ((bitbuf >> 8) & BITMASK(5)); - - STATIC_ASSERT(DEFLATE_NUM_PRECODE_SYMS == 4 + BITMASK(4)); - num_explicit_precode_lens = 4 + ((bitbuf >> 13) & BITMASK(4)); - - d->static_codes_loaded = false; - - /* - * Read the precode codeword lengths. - * - * A 64-bit bitbuffer is just one bit too small to hold the - * maximum number of precode lens, so to minimize branches we - * merge one len with the previous fields. - */ - STATIC_ASSERT(DEFLATE_MAX_PRE_CODEWORD_LEN == (1 << 3) - 1); - if (CAN_CONSUME(3 * (DEFLATE_NUM_PRECODE_SYMS - 1))) { - d->u.precode_lens[deflate_precode_lens_permutation[0]] = - (bitbuf >> 17) & BITMASK(3); - bitbuf >>= 20; - bitsleft -= 20; - REFILL_BITS(); - i = 1; - do { - d->u.precode_lens[deflate_precode_lens_permutation[i]] = - bitbuf & BITMASK(3); - bitbuf >>= 3; - bitsleft -= 3; - } while (++i < num_explicit_precode_lens); - } else { - bitbuf >>= 17; - bitsleft -= 17; - i = 0; - do { - if ((u8)bitsleft < 3) - REFILL_BITS(); - d->u.precode_lens[deflate_precode_lens_permutation[i]] = - bitbuf & BITMASK(3); - bitbuf >>= 3; - bitsleft -= 3; - } while (++i < num_explicit_precode_lens); - } - for (; i < DEFLATE_NUM_PRECODE_SYMS; i++) - d->u.precode_lens[deflate_precode_lens_permutation[i]] = 0; - - /* Build the decode table for the precode. */ - SAFETY_CHECK(build_precode_decode_table(d)); - - /* Decode the litlen and offset codeword lengths. */ - i = 0; - do { - unsigned presym; - u8 rep_val; - unsigned rep_count; - - if ((u8)bitsleft < DEFLATE_MAX_PRE_CODEWORD_LEN + 7) - REFILL_BITS(); - - /* - * The code below assumes that the precode decode table - * doesn't have any subtables. - */ - STATIC_ASSERT(PRECODE_TABLEBITS == DEFLATE_MAX_PRE_CODEWORD_LEN); - - /* Decode the next precode symbol. */ - entry = d->u.l.precode_decode_table[ - bitbuf & BITMASK(DEFLATE_MAX_PRE_CODEWORD_LEN)]; - bitbuf >>= (u8)entry; - bitsleft -= entry; /* optimization: subtract full entry */ - presym = entry >> 16; - - if (presym < 16) { - /* Explicit codeword length */ - d->u.l.lens[i++] = presym; - continue; - } - - /* Run-length encoded codeword lengths */ - - /* - * Note: we don't need to immediately verify that the - * repeat count doesn't overflow the number of elements, - * since we've sized the lens array to have enough extra - * space to allow for the worst-case overrun (138 zeroes - * when only 1 length was remaining). - * - * In the case of the small repeat counts (presyms 16 - * and 17), it is fastest to always write the maximum - * number of entries. That gets rid of branches that - * would otherwise be required. - * - * It is not just because of the numerical order that - * our checks go in the order 'presym < 16', 'presym == - * 16', and 'presym == 17'. For typical data this is - * ordered from most frequent to least frequent case. - */ - STATIC_ASSERT(DEFLATE_MAX_LENS_OVERRUN == 138 - 1); - - if (presym == 16) { - /* Repeat the previous length 3 - 6 times. */ - SAFETY_CHECK(i != 0); - rep_val = d->u.l.lens[i - 1]; - STATIC_ASSERT(3 + BITMASK(2) == 6); - rep_count = 3 + (bitbuf & BITMASK(2)); - bitbuf >>= 2; - bitsleft -= 2; - d->u.l.lens[i + 0] = rep_val; - d->u.l.lens[i + 1] = rep_val; - d->u.l.lens[i + 2] = rep_val; - d->u.l.lens[i + 3] = rep_val; - d->u.l.lens[i + 4] = rep_val; - d->u.l.lens[i + 5] = rep_val; - i += rep_count; - } else if (presym == 17) { - /* Repeat zero 3 - 10 times. */ - STATIC_ASSERT(3 + BITMASK(3) == 10); - rep_count = 3 + (bitbuf & BITMASK(3)); - bitbuf >>= 3; - bitsleft -= 3; - d->u.l.lens[i + 0] = 0; - d->u.l.lens[i + 1] = 0; - d->u.l.lens[i + 2] = 0; - d->u.l.lens[i + 3] = 0; - d->u.l.lens[i + 4] = 0; - d->u.l.lens[i + 5] = 0; - d->u.l.lens[i + 6] = 0; - d->u.l.lens[i + 7] = 0; - d->u.l.lens[i + 8] = 0; - d->u.l.lens[i + 9] = 0; - i += rep_count; - } else { - /* Repeat zero 11 - 138 times. */ - STATIC_ASSERT(11 + BITMASK(7) == 138); - rep_count = 11 + (bitbuf & BITMASK(7)); - bitbuf >>= 7; - bitsleft -= 7; - memset(&d->u.l.lens[i], 0, - rep_count * sizeof(d->u.l.lens[i])); - i += rep_count; - } - } while (i < num_litlen_syms + num_offset_syms); - - /* Unnecessary, but check this for consistency with zlib. */ - SAFETY_CHECK(i == num_litlen_syms + num_offset_syms); - - } else if (block_type == DEFLATE_BLOCKTYPE_UNCOMPRESSED) { - u16 len, nlen; - - /* - * Uncompressed block: copy 'len' bytes literally from the input - * buffer to the output buffer. - */ - - bitsleft -= 3; /* for BTYPE and BFINAL */ - - /* - * Align the bitstream to the next byte boundary. This means - * the next byte boundary as if we were reading a byte at a - * time. Therefore, we have to rewind 'in_next' by any bytes - * that have been refilled but not actually consumed yet (not - * counting overread bytes, which don't increment 'in_next'). - */ - bitsleft = (u8)bitsleft; - SAFETY_CHECK(overread_count <= (bitsleft >> 3)); - in_next -= (bitsleft >> 3) - overread_count; - overread_count = 0; - bitbuf = 0; - bitsleft = 0; - - SAFETY_CHECK(in_end - in_next >= 4); - len = get_unaligned_le16(in_next); - nlen = get_unaligned_le16(in_next + 2); - in_next += 4; - - SAFETY_CHECK(len == (u16)~nlen); - if (unlikely(len > out_end - out_next)) - return LIBDEFLATE_INSUFFICIENT_SPACE; - SAFETY_CHECK(len <= in_end - in_next); - - memcpy(out_next, in_next, len); - in_next += len; - out_next += len; - - goto block_done; - - } else { - unsigned i; - - SAFETY_CHECK(block_type == DEFLATE_BLOCKTYPE_STATIC_HUFFMAN); - - /* - * Static Huffman block: build the decode tables for the static - * codes. Skip doing so if the tables are already set up from - * an earlier static block; this speeds up decompression of - * degenerate input of many empty or very short static blocks. - * - * Afterwards, the remainder is the same as decompressing a - * dynamic Huffman block. - */ - - bitbuf >>= 3; /* for BTYPE and BFINAL */ - bitsleft -= 3; - - if (d->static_codes_loaded) - goto have_decode_tables; - - d->static_codes_loaded = true; - - STATIC_ASSERT(DEFLATE_NUM_LITLEN_SYMS == 288); - STATIC_ASSERT(DEFLATE_NUM_OFFSET_SYMS == 32); - - for (i = 0; i < 144; i++) - d->u.l.lens[i] = 8; - for (; i < 256; i++) - d->u.l.lens[i] = 9; - for (; i < 280; i++) - d->u.l.lens[i] = 7; - for (; i < 288; i++) - d->u.l.lens[i] = 8; - - for (; i < 288 + 32; i++) - d->u.l.lens[i] = 5; - - num_litlen_syms = 288; - num_offset_syms = 32; - } - - /* Decompressing a Huffman block (either dynamic or static) */ - - SAFETY_CHECK(build_offset_decode_table(d, num_litlen_syms, num_offset_syms)); - SAFETY_CHECK(build_litlen_decode_table(d, num_litlen_syms, num_offset_syms)); -have_decode_tables: - litlen_tablemask = BITMASK(d->litlen_tablebits); - - /* - * This is the "fastloop" for decoding literals and matches. It does - * bounds checks on in_next and out_next in the loop conditions so that - * additional bounds checks aren't needed inside the loop body. - * - * To reduce latency, the bitbuffer is refilled and the next litlen - * decode table entry is preloaded before each loop iteration. - */ - if (in_next >= in_fastloop_end || out_next >= out_fastloop_end) - goto generic_loop; - REFILL_BITS_IN_FASTLOOP(); - entry = d->u.litlen_decode_table[bitbuf & litlen_tablemask]; - do { - u32 length, offset, lit; - const u8 *src; - u8 *dst; - - /* - * Consume the bits for the litlen decode table entry. Save the - * original bitbuf for later, in case the extra match length - * bits need to be extracted from it. - */ - saved_bitbuf = bitbuf; - bitbuf >>= (u8)entry; - bitsleft -= entry; /* optimization: subtract full entry */ - - /* - * Begin by checking for a "fast" literal, i.e. a literal that - * doesn't need a subtable. - */ - if (entry & HUFFDEC_LITERAL) { - /* - * On 64-bit platforms, we decode up to 2 extra fast - * literals in addition to the primary item, as this - * increases performance and still leaves enough bits - * remaining for what follows. We could actually do 3, - * assuming LITLEN_TABLEBITS=11, but that actually - * decreases performance slightly (perhaps by messing - * with the branch prediction of the conditional refill - * that happens later while decoding the match offset). - * - * Note: the definitions of FASTLOOP_MAX_BYTES_WRITTEN - * and FASTLOOP_MAX_BYTES_READ need to be updated if the - * number of extra literals decoded here is changed. - */ - if (/* enough bits for 2 fast literals + length + offset preload? */ - CAN_CONSUME_AND_THEN_PRELOAD(2 * LITLEN_TABLEBITS + - LENGTH_MAXBITS, - OFFSET_TABLEBITS) && - /* enough bits for 2 fast literals + slow literal + litlen preload? */ - CAN_CONSUME_AND_THEN_PRELOAD(2 * LITLEN_TABLEBITS + - DEFLATE_MAX_LITLEN_CODEWORD_LEN, - LITLEN_TABLEBITS)) { - /* 1st extra fast literal */ - lit = entry >> 16; - entry = d->u.litlen_decode_table[bitbuf & litlen_tablemask]; - saved_bitbuf = bitbuf; - bitbuf >>= (u8)entry; - bitsleft -= entry; - *out_next++ = lit; - if (entry & HUFFDEC_LITERAL) { - /* 2nd extra fast literal */ - lit = entry >> 16; - entry = d->u.litlen_decode_table[bitbuf & litlen_tablemask]; - saved_bitbuf = bitbuf; - bitbuf >>= (u8)entry; - bitsleft -= entry; - *out_next++ = lit; - if (entry & HUFFDEC_LITERAL) { - /* - * Another fast literal, but - * this one is in lieu of the - * primary item, so it doesn't - * count as one of the extras. - */ - lit = entry >> 16; - entry = d->u.litlen_decode_table[bitbuf & litlen_tablemask]; - REFILL_BITS_IN_FASTLOOP(); - *out_next++ = lit; - continue; - } - } - } else { - /* - * Decode a literal. While doing so, preload - * the next litlen decode table entry and refill - * the bitbuffer. To reduce latency, we've - * arranged for there to be enough "preloadable" - * bits remaining to do the table preload - * independently of the refill. - */ - STATIC_ASSERT(CAN_CONSUME_AND_THEN_PRELOAD( - LITLEN_TABLEBITS, LITLEN_TABLEBITS)); - lit = entry >> 16; - entry = d->u.litlen_decode_table[bitbuf & litlen_tablemask]; - REFILL_BITS_IN_FASTLOOP(); - *out_next++ = lit; - continue; - } - } - - /* - * It's not a literal entry, so it can be a length entry, a - * subtable pointer entry, or an end-of-block entry. Detect the - * two unlikely cases by testing the HUFFDEC_EXCEPTIONAL flag. - */ - if (unlikely(entry & HUFFDEC_EXCEPTIONAL)) { - /* Subtable pointer or end-of-block entry */ - - if (unlikely(entry & HUFFDEC_END_OF_BLOCK)) - goto block_done; - - /* - * A subtable is required. Load and consume the - * subtable entry. The subtable entry can be of any - * type: literal, length, or end-of-block. - */ - entry = d->u.litlen_decode_table[(entry >> 16) + - EXTRACT_VARBITS(bitbuf, (entry >> 8) & 0x3F)]; - saved_bitbuf = bitbuf; - bitbuf >>= (u8)entry; - bitsleft -= entry; - - /* - * 32-bit platforms that use the byte-at-a-time refill - * method have to do a refill here for there to always - * be enough bits to decode a literal that requires a - * subtable, then preload the next litlen decode table - * entry; or to decode a match length that requires a - * subtable, then preload the offset decode table entry. - */ - if (!CAN_CONSUME_AND_THEN_PRELOAD(DEFLATE_MAX_LITLEN_CODEWORD_LEN, - LITLEN_TABLEBITS) || - !CAN_CONSUME_AND_THEN_PRELOAD(LENGTH_MAXBITS, - OFFSET_TABLEBITS)) - REFILL_BITS_IN_FASTLOOP(); - if (entry & HUFFDEC_LITERAL) { - /* Decode a literal that required a subtable. */ - lit = entry >> 16; - entry = d->u.litlen_decode_table[bitbuf & litlen_tablemask]; - REFILL_BITS_IN_FASTLOOP(); - *out_next++ = lit; - continue; - } - if (unlikely(entry & HUFFDEC_END_OF_BLOCK)) - goto block_done; - /* Else, it's a length that required a subtable. */ - } - - /* - * Decode the match length: the length base value associated - * with the litlen symbol (which we extract from the decode - * table entry), plus the extra length bits. We don't need to - * consume the extra length bits here, as they were included in - * the bits consumed by the entry earlier. We also don't need - * to check for too-long matches here, as this is inside the - * fastloop where it's already been verified that the output - * buffer has enough space remaining to copy a max-length match. - */ - length = entry >> 16; - length += EXTRACT_VARBITS8(saved_bitbuf, entry) >> (u8)(entry >> 8); - - /* - * Decode the match offset. There are enough "preloadable" bits - * remaining to preload the offset decode table entry, but a - * refill might be needed before consuming it. - */ - STATIC_ASSERT(CAN_CONSUME_AND_THEN_PRELOAD(LENGTH_MAXFASTBITS, - OFFSET_TABLEBITS)); - entry = d->offset_decode_table[bitbuf & BITMASK(OFFSET_TABLEBITS)]; - if (CAN_CONSUME_AND_THEN_PRELOAD(OFFSET_MAXBITS, - LITLEN_TABLEBITS)) { - /* - * Decoding a match offset on a 64-bit platform. We may - * need to refill once, but then we can decode the whole - * offset and preload the next litlen table entry. - */ - if (unlikely(entry & HUFFDEC_EXCEPTIONAL)) { - /* Offset codeword requires a subtable */ - if (unlikely((u8)bitsleft < OFFSET_MAXBITS + - LITLEN_TABLEBITS - PRELOAD_SLACK)) - REFILL_BITS_IN_FASTLOOP(); - bitbuf >>= OFFSET_TABLEBITS; - bitsleft -= OFFSET_TABLEBITS; - entry = d->offset_decode_table[(entry >> 16) + - EXTRACT_VARBITS(bitbuf, (entry >> 8) & 0x3F)]; - } else if (unlikely((u8)bitsleft < OFFSET_MAXFASTBITS + - LITLEN_TABLEBITS - PRELOAD_SLACK)) - REFILL_BITS_IN_FASTLOOP(); - } else { - /* Decoding a match offset on a 32-bit platform */ - REFILL_BITS_IN_FASTLOOP(); - if (unlikely(entry & HUFFDEC_EXCEPTIONAL)) { - /* Offset codeword requires a subtable */ - bitbuf >>= OFFSET_TABLEBITS; - bitsleft -= OFFSET_TABLEBITS; - entry = d->offset_decode_table[(entry >> 16) + - EXTRACT_VARBITS(bitbuf, (entry >> 8) & 0x3F)]; - REFILL_BITS_IN_FASTLOOP(); - /* No further refill needed before extra bits */ - STATIC_ASSERT(CAN_CONSUME( - OFFSET_MAXBITS - OFFSET_TABLEBITS)); - } else { - /* No refill needed before extra bits */ - STATIC_ASSERT(CAN_CONSUME(OFFSET_MAXFASTBITS)); - } - } - saved_bitbuf = bitbuf; - bitbuf >>= (u8)entry; - bitsleft -= entry; /* optimization: subtract full entry */ - offset = entry >> 16; - offset += EXTRACT_VARBITS8(saved_bitbuf, entry) >> (u8)(entry >> 8); - - /* Validate the match offset; needed even in the fastloop. */ - SAFETY_CHECK(offset <= out_next - (const u8 *)out); - src = out_next - offset; - dst = out_next; - out_next += length; - - /* - * Before starting to issue the instructions to copy the match, - * refill the bitbuffer and preload the litlen decode table - * entry for the next loop iteration. This can increase - * performance by allowing the latency of the match copy to - * overlap with these other operations. To further reduce - * latency, we've arranged for there to be enough bits remaining - * to do the table preload independently of the refill, except - * on 32-bit platforms using the byte-at-a-time refill method. - */ - if (!CAN_CONSUME_AND_THEN_PRELOAD( - MAX(OFFSET_MAXBITS - OFFSET_TABLEBITS, - OFFSET_MAXFASTBITS), - LITLEN_TABLEBITS) && - unlikely((u8)bitsleft < LITLEN_TABLEBITS - PRELOAD_SLACK)) - REFILL_BITS_IN_FASTLOOP(); - entry = d->u.litlen_decode_table[bitbuf & litlen_tablemask]; - REFILL_BITS_IN_FASTLOOP(); - - /* - * Copy the match. On most CPUs the fastest method is a - * word-at-a-time copy, unconditionally copying about 5 words - * since this is enough for most matches without being too much. - * - * The normal word-at-a-time copy works for offset >= WORDBYTES, - * which is most cases. The case of offset == 1 is also common - * and is worth optimizing for, since it is just RLE encoding of - * the previous byte, which is the result of compressing long - * runs of the same byte. - * - * Writing past the match 'length' is allowed here, since it's - * been ensured there is enough output space left for a slight - * overrun. FASTLOOP_MAX_BYTES_WRITTEN needs to be updated if - * the maximum possible overrun here is changed. - */ - if (UNALIGNED_ACCESS_IS_FAST && offset >= WORDBYTES) { - store_word_unaligned(load_word_unaligned(src), dst); - src += WORDBYTES; - dst += WORDBYTES; - store_word_unaligned(load_word_unaligned(src), dst); - src += WORDBYTES; - dst += WORDBYTES; - store_word_unaligned(load_word_unaligned(src), dst); - src += WORDBYTES; - dst += WORDBYTES; - store_word_unaligned(load_word_unaligned(src), dst); - src += WORDBYTES; - dst += WORDBYTES; - store_word_unaligned(load_word_unaligned(src), dst); - src += WORDBYTES; - dst += WORDBYTES; - while (dst < out_next) { - store_word_unaligned(load_word_unaligned(src), dst); - src += WORDBYTES; - dst += WORDBYTES; - store_word_unaligned(load_word_unaligned(src), dst); - src += WORDBYTES; - dst += WORDBYTES; - store_word_unaligned(load_word_unaligned(src), dst); - src += WORDBYTES; - dst += WORDBYTES; - store_word_unaligned(load_word_unaligned(src), dst); - src += WORDBYTES; - dst += WORDBYTES; - store_word_unaligned(load_word_unaligned(src), dst); - src += WORDBYTES; - dst += WORDBYTES; - } - } else if (UNALIGNED_ACCESS_IS_FAST && offset == 1) { - machine_word_t v; - - /* - * This part tends to get auto-vectorized, so keep it - * copying a multiple of 16 bytes at a time. - */ - v = (machine_word_t)0x0101010101010101 * src[0]; - store_word_unaligned(v, dst); - dst += WORDBYTES; - store_word_unaligned(v, dst); - dst += WORDBYTES; - store_word_unaligned(v, dst); - dst += WORDBYTES; - store_word_unaligned(v, dst); - dst += WORDBYTES; - while (dst < out_next) { - store_word_unaligned(v, dst); - dst += WORDBYTES; - store_word_unaligned(v, dst); - dst += WORDBYTES; - store_word_unaligned(v, dst); - dst += WORDBYTES; - store_word_unaligned(v, dst); - dst += WORDBYTES; - } - } else if (UNALIGNED_ACCESS_IS_FAST) { - store_word_unaligned(load_word_unaligned(src), dst); - src += offset; - dst += offset; - store_word_unaligned(load_word_unaligned(src), dst); - src += offset; - dst += offset; - do { - store_word_unaligned(load_word_unaligned(src), dst); - src += offset; - dst += offset; - store_word_unaligned(load_word_unaligned(src), dst); - src += offset; - dst += offset; - } while (dst < out_next); - } else { - *dst++ = *src++; - *dst++ = *src++; - do { - *dst++ = *src++; - } while (dst < out_next); - } - } while (in_next < in_fastloop_end && out_next < out_fastloop_end); - - /* - * This is the generic loop for decoding literals and matches. This - * handles cases where in_next and out_next are close to the end of - * their respective buffers. Usually this loop isn't performance- - * critical, as most time is spent in the fastloop above instead. We - * therefore omit some optimizations here in favor of smaller code. - */ -generic_loop: - for (;;) { - u32 length, offset; - const u8 *src; - u8 *dst; - - REFILL_BITS(); - entry = d->u.litlen_decode_table[bitbuf & litlen_tablemask]; - saved_bitbuf = bitbuf; - bitbuf >>= (u8)entry; - bitsleft -= entry; - if (unlikely(entry & HUFFDEC_SUBTABLE_POINTER)) { - entry = d->u.litlen_decode_table[(entry >> 16) + - EXTRACT_VARBITS(bitbuf, (entry >> 8) & 0x3F)]; - saved_bitbuf = bitbuf; - bitbuf >>= (u8)entry; - bitsleft -= entry; - } - length = entry >> 16; - if (entry & HUFFDEC_LITERAL) { - if (unlikely(out_next == out_end)) - return LIBDEFLATE_INSUFFICIENT_SPACE; - *out_next++ = length; - continue; - } - if (unlikely(entry & HUFFDEC_END_OF_BLOCK)) - goto block_done; - length += EXTRACT_VARBITS8(saved_bitbuf, entry) >> (u8)(entry >> 8); - if (unlikely(length > out_end - out_next)) - return LIBDEFLATE_INSUFFICIENT_SPACE; - - if (!CAN_CONSUME(LENGTH_MAXBITS + OFFSET_MAXBITS)) - REFILL_BITS(); - entry = d->offset_decode_table[bitbuf & BITMASK(OFFSET_TABLEBITS)]; - if (unlikely(entry & HUFFDEC_EXCEPTIONAL)) { - bitbuf >>= OFFSET_TABLEBITS; - bitsleft -= OFFSET_TABLEBITS; - entry = d->offset_decode_table[(entry >> 16) + - EXTRACT_VARBITS(bitbuf, (entry >> 8) & 0x3F)]; - if (!CAN_CONSUME(OFFSET_MAXBITS)) - REFILL_BITS(); - } - offset = entry >> 16; - offset += EXTRACT_VARBITS8(bitbuf, entry) >> (u8)(entry >> 8); - bitbuf >>= (u8)entry; - bitsleft -= entry; - - SAFETY_CHECK(offset <= out_next - (const u8 *)out); - src = out_next - offset; - dst = out_next; - out_next += length; - - STATIC_ASSERT(DEFLATE_MIN_MATCH_LEN == 3); - *dst++ = *src++; - *dst++ = *src++; - do { - *dst++ = *src++; - } while (dst < out_next); - } - -block_done: - /* Finished decoding a block */ - - if (!is_final_block) - goto next_block; - - /* That was the last block. */ - - bitsleft = (u8)bitsleft; - - /* - * If any of the implicit appended zero bytes were consumed (not just - * refilled) before hitting end of stream, then the data is bad. - */ - SAFETY_CHECK(overread_count <= (bitsleft >> 3)); - - /* Optionally return the actual number of bytes consumed. */ - if (actual_in_nbytes_ret) { - /* Don't count bytes that were refilled but not consumed. */ - in_next -= (bitsleft >> 3) - overread_count; - - *actual_in_nbytes_ret = in_next - (u8 *)in; - } - - /* Optionally return the actual number of bytes written. */ - if (actual_out_nbytes_ret) { - *actual_out_nbytes_ret = out_next - (u8 *)out; - } else { - if (out_next != out_end) - return LIBDEFLATE_SHORT_OUTPUT; - } - return LIBDEFLATE_SUCCESS; -} - -#undef FUNCNAME -#undef ATTRIBUTES -#undef EXTRACT_VARBITS -#undef EXTRACT_VARBITS8 diff --git a/vendor/libdeflate/lib/deflate_compress.c b/vendor/libdeflate/lib/deflate_compress.c deleted file mode 100644 index b24087c20c..0000000000 --- a/vendor/libdeflate/lib/deflate_compress.c +++ /dev/null @@ -1,4135 +0,0 @@ -/* - * deflate_compress.c - a compressor for DEFLATE - * - * Copyright 2016 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "deflate_compress.h" -#include "deflate_constants.h" - -/******************************************************************************/ - -/* - * The following parameters can be changed at build time to customize the - * compression algorithms slightly: - * - * (Note, not all customizable parameters are here. Some others can be found in - * libdeflate_alloc_compressor() and in *_matchfinder.h.) - */ - -/* - * If this parameter is defined to 1, then the near-optimal parsing algorithm - * will be included, and compression levels 10-12 will use it. This algorithm - * usually produces a compression ratio significantly better than the other - * algorithms. However, it is slow. If this parameter is defined to 0, then - * levels 10-12 will be the same as level 9 and will use the lazy2 algorithm. - */ -#define SUPPORT_NEAR_OPTIMAL_PARSING 1 - -/* - * This is the minimum block length that the compressor will use, in - * uncompressed bytes. This should be a value below which using shorter blocks - * is unlikely to be worthwhile, due to the per-block overhead. This value does - * not apply to the final block, which may be shorter than this (if the input is - * shorter, it will have to be), or to the final uncompressed block in a series - * of uncompressed blocks that cover more than UINT16_MAX bytes. - * - * This value is also approximately the amount by which what would otherwise be - * the second-to-last block is allowed to grow past the soft maximum length in - * order to avoid having to use a very short final block. - * - * Defining a fixed minimum block length is needed in order to guarantee a - * reasonable upper bound on the compressed size. It's also needed because our - * block splitting algorithm doesn't work well on very short blocks. - */ -#define MIN_BLOCK_LENGTH 5000 - -/* - * For the greedy, lazy, lazy2, and near-optimal compressors: This is the soft - * maximum block length, in uncompressed bytes. The compressor will try to end - * blocks at this length, but it may go slightly past it if there is a match - * that straddles this limit or if the input data ends soon after this limit. - * This parameter doesn't apply to uncompressed blocks, which the DEFLATE format - * limits to 65535 bytes. - * - * This should be a value above which it is very likely that splitting the block - * would produce a better compression ratio. For the near-optimal compressor, - * increasing/decreasing this parameter will increase/decrease per-compressor - * memory usage linearly. - */ -#define SOFT_MAX_BLOCK_LENGTH 300000 - -/* - * For the greedy, lazy, and lazy2 compressors: this is the length of the - * sequence store, which is an array where the compressor temporarily stores - * matches that it's going to use in the current block. This value is the - * maximum number of matches that can be used in a block. If the sequence store - * fills up, then the compressor will be forced to end the block early. This - * value should be large enough so that this rarely happens, due to the block - * being ended normally before then. Increasing/decreasing this value will - * increase/decrease per-compressor memory usage linearly. - */ -#define SEQ_STORE_LENGTH 50000 - -/* - * For deflate_compress_fastest(): This is the soft maximum block length. - * deflate_compress_fastest() doesn't use the regular block splitting algorithm; - * it only ends blocks when they reach FAST_SOFT_MAX_BLOCK_LENGTH bytes or - * FAST_SEQ_STORE_LENGTH matches. Therefore, this value should be lower than - * the regular SOFT_MAX_BLOCK_LENGTH. - */ -#define FAST_SOFT_MAX_BLOCK_LENGTH 65535 - -/* - * For deflate_compress_fastest(): this is the length of the sequence store. - * This is like SEQ_STORE_LENGTH, but this should be a lower value. - */ -#define FAST_SEQ_STORE_LENGTH 8192 - -/* - * These are the maximum codeword lengths, in bits, the compressor will use for - * each Huffman code. The DEFLATE format defines limits for these. However, - * further limiting litlen codewords to 14 bits is beneficial, since it has - * negligible effect on compression ratio but allows some optimizations when - * outputting bits. (It allows 4 literals to be written at once rather than 3.) - */ -#define MAX_LITLEN_CODEWORD_LEN 14 -#define MAX_OFFSET_CODEWORD_LEN DEFLATE_MAX_OFFSET_CODEWORD_LEN -#define MAX_PRE_CODEWORD_LEN DEFLATE_MAX_PRE_CODEWORD_LEN - -#if SUPPORT_NEAR_OPTIMAL_PARSING - -/* Parameters specific to the near-optimal parsing algorithm */ - -/* - * BIT_COST is a scaling factor that allows the near-optimal compressor to - * consider fractional bit costs when deciding which literal/match sequence to - * use. This is useful when the true symbol costs are unknown. For example, if - * the compressor thinks that a symbol has 6.5 bits of entropy, it can set its - * cost to 6.5 bits rather than have to use 6 or 7 bits. Although in the end - * each symbol will use a whole number of bits due to the Huffman coding, - * considering fractional bits can be helpful due to the limited information. - * - * BIT_COST should be a power of 2. A value of 8 or 16 works well. A higher - * value isn't very useful since the calculations are approximate anyway. - * - * BIT_COST doesn't apply to deflate_flush_block() and - * deflate_compute_true_cost(), which consider whole bits. - */ -#define BIT_COST 16 - -/* - * The NOSTAT_BITS value for a given alphabet is the number of bits assumed to - * be needed to output a symbol that was unused in the previous optimization - * pass. Assigning a default cost allows the symbol to be used in the next - * optimization pass. However, the cost should be relatively high because the - * symbol probably won't be used very many times (if at all). - */ -#define LITERAL_NOSTAT_BITS 13 -#define LENGTH_NOSTAT_BITS 13 -#define OFFSET_NOSTAT_BITS 10 - -/* - * This is (slightly less than) the maximum number of matches that the - * near-optimal compressor will cache per block. This behaves similarly to - * SEQ_STORE_LENGTH for the other compressors. - */ -#define MATCH_CACHE_LENGTH (SOFT_MAX_BLOCK_LENGTH * 5) - -#endif /* SUPPORT_NEAR_OPTIMAL_PARSING */ - -/******************************************************************************/ - -/* Include the needed matchfinders. */ -#define MATCHFINDER_WINDOW_ORDER DEFLATE_WINDOW_ORDER -#include "hc_matchfinder.h" -#include "ht_matchfinder.h" -#if SUPPORT_NEAR_OPTIMAL_PARSING -# include "bt_matchfinder.h" -/* - * This is the maximum number of matches the binary trees matchfinder can find - * at a single position. Since the matchfinder never finds more than one match - * for the same length, presuming one of each possible length is sufficient for - * an upper bound. (This says nothing about whether it is worthwhile to - * consider so many matches; this is just defining the worst case.) - */ -#define MAX_MATCHES_PER_POS \ - (DEFLATE_MAX_MATCH_LEN - DEFLATE_MIN_MATCH_LEN + 1) -#endif - -/* - * The largest block length we will ever use is when the final block is of - * length SOFT_MAX_BLOCK_LENGTH + MIN_BLOCK_LENGTH - 1, or when any block is of - * length SOFT_MAX_BLOCK_LENGTH + 1 + DEFLATE_MAX_MATCH_LEN. The latter case - * occurs when the lazy2 compressor chooses two literals and a maximum-length - * match, starting at SOFT_MAX_BLOCK_LENGTH - 1. - */ -#define MAX_BLOCK_LENGTH \ - MAX(SOFT_MAX_BLOCK_LENGTH + MIN_BLOCK_LENGTH - 1, \ - SOFT_MAX_BLOCK_LENGTH + 1 + DEFLATE_MAX_MATCH_LEN) - -static forceinline void -check_buildtime_parameters(void) -{ - /* - * Verify that MIN_BLOCK_LENGTH is being honored, as - * libdeflate_deflate_compress_bound() depends on it. - */ - STATIC_ASSERT(SOFT_MAX_BLOCK_LENGTH >= MIN_BLOCK_LENGTH); - STATIC_ASSERT(FAST_SOFT_MAX_BLOCK_LENGTH >= MIN_BLOCK_LENGTH); - STATIC_ASSERT(SEQ_STORE_LENGTH * DEFLATE_MIN_MATCH_LEN >= - MIN_BLOCK_LENGTH); - STATIC_ASSERT(FAST_SEQ_STORE_LENGTH * HT_MATCHFINDER_MIN_MATCH_LEN >= - MIN_BLOCK_LENGTH); -#if SUPPORT_NEAR_OPTIMAL_PARSING - STATIC_ASSERT(MIN_BLOCK_LENGTH * MAX_MATCHES_PER_POS <= - MATCH_CACHE_LENGTH); -#endif - - /* The definition of MAX_BLOCK_LENGTH assumes this. */ - STATIC_ASSERT(FAST_SOFT_MAX_BLOCK_LENGTH <= SOFT_MAX_BLOCK_LENGTH); - - /* Verify that the sequence stores aren't uselessly large. */ - STATIC_ASSERT(SEQ_STORE_LENGTH * DEFLATE_MIN_MATCH_LEN <= - SOFT_MAX_BLOCK_LENGTH + MIN_BLOCK_LENGTH); - STATIC_ASSERT(FAST_SEQ_STORE_LENGTH * HT_MATCHFINDER_MIN_MATCH_LEN <= - FAST_SOFT_MAX_BLOCK_LENGTH + MIN_BLOCK_LENGTH); - - /* Verify that the maximum codeword lengths are valid. */ - STATIC_ASSERT( - MAX_LITLEN_CODEWORD_LEN <= DEFLATE_MAX_LITLEN_CODEWORD_LEN); - STATIC_ASSERT( - MAX_OFFSET_CODEWORD_LEN <= DEFLATE_MAX_OFFSET_CODEWORD_LEN); - STATIC_ASSERT( - MAX_PRE_CODEWORD_LEN <= DEFLATE_MAX_PRE_CODEWORD_LEN); - STATIC_ASSERT( - (1U << MAX_LITLEN_CODEWORD_LEN) >= DEFLATE_NUM_LITLEN_SYMS); - STATIC_ASSERT( - (1U << MAX_OFFSET_CODEWORD_LEN) >= DEFLATE_NUM_OFFSET_SYMS); - STATIC_ASSERT( - (1U << MAX_PRE_CODEWORD_LEN) >= DEFLATE_NUM_PRECODE_SYMS); -} - -/******************************************************************************/ - -/* Table: length slot => length slot base value */ -static const u32 deflate_length_slot_base[] = { - 3, 4, 5, 6, 7, 8, 9, 10, - 11, 13, 15, 17, 19, 23, 27, 31, - 35, 43, 51, 59, 67, 83, 99, 115, - 131, 163, 195, 227, 258, -}; - -/* Table: length slot => number of extra length bits */ -static const u8 deflate_extra_length_bits[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 2, 2, 2, 2, - 3, 3, 3, 3, 4, 4, 4, 4, - 5, 5, 5, 5, 0, -}; - -/* Table: offset slot => offset slot base value */ -static const u32 deflate_offset_slot_base[] = { - 1, 2, 3, 4, 5, 7, 9, 13, - 17, 25, 33, 49, 65, 97, 129, 193, - 257, 385, 513, 769, 1025, 1537, 2049, 3073, - 4097, 6145, 8193, 12289, 16385, 24577, -}; - -/* Table: offset slot => number of extra offset bits */ -static const u8 deflate_extra_offset_bits[] = { - 0, 0, 0, 0, 1, 1, 2, 2, - 3, 3, 4, 4, 5, 5, 6, 6, - 7, 7, 8, 8, 9, 9, 10, 10, - 11, 11, 12, 12, 13, 13, -}; - -/* Table: length => length slot */ -static const u8 deflate_length_slot[DEFLATE_MAX_MATCH_LEN + 1] = { - 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, - 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, - 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, - 18, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, - 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, - 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, - 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 28, -}; - -/* - * Table: 'offset - 1 => offset_slot' for offset <= 256. - * This was generated by scripts/gen_offset_slot_map.py. - */ -static const u8 deflate_offset_slot[256] = { - 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, - 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, - 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, - 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, - 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, - 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, -}; - -/* The order in which precode codeword lengths are stored */ -static const u8 deflate_precode_lens_permutation[DEFLATE_NUM_PRECODE_SYMS] = { - 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 -}; - -/* Table: precode symbol => number of extra bits */ -static const u8 deflate_extra_precode_bits[DEFLATE_NUM_PRECODE_SYMS] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7 -}; - -/* Codewords for the DEFLATE Huffman codes */ -struct deflate_codewords { - u32 litlen[DEFLATE_NUM_LITLEN_SYMS]; - u32 offset[DEFLATE_NUM_OFFSET_SYMS]; -}; - -/* - * Codeword lengths (in bits) for the DEFLATE Huffman codes. - * A zero length means the corresponding symbol had zero frequency. - */ -struct deflate_lens { - u8 litlen[DEFLATE_NUM_LITLEN_SYMS]; - u8 offset[DEFLATE_NUM_OFFSET_SYMS]; -}; - -/* Codewords and lengths for the DEFLATE Huffman codes */ -struct deflate_codes { - struct deflate_codewords codewords; - struct deflate_lens lens; -}; - -/* Symbol frequency counters for the DEFLATE Huffman codes */ -struct deflate_freqs { - u32 litlen[DEFLATE_NUM_LITLEN_SYMS]; - u32 offset[DEFLATE_NUM_OFFSET_SYMS]; -}; - -/* - * Represents a run of literals followed by a match or end-of-block. This - * struct is needed to temporarily store items chosen by the parser, since items - * cannot be written until all items for the block have been chosen and the - * block's Huffman codes have been computed. - */ -struct deflate_sequence { - - /* - * Bits 0..22: the number of literals in this run. This may be 0 and - * can be at most MAX_BLOCK_LENGTH. The literals are not stored - * explicitly in this structure; instead, they are read directly from - * the uncompressed data. - * - * Bits 23..31: the length of the match which follows the literals, or 0 - * if this literal run was the last in the block, so there is no match - * which follows it. - */ -#define SEQ_LENGTH_SHIFT 23 -#define SEQ_LITRUNLEN_MASK (((u32)1 << SEQ_LENGTH_SHIFT) - 1) - u32 litrunlen_and_length; - - /* - * If 'length' doesn't indicate end-of-block, then this is the offset of - * the match which follows the literals. - */ - u16 offset; - - /* - * If 'length' doesn't indicate end-of-block, then this is the offset - * slot of the match which follows the literals. - */ - u16 offset_slot; -}; - -#if SUPPORT_NEAR_OPTIMAL_PARSING - -/* Costs for the near-optimal parsing algorithm */ -struct deflate_costs { - - /* The cost to output each possible literal */ - u32 literal[DEFLATE_NUM_LITERALS]; - - /* The cost to output each possible match length */ - u32 length[DEFLATE_MAX_MATCH_LEN + 1]; - - /* The cost to output a match offset of each possible offset slot */ - u32 offset_slot[DEFLATE_NUM_OFFSET_SYMS]; -}; - -/* - * This structure represents a byte position in the input data and a node in the - * graph of possible match/literal choices for the current block. - * - * Logically, each incoming edge to this node is labeled with a literal or a - * match that can be taken to reach this position from an earlier position; and - * each outgoing edge from this node is labeled with a literal or a match that - * can be taken to advance from this position to a later position. - * - * But these "edges" are actually stored elsewhere (in 'match_cache'). Here we - * associate with each node just two pieces of information: - * - * 'cost_to_end' is the minimum cost to reach the end of the block from - * this position. - * - * 'item' represents the literal or match that must be chosen from here to - * reach the end of the block with the minimum cost. Equivalently, this - * can be interpreted as the label of the outgoing edge on the minimum-cost - * path to the "end of block" node from this node. - */ -struct deflate_optimum_node { - - u32 cost_to_end; - - /* - * Notes on the match/literal representation used here: - * - * The low bits of 'item' are the length: 1 if this is a literal, - * or the match length if this is a match. - * - * The high bits of 'item' are the actual literal byte if this is a - * literal, or the match offset if this is a match. - */ -#define OPTIMUM_OFFSET_SHIFT 9 -#define OPTIMUM_LEN_MASK (((u32)1 << OPTIMUM_OFFSET_SHIFT) - 1) - u32 item; - -}; - -#endif /* SUPPORT_NEAR_OPTIMAL_PARSING */ - -/* Block split statistics. See "Block splitting algorithm" below. */ -#define NUM_LITERAL_OBSERVATION_TYPES 8 -#define NUM_MATCH_OBSERVATION_TYPES 2 -#define NUM_OBSERVATION_TYPES (NUM_LITERAL_OBSERVATION_TYPES + \ - NUM_MATCH_OBSERVATION_TYPES) -#define NUM_OBSERVATIONS_PER_BLOCK_CHECK 512 -struct block_split_stats { - u32 new_observations[NUM_OBSERVATION_TYPES]; - u32 observations[NUM_OBSERVATION_TYPES]; - u32 num_new_observations; - u32 num_observations; -}; - -struct deflate_output_bitstream; - -/* The main DEFLATE compressor structure */ -struct libdeflate_compressor { - - /* Pointer to the compress() implementation chosen at allocation time */ - void (*impl)(struct libdeflate_compressor *restrict c, const u8 *in, - size_t in_nbytes, struct deflate_output_bitstream *os); - - /* The free() function for this struct, chosen at allocation time */ - free_func_t free_func; - - /* The compression level with which this compressor was created */ - unsigned compression_level; - - /* Anything of this size or less we won't bother trying to compress. */ - size_t max_passthrough_size; - - /* - * The maximum search depth: consider at most this many potential - * matches at each position - */ - u32 max_search_depth; - - /* - * The "nice" match length: if a match of this length is found, choose - * it immediately without further consideration - */ - u32 nice_match_length; - - /* Frequency counters for the current block */ - struct deflate_freqs freqs; - - /* Block split statistics for the current block */ - struct block_split_stats split_stats; - - /* Dynamic Huffman codes for the current block */ - struct deflate_codes codes; - - /* The static Huffman codes defined by the DEFLATE format */ - struct deflate_codes static_codes; - - /* Temporary space for block flushing */ - union { - /* Information about the precode */ - struct { - u32 freqs[DEFLATE_NUM_PRECODE_SYMS]; - u32 codewords[DEFLATE_NUM_PRECODE_SYMS]; - u8 lens[DEFLATE_NUM_PRECODE_SYMS]; - unsigned items[DEFLATE_NUM_LITLEN_SYMS + - DEFLATE_NUM_OFFSET_SYMS]; - unsigned num_litlen_syms; - unsigned num_offset_syms; - unsigned num_explicit_lens; - unsigned num_items; - } precode; - /* - * The "full" length codewords. Used only after the information - * in 'precode' is no longer needed. - */ - struct { - u32 codewords[DEFLATE_MAX_MATCH_LEN + 1]; - u8 lens[DEFLATE_MAX_MATCH_LEN + 1]; - } length; - } o; - - union { - /* Data for greedy or lazy parsing */ - struct { - /* Hash chains matchfinder */ - struct hc_matchfinder hc_mf; - - /* Matches and literals chosen for the current block */ - struct deflate_sequence sequences[SEQ_STORE_LENGTH + 1]; - - } g; /* (g)reedy */ - - /* Data for fastest parsing */ - struct { - /* Hash table matchfinder */ - struct ht_matchfinder ht_mf; - - /* Matches and literals chosen for the current block */ - struct deflate_sequence sequences[ - FAST_SEQ_STORE_LENGTH + 1]; - - } f; /* (f)astest */ - - #if SUPPORT_NEAR_OPTIMAL_PARSING - /* Data for near-optimal parsing */ - struct { - - /* Binary tree matchfinder */ - struct bt_matchfinder bt_mf; - - /* - * Cached matches for the current block. This array - * contains the matches that were found at each position - * in the block. Specifically, for each position, there - * is a list of matches found at that position, if any, - * sorted by strictly increasing length. In addition, - * following the matches for each position, there is a - * special 'struct lz_match' whose 'length' member - * contains the number of matches found at that - * position, and whose 'offset' member contains the - * literal at that position. - * - * Note: in rare cases, there will be a very high number - * of matches in the block and this array will overflow. - * If this happens, we force the end of the current - * block. MATCH_CACHE_LENGTH is the length at which we - * actually check for overflow. The extra slots beyond - * this are enough to absorb the worst case overflow, - * which occurs if starting at - * &match_cache[MATCH_CACHE_LENGTH - 1], we write - * MAX_MATCHES_PER_POS matches and a match count header, - * then skip searching for matches at - * 'DEFLATE_MAX_MATCH_LEN - 1' positions and write the - * match count header for each. - */ - struct lz_match match_cache[MATCH_CACHE_LENGTH + - MAX_MATCHES_PER_POS + - DEFLATE_MAX_MATCH_LEN - 1]; - - /* - * Array of nodes, one per position, for running the - * minimum-cost path algorithm. - * - * This array must be large enough to accommodate the - * worst-case number of nodes, which is MAX_BLOCK_LENGTH - * plus 1 for the end-of-block node. - */ - struct deflate_optimum_node optimum_nodes[ - MAX_BLOCK_LENGTH + 1]; - - /* The current cost model being used */ - struct deflate_costs costs; - - /* Saved cost model */ - struct deflate_costs costs_saved; - - /* - * A table that maps match offset to offset slot. This - * differs from deflate_offset_slot[] in that this is a - * full map, not a condensed one. The full map is more - * appropriate for the near-optimal parser, since the - * near-optimal parser does more offset => offset_slot - * translations, it doesn't intersperse them with - * matchfinding (so cache evictions are less of a - * concern), and it uses more memory anyway. - */ - u8 offset_slot_full[DEFLATE_MAX_MATCH_OFFSET + 1]; - - /* Literal/match statistics saved from previous block */ - u32 prev_observations[NUM_OBSERVATION_TYPES]; - u32 prev_num_observations; - - /* - * Approximate match length frequencies based on a - * greedy parse, gathered during matchfinding. This is - * used for setting the initial symbol costs. - */ - u32 new_match_len_freqs[DEFLATE_MAX_MATCH_LEN + 1]; - u32 match_len_freqs[DEFLATE_MAX_MATCH_LEN + 1]; - - /* - * The maximum number of optimization passes - * (min-cost path searches) per block. - * Larger values = more compression. - */ - unsigned max_optim_passes; - - /* - * If an optimization pass improves the cost by fewer - * than this number of bits, then optimization will stop - * early, before max_optim_passes has been reached. - * Smaller values = more compression. - */ - u32 min_improvement_to_continue; - - /* - * The minimum number of bits that would need to be - * saved for it to be considered worth the time to - * regenerate and use the min-cost path from a previous - * optimization pass, in the case where the final - * optimization pass actually increased the cost. - * Smaller values = more compression. - */ - u32 min_bits_to_use_nonfinal_path; - - /* - * The maximum block length, in uncompressed bytes, at - * which to find and consider the optimal match/literal - * list for the static Huffman codes. This strategy - * improves the compression ratio produced by static - * Huffman blocks and can discover more cases in which - * static blocks are worthwhile. This helps mostly with - * small blocks, hence why this parameter is a max_len. - * - * Above this block length, static Huffman blocks are - * only used opportunistically. I.e. a static Huffman - * block is only used if a static block using the same - * match/literal list as the optimized dynamic block - * happens to be cheaper than the dynamic block itself. - */ - u32 max_len_to_optimize_static_block; - - } n; /* (n)ear-optimal */ - #endif /* SUPPORT_NEAR_OPTIMAL_PARSING */ - - } p; /* (p)arser */ -}; - -/* - * The type for the bitbuffer variable, which temporarily holds bits that are - * being packed into bytes and written to the output buffer. For best - * performance, this should have size equal to a machine word. - */ -typedef machine_word_t bitbuf_t; - -/* - * The capacity of the bitbuffer, in bits. This is 1 less than the real size, - * in order to avoid undefined behavior when doing bitbuf >>= bitcount & ~7. - */ -#define BITBUF_NBITS (8 * sizeof(bitbuf_t) - 1) - -/* - * Can the specified number of bits always be added to 'bitbuf' after any - * pending bytes have been flushed? There can be up to 7 bits remaining after a - * flush, so the count must not exceed BITBUF_NBITS after adding 'n' more bits. - */ -#define CAN_BUFFER(n) (7 + (n) <= BITBUF_NBITS) - -/* - * Structure to keep track of the current state of sending bits to the - * compressed output buffer - */ -struct deflate_output_bitstream { - - /* Bits that haven't yet been written to the output buffer */ - bitbuf_t bitbuf; - - /* - * Number of bits currently held in @bitbuf. This can be between 0 and - * BITBUF_NBITS in general, or between 0 and 7 after a flush. - */ - unsigned bitcount; - - /* - * Pointer to the position in the output buffer at which the next byte - * should be written - */ - u8 *next; - - /* Pointer to the end of the output buffer */ - u8 *end; - - /* true if the output buffer ran out of space */ - bool overflow; -}; - -/* - * Add some bits to the bitbuffer variable of the output bitstream. The caller - * must ensure that 'bitcount + n <= BITBUF_NBITS', by calling FLUSH_BITS() - * frequently enough. - */ -#define ADD_BITS(bits, n) \ -do { \ - bitbuf |= (bitbuf_t)(bits) << bitcount; \ - bitcount += (n); \ - ASSERT(bitcount <= BITBUF_NBITS); \ -} while (0) - -/* - * Flush bits from the bitbuffer variable to the output buffer. After this, the - * bitbuffer will contain at most 7 bits (a partial byte). - * - * Since deflate_flush_block() verified ahead of time that there is enough space - * remaining before actually writing the block, it's guaranteed that out_next - * won't exceed os->end. However, there might not be enough space remaining to - * flush a whole word, even though that's fastest. Therefore, flush a whole - * word if there is space for it, otherwise flush a byte at a time. - */ -#define FLUSH_BITS() \ -do { \ - if (UNALIGNED_ACCESS_IS_FAST && likely(out_next < out_fast_end)) { \ - /* Flush a whole word (branchlessly). */ \ - put_unaligned_leword(bitbuf, out_next); \ - bitbuf >>= bitcount & ~7; \ - out_next += bitcount >> 3; \ - bitcount &= 7; \ - } else { \ - /* Flush a byte at a time. */ \ - while (bitcount >= 8) { \ - ASSERT(out_next < os->end); \ - *out_next++ = bitbuf; \ - bitcount -= 8; \ - bitbuf >>= 8; \ - } \ - } \ -} while (0) - -/* - * Given the binary tree node A[subtree_idx] whose children already satisfy the - * maxheap property, swap the node with its greater child until it is greater - * than or equal to both of its children, so that the maxheap property is - * satisfied in the subtree rooted at A[subtree_idx]. 'A' uses 1-based indices. - */ -static void -heapify_subtree(u32 A[], unsigned length, unsigned subtree_idx) -{ - unsigned parent_idx; - unsigned child_idx; - u32 v; - - v = A[subtree_idx]; - parent_idx = subtree_idx; - while ((child_idx = parent_idx * 2) <= length) { - if (child_idx < length && A[child_idx + 1] > A[child_idx]) - child_idx++; - if (v >= A[child_idx]) - break; - A[parent_idx] = A[child_idx]; - parent_idx = child_idx; - } - A[parent_idx] = v; -} - -/* - * Rearrange the array 'A' so that it satisfies the maxheap property. - * 'A' uses 1-based indices, so the children of A[i] are A[i*2] and A[i*2 + 1]. - */ -static void -heapify_array(u32 A[], unsigned length) -{ - unsigned subtree_idx; - - for (subtree_idx = length / 2; subtree_idx >= 1; subtree_idx--) - heapify_subtree(A, length, subtree_idx); -} - -/* - * Sort the array 'A', which contains 'length' unsigned 32-bit integers. - * - * Note: name this function heap_sort() instead of heapsort() to avoid colliding - * with heapsort() from stdlib.h on BSD-derived systems. - */ -static void -heap_sort(u32 A[], unsigned length) -{ - A--; /* Use 1-based indices */ - - heapify_array(A, length); - - while (length >= 2) { - u32 tmp = A[length]; - - A[length] = A[1]; - A[1] = tmp; - length--; - heapify_subtree(A, length, 1); - } -} - -#define NUM_SYMBOL_BITS 10 -#define NUM_FREQ_BITS (32 - NUM_SYMBOL_BITS) -#define SYMBOL_MASK ((1 << NUM_SYMBOL_BITS) - 1) -#define FREQ_MASK (~SYMBOL_MASK) - -#define GET_NUM_COUNTERS(num_syms) (num_syms) - -/* - * Sort the symbols primarily by frequency and secondarily by symbol value. - * Discard symbols with zero frequency and fill in an array with the remaining - * symbols, along with their frequencies. The low NUM_SYMBOL_BITS bits of each - * array entry will contain the symbol value, and the remaining bits will - * contain the frequency. - * - * @num_syms - * Number of symbols in the alphabet, at most 1 << NUM_SYMBOL_BITS. - * - * @freqs[num_syms] - * Frequency of each symbol, summing to at most (1 << NUM_FREQ_BITS) - 1. - * - * @lens[num_syms] - * An array that eventually will hold the length of each codeword. This - * function only fills in the codeword lengths for symbols that have zero - * frequency, which are not well defined per se but will be set to 0. - * - * @symout[num_syms] - * The output array, described above. - * - * Returns the number of entries in 'symout' that were filled. This is the - * number of symbols that have nonzero frequency. - */ -static unsigned -sort_symbols(unsigned num_syms, const u32 freqs[], u8 lens[], u32 symout[]) -{ - unsigned sym; - unsigned i; - unsigned num_used_syms; - unsigned num_counters; - unsigned counters[GET_NUM_COUNTERS(DEFLATE_MAX_NUM_SYMS)]; - - /* - * We use heapsort, but with an added optimization. Since often most - * symbol frequencies are low, we first do a count sort using a limited - * number of counters. High frequencies are counted in the last - * counter, and only they will be sorted with heapsort. - * - * Note: with more symbols, it is generally beneficial to have more - * counters. About 1 counter per symbol seems fastest. - */ - - num_counters = GET_NUM_COUNTERS(num_syms); - - memset(counters, 0, num_counters * sizeof(counters[0])); - - /* Count the frequencies. */ - for (sym = 0; sym < num_syms; sym++) - counters[MIN(freqs[sym], num_counters - 1)]++; - - /* - * Make the counters cumulative, ignoring the zero-th, which counted - * symbols with zero frequency. As a side effect, this calculates the - * number of symbols with nonzero frequency. - */ - num_used_syms = 0; - for (i = 1; i < num_counters; i++) { - unsigned count = counters[i]; - - counters[i] = num_used_syms; - num_used_syms += count; - } - - /* - * Sort nonzero-frequency symbols using the counters. At the same time, - * set the codeword lengths of zero-frequency symbols to 0. - */ - for (sym = 0; sym < num_syms; sym++) { - u32 freq = freqs[sym]; - - if (freq != 0) { - symout[counters[MIN(freq, num_counters - 1)]++] = - sym | (freq << NUM_SYMBOL_BITS); - } else { - lens[sym] = 0; - } - } - - /* Sort the symbols counted in the last counter. */ - heap_sort(symout + counters[num_counters - 2], - counters[num_counters - 1] - counters[num_counters - 2]); - - return num_used_syms; -} - -/* - * Build a Huffman tree. - * - * This is an optimized implementation that - * (a) takes advantage of the frequencies being already sorted; - * (b) only generates non-leaf nodes, since the non-leaf nodes of a Huffman - * tree are sufficient to generate a canonical code; - * (c) Only stores parent pointers, not child pointers; - * (d) Produces the nodes in the same memory used for input frequency - * information. - * - * Array 'A', which contains 'sym_count' entries, is used for both input and - * output. For this function, 'sym_count' must be at least 2. - * - * For input, the array must contain the frequencies of the symbols, sorted in - * increasing order. Specifically, each entry must contain a frequency left - * shifted by NUM_SYMBOL_BITS bits. Any data in the low NUM_SYMBOL_BITS bits of - * the entries will be ignored by this function. Although these bits will, in - * fact, contain the symbols that correspond to the frequencies, this function - * is concerned with frequencies only and keeps the symbols as-is. - * - * For output, this function will produce the non-leaf nodes of the Huffman - * tree. These nodes will be stored in the first (sym_count - 1) entries of the - * array. Entry A[sym_count - 2] will represent the root node. Each other node - * will contain the zero-based index of its parent node in 'A', left shifted by - * NUM_SYMBOL_BITS bits. The low NUM_SYMBOL_BITS bits of each entry in A will - * be kept as-is. Again, note that although these low bits will, in fact, - * contain a symbol value, this symbol will have *no relationship* with the - * Huffman tree node that happens to occupy the same slot. This is because this - * implementation only generates the non-leaf nodes of the tree. - */ -static void -build_tree(u32 A[], unsigned sym_count) -{ - const unsigned last_idx = sym_count - 1; - - /* Index of the next lowest frequency leaf that still needs a parent */ - unsigned i = 0; - - /* - * Index of the next lowest frequency non-leaf that still needs a - * parent, or 'e' if there is currently no such node - */ - unsigned b = 0; - - /* Index of the next spot for a non-leaf (will overwrite a leaf) */ - unsigned e = 0; - - do { - u32 new_freq; - - /* - * Select the next two lowest frequency nodes among the leaves - * A[i] and non-leaves A[b], and create a new node A[e] to be - * their parent. Set the new node's frequency to the sum of the - * frequencies of its two children. - * - * Usually the next two lowest frequency nodes are of the same - * type (leaf or non-leaf), so check those cases first. - */ - if (i + 1 <= last_idx && - (b == e || (A[i + 1] & FREQ_MASK) <= (A[b] & FREQ_MASK))) { - /* Two leaves */ - new_freq = (A[i] & FREQ_MASK) + (A[i + 1] & FREQ_MASK); - i += 2; - } else if (b + 2 <= e && - (i > last_idx || - (A[b + 1] & FREQ_MASK) < (A[i] & FREQ_MASK))) { - /* Two non-leaves */ - new_freq = (A[b] & FREQ_MASK) + (A[b + 1] & FREQ_MASK); - A[b] = (e << NUM_SYMBOL_BITS) | (A[b] & SYMBOL_MASK); - A[b + 1] = (e << NUM_SYMBOL_BITS) | - (A[b + 1] & SYMBOL_MASK); - b += 2; - } else { - /* One leaf and one non-leaf */ - new_freq = (A[i] & FREQ_MASK) + (A[b] & FREQ_MASK); - A[b] = (e << NUM_SYMBOL_BITS) | (A[b] & SYMBOL_MASK); - i++; - b++; - } - A[e] = new_freq | (A[e] & SYMBOL_MASK); - /* - * A binary tree with 'n' leaves has 'n - 1' non-leaves, so the - * tree is complete once we've created 'n - 1' non-leaves. - */ - } while (++e < last_idx); -} - -/* - * Given the stripped-down Huffman tree constructed by build_tree(), determine - * the number of codewords that should be assigned each possible length, taking - * into account the length-limited constraint. - * - * @A - * The array produced by build_tree(), containing parent index information - * for the non-leaf nodes of the Huffman tree. Each entry in this array is - * a node; a node's parent always has a greater index than that node - * itself. This function will overwrite the parent index information in - * this array, so essentially it will destroy the tree. However, the data - * in the low NUM_SYMBOL_BITS of each entry will be preserved. - * - * @root_idx - * The 0-based index of the root node in 'A', and consequently one less - * than the number of tree node entries in 'A'. (Or, really 2 less than - * the actual length of 'A'.) - * - * @len_counts - * An array of length ('max_codeword_len' + 1) in which the number of - * codewords having each length <= max_codeword_len will be returned. - * - * @max_codeword_len - * The maximum permissible codeword length. - */ -static void -compute_length_counts(u32 A[], unsigned root_idx, unsigned len_counts[], - unsigned max_codeword_len) -{ - unsigned len; - int node; - - /* - * The key observations are: - * - * (1) We can traverse the non-leaf nodes of the tree, always visiting a - * parent before its children, by simply iterating through the array - * in reverse order. Consequently, we can compute the depth of each - * node in one pass, overwriting the parent indices with depths. - * - * (2) We can initially assume that in the real Huffman tree, both - * children of the root are leaves. This corresponds to two - * codewords of length 1. Then, whenever we visit a (non-leaf) node - * during the traversal, we modify this assumption to account for - * the current node *not* being a leaf, but rather its two children - * being leaves. This causes the loss of one codeword for the - * current depth and the addition of two codewords for the current - * depth plus one. - * - * (3) We can handle the length-limited constraint fairly easily by - * simply using the largest length available when a depth exceeds - * max_codeword_len. - */ - - for (len = 0; len <= max_codeword_len; len++) - len_counts[len] = 0; - len_counts[1] = 2; - - /* Set the root node's depth to 0. */ - A[root_idx] &= SYMBOL_MASK; - - for (node = root_idx - 1; node >= 0; node--) { - - /* Calculate the depth of this node. */ - - unsigned parent = A[node] >> NUM_SYMBOL_BITS; - unsigned parent_depth = A[parent] >> NUM_SYMBOL_BITS; - unsigned depth = parent_depth + 1; - - /* - * Set the depth of this node so that it is available when its - * children (if any) are processed. - */ - A[node] = (A[node] & SYMBOL_MASK) | (depth << NUM_SYMBOL_BITS); - - /* - * If needed, decrease the length to meet the length-limited - * constraint. This is not the optimal method for generating - * length-limited Huffman codes! But it should be good enough. - */ - if (depth >= max_codeword_len) { - depth = max_codeword_len; - do { - depth--; - } while (len_counts[depth] == 0); - } - - /* - * Account for the fact that we have a non-leaf node at the - * current depth. - */ - len_counts[depth]--; - len_counts[depth + 1] += 2; - } -} - -/* - * DEFLATE uses bit-reversed codewords, so we must bit-reverse the codewords - * after generating them. All codewords have length <= 16 bits. If the CPU has - * a bit-reversal instruction, then that is the fastest method. Otherwise the - * fastest method is to reverse the bits in each of the two bytes using a table. - * The table method is slightly faster than using bitwise operations to flip - * adjacent 1, 2, 4, and then 8-bit fields, even if 2 to 4 codewords are packed - * into a machine word and processed together using that method. - */ - -#ifdef rbit32 -static forceinline u32 reverse_codeword(u32 codeword, u8 len) -{ - return rbit32(codeword) >> ((32 - len) & 31); -} -#else -/* Generated by scripts/gen_bitreverse_tab.py */ -static const u8 bitreverse_tab[256] = { - 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, - 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, - 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, - 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, - 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, - 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, - 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, - 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, - 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, - 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, - 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, - 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, - 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, - 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, - 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, - 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, - 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, - 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, - 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, - 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, - 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, - 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, - 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, - 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, - 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, - 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, - 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, - 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, - 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, - 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, - 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, - 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, -}; - -static forceinline u32 reverse_codeword(u32 codeword, u8 len) -{ - STATIC_ASSERT(DEFLATE_MAX_CODEWORD_LEN <= 16); - codeword = ((u32)bitreverse_tab[codeword & 0xff] << 8) | - bitreverse_tab[codeword >> 8]; - return codeword >> (16 - len); -} -#endif /* !rbit32 */ - -/* - * Generate the codewords for a canonical Huffman code. - * - * @A - * The output array for codewords. In addition, initially this - * array must contain the symbols, sorted primarily by frequency and - * secondarily by symbol value, in the low NUM_SYMBOL_BITS bits of - * each entry. - * - * @len - * Output array for codeword lengths. - * - * @len_counts - * An array that provides the number of codewords that will have - * each possible length <= max_codeword_len. - * - * @max_codeword_len - * Maximum length, in bits, of each codeword. - * - * @num_syms - * Number of symbols in the alphabet, including symbols with zero - * frequency. This is the length of the 'A' and 'len' arrays. - */ -static void -gen_codewords(u32 A[], u8 lens[], const unsigned len_counts[], - unsigned max_codeword_len, unsigned num_syms) -{ - u32 next_codewords[DEFLATE_MAX_CODEWORD_LEN + 1]; - unsigned i; - unsigned len; - unsigned sym; - - /* - * Given the number of codewords that will have each length, assign - * codeword lengths to symbols. We do this by assigning the lengths in - * decreasing order to the symbols sorted primarily by increasing - * frequency and secondarily by increasing symbol value. - */ - for (i = 0, len = max_codeword_len; len >= 1; len--) { - unsigned count = len_counts[len]; - - while (count--) - lens[A[i++] & SYMBOL_MASK] = len; - } - - /* - * Generate the codewords themselves. We initialize the - * 'next_codewords' array to provide the lexicographically first - * codeword of each length, then assign codewords in symbol order. This - * produces a canonical code. - */ - next_codewords[0] = 0; - next_codewords[1] = 0; - for (len = 2; len <= max_codeword_len; len++) - next_codewords[len] = - (next_codewords[len - 1] + len_counts[len - 1]) << 1; - - for (sym = 0; sym < num_syms; sym++) { - /* DEFLATE requires bit-reversed codewords. */ - A[sym] = reverse_codeword(next_codewords[lens[sym]]++, - lens[sym]); - } -} - -/* - * --------------------------------------------------------------------- - * deflate_make_huffman_code() - * --------------------------------------------------------------------- - * - * Given an alphabet and the frequency of each symbol in it, construct a - * length-limited canonical Huffman code. - * - * @num_syms - * The number of symbols in the alphabet. The symbols are the integers in - * the range [0, num_syms - 1]. This parameter must be at least 2 and - * must not exceed (1 << NUM_SYMBOL_BITS). - * - * @max_codeword_len - * The maximum permissible codeword length. - * - * @freqs - * An array of length @num_syms that gives the frequency of each symbol. - * It is valid for some, none, or all of the frequencies to be 0. The sum - * of frequencies must not exceed (1 << NUM_FREQ_BITS) - 1. - * - * @lens - * An array of @num_syms entries in which this function will return the - * length, in bits, of the codeword assigned to each symbol. Symbols with - * 0 frequency will not have codewords per se, but their entries in this - * array will be set to 0. No lengths greater than @max_codeword_len will - * be assigned. - * - * @codewords - * An array of @num_syms entries in which this function will return the - * codeword for each symbol, right-justified and padded on the left with - * zeroes. Codewords for symbols with 0 frequency will be undefined. - * - * --------------------------------------------------------------------- - * - * This function builds a length-limited canonical Huffman code. - * - * A length-limited Huffman code contains no codewords longer than some - * specified length, and has exactly (with some algorithms) or approximately - * (with the algorithm used here) the minimum weighted path length from the - * root, given this constraint. - * - * A canonical Huffman code satisfies the properties that a longer codeword - * never lexicographically precedes a shorter codeword, and the lexicographic - * ordering of codewords of the same length is the same as the lexicographic - * ordering of the corresponding symbols. A canonical Huffman code, or more - * generally a canonical prefix code, can be reconstructed from only a list - * containing the codeword length of each symbol. - * - * The classic algorithm to generate a Huffman code creates a node for each - * symbol, then inserts these nodes into a min-heap keyed by symbol frequency. - * Then, repeatedly, the two lowest-frequency nodes are removed from the - * min-heap and added as the children of a new node having frequency equal to - * the sum of its two children, which is then inserted into the min-heap. When - * only a single node remains in the min-heap, it is the root of the Huffman - * tree. The codeword for each symbol is determined by the path needed to reach - * the corresponding node from the root. Descending to the left child appends a - * 0 bit, whereas descending to the right child appends a 1 bit. - * - * The classic algorithm is relatively easy to understand, but it is subject to - * a number of inefficiencies. In practice, it is fastest to first sort the - * symbols by frequency. (This itself can be subject to an optimization based - * on the fact that most frequencies tend to be low.) At the same time, we sort - * secondarily by symbol value, which aids the process of generating a canonical - * code. Then, during tree construction, no heap is necessary because both the - * leaf nodes and the unparented non-leaf nodes can be easily maintained in - * sorted order. Consequently, there can never be more than two possibilities - * for the next-lowest-frequency node. - * - * In addition, because we're generating a canonical code, we actually don't - * need the leaf nodes of the tree at all, only the non-leaf nodes. This is - * because for canonical code generation we don't need to know where the symbols - * are in the tree. Rather, we only need to know how many leaf nodes have each - * depth (codeword length). And this information can, in fact, be quickly - * generated from the tree of non-leaves only. - * - * Furthermore, we can build this stripped-down Huffman tree directly in the - * array in which the codewords are to be generated, provided that these array - * slots are large enough to hold a symbol and frequency value. - * - * Still furthermore, we don't even need to maintain explicit child pointers. - * We only need the parent pointers, and even those can be overwritten in-place - * with depth information as part of the process of extracting codeword lengths - * from the tree. So in summary, we do NOT need a big structure like: - * - * struct huffman_tree_node { - * unsigned int symbol; - * unsigned int frequency; - * unsigned int depth; - * struct huffman_tree_node *left_child; - * struct huffman_tree_node *right_child; - * }; - * - * - * ... which often gets used in "naive" implementations of Huffman code - * generation. - * - * Many of these optimizations are based on the implementation in 7-Zip (source - * file: C/HuffEnc.c), which was placed in the public domain by Igor Pavlov. - */ -static void -deflate_make_huffman_code(unsigned num_syms, unsigned max_codeword_len, - const u32 freqs[], u8 lens[], u32 codewords[]) -{ - u32 *A = codewords; - unsigned num_used_syms; - - STATIC_ASSERT(DEFLATE_MAX_NUM_SYMS <= 1 << NUM_SYMBOL_BITS); - STATIC_ASSERT(MAX_BLOCK_LENGTH <= ((u32)1 << NUM_FREQ_BITS) - 1); - - /* - * We begin by sorting the symbols primarily by frequency and - * secondarily by symbol value. As an optimization, the array used for - * this purpose ('A') shares storage with the space in which we will - * eventually return the codewords. - */ - num_used_syms = sort_symbols(num_syms, freqs, lens, A); - /* - * 'num_used_syms' is the number of symbols with nonzero frequency. - * This may be less than @num_syms. 'num_used_syms' is also the number - * of entries in 'A' that are valid. Each entry consists of a distinct - * symbol and a nonzero frequency packed into a 32-bit integer. - */ - - /* - * A complete Huffman code must contain at least 2 codewords. Yet, it's - * possible that fewer than 2 symbols were used. When this happens, - * it's usually for the offset code (0-1 symbols used). But it's also - * theoretically possible for the litlen and pre codes (1 symbol used). - * - * The DEFLATE RFC explicitly allows the offset code to contain just 1 - * codeword, or even be completely empty. But it's silent about the - * other codes. It also doesn't say whether, in the 1-codeword case, - * the codeword (which it says must be 1 bit) is '0' or '1'. - * - * In any case, some DEFLATE decompressors reject these cases. zlib - * generally allows them, but it does reject precodes that have just 1 - * codeword. More problematically, zlib v1.2.1 and earlier rejected - * empty offset codes, and this behavior can also be seen in Windows - * Explorer's ZIP unpacker (supposedly even still in Windows 11). - * - * Other DEFLATE compressors, including zlib, always send at least 2 - * codewords in order to make a complete Huffman code. Therefore, this - * is a case where practice does not entirely match the specification. - * We follow practice by generating 2 codewords of length 1: codeword - * '0' for symbol 0, and codeword '1' for another symbol -- the used - * symbol if it exists and is not symbol 0, otherwise symbol 1. This - * does worsen the compression ratio by having to send an unnecessary - * offset codeword length. But this only affects rare cases such as - * blocks containing all literals, and it only makes a tiny difference. - */ - if (unlikely(num_used_syms < 2)) { - unsigned sym = num_used_syms ? (A[0] & SYMBOL_MASK) : 0; - unsigned nonzero_idx = sym ? sym : 1; - - codewords[0] = 0; - lens[0] = 1; - codewords[nonzero_idx] = 1; - lens[nonzero_idx] = 1; - return; - } - - /* - * Build a stripped-down version of the Huffman tree, sharing the array - * 'A' with the symbol values. Then extract length counts from the tree - * and use them to generate the final codewords. - */ - - build_tree(A, num_used_syms); - - { - unsigned len_counts[DEFLATE_MAX_CODEWORD_LEN + 1]; - - compute_length_counts(A, num_used_syms - 2, - len_counts, max_codeword_len); - - gen_codewords(A, lens, len_counts, max_codeword_len, num_syms); - } -} - -/* - * Clear the Huffman symbol frequency counters. This must be called when - * starting a new DEFLATE block. - */ -static void -deflate_reset_symbol_frequencies(struct libdeflate_compressor *c) -{ - memset(&c->freqs, 0, sizeof(c->freqs)); -} - -/* - * Build the literal/length and offset Huffman codes for a DEFLATE block. - * - * This takes as input the frequency tables for each alphabet and produces as - * output a set of tables that map symbols to codewords and codeword lengths. - */ -static void -deflate_make_huffman_codes(const struct deflate_freqs *freqs, - struct deflate_codes *codes) -{ - deflate_make_huffman_code(DEFLATE_NUM_LITLEN_SYMS, - MAX_LITLEN_CODEWORD_LEN, - freqs->litlen, - codes->lens.litlen, - codes->codewords.litlen); - - deflate_make_huffman_code(DEFLATE_NUM_OFFSET_SYMS, - MAX_OFFSET_CODEWORD_LEN, - freqs->offset, - codes->lens.offset, - codes->codewords.offset); -} - -/* Initialize c->static_codes. */ -static void -deflate_init_static_codes(struct libdeflate_compressor *c) -{ - unsigned i; - - for (i = 0; i < 144; i++) - c->freqs.litlen[i] = 1 << (9 - 8); - for (; i < 256; i++) - c->freqs.litlen[i] = 1 << (9 - 9); - for (; i < 280; i++) - c->freqs.litlen[i] = 1 << (9 - 7); - for (; i < 288; i++) - c->freqs.litlen[i] = 1 << (9 - 8); - - for (i = 0; i < 32; i++) - c->freqs.offset[i] = 1 << (5 - 5); - - deflate_make_huffman_codes(&c->freqs, &c->static_codes); -} - -/* Return the offset slot for the given match offset, using the small map. */ -static forceinline unsigned -deflate_get_offset_slot(u32 offset) -{ - /* - * 1 <= offset <= 32768 here. For 1 <= offset <= 256, - * deflate_offset_slot[offset - 1] gives the slot. - * - * For 257 <= offset <= 32768, we take advantage of the fact that 257 is - * the beginning of slot 16, and each slot [16..30) is exactly 1 << 7 == - * 128 times larger than each slot [2..16) (since the number of extra - * bits increases by 1 every 2 slots). Thus, the slot is: - * - * deflate_offset_slot[2 + ((offset - 257) >> 7)] + (16 - 2) - * == deflate_offset_slot[((offset - 1) >> 7)] + 14 - * - * Define 'n = (offset <= 256) ? 0 : 7'. Then any offset is handled by: - * - * deflate_offset_slot[(offset - 1) >> n] + (n << 1) - * - * For better performance, replace 'n = (offset <= 256) ? 0 : 7' with - * the equivalent (for offset <= 536871168) 'n = (256 - offset) >> 29'. - */ - unsigned n = (256 - offset) >> 29; - - ASSERT(offset >= 1 && offset <= 32768); - - return deflate_offset_slot[(offset - 1) >> n] + (n << 1); -} - -static unsigned -deflate_compute_precode_items(const u8 lens[], const unsigned num_lens, - u32 precode_freqs[], unsigned precode_items[]) -{ - unsigned *itemptr; - unsigned run_start; - unsigned run_end; - unsigned extra_bits; - u8 len; - - memset(precode_freqs, 0, - DEFLATE_NUM_PRECODE_SYMS * sizeof(precode_freqs[0])); - - itemptr = precode_items; - run_start = 0; - do { - /* Find the next run of codeword lengths. */ - - /* len = the length being repeated */ - len = lens[run_start]; - - /* Extend the run. */ - run_end = run_start; - do { - run_end++; - } while (run_end != num_lens && len == lens[run_end]); - - if (len == 0) { - /* Run of zeroes. */ - - /* Symbol 18: RLE 11 to 138 zeroes at a time. */ - while ((run_end - run_start) >= 11) { - extra_bits = MIN((run_end - run_start) - 11, - 0x7F); - precode_freqs[18]++; - *itemptr++ = 18 | (extra_bits << 5); - run_start += 11 + extra_bits; - } - - /* Symbol 17: RLE 3 to 10 zeroes at a time. */ - if ((run_end - run_start) >= 3) { - extra_bits = MIN((run_end - run_start) - 3, - 0x7); - precode_freqs[17]++; - *itemptr++ = 17 | (extra_bits << 5); - run_start += 3 + extra_bits; - } - } else { - - /* A run of nonzero lengths. */ - - /* Symbol 16: RLE 3 to 6 of the previous length. */ - if ((run_end - run_start) >= 4) { - precode_freqs[len]++; - *itemptr++ = len; - run_start++; - do { - extra_bits = MIN((run_end - run_start) - - 3, 0x3); - precode_freqs[16]++; - *itemptr++ = 16 | (extra_bits << 5); - run_start += 3 + extra_bits; - } while ((run_end - run_start) >= 3); - } - } - - /* Output any remaining lengths without RLE. */ - while (run_start != run_end) { - precode_freqs[len]++; - *itemptr++ = len; - run_start++; - } - } while (run_start != num_lens); - - return itemptr - precode_items; -} - -/* - * Huffman codeword lengths for dynamic Huffman blocks are compressed using a - * separate Huffman code, the "precode", which contains a symbol for each - * possible codeword length in the larger code as well as several special - * symbols to represent repeated codeword lengths (a form of run-length - * encoding). The precode is itself constructed in canonical form, and its - * codeword lengths are represented literally in 19 3-bit fields that - * immediately precede the compressed codeword lengths of the larger code. - */ - -/* Precompute the information needed to output dynamic Huffman codes. */ -static void -deflate_precompute_huffman_header(struct libdeflate_compressor *c) -{ - /* Compute how many litlen and offset symbols are needed. */ - - for (c->o.precode.num_litlen_syms = DEFLATE_NUM_LITLEN_SYMS; - c->o.precode.num_litlen_syms > 257; - c->o.precode.num_litlen_syms--) - if (c->codes.lens.litlen[c->o.precode.num_litlen_syms - 1] != 0) - break; - - for (c->o.precode.num_offset_syms = DEFLATE_NUM_OFFSET_SYMS; - c->o.precode.num_offset_syms > 1; - c->o.precode.num_offset_syms--) - if (c->codes.lens.offset[c->o.precode.num_offset_syms - 1] != 0) - break; - - /* - * If we're not using the full set of literal/length codeword lengths, - * then temporarily move the offset codeword lengths over so that the - * literal/length and offset codeword lengths are contiguous. - */ - STATIC_ASSERT(offsetof(struct deflate_lens, offset) == - DEFLATE_NUM_LITLEN_SYMS); - if (c->o.precode.num_litlen_syms != DEFLATE_NUM_LITLEN_SYMS) { - memmove((u8 *)&c->codes.lens + c->o.precode.num_litlen_syms, - (u8 *)&c->codes.lens + DEFLATE_NUM_LITLEN_SYMS, - c->o.precode.num_offset_syms); - } - - /* - * Compute the "items" (RLE / literal tokens and extra bits) with which - * the codeword lengths in the larger code will be output. - */ - c->o.precode.num_items = - deflate_compute_precode_items((u8 *)&c->codes.lens, - c->o.precode.num_litlen_syms + - c->o.precode.num_offset_syms, - c->o.precode.freqs, - c->o.precode.items); - - /* Build the precode. */ - deflate_make_huffman_code(DEFLATE_NUM_PRECODE_SYMS, - MAX_PRE_CODEWORD_LEN, - c->o.precode.freqs, c->o.precode.lens, - c->o.precode.codewords); - - /* Count how many precode lengths we actually need to output. */ - for (c->o.precode.num_explicit_lens = DEFLATE_NUM_PRECODE_SYMS; - c->o.precode.num_explicit_lens > 4; - c->o.precode.num_explicit_lens--) - if (c->o.precode.lens[deflate_precode_lens_permutation[ - c->o.precode.num_explicit_lens - 1]] != 0) - break; - - /* Restore the offset codeword lengths if needed. */ - if (c->o.precode.num_litlen_syms != DEFLATE_NUM_LITLEN_SYMS) { - memmove((u8 *)&c->codes.lens + DEFLATE_NUM_LITLEN_SYMS, - (u8 *)&c->codes.lens + c->o.precode.num_litlen_syms, - c->o.precode.num_offset_syms); - } -} - -/* - * To make it faster to output matches, compute the "full" match length - * codewords, i.e. the concatenation of the litlen codeword and the extra bits - * for each possible match length. - */ -static void -deflate_compute_full_len_codewords(struct libdeflate_compressor *c, - const struct deflate_codes *codes) -{ - u32 len; - - STATIC_ASSERT(MAX_LITLEN_CODEWORD_LEN + - DEFLATE_MAX_EXTRA_LENGTH_BITS <= 32); - - for (len = DEFLATE_MIN_MATCH_LEN; len <= DEFLATE_MAX_MATCH_LEN; len++) { - unsigned slot = deflate_length_slot[len]; - unsigned litlen_sym = DEFLATE_FIRST_LEN_SYM + slot; - u32 extra_bits = len - deflate_length_slot_base[slot]; - - c->o.length.codewords[len] = - codes->codewords.litlen[litlen_sym] | - (extra_bits << codes->lens.litlen[litlen_sym]); - c->o.length.lens[len] = codes->lens.litlen[litlen_sym] + - deflate_extra_length_bits[slot]; - } -} - -/* Write a match to the output buffer. */ -#define WRITE_MATCH(c_, codes_, length_, offset_, offset_slot_) \ -do { \ - const struct libdeflate_compressor *c__ = (c_); \ - const struct deflate_codes *codes__ = (codes_); \ - u32 length__ = (length_); \ - u32 offset__ = (offset_); \ - unsigned offset_slot__ = (offset_slot_); \ - \ - /* Litlen symbol and extra length bits */ \ - STATIC_ASSERT(CAN_BUFFER(MAX_LITLEN_CODEWORD_LEN + \ - DEFLATE_MAX_EXTRA_LENGTH_BITS)); \ - ADD_BITS(c__->o.length.codewords[length__], \ - c__->o.length.lens[length__]); \ - \ - if (!CAN_BUFFER(MAX_LITLEN_CODEWORD_LEN + \ - DEFLATE_MAX_EXTRA_LENGTH_BITS + \ - MAX_OFFSET_CODEWORD_LEN + \ - DEFLATE_MAX_EXTRA_OFFSET_BITS)) \ - FLUSH_BITS(); \ - \ - /* Offset symbol */ \ - ADD_BITS(codes__->codewords.offset[offset_slot__], \ - codes__->lens.offset[offset_slot__]); \ - \ - if (!CAN_BUFFER(MAX_OFFSET_CODEWORD_LEN + \ - DEFLATE_MAX_EXTRA_OFFSET_BITS)) \ - FLUSH_BITS(); \ - \ - /* Extra offset bits */ \ - ADD_BITS(offset__ - deflate_offset_slot_base[offset_slot__], \ - deflate_extra_offset_bits[offset_slot__]); \ - \ - FLUSH_BITS(); \ -} while (0) - -/* - * Choose the best type of block to use (dynamic Huffman, static Huffman, or - * uncompressed), then output it. - * - * The uncompressed data of the block is @block_begin[0..@block_length-1]. The - * sequence of literals and matches that will be used to compress the block (if - * a compressed block is chosen) is given by @sequences if it's non-NULL, or - * else @c->p.n.optimum_nodes. @c->freqs and @c->codes must be already set - * according to the literals, matches, and end-of-block symbol. - */ -static void -deflate_flush_block(struct libdeflate_compressor *c, - struct deflate_output_bitstream *os, - const u8 *block_begin, u32 block_length, - const struct deflate_sequence *sequences, - bool is_final_block) -{ - /* - * It is hard to get compilers to understand that writes to 'os->next' - * don't alias 'os'. That hurts performance significantly, as - * everything in 'os' would keep getting re-loaded. ('restrict' - * *should* do the trick, but it's unreliable.) Therefore, we keep all - * the output bitstream state in local variables, and output bits using - * macros. This is similar to what the decompressor does. - */ - const u8 *in_next = block_begin; - const u8 * const in_end = block_begin + block_length; - bitbuf_t bitbuf = os->bitbuf; - unsigned bitcount = os->bitcount; - u8 *out_next = os->next; - u8 * const out_fast_end = - os->end - MIN(WORDBYTES - 1, os->end - out_next); - /* - * The cost for each block type, in bits. Start with the cost of the - * block header which is 3 bits. - */ - u32 dynamic_cost = 3; - u32 static_cost = 3; - u32 uncompressed_cost = 3; - u32 best_cost; - struct deflate_codes *codes; - unsigned sym; - - ASSERT(block_length >= MIN_BLOCK_LENGTH || - (is_final_block && block_length > 0)); - ASSERT(block_length <= MAX_BLOCK_LENGTH); - ASSERT(bitcount <= 7); - ASSERT((bitbuf & ~(((bitbuf_t)1 << bitcount) - 1)) == 0); - ASSERT(out_next <= os->end); - ASSERT(!os->overflow); - - /* Precompute the precode items and build the precode. */ - deflate_precompute_huffman_header(c); - - /* Account for the cost of encoding dynamic Huffman codes. */ - dynamic_cost += 5 + 5 + 4 + (3 * c->o.precode.num_explicit_lens); - for (sym = 0; sym < DEFLATE_NUM_PRECODE_SYMS; sym++) { - u32 extra = deflate_extra_precode_bits[sym]; - - dynamic_cost += c->o.precode.freqs[sym] * - (extra + c->o.precode.lens[sym]); - } - - /* Account for the cost of encoding literals. */ - for (sym = 0; sym < 144; sym++) { - dynamic_cost += c->freqs.litlen[sym] * - c->codes.lens.litlen[sym]; - static_cost += c->freqs.litlen[sym] * 8; - } - for (; sym < 256; sym++) { - dynamic_cost += c->freqs.litlen[sym] * - c->codes.lens.litlen[sym]; - static_cost += c->freqs.litlen[sym] * 9; - } - - /* Account for the cost of encoding the end-of-block symbol. */ - dynamic_cost += c->codes.lens.litlen[DEFLATE_END_OF_BLOCK]; - static_cost += 7; - - /* Account for the cost of encoding lengths. */ - for (sym = DEFLATE_FIRST_LEN_SYM; - sym < DEFLATE_FIRST_LEN_SYM + ARRAY_LEN(deflate_extra_length_bits); - sym++) { - u32 extra = deflate_extra_length_bits[ - sym - DEFLATE_FIRST_LEN_SYM]; - - dynamic_cost += c->freqs.litlen[sym] * - (extra + c->codes.lens.litlen[sym]); - static_cost += c->freqs.litlen[sym] * - (extra + c->static_codes.lens.litlen[sym]); - } - - /* Account for the cost of encoding offsets. */ - for (sym = 0; sym < ARRAY_LEN(deflate_extra_offset_bits); sym++) { - u32 extra = deflate_extra_offset_bits[sym]; - - dynamic_cost += c->freqs.offset[sym] * - (extra + c->codes.lens.offset[sym]); - static_cost += c->freqs.offset[sym] * (extra + 5); - } - - /* Compute the cost of using uncompressed blocks. */ - uncompressed_cost += (-(bitcount + 3) & 7) + 32 + - (40 * (DIV_ROUND_UP(block_length, - UINT16_MAX) - 1)) + - (8 * block_length); - - /* - * Choose and output the cheapest type of block. If there is a tie, - * prefer uncompressed, then static, then dynamic. - */ - - best_cost = MIN(dynamic_cost, MIN(static_cost, uncompressed_cost)); - - /* If the block isn't going to fit, then stop early. */ - if (DIV_ROUND_UP(bitcount + best_cost, 8) > os->end - out_next) { - os->overflow = true; - return; - } - /* - * Else, now we know that the block fits, so no further bounds checks on - * the output buffer are required until the next block. - */ - - if (best_cost == uncompressed_cost) { - /* - * Uncompressed block(s). DEFLATE limits the length of - * uncompressed blocks to UINT16_MAX bytes, so if the length of - * the "block" we're flushing is over UINT16_MAX, we actually - * output multiple blocks. - */ - do { - u8 bfinal = 0; - size_t len = UINT16_MAX; - - if (in_end - in_next <= UINT16_MAX) { - bfinal = is_final_block; - len = in_end - in_next; - } - /* It was already checked that there is enough space. */ - ASSERT(os->end - out_next >= - DIV_ROUND_UP(bitcount + 3, 8) + 4 + len); - /* - * Output BFINAL (1 bit) and BTYPE (2 bits), then align - * to a byte boundary. - */ - STATIC_ASSERT(DEFLATE_BLOCKTYPE_UNCOMPRESSED == 0); - *out_next++ = (bfinal << bitcount) | bitbuf; - if (bitcount > 5) - *out_next++ = 0; - bitbuf = 0; - bitcount = 0; - /* Output LEN and NLEN, then the data itself. */ - put_unaligned_le16(len, out_next); - out_next += 2; - put_unaligned_le16(~len, out_next); - out_next += 2; - memcpy(out_next, in_next, len); - out_next += len; - in_next += len; - } while (in_next != in_end); - /* Done outputting uncompressed block(s) */ - goto out; - } - - if (best_cost == static_cost) { - /* Static Huffman block */ - codes = &c->static_codes; - ADD_BITS(is_final_block, 1); - ADD_BITS(DEFLATE_BLOCKTYPE_STATIC_HUFFMAN, 2); - FLUSH_BITS(); - } else { - const unsigned num_explicit_lens = c->o.precode.num_explicit_lens; - const unsigned num_precode_items = c->o.precode.num_items; - unsigned precode_sym, precode_item; - unsigned i; - - /* Dynamic Huffman block */ - - codes = &c->codes; - STATIC_ASSERT(CAN_BUFFER(1 + 2 + 5 + 5 + 4 + 3)); - ADD_BITS(is_final_block, 1); - ADD_BITS(DEFLATE_BLOCKTYPE_DYNAMIC_HUFFMAN, 2); - ADD_BITS(c->o.precode.num_litlen_syms - 257, 5); - ADD_BITS(c->o.precode.num_offset_syms - 1, 5); - ADD_BITS(num_explicit_lens - 4, 4); - - /* Output the lengths of the codewords in the precode. */ - if (CAN_BUFFER(3 * (DEFLATE_NUM_PRECODE_SYMS - 1))) { - /* - * A 64-bit bitbuffer is just one bit too small to hold - * the maximum number of precode lens, so to minimize - * flushes we merge one len with the previous fields. - */ - precode_sym = deflate_precode_lens_permutation[0]; - ADD_BITS(c->o.precode.lens[precode_sym], 3); - FLUSH_BITS(); - i = 1; /* num_explicit_lens >= 4 */ - do { - precode_sym = - deflate_precode_lens_permutation[i]; - ADD_BITS(c->o.precode.lens[precode_sym], 3); - } while (++i < num_explicit_lens); - FLUSH_BITS(); - } else { - FLUSH_BITS(); - i = 0; - do { - precode_sym = - deflate_precode_lens_permutation[i]; - ADD_BITS(c->o.precode.lens[precode_sym], 3); - FLUSH_BITS(); - } while (++i < num_explicit_lens); - } - - /* - * Output the lengths of the codewords in the litlen and offset - * codes, encoded by the precode. - */ - i = 0; - do { - precode_item = c->o.precode.items[i]; - precode_sym = precode_item & 0x1F; - STATIC_ASSERT(CAN_BUFFER(MAX_PRE_CODEWORD_LEN + 7)); - ADD_BITS(c->o.precode.codewords[precode_sym], - c->o.precode.lens[precode_sym]); - ADD_BITS(precode_item >> 5, - deflate_extra_precode_bits[precode_sym]); - FLUSH_BITS(); - } while (++i < num_precode_items); - } - - /* Output the literals and matches for a dynamic or static block. */ - ASSERT(bitcount <= 7); - deflate_compute_full_len_codewords(c, codes); -#if SUPPORT_NEAR_OPTIMAL_PARSING - if (sequences == NULL) { - /* Output the literals and matches from the minimum-cost path */ - struct deflate_optimum_node *cur_node = - &c->p.n.optimum_nodes[0]; - struct deflate_optimum_node * const end_node = - &c->p.n.optimum_nodes[block_length]; - do { - u32 length = cur_node->item & OPTIMUM_LEN_MASK; - u32 offset = cur_node->item >> OPTIMUM_OFFSET_SHIFT; - - if (length == 1) { - /* Literal */ - ADD_BITS(codes->codewords.litlen[offset], - codes->lens.litlen[offset]); - FLUSH_BITS(); - } else { - /* Match */ - WRITE_MATCH(c, codes, length, offset, - c->p.n.offset_slot_full[offset]); - } - cur_node += length; - } while (cur_node != end_node); - } else -#endif /* SUPPORT_NEAR_OPTIMAL_PARSING */ - { - /* Output the literals and matches from the sequences list. */ - const struct deflate_sequence *seq; - - for (seq = sequences; ; seq++) { - u32 litrunlen = seq->litrunlen_and_length & - SEQ_LITRUNLEN_MASK; - u32 length = seq->litrunlen_and_length >> - SEQ_LENGTH_SHIFT; - unsigned lit; - - /* Output a run of literals. */ - if (CAN_BUFFER(4 * MAX_LITLEN_CODEWORD_LEN)) { - for (; litrunlen >= 4; litrunlen -= 4) { - lit = *in_next++; - ADD_BITS(codes->codewords.litlen[lit], - codes->lens.litlen[lit]); - lit = *in_next++; - ADD_BITS(codes->codewords.litlen[lit], - codes->lens.litlen[lit]); - lit = *in_next++; - ADD_BITS(codes->codewords.litlen[lit], - codes->lens.litlen[lit]); - lit = *in_next++; - ADD_BITS(codes->codewords.litlen[lit], - codes->lens.litlen[lit]); - FLUSH_BITS(); - } - if (litrunlen-- != 0) { - lit = *in_next++; - ADD_BITS(codes->codewords.litlen[lit], - codes->lens.litlen[lit]); - if (litrunlen-- != 0) { - lit = *in_next++; - ADD_BITS(codes->codewords.litlen[lit], - codes->lens.litlen[lit]); - if (litrunlen-- != 0) { - lit = *in_next++; - ADD_BITS(codes->codewords.litlen[lit], - codes->lens.litlen[lit]); - } - } - FLUSH_BITS(); - } - } else { - while (litrunlen--) { - lit = *in_next++; - ADD_BITS(codes->codewords.litlen[lit], - codes->lens.litlen[lit]); - FLUSH_BITS(); - } - } - - if (length == 0) { /* Last sequence? */ - ASSERT(in_next == in_end); - break; - } - - /* Output a match. */ - WRITE_MATCH(c, codes, length, seq->offset, - seq->offset_slot); - in_next += length; - } - } - - /* Output the end-of-block symbol. */ - ASSERT(bitcount <= 7); - ADD_BITS(codes->codewords.litlen[DEFLATE_END_OF_BLOCK], - codes->lens.litlen[DEFLATE_END_OF_BLOCK]); - FLUSH_BITS(); -out: - ASSERT(bitcount <= 7); - /* - * Assert that the block cost was computed correctly. This is relied on - * above for the bounds check on the output buffer. Also, - * libdeflate_deflate_compress_bound() relies on this via the assumption - * that uncompressed blocks will always be used when cheapest. - */ - ASSERT(8 * (out_next - os->next) + bitcount - os->bitcount == best_cost); - os->bitbuf = bitbuf; - os->bitcount = bitcount; - os->next = out_next; -} - -static void -deflate_finish_block(struct libdeflate_compressor *c, - struct deflate_output_bitstream *os, - const u8 *block_begin, u32 block_length, - const struct deflate_sequence *sequences, - bool is_final_block) -{ - c->freqs.litlen[DEFLATE_END_OF_BLOCK]++; - deflate_make_huffman_codes(&c->freqs, &c->codes); - deflate_flush_block(c, os, block_begin, block_length, sequences, - is_final_block); -} - -/******************************************************************************/ - -/* - * Block splitting algorithm. The problem is to decide when it is worthwhile to - * start a new block with new Huffman codes. There is a theoretically optimal - * solution: recursively consider every possible block split, considering the - * exact cost of each block, and choose the minimum cost approach. But this is - * far too slow. Instead, as an approximation, we can count symbols and after - * every N symbols, compare the expected distribution of symbols based on the - * previous data with the actual distribution. If they differ "by enough", then - * start a new block. - * - * As an optimization and heuristic, we don't distinguish between every symbol - * but rather we combine many symbols into a single "observation type". For - * literals we only look at the high bits and low bits, and for matches we only - * look at whether the match is long or not. The assumption is that for typical - * "real" data, places that are good block boundaries will tend to be noticeable - * based only on changes in these aggregate probabilities, without looking for - * subtle differences in individual symbols. For example, a change from ASCII - * bytes to non-ASCII bytes, or from few matches (generally less compressible) - * to many matches (generally more compressible), would be easily noticed based - * on the aggregates. - * - * For determining whether the probability distributions are "different enough" - * to start a new block, the simple heuristic of splitting when the sum of - * absolute differences exceeds a constant seems to be good enough. We also add - * a number proportional to the block length so that the algorithm is more - * likely to end long blocks than short blocks. This reflects the general - * expectation that it will become increasingly beneficial to start a new block - * as the current block grows longer. - * - * Finally, for an approximation, it is not strictly necessary that the exact - * symbols being used are considered. With "near-optimal parsing", for example, - * the actual symbols that will be used are unknown until after the block - * boundary is chosen and the block has been optimized. Since the final choices - * cannot be used, we can use preliminary "greedy" choices instead. - */ - -/* Initialize the block split statistics when starting a new block. */ -static void -init_block_split_stats(struct block_split_stats *stats) -{ - int i; - - for (i = 0; i < NUM_OBSERVATION_TYPES; i++) { - stats->new_observations[i] = 0; - stats->observations[i] = 0; - } - stats->num_new_observations = 0; - stats->num_observations = 0; -} - -/* - * Literal observation. Heuristic: use the top 2 bits and low 1 bits of the - * literal, for 8 possible literal observation types. - */ -static forceinline void -observe_literal(struct block_split_stats *stats, u8 lit) -{ - stats->new_observations[((lit >> 5) & 0x6) | (lit & 1)]++; - stats->num_new_observations++; -} - -/* - * Match observation. Heuristic: use one observation type for "short match" and - * one observation type for "long match". - */ -static forceinline void -observe_match(struct block_split_stats *stats, u32 length) -{ - stats->new_observations[NUM_LITERAL_OBSERVATION_TYPES + - (length >= 9)]++; - stats->num_new_observations++; -} - -static void -merge_new_observations(struct block_split_stats *stats) -{ - int i; - - for (i = 0; i < NUM_OBSERVATION_TYPES; i++) { - stats->observations[i] += stats->new_observations[i]; - stats->new_observations[i] = 0; - } - stats->num_observations += stats->num_new_observations; - stats->num_new_observations = 0; -} - -static bool -do_end_block_check(struct block_split_stats *stats, u32 block_length) -{ - if (stats->num_observations > 0) { - /* - * Compute the sum of absolute differences of probabilities. To - * avoid needing to use floating point arithmetic or do slow - * divisions, we do all arithmetic with the probabilities - * multiplied by num_observations * num_new_observations. E.g., - * for the "old" observations the probabilities would be - * (double)observations[i] / num_observations, but since we - * multiply by both num_observations and num_new_observations we - * really do observations[i] * num_new_observations. - */ - u32 total_delta = 0; - u32 num_items; - u32 cutoff; - int i; - - for (i = 0; i < NUM_OBSERVATION_TYPES; i++) { - u32 expected = stats->observations[i] * - stats->num_new_observations; - u32 actual = stats->new_observations[i] * - stats->num_observations; - u32 delta = (actual > expected) ? actual - expected : - expected - actual; - - total_delta += delta; - } - - num_items = stats->num_observations + - stats->num_new_observations; - /* - * Heuristic: the cutoff is when the sum of absolute differences - * of probabilities becomes at least 200/512. As above, the - * probability is multiplied by both num_new_observations and - * num_observations. Be careful to avoid integer overflow. - */ - cutoff = stats->num_new_observations * 200 / 512 * - stats->num_observations; - /* - * Very short blocks have a lot of overhead for the Huffman - * codes, so only use them if it clearly seems worthwhile. - * (This is an additional penalty, which adds to the smaller - * penalty below which scales more slowly.) - */ - if (block_length < 10000 && num_items < 8192) - cutoff += (u64)cutoff * (8192 - num_items) / 8192; - - /* Ready to end the block? */ - if (total_delta + - (block_length / 4096) * stats->num_observations >= cutoff) - return true; - } - merge_new_observations(stats); - return false; -} - -static forceinline bool -ready_to_check_block(const struct block_split_stats *stats, - const u8 *in_block_begin, const u8 *in_next, - const u8 *in_end) -{ - return stats->num_new_observations >= NUM_OBSERVATIONS_PER_BLOCK_CHECK - && in_next - in_block_begin >= MIN_BLOCK_LENGTH - && in_end - in_next >= MIN_BLOCK_LENGTH; -} - -static forceinline bool -should_end_block(struct block_split_stats *stats, - const u8 *in_block_begin, const u8 *in_next, const u8 *in_end) -{ - /* Ready to try to end the block (again)? */ - if (!ready_to_check_block(stats, in_block_begin, in_next, in_end)) - return false; - - return do_end_block_check(stats, in_next - in_block_begin); -} - -/******************************************************************************/ - -static void -deflate_begin_sequences(struct libdeflate_compressor *c, - struct deflate_sequence *first_seq) -{ - deflate_reset_symbol_frequencies(c); - first_seq->litrunlen_and_length = 0; -} - -static forceinline void -deflate_choose_literal(struct libdeflate_compressor *c, unsigned literal, - bool gather_split_stats, struct deflate_sequence *seq) -{ - c->freqs.litlen[literal]++; - - if (gather_split_stats) - observe_literal(&c->split_stats, literal); - - STATIC_ASSERT(MAX_BLOCK_LENGTH <= SEQ_LITRUNLEN_MASK); - seq->litrunlen_and_length++; -} - -static forceinline void -deflate_choose_match(struct libdeflate_compressor *c, - u32 length, u32 offset, bool gather_split_stats, - struct deflate_sequence **seq_p) -{ - struct deflate_sequence *seq = *seq_p; - unsigned length_slot = deflate_length_slot[length]; - unsigned offset_slot = deflate_get_offset_slot(offset); - - c->freqs.litlen[DEFLATE_FIRST_LEN_SYM + length_slot]++; - c->freqs.offset[offset_slot]++; - if (gather_split_stats) - observe_match(&c->split_stats, length); - - seq->litrunlen_and_length |= length << SEQ_LENGTH_SHIFT; - seq->offset = offset; - seq->offset_slot = offset_slot; - - seq++; - seq->litrunlen_and_length = 0; - *seq_p = seq; -} - -/* - * Decrease the maximum and nice match lengths if we're approaching the end of - * the input buffer. - */ -static forceinline void -adjust_max_and_nice_len(u32 *max_len, u32 *nice_len, size_t remaining) -{ - if (unlikely(remaining < DEFLATE_MAX_MATCH_LEN)) { - *max_len = remaining; - *nice_len = MIN(*nice_len, *max_len); - } -} - -/* - * Choose the minimum match length for the greedy and lazy parsers. - * - * By default the minimum match length is 3, which is the smallest length the - * DEFLATE format allows. However, with greedy and lazy parsing, some data - * (e.g. DNA sequencing data) benefits greatly from a longer minimum length. - * Typically, this is because literals are very cheap. In general, the - * near-optimal parser handles this case naturally, but the greedy and lazy - * parsers need a heuristic to decide when to use short matches. - * - * The heuristic we use is to make the minimum match length depend on the number - * of different literals that exist in the data. If there are many different - * literals, then literals will probably be expensive, so short matches will - * probably be worthwhile. Conversely, if not many literals are used, then - * probably literals will be cheap and short matches won't be worthwhile. - */ -static u32 -choose_min_match_len(u32 num_used_literals, u32 max_search_depth) -{ - /* map from num_used_literals to min_len */ - static const u8 min_lens[] = { - 9, 9, 9, 9, 9, 9, 8, 8, 7, 7, 6, 6, 6, 6, 6, 6, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - /* The rest is implicitly 3. */ - }; - u32 min_len; - - STATIC_ASSERT(DEFLATE_MIN_MATCH_LEN <= 3); - STATIC_ASSERT(ARRAY_LEN(min_lens) <= DEFLATE_NUM_LITERALS + 1); - - if (num_used_literals >= ARRAY_LEN(min_lens)) - return 3; - min_len = min_lens[num_used_literals]; - /* - * With a low max_search_depth, it may be too hard to find long matches. - */ - if (max_search_depth < 16) { - if (max_search_depth < 5) - min_len = MIN(min_len, 4); - else if (max_search_depth < 10) - min_len = MIN(min_len, 5); - else - min_len = MIN(min_len, 7); - } - return min_len; -} - -static u32 -calculate_min_match_len(const u8 *data, size_t data_len, u32 max_search_depth) -{ - u8 used[256] = { 0 }; - u32 num_used_literals = 0; - size_t i; - - /* - * For very short inputs, the static Huffman code has a good chance of - * being best, in which case there is no reason to avoid short matches. - */ - if (data_len < 512) - return DEFLATE_MIN_MATCH_LEN; - - /* - * For an initial approximation, scan the first 4 KiB of data. The - * caller may use recalculate_min_match_len() to update min_len later. - */ - data_len = MIN(data_len, 4096); - for (i = 0; i < data_len; i++) - used[data[i]] = 1; - for (i = 0; i < 256; i++) - num_used_literals += used[i]; - return choose_min_match_len(num_used_literals, max_search_depth); -} - -/* - * Recalculate the minimum match length for a block, now that we know the - * distribution of literals that are actually being used (freqs->litlen). - */ -static u32 -recalculate_min_match_len(const struct deflate_freqs *freqs, - u32 max_search_depth) -{ - u32 literal_freq = 0; - u32 cutoff; - u32 num_used_literals = 0; - int i; - - for (i = 0; i < DEFLATE_NUM_LITERALS; i++) - literal_freq += freqs->litlen[i]; - - cutoff = literal_freq >> 10; /* Ignore literals used very rarely. */ - - for (i = 0; i < DEFLATE_NUM_LITERALS; i++) { - if (freqs->litlen[i] > cutoff) - num_used_literals++; - } - return choose_min_match_len(num_used_literals, max_search_depth); -} - -static forceinline const u8 * -choose_max_block_end(const u8 *in_block_begin, const u8 *in_end, - size_t soft_max_len) -{ - if (in_end - in_block_begin < soft_max_len + MIN_BLOCK_LENGTH) - return in_end; - return in_block_begin + soft_max_len; -} - -/* - * This is the level 0 "compressor". It always outputs uncompressed blocks. - */ -static size_t -deflate_compress_none(const u8 *in, size_t in_nbytes, - u8 *out, size_t out_nbytes_avail) -{ - const u8 *in_next = in; - const u8 * const in_end = in + in_nbytes; - u8 *out_next = out; - u8 * const out_end = out + out_nbytes_avail; - - /* - * If the input is zero-length, we still must output a block in order - * for the output to be a valid DEFLATE stream. Handle this case - * specially to avoid potentially passing NULL to memcpy() below. - */ - if (unlikely(in_nbytes == 0)) { - if (out_nbytes_avail < 5) - return 0; - /* BFINAL and BTYPE */ - *out_next++ = 1 | (DEFLATE_BLOCKTYPE_UNCOMPRESSED << 1); - /* LEN and NLEN */ - put_unaligned_le32(0xFFFF0000, out_next); - return 5; - } - - do { - u8 bfinal = 0; - size_t len = UINT16_MAX; - - if (in_end - in_next <= UINT16_MAX) { - bfinal = 1; - len = in_end - in_next; - } - if (out_end - out_next < 5 + len) - return 0; - /* - * Output BFINAL and BTYPE. The stream is already byte-aligned - * here, so this step always requires outputting exactly 1 byte. - */ - *out_next++ = bfinal | (DEFLATE_BLOCKTYPE_UNCOMPRESSED << 1); - - /* Output LEN and NLEN, then the data itself. */ - put_unaligned_le16(len, out_next); - out_next += 2; - put_unaligned_le16(~len, out_next); - out_next += 2; - memcpy(out_next, in_next, len); - out_next += len; - in_next += len; - } while (in_next != in_end); - - return out_next - out; -} - -/* - * This is a faster variant of deflate_compress_greedy(). It uses the - * ht_matchfinder rather than the hc_matchfinder. It also skips the block - * splitting algorithm and just uses fixed length blocks. c->max_search_depth - * has no effect with this algorithm, as it is hardcoded in ht_matchfinder.h. - */ -static void -deflate_compress_fastest(struct libdeflate_compressor * restrict c, - const u8 *in, size_t in_nbytes, - struct deflate_output_bitstream *os) -{ - const u8 *in_next = in; - const u8 *in_end = in_next + in_nbytes; - const u8 *in_cur_base = in_next; - u32 max_len = DEFLATE_MAX_MATCH_LEN; - u32 nice_len = MIN(c->nice_match_length, max_len); - u32 next_hash = 0; - - ht_matchfinder_init(&c->p.f.ht_mf); - - do { - /* Starting a new DEFLATE block */ - - const u8 * const in_block_begin = in_next; - const u8 * const in_max_block_end = choose_max_block_end( - in_next, in_end, FAST_SOFT_MAX_BLOCK_LENGTH); - struct deflate_sequence *seq = c->p.f.sequences; - - deflate_begin_sequences(c, seq); - - do { - u32 length; - u32 offset; - size_t remaining = in_end - in_next; - - if (unlikely(remaining < DEFLATE_MAX_MATCH_LEN)) { - max_len = remaining; - if (max_len < HT_MATCHFINDER_REQUIRED_NBYTES) { - do { - deflate_choose_literal(c, - *in_next++, false, seq); - } while (--max_len); - break; - } - nice_len = MIN(nice_len, max_len); - } - length = ht_matchfinder_longest_match(&c->p.f.ht_mf, - &in_cur_base, - in_next, - max_len, - nice_len, - &next_hash, - &offset); - if (length) { - /* Match found */ - deflate_choose_match(c, length, offset, false, - &seq); - ht_matchfinder_skip_bytes(&c->p.f.ht_mf, - &in_cur_base, - in_next + 1, - in_end, - length - 1, - &next_hash); - in_next += length; - } else { - /* No match found */ - deflate_choose_literal(c, *in_next++, false, - seq); - } - - /* Check if it's time to output another block. */ - } while (in_next < in_max_block_end && - seq < &c->p.f.sequences[FAST_SEQ_STORE_LENGTH]); - - deflate_finish_block(c, os, in_block_begin, - in_next - in_block_begin, - c->p.f.sequences, in_next == in_end); - } while (in_next != in_end && !os->overflow); -} - -/* - * This is the "greedy" DEFLATE compressor. It always chooses the longest match. - */ -static void -deflate_compress_greedy(struct libdeflate_compressor * restrict c, - const u8 *in, size_t in_nbytes, - struct deflate_output_bitstream *os) -{ - const u8 *in_next = in; - const u8 *in_end = in_next + in_nbytes; - const u8 *in_cur_base = in_next; - u32 max_len = DEFLATE_MAX_MATCH_LEN; - u32 nice_len = MIN(c->nice_match_length, max_len); - u32 next_hashes[2] = {0, 0}; - - hc_matchfinder_init(&c->p.g.hc_mf); - - do { - /* Starting a new DEFLATE block */ - - const u8 * const in_block_begin = in_next; - const u8 * const in_max_block_end = choose_max_block_end( - in_next, in_end, SOFT_MAX_BLOCK_LENGTH); - struct deflate_sequence *seq = c->p.g.sequences; - u32 min_len; - - init_block_split_stats(&c->split_stats); - deflate_begin_sequences(c, seq); - min_len = calculate_min_match_len(in_next, - in_max_block_end - in_next, - c->max_search_depth); - do { - u32 length; - u32 offset; - - adjust_max_and_nice_len(&max_len, &nice_len, - in_end - in_next); - length = hc_matchfinder_longest_match( - &c->p.g.hc_mf, - &in_cur_base, - in_next, - min_len - 1, - max_len, - nice_len, - c->max_search_depth, - next_hashes, - &offset); - - if (length >= min_len && - (length > DEFLATE_MIN_MATCH_LEN || - offset <= 4096)) { - /* Match found */ - deflate_choose_match(c, length, offset, true, - &seq); - hc_matchfinder_skip_bytes(&c->p.g.hc_mf, - &in_cur_base, - in_next + 1, - in_end, - length - 1, - next_hashes); - in_next += length; - } else { - /* No match found */ - deflate_choose_literal(c, *in_next++, true, - seq); - } - - /* Check if it's time to output another block. */ - } while (in_next < in_max_block_end && - seq < &c->p.g.sequences[SEQ_STORE_LENGTH] && - !should_end_block(&c->split_stats, - in_block_begin, in_next, in_end)); - - deflate_finish_block(c, os, in_block_begin, - in_next - in_block_begin, - c->p.g.sequences, in_next == in_end); - } while (in_next != in_end && !os->overflow); -} - -static forceinline void -deflate_compress_lazy_generic(struct libdeflate_compressor * restrict c, - const u8 *in, size_t in_nbytes, - struct deflate_output_bitstream *os, bool lazy2) -{ - const u8 *in_next = in; - const u8 *in_end = in_next + in_nbytes; - const u8 *in_cur_base = in_next; - u32 max_len = DEFLATE_MAX_MATCH_LEN; - u32 nice_len = MIN(c->nice_match_length, max_len); - u32 next_hashes[2] = {0, 0}; - - hc_matchfinder_init(&c->p.g.hc_mf); - - do { - /* Starting a new DEFLATE block */ - - const u8 * const in_block_begin = in_next; - const u8 * const in_max_block_end = choose_max_block_end( - in_next, in_end, SOFT_MAX_BLOCK_LENGTH); - const u8 *next_recalc_min_len = - in_next + MIN(in_end - in_next, 10000); - struct deflate_sequence *seq = c->p.g.sequences; - u32 min_len; - - init_block_split_stats(&c->split_stats); - deflate_begin_sequences(c, seq); - min_len = calculate_min_match_len(in_next, - in_max_block_end - in_next, - c->max_search_depth); - do { - u32 cur_len; - u32 cur_offset; - u32 next_len; - u32 next_offset; - - /* - * Recalculate the minimum match length if it hasn't - * been done recently. - */ - if (in_next >= next_recalc_min_len) { - min_len = recalculate_min_match_len( - &c->freqs, - c->max_search_depth); - next_recalc_min_len += - MIN(in_end - next_recalc_min_len, - in_next - in_block_begin); - } - - /* Find the longest match at the current position. */ - adjust_max_and_nice_len(&max_len, &nice_len, - in_end - in_next); - cur_len = hc_matchfinder_longest_match( - &c->p.g.hc_mf, - &in_cur_base, - in_next, - min_len - 1, - max_len, - nice_len, - c->max_search_depth, - next_hashes, - &cur_offset); - if (cur_len < min_len || - (cur_len == DEFLATE_MIN_MATCH_LEN && - cur_offset > 8192)) { - /* No match found. Choose a literal. */ - deflate_choose_literal(c, *in_next++, true, - seq); - continue; - } - in_next++; - -have_cur_match: - /* - * We have a match at the current position. - * If it's very long, choose it immediately. - */ - if (cur_len >= nice_len) { - deflate_choose_match(c, cur_len, cur_offset, - true, &seq); - hc_matchfinder_skip_bytes(&c->p.g.hc_mf, - &in_cur_base, - in_next, - in_end, - cur_len - 1, - next_hashes); - in_next += cur_len - 1; - continue; - } - - /* - * Try to find a better match at the next position. - * - * Note: since we already have a match at the *current* - * position, we use only half the 'max_search_depth' - * when checking the *next* position. This is a useful - * trade-off because it's more worthwhile to use a - * greater search depth on the initial match. - * - * Note: it's possible to structure the code such that - * there's only one call to longest_match(), which - * handles both the "find the initial match" and "try to - * find a better match" cases. However, it is faster to - * have two call sites, with longest_match() inlined at - * each. - */ - adjust_max_and_nice_len(&max_len, &nice_len, - in_end - in_next); - next_len = hc_matchfinder_longest_match( - &c->p.g.hc_mf, - &in_cur_base, - in_next++, - cur_len - 1, - max_len, - nice_len, - c->max_search_depth >> 1, - next_hashes, - &next_offset); - if (next_len >= cur_len && - 4 * (int)(next_len - cur_len) + - ((int)bsr32(cur_offset) - - (int)bsr32(next_offset)) > 2) { - /* - * Found a better match at the next position. - * Output a literal. Then the next match - * becomes the current match. - */ - deflate_choose_literal(c, *(in_next - 2), true, - seq); - cur_len = next_len; - cur_offset = next_offset; - goto have_cur_match; - } - - if (lazy2) { - /* In lazy2 mode, look ahead another position */ - adjust_max_and_nice_len(&max_len, &nice_len, - in_end - in_next); - next_len = hc_matchfinder_longest_match( - &c->p.g.hc_mf, - &in_cur_base, - in_next++, - cur_len - 1, - max_len, - nice_len, - c->max_search_depth >> 2, - next_hashes, - &next_offset); - if (next_len >= cur_len && - 4 * (int)(next_len - cur_len) + - ((int)bsr32(cur_offset) - - (int)bsr32(next_offset)) > 6) { - /* - * There's a much better match two - * positions ahead, so use two literals. - */ - deflate_choose_literal( - c, *(in_next - 3), true, seq); - deflate_choose_literal( - c, *(in_next - 2), true, seq); - cur_len = next_len; - cur_offset = next_offset; - goto have_cur_match; - } - /* - * No better match at either of the next 2 - * positions. Output the current match. - */ - deflate_choose_match(c, cur_len, cur_offset, - true, &seq); - if (cur_len > 3) { - hc_matchfinder_skip_bytes(&c->p.g.hc_mf, - &in_cur_base, - in_next, - in_end, - cur_len - 3, - next_hashes); - in_next += cur_len - 3; - } - } else { /* !lazy2 */ - /* - * No better match at the next position. Output - * the current match. - */ - deflate_choose_match(c, cur_len, cur_offset, - true, &seq); - hc_matchfinder_skip_bytes(&c->p.g.hc_mf, - &in_cur_base, - in_next, - in_end, - cur_len - 2, - next_hashes); - in_next += cur_len - 2; - } - /* Check if it's time to output another block. */ - } while (in_next < in_max_block_end && - seq < &c->p.g.sequences[SEQ_STORE_LENGTH] && - !should_end_block(&c->split_stats, - in_block_begin, in_next, in_end)); - - deflate_finish_block(c, os, in_block_begin, - in_next - in_block_begin, - c->p.g.sequences, in_next == in_end); - } while (in_next != in_end && !os->overflow); -} - -/* - * This is the "lazy" DEFLATE compressor. Before choosing a match, it checks to - * see if there's a better match at the next position. If yes, it outputs a - * literal and continues to the next position. If no, it outputs the match. - */ -static void -deflate_compress_lazy(struct libdeflate_compressor * restrict c, - const u8 *in, size_t in_nbytes, - struct deflate_output_bitstream *os) -{ - deflate_compress_lazy_generic(c, in, in_nbytes, os, false); -} - -/* - * The lazy2 compressor. This is similar to the regular lazy one, but it looks - * for a better match at the next 2 positions rather than the next 1. This - * makes it take slightly more time, but compress some inputs slightly more. - */ -static void -deflate_compress_lazy2(struct libdeflate_compressor * restrict c, - const u8 *in, size_t in_nbytes, - struct deflate_output_bitstream *os) -{ - deflate_compress_lazy_generic(c, in, in_nbytes, os, true); -} - -#if SUPPORT_NEAR_OPTIMAL_PARSING - -/* - * Follow the minimum-cost path in the graph of possible match/literal choices - * for the current block and compute the frequencies of the Huffman symbols that - * would be needed to output those matches and literals. - */ -static void -deflate_tally_item_list(struct libdeflate_compressor *c, u32 block_length) -{ - struct deflate_optimum_node *cur_node = &c->p.n.optimum_nodes[0]; - struct deflate_optimum_node *end_node = - &c->p.n.optimum_nodes[block_length]; - - do { - u32 length = cur_node->item & OPTIMUM_LEN_MASK; - u32 offset = cur_node->item >> OPTIMUM_OFFSET_SHIFT; - - if (length == 1) { - /* Literal */ - c->freqs.litlen[offset]++; - } else { - /* Match */ - c->freqs.litlen[DEFLATE_FIRST_LEN_SYM + - deflate_length_slot[length]]++; - c->freqs.offset[c->p.n.offset_slot_full[offset]]++; - } - cur_node += length; - } while (cur_node != end_node); - - /* Tally the end-of-block symbol. */ - c->freqs.litlen[DEFLATE_END_OF_BLOCK]++; -} - -static void -deflate_choose_all_literals(struct libdeflate_compressor *c, - const u8 *block, u32 block_length) -{ - u32 i; - - deflate_reset_symbol_frequencies(c); - for (i = 0; i < block_length; i++) - c->freqs.litlen[block[i]]++; - c->freqs.litlen[DEFLATE_END_OF_BLOCK]++; - - deflate_make_huffman_codes(&c->freqs, &c->codes); -} - -/* - * Compute the exact cost, in bits, that would be required to output the matches - * and literals described by @c->freqs as a dynamic Huffman block. The litlen - * and offset codes are assumed to have already been built in @c->codes. - */ -static u32 -deflate_compute_true_cost(struct libdeflate_compressor *c) -{ - u32 cost = 0; - unsigned sym; - - deflate_precompute_huffman_header(c); - - memset(&c->codes.lens.litlen[c->o.precode.num_litlen_syms], 0, - DEFLATE_NUM_LITLEN_SYMS - c->o.precode.num_litlen_syms); - - cost += 5 + 5 + 4 + (3 * c->o.precode.num_explicit_lens); - for (sym = 0; sym < DEFLATE_NUM_PRECODE_SYMS; sym++) { - cost += c->o.precode.freqs[sym] * - (c->o.precode.lens[sym] + - deflate_extra_precode_bits[sym]); - } - - for (sym = 0; sym < DEFLATE_FIRST_LEN_SYM; sym++) - cost += c->freqs.litlen[sym] * c->codes.lens.litlen[sym]; - - for (; sym < DEFLATE_FIRST_LEN_SYM + - ARRAY_LEN(deflate_extra_length_bits); sym++) - cost += c->freqs.litlen[sym] * - (c->codes.lens.litlen[sym] + - deflate_extra_length_bits[sym - DEFLATE_FIRST_LEN_SYM]); - - for (sym = 0; sym < ARRAY_LEN(deflate_extra_offset_bits); sym++) - cost += c->freqs.offset[sym] * - (c->codes.lens.offset[sym] + - deflate_extra_offset_bits[sym]); - return cost; -} - -/* Set the current cost model from the codeword lengths specified in @lens. */ -static void -deflate_set_costs_from_codes(struct libdeflate_compressor *c, - const struct deflate_lens *lens) -{ - unsigned i; - - /* Literals */ - for (i = 0; i < DEFLATE_NUM_LITERALS; i++) { - u32 bits = (lens->litlen[i] ? - lens->litlen[i] : LITERAL_NOSTAT_BITS); - - c->p.n.costs.literal[i] = bits * BIT_COST; - } - - /* Lengths */ - for (i = DEFLATE_MIN_MATCH_LEN; i <= DEFLATE_MAX_MATCH_LEN; i++) { - unsigned length_slot = deflate_length_slot[i]; - unsigned litlen_sym = DEFLATE_FIRST_LEN_SYM + length_slot; - u32 bits = (lens->litlen[litlen_sym] ? - lens->litlen[litlen_sym] : LENGTH_NOSTAT_BITS); - - bits += deflate_extra_length_bits[length_slot]; - c->p.n.costs.length[i] = bits * BIT_COST; - } - - /* Offset slots */ - for (i = 0; i < ARRAY_LEN(deflate_offset_slot_base); i++) { - u32 bits = (lens->offset[i] ? - lens->offset[i] : OFFSET_NOSTAT_BITS); - - bits += deflate_extra_offset_bits[i]; - c->p.n.costs.offset_slot[i] = bits * BIT_COST; - } -} - -/* - * This lookup table gives the default cost of a literal symbol and of a length - * symbol, depending on the characteristics of the input data. It was generated - * by scripts/gen_default_litlen_costs.py. - * - * This table is indexed first by the estimated match probability: - * - * i=0: data doesn't contain many matches [match_prob=0.25] - * i=1: neutral [match_prob=0.50] - * i=2: data contains lots of matches [match_prob=0.75] - * - * This lookup produces a subtable which maps the number of distinct used - * literals to the default cost of a literal symbol, i.e.: - * - * int(-log2((1 - match_prob) / num_used_literals) * BIT_COST) - * - * ... for num_used_literals in [1, 256] (and 0, which is copied from 1). This - * accounts for literals usually getting cheaper as the number of distinct - * literals decreases, and as the proportion of literals to matches increases. - * - * The lookup also produces the cost of a length symbol, which is: - * - * int(-log2(match_prob/NUM_LEN_SLOTS) * BIT_COST) - * - * Note: we don't currently assign different costs to different literal symbols, - * or to different length symbols, as this is hard to do in a useful way. - */ -static const struct { - u8 used_lits_to_lit_cost[257]; - u8 len_sym_cost; -} default_litlen_costs[] = { - { /* match_prob = 0.25 */ - .used_lits_to_lit_cost = { - 6, 6, 22, 32, 38, 43, 48, 51, - 54, 57, 59, 61, 64, 65, 67, 69, - 70, 72, 73, 74, 75, 76, 77, 79, - 80, 80, 81, 82, 83, 84, 85, 85, - 86, 87, 88, 88, 89, 89, 90, 91, - 91, 92, 92, 93, 93, 94, 95, 95, - 96, 96, 96, 97, 97, 98, 98, 99, - 99, 99, 100, 100, 101, 101, 101, 102, - 102, 102, 103, 103, 104, 104, 104, 105, - 105, 105, 105, 106, 106, 106, 107, 107, - 107, 108, 108, 108, 108, 109, 109, 109, - 109, 110, 110, 110, 111, 111, 111, 111, - 112, 112, 112, 112, 112, 113, 113, 113, - 113, 114, 114, 114, 114, 114, 115, 115, - 115, 115, 115, 116, 116, 116, 116, 116, - 117, 117, 117, 117, 117, 118, 118, 118, - 118, 118, 118, 119, 119, 119, 119, 119, - 120, 120, 120, 120, 120, 120, 121, 121, - 121, 121, 121, 121, 121, 122, 122, 122, - 122, 122, 122, 123, 123, 123, 123, 123, - 123, 123, 124, 124, 124, 124, 124, 124, - 124, 125, 125, 125, 125, 125, 125, 125, - 125, 126, 126, 126, 126, 126, 126, 126, - 127, 127, 127, 127, 127, 127, 127, 127, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 129, 129, 129, 129, 129, 129, 129, - 129, 129, 130, 130, 130, 130, 130, 130, - 130, 130, 130, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 133, - 133, 133, 133, 133, 133, 133, 133, 133, - 133, 134, 134, 134, 134, 134, 134, 134, - 134, - }, - .len_sym_cost = 109, - }, { /* match_prob = 0.5 */ - .used_lits_to_lit_cost = { - 16, 16, 32, 41, 48, 53, 57, 60, - 64, 66, 69, 71, 73, 75, 76, 78, - 80, 81, 82, 83, 85, 86, 87, 88, - 89, 90, 91, 92, 92, 93, 94, 95, - 96, 96, 97, 98, 98, 99, 99, 100, - 101, 101, 102, 102, 103, 103, 104, 104, - 105, 105, 106, 106, 107, 107, 108, 108, - 108, 109, 109, 110, 110, 110, 111, 111, - 112, 112, 112, 113, 113, 113, 114, 114, - 114, 115, 115, 115, 115, 116, 116, 116, - 117, 117, 117, 118, 118, 118, 118, 119, - 119, 119, 119, 120, 120, 120, 120, 121, - 121, 121, 121, 122, 122, 122, 122, 122, - 123, 123, 123, 123, 124, 124, 124, 124, - 124, 125, 125, 125, 125, 125, 126, 126, - 126, 126, 126, 127, 127, 127, 127, 127, - 128, 128, 128, 128, 128, 128, 129, 129, - 129, 129, 129, 129, 130, 130, 130, 130, - 130, 130, 131, 131, 131, 131, 131, 131, - 131, 132, 132, 132, 132, 132, 132, 133, - 133, 133, 133, 133, 133, 133, 134, 134, - 134, 134, 134, 134, 134, 134, 135, 135, - 135, 135, 135, 135, 135, 135, 136, 136, - 136, 136, 136, 136, 136, 136, 137, 137, - 137, 137, 137, 137, 137, 137, 138, 138, - 138, 138, 138, 138, 138, 138, 138, 139, - 139, 139, 139, 139, 139, 139, 139, 139, - 140, 140, 140, 140, 140, 140, 140, 140, - 140, 141, 141, 141, 141, 141, 141, 141, - 141, 141, 141, 142, 142, 142, 142, 142, - 142, 142, 142, 142, 142, 142, 143, 143, - 143, 143, 143, 143, 143, 143, 143, 143, - 144, - }, - .len_sym_cost = 93, - }, { /* match_prob = 0.75 */ - .used_lits_to_lit_cost = { - 32, 32, 48, 57, 64, 69, 73, 76, - 80, 82, 85, 87, 89, 91, 92, 94, - 96, 97, 98, 99, 101, 102, 103, 104, - 105, 106, 107, 108, 108, 109, 110, 111, - 112, 112, 113, 114, 114, 115, 115, 116, - 117, 117, 118, 118, 119, 119, 120, 120, - 121, 121, 122, 122, 123, 123, 124, 124, - 124, 125, 125, 126, 126, 126, 127, 127, - 128, 128, 128, 129, 129, 129, 130, 130, - 130, 131, 131, 131, 131, 132, 132, 132, - 133, 133, 133, 134, 134, 134, 134, 135, - 135, 135, 135, 136, 136, 136, 136, 137, - 137, 137, 137, 138, 138, 138, 138, 138, - 139, 139, 139, 139, 140, 140, 140, 140, - 140, 141, 141, 141, 141, 141, 142, 142, - 142, 142, 142, 143, 143, 143, 143, 143, - 144, 144, 144, 144, 144, 144, 145, 145, - 145, 145, 145, 145, 146, 146, 146, 146, - 146, 146, 147, 147, 147, 147, 147, 147, - 147, 148, 148, 148, 148, 148, 148, 149, - 149, 149, 149, 149, 149, 149, 150, 150, - 150, 150, 150, 150, 150, 150, 151, 151, - 151, 151, 151, 151, 151, 151, 152, 152, - 152, 152, 152, 152, 152, 152, 153, 153, - 153, 153, 153, 153, 153, 153, 154, 154, - 154, 154, 154, 154, 154, 154, 154, 155, - 155, 155, 155, 155, 155, 155, 155, 155, - 156, 156, 156, 156, 156, 156, 156, 156, - 156, 157, 157, 157, 157, 157, 157, 157, - 157, 157, 157, 158, 158, 158, 158, 158, - 158, 158, 158, 158, 158, 158, 159, 159, - 159, 159, 159, 159, 159, 159, 159, 159, - 160, - }, - .len_sym_cost = 84, - }, -}; - -/* - * Choose the default costs for literal and length symbols. These symbols are - * both part of the litlen alphabet. - */ -static void -deflate_choose_default_litlen_costs(struct libdeflate_compressor *c, - const u8 *block_begin, u32 block_length, - u32 *lit_cost, u32 *len_sym_cost) -{ - u32 num_used_literals = 0; - u32 literal_freq = block_length; - u32 match_freq = 0; - u32 cutoff; - u32 i; - - /* Calculate the number of distinct literals that exist in the data. */ - memset(c->freqs.litlen, 0, - DEFLATE_NUM_LITERALS * sizeof(c->freqs.litlen[0])); - cutoff = literal_freq >> 11; /* Ignore literals used very rarely. */ - for (i = 0; i < block_length; i++) - c->freqs.litlen[block_begin[i]]++; - for (i = 0; i < DEFLATE_NUM_LITERALS; i++) { - if (c->freqs.litlen[i] > cutoff) - num_used_literals++; - } - if (num_used_literals == 0) - num_used_literals = 1; - - /* - * Estimate the relative frequency of literals and matches in the - * optimal parsing solution. We don't know the optimal solution, so - * this can only be a very rough estimate. Therefore, we basically use - * the match frequency from a greedy parse. We also apply the min_len - * heuristic used by the greedy and lazy parsers, to avoid counting too - * many matches when literals are cheaper than short matches. - */ - match_freq = 0; - i = choose_min_match_len(num_used_literals, c->max_search_depth); - for (; i < ARRAY_LEN(c->p.n.match_len_freqs); i++) { - match_freq += c->p.n.match_len_freqs[i]; - literal_freq -= i * c->p.n.match_len_freqs[i]; - } - if ((s32)literal_freq < 0) /* shouldn't happen */ - literal_freq = 0; - - if (match_freq > literal_freq) - i = 2; /* many matches */ - else if (match_freq * 4 > literal_freq) - i = 1; /* neutral */ - else - i = 0; /* few matches */ - - STATIC_ASSERT(BIT_COST == 16); - *lit_cost = default_litlen_costs[i].used_lits_to_lit_cost[ - num_used_literals]; - *len_sym_cost = default_litlen_costs[i].len_sym_cost; -} - -static forceinline u32 -deflate_default_length_cost(u32 len, u32 len_sym_cost) -{ - unsigned slot = deflate_length_slot[len]; - u32 num_extra_bits = deflate_extra_length_bits[slot]; - - return len_sym_cost + (num_extra_bits * BIT_COST); -} - -static forceinline u32 -deflate_default_offset_slot_cost(unsigned slot) -{ - u32 num_extra_bits = deflate_extra_offset_bits[slot]; - /* - * Assume that all offset symbols are equally probable. - * The resulting cost is 'int(-log2(1/30) * BIT_COST)', - * where 30 is the number of potentially-used offset symbols. - */ - u32 offset_sym_cost = 4*BIT_COST + (907*BIT_COST)/1000; - - return offset_sym_cost + (num_extra_bits * BIT_COST); -} - -/* Set default symbol costs for the first block's first optimization pass. */ -static void -deflate_set_default_costs(struct libdeflate_compressor *c, - u32 lit_cost, u32 len_sym_cost) -{ - u32 i; - - /* Literals */ - for (i = 0; i < DEFLATE_NUM_LITERALS; i++) - c->p.n.costs.literal[i] = lit_cost; - - /* Lengths */ - for (i = DEFLATE_MIN_MATCH_LEN; i <= DEFLATE_MAX_MATCH_LEN; i++) - c->p.n.costs.length[i] = - deflate_default_length_cost(i, len_sym_cost); - - /* Offset slots */ - for (i = 0; i < ARRAY_LEN(deflate_offset_slot_base); i++) - c->p.n.costs.offset_slot[i] = - deflate_default_offset_slot_cost(i); -} - -static forceinline void -deflate_adjust_cost(u32 *cost_p, u32 default_cost, int change_amount) -{ - if (change_amount == 0) - /* Block is very similar to previous; prefer previous costs. */ - *cost_p = (default_cost + 3 * *cost_p) / 4; - else if (change_amount == 1) - *cost_p = (default_cost + *cost_p) / 2; - else if (change_amount == 2) - *cost_p = (5 * default_cost + 3 * *cost_p) / 8; - else - /* Block differs greatly from previous; prefer default costs. */ - *cost_p = (3 * default_cost + *cost_p) / 4; -} - -static forceinline void -deflate_adjust_costs_impl(struct libdeflate_compressor *c, - u32 lit_cost, u32 len_sym_cost, int change_amount) -{ - u32 i; - - /* Literals */ - for (i = 0; i < DEFLATE_NUM_LITERALS; i++) - deflate_adjust_cost(&c->p.n.costs.literal[i], lit_cost, - change_amount); - - /* Lengths */ - for (i = DEFLATE_MIN_MATCH_LEN; i <= DEFLATE_MAX_MATCH_LEN; i++) - deflate_adjust_cost(&c->p.n.costs.length[i], - deflate_default_length_cost(i, - len_sym_cost), - change_amount); - - /* Offset slots */ - for (i = 0; i < ARRAY_LEN(deflate_offset_slot_base); i++) - deflate_adjust_cost(&c->p.n.costs.offset_slot[i], - deflate_default_offset_slot_cost(i), - change_amount); -} - -/* - * Adjust the costs when beginning a new block. - * - * Since the current costs are optimized for the data already, it can be helpful - * to reuse them instead of starting over with the default costs. However, this - * depends on how similar the new block is to the previous block. Therefore, - * use a heuristic to decide how similar the blocks are, and mix together the - * current costs and the default costs accordingly. - */ -static void -deflate_adjust_costs(struct libdeflate_compressor *c, - u32 lit_cost, u32 len_sym_cost) -{ - u64 total_delta = 0; - u64 cutoff; - int i; - - /* - * Decide how different the current block is from the previous block, - * using the block splitting statistics from the current and previous - * blocks. The more different the current block is, the more we prefer - * the default costs rather than the previous block's costs. - * - * The algorithm here is similar to the end-of-block check one, but here - * we compare two entire blocks rather than a partial block with a small - * extra part, and therefore we need 64-bit numbers in some places. - */ - for (i = 0; i < NUM_OBSERVATION_TYPES; i++) { - u64 prev = (u64)c->p.n.prev_observations[i] * - c->split_stats.num_observations; - u64 cur = (u64)c->split_stats.observations[i] * - c->p.n.prev_num_observations; - - total_delta += prev > cur ? prev - cur : cur - prev; - } - cutoff = ((u64)c->p.n.prev_num_observations * - c->split_stats.num_observations * 200) / 512; - - if (total_delta > 3 * cutoff) - /* Big change in the data; just use the default costs. */ - deflate_set_default_costs(c, lit_cost, len_sym_cost); - else if (4 * total_delta > 9 * cutoff) - deflate_adjust_costs_impl(c, lit_cost, len_sym_cost, 3); - else if (2 * total_delta > 3 * cutoff) - deflate_adjust_costs_impl(c, lit_cost, len_sym_cost, 2); - else if (2 * total_delta > cutoff) - deflate_adjust_costs_impl(c, lit_cost, len_sym_cost, 1); - else - deflate_adjust_costs_impl(c, lit_cost, len_sym_cost, 0); -} - -static void -deflate_set_initial_costs(struct libdeflate_compressor *c, - const u8 *block_begin, u32 block_length, - bool is_first_block) -{ - u32 lit_cost, len_sym_cost; - - deflate_choose_default_litlen_costs(c, block_begin, block_length, - &lit_cost, &len_sym_cost); - if (is_first_block) - deflate_set_default_costs(c, lit_cost, len_sym_cost); - else - deflate_adjust_costs(c, lit_cost, len_sym_cost); -} - -/* - * Find the minimum-cost path through the graph of possible match/literal - * choices for this block. - * - * We find the minimum cost path from 'c->p.n.optimum_nodes[0]', which - * represents the node at the beginning of the block, to - * 'c->p.n.optimum_nodes[block_length]', which represents the node at the end of - * the block. Edge costs are evaluated using the cost model 'c->p.n.costs'. - * - * The algorithm works backwards, starting at the end node and proceeding - * backwards one node at a time. At each node, the minimum cost to reach the - * end node is computed and the match/literal choice that begins that path is - * saved. - */ -static void -deflate_find_min_cost_path(struct libdeflate_compressor *c, - const u32 block_length, - const struct lz_match *cache_ptr) -{ - struct deflate_optimum_node *end_node = - &c->p.n.optimum_nodes[block_length]; - struct deflate_optimum_node *cur_node = end_node; - - cur_node->cost_to_end = 0; - do { - unsigned num_matches; - u32 literal; - u32 best_cost_to_end; - - cur_node--; - cache_ptr--; - - num_matches = cache_ptr->length; - literal = cache_ptr->offset; - - /* It's always possible to choose a literal. */ - best_cost_to_end = c->p.n.costs.literal[literal] + - (cur_node + 1)->cost_to_end; - cur_node->item = (literal << OPTIMUM_OFFSET_SHIFT) | 1; - - /* Also consider matches if there are any. */ - if (num_matches) { - const struct lz_match *match; - u32 len; - u32 offset; - u32 offset_slot; - u32 offset_cost; - u32 cost_to_end; - - /* - * Consider each length from the minimum - * (DEFLATE_MIN_MATCH_LEN) to the length of the longest - * match found at this position. For each length, we - * consider only the smallest offset for which that - * length is available. Although this is not guaranteed - * to be optimal due to the possibility of a larger - * offset costing less than a smaller offset to code, - * this is a very useful heuristic. - */ - match = cache_ptr - num_matches; - len = DEFLATE_MIN_MATCH_LEN; - do { - offset = match->offset; - offset_slot = c->p.n.offset_slot_full[offset]; - offset_cost = - c->p.n.costs.offset_slot[offset_slot]; - do { - cost_to_end = offset_cost + - c->p.n.costs.length[len] + - (cur_node + len)->cost_to_end; - if (cost_to_end < best_cost_to_end) { - best_cost_to_end = cost_to_end; - cur_node->item = len | - (offset << - OPTIMUM_OFFSET_SHIFT); - } - } while (++len <= match->length); - } while (++match != cache_ptr); - cache_ptr -= num_matches; - } - cur_node->cost_to_end = best_cost_to_end; - } while (cur_node != &c->p.n.optimum_nodes[0]); - - deflate_reset_symbol_frequencies(c); - deflate_tally_item_list(c, block_length); - deflate_make_huffman_codes(&c->freqs, &c->codes); -} - -/* - * Choose the literals and matches for the current block, then output the block. - * - * To choose the literal/match sequence, we find the minimum-cost path through - * the block's graph of literal/match choices, given a cost model. However, the - * true cost of each symbol is unknown until the Huffman codes have been built, - * but at the same time the Huffman codes depend on the frequencies of chosen - * symbols. Consequently, multiple passes must be used to try to approximate an - * optimal solution. The first pass uses default costs, mixed with the costs - * from the previous block when it seems appropriate. Later passes use the - * Huffman codeword lengths from the previous pass as the costs. - * - * As an alternate strategy, also consider using only literals. The boolean - * returned in *used_only_literals indicates whether that strategy was best. - */ -static void -deflate_optimize_and_flush_block(struct libdeflate_compressor *c, - struct deflate_output_bitstream *os, - const u8 *block_begin, u32 block_length, - const struct lz_match *cache_ptr, - bool is_first_block, bool is_final_block, - bool *used_only_literals) -{ - unsigned num_passes_remaining = c->p.n.max_optim_passes; - u32 best_true_cost = UINT32_MAX; - u32 true_cost; - u32 only_lits_cost; - u32 static_cost = UINT32_MAX; - struct deflate_sequence seq_; - struct deflate_sequence *seq = NULL; - u32 i; - - /* - * On some data, using only literals (no matches) ends up being better - * than what the iterative optimization algorithm produces. Therefore, - * consider using only literals. - */ - deflate_choose_all_literals(c, block_begin, block_length); - only_lits_cost = deflate_compute_true_cost(c); - - /* - * Force the block to really end at the desired length, even if some - * matches extend beyond it. - */ - for (i = block_length; - i <= MIN(block_length - 1 + DEFLATE_MAX_MATCH_LEN, - ARRAY_LEN(c->p.n.optimum_nodes) - 1); i++) - c->p.n.optimum_nodes[i].cost_to_end = 0x80000000; - - /* - * Sometimes a static Huffman block ends up being cheapest, particularly - * if the block is small. So, if the block is sufficiently small, find - * the optimal static block solution and remember its cost. - */ - if (block_length <= c->p.n.max_len_to_optimize_static_block) { - /* Save c->p.n.costs temporarily. */ - c->p.n.costs_saved = c->p.n.costs; - - deflate_set_costs_from_codes(c, &c->static_codes.lens); - deflate_find_min_cost_path(c, block_length, cache_ptr); - static_cost = c->p.n.optimum_nodes[0].cost_to_end / BIT_COST; - static_cost += 7; /* for the end-of-block symbol */ - - /* Restore c->p.n.costs. */ - c->p.n.costs = c->p.n.costs_saved; - } - - /* Initialize c->p.n.costs with default costs. */ - deflate_set_initial_costs(c, block_begin, block_length, is_first_block); - - do { - /* - * Find the minimum-cost path for this pass. - * Also set c->freqs and c->codes to match the path. - */ - deflate_find_min_cost_path(c, block_length, cache_ptr); - - /* - * Compute the exact cost of the block if the path were to be - * used. Note that this differs from - * c->p.n.optimum_nodes[0].cost_to_end in that true_cost uses - * the actual Huffman codes instead of c->p.n.costs. - */ - true_cost = deflate_compute_true_cost(c); - - /* - * If the cost didn't improve much from the previous pass, then - * doing more passes probably won't be helpful, so stop early. - */ - if (true_cost + c->p.n.min_improvement_to_continue > - best_true_cost) - break; - - best_true_cost = true_cost; - - /* Save the cost model that gave 'best_true_cost'. */ - c->p.n.costs_saved = c->p.n.costs; - - /* Update the cost model from the Huffman codes. */ - deflate_set_costs_from_codes(c, &c->codes.lens); - - } while (--num_passes_remaining); - - *used_only_literals = false; - if (MIN(only_lits_cost, static_cost) < best_true_cost) { - if (only_lits_cost < static_cost) { - /* Using only literals ended up being best! */ - deflate_choose_all_literals(c, block_begin, block_length); - deflate_set_costs_from_codes(c, &c->codes.lens); - seq_.litrunlen_and_length = block_length; - seq = &seq_; - *used_only_literals = true; - } else { - /* Static block ended up being best! */ - deflate_set_costs_from_codes(c, &c->static_codes.lens); - deflate_find_min_cost_path(c, block_length, cache_ptr); - } - } else if (true_cost >= - best_true_cost + c->p.n.min_bits_to_use_nonfinal_path) { - /* - * The best solution was actually from a non-final optimization - * pass, so recover and use the min-cost path from that pass. - */ - c->p.n.costs = c->p.n.costs_saved; - deflate_find_min_cost_path(c, block_length, cache_ptr); - deflate_set_costs_from_codes(c, &c->codes.lens); - } - deflate_flush_block(c, os, block_begin, block_length, seq, - is_final_block); -} - -static void -deflate_near_optimal_init_stats(struct libdeflate_compressor *c) -{ - init_block_split_stats(&c->split_stats); - memset(c->p.n.new_match_len_freqs, 0, - sizeof(c->p.n.new_match_len_freqs)); - memset(c->p.n.match_len_freqs, 0, sizeof(c->p.n.match_len_freqs)); -} - -static void -deflate_near_optimal_merge_stats(struct libdeflate_compressor *c) -{ - unsigned i; - - merge_new_observations(&c->split_stats); - for (i = 0; i < ARRAY_LEN(c->p.n.match_len_freqs); i++) { - c->p.n.match_len_freqs[i] += c->p.n.new_match_len_freqs[i]; - c->p.n.new_match_len_freqs[i] = 0; - } -} - -/* - * Save some literal/match statistics from the previous block so that - * deflate_adjust_costs() will be able to decide how much the current block - * differs from the previous one. - */ -static void -deflate_near_optimal_save_stats(struct libdeflate_compressor *c) -{ - int i; - - for (i = 0; i < NUM_OBSERVATION_TYPES; i++) - c->p.n.prev_observations[i] = c->split_stats.observations[i]; - c->p.n.prev_num_observations = c->split_stats.num_observations; -} - -static void -deflate_near_optimal_clear_old_stats(struct libdeflate_compressor *c) -{ - int i; - - for (i = 0; i < NUM_OBSERVATION_TYPES; i++) - c->split_stats.observations[i] = 0; - c->split_stats.num_observations = 0; - memset(c->p.n.match_len_freqs, 0, sizeof(c->p.n.match_len_freqs)); -} - -/* - * This is the "near-optimal" DEFLATE compressor. It computes the optimal - * representation of each DEFLATE block using a minimum-cost path search over - * the graph of possible match/literal choices for that block, assuming a - * certain cost for each Huffman symbol. - * - * For several reasons, the end result is not guaranteed to be optimal: - * - * - Nonoptimal choice of blocks - * - Heuristic limitations on which matches are actually considered - * - Symbol costs are unknown until the symbols have already been chosen - * (so iterative optimization must be used) - */ -static void -deflate_compress_near_optimal(struct libdeflate_compressor * restrict c, - const u8 *in, size_t in_nbytes, - struct deflate_output_bitstream *os) -{ - const u8 *in_next = in; - const u8 *in_block_begin = in_next; - const u8 *in_end = in_next + in_nbytes; - const u8 *in_cur_base = in_next; - const u8 *in_next_slide = - in_next + MIN(in_end - in_next, MATCHFINDER_WINDOW_SIZE); - u32 max_len = DEFLATE_MAX_MATCH_LEN; - u32 nice_len = MIN(c->nice_match_length, max_len); - struct lz_match *cache_ptr = c->p.n.match_cache; - u32 next_hashes[2] = {0, 0}; - bool prev_block_used_only_literals = false; - - bt_matchfinder_init(&c->p.n.bt_mf); - deflate_near_optimal_init_stats(c); - - do { - /* Starting a new DEFLATE block */ - const u8 * const in_max_block_end = choose_max_block_end( - in_block_begin, in_end, SOFT_MAX_BLOCK_LENGTH); - const u8 *prev_end_block_check = NULL; - bool change_detected = false; - const u8 *next_observation = in_next; - u32 min_len; - - /* - * Use the minimum match length heuristic to improve the - * literal/match statistics gathered during matchfinding. - * However, the actual near-optimal parse won't respect min_len, - * as it can accurately assess the costs of different matches. - * - * If the "use only literals" strategy happened to be the best - * strategy on the previous block, then probably the - * min_match_len heuristic is still not aggressive enough for - * the data, so force gathering literal stats only. - */ - if (prev_block_used_only_literals) - min_len = DEFLATE_MAX_MATCH_LEN + 1; - else - min_len = calculate_min_match_len( - in_block_begin, - in_max_block_end - in_block_begin, - c->max_search_depth); - - /* - * Find matches until we decide to end the block. We end the - * block if any of the following is true: - * - * (1) Maximum block length has been reached - * (2) Match catch may overflow. - * (3) Block split heuristic says to split now. - */ - for (;;) { - struct lz_match *matches; - u32 best_len; - size_t remaining = in_end - in_next; - - /* Slide the window forward if needed. */ - if (in_next == in_next_slide) { - bt_matchfinder_slide_window(&c->p.n.bt_mf); - in_cur_base = in_next; - in_next_slide = in_next + - MIN(remaining, MATCHFINDER_WINDOW_SIZE); - } - - /* - * Find matches with the current position using the - * binary tree matchfinder and save them in match_cache. - * - * Note: the binary tree matchfinder is more suited for - * optimal parsing than the hash chain matchfinder. The - * reasons for this include: - * - * - The binary tree matchfinder can find more matches - * in the same number of steps. - * - One of the major advantages of hash chains is that - * skipping positions (not searching for matches at - * them) is faster; however, with optimal parsing we - * search for matches at almost all positions, so this - * advantage of hash chains is negated. - */ - matches = cache_ptr; - best_len = 0; - adjust_max_and_nice_len(&max_len, &nice_len, remaining); - if (likely(max_len >= BT_MATCHFINDER_REQUIRED_NBYTES)) { - cache_ptr = bt_matchfinder_get_matches( - &c->p.n.bt_mf, - in_cur_base, - in_next - in_cur_base, - max_len, - nice_len, - c->max_search_depth, - next_hashes, - matches); - if (cache_ptr > matches) - best_len = cache_ptr[-1].length; - } - if (in_next >= next_observation) { - if (best_len >= min_len) { - observe_match(&c->split_stats, - best_len); - next_observation = in_next + best_len; - c->p.n.new_match_len_freqs[best_len]++; - } else { - observe_literal(&c->split_stats, - *in_next); - next_observation = in_next + 1; - } - } - - cache_ptr->length = cache_ptr - matches; - cache_ptr->offset = *in_next; - in_next++; - cache_ptr++; - - /* - * If there was a very long match found, don't cache any - * matches for the bytes covered by that match. This - * avoids degenerate behavior when compressing highly - * redundant data, where the number of matches can be - * very large. - * - * This heuristic doesn't actually hurt the compression - * ratio very much. If there's a long match, then the - * data must be highly compressible, so it doesn't - * matter much what we do. - */ - if (best_len >= DEFLATE_MIN_MATCH_LEN && - best_len >= nice_len) { - --best_len; - do { - remaining = in_end - in_next; - if (in_next == in_next_slide) { - bt_matchfinder_slide_window( - &c->p.n.bt_mf); - in_cur_base = in_next; - in_next_slide = in_next + - MIN(remaining, - MATCHFINDER_WINDOW_SIZE); - } - adjust_max_and_nice_len(&max_len, - &nice_len, - remaining); - if (max_len >= - BT_MATCHFINDER_REQUIRED_NBYTES) { - bt_matchfinder_skip_byte( - &c->p.n.bt_mf, - in_cur_base, - in_next - in_cur_base, - nice_len, - c->max_search_depth, - next_hashes); - } - cache_ptr->length = 0; - cache_ptr->offset = *in_next; - in_next++; - cache_ptr++; - } while (--best_len); - } - /* Maximum block length or end of input reached? */ - if (in_next >= in_max_block_end) - break; - /* Match cache overflowed? */ - if (cache_ptr >= - &c->p.n.match_cache[MATCH_CACHE_LENGTH]) - break; - /* Not ready to try to end the block (again)? */ - if (!ready_to_check_block(&c->split_stats, - in_block_begin, in_next, - in_end)) - continue; - /* Check if it would be worthwhile to end the block. */ - if (do_end_block_check(&c->split_stats, - in_next - in_block_begin)) { - change_detected = true; - break; - } - /* Ending the block doesn't seem worthwhile here. */ - deflate_near_optimal_merge_stats(c); - prev_end_block_check = in_next; - } - /* - * All the matches for this block have been cached. Now choose - * the precise end of the block and the sequence of items to - * output to represent it, then flush the block. - */ - if (change_detected && prev_end_block_check != NULL) { - /* - * The block is being ended because a recent chunk of - * data differs from the rest of the block. We could - * end the block at 'in_next' like the greedy and lazy - * compressors do, but that's not ideal since it would - * include the differing chunk in the block. The - * near-optimal compressor has time to do a better job. - * Therefore, we rewind to just before the chunk, and - * output a block that only goes up to there. - * - * We then set things up to correctly start the next - * block, considering that some work has already been - * done on it (some matches found and stats gathered). - */ - struct lz_match *orig_cache_ptr = cache_ptr; - const u8 *in_block_end = prev_end_block_check; - u32 block_length = in_block_end - in_block_begin; - bool is_first = (in_block_begin == in); - bool is_final = false; - u32 num_bytes_to_rewind = in_next - in_block_end; - size_t cache_len_rewound; - - /* Rewind the match cache. */ - do { - cache_ptr--; - cache_ptr -= cache_ptr->length; - } while (--num_bytes_to_rewind); - cache_len_rewound = orig_cache_ptr - cache_ptr; - - deflate_optimize_and_flush_block( - c, os, in_block_begin, - block_length, cache_ptr, - is_first, is_final, - &prev_block_used_only_literals); - memmove(c->p.n.match_cache, cache_ptr, - cache_len_rewound * sizeof(*cache_ptr)); - cache_ptr = &c->p.n.match_cache[cache_len_rewound]; - deflate_near_optimal_save_stats(c); - /* - * Clear the stats for the just-flushed block, leaving - * just the stats for the beginning of the next block. - */ - deflate_near_optimal_clear_old_stats(c); - in_block_begin = in_block_end; - } else { - /* - * The block is being ended for a reason other than a - * differing data chunk being detected. Don't rewind at - * all; just end the block at the current position. - */ - u32 block_length = in_next - in_block_begin; - bool is_first = (in_block_begin == in); - bool is_final = (in_next == in_end); - - deflate_near_optimal_merge_stats(c); - deflate_optimize_and_flush_block( - c, os, in_block_begin, - block_length, cache_ptr, - is_first, is_final, - &prev_block_used_only_literals); - cache_ptr = &c->p.n.match_cache[0]; - deflate_near_optimal_save_stats(c); - deflate_near_optimal_init_stats(c); - in_block_begin = in_next; - } - } while (in_next != in_end && !os->overflow); -} - -/* Initialize c->p.n.offset_slot_full. */ -static void -deflate_init_offset_slot_full(struct libdeflate_compressor *c) -{ - u32 offset_slot; - u32 offset; - u32 offset_end; - - for (offset_slot = 0; offset_slot < ARRAY_LEN(deflate_offset_slot_base); - offset_slot++) { - offset = deflate_offset_slot_base[offset_slot]; - offset_end = offset + - (1 << deflate_extra_offset_bits[offset_slot]); - do { - c->p.n.offset_slot_full[offset] = offset_slot; - } while (++offset != offset_end); - } -} - -#endif /* SUPPORT_NEAR_OPTIMAL_PARSING */ - -LIBDEFLATEAPI struct libdeflate_compressor * -libdeflate_alloc_compressor_ex(int compression_level, - const struct libdeflate_options *options) -{ - struct libdeflate_compressor *c; - size_t size = offsetof(struct libdeflate_compressor, p); - - check_buildtime_parameters(); - - /* - * Note: if more fields are added to libdeflate_options, this code will - * need to be updated to support both the old and new structs. - */ - if (options->sizeof_options != sizeof(*options)) - return NULL; - - /* - * Note: For similarity with zlib's API, -1 is accepted as an alias for - * the default compression level. - */ - if (compression_level == -1) - compression_level = 6; - - if (compression_level < 0 || compression_level > 12) - return NULL; - -#if SUPPORT_NEAR_OPTIMAL_PARSING - if (compression_level >= 10) - size += sizeof(c->p.n); - else -#endif - { - if (compression_level >= 2) - size += sizeof(c->p.g); - else if (compression_level == 1) - size += sizeof(c->p.f); - } - - c = libdeflate_aligned_malloc(options->malloc_func ? - options->malloc_func : - libdeflate_default_malloc_func, - MATCHFINDER_MEM_ALIGNMENT, size); - if (!c) - return NULL; - c->free_func = options->free_func ? - options->free_func : libdeflate_default_free_func; - - c->compression_level = compression_level; - - /* - * The higher the compression level, the more we should bother trying to - * compress very small inputs. - */ - c->max_passthrough_size = 55 - (compression_level * 4); - - switch (compression_level) { - case 0: - c->max_passthrough_size = SIZE_MAX; - c->impl = NULL; /* not used */ - break; - case 1: - c->impl = deflate_compress_fastest; - /* max_search_depth is unused. */ - c->nice_match_length = 32; - break; - case 2: - c->impl = deflate_compress_greedy; - c->max_search_depth = 6; - c->nice_match_length = 10; - break; - case 3: - c->impl = deflate_compress_greedy; - c->max_search_depth = 12; - c->nice_match_length = 14; - break; - case 4: - c->impl = deflate_compress_greedy; - c->max_search_depth = 16; - c->nice_match_length = 30; - break; - case 5: - c->impl = deflate_compress_lazy; - c->max_search_depth = 16; - c->nice_match_length = 30; - break; - case 6: - c->impl = deflate_compress_lazy; - c->max_search_depth = 35; - c->nice_match_length = 65; - break; - case 7: - c->impl = deflate_compress_lazy; - c->max_search_depth = 100; - c->nice_match_length = 130; - break; - case 8: - c->impl = deflate_compress_lazy2; - c->max_search_depth = 300; - c->nice_match_length = DEFLATE_MAX_MATCH_LEN; - break; - case 9: -#if !SUPPORT_NEAR_OPTIMAL_PARSING - default: -#endif - c->impl = deflate_compress_lazy2; - c->max_search_depth = 600; - c->nice_match_length = DEFLATE_MAX_MATCH_LEN; - break; -#if SUPPORT_NEAR_OPTIMAL_PARSING - case 10: - c->impl = deflate_compress_near_optimal; - c->max_search_depth = 35; - c->nice_match_length = 75; - c->p.n.max_optim_passes = 2; - c->p.n.min_improvement_to_continue = 32; - c->p.n.min_bits_to_use_nonfinal_path = 32; - c->p.n.max_len_to_optimize_static_block = 0; - deflate_init_offset_slot_full(c); - break; - case 11: - c->impl = deflate_compress_near_optimal; - c->max_search_depth = 100; - c->nice_match_length = 150; - c->p.n.max_optim_passes = 4; - c->p.n.min_improvement_to_continue = 16; - c->p.n.min_bits_to_use_nonfinal_path = 16; - c->p.n.max_len_to_optimize_static_block = 1000; - deflate_init_offset_slot_full(c); - break; - case 12: - default: - c->impl = deflate_compress_near_optimal; - c->max_search_depth = 300; - c->nice_match_length = DEFLATE_MAX_MATCH_LEN; - c->p.n.max_optim_passes = 10; - c->p.n.min_improvement_to_continue = 1; - c->p.n.min_bits_to_use_nonfinal_path = 1; - c->p.n.max_len_to_optimize_static_block = 10000; - deflate_init_offset_slot_full(c); - break; -#endif /* SUPPORT_NEAR_OPTIMAL_PARSING */ - } - - deflate_init_static_codes(c); - - return c; -} - - -LIBDEFLATEAPI struct libdeflate_compressor * -libdeflate_alloc_compressor(int compression_level) -{ - static const struct libdeflate_options defaults = { - .sizeof_options = sizeof(defaults), - }; - return libdeflate_alloc_compressor_ex(compression_level, &defaults); -} - -LIBDEFLATEAPI size_t -libdeflate_deflate_compress(struct libdeflate_compressor *c, - const void *in, size_t in_nbytes, - void *out, size_t out_nbytes_avail) -{ - struct deflate_output_bitstream os; - - /* - * For extremely short inputs, or for compression level 0, just output - * uncompressed blocks. - */ - if (unlikely(in_nbytes <= c->max_passthrough_size)) - return deflate_compress_none(in, in_nbytes, - out, out_nbytes_avail); - - /* Initialize the output bitstream structure. */ - os.bitbuf = 0; - os.bitcount = 0; - os.next = out; - os.end = os.next + out_nbytes_avail; - os.overflow = false; - - /* Call the actual compression function. */ - (*c->impl)(c, in, in_nbytes, &os); - - /* Return 0 if the output buffer is too small. */ - if (os.overflow) - return 0; - - /* - * Write the final byte if needed. This can't overflow the output - * buffer because deflate_flush_block() would have set the overflow flag - * if there wasn't enough space remaining for the full final block. - */ - ASSERT(os.bitcount <= 7); - if (os.bitcount) { - ASSERT(os.next < os.end); - *os.next++ = os.bitbuf; - } - - /* Return the compressed size in bytes. */ - return os.next - (u8 *)out; -} - -LIBDEFLATEAPI void -libdeflate_free_compressor(struct libdeflate_compressor *c) -{ - if (c) - libdeflate_aligned_free(c->free_func, c); -} - -unsigned int -libdeflate_get_compression_level(struct libdeflate_compressor *c) -{ - return c->compression_level; -} - -LIBDEFLATEAPI size_t -libdeflate_deflate_compress_bound(struct libdeflate_compressor *c, - size_t in_nbytes) -{ - size_t max_blocks; - - /* - * Since the compressor never uses a compressed block when an - * uncompressed block is cheaper, the worst case can be no worse than - * the case where only uncompressed blocks are used. - * - * This is true even though up to 7 bits are "wasted" to byte-align the - * bitstream when a compressed block is followed by an uncompressed - * block. This is because a compressed block wouldn't have been used if - * it wasn't cheaper than an uncompressed block, and uncompressed blocks - * always end on a byte boundary. So the alignment bits will, at worst, - * go up to the place where the uncompressed block would have ended. - */ - - /* - * Calculate the maximum number of uncompressed blocks that the - * compressor can use for 'in_nbytes' of data. - * - * The minimum length that is passed to deflate_flush_block() is - * MIN_BLOCK_LENGTH bytes, except for the final block if needed. If - * deflate_flush_block() decides to use an uncompressed block, it - * actually will (in general) output a series of uncompressed blocks in - * order to stay within the UINT16_MAX limit of DEFLATE. But this can - * be disregarded here as long as '2 * MIN_BLOCK_LENGTH <= UINT16_MAX', - * as in that case this behavior can't result in more blocks than the - * case where deflate_flush_block() is called with min-length inputs. - * - * So the number of uncompressed blocks needed would be bounded by - * DIV_ROUND_UP(in_nbytes, MIN_BLOCK_LENGTH). However, empty inputs - * need 1 (empty) block, which gives the final expression below. - */ - STATIC_ASSERT(2 * MIN_BLOCK_LENGTH <= UINT16_MAX); - max_blocks = MAX(DIV_ROUND_UP(in_nbytes, MIN_BLOCK_LENGTH), 1); - - /* - * Each uncompressed block has 5 bytes of overhead, for the BFINAL, - * BTYPE, LEN, and NLEN fields. (For the reason explained earlier, the - * alignment bits at the very start of the block can be disregarded; - * they would otherwise increase the overhead to 6 bytes per block.) - * Therefore, the maximum number of overhead bytes is '5 * max_blocks'. - * To get the final bound, add the number of uncompressed bytes. - */ - return (5 * max_blocks) + in_nbytes; -} diff --git a/vendor/libdeflate/lib/deflate_compress.h b/vendor/libdeflate/lib/deflate_compress.h deleted file mode 100644 index 9451d548bb..0000000000 --- a/vendor/libdeflate/lib/deflate_compress.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef LIB_DEFLATE_COMPRESS_H -#define LIB_DEFLATE_COMPRESS_H - -#include "lib_common.h" - -/* - * DEFLATE compression is private to deflate_compress.c, but we do need to be - * able to query the compression level for zlib and gzip header generation. - */ - -struct libdeflate_compressor; - -unsigned int libdeflate_get_compression_level(struct libdeflate_compressor *c); - -#endif /* LIB_DEFLATE_COMPRESS_H */ diff --git a/vendor/libdeflate/lib/deflate_constants.h b/vendor/libdeflate/lib/deflate_constants.h deleted file mode 100644 index 95c9e0a503..0000000000 --- a/vendor/libdeflate/lib/deflate_constants.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * deflate_constants.h - constants for the DEFLATE compression format - */ - -#ifndef LIB_DEFLATE_CONSTANTS_H -#define LIB_DEFLATE_CONSTANTS_H - -/* Valid block types */ -#define DEFLATE_BLOCKTYPE_UNCOMPRESSED 0 -#define DEFLATE_BLOCKTYPE_STATIC_HUFFMAN 1 -#define DEFLATE_BLOCKTYPE_DYNAMIC_HUFFMAN 2 - -/* Minimum and maximum supported match lengths (in bytes) */ -#define DEFLATE_MIN_MATCH_LEN 3 -#define DEFLATE_MAX_MATCH_LEN 258 - -/* Maximum supported match offset (in bytes) */ -#define DEFLATE_MAX_MATCH_OFFSET 32768 - -/* log2 of DEFLATE_MAX_MATCH_OFFSET */ -#define DEFLATE_WINDOW_ORDER 15 - -/* Number of symbols in each Huffman code. Note: for the literal/length - * and offset codes, these are actually the maximum values; a given block - * might use fewer symbols. */ -#define DEFLATE_NUM_PRECODE_SYMS 19 -#define DEFLATE_NUM_LITLEN_SYMS 288 -#define DEFLATE_NUM_OFFSET_SYMS 32 - -/* The maximum number of symbols across all codes */ -#define DEFLATE_MAX_NUM_SYMS 288 - -/* Division of symbols in the literal/length code */ -#define DEFLATE_NUM_LITERALS 256 -#define DEFLATE_END_OF_BLOCK 256 -#define DEFLATE_FIRST_LEN_SYM 257 - -/* Maximum codeword length, in bits, within each Huffman code */ -#define DEFLATE_MAX_PRE_CODEWORD_LEN 7 -#define DEFLATE_MAX_LITLEN_CODEWORD_LEN 15 -#define DEFLATE_MAX_OFFSET_CODEWORD_LEN 15 - -/* The maximum codeword length across all codes */ -#define DEFLATE_MAX_CODEWORD_LEN 15 - -/* Maximum possible overrun when decoding codeword lengths */ -#define DEFLATE_MAX_LENS_OVERRUN 137 - -/* - * Maximum number of extra bits that may be required to represent a match - * length or offset. - */ -#define DEFLATE_MAX_EXTRA_LENGTH_BITS 5 -#define DEFLATE_MAX_EXTRA_OFFSET_BITS 13 - -#endif /* LIB_DEFLATE_CONSTANTS_H */ diff --git a/vendor/libdeflate/lib/deflate_decompress.c b/vendor/libdeflate/lib/deflate_decompress.c deleted file mode 100644 index d5af5eb024..0000000000 --- a/vendor/libdeflate/lib/deflate_decompress.c +++ /dev/null @@ -1,1208 +0,0 @@ -/* - * deflate_decompress.c - a decompressor for DEFLATE - * - * Copyright 2016 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * --------------------------------------------------------------------------- - * - * This is a highly optimized DEFLATE decompressor. It is much faster than - * vanilla zlib, typically well over twice as fast, though results vary by CPU. - * - * Why this is faster than vanilla zlib: - * - * - Word accesses rather than byte accesses when reading input - * - Word accesses rather than byte accesses when copying matches - * - Faster Huffman decoding combined with various DEFLATE-specific tricks - * - Larger bitbuffer variable that doesn't need to be refilled as often - * - Other optimizations to remove unnecessary branches - * - Only full-buffer decompression is supported, so the code doesn't need to - * support stopping and resuming decompression. - * - On x86_64, a version of the decompression routine is compiled with BMI2 - * instructions enabled and is used automatically at runtime when supported. - */ - -#include "lib_common.h" -#include "deflate_constants.h" - -/* - * If the expression passed to SAFETY_CHECK() evaluates to false, then the - * decompression routine immediately returns LIBDEFLATE_BAD_DATA, indicating the - * compressed data is invalid. - * - * Theoretically, these checks could be disabled for specialized applications - * where all input to the decompressor will be trusted. - */ -#if 0 -# pragma message("UNSAFE DECOMPRESSION IS ENABLED. THIS MUST ONLY BE USED IF THE DECOMPRESSOR INPUT WILL ALWAYS BE TRUSTED!") -# define SAFETY_CHECK(expr) (void)(expr) -#else -# define SAFETY_CHECK(expr) if (unlikely(!(expr))) return LIBDEFLATE_BAD_DATA -#endif - -/***************************************************************************** - * Input bitstream * - *****************************************************************************/ - -/* - * The state of the "input bitstream" consists of the following variables: - * - * - in_next: a pointer to the next unread byte in the input buffer - * - * - in_end: a pointer to just past the end of the input buffer - * - * - bitbuf: a word-sized variable containing bits that have been read from - * the input buffer or from the implicit appended zero bytes - * - * - bitsleft: the number of bits in 'bitbuf' available to be consumed. - * After REFILL_BITS_BRANCHLESS(), 'bitbuf' can actually - * contain more bits than this. However, only the bits counted - * by 'bitsleft' can actually be consumed; the rest can only be - * used for preloading. - * - * As a micro-optimization, we allow bits 8 and higher of - * 'bitsleft' to contain garbage. When consuming the bits - * associated with a decode table entry, this allows us to do - * 'bitsleft -= entry' instead of 'bitsleft -= (u8)entry'. - * On some CPUs, this helps reduce instruction dependencies. - * This does have the disadvantage that 'bitsleft' sometimes - * needs to be cast to 'u8', such as when it's used as a shift - * amount in REFILL_BITS_BRANCHLESS(). But that one happens - * for free since most CPUs ignore high bits in shift amounts. - * - * - overread_count: the total number of implicit appended zero bytes that - * have been loaded into the bitbuffer, including any - * counted by 'bitsleft' and any already consumed - */ - -/* - * The type for the bitbuffer variable ('bitbuf' described above). For best - * performance, this should have size equal to a machine word. - * - * 64-bit platforms have a significant advantage: they get a bigger bitbuffer - * which they don't have to refill as often. - */ -typedef machine_word_t bitbuf_t; -#define BITBUF_NBITS (8 * (int)sizeof(bitbuf_t)) - -/* BITMASK(n) returns a bitmask of length 'n'. */ -#define BITMASK(n) (((bitbuf_t)1 << (n)) - 1) - -/* - * MAX_BITSLEFT is the maximum number of consumable bits, i.e. the maximum value - * of '(u8)bitsleft'. This is the size of the bitbuffer variable, minus 1 if - * the branchless refill method is being used (see REFILL_BITS_BRANCHLESS()). - */ -#define MAX_BITSLEFT \ - (UNALIGNED_ACCESS_IS_FAST ? BITBUF_NBITS - 1 : BITBUF_NBITS) - -/* - * CONSUMABLE_NBITS is the minimum number of bits that are guaranteed to be - * consumable (counted in 'bitsleft') immediately after refilling the bitbuffer. - * Since only whole bytes can be added to 'bitsleft', the worst case is - * 'MAX_BITSLEFT - 7': the smallest amount where another byte doesn't fit. - */ -#define CONSUMABLE_NBITS (MAX_BITSLEFT - 7) - -/* - * FASTLOOP_PRELOADABLE_NBITS is the minimum number of bits that are guaranteed - * to be preloadable immediately after REFILL_BITS_IN_FASTLOOP(). (It is *not* - * guaranteed after REFILL_BITS(), since REFILL_BITS() falls back to a - * byte-at-a-time refill method near the end of input.) This may exceed the - * number of consumable bits (counted by 'bitsleft'). Any bits not counted in - * 'bitsleft' can only be used for precomputation and cannot be consumed. - */ -#define FASTLOOP_PRELOADABLE_NBITS \ - (UNALIGNED_ACCESS_IS_FAST ? BITBUF_NBITS : CONSUMABLE_NBITS) - -/* - * PRELOAD_SLACK is the minimum number of bits that are guaranteed to be - * preloadable but not consumable, following REFILL_BITS_IN_FASTLOOP() and any - * subsequent consumptions. This is 1 bit if the branchless refill method is - * being used, and 0 bits otherwise. - */ -#define PRELOAD_SLACK MAX(0, FASTLOOP_PRELOADABLE_NBITS - MAX_BITSLEFT) - -/* - * CAN_CONSUME(n) is true if it's guaranteed that if the bitbuffer has just been - * refilled, then it's always possible to consume 'n' bits from it. 'n' should - * be a compile-time constant, to enable compile-time evaluation. - */ -#define CAN_CONSUME(n) (CONSUMABLE_NBITS >= (n)) - -/* - * CAN_CONSUME_AND_THEN_PRELOAD(consume_nbits, preload_nbits) is true if it's - * guaranteed that after REFILL_BITS_IN_FASTLOOP(), it's always possible to - * consume 'consume_nbits' bits, then preload 'preload_nbits' bits. The - * arguments should be compile-time constants to enable compile-time evaluation. - */ -#define CAN_CONSUME_AND_THEN_PRELOAD(consume_nbits, preload_nbits) \ - (CONSUMABLE_NBITS >= (consume_nbits) && \ - FASTLOOP_PRELOADABLE_NBITS >= (consume_nbits) + (preload_nbits)) - -/* - * REFILL_BITS_BRANCHLESS() branchlessly refills the bitbuffer variable by - * reading the next word from the input buffer and updating 'in_next' and - * 'bitsleft' based on how many bits were refilled -- counting whole bytes only. - * This is much faster than reading a byte at a time, at least if the CPU is - * little endian and supports fast unaligned memory accesses. - * - * The simplest way of branchlessly updating 'bitsleft' would be: - * - * bitsleft += (MAX_BITSLEFT - bitsleft) & ~7; - * - * To make it faster, we define MAX_BITSLEFT to be 'WORDBITS - 1' rather than - * WORDBITS, so that in binary it looks like 111111 or 11111. Then, we update - * 'bitsleft' by just setting the bits above the low 3 bits: - * - * bitsleft |= MAX_BITSLEFT & ~7; - * - * That compiles down to a single instruction like 'or $0x38, %rbp'. Using - * 'MAX_BITSLEFT == WORDBITS - 1' also has the advantage that refills can be - * done when 'bitsleft == MAX_BITSLEFT' without invoking undefined behavior. - * - * The simplest way of branchlessly updating 'in_next' would be: - * - * in_next += (MAX_BITSLEFT - bitsleft) >> 3; - * - * With 'MAX_BITSLEFT == WORDBITS - 1' we could use an XOR instead, though this - * isn't really better: - * - * in_next += (MAX_BITSLEFT ^ bitsleft) >> 3; - * - * An alternative which can be marginally better is the following: - * - * in_next += sizeof(bitbuf_t) - 1; - * in_next -= (bitsleft >> 3) & 0x7; - * - * It seems this would increase the number of CPU instructions from 3 (sub, shr, - * add) to 4 (add, shr, and, sub). However, if the CPU has a bitfield - * extraction instruction (e.g. arm's ubfx), it stays at 3, and is potentially - * more efficient because the length of the longest dependency chain decreases - * from 3 to 2. This alternative also has the advantage that it ignores the - * high bits in 'bitsleft', so it is compatible with the micro-optimization we - * use where we let the high bits of 'bitsleft' contain garbage. - */ -#define REFILL_BITS_BRANCHLESS() \ -do { \ - bitbuf |= get_unaligned_leword(in_next) << (u8)bitsleft; \ - in_next += sizeof(bitbuf_t) - 1; \ - in_next -= (bitsleft >> 3) & 0x7; \ - bitsleft |= MAX_BITSLEFT & ~7; \ -} while (0) - -/* - * REFILL_BITS() loads bits from the input buffer until the bitbuffer variable - * contains at least CONSUMABLE_NBITS consumable bits. - * - * This checks for the end of input, and it doesn't guarantee - * FASTLOOP_PRELOADABLE_NBITS, so it can't be used in the fastloop. - * - * If we would overread the input buffer, we just don't read anything, leaving - * the bits zeroed but marking them filled. This simplifies the decompressor - * because it removes the need to always be able to distinguish between real - * overreads and overreads caused only by the decompressor's own lookahead. - * - * We do still keep track of the number of bytes that have been overread, for - * two reasons. First, it allows us to determine the exact number of bytes that - * were consumed once the stream ends or an uncompressed block is reached. - * Second, it allows us to stop early if the overread amount gets so large (more - * than sizeof bitbuf) that it can only be caused by a real overread. (The - * second part is arguably unneeded, since libdeflate is buffer-based; given - * infinite zeroes, it will eventually either completely fill the output buffer - * or return an error. However, we do it to be slightly more friendly to the - * not-recommended use case of decompressing with an unknown output size.) - */ -#define REFILL_BITS() \ -do { \ - if (UNALIGNED_ACCESS_IS_FAST && \ - likely(in_end - in_next >= sizeof(bitbuf_t))) { \ - REFILL_BITS_BRANCHLESS(); \ - } else { \ - while ((u8)bitsleft < CONSUMABLE_NBITS) { \ - if (likely(in_next != in_end)) { \ - bitbuf |= (bitbuf_t)*in_next++ << \ - (u8)bitsleft; \ - } else { \ - overread_count++; \ - SAFETY_CHECK(overread_count <= \ - sizeof(bitbuf_t)); \ - } \ - bitsleft += 8; \ - } \ - } \ -} while (0) - -/* - * REFILL_BITS_IN_FASTLOOP() is like REFILL_BITS(), but it doesn't check for the - * end of the input. It can only be used in the fastloop. - */ -#define REFILL_BITS_IN_FASTLOOP() \ -do { \ - STATIC_ASSERT(UNALIGNED_ACCESS_IS_FAST || \ - FASTLOOP_PRELOADABLE_NBITS == CONSUMABLE_NBITS); \ - if (UNALIGNED_ACCESS_IS_FAST) { \ - REFILL_BITS_BRANCHLESS(); \ - } else { \ - while ((u8)bitsleft < CONSUMABLE_NBITS) { \ - bitbuf |= (bitbuf_t)*in_next++ << (u8)bitsleft; \ - bitsleft += 8; \ - } \ - } \ -} while (0) - -/* - * This is the worst-case maximum number of output bytes that are written to - * during each iteration of the fastloop. The worst case is 2 literals, then a - * match of length DEFLATE_MAX_MATCH_LEN. Additionally, some slack space must - * be included for the intentional overrun in the match copy implementation. - */ -#define FASTLOOP_MAX_BYTES_WRITTEN \ - (2 + DEFLATE_MAX_MATCH_LEN + (5 * WORDBYTES) - 1) - -/* - * This is the worst-case maximum number of input bytes that are read during - * each iteration of the fastloop. To get this value, we first compute the - * greatest number of bits that can be refilled during a loop iteration. The - * refill at the beginning can add at most MAX_BITSLEFT, and the amount that can - * be refilled later is no more than the maximum amount that can be consumed by - * 2 literals that don't need a subtable, then a match. We convert this value - * to bytes, rounding up; this gives the maximum number of bytes that 'in_next' - * can be advanced. Finally, we add sizeof(bitbuf_t) to account for - * REFILL_BITS_BRANCHLESS() reading a word past 'in_next'. - */ -#define FASTLOOP_MAX_BYTES_READ \ - (DIV_ROUND_UP(MAX_BITSLEFT + (2 * LITLEN_TABLEBITS) + \ - LENGTH_MAXBITS + OFFSET_MAXBITS, 8) + \ - sizeof(bitbuf_t)) - -/***************************************************************************** - * Huffman decoding * - *****************************************************************************/ - -/* - * The fastest way to decode Huffman-encoded data is basically to use a decode - * table that maps the next TABLEBITS bits of data to their symbol. Each entry - * decode_table[i] maps to the symbol whose codeword is a prefix of 'i'. A - * symbol with codeword length 'n' has '2**(TABLEBITS-n)' entries in the table. - * - * Ideally, TABLEBITS and the maximum codeword length would be the same; some - * compression formats are designed with this goal in mind. Unfortunately, in - * DEFLATE, the maximum litlen and offset codeword lengths are 15 bits, which is - * too large for a practical TABLEBITS. It's not *that* much larger, though, so - * the workaround is to use a single level of subtables. In the main table, - * entries for prefixes of codewords longer than TABLEBITS contain a "pointer" - * to the appropriate subtable along with the number of bits it is indexed with. - * - * The most efficient way to allocate subtables is to allocate them dynamically - * after the main table. The worst-case number of table entries needed, - * including subtables, is precomputable; see the ENOUGH constants below. - * - * A useful optimization is to store the codeword lengths in the decode table so - * that they don't have to be looked up by indexing a separate table that maps - * symbols to their codeword lengths. We basically do this; however, for the - * litlen and offset codes we also implement some DEFLATE-specific optimizations - * that build in the consideration of the "extra bits" and the - * literal/length/end-of-block division. For the exact decode table entry - * format we use, see the definitions of the *_decode_results[] arrays below. - */ - - -/* - * These are the TABLEBITS values we use for each of the DEFLATE Huffman codes, - * along with their corresponding ENOUGH values. - * - * For the precode, we use PRECODE_TABLEBITS == 7 since this is the maximum - * precode codeword length. This avoids ever needing subtables. - * - * For the litlen and offset codes, we cannot realistically avoid ever needing - * subtables, since litlen and offset codewords can be up to 15 bits. A higher - * TABLEBITS reduces the number of lookups that need a subtable, which increases - * performance; however, it increases memory usage and makes building the table - * take longer, which decreases performance. We choose values that work well in - * practice, making subtables rarely needed without making the tables too large. - * - * Our choice of OFFSET_TABLEBITS == 8 is a bit low; without any special - * considerations, 9 would fit the trade-off curve better. However, there is a - * performance benefit to using exactly 8 bits when it is a compile-time - * constant, as many CPUs can take the low byte more easily than the low 9 bits. - * - * zlib treats its equivalents of TABLEBITS as maximum values; whenever it - * builds a table, it caps the actual table_bits to the longest codeword. This - * makes sense in theory, as there's no need for the table to be any larger than - * needed to support the longest codeword. However, having the table bits be a - * compile-time constant is beneficial to the performance of the decode loop, so - * there is a trade-off. libdeflate currently uses the dynamic table_bits - * strategy for the litlen table only, due to its larger maximum size. - * PRECODE_TABLEBITS and OFFSET_TABLEBITS are smaller, so going dynamic there - * isn't as useful, and OFFSET_TABLEBITS=8 is useful as mentioned above. - * - * Each TABLEBITS value has a corresponding ENOUGH value that gives the - * worst-case maximum number of decode table entries, including the main table - * and all subtables. The ENOUGH value depends on three parameters: - * - * (1) the maximum number of symbols in the code (DEFLATE_NUM_*_SYMS) - * (2) the maximum number of main table bits (*_TABLEBITS) - * (3) the maximum allowed codeword length (DEFLATE_MAX_*_CODEWORD_LEN) - * - * The ENOUGH values were computed using the utility program 'enough' from zlib. - */ -#define PRECODE_TABLEBITS 7 -#define PRECODE_ENOUGH 128 /* enough 19 7 7 */ -#define LITLEN_TABLEBITS 11 -#define LITLEN_ENOUGH 2342 /* enough 288 11 15 */ -#define OFFSET_TABLEBITS 8 -#define OFFSET_ENOUGH 402 /* enough 32 8 15 */ - -/* - * make_decode_table_entry() creates a decode table entry for the given symbol - * by combining the static part 'decode_results[sym]' with the dynamic part - * 'len', which is the remaining codeword length (the codeword length for main - * table entries, or the codeword length minus TABLEBITS for subtable entries). - * - * In all cases, we add 'len' to each of the two low-order bytes to create the - * appropriately-formatted decode table entry. See the definitions of the - * *_decode_results[] arrays below, where the entry format is described. - */ -static forceinline u32 -make_decode_table_entry(const u32 decode_results[], u32 sym, u32 len) -{ - return decode_results[sym] + (len << 8) + len; -} - -/* - * Here is the format of our precode decode table entries. Bits not explicitly - * described contain zeroes: - * - * Bit 20-16: presym - * Bit 10-8: codeword length [not used] - * Bit 2-0: codeword length - * - * The precode decode table never has subtables, since we use - * PRECODE_TABLEBITS == DEFLATE_MAX_PRE_CODEWORD_LEN. - * - * precode_decode_results[] contains the static part of the entry for each - * symbol. make_decode_table_entry() produces the final entries. - */ -static const u32 precode_decode_results[] = { -#define ENTRY(presym) ((u32)presym << 16) - ENTRY(0) , ENTRY(1) , ENTRY(2) , ENTRY(3) , - ENTRY(4) , ENTRY(5) , ENTRY(6) , ENTRY(7) , - ENTRY(8) , ENTRY(9) , ENTRY(10) , ENTRY(11) , - ENTRY(12) , ENTRY(13) , ENTRY(14) , ENTRY(15) , - ENTRY(16) , ENTRY(17) , ENTRY(18) , -#undef ENTRY -}; - -/* Litlen and offset decode table entry flags */ - -/* Indicates a literal entry in the litlen decode table */ -#define HUFFDEC_LITERAL 0x80000000 - -/* Indicates that HUFFDEC_SUBTABLE_POINTER or HUFFDEC_END_OF_BLOCK is set */ -#define HUFFDEC_EXCEPTIONAL 0x00008000 - -/* Indicates a subtable pointer entry in the litlen or offset decode table */ -#define HUFFDEC_SUBTABLE_POINTER 0x00004000 - -/* Indicates an end-of-block entry in the litlen decode table */ -#define HUFFDEC_END_OF_BLOCK 0x00002000 - -/* Maximum number of bits that can be consumed by decoding a match length */ -#define LENGTH_MAXBITS (DEFLATE_MAX_LITLEN_CODEWORD_LEN + \ - DEFLATE_MAX_EXTRA_LENGTH_BITS) -#define LENGTH_MAXFASTBITS (LITLEN_TABLEBITS /* no subtable needed */ + \ - DEFLATE_MAX_EXTRA_LENGTH_BITS) - -/* - * Here is the format of our litlen decode table entries. Bits not explicitly - * described contain zeroes: - * - * Literals: - * Bit 31: 1 (HUFFDEC_LITERAL) - * Bit 23-16: literal value - * Bit 15: 0 (!HUFFDEC_EXCEPTIONAL) - * Bit 14: 0 (!HUFFDEC_SUBTABLE_POINTER) - * Bit 13: 0 (!HUFFDEC_END_OF_BLOCK) - * Bit 11-8: remaining codeword length [not used] - * Bit 3-0: remaining codeword length - * Lengths: - * Bit 31: 0 (!HUFFDEC_LITERAL) - * Bit 24-16: length base value - * Bit 15: 0 (!HUFFDEC_EXCEPTIONAL) - * Bit 14: 0 (!HUFFDEC_SUBTABLE_POINTER) - * Bit 13: 0 (!HUFFDEC_END_OF_BLOCK) - * Bit 11-8: remaining codeword length - * Bit 4-0: remaining codeword length + number of extra bits - * End of block: - * Bit 31: 0 (!HUFFDEC_LITERAL) - * Bit 15: 1 (HUFFDEC_EXCEPTIONAL) - * Bit 14: 0 (!HUFFDEC_SUBTABLE_POINTER) - * Bit 13: 1 (HUFFDEC_END_OF_BLOCK) - * Bit 11-8: remaining codeword length [not used] - * Bit 3-0: remaining codeword length - * Subtable pointer: - * Bit 31: 0 (!HUFFDEC_LITERAL) - * Bit 30-16: index of start of subtable - * Bit 15: 1 (HUFFDEC_EXCEPTIONAL) - * Bit 14: 1 (HUFFDEC_SUBTABLE_POINTER) - * Bit 13: 0 (!HUFFDEC_END_OF_BLOCK) - * Bit 11-8: number of subtable bits - * Bit 3-0: number of main table bits - * - * This format has several desirable properties: - * - * - The codeword length, length slot base, and number of extra length bits - * are all built in. This eliminates the need to separately look up this - * information by indexing separate arrays by symbol or length slot. - * - * - The HUFFDEC_* flags enable easily distinguishing between the different - * types of entries. The HUFFDEC_LITERAL flag enables a fast path for - * literals; the high bit is used for this, as some CPUs can test the - * high bit more easily than other bits. The HUFFDEC_EXCEPTIONAL flag - * makes it possible to detect the two unlikely cases (subtable pointer - * and end of block) in a single bit flag test. - * - * - The low byte is the number of bits that need to be removed from the - * bitstream; this makes this value easily accessible, and it enables the - * micro-optimization of doing 'bitsleft -= entry' instead of - * 'bitsleft -= (u8)entry'. It also includes the number of extra bits, - * so they don't need to be removed separately. - * - * - The flags in bits 15-13 are arranged to be 0 when the - * "remaining codeword length" in bits 11-8 is needed, making this value - * fairly easily accessible as well via a shift and downcast. - * - * - Similarly, bits 13-12 are 0 when the "subtable bits" in bits 11-8 are - * needed, making it possible to extract this value with '& 0x3F' rather - * than '& 0xF'. This value is only used as a shift amount, so this can - * save an 'and' instruction as the masking by 0x3F happens implicitly. - * - * litlen_decode_results[] contains the static part of the entry for each - * symbol. make_decode_table_entry() produces the final entries. - */ -static const u32 litlen_decode_results[] = { - - /* Literals */ -#define ENTRY(literal) (HUFFDEC_LITERAL | ((u32)literal << 16)) - ENTRY(0) , ENTRY(1) , ENTRY(2) , ENTRY(3) , - ENTRY(4) , ENTRY(5) , ENTRY(6) , ENTRY(7) , - ENTRY(8) , ENTRY(9) , ENTRY(10) , ENTRY(11) , - ENTRY(12) , ENTRY(13) , ENTRY(14) , ENTRY(15) , - ENTRY(16) , ENTRY(17) , ENTRY(18) , ENTRY(19) , - ENTRY(20) , ENTRY(21) , ENTRY(22) , ENTRY(23) , - ENTRY(24) , ENTRY(25) , ENTRY(26) , ENTRY(27) , - ENTRY(28) , ENTRY(29) , ENTRY(30) , ENTRY(31) , - ENTRY(32) , ENTRY(33) , ENTRY(34) , ENTRY(35) , - ENTRY(36) , ENTRY(37) , ENTRY(38) , ENTRY(39) , - ENTRY(40) , ENTRY(41) , ENTRY(42) , ENTRY(43) , - ENTRY(44) , ENTRY(45) , ENTRY(46) , ENTRY(47) , - ENTRY(48) , ENTRY(49) , ENTRY(50) , ENTRY(51) , - ENTRY(52) , ENTRY(53) , ENTRY(54) , ENTRY(55) , - ENTRY(56) , ENTRY(57) , ENTRY(58) , ENTRY(59) , - ENTRY(60) , ENTRY(61) , ENTRY(62) , ENTRY(63) , - ENTRY(64) , ENTRY(65) , ENTRY(66) , ENTRY(67) , - ENTRY(68) , ENTRY(69) , ENTRY(70) , ENTRY(71) , - ENTRY(72) , ENTRY(73) , ENTRY(74) , ENTRY(75) , - ENTRY(76) , ENTRY(77) , ENTRY(78) , ENTRY(79) , - ENTRY(80) , ENTRY(81) , ENTRY(82) , ENTRY(83) , - ENTRY(84) , ENTRY(85) , ENTRY(86) , ENTRY(87) , - ENTRY(88) , ENTRY(89) , ENTRY(90) , ENTRY(91) , - ENTRY(92) , ENTRY(93) , ENTRY(94) , ENTRY(95) , - ENTRY(96) , ENTRY(97) , ENTRY(98) , ENTRY(99) , - ENTRY(100) , ENTRY(101) , ENTRY(102) , ENTRY(103) , - ENTRY(104) , ENTRY(105) , ENTRY(106) , ENTRY(107) , - ENTRY(108) , ENTRY(109) , ENTRY(110) , ENTRY(111) , - ENTRY(112) , ENTRY(113) , ENTRY(114) , ENTRY(115) , - ENTRY(116) , ENTRY(117) , ENTRY(118) , ENTRY(119) , - ENTRY(120) , ENTRY(121) , ENTRY(122) , ENTRY(123) , - ENTRY(124) , ENTRY(125) , ENTRY(126) , ENTRY(127) , - ENTRY(128) , ENTRY(129) , ENTRY(130) , ENTRY(131) , - ENTRY(132) , ENTRY(133) , ENTRY(134) , ENTRY(135) , - ENTRY(136) , ENTRY(137) , ENTRY(138) , ENTRY(139) , - ENTRY(140) , ENTRY(141) , ENTRY(142) , ENTRY(143) , - ENTRY(144) , ENTRY(145) , ENTRY(146) , ENTRY(147) , - ENTRY(148) , ENTRY(149) , ENTRY(150) , ENTRY(151) , - ENTRY(152) , ENTRY(153) , ENTRY(154) , ENTRY(155) , - ENTRY(156) , ENTRY(157) , ENTRY(158) , ENTRY(159) , - ENTRY(160) , ENTRY(161) , ENTRY(162) , ENTRY(163) , - ENTRY(164) , ENTRY(165) , ENTRY(166) , ENTRY(167) , - ENTRY(168) , ENTRY(169) , ENTRY(170) , ENTRY(171) , - ENTRY(172) , ENTRY(173) , ENTRY(174) , ENTRY(175) , - ENTRY(176) , ENTRY(177) , ENTRY(178) , ENTRY(179) , - ENTRY(180) , ENTRY(181) , ENTRY(182) , ENTRY(183) , - ENTRY(184) , ENTRY(185) , ENTRY(186) , ENTRY(187) , - ENTRY(188) , ENTRY(189) , ENTRY(190) , ENTRY(191) , - ENTRY(192) , ENTRY(193) , ENTRY(194) , ENTRY(195) , - ENTRY(196) , ENTRY(197) , ENTRY(198) , ENTRY(199) , - ENTRY(200) , ENTRY(201) , ENTRY(202) , ENTRY(203) , - ENTRY(204) , ENTRY(205) , ENTRY(206) , ENTRY(207) , - ENTRY(208) , ENTRY(209) , ENTRY(210) , ENTRY(211) , - ENTRY(212) , ENTRY(213) , ENTRY(214) , ENTRY(215) , - ENTRY(216) , ENTRY(217) , ENTRY(218) , ENTRY(219) , - ENTRY(220) , ENTRY(221) , ENTRY(222) , ENTRY(223) , - ENTRY(224) , ENTRY(225) , ENTRY(226) , ENTRY(227) , - ENTRY(228) , ENTRY(229) , ENTRY(230) , ENTRY(231) , - ENTRY(232) , ENTRY(233) , ENTRY(234) , ENTRY(235) , - ENTRY(236) , ENTRY(237) , ENTRY(238) , ENTRY(239) , - ENTRY(240) , ENTRY(241) , ENTRY(242) , ENTRY(243) , - ENTRY(244) , ENTRY(245) , ENTRY(246) , ENTRY(247) , - ENTRY(248) , ENTRY(249) , ENTRY(250) , ENTRY(251) , - ENTRY(252) , ENTRY(253) , ENTRY(254) , ENTRY(255) , -#undef ENTRY - - /* End of block */ - HUFFDEC_EXCEPTIONAL | HUFFDEC_END_OF_BLOCK, - - /* Lengths */ -#define ENTRY(length_base, num_extra_bits) \ - (((u32)(length_base) << 16) | (num_extra_bits)) - ENTRY(3 , 0) , ENTRY(4 , 0) , ENTRY(5 , 0) , ENTRY(6 , 0), - ENTRY(7 , 0) , ENTRY(8 , 0) , ENTRY(9 , 0) , ENTRY(10 , 0), - ENTRY(11 , 1) , ENTRY(13 , 1) , ENTRY(15 , 1) , ENTRY(17 , 1), - ENTRY(19 , 2) , ENTRY(23 , 2) , ENTRY(27 , 2) , ENTRY(31 , 2), - ENTRY(35 , 3) , ENTRY(43 , 3) , ENTRY(51 , 3) , ENTRY(59 , 3), - ENTRY(67 , 4) , ENTRY(83 , 4) , ENTRY(99 , 4) , ENTRY(115, 4), - ENTRY(131, 5) , ENTRY(163, 5) , ENTRY(195, 5) , ENTRY(227, 5), - ENTRY(258, 0) , ENTRY(258, 0) , ENTRY(258, 0) , -#undef ENTRY -}; - -/* Maximum number of bits that can be consumed by decoding a match offset */ -#define OFFSET_MAXBITS (DEFLATE_MAX_OFFSET_CODEWORD_LEN + \ - DEFLATE_MAX_EXTRA_OFFSET_BITS) -#define OFFSET_MAXFASTBITS (OFFSET_TABLEBITS /* no subtable needed */ + \ - DEFLATE_MAX_EXTRA_OFFSET_BITS) - -/* - * Here is the format of our offset decode table entries. Bits not explicitly - * described contain zeroes: - * - * Offsets: - * Bit 31-16: offset base value - * Bit 15: 0 (!HUFFDEC_EXCEPTIONAL) - * Bit 14: 0 (!HUFFDEC_SUBTABLE_POINTER) - * Bit 11-8: remaining codeword length - * Bit 4-0: remaining codeword length + number of extra bits - * Subtable pointer: - * Bit 31-16: index of start of subtable - * Bit 15: 1 (HUFFDEC_EXCEPTIONAL) - * Bit 14: 1 (HUFFDEC_SUBTABLE_POINTER) - * Bit 11-8: number of subtable bits - * Bit 3-0: number of main table bits - * - * These work the same way as the length entries and subtable pointer entries in - * the litlen decode table; see litlen_decode_results[] above. - */ -static const u32 offset_decode_results[] = { -#define ENTRY(offset_base, num_extra_bits) \ - (((u32)(offset_base) << 16) | (num_extra_bits)) - ENTRY(1 , 0) , ENTRY(2 , 0) , ENTRY(3 , 0) , ENTRY(4 , 0) , - ENTRY(5 , 1) , ENTRY(7 , 1) , ENTRY(9 , 2) , ENTRY(13 , 2) , - ENTRY(17 , 3) , ENTRY(25 , 3) , ENTRY(33 , 4) , ENTRY(49 , 4) , - ENTRY(65 , 5) , ENTRY(97 , 5) , ENTRY(129 , 6) , ENTRY(193 , 6) , - ENTRY(257 , 7) , ENTRY(385 , 7) , ENTRY(513 , 8) , ENTRY(769 , 8) , - ENTRY(1025 , 9) , ENTRY(1537 , 9) , ENTRY(2049 , 10) , ENTRY(3073 , 10) , - ENTRY(4097 , 11) , ENTRY(6145 , 11) , ENTRY(8193 , 12) , ENTRY(12289 , 12) , - ENTRY(16385 , 13) , ENTRY(24577 , 13) , ENTRY(24577 , 13) , ENTRY(24577 , 13) , -#undef ENTRY -}; - -/* - * The main DEFLATE decompressor structure. Since libdeflate only supports - * full-buffer decompression, this structure doesn't store the entire - * decompression state, most of which is in stack variables. Instead, this - * struct just contains the decode tables and some temporary arrays used for - * building them, as these are too large to comfortably allocate on the stack. - * - * Storing the decode tables in the decompressor struct also allows the decode - * tables for the static codes to be reused whenever two static Huffman blocks - * are decoded without an intervening dynamic block, even across streams. - */ -struct libdeflate_decompressor { - - /* - * The arrays aren't all needed at the same time. 'precode_lens' and - * 'precode_decode_table' are unneeded after 'lens' has been filled. - * Furthermore, 'lens' need not be retained after building the litlen - * and offset decode tables. In fact, 'lens' can be in union with - * 'litlen_decode_table' provided that 'offset_decode_table' is separate - * and is built first. - */ - - union { - u8 precode_lens[DEFLATE_NUM_PRECODE_SYMS]; - - struct { - u8 lens[DEFLATE_NUM_LITLEN_SYMS + - DEFLATE_NUM_OFFSET_SYMS + - DEFLATE_MAX_LENS_OVERRUN]; - - u32 precode_decode_table[PRECODE_ENOUGH]; - } l; - - u32 litlen_decode_table[LITLEN_ENOUGH]; - } u; - - u32 offset_decode_table[OFFSET_ENOUGH]; - - /* used only during build_decode_table() */ - u16 sorted_syms[DEFLATE_MAX_NUM_SYMS]; - - bool static_codes_loaded; - unsigned litlen_tablebits; - - /* The free() function for this struct, chosen at allocation time */ - free_func_t free_func; -}; - -/* - * Build a table for fast decoding of symbols from a Huffman code. As input, - * this function takes the codeword length of each symbol which may be used in - * the code. As output, it produces a decode table for the canonical Huffman - * code described by the codeword lengths. The decode table is built with the - * assumption that it will be indexed with "bit-reversed" codewords, where the - * low-order bit is the first bit of the codeword. This format is used for all - * Huffman codes in DEFLATE. - * - * @decode_table - * The array in which the decode table will be generated. This array must - * have sufficient length; see the definition of the ENOUGH numbers. - * @lens - * An array which provides, for each symbol, the length of the - * corresponding codeword in bits, or 0 if the symbol is unused. This may - * alias @decode_table, since nothing is written to @decode_table until all - * @lens have been consumed. All codeword lengths are assumed to be <= - * @max_codeword_len but are otherwise considered untrusted. If they do - * not form a valid Huffman code, then the decode table is not built and - * %false is returned. - * @num_syms - * The number of symbols in the code, including all unused symbols. - * @decode_results - * An array which gives the incomplete decode result for each symbol. The - * needed values in this array will be combined with codeword lengths to - * make the final decode table entries using make_decode_table_entry(). - * @table_bits - * The log base-2 of the number of main table entries to use. - * If @table_bits_ret != NULL, then @table_bits is treated as a maximum - * value and it will be decreased if a smaller table would be sufficient. - * @max_codeword_len - * The maximum allowed codeword length for this Huffman code. - * Must be <= DEFLATE_MAX_CODEWORD_LEN. - * @sorted_syms - * A temporary array of length @num_syms. - * @table_bits_ret - * If non-NULL, then the dynamic table_bits is enabled, and the actual - * table_bits value will be returned here. - * - * Returns %true if successful; %false if the codeword lengths do not form a - * valid Huffman code. - */ -static bool -build_decode_table(u32 decode_table[], - const u8 lens[], - const unsigned num_syms, - const u32 decode_results[], - unsigned table_bits, - unsigned max_codeword_len, - u16 *sorted_syms, - unsigned *table_bits_ret) -{ - unsigned len_counts[DEFLATE_MAX_CODEWORD_LEN + 1]; - unsigned offsets[DEFLATE_MAX_CODEWORD_LEN + 1]; - unsigned sym; /* current symbol */ - unsigned codeword; /* current codeword, bit-reversed */ - unsigned len; /* current codeword length in bits */ - unsigned count; /* num codewords remaining with this length */ - u32 codespace_used; /* codespace used out of '2^max_codeword_len' */ - unsigned cur_table_end; /* end index of current table */ - unsigned subtable_prefix; /* codeword prefix of current subtable */ - unsigned subtable_start; /* start index of current subtable */ - unsigned subtable_bits; /* log2 of current subtable length */ - - /* Count how many codewords have each length, including 0. */ - for (len = 0; len <= max_codeword_len; len++) - len_counts[len] = 0; - for (sym = 0; sym < num_syms; sym++) - len_counts[lens[sym]]++; - - /* - * Determine the actual maximum codeword length that was used, and - * decrease table_bits to it if allowed. - */ - while (max_codeword_len > 1 && len_counts[max_codeword_len] == 0) - max_codeword_len--; - if (table_bits_ret != NULL) { - table_bits = MIN(table_bits, max_codeword_len); - *table_bits_ret = table_bits; - } - - /* - * Sort the symbols primarily by increasing codeword length and - * secondarily by increasing symbol value; or equivalently by their - * codewords in lexicographic order, since a canonical code is assumed. - * - * For efficiency, also compute 'codespace_used' in the same pass over - * 'len_counts[]' used to build 'offsets[]' for sorting. - */ - - /* Ensure that 'codespace_used' cannot overflow. */ - STATIC_ASSERT(sizeof(codespace_used) == 4); - STATIC_ASSERT(UINT32_MAX / (1U << (DEFLATE_MAX_CODEWORD_LEN - 1)) >= - DEFLATE_MAX_NUM_SYMS); - - offsets[0] = 0; - offsets[1] = len_counts[0]; - codespace_used = 0; - for (len = 1; len < max_codeword_len; len++) { - offsets[len + 1] = offsets[len] + len_counts[len]; - codespace_used = (codespace_used << 1) + len_counts[len]; - } - codespace_used = (codespace_used << 1) + len_counts[len]; - - for (sym = 0; sym < num_syms; sym++) - sorted_syms[offsets[lens[sym]]++] = sym; - - sorted_syms += offsets[0]; /* Skip unused symbols */ - - /* lens[] is done being used, so we can write to decode_table[] now. */ - - /* - * Check whether the lengths form a complete code (exactly fills the - * codespace), an incomplete code (doesn't fill the codespace), or an - * overfull code (overflows the codespace). A codeword of length 'n' - * uses proportion '1/(2^n)' of the codespace. An overfull code is - * nonsensical, so is considered invalid. An incomplete code is - * considered valid only in two specific cases; see below. - */ - - /* overfull code? */ - if (unlikely(codespace_used > (1U << max_codeword_len))) - return false; - - /* incomplete code? */ - if (unlikely(codespace_used < (1U << max_codeword_len))) { - u32 entry; - unsigned i; - - /* - * The DEFLATE RFC explicitly allows the offset code to be - * incomplete in two cases: a code containing just 1 codeword, - * if that codeword has length 1; and a code containing no - * codewords. Note: the list of offset codeword lengths is - * always nonempty, but lengths of 0 don't count as codewords. - * - * The RFC doesn't say whether the same cases are allowed for - * the litlen and pre codes. It's actually impossible for no - * symbols to be used from these codes; however, it's - * technically possible for only one symbol to be used. zlib - * allows 1 codeword for the litlen code, but not the precode. - * The RFC also doesn't say whether, when there is 1 codeword, - * that codeword is '0' or '1'. zlib uses '0'. - * - * We accept what zlib accepts, plus a bit more. First, we - * don't treat the precode more strictly than the litlen and - * offset codes. There's no convincing reason to add a special - * case for the precode here. - * - * Second, we just map each allowed incompete code to a complete - * code with only real symbols. To do this, we choose a symbol, - * either the used symbol (for codes with 1 codeword) or an - * arbitrary symbol (for empty codes), and give it both - * codewords '0' and '1'. zlib instead uses a special ERROR - * symbol in the part of the codespace the code doesn't use. - * However, having an ERROR symbol reduces the performance of - * the Huffman decoder, for no real benefit. Our approach also - * avoids having to decide whether '0' or '1' is correct. - * - * Like zlib, we still reject all incomplete codes that contain - * more than 1 codeword or a codeword length greater than 1. - */ - if (codespace_used == 0) { - sym = 0; /* arbitrary */ - } else { - if (codespace_used != (1U << (max_codeword_len - 1)) || - len_counts[1] != 1) - return false; - sym = sorted_syms[0]; - } - entry = make_decode_table_entry(decode_results, sym, 1); - for (i = 0; i < (1U << table_bits); i++) - decode_table[i] = entry; - return true; - } - - /* - * The lengths form a complete code. Now, enumerate the codewords in - * lexicographic order and fill the decode table entries for each one. - * - * First, process all codewords with len <= table_bits. Each one gets - * '2^(table_bits-len)' direct entries in the table. - * - * Since DEFLATE uses bit-reversed codewords, these entries aren't - * consecutive but rather are spaced '2^len' entries apart. This makes - * filling them naively somewhat awkward and inefficient, since strided - * stores are less cache-friendly and preclude the use of word or - * vector-at-a-time stores to fill multiple entries per instruction. - * - * To optimize this, we incrementally double the table size. When - * processing codewords with length 'len', the table is treated as - * having only '2^len' entries, so each codeword uses just one entry. - * Then, each time 'len' is incremented, the table size is doubled and - * the first half is copied to the second half. This significantly - * improves performance over naively doing strided stores. - * - * Note that some entries copied for each table doubling may not have - * been initialized yet, but it doesn't matter since they're guaranteed - * to be initialized later (because the Huffman code is complete). - */ - codeword = 0; - len = 1; - while ((count = len_counts[len]) == 0) - len++; - cur_table_end = 1U << len; - while (len <= table_bits) { - /* Process all 'count' codewords with length 'len' bits. */ - do { - unsigned bit; - - /* Fill the first entry for the current codeword. */ - decode_table[codeword] = - make_decode_table_entry(decode_results, - *sorted_syms++, len); - - if (codeword == cur_table_end - 1) { - /* Last codeword (all 1's) */ - for (; len < table_bits; len++) { - memcpy(&decode_table[cur_table_end], - decode_table, - cur_table_end * - sizeof(decode_table[0])); - cur_table_end <<= 1; - } - return true; - } - /* - * To advance to the lexicographically next codeword in - * the canonical code, the codeword must be incremented, - * then 0's must be appended to the codeword as needed - * to match the next codeword's length. - * - * Since the codeword is bit-reversed, appending 0's is - * a no-op. However, incrementing it is nontrivial. To - * do so efficiently, use the 'bsr' instruction to find - * the last (highest order) 0 bit in the codeword, set - * it, and clear any later (higher order) 1 bits. But - * 'bsr' actually finds the highest order 1 bit, so to - * use it first flip all bits in the codeword by XOR'ing - * it with (1U << len) - 1 == cur_table_end - 1. - */ - bit = 1U << bsr32(codeword ^ (cur_table_end - 1)); - codeword &= bit - 1; - codeword |= bit; - } while (--count); - - /* Advance to the next codeword length. */ - do { - if (++len <= table_bits) { - memcpy(&decode_table[cur_table_end], - decode_table, - cur_table_end * sizeof(decode_table[0])); - cur_table_end <<= 1; - } - } while ((count = len_counts[len]) == 0); - } - - /* Process codewords with len > table_bits. These require subtables. */ - cur_table_end = 1U << table_bits; - subtable_prefix = -1; - subtable_start = 0; - for (;;) { - u32 entry; - unsigned i; - unsigned stride; - unsigned bit; - - /* - * Start a new subtable if the first 'table_bits' bits of the - * codeword don't match the prefix of the current subtable. - */ - if ((codeword & ((1U << table_bits) - 1)) != subtable_prefix) { - subtable_prefix = (codeword & ((1U << table_bits) - 1)); - subtable_start = cur_table_end; - /* - * Calculate the subtable length. If the codeword has - * length 'table_bits + n', then the subtable needs - * '2^n' entries. But it may need more; if fewer than - * '2^n' codewords of length 'table_bits + n' remain, - * then the length will need to be incremented to bring - * in longer codewords until the subtable can be - * completely filled. Note that because the Huffman - * code is complete, it will always be possible to fill - * the subtable eventually. - */ - subtable_bits = len - table_bits; - codespace_used = count; - while (codespace_used < (1U << subtable_bits)) { - subtable_bits++; - codespace_used = (codespace_used << 1) + - len_counts[table_bits + subtable_bits]; - } - cur_table_end = subtable_start + (1U << subtable_bits); - - /* - * Create the entry that points from the main table to - * the subtable. - */ - decode_table[subtable_prefix] = - ((u32)subtable_start << 16) | - HUFFDEC_EXCEPTIONAL | - HUFFDEC_SUBTABLE_POINTER | - (subtable_bits << 8) | table_bits; - } - - /* Fill the subtable entries for the current codeword. */ - entry = make_decode_table_entry(decode_results, *sorted_syms++, - len - table_bits); - i = subtable_start + (codeword >> table_bits); - stride = 1U << (len - table_bits); - do { - decode_table[i] = entry; - i += stride; - } while (i < cur_table_end); - - /* Advance to the next codeword. */ - if (codeword == (1U << len) - 1) /* last codeword (all 1's)? */ - return true; - bit = 1U << bsr32(codeword ^ ((1U << len) - 1)); - codeword &= bit - 1; - codeword |= bit; - count--; - while (count == 0) - count = len_counts[++len]; - } -} - -/* Build the decode table for the precode. */ -static bool -build_precode_decode_table(struct libdeflate_decompressor *d) -{ - /* When you change TABLEBITS, you must change ENOUGH, and vice versa! */ - STATIC_ASSERT(PRECODE_TABLEBITS == 7 && PRECODE_ENOUGH == 128); - - STATIC_ASSERT(ARRAY_LEN(precode_decode_results) == - DEFLATE_NUM_PRECODE_SYMS); - - return build_decode_table(d->u.l.precode_decode_table, - d->u.precode_lens, - DEFLATE_NUM_PRECODE_SYMS, - precode_decode_results, - PRECODE_TABLEBITS, - DEFLATE_MAX_PRE_CODEWORD_LEN, - d->sorted_syms, - NULL); -} - -/* Build the decode table for the literal/length code. */ -static bool -build_litlen_decode_table(struct libdeflate_decompressor *d, - unsigned num_litlen_syms, unsigned num_offset_syms) -{ - /* When you change TABLEBITS, you must change ENOUGH, and vice versa! */ - STATIC_ASSERT(LITLEN_TABLEBITS == 11 && LITLEN_ENOUGH == 2342); - - STATIC_ASSERT(ARRAY_LEN(litlen_decode_results) == - DEFLATE_NUM_LITLEN_SYMS); - - return build_decode_table(d->u.litlen_decode_table, - d->u.l.lens, - num_litlen_syms, - litlen_decode_results, - LITLEN_TABLEBITS, - DEFLATE_MAX_LITLEN_CODEWORD_LEN, - d->sorted_syms, - &d->litlen_tablebits); -} - -/* Build the decode table for the offset code. */ -static bool -build_offset_decode_table(struct libdeflate_decompressor *d, - unsigned num_litlen_syms, unsigned num_offset_syms) -{ - /* When you change TABLEBITS, you must change ENOUGH, and vice versa! */ - STATIC_ASSERT(OFFSET_TABLEBITS == 8 && OFFSET_ENOUGH == 402); - - STATIC_ASSERT(ARRAY_LEN(offset_decode_results) == - DEFLATE_NUM_OFFSET_SYMS); - - return build_decode_table(d->offset_decode_table, - d->u.l.lens + num_litlen_syms, - num_offset_syms, - offset_decode_results, - OFFSET_TABLEBITS, - DEFLATE_MAX_OFFSET_CODEWORD_LEN, - d->sorted_syms, - NULL); -} - -/***************************************************************************** - * Main decompression routine - *****************************************************************************/ - -typedef enum libdeflate_result (*decompress_func_t) - (struct libdeflate_decompressor * restrict d, - const void * restrict in, size_t in_nbytes, - void * restrict out, size_t out_nbytes_avail, - size_t *actual_in_nbytes_ret, size_t *actual_out_nbytes_ret); - -#define FUNCNAME deflate_decompress_default -#undef ATTRIBUTES -#undef EXTRACT_VARBITS -#undef EXTRACT_VARBITS8 -#include "decompress_template.h" - -/* Include architecture-specific implementation(s) if available. */ -#undef DEFAULT_IMPL -#undef arch_select_decompress_func -#if defined(ARCH_X86_32) || defined(ARCH_X86_64) -# include "x86/decompress_impl.h" -#endif - -#ifndef DEFAULT_IMPL -# define DEFAULT_IMPL deflate_decompress_default -#endif - -#ifdef arch_select_decompress_func -static enum libdeflate_result -dispatch_decomp(struct libdeflate_decompressor * restrict d, - const void * restrict in, size_t in_nbytes, - void * restrict out, size_t out_nbytes_avail, - size_t *actual_in_nbytes_ret, size_t *actual_out_nbytes_ret); - -static volatile decompress_func_t decompress_impl = dispatch_decomp; - -/* Choose the best implementation at runtime. */ -static enum libdeflate_result -dispatch_decomp(struct libdeflate_decompressor * restrict d, - const void * restrict in, size_t in_nbytes, - void * restrict out, size_t out_nbytes_avail, - size_t *actual_in_nbytes_ret, size_t *actual_out_nbytes_ret) -{ - decompress_func_t f = arch_select_decompress_func(); - - if (f == NULL) - f = DEFAULT_IMPL; - - decompress_impl = f; - return f(d, in, in_nbytes, out, out_nbytes_avail, - actual_in_nbytes_ret, actual_out_nbytes_ret); -} -#else -/* The best implementation is statically known, so call it directly. */ -# define decompress_impl DEFAULT_IMPL -#endif - -/* - * This is the main DEFLATE decompression routine. See libdeflate.h for the - * documentation. - * - * Note that the real code is in decompress_template.h. The part here just - * handles calling the appropriate implementation depending on the CPU features - * at runtime. - */ -LIBDEFLATEAPI enum libdeflate_result -libdeflate_deflate_decompress_ex(struct libdeflate_decompressor *d, - const void *in, size_t in_nbytes, - void *out, size_t out_nbytes_avail, - size_t *actual_in_nbytes_ret, - size_t *actual_out_nbytes_ret) -{ - return decompress_impl(d, in, in_nbytes, out, out_nbytes_avail, - actual_in_nbytes_ret, actual_out_nbytes_ret); -} - -LIBDEFLATEAPI enum libdeflate_result -libdeflate_deflate_decompress(struct libdeflate_decompressor *d, - const void *in, size_t in_nbytes, - void *out, size_t out_nbytes_avail, - size_t *actual_out_nbytes_ret) -{ - return libdeflate_deflate_decompress_ex(d, in, in_nbytes, - out, out_nbytes_avail, - NULL, actual_out_nbytes_ret); -} - -LIBDEFLATEAPI struct libdeflate_decompressor * -libdeflate_alloc_decompressor_ex(const struct libdeflate_options *options) -{ - struct libdeflate_decompressor *d; - - /* - * Note: if more fields are added to libdeflate_options, this code will - * need to be updated to support both the old and new structs. - */ - if (options->sizeof_options != sizeof(*options)) - return NULL; - - d = (options->malloc_func ? options->malloc_func : - libdeflate_default_malloc_func)(sizeof(*d)); - if (d == NULL) - return NULL; - /* - * Note that only certain parts of the decompressor actually must be - * initialized here: - * - * - 'static_codes_loaded' must be initialized to false. - * - * - The first half of the main portion of each decode table must be - * initialized to any value, to avoid reading from uninitialized - * memory during table expansion in build_decode_table(). (Although, - * this is really just to avoid warnings with dynamic tools like - * valgrind, since build_decode_table() is guaranteed to initialize - * all entries eventually anyway.) - * - * - 'free_func' must be set. - * - * But for simplicity, we currently just zero the whole decompressor. - */ - memset(d, 0, sizeof(*d)); - d->free_func = options->free_func ? - options->free_func : libdeflate_default_free_func; - return d; -} - -LIBDEFLATEAPI struct libdeflate_decompressor * -libdeflate_alloc_decompressor(void) -{ - static const struct libdeflate_options defaults = { - .sizeof_options = sizeof(defaults), - }; - return libdeflate_alloc_decompressor_ex(&defaults); -} - -LIBDEFLATEAPI void -libdeflate_free_decompressor(struct libdeflate_decompressor *d) -{ - if (d) - d->free_func(d); -} diff --git a/vendor/libdeflate/lib/gzip_compress.c b/vendor/libdeflate/lib/gzip_compress.c deleted file mode 100644 index b7d5076e2e..0000000000 --- a/vendor/libdeflate/lib/gzip_compress.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * gzip_compress.c - compress with a gzip wrapper - * - * Copyright 2016 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "deflate_compress.h" -#include "gzip_constants.h" - -LIBDEFLATEAPI size_t -libdeflate_gzip_compress(struct libdeflate_compressor *c, - const void *in, size_t in_nbytes, - void *out, size_t out_nbytes_avail) -{ - u8 *out_next = out; - unsigned compression_level; - u8 xfl; - size_t deflate_size; - - if (out_nbytes_avail <= GZIP_MIN_OVERHEAD) - return 0; - - /* ID1 */ - *out_next++ = GZIP_ID1; - /* ID2 */ - *out_next++ = GZIP_ID2; - /* CM */ - *out_next++ = GZIP_CM_DEFLATE; - /* FLG */ - *out_next++ = 0; - /* MTIME */ - put_unaligned_le32(GZIP_MTIME_UNAVAILABLE, out_next); - out_next += 4; - /* XFL */ - xfl = 0; - compression_level = libdeflate_get_compression_level(c); - if (compression_level < 2) - xfl |= GZIP_XFL_FASTEST_COMPRESSION; - else if (compression_level >= 8) - xfl |= GZIP_XFL_SLOWEST_COMPRESSION; - *out_next++ = xfl; - /* OS */ - *out_next++ = GZIP_OS_UNKNOWN; /* OS */ - - /* Compressed data */ - deflate_size = libdeflate_deflate_compress(c, in, in_nbytes, out_next, - out_nbytes_avail - GZIP_MIN_OVERHEAD); - if (deflate_size == 0) - return 0; - out_next += deflate_size; - - /* CRC32 */ - put_unaligned_le32(libdeflate_crc32(0, in, in_nbytes), out_next); - out_next += 4; - - /* ISIZE */ - put_unaligned_le32((u32)in_nbytes, out_next); - out_next += 4; - - return out_next - (u8 *)out; -} - -LIBDEFLATEAPI size_t -libdeflate_gzip_compress_bound(struct libdeflate_compressor *c, - size_t in_nbytes) -{ - return GZIP_MIN_OVERHEAD + - libdeflate_deflate_compress_bound(c, in_nbytes); -} diff --git a/vendor/libdeflate/lib/gzip_constants.h b/vendor/libdeflate/lib/gzip_constants.h deleted file mode 100644 index 35e4728d86..0000000000 --- a/vendor/libdeflate/lib/gzip_constants.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * gzip_constants.h - constants for the gzip wrapper format - */ - -#ifndef LIB_GZIP_CONSTANTS_H -#define LIB_GZIP_CONSTANTS_H - -#define GZIP_MIN_HEADER_SIZE 10 -#define GZIP_FOOTER_SIZE 8 -#define GZIP_MIN_OVERHEAD (GZIP_MIN_HEADER_SIZE + GZIP_FOOTER_SIZE) - -#define GZIP_ID1 0x1F -#define GZIP_ID2 0x8B - -#define GZIP_CM_DEFLATE 8 - -#define GZIP_FTEXT 0x01 -#define GZIP_FHCRC 0x02 -#define GZIP_FEXTRA 0x04 -#define GZIP_FNAME 0x08 -#define GZIP_FCOMMENT 0x10 -#define GZIP_FRESERVED 0xE0 - -#define GZIP_MTIME_UNAVAILABLE 0 - -#define GZIP_XFL_SLOWEST_COMPRESSION 0x02 -#define GZIP_XFL_FASTEST_COMPRESSION 0x04 - -#define GZIP_OS_FAT 0 -#define GZIP_OS_AMIGA 1 -#define GZIP_OS_VMS 2 -#define GZIP_OS_UNIX 3 -#define GZIP_OS_VM_CMS 4 -#define GZIP_OS_ATARI_TOS 5 -#define GZIP_OS_HPFS 6 -#define GZIP_OS_MACINTOSH 7 -#define GZIP_OS_Z_SYSTEM 8 -#define GZIP_OS_CP_M 9 -#define GZIP_OS_TOPS_20 10 -#define GZIP_OS_NTFS 11 -#define GZIP_OS_QDOS 12 -#define GZIP_OS_RISCOS 13 -#define GZIP_OS_UNKNOWN 255 - -#endif /* LIB_GZIP_CONSTANTS_H */ diff --git a/vendor/libdeflate/lib/gzip_decompress.c b/vendor/libdeflate/lib/gzip_decompress.c deleted file mode 100644 index 76b74f6985..0000000000 --- a/vendor/libdeflate/lib/gzip_decompress.c +++ /dev/null @@ -1,144 +0,0 @@ -/* - * gzip_decompress.c - decompress with a gzip wrapper - * - * Copyright 2016 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "lib_common.h" -#include "gzip_constants.h" - -LIBDEFLATEAPI enum libdeflate_result -libdeflate_gzip_decompress_ex(struct libdeflate_decompressor *d, - const void *in, size_t in_nbytes, - void *out, size_t out_nbytes_avail, - size_t *actual_in_nbytes_ret, - size_t *actual_out_nbytes_ret) -{ - const u8 *in_next = in; - const u8 * const in_end = in_next + in_nbytes; - u8 flg; - size_t actual_in_nbytes; - size_t actual_out_nbytes; - enum libdeflate_result result; - - if (in_nbytes < GZIP_MIN_OVERHEAD) - return LIBDEFLATE_BAD_DATA; - - /* ID1 */ - if (*in_next++ != GZIP_ID1) - return LIBDEFLATE_BAD_DATA; - /* ID2 */ - if (*in_next++ != GZIP_ID2) - return LIBDEFLATE_BAD_DATA; - /* CM */ - if (*in_next++ != GZIP_CM_DEFLATE) - return LIBDEFLATE_BAD_DATA; - flg = *in_next++; - /* MTIME */ - in_next += 4; - /* XFL */ - in_next += 1; - /* OS */ - in_next += 1; - - if (flg & GZIP_FRESERVED) - return LIBDEFLATE_BAD_DATA; - - /* Extra field */ - if (flg & GZIP_FEXTRA) { - u16 xlen = get_unaligned_le16(in_next); - in_next += 2; - - if (in_end - in_next < (u32)xlen + GZIP_FOOTER_SIZE) - return LIBDEFLATE_BAD_DATA; - - in_next += xlen; - } - - /* Original file name (zero terminated) */ - if (flg & GZIP_FNAME) { - while (*in_next++ != 0 && in_next != in_end) - ; - if (in_end - in_next < GZIP_FOOTER_SIZE) - return LIBDEFLATE_BAD_DATA; - } - - /* File comment (zero terminated) */ - if (flg & GZIP_FCOMMENT) { - while (*in_next++ != 0 && in_next != in_end) - ; - if (in_end - in_next < GZIP_FOOTER_SIZE) - return LIBDEFLATE_BAD_DATA; - } - - /* CRC16 for gzip header */ - if (flg & GZIP_FHCRC) { - in_next += 2; - if (in_end - in_next < GZIP_FOOTER_SIZE) - return LIBDEFLATE_BAD_DATA; - } - - /* Compressed data */ - result = libdeflate_deflate_decompress_ex(d, in_next, - in_end - GZIP_FOOTER_SIZE - in_next, - out, out_nbytes_avail, - &actual_in_nbytes, - actual_out_nbytes_ret); - if (result != LIBDEFLATE_SUCCESS) - return result; - - if (actual_out_nbytes_ret) - actual_out_nbytes = *actual_out_nbytes_ret; - else - actual_out_nbytes = out_nbytes_avail; - - in_next += actual_in_nbytes; - - /* CRC32 */ - if (libdeflate_crc32(0, out, actual_out_nbytes) != - get_unaligned_le32(in_next)) - return LIBDEFLATE_BAD_DATA; - in_next += 4; - - /* ISIZE */ - if ((u32)actual_out_nbytes != get_unaligned_le32(in_next)) - return LIBDEFLATE_BAD_DATA; - in_next += 4; - - if (actual_in_nbytes_ret) - *actual_in_nbytes_ret = in_next - (u8 *)in; - - return LIBDEFLATE_SUCCESS; -} - -LIBDEFLATEAPI enum libdeflate_result -libdeflate_gzip_decompress(struct libdeflate_decompressor *d, - const void *in, size_t in_nbytes, - void *out, size_t out_nbytes_avail, - size_t *actual_out_nbytes_ret) -{ - return libdeflate_gzip_decompress_ex(d, in, in_nbytes, - out, out_nbytes_avail, - NULL, actual_out_nbytes_ret); -} diff --git a/vendor/libdeflate/lib/hc_matchfinder.h b/vendor/libdeflate/lib/hc_matchfinder.h deleted file mode 100644 index a0cddfca13..0000000000 --- a/vendor/libdeflate/lib/hc_matchfinder.h +++ /dev/null @@ -1,401 +0,0 @@ -/* - * hc_matchfinder.h - Lempel-Ziv matchfinding with a hash table of linked lists - * - * Copyright 2016 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * --------------------------------------------------------------------------- - * - * Algorithm - * - * This is a Hash Chains (hc) based matchfinder. - * - * The main data structure is a hash table where each hash bucket contains a - * linked list (or "chain") of sequences whose first 4 bytes share the same hash - * code. Each sequence is identified by its starting position in the input - * buffer. - * - * The algorithm processes the input buffer sequentially. At each byte - * position, the hash code of the first 4 bytes of the sequence beginning at - * that position (the sequence being matched against) is computed. This - * identifies the hash bucket to use for that position. Then, this hash - * bucket's linked list is searched for matches. Then, a new linked list node - * is created to represent the current sequence and is prepended to the list. - * - * This algorithm has several useful properties: - * - * - It only finds true Lempel-Ziv matches; i.e., those where the matching - * sequence occurs prior to the sequence being matched against. - * - * - The sequences in each linked list are always sorted by decreasing starting - * position. Therefore, the closest (smallest offset) matches are found - * first, which in many compression formats tend to be the cheapest to encode. - * - * - Although fast running time is not guaranteed due to the possibility of the - * lists getting very long, the worst degenerate behavior can be easily - * prevented by capping the number of nodes searched at each position. - * - * - If the compressor decides not to search for matches at a certain position, - * then that position can be quickly inserted without searching the list. - * - * - The algorithm is adaptable to sliding windows: just store the positions - * relative to a "base" value that is updated from time to time, and stop - * searching each list when the sequences get too far away. - * - * ---------------------------------------------------------------------------- - * - * Optimizations - * - * The main hash table and chains handle length 4+ matches. Length 3 matches - * are handled by a separate hash table with no chains. This works well for - * typical "greedy" or "lazy"-style compressors, where length 3 matches are - * often only helpful if they have small offsets. Instead of searching a full - * chain for length 3+ matches, the algorithm just checks for one close length 3 - * match, then focuses on finding length 4+ matches. - * - * The longest_match() and skip_bytes() functions are inlined into the - * compressors that use them. This isn't just about saving the overhead of a - * function call. These functions are intended to be called from the inner - * loops of compressors, where giving the compiler more control over register - * allocation is very helpful. There is also significant benefit to be gained - * from allowing the CPU to predict branches independently at each call site. - * For example, "lazy"-style compressors can be written with two calls to - * longest_match(), each of which starts with a different 'best_len' and - * therefore has significantly different performance characteristics. - * - * Although any hash function can be used, a multiplicative hash is fast and - * works well. - * - * On some processors, it is significantly faster to extend matches by whole - * words (32 or 64 bits) instead of by individual bytes. For this to be the - * case, the processor must implement unaligned memory accesses efficiently and - * must have either a fast "find first set bit" instruction or a fast "find last - * set bit" instruction, depending on the processor's endianness. - * - * The code uses one loop for finding the first match and one loop for finding a - * longer match. Each of these loops is tuned for its respective task and in - * combination are faster than a single generalized loop that handles both - * tasks. - * - * The code also uses a tight inner loop that only compares the last and first - * bytes of a potential match. It is only when these bytes match that a full - * match extension is attempted. - * - * ---------------------------------------------------------------------------- - */ - -#ifndef LIB_HC_MATCHFINDER_H -#define LIB_HC_MATCHFINDER_H - -#include "matchfinder_common.h" - -#define HC_MATCHFINDER_HASH3_ORDER 15 -#define HC_MATCHFINDER_HASH4_ORDER 16 - -#define HC_MATCHFINDER_TOTAL_HASH_SIZE \ - (((1UL << HC_MATCHFINDER_HASH3_ORDER) + \ - (1UL << HC_MATCHFINDER_HASH4_ORDER)) * sizeof(mf_pos_t)) - -struct MATCHFINDER_ALIGNED hc_matchfinder { - - /* The hash table for finding length 3 matches */ - mf_pos_t hash3_tab[1UL << HC_MATCHFINDER_HASH3_ORDER]; - - /* The hash table which contains the first nodes of the linked lists for - * finding length 4+ matches */ - mf_pos_t hash4_tab[1UL << HC_MATCHFINDER_HASH4_ORDER]; - - /* The "next node" references for the linked lists. The "next node" of - * the node for the sequence with position 'pos' is 'next_tab[pos]'. */ - mf_pos_t next_tab[MATCHFINDER_WINDOW_SIZE]; -}; - -/* Prepare the matchfinder for a new input buffer. */ -static forceinline void -hc_matchfinder_init(struct hc_matchfinder *mf) -{ - STATIC_ASSERT(HC_MATCHFINDER_TOTAL_HASH_SIZE % - MATCHFINDER_SIZE_ALIGNMENT == 0); - - matchfinder_init((mf_pos_t *)mf, HC_MATCHFINDER_TOTAL_HASH_SIZE); -} - -static forceinline void -hc_matchfinder_slide_window(struct hc_matchfinder *mf) -{ - STATIC_ASSERT(sizeof(*mf) % MATCHFINDER_SIZE_ALIGNMENT == 0); - - matchfinder_rebase((mf_pos_t *)mf, sizeof(*mf)); -} - -/* - * Find the longest match longer than 'best_len' bytes. - * - * @mf - * The matchfinder structure. - * @in_base_p - * Location of a pointer which points to the place in the input data the - * matchfinder currently stores positions relative to. This may be updated - * by this function. - * @in_next - * Pointer to the next position in the input buffer, i.e. the sequence - * being matched against. - * @best_len - * Require a match longer than this length. - * @max_len - * The maximum permissible match length at this position. - * @nice_len - * Stop searching if a match of at least this length is found. - * Must be <= @max_len. - * @max_search_depth - * Limit on the number of potential matches to consider. Must be >= 1. - * @next_hashes - * The precomputed hash codes for the sequence beginning at @in_next. - * These will be used and then updated with the precomputed hashcodes for - * the sequence beginning at @in_next + 1. - * @offset_ret - * If a match is found, its offset is returned in this location. - * - * Return the length of the match found, or 'best_len' if no match longer than - * 'best_len' was found. - */ -static forceinline u32 -hc_matchfinder_longest_match(struct hc_matchfinder * const mf, - const u8 ** const in_base_p, - const u8 * const in_next, - u32 best_len, - const u32 max_len, - const u32 nice_len, - const u32 max_search_depth, - u32 * const next_hashes, - u32 * const offset_ret) -{ - u32 depth_remaining = max_search_depth; - const u8 *best_matchptr = in_next; - mf_pos_t cur_node3, cur_node4; - u32 hash3, hash4; - u32 next_hashseq; - u32 seq4; - const u8 *matchptr; - u32 len; - u32 cur_pos = in_next - *in_base_p; - const u8 *in_base; - mf_pos_t cutoff; - - if (cur_pos == MATCHFINDER_WINDOW_SIZE) { - hc_matchfinder_slide_window(mf); - *in_base_p += MATCHFINDER_WINDOW_SIZE; - cur_pos = 0; - } - - in_base = *in_base_p; - cutoff = cur_pos - MATCHFINDER_WINDOW_SIZE; - - if (unlikely(max_len < 5)) /* can we read 4 bytes from 'in_next + 1'? */ - goto out; - - /* Get the precomputed hash codes. */ - hash3 = next_hashes[0]; - hash4 = next_hashes[1]; - - /* From the hash buckets, get the first node of each linked list. */ - cur_node3 = mf->hash3_tab[hash3]; - cur_node4 = mf->hash4_tab[hash4]; - - /* Update for length 3 matches. This replaces the singleton node in the - * 'hash3' bucket with the node for the current sequence. */ - mf->hash3_tab[hash3] = cur_pos; - - /* Update for length 4 matches. This prepends the node for the current - * sequence to the linked list in the 'hash4' bucket. */ - mf->hash4_tab[hash4] = cur_pos; - mf->next_tab[cur_pos] = cur_node4; - - /* Compute the next hash codes. */ - next_hashseq = get_unaligned_le32(in_next + 1); - next_hashes[0] = lz_hash(next_hashseq & 0xFFFFFF, HC_MATCHFINDER_HASH3_ORDER); - next_hashes[1] = lz_hash(next_hashseq, HC_MATCHFINDER_HASH4_ORDER); - prefetchw(&mf->hash3_tab[next_hashes[0]]); - prefetchw(&mf->hash4_tab[next_hashes[1]]); - - if (best_len < 4) { /* No match of length >= 4 found yet? */ - - /* Check for a length 3 match if needed. */ - - if (cur_node3 <= cutoff) - goto out; - - seq4 = load_u32_unaligned(in_next); - - if (best_len < 3) { - matchptr = &in_base[cur_node3]; - if (load_u24_unaligned(matchptr) == loaded_u32_to_u24(seq4)) { - best_len = 3; - best_matchptr = matchptr; - } - } - - /* Check for a length 4 match. */ - - if (cur_node4 <= cutoff) - goto out; - - for (;;) { - /* No length 4 match found yet. Check the first 4 bytes. */ - matchptr = &in_base[cur_node4]; - - if (load_u32_unaligned(matchptr) == seq4) - break; - - /* The first 4 bytes did not match. Keep trying. */ - cur_node4 = mf->next_tab[cur_node4 & (MATCHFINDER_WINDOW_SIZE - 1)]; - if (cur_node4 <= cutoff || !--depth_remaining) - goto out; - } - - /* Found a match of length >= 4. Extend it to its full length. */ - best_matchptr = matchptr; - best_len = lz_extend(in_next, best_matchptr, 4, max_len); - if (best_len >= nice_len) - goto out; - cur_node4 = mf->next_tab[cur_node4 & (MATCHFINDER_WINDOW_SIZE - 1)]; - if (cur_node4 <= cutoff || !--depth_remaining) - goto out; - } else { - if (cur_node4 <= cutoff || best_len >= nice_len) - goto out; - } - - /* Check for matches of length >= 5. */ - - for (;;) { - for (;;) { - matchptr = &in_base[cur_node4]; - - /* Already found a length 4 match. Try for a longer - * match; start by checking either the last 4 bytes and - * the first 4 bytes, or the last byte. (The last byte, - * the one which would extend the match length by 1, is - * the most important.) */ - #if UNALIGNED_ACCESS_IS_FAST - if ((load_u32_unaligned(matchptr + best_len - 3) == - load_u32_unaligned(in_next + best_len - 3)) && - (load_u32_unaligned(matchptr) == - load_u32_unaligned(in_next))) - #else - if (matchptr[best_len] == in_next[best_len]) - #endif - break; - - /* Continue to the next node in the list. */ - cur_node4 = mf->next_tab[cur_node4 & (MATCHFINDER_WINDOW_SIZE - 1)]; - if (cur_node4 <= cutoff || !--depth_remaining) - goto out; - } - - #if UNALIGNED_ACCESS_IS_FAST - len = 4; - #else - len = 0; - #endif - len = lz_extend(in_next, matchptr, len, max_len); - if (len > best_len) { - /* This is the new longest match. */ - best_len = len; - best_matchptr = matchptr; - if (best_len >= nice_len) - goto out; - } - - /* Continue to the next node in the list. */ - cur_node4 = mf->next_tab[cur_node4 & (MATCHFINDER_WINDOW_SIZE - 1)]; - if (cur_node4 <= cutoff || !--depth_remaining) - goto out; - } -out: - *offset_ret = in_next - best_matchptr; - return best_len; -} - -/* - * Advance the matchfinder, but don't search for matches. - * - * @mf - * The matchfinder structure. - * @in_base_p - * Location of a pointer which points to the place in the input data the - * matchfinder currently stores positions relative to. This may be updated - * by this function. - * @in_next - * Pointer to the next position in the input buffer. - * @in_end - * Pointer to the end of the input buffer. - * @count - * The number of bytes to advance. Must be > 0. - * @next_hashes - * The precomputed hash codes for the sequence beginning at @in_next. - * These will be used and then updated with the precomputed hashcodes for - * the sequence beginning at @in_next + @count. - */ -static forceinline void -hc_matchfinder_skip_bytes(struct hc_matchfinder * const mf, - const u8 ** const in_base_p, - const u8 *in_next, - const u8 * const in_end, - const u32 count, - u32 * const next_hashes) -{ - u32 cur_pos; - u32 hash3, hash4; - u32 next_hashseq; - u32 remaining = count; - - if (unlikely(count + 5 > in_end - in_next)) - return; - - cur_pos = in_next - *in_base_p; - hash3 = next_hashes[0]; - hash4 = next_hashes[1]; - do { - if (cur_pos == MATCHFINDER_WINDOW_SIZE) { - hc_matchfinder_slide_window(mf); - *in_base_p += MATCHFINDER_WINDOW_SIZE; - cur_pos = 0; - } - mf->hash3_tab[hash3] = cur_pos; - mf->next_tab[cur_pos] = mf->hash4_tab[hash4]; - mf->hash4_tab[hash4] = cur_pos; - - next_hashseq = get_unaligned_le32(++in_next); - hash3 = lz_hash(next_hashseq & 0xFFFFFF, HC_MATCHFINDER_HASH3_ORDER); - hash4 = lz_hash(next_hashseq, HC_MATCHFINDER_HASH4_ORDER); - cur_pos++; - } while (--remaining); - - prefetchw(&mf->hash3_tab[hash3]); - prefetchw(&mf->hash4_tab[hash4]); - next_hashes[0] = hash3; - next_hashes[1] = hash4; -} - -#endif /* LIB_HC_MATCHFINDER_H */ diff --git a/vendor/libdeflate/lib/ht_matchfinder.h b/vendor/libdeflate/lib/ht_matchfinder.h deleted file mode 100644 index 6e5a187c12..0000000000 --- a/vendor/libdeflate/lib/ht_matchfinder.h +++ /dev/null @@ -1,234 +0,0 @@ -/* - * ht_matchfinder.h - Lempel-Ziv matchfinding with a hash table - * - * Copyright 2022 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * --------------------------------------------------------------------------- - * - * This is a Hash Table (ht) matchfinder. - * - * This is a variant of the Hash Chains (hc) matchfinder that is optimized for - * very fast compression. The ht_matchfinder stores the hash chains inline in - * the hash table, whereas the hc_matchfinder stores them in a separate array. - * Storing the hash chains inline is the faster method when max_search_depth - * (the maximum chain length) is very small. It is not appropriate when - * max_search_depth is larger, as then it uses too much memory. - * - * Due to its focus on speed, the ht_matchfinder doesn't support length 3 - * matches. It also doesn't allow max_search_depth to vary at runtime; it is - * fixed at build time as HT_MATCHFINDER_BUCKET_SIZE. - * - * See hc_matchfinder.h for more information. - */ - -#ifndef LIB_HT_MATCHFINDER_H -#define LIB_HT_MATCHFINDER_H - -#include "matchfinder_common.h" - -#define HT_MATCHFINDER_HASH_ORDER 15 -#define HT_MATCHFINDER_BUCKET_SIZE 2 - -#define HT_MATCHFINDER_MIN_MATCH_LEN 4 -/* Minimum value of max_len for ht_matchfinder_longest_match() */ -#define HT_MATCHFINDER_REQUIRED_NBYTES 5 - -struct MATCHFINDER_ALIGNED ht_matchfinder { - mf_pos_t hash_tab[1UL << HT_MATCHFINDER_HASH_ORDER] - [HT_MATCHFINDER_BUCKET_SIZE]; -}; - -static forceinline void -ht_matchfinder_init(struct ht_matchfinder *mf) -{ - STATIC_ASSERT(sizeof(*mf) % MATCHFINDER_SIZE_ALIGNMENT == 0); - - matchfinder_init((mf_pos_t *)mf, sizeof(*mf)); -} - -static forceinline void -ht_matchfinder_slide_window(struct ht_matchfinder *mf) -{ - matchfinder_rebase((mf_pos_t *)mf, sizeof(*mf)); -} - -/* Note: max_len must be >= HT_MATCHFINDER_REQUIRED_NBYTES */ -static forceinline u32 -ht_matchfinder_longest_match(struct ht_matchfinder * const mf, - const u8 ** const in_base_p, - const u8 * const in_next, - const u32 max_len, - const u32 nice_len, - u32 * const next_hash, - u32 * const offset_ret) -{ - u32 best_len = 0; - const u8 *best_matchptr = in_next; - u32 cur_pos = in_next - *in_base_p; - const u8 *in_base; - mf_pos_t cutoff; - u32 hash; - u32 seq; - mf_pos_t cur_node; - const u8 *matchptr; -#if HT_MATCHFINDER_BUCKET_SIZE > 1 - mf_pos_t to_insert; - u32 len; -#endif -#if HT_MATCHFINDER_BUCKET_SIZE > 2 - int i; -#endif - - /* This is assumed throughout this function. */ - STATIC_ASSERT(HT_MATCHFINDER_MIN_MATCH_LEN == 4); - - if (cur_pos == MATCHFINDER_WINDOW_SIZE) { - ht_matchfinder_slide_window(mf); - *in_base_p += MATCHFINDER_WINDOW_SIZE; - cur_pos = 0; - } - in_base = *in_base_p; - cutoff = cur_pos - MATCHFINDER_WINDOW_SIZE; - - hash = *next_hash; - STATIC_ASSERT(HT_MATCHFINDER_REQUIRED_NBYTES == 5); - *next_hash = lz_hash(get_unaligned_le32(in_next + 1), - HT_MATCHFINDER_HASH_ORDER); - seq = load_u32_unaligned(in_next); - prefetchw(&mf->hash_tab[*next_hash]); -#if HT_MATCHFINDER_BUCKET_SIZE == 1 - /* Hand-unrolled version for BUCKET_SIZE == 1 */ - cur_node = mf->hash_tab[hash][0]; - mf->hash_tab[hash][0] = cur_pos; - if (cur_node <= cutoff) - goto out; - matchptr = &in_base[cur_node]; - if (load_u32_unaligned(matchptr) == seq) { - best_len = lz_extend(in_next, matchptr, 4, max_len); - best_matchptr = matchptr; - } -#elif HT_MATCHFINDER_BUCKET_SIZE == 2 - /* - * Hand-unrolled version for BUCKET_SIZE == 2. The logic here also - * differs slightly in that it copies the first entry to the second even - * if nice_len is reached on the first, as this can be slightly faster. - */ - cur_node = mf->hash_tab[hash][0]; - mf->hash_tab[hash][0] = cur_pos; - if (cur_node <= cutoff) - goto out; - matchptr = &in_base[cur_node]; - - to_insert = cur_node; - cur_node = mf->hash_tab[hash][1]; - mf->hash_tab[hash][1] = to_insert; - - if (load_u32_unaligned(matchptr) == seq) { - best_len = lz_extend(in_next, matchptr, 4, max_len); - best_matchptr = matchptr; - if (cur_node <= cutoff || best_len >= nice_len) - goto out; - matchptr = &in_base[cur_node]; - if (load_u32_unaligned(matchptr) == seq && - load_u32_unaligned(matchptr + best_len - 3) == - load_u32_unaligned(in_next + best_len - 3)) { - len = lz_extend(in_next, matchptr, 4, max_len); - if (len > best_len) { - best_len = len; - best_matchptr = matchptr; - } - } - } else { - if (cur_node <= cutoff) - goto out; - matchptr = &in_base[cur_node]; - if (load_u32_unaligned(matchptr) == seq) { - best_len = lz_extend(in_next, matchptr, 4, max_len); - best_matchptr = matchptr; - } - } -#else - /* Generic version for HT_MATCHFINDER_BUCKET_SIZE > 2 */ - to_insert = cur_pos; - for (i = 0; i < HT_MATCHFINDER_BUCKET_SIZE; i++) { - cur_node = mf->hash_tab[hash][i]; - mf->hash_tab[hash][i] = to_insert; - if (cur_node <= cutoff) - goto out; - matchptr = &in_base[cur_node]; - if (load_u32_unaligned(matchptr) == seq) { - len = lz_extend(in_next, matchptr, 4, max_len); - if (len > best_len) { - best_len = len; - best_matchptr = matchptr; - if (best_len >= nice_len) - goto out; - } - } - to_insert = cur_node; - } -#endif -out: - *offset_ret = in_next - best_matchptr; - return best_len; -} - -static forceinline void -ht_matchfinder_skip_bytes(struct ht_matchfinder * const mf, - const u8 ** const in_base_p, - const u8 *in_next, - const u8 * const in_end, - const u32 count, - u32 * const next_hash) -{ - s32 cur_pos = in_next - *in_base_p; - u32 hash; - u32 remaining = count; - int i; - - if (unlikely(count + HT_MATCHFINDER_REQUIRED_NBYTES > in_end - in_next)) - return; - - if (cur_pos + count - 1 >= MATCHFINDER_WINDOW_SIZE) { - ht_matchfinder_slide_window(mf); - *in_base_p += MATCHFINDER_WINDOW_SIZE; - cur_pos -= MATCHFINDER_WINDOW_SIZE; - } - - hash = *next_hash; - do { - for (i = HT_MATCHFINDER_BUCKET_SIZE - 1; i > 0; i--) - mf->hash_tab[hash][i] = mf->hash_tab[hash][i - 1]; - mf->hash_tab[hash][0] = cur_pos; - - hash = lz_hash(get_unaligned_le32(++in_next), - HT_MATCHFINDER_HASH_ORDER); - cur_pos++; - } while (--remaining); - - prefetchw(&mf->hash_tab[hash]); - *next_hash = hash; -} - -#endif /* LIB_HT_MATCHFINDER_H */ diff --git a/vendor/libdeflate/lib/lib_common.h b/vendor/libdeflate/lib/lib_common.h deleted file mode 100644 index 9246ca1e57..0000000000 --- a/vendor/libdeflate/lib/lib_common.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - * lib_common.h - internal header included by all library code - */ - -#ifndef LIB_LIB_COMMON_H -#define LIB_LIB_COMMON_H - -#ifdef LIBDEFLATE_H - /* - * When building the library, LIBDEFLATEAPI needs to be defined properly before - * including libdeflate.h. - */ -# error "lib_common.h must always be included before libdeflate.h" -#endif - -/* - * A build that compiles this library into a larger one does not want its - * symbols marked for export, as they would then leave whatever binary it - * ends up in. Such a build can say so by defining this itself. - */ -#ifndef LIBDEFLATE_EXPORT_SYM -#if defined(LIBDEFLATE_DLL) && (defined(_WIN32) || defined(__CYGWIN__)) -# define LIBDEFLATE_EXPORT_SYM __declspec(dllexport) -#elif defined(__GNUC__) -# define LIBDEFLATE_EXPORT_SYM __attribute__((visibility("default"))) -#else -# define LIBDEFLATE_EXPORT_SYM -#endif -#endif - -/* - * On i386, gcc assumes that the stack is 16-byte aligned at function entry. - * However, some compilers (e.g. MSVC) and programming languages (e.g. Delphi) - * only guarantee 4-byte alignment when calling functions. This is mainly an - * issue on Windows, but it has been seen on Linux too. Work around this ABI - * incompatibility by realigning the stack pointer when entering libdeflate. - * This prevents crashes in SSE/AVX code. - */ -#if defined(__GNUC__) && defined(__i386__) -# define LIBDEFLATE_ALIGN_STACK __attribute__((force_align_arg_pointer)) -#else -# define LIBDEFLATE_ALIGN_STACK -#endif - -#define LIBDEFLATEAPI LIBDEFLATE_EXPORT_SYM LIBDEFLATE_ALIGN_STACK - -#include "../common_defs.h" - -typedef void *(*malloc_func_t)(size_t); -typedef void (*free_func_t)(void *); - -extern malloc_func_t libdeflate_default_malloc_func; -extern free_func_t libdeflate_default_free_func; - -void *libdeflate_aligned_malloc(malloc_func_t malloc_func, - size_t alignment, size_t size); -void libdeflate_aligned_free(free_func_t free_func, void *ptr); - -#ifdef FREESTANDING -/* - * With -ffreestanding, may be missing, and we must provide - * implementations of memset(), memcpy(), memmove(), and memcmp(). - * See https://gcc.gnu.org/onlinedocs/gcc/Standards.html - * - * Also, -ffreestanding disables interpreting calls to these functions as - * built-ins. E.g., calling memcpy(&v, p, WORDBYTES) will make a function call, - * not be optimized to a single load instruction. For performance reasons we - * don't want that. So, declare these functions as macros that expand to the - * corresponding built-ins. This approach is recommended in the gcc man page. - * We still need the actual function definitions in case gcc calls them. - */ -void *memset(void *s, int c, size_t n); -#define memset(s, c, n) __builtin_memset((s), (c), (n)) - -void *memcpy(void *dest, const void *src, size_t n); -#define memcpy(dest, src, n) __builtin_memcpy((dest), (src), (n)) - -void *memmove(void *dest, const void *src, size_t n); -#define memmove(dest, src, n) __builtin_memmove((dest), (src), (n)) - -int memcmp(const void *s1, const void *s2, size_t n); -#define memcmp(s1, s2, n) __builtin_memcmp((s1), (s2), (n)) - -#undef LIBDEFLATE_ENABLE_ASSERTIONS -#else -# include - /* - * To prevent false positive static analyzer warnings, ensure that assertions - * are visible to the static analyzer. - */ -# ifdef __clang_analyzer__ -# define LIBDEFLATE_ENABLE_ASSERTIONS -# endif -#endif - -/* - * Runtime assertion support. Don't enable this in production builds; it may - * hurt performance significantly. - */ -#ifdef LIBDEFLATE_ENABLE_ASSERTIONS -NORETURN void -libdeflate_assertion_failed(const char *expr, const char *file, int line); -#define ASSERT(expr) { if (unlikely(!(expr))) \ - libdeflate_assertion_failed(#expr, __FILE__, __LINE__); } -#else -#define ASSERT(expr) (void)(expr) -#endif - -#define CONCAT_IMPL(a, b) a##b -#define CONCAT(a, b) CONCAT_IMPL(a, b) -#define ADD_SUFFIX(name) CONCAT(name, SUFFIX) - -#endif /* LIB_LIB_COMMON_H */ diff --git a/vendor/libdeflate/lib/matchfinder_common.h b/vendor/libdeflate/lib/matchfinder_common.h deleted file mode 100644 index 98e4bc7165..0000000000 --- a/vendor/libdeflate/lib/matchfinder_common.h +++ /dev/null @@ -1,224 +0,0 @@ -/* - * matchfinder_common.h - common code for Lempel-Ziv matchfinding - */ - -#ifndef LIB_MATCHFINDER_COMMON_H -#define LIB_MATCHFINDER_COMMON_H - -#include "lib_common.h" - -#ifndef MATCHFINDER_WINDOW_ORDER -# error "MATCHFINDER_WINDOW_ORDER must be defined!" -#endif - -/* - * Given a 32-bit value that was loaded with the platform's native endianness, - * return a 32-bit value whose high-order 8 bits are 0 and whose low-order 24 - * bits contain the first 3 bytes, arranged in octets in a platform-dependent - * order, at the memory location from which the input 32-bit value was loaded. - */ -static forceinline u32 -loaded_u32_to_u24(u32 v) -{ - if (CPU_IS_LITTLE_ENDIAN()) - return v & 0xFFFFFF; - else - return v >> 8; -} - -/* - * Load the next 3 bytes from @p into the 24 low-order bits of a 32-bit value. - * The order in which the 3 bytes will be arranged as octets in the 24 bits is - * platform-dependent. At least 4 bytes (not 3) must be available at @p. - */ -static forceinline u32 -load_u24_unaligned(const u8 *p) -{ -#if UNALIGNED_ACCESS_IS_FAST - return loaded_u32_to_u24(load_u32_unaligned(p)); -#else - if (CPU_IS_LITTLE_ENDIAN()) - return ((u32)p[0] << 0) | ((u32)p[1] << 8) | ((u32)p[2] << 16); - else - return ((u32)p[2] << 0) | ((u32)p[1] << 8) | ((u32)p[0] << 16); -#endif -} - -#define MATCHFINDER_WINDOW_SIZE (1UL << MATCHFINDER_WINDOW_ORDER) - -typedef s16 mf_pos_t; - -#define MATCHFINDER_INITVAL ((mf_pos_t)-MATCHFINDER_WINDOW_SIZE) - -/* - * This is the memory address alignment, in bytes, required for the matchfinder - * buffers by the architecture-specific implementations of matchfinder_init() - * and matchfinder_rebase(). "Matchfinder buffer" means an entire struct - * hc_matchfinder, bt_matchfinder, or ht_matchfinder; the next_tab field of - * struct hc_matchfinder; or the child_tab field of struct bt_matchfinder. - * - * This affects how the entire 'struct deflate_compressor' is allocated, since - * the matchfinder structures are embedded inside it. - * - * Currently the maximum memory address alignment required is 32 bytes, needed - * by the AVX-2 matchfinder functions. - */ -#define MATCHFINDER_MEM_ALIGNMENT 32 - -/* - * This declares a size, in bytes, that is guaranteed to divide the sizes of the - * matchfinder buffers (where "matchfinder buffers" is as defined for - * MATCHFINDER_MEM_ALIGNMENT). The architecture-specific implementations of - * matchfinder_init() and matchfinder_rebase() take advantage of this value. - * - * Currently the maximum size alignment required is 128 bytes, needed by - * the AVX-2 matchfinder functions. However, the RISC-V Vector Extension - * matchfinder functions can, in principle, take advantage of a larger size - * alignment. Therefore, we set this to 1024, which still easily divides the - * actual sizes that result from the current matchfinder struct definitions. - * This value can safely be changed to any power of two that is >= 128. - */ -#define MATCHFINDER_SIZE_ALIGNMENT 1024 - -#undef matchfinder_init -#undef matchfinder_rebase -#ifdef _aligned_attribute -# define MATCHFINDER_ALIGNED _aligned_attribute(MATCHFINDER_MEM_ALIGNMENT) -# if defined(ARCH_ARM32) || defined(ARCH_ARM64) -# include "arm/matchfinder_impl.h" -# elif defined(ARCH_RISCV) -# include "riscv/matchfinder_impl.h" -# elif defined(ARCH_X86_32) || defined(ARCH_X86_64) -# include "x86/matchfinder_impl.h" -# endif -#else -# define MATCHFINDER_ALIGNED -#endif - -/* - * Initialize the hash table portion of the matchfinder. - * - * Essentially, this is an optimized memset(). - * - * 'data' must be aligned to a MATCHFINDER_MEM_ALIGNMENT boundary, and - * 'size' must be a multiple of MATCHFINDER_SIZE_ALIGNMENT. - */ -#ifndef matchfinder_init -static forceinline void -matchfinder_init(mf_pos_t *data, size_t size) -{ - size_t num_entries = size / sizeof(*data); - size_t i; - - for (i = 0; i < num_entries; i++) - data[i] = MATCHFINDER_INITVAL; -} -#endif - -/* - * Slide the matchfinder by MATCHFINDER_WINDOW_SIZE bytes. - * - * This must be called just after each MATCHFINDER_WINDOW_SIZE bytes have been - * run through the matchfinder. - * - * This subtracts MATCHFINDER_WINDOW_SIZE bytes from each entry in the given - * array, making the entries be relative to the current position rather than the - * position MATCHFINDER_WINDOW_SIZE bytes prior. To avoid integer underflows, - * entries that would become less than -MATCHFINDER_WINDOW_SIZE stay at - * -MATCHFINDER_WINDOW_SIZE, keeping them permanently out of bounds. - * - * The given array must contain all matchfinder data that is position-relative: - * the hash table(s) as well as any hash chain or binary tree links. Its - * address must be aligned to a MATCHFINDER_MEM_ALIGNMENT boundary, and its size - * must be a multiple of MATCHFINDER_SIZE_ALIGNMENT. - */ -#ifndef matchfinder_rebase -static forceinline void -matchfinder_rebase(mf_pos_t *data, size_t size) -{ - size_t num_entries = size / sizeof(*data); - size_t i; - - if (MATCHFINDER_WINDOW_SIZE == 32768) { - /* - * Branchless version for 32768-byte windows. Clear all bits if - * the value was already negative, then set the sign bit. This - * is equivalent to subtracting 32768 with signed saturation. - */ - for (i = 0; i < num_entries; i++) - data[i] = 0x8000 | (data[i] & ~(data[i] >> 15)); - } else { - for (i = 0; i < num_entries; i++) { - if (data[i] >= 0) - data[i] -= (mf_pos_t)-MATCHFINDER_WINDOW_SIZE; - else - data[i] = (mf_pos_t)-MATCHFINDER_WINDOW_SIZE; - } - } -} -#endif - -/* - * The hash function: given a sequence prefix held in the low-order bits of a - * 32-bit value, multiply by a carefully-chosen large constant. Discard any - * bits of the product that don't fit in a 32-bit value, but take the - * next-highest @num_bits bits of the product as the hash value, as those have - * the most randomness. - */ -static forceinline u32 -lz_hash(u32 seq, unsigned num_bits) -{ - return (u32)(seq * 0x1E35A7BD) >> (32 - num_bits); -} - -/* - * Return the number of bytes at @matchptr that match the bytes at @strptr, up - * to a maximum of @max_len. Initially, @start_len bytes are matched. - */ -static forceinline u32 -lz_extend(const u8 * const strptr, const u8 * const matchptr, - const u32 start_len, const u32 max_len) -{ - u32 len = start_len; - machine_word_t v_word; - - if (UNALIGNED_ACCESS_IS_FAST) { - - if (likely(max_len - len >= 4 * WORDBYTES)) { - - #define COMPARE_WORD_STEP \ - v_word = load_word_unaligned(&matchptr[len]) ^ \ - load_word_unaligned(&strptr[len]); \ - if (v_word != 0) \ - goto word_differs; \ - len += WORDBYTES; \ - - COMPARE_WORD_STEP - COMPARE_WORD_STEP - COMPARE_WORD_STEP - COMPARE_WORD_STEP - #undef COMPARE_WORD_STEP - } - - while (len + WORDBYTES <= max_len) { - v_word = load_word_unaligned(&matchptr[len]) ^ - load_word_unaligned(&strptr[len]); - if (v_word != 0) - goto word_differs; - len += WORDBYTES; - } - } - - while (len < max_len && matchptr[len] == strptr[len]) - len++; - return len; - -word_differs: - if (CPU_IS_LITTLE_ENDIAN()) - len += (bsfw(v_word) >> 3); - else - len += (WORDBITS - 1 - bsrw(v_word)) >> 3; - return len; -} - -#endif /* LIB_MATCHFINDER_COMMON_H */ diff --git a/vendor/libdeflate/lib/riscv/matchfinder_impl.h b/vendor/libdeflate/lib/riscv/matchfinder_impl.h deleted file mode 100644 index 6d8bf7935d..0000000000 --- a/vendor/libdeflate/lib/riscv/matchfinder_impl.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * riscv/matchfinder_impl.h - RISC-V implementations of matchfinder functions - * - * Copyright 2024 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef LIB_RISCV_MATCHFINDER_IMPL_H -#define LIB_RISCV_MATCHFINDER_IMPL_H - -#if defined(ARCH_RISCV) && defined(__riscv_vector) -#include - -/* - * Return the maximum number of 16-bit (mf_pos_t) elements that fit in 8 RISC-V - * vector registers and also evenly divide the sizes of the matchfinder buffers. - */ -static forceinline size_t -riscv_matchfinder_vl(void) -{ - const size_t vl = __riscv_vsetvlmax_e16m8(); - - STATIC_ASSERT(sizeof(mf_pos_t) == sizeof(s16)); - /* - * MATCHFINDER_SIZE_ALIGNMENT is a power of 2, as is 'vl' because the - * RISC-V Vector Extension requires that the vector register length - * (VLEN) be a power of 2. Thus, a simple MIN() gives the correct - * answer here; rounding to a power of 2 is not required. - */ - STATIC_ASSERT((MATCHFINDER_SIZE_ALIGNMENT & - (MATCHFINDER_SIZE_ALIGNMENT - 1)) == 0); - ASSERT((vl & (vl - 1)) == 0); - return MIN(vl, MATCHFINDER_SIZE_ALIGNMENT / sizeof(mf_pos_t)); -} - -/* matchfinder_init() optimized using the RISC-V Vector Extension */ -static forceinline void -matchfinder_init_rvv(mf_pos_t *p, size_t size) -{ - const size_t vl = riscv_matchfinder_vl(); - const vint16m8_t v = __riscv_vmv_v_x_i16m8(MATCHFINDER_INITVAL, vl); - - ASSERT(size > 0 && size % (vl * sizeof(p[0])) == 0); - do { - __riscv_vse16_v_i16m8(p, v, vl); - p += vl; - size -= vl * sizeof(p[0]); - } while (size != 0); -} -#define matchfinder_init matchfinder_init_rvv - -/* matchfinder_rebase() optimized using the RISC-V Vector Extension */ -static forceinline void -matchfinder_rebase_rvv(mf_pos_t *p, size_t size) -{ - const size_t vl = riscv_matchfinder_vl(); - - ASSERT(size > 0 && size % (vl * sizeof(p[0])) == 0); - do { - vint16m8_t v = __riscv_vle16_v_i16m8(p, vl); - - /* - * This should generate the vsadd.vx instruction - * (Vector Saturating Add, integer vector-scalar) - */ - v = __riscv_vsadd_vx_i16m8(v, (s16)-MATCHFINDER_WINDOW_SIZE, - vl); - __riscv_vse16_v_i16m8(p, v, vl); - p += vl; - size -= vl * sizeof(p[0]); - } while (size != 0); -} -#define matchfinder_rebase matchfinder_rebase_rvv - -#endif /* ARCH_RISCV && __riscv_vector */ - -#endif /* LIB_RISCV_MATCHFINDER_IMPL_H */ diff --git a/vendor/libdeflate/lib/utils.c b/vendor/libdeflate/lib/utils.c deleted file mode 100644 index 5bb34eda40..0000000000 --- a/vendor/libdeflate/lib/utils.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * utils.c - utility functions for libdeflate - * - * Copyright 2016 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "lib_common.h" - -#ifdef FREESTANDING -# define malloc NULL -# define free NULL -#else -# include -#endif - -malloc_func_t libdeflate_default_malloc_func = malloc; -free_func_t libdeflate_default_free_func = free; - -void * -libdeflate_aligned_malloc(malloc_func_t malloc_func, - size_t alignment, size_t size) -{ - void *ptr = (*malloc_func)(sizeof(void *) + alignment - 1 + size); - - if (ptr) { - void *orig_ptr = ptr; - - ptr = (void *)ALIGN((uintptr_t)ptr + sizeof(void *), alignment); - ((void **)ptr)[-1] = orig_ptr; - } - return ptr; -} - -void -libdeflate_aligned_free(free_func_t free_func, void *ptr) -{ - (*free_func)(((void **)ptr)[-1]); -} - -LIBDEFLATEAPI void -libdeflate_set_memory_allocator(malloc_func_t malloc_func, - free_func_t free_func) -{ - libdeflate_default_malloc_func = malloc_func; - libdeflate_default_free_func = free_func; -} - -/* - * Implementations of libc functions for freestanding library builds. - * Normal library builds don't use these. Not optimized yet; usually the - * compiler expands these functions and doesn't actually call them anyway. - */ -#ifdef FREESTANDING -#undef memset -void * __attribute__((weak)) -memset(void *s, int c, size_t n) -{ - u8 *p = s; - size_t i; - - for (i = 0; i < n; i++) - p[i] = c; - return s; -} - -#undef memcpy -void * __attribute__((weak)) -memcpy(void *dest, const void *src, size_t n) -{ - u8 *d = dest; - const u8 *s = src; - size_t i; - - for (i = 0; i < n; i++) - d[i] = s[i]; - return dest; -} - -#undef memmove -void * __attribute__((weak)) -memmove(void *dest, const void *src, size_t n) -{ - u8 *d = dest; - const u8 *s = src; - size_t i; - - if (d <= s) - return memcpy(d, s, n); - - for (i = n; i > 0; i--) - d[i - 1] = s[i - 1]; - return dest; -} - -#undef memcmp -int __attribute__((weak)) -memcmp(const void *s1, const void *s2, size_t n) -{ - const u8 *p1 = s1; - const u8 *p2 = s2; - size_t i; - - for (i = 0; i < n; i++) { - if (p1[i] != p2[i]) - return (int)p1[i] - (int)p2[i]; - } - return 0; -} -#endif /* FREESTANDING */ - -#ifdef LIBDEFLATE_ENABLE_ASSERTIONS -#include -#include -NORETURN void -libdeflate_assertion_failed(const char *expr, const char *file, int line) -{ - fprintf(stderr, "Assertion failed: %s at %s:%d\n", expr, file, line); - abort(); -} -#endif /* LIBDEFLATE_ENABLE_ASSERTIONS */ diff --git a/vendor/libdeflate/lib/x86/adler32_impl.h b/vendor/libdeflate/lib/x86/adler32_impl.h deleted file mode 100644 index f1102b5e16..0000000000 --- a/vendor/libdeflate/lib/x86/adler32_impl.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * x86/adler32_impl.h - x86 implementations of Adler-32 checksum algorithm - * - * Copyright 2016 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef LIB_X86_ADLER32_IMPL_H -#define LIB_X86_ADLER32_IMPL_H - -#include "cpu_features.h" - -/* SSE2 and AVX2 implementations. Used on older CPUs. */ -#if defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER) -# define adler32_x86_sse2 adler32_x86_sse2 -# define SUFFIX _sse2 -# define ATTRIBUTES _target_attribute("sse2") -# define VL 16 -# define USE_VNNI 0 -# define USE_AVX512 0 -# include "adler32_template.h" - -# define adler32_x86_avx2 adler32_x86_avx2 -# define SUFFIX _avx2 -# define ATTRIBUTES _target_attribute("avx2") -# define VL 32 -# define USE_VNNI 0 -# define USE_AVX512 0 -# include "adler32_template.h" -#endif - -/* - * AVX-VNNI implementation. This is used on CPUs that have AVX2 and AVX-VNNI - * but don't have AVX-512, for example Intel Alder Lake. - * - * Unusually for a new CPU feature, gcc added support for the AVX-VNNI - * intrinsics (in gcc 11.1) slightly before binutils added support for - * assembling AVX-VNNI instructions (in binutils 2.36). Distros can reasonably - * have gcc 11 with binutils 2.35. Because of this issue, we check for gcc 12 - * instead of gcc 11. (libdeflate supports direct compilation without a - * configure step, so checking the binutils version is not always an option.) - */ -#if (GCC_PREREQ(12, 1) || CLANG_PREREQ(12, 0, 13000000) || MSVC_PREREQ(1930)) && \ - !defined(LIBDEFLATE_ASSEMBLER_DOES_NOT_SUPPORT_AVX_VNNI) -# define adler32_x86_avx2_vnni adler32_x86_avx2_vnni -# define SUFFIX _avx2_vnni -# define ATTRIBUTES _target_attribute("avx2,avxvnni") -# define VL 32 -# define USE_VNNI 1 -# define USE_AVX512 0 -# include "adler32_template.h" -#endif - -#if (GCC_PREREQ(8, 1) || CLANG_PREREQ(6, 0, 10000000) || MSVC_PREREQ(1920)) && \ - !(CLANG_PREREQ(18, 0, 18000000) && !CLANG_PREREQ(19, 0, 19000000)) && \ - !defined(LIBDEFLATE_ASSEMBLER_DOES_NOT_SUPPORT_AVX512VNNI) -/* - * AVX512VNNI implementation using 256-bit vectors. This is very similar to the - * AVX-VNNI implementation but takes advantage of masking and more registers. - * This is used on certain older Intel CPUs, specifically Ice Lake and Tiger - * Lake, which support AVX512VNNI but downclock a bit too eagerly when ZMM - * registers are used. - */ -# define adler32_x86_avx512_vl256_vnni adler32_x86_avx512_vl256_vnni -# define SUFFIX _avx512_vl256_vnni -# define ATTRIBUTES _target_attribute("avx512bw,avx512vl,avx512vnni") -# define VL 32 -# define USE_VNNI 1 -# define USE_AVX512 1 -# include "adler32_template.h" - -/* - * AVX512VNNI implementation using 512-bit vectors. This is used on CPUs that - * have a good AVX-512 implementation including AVX512VNNI. - */ -# define adler32_x86_avx512_vl512_vnni adler32_x86_avx512_vl512_vnni -# define SUFFIX _avx512_vl512_vnni -# define ATTRIBUTES _target_attribute("avx512bw,avx512vnni") -# define VL 64 -# define USE_VNNI 1 -# define USE_AVX512 1 -# include "adler32_template.h" -#endif - -static inline adler32_func_t -arch_select_adler32_func(void) -{ - const u32 features MAYBE_UNUSED = get_x86_cpu_features(); - -#ifdef adler32_x86_avx512_vl512_vnni - if ((features & X86_CPU_FEATURE_ZMM) && - HAVE_AVX512BW(features) && HAVE_AVX512VNNI(features)) - return adler32_x86_avx512_vl512_vnni; -#endif -#ifdef adler32_x86_avx512_vl256_vnni - if (HAVE_AVX512BW(features) && HAVE_AVX512VL(features) && - HAVE_AVX512VNNI(features)) - return adler32_x86_avx512_vl256_vnni; -#endif -#ifdef adler32_x86_avx2_vnni - if (HAVE_AVX2(features) && HAVE_AVXVNNI(features)) - return adler32_x86_avx2_vnni; -#endif -#ifdef adler32_x86_avx2 - if (HAVE_AVX2(features)) - return adler32_x86_avx2; -#endif -#ifdef adler32_x86_sse2 - if (HAVE_SSE2(features)) - return adler32_x86_sse2; -#endif - return NULL; -} -#define arch_select_adler32_func arch_select_adler32_func - -#endif /* LIB_X86_ADLER32_IMPL_H */ diff --git a/vendor/libdeflate/lib/x86/adler32_template.h b/vendor/libdeflate/lib/x86/adler32_template.h deleted file mode 100644 index 1593ee5a3f..0000000000 --- a/vendor/libdeflate/lib/x86/adler32_template.h +++ /dev/null @@ -1,518 +0,0 @@ -/* - * x86/adler32_template.h - template for vectorized Adler-32 implementations - * - * Copyright 2016 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * This file is a "template" for instantiating Adler-32 functions for x86. - * The "parameters" are: - * - * SUFFIX: - * Name suffix to append to all instantiated functions. - * ATTRIBUTES: - * Target function attributes to use. Must satisfy the dependencies of the - * other parameters as follows: - * VL=16 && USE_VNNI=0 && USE_AVX512=0: at least sse2 - * VL=32 && USE_VNNI=0 && USE_AVX512=0: at least avx2 - * VL=32 && USE_VNNI=1 && USE_AVX512=0: at least avx2,avxvnni - * VL=32 && USE_VNNI=1 && USE_AVX512=1: at least avx512bw,avx512vl,avx512vnni - * VL=64 && USE_VNNI=1 && USE_AVX512=1: at least avx512bw,avx512vnni - * (Other combinations are not useful and have not been tested.) - * VL: - * Vector length in bytes. Must be 16, 32, or 64. - * USE_VNNI: - * If 1, use the VNNI dot product based algorithm. - * If 0, use the legacy SSE2 and AVX2 compatible algorithm. - * USE_AVX512: - * If 1, take advantage of AVX-512 features such as masking. This doesn't - * enable the use of 512-bit vectors; the vector length is controlled by - * VL. If 0, assume that the CPU might not support AVX-512. - */ - -#if VL == 16 -# define vec_t __m128i -# define mask_t u16 -# define LOG2_VL 4 -# define VADD8(a, b) _mm_add_epi8((a), (b)) -# define VADD16(a, b) _mm_add_epi16((a), (b)) -# define VADD32(a, b) _mm_add_epi32((a), (b)) -# if USE_AVX512 -# define VDPBUSD(a, b, c) _mm_dpbusd_epi32((a), (b), (c)) -# else -# define VDPBUSD(a, b, c) _mm_dpbusd_avx_epi32((a), (b), (c)) -# endif -# define VLOAD(p) _mm_load_si128((const void *)(p)) -# define VLOADU(p) _mm_loadu_si128((const void *)(p)) -# define VMADD16(a, b) _mm_madd_epi16((a), (b)) -# define VMASKZ_LOADU(mask, p) _mm_maskz_loadu_epi8((mask), (p)) -# define VMULLO32(a, b) _mm_mullo_epi32((a), (b)) -# define VSAD8(a, b) _mm_sad_epu8((a), (b)) -# define VSET1_8(a) _mm_set1_epi8(a) -# define VSET1_32(a) _mm_set1_epi32(a) -# define VSETZERO() _mm_setzero_si128() -# define VSLL32(a, b) _mm_slli_epi32((a), (b)) -# define VUNPACKLO8(a, b) _mm_unpacklo_epi8((a), (b)) -# define VUNPACKHI8(a, b) _mm_unpackhi_epi8((a), (b)) -#elif VL == 32 -# define vec_t __m256i -# define mask_t u32 -# define LOG2_VL 5 -# define VADD8(a, b) _mm256_add_epi8((a), (b)) -# define VADD16(a, b) _mm256_add_epi16((a), (b)) -# define VADD32(a, b) _mm256_add_epi32((a), (b)) -# if USE_AVX512 -# define VDPBUSD(a, b, c) _mm256_dpbusd_epi32((a), (b), (c)) -# else -# define VDPBUSD(a, b, c) _mm256_dpbusd_avx_epi32((a), (b), (c)) -# endif -# define VLOAD(p) _mm256_load_si256((const void *)(p)) -# define VLOADU(p) _mm256_loadu_si256((const void *)(p)) -# define VMADD16(a, b) _mm256_madd_epi16((a), (b)) -# define VMASKZ_LOADU(mask, p) _mm256_maskz_loadu_epi8((mask), (p)) -# define VMULLO32(a, b) _mm256_mullo_epi32((a), (b)) -# define VSAD8(a, b) _mm256_sad_epu8((a), (b)) -# define VSET1_8(a) _mm256_set1_epi8(a) -# define VSET1_32(a) _mm256_set1_epi32(a) -# define VSETZERO() _mm256_setzero_si256() -# define VSLL32(a, b) _mm256_slli_epi32((a), (b)) -# define VUNPACKLO8(a, b) _mm256_unpacklo_epi8((a), (b)) -# define VUNPACKHI8(a, b) _mm256_unpackhi_epi8((a), (b)) -#elif VL == 64 -# define vec_t __m512i -# define mask_t u64 -# define LOG2_VL 6 -# define VADD8(a, b) _mm512_add_epi8((a), (b)) -# define VADD16(a, b) _mm512_add_epi16((a), (b)) -# define VADD32(a, b) _mm512_add_epi32((a), (b)) -# define VDPBUSD(a, b, c) _mm512_dpbusd_epi32((a), (b), (c)) -# define VLOAD(p) _mm512_load_si512((const void *)(p)) -# define VLOADU(p) _mm512_loadu_si512((const void *)(p)) -# define VMADD16(a, b) _mm512_madd_epi16((a), (b)) -# define VMASKZ_LOADU(mask, p) _mm512_maskz_loadu_epi8((mask), (p)) -# define VMULLO32(a, b) _mm512_mullo_epi32((a), (b)) -# define VSAD8(a, b) _mm512_sad_epu8((a), (b)) -# define VSET1_8(a) _mm512_set1_epi8(a) -# define VSET1_32(a) _mm512_set1_epi32(a) -# define VSETZERO() _mm512_setzero_si512() -# define VSLL32(a, b) _mm512_slli_epi32((a), (b)) -# define VUNPACKLO8(a, b) _mm512_unpacklo_epi8((a), (b)) -# define VUNPACKHI8(a, b) _mm512_unpackhi_epi8((a), (b)) -#else -# error "unsupported vector length" -#endif - -#define VADD32_3X(a, b, c) VADD32(VADD32((a), (b)), (c)) -#define VADD32_4X(a, b, c, d) VADD32(VADD32((a), (b)), VADD32((c), (d))) -#define VADD32_5X(a, b, c, d, e) VADD32((a), VADD32_4X((b), (c), (d), (e))) -#define VADD32_7X(a, b, c, d, e, f, g) \ - VADD32(VADD32_3X((a), (b), (c)), VADD32_4X((d), (e), (f), (g))) - -/* Sum the 32-bit elements of v_s1 and add them to s1, and likewise for s2. */ -#undef reduce_to_32bits -static forceinline ATTRIBUTES void -ADD_SUFFIX(reduce_to_32bits)(vec_t v_s1, vec_t v_s2, u32 *s1_p, u32 *s2_p) -{ - __m128i v_s1_128, v_s2_128; -#if VL == 16 - { - v_s1_128 = v_s1; - v_s2_128 = v_s2; - } -#else - { - __m256i v_s1_256, v_s2_256; - #if VL == 32 - v_s1_256 = v_s1; - v_s2_256 = v_s2; - #else - /* Reduce 512 bits to 256 bits. */ - v_s1_256 = _mm256_add_epi32(_mm512_extracti64x4_epi64(v_s1, 0), - _mm512_extracti64x4_epi64(v_s1, 1)); - v_s2_256 = _mm256_add_epi32(_mm512_extracti64x4_epi64(v_s2, 0), - _mm512_extracti64x4_epi64(v_s2, 1)); - #endif - /* Reduce 256 bits to 128 bits. */ - v_s1_128 = _mm_add_epi32(_mm256_extracti128_si256(v_s1_256, 0), - _mm256_extracti128_si256(v_s1_256, 1)); - v_s2_128 = _mm_add_epi32(_mm256_extracti128_si256(v_s2_256, 0), - _mm256_extracti128_si256(v_s2_256, 1)); - } -#endif - - /* - * Reduce 128 bits to 32 bits. - * - * If the bytes were summed into v_s1 using psadbw + paddd, then ignore - * the odd-indexed elements of v_s1_128 since they are zero. - */ -#if USE_VNNI - v_s1_128 = _mm_add_epi32(v_s1_128, _mm_shuffle_epi32(v_s1_128, 0x31)); -#endif - v_s2_128 = _mm_add_epi32(v_s2_128, _mm_shuffle_epi32(v_s2_128, 0x31)); - v_s1_128 = _mm_add_epi32(v_s1_128, _mm_shuffle_epi32(v_s1_128, 0x02)); - v_s2_128 = _mm_add_epi32(v_s2_128, _mm_shuffle_epi32(v_s2_128, 0x02)); - - *s1_p += (u32)_mm_cvtsi128_si32(v_s1_128); - *s2_p += (u32)_mm_cvtsi128_si32(v_s2_128); -} -#define reduce_to_32bits ADD_SUFFIX(reduce_to_32bits) - -static ATTRIBUTES u32 -ADD_SUFFIX(adler32_x86)(u32 adler, const u8 *p, size_t len) -{ -#if USE_VNNI - /* This contains the bytes [VL, VL-1, VL-2, ..., 1]. */ - static const u8 _aligned_attribute(VL) raw_mults[VL] = { - #if VL == 64 - 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, - 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, - #endif - #if VL >= 32 - 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, - #endif - 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, - }; - const vec_t ones = VSET1_8(1); -#else - /* - * This contains the 16-bit values [2*VL, 2*VL - 1, 2*VL - 2, ..., 1]. - * For VL==32 the ordering is weird because it has to match the way that - * vpunpcklbw and vpunpckhbw work on 128-bit lanes separately. - */ - static const u16 _aligned_attribute(VL) raw_mults[4][VL / 2] = { - #if VL == 16 - { 32, 31, 30, 29, 28, 27, 26, 25 }, - { 24, 23, 22, 21, 20, 19, 18, 17 }, - { 16, 15, 14, 13, 12, 11, 10, 9 }, - { 8, 7, 6, 5, 4, 3, 2, 1 }, - #elif VL == 32 - { 64, 63, 62, 61, 60, 59, 58, 57, 48, 47, 46, 45, 44, 43, 42, 41 }, - { 56, 55, 54, 53, 52, 51, 50, 49, 40, 39, 38, 37, 36, 35, 34, 33 }, - { 32, 31, 30, 29, 28, 27, 26, 25, 16, 15, 14, 13, 12, 11, 10, 9 }, - { 24, 23, 22, 21, 20, 19, 18, 17, 8, 7, 6, 5, 4, 3, 2, 1 }, - #else - # error "unsupported parameters" - #endif - }; - const vec_t mults_a = VLOAD(raw_mults[0]); - const vec_t mults_b = VLOAD(raw_mults[1]); - const vec_t mults_c = VLOAD(raw_mults[2]); - const vec_t mults_d = VLOAD(raw_mults[3]); -#endif - const vec_t zeroes = VSETZERO(); - u32 s1 = adler & 0xFFFF; - u32 s2 = adler >> 16; - - /* - * If the length is large and the pointer is misaligned, align it. - * For smaller lengths, just take the misaligned load penalty. - */ - if (unlikely(len > 65536 && ((uintptr_t)p & (VL-1)))) { - do { - s1 += *p++; - s2 += s1; - len--; - } while ((uintptr_t)p & (VL-1)); - s1 %= DIVISOR; - s2 %= DIVISOR; - } - -#if USE_VNNI - /* - * This is Adler-32 using the vpdpbusd instruction from AVX512VNNI or - * AVX-VNNI. vpdpbusd multiplies the unsigned bytes of one vector by - * the signed bytes of another vector and adds the sums in groups of 4 - * to the 32-bit elements of a third vector. We use it in two ways: - * multiplying the data bytes by a sequence like 64,63,62,...,1 for - * calculating part of s2, and multiplying the data bytes by an all-ones - * sequence 1,1,1,...,1 for calculating s1 and part of s2. The all-ones - * trick seems to be faster than the alternative of vpsadbw + vpaddd. - */ - while (len) { - /* - * Calculate the length of the next data chunk such that s1 and - * s2 are guaranteed to not exceed UINT32_MAX. - */ - size_t n = MIN(len, MAX_CHUNK_LEN & ~(4*VL - 1)); - vec_t mults = VLOAD(raw_mults); - vec_t v_s1 = zeroes; - vec_t v_s2 = zeroes; - - s2 += s1 * n; - len -= n; - - if (n >= 4*VL) { - vec_t v_s1_b = zeroes; - vec_t v_s1_c = zeroes; - vec_t v_s1_d = zeroes; - vec_t v_s2_b = zeroes; - vec_t v_s2_c = zeroes; - vec_t v_s2_d = zeroes; - vec_t v_s1_sums = zeroes; - vec_t v_s1_sums_b = zeroes; - vec_t v_s1_sums_c = zeroes; - vec_t v_s1_sums_d = zeroes; - vec_t tmp0, tmp1; - - do { - vec_t data_a = VLOADU(p + 0*VL); - vec_t data_b = VLOADU(p + 1*VL); - vec_t data_c = VLOADU(p + 2*VL); - vec_t data_d = VLOADU(p + 3*VL); - - /* - * Workaround for gcc bug where it generates - * unnecessary move instructions - * (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=107892) - */ - #if GCC_PREREQ(1, 0) - __asm__("" : "+v" (data_a), "+v" (data_b), - "+v" (data_c), "+v" (data_d)); - #endif - - v_s2 = VDPBUSD(v_s2, data_a, mults); - v_s2_b = VDPBUSD(v_s2_b, data_b, mults); - v_s2_c = VDPBUSD(v_s2_c, data_c, mults); - v_s2_d = VDPBUSD(v_s2_d, data_d, mults); - - v_s1_sums = VADD32(v_s1_sums, v_s1); - v_s1_sums_b = VADD32(v_s1_sums_b, v_s1_b); - v_s1_sums_c = VADD32(v_s1_sums_c, v_s1_c); - v_s1_sums_d = VADD32(v_s1_sums_d, v_s1_d); - - v_s1 = VDPBUSD(v_s1, data_a, ones); - v_s1_b = VDPBUSD(v_s1_b, data_b, ones); - v_s1_c = VDPBUSD(v_s1_c, data_c, ones); - v_s1_d = VDPBUSD(v_s1_d, data_d, ones); - - /* Same gcc bug workaround. See above */ - #if GCC_PREREQ(1, 0) && !defined(ARCH_X86_32) - __asm__("" : "+v" (v_s2), "+v" (v_s2_b), - "+v" (v_s2_c), "+v" (v_s2_d), - "+v" (v_s1_sums), - "+v" (v_s1_sums_b), - "+v" (v_s1_sums_c), - "+v" (v_s1_sums_d), - "+v" (v_s1), "+v" (v_s1_b), - "+v" (v_s1_c), "+v" (v_s1_d)); - #endif - p += 4*VL; - n -= 4*VL; - } while (n >= 4*VL); - - /* - * Reduce into v_s1 and v_s2 as follows: - * - * v_s2 = v_s2 + v_s2_b + v_s2_c + v_s2_d + - * (4*VL)*(v_s1_sums + v_s1_sums_b + - * v_s1_sums_c + v_s1_sums_d) + - * (3*VL)*v_s1 + (2*VL)*v_s1_b + VL*v_s1_c - * v_s1 = v_s1 + v_s1_b + v_s1_c + v_s1_d - */ - tmp0 = VADD32(v_s1, v_s1_b); - tmp1 = VADD32(v_s1, v_s1_c); - v_s1_sums = VADD32_4X(v_s1_sums, v_s1_sums_b, - v_s1_sums_c, v_s1_sums_d); - v_s1 = VADD32_3X(tmp0, v_s1_c, v_s1_d); - v_s2 = VADD32_7X(VSLL32(v_s1_sums, LOG2_VL + 2), - VSLL32(tmp0, LOG2_VL + 1), - VSLL32(tmp1, LOG2_VL), - v_s2, v_s2_b, v_s2_c, v_s2_d); - } - - /* Process the last 0 <= n < 4*VL bytes of the chunk. */ - if (n >= 2*VL) { - const vec_t data_a = VLOADU(p + 0*VL); - const vec_t data_b = VLOADU(p + 1*VL); - - v_s2 = VADD32(v_s2, VSLL32(v_s1, LOG2_VL + 1)); - v_s1 = VDPBUSD(v_s1, data_a, ones); - v_s1 = VDPBUSD(v_s1, data_b, ones); - v_s2 = VDPBUSD(v_s2, data_a, VSET1_8(VL)); - v_s2 = VDPBUSD(v_s2, data_a, mults); - v_s2 = VDPBUSD(v_s2, data_b, mults); - p += 2*VL; - n -= 2*VL; - } - if (n) { - /* Process the last 0 < n < 2*VL bytes of the chunk. */ - vec_t data; - - v_s2 = VADD32(v_s2, VMULLO32(v_s1, VSET1_32(n))); - - mults = VADD8(mults, VSET1_8((int)n - VL)); - if (n > VL) { - data = VLOADU(p); - v_s1 = VDPBUSD(v_s1, data, ones); - v_s2 = VDPBUSD(v_s2, data, mults); - p += VL; - n -= VL; - mults = VADD8(mults, VSET1_8(-VL)); - } - /* - * Process the last 0 < n <= VL bytes of the chunk. - * Utilize a masked load if it's available. - */ - #if USE_AVX512 - data = VMASKZ_LOADU((mask_t)-1 >> (VL - n), p); - #else - data = zeroes; - memcpy(&data, p, n); - #endif - v_s1 = VDPBUSD(v_s1, data, ones); - v_s2 = VDPBUSD(v_s2, data, mults); - p += n; - } - - reduce_to_32bits(v_s1, v_s2, &s1, &s2); - s1 %= DIVISOR; - s2 %= DIVISOR; - } -#else /* USE_VNNI */ - /* - * This is Adler-32 for SSE2 and AVX2. - * - * To horizontally sum bytes, use psadbw + paddd, where one of the - * arguments to psadbw is all-zeroes. - * - * For the s2 contribution from (2*VL - i)*data[i] for each of the 2*VL - * bytes of each iteration of the inner loop, use punpck{l,h}bw + paddw - * to sum, for each i across iterations, byte i into a corresponding - * 16-bit counter in v_byte_sums_*. After the inner loop, use pmaddwd - * to multiply each counter by (2*VL - i), then add the products to s2. - * - * An alternative implementation would use pmaddubsw and pmaddwd in the - * inner loop to do (2*VL - i)*data[i] directly and add the products in - * groups of 4 to 32-bit counters. However, on average that approach - * seems to be slower than the current approach which delays the - * multiplications. Also, pmaddubsw requires SSSE3; the current - * approach keeps the implementation aligned between SSE2 and AVX2. - * - * The inner loop processes 2*VL bytes per iteration. Increasing this - * to 4*VL doesn't seem to be helpful here. - */ - while (len) { - /* - * Calculate the length of the next data chunk such that s1 and - * s2 are guaranteed to not exceed UINT32_MAX, and every - * v_byte_sums_* counter is guaranteed to not exceed INT16_MAX. - * It's INT16_MAX, not UINT16_MAX, because v_byte_sums_* are - * used with pmaddwd which does signed multiplication. In the - * SSE2 case this limits chunks to 4096 bytes instead of 5536. - */ - size_t n = MIN(len, MIN(2 * VL * (INT16_MAX / UINT8_MAX), - MAX_CHUNK_LEN) & ~(2*VL - 1)); - len -= n; - - if (n >= 2*VL) { - vec_t v_s1 = zeroes; - vec_t v_s1_sums = zeroes; - vec_t v_byte_sums_a = zeroes; - vec_t v_byte_sums_b = zeroes; - vec_t v_byte_sums_c = zeroes; - vec_t v_byte_sums_d = zeroes; - vec_t v_s2; - - s2 += s1 * (n & ~(2*VL - 1)); - - do { - vec_t data_a = VLOADU(p + 0*VL); - vec_t data_b = VLOADU(p + 1*VL); - - v_s1_sums = VADD32(v_s1_sums, v_s1); - v_byte_sums_a = VADD16(v_byte_sums_a, - VUNPACKLO8(data_a, zeroes)); - v_byte_sums_b = VADD16(v_byte_sums_b, - VUNPACKHI8(data_a, zeroes)); - v_byte_sums_c = VADD16(v_byte_sums_c, - VUNPACKLO8(data_b, zeroes)); - v_byte_sums_d = VADD16(v_byte_sums_d, - VUNPACKHI8(data_b, zeroes)); - v_s1 = VADD32(v_s1, - VADD32(VSAD8(data_a, zeroes), - VSAD8(data_b, zeroes))); - /* - * Workaround for gcc bug where it generates - * unnecessary move instructions - * (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=107892) - */ - #if GCC_PREREQ(1, 0) - __asm__("" : "+x" (v_s1), "+x" (v_s1_sums), - "+x" (v_byte_sums_a), - "+x" (v_byte_sums_b), - "+x" (v_byte_sums_c), - "+x" (v_byte_sums_d)); - #endif - p += 2*VL; - n -= 2*VL; - } while (n >= 2*VL); - - /* - * Calculate v_s2 as (2*VL)*v_s1_sums + - * [2*VL, 2*VL - 1, 2*VL - 2, ..., 1] * v_byte_sums. - * Then update s1 and s2 from v_s1 and v_s2. - */ - v_s2 = VADD32_5X(VSLL32(v_s1_sums, LOG2_VL + 1), - VMADD16(v_byte_sums_a, mults_a), - VMADD16(v_byte_sums_b, mults_b), - VMADD16(v_byte_sums_c, mults_c), - VMADD16(v_byte_sums_d, mults_d)); - reduce_to_32bits(v_s1, v_s2, &s1, &s2); - } - /* - * Process the last 0 <= n < 2*VL bytes of the chunk using - * scalar instructions and reduce s1 and s2 mod DIVISOR. - */ - ADLER32_CHUNK(s1, s2, p, n); - } -#endif /* !USE_VNNI */ - return (s2 << 16) | s1; -} - -#undef vec_t -#undef mask_t -#undef LOG2_VL -#undef VADD8 -#undef VADD16 -#undef VADD32 -#undef VDPBUSD -#undef VLOAD -#undef VLOADU -#undef VMADD16 -#undef VMASKZ_LOADU -#undef VMULLO32 -#undef VSAD8 -#undef VSET1_8 -#undef VSET1_32 -#undef VSETZERO -#undef VSLL32 -#undef VUNPACKLO8 -#undef VUNPACKHI8 - -#undef SUFFIX -#undef ATTRIBUTES -#undef VL -#undef USE_VNNI -#undef USE_AVX512 diff --git a/vendor/libdeflate/lib/x86/cpu_features.c b/vendor/libdeflate/lib/x86/cpu_features.c deleted file mode 100644 index a4e09ada65..0000000000 --- a/vendor/libdeflate/lib/x86/cpu_features.c +++ /dev/null @@ -1,213 +0,0 @@ -/* - * x86/cpu_features.c - feature detection for x86 CPUs - * - * Copyright 2016 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "../cpu_features_common.h" /* must be included first */ -#include "cpu_features.h" - -#ifdef X86_CPU_FEATURES_KNOWN -/* Runtime x86 CPU feature detection is supported. */ - -/* Execute the CPUID instruction. */ -static inline void -cpuid(u32 leaf, u32 subleaf, u32 *a, u32 *b, u32 *c, u32 *d) -{ -#ifdef _MSC_VER - int result[4]; - - __cpuidex(result, leaf, subleaf); - *a = result[0]; - *b = result[1]; - *c = result[2]; - *d = result[3]; -#else - __asm__ volatile("cpuid" : "=a" (*a), "=b" (*b), "=c" (*c), "=d" (*d) - : "a" (leaf), "c" (subleaf)); -#endif -} - -/* Read an extended control register. */ -static inline u64 -read_xcr(u32 index) -{ -#ifdef _MSC_VER - return _xgetbv(index); -#else - u32 d, a; - - /* - * Execute the "xgetbv" instruction. Old versions of binutils do not - * recognize this instruction, so list the raw bytes instead. - * - * This must be 'volatile' to prevent this code from being moved out - * from under the check for OSXSAVE. - */ - __asm__ volatile(".byte 0x0f, 0x01, 0xd0" : - "=d" (d), "=a" (a) : "c" (index)); - - return ((u64)d << 32) | a; -#endif -} - -static const struct cpu_feature x86_cpu_feature_table[] = { - {X86_CPU_FEATURE_SSE2, "sse2"}, - {X86_CPU_FEATURE_PCLMULQDQ, "pclmulqdq"}, - {X86_CPU_FEATURE_AVX, "avx"}, - {X86_CPU_FEATURE_AVX2, "avx2"}, - {X86_CPU_FEATURE_BMI2, "bmi2"}, - {X86_CPU_FEATURE_ZMM, "zmm"}, - {X86_CPU_FEATURE_AVX512BW, "avx512bw"}, - {X86_CPU_FEATURE_AVX512VL, "avx512vl"}, - {X86_CPU_FEATURE_VPCLMULQDQ, "vpclmulqdq"}, - {X86_CPU_FEATURE_AVX512VNNI, "avx512_vnni"}, - {X86_CPU_FEATURE_AVXVNNI, "avx_vnni"}, -}; - -volatile u32 libdeflate_x86_cpu_features = 0; - -static inline bool -os_supports_avx512(u64 xcr0) -{ -#ifdef __APPLE__ - /* - * The Darwin kernel had a bug where it could corrupt the opmask - * registers. See - * https://community.intel.com/t5/Software-Tuning-Performance/MacOS-Darwin-kernel-bug-clobbers-AVX-512-opmask-register-state/m-p/1327259 - * Darwin also does not initially set the XCR0 bits for AVX512, but they - * are set if the thread tries to use AVX512 anyway. Thus, to safely - * and consistently use AVX512 on macOS we'd need to check the kernel - * version as well as detect AVX512 support using a macOS-specific - * method. We don't bother with this, especially given Apple's - * transition to arm64. - */ - return false; -#else - return (xcr0 & 0xe6) == 0xe6; -#endif -} - -/* - * Don't use 512-bit vectors (ZMM registers) on Intel CPUs before Rocket Lake - * and Sapphire Rapids, due to the overly-eager downclocking which can reduce - * the performance of workloads that use ZMM registers only occasionally. - */ -static inline bool -allow_512bit_vectors(const u32 manufacturer[3], u32 family, u32 model) -{ -#ifdef TEST_SUPPORT__DO_NOT_USE - return true; -#endif - if (memcmp(manufacturer, "GenuineIntel", 12) != 0) - return true; - if (family != 6) - return true; - switch (model) { - case 85: /* Skylake (Server), Cascade Lake, Cooper Lake */ - case 106: /* Ice Lake (Server) */ - case 108: /* Ice Lake (Server) */ - case 126: /* Ice Lake (Client) */ - case 140: /* Tiger Lake */ - case 141: /* Tiger Lake */ - return false; - } - return true; -} - -/* Initialize libdeflate_x86_cpu_features. */ -void libdeflate_init_x86_cpu_features(void) -{ - u32 max_leaf; - u32 manufacturer[3]; - u32 family, model; - u32 a, b, c, d; - u64 xcr0 = 0; - u32 features = 0; - - /* EAX=0: Highest Function Parameter and Manufacturer ID */ - cpuid(0, 0, &max_leaf, &manufacturer[0], &manufacturer[2], - &manufacturer[1]); - if (max_leaf < 1) - goto out; - - /* EAX=1: Processor Info and Feature Bits */ - cpuid(1, 0, &a, &b, &c, &d); - family = (a >> 8) & 0xf; - model = (a >> 4) & 0xf; - if (family == 6 || family == 0xf) - model += (a >> 12) & 0xf0; - if (family == 0xf) - family += (a >> 20) & 0xff; - if (d & (1 << 26)) - features |= X86_CPU_FEATURE_SSE2; - /* - * No known CPUs have pclmulqdq without sse4.1, so in practice code - * targeting pclmulqdq can use sse4.1 instructions. But to be safe, - * explicitly check for both the pclmulqdq and sse4.1 bits. - */ - if ((c & (1 << 1)) && (c & (1 << 19))) - features |= X86_CPU_FEATURE_PCLMULQDQ; - if (c & (1 << 27)) - xcr0 = read_xcr(0); - if ((c & (1 << 28)) && ((xcr0 & 0x6) == 0x6)) - features |= X86_CPU_FEATURE_AVX; - - if (max_leaf < 7) - goto out; - - /* EAX=7, ECX=0: Extended Features */ - cpuid(7, 0, &a, &b, &c, &d); - if (b & (1 << 8)) - features |= X86_CPU_FEATURE_BMI2; - if ((xcr0 & 0x6) == 0x6) { - if (b & (1 << 5)) - features |= X86_CPU_FEATURE_AVX2; - if (c & (1 << 10)) - features |= X86_CPU_FEATURE_VPCLMULQDQ; - } - if (os_supports_avx512(xcr0)) { - if (allow_512bit_vectors(manufacturer, family, model)) - features |= X86_CPU_FEATURE_ZMM; - if (b & (1 << 30)) - features |= X86_CPU_FEATURE_AVX512BW; - if (b & (1U << 31)) - features |= X86_CPU_FEATURE_AVX512VL; - if (c & (1 << 11)) - features |= X86_CPU_FEATURE_AVX512VNNI; - } - - /* EAX=7, ECX=1: Extended Features */ - cpuid(7, 1, &a, &b, &c, &d); - if ((a & (1 << 4)) && ((xcr0 & 0x6) == 0x6)) - features |= X86_CPU_FEATURE_AVXVNNI; - -out: - disable_cpu_features_for_testing(&features, x86_cpu_feature_table, - ARRAY_LEN(x86_cpu_feature_table)); - - libdeflate_x86_cpu_features = features | X86_CPU_FEATURES_KNOWN; -} - -#endif /* X86_CPU_FEATURES_KNOWN */ diff --git a/vendor/libdeflate/lib/x86/cpu_features.h b/vendor/libdeflate/lib/x86/cpu_features.h deleted file mode 100644 index cb225b983d..0000000000 --- a/vendor/libdeflate/lib/x86/cpu_features.h +++ /dev/null @@ -1,170 +0,0 @@ -/* - * x86/cpu_features.h - feature detection for x86 CPUs - * - * Copyright 2016 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef LIB_X86_CPU_FEATURES_H -#define LIB_X86_CPU_FEATURES_H - -#include "../lib_common.h" - -#if defined(ARCH_X86_32) || defined(ARCH_X86_64) - -#define X86_CPU_FEATURE_SSE2 (1 << 0) -#define X86_CPU_FEATURE_PCLMULQDQ (1 << 1) -#define X86_CPU_FEATURE_AVX (1 << 2) -#define X86_CPU_FEATURE_AVX2 (1 << 3) -#define X86_CPU_FEATURE_BMI2 (1 << 4) -/* - * ZMM indicates whether 512-bit vectors (zmm registers) should be used. On - * some CPUs, to avoid downclocking issues we don't set ZMM even if the CPU and - * operating system support AVX-512. On these CPUs, we may still use AVX-512 - * instructions, but only with xmm and ymm registers. - */ -#define X86_CPU_FEATURE_ZMM (1 << 5) -#define X86_CPU_FEATURE_AVX512BW (1 << 6) -#define X86_CPU_FEATURE_AVX512VL (1 << 7) -#define X86_CPU_FEATURE_VPCLMULQDQ (1 << 8) -#define X86_CPU_FEATURE_AVX512VNNI (1 << 9) -#define X86_CPU_FEATURE_AVXVNNI (1 << 10) - -#if defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER) -/* Runtime x86 CPU feature detection is supported. */ -# define X86_CPU_FEATURES_KNOWN (1U << 31) -extern volatile u32 libdeflate_x86_cpu_features; - -void libdeflate_init_x86_cpu_features(void); - -static inline u32 get_x86_cpu_features(void) -{ - if (libdeflate_x86_cpu_features == 0) - libdeflate_init_x86_cpu_features(); - return libdeflate_x86_cpu_features; -} -/* - * x86 intrinsics are also supported. Include the headers needed to use them. - * Normally just immintrin.h suffices. With clang in MSVC compatibility mode, - * immintrin.h incorrectly skips including sub-headers, so include those too. - */ -# include -# if defined(_MSC_VER) && defined(__clang__) -# include -# include -# include -# include -# include -# include -# include -# include -# if __has_include() -# include -# endif -# if __has_include() -# include -# endif -# if __has_include() -# include -# endif -# if __has_include() -# include -# endif -# if __has_include() -# include -# endif -# endif -#else -static inline u32 get_x86_cpu_features(void) { return 0; } -#endif - -#if defined(__SSE2__) || \ - (defined(_MSC_VER) && \ - (defined(ARCH_X86_64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2))) -# define HAVE_SSE2(features) 1 -# define HAVE_SSE2_NATIVE 1 -#else -# define HAVE_SSE2(features) ((features) & X86_CPU_FEATURE_SSE2) -# define HAVE_SSE2_NATIVE 0 -#endif - -#if (defined(__PCLMUL__) && defined(__SSE4_1__)) || \ - (defined(_MSC_VER) && defined(__AVX2__)) -# define HAVE_PCLMULQDQ(features) 1 -#else -# define HAVE_PCLMULQDQ(features) ((features) & X86_CPU_FEATURE_PCLMULQDQ) -#endif - -#ifdef __AVX__ -# define HAVE_AVX(features) 1 -#else -# define HAVE_AVX(features) ((features) & X86_CPU_FEATURE_AVX) -#endif - -#ifdef __AVX2__ -# define HAVE_AVX2(features) 1 -#else -# define HAVE_AVX2(features) ((features) & X86_CPU_FEATURE_AVX2) -#endif - -#if defined(__BMI2__) || (defined(_MSC_VER) && defined(__AVX2__)) -# define HAVE_BMI2(features) 1 -# define HAVE_BMI2_NATIVE 1 -#else -# define HAVE_BMI2(features) ((features) & X86_CPU_FEATURE_BMI2) -# define HAVE_BMI2_NATIVE 0 -#endif - -#ifdef __AVX512BW__ -# define HAVE_AVX512BW(features) 1 -#else -# define HAVE_AVX512BW(features) ((features) & X86_CPU_FEATURE_AVX512BW) -#endif - -#ifdef __AVX512VL__ -# define HAVE_AVX512VL(features) 1 -#else -# define HAVE_AVX512VL(features) ((features) & X86_CPU_FEATURE_AVX512VL) -#endif - -#ifdef __VPCLMULQDQ__ -# define HAVE_VPCLMULQDQ(features) 1 -#else -# define HAVE_VPCLMULQDQ(features) ((features) & X86_CPU_FEATURE_VPCLMULQDQ) -#endif - -#ifdef __AVX512VNNI__ -# define HAVE_AVX512VNNI(features) 1 -#else -# define HAVE_AVX512VNNI(features) ((features) & X86_CPU_FEATURE_AVX512VNNI) -#endif - -#ifdef __AVXVNNI__ -# define HAVE_AVXVNNI(features) 1 -#else -# define HAVE_AVXVNNI(features) ((features) & X86_CPU_FEATURE_AVXVNNI) -#endif - -#endif /* ARCH_X86_32 || ARCH_X86_64 */ - -#endif /* LIB_X86_CPU_FEATURES_H */ diff --git a/vendor/libdeflate/lib/x86/crc32_impl.h b/vendor/libdeflate/lib/x86/crc32_impl.h deleted file mode 100644 index 4fbdef7ebf..0000000000 --- a/vendor/libdeflate/lib/x86/crc32_impl.h +++ /dev/null @@ -1,160 +0,0 @@ -/* - * x86/crc32_impl.h - x86 implementations of the gzip CRC-32 algorithm - * - * Copyright 2016 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef LIB_X86_CRC32_IMPL_H -#define LIB_X86_CRC32_IMPL_H - -#include "cpu_features.h" - -/* - * pshufb(x, shift_tab[len..len+15]) left shifts x by 16-len bytes. - * pshufb(x, shift_tab[len+16..len+31]) right shifts x by len bytes. - */ -static const u8 MAYBE_UNUSED shift_tab[48] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -}; - -#if defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER) -/* - * PCLMULQDQ implementation. This targets PCLMULQDQ+SSE4.1, since in practice - * all CPUs that support PCLMULQDQ also support SSE4.1. - */ -# define crc32_x86_pclmulqdq crc32_x86_pclmulqdq -# define SUFFIX _pclmulqdq -# define ATTRIBUTES _target_attribute("pclmul,sse4.1") -# define VL 16 -# define USE_AVX512 0 -# include "crc32_pclmul_template.h" - -/* - * PCLMULQDQ/AVX implementation. Same as above, but this is compiled with AVX - * enabled so that the compiler can generate VEX-coded instructions which can be - * slightly more efficient. It still uses 128-bit vectors. - */ -# define crc32_x86_pclmulqdq_avx crc32_x86_pclmulqdq_avx -# define SUFFIX _pclmulqdq_avx -# define ATTRIBUTES _target_attribute("pclmul,avx") -# define VL 16 -# define USE_AVX512 0 -# include "crc32_pclmul_template.h" -#endif - -/* - * VPCLMULQDQ/AVX2 implementation. This is used on CPUs that have AVX2 and - * VPCLMULQDQ but don't have AVX-512, for example Intel Alder Lake. - * - * Currently this can't be enabled with MSVC because MSVC has a bug where it - * incorrectly assumes that VPCLMULQDQ implies AVX-512: - * https://developercommunity.visualstudio.com/t/Compiler-incorrectly-assumes-VAES-and-VP/10578785 - * - * gcc 8.1 and 8.2 had a similar bug where they assumed that - * _mm256_clmulepi64_epi128() always needed AVX512. It's fixed in gcc 8.3. - * - * _mm256_zextsi128_si256() requires gcc 10. - */ -#if (GCC_PREREQ(10, 1) || CLANG_PREREQ(6, 0, 10000000)) && \ - !defined(LIBDEFLATE_ASSEMBLER_DOES_NOT_SUPPORT_VPCLMULQDQ) -# define crc32_x86_vpclmulqdq_avx2 crc32_x86_vpclmulqdq_avx2 -# define SUFFIX _vpclmulqdq_avx2 -# define ATTRIBUTES _target_attribute("vpclmulqdq,pclmul,avx2") -# define VL 32 -# define USE_AVX512 0 -# include "crc32_pclmul_template.h" -#endif - -#if (GCC_PREREQ(10, 1) || CLANG_PREREQ(6, 0, 10000000) || MSVC_PREREQ(1920)) && \ - !(CLANG_PREREQ(18, 0, 18000000) && !CLANG_PREREQ(19, 0, 19000000)) && \ - !defined(LIBDEFLATE_ASSEMBLER_DOES_NOT_SUPPORT_VPCLMULQDQ) -/* - * VPCLMULQDQ/AVX512 implementation using 256-bit vectors. This is very similar - * to the VPCLMULQDQ/AVX2 implementation but takes advantage of the vpternlog - * instruction and more registers. This is used on certain older Intel CPUs, - * specifically Ice Lake and Tiger Lake, which support VPCLMULQDQ and AVX512 but - * downclock a bit too eagerly when ZMM registers are used. - * - * _mm256_zextsi128_si256() requires gcc 10. - */ -# define crc32_x86_vpclmulqdq_avx512_vl256 crc32_x86_vpclmulqdq_avx512_vl256 -# define SUFFIX _vpclmulqdq_avx512_vl256 -# define ATTRIBUTES _target_attribute("vpclmulqdq,pclmul,avx512bw,avx512vl") -# define VL 32 -# define USE_AVX512 1 -# include "crc32_pclmul_template.h" - -/* - * VPCLMULQDQ/AVX512 implementation using 512-bit vectors. This is used on CPUs - * that have a good AVX-512 implementation including VPCLMULQDQ. - * - * _mm512_zextsi128_si512() requires gcc 10. - */ -# define crc32_x86_vpclmulqdq_avx512_vl512 crc32_x86_vpclmulqdq_avx512_vl512 -# define SUFFIX _vpclmulqdq_avx512_vl512 -# define ATTRIBUTES _target_attribute("vpclmulqdq,pclmul,avx512bw,avx512vl") -# define VL 64 -# define USE_AVX512 1 -# include "crc32_pclmul_template.h" -#endif - -static inline crc32_func_t -arch_select_crc32_func(void) -{ - const u32 features MAYBE_UNUSED = get_x86_cpu_features(); - -#ifdef crc32_x86_vpclmulqdq_avx512_vl512 - if ((features & X86_CPU_FEATURE_ZMM) && - HAVE_VPCLMULQDQ(features) && HAVE_PCLMULQDQ(features) && - HAVE_AVX512BW(features) && HAVE_AVX512VL(features)) - return crc32_x86_vpclmulqdq_avx512_vl512; -#endif -#ifdef crc32_x86_vpclmulqdq_avx512_vl256 - if (HAVE_VPCLMULQDQ(features) && HAVE_PCLMULQDQ(features) && - HAVE_AVX512BW(features) && HAVE_AVX512VL(features)) - return crc32_x86_vpclmulqdq_avx512_vl256; -#endif -#ifdef crc32_x86_vpclmulqdq_avx2 - if (HAVE_VPCLMULQDQ(features) && HAVE_PCLMULQDQ(features) && - HAVE_AVX2(features)) - return crc32_x86_vpclmulqdq_avx2; -#endif -#ifdef crc32_x86_pclmulqdq_avx - if (HAVE_PCLMULQDQ(features) && HAVE_AVX(features)) - return crc32_x86_pclmulqdq_avx; -#endif -#ifdef crc32_x86_pclmulqdq - if (HAVE_PCLMULQDQ(features)) - return crc32_x86_pclmulqdq; -#endif - return NULL; -} -#define arch_select_crc32_func arch_select_crc32_func - -#endif /* LIB_X86_CRC32_IMPL_H */ diff --git a/vendor/libdeflate/lib/x86/crc32_pclmul_template.h b/vendor/libdeflate/lib/x86/crc32_pclmul_template.h deleted file mode 100644 index 8bb8bfd308..0000000000 --- a/vendor/libdeflate/lib/x86/crc32_pclmul_template.h +++ /dev/null @@ -1,424 +0,0 @@ -/* - * x86/crc32_pclmul_template.h - gzip CRC-32 with PCLMULQDQ instructions - * - * Copyright 2016 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * This file is a "template" for instantiating PCLMULQDQ-based crc32_x86 - * functions. The "parameters" are: - * - * SUFFIX: - * Name suffix to append to all instantiated functions. - * ATTRIBUTES: - * Target function attributes to use. Must satisfy the dependencies of the - * other parameters as follows: - * VL=16 && USE_AVX512=0: at least pclmul,sse4.1 - * VL=32 && USE_AVX512=0: at least vpclmulqdq,pclmul,avx2 - * VL=32 && USE_AVX512=1: at least vpclmulqdq,pclmul,avx512bw,avx512vl - * VL=64 && USE_AVX512=1: at least vpclmulqdq,pclmul,avx512bw,avx512vl - * (Other combinations are not useful and have not been tested.) - * VL: - * Vector length in bytes. Must be 16, 32, or 64. - * USE_AVX512: - * If 1, take advantage of AVX-512 features such as masking and the - * vpternlog instruction. This doesn't enable the use of 512-bit vectors; - * the vector length is controlled by VL. If 0, assume that the CPU might - * not support AVX-512. - * - * The overall algorithm used is CRC folding with carryless multiplication - * instructions. Note that the x86 crc32 instruction cannot be used, as it is - * for a different polynomial, not the gzip one. For an explanation of CRC - * folding with carryless multiplication instructions, see - * scripts/gen-crc32-consts.py and the following blog posts and papers: - * - * "An alternative exposition of crc32_4k_pclmulqdq" - * https://www.corsix.org/content/alternative-exposition-crc32_4k_pclmulqdq - * - * "Fast CRC Computation for Generic Polynomials Using PCLMULQDQ Instruction" - * https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf - * - * The original pclmulqdq instruction does one 64x64 to 128-bit carryless - * multiplication. The VPCLMULQDQ feature added instructions that do two - * parallel 64x64 to 128-bit carryless multiplications in combination with AVX - * or AVX512VL, or four in combination with AVX512F. - */ - -#if VL == 16 -# define vec_t __m128i -# define fold_vec fold_vec128 -# define VLOADU(p) _mm_loadu_si128((const void *)(p)) -# define VXOR(a, b) _mm_xor_si128((a), (b)) -# define M128I_TO_VEC(a) a -# define MULTS_8V _mm_set_epi64x(CRC32_X991_MODG, CRC32_X1055_MODG) -# define MULTS_4V _mm_set_epi64x(CRC32_X479_MODG, CRC32_X543_MODG) -# define MULTS_2V _mm_set_epi64x(CRC32_X223_MODG, CRC32_X287_MODG) -# define MULTS_1V _mm_set_epi64x(CRC32_X95_MODG, CRC32_X159_MODG) -#elif VL == 32 -# define vec_t __m256i -# define fold_vec fold_vec256 -# define VLOADU(p) _mm256_loadu_si256((const void *)(p)) -# define VXOR(a, b) _mm256_xor_si256((a), (b)) -# define M128I_TO_VEC(a) _mm256_zextsi128_si256(a) -# define MULTS(a, b) _mm256_set_epi64x(a, b, a, b) -# define MULTS_8V MULTS(CRC32_X2015_MODG, CRC32_X2079_MODG) -# define MULTS_4V MULTS(CRC32_X991_MODG, CRC32_X1055_MODG) -# define MULTS_2V MULTS(CRC32_X479_MODG, CRC32_X543_MODG) -# define MULTS_1V MULTS(CRC32_X223_MODG, CRC32_X287_MODG) -#elif VL == 64 -# define vec_t __m512i -# define fold_vec fold_vec512 -# define VLOADU(p) _mm512_loadu_si512((const void *)(p)) -# define VXOR(a, b) _mm512_xor_si512((a), (b)) -# define M128I_TO_VEC(a) _mm512_zextsi128_si512(a) -# define MULTS(a, b) _mm512_set_epi64(a, b, a, b, a, b, a, b) -# define MULTS_8V MULTS(CRC32_X4063_MODG, CRC32_X4127_MODG) -# define MULTS_4V MULTS(CRC32_X2015_MODG, CRC32_X2079_MODG) -# define MULTS_2V MULTS(CRC32_X991_MODG, CRC32_X1055_MODG) -# define MULTS_1V MULTS(CRC32_X479_MODG, CRC32_X543_MODG) -#else -# error "unsupported vector length" -#endif - -#undef fold_vec128 -static forceinline ATTRIBUTES __m128i -ADD_SUFFIX(fold_vec128)(__m128i src, __m128i dst, __m128i /* __v2du */ mults) -{ - dst = _mm_xor_si128(dst, _mm_clmulepi64_si128(src, mults, 0x00)); - dst = _mm_xor_si128(dst, _mm_clmulepi64_si128(src, mults, 0x11)); - return dst; -} -#define fold_vec128 ADD_SUFFIX(fold_vec128) - -#if VL >= 32 -#undef fold_vec256 -static forceinline ATTRIBUTES __m256i -ADD_SUFFIX(fold_vec256)(__m256i src, __m256i dst, __m256i /* __v4du */ mults) -{ -#if USE_AVX512 - /* vpternlog with immediate 0x96 is a three-argument XOR. */ - return _mm256_ternarylogic_epi32( - _mm256_clmulepi64_epi128(src, mults, 0x00), - _mm256_clmulepi64_epi128(src, mults, 0x11), - dst, - 0x96); -#else - return _mm256_xor_si256( - _mm256_xor_si256(dst, - _mm256_clmulepi64_epi128(src, mults, 0x00)), - _mm256_clmulepi64_epi128(src, mults, 0x11)); -#endif -} -#define fold_vec256 ADD_SUFFIX(fold_vec256) -#endif /* VL >= 32 */ - -#if VL >= 64 -#undef fold_vec512 -static forceinline ATTRIBUTES __m512i -ADD_SUFFIX(fold_vec512)(__m512i src, __m512i dst, __m512i /* __v8du */ mults) -{ - /* vpternlog with immediate 0x96 is a three-argument XOR. */ - return _mm512_ternarylogic_epi32( - _mm512_clmulepi64_epi128(src, mults, 0x00), - _mm512_clmulepi64_epi128(src, mults, 0x11), - dst, - 0x96); -} -#define fold_vec512 ADD_SUFFIX(fold_vec512) -#endif /* VL >= 64 */ - -/* - * Given 'x' containing a 16-byte polynomial, and a pointer 'p' that points to - * the next '1 <= len <= 15' data bytes, rearrange the concatenation of 'x' and - * the data into vectors x0 and x1 that contain 'len' bytes and 16 bytes, - * respectively. Then fold x0 into x1 and return the result. - * Assumes that 'p + len - 16' is in-bounds. - */ -#undef fold_lessthan16bytes -static forceinline ATTRIBUTES __m128i -ADD_SUFFIX(fold_lessthan16bytes)(__m128i x, const u8 *p, size_t len, - __m128i /* __v2du */ mults_128b) -{ - __m128i lshift = _mm_loadu_si128((const void *)&shift_tab[len]); - __m128i rshift = _mm_loadu_si128((const void *)&shift_tab[len + 16]); - __m128i x0, x1; - - /* x0 = x left-shifted by '16 - len' bytes */ - x0 = _mm_shuffle_epi8(x, lshift); - - /* - * x1 = the last '16 - len' bytes from x (i.e. x right-shifted by 'len' - * bytes) followed by the remaining data. - */ - x1 = _mm_blendv_epi8(_mm_shuffle_epi8(x, rshift), - _mm_loadu_si128((const void *)(p + len - 16)), - /* msb 0/1 of each byte selects byte from arg1/2 */ - rshift); - - return fold_vec128(x0, x1, mults_128b); -} -#define fold_lessthan16bytes ADD_SUFFIX(fold_lessthan16bytes) - -static ATTRIBUTES u32 -ADD_SUFFIX(crc32_x86)(u32 crc, const u8 *p, size_t len) -{ - /* - * mults_{N}v are the vectors of multipliers for folding across N vec_t - * vectors, i.e. N*VL*8 bits. mults_128b are the two multipliers for - * folding across 128 bits. mults_128b differs from mults_1v when - * VL != 16. All multipliers are 64-bit, to match what pclmulqdq needs, - * but since this is for CRC-32 only their low 32 bits are nonzero. - * For more details, see scripts/gen-crc32-consts.py. - */ - const vec_t mults_8v = MULTS_8V; - const vec_t mults_4v = MULTS_4V; - const vec_t mults_2v = MULTS_2V; - const vec_t mults_1v = MULTS_1V; - const __m128i mults_128b = _mm_set_epi64x(CRC32_X95_MODG, CRC32_X159_MODG); - const __m128i barrett_reduction_constants = - _mm_set_epi64x(CRC32_BARRETT_CONSTANT_2, CRC32_BARRETT_CONSTANT_1); - vec_t v0, v1, v2, v3, v4, v5, v6, v7; - __m128i x0 = _mm_cvtsi32_si128(crc); - __m128i x1; - - if (len < 8*VL) { - if (len < VL) { - STATIC_ASSERT(VL == 16 || VL == 32 || VL == 64); - if (len < 16) { - #if USE_AVX512 - if (len < 4) - return crc32_slice1(crc, p, len); - /* - * Handle 4 <= len <= 15 bytes by doing a masked - * load, XOR'ing the current CRC with the first - * 4 bytes, left-shifting by '16 - len' bytes to - * align the result to the end of x0 (so that it - * becomes the low-order coefficients of a - * 128-bit polynomial), and then doing the usual - * reduction from 128 bits to 32 bits. - */ - x0 = _mm_xor_si128( - x0, _mm_maskz_loadu_epi8((1 << len) - 1, p)); - x0 = _mm_shuffle_epi8( - x0, _mm_loadu_si128((const void *)&shift_tab[len])); - goto reduce_x0; - #else - return crc32_slice1(crc, p, len); - #endif - } - /* - * Handle 16 <= len < VL bytes where VL is 32 or 64. - * Use 128-bit instructions so that these lengths aren't - * slower with VL > 16 than with VL=16. - */ - x0 = _mm_xor_si128(_mm_loadu_si128((const void *)p), x0); - if (len >= 32) { - x0 = fold_vec128(x0, _mm_loadu_si128((const void *)(p + 16)), - mults_128b); - if (len >= 48) - x0 = fold_vec128(x0, _mm_loadu_si128((const void *)(p + 32)), - mults_128b); - } - p += len & ~15; - goto less_than_16_remaining; - } - v0 = VXOR(VLOADU(p), M128I_TO_VEC(x0)); - if (len < 2*VL) { - p += VL; - goto less_than_vl_remaining; - } - v1 = VLOADU(p + 1*VL); - if (len < 4*VL) { - p += 2*VL; - goto less_than_2vl_remaining; - } - v2 = VLOADU(p + 2*VL); - v3 = VLOADU(p + 3*VL); - p += 4*VL; - } else { - /* - * If the length is large and the pointer is misaligned, align - * it. For smaller lengths, just take the misaligned load - * penalty. Note that on recent x86 CPUs, vmovdqu with an - * aligned address is just as fast as vmovdqa, so there's no - * need to use vmovdqa in the main loop. - */ - if (len > 65536 && ((uintptr_t)p & (VL-1))) { - size_t align = -(uintptr_t)p & (VL-1); - - len -= align; - x0 = _mm_xor_si128(_mm_loadu_si128((const void *)p), x0); - p += 16; - if (align & 15) { - x0 = fold_lessthan16bytes(x0, p, align & 15, - mults_128b); - p += align & 15; - align &= ~15; - } - while (align) { - x0 = fold_vec128(x0, *(const __m128i *)p, - mults_128b); - p += 16; - align -= 16; - } - v0 = M128I_TO_VEC(x0); - # if VL == 32 - v0 = _mm256_inserti128_si256(v0, *(const __m128i *)p, 1); - # elif VL == 64 - v0 = _mm512_inserti32x4(v0, *(const __m128i *)p, 1); - v0 = _mm512_inserti64x4(v0, *(const __m256i *)(p + 16), 1); - # endif - p -= 16; - } else { - v0 = VXOR(VLOADU(p), M128I_TO_VEC(x0)); - } - v1 = VLOADU(p + 1*VL); - v2 = VLOADU(p + 2*VL); - v3 = VLOADU(p + 3*VL); - v4 = VLOADU(p + 4*VL); - v5 = VLOADU(p + 5*VL); - v6 = VLOADU(p + 6*VL); - v7 = VLOADU(p + 7*VL); - p += 8*VL; - - /* - * This is the main loop, processing 8*VL bytes per iteration. - * 4*VL is usually enough and would result in smaller code, but - * Skylake and Cascade Lake need 8*VL to get full performance. - */ - while (len >= 16*VL) { - v0 = fold_vec(v0, VLOADU(p + 0*VL), mults_8v); - v1 = fold_vec(v1, VLOADU(p + 1*VL), mults_8v); - v2 = fold_vec(v2, VLOADU(p + 2*VL), mults_8v); - v3 = fold_vec(v3, VLOADU(p + 3*VL), mults_8v); - v4 = fold_vec(v4, VLOADU(p + 4*VL), mults_8v); - v5 = fold_vec(v5, VLOADU(p + 5*VL), mults_8v); - v6 = fold_vec(v6, VLOADU(p + 6*VL), mults_8v); - v7 = fold_vec(v7, VLOADU(p + 7*VL), mults_8v); - p += 8*VL; - len -= 8*VL; - } - - /* Fewer than 8*VL bytes remain. */ - v0 = fold_vec(v0, v4, mults_4v); - v1 = fold_vec(v1, v5, mults_4v); - v2 = fold_vec(v2, v6, mults_4v); - v3 = fold_vec(v3, v7, mults_4v); - if (len & (4*VL)) { - v0 = fold_vec(v0, VLOADU(p + 0*VL), mults_4v); - v1 = fold_vec(v1, VLOADU(p + 1*VL), mults_4v); - v2 = fold_vec(v2, VLOADU(p + 2*VL), mults_4v); - v3 = fold_vec(v3, VLOADU(p + 3*VL), mults_4v); - p += 4*VL; - } - } - /* Fewer than 4*VL bytes remain. */ - v0 = fold_vec(v0, v2, mults_2v); - v1 = fold_vec(v1, v3, mults_2v); - if (len & (2*VL)) { - v0 = fold_vec(v0, VLOADU(p + 0*VL), mults_2v); - v1 = fold_vec(v1, VLOADU(p + 1*VL), mults_2v); - p += 2*VL; - } -less_than_2vl_remaining: - /* Fewer than 2*VL bytes remain. */ - v0 = fold_vec(v0, v1, mults_1v); - if (len & VL) { - v0 = fold_vec(v0, VLOADU(p), mults_1v); - p += VL; - } -less_than_vl_remaining: - /* - * Fewer than VL bytes remain. Reduce v0 (length VL bytes) to x0 - * (length 16 bytes) and fold in any 16-byte data segments that remain. - */ -#if VL == 16 - x0 = v0; -#else - { - #if VL == 32 - __m256i y0 = v0; - #else - const __m256i mults_256b = - _mm256_set_epi64x(CRC32_X223_MODG, CRC32_X287_MODG, - CRC32_X223_MODG, CRC32_X287_MODG); - __m256i y0 = fold_vec256(_mm512_extracti64x4_epi64(v0, 0), - _mm512_extracti64x4_epi64(v0, 1), - mults_256b); - if (len & 32) { - y0 = fold_vec256(y0, _mm256_loadu_si256((const void *)p), - mults_256b); - p += 32; - } - #endif - x0 = fold_vec128(_mm256_extracti128_si256(y0, 0), - _mm256_extracti128_si256(y0, 1), mults_128b); - } - if (len & 16) { - x0 = fold_vec128(x0, _mm_loadu_si128((const void *)p), - mults_128b); - p += 16; - } -#endif -less_than_16_remaining: - len &= 15; - - /* Handle any remainder of 1 to 15 bytes. */ - if (len) - x0 = fold_lessthan16bytes(x0, p, len, mults_128b); -#if USE_AVX512 -reduce_x0: -#endif - /* - * Multiply the remaining 128-bit message polynomial 'x0' by x^32, then - * reduce it modulo the generator polynomial G. This gives the CRC. - * - * This implementation matches that used in crc-pclmul-template.S from - * https://lore.kernel.org/r/20250210174540.161705-4-ebiggers@kernel.org/ - * with the parameters n=32 and LSB_CRC=1 (what the gzip CRC uses). See - * there for a detailed explanation of the math used here. - */ - x0 = _mm_xor_si128(_mm_clmulepi64_si128(x0, mults_128b, 0x10), - _mm_bsrli_si128(x0, 8)); - x1 = _mm_clmulepi64_si128(x0, barrett_reduction_constants, 0x00); - x1 = _mm_clmulepi64_si128(x1, barrett_reduction_constants, 0x10); - x0 = _mm_xor_si128(x0, x1); - return _mm_extract_epi32(x0, 2); -} - -#undef vec_t -#undef fold_vec -#undef VLOADU -#undef VXOR -#undef M128I_TO_VEC -#undef MULTS -#undef MULTS_8V -#undef MULTS_4V -#undef MULTS_2V -#undef MULTS_1V - -#undef SUFFIX -#undef ATTRIBUTES -#undef VL -#undef USE_AVX512 diff --git a/vendor/libdeflate/lib/x86/decompress_impl.h b/vendor/libdeflate/lib/x86/decompress_impl.h deleted file mode 100644 index daedcf2dbd..0000000000 --- a/vendor/libdeflate/lib/x86/decompress_impl.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef LIB_X86_DECOMPRESS_IMPL_H -#define LIB_X86_DECOMPRESS_IMPL_H - -#include "cpu_features.h" - -/* - * BMI2 optimized decompression function. - * - * With gcc and clang we just compile the whole function with - * __attribute__((target("bmi2"))), and the compiler uses bmi2 automatically. - * - * With MSVC, there is no target function attribute, but it's still possible to - * use bmi2 intrinsics explicitly. Currently we mostly don't, but there's a - * case in which we do (see below), so we at least take advantage of that. - * However, MSVC from VS2017 (toolset v141) apparently miscompiles the _bzhi_*() - * intrinsics. It seems to be fixed in VS2022. Hence, use MSVC_PREREQ(1930). - */ -#if defined(__GNUC__) || defined(__clang__) || MSVC_PREREQ(1930) -# define deflate_decompress_bmi2 deflate_decompress_bmi2 -# define FUNCNAME deflate_decompress_bmi2 -# define ATTRIBUTES _target_attribute("bmi2") - /* - * Even with __attribute__((target("bmi2"))), gcc doesn't reliably use the - * bzhi instruction for 'word & BITMASK(count)'. So use the bzhi intrinsic - * explicitly. EXTRACT_VARBITS() is equivalent to 'word & BITMASK(count)'; - * EXTRACT_VARBITS8() is equivalent to 'word & BITMASK((u8)count)'. - * Nevertheless, their implementation using the bzhi intrinsic is identical, - * as the bzhi instruction truncates the count to 8 bits implicitly. - */ -# ifndef __clang__ -# ifdef ARCH_X86_64 -# define EXTRACT_VARBITS(word, count) _bzhi_u64((word), (count)) -# define EXTRACT_VARBITS8(word, count) _bzhi_u64((word), (count)) -# else -# define EXTRACT_VARBITS(word, count) _bzhi_u32((word), (count)) -# define EXTRACT_VARBITS8(word, count) _bzhi_u32((word), (count)) -# endif -# endif -# include "../decompress_template.h" -#endif - -#if defined(deflate_decompress_bmi2) && HAVE_BMI2_NATIVE -#define DEFAULT_IMPL deflate_decompress_bmi2 -#else -static inline decompress_func_t -arch_select_decompress_func(void) -{ -#ifdef deflate_decompress_bmi2 - if (HAVE_BMI2(get_x86_cpu_features())) - return deflate_decompress_bmi2; -#endif - return NULL; -} -#define arch_select_decompress_func arch_select_decompress_func -#endif - -#endif /* LIB_X86_DECOMPRESS_IMPL_H */ diff --git a/vendor/libdeflate/lib/x86/matchfinder_impl.h b/vendor/libdeflate/lib/x86/matchfinder_impl.h deleted file mode 100644 index 37a4960a7d..0000000000 --- a/vendor/libdeflate/lib/x86/matchfinder_impl.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * x86/matchfinder_impl.h - x86 implementations of matchfinder functions - * - * Copyright 2016 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef LIB_X86_MATCHFINDER_IMPL_H -#define LIB_X86_MATCHFINDER_IMPL_H - -#include "cpu_features.h" - -#ifdef __AVX2__ -static forceinline void -matchfinder_init_avx2(mf_pos_t *data, size_t size) -{ - __m256i *p = (__m256i *)data; - __m256i v = _mm256_set1_epi16(MATCHFINDER_INITVAL); - - STATIC_ASSERT(MATCHFINDER_MEM_ALIGNMENT % sizeof(*p) == 0); - STATIC_ASSERT(MATCHFINDER_SIZE_ALIGNMENT % (4 * sizeof(*p)) == 0); - STATIC_ASSERT(sizeof(mf_pos_t) == 2); - - do { - p[0] = v; - p[1] = v; - p[2] = v; - p[3] = v; - p += 4; - size -= 4 * sizeof(*p); - } while (size != 0); -} -#define matchfinder_init matchfinder_init_avx2 - -static forceinline void -matchfinder_rebase_avx2(mf_pos_t *data, size_t size) -{ - __m256i *p = (__m256i *)data; - __m256i v = _mm256_set1_epi16((u16)-MATCHFINDER_WINDOW_SIZE); - - STATIC_ASSERT(MATCHFINDER_MEM_ALIGNMENT % sizeof(*p) == 0); - STATIC_ASSERT(MATCHFINDER_SIZE_ALIGNMENT % (4 * sizeof(*p)) == 0); - STATIC_ASSERT(sizeof(mf_pos_t) == 2); - - do { - /* PADDSW: Add Packed Signed Integers With Signed Saturation */ - p[0] = _mm256_adds_epi16(p[0], v); - p[1] = _mm256_adds_epi16(p[1], v); - p[2] = _mm256_adds_epi16(p[2], v); - p[3] = _mm256_adds_epi16(p[3], v); - p += 4; - size -= 4 * sizeof(*p); - } while (size != 0); -} -#define matchfinder_rebase matchfinder_rebase_avx2 - -#elif HAVE_SSE2_NATIVE -static forceinline void -matchfinder_init_sse2(mf_pos_t *data, size_t size) -{ - __m128i *p = (__m128i *)data; - __m128i v = _mm_set1_epi16(MATCHFINDER_INITVAL); - - STATIC_ASSERT(MATCHFINDER_MEM_ALIGNMENT % sizeof(*p) == 0); - STATIC_ASSERT(MATCHFINDER_SIZE_ALIGNMENT % (4 * sizeof(*p)) == 0); - STATIC_ASSERT(sizeof(mf_pos_t) == 2); - - do { - p[0] = v; - p[1] = v; - p[2] = v; - p[3] = v; - p += 4; - size -= 4 * sizeof(*p); - } while (size != 0); -} -#define matchfinder_init matchfinder_init_sse2 - -static forceinline void -matchfinder_rebase_sse2(mf_pos_t *data, size_t size) -{ - __m128i *p = (__m128i *)data; - __m128i v = _mm_set1_epi16((u16)-MATCHFINDER_WINDOW_SIZE); - - STATIC_ASSERT(MATCHFINDER_MEM_ALIGNMENT % sizeof(*p) == 0); - STATIC_ASSERT(MATCHFINDER_SIZE_ALIGNMENT % (4 * sizeof(*p)) == 0); - STATIC_ASSERT(sizeof(mf_pos_t) == 2); - - do { - /* PADDSW: Add Packed Signed Integers With Signed Saturation */ - p[0] = _mm_adds_epi16(p[0], v); - p[1] = _mm_adds_epi16(p[1], v); - p[2] = _mm_adds_epi16(p[2], v); - p[3] = _mm_adds_epi16(p[3], v); - p += 4; - size -= 4 * sizeof(*p); - } while (size != 0); -} -#define matchfinder_rebase matchfinder_rebase_sse2 -#endif /* HAVE_SSE2_NATIVE */ - -#endif /* LIB_X86_MATCHFINDER_IMPL_H */ diff --git a/vendor/libdeflate/lib/zlib_compress.c b/vendor/libdeflate/lib/zlib_compress.c deleted file mode 100644 index 12d43602d9..0000000000 --- a/vendor/libdeflate/lib/zlib_compress.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * zlib_compress.c - compress with a zlib wrapper - * - * Copyright 2016 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "deflate_compress.h" -#include "zlib_constants.h" - -LIBDEFLATEAPI size_t -libdeflate_zlib_compress(struct libdeflate_compressor *c, - const void *in, size_t in_nbytes, - void *out, size_t out_nbytes_avail) -{ - u8 *out_next = out; - u16 hdr; - unsigned compression_level; - unsigned level_hint; - size_t deflate_size; - - if (out_nbytes_avail <= ZLIB_MIN_OVERHEAD) - return 0; - - /* 2 byte header: CMF and FLG */ - hdr = (ZLIB_CM_DEFLATE << 8) | (ZLIB_CINFO_32K_WINDOW << 12); - compression_level = libdeflate_get_compression_level(c); - if (compression_level < 2) - level_hint = ZLIB_FASTEST_COMPRESSION; - else if (compression_level < 6) - level_hint = ZLIB_FAST_COMPRESSION; - else if (compression_level < 8) - level_hint = ZLIB_DEFAULT_COMPRESSION; - else - level_hint = ZLIB_SLOWEST_COMPRESSION; - hdr |= level_hint << 6; - hdr |= 31 - (hdr % 31); - - put_unaligned_be16(hdr, out_next); - out_next += 2; - - /* Compressed data */ - deflate_size = libdeflate_deflate_compress(c, in, in_nbytes, out_next, - out_nbytes_avail - ZLIB_MIN_OVERHEAD); - if (deflate_size == 0) - return 0; - out_next += deflate_size; - - /* ADLER32 */ - put_unaligned_be32(libdeflate_adler32(1, in, in_nbytes), out_next); - out_next += 4; - - return out_next - (u8 *)out; -} - -LIBDEFLATEAPI size_t -libdeflate_zlib_compress_bound(struct libdeflate_compressor *c, - size_t in_nbytes) -{ - return ZLIB_MIN_OVERHEAD + - libdeflate_deflate_compress_bound(c, in_nbytes); -} diff --git a/vendor/libdeflate/lib/zlib_constants.h b/vendor/libdeflate/lib/zlib_constants.h deleted file mode 100644 index f304310c73..0000000000 --- a/vendor/libdeflate/lib/zlib_constants.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * zlib_constants.h - constants for the zlib wrapper format - */ - -#ifndef LIB_ZLIB_CONSTANTS_H -#define LIB_ZLIB_CONSTANTS_H - -#define ZLIB_MIN_HEADER_SIZE 2 -#define ZLIB_FOOTER_SIZE 4 -#define ZLIB_MIN_OVERHEAD (ZLIB_MIN_HEADER_SIZE + ZLIB_FOOTER_SIZE) - -#define ZLIB_CM_DEFLATE 8 - -#define ZLIB_CINFO_32K_WINDOW 7 - -#define ZLIB_FASTEST_COMPRESSION 0 -#define ZLIB_FAST_COMPRESSION 1 -#define ZLIB_DEFAULT_COMPRESSION 2 -#define ZLIB_SLOWEST_COMPRESSION 3 - -#endif /* LIB_ZLIB_CONSTANTS_H */ diff --git a/vendor/libdeflate/lib/zlib_decompress.c b/vendor/libdeflate/lib/zlib_decompress.c deleted file mode 100644 index f5e43eaebd..0000000000 --- a/vendor/libdeflate/lib/zlib_decompress.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * zlib_decompress.c - decompress with a zlib wrapper - * - * Copyright 2016 Eric Biggers - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "lib_common.h" -#include "zlib_constants.h" - -LIBDEFLATEAPI enum libdeflate_result -libdeflate_zlib_decompress_ex(struct libdeflate_decompressor *d, - const void *in, size_t in_nbytes, - void *out, size_t out_nbytes_avail, - size_t *actual_in_nbytes_ret, - size_t *actual_out_nbytes_ret) -{ - const u8 *in_next = in; - const u8 * const in_end = in_next + in_nbytes; - u16 hdr; - size_t actual_in_nbytes; - size_t actual_out_nbytes; - enum libdeflate_result result; - - if (in_nbytes < ZLIB_MIN_OVERHEAD) - return LIBDEFLATE_BAD_DATA; - - /* 2 byte header: CMF and FLG */ - hdr = get_unaligned_be16(in_next); - in_next += 2; - - /* FCHECK */ - if ((hdr % 31) != 0) - return LIBDEFLATE_BAD_DATA; - - /* CM */ - if (((hdr >> 8) & 0xF) != ZLIB_CM_DEFLATE) - return LIBDEFLATE_BAD_DATA; - - /* CINFO */ - if ((hdr >> 12) > ZLIB_CINFO_32K_WINDOW) - return LIBDEFLATE_BAD_DATA; - - /* FDICT */ - if ((hdr >> 5) & 1) - return LIBDEFLATE_BAD_DATA; - - /* Compressed data */ - result = libdeflate_deflate_decompress_ex(d, in_next, - in_end - ZLIB_FOOTER_SIZE - in_next, - out, out_nbytes_avail, - &actual_in_nbytes, actual_out_nbytes_ret); - if (result != LIBDEFLATE_SUCCESS) - return result; - - if (actual_out_nbytes_ret) - actual_out_nbytes = *actual_out_nbytes_ret; - else - actual_out_nbytes = out_nbytes_avail; - - in_next += actual_in_nbytes; - - /* ADLER32 */ - if (libdeflate_adler32(1, out, actual_out_nbytes) != - get_unaligned_be32(in_next)) - return LIBDEFLATE_BAD_DATA; - in_next += 4; - - if (actual_in_nbytes_ret) - *actual_in_nbytes_ret = in_next - (u8 *)in; - - return LIBDEFLATE_SUCCESS; -} - -LIBDEFLATEAPI enum libdeflate_result -libdeflate_zlib_decompress(struct libdeflate_decompressor *d, - const void *in, size_t in_nbytes, - void *out, size_t out_nbytes_avail, - size_t *actual_out_nbytes_ret) -{ - return libdeflate_zlib_decompress_ex(d, in, in_nbytes, - out, out_nbytes_avail, - NULL, actual_out_nbytes_ret); -} diff --git a/vendor/libdeflate/libdeflate.h b/vendor/libdeflate/libdeflate.h deleted file mode 100644 index 1821acda8e..0000000000 --- a/vendor/libdeflate/libdeflate.h +++ /dev/null @@ -1,412 +0,0 @@ -/* - * libdeflate.h - public header for libdeflate - */ - -#ifndef LIBDEFLATE_H -#define LIBDEFLATE_H - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define LIBDEFLATE_VERSION_MAJOR 1 -#define LIBDEFLATE_VERSION_MINOR 26 -#define LIBDEFLATE_VERSION_STRING "1.26" - -/* - * Users of libdeflate.dll on Windows can define LIBDEFLATE_DLL to cause - * __declspec(dllimport) to be used. This should be done when it's easy to do. - * Otherwise it's fine to skip it, since it is a very minor performance - * optimization that is irrelevant for most use cases of libdeflate. - */ -#ifndef LIBDEFLATEAPI -# if defined(LIBDEFLATE_DLL) && (defined(_WIN32) || defined(__CYGWIN__)) -# define LIBDEFLATEAPI __declspec(dllimport) -# else -# define LIBDEFLATEAPI -# endif -#endif - -/* ========================================================================== */ -/* Compression */ -/* ========================================================================== */ - -struct libdeflate_compressor; -struct libdeflate_options; - -/* - * libdeflate_alloc_compressor() allocates a new compressor that supports - * DEFLATE, zlib, and gzip compression. 'compression_level' is the compression - * level on a zlib-like scale but with a higher maximum value (1 = fastest, 6 = - * medium/default, 9 = slow, 12 = slowest). Level 0 is also supported and means - * "no compression", specifically "create a valid stream, but only emit - * uncompressed blocks" (this will expand the data slightly). Level -1 is an - * alias indicating a default level of 6. - * - * The return value is a pointer to the new compressor, or NULL if out of memory - * or if the compression level is invalid (i.e. outside the range [-1, 12]). - * - * Note: for compression, the sliding window size is defined at compilation time - * to 32768, the largest size permissible in the DEFLATE format. It cannot be - * changed at runtime. - * - * A single compressor is not safe to use by multiple threads concurrently. - * However, different threads may use different compressors concurrently. - */ -LIBDEFLATEAPI struct libdeflate_compressor * -libdeflate_alloc_compressor(int compression_level); - -/* - * Like libdeflate_alloc_compressor(), but adds the 'options' argument. - */ -LIBDEFLATEAPI struct libdeflate_compressor * -libdeflate_alloc_compressor_ex(int compression_level, - const struct libdeflate_options *options); - -/* - * libdeflate_deflate_compress() performs raw DEFLATE compression on a buffer of - * data. It attempts to compress 'in_nbytes' bytes of data located at 'in' and - * write the result to 'out', which has space for 'out_nbytes_avail' bytes. The - * return value is the compressed size in bytes, or 0 if the data could not be - * compressed to 'out_nbytes_avail' bytes or fewer. - * - * If compression is successful, then the output data is guaranteed to be a - * valid DEFLATE stream that decompresses to the input data. No other - * guarantees are made about the output data. Notably, different versions of - * libdeflate can produce different compressed data for the same uncompressed - * data, even at the same compression level. Do ***NOT*** do things like - * writing tests that compare compressed data to a golden output, as this can - * break when libdeflate is updated. (This property isn't specific to - * libdeflate; the same is true for zlib and other compression libraries too.) - */ -LIBDEFLATEAPI size_t -libdeflate_deflate_compress(struct libdeflate_compressor *compressor, - const void *in, size_t in_nbytes, - void *out, size_t out_nbytes_avail); - -/* - * libdeflate_deflate_compress_bound() returns a worst-case upper bound on the - * number of bytes of compressed data that may be produced by compressing any - * buffer of length less than or equal to 'in_nbytes' using - * libdeflate_deflate_compress() with the specified compressor. This bound will - * necessarily be a number greater than or equal to 'in_nbytes'. It may be an - * overestimate of the true upper bound. The return value is guaranteed to be - * the same for all invocations with the same compressor and same 'in_nbytes'. - * - * As a special case, 'compressor' may be NULL. This causes the bound to be - * taken across *any* libdeflate_compressor that could ever be allocated with - * this build of the library, with any options. - * - * Note that this function is not necessary in many applications. With - * block-based compression, it is usually preferable to separately store the - * uncompressed size of each block and to store any blocks that did not compress - * to less than their original size uncompressed. In that scenario, there is no - * need to know the worst-case compressed size, since the maximum number of - * bytes of compressed data that may be used would always be one less than the - * input length. You can just pass a buffer of that size to - * libdeflate_deflate_compress() and store the data uncompressed if - * libdeflate_deflate_compress() returns 0, indicating that the compressed data - * did not fit into the provided output buffer. - */ -LIBDEFLATEAPI size_t -libdeflate_deflate_compress_bound(struct libdeflate_compressor *compressor, - size_t in_nbytes); - -/* - * Like libdeflate_deflate_compress(), but uses the zlib wrapper format instead - * of raw DEFLATE. - */ -LIBDEFLATEAPI size_t -libdeflate_zlib_compress(struct libdeflate_compressor *compressor, - const void *in, size_t in_nbytes, - void *out, size_t out_nbytes_avail); - -/* - * Like libdeflate_deflate_compress_bound(), but assumes the data will be - * compressed with libdeflate_zlib_compress() rather than with - * libdeflate_deflate_compress(). - */ -LIBDEFLATEAPI size_t -libdeflate_zlib_compress_bound(struct libdeflate_compressor *compressor, - size_t in_nbytes); - -/* - * Like libdeflate_deflate_compress(), but uses the gzip wrapper format instead - * of raw DEFLATE. - */ -LIBDEFLATEAPI size_t -libdeflate_gzip_compress(struct libdeflate_compressor *compressor, - const void *in, size_t in_nbytes, - void *out, size_t out_nbytes_avail); - -/* - * Like libdeflate_deflate_compress_bound(), but assumes the data will be - * compressed with libdeflate_gzip_compress() rather than with - * libdeflate_deflate_compress(). - */ -LIBDEFLATEAPI size_t -libdeflate_gzip_compress_bound(struct libdeflate_compressor *compressor, - size_t in_nbytes); - -/* - * libdeflate_free_compressor() frees a compressor that was allocated with - * libdeflate_alloc_compressor(). If a NULL pointer is passed in, no action is - * taken. - */ -LIBDEFLATEAPI void -libdeflate_free_compressor(struct libdeflate_compressor *compressor); - -/* ========================================================================== */ -/* Decompression */ -/* ========================================================================== */ - -struct libdeflate_decompressor; -struct libdeflate_options; - -/* - * libdeflate_alloc_decompressor() allocates a new decompressor that can be used - * for DEFLATE, zlib, and gzip decompression. The return value is a pointer to - * the new decompressor, or NULL if out of memory. - * - * This function takes no parameters, and the returned decompressor is valid for - * decompressing data that was compressed at any compression level and with any - * sliding window size. - * - * A single decompressor is not safe to use by multiple threads concurrently. - * However, different threads may use different decompressors concurrently. - */ -LIBDEFLATEAPI struct libdeflate_decompressor * -libdeflate_alloc_decompressor(void); - -/* - * Like libdeflate_alloc_decompressor(), but adds the 'options' argument. - */ -LIBDEFLATEAPI struct libdeflate_decompressor * -libdeflate_alloc_decompressor_ex(const struct libdeflate_options *options); - -/* - * Result of a call to libdeflate_deflate_decompress(), - * libdeflate_zlib_decompress(), or libdeflate_gzip_decompress(). - */ -enum libdeflate_result { - /* Decompression was successful. */ - LIBDEFLATE_SUCCESS = 0, - - /* Decompression failed because the compressed data was invalid, - * corrupt, or otherwise unsupported. */ - LIBDEFLATE_BAD_DATA = 1, - - /* A NULL 'actual_out_nbytes_ret' was provided, but the data would have - * decompressed to fewer than 'out_nbytes_avail' bytes. */ - LIBDEFLATE_SHORT_OUTPUT = 2, - - /* The data would have decompressed to more than 'out_nbytes_avail' - * bytes. */ - LIBDEFLATE_INSUFFICIENT_SPACE = 3, -}; - -/* - * libdeflate_deflate_decompress() decompresses a DEFLATE stream from the buffer - * 'in' with compressed size up to 'in_nbytes' bytes. The uncompressed data is - * written to 'out', a buffer with size 'out_nbytes_avail' bytes. If - * decompression succeeds, then 0 (LIBDEFLATE_SUCCESS) is returned. Otherwise, - * a nonzero result code such as LIBDEFLATE_BAD_DATA is returned, and the - * contents of the output buffer are undefined. - * - * Decompression stops at the end of the DEFLATE stream (as indicated by the - * BFINAL flag), even if it is actually shorter than 'in_nbytes' bytes. - * - * libdeflate_deflate_decompress() can be used in cases where the actual - * uncompressed size is known (recommended) or unknown (not recommended): - * - * - If the actual uncompressed size is known, then pass the actual - * uncompressed size as 'out_nbytes_avail' and pass NULL for - * 'actual_out_nbytes_ret'. This makes libdeflate_deflate_decompress() fail - * with LIBDEFLATE_SHORT_OUTPUT if the data decompressed to fewer than the - * specified number of bytes. - * - * - If the actual uncompressed size is unknown, then provide a non-NULL - * 'actual_out_nbytes_ret' and provide a buffer with some size - * 'out_nbytes_avail' that you think is large enough to hold all the - * uncompressed data. In this case, if the data decompresses to less than - * or equal to 'out_nbytes_avail' bytes, then - * libdeflate_deflate_decompress() will write the actual uncompressed size - * to *actual_out_nbytes_ret and return 0 (LIBDEFLATE_SUCCESS). Otherwise, - * it will return LIBDEFLATE_INSUFFICIENT_SPACE if the provided buffer was - * not large enough but no other problems were encountered, or another - * nonzero result code if decompression failed for another reason. - */ -LIBDEFLATEAPI enum libdeflate_result -libdeflate_deflate_decompress(struct libdeflate_decompressor *decompressor, - const void *in, size_t in_nbytes, - void *out, size_t out_nbytes_avail, - size_t *actual_out_nbytes_ret); - -/* - * Like libdeflate_deflate_decompress(), but adds the 'actual_in_nbytes_ret' - * argument. If decompression succeeds and 'actual_in_nbytes_ret' is not NULL, - * then the actual compressed size of the DEFLATE stream (aligned to the next - * byte boundary) is written to *actual_in_nbytes_ret. - */ -LIBDEFLATEAPI enum libdeflate_result -libdeflate_deflate_decompress_ex(struct libdeflate_decompressor *decompressor, - const void *in, size_t in_nbytes, - void *out, size_t out_nbytes_avail, - size_t *actual_in_nbytes_ret, - size_t *actual_out_nbytes_ret); - -/* - * Like libdeflate_deflate_decompress(), but assumes the zlib wrapper format - * instead of raw DEFLATE. - * - * Decompression will stop at the end of the zlib stream, even if it is shorter - * than 'in_nbytes'. If you need to know exactly where the zlib stream ended, - * use libdeflate_zlib_decompress_ex(). - */ -LIBDEFLATEAPI enum libdeflate_result -libdeflate_zlib_decompress(struct libdeflate_decompressor *decompressor, - const void *in, size_t in_nbytes, - void *out, size_t out_nbytes_avail, - size_t *actual_out_nbytes_ret); - -/* - * Like libdeflate_zlib_decompress(), but adds the 'actual_in_nbytes_ret' - * argument. If 'actual_in_nbytes_ret' is not NULL and the decompression - * succeeds (indicating that the first zlib-compressed stream in the input - * buffer was decompressed), then the actual number of input bytes consumed is - * written to *actual_in_nbytes_ret. - */ -LIBDEFLATEAPI enum libdeflate_result -libdeflate_zlib_decompress_ex(struct libdeflate_decompressor *decompressor, - const void *in, size_t in_nbytes, - void *out, size_t out_nbytes_avail, - size_t *actual_in_nbytes_ret, - size_t *actual_out_nbytes_ret); - -/* - * Like libdeflate_deflate_decompress(), but assumes the gzip wrapper format - * instead of raw DEFLATE. - * - * If multiple gzip-compressed members are concatenated, then only the first - * will be decompressed. Use libdeflate_gzip_decompress_ex() if you need - * multi-member support. - */ -LIBDEFLATEAPI enum libdeflate_result -libdeflate_gzip_decompress(struct libdeflate_decompressor *decompressor, - const void *in, size_t in_nbytes, - void *out, size_t out_nbytes_avail, - size_t *actual_out_nbytes_ret); - -/* - * Like libdeflate_gzip_decompress(), but adds the 'actual_in_nbytes_ret' - * argument. If 'actual_in_nbytes_ret' is not NULL and the decompression - * succeeds (indicating that the first gzip-compressed member in the input - * buffer was decompressed), then the actual number of input bytes consumed is - * written to *actual_in_nbytes_ret. - */ -LIBDEFLATEAPI enum libdeflate_result -libdeflate_gzip_decompress_ex(struct libdeflate_decompressor *decompressor, - const void *in, size_t in_nbytes, - void *out, size_t out_nbytes_avail, - size_t *actual_in_nbytes_ret, - size_t *actual_out_nbytes_ret); - -/* - * libdeflate_free_decompressor() frees a decompressor that was allocated with - * libdeflate_alloc_decompressor(). If a NULL pointer is passed in, no action - * is taken. - */ -LIBDEFLATEAPI void -libdeflate_free_decompressor(struct libdeflate_decompressor *decompressor); - -/* ========================================================================== */ -/* Checksums */ -/* ========================================================================== */ - -/* - * libdeflate_adler32() updates a running Adler-32 checksum with 'len' bytes of - * data and returns the updated checksum. When starting a new checksum, the - * required initial value for 'adler' is 1. This value is also returned when - * 'buffer' is specified as NULL. - */ -LIBDEFLATEAPI uint32_t -libdeflate_adler32(uint32_t adler, const void *buffer, size_t len); - - -/* - * libdeflate_crc32() updates a running CRC-32 checksum with 'len' bytes of data - * and returns the updated checksum. When starting a new checksum, the required - * initial value for 'crc' is 0. This value is also returned when 'buffer' is - * specified as NULL. - */ -LIBDEFLATEAPI uint32_t -libdeflate_crc32(uint32_t crc, const void *buffer, size_t len); - -/* ========================================================================== */ -/* Custom memory allocator */ -/* ========================================================================== */ - -/* - * Install a custom memory allocator which libdeflate will use for all memory - * allocations by default. 'malloc_func' is a function that must behave like - * malloc(), and 'free_func' is a function that must behave like free(). - * - * The per-(de)compressor custom memory allocator that can be specified in - * 'struct libdeflate_options' takes priority over this. - * - * This doesn't affect the free() function that will be used to free - * (de)compressors that were already in existence when this is called. - */ -LIBDEFLATEAPI void -libdeflate_set_memory_allocator(void *(*malloc_func)(size_t), - void (*free_func)(void *)); - -/* - * Advanced options. This is the options structure that - * libdeflate_alloc_compressor_ex() and libdeflate_alloc_decompressor_ex() - * require. Most users won't need this and should just use the non-"_ex" - * functions instead. If you do need this, it should be initialized like this: - * - * struct libdeflate_options options; - * - * memset(&options, 0, sizeof(options)); - * options.sizeof_options = sizeof(options); - * // Then set the fields that you need to override the defaults for. - */ -struct libdeflate_options { - - /* - * This field must be set to the struct size. This field exists for - * extensibility, so that fields can be appended to this struct in - * future versions of libdeflate while still supporting old binaries. - */ - size_t sizeof_options; - - /* - * An optional custom memory allocator to use for this (de)compressor. - * 'malloc_func' must be a function that behaves like malloc(), and - * 'free_func' must be a function that behaves like free(). - * - * This is useful in cases where a process might have multiple users of - * libdeflate who want to use different memory allocators. For example, - * a library might want to use libdeflate with a custom memory allocator - * without interfering with user code that might use libdeflate too. - * - * This takes priority over the "global" memory allocator (which by - * default is malloc() and free(), but can be changed by - * libdeflate_set_memory_allocator()). Moreover, libdeflate will never - * call the "global" memory allocator if a per-(de)compressor custom - * allocator is always given. - */ - void *(*malloc_func)(size_t); - void (*free_func)(void *); -}; - -#ifdef __cplusplus -} -#endif - -#endif /* LIBDEFLATE_H */ diff --git a/vendorpull.mask b/vendorpull.mask index d00a463f68..3c4678ad8d 100644 --- a/vendorpull.mask +++ b/vendorpull.mask @@ -40,7 +40,6 @@ vendor/wycheproof.mask vendor/jose-cookbook vendor/jose-cookbook.mask vendor/cmark-gfm.mask -vendor/libdeflate.mask vendor/openapi.mask vendor/unicodetools.mask vendor/unicodetools/unicodetools/data/ucd/dev/NormalizationTest.txt