pax_global_header00006660000000000000000000000064152253637040014521gustar00rootroot0000000000000052 comment=c1c9a9a3665c67e74ab3ec76b9c23d285a4ded5a zxc-0.13.0/000077500000000000000000000000001522536370400124065ustar00rootroot00000000000000zxc-0.13.0/.clang-format000066400000000000000000000003051522536370400147570ustar00rootroot00000000000000--- Language: Cpp BasedOnStyle: Google IndentWidth: 4 TabWidth: 4 UseTab: Never ColumnLimit: 100 AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false BreakBeforeBraces: Attachzxc-0.13.0/.clusterfuzzlite/000077500000000000000000000000001522536370400157425ustar00rootroot00000000000000zxc-0.13.0/.clusterfuzzlite/Dockerfile000066400000000000000000000007151522536370400177370ustar00rootroot00000000000000# Copyright (c) 2025-2026, Bertrand Lebonnois # All rights reserved. # # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. FROM gcr.io/oss-fuzz-base/base-builder:v1@sha256:33f90392168663c1b55ca3daf82089a619db54c1b9a43121198e50271c4ed266 COPY src $SRC/zxc/src COPY include $SRC/zxc/include COPY tests $SRC/zxc/tests COPY .clusterfuzzlite/build.sh $SRC/build.sh WORKDIR $SRC/zxczxc-0.13.0/.clusterfuzzlite/build.sh000066400000000000000000000014551522536370400174020ustar00rootroot00000000000000#!/bin/bash -eu # Copyright (c) 2025-2026, Bertrand Lebonnois # All rights reserved. # # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. FUZZERS="decompress roundtrip seekable pstream dict" LIB_SOURCES="src/lib/zxc_common.c src/lib/zxc_compress.c src/lib/zxc_decompress.c src/lib/zxc_dict.c src/lib/zxc_driver.c src/lib/zxc_dispatch.c src/lib/zxc_huffman.c src/lib/zxc_pivco_tables.c src/lib/zxc_pstream.c src/lib/zxc_seekable.c" for fuzzer in $FUZZERS; do $CC $CFLAGS -I include \ -I src/lib/vendors \ -DZXC_FUNCTION_SUFFIX=_default -DZXC_ONLY_DEFAULT \ $LIB_SOURCES \ tests/fuzz_${fuzzer}.c \ -o $OUT/zxc_fuzzer_${fuzzer} \ $LIB_FUZZING_ENGINE \ -lm -pthread donezxc-0.13.0/.github/000077500000000000000000000000001522536370400137465ustar00rootroot00000000000000zxc-0.13.0/.github/CODEOWNERS000066400000000000000000000000751522536370400153430ustar00rootroot00000000000000# Default owners for everything in the repo * @hellobertrand zxc-0.13.0/.github/CODE_OF_CONDUCT.md000066400000000000000000000054601522536370400165520ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex, gender characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at **zxc.codec@gmail.com**. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. [homepage]: https://www.contributor-covenant.org [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.htmlzxc-0.13.0/.github/CONTRIBUTING.md000066400000000000000000000027741522536370400162110ustar00rootroot00000000000000# Contributing to ZXC Thank you for your interest in contributing to ZXC! This guide will help you get started. ## Developer Certificate of Origin (DCO) By contributing, you certify that: - You have the right to submit the contribution - You agree to license your contribution under the BSD-3-Clause license Add this to your commits: ```bash git commit -s -m "Your commit message" ``` ## License Headers To maintain legal clarity and recognize all contributors, every new source file (.c, .h, .rs, .py, etc.) must include the following header at the very top: ```C /* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ ``` ## Quick Start ### Build and Test ```bash git clone https://github.com/hellobertrand/zxc.git cd zxc mkdir build && cd build cmake .. -DCMAKE_BUILD_TYPE=Debug make -j ctest --output-on-failure ``` ### Format Code ```bash clang-format -i src/lib/*.c include/*.h ``` ## Requirements - **C17** compiler (GCC, Clang, or MSVC) - **CMake** 3.10+ - Follow `.clang-format` style (Google) - All code must be ASCII-only - Pass `ctest` and static analysis ## Submitting Changes 1. Fork and create a feature branch 2. Add tests for new functionality 3. Ensure CI passes (build, tests, benchmarks) 4. Sign your commits with `-s` 5. Open a PR to `main` ## Reporting Issues Include: - ZXC version (`zxc --version`) - OS and architecture - Minimal reproduction steps Thank you for making ZXC better! zxc-0.13.0/.github/FUNDING.yml000066400000000000000000000000261522536370400155610ustar00rootroot00000000000000github: hellobertrand zxc-0.13.0/.github/SECURITY.md000066400000000000000000000004111522536370400155330ustar00rootroot00000000000000# Security Policy ## Reporting a Vulnerability **Please do not report security vulnerabilities through public GitHub issues.** If you have discovered a security issue in ZXC, please email us privately at **zxc.codec@gmail.com**. We will respond within 48 hours.zxc-0.13.0/.github/codeql/000077500000000000000000000000001522536370400152155ustar00rootroot00000000000000zxc-0.13.0/.github/codeql/codeql-config.yml000066400000000000000000000001601522536370400204470ustar00rootroot00000000000000name: "CodeQL Config" paths-ignore: - 'src/lib/vendors/rapidhash.h' queries: - uses: security-and-quality zxc-0.13.0/.github/dependabot.yml000066400000000000000000000012761522536370400166040ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: github-actions directory: / schedule: interval: monthly - package-ecosystem: pip directory: /wrappers/python schedule: interval: monthly - package-ecosystem: pip directory: /.github/requirements schedule: interval: monthly - package-ecosystem: cargo directory: /wrappers/rust schedule: interval: monthly - package-ecosystem: npm directory: /wrappers/nodejs schedule: interval: monthly - package-ecosystem: gomod directory: /wrappers/go schedule: interval: monthly - package-ecosystem: docker directory: /.clusterfuzzlite schedule: interval: monthlyzxc-0.13.0/.github/requirements/000077500000000000000000000000001522536370400164715ustar00rootroot00000000000000zxc-0.13.0/.github/requirements/meson.in000066400000000000000000000000141522536370400201350ustar00rootroot00000000000000meson ninja zxc-0.13.0/.github/requirements/meson.txt000066400000000000000000000037101522536370400203540ustar00rootroot00000000000000# This file was autogenerated by uv via the following command: # uv pip compile --universal --generate-hashes --python-version 3.12 --output-file requirements/meson.txt requirements/meson.in meson==1.11.1 \ --hash=sha256:9b3a023657e393dbc5335b95c561337d49b7a458f5541e47ec44f2cc566e0d80 # via -r requirements/meson.in ninja==1.13.0 \ --hash=sha256:11be2d22027bde06f14c343f01d31446747dbb51e72d00decca2eb99be911e2f \ --hash=sha256:1c97223cdda0417f414bf864cfb73b72d8777e57ebb279c5f6de368de0062988 \ --hash=sha256:3c0b40b1f0bba764644385319028650087b4c1b18cdfa6f45cb39a3669b81aa9 \ --hash=sha256:3d00c692fb717fd511abeb44b8c5d00340c36938c12d6538ba989fe764e79630 \ --hash=sha256:3d7d7779d12cb20c6d054c61b702139fd23a7a964ec8f2c823f1ab1b084150db \ --hash=sha256:4a40ce995ded54d9dc24f8ea37ff3bf62ad192b547f6c7126e7e25045e76f978 \ --hash=sha256:4be9c1b082d244b1ad7ef41eb8ab088aae8c109a9f3f0b3e56a252d3e00f42c1 \ --hash=sha256:5f8e1e8a1a30835eeb51db05cf5a67151ad37542f5a4af2a438e9490915e5b72 \ --hash=sha256:60056592cf495e9a6a4bea3cd178903056ecb0943e4de45a2ea825edb6dc8d3e \ --hash=sha256:6739d3352073341ad284246f81339a384eec091d9851a886dfa5b00a6d48b3e2 \ --hash=sha256:8cfbb80b4a53456ae8a39f90ae3d7a2129f45ea164f43fadfa15dc38c4aef1c9 \ --hash=sha256:aa45b4037b313c2f698bc13306239b8b93b4680eb47e287773156ac9e9304714 \ --hash=sha256:b4f2a072db3c0f944c32793e91532d8948d20d9ab83da9c0c7c15b5768072200 \ --hash=sha256:be7f478ff9f96a128b599a964fc60a6a87b9fa332ee1bd44fa243ac88d50291c \ --hash=sha256:d741a5e6754e0bda767e3274a0f0deeef4807f1fec6c0d7921a0244018926ae5 \ --hash=sha256:e8bad11f8a00b64137e9b315b137d8bb6cbf3086fbdc43bf1f90fd33324d2e96 \ --hash=sha256:fa2a8bfc62e31b08f83127d1613d10821775a0eb334197154c4d6067b7068ff1 \ --hash=sha256:fb46acf6b93b8dd0322adc3a4945452a4e774b75b91293bafcc7b7f8e6517dfa \ --hash=sha256:fb8ee8719f8af47fed145cced4a85f0755dd55d45b2bddaf7431fa89803c5f3e # via -r requirements/meson.in zxc-0.13.0/.github/workflows/000077500000000000000000000000001522536370400160035ustar00rootroot00000000000000zxc-0.13.0/.github/workflows/README.md000066400000000000000000000146211522536370400172660ustar00rootroot00000000000000# GitHub Actions Workflows This directory contains CI/CD workflows for the ZXC compression library. ## Core Workflows ### build.yml - Build & Release **Triggers:** Push to main, tags, pull requests, manual dispatch Builds and tests ZXC across multiple platforms (Linux x86_64/ARM64, macOS ARM64, Windows x64). Generates release artifacts and uploads binaries when tags are pushed. ### multiarch.yml - Multi-Architecture Build **Triggers:** Push to main, pull requests, manual dispatch Comprehensive build matrix testing across multiple architectures including 32-bit and 64-bit variants for Linux (x64, x86, ARM64, ARM) and Windows (x64, x86). Validates compilation compatibility across different platforms. ### multicomp.yml - Compiler Compatibility **Triggers:** Push to main, pull requests, manual dispatch Tests the codebase against a wide range of compilers (various versions of GCC and Clang) to ensure compatibility and identify any compiler-specific issues or warnings. ### benchmark.yml - Performance Benchmark **Triggers:** Push to main (src changes), pull requests, manual dispatch Runs performance benchmarks using LZbench on Ubuntu and macOS. Integrates ZXC into the LZbench framework and tests compression/decompression performance against the Silesia corpus. ## Quality & Security ### coverage.yml - Code Coverage **Triggers:** Push to main, pull requests, manual dispatch Builds the project with coverage instrumentation (`-DZXC_ENABLE_COVERAGE=ON`), runs unit and CLI tests, and generates a coverage report using `lcov`. The report is then uploaded to Codecov for analysis. ### fuzzing.yml - Fuzz Testing **Triggers:** Pull requests, scheduled (every 3 days), manual dispatch Executes fuzz testing using ClusterFuzzLite with multiple sanitizers (address, undefined) on decompression and roundtrip fuzzers. Helps identify memory safety issues and edge cases. ### quality.yml - Code Quality **Triggers:** Push to main, pull requests, manual dispatch Performs static analysis using Cppcheck and Clang Static Analyzer. Runs memory leak detection with Valgrind to ensure code quality and identify potential bugs. ### security.yml - Code Security **Triggers:** Push to main, pull requests Runs CodeQL security analysis to detect potential security vulnerabilities and coding errors in the C/C++ codebase. ### abi-check.yml - ABI Stability Check **Triggers:** Pull requests (lib/header changes), push to main, manual dispatch Builds `libzxc.so` with debug info, generates an ABI XML via [`abidw`](https://sourceware.org/libabigail/), and compares it against the committed baseline at [`docs/abi/libzxc-linux-x86_64.abi.xml`](../../docs/abi/libzxc-linux-x86_64.abi.xml) using `abidiff --no-added-syms`. Adding new symbols passes (MINOR bump); removing or changing existing symbols fails (MAJOR bump required + regenerate baseline). Run with `mode=regenerate` to produce a fresh baseline as a downloadable artifact. ### golden.yml - Golden Format Stability **Triggers:** Push to main, pull requests, manual dispatch Freezes the ZXC on-disk wire format. Runs `sha256sum -c` against the committed manifest [`tests/format/golden.sha256`](../../tests/format/golden.sha256), so the job fails if a single byte of any golden conformance file under [`tests/format/golden/`](../../tests/format/golden/) changes. Also verifies the golden file set and the manifest stay in sync (no file added or removed without updating the manifest). Any intentional format change requires regenerating the corpus with `zxc_golden_gen` and refreshing the manifest in the same commit (see [`tests/format/README.md`](../../tests/format/README.md)). The field-level structural validation runs separately as the `format_golden` ctest in `build.yml`. ### scorecard.yml - OSSF Scorecard **Triggers:** Push to main, scheduled (weekly), manual dispatch Runs the [OSSF Scorecard](https://github.com/ossf/scorecard) analysis to evaluate the project against open source security best practices (branch protection, signed releases, dependency pinning, etc.). Results are published to the OpenSSF public dashboard and uploaded to GitHub's code scanning view as SARIF. ### vendors.yml - Vendor Maintenance **Triggers:** Scheduled (weekly), manual dispatch Automatically checks for and updates third-party dependencies (like `rapidhash.h`) to ensure the project uses the latest stable versions of its vendors. ### changelog.yml - Generate CHANGELOG **Triggers:** Push to `bump/**` or `release/**` branches, manual dispatch Regenerates [`CHANGELOG.md`](../../CHANGELOG.md) with [`git-cliff`](https://git-cliff.org/), grouping all commits under their respective tags. On `bump/vX.Y.Z` and `release/vX.Y.Z` branches the version tag is auto-detected from the branch name; manual dispatch accepts an explicit `tag` input (empty = `Unreleased`). The generated file is uploaded as a workflow artifact, it is **not** committed back to the repo. Configuration lives in [`cliff.toml`](../../cliff.toml). ## Language Bindings ### wrapper-rust-publish.yml - Publish Rust Crates **Triggers:** Release published, manual dispatch Tests and publishes Rust crates to crates.io. Verifies the version matches the release tag, runs tests across platforms, and publishes `zxc-compress-sys` (FFI bindings) followed by `zxc-compress` (safe wrapper). ### wrapper-python-publish.yml - Publish Python Package **Triggers:** Release published, manual dispatch Builds platform-specific wheels using `cibuildwheel` for Linux (x86_64, ARM64), macOS (ARM64, Intel), and Windows (AMD64, ARM64). Tests wheels against Python 3.12-3.13, then publishes to PyPI via trusted publishing. ### wrapper-wasm.yml - WASM Build & Test **Triggers:** Release published, publish on main, manual dispatch Builds the WebAssembly target using Emscripten SDK. Compiles the library with SIMD disabled (scalar codepath) and no threading, then runs a Node.js roundtrip test suite covering all compression levels, reusable contexts, and error handling. Uploads `zxc.js` + `zxc.wasm` as build artifacts. ### wrapper-nodejs-publish.yml - Publish Node.js Package **Triggers:** Release published, manual dispatch Builds and publishes the Node.js package to npm. Handles the compilation of native bindings and ensures the package is correctly versioned and distributed. ### wrapper-go-test.yml - Test Go Package **Triggers:** Release published, manual dispatch Runs comprehensive tests for the Go bindings across various platforms and architectures to ensure the Go package is stable and functional. zxc-0.13.0/.github/workflows/abi-check.yml000066400000000000000000000112201522536370400203300ustar00rootroot00000000000000name: ABI Check on: workflow_dispatch: inputs: mode: description: 'check = diff against baseline; regenerate = produce a new baseline as artifact' required: true default: 'check' type: choice options: - check - regenerate pull_request: branches: [ main ] paths: - 'src/lib/**' - 'include/**' - 'CMakeLists.txt' - 'docs/abi/**' - '.github/workflows/abi-check.yml' push: branches: [ main ] paths: - 'src/lib/**' - 'include/**' - 'CMakeLists.txt' - 'docs/abi/**' permissions: contents: read concurrency: group: abi-${{ github.ref }} cancel-in-progress: true jobs: abi: name: Compare ABI vs baseline runs-on: ubuntu-latest timeout-minutes: 10 steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install abigail-tools run: | sudo apt-get update -qq sudo apt-get install -y -qq abigail-tools abidiff --version - name: Build libzxc.so (with debug info) run: | cmake -S . -B build \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DBUILD_SHARED_LIBS=ON \ -DZXC_BUILD_CLI=OFF \ -DZXC_BUILD_TESTS=OFF \ -DZXC_NATIVE_ARCH=OFF cmake --build build --parallel - name: Locate libzxc.so id: lib run: | LIB=$(find build -maxdepth 3 -name 'libzxc.so*' -not -type l | head -1) if [ -z "$LIB" ]; then echo "::error::libzxc.so not found in build/" find build -name 'libzxc*' exit 1 fi echo "Using $LIB" echo "path=$LIB" >> "$GITHUB_OUTPUT" - name: Generate current ABI XML run: | abidw --headers-dir include "${{ steps.lib.outputs.path }}" \ --out-file build/current.abi.xml echo "=== Symbols summary ===" grep -c '> "$GITHUB_STEP_SUMMARY" # ---- check mode (PR / push / manual): diff against committed baseline ---- - name: Diff against committed baseline if: github.event_name != 'workflow_dispatch' || inputs.mode != 'regenerate' run: | BASELINE=docs/abi/libzxc-linux-x86_64.abi.xml if [ ! -f "$BASELINE" ]; then echo "::error::Baseline $BASELINE not found. Run this workflow with mode=regenerate first, then commit the artifact." exit 1 fi SUPPR=docs/abi/libzxc.suppr if [ ! -f "$SUPPR" ]; then echo "::error::Suppression file $SUPPR not found." exit 1 fi # --no-added-syms: adding symbols is allowed (MINOR bump); # removals/changes are reported as ABI breaks (MAJOR bump required). # --suppressions: filters layout changes to the OPAQUE handle structs. # Consumers only ever hold pointers to these, so growing them is ABI-compatible. # See docs/abi/libzxc.suppr for the full rationale. set +e abidiff --no-added-syms --suppressions "$SUPPR" "$BASELINE" build/current.abi.xml | tee abi-report.txt STATUS=$? set -e { echo "## ABI diff vs baseline" echo echo '```' cat abi-report.txt echo '```' } >> "$GITHUB_STEP_SUMMARY" # abidiff exit codes: # 0 = compatible # bit 2 (4) = ABI incompatible change -> fail # bit 3 (8) = ABI compatible change -> pass (we report it) if [ $((STATUS & 4)) -ne 0 ]; then echo "::error::ABI break detected — bump SOVERSION and regenerate the baseline." exit 1 fi exit 0 zxc-0.13.0/.github/workflows/benchmark.yml000066400000000000000000000106431522536370400204640ustar00rootroot00000000000000name: Benchmark on: workflow_dispatch: push: branches: [ main ] paths: - 'src/**' - 'include/**' pull_request: branches: [ main ] paths: - 'src/**' - 'include/**' permissions: contents: read concurrency: cancel-in-progress: true group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} jobs: lzbench-benchmark: name: Run Benchmark if: github.event.pull_request.draft == false runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, macos-26-xlarge] env: LZBENCH_DIR: lzbench SILESIA_TAR: silesia.tar steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Select Latest Xcode if: runner.os == 'macOS' run: | sudo xcode-select -s /Applications/Xcode_26.4.app clang --version - name: Clone LZbench run: | # git clone --depth 1 https://github.com/inikep/lzbench "${LZBENCH_DIR}" git clone -b zxc-0.13.0 https://github.com/hellobertrand/lzbench "${LZBENCH_DIR}" - name: Copy Lib ZXC run: | mkdir -p ${LZBENCH_DIR}/lz/zxc/include/ mkdir -p ${LZBENCH_DIR}/lz/zxc/src/lib/ cp -r include ${LZBENCH_DIR}/lz/zxc/ cp -r src/lib ${LZBENCH_DIR}/lz/zxc/src/ - name: Build Lzbench working-directory: ${{ env.LZBENCH_DIR }} run: | SKIP_CODECS="DONT_BUILD_BRIEFLZ=1 DONT_BUILD_BROTLI=1 DONT_BUILD_BSC=1" SKIP_CODECS+=" DONT_BUILD_BZIP2=1 DONT_BUILD_BZIP3=1 DONT_BUILD_CSC=1" SKIP_CODECS+=" DONT_BUILD_CRUSH=1 DONT_BUILD_DENSITY=1 DONT_BUILD_FASTLZ=1" SKIP_CODECS+=" DONT_BUILD_FASTLZMA2=1 DONT_BUILD_GIPFELI=1 DONT_BUILD_GLZA=1" SKIP_CODECS+=" DONT_BUILD_KANZI=1 DONT_BUILD_LIBDEFLATE=1 DONT_BUILD_LIZARD=1" SKIP_CODECS+=" DONT_BUILD_LZF=1 DONT_BUILD_LZFSE=1 DONT_BUILD_LZG=1" SKIP_CODECS+=" DONT_BUILD_LZHAM=1 DONT_BUILD_LZJB=1 DONT_BUILD_LZLIB=1" SKIP_CODECS+=" DONT_BUILD_LZMA=1 DONT_BUILD_LZMAT=1 DONT_BUILD_LZO=1" SKIP_CODECS+=" DONT_BUILD_LZRW=1 DONT_BUILD_LZSSE=1 DONT_BUILD_PPMD=1" SKIP_CODECS+=" DONT_BUILD_QUICKLZ=1 DONT_BUILD_SLZ=1 DONT_BUILD_TAMP=1" SKIP_CODECS+=" DONT_BUILD_TORNADO=1 DONT_BUILD_UCL=1 DONT_BUILD_WFLZ=1" SKIP_CODECS+=" DONT_BUILD_XZ=1 DONT_BUILD_YALZ77=1 DONT_BUILD_YAPPY=1" SKIP_CODECS+=" DONT_BUILD_ZLIB_NG=1 DONT_BUILD_ZLING=1 DONT_BUILD_ZPAQ=1" SKIP_CODECS+=" DONT_BUILD_MEMLZ=1 DONT_BUILD_ACEAPEX=1 DONT_BUILD_OPENZL=1" # Use latest available compiler per platform if [ "$RUNNER_OS" = "Linux" ]; then export CC=gcc-14 CXX=g++-14 fi make -j $SKIP_CODECS CC="${CC:-cc}" MOREFLAGS="-march=native" - name: Cache Silesia Corpus id: cache-silesia uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: ${{ env.SILESIA_TAR }} key: silesia-tar-v1 - name: Download Silesia Corpus if: steps.cache-silesia.outputs.cache-hit != 'true' run: | wget -q https://github.com/DataCompression/corpus-collection/raw/main/Silesia-Corpus/silesia.tar.gz -O silesia.tar.gz gunzip silesia.tar.gz - name: SHA256 Checksum run: | shasum -a 256 "${SILESIA_TAR}" - name: Run Lzbench on Silesia Corpus working-directory: ${{ env.LZBENCH_DIR }} run: | if [ "${{ github.event_name }}" = "pull_request" ]; then CODECS="memcpy/zxc/lz4" else CODECS="memcpy/zxc/lz4/lz4fast,17/lz4hc,9/lzav,1/snappy/zstd_fast,-1/zstd,1,3/zlib,1" fi ./lzbench -e"${CODECS}" -j -t8,8 -o1 "../${SILESIA_TAR}" > ../benchmark.md echo "Benchmark finished. First lines:" head -n 40 ../benchmark.md - name: Add Benchmark to Job Summary run: | echo "### Benchmark Result (${{ matrix.os }})" >> $GITHUB_STEP_SUMMARY cat benchmark.md >> $GITHUB_STEP_SUMMARY - name: Upload Benchmark Result as Artifact uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: benchmark-${{ matrix.os }}-${{ github.sha }} path: benchmark.md if-no-files-found: error retention-days: 10zxc-0.13.0/.github/workflows/build.yml000066400000000000000000000223231522536370400176270ustar00rootroot00000000000000name: Build & Release on: workflow_dispatch: push: branches: [ main ] tags: - 'v*' release: types: [ published ] pull_request: branches: [ main ] permissions: contents: read concurrency: cancel-in-progress: true group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} jobs: build-and-test: name: Build ${{ matrix.name }} runs-on: ${{ matrix.os }} permissions: contents: read id-token: write attestations: write strategy: fail-fast: false matrix: include: - name: Linux x86_64 os: ubuntu-latest artifact_name: zxc platform: linux-x86_64 cmake_flags: "-DZXC_NATIVE_ARCH=OFF" - name: Linux ARM64 os: ubuntu-24.04-arm artifact_name: zxc platform: linux-arm64 cmake_flags: "-DZXC_NATIVE_ARCH=OFF" - name: macOS ARM64 os: macos-26 artifact_name: zxc platform: macos-arm64 cmake_flags: "-DZXC_NATIVE_ARCH=OFF" - name: Windows x86_64 os: windows-latest artifact_name: zxc.exe platform: windows-x86_64 cmake_flags: "-A x64 -DZXC_NATIVE_ARCH=OFF" - name: Windows ARM64 os: windows-11-arm artifact_name: zxc.exe platform: windows-arm64 cmake_flags: "-A ARM64 -DZXC_NATIVE_ARCH=OFF" steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Select Latest Compiler (Linux) if: runner.os == 'Linux' run: | echo "CC=gcc-14" >> $GITHUB_ENV echo "CXX=g++-14" >> $GITHUB_ENV gcc-14 --version - name: Select Latest Xcode (macOS) if: runner.os == 'macOS' run: | sudo xcode-select -s /Applications/Xcode_26.4.app clang --version - name: Configure CMake run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release ${{ matrix.cmake_flags }} - name: Build run: cmake --build build --config Release --parallel - name: Test (native) working-directory: build run: ctest -C Release --output-on-failure - name: Test CLI shell: bash run: | BUILD_DIR="build" # Find binary (handle both Ninja and Visual Studio generators) if [ -f "$BUILD_DIR/Release/${{ matrix.artifact_name }}" ]; then ZXC_BIN="$BUILD_DIR/Release/${{ matrix.artifact_name }}" elif [ -f "$BUILD_DIR/${{ matrix.artifact_name }}" ]; then ZXC_BIN="$BUILD_DIR/${{ matrix.artifact_name }}" else echo "Binary not found for CLI test!" find "$BUILD_DIR" -name "${{ matrix.artifact_name }}" exit 1 fi echo "Testing with binary: $ZXC_BIN" chmod +x tests/test_cli.sh ./tests/test_cli.sh "$ZXC_BIN" - name: Package Release (Archive) if: startsWith(github.ref, 'refs/tags/') shell: bash run: | VERSION="${GITHUB_REF_NAME#v}" ARCHIVE_BASE="zxc-${VERSION}-${{ matrix.platform }}" if [[ "${{ runner.os }}" == "Windows" ]]; then ARCHIVE="${ARCHIVE_BASE}.zip" else ARCHIVE="${ARCHIVE_BASE}.tar.gz" fi echo "ARCHIVE=$ARCHIVE" >> "$GITHUB_ENV" # Install to staging directory using CMake (Standard way for all OS) cmake --install build --prefix "release_staging/${ARCHIVE_BASE}" --config Release # Strip binary on Unix-like systems only (installed to bin/) if [[ "${{ runner.os }}" != "Windows" ]]; then strip "release_staging/${ARCHIVE_BASE}/bin/${{ matrix.artifact_name }}" fi cp LICENSE "release_staging/${ARCHIVE_BASE}/LICENSE.txt" cp docs/man/zxc.1.md "release_staging/${ARCHIVE_BASE}/MANUAL.md" cat > "release_staging/${ARCHIVE_BASE}/README.md" < --repo ${GITHUB_REPOSITORY} \`\`\` License: BSD-3-Clause (see LICENSE.txt) EOF # Create archive with versioned top-level directory cd release_staging if [[ "${{ runner.os }}" == "Windows" ]]; then 7z a "../${ARCHIVE}" "${ARCHIVE_BASE}" else tar -czvf "../${ARCHIVE}" "${ARCHIVE_BASE}" fi - name: Attest build provenance (SLSA) if: startsWith(github.ref, 'refs/tags/') uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1 with: subject-path: ${{ env.ARCHIVE }} - name: Upload Artifacts if: startsWith(github.ref, 'refs/tags/') uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: zxc-${{ matrix.platform }} path: ${{ env.ARCHIVE }} sbom: name: Generate SBOM if: startsWith(github.ref, 'refs/tags/') runs-on: ubuntu-slim permissions: contents: read id-token: write attestations: write steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Sanitize ref name id: ref run: echo "slug=${GITHUB_REF_NAME//\//-}" >> "$GITHUB_OUTPUT" - name: Render SBOM from template shell: bash env: SBOM_FILE: zxc-${{ steps.ref.outputs.slug }}-sbom.spdx.json VERSION: ${{ steps.ref.outputs.slug }} COMMIT: ${{ github.sha }} run: | CREATED="$(date -u +%Y-%m-%dT%H:%M:%SZ)" sed \ -e "s|@VERSION@|${VERSION}|g" \ -e "s|@COMMIT@|${COMMIT}|g" \ -e "s|@CREATED@|${CREATED}|g" \ sbom.spdx.json.in > "$SBOM_FILE" # Validate JSON syntax jq empty "$SBOM_FILE" - name: Attest SBOM provenance (SLSA) uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1 with: subject-path: zxc-${{ steps.ref.outputs.slug }}-sbom.spdx.json - name: Upload SBOM artifact uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: sbom path: zxc-${{ steps.ref.outputs.slug }}-sbom.spdx.json release: name: Create GitHub Release needs: [build-and-test, sbom] runs-on: ubuntu-slim if: startsWith(github.ref, 'refs/tags/') permissions: contents: write steps: - name: Download all artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: path: artifacts - name: Flatten artifacts run: | mkdir -p release find artifacts -type f \( -name '*.tar.gz' -o -name '*.zip' -o -name '*.spdx.json' \) -exec mv {} release/ \; ls -la release - name: Create Release uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1 with: files: | release/*.tar.gz release/*.zip release/*.spdx.json env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} hashes: name: Compute release artifact hashes needs: [build-and-test, sbom] runs-on: ubuntu-slim if: startsWith(github.ref, 'refs/tags/') outputs: digests: ${{ steps.hash.outputs.digests }} steps: - name: Download all artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: path: artifacts merge-multiple: true - name: Generate subject digests (base64 sha256sum) id: hash working-directory: artifacts run: | ls -la echo "digests=$(sha256sum *.tar.gz *.zip *.spdx.json | base64 -w0)" >> "$GITHUB_OUTPUT" provenance: name: SLSA provenance needs: [hashes, release] if: startsWith(github.ref, 'refs/tags/') permissions: actions: read # read the workflow run to assemble provenance id-token: write # keyless signing via Sigstore / OIDC contents: write # attach multiple.intoto.jsonl to the release uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0 with: base64-subjects: ${{ needs.hashes.outputs.digests }} upload-assets: true upload-tag-name: ${{ github.ref_name }}zxc-0.13.0/.github/workflows/changelog.yml000066400000000000000000000027401522536370400204600ustar00rootroot00000000000000name: Generate CHANGELOG on: workflow_dispatch: push: branches: - 'bump/**' - 'release/**' permissions: contents: read concurrency: group: changelog-${{ github.ref }} cancel-in-progress: false jobs: generate: name: Generate CHANGELOG.md runs-on: ubuntu-slim timeout-minutes: 5 steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - name: Determine version tag id: ver shell: bash run: | if [[ "${GITHUB_REF}" =~ ^refs/heads/(bump|release)/(v[0-9]+\.[0-9]+\.[0-9]+)$ ]]; then DETECTED="${BASH_REMATCH[2]}" echo "tag_arg=--tag $DETECTED" >> "$GITHUB_OUTPUT" echo "Detected tag from branch name: $DETECTED" else echo "tag_arg=" >> "$GITHUB_OUTPUT" echo "No tag determined — latest section will be 'Unreleased'." fi - name: Generate CHANGELOG uses: orhun/git-cliff-action@f50e11560dce63f7c33227798f90b924471a88b5 # v4.8.0 with: config: cliff.toml args: --verbose ${{ steps.ver.outputs.tag_arg }} env: OUTPUT: CHANGELOG.md GITHUB_REPO: ${{ github.repository }} - name: Upload CHANGELOG artifact uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: CHANGELOG path: CHANGELOG.md zxc-0.13.0/.github/workflows/compat-os.yml000066400000000000000000000103331522536370400204300ustar00rootroot00000000000000name: OS (libc) Compatibility # Portability across the OS / libc axis (orthogonal to multiarch.yml, which # covers CPU architectures on glibc Linux): # - musl libc (Alpine): catches non-glibc bugs the rest of CI can't see # (e.g. the 128 KB default thread stack vs glibc's 8 MB). Native on both # x86_64 and ARM64 runners via an Alpine container. # - FreeBSD: non-glibc / clang-default portability canary, and the BSD that # matters for packaging (ports, TrueNAS/ZFS, pfSense). Built in a VM via # cross-platform-actions (no native BSD runners exist); x86_64 only, as # FreeBSD arm64 would be QEMU-emulated and slow. # Both run the full STATIC+SHARED ctest suite and exercise runtime SIMD # dispatch (ZXC_NATIVE_ARCH=OFF). on: workflow_dispatch: push: branches: [ main ] pull_request: branches: [ main ] permissions: contents: read concurrency: cancel-in-progress: true group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} jobs: build-compat: name: "${{ matrix.name }}" runs-on: ${{ matrix.runner }} strategy: fail-fast: false matrix: include: - name: Linux x86_64 (Alpine musl) kind: musl runner: ubuntu-latest - name: Linux ARM64 (Alpine musl) kind: musl runner: ubuntu-24.04-arm - name: FreeBSD x86_64 kind: bsd runner: ubuntu-latest os: freebsd arch: x86-64 version: "14.3" install: sudo pkg install -y cmake ninja steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 # musl: build & test inside an Alpine container. - name: Configure, Build & Test under musl (Static & Shared) if: matrix.kind == 'musl' run: | docker run --rm -v "${{ github.workspace }}:/src" -w /src alpine:latest sh -euc ' apk add --no-cache build-base cmake echo "=== Toolchain ===" cc --version cmake --version for LIB_TYPE in STATIC SHARED; do echo ">>> Starting Build: $LIB_TYPE" BUILD_DIR="build_$LIB_TYPE" SHARED_FLAG=OFF if [ "$LIB_TYPE" = SHARED ]; then SHARED_FLAG=ON; fi cmake -S . -B "$BUILD_DIR" \ -DCMAKE_BUILD_TYPE=Release \ -DZXC_NATIVE_ARCH=OFF \ -DBUILD_SHARED_LIBS="$SHARED_FLAG" cmake --build "$BUILD_DIR" --parallel echo "Running Tests for $LIB_TYPE..." ctest --test-dir "$BUILD_DIR" --output-on-failure echo ">>> Completed Build: $LIB_TYPE" echo "" done ' # FreeBSD: boot the VM, then build & test inside it via the cpa.sh shell - name: Start FreeBSD VM if: matrix.kind == 'bsd' uses: cross-platform-actions/action@951f2f3aac1c695838eabd36b2750f6b2708c263 # v1.2.0 with: operating_system: ${{ matrix.os }} architecture: ${{ matrix.arch }} version: ${{ matrix.version }} memory: 5G cpu_count: 4 # Runs inside the VM - name: Configure, Build & Test on FreeBSD (Static & Shared) if: matrix.kind == 'bsd' shell: cpa.sh {0} run: | set -e ${{ matrix.install }} echo "=== Toolchain ===" cc --version 2>/dev/null || true cmake --version for LIB_TYPE in STATIC SHARED; do echo ">>> Starting Build: $LIB_TYPE" BUILD_DIR="build_$LIB_TYPE" SHARED_FLAG=OFF if [ "$LIB_TYPE" = SHARED ]; then SHARED_FLAG=ON; fi cmake -S . -B "$BUILD_DIR" -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ -DZXC_NATIVE_ARCH=OFF \ -DZXC_ENABLE_LTO=OFF \ -DBUILD_SHARED_LIBS="$SHARED_FLAG" cmake --build "$BUILD_DIR" --parallel echo "Running Tests for $LIB_TYPE..." ctest --test-dir "$BUILD_DIR" --output-on-failure echo ">>> Completed Build: $LIB_TYPE" echo "" done zxc-0.13.0/.github/workflows/coverage.yml000066400000000000000000000043471522536370400203310ustar00rootroot00000000000000name: Code Coverage on: workflow_dispatch: push: branches: [ main ] paths: - 'src/**' - 'include/**' - 'tests/**' pull_request: branches: [ main ] paths: - 'src/**' - 'include/**' - 'tests/**' permissions: contents: read jobs: coverage: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y lcov - name: Configure CMake run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DZXC_ENABLE_COVERAGE=ON -DZXC_NATIVE_ARCH=OFF - name: Build run: cmake --build build --parallel - name: Run Unit Tests working-directory: build run: ctest --output-on-failure - name: Run CLI Tests run: | chmod +x tests/test_cli.sh ./tests/test_cli.sh build/zxc - name: Generate Coverage Report run: | # Capture coverage data lcov --capture --directory . --output-file coverage.info --ignore-errors gcov,negative # Filter out system headers, external libraries, CLI frontend, and test files lcov --remove coverage.info '/usr/*' '*/tests/*' '*/cli/*' '*/src/lib/vendors/rapidhash.h' --output-file coverage.info --ignore-errors unused # Output file list lcov --list coverage.info - name: Generate HTML Report run: genhtml coverage.info --output-directory coverage-report - name: Publish Coverage Summary run: | echo "## Code Coverage Summary" >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY lcov --summary coverage.info 2>&1 | tail -n +2 >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY - name: Upload Coverage Artifact uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: coverage-report path: coverage-report retention-days: 10 - name: Upload coverage to Codecov uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 with: token: ${{ secrets.CODECOV_TOKEN }} files: ./coverage.info fail_ci_if_error: true zxc-0.13.0/.github/workflows/fuzzing.yml000066400000000000000000000211631522536370400202250ustar00rootroot00000000000000name: Fuzzing on: workflow_dispatch: inputs: mode: description: 'Fuzzing mode' required: true default: 'batch' type: choice options: - code-change - batch fuzz_seconds: description: 'Duration (seconds)' required: true default: '7200' pull_request: branches: [ main ] paths: - 'src/**' - 'include/**' - 'tests/fuzz_*.c' - '.clusterfuzzlite/**' schedule: - cron: '0 1 * * 1' permissions: contents: read jobs: fuzzing: name: Run Fuzzing (${{ matrix.sanitizer }}) runs-on: ubuntu-latest permissions: contents: read security-events: write concurrency: group: ${{ github.workflow }}-${{ matrix.sanitizer }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true strategy: fail-fast: false matrix: sanitizer: [address, undefined] steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 # TODO: Remove this step once ClusterFuzzLite updates to support Docker 29+ - name: Downgrade Docker (Temporary Workaround) run: | # ClusterFuzzLite v1 uses Docker API 1.41 which is incompatible with Docker 29.0+ # Downgrade to Docker 28 until the action is updated curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update # Install Docker 28.0.4 specifically sudo apt-get install -y --allow-downgrades docker-ce=5:28.0.4-1~ubuntu.$(lsb_release -rs)~$(lsb_release -cs) docker-ce-cli=5:28.0.4-1~ubuntu.$(lsb_release -rs)~$(lsb_release -cs) containerd.io sudo systemctl restart docker docker version - name: Build Fuzzers (${{ matrix.sanitizer }}) id: build uses: google/clusterfuzzlite/actions/build_fuzzers@884713a6c30a92e5e8544c39945cd7cb630abcd1 # v1 with: language: c github-token: ${{ secrets.GITHUB_TOKEN }} sanitizer: ${{ matrix.sanitizer }} - name: Run Fuzzers (${{ matrix.sanitizer }}) id: run uses: google/clusterfuzzlite/actions/run_fuzzers@884713a6c30a92e5e8544c39945cd7cb630abcd1 # v1 with: github-token: ${{ secrets.GITHUB_TOKEN }} mode: ${{ github.event_name == 'pull_request' && 'code-change' || inputs.mode || 'batch' }} fuzz-seconds: ${{ github.event_name == 'pull_request' && 300 || inputs.fuzz_seconds || 7200 }} sanitizer: ${{ matrix.sanitizer }} output-sarif: true storage-repo: https://${{ secrets.CFLITE_CORPUS_TOKEN }}@github.com/hellobertrand/zxc-fuzz-corpus.git storage-repo-branch: main - name: Upload SARIF to GitHub Security if: success() || failure() uses: github/codeql-action/upload-sarif@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 with: sarif_file: . category: clusterfuzzlite-${{ matrix.sanitizer }} tsan: name: Thread Sanitizer runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Build with TSan run: | cmake -B build -DCMAKE_C_FLAGS="-fsanitize=thread -g -fno-omit-frame-pointer" -DCMAKE_BUILD_TYPE=Debug cmake --build build - name: Run Tests run: ctest --test-dir build --output-on-failure coverage: name: Fuzz Coverage Report runs-on: ubuntu-latest if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install LLVM Tools run: | sudo apt-get update -qq sudo apt-get install -y -qq llvm - name: Clone Fuzz Corpus env: CORPUS_TOKEN: ${{ secrets.CFLITE_CORPUS_TOKEN }} run: | git clone --depth=1 \ "https://${CORPUS_TOKEN}@github.com/hellobertrand/zxc-fuzz-corpus.git" \ corpus echo "roundtrip inputs: $(find corpus/corpus/zxc_fuzzer_roundtrip -type f | wc -l)" echo "decompress inputs: $(find corpus/corpus/zxc_fuzzer_decompress -type f | wc -l)" echo "seekable inputs: $(find corpus/corpus/zxc_fuzzer_seekable -type f | wc -l)" echo "pstream inputs: $(find corpus/corpus/zxc_fuzzer_pstream -type f | wc -l)" echo "dict inputs: $(find corpus/corpus/zxc_fuzzer_dict -type f | wc -l)" - name: Build Coverage Harnesses run: | mkdir -p build-cov CFLAGS="-g -O1 -fprofile-instr-generate -fcoverage-mapping" DEFS="-DZXC_FUNCTION_SUFFIX=_default -DZXC_ONLY_DEFAULT" INCLUDES="-I include -I src/lib/vendors" SOURCES="src/lib/zxc_common.c src/lib/zxc_compress.c src/lib/zxc_decompress.c src/lib/zxc_dict.c src/lib/zxc_driver.c src/lib/zxc_dispatch.c src/lib/zxc_huffman.c src/lib/zxc_pivco_tables.c src/lib/zxc_seekable.c src/lib/zxc_pstream.c" clang $CFLAGS $INCLUDES $DEFS $SOURCES tests/fuzz_roundtrip.c \ -fsanitize=fuzzer -lm -lpthread -o build-cov/fuzz_roundtrip clang $CFLAGS $INCLUDES $DEFS $SOURCES tests/fuzz_decompress.c \ -fsanitize=fuzzer -lm -lpthread -o build-cov/fuzz_decompress clang $CFLAGS $INCLUDES $DEFS $SOURCES tests/fuzz_seekable.c \ -fsanitize=fuzzer -lm -lpthread -o build-cov/fuzz_seekable clang $CFLAGS $INCLUDES $DEFS $SOURCES tests/fuzz_pstream.c \ -fsanitize=fuzzer -lm -lpthread -o build-cov/fuzz_pstream clang $CFLAGS $INCLUDES $DEFS $SOURCES tests/fuzz_dict.c \ -fsanitize=fuzzer -lm -lpthread -o build-cov/fuzz_dict - name: Replay Corpora run: | LLVM_PROFILE_FILE="build-cov/roundtrip.profraw" \ build-cov/fuzz_roundtrip corpus/corpus/zxc_fuzzer_roundtrip/ -runs=0 2>&1 | tail -1 LLVM_PROFILE_FILE="build-cov/decompress.profraw" \ build-cov/fuzz_decompress corpus/corpus/zxc_fuzzer_decompress/ -runs=0 2>&1 | tail -1 if [ -d corpus/corpus/zxc_fuzzer_seekable ]; then LLVM_PROFILE_FILE="build-cov/seekable.profraw" \ build-cov/fuzz_seekable corpus/corpus/zxc_fuzzer_seekable/ -runs=0 2>&1 | tail -1 fi if [ -d corpus/corpus/zxc_fuzzer_pstream ]; then LLVM_PROFILE_FILE="build-cov/pstream.profraw" \ build-cov/fuzz_pstream corpus/corpus/zxc_fuzzer_pstream/ -runs=0 2>&1 | tail -1 fi if [ -d corpus/corpus/zxc_fuzzer_dict ]; then LLVM_PROFILE_FILE="build-cov/dict.profraw" \ build-cov/fuzz_dict corpus/corpus/zxc_fuzzer_dict/ -runs=0 2>&1 | tail -1 fi - name: Generate Coverage Report run: | llvm-profdata merge \ build-cov/roundtrip.profraw build-cov/decompress.profraw \ $([ -f build-cov/seekable.profraw ] && echo build-cov/seekable.profraw) \ $([ -f build-cov/pstream.profraw ] && echo build-cov/pstream.profraw) \ $([ -f build-cov/dict.profraw ] && echo build-cov/dict.profraw) \ -o build-cov/fuzz.profdata echo "=== COVERAGE SUMMARY ===" llvm-cov report \ build-cov/fuzz_roundtrip -object build-cov/fuzz_decompress \ $([ -f build-cov/fuzz_seekable ] && echo "-object build-cov/fuzz_seekable") \ $([ -f build-cov/fuzz_pstream ] && echo "-object build-cov/fuzz_pstream") \ $([ -f build-cov/fuzz_dict ] && echo "-object build-cov/fuzz_dict") \ -instr-profile=build-cov/fuzz.profdata \ --ignore-filename-regex='(vendors/|tests/|fuzz_|zxc_driver)' llvm-cov show \ build-cov/fuzz_roundtrip -object build-cov/fuzz_decompress \ $([ -f build-cov/fuzz_seekable ] && echo "-object build-cov/fuzz_seekable") \ $([ -f build-cov/fuzz_pstream ] && echo "-object build-cov/fuzz_pstream") \ $([ -f build-cov/fuzz_dict ] && echo "-object build-cov/fuzz_dict") \ -instr-profile=build-cov/fuzz.profdata \ --ignore-filename-regex='(vendors/|tests/|fuzz_|zxc_driver)' \ --format=html -output-dir=build-cov/coverage_html - name: Upload Coverage Report uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: fuzz-coverage-report path: build-cov/coverage_html/ retention-days: 30 zxc-0.13.0/.github/workflows/golden.yml000066400000000000000000000034061522536370400200010ustar00rootroot00000000000000name: Golden Format Stability # Fails if a single byte of any golden conformance file changes relative to the # committed manifest (tests/format/golden.sha256). The golden files freeze the # zxc wire format; any drift here is, by design, a deliberate format change that # must be made by regenerating the corpus and refreshing the manifest in the # same commit (see tests/format/README.md). on: workflow_dispatch: push: branches: [ main ] pull_request: branches: [ main ] permissions: contents: read concurrency: cancel-in-progress: true group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} jobs: byte-stability: name: Golden files - byte stability runs-on: ubuntu-slim steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Verify golden manifest is complete run: | # Every golden/*.zxc must be listed in the manifest, and vice versa. # This catches files added or removed without updating the manifest. comm_in_dir=$(find tests/format/golden -maxdepth 1 -name '*.zxc' | sort) comm_in_manifest=$(awk '{print $2}' tests/format/golden.sha256 | sort) if ! diff <(printf '%s\n' "$comm_in_dir") <(printf '%s\n' "$comm_in_manifest"); then echo "::error::Golden file set does not match tests/format/golden.sha256." echo "Regenerate with zxc_golden_gen and refresh the manifest (see tests/format/README.md)." exit 1 fi - name: Verify golden bytes are unchanged run: | # sha256sum -c fails on any content mismatch, so a single changed byte in any golden file fails the job. sha256sum -c tests/format/golden.sha256 zxc-0.13.0/.github/workflows/multiarch.yml000066400000000000000000000327071522536370400205270ustar00rootroot00000000000000name: Multi-Architecture Build on: workflow_dispatch: push: branches: [ main ] pull_request: branches: [ main ] permissions: contents: read concurrency: cancel-in-progress: true group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} jobs: # =========================================================================== # Tier 1: Main architectures # =========================================================================== build-tier1: name: "Tier 1: ${{ matrix.name }}" runs-on: ${{ matrix.runner }} strategy: fail-fast: false matrix: include: - name: Linux x64 runner: ubuntu-latest os: linux arch_flags: "" - name: Linux x86 (32-bit) runner: ubuntu-latest os: linux install_32bit_x86: true arch_flags: "-m32" - name: Linux ARM64 runner: ubuntu-24.04-arm os: linux arch_flags: "" - name: Linux ARM (32-bit) runner: ubuntu-latest os: linux install_32bit_arm: true cross_compiler_c: arm-linux-gnueabihf-gcc cross_compiler_cxx: arm-linux-gnueabihf-g++ cross_strip: arm-linux-gnueabihf-strip cmake_system_name: "Linux" cmake_system_processor: "arm" - name: Windows x64 runner: windows-latest os: windows cmake_arch: "x64" - name: Windows x86 (32-bit) runner: windows-latest os: windows cmake_arch: "Win32" - name: Windows ARM64 runner: windows-11-arm os: windows cmake_arch: "ARM64" # Note: Windows ARM 32-bit removed - deprecated and unsupported by Windows SDK 10.0.26100+ - name: macOS Intel x64 runner: macos-26-intel os: macos - name: macOS ARM64 runner: macos-26 os: macos steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 # Case: Linux x86 (32-bit) - name: Install x86 32-bit libs if: matrix.install_32bit_x86 run: | sudo apt-get update sudo apt-get install -y gcc-multilib g++-multilib # Case: Linux ARM (32-bit) - name: Install ARM 32-bit toolchain if: matrix.install_32bit_arm run: | sudo apt-get update sudo apt-get install -y gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf - name: Configure, Build & Test (Static & Shared) shell: bash run: | # Initialize variables EXTRA_ARGS="-DZXC_NATIVE_ARCH=OFF" # Disable native arch detection (Runtime Dispatch enabled) # Setup Environment for Linux/Mac (Flags & Cross-compilation) if [ "${{ matrix.os }}" != "windows" ]; then # Handle C flags (e.g., -m32) if [ ! -z "${{ matrix.arch_flags }}" ]; then export CFLAGS="${{ matrix.arch_flags }}" export CXXFLAGS="${{ matrix.arch_flags }}" fi # Handle cross-compiler (e.g., ARM 32-bit on Linux) if [ ! -z "${{ matrix.cross_compiler_c }}" ]; then EXTRA_ARGS="$EXTRA_ARGS -DCMAKE_C_COMPILER=${{ matrix.cross_compiler_c }} -DCMAKE_CXX_COMPILER=${{ matrix.cross_compiler_cxx }}" fi # Handle explicit System Name/Processor (crucial for cross-compilation) if [ ! -z "${{ matrix.cmake_system_name }}" ]; then EXTRA_ARGS="$EXTRA_ARGS -DCMAKE_SYSTEM_NAME=${{ matrix.cmake_system_name }}" fi if [ ! -z "${{ matrix.cmake_system_processor }}" ]; then EXTRA_ARGS="$EXTRA_ARGS -DCMAKE_SYSTEM_PROCESSOR=${{ matrix.cmake_system_processor }}" fi # Handle cross-strip tool (e.g., ARM 32-bit on Linux) if [ ! -z "${{ matrix.cross_strip }}" ]; then EXTRA_ARGS="$EXTRA_ARGS -DCMAKE_STRIP=/usr/bin/${{ matrix.cross_strip }}" fi fi # Iterate over Library Types for LIB_TYPE in "STATIC" "SHARED"; do echo ">>> Starting Build: $LIB_TYPE" BUILD_DIR="build_${LIB_TYPE}" SHARED_FLAG="OFF" if [ "$LIB_TYPE" == "SHARED" ]; then SHARED_FLAG="ON"; fi CURRENT_ARGS="$EXTRA_ARGS -DBUILD_SHARED_LIBS=$SHARED_FLAG" # 1. Configure if [ "${{ matrix.os }}" = "windows" ]; then # Visual Studio Generator cmake -S . -B $BUILD_DIR -A ${{ matrix.cmake_arch }} $CURRENT_ARGS else # Ninja/Makefiles cmake -S . -B $BUILD_DIR -DCMAKE_BUILD_TYPE=Release $CURRENT_ARGS fi # 2. Build cmake --build $BUILD_DIR --config Release --parallel # 3. Test (Skip if cross-compiling) if [ -z "${{ matrix.cross_compiler_c }}" ]; then echo "Running Tests for $LIB_TYPE..." ctest --test-dir $BUILD_DIR -C Release --output-on-failure else echo "Skipping tests for $LIB_TYPE (Cross-compilation)" fi echo ">>> Completed Build: $LIB_TYPE" echo "" done # =========================================================================== # Tier 2: Extended Linux architectures (cross-compiled, tested via QEMU) # =========================================================================== build-tier2: name: "Tier 2: ${{ matrix.name }}" runs-on: ubuntu-latest container: debian:trixie strategy: fail-fast: false matrix: include: - name: Linux amd64 (SSE2) cross_pkg: gcc cross_prefix: x86_64-linux-gnu cmake_processor: x86_64 qemu_binary: qemu-x86_64 qemu_cpu: core2duo - name: Linux amd64 (AVX2) cross_pkg: gcc cross_prefix: x86_64-linux-gnu cmake_processor: x86_64 qemu_binary: qemu-x86_64 qemu_cpu: Haswell-noTSX - name: Linux amd64 (AVX-512) cross_pkg: gcc cross_prefix: x86_64-linux-gnu cmake_processor: x86_64 qemu_binary: qemu-x86_64 qemu_cpu: Skylake-Server-noTSX-IBRS - name: Linux arm64 cross_pkg: gcc-aarch64-linux-gnu cross_prefix: aarch64-linux-gnu cmake_processor: aarch64 qemu_binary: qemu-aarch64 - name: Linux arm64 (cortex-a53) cross_pkg: gcc-aarch64-linux-gnu cross_prefix: aarch64-linux-gnu cmake_processor: aarch64 qemu_binary: qemu-aarch64 qemu_cpu: cortex-a53 - name: Linux i386 cross_pkg: gcc-i686-linux-gnu cross_prefix: i686-linux-gnu cmake_processor: i686 qemu_binary: qemu-i386 - name: Linux RISC-V 64 cross_pkg: gcc-riscv64-linux-gnu cross_prefix: riscv64-linux-gnu cmake_processor: riscv64 qemu_binary: qemu-riscv64 - name: Linux s390x cross_pkg: gcc-s390x-linux-gnu cross_prefix: s390x-linux-gnu cmake_processor: s390x qemu_binary: qemu-s390x - name: Linux armhf cross_pkg: gcc-arm-linux-gnueabihf cross_prefix: arm-linux-gnueabihf cmake_processor: arm qemu_binary: qemu-arm qemu_cpu: cortex-a15 - name: Linux ppc64el cross_pkg: gcc-powerpc64le-linux-gnu cross_prefix: powerpc64le-linux-gnu cmake_processor: ppc64le qemu_binary: qemu-ppc64le - name: Linux mipsel cross_pkg: gcc-mipsel-linux-gnu cross_prefix: mipsel-linux-gnu cmake_processor: mipsel qemu_binary: qemu-mipsel - name: Linux powerpc cross_pkg: gcc-powerpc-linux-gnu cross_prefix: powerpc-linux-gnu cmake_processor: powerpc qemu_binary: qemu-ppc - name: Linux ppc64 cross_pkg: gcc-powerpc64-linux-gnu cross_prefix: powerpc64-linux-gnu cmake_processor: ppc64 qemu_binary: qemu-ppc64 - name: Linux sparc64 cross_pkg: gcc-sparc64-linux-gnu cross_prefix: sparc64-linux-gnu cmake_processor: sparc64 qemu_binary: qemu-sparc64 - name: Linux armel cross_pkg: gcc-arm-linux-gnueabi cross_prefix: arm-linux-gnueabi cmake_processor: arm qemu_binary: qemu-arm steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install cross-compiler run: | apt-get update apt-get install -y cmake build-essential ${{ matrix.cross_pkg }} qemu-user - name: Configure, Build & Test (Static & Shared) shell: bash run: | CROSS_C=${{ matrix.cross_prefix }}-gcc CROSS_CXX=${{ matrix.cross_prefix }}-g++ CROSS_STRIP=/usr/bin/${{ matrix.cross_prefix }}-strip SYSROOT=/usr/${{ matrix.cross_prefix }} for LIB_TYPE in "STATIC" "SHARED"; do echo ">>> Starting Build: $LIB_TYPE" BUILD_DIR="build_${LIB_TYPE}" SHARED_FLAG="OFF" if [ "$LIB_TYPE" == "SHARED" ]; then SHARED_FLAG="ON"; fi cmake -S . -B $BUILD_DIR \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_C_COMPILER=$CROSS_C \ -DCMAKE_CXX_COMPILER=$CROSS_CXX \ -DCMAKE_STRIP=$CROSS_STRIP \ -DCMAKE_SYSTEM_NAME=Linux \ -DCMAKE_SYSTEM_PROCESSOR=${{ matrix.cmake_processor }} \ -DZXC_NATIVE_ARCH=OFF \ -DBUILD_SHARED_LIBS=$SHARED_FLAG cmake --build $BUILD_DIR --config Release --parallel QEMU_CPU_ARG="" if [ -n "${{ matrix.qemu_cpu }}" ]; then QEMU_CPU_ARG="-cpu ${{ matrix.qemu_cpu }}" fi echo "Running Tests for $LIB_TYPE via QEMU..." ${{ matrix.qemu_binary }} $QEMU_CPU_ARG -L $SYSROOT ./$BUILD_DIR/zxc_test echo "Running conformance + golden vectors for $LIB_TYPE via QEMU..." ${{ matrix.qemu_binary }} $QEMU_CPU_ARG -L $SYSROOT \ ./$BUILD_DIR/zxc_conformance_test ./conformance ${{ matrix.qemu_binary }} $QEMU_CPU_ARG -L $SYSROOT \ ./$BUILD_DIR/zxc_format_golden_test ./tests/format/golden echo ">>> Completed Build: $LIB_TYPE" echo "" done # =========================================================================== # Tier 3: Experimental Linux architectures (cross-compiled, tested via QEMU) # =========================================================================== build-tier3: name: "Tier 3: ${{ matrix.name }}" runs-on: ubuntu-latest container: debian:trixie strategy: fail-fast: false matrix: include: - name: Linux mips64el cross_pkg: gcc-mips64el-linux-gnuabi64 cross_prefix: mips64el-linux-gnuabi64 cmake_processor: mips64 qemu_binary: qemu-mips64el - name: Linux loong64 cross_pkg: gcc-loongarch64-linux-gnu cross_prefix: loongarch64-linux-gnu cmake_processor: loongarch64 qemu_binary: qemu-loongarch64 - name: Linux alpha cross_pkg: gcc-alpha-linux-gnu cross_prefix: alpha-linux-gnu cmake_processor: alpha qemu_binary: qemu-alpha steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install cross-compiler run: | apt-get update apt-get install -y cmake build-essential ${{ matrix.cross_pkg }} qemu-user - name: Configure, Build & Test (Static & Shared) shell: bash run: | CROSS_C=${{ matrix.cross_prefix }}-gcc CROSS_CXX=${{ matrix.cross_prefix }}-g++ CROSS_STRIP=/usr/bin/${{ matrix.cross_prefix }}-strip SYSROOT=/usr/${{ matrix.cross_prefix }} for LIB_TYPE in "STATIC" "SHARED"; do echo ">>> Starting Build: $LIB_TYPE" BUILD_DIR="build_${LIB_TYPE}" SHARED_FLAG="OFF" if [ "$LIB_TYPE" == "SHARED" ]; then SHARED_FLAG="ON"; fi cmake -S . -B $BUILD_DIR \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_C_COMPILER=$CROSS_C \ -DCMAKE_CXX_COMPILER=$CROSS_CXX \ -DCMAKE_STRIP=$CROSS_STRIP \ -DCMAKE_SYSTEM_NAME=Linux \ -DCMAKE_SYSTEM_PROCESSOR=${{ matrix.cmake_processor }} \ -DZXC_NATIVE_ARCH=OFF \ -DBUILD_SHARED_LIBS=$SHARED_FLAG cmake --build $BUILD_DIR --config Release --parallel echo "Running Tests for $LIB_TYPE via QEMU..." ${{ matrix.qemu_binary }} -L $SYSROOT ./$BUILD_DIR/zxc_test echo ">>> Completed Build: $LIB_TYPE" echo "" donezxc-0.13.0/.github/workflows/multicomp.yml000066400000000000000000000311001522536370400205320ustar00rootroot00000000000000name: Compiler & Build System Compatibility on: workflow_dispatch: push: branches: [ main ] pull_request: branches: [ main ] permissions: contents: read concurrency: cancel-in-progress: true group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} jobs: # =========================================================================== # Clang 12–20 on Ubuntu # =========================================================================== clang: name: "Clang ${{ matrix.version }} (${{ matrix.os }})" runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: # Ubuntu 22.04 ships Clang 13, 14, 15 - Clang 12 is in standard repos - { os: ubuntu-22.04, version: 12, needs_install: true, needs_llvm_apt: false, llvm_distro: "" } - { os: ubuntu-22.04, version: 13, needs_llvm_apt: false, llvm_distro: "" } - { os: ubuntu-22.04, version: 14, needs_llvm_apt: false, llvm_distro: "" } - { os: ubuntu-22.04, version: 15, needs_llvm_apt: false, llvm_distro: "" } # Ubuntu 24.04 ships Clang 16, 17, 18 - 19/20 need LLVM APT - { os: ubuntu-latest, version: 16, needs_llvm_apt: false, llvm_distro: "" } - { os: ubuntu-latest, version: 17, needs_llvm_apt: false, llvm_distro: "" } - { os: ubuntu-latest, version: 18, needs_llvm_apt: false, llvm_distro: "" } - { os: ubuntu-latest, version: 19, needs_llvm_apt: true, llvm_distro: noble } - { os: ubuntu-latest, version: 20, needs_llvm_apt: true, llvm_distro: noble } steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Clang ${{ matrix.version }} (standard repos) if: matrix.needs_install && !matrix.needs_llvm_apt run: | sudo apt-get update sudo apt-get install -y clang-${{ matrix.version }} - name: Install Clang ${{ matrix.version }} (LLVM APT) if: matrix.needs_llvm_apt run: | wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc echo "deb http://apt.llvm.org/${{ matrix.llvm_distro }}/ llvm-toolchain-${{ matrix.llvm_distro }}-${{ matrix.version }} main" | sudo tee /etc/apt/sources.list.d/llvm.list sudo apt-get update sudo apt-get install -y clang-${{ matrix.version }} - name: Configure, Build & Test (Static & Shared) shell: bash run: | for LIB_TYPE in "STATIC" "SHARED"; do echo ">>> Starting Build: Clang ${{ matrix.version }} – $LIB_TYPE" BUILD_DIR="build_${LIB_TYPE}" SHARED_FLAG="OFF" if [ "$LIB_TYPE" == "SHARED" ]; then SHARED_FLAG="ON"; fi cmake -S . -B $BUILD_DIR \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_C_COMPILER=clang-${{ matrix.version }} \ -DCMAKE_C_STANDARD=17 \ -DZXC_NATIVE_ARCH=OFF \ -DZXC_ENABLE_LTO=OFF \ -DBUILD_SHARED_LIBS=$SHARED_FLAG cmake --build $BUILD_DIR --config Release --parallel echo "Running Tests for $LIB_TYPE..." ctest --test-dir $BUILD_DIR -C Release --output-on-failure echo ">>> Completed Build: $LIB_TYPE" echo "" done # =========================================================================== # Apple Clang on macOS 14 # =========================================================================== clang-macos: name: "Apple Clang (macOS 14)" runs-on: macos-14 strategy: fail-fast: false steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Configure, Build & Test (Static & Shared) shell: bash run: | for LIB_TYPE in "STATIC" "SHARED"; do echo ">>> Starting Build: Apple Clang – $LIB_TYPE" BUILD_DIR="build_${LIB_TYPE}" SHARED_FLAG="OFF" if [ "$LIB_TYPE" == "SHARED" ]; then SHARED_FLAG="ON"; fi cmake -S . -B $BUILD_DIR \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_C_STANDARD=17 \ -DZXC_NATIVE_ARCH=OFF \ -DBUILD_SHARED_LIBS=$SHARED_FLAG cmake --build $BUILD_DIR --config Release --parallel echo "Running Tests for $LIB_TYPE..." ctest --test-dir $BUILD_DIR -C Release --output-on-failure echo ">>> Completed Build: $LIB_TYPE" echo "" done # =========================================================================== # GCC 9–16 on Ubuntu # =========================================================================== gcc: name: "GCC ${{ matrix.version }} (${{ matrix.os }})" runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: # Ubuntu 22.04 preinstalls GCC 10, 11, 12; GCC 9 lives in universe. - { os: ubuntu-22.04, version: 9, needs_install: true } - { os: ubuntu-22.04, version: 10 } - { os: ubuntu-22.04, version: 11 } - { os: ubuntu-22.04, version: 12 } # Ubuntu 24.04 preinstalls GCC 12, 13, 14. - { os: ubuntu-latest, version: 13 } - { os: ubuntu-latest, version: 14 } # GCC 15, 16 from the toolchain test PPA. - { os: ubuntu-latest, version: 15, needs_install: true, needs_ppa: true } - { os: ubuntu-latest, version: 16, needs_install: true, needs_ppa: true } steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Add Toolchain Test PPA (newer GCC) if: matrix.needs_ppa run: | sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test - name: Install GCC ${{ matrix.version }} if: matrix.needs_install run: | sudo apt-get update sudo apt-get install -y gcc-${{ matrix.version }} g++-${{ matrix.version }} - name: Configure, Build & Test (Static & Shared) shell: bash run: | for LIB_TYPE in "STATIC" "SHARED"; do echo ">>> Starting Build: GCC ${{ matrix.version }} – $LIB_TYPE" BUILD_DIR="build_${LIB_TYPE}" SHARED_FLAG="OFF" if [ "$LIB_TYPE" == "SHARED" ]; then SHARED_FLAG="ON"; fi cmake -S . -B $BUILD_DIR \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_C_COMPILER=gcc-${{ matrix.version }} \ -DCMAKE_CXX_COMPILER=g++-${{ matrix.version }} \ -DCMAKE_C_STANDARD=17 \ -DZXC_NATIVE_ARCH=OFF \ -DZXC_ENABLE_LTO=OFF \ -DBUILD_SHARED_LIBS=$SHARED_FLAG cmake --build $BUILD_DIR --config Release --parallel echo "Running Tests for $LIB_TYPE..." ctest --test-dir $BUILD_DIR -C Release --output-on-failure echo ">>> Completed Build: $LIB_TYPE" echo "" done # =========================================================================== # MinGW on Windows (32-bit & 64-bit) # =========================================================================== mingw: name: "MinGW ${{ matrix.name }}" runs-on: windows-latest strategy: fail-fast: false matrix: include: - name: "x86 32-bit" msystem: MINGW32 pkg_prefix: mingw-w64-i686 - name: "x86_64 64-bit" msystem: MINGW64 pkg_prefix: mingw-w64-x86_64 defaults: run: shell: msys2 {0} steps: - name: Setup MSYS2 uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2.32.0 with: msystem: ${{ matrix.msystem }} update: true install: >- ${{ matrix.pkg_prefix }}-gcc ${{ matrix.pkg_prefix }}-cmake make - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Configure, Build & Test (Static & Shared) run: | for LIB_TYPE in "STATIC" "SHARED"; do echo ">>> Starting Build: MinGW ${{ matrix.name }} – $LIB_TYPE" BUILD_DIR="build_${LIB_TYPE}" SHARED_FLAG="OFF" if [ "$LIB_TYPE" == "SHARED" ]; then SHARED_FLAG="ON"; fi cmake -S . -B $BUILD_DIR \ -G "MSYS Makefiles" \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_C_STANDARD=17 \ -DZXC_NATIVE_ARCH=OFF \ -DBUILD_SHARED_LIBS=$SHARED_FLAG cmake --build $BUILD_DIR --config Release --parallel echo "Running Tests for $LIB_TYPE..." ctest --test-dir $BUILD_DIR -C Release --output-on-failure echo ">>> Completed Build: $LIB_TYPE" echo "" done # =========================================================================== # GCC ARM cross-compilation on Ubuntu (32-bit & 64-bit) # =========================================================================== gcc-arm: name: "GCC ARM ${{ matrix.name }}" runs-on: ubuntu-latest strategy: fail-fast: false matrix: include: - name: "32-bit (armhf)" cross_pkg: gcc-arm-linux-gnueabihf cross_prefix: arm-linux-gnueabihf cmake_processor: arm qemu_binary: qemu-arm - name: "64-bit (aarch64)" cross_pkg: gcc-aarch64-linux-gnu cross_prefix: aarch64-linux-gnu cmake_processor: aarch64 qemu_binary: qemu-aarch64 steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install cross-compiler & QEMU run: | sudo apt-get update sudo apt-get install -y ${{ matrix.cross_pkg }} qemu-user - name: Configure, Build & Test (Static & Shared) shell: bash run: | CROSS_C=${{ matrix.cross_prefix }}-gcc CROSS_CXX=${{ matrix.cross_prefix }}-g++ CROSS_STRIP=/usr/bin/${{ matrix.cross_prefix }}-strip SYSROOT=/usr/${{ matrix.cross_prefix }} for LIB_TYPE in "STATIC" "SHARED"; do echo ">>> Starting Build: GCC ARM ${{ matrix.name }} – $LIB_TYPE" BUILD_DIR="build_${LIB_TYPE}" SHARED_FLAG="OFF" if [ "$LIB_TYPE" == "SHARED" ]; then SHARED_FLAG="ON"; fi cmake -S . -B $BUILD_DIR \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_C_COMPILER=$CROSS_C \ -DCMAKE_CXX_COMPILER=$CROSS_CXX \ -DCMAKE_STRIP=$CROSS_STRIP \ -DCMAKE_SYSTEM_NAME=Linux \ -DCMAKE_SYSTEM_PROCESSOR=${{ matrix.cmake_processor }} \ -DCMAKE_C_STANDARD=17 \ -DZXC_NATIVE_ARCH=OFF \ -DBUILD_SHARED_LIBS=$SHARED_FLAG cmake --build $BUILD_DIR --config Release --parallel echo "Running Tests for $LIB_TYPE via QEMU..." ${{ matrix.qemu_binary }} -L $SYSROOT ./$BUILD_DIR/zxc_test echo ">>> Completed Build: $LIB_TYPE" echo "" done # =========================================================================== # Meson (alternative build system) # =========================================================================== meson: name: "Meson ${{ matrix.name }}" runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: - name: Linux x86_64 os: ubuntu-latest - name: Linux ARM64 os: ubuntu-24.04-arm steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Meson and Ninja run: pip3 install --require-hashes -r .github/requirements/meson.txt - name: Setup (static) run: meson setup build-static -Ddefault_library=static - name: Build (static) run: meson compile -C build-static - name: Test (static) run: meson test -C build-static --print-errorlogs - name: Setup (shared) run: meson setup build-shared -Ddefault_library=shared - name: Build (shared) run: meson compile -C build-shared - name: Test (shared) run: meson test -C build-shared --print-errorlogs zxc-0.13.0/.github/workflows/quality.yml000066400000000000000000000076321522536370400202260ustar00rootroot00000000000000name: Code Quality on: workflow_dispatch: push: branches: [ main ] pull_request: branches: [ main ] permissions: contents: read concurrency: cancel-in-progress: true group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} jobs: format: name: Code Quality - Clang-Format runs-on: ubuntu-latest steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install clang-format 22 run: | wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc echo "deb http://apt.llvm.org/$(lsb_release -cs)/ llvm-toolchain-$(lsb_release -cs)-22 main" | sudo tee /etc/apt/sources.list.d/llvm-22.list sudo apt-get update sudo apt-get install -y clang-format-22 - name: Check Formatting run: make format-check env: CLANG_FORMAT: clang-format-22 cppcheck: name: Code Quality - Cppcheck runs-on: ubuntu-latest steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Cppcheck and Cross-Compiler run: | sudo apt-get update sudo apt-get install -y cppcheck gcc-aarch64-linux-gnu libc6-dev-arm64-cross - name: Configure CMake (x86 Compile Database) run: mkdir build_x86 && cd build_x86 && cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON .. - name: Configure CMake (ARM64 Compile Database) run: | mkdir build_arm && cd build_arm cmake -DCMAKE_SYSTEM_NAME=Linux \ -DCMAKE_SYSTEM_PROCESSOR=aarch64 \ -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc \ -DCMAKE_EXPORT_COMPILE_COMMANDS=ON .. - name: Run Cppcheck (x86 Variants) run: | cppcheck --project=build_x86/compile_commands.json \ --enable=all \ --suppress=missingIncludeSystem --suppress=*:src/lib/vendors/rapidhash.h \ --check-level=exhaustive \ --inline-suppr \ --error-exitcode=1 \ -j4 - name: Run Cppcheck (ARM64 Variants) run: | cppcheck --project=build_arm/compile_commands.json \ --enable=all \ --suppress=missingIncludeSystem --suppress=*:src/lib/vendors/rapidhash.h \ --check-level=exhaustive \ --inline-suppr \ --error-exitcode=1 \ -j4 quality-checks: name: Code Quality - Advanced Checks runs-on: ubuntu-latest steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Analysis Tools run: | sudo apt-get update sudo apt-get install -y clang-tools valgrind - name: Clang Static Analyzer (Scan-Build) run: | mkdir -p build_clang scan-build cmake -S . -B build_clang -DCMAKE_BUILD_TYPE=Debug scan-build --status-bugs cmake --build build_clang - name: Build for Valgrind (Debug) run: | cmake -S . -B build_valgrind -DCMAKE_BUILD_TYPE=Debug -DZXC_NATIVE_ARCH=OFF cmake --build build_valgrind - name: Valgrind Memory Check working-directory: build_valgrind run: | valgrind --leak-check=full \ --show-leak-kinds=all \ --track-origins=yes \ --error-exitcode=1 \ ./zxc_test - name: Unicode Linting shell: bash run: | echo "Scanning for non-ASCII characters in source files..." if grep -rP --include="*.c" --include="*.h" "[^\x00-\x7F]" .; then echo "::error::Non-ASCII characters found in source files." exit 1 else echo "No non-ASCII characters found." fizxc-0.13.0/.github/workflows/scorecard.yml000066400000000000000000000022201522536370400204670ustar00rootroot00000000000000name: OSSF Scorecard on: workflow_dispatch: schedule: - cron: '0 5 * * 3' push: branches: [ main ] permissions: contents: read jobs: analysis: name: Scorecard analysis runs-on: ubuntu-latest timeout-minutes: 10 permissions: security-events: write id-token: write contents: read actions: read steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Run analysis uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 with: results_file: results.sarif results_format: sarif publish_results: true - name: Upload artifact uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: SARIF file path: results.sarif retention-days: 5 - name: Upload to code-scanning uses: github/codeql-action/upload-sarif@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 with: sarif_file: results.sarif zxc-0.13.0/.github/workflows/security.yml000066400000000000000000000036231522536370400204010ustar00rootroot00000000000000name: Code Security on: workflow_dispatch: push: branches: [ main ] pull_request: branches: [ main ] permissions: contents: read jobs: analyze: name: Analyze (${{ matrix.language }}) runs-on: ubuntu-latest timeout-minutes: 10 permissions: contents: read security-events: write strategy: fail-fast: false matrix: language: [ 'c-cpp', 'rust', 'python', 'go', 'javascript-typescript' ] steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Initialize CodeQL uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/codeql-config.yml - name: Build Project run: | if [ "${{ matrix.language }}" = "c-cpp" ]; then make elif [ "${{ matrix.language }}" = "rust" ]; then cd wrappers/rust cargo build --workspace --all-targets elif [ "${{ matrix.language }}" = "python" ]; then python3 -m pip install --require-hashes -r wrappers/python/requirements/build-deps.txt python3 -m pip install -e wrappers/python --no-build-isolation --no-deps elif [ "${{ matrix.language }}" = "go" ]; then cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DZXC_BUILD_CLI=OFF -DZXC_BUILD_TESTS=OFF cmake --build build --parallel cd wrappers/go CGO_ENABLED=1 CGO_LDFLAGS="-L../../build -lzxc -lpthread" go build ./... elif [ "${{ matrix.language }}" = "javascript-typescript" ]; then cd wrappers/nodejs npm ci fi - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 with: category: "/language:${{ matrix.language }}" zxc-0.13.0/.github/workflows/vendors.yml000066400000000000000000000021541522536370400202100ustar00rootroot00000000000000name: Update Vendors on: workflow_dispatch: schedule: - cron: '0 4 * * 1' permissions: contents: read jobs: update-rapidhash-header: runs-on: ubuntu-slim permissions: contents: write pull-requests: write steps: - name: Checkout repo uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Download latest rapidhash.h run: | curl -o src/lib/vendors/rapidhash.h https://raw.githubusercontent.com/Nicoshev/rapidhash/refs/heads/master/rapidhash.h - name: Create Pull Request uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 with: token: ${{ secrets.GITHUB_TOKEN }} commit-message: "chore(deps): update rapidhash.h from upstream" title: "Automatic update of rapidhash.h" body: | This PR updates `rapidhash.h` with the latest version from the official repository. Source : [Nicoshev/rapidhash](https://github.com/Nicoshev/rapidhash) branch: auto-update/rapidhash delete-branch: truezxc-0.13.0/.github/workflows/wrapper-go-test.yml000066400000000000000000000062421522536370400215720ustar00rootroot00000000000000name: Test Go Package on: workflow_dispatch: release: types: [ published ] permissions: contents: read jobs: test: name: Test on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: - os: ubuntu-latest - os: ubuntu-24.04-arm - os: macos-26 - os: windows-latest - os: windows-11-arm steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Select Latest Compiler (Linux) if: runner.os == 'Linux' run: | echo "CC=gcc-14" >> $GITHUB_ENV echo "CXX=g++-14" >> $GITHUB_ENV gcc-14 --version - name: Select Latest Xcode (macOS) if: runner.os == 'macOS' run: | sudo xcode-select -s /Applications/Xcode_26.4.app clang --version - name: Install llvm-mingw (Windows ARM64) if: matrix.os == 'windows-11-arm' shell: pwsh run: | $LLVM_MINGW_URL = "https://github.com/mstorsjo/llvm-mingw/releases/download/20260407/llvm-mingw-20260407-ucrt-aarch64.zip" Invoke-WebRequest -Uri $LLVM_MINGW_URL -OutFile llvm-mingw.zip Expand-Archive llvm-mingw.zip -DestinationPath C:\llvm-mingw $MINGW_BIN = "C:\llvm-mingw\llvm-mingw-20260407-ucrt-aarch64\bin" echo "$MINGW_BIN" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append echo "CC=aarch64-w64-mingw32-gcc" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - name: Set up Go uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version: '1.21' cache: false - name: Build ZXC Core Library (Unix) if: runner.os != 'Windows' run: | cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DZXC_BUILD_CLI=OFF -DZXC_BUILD_TESTS=OFF cmake --build build --parallel - name: Build ZXC Core Library (Windows) if: runner.os == 'Windows' run: | cmake -B build -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DZXC_BUILD_CLI=OFF -DZXC_BUILD_TESTS=OFF cmake --build build --parallel - name: Run Go Tests (Unix) if: runner.os != 'Windows' working-directory: ./wrappers/go run: CGO_ENABLED=1 CGO_LDFLAGS="-L../../build -lzxc -lpthread" go test -v -count=1 ./... - name: Run Go Tests (Windows) if: runner.os == 'Windows' working-directory: ./wrappers/go env: CGO_ENABLED: "1" CGO_LDFLAGS: "-L../../build -lzxc" run: go test -v -count=1 ./... - name: Run Go Benchmarks (Unix) if: runner.os != 'Windows' working-directory: ./wrappers/go run: CGO_ENABLED=1 CGO_LDFLAGS="-L../../build -lzxc -lpthread" go test -bench=. -benchmem -count=1 ./... - name: Run Go Benchmarks (Windows) if: runner.os == 'Windows' working-directory: ./wrappers/go env: CGO_ENABLED: "1" CGO_LDFLAGS: "-L../../build -lzxc" run: go test -bench=. -benchmem -count=1 ./... zxc-0.13.0/.github/workflows/wrapper-nodejs-publish.yml000066400000000000000000000107201522536370400231320ustar00rootroot00000000000000name: Publish Node.js Package on: workflow_dispatch: release: types: [ published ] permissions: contents: read jobs: build: name: Build on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: - os: ubuntu-latest arch: x86_64 - os: ubuntu-24.04-arm arch: aarch64 - os: macos-26 arch: arm64 - os: macos-26-intel arch: x86_64 - os: windows-latest arch: AMD64 - os: windows-11-arm arch: ARM64 defaults: run: working-directory: ./wrappers/nodejs steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Select Latest Compiler (Linux) if: runner.os == 'Linux' run: | echo "CC=gcc-14" >> $GITHUB_ENV echo "CXX=g++-14" >> $GITHUB_ENV gcc-14 --version - name: Select Latest Xcode (macOS) if: runner.os == 'macOS' run: | sudo xcode-select -s /Applications/Xcode_26.4.app clang --version - name: Set up Node.js uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: "22" - name: Install dependencies and build run: npm ci - name: Run tests run: npx vitest run --globals - name: Package prebuilt addon run: | mkdir -p prebuilds/${{ matrix.os }}-${{ matrix.arch }} cp build/Release/zxc_nodejs.node prebuilds/${{ matrix.os }}-${{ matrix.arch }}/ - name: Upload prebuilt addon uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: prebuilt-${{ matrix.os }}-${{ matrix.arch }} path: wrappers/nodejs/prebuilds/ test: name: Test on ${{ matrix.os }} Node ${{ matrix.node }} needs: [build] runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, ubuntu-24.04-arm, macos-26, macos-26-intel, windows-latest, windows-11-arm] node: ["22", "24"] defaults: run: working-directory: ./wrappers/nodejs steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Select Latest Compiler (Linux) if: runner.os == 'Linux' run: | echo "CC=gcc-14" >> $GITHUB_ENV echo "CXX=g++-14" >> $GITHUB_ENV gcc-14 --version - name: Select Latest Xcode (macOS) if: runner.os == 'macOS' run: | sudo xcode-select -s /Applications/Xcode_26.4.app clang --version - name: Set up Node.js uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ matrix.node }} - name: Install dependencies and build run: npm ci - name: Run tests run: npx vitest run --globals publish: name: Publish to npm needs: [build, test] runs-on: ubuntu-slim if: github.event_name == 'release' permissions: id-token: write defaults: run: working-directory: ./wrappers/nodejs steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Node.js uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: "22" registry-url: "https://registry.npmjs.org" - name: Verify Version Matches Release Tag env: RELEASE_TAG: ${{ github.event.release.tag_name }} run: | # Remove 'v' prefix if present RELEASE_VERSION="${RELEASE_TAG#v}" PACKAGE_VERSION=$(node -p "require('./package.json').version") if [ "$RELEASE_VERSION" != "$PACKAGE_VERSION" ]; then echo "Version mismatch: Release tag is $RELEASE_VERSION but package.json has $PACKAGE_VERSION" exit 1 fi echo "Version matches: $RELEASE_VERSION" - name: Publish to npm run: npm publish --provenance --access public env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Summary if: success() run: | echo "Successfully published zxc-compress to npm!" echo "Package: https://www.npmjs.com/package/zxc-compress" zxc-0.13.0/.github/workflows/wrapper-python-publish.yml000066400000000000000000000116101522536370400231700ustar00rootroot00000000000000name: Publish Python Package on: workflow_dispatch: release: types: [ published ] permissions: contents: read jobs: build_wheels: name: Build wheels on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: - os: ubuntu-latest arch: x86_64 - os: ubuntu-24.04-arm arch: aarch64 - os: macos-26 arch: arm64 - os: macos-26-intel arch: x86_64 - os: windows-latest arch: AMD64 - os: windows-11-arm arch: ARM64 steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - name: Select Latest Compiler (Linux) if: runner.os == 'Linux' run: | echo "CC=gcc-14" >> $GITHUB_ENV echo "CXX=g++-14" >> $GITHUB_ENV gcc-14 --version - name: Select Latest Xcode (macOS) if: runner.os == 'macOS' run: | sudo xcode-select -s /Applications/Xcode_26.4.app clang --version - name: Set up Python uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "3.12" - name: Install build tools run: python -m pip install --require-hashes -r wrappers/python/requirements/build.txt - name: Get version from git id: version run: echo "version=$(python -m setuptools_scm)" >> $GITHUB_OUTPUT working-directory: ./wrappers/python - name: Build wheels run: python -m cibuildwheel wrappers/python --output-dir wheelhouse env: # Build for Python 3.10-3.13 CIBW_BUILD: "cp310-* cp311-* cp312-* cp313-*" # Skip 32-bit, musllinux, and Python 3.10-3.11 on Windows (missing Development.Module) CIBW_SKIP: "*-win32 *-manylinux_i686 *-musllinux_* cp310-win* cp311-win*" CIBW_ARCHS: "${{ matrix.arch }}" CIBW_ENVIRONMENT: "CMAKE_ARGS='-DZXC_NATIVE_ARCH=OFF -DCMAKE_POSITION_INDEPENDENT_CODE=ON' SETUPTOOLS_SCM_PRETEND_VERSION=${{ steps.version.outputs.version }}" - name: Upload wheels uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheels-${{ matrix.os }}-${{ matrix.arch }} path: ./wheelhouse/*.whl build_sdist: name: Build source distribution runs-on: ubuntu-slim steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - name: Set up Python uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "3.12" - name: Install build run: python -m pip install --require-hashes -r wrappers/python/requirements/sdist.txt - name: Build sdist run: python -m build --sdist working-directory: ./wrappers/python - name: Upload sdist uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: sdist path: ./wrappers/python/dist/*.tar.gz test_wheels: name: Test wheels on ${{ matrix.os }} ${{ matrix.python }} needs: [build_wheels] runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, ubuntu-24.04-arm, macos-26, macos-26-intel, windows-latest, windows-11-arm] python: ["3.12", "3.13"] steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Python uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: ${{ matrix.python }} - name: Download wheels uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: path: wheelhouse pattern: wheels-* merge-multiple: true - name: Install wheel and test dependencies run: | pip install --no-index --find-links wheelhouse zxc-compress pip install --require-hashes -r wrappers/python/requirements/test.txt - name: Run test suite run: pytest wrappers/python/tests/ -v publish: name: Publish to PyPI needs: [build_wheels, build_sdist, test_wheels] runs-on: ubuntu-latest if: github.event_name == 'release' permissions: id-token: write steps: - name: Download all artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: path: dist merge-multiple: true - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 with: packages-dir: dist/ zxc-0.13.0/.github/workflows/wrapper-rust-publish.yml000066400000000000000000000066151522536370400226550ustar00rootroot00000000000000name: Publish Rust Crates on: workflow_dispatch: release: types: [ published ] permissions: contents: read jobs: test: name: Test on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: - os: ubuntu-latest arch: x86_64 - os: ubuntu-24.04-arm arch: aarch64 - os: macos-26 arch: x86_64 arm64 - os: windows-latest arch: AMD64 - os: windows-11-arm arch: ARM64 defaults: run: working-directory: ./wrappers/rust steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Select Latest Compiler (Linux) if: runner.os == 'Linux' run: | echo "CC=gcc-14" >> $GITHUB_ENV echo "CXX=g++-14" >> $GITHUB_ENV gcc-14 --version - name: Select Latest Xcode (macOS) if: runner.os == 'macOS' run: | sudo xcode-select -s /Applications/Xcode_26.4.app clang --version - name: Install Rust Toolchain uses: actions-rust-lang/setup-rust-toolchain@166cdcfd11aee3cb47222f9ddb555ce30ddb9659 # v1.17.0 with: toolchain: stable cache-workspaces: "wrappers/rust -> target" - name: Run Tests run: cargo test --workspace publish: name: Publish to crates.io needs: [test] runs-on: ubuntu-slim if: github.event_name == 'release' defaults: run: working-directory: ./wrappers/rust steps: - name: Checkout Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Rust Toolchain uses: actions-rust-lang/setup-rust-toolchain@166cdcfd11aee3cb47222f9ddb555ce30ddb9659 # v1.17.0 with: toolchain: stable cache-workspaces: "wrappers/rust -> target" - name: Verify Version Matches Release Tag env: RELEASE_TAG: ${{ github.event.release.tag_name }} run: | # Remove 'v' prefix if present RELEASE_VERSION="${RELEASE_TAG#v}" CARGO_VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/') if [ "$RELEASE_VERSION" != "$CARGO_VERSION" ]; then echo "Version mismatch: Release tag is $RELEASE_VERSION but Cargo.toml has $CARGO_VERSION" exit 1 fi echo "Version matches: $RELEASE_VERSION" - name: Publish zxc-compress-sys (FFI bindings) env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} run: | cd zxc-sys cargo publish continue-on-error: false # Wait for zxc-compress-sys to be available on crates.io before publishing zxc-compress - name: Wait for zxc-compress-sys to propagate run: sleep 30 - name: Publish zxc-compress (safe wrapper) env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} run: | cd zxc cargo publish continue-on-error: false - name: Summary if: success() run: | echo "Successfully published:" echo "- zxc-compress-sys (FFI bindings)" echo "- zxc-compress (safe wrapper)" echo "" echo "Crates are now available on crates.io!" zxc-0.13.0/.github/workflows/wrapper-wasm.yml000066400000000000000000000035621522536370400211610ustar00rootroot00000000000000# ZXC - High-performance lossless compression # # Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. # SPDX-License-Identifier: BSD-3-Clause name: WASM Build on: workflow_dispatch: release: types: [ published ] push: branches: [ main ] paths: - 'src/**' - 'include/**' - 'wrappers/wasm/**' - 'CMakeLists.txt' - '.github/workflows/wrapper-wasm.yml' permissions: contents: read concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: wasm-build: name: Build & Test WASM runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup Emscripten uses: mymindstorm/setup-emsdk@4528d102f7230f0e7b276855c01ea1159be0e984 # v16 with: version: 3.1.56 - name: Verify Emscripten run: emcc --version - name: Configure run: emcmake cmake -B build-wasm -DCMAKE_BUILD_TYPE=Release - name: Build run: cmake --build build-wasm --parallel - name: Verify outputs run: | ls -lh build-wasm/zxc.js build-wasm/zxc.wasm echo "--- WASM file size ---" wc -c build-wasm/zxc.wasm - name: Setup Node.js uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: '22' - name: Run Tests run: BUILD_DIR=build-wasm node wrappers/wasm/test.mjs - name: Upload WASM Artifacts uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: zxc-wasm path: | build-wasm/zxc.js build-wasm/zxc.wasm wrappers/wasm/zxc_wasm.js wrappers/wasm/package.json wrappers/wasm/README.md retention-days: 30 zxc-0.13.0/.gitignore000066400000000000000000000021321522536370400143740ustar00rootroot00000000000000# Prerequisites *.d # Object files *.o *.ko *.obj *.elf # Linker output *.ilk *.map *.exp # Precompiled Headers *.gch *.pch # Libraries *.lib *.a *.la *.lo # Shared objects (inc. Windows DLLs) *.dll *.so *.so.* *.dylib # Executables *.exe *.out *.app *.i*86 *.x86_64 *.hex # Debug files *.dSYM/ *.su *.idb *.pdb # debug information files *.dwo CMakeLists.txt.user CMakeCache.txt CMakeFiles CMakeScripts Testing Makefile !/Makefile cmake_install.cmake install_manifest.txt compile_commands.json CTestTestfile.cmake _deps CMakeUserPresets.json # CLion # JetBrains specific template is maintained in a separate JetBrains.gitignore that can # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #cmake-build-* build/* # Doxygen docs/html/ docs/latex/ docs/rtf/ docs/man/*.1 docs/xml/ *.doxygen_sqlite3 doxyfile.inc searchdata.xml # Coverage build-cov/ # Meson builddir*/ subprojects/ zxc-0.13.0/.snyk000066400000000000000000000001321522536370400133670ustar00rootroot00000000000000# Snyk (https://snyk.io) policy file exclude: code: - tests/** - conformance/** zxc-0.13.0/.sonarcloud.properties000066400000000000000000000036321522536370400167570ustar00rootroot00000000000000#============================================================================== # SonarQube Cloud - Automatic Analysis scope (.sonarcloud.properties) #============================================================================== # Read by SonarQube Cloud's Automatic Analysis (the GitHub-app integration; no # CI / SonarScanner). The project key & organization come from the repository # binding configured in the SonarQube Cloud UI, so they are NOT set here. # # Analyses first-party shipped source (C library + CLI + public headers + the # hand-written language wrappers) and excludes everything that must not raise # findings. Note: Automatic Analysis does not compute coverage, so test files # are excluded outright (no need to keep them analysed-but-muted), and issue- # level ignores live in the UI (Analysis Scope) rather than this file. #============================================================================== sonar.sourceEncoding = UTF-8 # --- Sources (first-party, shipped) ------------------------------------------ # C library + CLI + public headers, and the hand-written wrapper bindings. sonar.sources = src,include,wrappers # --- Exclusions -------------------------------------------------------------- # tests / conformance + wrapper tests : not shipped # docs / build : documentation and build output # src/lib/vendors : vendored third-party (rapidhash) # wrappers/wasm/zxc_wasm.js : generated emscripten output # node_modules/target/.venv/... : per-language dependency & build dirs # (installed third-party code) sonar.exclusions = tests/**,conformance/**,wrappers/**/*_test.go,wrappers/**/test*/**,wrappers/wasm/test.mjs,docs/**,**/build/**,src/lib/vendors/**,wrappers/wasm/zxc_wasm.js,**/node_modules/**,**/target/**,**/.venv/**,**/venv/**,**/__pycache__/**,**/.pytest_cache/**,**/dist/**,**/prebuilds/** zxc-0.13.0/CHANGELOG.md000066400000000000000000000556141522536370400142320ustar00rootroot00000000000000# Changelog ## [0.13.0] - 2026-07-14 api: Refines code coverage reporting (#322) api: Adds Level 7 (Ultra) compression to wrappers (#317) api: ZXC format v7: PivCo Huffman, level 7 (ULTRA), space-speed selection, in-place decode (#315) api: bump node-addon-api from 8.8.0 to 8.9.0 in /wrappers/nodejs (#308) api: Refines documentation and API option descriptions (#299) api: Enhances code quality and analysis setup (#294) perf: Optimizes compression and decompression performance (#319) perf: Optimizes Huffman decoding and compression margins (#301) cli: Refines CLI with progress, robust parsing, and file I/O (#318) cli: Update README with zxc CLI installation option (#316) cli: Enhances code quality and robustness across components (#296) cli: Enhance README clarity and quick start build: Upgrades Python build deps and fixes Rust linker (#323) build: bump pytest from 9.0.3 to 9.1.1 in /wrappers/python (#310) build: bump setuptools-scm in /wrappers/python (#311) build: bump cibuildwheel from 3.4.1 to 4.1.0 in /wrappers/python (#309) build: bump vitest from 4.1.7 to 4.1.9 in /wrappers/nodejs (#307) build: Refines code style, type safety, and modernizes JS/Go wrappers (#297) build: Refines CI/CD workflows and test stability (#292) portability: Add OS and libc compatibility CI for musl Linux and FreeBSD (#291) doc: Refactor LZ77 search loop conditions (#298) doc: Update README badges misc: Ignore vendors code in coverage reports misc: Improves robustness and refines memory management (#321) misc: Update lzbench benchmark source to v0.13.0 misc: Caches dict Huffman tree upon attach (#320) misc: bump actions/setup-python from 6.2.0 to 6.3.0 (#305) misc: bump oss-fuzz-base/base-builder in /.clusterfuzzlite (#312) misc: bump msys2/setup-msys2 from 2.31.1 to 2.32.0 (#304) misc: bump actions-rust-lang/setup-rust-toolchain (#306) misc: bump actions/attest-build-provenance from 4.1.0 to 4.1.1 (#303) misc: bump softprops/action-gh-release from 3.0.0 to 3.0.1 (#302) misc: Refines code quality and addresses warnings (#295) misc: Update bash conditional expressions to '[[ ... ]]' (#293) misc: Statically define Meson project version and SOVERSION (#290) ## [0.12.0] - 2026-06-18 api: Implements shared dictionary Huffman table (#275) api: Enhances format validation (#271) api: Overhauls dictionary fuzzer for robust testing (#270) api: Adds comprehensive dictionary API to all wrappers (#269) api: Introduces pre-trained dictionary compression (#261) api: bump node-addon-api from 8.7.0 to 8.8.0 in /wrappers/nodejs (#253) api: Adds conformance test suite and improves empty frame handling (#246) api: Harden decompressor buffer bounds checks (#229) api: Introduces static context API for caller-managed workspaces (#242) api: Adds custom reader interface for seekable archives (#240) api: wrappers: Adds seekable random-access decompression API (#224) api: Internalizes Sans-IO API and frame primitives (#225) perf: Optimize LZ run decoding for short offsets (#276) perf: Add make target to run decoder conformance suite perf: Improve Huffman leaf sorting with bucket sort (#244) cli: enable native wildcard expansion for CLI on Windows (#284) cli: Adds `unzxc` alias and renames dictionary training option (#272) cli: Remove Snyk policy ignore cli: Addresses Snyk scan findings and improves robustness (#273) cli: Adds native Meson build system support (#245) build: consolidate ClusterFuzzLite fuzzer builds and runs (#287) build: bump tar from 7.5.13 to 7.5.16 in /wrappers/nodejs (#286) build: bump vite from 8.0.14 to 8.0.16 in /wrappers/nodejs (#285) build: Add GCC 15 and 16 to CI multi-compiler matrix (#274) build: Update LZbench branch for benchmark workflow build: Enables empty data compression/decompression (#265) build: Update package descriptions build: Introduce dedicated SSE2 SIMD optimization path for x86-64 (#259) build: tests: Enforces golden file format stability (#256) build: bump vitest from 4.1.6 to 4.1.7 in /wrappers/nodejs (#254) build: Restrict SBOM generation to tag pushes build: Add qemu cpu targeting for simd dispatch coverage (#238) build: Bump cibuildwheel from 3.3.1 to 3.4.1 in /wrappers/python (#233) build: Bump vitest from 4.1.5 to 4.1.6 in /wrappers/nodejs (#234) build: Use upstream LZbench for benchmarks build: Add automated ABI stability check workflow (#222) build: Generates SBOM for GitHub releases (#226) build: Automate CHANGELOG generation for releases (#220) build: Standardizes release artifact structure and naming (#223) build: Pins Python wrapper build dependencies (#221) build: Pin Python wrapper dependencies with hashes (#219) build: Strengthens CI/CD security and reliability (#218) build: Updates Node.js and Rust wrapper CI & scope heavy workflows to source paths (#217) portability: Abstracts memory and sorting for portability (#228) doc: Enhance API documentation with full Doxygen comments (#280) doc: Removes NUM block type and advances format to v6 (#264) doc: Enhance README with specific decode performance claims doc: Include README and add brief descriptions to Doxygen output doc: Add format conformance and stability overview to README doc: Add OpenSSF Scorecard badge to README misc: Gate AVX2/AVX512 detection on OS-enabled YMM/ZMM state (#283) misc: Support empty shared Huffman tables (#278) (#279) misc: Update Codecov action to v7 (#267) misc: Update Ubuntu version badge to 26.10 misc: Rename SBOM template to lowercase misc: Define LZ77 search limit as symbolic constant (#262) misc: bump oss-fuzz-base/base-builder in /.clusterfuzzlite (#260) misc: Accelerate LZ77 match finding with repeat offset seed @L6 (#257) misc: bump oss-fuzz-base/base-builder in /.clusterfuzzlite (#255) misc: Add seekable random-access decompression to Python (#250) misc: bump github/codeql-action from 4.35.5 to 4.36.0 (#252) misc: Pin Meson and Ninja dependencies with hashes (#204) (#251) misc: Adjust decompressor output buffer tail padding (#249) misc: exclude conformance directory from Snyk Code scanning misc: Move Doxyfile to docs directory (#243) misc: Bump actions/attest-build-provenance from 3.0.0 to 4.1.0 (#232) misc: Bump codecov/codecov-action from 6.0.0 to 6.0.1 (#231) misc: Update ClusterFuzzLite base image and enable Dependabot (#230) ## [0.11.0] - 2026-05-13 api: Increases default block size to 512 KB (#216) api: Introduces Level 6 compression with Huffman literals (#208) api: Feat: Add WHATWG TransformStream adapters and `detectZxc` utility (#211) api: Add Go io.Reader/Writer adapters for zxc streams (#209) api: Introduce push-based streaming API for non-blocking integrations (#204) api: Harden block API: overflow fix, memory shrink, wrapper coverage (#198) api: Integrates seekable API fuzzer (#196) fix: Fix TSan data race between async writer and shutdown sentinel (#201) perf: Introduce compression level 6 and tune default block size perf: Update whitepaper with Decompression Bandwidth Frontier graph perf: Refresh Apple M2 benchmark data and clarify effective throughput formula perf: Update whitepaper and README with AMD EPYC 7763 benchmarks perf: Update documentation with zxc v0.11.0 effective throughput benchmark perf: Optimize LZ77 match finding for fast levels with tag-first filter (#215) cli: Customize release artifacts with tailored README and manual build: Adds LEVEL_DENSITY support to wrappers (#214) build: Feat: Add `std::io` streaming adapters to Rust wrapper (#213) build: Fix: allow direct Emscripten module factory injection in WASM wrapper build: Reverts LZbench source to upstream repository build: Updates CI for Go ARM64 and Node.js 22 (#195) doc: Remove Python scripts for benchmark chart generation doc: Migrate benchmark figures to SVG format and streamline documentation doc: Refine benchmark presentation and standardize documentation terminology doc: Refresh v0.11.0 benchmark graphs and update documentation references doc: Update README with current fuzzing iteration count doc: Update whitepaper: streamline memory usage and remove ratio benchmarks doc: Update documentation with zxc v0.11.0 benchmarks for Apple M2 doc: Update documentation with zxc v0.11.0 benchmarks for Apple M2 doc: Update whitepaper memory usage statistics doc: Update documentation with zxc v0.11.0 benchmarks for Apple M2 doc: Update documentation with zxc v0.11.0 benchmarks for AMD EPYC 9B45 doc: Update documentation with zxc v0.11.0 benchmarks for Google Axion doc: Update README memory usage statistics to reflect recent optimizations doc: Add Node.js stream.Transform adapters and zxc frame detection (#212) doc: Add Free Pascal community binding to README + EXAMPLES doc: Adds Nim bindings to README and EXAMPLES doc: Updates README with TurboBench verification details doc: Updates README with architecture support and ARM performance details doc: Updates formatting in README performance table doc: Updates benchmark decompression cycles image for v0.10.0 doc: Updates benchmark performance metrics in documentation misc: Feat: add Python io.RawIOBase adapters and zxc magic detection (#210) misc: Refine: Use named constants for compression level checks misc: Bump mymindstorm/setup-emsdk from 14 to 16 (#206) misc: Bump softprops/action-gh-release from 2 to 3 (#205) misc: Refine: remove PPA requirement for GCC on Ubuntu 22.04 in CI (#207) misc: Harden decompression logic against integer overflows on 32-bits platforms (#202) misc: Split monolithic tests (#200) ## [0.10.0] - 2026-04-16 api: Adds Seekable API documentation and bumps SOVERSION to 3 api: Simplifies checksum selection and standardizes on RapidHash api: Adds seekable archives for random access (#188) api: Adds WebAssembly (WASM) support (#189) api: Adds runtime library information API (#163) api: Introduces enum for future pluggable hashing (#153) api: Add block-level API for compression without file framing (#148) api: Add clang-format integration and top-level Makefile (#147) fix: Fixes CLI error handling and edge cases (#170) perf: Refreshes benchmark performance metrics for competitor libraries perf: Refreshes benchmark performance metrics in whitepaper perf: Refreshes benchmark performance metrics in whitepaper perf: Refreshes benchmark performance metrics in README perf: Adds block size tuning guidelines and benchmarks to documentation perf: Optimizes literal copying with a unified 32-byte fast path (#181) perf: Optimizes LZ77 match finding with split hash table (#179) perf: Optimizes core compression and decompression logic (#177) perf: Optimizes block compression and decompression (#164) perf: Improves workflows (#166) perf: Enhances I/O buffering (#162) perf: Optimizes `log2` and enables BMI/LZCNT extensions (#159) cli: Standardizes PGO and documents usage (#185) cli: Refines stream engine for robustness (#146) cli: Change default file extension to .zxc (#143) cli: Updates CLI default checksum settings (#142) build: Bumps WASM package version to 0.10.0 build: Updates workflow documentation in README build: Updates README badge layout build: Adds Windows ARM64 Build and Refines CI (#194) build: Updates triggers for the WASM build workflow build: Updates GitHub Actions documentation for WASM and security workflows build: Update documentation for ZXC_DISABLE_SIMD build: Add option to disable explicit SIMD code paths (#174) build: Updates Xcode 26.4 path in CI workflows build: Upgrade CI to macOS 26 and latest compilers (#169) build: Update rand requirement from 0.9 to 0.10 in /wrappers/rust (#168) build: Adds fuzzing coverage report workflow (#160) build: Update benchmark suite build: Expands security analysis and updates Go CI (#152) build: Expands security analysis and updates Go CI (#151) build: Adds official Go wrapper (#149) build: Update security badges in README build: Update lzbench source in benchmark workflow doc: Updates status badges in README doc: Updates benchmark methodology and performance claims in documentation doc: Updates decompression efficiency benchmark graph to v0.10.0 doc: Refreshes benchmark data for v0.10.0 in documentation doc: Updates benchmark data and hardware targets in documentation doc: Updates version to 0.10.0 and refreshes benchmark data in documentation doc: Updates benchmark environment details in documentation doc: Refines varint decoding and match length (#186) doc: Streamlines internal encoding and block size definitions (#175) doc: Parameterizes lazy matching length threshold (#158) doc: Add compression ratio benchmarks to the whitepaper doc: Update benchmark graphs for version 0.9.0 misc: Updates Python publish runner to ubuntu-latest misc: Defines ZXC_STATIC_DEFINE for Rust sys-crate build misc: Defines versioning policy and error handling requirements misc: Defines versioning policy and error handling requirements misc: Refactors varint encoding and hash variables for clarity (#183) misc: Hardens decompression overflow checks with padding margin (#146) (#178) misc: Bump codecov/codecov-action from 5 to 6 (#167) misc: Factorize decompression macros to consolidate copy logic (#150) misc: Exclude hard-to-test error paths from code coverage misc: Rename pkg-config module from zxc to libzxc (#144) misc: Cache context variables locally in encoder blocks (#141) ## [0.9.1] - 2026-03-16 fix: Fix CLI parsing and adds block size option (#140) doc: Add tar integration instructions to README ## [0.9.0] - 2026-03-15 api: Add flexible block sizes options and reusable contexts (#138) cli: Update license headers and add SPDX identifier build: Use upstream lzbench and shallow clone in benchmark workflow doc: Update documentation for sticky options in reusable contexts doc: Add Homebrew installation instructions to README doc: Add Homebrew packaging status badge to README doc: Update README with installation instructions and packaging status doc: Add Conan installation section in README (#136) doc: Update documentation misc: Update benchmark images for 0.9.0 misc: Configures corpus storage for fuzzing CI (#139) misc: Implement direct decompression fast path (#135) ## [0.8.3] - 2026-03-06 build: Add compiler compatibility CI workflow (#134) ## [0.8.2] - 2026-03-05 api: Optimizes fuzzers for buffer API and memory reuse (#126) fix: Fix file handle leak in CLI benchmark (#132) cli: Resource management and enforces thread limits (#131) cli: CLI: enhances robustness and resource handling (#130) misc: Refine output file closing logic to avoid closing stdout (#133) misc: Prevents out-of-bounds memory access (#129) ## [0.8.1] - 2026-03-01 build: Updates wrapper versions and refactors ABI versioning build: Adds Node.js wrapper badge ## [0.8.0] - 2026-03-01 api: Adds Node.js wrapper for zxc compression (#107) api: Refactors error handling with errors codes, improves docs and benchmark CLI (#103) fix: Fixes CLI benchmark deadline calculation (#116) fix: Fix potential buffer overflows, decoder underflows, and path handling issues (#105) fix: Fixes typos in documentation perf: Optimizes Node.js wrapper builds and CI perf: Optimize LZ77 hashing strategy and standardize hash table configuration (#106) cli: Adds recursive directory mode (#112) cli: Enforces const correctness for local variables (#110) build: Adds CI support for Alpha architecture (#122) build: Wrapper Python: closes file handles in stream tests (#115) build: Updates benchmark branch build: Wrapper Python: exposes C error constants and enhances error reporting (#111) build: Adds vcpkg installation instructions build: Adds multi-arch support for more architectures doc: Refresh benchmark results for zxc 0.8.0 doc: Adopts dual-scale encoding for chunk size (#118) doc: Standardizes block unit and chunk size logic (#114) doc: Replaces benchmark PNG images with WebP doc: Adds zxc command man page doc: Format: Implement LZ offset bias (+1) to eliminate zero-offset attack vectors (#104) doc: Update README with additional package badges doc: Updates README with TL;DR doc: Adds vcpkg badge to README misc: Bump actions/download-artifact from 7 to 8 (#121) misc: Bump actions/upload-artifact from 6 to 7 (#120) misc: Refactors path handling for robustness (#119) misc: Revert "Standardizes block unit and chunk size logic (#114)" (#117) misc: Clarifies rapidhash component license misc: Adds file format specification misc: Refine introduction wording. misc: Add comprehensive ZXC file format spec with byte-level structures and worked hexdump example ## [0.7.3] - 2026-02-18 fix: Fix workfows builds build: Moves rapidhash.h to vendor directory misc: Finds rapidhash via system or vendored fallback misc: Use lzbench master branch ## [0.7.2] - 2026-02-16 fix: Fix warning for AVX2/AVX512 macro redefinition in native mode build (#94) ## [0.7.1] - 2026-02-14 fix: Fixes potential out-of-bounds read in decompression (#92) doc: Updates documentation misc: Specifies Release config for /O2 flag (#93) misc: Uses zxc-add-0.7.x branch of lzbench fork ## [0.7.0] - 2026-02-13 fix: Fix potential out-of-bounds read (static analysis) (#89) cli: Adds JSON output option for CLI (#88) build: Refactors multiarch CI to use Debian containers (#91) build: Enables big-endian architecture support (#86) ## [0.6.3] - 2026-02-11 api: Add shared library support with BUILD_SHARED_LIBS option (#81) fix: Fix rust version test fix: Fix whitepaper links to graphs build: Improves workflows and wrapper builds (#85) build: Updates binding package names (#84) build: Add Python wrapper (#75) doc: Update bindings docs misc: Add edge case tests (#83) misc: Add Rust support to CodeQL security analysis and fix config path filtering. (#82) ## [0.6.2] - 2026-02-09 fix: Fixes Rust wrapper build for cargo publish (#69) cli: Fixes: addresses vulnerabilities and input validation into CLI (#77) cli: Enables code coverage and configures thresholds cli: Adds test for decompressed size function (#73) cli: Enables code coverage reporting (#72) build: Reverts to official LZbench repository build: Wrapper Rust: improves compression/decompression handling (#70) doc: Move WHITEPAPER to docs folder misc: Add snyk config file misc: Updates license headers and contributing guidelines (#76) misc: Updates contribution guidelines misc: Adds TODO list for future development ## [0.6.1] - 2026-02-06 api: Adds Rust wrapper for ZXC compression (#68) misc: Corrects bit reader initialization (#67) ## [0.6.0] - 2026-02-05 api: Improves compression, ratio and data integrity (#65) build: Update vendors workflow build: Automatic update of rapidhash.h (#59) build: Adds workflow to update rapidhash.h build: Refactors CI workflows and removes SDE testing (#56) doc: Adds brotli to lzbench benchmark (#58) misc: Bump peter-evans/create-pull-request from 7 to 8 (#64) misc: Adds funding file ## [0.5.1] - 2026-01-22 fix: Fixes ARM32 compilation (#55) cli: Enhances CLI input/output security (#52) cli: I/O security, Bit Packing improving and code cleanup (#51) misc: Enables NEON32 support (#54) misc: Bump softprops/action-gh-release from 1 to 2 (#53) ## [0.5.0] - 2026-01-20 fix: Fix benchmark graph to reflect v0.5.0 perf: Improves compression with rapidhash integration and new GHI block format (#43) build: Implements runtime CPU feature dispatch (#50) build: Enhances code quality and security checks build: Updates benchmark workflow doc: Improves RLE and vbyte encoding (#45) doc: Updates copyright year misc: Adds bounds checks to decode block (#49) misc: Applies size mask to section sizes misc: Refactors RLE token handling for clarity misc: Removes release template file misc: Updates copyright year ## [0.4.0] - 2026-01-07 perf: v0.4.0 - Variable offsets, optimized parsing & layout (#23) ## [0.3.3] - 2026-01-06 fix: Fixes buffering issues with stdin and stdout (#37) portability: set binary mode for standard streams (#39) ## [0.3.2] - 2026-01-05 cli: Updates build system and CI configuration (#24) build: Add CI with multi-arch builds + dependabot (#30) build: Improves file handling and code robustness (#28) build: Configures CodeQL with config file (#27) build: Adds CodeQL security analysis (#26) misc: Bump actions/checkout from 4 to 6 (#31) misc: Bump actions/upload-artifact from 4 to 6 (#32) misc: Bump actions/download-artifact from 4 to 7 (#35) misc: Bump actions/cache from 4 to 5 (#33) misc: Bump github/codeql-action from 3 to 4 (#34) misc: Replaces not operator for branchless evaluation. (#29) ## [0.3.1] - 2025-12-27 doc: Add community bindings to readme (#21) doc: Update documentation misc: Fix: flush stdout buffer before exit when using -c flag (#22) ## [0.3.0] - 2025-12-26 perf: Improves compression and decompression (#18) build: Configures fuzzing workflows for multiple targets misc: Use -1..-5 as compression levels (#16) misc: add short options to help, version (#15) ## [0.2.0] - 2025-12-19 api: Updates include path in fuzz test api: Restructure public headers to provide a "sans-IO" API (#9) fix: Fixes potential buffer overflow in decompression fix: Fix fuzzers names perf: Optimize hot path cli: Updates includes for buffer and constants build: Removes 'candidate' branch from CI workflows build: Improves decompression robustness and fixes bugs misc: Initializes memory block after allocation misc: Suppresses cppcheck false positive misc: Refactors compression context buffer management misc: Reduces memory usage of chain table misc: Reduces thread contention in stream engine misc: Refactors file closing and adds benchmark mode misc: Updates atomic type definition misc: Improves fuzzing and stream handling misc: Adds checks to prevent buffer overflows misc: Remove duplicate docs from *.c files misc: Updates fuzzing schedule misc: Adds size check for raw blocks misc: Adds capacity check to avoid buffer overflow misc: Adds bounds check for bits value misc: Prevents buffer overflows during decompression misc: Moves buffer allocation for I/O performance misc: Removes unused memory freeing misc: Signals reader on I/O error in async writer misc: Updates I/O error flag to atomic type misc: Refactors fuzzing tests for better coverage misc: Format code ## [0.1.2] - 2025-12-17 fix: Fixes potential null pointer dereference doc: Adds parameter documentation misc: Adds more unit tests misc: Removes redundant file pointer check misc: Adds input validation to prevent crashes ## [0.1.1] - 2025-12-17 build: Adds name to fuzzing job for clarity build: Enhances fuzzing workflow with manual control build: Updates fuzzing workflow concurrency group build: Adds fuzzing infrastructure with ClusterFuzzLite doc: Update README misc: Quotes cron expression for fuzzing schedule misc: Prevents potential buffer overruns misc: Adds zstd_fast to benchmark misc: Use official lzbench repo for benchmarks ## [0.1.0] - 2025-12-15 api: Update API documentation api: Enhances C/C++ integration with examples fix: Fixes CRC32 detection for SSE4.2 (#6) fix: Fixes compilation on Windows perf: Optimizes decompression for ARM64 build: Removes path restrictions on workflows build: Updates concurrency group key for workflows build: Excludes Markdown files from CI triggers build: Updates benchmark branch build: Adds CI workflows for code quality and benchmarks doc: Initializes the ZXC compression library misc: Adds NEON 32-bit support (#3) misc: Adds code ownership using CODEOWNERS misc: Documenting SIMD Instructions misc: Improves code readability misc: Adds initial contribution guidelines misc: Adds initial implementation of ZXC library misc: Adds the project license misc: Initial commit zxc-0.13.0/CMakeLists.txt000066400000000000000000000717441522536370400151630ustar00rootroot00000000000000# ZXC - High-performance lossless compression # # Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. # SPDX-License-Identifier: BSD-3-Clause cmake_minimum_required(VERSION 3.14) # ============================================================================= # Version extraction from header # ============================================================================= file(READ "include/zxc_constants.h" version_header) string(REGEX MATCH "#define ZXC_VERSION_MAJOR ([0-9]+)" _ "${version_header}") set(MAJOR_VER ${CMAKE_MATCH_1}) string(REGEX MATCH "#define ZXC_VERSION_MINOR ([0-9]+)" _ "${version_header}") set(MINOR_VER ${CMAKE_MATCH_1}) string(REGEX MATCH "#define ZXC_VERSION_PATCH ([0-9]+)" _ "${version_header}") set(PATCH_VER ${CMAKE_MATCH_1}) project(zxc VERSION ${MAJOR_VER}.${MINOR_VER}.${PATCH_VER} LANGUAGES C DESCRIPTION "High-performance asymmetric lossless compression library" ) # ============================================================================= # Build Options # ============================================================================= option(BUILD_SHARED_LIBS "Build shared libraries instead of static" OFF) option(ZXC_NATIVE_ARCH "Enable -march=native for maximum performance" ON) option(ZXC_ENABLE_LTO "Enable Interprocedural Optimization (LTO)" ON) set(ZXC_PGO_MODE "OFF" CACHE STRING "Profile-Guided Optimization mode (OFF/GENERATE/USE)") set_property(CACHE ZXC_PGO_MODE PROPERTY STRINGS OFF GENERATE USE) option(ZXC_BUILD_CLI "Build the command-line interface" ON) option(ZXC_BUILD_TESTS "Build unit tests" ON) option(ZXC_ENABLE_COVERAGE "Enable code coverage generation" OFF) option(ZXC_DISABLE_SIMD "Disable explicit SIMD intrinsics (no AVX/NEON code paths)" OFF) # ============================================================================= # Emscripten / WebAssembly overrides # ============================================================================= if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten") message(STATUS "Emscripten detected - configuring for WebAssembly.") set(ZXC_DISABLE_SIMD ON CACHE BOOL "" FORCE) set(ZXC_BUILD_CLI OFF CACHE BOOL "" FORCE) set(ZXC_BUILD_TESTS OFF CACHE BOOL "" FORCE) set(ZXC_NATIVE_ARCH OFF CACHE BOOL "" FORCE) set(ZXC_ENABLE_LTO OFF CACHE BOOL "" FORCE) set(ZXC_PGO_MODE "OFF" CACHE STRING "" FORCE) set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) endif() if(ZXC_DISABLE_SIMD) add_compile_definitions(ZXC_DISABLE_SIMD) endif() # ============================================================================= # C Standard # ============================================================================= set(CMAKE_C_STANDARD 17) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_EXTENSIONS OFF) # Enable _GNU_SOURCE for ftello/fseeko on Linux # Enable 64-bit off_t on 32-bit Linux to prevent fseeko/ftello/pread truncation if(UNIX AND NOT APPLE) add_compile_definitions(_GNU_SOURCE _FILE_OFFSET_BITS=64 _LARGEFILE_SOURCE) elseif(APPLE) add_compile_definitions(_GNU_SOURCE) endif() # Check for LTO support if(ZXC_ENABLE_LTO AND NOT ZXC_ENABLE_COVERAGE) include(CheckIPOSupported) check_ipo_supported(RESULT result OUTPUT output) if(result) message(STATUS "LTO/IPO is supported and enabled.") else() message(WARNING "LTO/IPO is not supported: ${output}") set(ZXC_ENABLE_LTO OFF) endif() elseif(ZXC_ENABLE_COVERAGE) message(STATUS "Code coverage enabled: Disabling LTO and PGO.") set(ZXC_ENABLE_LTO OFF) set(ZXC_PGO_MODE "OFF") endif() # ============================================================================= # Rapidhash: system-installed (e.g. vcpkg) or vendored fallback # ============================================================================= find_path(RAPIDHASH_INCLUDE_DIR rapidhash.h) if(NOT RAPIDHASH_INCLUDE_DIR) set(RAPIDHASH_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src/lib/vendors") message(STATUS "Using vendored rapidhash from ${RAPIDHASH_INCLUDE_DIR}") else() message(STATUS "Found system rapidhash in ${RAPIDHASH_INCLUDE_DIR}") endif() # ============================================================================= # Core Library & Runtime Dispatch # ============================================================================= # --- PGO flag selection (Clang vs GCC) --- set(ZXC_PGO_DIR "${CMAKE_BINARY_DIR}/pgo") set(ZXC_PGO_GEN_CFLAGS "") set(ZXC_PGO_GEN_LDFLAGS "") set(ZXC_PGO_USE_CFLAGS "") set(ZXC_PGO_USE_LDFLAGS "") if(NOT MSVC AND NOT ZXC_PGO_MODE STREQUAL "OFF") if(CMAKE_C_COMPILER_ID MATCHES "Clang") # Clang: instrumentation-based PGO set(ZXC_PGO_PROFDATA "${ZXC_PGO_DIR}/default.profdata") set(ZXC_PGO_GEN_CFLAGS -fprofile-instr-generate=${ZXC_PGO_DIR}/default_%m.profraw) set(ZXC_PGO_GEN_LDFLAGS -fprofile-instr-generate) set(ZXC_PGO_USE_CFLAGS -fprofile-instr-use=${ZXC_PGO_PROFDATA}) set(ZXC_PGO_USE_LDFLAGS -fprofile-instr-use=${ZXC_PGO_PROFDATA}) else() # GCC: directory-based PGO set(ZXC_PGO_GEN_CFLAGS -fprofile-generate=${ZXC_PGO_DIR}) set(ZXC_PGO_GEN_LDFLAGS -fprofile-generate=${ZXC_PGO_DIR}) set(ZXC_PGO_USE_CFLAGS -fprofile-use=${ZXC_PGO_DIR} -fprofile-correction) set(ZXC_PGO_USE_LDFLAGS -fprofile-use=${ZXC_PGO_DIR}) endif() endif() # Helper: apply PGO flags to a target macro(zxc_apply_pgo target) if(ZXC_PGO_MODE STREQUAL "GENERATE") target_compile_options(${target} PRIVATE ${ZXC_PGO_GEN_CFLAGS}) target_link_options(${target} PRIVATE ${ZXC_PGO_GEN_LDFLAGS}) elseif(ZXC_PGO_MODE STREQUAL "USE") if(EXISTS "${ZXC_PGO_DIR}") target_compile_options(${target} PRIVATE ${ZXC_PGO_USE_CFLAGS}) target_link_options(${target} PRIVATE ${ZXC_PGO_USE_LDFLAGS}) endif() endif() endmacro() # Function Multi-Versioning Helper # Compiles compress/decompress/huffman with specific flags and suffix so the # runtime dispatcher can route to a BMI2/AVX2/AVX512/NEON-aware Huffman codec # in addition to the LZ77 stages. macro(zxc_add_variant suffix flags) foreach(_src compress decompress huffman) add_library(zxc_${_src}${suffix} OBJECT src/lib/zxc_${_src}.c) target_compile_options(zxc_${_src}${suffix} PRIVATE ${flags}) # MSVC C4244: block-bounded uint64->size_t narrowing, lossless (clean on GCC/Clang). target_compile_options(zxc_${_src}${suffix} PRIVATE $<$:/wd4244>) target_compile_definitions(zxc_${_src}${suffix} PRIVATE ZXC_FUNCTION_SUFFIX=${suffix}) # For static builds, define ZXC_STATIC_DEFINE if(NOT BUILD_SHARED_LIBS) target_compile_definitions(zxc_${_src}${suffix} PRIVATE ZXC_STATIC_DEFINE) else() # Mark as part of the DLL being built (avoids dllimport on internal symbols) target_compile_definitions(zxc_${_src}${suffix} PRIVATE zxc_lib_EXPORTS) set_target_properties(zxc_${_src}${suffix} PROPERTIES POSITION_INDEPENDENT_CODE ON) # Hide variant symbols from shared library public ABI if(NOT MSVC) target_compile_options(zxc_${_src}${suffix} PRIVATE -fvisibility=hidden) endif() endif() # Inherit include directories target_include_directories(zxc_${_src}${suffix} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/lib ${RAPIDHASH_INCLUDE_DIR} PUBLIC $) # Propagate PGO flags to variant objects zxc_apply_pgo(zxc_${_src}${suffix}) list(APPEND ZXC_VARIANT_OBJECTS $) endforeach() endmacro() set(ZXC_VARIANT_OBJECTS "") # --- 1. Default Variant (Scalar/Baseline) --- zxc_add_variant(_default "") # --- 2. Architecture Specific Variants (skipped in no-intrinsics mode) --- if(ZXC_DISABLE_SIMD) message(STATUS "ZXC_DISABLE_SIMD: Skipping SIMD variants (no explicit AVX/NEON code paths).") else() if(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64|x86_64|AMD64") message(STATUS "Building x86_64 AVX2 and AVX512 variants...") if(MSVC) # SSE2 for MSVC: SSE2 is the x86-64 baseline zxc_add_variant(_sse2 "/D__SSE2__") # AVX2 for MSVC (Enables AVX2/BMI1/BMI2 sets) zxc_add_variant(_avx2 "/arch:AVX2;/D__BMI__;/D__BMI2__;/D__LZCNT__") # AVX512 for MSVC (VS2019 16.10+ supports /arch:AVX512) zxc_add_variant(_avx512 "/arch:AVX512;/D__BMI__;/D__BMI2__;/D__LZCNT__") else() # SSE2 for GCC/Clang (x86-64 baseline) zxc_add_variant(_sse2 "-msse2") # AVX2 for GCC/Clang zxc_add_variant(_avx2 "-mavx2;-mfma;-mbmi;-mbmi2;-mlzcnt") # AVX512 for GCC/Clang zxc_add_variant(_avx512 "-mavx512f;-mavx512bw;-mavx512vbmi;-mavx512vbmi2;-mbmi;-mbmi2;-mlzcnt") endif() elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64|ARM64") message(STATUS "Building AArch64 NEON variant...") if(MSVC) # MSVC for ARM64 implies NEON support by default. zxc_add_variant(_neon "") else() # NEON is usually default on AArch64, but we add a specific variant for structure zxc_add_variant(_neon "-march=armv8-a+simd") endif() elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm") message(STATUS "Building ARM NEON variant...") zxc_add_variant(_neon "-march=armv7-a;-mfpu=neon") endif() endif() # Sources: exclude zxc_driver.c for Emscripten (requires pthreads + FILE* I/O). # zxc_huffman.c lives in the per-variant build (see zxc_add_variant). if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten") set(ZXC_CORE_SOURCES src/lib/zxc_common.c src/lib/zxc_pivco_tables.c src/lib/zxc_dict.c src/lib/zxc_dispatch.c src/lib/zxc_pstream.c src/lib/zxc_seekable.c ) else() set(ZXC_CORE_SOURCES src/lib/zxc_common.c src/lib/zxc_pivco_tables.c src/lib/zxc_dict.c src/lib/zxc_driver.c src/lib/zxc_dispatch.c src/lib/zxc_pstream.c src/lib/zxc_seekable.c ) endif() add_library(zxc_lib ${ZXC_CORE_SOURCES} ${ZXC_VARIANT_OBJECTS} ) # ============================================================================= # ABI Versioning (Debian/Linux shared libraries) # ============================================================================= # Increment this number ONLY when breaking the ABI. set(ZXC_SOVERSION 4) # Set library output name and version set_target_properties(zxc_lib PROPERTIES OUTPUT_NAME zxc VERSION ${PROJECT_VERSION} SOVERSION ${ZXC_SOVERSION} ) # Target-based include directories for the main lib target_include_directories(zxc_lib PUBLIC $ $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/lib ${RAPIDHASH_INCLUDE_DIR} ) # Symbol visibility for shared libraries if(BUILD_SHARED_LIBS) # Set visibility for GCC/Clang if(NOT MSVC) target_compile_options(zxc_lib PRIVATE -fvisibility=hidden) set_target_properties(zxc_lib PROPERTIES C_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN YES ) endif() else() # For static libraries, define ZXC_STATIC_DEFINE to avoid dllimport/dllexport target_compile_definitions(zxc_lib PUBLIC ZXC_STATIC_DEFINE) endif() # ============================================================================= # Compiler-specific options (using generator expressions) # ============================================================================= if(NOT MSVC) target_compile_options(zxc_lib PRIVATE $<$>:-O3> -Wall -Wextra -fomit-frame-pointer -fstrict-aliasing # Native Arch $<$:-march=native> # Dead code elimination -ffunction-sections -fdata-sections ) # Profile-Guided Optimization zxc_apply_pgo(zxc_lib) if(ZXC_PGO_MODE STREQUAL "GENERATE") message(STATUS "PGO: Generating profile data to ${ZXC_PGO_DIR}") elseif(ZXC_PGO_MODE STREQUAL "USE") if(NOT EXISTS "${ZXC_PGO_DIR}") message(FATAL_ERROR "PGO: Profile data not found at ${ZXC_PGO_DIR}. Run with ZXC_PGO_MODE=GENERATE first.") endif() message(STATUS "PGO: Using profile data from ${ZXC_PGO_DIR}") endif() # Linker options for Dead Code Stripping if(APPLE) target_link_options(zxc_lib PRIVATE -Wl,-dead_strip) else() target_link_options(zxc_lib PRIVATE -Wl,--gc-sections) endif() else() # /wd4244: same block-bounded uint64->size_t narrowing as the variants. target_compile_options(zxc_lib PRIVATE $<$:/O2> /W3 /wd4244) target_compile_definitions(zxc_lib PRIVATE _CRT_SECURE_NO_WARNINGS) endif() target_compile_definitions(zxc_lib PRIVATE $<$>:_GNU_SOURCE> ) # Coverage flags if(ZXC_ENABLE_COVERAGE) if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") target_compile_options(zxc_lib PRIVATE --coverage -fprofile-update=atomic) target_link_options(zxc_lib PRIVATE --coverage) endif() endif() # Enable LTO cleanly if(ZXC_ENABLE_LTO) set_property(TARGET zxc_lib PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) if(NOT MSVC) target_compile_options(zxc_lib PRIVATE -flto) target_link_options(zxc_lib PRIVATE -flto) endif() endif() # Threading support (not available in WASM single-threaded builds) if(NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten") find_package(Threads REQUIRED) target_link_libraries(zxc_lib PRIVATE Threads::Threads) endif() # ============================================================================= # CLI Executable # ============================================================================= if(ZXC_BUILD_CLI) add_executable(zxc src/cli/main.c) target_link_libraries(zxc PRIVATE zxc_lib) target_include_directories(zxc PRIVATE ${RAPIDHASH_INCLUDE_DIR}) # Math library on Unix if(UNIX) target_link_libraries(zxc PRIVATE m) endif() # Native command-line wildcard expansion on Windows: cmd/PowerShell don't glob if(MSVC) target_link_options(zxc PRIVATE setargv.obj) endif() # Propagate compile options and definitions target_compile_options(zxc PRIVATE $<$>,$>:-march=native> ) target_compile_definitions(zxc PRIVATE $<$:_CRT_SECURE_NO_WARNINGS> $<$>:_GNU_SOURCE> ) # Coverage flags for CLI if(ZXC_ENABLE_COVERAGE) if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") target_compile_options(zxc PRIVATE --coverage) target_link_options(zxc PRIVATE --coverage) endif() endif() # Enable LTO cleanly if(ZXC_ENABLE_LTO) set_property(TARGET zxc PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) if(NOT MSVC) target_compile_options(zxc PRIVATE -flto) target_link_options(zxc PRIVATE -flto) endif() endif() # Profile-Guided Optimization for CLI zxc_apply_pgo(zxc) # Linker options for Dead Code Stripping if(NOT MSVC) if(APPLE) target_link_options(zxc PRIVATE -Wl,-dead_strip) else() target_link_options(zxc PRIVATE -Wl,--gc-sections) endif() endif() # Strip symbols in Release mode for smaller binary if(NOT MSVC AND CMAKE_BUILD_TYPE STREQUAL "Release") # Set default strip command if not already set (e.g., for cross-compilation) if(NOT CMAKE_STRIP) set(CMAKE_STRIP strip) endif() add_custom_command(TARGET zxc POST_BUILD COMMAND ${CMAKE_STRIP} $ COMMENT "Stripping symbols from zxc" ) endif() endif() # ============================================================================= # Tests # ============================================================================= if(ZXC_BUILD_TESTS) enable_testing() add_executable(zxc_test tests/test_main.c tests/test_common.c tests/test_buffer_api.c tests/test_block_api.c tests/test_context_api.c tests/test_static_ctx.c tests/test_pstream_api.c tests/test_stream_api.c tests/test_seekable.c tests/test_seekable_mt.c tests/test_format.c tests/test_misc.c tests/test_dict.c ) # When building shared libraries, create a static version for tests # This allows tests to access internal functions for unit testing if(BUILD_SHARED_LIBS) # Create a static library specifically for tests. # zxc_huffman.c lives in the per-variant build (see zxc_add_variant) # and is already pulled in via ${ZXC_VARIANT_OBJECTS} below. add_library(zxc_lib_static STATIC src/lib/zxc_common.c src/lib/zxc_pivco_tables.c src/lib/zxc_dict.c src/lib/zxc_driver.c src/lib/zxc_dispatch.c src/lib/zxc_pstream.c src/lib/zxc_seekable.c ${ZXC_VARIANT_OBJECTS} ) # Copy all properties from the shared library target_include_directories(zxc_lib_static PUBLIC $ $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/lib ${RAPIDHASH_INCLUDE_DIR} ) # Apply same compiler settings as main library target_compile_options(zxc_lib_static PRIVATE $ ) target_compile_definitions(zxc_lib_static PUBLIC ZXC_STATIC_DEFINE) target_link_libraries(zxc_lib_static PRIVATE Threads::Threads) # Link tests against static library target_link_libraries(zxc_test PRIVATE zxc_lib_static) else() # For static builds, use the main library target_link_libraries(zxc_test PRIVATE zxc_lib) endif() # Propagate compile options target_compile_options(zxc_test PRIVATE $<$>,$>:-march=native> ) # Propagate definitions target_compile_definitions(zxc_test PRIVATE $<$:_CRT_SECURE_NO_WARNINGS>) # Coverage flags for Tests if(ZXC_ENABLE_COVERAGE) if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") target_link_options(zxc_test PRIVATE --coverage) endif() endif() # Profile-Guided Optimization for tests zxc_apply_pgo(zxc_test) target_include_directories(zxc_test PRIVATE src/lib ${RAPIDHASH_INCLUDE_DIR}) file(STRINGS tests/test_main.c ZXC_TEST_CASE_LINES REGEX "TEST_CASE\\(") set(ZXC_TEST_NAMES "") foreach(_line IN LISTS ZXC_TEST_CASE_LINES) if(_line MATCHES "TEST_CASE\\(([A-Za-z0-9_]+)\\)") list(APPEND ZXC_TEST_NAMES ${CMAKE_MATCH_1}) endif() endforeach() foreach(_name IN LISTS ZXC_TEST_NAMES) add_test(NAME ${_name} COMMAND zxc_test --exact ${_name}) endforeach() # --- Conformance suite --------------------------------------------------- add_executable(zxc_conformance_test conformance/test_conformance.c) target_link_libraries(zxc_conformance_test PRIVATE zxc_lib) target_include_directories(zxc_conformance_test PRIVATE ${CMAKE_SOURCE_DIR}/include) target_compile_definitions(zxc_conformance_test PRIVATE $<$:_CRT_SECURE_NO_WARNINGS>) if(ZXC_ENABLE_COVERAGE) target_link_options(zxc_conformance_test PRIVATE --coverage) endif() add_test( NAME conformance COMMAND zxc_conformance_test "${CMAKE_SOURCE_DIR}/conformance" ) # --- Golden-file format conformance -------------------------------------- # Parses the byte-frozen golden files and validates every on-disk field # against docs/FORMAT.md. Needs the private header (static-inline hashes), # hence the src/lib + rapidhash include paths. add_executable(zxc_format_golden_test tests/format/test_golden.c) target_link_libraries(zxc_format_golden_test PRIVATE zxc_lib) target_include_directories(zxc_format_golden_test PRIVATE ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/src/lib ${RAPIDHASH_INCLUDE_DIR}) target_compile_definitions(zxc_format_golden_test PRIVATE $<$:_CRT_SECURE_NO_WARNINGS>) if(ZXC_ENABLE_COVERAGE) target_link_options(zxc_format_golden_test PRIVATE --coverage) endif() add_test( NAME format_golden COMMAND zxc_format_golden_test "${CMAKE_SOURCE_DIR}/tests/format/golden" ) # Maintainer-only regeneration tool (public API only; not a registered test). add_executable(zxc_golden_gen tests/format/gen_golden.c) target_link_libraries(zxc_golden_gen PRIVATE zxc_lib) target_include_directories(zxc_golden_gen PRIVATE ${CMAKE_SOURCE_DIR}/include) target_compile_definitions(zxc_golden_gen PRIVATE $<$:_CRT_SECURE_NO_WARNINGS>) if(ZXC_ENABLE_COVERAGE) # Links the coverage-instrumented zxc_lib, so it needs the gcov runtime. target_link_options(zxc_golden_gen PRIVATE --coverage) endif() endif() # ============================================================================= # Installation # ============================================================================= include(GNUInstallDirs) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/libzxc.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libzxc.pc @ONLY ) install(TARGETS zxc_lib EXPORT zxc-targets ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "*.h" ) if(ZXC_BUILD_CLI) install(TARGETS zxc RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) # "unzxc" alias: a symlink to zxc that defaults to decompression. # Symbolic links are POSIX-only; skipped on Windows. if(NOT WIN32) install(CODE " execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink zxc \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_BINDIR}/unzxc\") ") endif() endif() install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libzxc.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig ) # CMake package config files for find_package(zxc) include(CMakePackageConfigHelpers) install(EXPORT zxc-targets FILE zxc-targets.cmake NAMESPACE zxc:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/zxc ) configure_package_config_file( ${CMAKE_CURRENT_SOURCE_DIR}/zxcConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/zxcConfig.cmake INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/zxc ) write_basic_package_version_file( ${CMAKE_CURRENT_BINARY_DIR}/zxcConfigVersion.cmake VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/zxcConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/zxcConfigVersion.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/zxc ) # ============================================================================= # Code Formatting (clang-format) # ============================================================================= # Allow override via environment variable (e.g., CLANG_FORMAT=clang-format-22) if(DEFINED ENV{CLANG_FORMAT}) set(CLANG_FORMAT "$ENV{CLANG_FORMAT}") else() find_program(CLANG_FORMAT clang-format) endif() if(CLANG_FORMAT) file(GLOB_RECURSE ZXC_FORMAT_SOURCES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/include/*.h" "${CMAKE_CURRENT_SOURCE_DIR}/src/lib/*.c" "${CMAKE_CURRENT_SOURCE_DIR}/src/lib/*.h" "${CMAKE_CURRENT_SOURCE_DIR}/src/cli/*.c" "${CMAKE_CURRENT_SOURCE_DIR}/src/cli/*.h" ) # Exclude vendored third-party code list(FILTER ZXC_FORMAT_SOURCES EXCLUDE REGEX ".*/vendors/.*") add_custom_target(format COMMAND ${CLANG_FORMAT} --style=file -i ${ZXC_FORMAT_SOURCES} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Formatting include/, src/lib/ and src/cli/ with clang-format" VERBATIM ) add_custom_target(format-check COMMAND ${CLANG_FORMAT} --style=file --dry-run --Werror ${ZXC_FORMAT_SOURCES} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Checking formatting of include/, src/lib/ and src/cli/" VERBATIM ) else() message(STATUS "clang-format not found - format/format-check targets disabled") endif() # ============================================================================= # Summary # ============================================================================= message(STATUS "") message(STATUS "ZXC Configuration Summary:") message(STATUS " Version: ${PROJECT_VERSION}") if(BUILD_SHARED_LIBS) message(STATUS " Library Type: Shared") else() message(STATUS " Library Type: Static") endif() message(STATUS " Native Arch: ${ZXC_NATIVE_ARCH}") message(STATUS " Disable SIMD: ${ZXC_DISABLE_SIMD}") message(STATUS " LTO Enabled: ${ZXC_ENABLE_LTO}") message(STATUS " PGO Mode: ${ZXC_PGO_MODE}") message(STATUS " Build CLI: ${ZXC_BUILD_CLI}") message(STATUS " Build Tests: ${ZXC_BUILD_TESTS}") message(STATUS "") # ============================================================================= # WebAssembly (Emscripten) Target # ============================================================================= if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten") # Exported C functions (with leading underscore for Emscripten convention) set(ZXC_WASM_EXPORTS "_zxc_compress" "_zxc_decompress" "_zxc_compress_bound" "_zxc_get_decompressed_size" "_zxc_decompress_inplace" "_zxc_decompress_inplace_bound" "_zxc_create_cctx" "_zxc_free_cctx" "_zxc_compress_cctx" "_zxc_create_dctx" "_zxc_free_dctx" "_zxc_decompress_dctx" # Push streaming API "_zxc_cstream_create" "_zxc_cstream_free" "_zxc_cstream_compress" "_zxc_cstream_end" "_zxc_cstream_in_size" "_zxc_cstream_out_size" "_zxc_dstream_create" "_zxc_dstream_free" "_zxc_dstream_decompress" "_zxc_dstream_finished" "_zxc_dstream_in_size" "_zxc_dstream_out_size" # Seekable API "_zxc_seekable_open" "_zxc_seekable_free" "_zxc_seekable_get_num_blocks" "_zxc_seekable_get_decompressed_size" "_zxc_seekable_get_block_comp_size" "_zxc_seekable_get_block_decomp_size" "_zxc_seekable_set_dict" # Dictionary API "_zxc_train_dict" "_zxc_train_dict_huf" "_zxc_dict_train" "_zxc_dict_id" "_zxc_get_dict_id" "_zxc_dict_get_id" "_zxc_dict_save" "_zxc_dict_save_bound" "_zxc_dict_load" "_zxc_dict_huf" # i32-offset shim for seekable_decompress_range (see wasm_entry.c); # avoids the i64 offset arg which cwrap cannot pass without BigInt. "_zxcw_seekable_decompress_range" "_zxc_write_seek_table" "_zxc_seek_table_size" "_zxc_min_level" "_zxc_max_level" "_zxc_default_level" "_zxc_version_string" "_zxc_error_name" # Options-struct layout guards "_zxc_compress_opts_size" "_zxc_decompress_opts_size" "_malloc" "_free" ) # Join list with commas for Emscripten linker flag string(JOIN "," ZXC_WASM_EXPORTS_STR ${ZXC_WASM_EXPORTS}) add_executable(zxc_wasm wrappers/wasm/wasm_entry.c) target_link_libraries(zxc_wasm PRIVATE zxc_lib) set_target_properties(zxc_wasm PROPERTIES OUTPUT_NAME "zxc" SUFFIX ".js" ) target_link_options(zxc_wasm PRIVATE "-sEXPORTED_FUNCTIONS=[${ZXC_WASM_EXPORTS_STR}]" "-sEXPORTED_RUNTIME_METHODS=[ccall,cwrap,UTF8ToString,getTempRet0,HEAPU8,HEAP32,HEAPU32]" "-sMODULARIZE=1" "-sEXPORT_ES6=1" "-sEXPORT_NAME=ZXCModule" "-sALLOW_MEMORY_GROWTH=1" "-sINITIAL_MEMORY=2097152" "-sSTACK_SIZE=131072" "-sENVIRONMENT=web,node" "-sNO_FILESYSTEM=1" "-sSTRICT=1" "-sWASM_BIGINT=0" ) target_compile_options(zxc_wasm PRIVATE -O3) message(STATUS " WASM Target: zxc.js + zxc.wasm") endif() # ============================================================================= # Documentation (Doxygen) # ============================================================================= find_package(Doxygen) if(DOXYGEN_FOUND) # Generate the Doxyfile with the current project version configure_file(${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) # Add a 'doc' target to generate documentation (e.g. 'make doc' or 'cmake --build . --target doc') add_custom_target(doc COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Generating API documentation with Doxygen" VERBATIM ) endif() zxc-0.13.0/LICENSE000066400000000000000000000063351522536370400134220ustar00rootroot00000000000000============================================================================== ZXC Copyright (c) 2025-2026, Bertrand Lebonnois and contributors License: BSD 3-Clause ============================================================================== BSD 3-Clause License ==================== Copyright (c) 2025-2026, Bertrand Lebonnois and contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of ZXC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BERTRAND LEBONNOIS OR CONTRIBUTORS BE LIABLE FOR DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ============================================================================== This project includes code from rapidhash Copyright (C) 2025 Nicolas De Carli License: MIT (Expat) ============================================================================== rapidhash - Very fast, high quality, platform-independent hashing algorithm. Copyright (C) 2025 Nicolas De Carli 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. You can contact the author at: - rapidhash source repository: https://github.com/Nicoshev/rapidhash zxc-0.13.0/Makefile000066400000000000000000000071501522536370400140510ustar00rootroot00000000000000# ZXC - High-performance lossless compression # # Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. # SPDX-License-Identifier: BSD-3-Clause # Top-level convenience Makefile (wraps CMake) # # Usage: # make Build the library + CLI (Release) # make test Build and run tests (parallel) # make conformance Build and run only the decoder conformance suite # make format Format source code with clang-format # make format-check Check formatting (CI mode) # make lint Scan source files for non-ASCII characters (CI mirror) # make doc Generate Doxygen documentation # make clean Remove build directory # # Override build directory: make BUILD=mybuild # Pass extra CMake flags: make CMAKE_EXTRA="-DZXC_NATIVE_ARCH=OFF" BUILD ?= build CMAKE ?= cmake CMAKE_EXTRA ?= JOBS ?= $(shell nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4) .PHONY: all test conformance format format-check lint doc clean # ── Build ──────────────────────────────────────────────────── all: @$(CMAKE) -S . -B $(BUILD) -DCMAKE_BUILD_TYPE=Release $(CMAKE_EXTRA) @$(CMAKE) --build $(BUILD) -j$(JOBS) # ── Test ───────────────────────────────────────────────────── test: @$(CMAKE) -S . -B $(BUILD) -DCMAKE_BUILD_TYPE=Release -DZXC_BUILD_TESTS=ON $(CMAKE_EXTRA) @$(CMAKE) --build $(BUILD) -j$(JOBS) @cd $(BUILD) && ctest --output-on-failure -j$(JOBS) # ── Conformance ────────────────────────────────────────────── # Runs only the decoder conformance suite: the reference test # vectors under conformance/ (valid/ must decode byte-for-byte, # invalid/ must be rejected). Same test as `make test`, filtered. conformance: @$(CMAKE) -S . -B $(BUILD) -DCMAKE_BUILD_TYPE=Release -DZXC_BUILD_TESTS=ON $(CMAKE_EXTRA) @$(CMAKE) --build $(BUILD) -j$(JOBS) --target zxc_conformance_test @cd $(BUILD) && ctest --output-on-failure -R '^conformance$$' # ── Formatting ─────────────────────────────────────────────── format: @$(CMAKE) -S . -B $(BUILD) @$(CMAKE) --build $(BUILD) --target format format-check: @$(CMAKE) -S . -B $(BUILD) @$(CMAKE) --build $(BUILD) --target format-check # ── Lint (mirrors .github/workflows/quality.yml) ───────────── # Scans .c and .h files under src/, include/, tests/ for non-ASCII bytes. # Uses Perl for portability. lint: @echo "Scanning for non-ASCII characters in .c and .h files..." @files=$$(find src include tests -type f \( -name '*.c' -o -name '*.h' \) 2>/dev/null); \ if [ -z "$$files" ]; then echo "No source files found."; exit 0; fi; \ LC_ALL=C perl -ne \ 'if (/[^[:ascii:]]/) { print "$$ARGV:$$.:$$_"; $$bad=1 } \ END { exit($$bad ? 1 : 0) }' \ $$files \ && echo "OK: No non-ASCII characters found." \ || { echo "ERROR: Non-ASCII characters found in source files."; exit 1; } # ── Documentation ──────────────────────────────────────────── doc: @$(CMAKE) -S . -B $(BUILD) @$(CMAKE) --build $(BUILD) --target doc # ── Clean ──────────────────────────────────────────────────── clean: @rm -rf $(BUILD) zxc-0.13.0/README.md000066400000000000000000001202701522536370400136670ustar00rootroot00000000000000# ZXC - Asymmetric Lossless Compression Built for Ultra-Fast Decode [![Build & Release](https://github.com/hellobertrand/zxc/actions/workflows/build.yml/badge.svg)](https://github.com/hellobertrand/zxc/actions/workflows/build.yml) [![Code Quality](https://github.com/hellobertrand/zxc/actions/workflows/quality.yml/badge.svg)](https://github.com/hellobertrand/zxc/actions/workflows/quality.yml) [![Code Security](https://github.com/hellobertrand/zxc/actions/workflows/security.yml/badge.svg)](https://github.com/hellobertrand/zxc/actions/workflows/security.yml) [![Fuzzing](https://github.com/hellobertrand/zxc/actions/workflows/fuzzing.yml/badge.svg)](https://github.com/hellobertrand/zxc/actions/workflows/fuzzing.yml) [![Security](https://sonarcloud.io/api/project_badges/measure?project=hellobertrand_zxc&metric=security_rating)](https://sonarcloud.io/summary/overall?id=hellobertrand_zxc) [![Code Coverage](https://codecov.io/github/hellobertrand/zxc/branch/main/graph/badge.svg?token=LHA03HOA1X)](https://codecov.io/github/hellobertrand/zxc) [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/hellobertrand/zxc/badge)](https://scorecard.dev/viewer/?uri=github.com/hellobertrand/zxc) [![License](https://img.shields.io/badge/license-BSD--3--Clause-blue)](LICENSE) ZXC is a lossless compression **C library** (with official Rust, Python, Node.js, and Go bindings). It trades compression speed for maximum decode throughput — the appropriate trade-off whenever data is **compressed once and read many times**: content delivery, embedded systems, FOTA (Firmware Over-The-Air) updates, game assets, and app bundles. It runs on all major architectures (`x86_64`, `ARM64`, `ARMv7`, `ARMv6`, `RISC-V`, `POWER`, `s390x`, `i386`) with hand-tuned SIMD paths, and shows particularly strong gains on modern ARM cores (Apple Silicon, AWS Graviton, Google Axion) thanks to a bitstream layout tuned for their deep pipelines. ## TL;DR - **Faster decode than LZ4, at a smaller size.** 9–48% faster decode at the default level (best on ARM64), rising to up to 2.3× in the speed-optimized tier, always at an equal-or-better compression ratio. See the [benchmarks](#benchmarks). - **Independently verified.** Merged into [lzbench](https://github.com/inikep/lzbench) (@inikep) and [TurboBench](https://github.com/powturbo/TurboBench) (@powturbo); every benchmark below is reproducible against 70+ codecs. - **Cross-platform.** x86_64, ARM64, ARMv7, ARMv6, RISC-V, POWER (ppc64el), s390x, i386, with hand-tuned SIMD (SSE2/AVX2/AVX-512 on x86, NEON on ARMv8+). - **Built for "Write Once, Read Many."** Compress once at build time, decompress millions of times at run time. - **Production-grade.** 5B+ fuzzing iterations, ASan/UBSan/Valgrind-clean, SLSA-signed releases, thread-safe API, BSD-3-Clause. - **Seekable.** Built-in seek table for O(1) random-access decompression. - **Broadly packaged.** Conan, vcpkg, Homebrew, Winget and Rust/Python/Node packages. ## Quick start ```bash # Install (pick your package manager) brew install zxc conan install --requires="zxc/[*]" # or: vcpkg install zxc # Compress once, decompress fast zxc -5 assets.tar assets.tar.zxc zxc -d assets.tar.zxc assets.tar ``` > **Independently verified:** ZXC is merged into both major open-source compression benchmark suites — [lzbench](https://github.com/inikep/lzbench) (master, by @inikep) and [TurboBench](https://github.com/powturbo/TurboBench) (master, by @powturbo). Every number in this README is reproducible with either tool, alongside 70+ other codecs. ## Design Philosophy: Asymmetric Efficiency Traditional codecs force a trade-off between **symmetric speed** (LZ4) and **archival density** (Zstd). **ZXC takes a third path: asymmetric efficiency.** The encoder does the heavy lifting upfront — match selection, optimal parsing, statistics tuning — to emit a bitstream structured for the instruction pipelining and branch prediction of modern CPUs (particularly ARMv8). Complexity is **offloaded from the decoder to the encoder**, which is exactly the trade-off WORM workloads want. * **Build time:** you compress only once (on CI/CD). * **Run time:** you decompress millions of times (on every user's device). **ZXC respects this asymmetry.** [👉 **Read the Technical Whitepaper**](docs/WHITEPAPER.md) ## Benchmarks To ensure consistent performance, benchmarks are automatically executed on every commit via GitHub Actions. We monitor metrics on both **x86_64** (Linux) and **ARM64** (Apple Silicon M2) runners to track compression speed, decompression speed, and ratios. *(See the [latest benchmark logs](https://github.com/hellobertrand/zxc/actions/workflows/benchmark.yml))* *Decompression Speed vs Compressed Size — ARM64 Apple M2* ![Decompression Speed vs Compressed Size](docs/images/bench-arm64.svg) ### 1. Mobile & Client: Apple Silicon (M2) *Scenario: Game Assets loading, App startup.* | Target | ZXC vs Competitor | Decompression Speed | Ratio | Verdict | | :--- | :--- | :--- | :--- | :--- | | **1. Max Speed** | **ZXC -1** vs *LZ4 --fast* | **12,880 MB/s** vs 5,624 MB/s **2.29x Faster** | **61.5** vs 62.2 **Smaller** (-0.7%) | **ZXC** leads in raw throughput. | | **2. Standard** | **ZXC -3** vs *LZ4 Default* | **7,063 MB/s** vs 4,784 MB/s **1.48x Faster** | **45.8** vs 47.6 **Smaller** (-1.8%) | **ZXC** outperforms LZ4 in read speed and ratio. | | **3. Density** | **ZXC -6** vs *LZ4HC -9* | **5,980 MB/s** vs 4,528 MB/s **1.32x Faster** | **36.3** vs 36.8 **Smaller** (-0.5%) | **ZXC** beats LZ4HC on both decode speed and ratio. | | **4. Ultra** | **ZXC -7** vs *zstd -1* | **3,790 MB/s** vs 1,809 MB/s **2.10x Faster** | **33.0** vs 34.5 **Smaller** (-1.5%) | **ZXC -7** bridges the gap between LZ4HC and `zstd -1` — smaller output, ~2.1x faster decode. | ### 2. Cloud Server: Google Axion (ARM Neoverse V2) *Scenario: High-throughput Microservices, ARM Cloud Instances.* | Target | ZXC vs Competitor | Decompression Speed | Ratio | Verdict | | :--- | :--- | :--- | :--- | :--- | | **1. Max Speed** | **ZXC -1** vs *LZ4 --fast* | **9,383 MB/s** vs 4,947 MB/s **1.90x Faster** | **61.5** vs 62.2 **Smaller** (-0.7%) | **ZXC** leads in raw throughput. | | **2. Standard** | **ZXC -3** vs *LZ4 Default* | **5,377 MB/s** vs 4,263 MB/s **1.26x Faster** | **45.8** vs 47.6 **Smaller** (-1.8%) | **ZXC** outperforms LZ4 in read speed and ratio. | | **3. Density** | **ZXC -6** vs *LZ4HC -9* | **4,450 MB/s** vs 3,855 MB/s **1.15x Faster** | **36.3** vs 36.8 **Smaller** (-0.5%) | **ZXC** beats LZ4HC on both decode speed and ratio. | | **4. Ultra** | **ZXC -7** vs *zstd -1* | **2,843 MB/s** vs 1,644 MB/s **1.73x Faster** | **33.0** vs 34.5 **Smaller** (-1.5%) | **ZXC -7** bridges the gap between LZ4HC and `zstd -1` — smaller output, ~1.7x faster decode. | ### 3. Build Server: x86_64 (AMD EPYC 9B45 / Zen 5) *Scenario: CI/CD Pipelines compatibility.* | Target | ZXC vs Competitor | Decompression Speed | Ratio | Verdict | | :--- | :--- | :--- | :--- | :--- | | **1. Max Speed** | **ZXC -1** vs *LZ4 --fast* | **11,032 MB/s** vs 5,293 MB/s **2.08x Faster** | **61.5** vs 62.2 **Smaller** (-0.7%) | **ZXC** achieves higher throughput. | | **2. Standard** | **ZXC -3** vs *LZ4 Default* | **6,096 MB/s** vs 5,029 MB/s **1.21x Faster** | **45.8** vs 47.6 **Smaller** (-1.8%) | **ZXC** offers improved speed and ratio. | | **3. Density** | **ZXC -6** vs *LZ4HC -9* | **5,389 MB/s** vs 4,844 MB/s **1.11x Faster** | **36.3** vs 36.8 **Smaller** (-0.5%) | **ZXC** now beats LZ4HC on both decode speed and ratio. | | **4. Ultra** | **ZXC -7** vs *zstd -1* | **3,742 MB/s** vs 1,888 MB/s **1.98x Faster** | **33.0** vs 34.5 **Smaller** (-1.5%) | **ZXC -7** bridges the gap between LZ4HC and `zstd -1` — smaller output, ~2x faster decode. | ### 4. Production Server: x86_64 (AMD EPYC 7B13 / Zen 3) *Scenario: Mainstream cloud workloads (AWS c6a, Azure HBv3, GCP n2d).* | Target | ZXC vs Competitor | Decompression Speed | Ratio | Verdict | | :--- | :--- | :--- | :--- | :--- | | **1. Max Speed** | **ZXC -1** vs *LZ4 --fast* | **7,726 MB/s** vs 4,494 MB/s **1.72x Faster** | **61.5** vs 62.2 **Smaller** (-0.7%) | **ZXC** holds a strong lead on the legacy x86 pipeline. | | **2. Standard** | **ZXC -3** vs *LZ4 Default* | **4,250 MB/s** vs 3,891 MB/s **1.09x Faster** | **45.8** vs 47.6 **Smaller** (-1.8%) | **ZXC** delivers faster decode and smaller output. | | **3. Density** | **ZXC -6** vs *LZ4HC -9* | 3,695 MB/s vs **3,727 MB/s** (decode within 1%) | **36.3** vs 36.8 **Smaller** (-0.5%) | **ZXC** wins on ratio; decode trails `LZ4HC -9` by <1% on Zen 3. | | **4. Ultra** | **ZXC -7** vs *zstd -1* | **2,474 MB/s** vs 1,339 MB/s **1.85x Faster** | **33.0** vs 34.5 **Smaller** (-1.5%) | **ZXC -7** bridges the gap between LZ4HC and `zstd -1` — smaller output, ~1.8x faster decode. | *Decompression Speed: ZXC vs LZ4 family at equivalent ratio tiers, across 4 CPUs (Fast ≈ 62%, Default ≈ 47%, High ≈ 37%)* ![Decompression Speed: ZXC vs LZ4 family at equivalent ratio tiers](docs/images/bench-bars.svg) *Effective Throughput : Ratio-Normalized Decode across ARM64 and x86 (decode x 100 / ratio%, LZ4 baseline = 1.00x)* ![Effective Throughput](docs/images/bench-effective.svg) > **What is Effective Throughput?** > > Raw decode speed misses half the picture: in real workloads (asset streaming, container pulls, microservice payloads), the decoder is fed by a compressed-byte source - disk, network, inter-core - whose bandwidth is the bottleneck. The right question is *how much original data is delivered per MB of compressed input*. > > Formula: `Effective (MB/s) = Decode × 100 / Ratio (%)`: combines decode speed and ratio in one number. **Every ZXC level from -1 to -6 sits above LZ4** on every architecture, peaking at **2.0x on Apple Silicon** and ranging **1.14x–1.74x** on x86 and ARM cloud platforms. The density-optimized ULTRA level -7 trades decode throughput for ratio. ### Benchmark ARM64 (Apple Silicon M2) Benchmarks were conducted using lzbench 2.3 (from @inikep), compiled with Clang 21.0.0 using *MOREFLAGS="-march=native"* on macOS Tahoe 26 (`macos-26-xlarge`). The reference hardware is an Apple M2 processor (ARM64). All performance metrics reflect single-threaded execution on the standard Silesia Corpus and the benchmark made use of [silesia.tar](https://github.com/DataCompression/corpus-collection/tree/main/Silesia-Corpus), which contains tarred files from the Silesia compression corpus. | Compressor name | Compression| Decompress.| Compr. size | Ratio | Filename | | --------------- | -----------| -----------| ----------- | ----- | -------- | | memcpy | 52855 MB/s | 52878 MB/s | 211947520 |100.00 | 1 files| | **zxc 0.13.0 -1** | 888 MB/s | **12880 MB/s** | 130356147 | **61.50** | 1 files| | **zxc 0.13.0 -2** | 594 MB/s | **10683 MB/s** | 113633866 | **53.61** | 1 files| | **zxc 0.13.0 -3** | 251 MB/s | **7063 MB/s** | 97051444 | **45.79** | 1 files| | **zxc 0.13.0 -4** | 173 MB/s | **6696 MB/s** | 90392857 | **42.65** | 1 files| | **zxc 0.13.0 -5** | 103 MB/s | **6295 MB/s** | 85341256 | **40.27** | 1 files| | **zxc 0.13.0 -6** | 12.4 MB/s | **5980 MB/s** | 76877873 | **36.27** | 1 files| | **zxc 0.13.0 -7** | 8.74 MB/s | **3790 MB/s** | 69950212 | **33.00** | 1 files| | lz4 1.10.0 | 814 MB/s | 4784 MB/s | 100880800 | 47.60 | 1 files| | lz4 1.10.0 --fast -17 | 1354 MB/s | 5624 MB/s | 131732802 | 62.15 | 1 files| | lz4hc 1.10.0 -9 | 48.0 MB/s | 4528 MB/s | 77884448 | 36.75 | 1 files| | lzav 5.9 -1 | 674 MB/s | 3901 MB/s | 84644732 | 39.94 | 1 files| | snappy 1.2.2 | 883 MB/s | 3265 MB/s | 101415443 | 47.85 | 1 files| | zstd 1.5.7 --fast --1 | 725 MB/s | 2539 MB/s | 86916294 | 41.01 | 1 files| | zstd 1.5.7 -1 | 646 MB/s | 1809 MB/s | 73193704 | 34.53 | 1 files| | zstd 1.5.7 -3 | 394 MB/s | 1704 MB/s | 66133500 | 31.20 | 1 files| | zlib 1.3.2 -1 | 150 MB/s | 412 MB/s | 77259029 | 36.45 | 1 files| ### Benchmark ARM64 (Google Axion Neoverse-V2) Benchmarks were conducted using lzbench 2.3 (from @inikep), compiled with GCC 14.3.0 using *MOREFLAGS="-march=native"* on 64-bit Linux. The reference hardware is a Google Axion (Neoverse-V2) processor on a **Google Cloud C4A** instance (ARM64, 1 thread per core). All performance metrics reflect single-threaded execution on the standard Silesia Corpus and the benchmark made use of [silesia.tar](https://github.com/DataCompression/corpus-collection/tree/main/Silesia-Corpus), which contains tarred files from the Silesia compression corpus. | Compressor name | Compression| Decompress.| Compr. size | Ratio | Filename | | --------------- | -----------| -----------| ----------- | ----- | -------- | | memcpy | 24338 MB/s | 24266 MB/s | 211947520 |100.00 | 1 files| | **zxc 0.13.0 -1** | 868 MB/s | **9383 MB/s** | 130356147 | **61.50** | 1 files| | **zxc 0.13.0 -2** | 588 MB/s | **7816 MB/s** | 113633866 | **53.61** | 1 files| | **zxc 0.13.0 -3** | 241 MB/s | **5377 MB/s** | 97051444 | **45.79** | 1 files| | **zxc 0.13.0 -4** | 167 MB/s | **5115 MB/s** | 90392857 | **42.65** | 1 files| | **zxc 0.13.0 -5** | 98.5 MB/s | **4794 MB/s** | 85341256 | **40.27** | 1 files| | **zxc 0.13.0 -6** | 11.7 MB/s | **4450 MB/s** | 76877873 | **36.27** | 1 files| | **zxc 0.13.0 -7** | 7.72 MB/s | **2843 MB/s** | 69950212 | **33.00** | 1 files| | lz4 1.10.0 | 729 MB/s | 4263 MB/s | 100880800 | 47.60 | 1 files| | lz4 1.10.0 --fast -17 | 1276 MB/s | 4947 MB/s | 131732802 | 62.15 | 1 files| | lz4hc 1.10.0 -9 | 24.1 MB/s | 3855 MB/s | 77884448 | 36.75 | 1 files| | lzav 5.9 -1 | 633 MB/s | 2799 MB/s | 84644732 | 39.94 | 1 files| | snappy 1.2.2 | 755 MB/s | 2296 MB/s | 101415443 | 47.85 | 1 files| | zstd 1.5.7 --fast --1 | 605 MB/s | 2278 MB/s | 86916294 | 41.01 | 1 files| | zstd 1.5.7 -1 | 522 MB/s | 1644 MB/s | 73193704 | 34.53 | 1 files| | zstd 1.5.7 -3 | 330 MB/s | 1526 MB/s | 66133500 | 31.20 | 1 files| | zlib 1.3.2 -1 | 115 MB/s | 389 MB/s | 77259029 | 36.45 | 1 files| ### Benchmark x86_64 (AMD EPYC 9B45) Benchmarks were conducted using lzbench 2.3 (from @inikep), compiled with GCC 14.3.0 using *MOREFLAGS="-march=native"* on 64-bit Linux. The reference hardware is an AMD EPYC 9B45 processor on a **Google Cloud C4D** instance (x86_64, SMT disabled — 1 thread per core). All performance metrics reflect single-threaded execution on the standard Silesia Corpus and the benchmark made use of [silesia.tar](https://github.com/DataCompression/corpus-collection/tree/main/Silesia-Corpus), which contains tarred files from the Silesia compression corpus. | Compressor name | Compression| Decompress.| Compr. size | Ratio | Filename | | --------------- | -----------| -----------| ----------- | ----- | -------- | | memcpy | 25324 MB/s | 19789 MB/s | 211947520 |100.00 | 1 files| | **zxc 0.13.0 -1** | 879 MB/s | **11032 MB/s** | 130356147 | **61.50** | 1 files| | **zxc 0.13.0 -2** | 597 MB/s | **9829 MB/s** | 113633866 | **53.61** | 1 files| | **zxc 0.13.0 -3** | 239 MB/s | **6096 MB/s** | 97051444 | **45.79** | 1 files| | **zxc 0.13.0 -4** | 165 MB/s | **5786 MB/s** | 90392857 | **42.65** | 1 files| | **zxc 0.13.0 -5** | 98.9 MB/s | **5468 MB/s** | 85341256 | **40.27** | 1 files| | **zxc 0.13.0 -6** | 12.3 MB/s | **5389 MB/s** | 76877873 | **36.27** | 1 files| | **zxc 0.13.0 -7** | 8.52 MB/s | **3742 MB/s** | 69950212 | **33.00** | 1 files| | lz4 1.10.0 | 772 MB/s | 5029 MB/s | 100880800 | 47.60 | 1 files| | lz4 1.10.0 --fast -17 | 1287 MB/s | 5293 MB/s | 131732802 | 62.15 | 1 files| | lz4hc 1.10.0 -9 | 45.3 MB/s | 4844 MB/s | 77884448 | 36.75 | 1 files| | lzav 5.9 -1 | 652 MB/s | 3495 MB/s | 84644732 | 39.94 | 1 files| | snappy 1.2.2 | 773 MB/s | 2078 MB/s | 101512076 | 47.89 | 1 files| | zstd 1.5.7 --fast --1 | 663 MB/s | 2437 MB/s | 86916294 | 41.01 | 1 files| | zstd 1.5.7 -1 | 600 MB/s | 1888 MB/s | 73193704 | 34.53 | 1 files| | zstd 1.5.7 -3 | 363 MB/s | 1722 MB/s | 66133500 | 31.20 | 1 files| | zlib 1.3.2 -1 | 136 MB/s | 402 MB/s | 77259029 | 36.45 | 1 files| ### Benchmark x86_64 (AMD EPYC 7B13) Benchmarks were conducted using lzbench 2.3 (from @inikep), compiled with GCC 14.3.0 using *MOREFLAGS="-march=native"* on 64-bit Linux. The reference hardware is an AMD EPYC 7B13 64-Core processor on a **Google Cloud C2D** instance (x86_64, SMT disabled — 1 thread per core). All performance metrics reflect single-threaded execution on the standard Silesia Corpus and the benchmark made use of [silesia.tar](https://github.com/DataCompression/corpus-collection/tree/main/Silesia-Corpus), which contains tarred files from the Silesia compression corpus. | Compressor name | Compression| Decompress.| Compr. size | Ratio | Filename | | --------------- | -----------| -----------| ----------- | ----- | -------- | | memcpy | 23953 MB/s | 23834 MB/s | 211947520 |100.00 | 1 files| | **zxc 0.13.0 -1** | 717 MB/s | **7726 MB/s** | 130356147 | **61.50** | 1 files| | **zxc 0.13.0 -2** | 483 MB/s | **6461 MB/s** | 113633866 | **53.61** | 1 files| | **zxc 0.13.0 -3** | 202 MB/s | **4250 MB/s** | 97051444 | **45.79** | 1 files| | **zxc 0.13.0 -4** | 141 MB/s | **4082 MB/s** | 90392857 | **42.65** | 1 files| | **zxc 0.13.0 -5** | 84.0 MB/s | **3938 MB/s** | 85341256 | **40.27** | 1 files| | **zxc 0.13.0 -6** | 10.6 MB/s | **3695 MB/s** | 76877873 | **36.27** | 1 files| | **zxc 0.13.0 -7** | 6.68 MB/s | **2474 MB/s** | 69950212 | **33.00** | 1 files| | lz4 1.10.0 | 641 MB/s | 3891 MB/s | 100880800 | 47.60 | 1 files| | lz4 1.10.0 --fast -17 | 1112 MB/s | 4494 MB/s | 131732802 | 62.15 | 1 files| | lz4hc 1.10.0 -9 | 37.3 MB/s | 3727 MB/s | 77884448 | 36.75 | 1 files| | lzav 5.9 -1 | 481 MB/s | 2939 MB/s | 84644732 | 39.94 | 1 files| | snappy 1.2.2 | 665 MB/s | 1741 MB/s | 101512076 | 47.89 | 1 files| | zstd 1.5.7 --fast --1 | 488 MB/s | 1785 MB/s | 86916294 | 41.01 | 1 files| | zstd 1.5.7 -1 | 445 MB/s | 1339 MB/s | 73193704 | 34.53 | 1 files| | zstd 1.5.7 -3 | 233 MB/s | 1208 MB/s | 66133500 | 31.20 | 1 files| | zlib 1.3.2 -1 | 107 MB/s | 359 MB/s | 77259029 | 36.45 | 1 files| --- ## Installation ZXC is packaged across major ecosystems and kept current by their maintainers: [![ConanCenter](https://repology.org/badge/version-for-repo/conancenter/zxc.svg)](https://repology.org/project/zxc/versions) [![Vcpkg](https://repology.org/badge/version-for-repo/vcpkg/zxc.svg)](https://repology.org/project/zxc/versions) [![Homebrew](https://repology.org/badge/version-for-repo/homebrew/zxc.svg)](https://repology.org/project/zxc/versions) [![Debian 14](https://repology.org/badge/version-for-repo/debian_14/zxc.svg)](https://repology.org/project/zxc/versions) [![Ubuntu 26.10](https://repology.org/badge/version-for-repo/ubuntu_26_10/zxc.svg)](https://repology.org/project/zxc/versions) [![Crates.io](https://img.shields.io/crates/v/zxc-compress)](https://crates.io/crates/zxc-compress) [![PyPi](https://img.shields.io/pypi/v/zxc-compress)](https://pypi.org/project/zxc-compress) [![npm](https://img.shields.io/npm/v/zxc-compress)](https://www.npmjs.com/package/zxc-compress) ### Option 1: Download Release (GitHub) 1. Go to the [Releases page](https://github.com/hellobertrand/zxc/releases). 2. Download the archive matching your architecture (replace `` with the release, e.g. `0.13.0`): **macOS:** * `zxc--macos-arm64.tar.gz` (NEON optimizations included). **Linux:** * `zxc--linux-arm64.tar.gz` (NEON optimizations included). * `zxc--linux-x86_64.tar.gz` (Runtime dispatch for AVX2/AVX512). **Windows:** * `zxc--windows-x86_64.zip` (Runtime dispatch for AVX2/AVX512). * `zxc--windows-arm64.zip` (NEON optimizations included). 3. Verify, then extract. Every archive is signed via SLSA build provenance; the SHA-256 of each asset is also shown directly on the GitHub release page: ```bash # Authenticity + integrity in one shot (SLSA — recommended) gh attestation verify zxc--linux-x86_64.tar.gz --repo hellobertrand/zxc # Extract tar -xzf zxc--linux-x86_64.tar.gz sudo cp -r zxc--linux-x86_64/* /usr/local/ ``` Each archive contains a versioned top-level directory with: ``` bin/zxc # CLI binary include/ # C headers (zxc.h, zxc_buffer.h, ...) lib/libzxc.a # Static library lib/pkgconfig/libzxc.pc # pkg-config support lib/cmake/zxc/zxcConfig.cmake # CMake find_package(zxc) support ``` 4. Use in your project: **CMake:** ```cmake find_package(zxc REQUIRED) target_link_libraries(myapp PRIVATE zxc::zxc_lib) ``` **pkg-config:** ```bash cc myapp.c $(pkg-config --cflags --libs libzxc) -o myapp ``` ### Option 2: vcpkg **Classic mode:** ```bash vcpkg install zxc ``` **Manifest mode** (add to `vcpkg.json`): ```json { "dependencies": ["zxc"] } ``` Then in your CMake project: ```cmake find_package(zxc CONFIG REQUIRED) target_link_libraries(myapp PRIVATE zxc::zxc_lib) ``` ### Option 3: Conan You also can download and install zxc using the [Conan](https://conan.io/) package manager: ```bash conan install -r conancenter --requires="zxc/[*]" --build=missing ``` Or add to your `conanfile.txt`: ```ini [requires] zxc/[*] ``` The zxc package in Conan Center is kept up to date by [ConanCenterIndex](https://github.com/conan-io/conan-center-index) contributors. If the version is out of date, please create an issue or pull request on the Conan Center Index repository. ### Option 4: Homebrew ```bash brew install zxc ``` The formula is maintained in [homebrew-core](https://formulae.brew.sh/formula/zxc). ### Option 5: Meson Subproject zxc ships a native `meson.build`, so any Meson project can pull it in as a subproject or via [WrapDB](https://mesonbuild.com/Wrapdb-projects.html). **1. Create `subprojects/zxc.wrap`:** ```ini [wrap-git] url = https://github.com/hellobertrand/zxc.git revision = head depth = 1 [provide] libzxc = libzxc_dep ``` **2. Use the dependency in your `meson.build`:** ```meson zxc_dep = dependency('libzxc', fallback : ['zxc', 'libzxc_dep']) executable('myapp', 'main.c', dependencies : zxc_dep) ``` **3. Build:** ```bash meson setup build meson compile -C build ``` When consumed as a subproject, only the library is built (CLI and tests are skipped automatically). ### Option 6: **Requirements:** Windows 10 1709 (or later) Use winget to install the zxc CLI: ```ps1 winget install hellobertrand.zxc ``` ### Option 7: Building from Source (CMake) **Requirements:** CMake (3.14+), C17 Compiler (Clang/GCC/MSVC). ```bash git clone https://github.com/hellobertrand/zxc.git cd zxc cmake -B build -DCMAKE_BUILD_TYPE=Release cmake --build build --parallel # Run tests ctest --test-dir build -C Release --output-on-failure # CLI usage ./build/zxc --help # Install library, headers, and CMake/pkg-config files sudo cmake --install build ``` #### CMake Options | Option | Default | Description | |--------|---------|-------------| | `BUILD_SHARED_LIBS` | OFF | Build shared libraries instead of static (`libzxc.so`, `libzxc.dylib`, `zxc.dll`) | | `ZXC_NATIVE_ARCH` | ON | Enable `-march=native` for maximum performance | | `ZXC_ENABLE_LTO` | ON | Enable Link-Time Optimization (LTO) | | `ZXC_PGO_MODE` | OFF | Profile-Guided Optimization mode (`OFF`, `GENERATE`, `USE`) | | `ZXC_BUILD_CLI` | ON | Build command-line interface | | `ZXC_BUILD_TESTS` | ON | Build unit tests | | `ZXC_ENABLE_COVERAGE` | OFF | Enable code coverage generation (disables LTO/PGO) | | `ZXC_DISABLE_SIMD` | OFF | Disable hand-written SIMD paths (AVX2/AVX512/NEON) | ```bash # Build shared library cmake -B build -DBUILD_SHARED_LIBS=ON # Portable build (without -march=native) cmake -B build -DZXC_NATIVE_ARCH=OFF # Library only (no CLI, no tests) cmake -B build -DZXC_BUILD_CLI=OFF -DZXC_BUILD_TESTS=OFF # Code coverage build cmake -B build -DZXC_ENABLE_COVERAGE=ON # Disable explicit SIMD code paths (compiler auto-vectorisation is unaffected) cmake -B build -DZXC_DISABLE_SIMD=ON ``` #### Profile-Guided Optimization (PGO) PGO uses runtime profiling data to optimize branch layout, inlining decisions, and code placement. **Step 1 - Build with instrumentation:** ```bash cmake -B build -DCMAKE_BUILD_TYPE=Release -DZXC_PGO_MODE=GENERATE cmake --build build --parallel ``` **Step 2 - Run a representative workload to collect profile data:** ```bash # Run the test suite (exercises all block types and compression levels) ./build/zxc_test # Or compress/decompress representative data ./build/zxc -b your_data_file ``` **Step 3 - (Clang only) Merge raw profiles:** ```bash # Clang generates .profraw files that must be merged before use llvm-profdata merge -output=build/pgo/default.profdata build/pgo/*.profraw ``` > GCC uses a directory-based format and does not require this step. **Step 4 - Rebuild with profile data:** ```bash cmake -B build -DCMAKE_BUILD_TYPE=Release -DZXC_PGO_MODE=USE cmake --build build --parallel ``` ### Packaging Status [![Packaging status](https://repology.org/badge/vertical-allrepos/zxc.svg)](https://repology.org/project/zxc/versions) --- ## Compression Levels * **Level 1, 2 (Fast):** Optimized for real-time assets (Gaming, UI). * **Level 3, 4 (Balanced):** A strong middle-ground offering efficient compression speed and a ratio superior to LZ4. * **Level 5 (Compact):** A good choice for Embedded and Firmware. Better compression than LZ4 and significantly faster decoding than Zstd. * **Level 6 (Density):** Beats LZ4HC on both axes — better ratio *and* matching-or-faster decode — while staying in the multi-GB/s decode class. Best for Archival and write-once / read-many workloads where compression time is amortized over many reads. * **Level 7 (Ultra):** Maximum density. Deep parse plus Huffman-coded literals *and* tokens (11-bit codes) push the ratio past `zstd -1` while decoding several times faster than it. Choose it when storage or bandwidth dominates but decode must remain fast; compression is the slowest tier. ## Block Size Tuning The default block size is **512 KB**, tuned for bulk/archival workloads where ratio and decompression throughput matter most. For **memory-constrained or streaming use cases**, **256 KB blocks** halve the per-context memory footprint at a small cost in ratio and decompression speed. **Why larger blocks help:** Each block starts with a cold hash table, so the LZ match-finder has no history and produces more literals until the table warms up. Doubling the block size halves the number of cold-start penalties, improving both ratio and decompression speed. | Block Size | cctx memory | dctx memory | Ratio (level -3) | Decompression gain vs 256 KB | |:----------:|:-----------:|:-----------:|:----------------:|:----------------------------:| | 256 KB | ~1.03 MB | ~256 KB | 46.36% | — | | 512 KB *(default)* | ~1.78 MB | ~512 KB | 45.81% *(−0.55 pp)* | +1% to +8% depending on CPU | ```bash # CLI — fall back to 256 KB blocks (e.g. embedded / streaming) zxc -B 256K -5 input_file output_file # API zxc_compress_opts_t opts = { .level = ZXC_LEVEL_COMPACT, .block_size = 256 * 1024, }; ``` **Guideline:** Stick with 512 KB (default) for bulk compression pipelines, CI/CD asset packaging, and high-throughput servers. Use 256 KB (`-B 256K`) for streaming, embedded, or memory-constrained environments. --- ## In-Place Decompression When the whole archive already lives in RAM (a firmware image, a game asset, a FOTA payload), ZXC can decompress **inside a single buffer** — no separate output allocation. You place the compressed archive flush-right in a buffer, and ZXC decodes left-to-right into the same memory. Because a ZXC block never expands, the write cursor provably never overtakes the read cursor given a one-block safety margin, so peak memory drops from *compressed + decompressed* to roughly *decompressed* alone. ```c // One allocation instead of two. size_t need = zxc_decompress_inplace_bound(archive, archive_size); // reads header+footer uint8_t* buf = malloc(need); memcpy(buf + (need - archive_size), archive, archive_size); // archive flush-right int64_t n = zxc_decompress_inplace(buf, need, archive_size, NULL); // decode into buf[0..] // buf[0 .. n) now holds the decompressed data ``` The required margin is one block plus the accumulated per-block framing overhead and the wild-copy tail (`block_size + nblocks x (8-12 B) + ~2 KB`) — about **1 %** overhead on a large archive; always size the buffer via `zxc_decompress_inplace_bound` rather than the formula. An undersized buffer is rejected with `ZXC_ERROR_DST_TOO_SMALL`, never silent corruption. This is a library/API capability (like LZ4's and Zstd's in-place modes): it targets embedded/firmware integrators, not the file→file CLI, whose streaming decode is already memory-bounded. --- ## Dictionary Compression For workloads compressed in **small blocks** (4 KB–128 KB), a pre-trained dictionary dramatically improves compression ratio. Because the dictionary prefills the LZ77 sliding window at the *start of each block*, the benefit is per-block: a block only has its own preceding bytes as history, so the smaller the block, the more it leans on the dictionary for representative patterns. This applies whether the input is a single small payload or a large payload split into many small blocks — any time the block size is small enough that early bytes would otherwise lack history to match against. **Typical use cases:** JSON API responses, small game assets, structured logs, key-value store records, RPC messages, and any large but homogeneous corpus compressed in small blocks for random access (e.g. seekable archives). ### Training a dictionary ```bash # Train a dictionary from a corpus of similar files. # Without -o the dictionary is written as ./dictionary_.zxd. zxc --train samples/*.json # Choose the output file explicitly with -o: zxc --train -o corpus.zxd samples/*.json # Or point -o at a directory: the dictionary is saved as dictionary_.zxd # inside it (the dict_id embeds in the name), e.g. dicts/dictionary_bc46eec1.zxd zxc --train -o dicts/ samples/*.json ``` ```c // C API const void* samples[] = { buf1, buf2, buf3 }; size_t sizes[] = { len1, len2, len3 }; uint8_t dict[32768]; int64_t dict_sz = zxc_train_dict(samples, sizes, 3, dict, sizeof(dict)); ``` ### Compressing with a dictionary ```bash # CLI — the same dictionary is required to decompress (pass it with -D) zxc -z -D corpus.zxd input.json zxc -d -D corpus.zxd input.json.zxc ``` ```c // C API — compression zxc_compress_opts_t copts = { .level = ZXC_LEVEL_DEFAULT, .dict = dict_content, .dict_size = dict_sz, }; int64_t compressed_size = zxc_compress(src, src_size, dst, dst_cap, &copts); // C API — decompression (same dictionary required) zxc_decompress_opts_t dopts = { .dict = dict_content, .dict_size = dict_sz, }; int64_t original_size = zxc_decompress(compressed, comp_size, out, out_cap, &dopts); ``` The dictionary is stored as an external `.zxd` file and referenced by a 32-bit ID (`dict_id`) in the ZXC file header. The **same dictionary is required to decompress** and must be supplied explicitly with `-D` — there is no auto-lookup. Decompressing an archive that needs a dictionary without supplying one returns `ZXC_ERROR_DICT_REQUIRED`; supplying the wrong one returns `ZXC_ERROR_DICT_MISMATCH`. Training to a directory names the file `dictionary_.zxd`. See [FORMAT.md](docs/FORMAT.md) §12 for the full specification. --- ## Usage ### 1. CLI The CLI is perfect for benchmarking or manually compressing assets. ```bash # Basic Compression (Level 3 is default) zxc -z input_file output_file # High Compression (Level 5) zxc -z -5 input_file output_file # Seekable Archive (enables O(1) random-access decompression) zxc -z -S input_file output_file # -z for compression can be omitted zxc input_file output_file # as well as output file; it will be automatically assigned to input_file.zxc zxc input_file # Decompression zxc -d compressed_file output_file # When installed, "unzxc" is an alias for "zxc -d" unzxc compressed_file output_file # Benchmark Mode (Testing speed on your machine) zxc -b input_file ``` #### Using with `tar` ZXC works as a drop-in external compressor for `tar` (reads stdin, writes stdout, returns 0 on success): ```bash # GNU tar (Linux) tar -I 'zxc -5' -cf archive.tar.zxc data/ tar -I 'zxc -d' -xf archive.tar.zxc # bsdtar (macOS) tar --use-compress-program='zxc -5' -cf archive.tar.zxc data/ tar --use-compress-program='zxc -d' -xf archive.tar.zxc # Pipes (universal) tar cf - data/ | zxc > archive.tar.zxc zxc -d < archive.tar.zxc | tar xf - ``` ### 2. API ZXC provides a **thread-safe API** with two usage patterns. Parameters are passed through dedicated options structs, making call sites self-documenting and forward-compatible. #### Buffer API (In-Memory) ```c #include "zxc.h" // Compression uint64_t bound = zxc_compress_bound(src_size); zxc_compress_opts_t c_opts = { .level = ZXC_LEVEL_DEFAULT, .checksum_enabled = 1, /* .block_size = 0 -> 512 KB default */ }; int64_t compressed_size = zxc_compress(src, src_size, dst, bound, &c_opts); // Decompression zxc_decompress_opts_t d_opts = { .checksum_enabled = 1 }; int64_t decompressed_size = zxc_decompress(src, src_size, dst, dst_capacity, &d_opts); ``` #### Stream API (Files, Multi-Threaded) ```c #include "zxc.h" // Compression (auto-detect threads, level 3, checksum on) zxc_compress_opts_t c_opts = { .n_threads = 0, // 0 = auto .level = ZXC_LEVEL_DEFAULT, .checksum_enabled = 1, /* .block_size = 0 -> 512 KB default */ }; int64_t bytes_written = zxc_stream_compress(f_in, f_out, &c_opts); // Decompression zxc_decompress_opts_t d_opts = { .n_threads = 0, .checksum_enabled = 1 }; int64_t bytes_out = zxc_stream_decompress(f_in, f_out, &d_opts); ``` #### Reusable Context API (Low-Latency / Embedded) For tight loops (e.g. filesystem plug-ins) where per-call `malloc`/`free` overhead matters, use opaque reusable contexts. Options are **sticky** - settings from `zxc_create_cctx()` are reused when passing `NULL`: ```c #include "zxc.h" zxc_compress_opts_t opts = { .level = 3, .checksum_enabled = 0 }; zxc_cctx* cctx = zxc_create_cctx(&opts); // allocate once, settings remembered zxc_dctx* dctx = zxc_create_dctx(); // allocate once // reuse across many blocks - NULL reuses sticky settings: int64_t csz = zxc_compress_cctx(cctx, src, src_sz, dst, dst_cap, NULL); int64_t dsz = zxc_decompress_dctx(dctx, dst, csz, out, src_sz, NULL); zxc_free_cctx(cctx); zxc_free_dctx(dctx); ``` **Features:** - Caller-allocated buffers with explicit bounds - Thread-safe (stateless) - Configurable block sizes (4 KB – 2 MB, powers of 2) - Multi-threaded streaming (auto-detects CPU cores) - Optional checksum validation - Reusable contexts for high-frequency call sites - Seekable archives: optional seek table for O(1) random-access decompression (`.seekable = 1`) **[👉 See complete examples and advanced usage](docs/EXAMPLES.md)** ## Language Bindings [![Crates.io](https://img.shields.io/crates/v/zxc-compress)](https://crates.io/crates/zxc-compress) [![PyPi](https://img.shields.io/pypi/v/zxc-compress)](https://pypi.org/project/zxc-compress) [![npm](https://img.shields.io/npm/v/zxc-compress)](https://www.npmjs.com/package/zxc-compress) Official wrappers maintained in this repository: | Language | Package Manager | Install Command | Documentation | Author | |----------|-----------------|-----------------|---------------|--------| | **Rust** | [`crates.io`](https://crates.io/crates/zxc-compress) | `cargo add zxc-compress` | [README](wrappers/rust/zxc/README.md) | [@hellobertrand](https://github.com/hellobertrand) | | **Python**| [`PyPI`](https://pypi.org/project/zxc-compress) | `pip install zxc-compress` | [README](wrappers/python/README.md) | [@nuberchardzer1](https://github.com/nuberchardzer1) | | **Node.js**| [`npm`](https://www.npmjs.com/package/zxc-compress) | `npm install zxc-compress` | [README](wrappers/nodejs/README.md) | [@hellobertrand](https://github.com/hellobertrand) | | **Go** | `go get` | `go get github.com/hellobertrand/zxc/wrappers/go` | [README](wrappers/go/README.md) | [@hellobertrand](https://github.com/hellobertrand) | | **WASM** | Build from source | `emcmake cmake -B build-wasm && cmake --build build-wasm` | [README](wrappers/wasm/README.md) | [@hellobertrand](https://github.com/hellobertrand) | Community-maintained bindings: | Language | Package Manager | Install Command | Repository | Author | | -------- | --------------- | --------------- | ---------- | ------ | | **Go** | pkg.go.dev | `go get github.com/meysam81/go-zxc` | | [@meysam81](https://github.com/meysam81) | | **Nim** | nimble | `nimble install zxc` | | [@georgelemon](https://github.com/georgelemon) | | **Free Pascal** | Build from source | Clone the repository | | [@Xelitan](https://github.com/Xelitan) | ## Format & Conformance The ZXC on-disk wire format is fully specified in [`docs/FORMAT.md`](docs/FORMAT.md) (format version 7), so any third party can build an independent, interoperable decoder. > **Upgrading?** The current format is **v7** — Huffman entropy sections in the new PivCo layout (faster SIMD-merge decode), Huffman-coded tokens and 11-bit codes at level 7. Like the v5→v6 change, this is a deliberate clean break: v7 tools reject v6 archives (see [`docs/MIGRATION.md`](docs/MIGRATION.md) to convert). Two complementary, byte-frozen suites guard that format: * **Decoder conformance** — [`conformance/`](conformance/README.md) ships public reference vectors: `valid/*.zxc` streams paired with their expected decompressed output, plus `invalid/*.zxc` streams that a correct decoder **must** reject. Point your own decoder at them to prove interoperability — no dependency on this implementation. Run locally via the `conformance` CTest. * **Wire-format stability** — [`tests/format/`](tests/format/README.md) pins the exact bytes the encoder emits for every block type and integrity field. A dedicated CI job ([`golden.yml`](.github/workflows/golden.yml)) fails on any single-byte drift, so a format change can only ever be deliberate. The distinction: conformance freezes decoder *behaviour* (`decode(x) == expected`), while the golden suite freezes the encoder's *bytes*. Together they make the format both interoperable and stable. ## Safety & Quality * **Unit Tests**: Comprehensive test suite with CTest integration. * **Continuous Fuzzing**: Integrated with ClusterFuzzLite suites — **5+ billion iterations** accumulated to date across compress, decompress, streaming and seekable API surfaces. * **Static Analysis**: Checked with Cppcheck & Clang Static Analyzer. * **CodeQL Analysis**: GitHub Advanced Security scanning for vulnerabilities. * **Snyk**: Continuous security and code analysis for dependencies and source. * **Code Coverage**: Automated tracking with Codecov integration. * **Dynamic Analysis**: Validated with Valgrind and ASan/UBSan in CI pipelines. * **Safe API**: Explicit buffer capacity is required for all operations. ## License & Credits **ZXC** Copyright © 2025-2026, Bertrand Lebonnois and contributors. Licensed under the **BSD 3-Clause License**. See LICENSE for details. **Third-Party Components:** - **rapidhash** by Nicolas De Carli (MIT) - Used for high-speed, platform-independent checksums. zxc-0.13.0/cliff.toml000066400000000000000000000102231522536370400143640ustar00rootroot00000000000000# git-cliff configuration # https://git-cliff.org/docs/configuration [changelog] header = """ # Changelog """ body = """ {% if version -%} ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} {% else -%} ## [Unreleased] {% endif -%} {% for group, commits in commits | group_by(attribute="group") -%} {% for commit in commits -%} {{ group | split(pat="-") | last }}: {{ commit.message | split(pat="\n") | first | trim }} {% endfor -%} {% endfor %} """ footer = "" trim = true [git] conventional_commits = false filter_unconventional = false split_commits = false # Strip conventional-commit prefixes from the displayed message # (the category prefix is re-added by the template via the parser group). commit_preprocessors = [ { pattern = '^(feat|fix|perf|docs?|build|ci|chore|refactor|test|api|cli|port|portability|misc|style)(\([^)]+\))?!?:\s*', replace = '' }, ] # Order matters: the first matching rule wins. # Skip rules first, then specific categories, then a catch-all. commit_parsers = [ # ---- Skip noise ---- { message = "^[Bb]ump (to )?v?[0-9]", skip = true }, { message = "^chore\\(release\\)", skip = true }, { message = "^docs\\(changelog\\)", skip = true }, { message = "(?i)\\.ds_store", skip = true }, { message = "^[Mm]erge ", skip = true }, # ---- Categories ---- { message = "^feat", group = "01-api" }, { message = "^api[:(]", group = "01-api" }, { message = "(?i)\\bapi\\b", group = "01-api" }, { message = "^fix", group = "02-fix" }, { message = "^[Ff]ix(es|ed)? ", group = "02-fix" }, { message = "^perf", group = "03-perf" }, { message = "^[Oo]ptimi[sz]e", group = "03-perf" }, { message = "(?i)speed[- ]?up|faster|throughput", group = "03-perf" }, { message = "^cli[:(]", group = "04-cli" }, { message = "(?i)\\bcli\\b", group = "04-cli" }, { message = "^build", group = "05-build" }, { message = "^ci[:(]", group = "05-build" }, { message = "(?i)workflow|makefile|cmake|wrapper", group = "05-build" }, { message = "(?i)portability|android|windows|linux|macos|emscripten|wasm|freebsd|qnx", group = "06-portability" }, { message = "^docs?", group = "07-doc" }, { message = "(?i)readme|whitepaper|documentation|benchmark.*(graph|figure|chart)|refresh.*benchmark|update.*docs?", group = "07-doc" }, # ---- Catch-all ---- { message = ".*", group = "08-misc" }, ] filter_commits = false tag_pattern = "v[0-9]+\\.[0-9]+\\.[0-9]+" sort_commits = "newest" zxc-0.13.0/codecov.yml000066400000000000000000000001671522536370400145570ustar00rootroot00000000000000coverage: status: project: default: target: 80% ignore: - "src/cli/**" - "src/lib/vendors/**" zxc-0.13.0/conformance/000077500000000000000000000000001522536370400147005ustar00rootroot00000000000000zxc-0.13.0/conformance/README.md000066400000000000000000000046731522536370400161710ustar00rootroot00000000000000# ZXC Decoder Conformance Suite Reference test vectors for validating any ZXC decoder implementation. ## Contents ``` valid/ *.zxc Compressed files (frozen wire format) *.expected Expected decompressed output (plaintext reference) invalid/ *.zxc Malformed files that must be rejected ``` ## Validating a decoder For each `valid/*.zxc`: 1. Decompress the file with your decoder 2. Compare the output byte-for-byte against the matching `.expected` file 3. Any mismatch is a decoder bug For each `invalid/*.zxc`: 1. Attempt to decompress the file with your decoder 2. The decoder **must** reject it (return an error, not produce output) 3. Accepting a malformed file is a decoder bug ## Quick check (shell) ```sh pass=0; fail=0 for f in valid/*.zxc; do expected="${f%.zxc}.expected" your-decoder "$f" /tmp/out if cmp -s /tmp/out "$expected"; then pass=$((pass + 1)) else echo "FAIL: $f"; fail=$((fail + 1)) fi done for f in invalid/*.zxc; do if your-decoder "$f" /dev/null 2>/dev/null; then echo "FAIL (should reject): $f"; fail=$((fail + 1)) else pass=$((pass + 1)) fi done echo "Passed: $pass Failed: $fail" ``` ## Vector coverage | Category | Count | Description | |----------------------|-------|--------------------------------------------------| | Basic | 5 | Empty, 1 byte, all 256 values, all-zeros | | Text | 3 | Compressible text with and without checksum | | Random | 3 | Incompressible data (stored as raw blocks) | | Match patterns | 3 | Long matches, short matches, max offset distance | | Compression levels | 6 | Same input compressed at levels 1 through 6 | | Level 7 | 1 | Level-7 literals with 9-11-bit Huffman codes | | PivCo (L7) | 1 | Level-7 PivCo sections (v7 enc 2, level-ordered bits) | | Block size variants | 2 | 4 KB and 2 MB block sizes | | Checksum | 3 | Per-block and global checksum enabled | | Multi-block | 2 | 16 blocks per file (4 KB block size) | | Seekable | 3 | Seekable archives with seek table | | Invalid | 12 | Bad magic, bad version, bad CRC, truncated, corrupt payload, garbage | ## License BSD-3-Clause. Same as the ZXC library. zxc-0.13.0/conformance/invalid/000077500000000000000000000000001522536370400163265ustar00rootroot00000000000000zxc-0.13.0/conformance/invalid/all_0xff_garbage.zxc000066400000000000000000000001001522536370400222060ustar00rootroot00000000000000zxc-0.13.0/conformance/invalid/bad_block_checksum.zxc000066400000000000000000000002521522536370400226350ustar00rootroot00000000000000.;zEEEthe quick brown fox jumps over the lazy dog. ZXC v6 conformance base.D6M8W8Wzxc-0.13.0/conformance/invalid/bad_block_size_field.zxc000066400000000000000000000002521522536370400231500ustar00rootroot00000000000000.;EEEthe quick brown fox jumps over the lazy dog. ZXC v6 conformance base.D68W8Wzxc-0.13.0/conformance/invalid/bad_block_type.zxc000066400000000000000000000003021522536370400220100ustar00rootroot00000000000000.;]]]GET /api/v1/users/12345/profile HTTP/1.1 Host: api.example.com Accept: application/json \Nwwzxc-0.13.0/conformance/invalid/bad_checksum_algo.zxc000066400000000000000000000002571522536370400224720ustar00rootroot00000000000000.AJJJthe quick brown fox jumps over the lazy dog. ZXC v6 freeze-hardening base.I;  zxc-0.13.0/conformance/invalid/bad_enc_lit.zxc000066400000000000000000000002761522536370400213040ustar00rootroot00000000000000.|;n]]]GET /api/v1/users/12345/profile HTTP/1.1 Host: api.example.com Accept: application/json \Nzxc-0.13.0/conformance/invalid/bad_eof_compsize.zxc000066400000000000000000000002571522536370400223500ustar00rootroot00000000000000.;AJJJthe quick brown fox jumps over the lazy dog. ZXC v6 freeze-hardening base.I; F zxc-0.13.0/conformance/invalid/bad_header_crc.zxc000066400000000000000000000002521522536370400217400ustar00rootroot00000000000000.;zEEEthe quick brown fox jumps over the lazy dog. ZXC v6 conformance base.D68W8Wzxc-0.13.0/conformance/invalid/bad_magic.zxc000066400000000000000000000004461522536370400207460ustar00rootroot00000000000000gLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. 0>::ttWWRUfffvwwwwwwwx   ÄQ[DNQ|תz%O@1cBKg!ƥ o.nly23 ǖ}`HX\YWJQ&cnk3S^L^h- o |Tn1Fxҋb]Y9P2SDemSQe4(!\Sx;LΞdsMtE_fsHfWa!ٛ][jt>OaN9Jo]Q3|=HD\Ѳ$;Ws&1!#zϏ,4ikF 1ۙ Ng#Q 4jwaQ G!GBycٖq_3i`W7}H[fNUs܁w Qv96NU ;n)'+}Vip, F]#y{L.A *:*1$ʟ@CFK.h]2)ɞA!\mTA Z=zS ^?e/'h鸷Gd 7븐z)O kc_Օ?T%Qx*[oͤ`.+{q6 aF b:дeĊ빲NfkaY:XCmd(H1n!]rDz|-|}/p_oH%E.s?錽O<4^Yw@%Lry+l!KF+u#-Qo53Bhג6bGOmP8V_h!X4Mwq5S\z5fEYskVaꛐY.yXi!b!ן%yY0%P Y( f'{3H@ 0W$U"|o`: i4V0 Jژc 4ND Ėһ{0̩kb8 #LXMإ p-@< #E8li^ϱbR xc\` ؆Ǭ728+z1E(/+λUr9[9R_d.`^#XN(B"#'=)I 4$?Zbg[Mw:;RsZZQ&˟ٳw\8ln1;.;p^TD`[)C< %IY4e@a>ob@u1P*H|_D۽Eg Vd)7}9<$rϊ aah@hG?Uj"V8^\Xk6|2=EZɱO_\ &g:8a5Id|B7u@O s?x |eږOW4xRx[~EsEN刏ui :f+ , *_k/H J{ dſ@3%oN6a3hRf;='f\>R"ve`sϰHQ5uXuqP/_6?t3<.4,cZ~DVlP Xd+q3Ѿ"Ff~VTԌ [ .UvK/.SZ)-Ly/J^g0Y 8"F#ɵbH>M3?Fc~!hT?7-U2顆@sSzH-$F٬ܣxr( `&R({Ǡ } /h_g{pZt3qB.zLmc# q[&Ҧ][I&1i7RwkjP5MSj+4*UwbVrߚk׶Њ$-뭅\>&Jbl2v4 b4+>j=%; eg;_ wDӵ$gO$&A$K"vuj܈ kO?0cODyBs -' {o£͎|'g?҃kCvZXNt΄|N}[cSJ{w Q<f^@fn }% JFh꒿anY1c!2/o!S}Vxq!l)uhRRȐ˻M#^*{cmo˞"x[y5[T?\~l4!όīu6< T)i5eI ǧ&Mlz7]{zWa5ɧ lwMm)3SZ%o4ŔXɝȇO ݞCobܪRK6xg=*gOosj'oײ^}Qkz߇ZMU\&3@2}+yŘ5G<A7mLe'Kd Tr/t{L)`Jr~ u1fъװ^>;3a6>D: EAqqFZ1G-WL_Q_?;.o!޼ //8:y.=ɗVvPgDz0Lvx s1虵oim WHHwz^2-ɟD| 8'b_g#rMIN4a&ӛ~&$Hjܝܟ"TK\)g$'kdj-)/WJg"~FW=ʝ)"N9݊*a.&լW)8+aj/7%|#Sh'2ZQWaN#(UGLTճA)FĈrN1wb#o(4R{mq*HYkOH&*cl,ޓpkiܧׂr{n*ѳH )o}A&[Ÿcw튃۹rN>~hWKXQ@8`TSSoOLˠ<\-#"Ve*.D`*>Rɠ{K,- !*@PN <==l;+^kO[nx}Ƭ+ky=;  h;5<?Z;C:W!nAyjxT{L zFaT9?`DŽ:CVd$0,{ i(` Qe"d|:f;K8{K];N㥐]VJU̢ ['t;R_EŲ() B^3_gPbk@,h Kl fiK.vT&G\A$ϩn<4;i]]YZMs-,}rP99_ 1-0X"),HK{Xh")Rk)+R#!˕u hV,oP#=||}.Y0i&r2v<:|ӿ8!I VRǥF_HB6'OXosclK!KQ8QbDxxŁ)3bp>6ogl=8%V|64.^nklc.~CS;WmG?xo)4ˀmyn疫e];?BUU$#,* |)-qbv*P;l=sƥ.,?5e3 .U#++3dXHsT}Meg]tGvu3-KȤ.T Á7엧Ɛ<",nt `uh¥E׷Ȭ+1G!D9>lW. p|(4?|l10dNŊ%xÝI0t 7^ >C36MN鷱{$ ߔS_{PJ|”Q2u-*+f0ʧ+bO. M3=U)겖^y%gv L@FP "R փbtO2.A&ٽ1Sy0@  \4v)땂 r/CWѐ(u!886Gh+=gޣE}D"Y2b|P$2Jyw8qQ=QUiŗ:+'py\V?Xe"(0Zh-~:3}6cdRʼntb^;wX4rN5K4D(w"ޚ/#/s6*y8^ɻϓj›4nj(0w5r0B[)섍2E 7eX%Iyr,Cۏ([?gvj`a[xӓTǭC,9ȀoV bE:!xg~?+&z5{{5cҋPv]/;DK1;fnVa}>W]X⹸zR*!xu'FGJ!#GXiQH3mPQ=E7_( -"$E.8"x5i<|hd|9crJV|%k䧭B(!VWK=Jc "yI5\"syc4`Sfљi{9%&`s Db)ȜLɄqL4 [7X m?#Hfϑ 96@t ,KCejo}ⓍGx'?`> Be 1d˒7ڜݩ(Zc NrbFwd=5qqZ>' g`2&X F+VF},0q7 584&7cf?\|Ч%Eǝ]GZ VVFM]R%x(>d=JsMI},o-l}i|V+*ZT )uaߖ @e2TJJt*%v| >K4/?jڴ?P7:^,MN=ސ=jKec%8XkÏ6xC\B?%LJs_ ;UFMؐtac@M:n1n])}!(1[|8esE-=§r>EGL;z,:QxQc-Ob93 I?@)Jݒ ɔ֡I;; pSKX9;Ba'cQǠa=Q_~5>m- VGEԶԕJ>zub'2G*`ܥTօ|~O.-|G.k:4R}_c%BL1Ku>OZhH㛗H* Wn&X“u?-5~4[)M2]} mCI|iRcU .Б6s9ÿ_!CD%Ks3.œl$%bh]jP#ٓмY:?bB҇6'zvgٺma?BgQ>b0 cGZGM mJX&5 ׅt!~r-I6D{K8} 7(V\5.D@ha.r~(ʒra xm5h ?Mdҩ8JJr_$'߰.8l\=0xJMYuFSS 6olz5d'8Fszn*AJn\@)?8`/ȕ~ƤyN54")cH`˒fjХNB6.jW,%K/{0eL`C9l}o,zk6 ֥lU]KFf0XztRwbi"u`YKޞ lm~ jE-rc|y;N08& (󪍯F_bဨ~d@kI}K3b=ȍ”fpE-Әw9a;h`zAЊ rϻҹDTߍz]XߡA_CUwq2J_~p!Y+(aQli&>,ꁜ7 .VEC]~6>[H^пWeˊ ħ!֓AB*{l0!CE?V_aDgg D栝2έ$ŵea"zkF}zC_75zY& KgvS ֥.lL7>g":|2XW۩Nh=p 3l(@gˮ( Z?Pm"8XHyĤOgx54"HQj'3#hmm8)Luw8vw3] ,BĻCfHNW^c`*̏6\~/bQt4V\’:x#n.7YoJ(3]5DX񆳓llʿKxm!4 =xF`Ў֐^4Vݤ4"wrLS&9;Pַ \٢7ϲ_c5M~/>EkO2?>)<\J5;Ƨ OA"RXh 6j`-$f =h@ 1t3}oDjDr5LX#-c24vX.Ţu. Yw f2(T;;,'kHP/b]dChԱC5r!L`kT7 8?ԍ ?ZA3i} X,N̑OMZC )m\%6vd9<99i3d\O[x5)86X`4Ag߬Y-a !܅<*{ćxr+odS<$Mō-vt1bO x;-t]Ƅ#P̉2zơbN2M(ϰ,>L3*Kf4qL-"dŶ'D@TG-f $, Ľ7R!Z*xƛ"~U 5 6o~Cg8{D91k_ 3B0 lD9U SJnpUcüf}70-=r;tfˉ/af | ͆d bVs/l!&("k\+'}E f J?Gkx]wih+=l֠ ڕ_U2eF} Ad } >a¯W19<ʶ¢Z*>9s$ǿavdƄ(Pw*AД54q4aie1ƾhw9e;[;@L?" PTxvI'KI,|w^ Fh gd , 3w/.Ok@h&xaDo̯4B1D?#LZIЉ9QG˹T|mG]~hs7!?U`ž" Hy)hV3Dާ^;h~̶0J&sBJV/+c"} @Cm0eG/ Q^ 7w{vSZEm&\^u;fErP*^ IO@2BiШq5QTjq_7 i-ʇ.lr{] fd{&\ߖC0>H{J}Nʹ.w67BD*pDt,0uz^po;Z:g*R 6LTL-H@/A&D@L L; EC3ҊJoW&ۀ.$Dq¢)&Pq Zbb0,\OGL P3mBߤc=X`m(&- wNwE^K`7Kvh^?tLq\Go_jSY޳(?B{E"3,-]` ^έ|ϫ'X{5iٿe6k+=Fn%Ck>9?>q鏛^,y~iw:~/.=3~ލ-{`EMkHQJD,]٩-^XCs. >: [(n?$DF&͈Q%@^ YV[/$e-:~[Gt`x 0ZNF/C7 H66Ҝ- C8Խl1\Tb\^|hf0xrDHkqV?-# X|! Kڠ@ ĢgSesPj{Ҕ)#0 QGTg&ΒB\IR((kch9&W-FInDu\fp )v hl-c)(ad< Yo3͒oKf1A&p&uu A  6j !"X f#0?,)G,- B,0n 5Y X!s"@$XA pO) (v  ##G-|6 j[# XS #include #include #include #ifdef _WIN32 #include #else #include #include #endif #include "../include/zxc_buffer.h" #include "../include/zxc_dict.h" #include "../include/zxc_error.h" /* ---------- helpers ------------------------------------------------------ */ static uint8_t *read_file(const char *path, size_t *out_size) { FILE *f = fopen(path, "rb"); if (!f) return NULL; fseek(f, 0, SEEK_END); long len = ftell(f); fseek(f, 0, SEEK_SET); if (len < 0) { fclose(f); return NULL; } if (len == 0) { fclose(f); *out_size = 0; return calloc(1, 1); } uint8_t *buf = malloc((size_t)len); if (!buf) { fclose(f); return NULL; } if (fread(buf, 1, (size_t)len, f) != (size_t)len) { free(buf); fclose(f); return NULL; } fclose(f); *out_size = (size_t)len; return buf; } static int file_exists(const char *path) { #ifdef _WIN32 DWORD attr = GetFileAttributesA(path); return attr != INVALID_FILE_ATTRIBUTES; #else struct stat st; return stat(path, &st) == 0; #endif } static int has_suffix(const char *s, const char *suffix) { size_t slen = strlen(s), xlen = strlen(suffix); if (slen < xlen) return 0; return memcmp(s + slen - xlen, suffix, xlen) == 0; } /* ---------- valid vector test -------------------------------------------- */ /** * @brief Searches for a .zxd dictionary file in the same directory as @p zxc_path * whose dict_id matches @p target_id. Returns the loaded content (caller frees) * and, via @p huf_out, the shared literal Huffman table carried by the .zxd. */ static uint8_t *find_dict_for_id(const char *zxc_path, uint32_t target_id, const void **content_out, size_t *content_size_out, const void **huf_out) { /* Derive directory from zxc_path */ char dir[512]; size_t plen = strlen(zxc_path); if (plen >= sizeof(dir)) plen = sizeof(dir) - 1; memcpy(dir, zxc_path, plen); dir[plen] = '\0'; char *sep = strrchr(dir, '/'); if (sep) *(sep + 1) = '\0'; else snprintf(dir, sizeof(dir), "./"); #ifdef _WIN32 char pattern[512]; snprintf(pattern, sizeof(pattern), "%s*.zxd", dir); WIN32_FIND_DATAA fd; HANDLE hf = FindFirstFileA(pattern, &fd); if (hf == INVALID_HANDLE_VALUE) return NULL; do { char path[1024]; snprintf(path, sizeof(path), "%s%s", dir, fd.cFileName); size_t sz = 0; uint8_t *buf = read_file(path, &sz); if (buf && zxc_dict_get_id(buf, sz) == target_id) { if (zxc_dict_load(buf, sz, content_out, content_size_out, huf_out, NULL) == 0) { FindClose(hf); return buf; } } free(buf); } while (FindNextFileA(hf, &fd)); FindClose(hf); #else DIR *dp = opendir(dir); if (!dp) return NULL; const struct dirent *ent; while ((ent = readdir(dp)) != NULL) { if (!has_suffix(ent->d_name, ".zxd")) continue; char path[1024]; snprintf(path, sizeof(path), "%s%s", dir, ent->d_name); size_t sz = 0; uint8_t *buf = read_file(path, &sz); if (buf && zxc_dict_get_id(buf, sz) == target_id) { if (zxc_dict_load(buf, sz, content_out, content_size_out, huf_out, NULL) == 0) { closedir(dp); return buf; } } free(buf); } closedir(dp); #endif return NULL; } static int test_valid_vector(const char *zxc_path, const char *expected_path) { size_t comp_sz = 0, expected_sz = 0; uint8_t *comp = read_file(zxc_path, &comp_sz); uint8_t *expected = read_file(expected_path, &expected_sz); if (!comp) { fprintf(stderr, "FAIL: cannot read %s\n", zxc_path); free(expected); return 0; } if (!expected) { fprintf(stderr, "FAIL: cannot read %s\n", expected_path); free(comp); return 0; } /* Auto-detect dictionary: if the archive has a dict_id, find the .zxd */ const void *dict = NULL; size_t dict_size = 0; const void *dict_huf = NULL; uint8_t *dict_buf = NULL; uint32_t did = zxc_get_dict_id(comp, comp_sz); if (did != 0) { dict_buf = find_dict_for_id(zxc_path, did, &dict, &dict_size, &dict_huf); if (!dict_buf) { fprintf(stderr, "FAIL: %s requires dict 0x%08X but no matching .zxd found\n", zxc_path, did); free(comp); free(expected); return 0; } } int ok = 1; uint64_t dec_sz = zxc_get_decompressed_size(comp, comp_sz); if (dec_sz != expected_sz) { fprintf(stderr, "FAIL: %s size mismatch: got %llu, expected %zu\n", zxc_path, (unsigned long long)dec_sz, expected_sz); ok = 0; } else if (expected_sz == 0) { /* Nothing to decompress: size match is sufficient. */ } else { uint8_t *output = malloc((size_t)dec_sz); if (!output) { fprintf(stderr, "FAIL: %s OOM\n", zxc_path); ok = 0; } else { zxc_decompress_opts_t dopts = {0}; if (dict) { dopts.dict = dict; dopts.dict_size = dict_size; dopts.dict_huf = dict_huf; } int64_t result = zxc_decompress(comp, comp_sz, output, (size_t)dec_sz, &dopts); if (result < 0) { fprintf(stderr, "FAIL: %s decompress failed: %s\n", zxc_path, zxc_error_name((int)result)); ok = 0; } else if ((size_t)result != expected_sz) { fprintf(stderr, "FAIL: %s output size %lld != expected %zu\n", zxc_path, (long long)result, expected_sz); ok = 0; } else if (memcmp(output, expected, expected_sz) != 0) { fprintf(stderr, "FAIL: %s output content mismatch\n", zxc_path); ok = 0; } free(output); } } free(dict_buf); free(comp); free(expected); return ok; } /* ---------- invalid vector test ------------------------------------------ */ static int test_invalid_vector(const char *zxc_path) { size_t comp_sz = 0; uint8_t *comp = read_file(zxc_path, &comp_sz); if (!comp) { fprintf(stderr, "FAIL: cannot read %s\n", zxc_path); return 0; } int ok = 1; uint64_t dec_sz = zxc_get_decompressed_size(comp, comp_sz); if (dec_sz > 0 && dec_sz <= (1 << 20)) { uint8_t *output = malloc((size_t)dec_sz); if (output) { /* Verify with checksum enabled so checksum/payload-corruption * vectors are caught (verification needs both the file flag and * this opt; see zxc_decompress_block in zxc_dispatch.c). */ const zxc_decompress_opts_t io = {.checksum_enabled = 1}; int64_t result = zxc_decompress(comp, comp_sz, output, (size_t)dec_sz, &io); if (result >= 0) { fprintf(stderr, "FAIL: %s should be rejected but decoded %lld bytes\n", zxc_path, (long long)result); ok = 0; } free(output); } } free(comp); return ok; } /* ---------- portable directory scanner ----------------------------------- */ typedef struct { char **names; size_t count; size_t capacity; } name_list_t; static void name_list_add(name_list_t *l, const char *name) { if (l->count >= l->capacity) { l->capacity = l->capacity ? l->capacity * 2 : 64; l->names = realloc(l->names, l->capacity * sizeof(char*)); } #ifdef _WIN32 l->names[l->count++] = _strdup(name); #else l->names[l->count++] = strdup(name); #endif } static int name_cmp(const void *a, const void *b) { return strcmp(*(const char * const *)a, *(const char * const *)b); } static void name_list_sort(name_list_t *l) { if (l->count > 1) qsort(l->names, l->count, sizeof(char*), name_cmp); } static void name_list_free(name_list_t *l) { for (size_t i = 0; i < l->count; i++) free(l->names[i]); free(l->names); l->names = NULL; l->count = l->capacity = 0; } static int list_zxc_files(const char *dir, name_list_t *out) { #ifdef _WIN32 char pattern[512]; snprintf(pattern, sizeof pattern, "%s\\*.zxc", dir); WIN32_FIND_DATAA fd; HANDLE h = FindFirstFileA(pattern, &fd); if (h == INVALID_HANDLE_VALUE) return -1; do { if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) name_list_add(out, fd.cFileName); } while (FindNextFileA(h, &fd)); FindClose(h); #else DIR *d = opendir(dir); if (!d) return -1; const struct dirent *ent; while ((ent = readdir(d)) != NULL) { if (has_suffix(ent->d_name, ".zxc")) name_list_add(out, ent->d_name); } closedir(d); #endif name_list_sort(out); return 0; } /* ---------- main --------------------------------------------------------- */ int main(int argc, char **argv) { const char *base = "conformance"; if (argc > 1) base = argv[1]; char valid_dir[512], invalid_dir[512]; snprintf(valid_dir, sizeof valid_dir, "%s/valid", base); snprintf(invalid_dir, sizeof invalid_dir, "%s/invalid", base); int passed = 0, failed = 0, total = 0; /* --- Valid vectors --- */ printf("=== Valid vectors (%s) ===\n", valid_dir); { name_list_t zxc_files = {0}; if (list_zxc_files(valid_dir, &zxc_files) < 0) { fprintf(stderr, "Cannot open %s\n", valid_dir); return 1; } for (size_t i = 0; i < zxc_files.count; i++) { char zxc_path[2048], exp_path[2048]; snprintf(zxc_path, sizeof zxc_path, "%s/%s", valid_dir, zxc_files.names[i]); char stem[1024]; snprintf(stem, sizeof stem, "%s", zxc_files.names[i]); stem[strlen(stem) - 4] = '\0'; snprintf(exp_path, sizeof exp_path, "%s/%s.expected", valid_dir, stem); if (!file_exists(exp_path)) { fprintf(stderr, "SKIP: %s (no .expected file)\n", zxc_files.names[i]); continue; } total++; if (test_valid_vector(zxc_path, exp_path)) { printf(" PASS: %s\n", stem); passed++; } else { failed++; } } name_list_free(&zxc_files); } /* --- Invalid vectors --- */ printf("\n=== Invalid vectors (%s) ===\n", invalid_dir); { name_list_t zxc_files = {0}; if (list_zxc_files(invalid_dir, &zxc_files) < 0) { fprintf(stderr, "Cannot open %s\n", invalid_dir); return 1; } for (size_t i = 0; i < zxc_files.count; i++) { char zxc_path[1024]; snprintf(zxc_path, sizeof zxc_path, "%s/%s", invalid_dir, zxc_files.names[i]); char stem[1024]; snprintf(stem, sizeof stem, "%s", zxc_files.names[i]); stem[strlen(stem) - 4] = '\0'; total++; if (test_invalid_vector(zxc_path)) { printf(" PASS: %s (correctly rejected)\n", stem); passed++; } else { failed++; } } name_list_free(&zxc_files); } /* --- Summary --- */ printf("\n=== Summary ===\n"); printf("Total: %d Passed: %d Failed: %d\n", total, passed, failed); if (failed > 0) { printf("CONFORMANCE TESTS FAILED.\n"); return 1; } printf("ALL CONFORMANCE TESTS PASSED.\n"); return 0; } zxc-0.13.0/conformance/valid/000077500000000000000000000000001522536370400157775ustar00rootroot00000000000000zxc-0.13.0/conformance/valid/all_256_values.expected000066400000000000000000000004001522536370400222370ustar00rootroot00000000000000  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~zxc-0.13.0/conformance/valid/all_256_values.zxc000066400000000000000000000004541522536370400212530ustar00rootroot00000000000000.]B  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~zxc-0.13.0/conformance/valid/all_zeros_4k.expected000066400000000000000000000100001522536370400221010ustar00rootroot00000000000000zxc-0.13.0/conformance/valid/all_zeros_4k.zxc000066400000000000000000000001411522536370400211110ustar00rootroot00000000000000.]5;?zxc-0.13.0/conformance/valid/all_zeros_64k.expected000066400000000000000000002000001522536370400221700ustar00rootroot00000000000000zxc-0.13.0/conformance/valid/all_zeros_64k.zxc000066400000000000000000000001421522536370400212000ustar00rootroot00000000000000.]6Wzxc-0.13.0/conformance/valid/dict_http.expected000066400000000000000000000040001522536370400214760ustar00rootroot00000000000000GET /api/v1/users/12345/profile HTTP/1.1 Host: api.example.com Accept: application/json Accept-Encoding: gzip, br User-Agent: zxc-client/1.0 Connection: keep-alive GET /api/v1/users/12345/profile HTTP/1.1 Host: api.example.com Accept: application/json Accept-Encoding: gzip, br User-Agent: zxc-client/1.0 Connection: keep-alive GET /api/v1/users/12345/profile HTTP/1.1 Host: api.example.com Accept: application/json Accept-Encoding: gzip, br User-Agent: zxc-client/1.0 Connection: keep-alive GET /api/v1/users/12345/profile HTTP/1.1 Host: api.example.com Accept: application/json Accept-Encoding: gzip, br User-Agent: zxc-client/1.0 Connection: keep-alive GET /api/v1/users/12345/profile HTTP/1.1 Host: api.example.com Accept: application/json Accept-Encoding: gzip, br User-Agent: zxc-client/1.0 Connection: keep-alive GET /api/v1/users/12345/profile HTTP/1.1 Host: api.example.com Accept: application/json Accept-Encoding: gzip, br User-Agent: zxc-client/1.0 Connection: keep-alive GET /api/v1/users/12345/profile HTTP/1.1 Host: api.example.com Accept: application/json Accept-Encoding: gzip, br User-Agent: zxc-client/1.0 Connection: keep-alive GET /api/v1/users/12345/profile HTTP/1.1 Host: api.example.com Accept: application/json Accept-Encoding: gzip, br User-Agent: zxc-client/1.0 Connection: keep-alive GET /api/v1/users/12345/profile HTTP/1.1 Host: api.example.com Accept: application/json Accept-Encoding: gzip, br User-Agent: zxc-client/1.0 Connection: keep-alive GET /api/v1/users/12345/profile HTTP/1.1 Host: api.example.com Accept: application/json Accept-Encoding: gzip, br User-Agent: zxc-client/1.0 Connection: keep-alive GET /api/v1/users/12345/profile HTTP/1.1 Host: api.example.com Accept: application/json Accept-Encoding: gzip, br User-Agent: zxc-client/1.0 Connection: keep-alive GET /api/v1/users/12345/profile HTTP/1.1 Host: api.example.com Accept: application/json Accept-Encoding: gzip, br User-Agent: zxc-client/1.0 Connection: keep-alive GET /api/v1/users/12345/profile HTTP/1.1 Host: api.example.com Accept: applicatizxc-0.13.0/conformance/valid/dict_http.zxc000066400000000000000000000001641522536370400205100ustar00rootroot00000000000000.wDGET345/p<_Dwzxc-0.13.0/conformance/valid/dict_http.zxd000066400000000000000000000007321522536370400205120ustar00rootroot00000000000000ѰJBs/123GET /api/v1/users/12rofile HTTP/1.1 Host: api.example.com Accept: application/json Accept-Encoding: gzip, br User-Agent: zxc-client/1.0 Connection: keep-alive GET /api/v1/userofile HTTP/1.1 Host: api.example.com Accept: application/json Accept-Encoding: gzip, br User-Agent: zxc-client/1.0 Connection: keep-alive GET /api/v1/00300zxc-0.13.0/conformance/valid/dict_seekable_l7.expected000066400000000000000000000020001522536370400226720ustar00rootroot00000000000000Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim zxc-0.13.0/conformance/valid/dict_seekable_l7.zxc000066400000000000000000000004171522536370400217070ustar00rootroot00000000000000./Z   Wd m D&I(t*l\!y ZL}t3W젛!;{@4pQ ,d%%I iK~Ep&v@z ޲~=fRO?ORtWp > PƵ PƵzxc-0.13.0/conformance/valid/dict_text.zxd000066400000000000000000000005171522536370400205200ustar00rootroot00000000000000Ѱ&Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.@WEf7TTTDzxc-0.13.0/conformance/valid/empty.expected000066400000000000000000000000001522536370400206460ustar00rootroot00000000000000zxc-0.13.0/conformance/valid/empty.zxc000066400000000000000000000000441522536370400176610ustar00rootroot00000000000000.]zxc-0.13.0/conformance/valid/glo_pivco_wide_l7.expected000066400000000000000000000400001522536370400231070ustar00rootroot00000000000000=*<" >&0G&{\T _0 l#!8_]8#%,\ k(HR' ' S. n xq  '[IW[+'EOH ))DM'l  z1*k~B W!V U  =|7_4!w #   `- l(. jd) !Co#, oT%6 eKpI h9 {[J' C,14.)F78 sU1fKD";  5-  qkv{J Ya  lB,O50 Q $ <(FiSal?/  2U]'R.)9 %YdIL?D!` "#K&26y%>@Y&f |,Bi{pw vG:E%@D#0\' x-aG +84\ :xHl#5;'.F5u0&M&Q\"^8<gȟ ? *a P,E5  3*[Wy w  ?[r^I<6 ĵ9K;HS h?mLy'cHy{9"BfTv ;8ؠ D?u8sՃ6m "J)2>T-`cZ`~/*$ SQr92 '!l L! ?FQ 9;zBL+-BJMJ9t *"7tv>SD\/// ^7 # n7 [2 )|&)$qW Gj ?gUN4L)lMneV#7M"B6=    #Tt z  n X1_ *d |uGN32U^lA XZ* d@r9/RaMd ?X )VxEg  S;"5:@1\%E&cr  !WT9>!_trI x?O#w-`-`q ^d :^0 .dsXA"@j)R~0j mkx9w-f , K.Wk--I' V:2Z#* P1β m <( ^{B]/ S P6Q)* x z"G,?^: J{c>2O'&NVOlL'"Íx n~bb"4l24883K3g0W;D']!+lk F (*P L3 W tV ]vBF:H؆,xnN' , / 9='F$~i 1Z" fiI_ ?gY8 DU4. O- 0{-ѹ QrkPm,Au^MZ-a 'W28*WAqh]%]M  5_ @(B YrZCF; ^+35 t*;;G b /FS1Lg0%`Q' NX])(I +qƋ)Uk 6 /{w%ӟp|l6, DCsl <ѯ)+ $p= B(1i .2 [5"  M*yjb z( K/+G&'z 3!:VH.;"!M i_} B1 V#Ie{wCx I0YQf'>gGFJs@E&bDDSD3q! R v>Ma %cD!1 IiBJmq%Tx0 fCE?-GB nm 6S  N# aC !oJQgF9/" 2;:e_mZ ",dE,:*Ar5z c*{p[B!?P?ZTS>}Gp2t1;bUVsm49% 3$6~,>#a,K5׿  El12H e+i) j}8M^n/:J0s* ^ %7~ $.~n vJ'ϊ1a VhgL\ '9"8><!" _"̯HhI w_ >Mk`T%iX]  Yc)/ 3 %1Q- ys2-S @g`F@*Pp 8JD ,[|)'CDD$  :O="  CBL  %.5=))H Q%2|/D$h B M&&9aB ~(+y4f F = #?U/ɈKnA;nLEN+wJt 1@C F# XK.`<ֻ6 (] +%&(W!ײw32{R,ij83YOQ#vv cBµ % J * Rfm[#R sg \[U@Y q" 0 $< H :iM2 0q 0#^+4AgKf> D(`A~tY/G TR_K8d& 7&baxg7Y$ "*NH&$-}Th/T= =@\L'&>Ay amSM53~!-Y 4{Z6F##CZ[Z_\ y SwQF)<"!m8 z   g $!)'$VH ?3 Ht fA O46, 4;YmK lDŽDfkE $ ,ce0 Q >2 !y*k(2:R/C8+,%M U9 nj¶K;_WxF XY$jLh sXqK #US`"Z0Di? B W,%z 3U-{qs37S, :;+W/9^T)   O/ h%AY ADNkX*"  CAUF!>`8m@hR*] - B/: >>3* 9c]4Z- i'JU?%Bo22]X J & \O !5K1b(E (Mŗ?&.^TpT((ehBb%h{swY>d7553Gt{C h ! 1tHD6wp1 2W 8`9S ,, Sf Jk_g<[ h16M+<2o ?b~4K4X?: )zm%'jO} ~ !3+H d>.S( !s3 ,r{u?YiA-AjhE; Ĩk&W \@2  &w4 U>7 $ %![ ^&s]}`KU ^wIZ? I 0DF^?jF%V  g ! `M""#W#~'_C[;  %xU2W>"# D=&B2;< X% @#\[&!/ a K3FE/~,$CYB DA "g;0QcIMd,8c32C(+"T$Dko_' '  xadfI @ ,9 cNJP-^  GD- 1 \"(;# t y|}ABV$^ .#+{ CL99iP( Y!BZ #S1/$D 5 z؀iԊkOW# 2; t 2NRj v#.J xB^&22$P={lA 3kJ^X ^Z #e&M89G C4ԟuoF̑q\# n 6Eb@EU* O' .t H+ v 1hT! = @O 9KqUƚ6AEP(t,R 2{(=n "nB,K $`r2 zpd< m M!h D<0: S19 e_9L,&DDsSfbV= jW9""{" ,ebD% s(* ;&-SW;/w\~+>>i %xA&65:H~ EA=QW2o=f>J8aj H-."n9 k  ~:F z[##?(<- )  >  >Eeq^T*   HgZ Z$ Lx6$ J>I% WVs Q )z*%)1 6rR6\h ,8)  t u! ! \{ ? *>  }S5?k  !lr `<%} te" $xA o5LA$0/Gf&]|{+)>3YJj`*y 9i  V. ;8D);#l/~4   rB!"˯   UVc<B Y4$?f  L<(V'ig({H5*N1\ ~W s/e, [<  Ft|v$0 -F J:1@\X+m/%KrUr<)a0]*IQ3 d,JFR*?t"MZ5 i )n /m6s J EvY DVF :#SA6QS (,:3  _ lUF%SfJKC(- , c !@hjN D U0m) 5 PoT C [r <1* X ee]ȩHqFp6DfIY'/1h#Y4ca 3$ +I 7$$ ZA_A% 8kxL~u2Lj*QV$M cs3'a)%`_' 2e?v<Y  N 5uD 0lor1O((E7sB:`uإ#y XxJ l{*03)a ]G'Q&tuʳI!HLP:f e!=f%wS" S ( ;<۝%9W" "/$ۡmp>f56)i  LM#Wh#Nv5)Nc{J#z)4 .TI"TQUQrHm?YzF.OP#I$g (#u!NLmJ.oV4xhi? HE =$C# \Esa JOP  $ <I'5p K J7 8 CW:]LEB ) 2B<&1_:f3W+~Pn+6#68 i;)n"f-h7'5A-_/3 C M. }lDhS O73%B1a "6 >*PE QPt_bm@*.a. n9(5(Tt# P +Zk=.&`&b ^3  5 +m4ڙC>Ox G,`,{e wAG,SB'Lf9 0) M'VM2+  V~Q b kQ }`F/ O* (As R\<o'Y?;/ĿF"/L] 2`r%i'CjAi  +PZ^r +>J )CTŒ z $h 2 H +"nXEzmr 0G!  aqFC47'ϭk} T|\%)V6! 7>i3GHD!Wb^)P <0+ c!A!3:,%..A 2Hz^KEX):&^`$7'f 7t"D!" ! % 41(&#AMc`d 7ʛY-yhy(1t ):9&9vCvosxxg-h6W3L w$!E&L :CO  !3];8n$ T%AK6/m}L% ee .)uip K>9Y.MN"2&w{uSW N[#C*Id+@#.zVb YX}?\;2} q! 3rQ':'E_iBN8Ns,  "yJfQ d7$yK13(DF'iVbij. A& p ?o~xu*( /( Z  #'5)׌4eA)B)-%L$Qw0> *D|'hO_G+E 8fPw"+LIH~ BC QUe,ue@1L 3Cc `( D3fc=N-+!"}0Eٛb-! Nu,)!e6- !PJjJ5OHCCD #`v~yj 4KڪFg9 ]2sSj'5G}}r/D?[] Qr0#C%62t+x>R='ro=vK2k  V\(NeGV3\4?q V> E"Q &<8EBfESFN ѬMS& CGl  -) . LG!) fsI! 8" <, N r`?QE1 G( c)D,0Gd?VM. QG$_ Q!b 22 F+  ?,oF3$x   cjJ i  * g K:h=7ڂ  (=?r 8i>7 &9MdFo:ZRW!y(sW4X LqD6,0 ts E'a# <%4 K q9 @  J e< J !S0"~V9 : "a ny-209 _ w?ZU_ '+Y"P#@ ,t-19.BxR)'-w PE~\D l%2 R ~ -h e -? ( p tAp(R5E:`Q- 2V"}[Jn _BjOZ%dAN LpFS/>[  [ f  <S:Mq6.8$u8R-GpQ A#)A  MKA. }k D4AoՇG) K$2zaVD.y 5P>?r;`>JX#?ow zkNX"C \4x:uHurMQ:S!3o \s&3$n0l4NJm $I3O!.xZ4 YB +[ K  @3==B+t2/Bk =`2 `N>P"|E51o  8 H @m E1  _ dAvGO:3b#;=3drp}M q L-@)!t:=u?+ 0"F w6 "Z>' |F\ *+նO<$eGHU6u7f c G: =/ @=F 8.zJg.S^Dz/<0'+/=wy M -p30K +`d0b +? 1 b+;|Z? _U|<vM7!.1c Ql,q %q(#-9&E<Jn? pi /SIk .'0/S4O{ U8enRBWKL,{'($<0WkB*\TR%0', W q:mk*IC8; 1 7<] SX('V Kg iE6L8 @46_3    8r 6-SmU&M9( !BRLH{ -"k 3?3p;/sJ1\G@}(P X 2p;\$s}A6`]?"/U , /u. 0 F"13 - XH|)"!i:E (>0- Ck3 - .e7"_%D&+-V.59pA U/E8xm #FDBS!w  H K L B"yhC0JeB $ ER; $S!}fLF@x FY>$ D7,d)34!7 L=  5S ˠ.F!OE L5(4H E'@q! cC/ 6a:0CL| 0f-^I YemP2b S _"B(8>=z"-=h2ZD &? F/'^iB ^$/@~7+`Yt?:G*%Y )G f7r (+R/btH b98$0\p@ ".47nTP\G/Ϳ+! P'*SM G:[]>MM1F @[@pZI,h]XV5:3RL}K-^ H s2L $./*f * )!$L1#%]"no=RI u0 #K1^X=U?h}2& V @J<;"})pG uy~3ji H  gHWY1+Ec4hX8fr WEwvS T%  wASt9t !x?!}AB XJ@~1 Dt8@n& Dg>Q:2g <g)|4'sG  Ts& ;1z v| gq/<=T8J X_DM1{% >@j-~$) %; 2%2 LF./z- (E;f <Dh-%% p1_:&Lxdz #$* ?G,&< i zxc-0.13.0/conformance/valid/glo_pivco_wide_l7.zxc000066400000000000000000000310721522536370400221230ustar00rootroot00000000000000.]2 :>0>::ttWWRUfffvwwwwwwwxL`y(WuhFH\M7Bll( #υAjT)-wpQV-~B9?ȑҧb{)z)P^.:<@B:EX@,=_6(/."۟tKHxR=cJtK$cʸq *;.”:,B"}zI[g*Y. ~VMмnQO%` A>97<ئ4TvozRUfPζ I)v4陫RՇF>qgž'i8R+=x1%fFtMDDW"p'JQRN?w@kB[Q=ЧbBLLf4E cLy3sH"ZbD>sH})@J ؉Y >P'Hx?FN8E95 9(d+GS OAH/YP*pdڼW-n+m(MA(u 0DU9weFxb@y:'pRhr=G0nk$nF-4M [Ny|Ы wb]s֦-\,*"0<0"LR h!=wQARFJGLjHS$~nq-M\lzLTGP+2^Pc$&DЏfkg19d#gyK~AHw)X=@7B.S_-Fdqϑ-{x v:}'K\u[7yj*˓H9F2n0X߄@$K`E)Q4ڛhcI1E?\e30Qc%5݆eR?pD >^C^8k%u4< - ;!Ǒxxԏ8"t/mq@߬Ţfsb Yby6#$]dU/ zz r#_َ?BW_BlH9s-8 wљPIʸF R-Ny 3$BDT8l:!SDg-#u )9@WO3V,MYwP0?0RiEޅK@0qO;{0-e?ĈjƮd~H-g7ka7"?fЎ2bҠ/?hQ%vU6zbL s䣑~r'˕41k(R]QȘb[*UEp 'd1=^TK2B@ӷ'S$,̸a N(:nZtJQF}IbMև*RR 1 )yq|sxN%_jN h;1:VdrNl55=%+k{\j[zNh=KLXg t yU+Z3-"IoA*ZCд #"6C{ - 2W-Q+o$C_*ZNv% #:KJrD:iuM({YG6(ᴽ?ta}EqC*YӦ3֩%ӫ(}Λ/$>ȴ!kYyo?GsLWO,KSBT 玣$Dpp9[dP[l' )h'BᐚnDQ;U2ӯdNUmR;,qL $LMFDT=gKV~I5sռhu2e.9jM CԪf>J1rs' \.j: 3);Ԗy__]{1.`/uZB*XZ*!nhkEv. .C҈L^ E|-%qÈҵ׊DhSreI1 ]dW]%7Ƕq>`^VrŨ 1WTh1<*3@*+XnIcl B}-[8 nʐnaAoJTK1N/jjbsK.4{@=W*8I&5+}\DVof;7FS#JfOtYf<. 8 >\ f.wLDqIEXt-)b-i\ٽY='EgY>޿kzo1'aB>n͸5>,b,.ZsMbIR#<0\vM>vԹш>ۜ߀kHWS,BE.;sͲtsX]EfR9P|ҵ‡ѳcLʌsvSw?r"t|)3/T=\њ[{ 4/m^vw ڬ%SD&aƯGlU98 r[-R?Z}xP,|fQ6`ӄ߯|f? }W(}@q`c0x$ڎ-`UDnj4<©ǑQƍB*Xl汹/7`_;%\eukƽ}Y]N.҈83u7q27'}"%̓`$+nK ;hD["'pGX`MLw9_Yp(S+ީ#q+C~VrqW JI_B":zi(@\g087q;0 r|f{qMx5U@9`5QNǾƺY7LtҜؔrP _Oى(h1޻k1SUμ44 rl2ć ^![E,T,O\CvRpmag[?F7ߞ/%{΃}Ni6iVS^@+H^0+tn}Z,xIg,!+`xb^&-kE7$|YLD07dAq($ ذĒܹ!VyŁܟX5I+s$" (a1"/> D_47g|RVf5n s$yw @0%jt"ǥe"bsH@_U)F!eJZÈѢ(9(ܕ%c5{_Scr 79`beeve-'Jz'w xUcf{?SB 6!. q;:xWπڥVŃr%83I>墊*(ݍ{*. U ;}zKbWB0=C:w_ ẍX%d`짃>K¤\E=6 .5h1!= RрEm>4p13UVUC<C&I5}[)BEtO=Q<d493u|\_#kO:n2!v>8o0 :M(pm+`㦽䗆2)BimgI+>a9ærIǧ|I]RJ5te;7qXE0 crYK>4!X 8A&#<%shy2|_A(ҳ5 iaA^my`mֲu܍;QftGFk28cbi57palāixDd. 几vg?0l{fL3Pj,,e*#/2!SD_<4ڨШ;ڄ+bZKZZuwT:M]`\gK6Y`DtAwqr#Z.goc} ;ߛ@Au}#f`3Vd xK4T8u_kYTJw+y>E g9h ђyISY"CҿNdϛE7Xi-#=,r:vO=+н0QNRCΥb١}%CT`2/GG lY 4t{&ľڻpQĄft4Nɽ4m/p€IhX=m:Pt:Jtdۉ 8,js=͹N4] Jc0/_ k[_Sk̡HB-TOBy6,bF):!p:r;7$/B%\m6S*+9LNM6qjT_!\bSbBp)h kf1Q~#ib f\Z@"^ =mReCyI3N8byus?߿wg0US# uޡ2$#fEϿ-ޝ}9>}y=k;pcco1?ǽ0/G 륷xIWʀfθX@-w ' \6h[DӬ:3Eoj`랩Z^8Ǫi礍YcB\̦j-qx^H< Z6c'ǡ'sLg_#uHVp0;VMSH'g) Cz>2FPDіKKqE};=ƺ|u[yfӇz?W[j\3"t^%yTHs +} DFRT;⵷0KVGHC.jt=nY%c~w]~4Nƕ46C^VeKo{@K]rY}=׹OYM,!mmǤg>P"zBAޜlG ޼ !H*pY:hu&Ig-BJڗI~J#5/@:z}E`WJz`&(ya^n!K=ùPӉC@ՅQL@Wop^7JSLM0C-yxUs%FBvaD \1$]_oPun%^ٹcT*|:G.l!kxt$cX' S7޿31%lc6DL["PV<4i0Myl!YWc"4 C;!*[xLw/v:#ͧ*-pxY)Ϩk xc12$8EJ>Pyea}Os|kxуHje#V./%Nu:E N>Hf9zmo%bԹVx_*, ߒ'ev:1>Vڠ98*Òv{jT[RBI'1yx?aIE%eȵYT-yb %h9>8JFKD ̀|ÁBU>Ț='jĽ4AN/ $kZ1IV dJ{Y,b(&p@{To b%_.Z+PY^A[7yO Jɳf"S *l\b =riޝ2Qm yșd &sCx *qc>Vg'4Sɭ! 0 @0dnÞ99w:b_%jTCBewvh?/jߙA8Q@,;z1-{i* C;ixM]G߽hTPuh nG]9}% 7랠,.NE 'JlRB2F qaU' ~ФC+ū向ژך?&oik7.'S ]յ0JK'J#pFة ڏm@vUC+(OmZ)j /8=;G"IcEJko*(x)Dq4,+/Atڠ.xOMBAlQ$v;u8;uf1cύ5a^P{ҫ=_/ď^?5+wǺpa@'l1 >΄=਻:Q6QH %B =S%LVYOwLiX̙V7GMzmUK= ?(5 }jA,*8*8iW<'ʁ^9XV4܎,Tj\%eq8}fL:J.kM:۷`s5D79V A|%5? NMg?7IAA,Kۆ1mK#a3}5w)(MV5wB%H/  ~XV-Bv?{z[nTޅ'm@-3{0s>|s@^Fԣm%uꟵf \/څAhekTm(7qF%ړ9򳬉{ئcPC~t2guܷ_W62â w۵ŏ n M+׋Iss(gf)UA[Bֵi?;▥@?:yLAh5V1FGaW\jPڤP,@'~JJ 0 mLjP o;M{RX,Y+ups 猨 )j6[^P$ަjs: {p w$\Y X7?zVmťr u:BrbƩC'ld-pn6l(H2vH :ب $_:\;}y'.7XE@`;!e\=vPVa|" s}aVJg}M%Aֶ4E H놖&aʽDS0NcP髷q>U#25mYK^,'4-y7CQ%q=Z0qWA*.cQd\%.Ƶ_p&uu A  6j !"X f#0?,)G,- B,0n 5Y X!s"@$XA pO) (v  ##G-|6 j[# X@zxc-0.13.0/conformance/valid/long_match_64k.expected000066400000000000000000002000001522536370400223110ustar00rootroot00000000000000#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elszzxc-0.13.0/conformance/valid/long_match_64k.zxc000066400000000000000000000002421522536370400213220ustar00rootroot00000000000000.]vG@@@#*18?FMT[bipw~ &-4;BIPW^elsz?1zxc-0.13.0/conformance/valid/long_match_checksum.expected000066400000000000000000002000001522536370400235070ustar00rootroot00000000000000#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elsz#*18?FMT[bipw~ &-4;BIPW^elszzxc-0.13.0/conformance/valid/long_match_checksum.zxc000066400000000000000000000002461522536370400225240ustar00rootroot00000000000000.>]vG@@@#*18?FMT[bipw~ &-4;BIPW^elsz?1 j jzxc-0.13.0/conformance/valid/max_offset_128k.expected000066400000000000000000004000001522536370400224140ustar00rootroot00000000000000  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~zxc-0.13.0/conformance/valid/max_offset_128k.zxc000066400000000000000000000007741522536370400214350ustar00rootroot00000000000000.]w   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~lrlklzxc-0.13.0/conformance/valid/multiblock_mixed.expected000066400000000000000000002000001522536370400230450ustar00rootroot00000000000000  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~zxc-0.13.0/conformance/valid/multiblock_mixed.zxc000066400000000000000000000120041522536370400220550ustar00rootroot00000000000000. ?6  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~;6  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~;6  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~;6  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~;6  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~;6  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~;6  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~;6  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~;6  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~;6  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~;6  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~;6  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~;6  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~;6  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~;6  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~;6  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~;zxc-0.13.0/conformance/valid/multiblock_text.expected000066400000000000000000002000001522536370400227230ustar00rootroot00000000000000Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do zxc-0.13.0/conformance/valid/multiblock_text.zxc000066400000000000000000000077651522536370400217550ustar00rootroot00000000000000. ?gLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. <geiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do <nim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore ete magna aliqua. Ut eZ<gLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. <geiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do <nim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore ete magna aliqua. Ut eZ<gLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. <geiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do <nim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore ete magna aliqua. Ut eZ<gLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. <geiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do <nim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore ete magna aliqua. Ut eZ<gLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. <geiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do <nim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore ete magna aliqua. Ut eZ<gLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. <zxc-0.13.0/conformance/valid/one_byte.expected000066400000000000000000000000011522536370400213150ustar00rootroot00000000000000Bzxc-0.13.0/conformance/valid/one_byte.zxc000066400000000000000000000000551522536370400203310ustar00rootroot00000000000000.]'Bzxc-0.13.0/conformance/valid/random_256.expected000066400000000000000000000004001522536370400213700ustar00rootroot00000000000000B֟*m &njD'vl *46+f$RM UzH3J88:d2U=kDT'3 vwF3u?i'%1\>gE>ॽdwzZ*g8ࠫ܁qE)&nMb'p'k5k_s[u!6( ߁ŲFrȶaw-&eM:MoD8?j%L *²]_Q}d^)u7 NwժwJTD( Gh)B +eғU#M)aOW$1v2 I;AÁ@FՅ=Yj5&"{EDU@NF#vfIg W#{n)X;J;OFU6yYfϐ WsPkԎBwo1;w!ݰ},"ȍd*ܧz 35WѴ܉(]@&LYvp{ BjύͼMGHPLs[+2, 4rwG x+s(~DٓI %,ȱK[6$l X\F z|=UByTԡl8U[&$MK cAO"ϫfċzఓ}ꯨ@}%jB--mMv))0kpBy6/|SS:fG'?V#*f*m2qdZe5%'[h\Js!Ekz}z-m,x@d_lҡ|pahSS]-u1Y4ܖ HCSd!EhT<|V7F{S+5sHܲKt_ZUV|-&>&**W/BR\MHSPՇꊡ˂;6zMy07fEXja:x<: cJΰ*wbJa٢'c"2d'Ij/JgK`}]@l;z/dm?ݭ$l o9eǯJX*)+8|u5xvQưF`BZftFm{@d :?,hJF=jc}LEŏ\<>X l_ a,Rsx3lX 5d5U;!!X 2cxl f_>~-@R-a WK@uBBL#X+^J!2>umMy؋eo'K}ֹ_xO F|@p/73Vt xCǔ]Dͧ߯SS2NJELX7#Ma%\nYsG%RtXx7A%1ʬ\7Ig\b}o(l4e4҈$v9Sx){^ z $e,zixBv˽6yPh6{{V3 exHŬkn;Nr3;l}]OVO gӱq_ùIֆW}c13m# 3H 戔EPxBƑQ/JٵvtO/gP~ E/=kg 6  Sn'_:݊sr- It#+#AD祆TiltTxbxڛ@X|A.-fH' kgޠ 1E[Nry,B]-ʼnSMӂ$dl-6j5 1)qV7uC%J)[l0AՆS;3?Fvys2qY6VQLpHM;y=f ^5VqV~6؆e^E&nN{a}##-R'G{{CC`Qyy(wW%EzS9%dރ y3p5-sMOC{iE..~g GZ~uns&Nx~G30շGq v-R)b#3 ƎWp8@^P8!vx'_;4._?AfG gy~8Ϥ(vC@GYit,yIb^4n3qFÖKlEP/<.bFJI=_Ed&M|{ml5Oc$9{Ee+%1kBԪDAۚ~QsavH;!3V` c e"a)GTkpKՔ$x<^u?t''jPyġap^̗?F@CGhpkzʽVQNϔ?0@:2wƺ9l%XL_[^nbɔ˂A;wvu 74-._{MD_J$Ķ᥶YZ?$?ߗp( p =X|lWߟ\ߛ߻@ѱg$p? K'Sg\^#aRꍾ䂆/\EtHx\.HfB ݟX^6l1IK H7Lt¤FdeɨK%8R"ҁ1Bhxmudߥ(az +`z3ܴ+OQC:39&i =\^ iVI ˪ݙП]PʹP6 <)Uv#>.R8 U}r.9ŃA(Bcbe[n F rB(-Rg=W t%.m9x< JrPfu 5wz =mMg/dP4߬p72c>g|T6bNb[ XѤ}] @:rߢ?ۼe&F+ɷ6ܗAu '~Ckip!v )Ye]5#"8 3q /{Q|t49O(Pq#ʧU%MBOl+e ƽj u)eٴ![kHь*ƨ!XxTCSOխ#ڥpT೯QCIC$Totn|] Sٮ22Dv~V׊Jny!Ś}"z~/V%s2{ bգQZy#޼+`6: [ B;l$iL#Ll"+2Նd˼]Ma3*/BWɊ3dͥq&.[zO]Ƚ D'oy3d_c'Cg8O}XZJe>V]ףE/ nC9XC |e1?X,9XGb/7H(׵yJ_x~?zxc-0.13.0/conformance/valid/random_4k.zxc000066400000000000000000000100541522536370400204030ustar00rootroot00000000000000.]9 b_X=s#*:6t%Ae7}ڣazSSr1~(ۏG 1g݉Nvm.>U=kDT'3 vwF3u?i'%1\>gE>ॽdwzZ*g8ࠫ܁qE)&nMb'p'k5k_s[u!6( ߁ŲFrȶaw-&eM:MoD8?j%L *²]_Q}d^)u7 NwժwJTD( Gh)B +eғU#M)aOW$1v2 I;AÁ@FՅ=Yj5&"{EDU@NF#vfIg W#{n)X;J;OFU6yYfϐ WsPkԎBwo1;w!ݰ},"ȍd*ܧz 35WѴ܉(]@&LYvp{ BjύͼMGHPLs[+2, 4rwG x+s(~DٓI %,ȱK[6$l X\F z|=UByTԡl8U[&$MK cAO"ϫfċzఓ}ꯨ@}%jB--mMv))0kpBy6/|SS:fG'?V#*f*m2qdZe5%'[h\Js!Ekz}z-m,x@d_lҡ|pahSS]-u1Y4ܖ HCSd!EhT<|V7F{S+5sHܲKt_ZUV|-&>&**W/BR\MHSPՇꊡ˂;6zMy07fEXja:x<: cJΰ*wbJa٢'c"2d'Ij/JgK`}]@l;z/dm?ݭ$l o9eǯJX*)+8|u5xvQưF`BZftFm{@d :?,hJF=jc}LEŏ\<>X l_ a,Rsx3lX 5d5U;!!X 2cxl f_>~-@R-a WK@uBBL#X+^J!2>umMy؋eo'K}ֹ_xO F|@p/73Vt xCǔ]Dͧ߯SS2NJELX7#Ma%\nYsG%RtXx7A%1ʬ\7Ig\b}o(l4e4҈$v9Sx){^ z $e,zixBv˽6yPh6{{V3 exHŬkn;Nr3;l}]OVO gӱq_ùIֆW}c13m# 3H 戔EPxBƑQ/JٵvtO/gP~ E/=kg 6  Sn'_:݊sr- It#+#AD祆TiltTxbxڛ@X|A.-fH' kgޠ 1E[Nry,B]-ʼnSMӂ$dl-6j5 1)qV7uC%J)[l0AՆS;3?Fvys2qY6VQLpHM;y=f ^5VqV~6؆e^E&nN{a}##-R'G{{CC`Qyy(wW%EzS9%dރ y3p5-sMOC{iE..~g GZ~uns&Nx~G30շGq v-R)b#3 ƎWp8@^P8!vx'_;4._?AfG gy~8Ϥ(vC@GYit,yIb^4n3qFÖKlEP/<.bFJI=_Ed&M|{ml5Oc$9{Ee+%1kBԪDAۚ~QsavH;!3V` c e"a)GTkpKՔ$x<^u?t''jPyġap^̗?F@CGhpkzʽVQNϔ?0@:2wƺ9l%XL_[^nbɔ˂A;wvu 74-._{MD_J$Ķ᥶YZ?$?ߗp( p =X|lWߟ\ߛ߻@ѱg$p? K'Sg\^#aRꍾ䂆/\EtHx\.HfB ݟX^6l1IK H7Lt¤FdeɨK%8R"ҁ1Bhxmudߥ(az +`z3ܴ+OQC:39&i =\^ iVI ˪ݙП]PʹP6 <)Uv#>.R8 U}r.9ŃA(Bcbe[n F rB(-Rg=W t%.m9x< JrPfu 5wz =mMg/dP4߬p72c>g|T6bNb[ XѤ}] @:rߢ?ۼe&F+ɷ6ܗAu '~Ckip!v )Ye]5#"8 3q /{Q|t49O(Pq#ʧU%MBOl+e ƽj u)eٴ![kHь*ƨ!XxTCSOխ#ڥpT೯QCIC$Totn|] Sٮ22Dv~V׊Jny!Ś}"z~/V%s2{ bգQZy#޼+`6: [ B;l$iL#Ll"+2Նd˼]Ma3*/BWɊ3dͥq&.[zO]Ƚ D'oy3d_c'Cg8O}XZJe>V]ףE/ nC9XC |e1?X,9XGb/7H(׵yJ_x~?zxc-0.13.0/conformance/valid/random_4k_checksum.expected000066400000000000000000000100001522536370400232510ustar00rootroot00000000000000m粭'nu։ΏJ4q\!N˺.JX2ܶi!'nO-q~7l\Rf˷'QkDW@`ͥYT~PC$˅+O{ek El oi Yc_/oл}lG8iN)~"K187ߣp ʞj^EMפAX, +RcŻllzRv^"B/]$Ɛ=dxn SF" lPoh }6g+ʐoN u. &Ǚrß_P)Enj$9w̢EqJs#l\@>8R")+6XC^u*欨o)J_jjDX(&slԏ 1thR_E4) NI'Cvۃ\<"lT GUiuo}%`TJ]vH$ӄPqMC9a=?5"@LM0hIHJ|E8^z4[jG^zً!t˾ ӓP&?a^2:6 ~v:%+\nWUM܂ .iMl^7qwm+ڥh0D`5̈́5}e3Tx0CW9hf aA l*M~:e&Ẹd4yI$jȥ_GR)a[4V_ͣ/Q\x*-1#zpyynU0S~INhkKT ]Y8&> ^hhV=泾u=(QLr>l2 Eb@S!xB, 4NbR*}<\4:( S(pVS% FhwoGR}]][1M| edjE3`s_cBdJ@".,Ow%yx! vH>\\FsL^\u +hF#}TݟdP˷ aᇻ ^V%r7~OT|k .20>z%~5tZkފn Y ACCi}R B+}"8K_?٘1W,`]bB8(NS|0v 0rv[ XN_a=bߑTrI6dsmm.W%S3g>#9'ʖ{|TQ GO31TJxlSh[l&EǃO8,#p l:Dd\G;ʗ BN e}@*Mƌa)ʥ6d\ӯ&'r"n;W3M%6ԵK{YG?|()]mb0Iǩ'rk QĴÕ'6qNjn2!܌;\ַ}}'D<-n=4+no%#9krMm@x(uU:ۨj2[CVUO"a35 8$S)0CkqzoЄhs笞TGܢχ̕Pr~*rB_ b MwMqV;H R=d /MI J{v =kUg #,;a  Q?JF|6҇ ݅bZd:A$j{(]TQ&Ws\}=&H p\nr$c.a{yvJMdFô ,OF&,:a6 z.aӮS%u8:a?ʡbR~WqKav[B'/ib{}K5mBన_JdTG'#J\{J4BR/6# aל^-pޞKH{;M 6? C 7F#+#e9 NvGVR_|GR*!RD|H貘B](mXUX4H^haU8Ott7Mй !i^R2Tm,%mkMk3yv)ƙǡĪC.r\Z#v VEyEuQOB]j !8ӉRS'14|6", Hm2TCMW+ƾv>L_Xscp,+%loQp7fͯl،T92b?f-椨};E<+}Ъ ({R8CX8ztLpSmO1/e$L2X79(pv`H^otYb6+|e"oZP^sjq.1V/ZrBcjS O}fj~ 瀮3ĕs8Iygŝ8"JK;:?&Ò2HtZ>羣&ؾ^l~- ]nb|X|?FUsre%m#`woU%T?pq$y5 H湴&cy0)<KȂ!&| S&'? $z" v+\k"h#qHMbHG[gx(.0y~}H,4;Uenex]LPWٮ!o8$EE>Trˏ:<#1nG"ء`!O]=,plVZ39If8|xԣx_M1~:L@<xh?΅h 2-F$ĩV~sΣ¶5ޥ^2Ԏ6LcnlQVw# 8 V@&%w'hwPۇJdK>1輺҇ܚ$dll-.7@q{@%ycV?,"E3!a=3p/sSzk60MZGxQ>+.ԠK߾sl rR}mJTܰ}ugp0>Ke0m V=,"j%\DBa8H{HU R"HaTkk fV_Du`+TeM}Ȓ3AODf{ t f{*һWď2{",1+իPzxc-0.13.0/conformance/valid/random_4k_checksum.zxc000066400000000000000000000100601522536370400222620ustar00rootroot00000000000000.>]m粭'nu։ΏJ4q\!N˺.JX2ܶi!'nO-q~7l\Rf˷'QkDW@`ͥYT~PC$˅+O{ek El oi Yc_/oл}lG8iN)~"K187ߣp ʞj^EMפAX, +RcŻllzRv^"B/]$Ɛ=dxn SF" lPoh }6g+ʐoN u. &Ǚrß_P)Enj$9w̢EqJs#l\@>8R")+6XC^u*欨o)J_jjDX(&slԏ 1thR_E4) NI'Cvۃ\<"lT GUiuo}%`TJ]vH$ӄPqMC9a=?5"@LM0hIHJ|E8^z4[jG^zً!t˾ ӓP&?a^2:6 ~v:%+\nWUM܂ .iMl^7qwm+ڥh0D`5̈́5}e3Tx0CW9hf aA l*M~:e&Ẹd4yI$jȥ_GR)a[4V_ͣ/Q\x*-1#zpyynU0S~INhkKT ]Y8&> ^hhV=泾u=(QLr>l2 Eb@S!xB, 4NbR*}<\4:( S(pVS% FhwoGR}]][1M| edjE3`s_cBdJ@".,Ow%yx! vH>\\FsL^\u +hF#}TݟdP˷ aᇻ ^V%r7~OT|k .20>z%~5tZkފn Y ACCi}R B+}"8K_?٘1W,`]bB8(NS|0v 0rv[ XN_a=bߑTrI6dsmm.W%S3g>#9'ʖ{|TQ GO31TJxlSh[l&EǃO8,#p l:Dd\G;ʗ BN e}@*Mƌa)ʥ6d\ӯ&'r"n;W3M%6ԵK{YG?|()]mb0Iǩ'rk QĴÕ'6qNjn2!܌;\ַ}}'D<-n=4+no%#9krMm@x(uU:ۨj2[CVUO"a35 8$S)0CkqzoЄhs笞TGܢχ̕Pr~*rB_ b MwMqV;H R=d /MI J{v =kUg #,;a  Q?JF|6҇ ݅bZd:A$j{(]TQ&Ws\}=&H p\nr$c.a{yvJMdFô ,OF&,:a6 z.aӮS%u8:a?ʡbR~WqKav[B'/ib{}K5mBన_JdTG'#J\{J4BR/6# aל^-pޞKH{;M 6? C 7F#+#e9 NvGVR_|GR*!RD|H貘B](mXUX4H^haU8Ott7Mй !i^R2Tm,%mkMk3yv)ƙǡĪC.r\Z#v VEyEuQOB]j !8ӉRS'14|6", Hm2TCMW+ƾv>L_Xscp,+%loQp7fͯl،T92b?f-椨};E<+}Ъ ({R8CX8ztLpSmO1/e$L2X79(pv`H^otYb6+|e"oZP^sjq.1V/ZrBcjS O}fj~ 瀮3ĕs8Iygŝ8"JK;:?&Ò2HtZ>羣&ؾ^l~- ]nb|X|?FUsre%m#`woU%T?pq$y5 H湴&cy0)<KȂ!&| S&'? $z" v+\k"h#qHMbHG[gx(.0y~}H,4;Uenex]LPWٮ!o8$EE>Trˏ:<#1nG"ء`!O]=,plVZ39If8|xԣx_M1~:L@<xh?΅h 2-F$ĩV~sΣ¶5ޥ^2Ԏ6LcnlQVw# 8 V@&%w'hwPۇJdK>1輺҇ܚ$dll-.7@q{@%ycV?,"E3!a=3p/sSzk60MZGxQ>+.ԠK߾sl rR}mJTܰ}ugp0>Ke0m V=,"j%\DBa8H{HU R"HaTkk fV_Du`+TeM}Ȓ3AODf{ t f{*һWď2{",1+իP++zxc-0.13.0/conformance/valid/random_64k.expected000066400000000000000000002000001522536370400214560ustar00rootroot00000000000000%1>$̇ &KU7\n?\I`o{Y/c$`twjh^Ub[dL_K#|0#ml#vz HcQ.+{ -Mv#-u#~{-1R?ihU3 }L)S`uR\4>v | *Q0ūAt|%Ht횄o8٢ecy1([ #U @N87*rWQ[7{:3z@l' T@$YcekrP9n%]0捬z:;)TWM%u# qA+h:",&]8ZԻNGghZK`VyetS!y9Ct$sIA"͍[dEJ|݃l!A2邠k9Muhx KIa o=J{7DT2 WYs|ߦq ӁϿ5ƛs=`q|S\.RX2 keFth]TL]AՏ"\P`HX\c!UΙ.Od(#ߐZ!oM*9"B?.F149NܼaZ#+S2hFjX˫2gOt(>wj[bZlu.+jx/=s(qPfb*BoC".E}xkMŸw`{-au(R?ǀ$-is?z:!4%~uOv8KPє؀TUj4+nL#JbB_GJWkv>{1,aWc"h"fw|蟢o.'~@YvUZkǸ.{GٞmcY!{Y)AWTuo/+K $ybdK:?:8nУlMA/fZ=E6m^ѿW4Vm"$!h2 {\W*‹hSmw5JU@kt9^dX,m:uiMҭ,zi5s&v{" uLYR;j =/ꜽF-)`kS7[{xqHbl]*&T\Frϸ|V[Fitc>d6 WYog@ϐ8[Q̉VW-:?X tp`TdA GSI݁Nx(X !["K`Q/GTÞz }P]'OWH'eca0 *"mTDk KW_3ڻ]$W :FN&Mt9O6 VR] tL1t גř5GD9%#G6ljk% jPb /UoK'C].9 Q|80/8 )s6,WGe=e,nXl4ؠDRī܆͖x)NFB\ep%`L=P2까^T]*X[ilSy sYCf 6Q'ƹ3΀?<6аե~D_t(gȞt9f}n̕>wVsI#LIL6 4'Qm~IԴкQu@VH"b:Fٯf԰+8u m*]pQ̐KH6`~ȕzX )^A2fmJ.`%"HN@|_17QLj6h2sʣNvw>gU}ԟ\ 9i/2C3q].PZN 7(~^$#^y=^cRv@XoYab~};m]R|~r ޚ7J|;5a;<ὲvLF4 Cl;`J--PqM6o Bvd18='T|x JX+[2~\[e!5}8.I~cZ^0R#wB޺%GnZ h ywNO`=[w;Ů*dDg uBE%&ƍc#dT_3[l;5ybmd' ziSHUs74dj|mQKKh`+ ƿ刲r=She ^֯q>R-L8O¯ou⌇b8$Y RVsRJ~'Ft#GͱFnMW! XY5*a8'l+sFj@&RC]cVSg($+VA<<% GC|\M | maz}}hdsJ_7p3`g- 1`Ud +#qIb F^Td u6  @82U?`>/"Ms,?߈CDCNZ&/g]7[:d,hOVmgBG45iߵ [j!a Hh}+(T fIдHUmg3$SyBj# ZvlvP2gm`` e4ɖB-YwrYXp}W7R)t\g4t%顋^4|L@1cd(qOw>j}vM8)PIaci( ֢2"S9ko]9^>An.T (LSp/TֹYpT x 0LnubK?r#Cy菙y-{ t%t` v|m?%Ic0jVN$FB-ݽ=W$Wu'uؼA/^xػ+ՆJk3;1)6g\po3-pl@C`"ch67:Ӹc`h5]?W\W",{P7^ ݜ.9 _2y0 roh.>bOTSP,Rj6!"ִTsYl6QJ|+qk8{=3`@ BkGIkqkҜ"P̩+;6,7GKy;HM9=oQ_$tmtQm1Iq$X2C~l1`#@d1͘sKh8a{p=IGA<:憠13&Rw>vVUog Gƞh2 k!{t1Һ];t@ɢsud(!{mRpnAq=MT"ڢiD"ױ7€{&6z9`9H^.9hP>["]inuZ(p1E߂0w9ttSJ6YBU@<;.%NP|ؑB<^⟅l7$[rUxmȡ'h[e%GJm% Ob {E.Q*iVP#W#{ܥs8Gv G?cՆ 6y{riN%j~g7cKP^t[i.}2uƥ0/ځ*8KR(jQOiXyA'~W A[ٹ4`XzR V@mJΗ 8mTıoTJLOXNlvtGZf@a7x=8?qfO=5*wkХ؁Ena,;:,@*Nx4GJByKzŘ@Dvv7Y#MML_"߀7ovdwqDP2 H~Zܤ@eW"/ك)%^O hCڭ_g"M\36\ʃMRpו&75 b+f4UݣR~4dAb>08q /`z˗mtkᙏ'/#X|AXc%ODfBbcʕ 爱Ňn8ד!GVMW  G4;w· i,KűI &FTqF~kP=j&eEV'@ހp|5lSP]Kf:b]vg^"+ H/42`YL d_>RjF1/E’Rm,0y:~tȐ릎k1`׾ ᓼbc "'Zw#K(n_l`wāg8XgJMbȠsӇ92Ss[H7Θߞ1yv#_ɬqt!BS0ʽ19w<3/~Cr')j6&Vhfmϖ:v=:OD6~R*o3q\"~3͵͎ R'u9=2(Yf]J!_-+5*LQ8 \ J贰k&VA:Nh;/#r)TEu/)wTF}2v b%;Lfj2)R{;~j!;P晦&*9J( )YHX;!MmguRH2/ZGiLCB-yJsx d6z Ͱrd ~9&yּf [wf[*0jAhZ5ʢ$QLN,9UKKG43Ybr˼8dDP=FNbZE׽-]iYRh}䒃;][)unӹ&ǜmR =N3d +?$֜Iwf[F鰭ѯˌ. Y[$&lm(R8\N`; OiygC| U':zZ11}VRb16ڂ*##-7bGQh&^B~r>ם~tS4,̜ ^(H؊8C-ˮ-> \7!f_ [ {@ |9 r :̵ܐI]PW-lZzsdbrV\M09;OJ`C/nDD .\ e}ߏQ0ϐp=sjzO?z&ť\YnDǡFhovwÙ/[8ti_ʢIv9ˏ kȲe$0 ԊI[hC >א OD#3D-Y^|2v%EEA3G Z*gnh]pWh&PPJcnxpcb~eI]/KJ^,ɷ~;#y~nCryunʗЊ:9%Ea3Z 93v/$$3YI{Ov n$7^1u~o=Ϗڸv: ?\bQ9Rܪr gV)ί Xҕ0xBWȥi|mt\'QZKJWlF5RUcO QBM>_ ;TEnhq**jZ@ yV/Zb1*wf/ R `.v 9;JЉR13_nmqذH bkj0{Sֿ0z#aG[3SRĪ []hm):MΛY}M0O1ޓKh5 ".H\-u#/ NO]^gm5j[џVw\e q2idv#27 vj45նJH,ވ3!1}M$0Pq:9isR{Yu旐^: -"nYkVS+*M$8(cfZue 6Uwdi Sg_?tC_t4iҺ]&],'24IPBGXhw?3ylhY*k0+*m B rxAi|${^] yN&X Ɯ(`_Xt hM͐y? (Ǽ$l9μRQf~K;g+M&1YFB}'+q:7S*;- B~veEP%.Vu2xz *pDZ{xǰE9]Y\Q}[?oo@(U,d%~MK*:f#^.SsR:\-ҥQ.3!~_qi'].SC*V%i[ȧ^W_JY8 H"_ Sb=hT u߅-^ի #uN 4zu>>y7E?@`RJq 705„ǩ=AgU Ȟ=у.(ʵ02PZ}AS=bt*Y:Mbr.<5R0]{bY0GbhzPoQqjk,llK9y-d'SϹ@)_QC4#Cek9GV(Vis< xZhEz{|AĜgi` W{BvhFv`YƨN#  ma`dm~9Y5oB?Wt-ʐX9>Ш\>?baY#Fmu5P0r'vgCCWyP"at&bH-ɛ{2!̓y@q{utebзf3YM;s@X. Ѕ_-NKv24MuN ?O8z^w6XJ"O mfm9Gx8EU ~搧l>Ge)P{mLӾx[ٔCFZ LGUշ;_2Ho63d3\MqaHݣ+i:%c?]fB|'>!z%Q uYar&_LI03jCBhEtM\Ii7ۙ>AddvD~-p>5NrcI4GJa.R?eY !`LRt*GO$zNiv-%|+"V +LV ^ HJ>H+ۺsH{F *wlq9>>rO]hVsJ`Pf,B{DQ}G}J":giyc0x98n:詞L !'a=dҥOgI4qb6c;Ž1Om^j4E^NU]qIb"}t7nUcZ2}O8ZFKF)'vvd}z3Tzl5K#EK䏭} #CMp~Y {Z*> y%zDY=x( Pj7R2r$`}SҽC@o8ΫgPWu-?w3P_)l;YX΃͌!B#]` &Ľ|Q$9fJ93m~^ r*lFvDm| -+0T\vLUl6ZʜP5T: A[a$pV WNHGXB3Gs'4u,61k?ơ޿|,hZΡ9X;; 32k8@ϳg0$RuӘר;F:*T$vf}E}HY10Wqmx/ֲ̩fЋNbAg"Om aΐzx[e6Zl׈}? XyE}H<hhoŅ%W Sz;]ǢWÙf:M9q4,x{f7EAa~ M@t-yHғt:EäX3y+&cR ڬx\HRQ:TXMD9Giˡ@O5u.oK 9)c=3¨@qӤzzg،L-NGڄ)UE20#ޑu|S, sWT=|,qE2.DtPǎ`'푺uGĵeJ5uT0+'2 eKwt(WL,&>Xc{U|;'qգTb)ޟ )zL^iBKL^j#V7Q7>gt`JHAHϳ@h]RZTx+JIAt#;Zv\fhs5L.cހ:˕r^y^9qֱs{U>F0bi/霶Gz`Yi!q^XL4F{%7 ځidZ"jړLiJм̀$GgG(|@hvf6&wVٰY::TDSbuADf EΜ (?݃'5J`u^6,*'.fyqlp c[j[.K[_E \&Unu?2/ًe[*'T_>F3QIm_ [Si1KOQTOhؒIT]Y& F^:+vե(.j=/C$G6[ɖ򈉋Z_n{/Ҝ WݸZq1}C.e7]F"OuX0G*{ wZ7֦{woiC5iMq[L_9w$.D>sgPocb<#.nO?Si',։}G+Yc@c`WIi ႟*vzřf&R̙E(rj6Ms&tF._wci׶0lSY ˽zl~a{m=ݜ9rF#z` $vxY+x4 zit 3 ؏GC!bK@_PC:ug<~ ؈W,D6 έ^eH6@?7uc Lӂ椫:Ƨ\G%fq3 -ٻ 75&tp!+oJ4"q&&t2j,#[/ =r 03xj0nӾH#dײY" p 8'/֧B/pxGj%m_c۵8F8E#i U_[(*q npc#.Gk8Poz#l vt9Ue=dӭQDYRn9񠻵 %(YHq5#N)~F_dy3V6'?Hi48Phb\ o˒ a$l<=f{V)R[}30FHVn ;YB}C4L0TNAbbRHm/r*h)SSTQZ*,%2x1X[v0a),_ezB~ OI [xp{qɃrj (I(݆蛑3|CӝClYa Omb5oz@H{?TbyI0a)%r4^ 33FLl1IC2)udc? :<zgmd0} |U I#S=Х`e.9} f@20NˀNtD@/?o]CɛtF{mb$$"LSA2GpFr[P,ɈZ*/0+vh-w^ T,97ebaͦ,5p3IwQ(y~OMT[mvh,E62 ^NWV*ֿЬpuXdY.6q190#!@BIv"Qq>,>J'&zL:ֲIB'(9u+<*|CA* mOmM2 gcG:f%̯QlFOaܸX*$ɵ݀"p`b]00IMڸ2Hgⱎ˸›mrŽ0e"]'^xCVjJ7$w*yFdKkoц]?LO7o) *mO3<D23  ֜fG.&v@l 7:oY2C6~N^~ʥh4QOBS9X¹Uuyݲ 1w$ {1wL[w%2{ȽTiVŇ@?Xfw,W-ĉ"<0GBx8J۴! 04?KPOx]Fv)a)uPkI_Ag4Mn7*lG]˒W]o,u|].!ŃZN1f@hDcGsҏCUE(Rd:*KiViz=1)TZjBvdKCȽɋwVy3쫷*Ke?O@tNrFkDV6ݷ˚fH12`P З9Dbp=hfN)9 }qGVF6uNBQie#{Q~gO>L!m|*=Tfg3fb@"Oe'Zp'Meod&jk.ɢۑt9  7ހq*FxXB|hK9 :D Ƅ<"RGxQsj{r$k=瓃7^;]jwQ2G8]`u6s{sXYs?v8'1Xr<j%k[JmqaR}=N| otZ6kEW `2c?nb2Qjj͢pNH]m6+9%""2vS(:P- 6tKa q+t*0m IT^o2bNG,G|kXl彐xճ|~>b.CPc{5:Jkupm|FU,LD+ro+q%g:׀^P1^xUQ*5GAxэv6ˆ87L$_B-]Kvghrb̧>,J}|2o8;|:iWm:NxZaY.}qJQ'AOY'/[OԌ"QmoL &k.ӅAaW% m??+ϡh$=g zcptwIsZIպ7<1lj~`:9kͻ(VY5{G j兝L/?/vf^ъxDi}̘LHn҇sٓfb 4ș?L<z=<'T!JQ9;uRrK7, =1oE`Ne~iܼ.%jVN`Bzcj* \jw~m%9}/|0짱n0֓5HjH4ڨ TtL_Vߞ]L& RD"&bmƯR.Պ^DM{hi7]\`ɗVfk{$pIV4nRJ񻗔 j {&(CރbV͠%,̲ )¢|E!kXGkɰLsf^1Ռmlغ){X7Y^>oPRf`6?G5Z6rhIEJ1pi!ͦdn͖Q asI_5#`}7cԄ.ZeT߰ɥxξ Ba<R2ݍzՕvZJ*f'Z5RU)WFZR תgNOĽS~}+X􉞗>L|52hhLַ@IJb¾}ɱY_ 5l u^ZS .eoۏ@Fmp$^T-]vY^Ȳл<ɡx ZP2DyE+kx%S]lP$\2kT놟4\jږU#N30<"ݕ-EtS즄b?0%z?'?fD?Ƥ ;6cXiFC/-7[I)LYWNJa\V)k:J7%M{Xh*8 ZWeMɅJngP2ڔ:uiIN6:0fKef8ęa?x_WWF|3RIԲM4y'"diMQdNmO R3m%]tuQOԃ?^n^VהFPa EqoHJVX}3PԞVGnMDiqBA™E&mE왿&:WDƟGL& f}f: ". WrVQ&9S1: iXU&yYƔߊ"S)#5ch;AFE k˅' *8IoJV-s( QJiySJRI[};CD<0}֪lMkDL6Yd4;E\L{hH~<\%׍3h8:K|Sv).7–$%`fHn]^x'D"yf$Kf5!z02#m{)l#[vP??mF5Jw佚34B ^}6Dpt?C)z+hL-~!MWyF}S=} MH .xמ+5%Wsd]l%Ā ;MYPA'F<!=B2Τ.J5xn[<pT|}P.0;UfAQeUnqb8y1bI\`$*^^*Z+%+v#Pif$,Q WRsS@T|FW@"cƔD֠d ]Z"AD|4FwQX}Q BV(IgKu*b(ùB۶ R'Κtd&A1% T8_`: Q'_C2vx9UQ)w?1҄(P)5~0஡< ^[ `k\ ]<1}TmVm!hUBu8~(~e* o \%F.w~:u\C$2.EXۜ?bE,8/Vm<-+fb* +z}y ^#:kS߽RL9/j@ ?)4zK&cحQ런,d˺qXw3 O.* *E/ yNԊ+Lnf`& Ɠ-3W_S>6`)P_+:~u]}3|27,هKYrM^/BQ0vwd7bpaAZ#VYٛ6x0aGI; @~y6 L-CW'@TfV-g=' A>šY#;OC*m'$W9'-,,%o2?Ti1aogNwb&CRE9|ާW%ED Ekxvse}` Z|xq0aeJ[?(a,'[88#Ë :h@>0&~e _V5;"&y=jD=5w9;*%r@60'?t7;b]_֮C(JcoL"F۪TB)SKt.> #XƚiK9쨲kx1ޮ\sɖo}}sxBvEifn63Gw}VFK= a0 oH5X7Z{4.O +\owk"f$!MW0 MpaO^\2)gVx/@)7;lPj?cޣ[!zS.I+*("/%n4TILJ$E5.7fOg!ZUTȲ;<ōK\/Mx>`=%fѦ`R~Lϻ3h_[FBitW,2,CzamT,@lX幰s)W7g )cp)pWK84DJ5_ \j9 N*G J8ofGl,7qAF9 c X{؈?%#FC h@Fʣv;0 yZ#dYH4ژ~^ δФ_% /nflrǼHK>(v}qbڌg/W턞UTpXЍoۨѝKSA ZD)d*g&JӜh"ezj4f ~1[Lpi! djikMAT#|$}ȏH[A}bMWWUϬj_6͑pyHzqĮz@TI'킲da!ܰG_JW-EIeJ ggVa%pN)g=R*i$}XDn"/sE]vLZ=̈́6*ͧz`{506+5Җ!h99)$(_0;Ӎ?N*jc lv_-q;N R3A3{E*uHiMdBqL뭊UwAB`1ؿ˝\dPByVLJܷ[' *wv#B˅GAhFfDŽU-Sc9YgGfwS[^DEֈ>{J!ܘc~R{BvKRkcHlJ@ƫ%Loil'{{\ Qm+,nL I k"TVigVx]ڪƊo?֍@\@)>&ěi ~R,陦|/WCXC RBpٵ'*sfd{3Fb-q5K79}OKJ@M,;Dq'-Ď:cB Lޟ\MIS?}?\|@ E`܄|fSؼ0^2uTEf^3_WͺMnۆ49!SY->T8y"n43͔ضL{`M奼_LýeK5Hñ2#u0mŭA~SAXө'YԔ95Ɇ3Z$jҿhM|g&ҝI3zڱTc wJV qY;sb@8&YrK[8!q_4 .(5^?$1(iBsKd2"& ErOpK} ՒMuޗxpC6櫃L9vLKEui{PX샷>s5 Zα>xސ†&HE-%%?`-T D>c2<+)Tyɥ-fΒ9)K*@ĵo~DA|$ E`"whq)^ $oOO˓8 \9g2]xԝ ސ-fQg,(K/dđR >bV`S޶4ɹ@ 3GL.Rk"ta77ʕ'~)@SNˇ1ONjDqU~W)AY@{u}dዐf2rwOBQO]8f̛Jr}U%JJx ]L.'YWCL,ߤ(Ȅ9@$"h9ɦôxt] rh>5~~% >i;%Ep-fi/\) é+ z#&WaA`-B/۪]rxWMáOls1]_^N2!#-jgXH@A ;>38\QM&!r 2W*V#v I"n z䪮!EZb/2P"9} utKoH2 .ԦoE4QĨmM ѯ~)1:'٪)E<隸0E س.<4)߽q Ĕ8mL`jU =kaQ ?nnPm:\FZ֮ëaXa?Kb [n1tE?/R7\dug+!>u+kTL3tqAj`]"CqքCBvֽ҃AsLʝNl>a+76p*ᅥ$a1-) &ɸ" 40,(tZĨ/z ]2rPs cUX\Mϗ_K8OΩRzE@\è.6f1^U*ҋ2iQ汬4z{zI4sm)*TPޡ߶|0U)~3I~\x #7eT[Lvz<hw#tP pbg䷊f]exÒ4GS{UBGPhCp>׊\YcJQC_7a$?I1Ň1@-ρ5eo,O)J^JKA &z)|(0^փf[ֱJGw!hGf(-mojR%m }Uߥ߽is :x{lp7% 52ݿK,!FJ;`fJV"(ҎFf(vAS2_:O`[㯻Sþ/710Ʉu!a-L lz7=ֲy~ͨn =**TR ߜQ`1d+qvj&'zg("Aj|+g{ĕh;r GUZj%9 kBp@SͲւP,[Uoo/&t5pP8O}u ](kVm Bii-saJ(R5i;|Q6S(kkV󙤂CWCy33_c&u #zzAA_d;IdoaVP%%A==K5v4ڴ}W6ܝMLBvGXNHw=nAC8iM++d9G:y ZQA$"(Yc- Ҥ!*U93aPT g)HbMB eIſ^0= IC`nGgG+a,#ƦzЫ=cѩN^w/=Ob.`[@Sf-ccտxcwȞ,bv}7_2.a6V Q߅.yrCݤ7hXjwqk҄xON'm6g:xD8l zR XE=ea}<:g6tՁJU ]nΡN'{sau{$M!G2K+V+'OW1:[*4< c$ÇEGIpIh̷ZWo"#=-)#^i(PYzr_Ż~˔me%Q8L !}I`09`n?QtHJ5,M9d=S)̦< |2\m7\WWқ=fydd+d]*qfZ*m(!DR]MW_. e,%mT̤~uh,Sc!a ATy 0Y`|49E_BN bXUebJBAxpz?AqV.ɆwG:tAl~0iU kDyglTynnfso=% &N* _o C÷qU dSy~@BiF Big@7[s"6mnT",;e2#Y(0BY3v;b)NxW@/m4 OТ*MF3лQސ.)ĩ? #hyn7jIeއ>#pe@T3:{YɃHw*lP{2z`Pe)/[AFcd+T~#qR/Ncr< ؎UP:6o5fUsF.7#Zsc ݗݔH& zEtH@KǙniA(@kzl/Ywj:)IC&o+POثK)dFh9a<]TW^A66KZL)elX?Y%v8Ҿ}cuA14/~Q.C6vkOMvV.d9E`_AW.ȖFGYT֫RlU1ďEwd<@Lw*lXP0۹BM)i&3!5ߟT-+ZRgӾ= {:lNq?pU][?'a gt4#h3m;PĸǬ4k#*bE] Q`afv ^ !bFZd]WC*zV/b)WaojvOc1#ZCa^* yX.FpTu&NLtS̡;َ9&]^ 5 0gKf1`)BZT/3ڎkVh J`,zUB@kC#oWjf?Hڑ.&-#t@O,sYE)-GKUoz.PdKKA1A1h_4t!L2ns͇ۃ,ˁܑˆi=vXa)"gUŀU:V (+\P [ G`4C^Y{r ^Gzو|I\Wn2 yGx!xRYj+ ټ^i륜{Yyk62Lr D:Nie{ut/i+;` jԫ2I%ɯ]ɼ@2͏ESL wYɽOe/W`)% GV^՘3/ݾ z"ȹ i7%lћ~3״>09:j p凳ik!ڭʐy# ` |QfqkU}Rx"hVD-rUjܣ꒟ao:6%'(3l.f+L5BHt X3:\MT| mL_<+{ɮ/_B4ګ)Ѹ'ρ9V*&yWp 5QZKCUy?'Fu`}K+g2 U(ٕ@hSŠAu[(fVe}%?X,43?X,2FC? zɡ x?15_']%oι<ߓgvpS(UUzܮU? Qoڱkq<[ t0TLv¥h:Hy 7wOE}7ĽGcl`5H`heVku w"XOS| gtI\7>$i\+`Qݗ*osy=%$ UL+ϸ|&jj)$):ǐ)w"RNoS]fSsn4Y1#s3Wj\Cx:ҫW÷⁰n6ڰi^Uɻ2B>?9dw ?N+E wzm|3g"1MCxxOJhC; -ɘ%~xQ‘Т {t()  ڽ ϶k9VtUULTIN BA+ e`mTŲtfh`)d&eDxِcT;D`Y }Sbol2R>R/^]wrfcL tmlvW#ڰI pH,x>r W"{,c iRӏbЇ`똲NڎuWPHdߐ_%\cI)"ey0Ѣ$ |kxKq^p:C!!&?<{U8'TVNy)97l:rT t>:QjX"S1Γ\X|Br< >tLjo@j04Q1K(2-!-$'L5j9U40FHoo0y5Oq{9Xg@4#4vqߌ"{*1-?EE<^)i䝇> cpg6>yZh]_.s|,8'4t۟p.wlxVЃjN׻#"W1b[ÔBCsWH^κL><}h˘/?$3ȅZ/twmDKz8yhƯKز۩Iʝ,ńyK0+DK}R/jzጂv߽FNrX!|NA+ßKԆ:G$/V]vJo, uy'"ˠ[$g*1vN#B,WHYJ4)y:y*Cu>2rt`.2ƻ"/z=7(]},U9pMdh bQ^h_ଗ8! U2Щ)WBz,Px%_ja5|U "J: 96PEFpi_(,^|RN7i#YC}O*.A\nydU?zM.yWԋJNIVRD%p-s1cڪ1an5mʙ1xn&McqULظS55^Ǐ 23F5y+sMk ?xQs 3DZ ? * 54XY|?)@ o|$䕯y|l6ҾtL!/Pg[/x8IV״HK5K|/Wb[| ,uRBmtA7)1en ^җgGKgWa%IvN~Ox;ԣeͬN]Q5C/ա聀&Rӧs6Bhk<f$}~ׄ Ufm_Ucn+.4%KwL5|s:cH|^|Lv?Z[a~"Kuf$&Qg ?4:! H2TB m<% d ó7h=+ϟ5r4)>ZyΏ*>C03 /ړY#h7ok⨝?؂r|gm6ިkbj$` HOeyˡ7*Jt+K{f]NΈd,]lR4lk9%Ue/qJTd*0BT 9GO~` D]Z*pwVcf:fդ7pp02Xnad<>lbLJ7q~~{@!YT81 x{l|y(я?@6{n- 8`95E5cBfNhĩdkr@I Q7gjaW X B}H,6iIAO~]wRI`|{LW:.tԖ+>ɲSn^krp,Hy4 L{;W_FZp]vϘWZ~:%K9+s#H 8#T6 n`[=K^2_ڗǃ-i]t4B;Ls{va&X/vԺY kbㄼ8l@ȰVkpcTӰc  l0!{7u7ҷhZ ֗w|a[0 -Ug(0O-=J⹯U1Q_P0c&&1\g?O֣锼_Zm%p R?/4lxh< _0Gj uQ c9LH\'?)}@[Uܱ \+g EY_nu"+.wHm)MN ,T)}&oz1T6,a|h="'r&^o񠧄B"QzYR7*Duz8GO*:9'DG !:^N]=aKF׳4} \h vZm1p,Sajb?e0$HM)|5?|QmO],J 4lY%!Vm$y-衬8ob*!S~֍(p5jT)HB6J2&j"LjxԖYK1|)Weo,ṛ+i HZaD5(!I-J4|E[}x`g~,AEpf]Vi K榿C,T洧7Jqni10WS`_R|? kEƫQZwmQjVאskx+>h7Є*BOڙ ^r|. ag 36T[n3#$5/;0,AUb(K/cRTѱz?Z^Gq|% ϖPV.bϖYco+xu}0ν o@ɜ Z Fm#ִm}'ҕYUw"Hgn*~=t#I.rN@VaT=ú`EMo d]{ddj\wo &Ta?d PaY(ʬh@ΥA @4]"OZ>rXʪ,YZTmF@ف$/NCg"C0.e⽏(;6 hO\JLX$sWTK*v6} V95*E9Yi¿g-*--Y qRgԹeLiBӖtt$"NdOHM:R:)O_)oqi v ֳmC =jdCZ7 6/]T q\f/-{m5U7fhQڕ<YqT{AT.~<-1v:11nI[R# L+rIqu")0:T<OfI'救[XڞX !uJ NPK4[-[FHSg@O4x0X1b?[@i܈qQo B-܄c'h "CUMxz߷JK:oq~quSQljeyR͚1Xd֡vWw{8fƸ3ɏMs$) \:[-(٤ :+wt9 0ϫ횩w@u,G|fA[G~IQݢB{Ărw0FEdݔfQ 60=9NPpwB D L q0i!í u_7rpTq[Z s8[b ͐V͠U7 k pTȮ탡pޙH50&ѺbaƠ0v >2)k%]{`B,m-jCm7RiB5q -p[SRPJؕ[&JBy})EqN '*Yx( *z5&@_<߷v4vi%)^DǠIELNT6_ ij Qm.= #S1Ri+օAiB f,sEOn>zFtk!gk 0uӜ VJavecO?';n= Z%+r\RjR@ .;۬77t&*ى'm2Yv4?W&5Qlpq,Ku%flb`E~—.lMlEh*5#Xcu&u5q-hUyS𵒼tw| ٪K{h~qyV"  mbĜ`Wlix9[u'x },㕶\]=xƛ^ dn=1|{ 8EL2kR"E1J;Lb"LBFKF: +JDH?iR6}2})Р'].uҿ(%q kgAb `a)p8y|1דzf&(q6M4xn|< P*X%Ϊ3 pUW[To/ n2ޤ%E`Of%j_bAx!V;6O9.c`BP )14)(J. XlGDSUZMsN} 8дeeWóav_nC<Խ]8:_. J>Xt;mRg궁S zfr=MN,갏bMݽ E4\yqV@ ࠊ>׆ H'oN,v_4c '_Fu|28<եCP~ihF;uHVӊ )AWT 5/L~:fKr{ ڭbl‘[v >fzkOO/`r{?qxZ?5Zƍne<$gmLT%BO8CbI6~3ո Ao>Yun}eBO\S{Hop mI\][X?h'6d2 '4`y)QW[us 8~zj5\q_c󤱃*#υFz;5qKx?^??=ai Ȏ¯V+6LGS?g3c)э/}W'fiؒ٨wTÞZS'0$դ{$,E\%=C :/ qzm|ekM!/Bmb"6. T&Ptx -3|ڼxّa/U44&y8MW)3eVa,6H-ohy-P\fK*9y2`ˆP”% !nYPBOԙ )9F f[tюZ=zؤ䯌p49V6=Zܘە&~mSF6x8;K &<Ÿi"^l|䨜dQfܷ9ꞻJJ"B Naḿ5iɌ Xg[Dm!2~N{p%$Uʎj{oЫ^->lsŜvv"?-vx ĂY}RX3'UW0u!0,x:&ʑF]P>Z2d#byׂԈ+S[Tfs#DH󢈞?'c|[>t1>PA3r)jF̓skTb\GQ0H 4x8OnbL|#˘"SL$Es :_=)57W^j!G΢b[%JJͳ]{y٬J)Rqxsn9Xj?>] >:߾پq9N<Ĩqqg_ X{{eVX xI|nb4I7c|մIs)&j{ӆ+E^t>'|R-Qo%PCF1K\B~{F1cVa搚Q7c d`a¤F8cz Wl4JǷ]˔Z* u)(PTՔn(i E'dG{0 C8 ͹F>l;M.xvU:(L섐ilHs1(f]= qX/-UcoM+ H* T*Es[]C 0  X]m!K7+"}.di+MIX.c+@_\a0M*cbgL5mlzS W!~"rAcQ yWAf4#N֧9nj0!?3ߺNS5f&OQAakX^zd+P@?D5((68v|gVl'p8r ?" V)Qyج*$ rzoM7 2s{k%ŃJ$1,<ҳ@Fxw MIh>8/ҳZcGn+-N4Y1 AW$Dٿ(0@!;!T4Y6w#㯇g``g;3Jmς^ n~=#^M)kzmnKXޓұ d.AgwӨ~[b=*R1ҥ`o\L SI ~ds .an c; ChWohDkj\%NVA|#rwzbYUMFO4\YHm 7A*0wFP9X%~@y͢0c@ݐi]zKвFK>WגLQT丮 U bBЍE0/+|C-^#K ξ2Tț\\hΎ{-T,t{owlu}@Ij>@>&9흖';/֗E@ܻOEA6 ᰣS&>VB*-qAXp:zR +L/ y 9\~4ʓ?]ۑ- Ɵ'~p/_q3rʃ˅l r- AV0y;bʗe0g$E[Q!C(då1S=#BTA<8$4U>.%qr}X}Х!$V>wC+Jsl *mلzޘFiy[$J S/pUOcaxb+:wBh;)=ZclH𨩌`q;Þy:>:"~(R^P&Zz̚\RK@/"eaI.Dkɓ\pH+3%> O9I4 j}rm1ujP"`F*w4tot!YP%q \ignZ U.V{nɼ Ūr4aeMvDXI%Y" uᘴJ5>+lh9䘆\lcn?]%ju"s ձTJlgݪ@%|;Z(,L= 2IUU#[l/>ɩ $Nc!Px10~2 6}IHWg$ugӎJ1Dv9P @4Eד!8Nm LR!@vaL ʹ F;V؂AFR~Oݤ4{5W`OMGIDKY9ި'Nd)'(J9@$'L,tԁ/o쾺ʓÐB;ۈ..EgeѩIةq ["gź1E-rCo޸IǡJS3*knfjRz7tkg BK %WW r?.]V|wn WgDnW102,q:6Ϩ"!2ܰ)S #ԻW+-*t,}}{F@sCuuVyh9n>}$:Ӑvml9O!Ӓ~eJvpTeu@N[/S7.:O`hUȾe Bn.UNḯZU1X}ZU] R?V4ҝ$I r ٛP8fnXV:_Pa5j;褂_%23=5HdnpA 0)_ރ~mV ϝh숭h_\z "v!A\Y|N~ v:{ѻ̕F dC)%fIB`$%ґ)&^> ecVS_R\Yەdp@-ͅ<[I8Wؕ#IA;0U/꛾l]/-7$%/Uもc*F'e *qK+h F*18)I(!%@Dgw ?9r x<ѷDO.zcW4{!2(!V e3:(8dlۑ]n*vP0Nuj4d/$ρt_%:{[@L.ۀjo7j%;}8cAEa!/O atg_ l#ݤ~v\aӎvڃH:`~-$P$mBx.s+RQsL]9J3pho)+Vފ-~kYRĹʛIB\S };9}6uv 3e- &!t\A^錯`F?P?ȿ>>7zs\ޖUwC &]Fuc(σ ʢٸ3l0sBOD~#fI )効zJ5r\]q(&k問2F'w4,yˡfwY V }-4miR[qiBfi/bGtiӢ?Zqp =ރ rRf[Kp*AIqcn ?pa u}ͻe*O'VqڹD{ԫJfJ3^6)<~JIщG D=Tp{:޳WܹSvڴ2{\> ro+ if"r m?@.oz/.G'{K K'!gGtDlO]v $%L䞸ENw֛baQ'$<睊T rSx(tjBGGgR&[o* pĄGz & H߸KGa5CZnN魥H$@׃ NzҤ*}<ɼ#I׌U&2%Uf8q.D뒗Ie98-ɴ`jH_NIXu愗pY= KH)n6GTBIjEo5YhR[3$>Q3+C{ZPT 􊂇\L9#>&H9 pϦm޺;7GL|H*4/;.c,2ʽ[.iɍ ސwIԭdG 0kMj> 'b50^Ix 5*h=OvN-X$fPr'q#&E@N%C2yR]13q}w1!;U$e}H ˑ/>H( W_N}fW#dOMntH=$'#KS"`HbAР)<*p7ވOW.pq,Y<BY." CÏ1Yt{;T`p% ``R\(HLO֛GѸ}-NZ|O* TsM~<aD T3kLRv㚸|³,҅85An5xZ!}=f])mB#zf?>36|$Ƅi|&N&5^'tGey QfW:n lW6<`}ll4sWe:5z2X2Ҋ |Xk%ah4sc;f֮55=0Ff}ceDt(#,113"Trr/Q2 xϴf5fX oy8zJj czW$o>&7:xY^re$C >5 K߻xZl(GBO?T?c#*'^ܤ^;TD&dR܋2 Y!'\L%xtEtԄn:/4khWn44wV=TM/0<;˨S)3@Em*w@cgաU~+d.eמ'h8co@_ r~R. *9XRt@*[=zB\DVt g"zKogP3t3>y@BAzdN Ԛ"kHWWGџq7ttmR(~RGC!1Ee|ú pWR+ڤ~w/쐝O:i K4]CPCyYjUOcTu0 <ŧ<ͤ`.i6y|0^b^@h/Y!Lʯ3Wjpb Qo0[ĵLm6w>oK^y*BI#vbKyU:!5MO0fMIG/У`dPe93j?C5^+؍fy%W 46;Vo aKÂݶp:Ck wn!D{@cp2ud 3)m]91Fc]m +kg{jP*ՃhtjPI:)->cWࠊ=Ԇ6ش6is*:?Wp[ = fĸ 8MΒII#%E]3-1ieîP_V`{D7vqb fo+;Mw4ex P:K;ym\Y.Oʛ$^O2Gy?6a%BL\ vK, wZ gRDt2\w1wI$[*iGN' $DV>ՠ|Zt^GM5FCTuՋtMPhnk |9VG">Enbl4 (v~+Qu-=R@483@+ Tpl{}dEJVmCpαj-Fǡ.+P7C(TN5ĕ2~y5TNUF|}'7i3Y{cKPU#(3)%nL[{$s}4uĽ05ݵ?O<"LO}N-}bfPVAwѲ jŧ#)W_J՗8Y>!NHbxRX:>Z0f+q{ByQeELifgR'D54򑆈beUquHE폥:F<]>u mpo㡇%wjQvWȢJ}nh@gH$rhJBB-IYQE5Vw8SHim|tk0ɛDHYQP)A_נJi0~Mex,Z*6yBՕZI )DTc UCc`7*Pr7 Rr]4.s~v)˶q =cŷ0Dr͞ vuw1eD(Mim.? u8EF9\:aheRQ9]q"KS/%OXnM*{"3%#zU˨s'G~a)F:5C,qPH7!"[tWzi:ol7h#66 5mFB|_Sļ#]\y%(-0kҰ$9[c ;w)7H[B- po0 @ݰmme?9o(Ed}2f1P[em̏^&~%+-eqKW@P\O"WG0&B,\*lTY6UG:qW}?⻆c-VX P%K7߿xm(`~hr%0A˱",M'Ck['w)Фc8躰U%_чBډO\C&'Pxf|,:_q1ZzW9Qjt}!. F6lA*#zH |8oxj!7t6SU8:֏%[CD`VO0sķPo&ڣJ ;حlju Sq$ :,߾=k|#Jz=d?q! / 큙C$(Zljc,M Tdg[kcV&<~l!d9Hu uH;i[9L|b<СA`K bW>yOfB&凧iḠuKLy3;X!ܳn͒A9J._̇އB6y(#RIFxii-mC, y3$0D_Nڮ3̯n.rn2%awh <,P\ƕ_'n}OvX*)`R!MxI1fo2ǀ)Wj]6LQ4ux/xZRmz&~yF\YIM+:h.g?<ŽRj`&"=y(Me:.VVc)Xۧs|Yla?p@͡ҋc踏1iw!Z' (i80Q@vVQjhӦ.'O5-! w-a[h*q61c.;5>rO?UʆO6,׻`6 QzJsiI&8g.gIiv$N 7Q22c{*aojjj0n$Vj*# ?wX>O n9dc QT|ThńtxJ![1#-B}!hsMn85ʓgW8 Q?˔k}{Է<*0~,Ϡ <_u-B qhR>N!Qh8F?a9s;)Tn;dxհ_%#DM  0R!~}oOHk{șd-V[^-I2AɔjXY Lͤ+ۦB1Q74zp(5&]x}c(U\W~%ンf?<;D`UAD_kg,_,h)ehS ĭbNҔl},fCajyWQpy/R],P6A3U :{[7}(}B" r ~7Q\kX/}žcU`Ɏ `s"5_&uзtXxpO]ǖ9ϕ]ic,~aGKL~هy:}O4+ 5AWY㇪ *+5PoC r_Sl.GLϐ}{{QUyp#SB'PIbWphNԵI&<[p}Ykڴpus\pfy@ ]'OY&5l+g?itlQ&4PP ÿ́BJ^;(v4mA} B н|PK,d[iU@ szv;pTڥ=Y\ 7t!=̺&wO^l}z #va*] XY](rԓS?crnY\㈄Bpirr38PopSAE9 !{\JTqD/)c\uHmF?߿Lf[N=@}, -znz2kc" 9QWc,(7xQQǡDfeiσ N _h2B #tR5 yw˖1c([LEi,h:GzE#p:,m/CAWIINN^|@pjɦa۠t>̦w5)&=AfCz( v5u`Y:KOB"f_]@9*e-9f]$:AmGʮ5E4Ul:5_oa^#)1YOji+fJݡa!r 8R<+fykٲ@ǸyČ2@dIּ:̅q&ow_8^@Tjsֳث01TUҒz[[c0wqL~ROmYMv!IU@$nc>1v3@jC%*п"1j.u#Iӽ@e_f<0"GMax IK }C] 6Vf34ɬa0!Na W{7|oqyۅse0VPI7qT+}OӳE k7pJ2nuh<֖'AMȂ C8ѫWC>BoyH"RrMusM K\_v=ֱ{G)ܨ@!=qݵLeNaP쮧b{l?;tdT0Sv#8uyhG]:.a_Wh0p}Cq~\rZҞP&WO'Ktpok%4 Itbg Ѳ%dsC-g,@n2~'5+(2rOU3nd JCgxD%kVر1eEWB6R2r`ttP1sT-ROTMm P+%k't:ja5ZhҟgʚH_`W ܥc k _@41c̍a\yrjkTah ;d7$Kϓi\vk+k3 hV|bx&ਫ਼q :8rt]؃MeQ6Gvb4joȟ,E$?VڎXk=藊 X v (iȪbʟp Y-'&I-)mk#6m9߳'A])M¥YRKB|RڼhVAhcԑpGlN=$ӝ/>#"k!׏ߘ&Eg ?IuvO@3=J;"B涣:\kMYߙ6"nS}_ɚ3ۭ֪j(߼m cp0?|<%-ph>O0p7vk銵˖0xC~s0C e-_/á*6;(K6叀K*X\u f+V}Ovxk9~,pX[\\j8&"fhL1do@oqPeǛ+e9k}gυ PP\oѨ^-|4oz=qo)59k# L f4s -扜J7/xz4 _y}dz,MLo5n2?O!&rr^Lah1;Iy<_x;hMU95`|Q*υ"%eb(@sԍ +XpXRpL.pTMBvCCB|ݝ8t3sZ߄h,˪HY/`i}1 i?H0wA~= ,c/A/>Ʈ+Tlj9 =mU90$C_D%؇{euG;^5NMaI@/\t'*)O@e-D-*NvK\IH/`sImz yKw[Q̊DXLYy( y䥄:c[qtD5zt//S}ם'hbraz4BH%r(?'(5nzp1C\<lj] an=6 rwهcl%aG6EadXAZG|5e`[*CP4JrV1Ph@6l)ٝҧgyțo,Y Ub4,XQ:XNj-*_b`X?ϫ!<ӵ9v{}wm٪e[&IAP;30x2΍z2A }ki$ 5uQ,\ qDS{VGȈfx8z.&.q|<1;O[ %R;,1Ei52c-9&Ob1F91.Z>#`z;he )l{^H3K0OH \`곈:;t* Jr}LhϤ`鯺rM8[|+++Qr~;Uj^vcK<ʕmb>Mjgq^I4h3hxO9 Br;=c3p^OhEI^w X/$p1>ԋ 0NO&7%<t-sJ9;Ek>jPm=cˢ?@o|%~u.ABG<ǣla-fN Hgxds3mhWT^`})&uGB5F!n?)*kiMmL rs!+W@A} vLa➹`+_im{s*Hntyo#dAGDP2!9.]8KSMDdlacoF_Eʈ$"\E9Ux~2Vq xCBL*<={& ] Ͼ$2oM6ϭ #ozaF8BsWi1RߺXѡ&Q4Z`9#P8d*kHUNAȅ*io7bo/j6w ~J TPn#{2MLV@j)71#% nĶi9C,K̇.!iKLy'.n*~Jk$a/k$@4%lp¦FZm8<P*]#}U*1ϖa\6aWo1EILfO"?*7L>uRJ]}g>Sw5q5w-dSUXF! +jp yИķ[7~w=ٿܟ%a_ Ěڞxj^Nnw(*E[E[X}I^2wW41~Ds9NZ薦X?,|K]Q _q{&l\f Jc`+Ll2[&YȄCHOXrlÌjbJZ o<j+pRĄ<'CT4^igwQOC/{Ko9U]1F8.#fAӒ-r}0?b (6<ȡ㓬;s;H")L(,QMxr.'rVxl)^u0Jf*|! -)(:믬?%HW|߅i"hPɞ^ @-?5{, A 9˩6q{w2[XGǢ 31MbmM7XKhMV Y0={WynKv\C i60ݝ^uE%vc,8mZהfD'/3\]Y Xn rtΣMڷy (VŲz B58~]ӆ6ND[PzQl05oDCr:غru8Wz^,3ȏ>Б-QteE~#|!SkLb1mߤ#j#_fRкƲ$Ljqǿ4͍3o;uGՖ`ot`ޗTj79ځ ]aP8c4{)t?yN`ze} 4W$!-)\W2-p b"*U7)6|~x>@8OG"oElPDTIROI iؗ[%Ma( Z-}bpJZIMi* 9`~i\ˎN\ ؅& t1?Ô|S c6IbχLUD[.o9u2V eD-7cxd,MF5;KkV&H.xo%⅏3Zn0!7#8 p$L_KҊVb Aal쾺:&V.-dsݤ T +ٜ|N}hRs/!"vY@z[Vu TG v&[ J}`,@KϹ4!\d[1ϮHvO~6ʅ3D?7}0^>L>pk;%B:( YhEN9?$Y=v1uCXŠRX|olC mq0?9tk%|"9s1rٗmg ,2kG]Gpv>%B 8Cj@)1sн&>pDrk_{>הu<Jv$aa1{92"vs(lHe3sI=9Y_T7Ηofve$eT9Pu>Ď! mũ )~Ӊ^S_ָτe=we:7LgVNfk`Ҋ[>@rۺddm>1MsEրs=;浢fw<}R0NًkrsYH=,<;1޼bi0R`eֿ璒QIa^:+R _3w?WX7xv @^alr|;'e|܄S6|2=(Dx_: ŋÒfr X|0P_RŧW;=P -ϩ*_sHij!m3GL9\O3)$Z͵gumYr_VCb-^6?+ȟPPv\y"eo FTv=S%6fj҂u<)l}H*ΐQz Kr<%Ў$ 4΢k7TWg@0DK03wt95@/[e#1qGJ[ E -a0&R }\Y# (EQ^jӿ\R K g4$$yY#RTâC3s|X9tjbG\sʁOd#EڒMcOxIʧ%|2ˀc"CG Nzm)F=#ܙ6h0mKIJ=2=I$SrL=V3I0#wT'cl%@s`ZGG:{C[vi {9NAM*͠M^!?Eus01dA-W#c߹Y)^b@4t𥁟W.Om`)"Cڎ8zNA2 z֟r`|;ebvbfhksxݐCZkd 'FRM$ !9};5jԙ[Cw r?Ct*aȮi#@P^ R#_Z3 zEk媛rp=^DB́(RdXKx8aМv$騢+aZ3f6:ÿU2JVi9򌦁s/5O-42fp0dCbus]5GY'bfsoKLF7c5)֭wQ JK$hbbrPem i"%S3اL%KTQ4Pp=A=.2,{1tr\?aI#ܤ>9.)p@Ed|p}e#N+h;b05P3sNx[II7%KʱvU2HP=0<C*v\,5iOb2Ǩ<:FXݱ;y/ v!>l@-nPd46xy,1Dho@8yڶ\+ ˣu Jׂ߿qp㟼~1cx B8>Z29u߳3wN1v3: b r_CǶleޡ:} (YۓXaeX:DŽL~7^vP/a8t|Dw „c"B2,d;[WjmM#)a#/5΂ڋWo]h߭箎DBW0^gsS˒Gjq? ~@ {;yOi}Jen91t*'@-)51̀<ȹ!ObҥroɰTBؕ:d57˅Aҷ' F$փמFD */ˆ8eQ7 nIblQ5~)Si⬘VPǁќ] t*1dҏsd8"h֠n J&֕6n dܢcL9HGU!^ZRqyμ7t.$ȊG'Ko38@+JEi+Ex ;-W|ċj@tN8x(B%4nB*@W;%~?CiO4l7($ %-]#n`̀yjC,Ƅ[rpҎ-mZ?!ɕ }i—7(gY%ٱXA?llbY^b_K`ivK^RcnEA@*T P4‹6= QLvXO3o͗^Nr>㼋Lk^r<}( 7 ]N1/ ]`w. /Wo>dHP3bRFRyZyVG /0}[MGj}}4Ϻ‰\"=<1ޚr(8ٖo'>w$*i]ؗBPXZ@3eAYnorYfcI [b WԆ;,A סWj|0k%*L8}DӌBCe`"^TRdyY+p[jGO(YSiwj?$ j p _e9tær~0OeReN[\5Iw%IfFK1[-y _Qk4q `6 .^\wmxLzs'\6Ma`y ŏTv×aQ,UiUP]^IB`p~ӑ;kMq.~,1@; μO]<~{MO㞃ˮAϭ.m*X__աּc-'#l4ZAUĄH4kU&K5 \G rOwdxNzxc-0.13.0/conformance/valid/random_64k.zxc000066400000000000000000002000541522536370400204720ustar00rootroot00000000000000.]%1>$̇ &KU7\n?\I`o{Y/c$`twjh^Ub[dL_K#|0#ml#vz HcQ.+{ -Mv#-u#~{-1R?ihU3 }L)S`uR\4>v | *Q0ūAt|%Ht횄o8٢ecy1([ #U @N87*rWQ[7{:3z@l' T@$YcekrP9n%]0捬z:;)TWM%u# qA+h:",&]8ZԻNGghZK`VyetS!y9Ct$sIA"͍[dEJ|݃l!A2邠k9Muhx KIa o=J{7DT2 WYs|ߦq ӁϿ5ƛs=`q|S\.RX2 keFth]TL]AՏ"\P`HX\c!UΙ.Od(#ߐZ!oM*9"B?.F149NܼaZ#+S2hFjX˫2gOt(>wj[bZlu.+jx/=s(qPfb*BoC".E}xkMŸw`{-au(R?ǀ$-is?z:!4%~uOv8KPє؀TUj4+nL#JbB_GJWkv>{1,aWc"h"fw|蟢o.'~@YvUZkǸ.{GٞmcY!{Y)AWTuo/+K $ybdK:?:8nУlMA/fZ=E6m^ѿW4Vm"$!h2 {\W*‹hSmw5JU@kt9^dX,m:uiMҭ,zi5s&v{" uLYR;j =/ꜽF-)`kS7[{xqHbl]*&T\Frϸ|V[Fitc>d6 WYog@ϐ8[Q̉VW-:?X tp`TdA GSI݁Nx(X !["K`Q/GTÞz }P]'OWH'eca0 *"mTDk KW_3ڻ]$W :FN&Mt9O6 VR] tL1t גř5GD9%#G6ljk% jPb /UoK'C].9 Q|80/8 )s6,WGe=e,nXl4ؠDRī܆͖x)NFB\ep%`L=P2까^T]*X[ilSy sYCf 6Q'ƹ3΀?<6аե~D_t(gȞt9f}n̕>wVsI#LIL6 4'Qm~IԴкQu@VH"b:Fٯf԰+8u m*]pQ̐KH6`~ȕzX )^A2fmJ.`%"HN@|_17QLj6h2sʣNvw>gU}ԟ\ 9i/2C3q].PZN 7(~^$#^y=^cRv@XoYab~};m]R|~r ޚ7J|;5a;<ὲvLF4 Cl;`J--PqM6o Bvd18='T|x JX+[2~\[e!5}8.I~cZ^0R#wB޺%GnZ h ywNO`=[w;Ů*dDg uBE%&ƍc#dT_3[l;5ybmd' ziSHUs74dj|mQKKh`+ ƿ刲r=She ^֯q>R-L8O¯ou⌇b8$Y RVsRJ~'Ft#GͱFnMW! XY5*a8'l+sFj@&RC]cVSg($+VA<<% GC|\M | maz}}hdsJ_7p3`g- 1`Ud +#qIb F^Td u6  @82U?`>/"Ms,?߈CDCNZ&/g]7[:d,hOVmgBG45iߵ [j!a Hh}+(T fIдHUmg3$SyBj# ZvlvP2gm`` e4ɖB-YwrYXp}W7R)t\g4t%顋^4|L@1cd(qOw>j}vM8)PIaci( ֢2"S9ko]9^>An.T (LSp/TֹYpT x 0LnubK?r#Cy菙y-{ t%t` v|m?%Ic0jVN$FB-ݽ=W$Wu'uؼA/^xػ+ՆJk3;1)6g\po3-pl@C`"ch67:Ӹc`h5]?W\W",{P7^ ݜ.9 _2y0 roh.>bOTSP,Rj6!"ִTsYl6QJ|+qk8{=3`@ BkGIkqkҜ"P̩+;6,7GKy;HM9=oQ_$tmtQm1Iq$X2C~l1`#@d1͘sKh8a{p=IGA<:憠13&Rw>vVUog Gƞh2 k!{t1Һ];t@ɢsud(!{mRpnAq=MT"ڢiD"ױ7€{&6z9`9H^.9hP>["]inuZ(p1E߂0w9ttSJ6YBU@<;.%NP|ؑB<^⟅l7$[rUxmȡ'h[e%GJm% Ob {E.Q*iVP#W#{ܥs8Gv G?cՆ 6y{riN%j~g7cKP^t[i.}2uƥ0/ځ*8KR(jQOiXyA'~W A[ٹ4`XzR V@mJΗ 8mTıoTJLOXNlvtGZf@a7x=8?qfO=5*wkХ؁Ena,;:,@*Nx4GJByKzŘ@Dvv7Y#MML_"߀7ovdwqDP2 H~Zܤ@eW"/ك)%^O hCڭ_g"M\36\ʃMRpו&75 b+f4UݣR~4dAb>08q /`z˗mtkᙏ'/#X|AXc%ODfBbcʕ 爱Ňn8ד!GVMW  G4;w· i,KűI &FTqF~kP=j&eEV'@ހp|5lSP]Kf:b]vg^"+ H/42`YL d_>RjF1/E’Rm,0y:~tȐ릎k1`׾ ᓼbc "'Zw#K(n_l`wāg8XgJMbȠsӇ92Ss[H7Θߞ1yv#_ɬqt!BS0ʽ19w<3/~Cr')j6&Vhfmϖ:v=:OD6~R*o3q\"~3͵͎ R'u9=2(Yf]J!_-+5*LQ8 \ J贰k&VA:Nh;/#r)TEu/)wTF}2v b%;Lfj2)R{;~j!;P晦&*9J( )YHX;!MmguRH2/ZGiLCB-yJsx d6z Ͱrd ~9&yּf [wf[*0jAhZ5ʢ$QLN,9UKKG43Ybr˼8dDP=FNbZE׽-]iYRh}䒃;][)unӹ&ǜmR =N3d +?$֜Iwf[F鰭ѯˌ. Y[$&lm(R8\N`; OiygC| U':zZ11}VRb16ڂ*##-7bGQh&^B~r>ם~tS4,̜ ^(H؊8C-ˮ-> \7!f_ [ {@ |9 r :̵ܐI]PW-lZzsdbrV\M09;OJ`C/nDD .\ e}ߏQ0ϐp=sjzO?z&ť\YnDǡFhovwÙ/[8ti_ʢIv9ˏ kȲe$0 ԊI[hC >א OD#3D-Y^|2v%EEA3G Z*gnh]pWh&PPJcnxpcb~eI]/KJ^,ɷ~;#y~nCryunʗЊ:9%Ea3Z 93v/$$3YI{Ov n$7^1u~o=Ϗڸv: ?\bQ9Rܪr gV)ί Xҕ0xBWȥi|mt\'QZKJWlF5RUcO QBM>_ ;TEnhq**jZ@ yV/Zb1*wf/ R `.v 9;JЉR13_nmqذH bkj0{Sֿ0z#aG[3SRĪ []hm):MΛY}M0O1ޓKh5 ".H\-u#/ NO]^gm5j[џVw\e q2idv#27 vj45նJH,ވ3!1}M$0Pq:9isR{Yu旐^: -"nYkVS+*M$8(cfZue 6Uwdi Sg_?tC_t4iҺ]&],'24IPBGXhw?3ylhY*k0+*m B rxAi|${^] yN&X Ɯ(`_Xt hM͐y? (Ǽ$l9μRQf~K;g+M&1YFB}'+q:7S*;- B~veEP%.Vu2xz *pDZ{xǰE9]Y\Q}[?oo@(U,d%~MK*:f#^.SsR:\-ҥQ.3!~_qi'].SC*V%i[ȧ^W_JY8 H"_ Sb=hT u߅-^ի #uN 4zu>>y7E?@`RJq 705„ǩ=AgU Ȟ=у.(ʵ02PZ}AS=bt*Y:Mbr.<5R0]{bY0GbhzPoQqjk,llK9y-d'SϹ@)_QC4#Cek9GV(Vis< xZhEz{|AĜgi` W{BvhFv`YƨN#  ma`dm~9Y5oB?Wt-ʐX9>Ш\>?baY#Fmu5P0r'vgCCWyP"at&bH-ɛ{2!̓y@q{utebзf3YM;s@X. Ѕ_-NKv24MuN ?O8z^w6XJ"O mfm9Gx8EU ~搧l>Ge)P{mLӾx[ٔCFZ LGUշ;_2Ho63d3\MqaHݣ+i:%c?]fB|'>!z%Q uYar&_LI03jCBhEtM\Ii7ۙ>AddvD~-p>5NrcI4GJa.R?eY !`LRt*GO$zNiv-%|+"V +LV ^ HJ>H+ۺsH{F *wlq9>>rO]hVsJ`Pf,B{DQ}G}J":giyc0x98n:詞L !'a=dҥOgI4qb6c;Ž1Om^j4E^NU]qIb"}t7nUcZ2}O8ZFKF)'vvd}z3Tzl5K#EK䏭} #CMp~Y {Z*> y%zDY=x( Pj7R2r$`}SҽC@o8ΫgPWu-?w3P_)l;YX΃͌!B#]` &Ľ|Q$9fJ93m~^ r*lFvDm| -+0T\vLUl6ZʜP5T: A[a$pV WNHGXB3Gs'4u,61k?ơ޿|,hZΡ9X;; 32k8@ϳg0$RuӘר;F:*T$vf}E}HY10Wqmx/ֲ̩fЋNbAg"Om aΐzx[e6Zl׈}? XyE}H<hhoŅ%W Sz;]ǢWÙf:M9q4,x{f7EAa~ M@t-yHғt:EäX3y+&cR ڬx\HRQ:TXMD9Giˡ@O5u.oK 9)c=3¨@qӤzzg،L-NGڄ)UE20#ޑu|S, sWT=|,qE2.DtPǎ`'푺uGĵeJ5uT0+'2 eKwt(WL,&>Xc{U|;'qգTb)ޟ )zL^iBKL^j#V7Q7>gt`JHAHϳ@h]RZTx+JIAt#;Zv\fhs5L.cހ:˕r^y^9qֱs{U>F0bi/霶Gz`Yi!q^XL4F{%7 ځidZ"jړLiJм̀$GgG(|@hvf6&wVٰY::TDSbuADf EΜ (?݃'5J`u^6,*'.fyqlp c[j[.K[_E \&Unu?2/ًe[*'T_>F3QIm_ [Si1KOQTOhؒIT]Y& F^:+vե(.j=/C$G6[ɖ򈉋Z_n{/Ҝ WݸZq1}C.e7]F"OuX0G*{ wZ7֦{woiC5iMq[L_9w$.D>sgPocb<#.nO?Si',։}G+Yc@c`WIi ႟*vzřf&R̙E(rj6Ms&tF._wci׶0lSY ˽zl~a{m=ݜ9rF#z` $vxY+x4 zit 3 ؏GC!bK@_PC:ug<~ ؈W,D6 έ^eH6@?7uc Lӂ椫:Ƨ\G%fq3 -ٻ 75&tp!+oJ4"q&&t2j,#[/ =r 03xj0nӾH#dײY" p 8'/֧B/pxGj%m_c۵8F8E#i U_[(*q npc#.Gk8Poz#l vt9Ue=dӭQDYRn9񠻵 %(YHq5#N)~F_dy3V6'?Hi48Phb\ o˒ a$l<=f{V)R[}30FHVn ;YB}C4L0TNAbbRHm/r*h)SSTQZ*,%2x1X[v0a),_ezB~ OI [xp{qɃrj (I(݆蛑3|CӝClYa Omb5oz@H{?TbyI0a)%r4^ 33FLl1IC2)udc? :<zgmd0} |U I#S=Х`e.9} f@20NˀNtD@/?o]CɛtF{mb$$"LSA2GpFr[P,ɈZ*/0+vh-w^ T,97ebaͦ,5p3IwQ(y~OMT[mvh,E62 ^NWV*ֿЬpuXdY.6q190#!@BIv"Qq>,>J'&zL:ֲIB'(9u+<*|CA* mOmM2 gcG:f%̯QlFOaܸX*$ɵ݀"p`b]00IMڸ2Hgⱎ˸›mrŽ0e"]'^xCVjJ7$w*yFdKkoц]?LO7o) *mO3<D23  ֜fG.&v@l 7:oY2C6~N^~ʥh4QOBS9X¹Uuyݲ 1w$ {1wL[w%2{ȽTiVŇ@?Xfw,W-ĉ"<0GBx8J۴! 04?KPOx]Fv)a)uPkI_Ag4Mn7*lG]˒W]o,u|].!ŃZN1f@hDcGsҏCUE(Rd:*KiViz=1)TZjBvdKCȽɋwVy3쫷*Ke?O@tNrFkDV6ݷ˚fH12`P З9Dbp=hfN)9 }qGVF6uNBQie#{Q~gO>L!m|*=Tfg3fb@"Oe'Zp'Meod&jk.ɢۑt9  7ހq*FxXB|hK9 :D Ƅ<"RGxQsj{r$k=瓃7^;]jwQ2G8]`u6s{sXYs?v8'1Xr<j%k[JmqaR}=N| otZ6kEW `2c?nb2Qjj͢pNH]m6+9%""2vS(:P- 6tKa q+t*0m IT^o2bNG,G|kXl彐xճ|~>b.CPc{5:Jkupm|FU,LD+ro+q%g:׀^P1^xUQ*5GAxэv6ˆ87L$_B-]Kvghrb̧>,J}|2o8;|:iWm:NxZaY.}qJQ'AOY'/[OԌ"QmoL &k.ӅAaW% m??+ϡh$=g zcptwIsZIպ7<1lj~`:9kͻ(VY5{G j兝L/?/vf^ъxDi}̘LHn҇sٓfb 4ș?L<z=<'T!JQ9;uRrK7, =1oE`Ne~iܼ.%jVN`Bzcj* \jw~m%9}/|0짱n0֓5HjH4ڨ TtL_Vߞ]L& RD"&bmƯR.Պ^DM{hi7]\`ɗVfk{$pIV4nRJ񻗔 j {&(CރbV͠%,̲ )¢|E!kXGkɰLsf^1Ռmlغ){X7Y^>oPRf`6?G5Z6rhIEJ1pi!ͦdn͖Q asI_5#`}7cԄ.ZeT߰ɥxξ Ba<R2ݍzՕvZJ*f'Z5RU)WFZR תgNOĽS~}+X􉞗>L|52hhLַ@IJb¾}ɱY_ 5l u^ZS .eoۏ@Fmp$^T-]vY^Ȳл<ɡx ZP2DyE+kx%S]lP$\2kT놟4\jږU#N30<"ݕ-EtS즄b?0%z?'?fD?Ƥ ;6cXiFC/-7[I)LYWNJa\V)k:J7%M{Xh*8 ZWeMɅJngP2ڔ:uiIN6:0fKef8ęa?x_WWF|3RIԲM4y'"diMQdNmO R3m%]tuQOԃ?^n^VהFPa EqoHJVX}3PԞVGnMDiqBA™E&mE왿&:WDƟGL& f}f: ". WrVQ&9S1: iXU&yYƔߊ"S)#5ch;AFE k˅' *8IoJV-s( QJiySJRI[};CD<0}֪lMkDL6Yd4;E\L{hH~<\%׍3h8:K|Sv).7–$%`fHn]^x'D"yf$Kf5!z02#m{)l#[vP??mF5Jw佚34B ^}6Dpt?C)z+hL-~!MWyF}S=} MH .xמ+5%Wsd]l%Ā ;MYPA'F<!=B2Τ.J5xn[<pT|}P.0;UfAQeUnqb8y1bI\`$*^^*Z+%+v#Pif$,Q WRsS@T|FW@"cƔD֠d ]Z"AD|4FwQX}Q BV(IgKu*b(ùB۶ R'Κtd&A1% T8_`: Q'_C2vx9UQ)w?1҄(P)5~0஡< ^[ `k\ ]<1}TmVm!hUBu8~(~e* o \%F.w~:u\C$2.EXۜ?bE,8/Vm<-+fb* +z}y ^#:kS߽RL9/j@ ?)4zK&cحQ런,d˺qXw3 O.* *E/ yNԊ+Lnf`& Ɠ-3W_S>6`)P_+:~u]}3|27,هKYrM^/BQ0vwd7bpaAZ#VYٛ6x0aGI; @~y6 L-CW'@TfV-g=' A>šY#;OC*m'$W9'-,,%o2?Ti1aogNwb&CRE9|ާW%ED Ekxvse}` Z|xq0aeJ[?(a,'[88#Ë :h@>0&~e _V5;"&y=jD=5w9;*%r@60'?t7;b]_֮C(JcoL"F۪TB)SKt.> #XƚiK9쨲kx1ޮ\sɖo}}sxBvEifn63Gw}VFK= a0 oH5X7Z{4.O +\owk"f$!MW0 MpaO^\2)gVx/@)7;lPj?cޣ[!zS.I+*("/%n4TILJ$E5.7fOg!ZUTȲ;<ōK\/Mx>`=%fѦ`R~Lϻ3h_[FBitW,2,CzamT,@lX幰s)W7g )cp)pWK84DJ5_ \j9 N*G J8ofGl,7qAF9 c X{؈?%#FC h@Fʣv;0 yZ#dYH4ژ~^ δФ_% /nflrǼHK>(v}qbڌg/W턞UTpXЍoۨѝKSA ZD)d*g&JӜh"ezj4f ~1[Lpi! djikMAT#|$}ȏH[A}bMWWUϬj_6͑pyHzqĮz@TI'킲da!ܰG_JW-EIeJ ggVa%pN)g=R*i$}XDn"/sE]vLZ=̈́6*ͧz`{506+5Җ!h99)$(_0;Ӎ?N*jc lv_-q;N R3A3{E*uHiMdBqL뭊UwAB`1ؿ˝\dPByVLJܷ[' *wv#B˅GAhFfDŽU-Sc9YgGfwS[^DEֈ>{J!ܘc~R{BvKRkcHlJ@ƫ%Loil'{{\ Qm+,nL I k"TVigVx]ڪƊo?֍@\@)>&ěi ~R,陦|/WCXC RBpٵ'*sfd{3Fb-q5K79}OKJ@M,;Dq'-Ď:cB Lޟ\MIS?}?\|@ E`܄|fSؼ0^2uTEf^3_WͺMnۆ49!SY->T8y"n43͔ضL{`M奼_LýeK5Hñ2#u0mŭA~SAXө'YԔ95Ɇ3Z$jҿhM|g&ҝI3zڱTc wJV qY;sb@8&YrK[8!q_4 .(5^?$1(iBsKd2"& ErOpK} ՒMuޗxpC6櫃L9vLKEui{PX샷>s5 Zα>xސ†&HE-%%?`-T D>c2<+)Tyɥ-fΒ9)K*@ĵo~DA|$ E`"whq)^ $oOO˓8 \9g2]xԝ ސ-fQg,(K/dđR >bV`S޶4ɹ@ 3GL.Rk"ta77ʕ'~)@SNˇ1ONjDqU~W)AY@{u}dዐf2rwOBQO]8f̛Jr}U%JJx ]L.'YWCL,ߤ(Ȅ9@$"h9ɦôxt] rh>5~~% >i;%Ep-fi/\) é+ z#&WaA`-B/۪]rxWMáOls1]_^N2!#-jgXH@A ;>38\QM&!r 2W*V#v I"n z䪮!EZb/2P"9} utKoH2 .ԦoE4QĨmM ѯ~)1:'٪)E<隸0E س.<4)߽q Ĕ8mL`jU =kaQ ?nnPm:\FZ֮ëaXa?Kb [n1tE?/R7\dug+!>u+kTL3tqAj`]"CqքCBvֽ҃AsLʝNl>a+76p*ᅥ$a1-) &ɸ" 40,(tZĨ/z ]2rPs cUX\Mϗ_K8OΩRzE@\è.6f1^U*ҋ2iQ汬4z{zI4sm)*TPޡ߶|0U)~3I~\x #7eT[Lvz<hw#tP pbg䷊f]exÒ4GS{UBGPhCp>׊\YcJQC_7a$?I1Ň1@-ρ5eo,O)J^JKA &z)|(0^փf[ֱJGw!hGf(-mojR%m }Uߥ߽is :x{lp7% 52ݿK,!FJ;`fJV"(ҎFf(vAS2_:O`[㯻Sþ/710Ʉu!a-L lz7=ֲy~ͨn =**TR ߜQ`1d+qvj&'zg("Aj|+g{ĕh;r GUZj%9 kBp@SͲւP,[Uoo/&t5pP8O}u ](kVm Bii-saJ(R5i;|Q6S(kkV󙤂CWCy33_c&u #zzAA_d;IdoaVP%%A==K5v4ڴ}W6ܝMLBvGXNHw=nAC8iM++d9G:y ZQA$"(Yc- Ҥ!*U93aPT g)HbMB eIſ^0= IC`nGgG+a,#ƦzЫ=cѩN^w/=Ob.`[@Sf-ccտxcwȞ,bv}7_2.a6V Q߅.yrCݤ7hXjwqk҄xON'm6g:xD8l zR XE=ea}<:g6tՁJU ]nΡN'{sau{$M!G2K+V+'OW1:[*4< c$ÇEGIpIh̷ZWo"#=-)#^i(PYzr_Ż~˔me%Q8L !}I`09`n?QtHJ5,M9d=S)̦< |2\m7\WWқ=fydd+d]*qfZ*m(!DR]MW_. e,%mT̤~uh,Sc!a ATy 0Y`|49E_BN bXUebJBAxpz?AqV.ɆwG:tAl~0iU kDyglTynnfso=% &N* _o C÷qU dSy~@BiF Big@7[s"6mnT",;e2#Y(0BY3v;b)NxW@/m4 OТ*MF3лQސ.)ĩ? #hyn7jIeއ>#pe@T3:{YɃHw*lP{2z`Pe)/[AFcd+T~#qR/Ncr< ؎UP:6o5fUsF.7#Zsc ݗݔH& zEtH@KǙniA(@kzl/Ywj:)IC&o+POثK)dFh9a<]TW^A66KZL)elX?Y%v8Ҿ}cuA14/~Q.C6vkOMvV.d9E`_AW.ȖFGYT֫RlU1ďEwd<@Lw*lXP0۹BM)i&3!5ߟT-+ZRgӾ= {:lNq?pU][?'a gt4#h3m;PĸǬ4k#*bE] Q`afv ^ !bFZd]WC*zV/b)WaojvOc1#ZCa^* yX.FpTu&NLtS̡;َ9&]^ 5 0gKf1`)BZT/3ڎkVh J`,zUB@kC#oWjf?Hڑ.&-#t@O,sYE)-GKUoz.PdKKA1A1h_4t!L2ns͇ۃ,ˁܑˆi=vXa)"gUŀU:V (+\P [ G`4C^Y{r ^Gzو|I\Wn2 yGx!xRYj+ ټ^i륜{Yyk62Lr D:Nie{ut/i+;` jԫ2I%ɯ]ɼ@2͏ESL wYɽOe/W`)% GV^՘3/ݾ z"ȹ i7%lћ~3״>09:j p凳ik!ڭʐy# ` |QfqkU}Rx"hVD-rUjܣ꒟ao:6%'(3l.f+L5BHt X3:\MT| mL_<+{ɮ/_B4ګ)Ѹ'ρ9V*&yWp 5QZKCUy?'Fu`}K+g2 U(ٕ@hSŠAu[(fVe}%?X,43?X,2FC? zɡ x?15_']%oι<ߓgvpS(UUzܮU? Qoڱkq<[ t0TLv¥h:Hy 7wOE}7ĽGcl`5H`heVku w"XOS| gtI\7>$i\+`Qݗ*osy=%$ UL+ϸ|&jj)$):ǐ)w"RNoS]fSsn4Y1#s3Wj\Cx:ҫW÷⁰n6ڰi^Uɻ2B>?9dw ?N+E wzm|3g"1MCxxOJhC; -ɘ%~xQ‘Т {t()  ڽ ϶k9VtUULTIN BA+ e`mTŲtfh`)d&eDxِcT;D`Y }Sbol2R>R/^]wrfcL tmlvW#ڰI pH,x>r W"{,c iRӏbЇ`똲NڎuWPHdߐ_%\cI)"ey0Ѣ$ |kxKq^p:C!!&?<{U8'TVNy)97l:rT t>:QjX"S1Γ\X|Br< >tLjo@j04Q1K(2-!-$'L5j9U40FHoo0y5Oq{9Xg@4#4vqߌ"{*1-?EE<^)i䝇> cpg6>yZh]_.s|,8'4t۟p.wlxVЃjN׻#"W1b[ÔBCsWH^κL><}h˘/?$3ȅZ/twmDKz8yhƯKز۩Iʝ,ńyK0+DK}R/jzጂv߽FNrX!|NA+ßKԆ:G$/V]vJo, uy'"ˠ[$g*1vN#B,WHYJ4)y:y*Cu>2rt`.2ƻ"/z=7(]},U9pMdh bQ^h_ଗ8! U2Щ)WBz,Px%_ja5|U "J: 96PEFpi_(,^|RN7i#YC}O*.A\nydU?zM.yWԋJNIVRD%p-s1cڪ1an5mʙ1xn&McqULظS55^Ǐ 23F5y+sMk ?xQs 3DZ ? * 54XY|?)@ o|$䕯y|l6ҾtL!/Pg[/x8IV״HK5K|/Wb[| ,uRBmtA7)1en ^җgGKgWa%IvN~Ox;ԣeͬN]Q5C/ա聀&Rӧs6Bhk<f$}~ׄ Ufm_Ucn+.4%KwL5|s:cH|^|Lv?Z[a~"Kuf$&Qg ?4:! H2TB m<% d ó7h=+ϟ5r4)>ZyΏ*>C03 /ړY#h7ok⨝?؂r|gm6ިkbj$` HOeyˡ7*Jt+K{f]NΈd,]lR4lk9%Ue/qJTd*0BT 9GO~` D]Z*pwVcf:fդ7pp02Xnad<>lbLJ7q~~{@!YT81 x{l|y(я?@6{n- 8`95E5cBfNhĩdkr@I Q7gjaW X B}H,6iIAO~]wRI`|{LW:.tԖ+>ɲSn^krp,Hy4 L{;W_FZp]vϘWZ~:%K9+s#H 8#T6 n`[=K^2_ڗǃ-i]t4B;Ls{va&X/vԺY kbㄼ8l@ȰVkpcTӰc  l0!{7u7ҷhZ ֗w|a[0 -Ug(0O-=J⹯U1Q_P0c&&1\g?O֣锼_Zm%p R?/4lxh< _0Gj uQ c9LH\'?)}@[Uܱ \+g EY_nu"+.wHm)MN ,T)}&oz1T6,a|h="'r&^o񠧄B"QzYR7*Duz8GO*:9'DG !:^N]=aKF׳4} \h vZm1p,Sajb?e0$HM)|5?|QmO],J 4lY%!Vm$y-衬8ob*!S~֍(p5jT)HB6J2&j"LjxԖYK1|)Weo,ṛ+i HZaD5(!I-J4|E[}x`g~,AEpf]Vi K榿C,T洧7Jqni10WS`_R|? kEƫQZwmQjVאskx+>h7Є*BOڙ ^r|. ag 36T[n3#$5/;0,AUb(K/cRTѱz?Z^Gq|% ϖPV.bϖYco+xu}0ν o@ɜ Z Fm#ִm}'ҕYUw"Hgn*~=t#I.rN@VaT=ú`EMo d]{ddj\wo &Ta?d PaY(ʬh@ΥA @4]"OZ>rXʪ,YZTmF@ف$/NCg"C0.e⽏(;6 hO\JLX$sWTK*v6} V95*E9Yi¿g-*--Y qRgԹeLiBӖtt$"NdOHM:R:)O_)oqi v ֳmC =jdCZ7 6/]T q\f/-{m5U7fhQڕ<YqT{AT.~<-1v:11nI[R# L+rIqu")0:T<OfI'救[XڞX !uJ NPK4[-[FHSg@O4x0X1b?[@i܈qQo B-܄c'h "CUMxz߷JK:oq~quSQljeyR͚1Xd֡vWw{8fƸ3ɏMs$) \:[-(٤ :+wt9 0ϫ횩w@u,G|fA[G~IQݢB{Ărw0FEdݔfQ 60=9NPpwB D L q0i!í u_7rpTq[Z s8[b ͐V͠U7 k pTȮ탡pޙH50&ѺbaƠ0v >2)k%]{`B,m-jCm7RiB5q -p[SRPJؕ[&JBy})EqN '*Yx( *z5&@_<߷v4vi%)^DǠIELNT6_ ij Qm.= #S1Ri+օAiB f,sEOn>zFtk!gk 0uӜ VJavecO?';n= Z%+r\RjR@ .;۬77t&*ى'm2Yv4?W&5Qlpq,Ku%flb`E~—.lMlEh*5#Xcu&u5q-hUyS𵒼tw| ٪K{h~qyV"  mbĜ`Wlix9[u'x },㕶\]=xƛ^ dn=1|{ 8EL2kR"E1J;Lb"LBFKF: +JDH?iR6}2})Р'].uҿ(%q kgAb `a)p8y|1דzf&(q6M4xn|< P*X%Ϊ3 pUW[To/ n2ޤ%E`Of%j_bAx!V;6O9.c`BP )14)(J. XlGDSUZMsN} 8дeeWóav_nC<Խ]8:_. J>Xt;mRg궁S zfr=MN,갏bMݽ E4\yqV@ ࠊ>׆ H'oN,v_4c '_Fu|28<եCP~ihF;uHVӊ )AWT 5/L~:fKr{ ڭbl‘[v >fzkOO/`r{?qxZ?5Zƍne<$gmLT%BO8CbI6~3ո Ao>Yun}eBO\S{Hop mI\][X?h'6d2 '4`y)QW[us 8~zj5\q_c󤱃*#υFz;5qKx?^??=ai Ȏ¯V+6LGS?g3c)э/}W'fiؒ٨wTÞZS'0$դ{$,E\%=C :/ qzm|ekM!/Bmb"6. T&Ptx -3|ڼxّa/U44&y8MW)3eVa,6H-ohy-P\fK*9y2`ˆP”% !nYPBOԙ )9F f[tюZ=zؤ䯌p49V6=Zܘە&~mSF6x8;K &<Ÿi"^l|䨜dQfܷ9ꞻJJ"B Naḿ5iɌ Xg[Dm!2~N{p%$Uʎj{oЫ^->lsŜvv"?-vx ĂY}RX3'UW0u!0,x:&ʑF]P>Z2d#byׂԈ+S[Tfs#DH󢈞?'c|[>t1>PA3r)jF̓skTb\GQ0H 4x8OnbL|#˘"SL$Es :_=)57W^j!G΢b[%JJͳ]{y٬J)Rqxsn9Xj?>] >:߾پq9N<Ĩqqg_ X{{eVX xI|nb4I7c|մIs)&j{ӆ+E^t>'|R-Qo%PCF1K\B~{F1cVa搚Q7c d`a¤F8cz Wl4JǷ]˔Z* u)(PTՔn(i E'dG{0 C8 ͹F>l;M.xvU:(L섐ilHs1(f]= qX/-UcoM+ H* T*Es[]C 0  X]m!K7+"}.di+MIX.c+@_\a0M*cbgL5mlzS W!~"rAcQ yWAf4#N֧9nj0!?3ߺNS5f&OQAakX^zd+P@?D5((68v|gVl'p8r ?" V)Qyج*$ rzoM7 2s{k%ŃJ$1,<ҳ@Fxw MIh>8/ҳZcGn+-N4Y1 AW$Dٿ(0@!;!T4Y6w#㯇g``g;3Jmς^ n~=#^M)kzmnKXޓұ d.AgwӨ~[b=*R1ҥ`o\L SI ~ds .an c; ChWohDkj\%NVA|#rwzbYUMFO4\YHm 7A*0wFP9X%~@y͢0c@ݐi]zKвFK>WגLQT丮 U bBЍE0/+|C-^#K ξ2Tț\\hΎ{-T,t{owlu}@Ij>@>&9흖';/֗E@ܻOEA6 ᰣS&>VB*-qAXp:zR +L/ y 9\~4ʓ?]ۑ- Ɵ'~p/_q3rʃ˅l r- AV0y;bʗe0g$E[Q!C(då1S=#BTA<8$4U>.%qr}X}Х!$V>wC+Jsl *mلzޘFiy[$J S/pUOcaxb+:wBh;)=ZclH𨩌`q;Þy:>:"~(R^P&Zz̚\RK@/"eaI.Dkɓ\pH+3%> O9I4 j}rm1ujP"`F*w4tot!YP%q \ignZ U.V{nɼ Ūr4aeMvDXI%Y" uᘴJ5>+lh9䘆\lcn?]%ju"s ձTJlgݪ@%|;Z(,L= 2IUU#[l/>ɩ $Nc!Px10~2 6}IHWg$ugӎJ1Dv9P @4Eד!8Nm LR!@vaL ʹ F;V؂AFR~Oݤ4{5W`OMGIDKY9ި'Nd)'(J9@$'L,tԁ/o쾺ʓÐB;ۈ..EgeѩIةq ["gź1E-rCo޸IǡJS3*knfjRz7tkg BK %WW r?.]V|wn WgDnW102,q:6Ϩ"!2ܰ)S #ԻW+-*t,}}{F@sCuuVyh9n>}$:Ӑvml9O!Ӓ~eJvpTeu@N[/S7.:O`hUȾe Bn.UNḯZU1X}ZU] R?V4ҝ$I r ٛP8fnXV:_Pa5j;褂_%23=5HdnpA 0)_ރ~mV ϝh숭h_\z "v!A\Y|N~ v:{ѻ̕F dC)%fIB`$%ґ)&^> ecVS_R\Yەdp@-ͅ<[I8Wؕ#IA;0U/꛾l]/-7$%/Uもc*F'e *qK+h F*18)I(!%@Dgw ?9r x<ѷDO.zcW4{!2(!V e3:(8dlۑ]n*vP0Nuj4d/$ρt_%:{[@L.ۀjo7j%;}8cAEa!/O atg_ l#ݤ~v\aӎvڃH:`~-$P$mBx.s+RQsL]9J3pho)+Vފ-~kYRĹʛIB\S };9}6uv 3e- &!t\A^錯`F?P?ȿ>>7zs\ޖUwC &]Fuc(σ ʢٸ3l0sBOD~#fI )効zJ5r\]q(&k問2F'w4,yˡfwY V }-4miR[qiBfi/bGtiӢ?Zqp =ރ rRf[Kp*AIqcn ?pa u}ͻe*O'VqڹD{ԫJfJ3^6)<~JIщG D=Tp{:޳WܹSvڴ2{\> ro+ if"r m?@.oz/.G'{K K'!gGtDlO]v $%L䞸ENw֛baQ'$<睊T rSx(tjBGGgR&[o* pĄGz & H߸KGa5CZnN魥H$@׃ NzҤ*}<ɼ#I׌U&2%Uf8q.D뒗Ie98-ɴ`jH_NIXu愗pY= KH)n6GTBIjEo5YhR[3$>Q3+C{ZPT 􊂇\L9#>&H9 pϦm޺;7GL|H*4/;.c,2ʽ[.iɍ ސwIԭdG 0kMj> 'b50^Ix 5*h=OvN-X$fPr'q#&E@N%C2yR]13q}w1!;U$e}H ˑ/>H( W_N}fW#dOMntH=$'#KS"`HbAР)<*p7ވOW.pq,Y<BY." CÏ1Yt{;T`p% ``R\(HLO֛GѸ}-NZ|O* TsM~<aD T3kLRv㚸|³,҅85An5xZ!}=f])mB#zf?>36|$Ƅi|&N&5^'tGey QfW:n lW6<`}ll4sWe:5z2X2Ҋ |Xk%ah4sc;f֮55=0Ff}ceDt(#,113"Trr/Q2 xϴf5fX oy8zJj czW$o>&7:xY^re$C >5 K߻xZl(GBO?T?c#*'^ܤ^;TD&dR܋2 Y!'\L%xtEtԄn:/4khWn44wV=TM/0<;˨S)3@Em*w@cgաU~+d.eמ'h8co@_ r~R. *9XRt@*[=zB\DVt g"zKogP3t3>y@BAzdN Ԛ"kHWWGџq7ttmR(~RGC!1Ee|ú pWR+ڤ~w/쐝O:i K4]CPCyYjUOcTu0 <ŧ<ͤ`.i6y|0^b^@h/Y!Lʯ3Wjpb Qo0[ĵLm6w>oK^y*BI#vbKyU:!5MO0fMIG/У`dPe93j?C5^+؍fy%W 46;Vo aKÂݶp:Ck wn!D{@cp2ud 3)m]91Fc]m +kg{jP*ՃhtjPI:)->cWࠊ=Ԇ6ش6is*:?Wp[ = fĸ 8MΒII#%E]3-1ieîP_V`{D7vqb fo+;Mw4ex P:K;ym\Y.Oʛ$^O2Gy?6a%BL\ vK, wZ gRDt2\w1wI$[*iGN' $DV>ՠ|Zt^GM5FCTuՋtMPhnk |9VG">Enbl4 (v~+Qu-=R@483@+ Tpl{}dEJVmCpαj-Fǡ.+P7C(TN5ĕ2~y5TNUF|}'7i3Y{cKPU#(3)%nL[{$s}4uĽ05ݵ?O<"LO}N-}bfPVAwѲ jŧ#)W_J՗8Y>!NHbxRX:>Z0f+q{ByQeELifgR'D54򑆈beUquHE폥:F<]>u mpo㡇%wjQvWȢJ}nh@gH$rhJBB-IYQE5Vw8SHim|tk0ɛDHYQP)A_נJi0~Mex,Z*6yBՕZI )DTc UCc`7*Pr7 Rr]4.s~v)˶q =cŷ0Dr͞ vuw1eD(Mim.? u8EF9\:aheRQ9]q"KS/%OXnM*{"3%#zU˨s'G~a)F:5C,qPH7!"[tWzi:ol7h#66 5mFB|_Sļ#]\y%(-0kҰ$9[c ;w)7H[B- po0 @ݰmme?9o(Ed}2f1P[em̏^&~%+-eqKW@P\O"WG0&B,\*lTY6UG:qW}?⻆c-VX P%K7߿xm(`~hr%0A˱",M'Ck['w)Фc8躰U%_чBډO\C&'Pxf|,:_q1ZzW9Qjt}!. F6lA*#zH |8oxj!7t6SU8:֏%[CD`VO0sķPo&ڣJ ;حlju Sq$ :,߾=k|#Jz=d?q! / 큙C$(Zljc,M Tdg[kcV&<~l!d9Hu uH;i[9L|b<СA`K bW>yOfB&凧iḠuKLy3;X!ܳn͒A9J._̇އB6y(#RIFxii-mC, y3$0D_Nڮ3̯n.rn2%awh <,P\ƕ_'n}OvX*)`R!MxI1fo2ǀ)Wj]6LQ4ux/xZRmz&~yF\YIM+:h.g?<ŽRj`&"=y(Me:.VVc)Xۧs|Yla?p@͡ҋc踏1iw!Z' (i80Q@vVQjhӦ.'O5-! w-a[h*q61c.;5>rO?UʆO6,׻`6 QzJsiI&8g.gIiv$N 7Q22c{*aojjj0n$Vj*# ?wX>O n9dc QT|ThńtxJ![1#-B}!hsMn85ʓgW8 Q?˔k}{Է<*0~,Ϡ <_u-B qhR>N!Qh8F?a9s;)Tn;dxհ_%#DM  0R!~}oOHk{șd-V[^-I2AɔjXY Lͤ+ۦB1Q74zp(5&]x}c(U\W~%ンf?<;D`UAD_kg,_,h)ehS ĭbNҔl},fCajyWQpy/R],P6A3U :{[7}(}B" r ~7Q\kX/}žcU`Ɏ `s"5_&uзtXxpO]ǖ9ϕ]ic,~aGKL~هy:}O4+ 5AWY㇪ *+5PoC r_Sl.GLϐ}{{QUyp#SB'PIbWphNԵI&<[p}Ykڴpus\pfy@ ]'OY&5l+g?itlQ&4PP ÿ́BJ^;(v4mA} B н|PK,d[iU@ szv;pTڥ=Y\ 7t!=̺&wO^l}z #va*] XY](rԓS?crnY\㈄Bpirr38PopSAE9 !{\JTqD/)c\uHmF?߿Lf[N=@}, -znz2kc" 9QWc,(7xQQǡDfeiσ N _h2B #tR5 yw˖1c([LEi,h:GzE#p:,m/CAWIINN^|@pjɦa۠t>̦w5)&=AfCz( v5u`Y:KOB"f_]@9*e-9f]$:AmGʮ5E4Ul:5_oa^#)1YOji+fJݡa!r 8R<+fykٲ@ǸyČ2@dIּ:̅q&ow_8^@Tjsֳث01TUҒz[[c0wqL~ROmYMv!IU@$nc>1v3@jC%*п"1j.u#Iӽ@e_f<0"GMax IK }C] 6Vf34ɬa0!Na W{7|oqyۅse0VPI7qT+}OӳE k7pJ2nuh<֖'AMȂ C8ѫWC>BoyH"RrMusM K\_v=ֱ{G)ܨ@!=qݵLeNaP쮧b{l?;tdT0Sv#8uyhG]:.a_Wh0p}Cq~\rZҞP&WO'Ktpok%4 Itbg Ѳ%dsC-g,@n2~'5+(2rOU3nd JCgxD%kVر1eEWB6R2r`ttP1sT-ROTMm P+%k't:ja5ZhҟgʚH_`W ܥc k _@41c̍a\yrjkTah ;d7$Kϓi\vk+k3 hV|bx&ਫ਼q :8rt]؃MeQ6Gvb4joȟ,E$?VڎXk=藊 X v (iȪbʟp Y-'&I-)mk#6m9߳'A])M¥YRKB|RڼhVAhcԑpGlN=$ӝ/>#"k!׏ߘ&Eg ?IuvO@3=J;"B涣:\kMYߙ6"nS}_ɚ3ۭ֪j(߼m cp0?|<%-ph>O0p7vk銵˖0xC~s0C e-_/á*6;(K6叀K*X\u f+V}Ovxk9~,pX[\\j8&"fhL1do@oqPeǛ+e9k}gυ PP\oѨ^-|4oz=qo)59k# L f4s -扜J7/xz4 _y}dz,MLo5n2?O!&rr^Lah1;Iy<_x;hMU95`|Q*υ"%eb(@sԍ +XpXRpL.pTMBvCCB|ݝ8t3sZ߄h,˪HY/`i}1 i?H0wA~= ,c/A/>Ʈ+Tlj9 =mU90$C_D%؇{euG;^5NMaI@/\t'*)O@e-D-*NvK\IH/`sImz yKw[Q̊DXLYy( y䥄:c[qtD5zt//S}ם'hbraz4BH%r(?'(5nzp1C\<lj] an=6 rwهcl%aG6EadXAZG|5e`[*CP4JrV1Ph@6l)ٝҧgyțo,Y Ub4,XQ:XNj-*_b`X?ϫ!<ӵ9v{}wm٪e[&IAP;30x2΍z2A }ki$ 5uQ,\ qDS{VGȈfx8z.&.q|<1;O[ %R;,1Ei52c-9&Ob1F91.Z>#`z;he )l{^H3K0OH \`곈:;t* Jr}LhϤ`鯺rM8[|+++Qr~;Uj^vcK<ʕmb>Mjgq^I4h3hxO9 Br;=c3p^OhEI^w X/$p1>ԋ 0NO&7%<t-sJ9;Ek>jPm=cˢ?@o|%~u.ABG<ǣla-fN Hgxds3mhWT^`})&uGB5F!n?)*kiMmL rs!+W@A} vLa➹`+_im{s*Hntyo#dAGDP2!9.]8KSMDdlacoF_Eʈ$"\E9Ux~2Vq xCBL*<={& ] Ͼ$2oM6ϭ #ozaF8BsWi1RߺXѡ&Q4Z`9#P8d*kHUNAȅ*io7bo/j6w ~J TPn#{2MLV@j)71#% nĶi9C,K̇.!iKLy'.n*~Jk$a/k$@4%lp¦FZm8<P*]#}U*1ϖa\6aWo1EILfO"?*7L>uRJ]}g>Sw5q5w-dSUXF! +jp yИķ[7~w=ٿܟ%a_ Ěڞxj^Nnw(*E[E[X}I^2wW41~Ds9NZ薦X?,|K]Q _q{&l\f Jc`+Ll2[&YȄCHOXrlÌjbJZ o<j+pRĄ<'CT4^igwQOC/{Ko9U]1F8.#fAӒ-r}0?b (6<ȡ㓬;s;H")L(,QMxr.'rVxl)^u0Jf*|! -)(:믬?%HW|߅i"hPɞ^ @-?5{, A 9˩6q{w2[XGǢ 31MbmM7XKhMV Y0={WynKv\C i60ݝ^uE%vc,8mZהfD'/3\]Y Xn rtΣMڷy (VŲz B58~]ӆ6ND[PzQl05oDCr:غru8Wz^,3ȏ>Б-QteE~#|!SkLb1mߤ#j#_fRкƲ$Ljqǿ4͍3o;uGՖ`ot`ޗTj79ځ ]aP8c4{)t?yN`ze} 4W$!-)\W2-p b"*U7)6|~x>@8OG"oElPDTIROI iؗ[%Ma( Z-}bpJZIMi* 9`~i\ˎN\ ؅& t1?Ô|S c6IbχLUD[.o9u2V eD-7cxd,MF5;KkV&H.xo%⅏3Zn0!7#8 p$L_KҊVb Aal쾺:&V.-dsݤ T +ٜ|N}hRs/!"vY@z[Vu TG v&[ J}`,@KϹ4!\d[1ϮHvO~6ʅ3D?7}0^>L>pk;%B:( YhEN9?$Y=v1uCXŠRX|olC mq0?9tk%|"9s1rٗmg ,2kG]Gpv>%B 8Cj@)1sн&>pDrk_{>הu<Jv$aa1{92"vs(lHe3sI=9Y_T7Ηofve$eT9Pu>Ď! mũ )~Ӊ^S_ָτe=we:7LgVNfk`Ҋ[>@rۺddm>1MsEրs=;浢fw<}R0NًkrsYH=,<;1޼bi0R`eֿ璒QIa^:+R _3w?WX7xv @^alr|;'e|܄S6|2=(Dx_: ŋÒfr X|0P_RŧW;=P -ϩ*_sHij!m3GL9\O3)$Z͵gumYr_VCb-^6?+ȟPPv\y"eo FTv=S%6fj҂u<)l}H*ΐQz Kr<%Ў$ 4΢k7TWg@0DK03wt95@/[e#1qGJ[ E -a0&R }\Y# (EQ^jӿ\R K g4$$yY#RTâC3s|X9tjbG\sʁOd#EڒMcOxIʧ%|2ˀc"CG Nzm)F=#ܙ6h0mKIJ=2=I$SrL=V3I0#wT'cl%@s`ZGG:{C[vi {9NAM*͠M^!?Eus01dA-W#c߹Y)^b@4t𥁟W.Om`)"Cڎ8zNA2 z֟r`|;ebvbfhksxݐCZkd 'FRM$ !9};5jԙ[Cw r?Ct*aȮi#@P^ R#_Z3 zEk媛rp=^DB́(RdXKx8aМv$騢+aZ3f6:ÿU2JVi9򌦁s/5O-42fp0dCbus]5GY'bfsoKLF7c5)֭wQ JK$hbbrPem i"%S3اL%KTQ4Pp=A=.2,{1tr\?aI#ܤ>9.)p@Ed|p}e#N+h;b05P3sNx[II7%KʱvU2HP=0<C*v\,5iOb2Ǩ<:FXݱ;y/ v!>l@-nPd46xy,1Dho@8yڶ\+ ˣu Jׂ߿qp㟼~1cx B8>Z29u߳3wN1v3: b r_CǶleޡ:} (YۓXaeX:DŽL~7^vP/a8t|Dw „c"B2,d;[WjmM#)a#/5΂ڋWo]h߭箎DBW0^gsS˒Gjq? ~@ {;yOi}Jen91t*'@-)51̀<ȹ!ObҥroɰTBؕ:d57˅Aҷ' F$փמFD */ˆ8eQ7 nIblQ5~)Si⬘VPǁќ] t*1dҏsd8"h֠n J&֕6n dܢcL9HGU!^ZRqyμ7t.$ȊG'Ko38@+JEi+Ex ;-W|ċj@tN8x(B%4nB*@W;%~?CiO4l7($ %-]#n`̀yjC,Ƅ[rpҎ-mZ?!ɕ }i—7(gY%ٱXA?llbY^b_K`ivK^RcnEA@*T P4‹6= QLvXO3o͗^Nr>㼋Lk^r<}( 7 ]N1/ ]`w. /Wo>dHP3bRFRyZyVG /0}[MGj}}4Ϻ‰\"=<1ޚr(8ٖo'>w$*i]ؗBPXZ@3eAYnorYfcI [b WԆ;,A סWj|0k%*L8}DӌBCe`"^TRdyY+p[jGO(YSiwj?$ j p _e9tær~0OeReN[\5Iw%IfFK1[-y _Qk4q `6 .^\wmxLzs'\6Ma`y ŏTv×aQ,UiUP]^IB`p~ӑ;kMq.~,1@; μO]<~{MO㞃ˮAϭ.m*X__աּc-'#l4ZAUĄH4kU&K5 \G rOwdxNzxc-0.13.0/conformance/valid/seekable_1block.expected000066400000000000000000000200001522536370400225200ustar00rootroot00000000000000Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut ezxc-0.13.0/conformance/valid/seekable_1block.zxc000066400000000000000000000004561522536370400215400ustar00rootroot00000000000000.]gLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. | zxc-0.13.0/conformance/valid/seekable_4blocks.expected000066400000000000000000001000001522536370400227050ustar00rootroot00000000000000Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut ezxc-0.13.0/conformance/valid/seekable_4blocks.zxc000066400000000000000000000020611522536370400217200ustar00rootroot00000000000000. <gLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. |nim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore ete magna aliqua. Ut eZ|geiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do |gLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. |zxc-0.13.0/conformance/valid/seekable_checksum.expected000066400000000000000000000400001522536370400231510ustar00rootroot00000000000000Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do zxc-0.13.0/conformance/valid/seekable_checksum.zxc000066400000000000000000000004621522536370400221640ustar00rootroot00000000000000.>]gLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. o@ozxc-0.13.0/conformance/valid/short_matches_4k.expected000066400000000000000000000100001522536370400227520ustar00rootroot00000000000000   !"# !"#)(+*)(+*23012301;:98;:98LMNOLMNOEDGFEDGF^_\]^_\]WVUTWVUThijkhijka`cba`cbz{xyz{xysrqpsrqp$%&'$%&'-,/.-,/.67456745?>==<   lmnolmnoedgfedgf~|}~|}wvutwvutHIJKHIJKA@CBA@CBZ[XYZ[XYSRQPSRQPDEFGDEFGMLONMLONVWTUVWTU_^]\_^]\`abc`abcihkjihkjrspqrspq{zyx{zyx  ()*+()*+! #"! #":;89:;8932103210蜝defgdefgmlonmlonvwtuvwtu~}|~}|@ABC@ABCIHKJIHKJRSPQRSPQ[ZYX[ZYX,-./,-./%$'&%$'&>?<=>?<=76547654  ȼ  0123012398;:98;:"# !"# !+*)(+*)(\]^_\]^_UTWVUTWVNOLMNOLMGFEDGFEDxyz{xyz{qpsrqpsrjkhijkhicba`cba`45674567==&'$%&'$%/.-,/.-,  |}~|}~utwvutwvnolmnolmgfedgfedXYZ[XYZ[QPSRQPSRJKHIJKHICBA@CBA@TUVWTUVW]\_^]\_^FGDEFGDEONMLONMLpqrspqrsyx{zyx{zbc`abc`akjihkjih  89:;89:;10321032*+()*+()#"! #"! جtuvwtuvw}|~}|~fgdefgdeonmlonmlPQRSPQRSYX[ZYX[ZBC@ABC@AKJIHKJIH<=>?<=>?54765476./,-./,-'&%$'&%$      !"# !"#)(+*)(+*23012301;:98;:98LMNOLMNOEDGFEDGF^_\]^_\]WVUTWVUThijkhijka`cba`cbz{xyz{xysrqpsrqp$%&'$%&'-,/.-,/.67456745?>==<   lmnolmnoedgfedgf~|}~|}wvutwvutHIJKHIJKA@CBA@CBZ[XYZ[XYSRQPSRQPDEFGDEFGMLONMLONVWTUVWTU_^]\_^]\`abc`abcihkjihkjrspqrspq{zyx{zyx  ()*+()*+! #"! #":;89:;8932103210蜝defgdefgmlonmlonvwtuvwtu~}|~}|@ABC@ABCIHKJIHKJRSPQRSPQ[ZYX[ZYX,-./,-./%$'&%$'&>?<=>?<=76547654  ȼ  0123012398;:98;:"# !"# !+*)(+*)(\]^_\]^_UTWVUTWVNOLMNOLMGFEDGFEDxyz{xyz{qpsrqpsrjkhijkhicba`cba`45674567==&'$%&'$%/.-,/.-,  |}~|}~utwvutwvnolmnolmgfedgfedXYZ[XYZ[QPSRQPSRJKHIJKHICBA@CBA@TUVWTUVW]\_^]\_^FGDEFGDEONMLONMLpqrspqrsyx{zyx{zbc`abc`akjihkjih  89:;89:;10321032*+()*+()#"! #"! جtuvwtuvw}|~}|~fgdefgdeonmlonmlPQRSPQRSYX[ZYX[ZBC@ABC@AKJIHKJIH<=>?<=>?54765476./,-./,-'&%$'&%$   zxc-0.13.0/conformance/valid/short_matches_4k.zxc000066400000000000000000000040661522536370400217740ustar00rootroot00000000000000.]  ((    !"# !"#)(+*)(+*23012301;:98;:98LMNOLMNOEDGFEDGF^_\]^_\]WVUTWVUThijkhijka`cba`cbz{xyz{xysrqpsrqp$%&'$%&'-,/.-,/.67456745?>==<   lmnolmnoedgfedgf~|}~|}wvutwvutHIJKHIJKA@CBA@CBZ[XYZ[XYSRQPSRQPDEFGDEFGMLONMLONVWTUVWTU_^]\_^]\`abc`abcihkjihkjrspqrspq{zyx{zyx  ()*+()*+! #"! #":;89:;8932103210蜝defgdefgmlonmlonvwtuvwtu~}|~}|@ABC@ABCIHKJIHKJRSPQRSPQ[ZYX[ZYX,-./,-./%$'&%$'&>?<=>?<=76547654  ȼ 23;: !)(^_UTLMGFxyz{xyz{qpsrqpsrjkhijkhicba`cba`45==&'/.-,/.-,  |}~|}~utwvutwvnolmnolmgfedgfedXYZ[XYZ[QPSRQPSRJKHIJKHICBA@CBA@TUVWTUVW]\_^]\_^FGDEFGDEONMLONMLpqrspqrsyx{zyx{zbc`abc`akjihkjih  89:;89:;10321032*+()*+()#"جtuvwtuvw}|~}|~fgdefgdeonmlonmlPQRSPQRSYX[ZYX[ZBC@ABC@AKJIHKJIH<=>?<=>?54765476./,-./,-'&%$'&%$   !!!!!!!!AAmmmmmqqqa zxc-0.13.0/conformance/valid/text_1k.expected000066400000000000000000000020001522536370400210710ustar00rootroot00000000000000Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do zxc-0.13.0/conformance/valid/text_1k.zxc000066400000000000000000000004421522536370400201040ustar00rootroot00000000000000.]gLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.  zxc-0.13.0/conformance/valid/text_1k_checksum.expected000066400000000000000000000020001522536370400227530ustar00rootroot00000000000000Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do zxc-0.13.0/conformance/valid/text_1k_checksum.zxc000066400000000000000000000004461522536370400217720ustar00rootroot00000000000000.>]gLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.  zzzxc-0.13.0/conformance/valid/text_64k.expected000066400000000000000000002000001522536370400211620ustar00rootroot00000000000000Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do zxc-0.13.0/conformance/valid/text_64k.zxc000066400000000000000000000004431522536370400201760ustar00rootroot00000000000000.]CLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. zxc-0.13.0/conformance/valid/text_64k_bs2m.expected000066400000000000000000002000001522536370400221050ustar00rootroot00000000000000Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do zxc-0.13.0/conformance/valid/text_64k_bs2m.zxc000066400000000000000000000004431522536370400211210ustar00rootroot00000000000000.jCLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. zxc-0.13.0/conformance/valid/text_64k_level1.expected000066400000000000000000002000001522536370400224320ustar00rootroot00000000000000Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do zxc-0.13.0/conformance/valid/text_64k_level1.zxc000066400000000000000000000004331522536370400214450ustar00rootroot00000000000000.]Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. zxc-0.13.0/conformance/valid/text_64k_level2.expected000066400000000000000000002000001522536370400224330ustar00rootroot00000000000000Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do zxc-0.13.0/conformance/valid/text_64k_level2.zxc000066400000000000000000000004331522536370400214460ustar00rootroot00000000000000.]Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. zxc-0.13.0/conformance/valid/text_64k_level3.expected000066400000000000000000002000001522536370400224340ustar00rootroot00000000000000Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do zxc-0.13.0/conformance/valid/text_64k_level3.zxc000066400000000000000000000004431522536370400214500ustar00rootroot00000000000000.]CLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. zxc-0.13.0/conformance/valid/text_64k_level4.expected000066400000000000000000002000001522536370400224350ustar00rootroot00000000000000Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do zxc-0.13.0/conformance/valid/text_64k_level4.zxc000066400000000000000000000004431522536370400214510ustar00rootroot00000000000000.]CLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. zxc-0.13.0/conformance/valid/text_64k_level5.expected000066400000000000000000002000001522536370400224360ustar00rootroot00000000000000Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do zxc-0.13.0/conformance/valid/text_64k_level5.zxc000066400000000000000000000004331522536370400214510ustar00rootroot00000000000000.]%Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore ete magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamcois. OZYW;zxc-0.13.0/conformance/valid/text_64k_level6.expected000066400000000000000000002000001522536370400224370ustar00rootroot00000000000000Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do zxc-0.13.0/conformance/valid/text_64k_level6.zxc000066400000000000000000000004341522536370400214530ustar00rootroot00000000000000.]Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore ete magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamcois. L_ZYW;zxc-0.13.0/conformance/valid/text_8k_bs4k.expected000066400000000000000000000200001522536370400220230ustar00rootroot00000000000000Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut ezxc-0.13.0/conformance/valid/text_8k_bs4k.zxc000066400000000000000000000010401522536370400210310ustar00rootroot00000000000000. ?gLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. <geiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do < zxc-0.13.0/conformance/valid/zeros_4k_checksum.expected000066400000000000000000000100001522536370400231330ustar00rootroot00000000000000zxc-0.13.0/conformance/valid/zeros_4k_checksum.zxc000066400000000000000000000001451522536370400221470ustar00rootroot00000000000000.>]5;?,,zxc-0.13.0/docs/000077500000000000000000000000001522536370400133365ustar00rootroot00000000000000zxc-0.13.0/docs/API.md000066400000000000000000001560211522536370400142760ustar00rootroot00000000000000# ZXC API & ABI Reference **Library version**: 0.13.0 **SOVERSION**: 4 **License**: BSD-3-Clause This document is the authoritative reference for the public API surface and ABI guarantees of **libzxc**. It is intended for integrators, packagers, and language-binding authors. For usage examples see [`EXAMPLES.md`](EXAMPLES.md). For the on-disk binary format see [`FORMAT.md`](FORMAT.md). --- ## Table of Contents - [1. Headers and Include Graph](#1-headers-and-include-graph) - [2. Symbol Visibility](#2-symbol-visibility) - [3. ABI Versioning](#3-abi-versioning) - [4. Runtime Dependencies](#4-runtime-dependencies) - [5. Constants and Enumerations](#5-constants-and-enumerations) - [6. Type Definitions](#6-type-definitions) - [7. Buffer API](#7-buffer-api) - [8. Block API](#8-block-api) - [9. Reusable Context API](#9-reusable-context-api) - [9b. Static Context API](#9b-static-context-api) - [10. Streaming API](#10-streaming-api) - [10b. Push Streaming API](#10b-push-streaming-api) - [11. Seekable API](#11-seekable-api) - [11b. Dictionary API](#11b-dictionary-api) - [12. Error Handling](#12-error-handling) - [13. Thread Safety](#13-thread-safety) - [14. Exported Symbols Summary](#14-exported-symbols-summary) --- ## 1. Headers and Include Graph ```text zxc.h <- freestanding umbrella (no ; kernel-safe) ├── zxc_buffer.h <- Buffer API + Reusable Context API │ └── zxc_export.h <- visibility macros ├── zxc_constants.h <- version macros, compression levels, block sizes, dict sizes ├── zxc_dict.h <- Dictionary training, save/load, identification │ └── zxc_export.h ├── zxc_error.h <- error codes + zxc_error_name() │ └── zxc_export.h ├── zxc_opts.h <- compression / decompression options structs │ └── zxc_export.h └── zxc_pstream.h <- Push streaming API (caller-driven, single-thread) └── zxc_export.h opt-in, freestanding-safe (no ): zxc_seekable.h <- Seekable random-access API (storage-agnostic via zxc_reader_t; usable from kernel-space) └── zxc_export.h opt-in, userspace only (pull ; not freestanding-safe): zxc_stream.h <- Multi-threaded FILE*-based streaming + the FILE* open helper for the seekable API (zxc_seekable_open_file) └── zxc_export.h └── zxc_seekable.h ``` `` pulls only the minimal freestanding-compatible core (Buffer, Reusable Context, Push Streaming, one-shot Block APIs) and is therefore suitable for kernel-space, embedded, or any target where `` is unavailable. `zxc_seekable.h` is **also freestanding-safe**: it provides the random-access reader API on top of a caller-supplied `zxc_reader_t` callback. It is opt-in only because most consumers do not need random-access decompression; it does not pull ``. Kernel / embedded consumers can include it directly. `zxc_stream.h` is the only header that requires ``. It groups every `FILE*`-flavored entry point: the multi-threaded streaming driver (`zxc_stream_compress` / `zxc_stream_decompress`) and the seekable `FILE*` open helper (`zxc_seekable_open_file`, a thin wrapper that builds a `pread`/`ReadFile`-backed `zxc_reader_t` and delegates to `zxc_seekable_open_reader`). Kernel / freestanding builds simply do not include this header. --- ## 2. Symbol Visibility libzxc uses an **opt-in** export strategy: | Build type | How symbols are exposed | |-----------|------------------------| | **Shared library** | Default visibility is `hidden`. Only functions annotated with `ZXC_EXPORT` are exported. Internal FMV variants (`_default`, `_neon`, `_avx2`, `_avx512`) are hidden. | | **Static library** | Define `ZXC_STATIC_DEFINE` (set automatically by CMake) to disable import/export annotations. | ### Macros | Macro | Purpose | |-------|---------| | `ZXC_EXPORT` | Marks a symbol as part of the public API (`__declspec(dllexport/dllimport)` on Windows, `visibility("default")` on GCC/Clang). | | `ZXC_NO_EXPORT` | Forces a symbol to be hidden (`visibility("hidden")`). | | `ZXC_DEPRECATED` | Emits a compiler warning when a deprecated symbol is used. | | `ZXC_STATIC_DEFINE` | Define when building or consuming as a static library. | | `zxc_lib_EXPORTS` | Set automatically by CMake when building the shared library. Do not define manually. | --- ## 3. ABI Versioning libzxc follows the shared-library versioning convention: ``` libzxc.so.{SOVERSION}.{MAJOR}.{MINOR}.{PATCH} ``` | Field | Description | Current | |-------|-------------|---------| | `SOVERSION` | Bumped on **ABI-breaking** changes (struct layout, removed symbols, changed signatures). | **4** | | `VERSION` | Tracks the library release. | **0.13.0** | **Compatibility rule**: any binary compiled against SOVERSION N will load against any libzxc with the same SOVERSION, regardless of the `VERSION` triple. ### Platform naming | Platform | Files | |----------|-------| | Linux | `libzxc.so` -> `libzxc.so.4` -> `libzxc.so.0.13.0` | | macOS | `libzxc.dylib` -> `libzxc.4.dylib` -> `libzxc.0.13.0.dylib` | | Windows | `zxc.dll` + `zxc.lib` (import) | --- ## 4. Runtime Dependencies libzxc has **zero external dependencies**. | Dependency | Notes | |-----------|-------| | C standard library | ``, ``, ``, `` | | POSIX threads | `pthread` on Unix/macOS; Windows threads on Win32 | No dependency on OpenSSL, zlib, or any other compression library. --- ## 5. Constants and Enumerations ### 5.1 Version (compile-time) Defined in `zxc_constants.h`: ```c #define ZXC_VERSION_MAJOR 0 #define ZXC_VERSION_MINOR 10 #define ZXC_VERSION_PATCH 0 #define ZXC_LIB_VERSION_STR "0.13.0" ``` ### 5.2 Block Size Constraints ```c #define ZXC_BLOCK_SIZE_MIN_LOG2 12 // exponent for minimum #define ZXC_BLOCK_SIZE_MAX_LOG2 21 // exponent for maximum #define ZXC_BLOCK_SIZE_MIN (1U << 12) // 4 KB #define ZXC_BLOCK_SIZE_MAX (1U << 21) // 2 MB #define ZXC_BLOCK_SIZE_DEFAULT (512 * 1024) // 512 KB ``` Block size must be a power of two within `[ZXC_BLOCK_SIZE_MIN, ZXC_BLOCK_SIZE_MAX]`. Pass `0` to any API to use `ZXC_BLOCK_SIZE_DEFAULT`. ### 5.3 Compression Levels ```c typedef enum { ZXC_LEVEL_FASTEST = 1, // Best throughput, lowest ratio ZXC_LEVEL_FAST = 2, // Fast, good for real-time ZXC_LEVEL_DEFAULT = 3, // Recommended balance ZXC_LEVEL_BALANCED = 4, // Higher ratio, good speed ZXC_LEVEL_COMPACT = 5, // High density ZXC_LEVEL_DENSITY = 6, // Higher density: adds PIVCO-Huffman-coded literals ZXC_LEVEL_ULTRA = 7 // Maximum density: PIVCO-Huffman literals + tokens } zxc_compression_level_t; ``` Levels 1..5 produce data decompressible at essentially the **same speed**; levels 6 and 7 add a per-block Huffman decode step — literals at level 6, literals *and* sequence tokens at level 7 — trading some decode throughput for denser output (level 7 is the densest and carries the largest decode cost). Pass `0` for level to use `ZXC_LEVEL_DEFAULT`. ### 5.4 Error Codes ```c typedef enum { ZXC_OK = 0, ZXC_ERROR_MEMORY = -1, // malloc failure ZXC_ERROR_DST_TOO_SMALL = -2, // output buffer too small ZXC_ERROR_SRC_TOO_SMALL = -3, // input truncated ZXC_ERROR_BAD_MAGIC = -4, // invalid magic word ZXC_ERROR_BAD_VERSION = -5, // unsupported format version ZXC_ERROR_BAD_HEADER = -6, // corrupted header (CRC mismatch) ZXC_ERROR_BAD_CHECKSUM = -7, // checksum verification failed ZXC_ERROR_CORRUPT_DATA = -8, // corrupted compressed data ZXC_ERROR_BAD_OFFSET = -9, // invalid match offset ZXC_ERROR_OVERFLOW = -10, // buffer overflow detected ZXC_ERROR_IO = -11, // file read/write/seek failure ZXC_ERROR_NULL_INPUT = -12, // required pointer is NULL ZXC_ERROR_BAD_BLOCK_TYPE = -13, // unknown block type ZXC_ERROR_BAD_BLOCK_SIZE = -14, // invalid block size ZXC_ERROR_DICT_REQUIRED = -15, // file requires a dictionary but none provided ZXC_ERROR_DICT_MISMATCH = -16, // provided dictionary ID does not match header ZXC_ERROR_DICT_TOO_LARGE = -17, // dictionary exceeds ZXC_DICT_SIZE_MAX ZXC_ERROR_BAD_LEVEL = -18 // level unsupported by this context's workspace // (static context dense-tier raise; out-of-range // levels are otherwise silently clamped) } zxc_error_t; ``` All public functions that can fail return negative `zxc_error_t` values on error. --- ## 6. Type Definitions ### 6.1 Options Structs **Compression options** (defined in `zxc_opts.h`, used by all compression functions): ```c typedef struct { int n_threads; // Worker thread count (0 = auto-detect). int level; // Compression level 1–7 (0 = default). size_t block_size; // Block size in bytes (0 = 512 KB default). int checksum_enabled; // 1 = enable checksums, 0 = disable. int seekable; // 1 = append seek table for random access. const void* dict; // Pre-trained dictionary content (NULL = none). size_t dict_size; // Dictionary size in bytes (0 = none, max 64 KB). const void* dict_huf; // Shared literal Huffman table, 128 bytes // (NULL = none; ignored without dict). zxc_progress_callback_t progress_cb; // Optional callback (NULL to disable). void* user_data; // Passed through to progress_cb. } zxc_compress_opts_t; ``` **Decompression options**: ```c typedef struct { int n_threads; // Worker thread count (0 = auto-detect). int checksum_enabled; // 1 = verify checksums, 0 = skip. const void* dict; // Pre-trained dictionary content (NULL = none). size_t dict_size; // Dictionary size in bytes (0 = none). const void* dict_huf; // Shared literal Huffman table, 128 bytes, // matching the one used at compression time // (NULL = none; ignored without dict). zxc_progress_callback_t progress_cb; // Optional callback. void* user_data; // Passed through to progress_cb. } zxc_decompress_opts_t; ``` Both structs are safe to zero-initialize for default behavior. Pass `NULL` instead of an options pointer to use all defaults. ### 6.2 Progress Callback ```c typedef void (*zxc_progress_callback_t)( uint64_t bytes_processed, // Total input bytes processed so far uint64_t bytes_total, // Total input bytes (0 if unknown, e.g. stdin) const void* user_data // User-provided context ); ``` Called from the writer thread after each block is processed. Must be fast and non-blocking. ### 6.3 Opaque Context Types ```c typedef struct zxc_cctx_s zxc_cctx; // Opaque compression context typedef struct zxc_dctx_s zxc_dctx; // Opaque decompression context ``` Internal layout is hidden. Interact only through `zxc_create_*` / `zxc_free_*` / `zxc_*_cctx` / `zxc_*_dctx` functions. --- ## 7. Buffer API Declared in `zxc_buffer.h`. Single-threaded, blocking, in-memory operations. ### Library Info Helpers Runtime-queryable library metadata. Exposed so integrations and language bindings can discover the supported level range and library version without relying on compile-time constants alone. #### `zxc_min_level` ```c ZXC_EXPORT int zxc_min_level(void); ``` Returns the minimum supported compression level (currently `1`, equivalent to `ZXC_LEVEL_FASTEST`). #### `zxc_max_level` ```c ZXC_EXPORT int zxc_max_level(void); ``` Returns the maximum supported compression level (currently `7`, equivalent to `ZXC_LEVEL_ULTRA`). #### `zxc_default_level` ```c ZXC_EXPORT int zxc_default_level(void); ``` Returns the default compression level (currently `3`, equivalent to `ZXC_LEVEL_DEFAULT`). #### `zxc_version_string` ```c ZXC_EXPORT const char* zxc_version_string(void); ``` Returns the library version as a null-terminated string (e.g. `"0.13.0"`). The returned pointer is a compile-time constant and must not be freed. ### `zxc_compress_bound` ```c ZXC_EXPORT uint64_t zxc_compress_bound(size_t input_size); ``` Returns the worst-case compressed size for `input_size` bytes. Use to allocate the destination buffer before compression. ### `zxc_compress` ```c ZXC_EXPORT int64_t zxc_compress( const void* src, size_t src_size, void* dst, size_t dst_capacity, const zxc_compress_opts_t* opts // NULL = defaults ); ``` Compresses `src` into `dst`. Only `level`, `block_size`, `checksum_enabled`, and `seekable` fields of `opts` are used. `n_threads` is ignored (always single-threaded). **Returns**: compressed size (> 0) on success, or negative `zxc_error_t`. ### `zxc_decompress` ```c ZXC_EXPORT int64_t zxc_decompress( const void* src, size_t src_size, void* dst, size_t dst_capacity, const zxc_decompress_opts_t* opts // NULL = defaults ); ``` Decompresses `src` into `dst`. Only `checksum_enabled` is used. `src` and `dst` must not overlap (same contract as `memcpy`); for overlapping single-buffer decode, use `zxc_decompress_inplace` below. **Returns**: decompressed size (> 0) on success, or negative `zxc_error_t`. ### `zxc_decompress_inplace_bound` ```c ZXC_EXPORT size_t zxc_decompress_inplace_bound( const void* src, size_t src_size ); ``` Reads `src`'s header and footer (no decoding) and returns the single-buffer size `zxc_decompress_inplace` needs: `decompressed_size` plus the safety margin `block_size + nblocks x (block header + per-block checksum, if any) + file footer + ZXC_DECOMPRESS_TAIL_PAD` — one block, the accumulated per-block framing overhead (incompressible blocks make the compressed stream run that much longer than the output), the footer, and the wild-copy tail. Always size the buffer with this function rather than re-deriving the formula. **Returns**: required buffer size, or `0` if `src` is not a valid archive. ### `zxc_decompress_inplace` ```c ZXC_EXPORT int64_t zxc_decompress_inplace( void* buffer, size_t buffer_capacity, size_t comp_size, const zxc_decompress_opts_t* opts // NULL = defaults ); ``` Decompresses **in place** inside a single caller-owned buffer, replacing the usual input + output pair with one allocation — decisive for memory-constrained targets (embedded, FOTA, firmware). The compressed archive must sit **flush-right** in `buffer` (its last `comp_size` bytes); decoding runs left-to-right into `buffer[0..]`. Because ZXC never expands a block, the write cursor provably never overtakes the flush-right read cursor once `buffer_capacity >= zxc_decompress_inplace_bound(...)`. Dictionary archives are supported. An undersized buffer is rejected with `ZXC_ERROR_DST_TOO_SMALL`, never corruption. ```c size_t need = zxc_decompress_inplace_bound(archive, archive_size); uint8_t* buf = malloc(need); memcpy(buf + (need - archive_size), archive, archive_size); // flush-right int64_t n = zxc_decompress_inplace(buf, need, archive_size, NULL); // buf[0 .. n) now holds the decompressed data ``` **Returns**: decompressed size (> 0), `0` for an empty frame, or negative `zxc_error_t`. ### `zxc_get_decompressed_size` ```c ZXC_EXPORT uint64_t zxc_get_decompressed_size( const void* src, size_t src_size ); ``` Reads the original size from the file footer without decompressing. **Returns**: original size, or `0` if the buffer is invalid. --- ## 8. Block API Declared in `zxc_buffer.h`. Single-block compression and decompression **without file framing** (no file header, EOF block, or footer). Designed for filesystem integrations (DwarFS, EROFS, SquashFS) where the caller manages its own block indexing. Output format: `block_header (8 B)` + compressed payload + optional `checksum (4 B)`. ### `zxc_compress_block_bound` ```c ZXC_EXPORT uint64_t zxc_compress_block_bound(size_t input_size); ``` Returns the maximum compressed size for a single block. Unlike `zxc_compress_bound()`, this does **not** include file header, EOF block, or footer overhead. `input_size` must be in `[0, ZXC_BLOCK_SIZE_MAX]` (the Block API limit). **Returns**: upper bound in bytes, or `0` if `input_size > ZXC_BLOCK_SIZE_MAX` or would overflow the computation. ### `zxc_decompress_block_bound` ```c ZXC_EXPORT uint64_t zxc_decompress_block_bound(size_t uncompressed_size); ``` Returns the minimum `dst_capacity` required by `zxc_decompress_block()` for a block of `uncompressed_size` bytes. The fast decoder uses speculative wild-copy writes and needs a small tail pad beyond the declared uncompressed size: passing exactly `uncompressed_size` as `dst_capacity` forces the slow tail path and may trigger `ZXC_ERROR_OVERFLOW` on some inputs. Use this helper to size destination buffers for the fast path. For callers that genuinely cannot oversize their output buffer, use `zxc_decompress_block_safe()` instead. `uncompressed_size` must be in `[0, ZXC_BLOCK_SIZE_MAX]` (the Block API limit). **Returns**: minimum `dst_capacity` in bytes, or `0` if `uncompressed_size > ZXC_BLOCK_SIZE_MAX` or would overflow the computation. ### `zxc_compress_block` ```c ZXC_EXPORT int64_t zxc_compress_block( zxc_cctx* cctx, const void* src, size_t src_size, void* dst, size_t dst_capacity, const zxc_compress_opts_t* opts // NULL = defaults ); ``` Compresses a single block using a reusable context. Only `level`, `block_size`, and `checksum_enabled` fields of `opts` are used. `src_size` must be in `[1, ZXC_BLOCK_SIZE_MAX]` (2 MiB). For larger payloads, use the frame API (`zxc_compress`) or streaming API (`zxc_cstream_*`), which chunk transparently into format-conformant blocks. **Returns**: compressed block size (> 0) on success, or negative `zxc_error_t`. Returns `ZXC_ERROR_BAD_BLOCK_SIZE` if `src_size > ZXC_BLOCK_SIZE_MAX`. ### `zxc_decompress_block` ```c ZXC_EXPORT int64_t zxc_decompress_block( zxc_dctx* dctx, const void* src, size_t src_size, void* dst, size_t dst_capacity, const zxc_decompress_opts_t* opts // NULL = defaults ); ``` Decompresses a single block produced by `zxc_compress_block()`. `dst_capacity` should be at least `zxc_decompress_block_bound(uncompressed_size)` to enable the fast path, and **must not exceed** `ZXC_BLOCK_SIZE_MAX + ZXC_DECOMPRESS_TAIL_PAD`. For payloads produced by the frame or streaming APIs, use `zxc_decompress` instead. Only `checksum_enabled` is used. **Returns**: decompressed size (> 0) on success, or negative `zxc_error_t`. Returns `ZXC_ERROR_BAD_BLOCK_SIZE` if `dst_capacity` exceeds the per-block limit. ### `zxc_decompress_block_safe` ```c ZXC_EXPORT int64_t zxc_decompress_block_safe( zxc_dctx* dctx, const void* src, size_t src_size, void* dst, size_t dst_capacity, const zxc_decompress_opts_t* opts // NULL = defaults ); ``` Strict-sized variant of `zxc_decompress_block()`. Accepts `dst_capacity == uncompressed_size` exactly: no tail pad required. Intended for integrations whose output buffer cannot be oversized (page-aligned decoding into mapped pages, fixed-size slots in a columnar layout, etc.). Output is **bit-identical** to `zxc_decompress_block()`. RAW blocks transparently forward to the fast path; only GLO/GHI blocks use the strict-tail decoder, which is slightly slower than the wild-copy fast path (see the performance table in `EXAMPLES.md`). Strict-tail variant: `dst_capacity` is the exact uncompressed size with no tail-pad margin, so the upper limit is `ZXC_BLOCK_SIZE_MAX` (not `MAX+TAIL_PAD` as for `zxc_decompress_block`). Returns `ZXC_ERROR_BAD_BLOCK_SIZE` if `dst_capacity > ZXC_BLOCK_SIZE_MAX`. Only `checksum_enabled` is used. **Returns**: decompressed size (> 0) on success, or negative `zxc_error_t`. ### `zxc_estimate_cctx_size` ```c ZXC_EXPORT uint64_t zxc_estimate_cctx_size(size_t src_size, int level); ``` Returns an accurate estimate of the peak memory used when compressing a single block of `src_size` bytes at the given `level` via `zxc_compress_block()`. The estimate covers all per-chunk working buffers (chain table, literals, sequence/token/offset/extras buffers) plus the fixed hash tables and the cache-line alignment padding. At `level >= 6` it also includes the transient DP scratch (~18 × `src_size` bytes) malloc'd by the price-based optimal parser for the duration of each block. It scales roughly linearly with `src_size` and is intended for integrators that need to build an accurate memory budget (filesystems, embedded devices, sandboxed workloads). **Returns**: estimated peak cctx memory usage in bytes, or `0` if `src_size == 0`. --- ## 9. Reusable Context API Declared in `zxc_buffer.h`. Eliminates per-call allocation overhead for hot-path integrations (filesystem plug-ins, batch processing). ### `zxc_create_cctx` ```c ZXC_EXPORT zxc_cctx* zxc_create_cctx(const zxc_compress_opts_t* opts); ``` Creates a heap-allocated compression context. When `opts` is non-NULL, internal buffers are pre-allocated (eager init). When `opts` is NULL, allocation is deferred to first use (lazy init). **Returns**: context pointer, or `NULL` on allocation failure. ### `zxc_free_cctx` ```c ZXC_EXPORT void zxc_free_cctx(zxc_cctx* cctx); ``` Frees all resources. Safe to pass `NULL`. ### `zxc_compress_cctx` ```c ZXC_EXPORT int64_t zxc_compress_cctx( zxc_cctx* cctx, const void* src, size_t src_size, void* dst, size_t dst_capacity, const zxc_compress_opts_t* opts ); ``` Same as `zxc_compress()` but reuses internal buffers from `cctx`. Automatically re-initializes when `block_size` or `level` changes. ### `zxc_create_dctx` ```c ZXC_EXPORT zxc_dctx* zxc_create_dctx(void); ``` Creates a heap-allocated decompression context. ### `zxc_free_dctx` ```c ZXC_EXPORT void zxc_free_dctx(zxc_dctx* dctx); ``` Frees all resources. Safe to pass `NULL`. ### `zxc_decompress_dctx` ```c ZXC_EXPORT int64_t zxc_decompress_dctx( zxc_dctx* dctx, const void* src, size_t src_size, void* dst, size_t dst_capacity, const zxc_decompress_opts_t* opts ); ``` Same as `zxc_decompress()` but reuses buffers from `dctx`. --- ## 9b. Static Context API Declared in `zxc_buffer.h`. Mirrors the Reusable Context API but places the entire context, opaque handle plus every persistent sub-buffer, inside a **single buffer allocated and owned by the caller**. This pattern is mandatory for environments where the library cannot call into the host allocator on the hot path: Linux kernel filesystems (one workspace per mount, served via `vmalloc` / `kmalloc` up front), embedded targets without a heap (`.bss` or stack-allocated workspace), sandboxed runtimes with a fixed memory budget, etc. Trade-off vs the dynamic API: the workspace is **pinned** to a single `block_size` at init time; subsequent compress / decompress calls cannot enlarge the footprint, so a workload that needs to mix block sizes must size the workspace for the maximum block size up front. ### Properties - **Single allocation.** All hash tables, chain table, sequence buffers, literal scratch, work buffer, and (at `ZXC_LEVEL_DENSITY`) the optimal- parser scratch live inside the caller-supplied buffer. No further `ZXC_MALLOC` / `ZXC_ALIGNED_MALLOC` is performed for the lifetime of the context. - **`zxc_free_*ctx` is a no-op** on a static handle. The caller owns the workspace. - **`block_size` is locked.** A subsequent call passing a different `block_size` returns `ZXC_ERROR_BAD_BLOCK_SIZE` without re-initialising. `level` and `checksum_enabled` may still vary per call (they affect only the encoder state, not the buffer layout). - **Workspace alignment.** Must be at least cache-line (64-byte) aligned. Use `posix_memalign(..., 64, ...)`, `aligned_alloc(64, ...)`, the slab allocator (kmalloc returns ≥ `ARCH_KMALLOC_MINALIGN`), or a `_Alignas(64)` static array. ### `zxc_static_cctx_workspace_size` ```c ZXC_EXPORT size_t zxc_static_cctx_workspace_size( const size_t block_size, const int level ); ``` Returns the exact byte count required by a static compression workspace for the given `block_size` and `level`. Sum of the opaque `zxc_cctx` wrapper plus every persistent sub-buffer the library would partition. **Returns**: workspace size in bytes, or `0` if either argument is invalid (non-power-of-two `block_size`, out-of-range level, ...). **Note**: level 6 (`ZXC_LEVEL_DENSITY`) adds the optimal-parser scratch (~8.125 × `block_size`); levels 1–5 share the same workspace size. ### `zxc_init_static_cctx` ```c ZXC_EXPORT zxc_cctx* zxc_init_static_cctx( void* workspace, const size_t workspace_size, const zxc_compress_opts_t* opts ); ``` Initialises a compression context inside a caller-supplied workspace. `workspace_size` must be at least `zxc_static_cctx_workspace_size` for the same `block_size` / `level`. `opts` is **required**: `block_size` and `level` are pinned at init time and must be set explicitly. The returned handle points **inside** `workspace`; the workspace must remain valid for the lifetime of the handle. `zxc_free_cctx` is a no-op. **Returns**: handle pointing inside `workspace`, or `NULL` if the workspace is too small, the options are invalid, or `workspace` / `opts` is `NULL`. ### `zxc_static_dctx_workspace_size` ```c ZXC_EXPORT size_t zxc_static_dctx_workspace_size( const size_t block_size ); ``` Returns the exact byte count required by a static decompression workspace for the given `block_size`. Unlike the compression variant, this size is **independent of the source archive's level**: `lit_buffer` is always provisioned worst-case because the decoder cannot predict the per-block literal encoding (RAW / RLE / HUFFMAN) until it sees each block header. **Returns**: workspace size in bytes, or `0` if `block_size` is invalid. ### `zxc_init_static_dctx` ```c ZXC_EXPORT zxc_dctx* zxc_init_static_dctx( void* workspace, const size_t workspace_size, const size_t block_size ); ``` Initialises a decompression context inside a caller-supplied workspace. `block_size` is **pinned** at init time: feeding the returned handle an archive whose file header declares a different `block_size` returns `ZXC_ERROR_BAD_BLOCK_SIZE`. The returned handle points inside `workspace`; the workspace must remain valid for the lifetime of the handle. `zxc_free_dctx` is a no-op. **Returns**: handle pointing inside `workspace`, or `NULL` if the workspace is too small, `block_size` is invalid, or `workspace` is `NULL`. ### Typical usage ```c #include #define BLOCK_SZ (64 * 1024) /* must match the archive's block_size at decode */ #define LEVEL ZXC_LEVEL_DEFAULT /* --- Compression side --- */ size_t cws_sz = zxc_static_cctx_workspace_size(BLOCK_SZ, LEVEL); void *cws = NULL; posix_memalign(&cws, 64, cws_sz); /* or kmalloc / vmalloc / .bss */ zxc_compress_opts_t copts = { .level = LEVEL, .block_size = BLOCK_SZ }; zxc_cctx *cctx = zxc_init_static_cctx(cws, cws_sz, &copts); for (/* each block */) { zxc_compress_cctx(cctx, src, n, dst, cap, NULL); /* no allocation */ } zxc_free_cctx(cctx); /* no-op for static */ free(cws); /* caller owns the workspace */ /* --- Decompression side --- */ size_t dws_sz = zxc_static_dctx_workspace_size(BLOCK_SZ); void *dws = NULL; posix_memalign(&dws, 64, dws_sz); zxc_dctx *dctx = zxc_init_static_dctx(dws, dws_sz, BLOCK_SZ); zxc_decompress_dctx(dctx, in, in_sz, out, out_cap, NULL); zxc_free_dctx(dctx); /* no-op */ free(dws); ``` ### Sizing notes | `block_size` | level 1–5 cctx | level 6 cctx | dctx | |---|---|---|---| | 4 KB | ~280 KB | ~320 KB | ~9 KB | | 64 KB | ~410 KB | ~930 KB | ~129 KB | | 512 KB | ~1.5 MB | ~5.7 MB | ~1 MB | | 2 MB | ~5.0 MB | ~21 MB | ~4 MB | (Exact figures depend on architecture and alignment padding; always query `zxc_static_*_workspace_size` rather than hard-coding.) ### Discovering `block_size` at decode time `zxc_init_static_dctx` pins `block_size` at workspace creation, so the caller must know it *before* calling `init`. Four patterns cover every use case: 1. **Pinned by contract.** Producer and consumer agree on `block_size` at deployment time (kernel module, embedded firmware, intra-application pipeline). The constant lives in a shared header — no runtime discovery needed. This is the canonical static-context use case. 2. **Seekable archive — read the SEK table.** The block size is recoverable from the table opened in any backing mode (buffer, `FILE*`, or `zxc_reader_t`): ```c zxc_seekable* s = zxc_seekable_open_reader(&r); /* parses SEK table only */ const uint32_t bs = zxc_seekable_get_block_decomp_size(s, 0); /* For multi-block archives, block index 0 is always a full block. * For a single-block archive, bs equals the total decompressed size. */ ``` 3. **Stream / buffer archive — peek the 16-byte file header.** The block size is encoded at offset `0x05` (the *Chunk Size Code*, see [FORMAT.md §3](FORMAT.md)). Read the first `ZXC_FILE_HEADER_SIZE` bytes, validate the magic word, then decode: ```c uint8_t hdr[ZXC_FILE_HEADER_SIZE]; /* fread / read / pread the first 16 bytes of the archive */ const uint32_t magic = (uint32_t)hdr[0] | ((uint32_t)hdr[1] << 8) | ((uint32_t)hdr[2] << 16) | ((uint32_t)hdr[3] << 24); if (magic != 0x9CB02EF5U) { /* not a ZXC archive */ } const uint8_t code = hdr[5]; size_t block_size; if (code >= ZXC_BLOCK_SIZE_MIN_LOG2 && code <= ZXC_BLOCK_SIZE_MAX_LOG2) block_size = (size_t)1U << code; /* 4 KB..2 MB */ else /* invalid archive */; ``` After peeking, rewind the input (or keep the 16-byte prefix) and feed the whole archive to `zxc_decompress_dctx` / streaming decoder normally — the decoder re-parses the header. 4. **Worst-case sizing fallback.** When the archive's block size cannot be pre-fetched (e.g., a one-shot streaming decoder that consumes the header in the same pass), size the dctx workspace for `ZXC_BLOCK_SIZE_MAX` (2 MB). The handle accepts any conforming archive at the cost of over-allocation (~4 MB dctx). ```c size_t dws_sz = zxc_static_dctx_workspace_size(ZXC_BLOCK_SIZE_MAX); ``` If the workspace pool must stay tight and worst-case sizing is too expensive, fall back to the dynamic Context API (`zxc_create_dctx`) — its `block_size` is set per call. --- ## 10. Streaming API Declared in `zxc_stream.h` — the umbrella for every `FILE*`-flavored entry point (opt-in: **not** included by `` because `` would break freestanding/kernel builds). Hosts the multi-threaded streaming pipeline described below (reader -> workers -> writer) **and** the seekable `FILE*` open helper `zxc_seekable_open_file` (documented in §11). ### `zxc_stream_compress` ```c ZXC_EXPORT int64_t zxc_stream_compress( FILE* f_in, FILE* f_out, const zxc_compress_opts_t* opts ); ``` Compresses `f_in` -> `f_out` using a parallel pipeline. All fields of `opts` are used, including `n_threads` and `progress_cb`. **Returns**: total compressed bytes written, or negative `zxc_error_t`. ### `zxc_stream_decompress` ```c ZXC_EXPORT int64_t zxc_stream_decompress( FILE* f_in, FILE* f_out, const zxc_decompress_opts_t* opts ); ``` Decompresses `f_in` -> `f_out` using a parallel pipeline. **Returns**: total decompressed bytes written, or negative `zxc_error_t`. ### `zxc_stream_get_decompressed_size` ```c ZXC_EXPORT int64_t zxc_stream_get_decompressed_size(FILE* f_in); ``` Reads the original size from the file footer. File position is restored. **Returns**: original size, or negative `zxc_error_t`. --- ## 10b. Push Streaming API Declared in `zxc_pstream.h`. Single-threaded, **caller-driven** streaming, the inverse of the `FILE*`-based pipeline. Designed for callback-based integrations (async runtimes, network protocols) that cannot block on a `FILE*` and need to feed/drain in arbitrary chunks. The on-disk output is **bit-compatible** with the Buffer / Stream APIs: files produced by the push API decode with `zxc_decompress()` / `zxc_stream_decompress()`, and vice-versa. ### Buffer Descriptors ```c typedef struct { const void* src; size_t size; size_t pos; // advanced by the library } zxc_inbuf_t; typedef struct { void* dst; size_t size; size_t pos; // advanced by the library } zxc_outbuf_t; ``` The library reads `[src+pos .. src+size)` and writes `[dst+pos .. dst+size)`, advancing `pos` by what it consumed/produced. Buffers are caller-owned. The whole `[dst+pos .. dst+size)` window is writable scratch: with enough remaining capacity the decoder decodes blocks straight into it with speculative (wild-copy) stores, so bytes beyond the final `pos` are unspecified — never keep live data inside the declared capacity. ### Compression #### `zxc_cstream_create` ```c ZXC_EXPORT zxc_cstream* zxc_cstream_create(const zxc_compress_opts_t* opts); ``` Creates a push compression context. Settings (`level`, `block_size`, `checksum_enabled`) are copied from `opts` and frozen for the lifetime of the stream. `n_threads`, `progress_cb`, `seekable` are ignored on this path. **Returns**: context, or `NULL` on allocation failure. #### `zxc_cstream_free` ```c ZXC_EXPORT void zxc_cstream_free(zxc_cstream* cs); ``` Releases all resources. Safe with `NULL`. #### `zxc_cstream_compress` ```c ZXC_EXPORT int64_t zxc_cstream_compress( zxc_cstream* cs, zxc_outbuf_t* out, zxc_inbuf_t* in ); ``` Pushes input into the stream and drains compressed output: - emits the file header on the first call; - copies input into the internal block accumulator; - compresses one block whenever the accumulator fills, writing it into `out` (up to `out->size`); - returns when `in` is fully consumed *and* no more compressed bytes are pending, or when `out` has no room left. Fully reentrant: if `out` fills mid-block, the next call resumes draining where it left off. Safe to call with empty input (drain-only). **Returns**: - `0` — `in` fully consumed and no pending output; - `>0` — bytes still pending in the staging buffer (drain `out` and call again); - `<0` — `zxc_error_t` (sticky: subsequent calls return the same code). #### `zxc_cstream_end` ```c ZXC_EXPORT int64_t zxc_cstream_end(zxc_cstream* cs, zxc_outbuf_t* out); ``` Finalises the stream: compresses any partial last block, emits the EOF block (8 B) and the file footer (12 B). **Must be called** to produce a valid ZXC file. Reentrant the same way `_compress` is: loop until it returns `0`. After `_end` returns `0`, the stream is in DONE state and any further call returns `ZXC_ERROR_NULL_INPUT`. **Returns**: - `0` — finalisation complete; - `>0` — bytes still pending (drain `out` and call again); - `<0` — `zxc_error_t`. #### `zxc_cstream_in_size` / `zxc_cstream_out_size` ```c ZXC_EXPORT size_t zxc_cstream_in_size(const zxc_cstream* cs); ZXC_EXPORT size_t zxc_cstream_out_size(const zxc_cstream* cs); ``` Suggested buffer sizes for best throughput. The caller may use any size; these are purely performance hints. ### Decompression #### `zxc_dstream_create` ```c ZXC_EXPORT zxc_dstream* zxc_dstream_create(const zxc_decompress_opts_t* opts); ``` Creates a push decompression context. Only `checksum_enabled` from `opts` is honoured (controls whether the global file-level checksum is verified when the file carries one). **Returns**: context, or `NULL` on allocation failure. #### `zxc_dstream_free` ```c ZXC_EXPORT void zxc_dstream_free(zxc_dstream* ds); ``` Releases all resources. Safe with `NULL`. #### `zxc_dstream_decompress` ```c ZXC_EXPORT int64_t zxc_dstream_decompress( zxc_dstream* ds, zxc_outbuf_t* out, zxc_inbuf_t* in ); ``` Drives the parser state machine (file header → blocks → EOF → optional SEK → footer). Each call makes as much progress as `in` and `out` allow. Trailing bytes after the validated footer are silently ignored (the caller can inspect `in->pos` to detect how many were consumed). **Returns**: - `>0` — number of decompressed bytes written into `out` this call; - `0` — either DONE (use `zxc_dstream_finished` to confirm) or no progress possible without more input; - `<0` — `zxc_error_t` (sticky). #### `zxc_dstream_finished` ```c ZXC_EXPORT int zxc_dstream_finished(const zxc_dstream* ds); ``` Returns `1` iff the parser has fully validated the file footer. Callers that have finished feeding input should check this to detect truncated streams: `zxc_dstream_decompress` returning `0` with no output is ambiguous (DONE vs need-more-input) — `_finished` disambiguates. #### `zxc_dstream_in_size` / `zxc_dstream_out_size` ```c ZXC_EXPORT size_t zxc_dstream_in_size(const zxc_dstream* ds); ZXC_EXPORT size_t zxc_dstream_out_size(const zxc_dstream* ds); ``` Suggested buffer sizes. Returns `0` if `ds` is `NULL`. Before the file header has been parsed, `zxc_dstream_in_size()` may return a default recommended input size hint (for example `ZXC_BLOCK_SIZE_DEFAULT`), because the actual block size is not known until it is learned from the header. ### Threading Each `zxc_cstream` / `zxc_dstream` is single-threaded: one context, one thread. Multiple contexts may be used concurrently from different threads. ### Compression Example ```c zxc_compress_opts_t opts = { .level = 3, .checksum_enabled = 1 }; zxc_cstream* cs = zxc_cstream_create(&opts); uint8_t in_buf[64*1024], out_buf[64*1024]; zxc_outbuf_t out = { out_buf, sizeof out_buf, 0 }; ssize_t n; while ((n = read_some(in_buf, sizeof in_buf)) > 0) { zxc_inbuf_t in = { in_buf, (size_t)n, 0 }; while (in.pos < in.size) { int64_t r = zxc_cstream_compress(cs, &out, &in); if (r < 0) goto fatal; if (out.pos > 0) { write_to_sink(out_buf, out.pos); out.pos = 0; } } } int64_t pending; do { pending = zxc_cstream_end(cs, &out); if (pending < 0) goto fatal; if (out.pos > 0) { write_to_sink(out_buf, out.pos); out.pos = 0; } } while (pending > 0); zxc_cstream_free(cs); ``` --- ## 11. Seekable API Declared in `zxc_seekable.h` (opt-in: not included by `` because most consumers do not need random-access decompression). The header is **freestanding-safe** — it does not pull `` — so kernel / embedded consumers can include it directly and plug their own storage backend behind the `zxc_reader_t` callback. The `FILE*` convenience open (`zxc_seekable_open_file`) lives in `zxc_stream.h` instead. Seekable archives are produced with `seekable = 1` in `zxc_compress_opts_t`. ### Creating a Seekable Archive Set `seekable = 1` in `zxc_compress_opts_t`. Works with both the Buffer API and the Streaming API: ```c zxc_compress_opts_t opts = { .level = 3, .seekable = 1 }; int64_t csize = zxc_compress(src, src_size, dst, dst_cap, &opts); ``` The resulting archive contains a Seek Table block (SEK) between the EOF block and the file footer. Standard decompressors handle seekable archives transparently, the seek table is skipped during sequential decompression. ### `zxc_seekable_open` ```c ZXC_EXPORT zxc_seekable* zxc_seekable_open(const void* src, const size_t src_size); ``` Opens a seekable archive from a memory buffer. The buffer must remain valid for the lifetime of the handle. **Returns**: handle on success, or `NULL` if the buffer is not a valid seekable archive. ### `zxc_seekable_open_file` ```c #include ZXC_EXPORT zxc_seekable* zxc_seekable_open_file(FILE* f); ``` Opens a seekable archive from a `FILE*`. The file must be seekable (not stdin/pipe). The file position is saved and restored after parsing. Internally builds a `zxc_reader_t` over `pread()`/`ReadFile()` and delegates to `zxc_seekable_open_reader`. Declared in `zxc_stream.h` (which gathers all `FILE*`-flavored entry points) rather than `zxc_seekable.h` — that header stays freestanding so it can be included from kernel-space / freestanding environments. **Returns**: handle on success, or `NULL` on error. ### `zxc_reader_t` ```c typedef struct { int64_t (*read_at)(void* ctx, void* dst, size_t len, uint64_t offset); void* ctx; uint64_t size; // total size of the compressed archive in bytes } zxc_reader_t; ``` Storage-agnostic reader interface. Lets the caller plug any backend that supports positional reads — `mmap`, HTTP `Range:` requests, S3, a custom VFS, `vfs_read()` in Linux kernel space, etc. — behind the seekable API. `read_at` returns the number of bytes read (`== len` on success), or a negative `zxc_error_t` on failure. Short reads are treated as errors. **Thread safety**: `read_at` MUST be safe to call concurrently from multiple threads when the resulting handle is used with `zxc_seekable_decompress_range_mt()`. The single-threaded path makes no concurrent calls. **Lifetime**: `ctx` and the backing storage must remain valid until `zxc_seekable_free()`. ### `zxc_seekable_open_reader` ```c ZXC_EXPORT zxc_seekable* zxc_seekable_open_reader(const zxc_reader_t* r); ``` Opens a seekable archive through a user-supplied reader. The reader is invoked to fetch the file header, footer, and seek table at open time (3 reads), then once per block during decompression. No `FILE*` is involved — this is the entry point to use for kernel space, networked storage, or any non-POSIX backend. **Returns**: handle on success, or `NULL` if `r`/`r->read_at` is `NULL`, `r->size` is `0`, the archive is not seekable, or any `read_at` call fails. ### `zxc_seekable_get_num_blocks` ```c ZXC_EXPORT uint32_t zxc_seekable_get_num_blocks(const zxc_seekable* s); ``` Returns the total number of data blocks in the archive. ### `zxc_seekable_get_decompressed_size` ```c ZXC_EXPORT uint64_t zxc_seekable_get_decompressed_size(const zxc_seekable* s); ``` Returns the total decompressed size of the archive. ### `zxc_seekable_get_block_comp_size` ```c ZXC_EXPORT uint32_t zxc_seekable_get_block_comp_size( const zxc_seekable* s, uint32_t block_idx ); ``` Returns the compressed size (on-disk, including header) of a specific block. ### `zxc_seekable_get_block_decomp_size` ```c ZXC_EXPORT uint32_t zxc_seekable_get_block_decomp_size( const zxc_seekable* s, uint32_t block_idx ); ``` Returns the decompressed size of a specific block. ### `zxc_seekable_decompress_range` ```c ZXC_EXPORT int64_t zxc_seekable_decompress_range( zxc_seekable* s, void* dst, size_t dst_capacity, uint64_t offset, size_t len ); ``` Decompresses `len` bytes starting at byte `offset` in the original uncompressed data. Only the blocks overlapping the requested range are read and decompressed. **Returns**: `len` on success, or negative `zxc_error_t`. ### `zxc_seekable_decompress_range_mt` ```c ZXC_EXPORT int64_t zxc_seekable_decompress_range_mt( zxc_seekable* s, void* dst, size_t dst_capacity, uint64_t offset, size_t len, int n_threads // 0 = auto-detect ); ``` Multi-threaded variant. Each worker thread uses `pread()` (POSIX) or `ReadFile()` (Windows) for lock-free concurrent I/O. Falls back to single-threaded mode when `n_threads <= 1` or the range spans a single block. **Returns**: `len` on success, or negative `zxc_error_t`. ### `zxc_seekable_free` ```c ZXC_EXPORT void zxc_seekable_free(zxc_seekable* s); ``` Frees a seekable handle and all associated resources. Safe to call with `NULL`. ### `zxc_write_seek_table` ```c ZXC_EXPORT int64_t zxc_write_seek_table( uint8_t* dst, size_t dst_capacity, const uint32_t* comp_sizes, uint32_t num_blocks ); ``` Low-level: writes a seek table (block header + entries) to `dst`. **Returns**: bytes written, or negative `zxc_error_t`. ### `zxc_seek_table_size` ```c ZXC_EXPORT size_t zxc_seek_table_size(uint32_t num_blocks); ``` Returns the encoded byte size of a seek table for `num_blocks` blocks. --- ## 11b. Dictionary API Declared in ``. Provides dictionary training, serialization (`.zxd` format), and identification. > **The simple path**: most callers only need two functions — `zxc_dict_train()` > to build a `.zxd` from samples, and `zxc_dict_load()` to unpack one for > (de)compression. The remaining functions are the lower-level primitives those > two are built from, exposed for advanced use (raw content-only dictionaries, > retraining only the table, supplying externally-sourced content). ### `zxc_dict_train` ```c ZXC_EXPORT int64_t zxc_dict_train( const void* const* samples, const size_t* sample_sizes, size_t n_samples, void* zxd_buf, // receives the .zxd bytes size_t zxd_capacity // zxc_dict_save_bound(ZXC_DICT_SIZE_MAX) is always safe ); ``` **One-call dictionary creation.** Trains the content, trains the shared literal Huffman table from it, and serializes both into ready-to-write `.zxd` bytes. Hides the (otherwise sequential) train-content → train-table → save pipeline. Returns the number of `.zxd` bytes written, or a negative `zxc_error_t` code. ### `zxc_train_dict` ```c ZXC_EXPORT int64_t zxc_train_dict( const void* const* samples, const size_t* sample_sizes, size_t n_samples, void* dict_buf, size_t dict_capacity // max ZXC_DICT_SIZE_MAX (64KB - 1) ); ``` Trains a dictionary from a corpus of representative samples. Returns the size of the trained dictionary, or a negative `zxc_error_t` code. ### `zxc_train_dict_huf` ```c ZXC_EXPORT int zxc_train_dict_huf( const void* const* samples, const size_t* sample_sizes, size_t n_samples, const void* dict, // content from zxc_train_dict size_t dict_size, uint8_t* huf_lengths_out // ZXC_HUF_TABLE_SIZE (128) bytes ); ``` Trains the **shared literal Huffman table** for an already-trained dictionary. It compresses the samples with `dict` and derives canonical code lengths from the real post-LZ literal distribution. The 128-byte table is required by `zxc_dict_save()` and can be attached at (de)compression time via the `dict_huf` option field. Returns `ZXC_OK` or a negative error code. ### `zxc_dict_id` ```c ZXC_EXPORT uint32_t zxc_dict_id(const void* dict, size_t dict_size, const void* huf_lengths); ``` Returns the deterministic 32-bit dictionary ID. With `huf_lengths` NULL it hashes the raw content only — the id of an in-memory content-only dictionary (buffer API). With a 128-byte table it binds the **(content, table)** pair: `hash(table, seed = hash(content))` — the value recorded in `.zxd` files and in archive headers when a shared table is attached. Returns 0 for NULL/empty content. ### `zxc_dict_save` ```c ZXC_EXPORT int64_t zxc_dict_save( const void* content, size_t content_size, const void* huf_lengths, // 128-byte table from zxc_train_dict_huf (required) void* buf, size_t buf_capacity ); ``` Serializes dictionary content **and its shared Huffman table** to the `.zxd` file format. The table is mandatory (pass the 128 bytes from `zxc_train_dict_huf()`); the stored `dict_id` covers both. Use `zxc_dict_save_bound(content_size)` to size `buf`. ### `zxc_dict_load` ```c ZXC_EXPORT int zxc_dict_load( const void* buf, size_t buf_size, const void** content_out, size_t* content_size_out, const void** huf_out, // 128-byte shared table; may be NULL uint32_t* dict_id_out // may be NULL ); ``` Validates and parses a `.zxd` file from memory in **one call**. On success, `content_out` and `huf_out` (when non-NULL) both point into the input buffer (zero-copy) — pass them straight to `dict` / `dict_huf` in the options. Returns `ZXC_OK` or a negative error code. ### `zxc_dict_huf` ```c ZXC_EXPORT const void* zxc_dict_huf(const void* buf, size_t buf_size); ``` Standalone accessor for the 128-byte shared table inside a `.zxd` buffer (zero-copy), or NULL if invalid. Equivalent to the `huf_out` of `zxc_dict_load()`; use it when you only need the table. Returns a zero-copy pointer to the 128-byte shared Huffman table inside a `.zxd` buffer (valid as long as `buf` is), or NULL if `buf` is not a valid `.zxd` file. Pass this to `zxc_compress_opts_t::dict_huf` / `zxc_decompress_opts_t::dict_huf`. ### `zxc_dict_save_bound` ```c ZXC_EXPORT size_t zxc_dict_save_bound(size_t content_size); ``` Returns the `.zxd` file size for a given content size (`ZXC_DICT_HEADER_SIZE + content_size + ZXC_HUF_TABLE_SIZE`). ### `zxc_seekable_set_dict` ```c ZXC_EXPORT int zxc_seekable_set_dict( zxc_seekable* s, const void* dict, size_t dict_size, const void* dict_huf // 128-byte table, or NULL ); ``` Attaches a dictionary to a seekable handle for random-access decompression. Pass the shared table as `dict_huf` (the archive's `dict_id` binds the pair); pass NULL for a raw content-only dictionary. Both buffers are copied internally. Must be called before any `zxc_seekable_decompress_range()` call. --- ## 12. Error Handling ### `zxc_error_name` ```c ZXC_EXPORT const char* zxc_error_name(int code); ``` Returns a constant, null-terminated string for any error code (e.g. `"ZXC_OK"`, `"ZXC_ERROR_MEMORY"`). Returns `"ZXC_UNKNOWN_ERROR"` for unrecognized codes. ### Pattern All functions that can fail use the same convention: ```c int64_t result = zxc_compress(src, src_size, dst, dst_cap, &opts); if (result < 0) { fprintf(stderr, "Error: %s\n", zxc_error_name((int)result)); } ``` --- ## 13. Thread Safety | API Layer | Safe to call concurrently? | Notes | |-----------|---------------------------|-------| | **Buffer API** | Yes (stateless) | Each call is self-contained. Multiple threads can compress/decompress simultaneously with independent buffers. | | **Block API** | Per-context | Uses `zxc_cctx` / `zxc_dctx`: same rule as Context API. Create one context per thread. | | **Context API** | Per-context | A single `zxc_cctx` / `zxc_dctx` must not be shared between threads. Create one context per thread. | | **Static Context API** | Per-handle | Same per-context rule as the dynamic Context API. Allocate one workspace per thread; the static handle does not introduce any cross-thread synchronisation. | | **Streaming API** | Per-call | Each `zxc_stream_*` call manages its own thread pool internally. Do not call from multiple threads on the same `FILE*`. | | **Seekable API** | Per-handle | A single `zxc_seekable` handle must not be shared between threads for single-threaded decompression. Use `zxc_seekable_decompress_range_mt()` for parallel access. | | `zxc_error_name` | Yes | Returns a pointer to a static string. | --- ## 14. Exported Symbols Summary The shared library exports **47 symbols** (verified with `nm -gU`): | # | Symbol | API Layer | Header | |---|--------|-----------|--------| | 1 | `zxc_min_level` | Info | `zxc_buffer.h` | | 2 | `zxc_max_level` | Info | `zxc_buffer.h` | | 3 | `zxc_default_level` | Info | `zxc_buffer.h` | | 4 | `zxc_version_string` | Info | `zxc_buffer.h` | | 5 | `zxc_compress_bound` | Buffer | `zxc_buffer.h` | | 6 | `zxc_compress` | Buffer | `zxc_buffer.h` | | 7 | `zxc_decompress` | Buffer | `zxc_buffer.h` | | 8 | `zxc_get_decompressed_size` | Buffer | `zxc_buffer.h` | | 9 | `zxc_compress_block_bound` | Block | `zxc_buffer.h` | | 10 | `zxc_decompress_block_bound` | Block | `zxc_buffer.h` | | 11 | `zxc_compress_block` | Block | `zxc_buffer.h` | | 12 | `zxc_decompress_block` | Block | `zxc_buffer.h` | | 13 | `zxc_decompress_block_safe` | Block | `zxc_buffer.h` | | 14 | `zxc_estimate_cctx_size` | Block | `zxc_buffer.h` | | 15 | `zxc_create_cctx` | Context | `zxc_buffer.h` | | 16 | `zxc_free_cctx` | Context | `zxc_buffer.h` | | 17 | `zxc_compress_cctx` | Context | `zxc_buffer.h` | | 18 | `zxc_create_dctx` | Context | `zxc_buffer.h` | | 19 | `zxc_free_dctx` | Context | `zxc_buffer.h` | | 20 | `zxc_decompress_dctx` | Context | `zxc_buffer.h` | | 21 | `zxc_static_cctx_workspace_size` | Static Context | `zxc_buffer.h` | | 22 | `zxc_init_static_cctx` | Static Context | `zxc_buffer.h` | | 23 | `zxc_static_dctx_workspace_size` | Static Context | `zxc_buffer.h` | | 24 | `zxc_init_static_dctx` | Static Context | `zxc_buffer.h` | | 25 | `zxc_stream_compress` | Streaming | `zxc_stream.h` | | 26 | `zxc_stream_decompress` | Streaming | `zxc_stream.h` | | 27 | `zxc_stream_get_decompressed_size` | Streaming | `zxc_stream.h` | | 28 | `zxc_cstream_create` | Push Streaming | `zxc_pstream.h` | | 29 | `zxc_cstream_free` | Push Streaming | `zxc_pstream.h` | | 30 | `zxc_cstream_compress` | Push Streaming | `zxc_pstream.h` | | 31 | `zxc_cstream_end` | Push Streaming | `zxc_pstream.h` | | 32 | `zxc_cstream_in_size` | Push Streaming | `zxc_pstream.h` | | 33 | `zxc_cstream_out_size` | Push Streaming | `zxc_pstream.h` | | 34 | `zxc_dstream_create` | Push Streaming | `zxc_pstream.h` | | 35 | `zxc_dstream_free` | Push Streaming | `zxc_pstream.h` | | 36 | `zxc_dstream_decompress` | Push Streaming | `zxc_pstream.h` | | 37 | `zxc_dstream_finished` | Push Streaming | `zxc_pstream.h` | | 38 | `zxc_dstream_in_size` | Push Streaming | `zxc_pstream.h` | | 39 | `zxc_dstream_out_size` | Push Streaming | `zxc_pstream.h` | | 40 | `zxc_seekable_open` | Seekable | `zxc_seekable.h` | | 41 | `zxc_seekable_open_file` | Seekable | `zxc_stream.h` | | 42 | `zxc_seekable_get_num_blocks` | Seekable | `zxc_seekable.h` | | 43 | `zxc_seekable_get_decompressed_size` | Seekable | `zxc_seekable.h` | | 44 | `zxc_seekable_get_block_comp_size` | Seekable | `zxc_seekable.h` | | 45 | `zxc_seekable_get_block_decomp_size` | Seekable | `zxc_seekable.h` | | 46 | `zxc_seekable_decompress_range` | Seekable | `zxc_seekable.h` | | 47 | `zxc_seekable_decompress_range_mt` | Seekable | `zxc_seekable.h` | | 48 | `zxc_seekable_free` | Seekable | `zxc_seekable.h` | | 49 | `zxc_write_seek_table` | Seekable | `zxc_seekable.h` | | 50 | `zxc_seek_table_size` | Seekable | `zxc_seekable.h` | | 51 | `zxc_error_name` | Error | `zxc_error.h` | | 52 | `zxc_dict_train` | Dictionary | `zxc_dict.h` | | 53 | `zxc_train_dict` | Dictionary | `zxc_dict.h` | | 54 | `zxc_train_dict_huf` | Dictionary | `zxc_dict.h` | | 55 | `zxc_dict_id` | Dictionary | `zxc_dict.h` | | 56 | `zxc_dict_save` | Dictionary | `zxc_dict.h` | | 57 | `zxc_dict_load` | Dictionary | `zxc_dict.h` | | 58 | `zxc_dict_huf` | Dictionary | `zxc_dict.h` | | 59 | `zxc_dict_get_id` | Dictionary | `zxc_dict.h` | | 60 | `zxc_dict_save_bound` | Dictionary | `zxc_dict.h` | | 61 | `zxc_seekable_set_dict` | Seekable | `zxc_seekable.h` | No internal symbols leak into the public ABI. FMV dispatch variants (`_default`, `_neon`, `_avx2`, `_avx512`) are compiled with `-fvisibility=hidden` and are not exported. zxc-0.13.0/docs/Doxyfile.in000066400000000000000000000252041522536370400154540ustar00rootroot00000000000000# Doxyfile 1.9.1 #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- PROJECT_NAME = "@PROJECT_NAME@" PROJECT_NUMBER = "@PROJECT_VERSION@" PROJECT_BRIEF = "@PROJECT_DESCRIPTION@" OUTPUT_DIRECTORY = docs CREATE_SUBDIRS = NO ALLOW_UNICODE_NAMES = NO OUTPUT_LANGUAGE = English BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = YES STRIP_FROM_PATH = STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES JAVADOC_BANNER = NO QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO PYTHON_DOCSTRING = YES INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 4 ALIASES = OPTIMIZE_OUTPUT_FOR_C = YES OPTIMIZE_OUTPUT_JAVA = NO OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO OPTIMIZE_OUTPUT_SLICE = NO EXTENSION_MAPPING = MARKDOWN_SUPPORT = YES TOC_INCLUDE_HEADINGS = 5 AUTOLINK_SUPPORT = YES BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = YES DISTRIBUTE_GROUP_DOC = NO GROUP_NESTED_COMPOUNDS = NO SUBGROUPING = YES INLINE_GROUPED_CLASSES = NO INLINE_SIMPLE_STRUCTS = NO TYPEDEF_HIDES_STRUCT = NO LOOKUP_CACHE_SIZE = 0 NUM_PROC_THREADS = 1 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = NO EXTRACT_PRIVATE = NO EXTRACT_PRIV_VIRTUAL = NO EXTRACT_PACKAGE = NO EXTRACT_STATIC = YES EXTRACT_LOCAL_CLASSES = NO EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO RESOLVE_UNNAMED_PARAMS = YES HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO HIDE_COMPOUND_REFERENCE= NO SHOW_HEADERFILE = YES SHOW_INCLUDE_FILES = YES SHOW_GROUPED_MEMB_INC = NO FORCE_LOCAL_INCLUDES = NO INLINE_INFO = YES SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = NO SORT_MEMBERS_CTORS_1ST = NO SORT_GROUP_NAMES = NO SORT_BY_SCOPE_NAME = NO STRICT_PROTO_MATCHING = NO GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES SHOW_FILES = YES SHOW_NAMESPACES = YES FILE_VERSION_FILTER = LAYOUT_FILE = CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = NO WARNINGS = YES WARN_IF_UNDOCUMENTED = NO WARN_IF_DOC_ERROR = YES WARN_IF_INCOMPLETE_DOC = YES WARN_NO_PARAMDOC = YES WARN_AS_ERROR = NO WARN_FORMAT = "$file:$line: $text" WARN_LINE_FORMAT = "at line $line of file $file" WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- INPUT = README.md \ include \ src/lib INPUT_ENCODING = UTF-8 INPUT_FILE_ENCODING = FILE_PATTERNS = *.h \ *.c RECURSIVE = YES EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = */test/* \ */tests/* \ */build/* \ */target/* \ */src/lib/*.c EXCLUDE_SYMBOLS = EXAMPLE_PATH = EXAMPLE_PATTERNS = * EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO FILTER_SOURCE_PATTERNS = USE_MDFILE_AS_MAINPAGE = README.md #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = YES INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = YES REFERENCES_RELATION = YES REFERENCES_LINK_SOURCE = YES SOURCE_TOOLTIPS = YES USE_HTAGS = NO VERBATIM_HEADERS = YES CLANG_ASSISTED_PARSING = NO CLANG_ADD_INC_PATHS = YES CLANG_OPTIONS = CLANG_DATABASE_PATH = #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = YES IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = html HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = HTML_EXTRA_STYLESHEET = HTML_EXTRA_FILES = HTML_COLORSTYLE = AUTO_LIGHT HTML_COLORSTYLE_HUE = 220 HTML_COLORSTYLE_SAT = 100 HTML_COLORSTYLE_GAMMA = 80 HTML_TIMESTAMP = NO HTML_DYNAMIC_MENUS = YES HTML_DYNAMIC_SECTIONS = NO HTML_INDEX_NUM_ENTRIES = 100 GENERATE_DOCSET = NO GENERATE_HTMLHELP = NO GENERATE_QHP = NO GENERATE_ECLIPSEHELP = NO DISABLE_INDEX = NO GENERATE_TREEVIEW = YES FULL_SIDEBAR = NO ENUM_VALUES_PER_LINE = 4 TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO OBFUSCATE_EMAILS = YES HTML_FORMULA_FORMAT = png FORMULA_FONTSIZE = 10 FORMULA_MACROFILE = USE_MATHJAX = NO MATHJAX_VERSION = MathJax_2 MATHJAX_FORMAT = HTML-CSS MATHJAX_RELPATH = MATHJAX_EXTENSIONS = MATHJAX_CODEFILE = SEARCHENGINE = YES SERVER_BASED_SEARCH = NO EXTERNAL_SEARCH = NO SEARCHENGINE_URL = SEARCHDATA_FILE = searchdata.xml EXTERNAL_SEARCH_ID = EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- # Configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- GENERATE_RTF = NO #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = NO #--------------------------------------------------------------------------- # Configuration options related to the XML output #--------------------------------------------------------------------------- GENERATE_XML = NO #--------------------------------------------------------------------------- # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- GENERATE_DOCBOOK = NO #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # Configuration options related to Sqlite3 output #--------------------------------------------------------------------------- GENERATE_SQLITE3 = NO #--------------------------------------------------------------------------- # Configuration options related to the Perl module output #--------------------------------------------------------------------------- GENERATE_PERLMOD = NO #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES MACRO_EXPANSION = YES EXPAND_ONLY_PREDEF = NO SEARCH_INCLUDES = YES INCLUDE_PATH = include INCLUDE_FILE_PATTERNS = PREDEFINED = ZXC_EXPORT= \ ZXC_NO_EXPORT= \ ZXC_DEPRECATED= \ __attribute__(x)= EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration options related to external references #--------------------------------------------------------------------------- TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES #--------------------------------------------------------------------------- # Configuration options related to diagram generator tools #--------------------------------------------------------------------------- HIDE_UNDOC_RELATIONS = YES HAVE_DOT = NO DOT_NUM_THREADS = 0 DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10" DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10" DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" DOT_FONTPATH = CLASS_GRAPH = YES COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES UML_LOOK = NO UML_LIMIT_NUM_FIELDS = 10 DOT_UML_DETAILS = NO DOT_WRAP_THRESHOLD = 17 TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES CALL_GRAPH = NO CALLER_GRAPH = NO GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES DIR_GRAPH_MAX_DEPTH = 1 DOT_IMAGE_FORMAT = png INTERACTIVE_SVG = NO DOT_PATH = DOTFILE_DIRS = DIA_PATH = DIAFILE_DIRS = PLANTUML_JAR_PATH = PLANTUML_CFG_FILE = PLANTUML_INCLUDE_PATH = DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES DOT_CLEANUP = YES MSCGEN_TOOL = MSCFILE_DIRS = zxc-0.13.0/docs/EXAMPLES.md000066400000000000000000000635251522536370400151110ustar00rootroot00000000000000# ZXC API Examples This document provides complete, working examples for using the ZXC compression library in C. ## Table of Contents - [Buffer API (In-Memory)](#buffer-api-in-memory) - [Stream API (Multi-Threaded)](#stream-api-multi-threaded) - [Reusable Context API](#reusable-context-api) - [Seekable Reader (Custom Storage Backend)](#seekable-reader-custom-storage-backend) - [Compressing Numeric Data (Pre-Filters)](#compressing-numeric-data-pre-filters) - [Using a Pre-Trained Dictionary](#using-a-pre-trained-dictionary) - [Meson Integration](#meson-integration) - [Language Bindings](#language-bindings) --- ## Buffer API (In-Memory) Ideal for small assets or simple integrations. Thread-safe and ready for highly concurrent environments (Go routines, Node.js workers, Python threads). ```c #include "zxc.h" #include #include #include int main(void) { // Original data to compress const char* original = "Hello, ZXC! This is a sample text for compression."; size_t original_size = strlen(original) + 1; // Include null terminator // Step 1: Calculate maximum compressed size uint64_t max_compressed_size = zxc_compress_bound(original_size); // Step 2: Allocate buffers void* compressed = malloc(max_compressed_size); void* decompressed = malloc(original_size); if (!compressed || !decompressed) { fprintf(stderr, "Memory allocation failed\n"); free(compressed); free(decompressed); return 1; } // Step 3: Compress data (level 3, checksum enabled, default block size) zxc_compress_opts_t c_opts = { .level = ZXC_LEVEL_DEFAULT, .checksum_enabled = 1, /* .block_size = 0 -> 512 KB default */ }; int64_t compressed_size = zxc_compress( original, // Source buffer original_size, // Source size compressed, // Destination buffer max_compressed_size, // Destination capacity &c_opts // Options (NULL = all defaults) ); if (compressed_size <= 0) { fprintf(stderr, "Compression failed (error %lld)\n", (long long)compressed_size); free(compressed); free(decompressed); return 1; } printf("Original size: %zu bytes\n", original_size); printf("Compressed size: %lld bytes (%.1f%% ratio)\n", (long long)compressed_size, 100.0 * (double)compressed_size / (double)original_size); // Step 4: Decompress data (checksum verification enabled) zxc_decompress_opts_t d_opts = { .checksum_enabled = 1 }; int64_t decompressed_size = zxc_decompress( compressed, // Source buffer (size_t)compressed_size, decompressed, // Destination buffer original_size, // Destination capacity &d_opts // Options (NULL = all defaults) ); if (decompressed_size <= 0) { fprintf(stderr, "Decompression failed (error %lld)\n", (long long)decompressed_size); free(compressed); free(decompressed); return 1; } // Step 5: Verify integrity if ((size_t)decompressed_size == original_size && memcmp(original, decompressed, original_size) == 0) { printf("Success! Data integrity verified.\n"); printf("Decompressed: %s\n", (char*)decompressed); } else { fprintf(stderr, "Data mismatch after decompression\n"); } // Cleanup free(compressed); free(decompressed); return 0; } ``` **Compilation:** ```bash gcc -o buffer_example buffer_example.c -I include -L build -lzxc_lib ``` --- ## Stream API (Multi-Threaded) For large files, use the streaming API to process data in parallel chunks. The `FILE*`-based entry points live in `zxc_stream.h`, which the freestanding `` umbrella does *not* pull (so kernel/embedded builds stay clean of ``). Userspace consumers include it explicitly. ```c #include "zxc.h" #include "zxc_stream.h" // FILE*-based streaming, opt-in #include #include int main(int argc, char* argv[]) { if (argc != 4) { fprintf(stderr, "Usage: %s \n", argv[0]); return 1; } const char* input_path = argv[1]; const char* compressed_path = argv[2]; const char* output_path = argv[3]; /* ------------------------------------------------------------------ */ /* Step 1: Compress */ /* ------------------------------------------------------------------ */ printf("Compressing '%s' to '%s'...\n", input_path, compressed_path); FILE* f_in = fopen(input_path, "rb"); if (!f_in) { fprintf(stderr, "Error: cannot open '%s'\n", input_path); return 1; } FILE* f_out = fopen(compressed_path, "wb"); if (!f_out) { fprintf(stderr, "Error: cannot create '%s'\n", compressed_path); fclose(f_in); return 1; } // 0 threads = auto-detect CPU cores; 0 block_size = 512 KB default zxc_compress_opts_t c_opts = { .n_threads = 0, .level = ZXC_LEVEL_DEFAULT, .checksum_enabled = 1, /* .block_size = 0 -> 512 KB */ }; int64_t compressed_bytes = zxc_stream_compress(f_in, f_out, &c_opts); fclose(f_in); fclose(f_out); if (compressed_bytes < 0) { fprintf(stderr, "Compression failed (error %lld)\n", (long long)compressed_bytes); return 1; } printf("Compression complete: %lld bytes written\n", (long long)compressed_bytes); /* ------------------------------------------------------------------ */ /* Step 2: Decompress */ /* ------------------------------------------------------------------ */ printf("\nDecompressing '%s' to '%s'...\n", compressed_path, output_path); FILE* f_compressed = fopen(compressed_path, "rb"); if (!f_compressed) { fprintf(stderr, "Error: cannot open '%s'\n", compressed_path); return 1; } FILE* f_decompressed = fopen(output_path, "wb"); if (!f_decompressed) { fprintf(stderr, "Error: cannot create '%s'\n", output_path); fclose(f_compressed); return 1; } zxc_decompress_opts_t d_opts = { .n_threads = 0, .checksum_enabled = 1 }; int64_t decompressed_bytes = zxc_stream_decompress(f_compressed, f_decompressed, &d_opts); fclose(f_compressed); fclose(f_decompressed); if (decompressed_bytes < 0) { fprintf(stderr, "Decompression failed (error %lld)\n", (long long)decompressed_bytes); return 1; } printf("Decompression complete: %lld bytes written\n", (long long)decompressed_bytes); printf("\nSuccess! Verify the output file matches the original.\n"); return 0; } ``` **Compilation:** ```bash gcc -o stream_example stream_example.c -I include -L build -lzxc_lib -lpthread -lm ``` **Usage:** ```bash ./stream_example large_file.bin compressed.zxc decompressed.bin ``` **Features demonstrated:** - Multi-threaded parallel processing (auto-detects CPU cores) - Checksum validation for data integrity - Error handling for file operations --- ## Reusable Context API For tight loops - such as filesystem plug-ins (squashfs, dwarfs, erofs) - where allocating and freeing internal buffers on every call would add latency, ZXC provides opaque reusable contexts. Internal buffers are only reallocated when the **block size changes**, so the common case (same block size, different data) is completely allocation-free. Options are **sticky**: settings passed via `opts` to `zxc_create_cctx()` or `zxc_compress_cctx()` are remembered and reused on subsequent calls where `opts` is `NULL`. ```c #include "zxc.h" #include #include #include #define BLOCK_SIZE (64 * 1024) // 64 KB - typical squashfs block size #define NUM_BLOCKS 32 int main(void) { // Create context with sticky options (level 3, no checksum, 64 KB blocks). // These settings are remembered for all subsequent calls. zxc_compress_opts_t create_opts = { .level = 3, .checksum_enabled = 0, .block_size = BLOCK_SIZE, }; zxc_cctx* cctx = zxc_create_cctx(&create_opts); zxc_dctx* dctx = zxc_create_dctx(); if (!cctx || !dctx) { fprintf(stderr, "Context creation failed\n"); zxc_free_cctx(cctx); zxc_free_dctx(dctx); return 1; } const size_t comp_cap = (size_t)zxc_compress_bound(BLOCK_SIZE); uint8_t* comp = malloc(comp_cap); uint8_t* src = malloc(BLOCK_SIZE); uint8_t* dec = malloc(BLOCK_SIZE); for (int i = 0; i < NUM_BLOCKS; i++) { // Fill block with pseudo-random data for (size_t j = 0; j < BLOCK_SIZE; j++) src[j] = (uint8_t)(i ^ j); // Compress - pass NULL to reuse sticky settings from create_opts. // No malloc/free inside, no need to pass opts again. int64_t csz = zxc_compress_cctx(cctx, src, BLOCK_SIZE, comp, comp_cap, NULL); if (csz <= 0) { fprintf(stderr, "Block %d: compress error %lld\n", i, (long long)csz); break; } // Decompress - no malloc/free inside int64_t dsz = zxc_decompress_dctx(dctx, comp, (size_t)csz, dec, BLOCK_SIZE, NULL); if (dsz != BLOCK_SIZE || memcmp(src, dec, BLOCK_SIZE) != 0) { fprintf(stderr, "Block %d: roundtrip mismatch\n", i); break; } } // Override sticky settings for a single call (e.g. switch to level 5). // This also updates the sticky settings for future NULL calls. zxc_compress_opts_t high_opts = { .level = 5 }; int64_t csz = zxc_compress_cctx(cctx, src, BLOCK_SIZE, comp, comp_cap, &high_opts); printf("Level-5 compressed: %lld bytes\n", (long long)csz); printf("Processed %d blocks of %d KB - no per-block allocation.\n", NUM_BLOCKS, BLOCK_SIZE / 1024); zxc_free_cctx(cctx); zxc_free_dctx(dctx); free(src); free(comp); free(dec); return 0; } ``` **Compilation:** ```bash gcc -o ctx_example ctx_example.c -I include -L build -lzxc_lib ``` --- ## Seekable Reader (Custom Storage Backend) The seekable API also accepts a user-supplied **reader callback**, letting you back random-access decompression with any storage that supports positional reads: `mmap`, an HTTP range-request client, an S3 object, a custom VFS, `vfs_read()` in kernel space, etc. The reader exposes a single primitive: ```c typedef struct { int64_t (*read_at)(void* ctx, void* dst, size_t len, uint64_t offset); void* ctx; uint64_t size; // total size of the compressed archive } zxc_reader_t; ``` `read_at` MUST be safe to call concurrently from multiple threads if you intend to use `zxc_seekable_decompress_range_mt()`. The single-threaded path makes no concurrent calls. The example below wires the reader to an `mmap()`'d archive file, then decompresses an arbitrary byte range — no `FILE*` is involved. ```c #include "zxc.h" #include "zxc_seekable.h" #include #include #include #include #include #include #include typedef struct { const uint8_t* base; // mmap'd start of the archive uint64_t size; } mmap_reader_ctx_t; // read_at: just memcpy from the mapping. Naturally thread-safe. static int64_t mmap_read_at(void* ctx, void* dst, size_t len, uint64_t offset) { mmap_reader_ctx_t* m = (mmap_reader_ctx_t*)ctx; if (offset > m->size || len > m->size - offset) return -1; // bounds check memcpy(dst, m->base + offset, len); return (int64_t)len; } int main(int argc, char** argv) { if (argc != 4) { fprintf(stderr, "Usage: %s \n", argv[0]); return 1; } // Open and mmap the seekable archive. int fd = open(argv[1], O_RDONLY); if (fd < 0) { perror("open"); return 1; } struct stat st; if (fstat(fd, &st) != 0) { perror("fstat"); close(fd); return 1; } void* mapping = mmap(NULL, (size_t)st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (mapping == MAP_FAILED) { perror("mmap"); close(fd); return 1; } // Wire the reader callback to the mmap'd region. mmap_reader_ctx_t mctx = { .base = (const uint8_t*)mapping, .size = (uint64_t)st.st_size }; zxc_reader_t reader = { .read_at = mmap_read_at, .ctx = &mctx, .size = (uint64_t)st.st_size }; zxc_seekable* s = zxc_seekable_open_reader(&reader); if (!s) { fprintf(stderr, "Not a valid seekable ZXC archive\n"); munmap(mapping, (size_t)st.st_size); close(fd); return 1; } fprintf(stderr, "Archive: %u blocks, %llu bytes decompressed.\n", zxc_seekable_get_num_blocks(s), (unsigned long long)zxc_seekable_get_decompressed_size(s)); // Decompress a user-requested byte range. const uint64_t offset = strtoull(argv[2], NULL, 0); const size_t length = (size_t)strtoull(argv[3], NULL, 0); uint8_t* out = malloc(length); if (!out) { zxc_seekable_free(s); munmap(mapping, st.st_size); close(fd); return 1; } // The MT path is safe here because mmap_read_at is reentrant. int64_t n = zxc_seekable_decompress_range_mt(s, out, length, offset, length, 0); if (n < 0) { fprintf(stderr, "decompress_range failed: %lld\n", (long long)n); free(out); zxc_seekable_free(s); munmap(mapping, st.st_size); close(fd); return 1; } fwrite(out, 1, (size_t)n, stdout); free(out); zxc_seekable_free(s); munmap(mapping, (size_t)st.st_size); close(fd); return 0; } ``` **Compilation:** ```bash gcc -o seekable_reader seekable_reader.c -I include -L build -lzxc -lpthread ``` **Other backends.** The same pattern applies to anything addressable by offset: an HTTP `Range:` GET (return `-1` on a non-`206` response), an S3 `GetObject` with `--range`, a kernel `vfs_read(file, ..., &pos)`, a custom VFS plug-in. As long as `read_at` returns exactly the requested length (or a negative `zxc_error_t` code), the seekable API treats it as a transparent backend. --- ## Compressing Numeric Data (Pre-Filters) ZXC is a **pure LZ byte codec**: it compresses by finding repeated byte sequences and entropy-coding the residue. Arrays of numbers (timestamps, IDs, counters, sensor readings, floats) carry a lot of redundancy — but it lives in their *numeric* structure (smooth progression, shared magnitude), which is invisible at the byte level, so a raw `zxc_compress` often barely compresses them. The fix is a **reversible pre-filter**: a cheap, lossless transform applied **before** compression and inverted **after** decompression. It does not compress anything itself — it re-expresses the numeric structure as byte-level redundancy that ZXC can then exploit: ``` compress : data -> [filter] -> zxc_compress -> archive decompress: archive -> zxc_decompress -> [inverse filter] -> data ``` ZXC keeps the filter **out of the format on purpose**: the codec stays pure, and *you* own the transform — including remembering which one you applied, in your own schema/metadata, so you can invert it. The two most useful filters are **delta** (correlated sequences) and **byte-shuffle** (wide values whose high bytes are similar, e.g. floats). ```c #include "zxc.h" #include #include #include /* --- Delta: each element becomes its difference from the previous one. * Great for monotonic / smooth sequences (timestamps, IDs, counters). --- */ static void delta_encode_i64(int64_t *a, size_t n) { for (size_t i = n; i-- > 1;) a[i] -= a[i - 1]; /* back to front */ } static void delta_decode_i64(int64_t *a, size_t n) { for (size_t i = 1; i < n; i++) a[i] += a[i - 1]; /* prefix sum */ } /* --- Byte-shuffle: group byte k of every element together, plane by plane. * Great for float/double columns: similar sign/exponent bytes become * contiguous runs. src and dst must not overlap; w = element size. --- */ static void shuffle_encode(const uint8_t *src, uint8_t *dst, size_t n, size_t w) { for (size_t b = 0; b < w; b++) for (size_t i = 0; i < n; i++) dst[b * n + i] = src[i * w + b]; } static void shuffle_decode(const uint8_t *src, uint8_t *dst, size_t n, size_t w) { for (size_t b = 0; b < w; b++) for (size_t i = 0; i < n; i++) dst[i * w + b] = src[b * n + i]; } int main(void) { /* A column of ~1 ms timestamps: huge values, tiny differences. */ const size_t n = 100000; int64_t *ts = malloc(n * sizeof *ts); int64_t t = 1700000000000000LL; for (size_t i = 0; i < n; i++) { t += 1000000 + (int64_t)(i % 7); ts[i] = t; } const size_t raw = n * sizeof *ts; size_t cap = (size_t)zxc_compress_bound(raw); void *comp = malloc(cap); zxc_compress_opts_t opts = { .level = 3 }; /* 1. Filter, then compress. */ delta_encode_i64(ts, n); /* numeric struct -> small repeated deltas */ int64_t csize = zxc_compress(ts, raw, comp, cap, &opts); /* 2. Decompress, then invert the filter. */ int64_t *out = malloc(raw); zxc_decompress(comp, (size_t)csize, out, raw, NULL); delta_decode_i64(out, n); /* exact original timestamps restored */ free(ts); free(comp); free(out); return 0; } ``` **Choosing a filter (you must record this choice yourself):** | Data | Filter | Inverse | |---|---|---| | Monotonic / correlated integers (timestamps, IDs, counters) | `delta` | prefix sum | | Fixed-cadence series (drifting stride) | `delta` twice (delta-of-delta) | prefix sum twice | | Float/double columns, wide integers | `shuffle` (byte transpose) | un-shuffle | | Floats varying slightly between samples | XOR with previous | XOR again | > **The archive does not record the filter.** ZXC stores only the compressed > bytes; it has no idea a filter was applied. Your application is responsible for > remembering — in its own schema or metadata — which filter and `elem_size` it > used, so it can run the matching inverse after `zxc_decompress`. Apply the > filter only to genuinely numeric, type-known data; on arbitrary bytes it will > not help (and may hurt). --- ## Using a Pre-Trained Dictionary For corpora of **small, similar payloads** (JSON records, log lines, RPC messages, small files), a pre-trained dictionary substantially improves the ratio. The dictionary is logically **prepended to the LZ window** at the start of each block, so even the first bytes of a tiny payload can match against it. How it ties together: - A dictionary is up to **64 KB** of content plus a **shared literal Huffman table** (128 bytes, derived from the real post-LZ literal distribution). A `.zxd` file always carries both. `zxc_dict_train()` builds the whole thing from samples in one call; `zxc_dict_load()` unpacks it in one call. - It is identified by a **`dict_id`** that binds the **(content, table)** pair, stored in the `.zxd` file and in every archive compressed with it. - A dictionary-compressed archive records `HAS_DICTIONARY` + the `dict_id` in its header, but **not the dictionary itself** — you must provide the same dictionary (content **and** table) to decompress. A decoder reads the required id with `zxc_get_dict_id()`. - The biggest gains are on **small blocks** (4–128 KB): at level 6 the shared table lets literal sections skip their 128-byte per-block Huffman header, and the decoder builds the table once instead of once per block. On large blocks the data builds its own match history, so a dictionary helps little. - **Raw content-only dictionaries** are still possible via the library API (pass `dict`/`dict_size` with `dict_huf = NULL`) for ad-hoc priming where a trained table adds nothing (binary data, levels 1–5). Such archives never use the shared-table literal encoding. The `.zxd` *file* format always includes the table. ### CLI quickstart ```sh # Train from a corpus of files -> writes dictionary_.zxd in the # current directory (use -o DIR/ or -o FILE to choose the destination). zxc --train ./corpus/* # Compress / decompress with the dictionary (required at both ends). zxc -D dictionary_1a2b3c4d.zxd -z record.json zxc -D dictionary_1a2b3c4d.zxd -d record.json.zxc -o record.json ``` ### C API ```c #include "zxc.h" #include "zxc_dict.h" // training, save/load, identification #include #include #include #include int main(void) { /* 1. Build a dictionary from representative samples: produces the * complete .zxd bytes (trains content + shared table, serializes). */ const char *s0 = "{\"event\":\"login\",\"user_id\":1001,\"ok\":true}"; const char *s1 = "{\"event\":\"logout\",\"user_id\":1002,\"ok\":true}"; const void *samples[] = { s0, s1 }; const size_t sizes[] = { strlen(s0), strlen(s1) }; size_t zbnd = zxc_dict_save_bound(ZXC_DICT_SIZE_MAX); /* always-safe upper bound */ uint8_t *zxd = malloc(zbnd); int64_t zxd_size = zxc_dict_train(samples, sizes, 2, zxd, zbnd); if (zxd_size < 0) { fprintf(stderr, "dict train failed\n"); return 1; } /* ... write zxd[0..zxd_size) to "samples.zxd" and ship it to decoders ... */ /* 2. Unpack the .zxd: content + table + id (zero-copy). */ const void *content, *table; size_t content_size; uint32_t dict_id; zxc_dict_load(zxd, (size_t)zxd_size, &content, &content_size, &table, &dict_id); /* 3. Compress WITH the dictionary (level 6 to engage the shared table). */ const char *msg = "{\"event\":\"login\",\"user_id\":2003,\"ok\":true}"; size_t n = strlen(msg) + 1; size_t cap = (size_t)zxc_compress_bound(n); uint8_t *comp = malloc(cap); zxc_compress_opts_t copts = { .level = 6, .dict = content, .dict_size = content_size, .dict_huf = table }; int64_t csize = zxc_compress(msg, n, comp, cap, &copts); if (csize < 0) { fprintf(stderr, "compress failed\n"); return 1; } /* The archive header now carries HAS_DICTIONARY + dict_id; a decoder reads * which dictionary it needs with zxc_get_dict_id(comp, csize). */ /* 4. Decompress WITH the same dictionary (content + table). */ uint8_t *out = malloc(n); zxc_decompress_opts_t dopts = { .dict = content, .dict_size = content_size, .dict_huf = table }; int64_t dsize = zxc_decompress(comp, (size_t)csize, out, n, &dopts); if (dsize != (int64_t)n || memcmp(msg, out, n) != 0) { fprintf(stderr, "round-trip failed\n"); return 1; } free(zxd); free(comp); free(out); /* content/table point INTO zxd; freed last */ return 0; } ``` > The `content` and `table` pointers returned by `zxc_dict_load()` aim **into** > `zxd` (zero-copy), so keep `zxd` alive until you're done compressing or > decompressing. The lower-level primitives (`zxc_train_dict` + > `zxc_train_dict_huf` + `zxc_dict_save`, and `zxc_dict_huf`) remain available > for advanced use — e.g. a **raw content-only dictionary** with no table > (`dict` set, `dict_huf = NULL`) for ad-hoc window priming where a trained > table adds nothing. ```c // (the primitives, for reference — zxc_dict_train above wraps the first three) // int64_t cs = zxc_train_dict(samples, sizes, n, content_buf, cap); // zxc_train_dict_huf(samples, sizes, n, content_buf, cs, huf /*128 bytes*/); // int64_t zs = zxc_dict_save(content_buf, cs, huf, zxd_buf, zbnd); ``` ### Error contract Decoding a dictionary archive enforces that the right dictionary is supplied: | Situation | Result | |---|---| | No dictionary supplied (`opts == NULL` or `dict == NULL`) | `ZXC_ERROR_DICT_REQUIRED` | | Wrong dictionary, or table missing/mismatched (`dict_id` mismatch) | `ZXC_ERROR_DICT_MISMATCH` | | Dictionary larger than `ZXC_DICT_SIZE_MAX` (64 KB) | `ZXC_ERROR_DICT_TOO_LARGE` | Because the `dict_id` binds the **(content, table)** pair, decoding an archive that was compressed with a table requires passing that same table via `dict_huf` — omitting it (or supplying a different one) fails the id check with `ZXC_ERROR_DICT_MISMATCH`. So a decoder reads `zxc_get_dict_id()` first, fetches the matching `.zxd`, and passes its loaded `content` + `zxc_dict_huf()` table via `zxc_decompress_opts_t`. See `docs/FORMAT.md` §12 for the on-disk dictionary format and §3.1 for the header fields. --- ## Meson Integration zxc can be consumed as a Meson subproject. This is the recommended approach for Meson-based projects that want to vendor or pin a specific zxc version. **Step 1 — Create `subprojects/zxc.wrap`:** ```ini [wrap-git] url = https://github.com/hellobertrand/zxc.git revision = head depth = 1 [provide] libzxc = libzxc_dep ``` **Step 2 — Declare the dependency in your `meson.build`:** ```meson project('my_project', 'c', default_options : ['c_std=c17']) zxc_dep = dependency('libzxc', fallback : ['zxc', 'libzxc_dep']) executable('my_app', 'main.c', dependencies : zxc_dep) ``` When `zxc` is used as a subproject, the CLI and test suite are automatically skipped. Only the library is built. **Step 3 — Build and run:** ```bash meson setup build meson compile -C build ./build/my_app ``` --- ## Language Bindings For non-C languages, see the official bindings: | Language | Package | Install Command | Documentation | |----------|---------|-----------------|---------------| | **Rust** | [`crates.io`](https://crates.io/crates/zxc-compress) | `cargo add zxc-compress` | [README](../wrappers/rust/zxc/README.md) | | **Python**| [`PyPI`](https://pypi.org/project/zxc-compress) | `pip install zxc-compress` | [README](../wrappers/python/README.md) | | **Node.js**| [`npm`](https://www.npmjs.com/package/zxc-compress) | `npm install zxc-compress` | [README](../wrappers/nodejs/README.md) | | **Go** | `go get` | `go get github.com/hellobertrand/zxc/wrappers/go` | [README](../wrappers/go/README.md) | Community-maintained: - **Go**: https://github.com/meysam81/go-zxc - **Nim**: https://github.com/openpeeps/zxc-nim - **Free Pascal**: https://github.com/Xelitan/Free-Pascal-port-of-ZXC-compressor-decompressor zxc-0.13.0/docs/FORMAT.md000066400000000000000000001063611522536370400146570ustar00rootroot00000000000000# ZXC Compressed File Format (Technical Specification) **Date**: June 2026 **Format Version**: 7 This document describes the on-disk binary format of a ZXC compressed file. It formalizes the current reference implementation of format version **7**. ## 1. Conventions - **Byte order**: all multi-byte integers are **little-endian**. - **Unit**: offsets are in bytes, zero-based from the start of each structure. - **Checksum mode**: enabled globally by a flag in the file header. - **Block model**: a file is a sequence of blocks terminated by an EOF block, then a footer. --- ## 2. Full File Layout ```text +----------------------+ 16 bytes | File Header | +----------------------+ | Block #0 | | - 8B Block Header | | - Block Payload | | - Optional 4B CRC32 | +----------------------+ | Block #1 | | ... | +----------------------+ | EOF Block | 8 bytes (type=255, comp_size=0) +----------------------+ | SEK Block (Optional) | table of contents for random access +----------------------+ | File Footer | 12 bytes +----------------------+ ``` --- ## 3. File Header (16 bytes) ```text Offset Size Field 0x00 4 Magic Word 0x04 1 Format Version 0x05 1 Chunk Size Code 0x06 1 Flags 0x07 7 Reserved (must be 0) 0x0E 2 Header CRC16 ``` ### 3.1 Field definitions - **Magic Word** (`u32`): `0x9CB02EF5`. - **Format Version** (`u8`): `7`. Any other value is rejected (`ZXC_ERROR_BAD_VERSION`); - **Chunk Size Code** (`u8`): - The value is an **exponent** in the range `[12, 21]`: `block_size = 2^code`. - `12` = 4 KB, `13` = 8 KB, ..., `19` = 512 KB (default), ..., `21` = 2 MB. - All other values are rejected (`ZXC_ERROR_BAD_BLOCK_SIZE`). - Valid block sizes are powers of 2 in the range **4 KB – 2 MB**. - **Flags** (`u8`): - Bit 7 (`0x80`): `HAS_CHECKSUM`. - Bit 6 (`0x40`): `HAS_DICTIONARY` — a pre-trained dictionary is required for decompression. - Bits 0..3: checksum algorithm id (`0` = RapidHash-based folding). - Bits 4..5: reserved. - **Reserved / Dictionary ID**: 7 bytes. - When `HAS_DICTIONARY` is set: bytes `0x07..0x0A` contain a `dict_id` (`u32` LE), a 32-bit hash of the dictionary content. Bytes `0x0B..0x0D` remain zero. - When `HAS_DICTIONARY` is clear: all 7 bytes are zero. - **Header CRC16** (`u16`): computed with `zxc_hash16` on the 16-byte header where bytes `0x0E..0x0F` are zeroed. --- ## 4. Generic Block Container Each block starts with a fixed 8-byte block header. ```text Offset Size Field 0x00 1 Block Type 0x01 1 Block Flags 0x02 1 Reserved 0x03 4 Compressed Payload Size (comp_size) 0x07 1 Header CRC8 ``` ### 4.1 Header semantics - **Block Type**: - `0` = RAW - `1` = GLO - `2` = GHI - `254` = SEK - `255` = EOF - **Block Flags**: currently not used by implementation (written as `0`). - **Reserved**: must be 0. - **comp_size**: payload size in bytes (does **not** include the optional trailing 4-byte block checksum). - **Header CRC8**: `zxc_hash8` over the 8-byte header with byte `0x07` forced to zero before hashing. ### 4.2 Block physical layout ```text [8B Block Header] + [comp_size bytes payload] + [optional 4B checksum] ``` When checksums are enabled at file level, each non-EOF block carries one trailing 4-byte checksum of its compressed payload. --- ## 5. Block Types and Payload Formats ## 5.1 RAW block (`type=0`) Payload is uncompressed data. ```text Payload = raw bytes raw_size = comp_size ``` No internal sub-header. --- ## 5.2 GLO block (`type=1`) General LZ-style format with separated streams. ### GLO payload layout ```text +-------------------------------+ | GLO Header (16 bytes) | +-------------------------------+ | 4 Section Descriptors (32B) | +-------------------------------+ | Literals stream | +-------------------------------+ | Tokens stream | +-------------------------------+ | Offsets stream | +-------------------------------+ | Extras stream | +-------------------------------+ ``` ### GLO Header (16 bytes) ```text Offset Size Field 0x00 4 n_sequences (u32) 0x04 4 n_literals (u32) 0x08 1 enc_lit (0=RAW, 1=RLE, 2=HUFFMAN, 3=HUFFMAN_DICT) 0x09 1 enc_litlen (0=RAW tokens, 2=HUFFMAN tokens; level 7 only) 0x0A 1 enc_mlen (reserved; match lengths share the token byte) 0x0B 1 enc_off (0=16-bit offsets, 1=8-bit offsets) 0x0C 4 reserved ``` ### GLO section descriptors (4 × 8 bytes) Descriptor format (packed `u64`): - low 32 bits: compressed size - high 32 bits: raw size Section order: 1. Literals 2. Tokens 3. Offsets 4. Extras ### GLO stream content - **Literals stream**: - raw literal bytes if `enc_lit=0`, or - RLE tokenized if `enc_lit=1`, or - Huffman-coded if `enc_lit=2` (see [§ 5.2.1 Huffman literal section](#521-huffman-literal-section)), or - Huffman-coded with the dictionary's shared code lengths if `enc_lit=3` (dictionary-compressed archives only; same section layout, no inline lengths header). - **Tokens stream**: - one byte per sequence: `(LL << 4) | ML`, `LL` and `ML` being 4-bit fields. - if `enc_litlen=0` (all levels ≤ 6), these `n_sequences` bytes are stored verbatim and the Tokens section's compressed size equals `n_sequences`. - if `enc_litlen=2` (level 7 only), the token bytes are Huffman-coded over the token alphabet using the exact § 5.2.1 layout (inline 128-byte lengths header included); the section's compressed size is the encoded payload size and the decoder expands it back to `n_sequences` bytes. - **Offsets stream**: - `n_sequences × 1` byte if `enc_off=1`, else `n_sequences × 2` bytes LE. - Values are **biased**: stored value = `actual_offset - 1`. Decoder adds `+ 1`. - This makes `offset == 0` impossible by construction (minimum decoded offset = 1). - **Extras stream**: - Prefix-varint overflow values for token saturations: - if `LL == 15`, read varint and add to LL - if `ML == 15`, read varint and add to ML - actual match length is `ML + 5` (minimum match = 5). ### 5.2.1 Huffman literal section `enc_lit=2` carries a length-limited **canonical Huffman code** over the literal bytes. The bits are placed on the wire with the **PivCo layout** (level-ordered Huffman, after [Giesen 2026](https://fgiesen.wordpress.com/2026/06/21/pivco-huffman-merge-operations/)): the encoding is ordinary Huffman — same code lengths, same code bits, same size — and PivCo only reorders those bits, grouping them by TREE LEVEL rather than by symbol so decoding runs data-parallel list merges instead of a serial bit chain. ```text Offset Size Field 0x00 128 256 x 4-bit code lengths, packed two-per-byte (low nibble first). code_len[i] in [0, 11] (0 means symbol absent). 0x80 var One run per EMITTING node of the canonical code tree, in BFS order (parents before children, left before right). Runs are LSB-first within bytes and each run is padded to a byte boundary. Emitting nodes and run contents are defined below. ``` **Canonical code.** Codes are length-limited at `L = 11` bits (levels ≤ 6 never emit codes longer than 8; level 7 / ULTRA may emit up to 11). Symbols are ordered by `(code_len, symbol)` and assigned consecutive code values in that order, starting at 0 and left-shifting when the length increases — the standard canonical construction. The **code tree** is the binary trie of these codewords read MSB-first: at depth `d`, a codeword's bit `code_len-1-d` selects the left (0) or right (1) child. The Kraft equality (validated below) makes this trie complete: every internal node has exactly two children. **Emitting nodes and flat subtrees.** Both sides derive, from the code lengths alone, the set of *flat roots*: an internal node is a flat root iff 1. it is not itself inside another flat subtree (BFS order resolves this: parents are classified first), and 2. every leaf below it sits at the same relative depth `D`, and 3. `D >= 2`. (Completeness of the trie makes condition 2 imply a perfect binary subtree of `2^D` leaves. Every maximal complete subtree of depth `D >= 2` is a flat root — a fixed format rule. The reference decoder unpacks the packed codes directly, with SIMD kernels for `D` in 2..6 and a scalar table lookup for `D >= 7`, instead of running the level-merge cascade.) Every internal node that is neither a flat root nor a descendant of one is a **bitmap node**: its run holds one branch bit per symbol routed through it, in symbol-sequence order (0 = left child, 1 = right child). A **flat root**'s run instead holds `D` packed bits per symbol routed through it, in symbol sequence order: bit `j` (bit 0 first) is the branch taken at relative depth `j` below the flat root. Strict descendants of a flat root emit **no run at all** — they are skipped in the BFS enumeration. **Derived sizes.** There is no stored size field of any kind: the root handles `n_literals` symbols, the popcount of a bitmap node's bits equals its right child's symbol count, and a flat root consuming `c` symbols occupies exactly `ceil(c*D/8)` bytes, so every run length is derived while walking the BFS order once. Selection is an encoder policy, not a format rule: the reference encoder requires level ≥ 6 and at least 1024 literals, prices every candidate (RAW / RLE / Huffman / shared-table Huffman) as `J = size + premium(level) * n_decoded_bytes`, and picks the minimum — a space-speed Lagrangian with a per-level decode-time premium. Decoder validation requirements: - Every code length must satisfy `code_len[i] ≤ 11`. - At least one symbol must be present (`code_len[i] != 0` for some `i`). - The Kraft sum `Σ 2^(11 − code_len[i])` over present symbols must equal `2^11`, except for the single-present-symbol degenerate case where exactly one symbol has `code_len = 1` and the Kraft sum is `2^10`. - Every node's run (bitmap or flat) must lie within the section payload. - A popcount that routes symbols to an absent child is a corruption error. - A failure on any of the above results in `ZXC_ERROR_CORRUPT_DATA`. ### 5.2.2 Shared-table Huffman literal section `enc_lit=3` is only valid in archives compressed with a dictionary (`HAS_DICTIONARY` set): the payload is the same Huffman/PivCo section as § 5.2.1 with the 128-byte lengths header **omitted** — the code lengths come from the shared literal table carried by the `.zxd` dictionary (see § 12.4), validated once when the dictionary is attached (same rules as § 5.2.1). Decoders **MUST** reject `enc_lit=3` sections with `ZXC_ERROR_DICT_REQUIRED` when no dictionary table is attached. The archive's `dict_id` binds the (content, table) pair, so a matching table is guaranteed present whenever the dictionary check passed. The shared table is trained on the corpus' post-LZ literal distribution and covers only the symbols seen in training; the encoder falls back to a per-block table (`enc_lit=2`) or RAW/RLE for any block containing a literal byte without a code. The level-7 token section reuses the § 5.2.1 layout (`enc_litlen=2`, with the inline lengths header) over the token byte alphabet. ## 5.3 GHI block (`type=2`) High-throughput LZ format with packed 32-bit sequences. ### GHI payload layout ```text +-------------------------------+ | GHI Header (16 bytes) | +-------------------------------+ | 3 Section Descriptors (24B) | +-------------------------------+ | Literals stream | +-------------------------------+ | Sequences stream (N * 4B) | +-------------------------------+ | Extras stream | +-------------------------------+ ``` ### GHI Header (16 bytes) Same binary layout as GLO header: - `n_sequences`, `n_literals`, `enc_lit`, `enc_litlen`, `enc_mlen`, `enc_off`, reserved. In practice for GHI: - `enc_lit = 0` (raw literals) - `enc_off` is metadata (sequence words always store 16-bit offsets) ### GHI section descriptors (3 × 8 bytes) Section order: 1. Literals 2. Sequences 3. Extras Each descriptor uses the same packed size encoding as GLO (`u64`: comp32|raw32). ### GHI sequence word format (32 bits) ```text Bits 31..24 : LL (literal length, 8 bits) Bits 23..16 : ML (match length minus 5, 8 bits) Bits 15..0 : Offset - 1 (16 bits, biased; decode: stored + 1) ``` Memory order (little-endian word): ```text byte0 = offset low byte1 = offset high byte2 = ML byte3 = LL ``` Overflow rules: - if `LL == 255`, read varint from Extras and add it to LL. - if `ML == 255`, read varint, then add minimum match (`+5`). - otherwise decoded match length is `ML + 5`. --- ## 5.4 EOF block (`type=255`) EOF marks end of block stream. Constraints: - block header is present (8 bytes) - `comp_size` **must be 0** - no payload - no per-block trailing checksum Immediately after EOF block header comes the Optional SEK block, followed by the 12-byte file footer. --- ## 5.5 SEK block (`type=254`) The **Seek Table** block is an optional block appended between the EOF block and the File Footer. It provides `O(1)` random-access capabilities by recording the compressed size of every block in the archive. Decompressed sizes and block indices are derived from the file header's `block_size` (all blocks are `block_size` except the last, which may be smaller). **Layout of a SEK Block**: ```text Offset Size Field 0x00 8 Block Header (type=254, comp_size=N*4) 0x08 4 Block 0 Compressed Size (u32 LE) 0x0C 4 Block 1 Compressed Size (u32 LE) ... ... ... 8 + (N-1)*4 4 Block N-1 Compressed Size (u32 LE) ``` **Backward Detection Strategy**: 1. Read the **File Header** (first 16 bytes) -> extract `block_size`. 2. Read the **File Footer** (last 12 bytes) -> extract `total_decompressed_size`. 3. Derive `num_blocks = ceil(total_decompressed_size / block_size)`. 4. Calculate `seek_block_size = 8 + (N × 4)`. 5. Seek backward by `seek_block_size` bytes from the start of the footer to read the Block Header. 6. Validate `block_type == 254 (SEK)` and `comp_size == N × 4`. --- ## 6. Prefix Varint (Extras stream) ZXC extras use a prefix-length varint. The length is encoded in unary form in the high bits of the first byte: the number of leading `1` bits, followed by a terminating `0`, indicates how many additional payload bytes follow. The scheme generalizes to N bytes (`11110xxx` = 5, `111110xx` = 6, ...), but the current ZXC spec caps the encoding at 3 bytes because no legitimate value exceeds 21 bits (see below). Encodings used: - `0xxxxxxx` -> 1 byte total (7 bits payload, value < 128) - `10xxxxxx` -> 2 bytes total (14 bits, value < 16384) - `110xxxxx` -> 3 bytes total (21 bits, value < 2 MiB) Payload bits from the following bytes are concatenated little-endian style (low bits first). Used by GLO/GHI to carry LL/ML overflows beyond token/sequence inline limits. **Value bound**: a varint encodes `(LL - MASK)` or `(ML - MASK)`. Since LL/ML are bounded by `ZXC_BLOCK_SIZE_MAX = 2 MiB` (2^21), every legitimate varint value is strictly less than 2^21 and therefore fits in **at most 3 bytes**. Any prefix indicating a length >= 4 bytes (first byte `>= 0xE0`) is out of spec for this format version: encoders must never emit such a varint, and conforming decoders reject it as corrupt input. This caps the varint surface to the format-defined block size limit and neutralizes integer-overflow attacks in downstream bounds arithmetic. A future version of the format that raises `ZXC_BLOCK_SIZE_MAX` would also extend the accepted prefix lengths. --- ## 7. Checksums and Integrity ## 7.1 Header checksums - File header: 16-bit (`zxc_hash16`). - Block header: 8-bit (`zxc_hash8`). These protect metadata/navigation fields. ## 7.2 Per-block checksum (optional) When file header has `HAS_CHECKSUM=1`: - each data block appends a 4-byte checksum after payload. - checksum input is **compressed payload bytes only** (not block header). - algorithm id currently `0` (RapidHash folded to 32-bit). ## 7.3 Global stream hash A rolling global hash is maintained from per-block checksums in stream order: ```text global = 0 for each data block checksum b: global = ((global << 1) | (global >> 31)) XOR b ``` This value is stored in the file footer (or zeroed when checksum mode is disabled). --- ## 8. File Footer (12 bytes) Footer is mandatory and placed immediately after EOF block header. ```text Offset Size Field 0x00 8 original_source_size (u64) 0x08 4 global_hash (u32) ``` - **original_source_size**: full uncompressed size of the file. - **global_hash**: - valid when checksum mode is active; - set to zero when checksum mode is disabled. --- ## 9. Decoder Validation Checklist (Practical) 1. Validate file header magic/version/CRC16. 2. Parse blocks sequentially: - validate block header CRC8, - check block bounds using `comp_size`, - if enabled, verify trailing block checksum. 3. Decode payload according to block type. 4. On EOF: - require `comp_size == 0`, - read footer, - compare footer `original_source_size` with produced output size, - if enabled, compare footer `global_hash` with recomputed rolling hash. --- ## 10. Versioning Policy ### 10.1 Format version field The format version is a single byte at offset `0x04` of the file header. A conforming decoder **MUST** reject any file whose version it does not support. ### 10.2 Version bump criteria ZXC has **no forward compatibility**: the set of block types and the meaning of every field are fixed per format version. Any change a decoder must understand — adding a block type, assigning meaning to a reserved field/flag bit, changing an encoding, layout, or the checksum algorithm — requires a **version bump**. | Change class | Version action | Example | |---|---|---| | New block type added | **Version bump** (decoders reject unknown types) | Adding a hypothetical `GLR` block type | | Reserved field/flag bit assigned meaning | **Version bump** | Defining a reserved flag bit | | Existing block encoding changed | **Version bump** | Changing GLO token layout | | Header/footer layout changed | **Version bump** | Resizing the file header | | Checksum algorithm changed | **Version bump** | Replacing RapidHash with Komihash | ### 10.3 Compatibility rules - **Version compatibility**: a decoder accepts **only** the format version it implements and **MUST** reject any other version with `ZXC_ERROR_BAD_VERSION`. Because block-type numbering and payload formats may change between versions, a decoder **MUST NOT** attempt to interpret an archive whose version byte it does not recognise. - **Unknown block types**: a decoder **MUST reject** any block whose type is not defined for its format version (`ZXC_ERROR_BAD_BLOCK_TYPE`). The block-type set is fixed per version; introducing a new type is a version bump (decoders do **not** skip unknown blocks — silently advancing past untrusted, unrecognised data is unsafe). - **Reserved fields**: all reserved bytes and flag bits **MUST** be written as zero by encoders. The current decoder tolerates (ignores) non-zero reserved values — they are covered by the header CRC, so accidental corruption is still caught — but assigning a reserved field any meaning is a **version bump**, never a same-version extension. - **Defined-but-bounded fields**: where only specific values are defined (e.g. the checksum-algorithm id, currently `0` = RapidHash only), the decoder **rejects** out-of-range values (`ZXC_ERROR_BAD_HEADER`). ### 10.4 Minimum conforming decoder A minimal conforming decoder for version 7 **MUST** support: - File header parsing and CRC16 validation - **RAW** blocks (type 0) - passthrough copy. - **GLO** blocks (type 1) - full LZ decode with extras varint, including Huffman entropy sections (§5.2.1, PivCo layout) with code lengths up to 11 bits. - **GHI** blocks (type 2) - full LZ decode with extras varint. - **EOF** block (type 255) - stream termination. - File footer validation (source size check). Support for checksum verification is **RECOMMENDED** but not strictly required for a minimal implementation. --- ## 11. Error Handling ### 11.1 Error classes Decoders **MUST** detect and handle the following error conditions. The recommended behavior for each class is specified below. | Error | Detection point | Required behavior | |---|---|---| | **Bad magic** | File header, offset 0x00 | Reject immediately. Not a ZXC file. | | **Unsupported version** | File header, offset 0x04 | Reject immediately. Version not supported. | | **Header CRC16 mismatch** | File header, offset 0x0E | Reject. Header is corrupt or truncated. | | **Invalid chunk size code** | File header, offset 0x05 | Reject. Code outside the valid range `[12..21]`. | | **Block header CRC8 mismatch** | Block header, offset 0x07 | Reject block. Stream is corrupt. | | **Unknown block type** | Block header, offset 0x00 | Skip block using `comp_size` (see §10.3), or reject. | | **Block payload truncated** | During `fread` of `comp_size` bytes | Reject. Unexpected end of stream. | | **Block checksum mismatch** | Trailing 4-byte checksum | Reject block. Payload is corrupt. | | **EOF block with non-zero comp_size** | EOF block header | Reject. Malformed EOF marker. | | **Footer source size mismatch** | File footer, offset 0x00 | Reject. Output size does not match declared original size. | | **Footer global hash mismatch** | File footer, offset 0x08 | Reject (if checksum mode active). Integrity failure. | | **Decompressed output exceeds chunk size** | During LZ decode | Reject. Corrupt or malicious payload. | | **Match offset out of bounds** | During LZ copy | Reject. Offset references data before output start. | | **Varint exceeds maximum length** | Extras stream | Reject. Overflow or corrupt extras data. | ### 11.2 Severity levels - **Fatal**: the decoder **MUST** stop processing and report an error. All errors in the table above are fatal by default. - **Warning**: not currently defined. Future versions may introduce non-fatal conditions (e.g. unknown flag bits set in reserved positions). ### 11.3 Partial output When a fatal error occurs mid-stream, the decoder **SHOULD**: 1. Stop producing output immediately. 2. Report the specific error condition (see `zxc_error_name` in the reference implementation). 3. Not return partially decompressed data as a valid result. Buffer-mode decoders **MUST** return a negative error code. Stream-mode decoders **MUST** signal the error and cease writing to the output. ### 11.4 Decoder hardening recommendations For decoders processing untrusted input (e.g. network data, user uploads): - Validate **all** header checksums before processing payloads. - Enforce maximum allocation limits based on `comp_size` and chunk size code. - Reject files where `comp_size` exceeds `zxc_compress_bound(chunk_size)`. - Use bounded memory copies - never trust decoded lengths without cross-checking against output buffer capacity. --- ## 12. Pre-Trained Dictionary Support ### 12.1 Overview A pre-trained dictionary improves compression ratio on small, similar payloads (e.g. JSON API responses, game assets, structured logs) by prefilling the LZ77 sliding window at the start of each block. The dictionary is an external file (`.zxd` format) referenced by a 32-bit ID in the ZXC file header. ### 12.2 Mechanism The dictionary contains raw byte content (max 64 KB, bounded by the 64 KB LZ sliding window). At compression time, the dictionary is logically prepended to each block's input, seeding the hash tables so the match finder can reference dictionary content immediately. At decompression time, the dictionary is prepended to the output buffer so match copies that reference dictionary bytes resolve naturally via pointer arithmetic. Since each block is independent, the dictionary prefill happens per-block. This preserves O(1) seekable random-access: load the dictionary once, then decompress any block independently. ### 12.3 File header encoding When `HAS_DICTIONARY` (flag bit 6) is set, the reserved bytes at offsets `0x07..0x0A` contain the `dict_id` (`u32` LE). A decoder **MUST**: 1. Verify that a dictionary is provided (`ZXC_ERROR_DICT_REQUIRED` if not). 2. Verify that the dictionary id matches `header.dict_id` (`ZXC_ERROR_DICT_MISMATCH` if not). For a raw in-memory dictionary without a shared table, the id is `zxc_dict_id(dict, dict_size, NULL)`. When a shared literal table is attached, the id also binds the table: `id = fold32(hash(table_128_bytes, seed = hash(content)))` (i.e. `zxc_dict_id(content, size, table)`). Older decoders that do not recognize the `HAS_DICTIONARY` flag will ignore it (per §10.3: reserved flag bits are ignored). However, blocks compressed with a dictionary contain match offsets that reference dictionary content; decoding without the dictionary produces corrupt output. Per-block and global checksums (when enabled) will detect this corruption. ### 12.4 Dictionary file format (`.zxd`) Dictionaries are stored as standalone `.zxd` files with the following layout: ```text Offset Size Field 0x00 4 Magic Word (0x9CB0D1C7 LE) 0x04 1 Dictionary format version (currently 1) 0x05 1 Flags (bits 0..3: checksum algorithm id; bits 4..7 reserved) 0x06 2 Content size (u16 LE, max 65535) 0x08 4 dict_id (u32 LE, binds content AND shared table, see below) 0x0C 2 Reserved (0) 0x0E 2 Header CRC16 (zxc_hash16, computed with bytes 0x0C-0x0F zeroed) 0x10 N Dictionary content (raw bytes) 0x10+N 128 Shared literal Huffman table (256 × 4-bit packed code lengths, same layout as the § 5.2.1 code-length header; always present) ``` - **Magic Word**: `0x9CB0D1C7`. Allows immediate rejection of non-dictionary files. - **Version**: `1`. Decoders reject any other version with `ZXC_ERROR_BAD_VERSION`. - **Flags**: bits `0..3` carry the checksum algorithm id (`0` = RapidHash-based folding), matching the ZXC file header flags; bits `4..7` are reserved (must be 0). - **Shared literal Huffman table**: code lengths for the `enc_lit=3` literal sections (§ 5.2.2), trained on the corpus' post-LZ literal distribution. - **dict_id**: `fold32(hash(table_128_bytes, seed = hash(content)))` — binds the exact (content, table) pair. Must match the `dict_id` stored in any ZXC file header that references this dictionary. - **Header CRC16**: `zxc_hash16` checksum of the 16-byte header with bytes `0x0C..0x0F` zeroed before hashing — same method as the ZXC file header. - **Content**: raw bytes that prefill the LZ77 window. Not compressed. ### 12.5 Dictionary training The `zxc_train_dict()` function analyzes a corpus of representative samples to select byte segments that maximize LZ77 match coverage. The most frequently matched segments are placed at the end of the dictionary so they produce the shortest offsets (closest to the block start in the virtual window). ### 12.6 Naming convention The `.zxd` extension is cosmetic — files are identified by the magic word at offset `0x00`, never by extension. This is a tooling convention, not a format requirement; it does not affect bytes on the wire. The reference CLI applies it as follows: - `zxc --train -o / ` writes the trained dictionary as `/dictionary_.zxd`, where `` is the lowercase 8-digit hex of the dictionary id (e.g. `dictionary_bc46eec1.zxd`). Embedding the id keeps the name unique per dictionary and easy to match against the `Dict ID` reported by `zxc -l`. With no `-o`, the file is written to the current directory; with `-o ` it is written there verbatim. - On **decompression**, a dictionary is **not** auto-located: an archive that was compressed with a dictionary must be decompressed by passing that dictionary explicitly with `-D`. Without it, decompression fails with `ZXC_ERROR_DICT_REQUIRED` (the `dict_id` in the header is still verified against the supplied dictionary, yielding `ZXC_ERROR_DICT_MISMATCH` on a mismatch). --- ## 13. Summary of Useful Fixed Sizes - File header: **16** bytes - Block header: **8** bytes - Block checksum (optional): **4** bytes - GLO header: **16** bytes - GHI header: **16** bytes - Section descriptor: **8** bytes - GLO descriptors total: **32** bytes - GHI descriptors total: **24** bytes - File footer: **12** bytes - Dictionary file header (`.zxd`): **16** bytes **Magic words** — both are little-endian `u32` at offset `0x00` and deliberately share the `0x9CB0...` family prefix, so check the full value (or the file extension) to tell them apart: | File | Magic (value) | On-disk bytes (LE) | |------|---------------|--------------------| | ZXC archive (`.zxc`) | `0x9CB02EF5` | `F5 2E B0 9C` | | ZXC dictionary (`.zxd`) | `0x9CB0D1C7` | `C7 D1 B0 9C` | --- ## 14. Worked Example (Real Hexdump) This example was produced with the CLI from a 10-byte input (`Hello ZXC\n`) using: ```bash zxc -z -C -1 sample.txt ``` Generated archive size: **58 bytes**. ### 14.1 Full hexdump ```text 00000000: F5 2E B0 9C 07 13 80 00 00 00 00 00 00 00 3E 5D 00000010: 00 00 00 0A 00 00 00 69 48 65 6C 6C 6F 20 5A 58 00000020: 43 0A 90 BB A1 75 FF 00 00 00 00 00 00 02 0A 00 00000030: 00 00 00 00 00 00 90 BB A1 75 ``` ### 14.2 Byte-level decoding #### A) File Header (offset `0x00`, 16 bytes) ```text F5 2E B0 9C | 07 | 13 | 80 | 00 00 00 00 00 00 00 | 3E 5D ``` - `F5 2E B0 9C` -> magic word (LE) = `0x9CB02EF5`. - `07` -> format version 7. - `13` -> chunk-size code 19 (exponent encoding: `2^19 = 524288` bytes, i.e. 512 KiB, the default). - `80` -> checksum enabled (`HAS_CHECKSUM=1`, algo id 0). - next 7 bytes are reserved zeros. - `3E 5D` -> header CRC16 (LE value `0x5D3E`). #### B) Data Block #0 (RAW) Block header at offset `0x10`: ```text 00 | 00 | 00 | 0A 00 00 00 | 69 ``` - type `00` = RAW. - flags `00`, reserved `00`. - `comp_size = 0x0000000A = 10` bytes. - header CRC8 = `0x69`. Payload at `0x18..0x21` (10 bytes): ```text 48 65 6C 6C 6F 20 5A 58 43 0A ``` ASCII: `Hello ZXC\n`. Trailing block checksum at `0x22..0x25`: ```text 90 BB A1 75 ``` LE value: `0x75A1BB90`. #### C) EOF Block (offset `0x26`, 8 bytes) ```text FF | 00 | 00 | 00 00 00 00 | 02 ``` - type `FF` = EOF. - `comp_size = 0` (mandatory). - header CRC8 = `0x02`. #### D) File Footer (offset `0x2E`, 12 bytes) ```text 0A 00 00 00 00 00 00 00 | 90 BB A1 75 ``` - original source size = `10` bytes. - global hash = `0x75A1BB90`. Since there is exactly one data block, the global hash equals that block checksum: ```text global0 = 0 global1 = rotl1(global0) XOR block_crc = block_crc ``` ### 14.3 Structural view with absolute offsets ```text 0x00..0x0F File Header (16) 0x10..0x17 RAW Block Header (8) 0x18..0x21 RAW Payload (10) 0x22..0x25 RAW Block Checksum (4) 0x26..0x2D EOF Block Header (8) 0x2E..0x39 File Footer (12) ``` ### 14.4 Seekable Variant (with Seek Table) Same 10-byte input (`Hello ZXC\n`), compressed with seekable mode enabled: ```bash zxc -z -C -1 -S sample.txt ``` Generated archive size: **70 bytes** (12 bytes larger than the non-seekable variant). #### Full hexdump ```text 00000000: F5 2E B0 9C 07 13 80 00 00 00 00 00 00 00 3E 5D 00000010: 00 00 00 0A 00 00 00 69 48 65 6C 6C 6F 20 5A 58 00000020: 43 0A 90 BB A1 75 FF 00 00 00 00 00 00 02 FE 00 00000030: 00 04 00 00 00 D2 16 00 00 00 0A 00 00 00 00 00 00000040: 00 00 90 BB A1 75 ``` #### Byte-level decoding **A) File Header** (offset `0x00`, 16 bytes) - identical to non-seekable. **B) Data Block #0 (RAW)** (offset `0x10`, 22 bytes) - identical to non-seekable. **C) EOF Block** (offset `0x26`, 8 bytes) - identical to non-seekable. **D) SEK Block** (offset `0x2E`, 12 bytes) Block header at `0x2E`: ```text FE | 00 | 00 | 04 00 00 00 | D2 ``` - `FE` -> type 254 = SEK (Seek Table). - flags `00`, reserved `00`. - `comp_size = 0x00000004 = 4` bytes (one entry x 4 bytes/entry). - header CRC8 = `0xD2`. Seek table entry at `0x36`: ```text 16 00 00 00 ``` - Entry #0: compressed block size = `0x00000016 = 22` bytes. This is the total size of data block #0 including its header (8) + payload (10) + checksum (4) = 22. ✓ **E) File Footer** (offset `0x3A`, 12 bytes) ```text 0A 00 00 00 00 00 00 00 | 90 BB A1 75 ``` - original source size = `10` bytes. - global hash = `0x75A1BB90`. #### Structural view with absolute offsets ```text 0x00..0x0F File Header (16) 0x10..0x17 RAW Block Header (8) 0x18..0x21 RAW Payload (10) 0x22..0x25 RAW Block Checksum (4) 0x26..0x2D EOF Block Header (8) 0x2E..0x35 SEK Block Header (8) <- seek table 0x36..0x39 SEK Entry #0 (4) <- comp_size of block #0 0x3A..0x45 File Footer (12) ``` > **Compatibility note**: The SEK block is inserted between the EOF block and the file footer. The footer always remains the **last 12 bytes of the file**, so decoders that locate the footer from the end of the file (e.g. `src + src_size - 12` for buffer APIs, or `fseek(END - 12)` for file APIs) work unchanged with seekable archives. However, **streaming decoders** that read the footer sequentially immediately after the EOF block must be updated to detect and skip the SEK block. In practice, all ZXC decoders since v0.9.0 handle both seekable and non-seekable archives transparently. --- ## 15. Worked Example: Dictionary File (`.zxd` Hexdump) A minimal dictionary whose content is the 5 ASCII bytes `hello`. Total file size: **149 bytes** (16-byte header + 5-byte content + 128-byte shared Huffman table). This is the on-disk form produced by `zxc_dict_save()` (see §12.4); the table is always present. ### 15.1 Full hexdump ```text 00000000: C7 D1 B0 9C 01 00 05 00 23 58 DF 6F 00 00 63 65 00000010: 68 65 6C 6C 6F 00 00 00 00 00 00 00 00 00 00 00 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00000040: 00 00 00 00 00 00 00 20 00 02 00 02 20 00 00 00 00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00000090: 00 00 00 00 00 ``` ### 15.2 Byte-level decoding #### A) Dictionary Header (offset `0x00`, 16 bytes) ```text C7 D1 B0 9C | 01 | 00 | 05 00 | 17 0F 72 9A | 00 00 | 4A D9 ``` - `C7 D1 B0 9C` -> magic word (LE) = `0x9CB0D1C7` (`.zxd` dictionary). - `01` -> dictionary format version 1. - `00` -> flags (bits 0..3 = checksum algorithm id `0` = RapidHash; bits 4..7 reserved). - `05 00` -> content size (LE) = `5` bytes. - `23 58 DF 6F` -> `dict_id` (LE) = `0x6FDF5823`. Binds the **(content, table)** pair (see §12.4) and must match the `dict_id` stored in the file header of any `.zxc` archive compressed with this dictionary. - `00 00` -> reserved. - `63 65` -> header CRC16 (LE) = `0x6563`, computed over the 16-byte header with bytes `0x0C..0x0F` zeroed (same method as the ZXC file header — the CRC is the last 2 bytes of the header). #### B) Dictionary Content (offset `0x10`, 5 bytes) ```text 68 65 6C 6C 6F ``` ASCII: `hello`. Raw bytes that prefill the LZ77 window — not compressed. #### C) Shared Huffman Table (offset `0x15`, 128 bytes) ```text ... 20 00 02 00 02 20 ... (remaining bytes 0x00) ``` 256 × 4-bit code lengths, packed two-per-byte (low nibble first), for the shared literal table (§5.2.2). Symbols absent from the training distribution have length `0`; here only the four bytes of `hello` carry codes (e.g. the nibble at table index `'e'`=0x65 gives length `2`), so all other entries are zero. ### 15.3 Structural view with absolute offsets ```text 0x00..0x0F Dictionary Header (16) 0x10..0x14 Dictionary Content (5) 0x15..0x94 Shared Huffman Table (128) ``` zxc-0.13.0/docs/MIGRATION.md000066400000000000000000000133241522536370400152140ustar00rootroot00000000000000# Migrating ZXC archives across format versions ZXC's on-disk format has had two deliberate, **non-backward-compatible** breaks. A decoder accepts **only its own format version** — it compares the header version byte for exact equality and rejects anything else with `ZXC_ERROR_BAD_VERSION`, rather than risk misreading it. | Format | Introduced by | A decoder reads | Headline change | | :--- | :--- | :--- | :--- | | **v7** | ZXC **0.13.0** | v7 only | Huffman bits use the **PivCo** wire layout; new **level 7 (ULTRA)** adds Huffman-coded tokens | | **v6** | ZXC 0.12.x | v6 only | **NUM** block removed; **GHI** renumbered type 3 → 2 | | **v5** | earlier | v5 only | — | Because each decoder rejects other versions outright, migrating an archive means a one-time **transcode**: decompress it with a build that understands the *old* format, then recompress the bytes with the *new* build. The same recipe covers every jump (v6 → v7, v5 → v7, or the historical v5 → v6). --- ## v6 → v7 (ZXC 0.13.0) Format **v7** is a deliberate, non-backward-compatible break with **v6**: - Huffman-coded literal sections (`enc_lit = 2`) now place their bits on the wire in the **PivCo layout** — the *code* is the same length-limited canonical Huffman code, but the bits are grouped by tree level so the decoder can merge them data-parallel. v6 and v7 Huffman blocks are therefore not interchangeable. - A new **level 7 (ULTRA)** tier additionally Huffman-codes the **sequence-token** stream (`enc_litlen = 2`, code lengths up to 11 bits) on top of the literals. - The file-header version byte changed from `6` to `7`. Block **types** are unchanged from v6 (GHI stays type 2, and so on) — the break is the Huffman wire layout plus the new token encoding. A **v7 decoder rejects v6 archives** with `ZXC_ERROR_BAD_VERSION` rather than misinterpret them, and a v6 decoder likewise cannot read v7 archives. > A v7 build is ZXC **0.13.0** or newer; a v6 build is any **0.12.x**. Keep your > old 0.12.x binary until every archive you care about is transcoded — it is the > only thing that can read v6 data. ## Do I need to migrate? Only if **both** are true: 1. You have archives produced by a **v6** (or **v5**) build of ZXC, **and** 2. You want to read them with **v7-only** tools (ZXC 0.13.0+), or standardize your stored data on v7. If you keep the old build around, it can still read its own archives — migration is not urgent. There is no rush to convert data at rest. ## Check an archive's format version The version is a single byte at offset `0x04` of the file header: ```sh xxd -s 4 -l 1 archive.zxc # -> "05" = v5, "06" = v6, "07" = v7 ``` (`zxc -V` reports the *tool* version, not the archive's.) ## Migrate: transcode with the old build, recompress with v7 Migration is a one-time **transcode**: decompress with a build that reads the old format and recompress with a **v7** build (ZXC 0.13.0+). This rebuilds the seek table and checksums as needed and, for a v5 source, handles legacy NUM blocks by decoding them and re-encoding as ordinary LZ/RAW. Assuming `zxc-old` is your existing (v5 or v6) binary and `zxc-v7` is ZXC 0.13.0+: ```sh zxc-old -dc old.zxc | zxc-v7 -z -c > new.zxc ``` - `zxc-old -dc old.zxc` — decompress the old archive to stdout. - `zxc-v7 -z -c` — compress stdin and write the v7 archive to stdout. The recompression uses the v7 encoder's options, so pick them explicitly to match your needs (the original encoding level is **not** recorded in the archive): ```sh # Examples zxc-old -dc old.zxc | zxc-v7 -z -6 -c > new.zxc # densest fast-decode tier zxc-old -dc old.zxc | zxc-v7 -z -7 -c > new.zxc # ULTRA — new v7 density level zxc-old -dc old.zxc | zxc-v7 -z -N -c > new.zxc # no checksums zxc-old -dc old.zxc | zxc-v7 -z -B 1M -c > new.zxc # 1 MB blocks zxc-old -dc old.zxc | zxc-v7 -z -S -c > new.zxc # keep it seekable ``` If the old archive was compressed **with a dictionary**, supply it to the old build on the decompress side: `zxc-old -dc -D dict.zxd old.zxc | ...`. ### Two-step variant (no pipe) ```sh zxc-old -dc old.zxc > tmp.raw # decompress to a plain file zxc-v7 -z -c tmp.raw > new.zxc # recompress as v7 rm tmp.raw ``` ### Bulk migration ```sh for f in *.zxc; do zxc-old -dc "$f" | zxc-v7 -z -c > "migrated/$f" || echo "FAILED: $f" done ``` ## Verify the result ```sh zxc-v7 -t new.zxc # integrity check (v7) # strong check: decompressed output is byte-identical to the original diff <(zxc-old -dc old.zxc) <(zxc-v7 -dc new.zxc) && echo OK ``` --- ## v5 → v6 (historical) Format **v6** was the previous break, with **v5**: - The **NUM** block (v5 type 2 — a numeric delta/ZigZag/bit-packed codec) was removed. - **GHI** was renumbered from type 3 to **type 2**. - The file-header version byte changed from `5` to `6`. Because v5 and v6 number their block types differently, a v6 decoder rejects v5 archives outright with `ZXC_ERROR_BAD_VERSION`. The transcode recipe above applies directly — use a v5 build on the decompress side. To land straight on v7, pipe a v5 build into a v7 build (`zxc-v5 -dc old.zxc | zxc-v7 -z -c`); there is no need to stop at v6. ## Notes - **Keep the old build available** until all archives you care about are migrated — it is the only thing that can read the old format (and it is what the pipe above uses). - Migration **re-encodes** the data, so the new archive's bytes and exact size may differ from the old one even at the same level; the *decompressed output* is identical. - There is no in-place, byte-preserving converter between format versions: the wire layout differs (v5 NUM blocks; the v6 → v7 Huffman/PivCo change), so the data must be decoded and re-encoded, which the transcode above does. zxc-0.13.0/docs/WHITEPAPER.md000066400000000000000000002201241522536370400153310ustar00rootroot00000000000000# ZXC: High-Performance Asymmetric Lossless Compression **Version**: 0.13.0 **Date**: June 2026 **Author**: Bertrand Lebonnois --- ## 1. Executive Summary In modern software delivery pipelines-specifically **Mobile Gaming**, **Embedded Systems**, and **FOTA (Firmware Over-The-Air)**-data is typically generated on high-performance x86 workstations but consumed on energy-constrained ARM devices. Standard industry codecs like LZ4 offer excellent performance but fail to exploit the "Write-Once, Read-Many" (WORM) nature of these pipelines. **ZXC** is a lossless codec designed to bridge this gap. By utilizing an **asymmetric compression model**, ZXC achieves a **>40% increase in decompression speed on ARM** compared to LZ4, while simultaneously reducing storage footprints. On x86 development architecture, ZXC maintains competitive throughput, ensuring no disruption to build pipelines. ## 2. The Efficiency Gap The industry standard, LZ4, prioritizes symmetric speed (fast compression and fast decompression). While ideal for real-time logs or RAM swapping, this symmetry is useless for asset distribution. * **Wasted Cycles**: CPU cycles saved during the single compression event (on a build server) do not benefit the millions of end-users decoding the data. * **The Battery Tax**: On mobile devices, slower decompression keeps the CPU active longer, draining battery and generating heat. ## 3. The ZXC Solution ZXC utilizes a computationally intensive encoder to generate a bitstream specifically structured to **maximize decompression throughput**. By performing heavy analysis upfront, the encoder produces a layout optimized for the instruction pipelining and branch prediction capabilities of modern CPUs, particularly ARMv8, effectively offloading complexity from the decoder to the encoder. ### 3.1 Asymmetric Pipeline ZXC employs a Producer-Consumer architecture to decouple I/O operations from CPU-intensive tasks. This allows for parallel processing where input reading, compression/decompression, and output writing occur simultaneously, effectively hiding I/O latency. ### 3.2 Modular Architecture The ZXC file format is inherently modular. **Each block is independent and is encoded with whichever of ZXC's block codecs yields the smallest output** — GLO or GHI (the LZ paths) or RAW (stored). Independent blocks keep the format simple, enable O(1) seekable random access, and let it evolve without breaking backward compatibility. > **ZXC is a pure LZ byte codec.** It compresses byte streams via LZ77 matching plus entropy coding; it has no type-aware or numeric block codec. Numeric and columnar data (timestamps, IDs, float columns) compress best when a **reversible pre-filter** — delta or byte-shuffle — is applied *before* compression and inverted *after* decompression. ZXC keeps such transforms **out of the codec and the format on purpose**: the caller, which knows the element type, owns the filter. See the *Compressing Numeric Data* example in [`EXAMPLES.md`](EXAMPLES.md). ## 4. Core Algorithms ZXC utilizes a hybrid approach combining LZ77 (Lempel-Ziv) dictionary matching with advanced entropy coding and specialized data transforms. ### 4.1 LZ77 Engine The heart of ZXC is a heavily optimized LZ77 engine that adapts its behavior based on the requested compression level: * **Hash Chain & Collision Resolution**: Uses a fast hash table with chaining to find matches in the history window (configurable sliding window, power-of-2 from 4 KB to 2 MB, default 512 KB). * **Lazy Matching**: Implements a "lookahead" strategy to find better matches at the cost of slight encoding speed, significantly improving decompression density. ### 4.2 Specialized SIMD Acceleration & Hardware Hashing ZXC leverages modern instruction sets to maximize throughput on both ARM and x86 architectures. * **ARM NEON Optimization**: Extensive usage of vld1q_u8 (vector load) and vceqq_u8 (parallel comparison) allows scanning data at wire speed, while vminvq_u8 provides fast rejection of non-matches. * **x86 Vectorization**: Maintains high performance on Intel/AMD platforms via dedicated AVX2 and AVX512 paths, falling back to a portable **SSE2** baseline — the x86-64 architectural guarantee, present on every 64-bit Intel/AMD CPU. The SSE2 tier emulates the few SSE4.1 operations it needs (e.g. unsigned 32-bit max, byte blend, saturating `u32`→`u16` pack), so no SSE4.x hardware is required. This ensures parity with ARM throughput across the full x86 range. * **High-Speed Integrity**: Block validation relies on **rapidhash**, a modern non-cryptographic hash algorithm that fully exploits hardware acceleration to verify data integrity without bottlenecking the decompression pipeline. ### 4.3 Entropy Coding & Bitpacking * **RLE (Run-Length Encoding)**: Automatically detects runs of identical bytes. * **Prefix Varint Encoding**: Variable-length integer encoding (similar to LEB128 but prefix-based) for overflow values. * **Canonical Huffman (Literals level ≥ 6, tokens level 7)**: Length-limited canonical Huffman code (max code length **8 bits** at level 6, **11 bits** at level 7 / ULTRA) over the literal byte distribution — and, at level 7, over the sequence-token distribution. The *code* is classical Huffman; only the *wire layout* is new — PivCo (format v7) transposes the code bits by tree level into per-node branch bitmaps, so the decoder runs data-parallel list merges (byte shuffles) instead of a serial bit chain. See the dedicated section below. * **Bit-Packing**: Compressed sequences are packed into dedicated streams using minimal bit widths. #### Huffman decoding with the PivCo level-ordered layout Entropy decoding is the hot path of the high-compression levels, and classical Huffman decoding is a *serial bit chain*: each codeword's length must be known before the next codeword's position is. Interleaving N independent streams caps the parallelism at N and still executes a load-shift-mask dependency chain per symbol. Format v7 replaces the layout with **Pivoted Coding Huffman** (level-ordered Huffman): * **Same code, transposed layout**: The canonical code, the 128-byte packed code-length header and the compressed size (to within per-node byte padding) are unchanged from classic Huffman. Only the *order* of the bits on the wire changes: instead of symbol-after-symbol, the section stores, for every internal node of the code tree in BFS order, one branch bit per symbol routed through that node. * **Decoding = list merges, no gather**: The decoder rebuilds each tree level bottom-up: a node's bitmap says how to interleave its two children's symbol lists, which is a data-parallel *merge* — implemented with byte shuffles (`TBL` on NEON, `pshufb` on SSSE3/AVX2, `vpexpandb` on AVX-512-VBMI2, a 2-instruction merge). No gather instructions, no per-symbol dependency chain; throughput scales with SIMD width. * **Flat-subtree fast path (format rule)**: perfect subtrees whose leaves all sit at the same relative depth `D ≥ 2` skip the per-level bitmaps and store packed `D`-bit residual codes instead (FORMAT.md § 5.2.1). Dense tree regions — the common case for 8-bit-capped level-6 tables — then decode by direct table unpacking instead of `D` merge rounds. * **Leaf-pair kernel**: two-leaf nodes decode via an XOR/blend on the bitmap (`out = sym0 ^ (delta & bitmask)`) without materializing index lists. * **No stream-size header**: the popcount of a node's bitmap *is* its right child's element count, so sub-stream sizes are derived, not stored — this also removes the v6 u16 sub-stream size limit. Measured on the real post-LZ literal sections of silesia.tar (level 7, Apple M3): **1.3 cycles/symbol, ~3.1 GB/s single-threaded — +77 % over the tuned 4-stream classic decoder** it replaced, at byte-identical compression ratio. The scalar fallback (no SIMD) is ~4x slower than the SIMD kernels; embedded targets typically stay on levels 1-5, which carry no entropy sections. **Selection — space-speed Lagrangian.** Section encodings are chosen by pricing every candidate (RAW, RLE, Huffman, shared-table Huffman) with `J = compressed_size + premium(level) * decoded_bytes` and taking the minimum — a Lagrangian trade of bytes saved against decode time. Below ULTRA the premium reproduces the historical conservative margin (~3 %); at level 7 it is lowered (~1.6 % for entropy, ~0.4 % for RLE) so the encoder buys more ratio with decode cycles, which is exactly the level-7 contract. #### Prefix Varint Format ZXC uses a **Prefix Varint** encoding for overflow values. Unlike standard VByte (which uses a continuation bit in every byte), Prefix Varint encodes the total length of the integer in the **unary prefix of the first byte**. This allows the decoder to determine the sequence length immediately, enabling branchless or highly predictable decoding without serial dependencies. **Encoding Scheme:** The prefix-length encoding generalizes to **N bytes** by construction: the number of leading `1` bits in the first byte (followed by a terminating `0`) determines how many additional payload bytes follow. An N-byte varint carries `7*N` payload bits (`8 - N` bits in the first byte plus 8 bits per following byte). ZXC caps the format at 3 bytes because no legitimate value exceeds 21 bits (see below); longer prefixes (`1110xxxx`, `11110xxx`, ...) are reserved for a future format version that would raise `ZXC_BLOCK_SIZE_MAX`. Encodings used: | Prefix (Binary) | Total Bytes | Data Bits (1st Byte) | Total Data Bits | Range (Value < X) | |-----------------|-------------|----------------------|-----------------|-------------------| | `0xxxxxxx` | 1 | 7 | 7 | 128 | | `10xxxxxx` | 2 | 6 | 14 (6+8) | 16,384 | | `110xxxxx` | 3 | 5 | 21 (5+8+8) | 2,097,152 (2 MiB) | A varint encodes `(LL - MASK)` or `(ML - MASK)`, both bounded by `ZXC_BLOCK_SIZE_MAX = 2 MiB`. **All legitimate ZXC varints fit in at most 3 bytes.** Any prefix indicating a length >= 4 bytes (first byte `>= 0xE0`) is out of spec for v1: encoders must never emit such a varint, and conforming decoders reject it as corrupt input. This caps the varint surface to the format-defined block size limit and neutralizes integer-overflow attacks in downstream bounds arithmetic. **Example**: Encoding value `300` (binary: `100101100`): ```text Value 300 > 127 and < 16383 -> Uses 2-byte format (Prefix '10'). Step 1: Low 6 bits 300 & 0x3F = 44 (0x2C, binary 101100) Byte 1 = Prefix '10' | 101100 = 10101100 (0xAC) Step 2: Remaining high bits 300 >> 6 = 4 (0x04, binary 00000100) Byte 2 = 0x04 Result: 0xAC 0x04 Decoding Verification: Byte 1 (0xAC) & 0x3F = 44 Byte 2 (0x04) << 6 = 256 Total = 256 + 44 = 300 ``` ## 5. File Format Specification The ZXC file format is block-based, robust, and designed for parallel processing. ### 5.1 Global Structure (File Header) The file begins with a **16-byte** header that identifies the format and specifies decompression parameters. **FILE Header (16 bytes):** ``` Offset: 0 4 5 6 7 14 16 +---------------+-------+-------+-------+-----------------------+-------+ | Magic Word | Ver | Chunk | Flags | Reserved / Dict ID | CRC | | (4 bytes) | (1B) | (1B) | (1B) | (7 bytes) | (2B) | +---------------+-------+-------+-------+-----------------------+-------+ ``` * **Magic Word (4 bytes)**: `0x9 0xCB 0x02E 0xF5`. * **Version (1 byte)**: Current version is `6`. * **Chunk Size Code (1 byte)**: Defines the processing block size using **exponent encoding**: - The value is in `[12, 21]`: block size = `2^value` bytes (4 KB to 2 MB). - `12` = 4 KB, `13` = 8 KB, `14` = 16 KB, `15` = 32 KB, `16` = 64 KB, `17` = 128 KB, `18` = 256 KB, `19` = 512 KB (default), `20` = 1 MB, `21` = 2 MB. - All other values are rejected; block sizes are powers of 2. * **Flags (1 byte)**: Global configuration flags. - **Bit 7 (MSB)**: `HAS_CHECKSUM`. If `1`, checksums are enabled for the stream. Every block will carry a trailing 4-byte checksum, and the footer will contain a global checksum. If `0`, no checksums are present. - **Bit 6**: `HAS_DICTIONARY`. If `1`, the stream was compressed with a pre-trained dictionary and **requires** it for decompression; the reserved field carries the `dict_id` (see below and §5.10). - **Bits 4-5**: Reserved. - **Bits 0-3**: Checksum Algorithm ID (e.g., `0` = RapidHash). * **Reserved / Dictionary ID (7 bytes)**: Zero when `HAS_DICTIONARY` is clear. When `HAS_DICTIONARY` is set, bytes `0x07..0x0A` hold the `dict_id` (`u32` LE, a 32-bit hash of the dictionary content); the remaining bytes `0x0B..0x0D` stay zero. * **CRC (2 bytes)**: 16-bit Header Checksum. Calculated on the 16-byte header (with CRC bytes set to 0) using `zxc_hash16`. ### 5.2 Block Header Structure Each data block consists of an **8-byte** generic header that precedes the specific payload. This header allows the decoder to navigate the stream and identify the processing method required for the next chunk of data. **BLOCK Header (8 bytes):** ``` Offset: 0 1 2 3 7 8 +-------+-------+-------+-----------------------+-------+ | Type | Flags | Rsrvd | Comp Size | CRC | | (1B) | (1B) | (1B) | (4 bytes) | (1B) | +-------+-------+-------+-----------------------+-------+ Block Layout: [ Header (8B) ] + [ Compressed Payload (Comp Size bytes) ] + [ Optional Checksum (4B) ] ``` **Note**: The Checksum (if enabled in File Header) is **4 bytes** (32-bit), is always located **at the end** of the compressed data, and is calculated **on the compressed payload**. * **Type**: Block encoding type (0=RAW, 1=GLO, 2=GHI, 255=EOF). * **Flags**: Not used for now. * **Rsrvd**: Reserved for future use (must be 0). * **Comp Size**: Compressed payload size (excluding header and optional checksum). * **CRC**: 1-byte Header Checksum (located at the end of the header). Calculated on the 8-byte header (with CRC byte set to 0) using `zxc_hash8`. > **Note**: The decompressed size is not stored in the block header. It is derived from internal Section Descriptors within the compressed payload (for GLO/GHI blocks), or equals `Comp Size` (for RAW blocks). > **Note**: While the format is designed for threaded execution, a single-threaded API is also available for constrained environments or simple integration cases. ### 5.3 Specific Header: GLO (Generic Low) (Present immediately after the Block Header) **GLO Header (16 bytes):** ``` Offset: 0 4 8 9 10 11 12 16 +---------------+---------------+---+---+---+---+---------------+ | N Sequences | N Literals |Lit|LL |ML |Off| Reserved | | (4 bytes) | (4 bytes) |Enc|Enc|Enc|Enc| (4 bytes) | +---------------+---------------+---+---+---+---+---------------+ ``` * **N Sequences**: Total count of LZ sequences in the block. * **N Literals**: Total count of literal bytes. * **Encoding Types** - `Lit Enc`: Literal stream encoding (0=RAW, 1=RLE, 2=HUFFMAN, 3=HUFFMAN_DICT). **Currently used.** `HUFFMAN_DICT` uses the same bitstream as `HUFFMAN` but omits the 128-byte code-lengths header: the codes come from the shared literal table carried by the dictionary (see §5.10). Only valid in dictionary-compressed archives. - `LL Enc`: Literal lengths encoding. **Reserved for future use** (lengths are packed in tokens). - `ML Enc`: Match lengths encoding. **Reserved for future use** (lengths are packed in tokens). - `Off Enc`: Offset encoding mode. **Currently used** - `0` = 16-bit offsets (2 bytes each, max distance 65535) - `1` = 8-bit offsets (1 byte each, max distance 255) * **Reserved**: Padding for alignment. **Section Descriptors (4 × 8 bytes = 32 bytes total):** Each descriptor stores sizes as a packed 64-bit value: ``` Single Descriptor (8 bytes): +-----------------------------------+-----------------------------------+ | Compressed Size (4 bytes) | Raw Size (4 bytes) | | (low 32 bits) | (high 32 bits) | +-----------------------------------+-----------------------------------+ Full Layout (32 bytes): Offset: 0 8 16 24 32 +---------------+---------------+---------------+---------------+ | Literals Desc | Tokens Desc | Offsets Desc | Extras Desc | | (8 bytes) | (8 bytes) | (8 bytes) | (8 bytes) | +---------------+---------------+---------------+---------------+ ``` **Section Contents:** | # | Section | Description | |---|-------------|-------------------------------------------------------| | 0 | **Literals**| Raw bytes to copy, or RLE-compressed if `enc_lit=1` | | 1 | **Tokens** | Packed bytes: `(LiteralLen << 4) \| MatchLen` | | 2 | **Offsets** | Match distances: 8-bit if `enc_off=1`, else 16-bit LE | | 3 | **Extras** | Prefix Varint overflow values when LitLen or MatchLen ≥ 15 | **Data Flow Example:** ``` GLO Block Data Layout: +------------------------------------------------------------------------+ | Literals Stream | Tokens Stream | Offsets Stream | Extras Stream | | (desc[0] bytes) | (desc[1] bytes)| (desc[2] bytes)| (desc[3] bytes) | +------------------------------------------------------------------------+ ↓ ↓ ↓ ↓ Raw bytes Token parsing Match lookup Length overflow ``` **Why Comp Size and Raw Size?** Each descriptor stores both a compressed and raw size to support secondary encoding of streams: | Section | Comp Size | Raw Size | Different? | |-------------|----------------------|---------------------|----------------------| | **Literals**| RLE size (if used) | Original byte count | Yes, if RLE enabled | | **Tokens** | Stream size | Stream size | No | | **Offsets** | N×1 or N×2 bytes | N×1 or N×2 bytes | No (size depends on `enc_off`) | | **Extras** | Prefix Varint stream size | Prefix Varint stream size | No | Currently, the **Literals** section uses different sizes when RLE compression is applied (`enc_lit=1`). The **Offsets** section size depends on `enc_off`: N sequences × 1 byte (if `enc_off=1`) or N sequences × 2 bytes (if `enc_off=0`). > **Design Note**: This format is designed for future extensibility. The dual-size architecture allows adding entropy coding (FSE/ANS) or bitpacking to any stream without breaking backward compatibility. ### 5.4 Specific Header: GHI (Generic High) (Present immediately after the Block Header) The **GHI** (Generic High-Velocity) block format is optimized for maximum decompression speed. It uses a **packed 32-bit sequence** format that allows 4-byte aligned reads, reducing memory access latency and enabling efficient SIMD processing. **GHI Header (16 bytes):** ``` Offset: 0 4 8 9 10 11 12 16 +---------------+---------------+---+---+---+---+---------------+ | N Sequences | N Literals |Lit|LL |ML |Off| Reserved | | (4 bytes) | (4 bytes) |Enc|Enc|Enc|Enc| (4 bytes) | +---------------+---------------+---+---+---+---+---------------+ ``` * **N Sequences**: Total count of LZ sequences in the block. * **N Literals**: Total count of literal bytes. * **Encoding Types** - `Lit Enc`: Literal stream encoding (0=RAW). - `LL Enc`: Reserved for future use. - `ML Enc`: Reserved for future use. - `Off Enc`: Offset encoding mode: - `0` = 16-bit offsets (max distance 65535) - `1` = 8-bit offsets (max distance 255, enables smaller sequence packing) * **Reserved**: Padding for alignment. **Section Descriptors (3 × 8 bytes = 24 bytes total):** ``` Full Layout (24 bytes): Offset: 0 8 16 24 +---------------+---------------+---------------+ | Literals Desc | Sequences Desc| Extras Desc | | (8 bytes) | (8 bytes) | (8 bytes) | +---------------+---------------+---------------+ ``` **Section Contents:** | # | Section | Description | |---|---------------|-------------------------------------------------------| | 0 | **Literals** | Raw bytes to copy | | 1 | **Sequences** | Packed 32-bit sequences (see format below) | | 2 | **Extras** | Prefix Varint overflow values when LitLen or MatchLen ≥ 255 | **Packed Sequence Format (32 bits):** Unlike GLO which uses separate token and offset streams, GHI packs all sequence data into a single 32-bit word for cache-friendly sequential access: ``` 32-bit Sequence Word (Little Endian): +--------+--------+------------------+ | LL | ML | Offset | | 8 bits | 8 bits | 16 bits | +--------+--------+------------------+ [31:24] [23:16] [15:0] Byte Layout in Memory: Offset: 0 1 2 3 +--------+--------+--------+--------+ | Off Lo | Off Hi | ML | LL | +--------+--------+--------+--------+ ``` * **LL (Literal Length)**: 8 bits (0-254, value 255 triggers Prefix Varint overflow) * **ML (Match Length - 5)**: 8 bits (actual length = ML + 5, range 5-259, value 255 triggers Prefix Varint overflow) * **Offset**: 16 bits (match distance, 1-65535) **Data Flow Example:** ``` GHI Block Data Layout: +------------------------------------------------------------+ | Literals Stream | Sequences Stream | Extras Stream | | (desc[0] bytes) | (desc[1] bytes = N×4) | (desc[2] bytes) | +------------------------------------------------------------+ ↓ ↓ ↓ Raw bytes 32-bit seq read Length overflow ``` **Key Differences: GLO vs GHI** | Feature | GLO (Global) | GHI (High-Velocity) | |--------------------|---------------------------------|----------------------------------| | **Sections** | 4 (Lit, Tokens, Offsets, Extras)| 3 (Lit, Sequences, Extras) | | **Sequence Format**| 1-byte token + separate offset | Packed 32-bit word | | **LL/ML Bits** | 4 bits each (overflow at 15) | 8 bits each (overflow at 255) | | **Memory Access** | Multiple stream pointers | Single aligned 4-byte reads | | **Decoder Speed** | Fast | Fastest (optimized for ARM/x86) | | **RLE Support** | Yes (literals) | No | | **Huffman Literals** | Yes (level ≥ 6, ≥ 1024 lits) | No | | **Parser** | Lazy (≤ L5), Optimal DP (L6) | Lazy | | **Best For** | General data, good compression | Maximum decode throughput | > **Design Rationale**: The 32-bit packed format eliminates pointer chasing between token and offset streams. By reading a single aligned word per sequence, the decoder achieves better cache utilization and enables aggressive loop unrolling (4x) for maximum throughput on modern CPUs. ### 5.5 Specific Header: EOF (End of File) (Block Type 255) The **EOF** block marks the end of the ZXC stream. It ensures that the decompressor knows exactly when to stop processing, allowing for robust stream termination even when file size metadata is unavailable or when concatenating streams. * **Structure**: Standard 8-byte Block Header. * **Flags**: * **Bit 7 (0x80)**: `has_checksum`. If set, implies the **Global Stream Checksum** in the footer is valid and should be verified. * **Comp Size**: Unlike other blocks, these **MUST be set to 0**. The decoder enforces strict validation (`Type == EOF` AND `Comp Size == 0`) to prevent processing of malformed termination blocks. * **CRC**: 1-byte Header Checksum (located at the end of the header). Calculated on the 8-byte header (with CRC byte set to 0) using `zxc_hash8`. ### 5.6 File Footer (Present immediately after the EOF Block) A mandatory **12-byte footer** closes the stream, providing total source size information and the global checksum. **Footer Structure (12 bytes):** ``` Offset: 0 8 12 +-------------------------------+---------------+ | Original Source Size | Global Hash | | (8 bytes) | (4 bytes) | +-------------------------------+---------------+ ``` * **Original Source Size** (8 bytes): Total size of the uncompressed data. * **Global Hash** (4 bytes): The **Global Stream Checksum**. Valid only if the EOF block has the `has_checksum` flag set (or the decoder context requires it). * **Algorithm**: `Rotation + XOR`. * For each block with a checksum: `global_hash = (global_hash << 1) | (global_hash >> 31); global_hash ^= block_hash;` ### 5.7 Block Encoding & Processing Algorithms The efficiency of ZXC relies on specialized algorithmic pipelines for each block type. #### Type 1: GLO (Global) This format is used for standard data. It employs a **multi-stage encoding pipeline**: **Encoding Process**: 1. **LZ77 Parsing**: The encoder iterates through the input using a rolling hash to detect matches. * *Hash Chain*: Collisions are resolved via a chain table to find optimal matches in dense data. * *Lazy Matching* (levels 3–5): If a match is found, the encoder checks the next position. If a better match starts there, the current byte is emitted as a literal (deferred matching). * *Price-Based Optimal Parser* (level 6): A forward dynamic-programming pass replaces the lazy parser. `dp[p]` holds the minimum bit-cost to encode `src[0..p)`; transitions consider either emitting a single literal or any sub-length of the longest match found at `p`, using static prices (literal ≈ 9 bits, match ≈ 24 bits + varint extras). Backtracking from `dp[N]` yields the globally optimal token sequence. A long-match guard skips re-search at intra-match positions to keep the parser O(N) on highly repetitive data. 2. **Tokenization**: Matches are split into three components: * *Literal Length*: Number of raw bytes before the match. * *Match Length*: Duration of the repeated pattern. * *Offset*: Distance back to the pattern start. 3. **Stream Separation**: These components are routed to separate buffers: * *Literals Buffer*: Raw bytes. * *Tokens Buffer*: Packed `(LitLen << 4) | MatchLen`. * *Offsets Buffer*: Variable-width distances (8-bit or 16-bit, see below). * *Extras Buffer*: Overflow values for lengths >= 15 (Prefix Varint encoded). * *Offset Mode Selection*: The encoder tracks the maximum offset across all sequences. If all offsets are ≤ 255, the 8-bit mode (`enc_off=1`) is selected, saving 1 byte per sequence compared to 16-bit mode. 4. **RLE Pass**: The literals buffer is scanned for run-length encoding opportunities (runs of identical bytes). If beneficial (>10% gain), it is compressed in place. 5. **Entropy Pass** (level ≥ 6, ≥ 1024 literals): A length-limited canonical Huffman code (`L = 8` at level 6, `L = 11` at level 7) is fitted to the literal byte distribution and emitted in the PivCo level-ordered layout (§4.3, FORMAT.md §5.2.1). At level 7 the same treatment is applied to the sequence-token stream (`enc_litlen = 2`). Candidates are selected by the space-speed Lagrangian `J = size + premium(level) × decoded_bytes`. * **Shared-Table Candidate** (dictionary archives only): when the dictionary carries a shared literal table (§5.10), a second entropy candidate is sized with the dictionary's code lengths and **no inline 128-byte lengths header**. The Lagrangian picks the minimum-J of {RAW, RLE, per-block Huffman, shared-table Huffman} (`enc_lit = 3` for the latter), so the choice is never a regression. Because the shared table only covers symbols seen in training, a block containing an uncovered literal byte automatically falls back to its per-block table. The 128-byte header amortization makes `enc_lit = 3` viable on literal sections far below the 1024-literal threshold of the per-block table — precisely the small-block regime dictionaries target. 6. **Final Serialization**: All buffers are concatenated into the payload, preceded by section descriptors. **Decoding Process**: 1. **Deserizalization**: The decoder reads the section descriptors to obtain pointers to the start of each stream (Literals, Tokens, Offsets). 2. **Literal Decompression**: * `enc_lit = 0` (RAW): zero-copy view into the source buffer. * `enc_lit = 1` (RLE): single pass that expands runs and copies literal chunks. * `enc_lit = 2` (PIVCO): the section's per-node branch bitmaps are decoded bottom-up by SIMD list merges (shuffle-based, no gather — §4.3), with direct unpacking of flat subtrees and an XOR/blend kernel for leaf pairs. ~1.3 cycles/symbol on Apple Silicon. * `enc_lit = 3` (PIVCO_DICT): same decode, but the code lengths come from the dictionary's shared literal table (validated once at attach time) instead of an inline 128-byte header. The header amortization makes entropy coding viable on literal sections far below the per-block threshold — precisely the small-block regime dictionaries target. 3. **Token Decompression** (level 7 only): when `enc_litlen = 2`, the token stream is Huffman-decoded (PivCo layout) into a scratch buffer through a dedicated specialization of the block decoder, so the common RAW-token path keeps its exact code shape (a hot pointer with a single provenance). 4. **Vertical Execution**: The main loop reads from all three streams simultaneously. 5. **Wild Copy**: * *Literals*: Copied using unaligned 16-byte SIMD loads/stores (`vld1/vst1` on ARM). * *Matches*: Copied using 16-byte stores. Overlapping matches (e.g., repeating pattern "ABC" for 100 bytes) are handled naturally by the CPU's store forwarding or by specific overlapped-copy primitives. * **Safety**: A "Safe Zone" at the end of the buffer forces a switch to a cautious byte-by-byte loop, allowing the main loop to run without bounds checks. #### Type 2: GHI (High-Velocity) This format prioritizes decompression throughput over compression ratio. It uses a **unified sequence stream**: **Encoding Process**: 1. **LZ77 Parsing**: Same as GLO, with aggressive lazy matching and step skipping for optimal matches. 2. **Sequence Packing**: Each match is packed into a 32-bit word: * Bits [31:24]: Literal Length (8 bits) * Bits [23:16]: Match Length - 5 (8 bits) * Bits [15:0]: Offset (16 bits) 3. **Stream Assembly**: Only three streams are generated: * *Literals Buffer*: Raw bytes (no RLE). * *Sequences Buffer*: Packed 32-bit words (4 bytes each). * *Extras Buffer*: Prefix Varint overflow values for lengths >= 255. 4. **Final Serialization**: Streams are concatenated with 3 section descriptors. **Decoding Process**: 1. **Single-Read Loop**: The decoder reads one 32-bit word per sequence, extracting LL, ML, and offset in a single operation. 2. **4x Unrolled Fast Path**: When sufficient buffer margin exists, the decoder processes 4 sequences per iteration: * Pre-reads 4 sequences into registers * Copies literals and matches with 32-byte SIMD operations * Minimal branching for maximum instruction-level parallelism 3. **Offset Validation Threshold**: For the first 256 (8-bit mode) or 65536 (16-bit mode) bytes, offsets are validated against written bytes. After this threshold, all offsets are guaranteed valid. 4. **Wild Copy**: Same 32-byte SIMD copies as GLO, with special handling for overlapping matches (offset < 32). ### 5.8 Data Integrity Every compressed block can optionally be protected by a **32-bit checksum** to ensure data reliability. #### Post-Compression Verification Unlike traditional codecs that verify the integrity of the original uncompressed data, ZXC calculates checksums on the **compressed** payload. * **Zero-Overhead Decompression**: Verifying uncompressed data requires computing a hash over the output *after* decompression, contending for cache and CPU resources with the decompression logic itself. By checksumming the compressed stream, verification happens *during* the read phase, before the data even enters the decoder. * **Early Failure Detection**: Corruption is detected before attempting to decompress, preventing potential crashes or buffer overruns in the decoder caused by malformed data. * **Reduced Memory Bandwidth**: The checksum is computed over a much smaller dataset (the compressed block), saving significant memory bandwidth. #### Multi-Algorithm Support ZXC supports multiple integrity verification algorithms (though currently standardized on rapidhash). * **Identified Algorithm (0x00: rapidhash)**: The default algorithm. The 64-bit rapidhash result is folded (XORed) into a 32-bit value to minimize storage overhead while maintaining strong collision resistance for block-level integrity. * **Performance First**: By using a modern non-cryptographic hash, ZXC ensures that integrity checks do not bottleneck decompression throughput. #### Credit The default `rapidhash` algorithm is based on wyhash and was developed by Nicolas De Carli. It is designed to fully exploit hardware performance while maintaining top-tier mathematical distribution qualities. ### 5.9 Seekable Archives (Random Access) ZXC supports **O(1)** random-access decompression without decoding the entire stream. This is achieved by appending an optional **Seek Table** (a `SEK` block) at the end of the archive, immediately before the file footer. * **Structure**: The seek table contains an array of 4-byte entries (compressed block size, LE uint32) for every block in the archive. * **Performance**: Reading backward from the file footer instantly locates the seek table. Since blocks have a fixed power-of-2 size, the target block is found by a single division (`block_index = offset / block_size`), with no binary search required. * **Use Cases**: This feature transforms ZXC from a sequential stream into a random-access volume format. ### 5.10 Pre-Trained Dictionaries For workloads compressed in **small blocks** (4 KB–128 KB), a pre-trained dictionary substantially improves the compression ratio. Because each block is encoded independently — a deliberate choice that preserves the O(1) seekable random access of §5.9 — a block only has its own preceding bytes as match history. The smaller the block, the less history it has, and the more it benefits from external priming. * **Mechanism**: A dictionary is raw byte content (max 64 KB, bounded by the 64 KB LZ window). At compression, it is logically prepended to every block's input, seeding the hash tables so the match finder can reference dictionary content from the first byte. At decompression, it is prepended to the output buffer so match copies that point into dictionary bytes resolve naturally by pointer arithmetic. The prefill is **per-block**, so random access is preserved: load the dictionary once, then decode any block independently. * **External, content-addressed model**: Dictionaries are **external** files (`.zxd`), referenced from the file header by a 32-bit `dict_id`. This follows the industry-standard train-once / reuse-many model (the dictionary is amortized across many archives rather than duplicated inside each). The `dict_id` is **self-validating**: it identifies *which* dictionary is required and simultaneously detects an accidentally wrong one. A decoder **MUST** reject decompression when the required dictionary is absent (`ZXC_ERROR_DICT_REQUIRED`) or when the supplied dictionary's id does not match `header.dict_id` (`ZXC_ERROR_DICT_MISMATCH`). The per-block and global checksums of §5.9 are a second line of defense: a wrong dictionary yields wrong output that fails the checksum (when enabled). * **Shared literal Huffman table**: Beyond LZ priming, a dictionary carries a **shared canonical Huffman table** for the literal stream (128 bytes of packed code lengths, trained on the corpus' *post-LZ* literal distribution). Blocks whose literals compress better with this table use `enc_lit = 3` (§5.7) and skip the 128-byte per-block lengths header entirely — decisive at small block sizes, where the header never amortizes. The code lengths are validated **once per context** when the dictionary is attached, instead of being parsed per block. The result is a simultaneous ratio *and* decode-speed improvement on homogeneous corpora at small block sizes, tapering to neutral as blocks grow and per-block tables win on their own. The selection is by exact byte accounting, so the shared table is never a regression. * **Two binding flavours**: the `dict_id` binds exactly what the encoder used. A **raw in-memory dictionary** (library API, content bytes only, no table) yields `dict_id = checksum(content)`; such archives never contain `enc_lit = 3` blocks. A **table-carrying dictionary** (the `.zxd` path) yields `dict_id = checksum(LE32(checksum(content)) || table)`, binding the exact (content, table) pair. There is no flag on the wire: the decoder simply computes the id for the pair it was given and matches it against the header. * **`.zxd` file format**: A standalone dictionary file has a 16-byte header (magic `0x9CB0D1C7`, version, content size, `dict_id`, header CRC16) followed by the raw content bytes and the **128-byte shared Huffman table (always present)**. The `.zxd` extension is cosmetic — files are identified by their magic word, not their name. * **Training**: `zxc_train_dict()` analyzes a corpus of representative samples and selects the byte segments that maximize LZ77 match coverage, placing the most frequently matched segments at the **end** of the dictionary so they produce the shortest (most efficient) offsets in the virtual window. `zxc_train_dict_huf()` then compresses the same samples *with* the trained dictionary to histogram the **real post-LZ literals** (raw sample bytes are a poor proxy: LZ matches against the dictionary remove most repeated content first) and derives the shared table from that distribution. Training cost is bounded: past an 8 MiB budget, 4 KB sample slices are strided evenly across the corpus — a 256-symbol histogram converges long before, so even multi-hundred-MB corpora train in well under a second. Symbols unseen in training stay code-less by design: with `L = 8`, a code covering all 256 symbols would be forced by Kraft equality to the degenerate uniform 8-bit code; the encoder's per-block fallback handles uncovered bytes instead. * **Naming**: training to a directory writes `dictionary_.zxd` (the `dict_id` is the lowercase 8-digit hex stored in the archive header). The dictionary must be supplied explicitly with `-D` to decompress; the `dict_id` lets the decoder verify the supplied dictionary matches (`ZXC_ERROR_DICT_MISMATCH` otherwise). This naming is a tooling convention and does not affect bytes on the wire. ## 6. System Architecture (Threading) ZXC leverages a threaded **Producer-Consumer** model to saturate modern multi-core CPUs. ### 6.1 Asynchronous Compression Pipeline 1. **Block Splitting (Main Thread)**: The input file is read and sliced into fixed-size chunks (configurable, default 512 KB, power of 2 from 4 KB to 2 MB). 2. **Ring Buffer Submission**: Chunks are placed into a lock-free ring buffer. 3. **Parallel Compression (Worker Threads)**: * Workers pull chunks from the queue. * Each worker compresses its chunk independently in its own context (`zxc_cctx_t`). * Output is written to a thread-local buffer. 4. **Reordering & Write (Writer Thread)**: The writer thread ensures chunks are written to disk in the correct original order, regardless of which worker finished first. ### 6.2 Asynchronous Decompression Pipeline 1. **Header Parsing (Main Thread)**: The main thread scans block headers to identify boundaries and payload sizes. 2. **Dispatch**: Compressed payloads are fed into the worker job queue. 3. **Parallel Decoding (Worker Threads)**: * Workers decode chunks into pre-allocated output buffers. * **Fast Path**: If the output buffer has sufficient margin, the decoder uses "wild copies" (16-byte SIMD stores) to bypass bounds checking for maximal speed. 4. **Serialization**: Decompressed blocks are committed to the output stream sequentially. ## 7. Performance Analysis (Benchmarks) **Methodology:** Benchmarks were conducted using `lzbench` (by inikep) with default block size of 512 KB, checksums disabled, single-threaded execution, on the standard Silesia Corpus ([silesia.tar](https://github.com/DataCompression/corpus-collection/tree/main/Silesia-Corpus), 202 MB). The three Google Cloud instances run with **1 thread per core** (SMT disabled on the x86 C4D and C2D instances). * **Target 1 (Client):** Apple M2 / macOS 26 (Clang 21) * **Target 2 (Cloud):** Google Axion — Google Cloud C4A / Linux (GCC 14) * **Target 3 (Build):** AMD EPYC 9B45 — Google Cloud C4D / Linux (GCC 14) * **Target 4 (Production):** AMD EPYC 7B13 — Google Cloud C2D / Linux (GCC 14) **Figure A**: Pareto Frontier — Decompression Speed vs. Compressed Size (across 4 CPUs) ![Pareto Frontier — Decompression Speed vs Compressed Size](./images/bench-pareto-ratio.svg) ### 7.1 Client ARM64 Summary (Apple Silicon M2) | Compressor | Decompression Speed (Ratio vs LZ4) | Compressed Size (Index LZ4=100) (Lower is Better) | | :--- | :--- | :--- | | **zxc 0.13.0 -1** | **2.69x** | **129.22** | | **zxc 0.13.0 -2** | **2.23x** | **112.64** | | **zxc 0.13.0 -3** | **1.48x** | **96.20** | | **zxc 0.13.0 -4** | **1.40x** | **89.60** | | **zxc 0.13.0 -5** | **1.32x** | **84.59** | | **zxc 0.13.0 -6** | **1.25x** | **76.21** | | **zxc 0.13.0 -7** | **0.79x** | **69.34** | | lz4 1.10.0 --fast -17 | 1.18x | 130.58 | | lz4 1.10.0 (Ref) | 1.00x | 100.00 | | lz4hc 1.10.0 -9 | 0.95x | 77.20 | | lzav 5.9 -1 | 0.82x | 83.91 | | snappy 1.2.2 | 0.68x | 100.53 | | zstd 1.5.7 --fast --1 | 0.53x | 86.16 | | zstd 1.5.7 -1 | 0.38x | 72.55 | | zstd 1.5.7 -3 | 0.36x | 65.56 | | zlib 1.3.2 -1 | 0.09x | 76.58 | **Decompression Efficiency (Cycles per Byte @ 3.5 GHz)** | Compressor. | Cycles/Byte | Performance vs memcpy (*) | | ----------------------- | ----------- | --------------------- | | memcpy | 0.066 | 1.00x (baseline) | | **zxc 0.13.0 -1** | **0.272** | **4.1x** | | **zxc 0.13.0 -2** | **0.328** | **5.0x** | | **zxc 0.13.0 -3** | **0.496** | **7.5x** | | **zxc 0.13.0 -4** | **0.523** | **7.9x** | | **zxc 0.13.0 -5** | **0.556** | **8.4x** | | **zxc 0.13.0 -6** | **0.585** | **8.8x** | | **zxc 0.13.0 -7** | **0.924** | **14.0x** | | lz4 1.10.0 | 0.732 | 11.1x | | lz4 1.10.0 --fast -17 | 0.622 | 9.4x | | lz4hc 1.10.0 -9 | 0.773 | 11.7x | | lzav 5.9 -1 | 0.897 | 13.6x | | zstd 1.5.7 -1 | 1.935 | 29.2x | | zstd 1.5.7 --fast --1 | 1.379 | 20.8x | | zstd 1.5.7 -3 | 2.054 | 31.0x | | snappy 1.2.2 | 1.072 | 16.2x | | zlib 1.3.2 -1 | 8.495 | 128x | *Lower is better. Calculated using Apple M2 Performance Core frequency (3.5 GHz). Formula: `Cycles/Byte = 3500 / Decompression Speed (MB/s)`.* **Effective Throughput (Ratio-normalized decode)** | Compressor | Decode (MB/s) | Ratio (%) | Effective (MB/s) | vs LZ4 | | :--- | ---: | ---: | ---: | ---: | | **zxc 0.13.0 -1** | 12 880 | 61.50 | **20 943** | **2.08x** | | **zxc 0.13.0 -2** | 10 683 | 53.61 | **19 927** | **1.98x** | | **zxc 0.13.0 -3** | 7 063 | 45.79 | **15 425** | **1.53x** | | **zxc 0.13.0 -4** | 6 696 | 42.65 | **15 700** | **1.56x** | | **zxc 0.13.0 -5** | 6 295 | 40.27 | **15 632** | **1.56x** | | **zxc 0.13.0 -6** | 5 980 | 36.27 | **16 488** | **1.64x** | | **zxc 0.13.0 -7** | 3 790 | 33.00 | **11 485** | **1.14x** | | lz4 1.10.0 (Ref) | 4 784 | 47.60 | 10 050 | 1.00x | | lz4 1.10.0 --fast -17 | 5 624 | 62.15 | 9 049 | 0.90x | | lz4hc 1.10.0 -9 | 4 528 | 36.75 | 12 321 | 1.23x | | lzav 5.9 -1 | 3 901 | 39.94 | 9 767 | 0.97x | | snappy 1.2.2 | 3 265 | 47.85 | 6 823 | 0.68x | | zstd 1.5.7 --fast --1 | 2 539 | 41.01 | 6 191 | 0.62x | | zstd 1.5.7 -1 | 1 809 | 34.53 | 5 240 | 0.52x | | zstd 1.5.7 -3 | 1 704 | 31.20 | 5 462 | 0.54x | | zlib 1.3.2 -1 | 412 | 36.45 | 1 130 | 0.11x | *Higher is better. Captures how much *original* data is delivered per unit of compressed input bandwidth. Formula: `Effective (MB/s) = Decompression Speed × 100 / Compression Ratio (%)`.* *Reading: on Apple M2, ZXC levels -1 through -6 deliver between **1.53x** and **2.08x** LZ4 effective bandwidth, and the new ULTRA level -7 reaches a **33.00%** ratio at **1.14x** LZ4. ZXC -6 (16 488 MB/s, 1.64x LZ4) clearly leads `lz4hc -9` (12 321 MB/s, 1.23x) on this platform — **1.34x more effective bandwidth at equivalent ratio**. Apple Silicon's deep pipelines amplify ZXC's lead at every level.* ### 7.2 Cloud Server Summary (ARM64 / Google Axion Neoverse-V2) | Compressor | Decompression Speed (Ratio vs LZ4) | Compressed Size (Index LZ4=100) (Lower is Better) | | :--- | :--- | :--- | | **zxc 0.13.0 -1** | **2.20x** | **129.22** | | **zxc 0.13.0 -2** | **1.83x** | **112.64** | | **zxc 0.13.0 -3** | **1.26x** | **96.20** | | **zxc 0.13.0 -4** | **1.20x** | **89.60** | | **zxc 0.13.0 -5** | **1.12x** | **84.59** | | **zxc 0.13.0 -6** | **1.04x** | **76.21** | | **zxc 0.13.0 -7** | **0.67x** | **69.34** | | lz4 1.10.0 --fast -17 | 1.16x | 130.58 | | lz4 1.10.0 (Ref) | 1.00x | 100.00 | | lz4hc 1.10.0 -9 | 0.90x | 77.20 | | lzav 5.9 -1 | 0.66x | 83.91 | | snappy 1.2.2 | 0.54x | 100.53 | | zstd 1.5.7 --fast --1 | 0.53x | 86.16 | | zstd 1.5.7 -1 | 0.39x | 72.55 | | zstd 1.5.7 -3 | 0.36x | 65.56 | | zlib 1.3.2 -1 | 0.09x | 76.58 | **Decompression Efficiency (Cycles per Byte @ 2.6 GHz)** | Compressor. | Cycles/Byte | Performance vs memcpy (*) | | ----------------------- | ----------- | --------------------- | | memcpy | 0.107 | 1.00x (baseline) | | **zxc 0.13.0 -1** | **0.277** | **2.6x** | | **zxc 0.13.0 -2** | **0.333** | **3.1x** | | **zxc 0.13.0 -3** | **0.484** | **4.5x** | | **zxc 0.13.0 -4** | **0.508** | **4.7x** | | **zxc 0.13.0 -5** | **0.542** | **5.1x** | | **zxc 0.13.0 -6** | **0.584** | **5.5x** | | **zxc 0.13.0 -7** | **0.915** | **8.5x** | | lz4 1.10.0 | 0.610 | 5.7x | | lz4 1.10.0 --fast -17 | 0.526 | 4.9x | | lz4hc 1.10.0 -9 | 0.674 | 6.3x | | lzav 5.9 -1 | 0.929 | 8.7x | | zstd 1.5.7 -1 | 1.582 | 14.8x | | zstd 1.5.7 --fast --1 | 1.141 | 10.7x | | zstd 1.5.7 -3 | 1.704 | 15.9x | | snappy 1.2.2 | 1.132 | 10.6x | | zlib 1.3.2 -1 | 6.684 | 62.4x | *Lower is better. Calculated using Neoverse-V2 base frequency (2.6 GHz). Formula: `Cycles/Byte = 2600 / Decompression Speed (MB/s)`.* **Effective Throughput (Ratio-normalized decode)** This metric expresses how much *original* data is delivered per unit of compressed input bandwidth. Formula: `Effective (MB/s) = Decompression Speed × 100 / Ratio (%)`. It captures the combined benefit of fast decode and good ratio: a smaller compressed file feeds the decoder with less bandwidth pressure on the source (storage / network / inter-core), so each MB of compressed data yields more MB of original data per second of decode work. *Higher is better.* | Compressor | Decode (MB/s) | Ratio (%) | Effective (MB/s) | vs LZ4 | | :--- | ---: | ---: | ---: | ---: | | **zxc 0.13.0 -1** | 9 383 | 61.50 | **15 257** | **1.70x** | | **zxc 0.13.0 -2** | 7 816 | 53.61 | **14 579** | **1.63x** | | **zxc 0.13.0 -3** | 5 377 | 45.79 | **11 743** | **1.31x** | | **zxc 0.13.0 -4** | 5 115 | 42.65 | **11 993** | **1.34x** | | **zxc 0.13.0 -5** | 4 794 | 40.27 | **11 904** | **1.33x** | | **zxc 0.13.0 -6** | 4 450 | 36.27 | **12 269** | **1.37x** | | **zxc 0.13.0 -7** | 2 843 | 33.00 | **8 615** | **0.96x** | | lz4 1.10.0 (Ref) | 4 263 | 47.60 | 8 956 | 1.00x | | lz4 1.10.0 --fast -17 | 4 947 | 62.15 | 7 961 | 0.89x | | lz4hc 1.10.0 -9 | 3 855 | 36.75 | 10 490 | 1.17x | | lzav 5.9 -1 | 2 799 | 39.94 | 7 008 | 0.78x | | snappy 1.2.2 | 2 296 | 47.85 | 4 798 | 0.54x | | zstd 1.5.7 --fast --1 | 2 278 | 41.01 | 5 555 | 0.62x | | zstd 1.5.7 -1 | 1 644 | 34.53 | 4 761 | 0.53x | | zstd 1.5.7 -3 | 1 526 | 31.20 | 4 891 | 0.55x | | zlib 1.3.2 -1 | 389 | 36.45 | 1 067 | 0.12x | *Higher is better. Captures how much *original* data is delivered per unit of compressed input bandwidth. Formula: `Effective (MB/s) = Decompression Speed × 100 / Compression Ratio (%)`.* *Reading: at ZXC -6, every MB/s of compressed input yields **12 269 MB/s** of original output — **1.17x** more effective bandwidth than `lz4hc -9` at equivalent ratio (36.27 vs 36.75), and **1.37x** more than LZ4 default. Levels -1 through -6 stay above **1.31x** LZ4; the new ULTRA level -7 trades throughput for a 33.00% ratio at 0.96x LZ4.* ### 7.3 Build Server Summary (x86_64 / AMD EPYC 9B45, Zen 5) | Compressor | Decompression Speed (Ratio vs LZ4) | Compressed Size (Index LZ4=100) (Lower is Better) | | :--- | :--- | :--- | | **zxc 0.13.0 -1** | **2.19x** | **129.22** | | **zxc 0.13.0 -2** | **1.95x** | **112.64** | | **zxc 0.13.0 -3** | **1.21x** | **96.20** | | **zxc 0.13.0 -4** | **1.15x** | **89.60** | | **zxc 0.13.0 -5** | **1.09x** | **84.59** | | **zxc 0.13.0 -6** | **1.07x** | **76.21** | | **zxc 0.13.0 -7** | **0.74x** | **69.34** | | lz4 1.10.0 --fast -17 | 1.05x | 130.58 | | lz4 1.10.0 (Ref) | 1.00x | 100.00 | | lz4hc 1.10.0 -9 | 0.96x | 77.20 | | lzav 5.9 -1 | 0.70x | 83.91 | | snappy 1.2.2 | 0.41x | 100.63 | | zstd 1.5.7 --fast --1 | 0.48x | 86.16 | | zstd 1.5.7 -1 | 0.38x | 72.55 | | zstd 1.5.7 -3 | 0.34x | 65.56 | | zlib 1.3.2 -1 | 0.08x | 76.58 | **Decompression Efficiency (Cycles per Byte @ 2.1 GHz)** | Compressor. | Cycles/Byte | Performance vs memcpy (*) | | ----------------------- | ----------- | --------------------- | | memcpy | 0.106 | 1.00x (baseline) | | **zxc 0.13.0 -1** | **0.190** | **1.8x** | | **zxc 0.13.0 -2** | **0.214** | **2.0x** | | **zxc 0.13.0 -3** | **0.345** | **3.2x** | | **zxc 0.13.0 -4** | **0.363** | **3.4x** | | **zxc 0.13.0 -5** | **0.384** | **3.6x** | | **zxc 0.13.0 -6** | **0.390** | **3.7x** | | **zxc 0.13.0 -7** | **0.561** | **5.3x** | | lz4 1.10.0 | 0.418 | 3.9x | | lz4 1.10.0 --fast -17 | 0.397 | 3.7x | | lz4hc 1.10.0 -9 | 0.434 | 4.1x | | lzav 5.9 -1 | 0.601 | 5.7x | | zstd 1.5.7 -1 | 1.112 | 10.5x | | zstd 1.5.7 --fast --1 | 0.862 | 8.1x | | zstd 1.5.7 -3 | 1.220 | 11.5x | | snappy 1.2.2 | 1.011 | 9.5x | | zlib 1.3.2 -1 | 5.224 | 49.2x | *Lower is better. Calculated using AMD EPYC 9B45 base frequency (2.1 GHz). Formula: `Cycles/Byte = 2100 / Decompression Speed (MB/s)`.* **Effective Throughput (Ratio-normalized decode)** | Compressor | Decode (MB/s) | Ratio (%) | Effective (MB/s) | vs LZ4 | | :--- | ---: | ---: | ---: | ---: | | **zxc 0.13.0 -1** | 11 032 | 61.50 | **17 938** | **1.70x** | | **zxc 0.13.0 -2** | 9 829 | 53.61 | **18 334** | **1.74x** | | **zxc 0.13.0 -3** | 6 096 | 45.79 | **13 313** | **1.26x** | | **zxc 0.13.0 -4** | 5 786 | 42.65 | **13 566** | **1.28x** | | **zxc 0.13.0 -5** | 5 468 | 40.27 | **13 580** | **1.29x** | | **zxc 0.13.0 -6** | 5 389 | 36.27 | **14 858** | **1.41x** | | **zxc 0.13.0 -7** | 3 742 | 33.00 | **11 339** | **1.07x** | | lz4 1.10.0 (Ref) | 5 029 | 47.60 | 10 565 | 1.00x | | lz4 1.10.0 --fast -17 | 5 293 | 62.15 | 8 517 | 0.81x | | lz4hc 1.10.0 -9 | 4 844 | 36.75 | 13 181 | 1.25x | | lzav 5.9 -1 | 3 495 | 39.94 | 8 751 | 0.83x | | snappy 1.2.2 | 2 078 | 47.89 | 4 339 | 0.41x | | zstd 1.5.7 --fast --1 | 2 437 | 41.01 | 5 943 | 0.56x | | zstd 1.5.7 -1 | 1 888 | 34.53 | 5 468 | 0.52x | | zstd 1.5.7 -3 | 1 722 | 31.20 | 5 519 | 0.52x | | zlib 1.3.2 -1 | 402 | 36.45 | 1 103 | 0.10x | *Higher is better. Captures how much *original* data is delivered per unit of compressed input bandwidth. Formula: `Effective (MB/s) = Decompression Speed × 100 / Compression Ratio (%)`.* *Reading: on EPYC 9B45, ZXC levels -1 through -6 deliver between **1.26x** and **1.74x** LZ4 effective bandwidth. On this Zen 5 platform ZXC -6 (14 858 MB/s, **1.41x** LZ4) now clearly leads `lz4hc -9` (13 181 MB/s, 1.25x) — ZXC -6's decode (5 389 MB/s) runs ~11% faster than lz4hc -9 (4 844 MB/s) here while keeping the ratio advantage (36.27 vs 36.75). The new ULTRA level -7 reaches a 33.00% ratio at 1.07x LZ4.* ### 7.4 Production Server Summary (x86_64 / AMD EPYC 7B13, Zen 3) | Compressor | Decompression Speed (Ratio vs LZ4) | Compressed Size (Index LZ4=100) (Lower is Better) | | :--- | :--- | :--- | | **zxc 0.13.0 -1** | **1.99x** | **129.22** | | **zxc 0.13.0 -2** | **1.66x** | **112.64** | | **zxc 0.13.0 -3** | **1.09x** | **96.20** | | **zxc 0.13.0 -4** | **1.05x** | **89.60** | | **zxc 0.13.0 -5** | **1.01x** | **84.59** | | **zxc 0.13.0 -6** | **0.95x** | **76.21** | | **zxc 0.13.0 -7** | **0.64x** | **69.34** | | lz4 1.10.0 --fast -17 | 1.15x | 130.58 | | lz4 1.10.0 (Ref) | 1.00x | 100.00 | | lz4hc 1.10.0 -9 | 0.96x | 77.20 | | lzav 5.9 -1 | 0.76x | 83.91 | | snappy 1.2.2 | 0.45x | 100.63 | | zstd 1.5.7 --fast --1 | 0.46x | 86.16 | | zstd 1.5.7 -1 | 0.34x | 72.55 | | zstd 1.5.7 -3 | 0.31x | 65.56 | | zlib 1.3.2 -1 | 0.09x | 76.58 | **Decompression Efficiency (Cycles per Byte @ 2.2 GHz)** | Compressor. | Cycles/Byte | Performance vs memcpy (*) | | ----------------------- | ----------- | --------------------- | | memcpy | 0.092 | 1.00x (baseline) | | **zxc 0.13.0 -1** | **0.285** | **3.1x** | | **zxc 0.13.0 -2** | **0.341** | **3.7x** | | **zxc 0.13.0 -3** | **0.518** | **5.6x** | | **zxc 0.13.0 -4** | **0.539** | **5.8x** | | **zxc 0.13.0 -5** | **0.559** | **6.1x** | | **zxc 0.13.0 -6** | **0.595** | **6.5x** | | **zxc 0.13.0 -7** | **0.889** | **9.6x** | | lz4 1.10.0 | 0.565 | 6.1x | | lz4 1.10.0 --fast -17 | 0.490 | 5.3x | | lz4hc 1.10.0 -9 | 0.590 | 6.4x | | lzav 5.9 -1 | 0.749 | 8.1x | | zstd 1.5.7 -1 | 1.643 | 17.8x | | zstd 1.5.7 --fast --1 | 1.232 | 13.4x | | zstd 1.5.7 -3 | 1.821 | 19.7x | | snappy 1.2.2 | 1.264 | 13.7x | | zlib 1.3.2 -1 | 6.128 | 66.4x | *Lower is better. Calculated using AMD EPYC 7B13 base frequency (2.2 GHz). Formula: `Cycles/Byte = 2200 / Decompression Speed (MB/s)`.* **Effective Throughput (Ratio-normalized decode)** | Compressor | Decode (MB/s) | Ratio (%) | Effective (MB/s) | vs LZ4 | | :--- | ---: | ---: | ---: | ---: | | **zxc 0.13.0 -1** | 7 726 | 61.50 | **12 563** | **1.54x** | | **zxc 0.13.0 -2** | 6 461 | 53.61 | **12 052** | **1.47x** | | **zxc 0.13.0 -3** | 4 250 | 45.79 | **9 282** | **1.14x** | | **zxc 0.13.0 -4** | 4 082 | 42.65 | **9 571** | **1.17x** | | **zxc 0.13.0 -5** | 3 938 | 40.27 | **9 779** | **1.20x** | | **zxc 0.13.0 -6** | 3 695 | 36.27 | **10 188** | **1.25x** | | **zxc 0.13.0 -7** | 2 474 | 33.00 | **7 497** | **0.92x** | | lz4 1.10.0 (Ref) | 3 891 | 47.60 | 8 174 | 1.00x | | lz4 1.10.0 --fast -17 | 4 494 | 62.15 | 7 232 | 0.88x | | lz4hc 1.10.0 -9 | 3 727 | 36.75 | 10 142 | 1.24x | | lzav 5.9 -1 | 2 939 | 39.94 | 7 359 | 0.90x | | snappy 1.2.2 | 1 741 | 47.89 | 3 635 | 0.44x | | zstd 1.5.7 --fast --1 | 1 785 | 41.01 | 4 353 | 0.53x | | zstd 1.5.7 -1 | 1 339 | 34.53 | 3 878 | 0.47x | | zstd 1.5.7 -3 | 1 208 | 31.20 | 3 872 | 0.47x | | zlib 1.3.2 -1 | 359 | 36.45 | 985 | 0.12x | *Higher is better. Captures how much *original* data is delivered per unit of compressed input bandwidth. Formula: `Effective (MB/s) = Decompression Speed × 100 / Compression Ratio (%)`.* *Reading: on EPYC 7B13 (Zen 3), ZXC levels -1 through -6 deliver between **1.14x** and **1.54x** LZ4 effective bandwidth. On this older Zen 3 microarchitecture ZXC -6 (10 188 MB/s, **1.25x** LZ4) now edges ahead of `lz4hc -9` (10 142 MB/s, 1.24x) on effective bandwidth — lz4hc's raw decode stays ~1% faster (3 727 vs 3 695 MB/s) but ZXC -6's ratio advantage (36.27 vs 36.75) tips the balance. The new ULTRA level -7 reaches a 33.00% ratio at 0.92x LZ4.* ### 7.5 Benchmarks Results **Figure B**: Decompression Efficiency : Cycles Per Byte Comparaison ![Benchmark Cycles Per Byte](./images/bench-cycles.svg) **Figure C**: Effective Throughput — Ratio-Normalized Decode (vs LZ4 baseline = 1.00x) ![Effective Throughput vs LZ4](./images/bench-effective.svg) #### 7.5.1 ARM64 Architecture (Apple Silicon M2) Benchmarks were conducted using lzbench 2.3 (from @inikep), compiled with Clang 21.0.0 using *MOREFLAGS="-march=native"* on macOS Tahoe 26 (`macos-26-xlarge`). The reference hardware is an Apple M2 processor (ARM64). **All performance metrics reflect single-threaded execution on the standard Silesia Corpus and the benchmark made use of [silesia.tar](https://github.com/DataCompression/corpus-collection/tree/main/Silesia-Corpus), which contains tarred files from the Silesia compression corpus.** | Compressor name | Compression| Decompress.| Compr. size | Ratio | Filename | | --------------- | -----------| -----------| ----------- | ----- | -------- | | memcpy | 52855 MB/s | 52878 MB/s | 211947520 |100.00 | 1 files| | **zxc 0.13.0 -1** | 888 MB/s | **12880 MB/s** | 130356147 | **61.50** | 1 files| | **zxc 0.13.0 -2** | 594 MB/s | **10683 MB/s** | 113633866 | **53.61** | 1 files| | **zxc 0.13.0 -3** | 251 MB/s | **7063 MB/s** | 97051444 | **45.79** | 1 files| | **zxc 0.13.0 -4** | 173 MB/s | **6696 MB/s** | 90392857 | **42.65** | 1 files| | **zxc 0.13.0 -5** | 103 MB/s | **6295 MB/s** | 85341256 | **40.27** | 1 files| | **zxc 0.13.0 -6** | 12.4 MB/s | **5980 MB/s** | 76877873 | **36.27** | 1 files| | **zxc 0.13.0 -7** | 8.74 MB/s | **3790 MB/s** | 69950212 | **33.00** | 1 files| | lz4 1.10.0 | 814 MB/s | 4784 MB/s | 100880800 | 47.60 | 1 files| | lz4 1.10.0 --fast -17 | 1354 MB/s | 5624 MB/s | 131732802 | 62.15 | 1 files| | lz4hc 1.10.0 -9 | 48.0 MB/s | 4528 MB/s | 77884448 | 36.75 | 1 files| | lzav 5.9 -1 | 674 MB/s | 3901 MB/s | 84644732 | 39.94 | 1 files| | snappy 1.2.2 | 883 MB/s | 3265 MB/s | 101415443 | 47.85 | 1 files| | zstd 1.5.7 --fast --1 | 725 MB/s | 2539 MB/s | 86916294 | 41.01 | 1 files| | zstd 1.5.7 -1 | 646 MB/s | 1809 MB/s | 73193704 | 34.53 | 1 files| | zstd 1.5.7 -3 | 394 MB/s | 1704 MB/s | 66133500 | 31.20 | 1 files| | zlib 1.3.2 -1 | 150 MB/s | 412 MB/s | 77259029 | 36.45 | 1 files| #### 7.5.2 ARM64 Architecture (Google Axion Neoverse-V2) Benchmarks were conducted using lzbench 2.3 (from @inikep), compiled with GCC 14.3.0 using *MOREFLAGS="-march=native"* on 64-bit Linux. The reference hardware is a Google Axion (Neoverse-V2) processor on a **Google Cloud C4A** instance (ARM64, 1 thread per core). **All performance metrics reflect single-threaded execution on the standard Silesia Corpus and the benchmark made use of [silesia.tar](https://github.com/DataCompression/corpus-collection/tree/main/Silesia-Corpus), which contains tarred files from the Silesia compression corpus.** | Compressor name | Compression| Decompress.| Compr. size | Ratio | Filename | | --------------- | -----------| -----------| ----------- | ----- | -------- | | memcpy | 24338 MB/s | 24266 MB/s | 211947520 |100.00 | 1 files| | **zxc 0.13.0 -1** | 868 MB/s | **9383 MB/s** | 130356147 | **61.50** | 1 files| | **zxc 0.13.0 -2** | 588 MB/s | **7816 MB/s** | 113633866 | **53.61** | 1 files| | **zxc 0.13.0 -3** | 241 MB/s | **5377 MB/s** | 97051444 | **45.79** | 1 files| | **zxc 0.13.0 -4** | 167 MB/s | **5115 MB/s** | 90392857 | **42.65** | 1 files| | **zxc 0.13.0 -5** | 98.5 MB/s | **4794 MB/s** | 85341256 | **40.27** | 1 files| | **zxc 0.13.0 -6** | 11.7 MB/s | **4450 MB/s** | 76877873 | **36.27** | 1 files| | **zxc 0.13.0 -7** | 7.72 MB/s | **2843 MB/s** | 69950212 | **33.00** | 1 files| | lz4 1.10.0 | 729 MB/s | 4263 MB/s | 100880800 | 47.60 | 1 files| | lz4 1.10.0 --fast -17 | 1276 MB/s | 4947 MB/s | 131732802 | 62.15 | 1 files| | lz4hc 1.10.0 -9 | 24.1 MB/s | 3855 MB/s | 77884448 | 36.75 | 1 files| | lzav 5.9 -1 | 633 MB/s | 2799 MB/s | 84644732 | 39.94 | 1 files| | snappy 1.2.2 | 755 MB/s | 2296 MB/s | 101415443 | 47.85 | 1 files| | zstd 1.5.7 --fast --1 | 605 MB/s | 2278 MB/s | 86916294 | 41.01 | 1 files| | zstd 1.5.7 -1 | 522 MB/s | 1644 MB/s | 73193704 | 34.53 | 1 files| | zstd 1.5.7 -3 | 330 MB/s | 1526 MB/s | 66133500 | 31.20 | 1 files| | zlib 1.3.2 -1 | 115 MB/s | 389 MB/s | 77259029 | 36.45 | 1 files| #### 7.5.3 x86_64 Architecture (AMD EPYC 9B45) Benchmarks were conducted using lzbench 2.3 (from @inikep), compiled with GCC 14.3.0 using *MOREFLAGS="-march=native"* on 64-bit Linux. The reference hardware is an AMD EPYC 9B45 processor on a **Google Cloud C4D** instance (x86_64, Zen 5, 2.1 GHz, SMT disabled — 1 thread per core). **All performance metrics reflect single-threaded execution on the standard Silesia Corpus and the benchmark made use of [silesia.tar](https://github.com/DataCompression/corpus-collection/tree/main/Silesia-Corpus), which contains tarred files from the Silesia compression corpus.** | Compressor name | Compression| Decompress.| Compr. size | Ratio | Filename | | --------------- | -----------| -----------| ----------- | ----- | -------- | | memcpy | 25324 MB/s | 19789 MB/s | 211947520 |100.00 | 1 files| | **zxc 0.13.0 -1** | 879 MB/s | **11032 MB/s** | 130356147 | **61.50** | 1 files| | **zxc 0.13.0 -2** | 597 MB/s | **9829 MB/s** | 113633866 | **53.61** | 1 files| | **zxc 0.13.0 -3** | 239 MB/s | **6096 MB/s** | 97051444 | **45.79** | 1 files| | **zxc 0.13.0 -4** | 165 MB/s | **5786 MB/s** | 90392857 | **42.65** | 1 files| | **zxc 0.13.0 -5** | 98.9 MB/s | **5468 MB/s** | 85341256 | **40.27** | 1 files| | **zxc 0.13.0 -6** | 12.3 MB/s | **5389 MB/s** | 76877873 | **36.27** | 1 files| | **zxc 0.13.0 -7** | 8.52 MB/s | **3742 MB/s** | 69950212 | **33.00** | 1 files| | lz4 1.10.0 | 772 MB/s | 5029 MB/s | 100880800 | 47.60 | 1 files| | lz4 1.10.0 --fast -17 | 1287 MB/s | 5293 MB/s | 131732802 | 62.15 | 1 files| | lz4hc 1.10.0 -9 | 45.3 MB/s | 4844 MB/s | 77884448 | 36.75 | 1 files| | lzav 5.9 -1 | 652 MB/s | 3495 MB/s | 84644732 | 39.94 | 1 files| | snappy 1.2.2 | 773 MB/s | 2078 MB/s | 101512076 | 47.89 | 1 files| | zstd 1.5.7 --fast --1 | 663 MB/s | 2437 MB/s | 86916294 | 41.01 | 1 files| | zstd 1.5.7 -1 | 600 MB/s | 1888 MB/s | 73193704 | 34.53 | 1 files| | zstd 1.5.7 -3 | 363 MB/s | 1722 MB/s | 66133500 | 31.20 | 1 files| | zlib 1.3.2 -1 | 136 MB/s | 402 MB/s | 77259029 | 36.45 | 1 files| #### 7.5.4 x86_64 Architecture (AMD EPYC 7B13, Zen 3) Benchmarks were conducted using lzbench 2.3 (from @inikep), compiled with GCC 14.3.0 using *MOREFLAGS="-march=native"* on 64-bit Linux. The reference hardware is an AMD EPYC 7B13 64-Core processor on a **Google Cloud C2D** instance (x86_64, Zen 3, 2.2 GHz, SMT disabled — 1 thread per core). **All performance metrics reflect single-threaded execution on the standard Silesia Corpus and the benchmark made use of [silesia.tar](https://github.com/DataCompression/corpus-collection/tree/main/Silesia-Corpus), which contains tarred files from the Silesia compression corpus.** | Compressor name | Compression| Decompress.| Compr. size | Ratio | Filename | | --------------- | -----------| -----------| ----------- | ----- | -------- | | memcpy | 23953 MB/s | 23834 MB/s | 211947520 |100.00 | 1 files| | **zxc 0.13.0 -1** | 717 MB/s | **7726 MB/s** | 130356147 | **61.50** | 1 files| | **zxc 0.13.0 -2** | 483 MB/s | **6461 MB/s** | 113633866 | **53.61** | 1 files| | **zxc 0.13.0 -3** | 202 MB/s | **4250 MB/s** | 97051444 | **45.79** | 1 files| | **zxc 0.13.0 -4** | 141 MB/s | **4082 MB/s** | 90392857 | **42.65** | 1 files| | **zxc 0.13.0 -5** | 84.0 MB/s | **3938 MB/s** | 85341256 | **40.27** | 1 files| | **zxc 0.13.0 -6** | 10.6 MB/s | **3695 MB/s** | 76877873 | **36.27** | 1 files| | **zxc 0.13.0 -7** | 6.68 MB/s | **2474 MB/s** | 69950212 | **33.00** | 1 files| | lz4 1.10.0 | 641 MB/s | 3891 MB/s | 100880800 | 47.60 | 1 files| | lz4 1.10.0 --fast -17 | 1112 MB/s | 4494 MB/s | 131732802 | 62.15 | 1 files| | lz4hc 1.10.0 -9 | 37.3 MB/s | 3727 MB/s | 77884448 | 36.75 | 1 files| | lzav 5.9 -1 | 481 MB/s | 2939 MB/s | 84644732 | 39.94 | 1 files| | snappy 1.2.2 | 665 MB/s | 1741 MB/s | 101512076 | 47.89 | 1 files| | zstd 1.5.7 --fast --1 | 488 MB/s | 1785 MB/s | 86916294 | 41.01 | 1 files| | zstd 1.5.7 -1 | 445 MB/s | 1339 MB/s | 73193704 | 34.53 | 1 files| | zstd 1.5.7 -3 | 233 MB/s | 1208 MB/s | 66133500 | 31.20 | 1 files| | zlib 1.3.2 -1 | 107 MB/s | 359 MB/s | 77259029 | 36.45 | 1 files| ### 7.6 Memory Usage per Compression Context | Block Size | Levels -1 to -5 | Level -6 (DENSITY) | |:---------------------:|----------------:|-------------------:| | 256 KB | ~1.03 MB | ~3.06 MB | | **512 KB** *(default)*| **~1.78 MB** | **~5.84 MB** | | 2 MB *(max)* | ~6.28 MB | ~22.53 MB | *Levels -1 to -5 share the same context layout (LZ77 hash + chain + sequence / literal buffers) and scale linearly with block size. Level -6 (DENSITY) lazily allocates the optimal-parser scratch (per-position DP cost, parent length / offset, packed match-end bitmap), adding ~×3 overhead. Exact values for any (block, level) combination are reproducible via the public API call `zxc_estimate_cctx_size(block_size, level)`.* > **Guideline:** Default 512 KB block keeps cctx under 6 MB even at the densest level (-6) — well within reach for typical server / desktop pipelines. For streaming, embedded, or memory-constrained environments, use `-B 256K` (or smaller) and stick to levels -1 to -5. Level -6 is best reserved for offline encoding pipelines where ratio matters and per-thread RAM is plentiful. ### 7.7 In-Place Decompression For integrators that hold the whole archive in RAM — firmware unpackers, game asset loaders, FOTA payloads — ZXC decompresses **inside a single buffer**, removing the second (output) allocation entirely. The compressed archive is placed **flush-right** in a buffer of size `zxc_decompress_inplace_bound(...)`, and `zxc_decompress_inplace()` decodes left-to-right into the same memory. The safety argument is exact. Decompression consumes `c` compressed bytes and produces `d ≥ c` decompressed bytes per block payload (a ZXC block never expands — it falls back to a RAW block otherwise), but each block also carries its framing (header + optional checksum), so on incompressible input the compressed stream runs `nblocks × framing` bytes longer than the output. Per block, safety requires `output_through_k + wild_copy_pad ≤ compressed_start_of_block_k`, which holds for every block once the buffer carries a margin of one block plus the accumulated per-block framing plus the decoder's wild-copy tail (`block_size + nblocks × (header + optional checksum) + footer + ZXC_DECOMPRESS_TAIL_PAD`). The bound is derived from the archive header (block size) and footer (decompressed size) without decoding; an undersized buffer is rejected (`ZXC_ERROR_DST_TOO_SMALL`), never allowed to corrupt. Peak decode memory therefore drops from *compressed + decompressed* to *decompressed + ~1 %*. This mirrors the in-place decode modes of LZ4 (`LZ4_DECOMPRESS_INPLACE_MARGIN`) and Zstd (decompression margin) — a library capability for embedded integration, orthogonal to the streaming CLI, whose block-at-a-time decode is already memory-bounded. Dictionary archives are supported (they resolve through the context's own bounce buffer, which does not alias the in-place buffer). ## 8. Strategic Implementation ZXC is designed to adapt to various deployment scenarios by selecting the appropriate compression level: * **Interactive Media & Gaming (Levels 1-2-3)**: Optimized for hard real-time constraints. Ideal for texture streaming and asset loading, offering **~40% faster** load times to minimize latency and frame drops. * **Embedded Systems & Firmware (Levels 3-4-5)**: The sweet spot for maximizing storage density on limited flash memory (e.g., Kernel, Initramfs) while ensuring rapid "instant-on" (XIP-like) boot performance. * **Data Archival (Levels 5-6)**: A high-efficiency alternative for cold storage, providing better compression ratios than LZ4 and significantly faster retrieval speeds than Zstd. **Level 6** (DENSITY) beats LZ4-HC on both axes — better ratio (36.27 vs 36.75 on silesia) *and* faster-or-equal decode on every measured platform (+32 % on Apple Silicon, +11 % to +15 % on the Neoverse-V2 and Zen 5 cloud CPUs, and parity on Zen 3): ideal for write-once / read-many archives where compression time is amortized over many reads. * **Maximum Density (Level 7)**: Deep parse (search depth 128), 11-bit entropy codes and Huffman-coded sequence tokens. On silesia it lands at **33.01 %** — a better ratio than `zstd -1` (34.53 %) — while decoding at **1.8-2.1x** zstd -1's speed (3.7 GB/s on Apple M2). It occupies the historical gap between the LZ4 family and Zstd: choose it when storage or bandwidth dominates but decompression must stay in the multi-GB/s class. * **Small & Homogeneous Payloads — Pre-Trained Dictionaries (§5.10)**: An orthogonal lever, combinable with any level. Where data is compressed in **small blocks** (4 KB–128 KB) — JSON API responses, RPC messages, key-value records, structured logs, small game assets, or any large homogeneous corpus split for seekable random access — a pre-trained dictionary primes the LZ77 window per block and recovers the ratio that small blocks would otherwise lose. The external, content-addressed model (`.zxd` + `dict_id`) fits the **train-once / reuse-many** deployment pattern: a single dictionary is built offline on the build pipeline and amortized across millions of independently decodable payloads — no per-archive storage overhead, and O(1) seekable access preserved. ## 9. Conclusion ZXC redefines asset distribution by prioritizing the end-user experience. Through its asymmetric design and modular architecture, it shifts computational cost to the build pipeline, unlocking unparalleled decompression speeds on ARM devices. This efficiency translates directly into faster load times, reduced battery consumption, and a smoother user experience, making ZXC a best choice for modern, high-performance deployment constraints. zxc-0.13.0/docs/abi/000077500000000000000000000000001522536370400140715ustar00rootroot00000000000000zxc-0.13.0/docs/abi/libzxc-linux-x86_64.abi.xml000066400000000000000000005017051522536370400207410ustar00rootroot00000000000000 zxc-0.13.0/docs/abi/libzxc.suppr000066400000000000000000000036321522536370400164630ustar00rootroot00000000000000# libzxc — abidiff suppressions # # These four types are OPAQUE handles. The public headers (include/*.h) only # forward-declare them: # # typedef struct zxc_cctx_s zxc_cctx; /* include/zxc_buffer.h */ # typedef struct zxc_dctx_s zxc_dctx; /* include/zxc_buffer.h */ # typedef struct zxc_dstream_s zxc_dstream; /* include/zxc_pstream.h */ # typedef struct zxc_seekable_s zxc_seekable; /* include/zxc_seekable.h */ # # The full struct bodies live in src/lib/*.c (and the non-installed # zxc_internal.h) — never in an installed header. Consumers only ever hold a # POINTER to them, obtained from zxc_create_*/…_create, or placed into a # caller-provided workspace whose required size is queried AT RUNTIME via # zxc_static_cctx_workspace_size() / zxc_static_dctx_workspace_size(). No public # header exposes their layout, passes them by value, or defines a compile-time # size constant. # # Therefore growing or reordering their members is ABI-backward-compatible: a # binary built against an older libzxc keeps working with a newer libzxc.so.4, # because the library itself allocates the (now larger) struct and reports its # size dynamically. abidiff flags these only because it walks the pointed-to # type in DWARF — a known false positive for the opaque-pointer (pimpl) pattern. # # A layout change to these types must NOT force a SOVERSION bump. A genuine ABI # break — one that DOES require bumping ZXC_SOVERSION and regenerating the # baseline — is a removed/renamed exported symbol, a changed public function # signature, or a layout change to a by-value/visible type (e.g. an *_opts_t # struct). Those are intentionally NOT suppressed here. [suppress_type] type_kind = struct name = zxc_cctx_s [suppress_type] type_kind = struct name = zxc_dctx_s [suppress_type] type_kind = struct name = zxc_dstream_s [suppress_type] type_kind = struct name = zxc_seekable_s zxc-0.13.0/docs/images/000077500000000000000000000000001522536370400146035ustar00rootroot00000000000000zxc-0.13.0/docs/images/bench-arm64.svg000066400000000000000000002560341522536370400173440ustar00rootroot00000000000000zxc-0.13.0/docs/images/bench-bars.svg000066400000000000000000004662621522536370400173500ustar00rootroot00000000000000zxc-0.13.0/docs/images/bench-cycles.svg000066400000000000000000002531611522536370400176730ustar00rootroot00000000000000zxc-0.13.0/docs/images/bench-effective.svg000066400000000000000000003177721522536370400203620ustar00rootroot00000000000000zxc-0.13.0/docs/images/bench-pareto-ratio.svg000066400000000000000000004404611522536370400210200ustar00rootroot00000000000000zxc-0.13.0/docs/man/000077500000000000000000000000001522536370400141115ustar00rootroot00000000000000zxc-0.13.0/docs/man/zxc.1.md000066400000000000000000000240251522536370400154010ustar00rootroot00000000000000# zxc(1) ## NAME **zxc**, **unzxc** - Compress or decompress .zxc files ## SYNOPSIS **zxc** [*OPTIONS*] [*INPUT-FILE*] [*OUTPUT-FILE*] **unzxc** is equivalent to **zxc -d**. ## DESCRIPTION **zxc** is a command-line interface for the ZXC compression library, a high-performance lossless compression algorithm optimized for maximum decompression throughput. **zxc** is designed for the *"Write Once, Read Many"* paradigm. It trades compression speed to generate a bitstream specifically structured to maximize decompression speed, effectively offloading complexity from the decoder to the encoder. It aims to provide very high decompression speeds across modern architectures while maintaining competitive compression ratios. ZXC is particularly suited for scenarios such as Game Assets, Firmware or App Bundles where data is compressed once on a build server and decompressed millions of times on user devices. By default, **zxc** compresses a single *INPUT-FILE*. If no *OUTPUT-FILE* is provided, **zxc** will automatically append the `.zxc` extension to the input filename. If no *INPUT-FILE* is provided, **zxc** will read from standard input (`stdin`) and write to standard output (`stdout`). ## STANDARD MODES **-z**, **--compress** : Compress FILE. This is the default mode if no mode is specified. **-d**, **--decompress** : Decompress FILE. This is the default mode when **zxc** is invoked under the name **unzxc** (typically an installed symlink). An explicit mode flag still takes precedence. **-l**, **--list** : List archive information, including compressed size, uncompressed size, compression ratio, checksum method, and dictionary ID (if any). Also accepts a `.zxd` dictionary file, in which case it prints the dictionary's `dict_id`. **--train** : Train a dictionary from the input *FILE*s given as training samples. The output path is set with **-o** (see **--output**); when **-o** is omitted, the dictionary is written to `dictionary_.zxd` in the current directory. See **DICTIONARIES**. **-t**, **--test** : Test the integrity of a compressed FILE. It decodes the file and verifies its checksum (if present) without writing any output. **-b**, **--bench** [*N*] : Benchmark in-memory performance. Loads the input file entirely into RAM and measures raw algorithm throughput (default duration is 5 seconds). ## OPTIONS **-m**, **--multiple** : Process multiple files at once. When specified, all subsequent non-option arguments are treated as input files. For each input file, a corresponding `.zxc` file is created (or decompressed into its original name). Output cannot be written to standard output (`stdout`) when this mode is enabled. **-r**, **--recursive** : Recursively process directories. When specified, any directory listed as an argument will be traversed, and all regular files within it will be processed (compressed or decompressed). This option implicitly enables `--multiple` mode. **-1**..**-7** : Set the compression level from 1 (fastest compression) to 7 (maximum density). - **-1, -2 (Fast):** Optimized for real-time assets or when compression speed is a priority. - **-3 (Default):** Balanced middle-ground offering efficient compression and superior ratio to fast codecs. - **-4, -5 (Compact):** Better ratio than LZ4 with faster decoding than Zstd. Suited for embedded systems and firmware. - **-6 (Max):** Beats LZ4-HC on both axes — better ratio *and* faster decode — while staying in the multi-GB/s decode class. Best for archival and write-once / read-many workloads where compression time is amortized over many reads. - **-7 (Ultra):** Maximum density. A deep optimal parse plus Huffman-coded literals *and* tokens (11-bit codes, decoded via the SIMD-merge PivCo layout) push the ratio past `zstd -1` while still decoding several times faster than it. Choose it when storage or bandwidth dominates but decode must stay fast; compression is the slowest tier. **-T**, **--threads** *N* : Set the number of threads to use for compression and decompression. A value of `0` means auto-detection based on the number of available CPU cores. **-B**, **--block-size** *SIZE* : Set the compression block size. *SIZE* must be a power of two between **4K** and **2M** (e.g. `4K`, `64K`, `256K`, `512K`, `1M`). Smaller blocks reduce memory usage and improve random-access decompression; larger blocks generally yield better compression ratios. The default is **512K**, tuned for bulk/archival workloads where ratio and decompression throughput matter most. This option is only meaningful during compression; the block size is stored in the archive header and automatically used during decompression. **-C**, **--checksum** : Enable block hashing during compression using the rapidhash algorithm. Recommended for data integrity validation. Checksum verification is automatically performed during extraction when enabled. **-N**, **--no-checksum** : Explicitly disable checksum generation. **-D**, **--dict** *FILE* : Use a pre-trained dictionary (`.zxd`) for compression or decompression. On compression, the archive records the dictionary's `dict_id` in its header. On decompression, **-D** is **required** for any archive that was compressed with a dictionary — there is no auto-lookup; without it, decompression fails with a dictionary-required error (see **DICTIONARIES**). **-o**, **--output** *FILE* : Write output to *FILE*. For compression/decompression this overrides the positional *OUTPUT-FILE* (single-file mode only); when omitted the output name is derived from the input. For **--train** it sets the dictionary path: if *FILE* is a directory (or ends with a path separator) the dictionary is saved inside it as `dictionary_.zxd`, otherwise *FILE* is used verbatim; when omitted the dictionary is written to `dictionary_.zxd` in the current directory. **-S**, **--seekable** : Append a seek table to the archive during compression. This transforms the file into a random-access format (Seekable Archive), allowing the decoder to instantly locate and decompress specific blocks in `O(1)` time without reading the entire file. Ideal for compressed filesystems, game assets, and log analysis. **-k**, **--keep** : Keep the input file after an in-place compression or decompression. By default the input is removed **only** when its output name is auto-derived (e.g. `file` → `file.zxc`). When the output is named explicitly — with **-o** or a positional *OUTPUT-FILE* — the input is always kept, so this flag is only needed for the in-place case. **-f**, **--force** : Force overwrite of the *OUTPUT-FILE* if it already exists. **-c**, **--stdout** : Force writing to standard output (`stdout`), even if it is the console. **-v**, **--verbose** : Enable verbose logging mode. Outputs more detailed information during operations. **-q**, **--quiet** : Enable quiet mode, suppressing all non-error output (such as progress bars or real-time statistics). **-j**, **--json** : Output results in JSON format. This is particularly useful for scripting, benchmarking, and the `--list` mode. **--progress** *MODE* : Control the progress display: **auto** (default) shows a progress bar on the standard error stream when it is a terminal and the file is larger than 1 MB; **always** forces progress reporting even when standard error is not a terminal (updates are then printed one per line, at most once per second) or when the size is unknown (stdin input); **never** disables it. **-q** suppresses progress regardless of this option. ## SPECIAL OPTIONS **-V**, **--version** : Display the version of the zxc library and the compiled architecture information, then exit. **-h**, **--help** : Display a help message and exit. ## DICTIONARIES For workloads compressed in small blocks (4K–128K), a pre-trained dictionary can dramatically improve the compression ratio. The dictionary prefills the LZ77 window at the start of every block, so the benefit is per-block: the smaller the block, the more it relies on the dictionary for early matches. Dictionaries are external `.zxd` files referenced from the archive header by a 32-bit `dict_id` (a hash of the dictionary content). The `.zxd` extension is cosmetic; a `.zxd` file is identified by its magic word, not its name. When **--train**'s **-o** targets a directory (or is omitted, defaulting to the current directory), **zxc** names the file `dictionary_.zxd` (the `dict_id` is the lowercase 8-digit hex reported by `zxc -l`). On decompression the dictionary is **not** auto-located: an archive compressed with a dictionary must be decompressed by supplying that dictionary with **-D**, otherwise decompression fails with a dictionary-required error. If no matching dictionary can be found (or supplied), decompression fails with a dictionary-required error rather than producing corrupt output. ## EXAMPLES **Compress a file:** zxc data.txt **Compress a file with high density (Level 6, archival):** zxc -6 data.bin **Compress a file with maximum density (Level 7, Ultra):** zxc -7 data.bin **Decompress a file:** zxc -d data.txt.zxc **Decompress a file using the unzxc alias:** unzxc data.txt.zxc **Compress multiple files independently:** zxc -m file1.txt file2.txt file3.txt **Compress all files in a directory recursively:** zxc -r ./my_folder **Decompress all files in a directory recursively:** zxc -d -r ./my_folder **Decompress a file to standard output:** zxc -dc data.txt.zxc > data.txt **List archive information:** zxc -l data.txt.zxc **Compress with a custom block size (64 KB):** zxc -B 64K data.bin data.zxc **Compress with maximum block size (2 MB):** zxc -6 -B 2M data.bin data.zxc **Run a benchmark for 10 seconds:** zxc -b 10 data.txt **Train a dictionary into a directory (saved as `dictionary_.zxd`):** zxc --train -o dicts/ samples/*.json **Compress with a dictionary using small blocks:** zxc -B 4K -D dicts/dictionary_bc46eec1.zxd input.json **Decompress (the dictionary is required, pass it with -D):** zxc -d -D dicts/dictionary_bc46eec1.zxd input.json.zxc ## BUGS Report bugs at . ## AUTHORS Bertrand Lebonnois ## LICENSE BSD 3-Clause License. zxc-0.13.0/include/000077500000000000000000000000001522536370400140315ustar00rootroot00000000000000zxc-0.13.0/include/zxc.h000066400000000000000000000007671522536370400150200ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef ZXC_H #define ZXC_H #include "zxc_buffer.h" // IWYU pragma: keep #include "zxc_constants.h" // IWYU pragma: keep #include "zxc_dict.h" // IWYU pragma: keep #include "zxc_error.h" // IWYU pragma: keep #include "zxc_opts.h" // IWYU pragma: keep #include "zxc_pstream.h" // IWYU pragma: keep #endif // ZXC_Hzxc-0.13.0/include/zxc_buffer.h000066400000000000000000000731401522536370400163440ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file zxc_buffer.h * @brief Buffer-based (single-shot) compression and decompression API. * * This header exposes the simplest way to use ZXC: pass an entire input buffer * and receive the result in a single output buffer. All functions in this * header are single-threaded and blocking. * * @par Typical usage * @code * // Compress * size_t bound = zxc_compress_bound(src_size); * void *dst = malloc(bound); * zxc_compress_opts_t opts = { .level = ZXC_LEVEL_DEFAULT, .checksum_enabled = 1 }; * int64_t csize = zxc_compress(src, src_size, dst, bound, &opts); * * // Decompress * uint64_t orig = zxc_get_decompressed_size(dst, csize); * void *out = malloc(orig); * zxc_decompress_opts_t dopts = { .checksum_enabled = 1 }; * int64_t dsize = zxc_decompress(dst, csize, out, orig, &dopts); * @endcode * * @see zxc_stream.h for the streaming (multi-threaded) API. * @see zxc_pstream.h for single-threaded push-based streaming. */ #ifndef ZXC_BUFFER_H #define ZXC_BUFFER_H #include #include #include "zxc_export.h" #include "zxc_opts.h" #ifdef __cplusplus extern "C" { #endif /** * @defgroup library_info Library Information * @brief Runtime-queryable library metadata. * * These functions allow callers (including filesystem integrations) * to discover the supported compression level range and library version at * runtime, without relying on compile-time constants alone. * @{ */ /** * @brief Returns the minimum supported compression level. * * Currently returns @ref ZXC_LEVEL_FASTEST (1). * * @return Minimum compression level value. */ ZXC_EXPORT int zxc_min_level(void); /** * @brief Returns the maximum supported compression level. * * Currently returns @ref ZXC_LEVEL_ULTRA (7). * * @return Maximum compression level value. */ ZXC_EXPORT int zxc_max_level(void); /** * @brief Returns the default compression level. * * Currently returns @ref ZXC_LEVEL_DEFAULT (3). * * @return Default compression level value. */ ZXC_EXPORT int zxc_default_level(void); /** * @brief Returns the human-readable library version string. * * The returned pointer is a compile-time constant and must not be freed. * Format: "MAJOR.MINOR.PATCH" (e.g. "0.13.0"). * * @return Null-terminated version string. */ ZXC_EXPORT const char* zxc_version_string(void); /** @} */ /* end of library_info */ /** * @defgroup buffer_api Buffer API * @brief Single-shot, buffer-based compression and decompression. * @{ */ /** * @brief Calculates the maximum theoretical compressed size for a given input. * * Useful for allocating output buffers before compression. * Accounts for file headers, block headers, and potential expansion * of incompressible data. * * @param[in] input_size Size of the input data in bytes. * * @return Maximum required buffer size in bytes. */ ZXC_EXPORT uint64_t zxc_compress_bound(const size_t input_size); /** * @brief Compresses a data buffer using the ZXC algorithm. * * This version uses standard size_t types and void pointers. * It executes in a single thread (blocking operation). * It writes the ZXC file header followed by compressed blocks. * * @param[in] src Pointer to the source buffer. * @param[in] src_size Size of the source data in bytes. * @param[out] dst Pointer to the destination buffer. * @param[in] dst_capacity Maximum capacity of the destination buffer. * @param[in] opts Compression options (NULL uses all defaults). * @c n_threads and the progress callback are ignored * (this call is single-threaded and blocking). * * @note @p src and @p dst must not overlap (same contract as memcpy). * @note Levels above @ref ZXC_LEVEL_ULTRA are silently clamped to * @ref ZXC_LEVEL_ULTRA; levels <= 0 select @ref ZXC_LEVEL_DEFAULT. * * @return The number of bytes written to dst (>0 on success), * or a negative zxc_error_t code (e.g., ZXC_ERROR_DST_TOO_SMALL) on failure. */ ZXC_EXPORT int64_t zxc_compress(const void* src, const size_t src_size, void* dst, const size_t dst_capacity, const zxc_compress_opts_t* opts); /** * @brief Decompresses a ZXC compressed buffer. * * This version uses standard size_t types and void pointers. * It executes in a single thread (blocking operation). * It expects a valid ZXC file header followed by compressed blocks. * * @param[in] src Pointer to the source buffer containing compressed data. * @param[in] src_size Size of the compressed data in bytes. * @param[out] dst Pointer to the destination buffer. * @param[in] dst_capacity Capacity of the destination buffer. * @param[in] opts Decompression options (NULL uses all defaults). * @c n_threads and the progress callback are ignored * (this call is single-threaded and blocking). * * @note @p src and @p dst must not overlap (same contract as memcpy). * * @return The number of bytes written to dst (>0 on success), * or a negative zxc_error_t code (e.g., ZXC_ERROR_CORRUPT_DATA) on failure. */ ZXC_EXPORT int64_t zxc_decompress(const void* src, const size_t src_size, void* dst, const size_t dst_capacity, const zxc_decompress_opts_t* opts); /** * @brief Minimum single-buffer size for an in-place decompression. * * Reads @p src's header and footer (no decoding) and returns the buffer size * @ref zxc_decompress_inplace needs: `decompressed_size` plus a one-block and * wild-copy safety margin. Allocate a buffer of this size, place the * `comp_size`-byte archive flush-right in it, then call * @ref zxc_decompress_inplace. * * @param[in] src Compressed archive (only header + footer are read). * @param[in] src_size Size of the archive in bytes. * @return Required buffer size in bytes, or 0 if @p src is not a valid archive. */ ZXC_EXPORT size_t zxc_decompress_inplace_bound(const void* src, const size_t src_size); /** * @brief Decompresses in place inside a single caller-owned buffer. * * The compressed archive must sit **flush-right** in @p buffer (its last * @p comp_size bytes). Decoding runs left-to-right into `buffer[0..]`; the * write cursor provably never overtakes the flush-right read cursor as long as * @p buffer_capacity is at least @ref zxc_decompress_inplace_bound. This * replaces the usual separate input and output buffers with one allocation: * decisive for memory-constrained targets (embedded, FOTA, firmware). * * @note @p buffer is both input and output; on success its first @c N bytes * hold the decompressed data (@c N = the return value). * * @param[in,out] buffer Single work buffer with the flush-right archive. * @param[in] buffer_capacity Total size of @p buffer in bytes. * @param[in] comp_size Size of the compressed archive in bytes. * @param[in] opts Decompression options, or NULL for defaults. * @return Decompressed size (>0), 0 for an empty frame, or a negative * @ref zxc_error_t (`ZXC_ERROR_DST_TOO_SMALL` if the margin is missing). */ ZXC_EXPORT int64_t zxc_decompress_inplace(void* buffer, const size_t buffer_capacity, const size_t comp_size, const zxc_decompress_opts_t* opts); /** * @brief Returns the decompressed size stored in a ZXC compressed buffer. * * This function reads the file footer to extract the original uncompressed size * without performing any decompression. Useful for allocating output buffers. * * The footer is untrusted input: the value is checked for plausibility against * the archive size (each block costs at least a block header and decodes to at * most one block), so a forged footer claiming an absurd size returns 0 rather * than driving an oversized allocation. * * @param[in] src Pointer to the compressed data buffer. * @param[in] src_size Size of the compressed data in bytes. * * @return The original uncompressed size in bytes, or 0 if the buffer is invalid, * too small to contain a valid ZXC archive, or carries an implausible * footer value. */ ZXC_EXPORT uint64_t zxc_get_decompressed_size(const void* src, const size_t src_size); /** * @brief Returns the dictionary ID stored in a ZXC compressed buffer. * * Reads the file header flag and dict_id field without decompressing. * Returns 0 if the file does not require a dictionary or the buffer is invalid. * * @param[in] src Pointer to the compressed data buffer. * @param[in] src_size Size of the compressed data in bytes. * @return Dictionary ID, or 0 if no dictionary is required. */ ZXC_EXPORT uint32_t zxc_get_dict_id(const void* src, size_t src_size); /* ========================================================================= */ /* Block-Level API (no file framing) */ /* ========================================================================= */ /** * @defgroup block_api Block API * @brief Single-block compression/decompression without file framing. * * These functions compress or decompress a single independent block, producing * only the block header (8 bytes) + compressed payload + optional checksum (4 bytes). * No file header, EOF block, or footer is written. * * This API is designed for filesystem integrations where the filesystem manages its own block * indexing and each block is compressed independently. * * @par Typical usage * @code * // Compress a single filesystem block * zxc_cctx* cctx = zxc_create_cctx(NULL); * zxc_compress_opts_t opts = { .level = 3 }; * size_t bound = zxc_compress_block_bound(block_size); * void *dst = malloc(bound); * int64_t csize = zxc_compress_block(cctx, block, block_size, dst, bound, &opts); * * // Decompress * zxc_dctx* dctx = zxc_create_dctx(); * int64_t dsize = zxc_decompress_block(dctx, dst, csize, out, block_size, NULL); * * zxc_free_cctx(cctx); * zxc_free_dctx(dctx); * @endcode * @{ */ /* Forward declarations for context types (defined below). */ /** @brief Opaque reusable compression context (see @ref zxc_create_cctx). */ typedef struct zxc_cctx_s zxc_cctx; /** @brief Opaque reusable decompression context (see @ref zxc_create_dctx). */ typedef struct zxc_dctx_s zxc_dctx; /** * @brief Returns the maximum compressed size for a single block. * * Unlike zxc_compress_bound(), this does NOT include file header, * EOF block, or footer overhead. Use this to size the destination * buffer for zxc_compress_block(). * * @param[in] input_size Size of the uncompressed block in bytes * (must be <= @ref ZXC_BLOCK_SIZE_MAX). * @return Upper bound on compressed block size, or 0 if @p input_size is * out of range for the Block API * (@p input_size > @ref ZXC_BLOCK_SIZE_MAX) or would overflow. */ ZXC_EXPORT uint64_t zxc_compress_block_bound(size_t input_size); /** * @brief Returns the minimum destination capacity required by * zxc_decompress_block() for a block of @p uncompressed_size bytes. * * The decoder uses speculative (wild-copy) writes on its fast path and * therefore needs a tail pad beyond the declared uncompressed size. * Passing exactly @c uncompressed_size as @c dst_capacity forces the slow * tail path and may trigger @ref ZXC_ERROR_OVERFLOW on some inputs. * * Use this helper to size the destination buffer. The returned value is * guaranteed to enable the fastest decode path without aliasing or * overrun checks tripping. * * @param[in] uncompressed_size Original uncompressed block size in bytes * (must be <= @ref ZXC_BLOCK_SIZE_MAX). * @return Minimum @c dst_capacity to pass to zxc_decompress_block(), or 0 if * @p uncompressed_size is out of range for the Block API * (@p uncompressed_size > @ref ZXC_BLOCK_SIZE_MAX) or would overflow. */ ZXC_EXPORT uint64_t zxc_decompress_block_bound(const size_t uncompressed_size); /** * @brief Compresses a single block without file framing. * * Output format: @c block_header(8B) + payload + optional @c checksum(4B). * The output can be decompressed with zxc_decompress_block(). * * The Block API processes a single format-conformant block at a time: * @p src_size must not exceed @ref ZXC_BLOCK_SIZE_MAX (2 MiB). For larger * payloads, use the frame API (zxc_compress) or the streaming API * (zxc_cstream_*), both of which chunk transparently into compliant blocks. * * @param[in,out] cctx Reusable compression context. * @param[in] src Source data. * @param[in] src_size Source data size in bytes * (must be in [1, @ref ZXC_BLOCK_SIZE_MAX]). * @param[out] dst Destination buffer. * @param[in] dst_capacity Capacity of the destination buffer * (use zxc_compress_block_bound() to size). * @param[in] opts Compression options, or NULL for defaults. * Only @c level, @c block_size, and * @c checksum_enabled are used. * * @note @p src and @p dst must not overlap (same contract as memcpy). * * @return Compressed block size in bytes (> 0) on success, * or a negative @ref zxc_error_t code on failure. * Returns @ref ZXC_ERROR_BAD_BLOCK_SIZE if * @p src_size > @ref ZXC_BLOCK_SIZE_MAX, and * @ref ZXC_ERROR_BAD_LEVEL on a static context for a level raise * the workspace cannot accommodate (levels above * @ref ZXC_LEVEL_ULTRA are otherwise silently clamped). */ ZXC_EXPORT int64_t zxc_compress_block(zxc_cctx* cctx, const void* src, size_t src_size, void* dst, size_t dst_capacity, const zxc_compress_opts_t* opts); /** * @brief Decompresses a single block produced by zxc_compress_block(). * * The Block API decompresses a single format-conformant block at a time: * @p dst_capacity must not exceed @ref ZXC_BLOCK_SIZE_MAX + * @ref ZXC_DECOMPRESS_TAIL_PAD (the size returned by * zxc_decompress_block_bound() for the maximum block size). For payloads * produced by the frame or streaming APIs, use zxc_decompress instead. * * @param[in,out] dctx Reusable decompression context. * @param[in] src Compressed block data. * @param[in] src_size Compressed data size in bytes. * @param[out] dst Destination buffer for decompressed data. * @param[in] dst_capacity Capacity of the destination buffer (must be * at least the original uncompressed size, * and at most @ref ZXC_BLOCK_SIZE_MAX + * @ref ZXC_DECOMPRESS_TAIL_PAD). * @param[in] opts Decompression options (NULL for defaults). * Only @c checksum_enabled is used. * * @note @p src and @p dst must not overlap (same contract as memcpy). * * @return Decompressed size in bytes (> 0) on success, * or a negative @ref zxc_error_t code on failure. * Returns @ref ZXC_ERROR_BAD_BLOCK_SIZE if @p dst_capacity exceeds * the per-block limit. */ ZXC_EXPORT int64_t zxc_decompress_block(zxc_dctx* dctx, const void* src, size_t src_size, void* dst, size_t dst_capacity, const zxc_decompress_opts_t* opts); /** * @brief Decompresses a single block with a strict-sized destination buffer. * * Identical semantics to zxc_decompress_block() but accepts * @p dst_capacity == @c uncompressed_size (no trailing @c ZXC_DECOMPRESS_TAIL_PAD * required). Intended for integrations whose destination buffer cannot be * oversized (for example, decoding into an exactly-sized, page-aligned * region). Here "in-place" means a tightly-sized destination, not an * overlapping @p src / @p dst (see @note below). * * This path is slightly slower than zxc_decompress_block() on the same input * because it avoids the wild-copy overshoot that the fast decoder relies on. * Output is bit-identical to zxc_decompress_block(). * * RAW blocks transparently forward to zxc_decompress_block(); only * GLO/GHI use the strict-tail decoder path. * * Strict-tail variant: @p dst_capacity is the exact uncompressed size with * no tail-pad margin, so the upper limit is @ref ZXC_BLOCK_SIZE_MAX (not * @c MAX+TAIL_PAD as for zxc_decompress_block). * * @param[in,out] dctx Reusable decompression context. * @param[in] src Compressed block data. * @param[in] src_size Compressed data size in bytes. * @param[out] dst Destination buffer for decompressed data. * @param[in] dst_capacity Capacity of the destination buffer (must be * at least the original uncompressed size, * and at most @ref ZXC_BLOCK_SIZE_MAX; unlike * zxc_decompress_block, no trailing tail-pad * margin is required). * @param[in] opts Decompression options (NULL for defaults). * Only @c checksum_enabled is used. * * @note @p src and @p dst must not overlap (same contract as memcpy). * * @return Decompressed size in bytes (> 0) on success, * or a negative @ref zxc_error_t code on failure. * Returns @ref ZXC_ERROR_BAD_BLOCK_SIZE if * @p dst_capacity > @ref ZXC_BLOCK_SIZE_MAX. */ ZXC_EXPORT int64_t zxc_decompress_block_safe(zxc_dctx* dctx, const void* src, const size_t src_size, void* dst, const size_t dst_capacity, const zxc_decompress_opts_t* opts); /** * @brief Estimates the peak memory used by compression for a given block & level. * * Returns the total bytes reserved by @ref zxc_compress_block for a block of * @p src_size bytes: all per-chunk working buffers (chain table, literals, * sequence/token/offset/extras buffers) plus the fixed hash tables and * cache-line alignment padding. At @p level >= 6 the value also includes the * `opt_scratch` region (~8.125 x @p src_size bytes) used by the price-based * optimal parser. That region is lazy-allocated on the first level-6 call * and reused across blocks for the lifetime of the cctx. Scales roughly * linearly with @p src_size. * * Intended for integrators that need an accurate memory-budget figure. * * @param[in] src_size Uncompressed block size in bytes. * @param[in] level Compression level (1..7). Levels <= 5 share the same * persistent cctx footprint; levels >= 6 add the optimal- * parser scratch. * @return Estimated peak cctx memory usage in bytes, or 0 if @p src_size is 0. */ ZXC_EXPORT uint64_t zxc_estimate_cctx_size(size_t src_size, int level); /** @} */ /* end of block_api */ /* ========================================================================= */ /* Reusable Context API (opaque, heap-allocated) */ /* ========================================================================= */ /** * @defgroup context_api Reusable Context API * @brief Opaque, reusable compression / decompression contexts. * * This API eliminates per-call allocation overhead by letting callers retain * a context across multiple operations. The internal layout is hidden behind * an opaque pointer. * * @{ */ /* --- Compression context ------------------------------------------------- */ /** * @brief Creates a new reusable compression context. * * When @p opts is non-NULL the context pre-allocates all internal buffers * using the supplied level, block_size, and checksum_enabled settings. * When @p opts is NULL, allocation is deferred to the first call to * zxc_compress_cctx(). * * The returned context must be freed with zxc_free_cctx(). * * Levels above @ref ZXC_LEVEL_ULTRA are silently clamped to * @ref ZXC_LEVEL_ULTRA. * * @param[in] opts Compression options for eager init, or NULL for lazy init. * @return Pointer to the new context, or @c NULL on allocation failure or an * invalid block size (not a power of two in range). */ ZXC_EXPORT zxc_cctx* zxc_create_cctx(const zxc_compress_opts_t* opts); /** * @brief Frees a compression context and all associated resources. * * It is safe to pass @c NULL; the call is a no-op in that case. * * @param[in] cctx Context to free. */ ZXC_EXPORT void zxc_free_cctx(zxc_cctx* cctx); /** * @brief Compresses data using a reusable context. * * Identical to zxc_compress() but reuses the internal buffers from @p cctx, * avoiding per-call malloc/free overhead. The context automatically * re-initializes when block_size changes between calls, or when a level * raise into @ref ZXC_LEVEL_DENSITY requires the optimal-parser scratch * that lower-level inits do not allocate. On a static (caller-workspace) * context such a raise is rejected with @ref ZXC_ERROR_BAD_LEVEL instead, * since the workspace cannot grow. * * Options are **sticky**: settings passed via @p opts are remembered and * reused on subsequent calls where @p opts is NULL. The initial sticky * values come from the @p opts passed to zxc_create_cctx(). Levels above * @ref ZXC_LEVEL_ULTRA are silently clamped. * * @param[in,out] cctx Reusable compression context. * @param[in] src Source data. * @param[in] src_size Source data size in bytes. * @param[out] dst Destination buffer. * @param[in] dst_capacity Capacity of the destination buffer. * @param[in] opts Compression options, or NULL to reuse * settings from create / last call. * * @note @p src and @p dst must not overlap (same contract as memcpy). * * @return Compressed size in bytes (> 0) on success, * or a negative @ref zxc_error_t code on failure. */ ZXC_EXPORT int64_t zxc_compress_cctx(zxc_cctx* cctx, const void* src, size_t src_size, void* dst, size_t dst_capacity, const zxc_compress_opts_t* opts); /* --- Decompression context ----------------------------------------------- */ /** * @brief Creates a new reusable decompression context. * * @return Pointer to the new context, or @c NULL on allocation failure. */ ZXC_EXPORT zxc_dctx* zxc_create_dctx(void); /** * @brief Frees a decompression context and all associated resources. * * It is safe to pass @c NULL. * * @param[in] dctx Context to free. */ ZXC_EXPORT void zxc_free_dctx(zxc_dctx* dctx); /** * @brief Decompresses data using a reusable context. * * Identical to zxc_decompress() but reuses buffers from @p dctx. * * @param[in,out] dctx Reusable decompression context. * @param[in] src Compressed data. * @param[in] src_size Compressed data size in bytes. * @param[out] dst Destination buffer. * @param[in] dst_capacity Capacity of the destination buffer. * @param[in] opts Decompression options (NULL for defaults). * * @note @p src and @p dst must not overlap (same contract as memcpy). * * @return Decompressed size in bytes (> 0) on success, * or a negative @ref zxc_error_t code on failure. */ ZXC_EXPORT int64_t zxc_decompress_dctx(zxc_dctx* dctx, const void* src, size_t src_size, void* dst, size_t dst_capacity, const zxc_decompress_opts_t* opts); /* ========================================================================= */ /* Static Context API (caller-allocated workspace) */ /* ========================================================================= */ /** * @defgroup static_context_api Static Context API * @brief Caller-allocated, fixed-footprint compression / decompression * contexts. * * Mirrors the dynamic Reusable Context API but places the entire context * (handle + persistent buffers) inside a single buffer allocated and owned * by the caller. This pattern is mandatory for environments where the * library cannot call into the host allocator on the hot path: Linux * kernel filesystems (one workspace per mount, served via @c vmalloc / * @c kmalloc up front), embedded targets without a heap (`.bss` or * stack-allocated workspace), sandboxed runtimes with a fixed memory * budget, etc. * * The trade-off vs the dynamic API: the workspace is pinned to a single @c block_size and @c level * at init time; subsequent compress/decompress calls cannot enlarge the footprint, so a workload * that needs to mix block sizes must size the workspace for the maximum block_size up front. * * @par Typical usage * @code * size_t ws_sz = zxc_static_cctx_workspace_size(64 * 1024, ZXC_LEVEL_DEFAULT); * void *ws = aligned_alloc(64, ws_sz); // or kmalloc, vmalloc, .bss * zxc_compress_opts_t opts = { .level = ZXC_LEVEL_DEFAULT, .block_size = 64 * 1024 }; * zxc_cctx *cctx = zxc_init_static_cctx(ws, ws_sz, &opts); * * for (each block) zxc_compress_cctx(cctx, src, n, dst, cap, NULL); * * // zxc_free_cctx is a no-op on a static cctx; the caller owns @c ws. * free(ws); * @endcode * @{ */ /** * @brief Returns the exact byte count required by a static compression * workspace for the given @p block_size and @p level. * * The value is the sum of the opaque @ref zxc_cctx wrapper plus every * persistent sub-buffer the library would partition (hash tables, chain * table, sequence buffers, literal scratch, plus the optimal-parser * scratch at @ref ZXC_LEVEL_DENSITY). Round up to your allocator's * alignment before calling @c posix_memalign / @c aligned_alloc, the * workspace must be at least cache-line aligned. * * @param[in] block_size Block size in bytes (must satisfy the regular * block-size constraints: power of two in * [@ref ZXC_BLOCK_SIZE_MIN, @ref ZXC_BLOCK_SIZE_MAX]). * @param[in] level Compression level (1..7); levels at or above * @ref ZXC_LEVEL_DENSITY add the optimal-parser * scratch (~8.125 x block_size). * @return Workspace size in bytes, or 0 if either argument is invalid. */ ZXC_EXPORT size_t zxc_static_cctx_workspace_size(const size_t block_size, const int level); /** * @brief Initialises a compression context inside a caller-supplied * workspace. * * @p workspace_size must be at least @ref zxc_static_cctx_workspace_size * for the same @c block_size and @c level. The workspace must remain * valid for the lifetime of the returned handle and must be cache-line * (64-byte) aligned. The caller owns the workspace; @ref zxc_free_cctx * is a no-op on the returned handle. * * @par Locked parameters * The @c block_size, @c level, and @c checksum_enabled fields of @p opts * are pinned at init time. Subsequent @ref zxc_compress_cctx calls that * pass options requesting a different @c block_size return * @ref ZXC_ERROR_BAD_BLOCK_SIZE without re-initialising. A different * @c level / @c checksum_enabled is honoured per-call without * re-partitioning, except a raise into @ref ZXC_LEVEL_DENSITY on a * workspace carved below it: that would require the optimal-parser * scratch the workspace does not carry, so the call returns * @ref ZXC_ERROR_BAD_LEVEL. * * @param[in,out] workspace Caller-allocated buffer, cache-line aligned. * @param[in] workspace_size Capacity of @p workspace in bytes. * @param[in] opts Must be non-NULL: @c block_size and * @c level must be set explicitly to size * the workspace correctly. * @return Handle pointing inside @p workspace on success, or @c NULL if * the workspace is too small or the options are invalid. */ ZXC_EXPORT zxc_cctx* zxc_init_static_cctx(void* workspace, const size_t workspace_size, const zxc_compress_opts_t* opts); /** * @brief Returns the exact byte count required by a static decompression * workspace for the given @p block_size. * * Unlike the compression variant, this size is independent of the source * archive's level: @c lit_buffer is always provisioned worst-case because * the decoder cannot predict the per-block literal encoding until it sees * each block header. * * @param[in] block_size Maximum block size the decoder will encounter * (must satisfy the regular block-size constraints). * @return Workspace size in bytes, or 0 if @p block_size is invalid. */ ZXC_EXPORT size_t zxc_static_dctx_workspace_size(const size_t block_size); /** * @brief Initialises a decompression context inside a caller-supplied * workspace. * * @p workspace_size must be at least @ref zxc_static_dctx_workspace_size * for the same @p block_size. The workspace must remain valid for the * lifetime of the returned handle and must be cache-line aligned. The * caller owns the workspace; @ref zxc_free_dctx is a no-op on the * returned handle. * * @par Locked block size * @p block_size is pinned at init time: feeding the returned handle an * archive whose header declares a different @c block_size returns * @ref ZXC_ERROR_BAD_BLOCK_SIZE. * * @param[in,out] workspace Caller-allocated buffer, cache-line aligned. * @param[in] workspace_size Capacity of @p workspace in bytes. * @param[in] block_size Block size the decoder will accept. * @return Handle pointing inside @p workspace on success, or @c NULL if * the workspace is too small or @p block_size is invalid. */ ZXC_EXPORT zxc_dctx* zxc_init_static_dctx(void* workspace, const size_t workspace_size, const size_t block_size); /** @} */ /* end of static_context_api */ /** @} */ /* end of context_api */ /** @} */ /* end of buffer_api */ #ifdef __cplusplus } #endif #endif // ZXC_BUFFER_Hzxc-0.13.0/include/zxc_constants.h000066400000000000000000000112201522536370400170760ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file zxc_constants.h * @brief Public constants: library version and compression levels. * * Include this header to query the library version at compile time or to * reference the predefined compression-level constants used throughout the API. */ #ifndef ZXC_CONSTANTS_H #define ZXC_CONSTANTS_H /** * @defgroup version Library Version * @brief Compile-time version information. * @{ */ /** @brief Major version number. */ #define ZXC_VERSION_MAJOR 0 /** @brief Minor version number. */ #define ZXC_VERSION_MINOR 13 /** @brief Patch version number. */ #define ZXC_VERSION_PATCH 0 /** @cond INTERNAL */ #define ZXC_STR_HELPER(x) #x #define ZXC_STR(x) ZXC_STR_HELPER(x) /** @endcond */ /** * @brief Human-readable version string in "MAJOR.MINOR.PATCH" form (e.g. "0.13.0"). */ #define ZXC_LIB_VERSION_STR \ ZXC_STR(ZXC_VERSION_MAJOR) \ "." ZXC_STR(ZXC_VERSION_MINOR) "." ZXC_STR(ZXC_VERSION_PATCH) /** @} */ /* end of version */ /** * @defgroup block_size Block Size * @brief Block size constraints for compression. * * Block size must be a power of two in range * [@ref ZXC_BLOCK_SIZE_MIN, @ref ZXC_BLOCK_SIZE_MAX]. * Pass 0 to any API to use @ref ZXC_BLOCK_SIZE_DEFAULT. * @{ */ /** @brief log2(ZXC_BLOCK_SIZE_MIN) - exponent code for minimum block size. */ #define ZXC_BLOCK_SIZE_MIN_LOG2 12 /** @brief log2(ZXC_BLOCK_SIZE_MAX) - exponent code for maximum block size. */ #define ZXC_BLOCK_SIZE_MAX_LOG2 21 /** @brief Default block size (512 KB). */ #define ZXC_BLOCK_SIZE_DEFAULT (512 * 1024) /** @brief Minimum allowed block size (4 KB = 2^12). */ #define ZXC_BLOCK_SIZE_MIN (1U << ZXC_BLOCK_SIZE_MIN_LOG2) /** @brief Maximum allowed block size (2 MB = 2^21). */ #define ZXC_BLOCK_SIZE_MAX (1U << ZXC_BLOCK_SIZE_MAX_LOG2) /** @} */ /* end of block_size */ /** * @defgroup dictionary Dictionary * @brief Constants for pre-trained dictionary support. * @{ */ /** @brief Maximum dictionary content size in bytes (64 KB - 1). * * Bounded to a 16-bit value (65535) by two constraints that both cap at the * same number: the `.zxd` header stores the content size in a 16-bit field, and * LZ77 match offsets are 16-bit (max distance 65535). */ #define ZXC_DICT_SIZE_MAX ((1U << 16) - 1U) /** @brief Size of the .zxd dictionary file header in bytes. */ #define ZXC_DICT_HEADER_SIZE 16 /** @brief Size in bytes of a packed literal Huffman code-lengths table * (256 symbols, 4 bits each): the shared table carried by a .zxd * file and, internally, the per-block lengths header. See * zxc_train_dict_huf() / zxc_dict_huf(). */ #define ZXC_HUF_TABLE_SIZE 128 /** @} */ /* end of dictionary */ /** * @defgroup threading Threading Limits * @brief Bounds on thread-count parameters accepted by the streaming APIs. * @{ */ /** @brief Maximum value accepted for `n_threads` in `zxc_stream_compress` * / `zxc_stream_decompress`. Higher values are clamped to `ZXC_MAX_THREADS`. */ #define ZXC_MAX_THREADS 512 /** @} */ /* end of threading */ /** * @defgroup file_format File Format Constants * @brief On-disk byte sizes of the archive header and footer. * * @{ */ /** @brief File header size: Magic(4) + Version(1) + Chunk(1) + Flags(1) + Reserved(7) + CRC(2). */ #define ZXC_FILE_HEADER_SIZE 16 /** @brief File footer size: original_size(8) + global_checksum(4). */ #define ZXC_FILE_FOOTER_SIZE 12 /** @} */ /* end of file_format */ /** * @defgroup levels Compression Levels * @brief Predefined compression levels for the ZXC library. * * Higher levels trade encoding speed for better compression ratio. * All levels produce data that can be decompressed at the same speed. * @{ */ /** * @brief Enumeration of ZXC compression levels. * * Use one of these constants as the @p level parameter of * zxc_compress() or zxc_stream_compress(). */ typedef enum { ZXC_LEVEL_FASTEST = 1, /**< Fastest compression; lowest ratio. Best for real-time use. */ ZXC_LEVEL_FAST = 2, /**< Fast compression; slightly better ratio than FASTEST. */ ZXC_LEVEL_DEFAULT = 3, /**< Recommended default: better ratio and decode speed than LZ4. */ ZXC_LEVEL_BALANCED = 4, /**< Balanced trade-off between ratio and decode speed. */ ZXC_LEVEL_COMPACT = 5, /**< Denser encoding. Best for storage, firmware, and assets. */ ZXC_LEVEL_DENSITY = 6, /**< Higher density: adds Huffman-coded literals on top of COMPACT. */ ZXC_LEVEL_ULTRA = 7 /**< Maximum density: Huffman-coded literals and sequence tokens. */ } zxc_compression_level_t; /** @} */ /* end of levels */ #endif // ZXC_CONSTANTS_H zxc-0.13.0/include/zxc_dict.h000066400000000000000000000226261522536370400160210ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file zxc_dict.h * @brief Pre-trained dictionary API for ZXC compression. * * Provides functions to train, save, load, and identify dictionaries that * improve compression ratio on small, similar payloads. Dictionaries are * stored as external `.zxd` files and referenced by a 32-bit ID in the * ZXC file header. * * A dictionary contains raw byte content that prefills the LZ77 sliding * window at the start of each block, giving the compressor immediate * access to representative patterns without waiting for them to appear * in the input stream. * * @code * // Train a dictionary from a corpus of JSON samples * void* dict_buf = malloc(32768); * int64_t dict_sz = zxc_train_dict(samples, sizes, n, dict_buf, 32768); * * // Train the shared literal Huffman table on the same corpus * uint8_t huf[ZXC_HUF_TABLE_SIZE]; * zxc_train_dict_huf(samples, sizes, n, dict_buf, dict_sz, huf); * * // Save to .zxd file (content + table) * void* zxd = malloc(zxc_dict_save_bound(dict_sz)); * int64_t zxd_sz = zxc_dict_save(dict_buf, dict_sz, huf, zxd, ...); * * // Use for compression * zxc_compress_opts_t opts = { * .level = 6, .dict = dict_buf, .dict_size = dict_sz, .dict_huf = huf }; * zxc_compress(src, src_size, dst, dst_capacity, &opts); * @endcode */ #ifndef ZXC_DICT_H #define ZXC_DICT_H #include #include #include "zxc_export.h" #ifdef __cplusplus extern "C" { #endif /** * @defgroup dict Dictionary * @brief Pre-trained dictionary training, serialization, and identification. * @{ */ /** * @brief Compute the dictionary ID for the given content and optional table. * * The ID is a deterministic 32-bit hash stored in the ZXC file header so the * decoder can verify that the correct dictionary is provided at decompression * time. With @p huf_lengths NULL it hashes the raw content only (the in-memory * content-only dictionary of the buffer API). With a table it binds the * (content, table) pair: `hash(table, seed = hash(content))` -- the value * stored in `.zxd` files and in archives compressed with a shared table. * * @param[in] dict Pointer to dictionary content. * @param[in] dict_size Size in bytes. * @param[in] huf_lengths Shared literal Huffman table (@ref ZXC_HUF_TABLE_SIZE * bytes), or NULL for a content-only ID. * @return 32-bit dictionary ID. Returns 0 if @p dict is NULL or @p dict_size is 0. */ ZXC_EXPORT uint32_t zxc_dict_id(const void* dict, size_t dict_size, const void* huf_lengths); /** * @brief Load and validate a `.zxd` dictionary file from a memory buffer. * * On success, @p content_out and @p huf_out (when non-NULL) point into the * input buffer (zero-copy); the caller must keep @p buf alive while they are in * use. A single call yields everything needed to (de)compress with the * dictionary, pass @p content_out / @p huf_out straight to the @c dict / * @c dict_huf option fields. * * @param[in] buf Buffer containing the .zxd file. * @param[in] buf_size Size of @p buf in bytes. * @param[out] content_out Receives a pointer to the dictionary content. * @param[out] content_size_out Receives the content size in bytes. * @param[out] huf_out Receives a pointer to the 128-byte shared Huffman * table (may be NULL if not needed). * @param[out] dict_id_out Receives the dictionary ID (may be NULL). * @return @ref ZXC_OK on success, or a negative @ref zxc_error_t code. */ ZXC_EXPORT int zxc_dict_load(const void* buf, size_t buf_size, const void** content_out, size_t* content_size_out, const void** huf_out, uint32_t* dict_id_out); /** * @brief Serialize dictionary content and its shared Huffman table to the * `.zxd` file format. * * The 128-byte packed code-lengths table (from zxc_train_dict_huf()) is * mandatory and is appended after the content. The stored dict_id covers both * content and table, so archives compressed with this dictionary are bound to * the exact pair. * * @param[in] content Raw dictionary content. * @param[in] content_size Size of @p content in bytes (max ZXC_DICT_SIZE_MAX). * @param[in] huf_lengths 128-byte packed Huffman code lengths (required). * @param[out] buf Output buffer for the .zxd file. * @param[in] buf_capacity Capacity of @p buf (see zxc_dict_save_bound()). * @return Number of bytes written on success, or a negative @ref zxc_error_t code. */ ZXC_EXPORT int64_t zxc_dict_save(const void* content, size_t content_size, const void* huf_lengths, void* buf, size_t buf_capacity); /** * @brief Returns the maximum .zxd file size for a given content size. * * @param[in] content_size Size of the dictionary content. * @return Total .zxd file size (header + content). */ ZXC_EXPORT size_t zxc_dict_save_bound(size_t content_size); /** * @brief Returns the dictionary ID stored in a `.zxd` file buffer. * * Reads the dict_id field from the .zxd header without validating the full * file. Returns 0 if the buffer is too small or the magic word doesn't match. * * @param[in] buf Buffer containing the .zxd file. * @param[in] buf_size Size of @p buf in bytes. * @return Dictionary ID, or 0 if the buffer is not a valid .zxd file. */ ZXC_EXPORT uint32_t zxc_dict_get_id(const void* buf, size_t buf_size); /** * @brief Train a dictionary from a corpus of samples. * * Analyzes the samples to select byte sequences that maximize LZ77 match * coverage. The resulting dictionary content can be passed directly to * zxc_compress_opts_t::dict or serialized with zxc_dict_save(). * * @param[in] samples Array of pointers to sample buffers. * @param[in] sample_sizes Array of sample sizes in bytes. * @param[in] n_samples Number of samples. * @param[out] dict_buf Output buffer for trained dictionary content. * @param[in] dict_capacity Capacity of @p dict_buf (max ZXC_DICT_SIZE_MAX). * @return Size of the trained dictionary on success, or a negative * @ref zxc_error_t code. */ ZXC_EXPORT int64_t zxc_train_dict(const void* const* samples, const size_t* sample_sizes, size_t n_samples, void* dict_buf, size_t dict_capacity); /** * @brief Train the shared literal Huffman table for an already-trained dictionary. * * Compresses the samples with @p dict and builds canonical Huffman code * lengths from the real post-LZ literal distribution. The resulting 128-byte * packed table can be embedded in a `.zxd` file via zxc_dict_save() and * passed to the compressor/decompressor via the `dict_huf` option field. * Blocks whose literals compress better with the shared table skip their * per-block 128-byte table header, which is decisive at small block sizes. * * @param[in] samples Array of pointers to sample buffers (typically * the same corpus used for zxc_train_dict()). * @param[in] sample_sizes Array of sample sizes in bytes. * @param[in] n_samples Number of samples. * @param[in] dict Trained dictionary content. * @param[in] dict_size Dictionary content size in bytes. * @param[out] huf_lengths_out Receives the 128-byte packed code-lengths table. * @return @ref ZXC_OK on success, or a negative @ref zxc_error_t code. */ ZXC_EXPORT int zxc_train_dict_huf(const void* const* samples, const size_t* sample_sizes, size_t n_samples, const void* dict, size_t dict_size, uint8_t* huf_lengths_out); /** * @brief One-call dictionary creation: train content + shared table, serialize * to ready-to-write `.zxd` bytes. * * Convenience over the train/train-table/save sequence: it runs * zxc_train_dict() then zxc_train_dict_huf() (which depends on the trained * content) then zxc_dict_save(), writing a complete `.zxd` into @p zxd_buf. * Use zxc_dict_save_bound(ZXC_DICT_SIZE_MAX) for a safe @p zxd_capacity, or * size to the dictionary you expect. The lower-level primitives remain * available for advanced use (raw content-only dictionaries, retraining only * the table, or supplying externally-sourced content). * * @param[in] samples Array of pointers to sample buffers. * @param[in] sample_sizes Array of sample sizes in bytes. * @param[in] n_samples Number of samples. * @param[out] zxd_buf Output buffer for the `.zxd` file. * @param[in] zxd_capacity Capacity of @p zxd_buf. * @return Number of `.zxd` bytes written on success, or a negative * @ref zxc_error_t code. */ ZXC_EXPORT int64_t zxc_dict_train(const void* const* samples, const size_t* sample_sizes, size_t n_samples, void* zxd_buf, size_t zxd_capacity); /** * @brief Returns a pointer to the shared Huffman table inside a `.zxd` buffer. * * Zero-copy accessor: the returned pointer aims into @p buf and is valid as * long as @p buf is. Returns NULL if the buffer is not a valid `.zxd` file or * carries no table. * * @param[in] buf Buffer containing the .zxd file. * @param[in] buf_size Size of @p buf in bytes. * @return Pointer to the 128-byte packed code-lengths table, or NULL. */ ZXC_EXPORT const void* zxc_dict_huf(const void* buf, size_t buf_size); /** @} */ /* end of dict */ #ifdef __cplusplus } #endif #endif /* ZXC_DICT_H */ zxc-0.13.0/include/zxc_error.h000066400000000000000000000060711522536370400162230ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file zxc_error.h * @brief Error codes and error-name lookup for the ZXC library. * * Every public function that can fail returns a value from @ref zxc_error_t. * A return value < 0 indicates an error; use zxc_error_name() to convert * any code to a human-readable string. */ #ifndef ZXC_ERROR_H #define ZXC_ERROR_H #include "zxc_export.h" #ifdef __cplusplus extern "C" { #endif /** * @defgroup error Error Handling * @brief Error codes returned by ZXC library functions. * @{ */ /** * @brief Error codes returned by ZXC library functions. * * All error codes are negative integers. Functions that return int or int64_t * will return these codes on failure. Check with `result < 0` for errors. * * Use zxc_error_name() to get a human-readable string for any error code. */ typedef enum { ZXC_OK = 0, /**< Success (no error). */ /* Memory errors */ ZXC_ERROR_MEMORY = -1, /**< Memory allocation failure. */ /* Buffer/capacity errors */ ZXC_ERROR_DST_TOO_SMALL = -2, /**< Destination buffer too small. */ ZXC_ERROR_SRC_TOO_SMALL = -3, /**< Source buffer too small or truncated input. */ /* Format/header errors */ ZXC_ERROR_BAD_MAGIC = -4, /**< Invalid magic word in file header. */ ZXC_ERROR_BAD_VERSION = -5, /**< Unsupported file format version. */ ZXC_ERROR_BAD_HEADER = -6, /**< Corrupted or invalid header (CRC mismatch). */ ZXC_ERROR_BAD_CHECKSUM = -7, /**< Block or global checksum verification failed. */ /* Data integrity errors */ ZXC_ERROR_CORRUPT_DATA = -8, /**< Corrupted compressed data. */ ZXC_ERROR_BAD_OFFSET = -9, /**< Invalid match offset during decompression. */ ZXC_ERROR_OVERFLOW = -10, /**< Buffer overflow detected during processing. */ /* I/O errors */ ZXC_ERROR_IO = -11, /**< Read/write/seek failure on file. */ ZXC_ERROR_NULL_INPUT = -12, /**< Required input pointer is NULL. */ /* Block errors */ ZXC_ERROR_BAD_BLOCK_TYPE = -13, /**< Unknown or unexpected block type. */ ZXC_ERROR_BAD_BLOCK_SIZE = -14, /**< Invalid block size. */ /* Dictionary errors */ ZXC_ERROR_DICT_REQUIRED = -15, /**< File requires a dictionary but none was provided. */ ZXC_ERROR_DICT_MISMATCH = -16, /**< Provided dictionary ID does not match the file header. */ ZXC_ERROR_DICT_TOO_LARGE = -17, /**< Dictionary exceeds maximum allowed size. */ /* Parameter errors */ ZXC_ERROR_BAD_LEVEL = -18, /**< Compression level not supported by this context's workspace. */ } zxc_error_t; /** * @brief Returns a human-readable name for the given error code. * * @param[in] code An error code from zxc_error_t (or any integer). * @return A constant string such as "ZXC_OK" or "ZXC_ERROR_MEMORY". * Returns "ZXC_UNKNOWN_ERROR" for unrecognized codes. */ ZXC_EXPORT const char* zxc_error_name(const int code); /** @} */ /* end of error */ #ifdef __cplusplus } #endif #endif /* ZXC_ERROR_H */ zxc-0.13.0/include/zxc_export.h000066400000000000000000000057111522536370400164130ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file zxc_export.h * @brief Platform-specific symbol visibility macros. * * This header defines the `ZXC_EXPORT`, `ZXC_NO_EXPORT`, and `ZXC_DEPRECATED` * macros that control which symbols are exported from the shared library. * * - Define @c ZXC_STATIC_DEFINE when building or consuming ZXC as a **static** * library to disable import/export annotations. * - When building the shared library the CMake target defines * @c zxc_lib_EXPORTS automatically, selecting `dllexport` / `visibility("default")`. * - When consuming the shared library neither macro is defined, so the header * selects `dllimport` / `visibility("default")`. */ #ifndef ZXC_EXPORT_H #define ZXC_EXPORT_H /** * @defgroup export Symbol Visibility * @brief Macros controlling DLL export/import and deprecation attributes. * @{ */ #ifdef ZXC_STATIC_DEFINE /** * @def ZXC_EXPORT * @brief Marks a symbol as part of the public shared-library API. * * Expands to nothing when building a static library (@c ZXC_STATIC_DEFINE), * to `__declspec(dllexport)` or `__declspec(dllimport)` on Windows, or * to `__attribute__((visibility("default")))` on GCC/Clang. */ #define ZXC_EXPORT /** * @def ZXC_NO_EXPORT * @brief Marks a symbol as hidden (not exported from the shared library). * * Expands to nothing for static builds or Windows, and to * `__attribute__((visibility("hidden")))` on GCC/Clang. */ #define ZXC_NO_EXPORT #else /* shared library */ #ifndef ZXC_EXPORT #ifdef zxc_lib_EXPORTS /* Building the library */ #ifdef _WIN32 #define ZXC_EXPORT __declspec(dllexport) #else #define ZXC_EXPORT __attribute__((visibility("default"))) #endif #else /* Consuming the library */ #ifdef _WIN32 #define ZXC_EXPORT __declspec(dllimport) #else #define ZXC_EXPORT __attribute__((visibility("default"))) #endif #endif #endif #ifndef ZXC_NO_EXPORT #ifdef _WIN32 #define ZXC_NO_EXPORT #else #define ZXC_NO_EXPORT __attribute__((visibility("hidden"))) #endif #endif #endif /* ZXC_STATIC_DEFINE */ #ifndef ZXC_DEPRECATED /** * @def ZXC_DEPRECATED * @brief Marks a symbol as deprecated. * * The compiler will emit a warning when a deprecated symbol is referenced. * Expands to `__declspec(deprecated)` on MSVC or * `__attribute__((__deprecated__))` on GCC/Clang. */ #ifdef _WIN32 #define ZXC_DEPRECATED __declspec(deprecated) #else #define ZXC_DEPRECATED __attribute__((__deprecated__)) #endif #endif /** * @def ZXC_DEPRECATED_EXPORT * @brief Combines `ZXC_EXPORT` with `ZXC_DEPRECATED`. */ #ifndef ZXC_DEPRECATED_EXPORT #define ZXC_DEPRECATED_EXPORT ZXC_EXPORT ZXC_DEPRECATED #endif /** * @def ZXC_DEPRECATED_NO_EXPORT * @brief Combines `ZXC_NO_EXPORT` with `ZXC_DEPRECATED`. */ #ifndef ZXC_DEPRECATED_NO_EXPORT #define ZXC_DEPRECATED_NO_EXPORT ZXC_NO_EXPORT ZXC_DEPRECATED #endif /** @} */ /* end of export */ #endif /* ZXC_EXPORT_H */ zxc-0.13.0/include/zxc_opts.h000066400000000000000000000113651522536370400160610ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file zxc_opts.h * @brief Shared option structures for the ZXC compression APIs. * * Defines @ref zxc_compress_opts_t, @ref zxc_decompress_opts_t and * @ref zxc_progress_callback_t. These types are consumed by every public * ZXC API (one-shot buffer, multi-threaded @c FILE* streaming, push * streaming, seekable). * * This header is never used in isolation: include the API header you * actually use (@c zxc_buffer.h, @c zxc_stream.h, @c zxc_pstream.h, ...) * which pulls this one in transitively. */ #ifndef ZXC_OPTS_H #define ZXC_OPTS_H #include #include #include "zxc_export.h" #ifdef __cplusplus extern "C" { #endif /** * @brief Progress callback function type. * * This callback is invoked periodically during compression/decompression to report * progress. It is called from the writer thread after each block is processed. * * @param[in] bytes_processed Total input bytes processed so far. * @param[in] bytes_total Total input bytes to process (0 if unknown, e.g., stdin). * @param[in] user_data User-provided context pointer (passed through from API call). * * @note The callback should be fast and non-blocking. Avoid heavy I/O or mutex locks. */ typedef void (*zxc_progress_callback_t)(uint64_t bytes_processed, uint64_t bytes_total, const void* user_data); /** * @brief Options for streaming compression. * * Zero-initialise for safe defaults: level 0 maps to ZXC_LEVEL_DEFAULT (3), * block_size 0 maps to ZXC_BLOCK_SIZE_DEFAULT, n_threads 0 means * auto-detect, and all other fields are disabled. * * @code * zxc_compress_opts_t opts = { .level = ZXC_LEVEL_COMPACT }; * zxc_stream_compress(f_in, f_out, &opts); * @endcode */ typedef struct { int n_threads; /**< Worker thread count (0 = auto-detect CPU cores). */ int level; /**< Compression level 1-7 (0 = default, ZXC_LEVEL_DEFAULT). */ size_t block_size; /**< Block size in bytes (0 = default ZXC_BLOCK_SIZE_DEFAULT). Must be power of 2, [4KB - 2MB]. */ int checksum_enabled; /**< 1 to enable per-block and global checksums, 0 to disable. */ int seekable; /**< 1 to append a seek table for random-access decompression. */ const void* dict; /**< Pre-trained dictionary content (NULL = none). */ size_t dict_size; /**< Dictionary size in bytes (0 = none, max ZXC_DICT_SIZE_MAX). */ const void* dict_huf; /**< Optional shared literal Huffman table: 128-byte packed code-lengths header from zxc_train_dict_huf() / zxc_dict_huf() (NULL = none; ignored without dict). Becomes part of the archive's dict_id binding. */ zxc_progress_callback_t progress_cb; /**< Optional progress callback (NULL to disable). */ void* user_data; /**< User context pointer passed to progress_cb. */ } zxc_compress_opts_t; /** * @brief Options for streaming decompression. * * Zero-initialise for safe defaults. * * @code * zxc_decompress_opts_t opts = { .checksum_enabled = 1 }; * zxc_stream_decompress(f_in, f_out, &opts); * @endcode */ typedef struct { int n_threads; /**< Worker thread count (0 = auto-detect CPU cores). */ int checksum_enabled; /**< 1 to verify per-block and global checksums, 0 to skip. */ const void* dict; /**< Pre-trained dictionary content (NULL = none). */ size_t dict_size; /**< Dictionary size in bytes (0 = none). */ const void* dict_huf; /**< Optional shared literal Huffman table: 128-byte packed code-lengths header matching the one used at compression time (NULL = none; ignored without dict). */ zxc_progress_callback_t progress_cb; /**< Optional progress callback (NULL to disable). */ void* user_data; /**< User context pointer passed to progress_cb. */ } zxc_decompress_opts_t; /** * @brief Returns `sizeof(zxc_compress_opts_t)` as compiled into the library. * * Layout guard for bindings that mirror the options structs by hand (raw FFI * declarations, byte-offset serialization) instead of compiling against this * header: comparing the mirrored size against this value at load time turns a * silent layout drift (undefined behaviour) into an immediate, explicit error. */ ZXC_EXPORT size_t zxc_compress_opts_size(void); /** * @brief Returns `sizeof(zxc_decompress_opts_t)` as compiled into the library. * @see zxc_compress_opts_size */ ZXC_EXPORT size_t zxc_decompress_opts_size(void); #ifdef __cplusplus } #endif #endif // ZXC_OPTS_H zxc-0.13.0/include/zxc_pstream.h000066400000000000000000000302411522536370400165410ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file zxc_pstream.h * @brief Push-based, single-threaded streaming compression and decompression. * * This header exposes a non-blocking, caller-driven streaming API, the * counterpart of the @c FILE*-based @ref zxc_stream_compress / @ref * zxc_stream_decompress. Where the @c FILE* API takes ownership of the * pipeline (it reads until EOF and writes until done), the push API hands the * control back to the caller: feed input chunks when available, drain output * chunks when ready, finalise on demand. * * Use this API when you need to integrate ZXC into: * - a callback-driven library; * - an asynchronous event loop; * - a network protocol that streams data without seeking (HTTP chunked * transfer, gRPC, custom binary protocols); * - any pipeline where you cannot block on a @c FILE*. * * The API is single-threaded: one context is processed by one thread at a * time. For multi-threaded compression of a single file end-to-end, use * @ref zxc_stream_compress instead. * * @par Compression usage * @code * zxc_compress_opts_t opts = { .level = 3, .checksum_enabled = 1 }; * zxc_cstream* cs = zxc_cstream_create(&opts); * * uint8_t in_buf[64*1024], out_buf[64*1024]; * zxc_outbuf_t out = { out_buf, sizeof out_buf, 0 }; * * ssize_t n; * while ((n = read_some(in_buf, sizeof in_buf)) > 0) { * zxc_inbuf_t in = { in_buf, (size_t)n, 0 }; * while (in.pos < in.size) { * int64_t r = zxc_cstream_compress(cs, &out, &in); * if (r < 0) goto fatal; * if (out.pos > 0) { write_to_sink(out_buf, out.pos); out.pos = 0; } * } * } * * int64_t pending; * do { * pending = zxc_cstream_end(cs, &out); * if (pending < 0) goto fatal; * if (out.pos > 0) { write_to_sink(out_buf, out.pos); out.pos = 0; } * } while (pending > 0); * * zxc_cstream_free(cs); * @endcode * * @see zxc_stream.h for the multi-threaded @c FILE*-based pipeline. * @see zxc_buffer.h for one-shot in-memory compression. */ #ifndef ZXC_PSTREAM_H #define ZXC_PSTREAM_H #include #include #include "zxc_export.h" #include "zxc_opts.h" #ifdef __cplusplus extern "C" { #endif /** * @defgroup pstream Push Streaming API * @brief Caller-driven, single-threaded streaming compression and decompression. * @{ */ /** * @brief Input buffer descriptor for push streaming. * * The caller fills @c src with bytes to feed in and sets @c size to their * count. The library advances @c pos as it consumes input; the caller must * not modify @c pos between calls. */ typedef struct { const void* src; /**< Caller-owned input bytes. */ size_t size; /**< Total bytes available in @c src. */ size_t pos; /**< Bytes already consumed by the library (in/out). */ } zxc_inbuf_t; /** * @brief Output buffer descriptor for push streaming. * * The caller provides a writable region of capacity @c size starting at * @c dst. The library writes starting at @c dst+pos and advances @c pos by * the number of bytes produced. The caller drains @c [dst, dst+pos) and * resets @c pos to 0 between rounds (or grows @c size). * * The ENTIRE region @c [dst+pos, dst+size) must be treated as writable * scratch: when the remaining capacity is large enough, the decoder writes * blocks directly into it using speculative (wild-copy) stores, so bytes * beyond the final @c pos are unspecified after a call. Do not keep live * data inside the declared capacity. */ typedef struct { void* dst; /**< Caller-owned output region. */ size_t size; /**< Total capacity available at @c dst. */ size_t pos; /**< Bytes already produced by the library (in/out). */ } zxc_outbuf_t; /* Opaque streaming contexts. */ /** @brief Opaque push-model compression stream (see @ref zxc_cstream_create). */ typedef struct zxc_cstream_s zxc_cstream; /** @brief Opaque push-model decompression stream (see @ref zxc_dstream_create). */ typedef struct zxc_dstream_s zxc_dstream; /* ===== Compression =================================================== */ /** * @brief Creates a push compression stream. * * All settings from @p opts are copied into the context. After this call, * the @p opts struct may be freed or reused. * * Only @c level, @c block_size, and @c checksum_enabled are honoured * (levels above @ref ZXC_LEVEL_ULTRA are clamped). @c n_threads is ignored * (this API is single-threaded; use @ref zxc_stream_compress for the * multi-threaded @c FILE* pipeline). Dictionary options are rejected: the * push-stream format carries no dict_id, so passing @c dict / @c dict_size / * @c dict_huf makes creation fail rather than silently dropping them. * * If @p opts is not @c NULL, the honoured fields must contain valid values. * Invalid option values (for example an unsupported @c block_size or a * dictionary) cause stream creation to fail. * * @param[in] opts Compression options, or @c NULL for all defaults. * @return Allocated context to be released with @ref zxc_cstream_free, * or @c NULL if stream creation fails due to memory allocation * failure or invalid option values in @p opts. */ ZXC_EXPORT zxc_cstream* zxc_cstream_create(const zxc_compress_opts_t* opts); /** * @brief Releases a compression stream and all internal buffers. * * Safe to call with @c NULL (no-op). * * @param[in] cs Stream returned by @ref zxc_cstream_create. */ ZXC_EXPORT void zxc_cstream_free(zxc_cstream* cs); /** * @brief Pushes input bytes into the stream and drains compressed output. * * Reads from @c in->src starting at @c in->pos, writes to @c out->dst * starting at @c out->pos, advancing both as data flows. Each call makes as * much progress as either buffer allows in a single visit: * * - emits the file header on the first invocation (16 B); * - copies input into the internal block accumulator; * - whenever the accumulator is full, compresses one block and writes it * into @p out (up to @c out->size); * - returns when @p in is fully consumed *and* no more compressed bytes are * pending, or when @p out has no room left. * * The function is fully reentrant: if @p out fills mid-block, the next call * resumes draining from where the previous left off. Safe to call with * @c in->size == in->pos (drain-only mode). * * @par Errors * On failure the context becomes errored (sticky): every subsequent call to * @ref zxc_cstream_compress / @ref zxc_cstream_end returns the same negative * code without doing further work. Only @ref zxc_cstream_free is safe. * * @param[in,out] cs Compression stream. * @param[in,out] out Output descriptor; @c pos is advanced by produced bytes. * @param[in,out] in Input descriptor; @c pos is advanced by consumed bytes. * * @return @c 0 if @p in was fully consumed and no compressed bytes remain * pending in the internal staging area; * @c >0 number of bytes still pending, drain @p out and call again * with the same (or new) input; * @c <0 a @ref zxc_error_t code. */ ZXC_EXPORT int64_t zxc_cstream_compress(zxc_cstream* cs, zxc_outbuf_t* out, zxc_inbuf_t* in); /** * @brief Finalises the stream: flushes pending data, writes EOF block + footer. * * Must be called after the last @ref zxc_cstream_compress invocation to * produce a valid ZXC file. Like @ref zxc_cstream_compress, this function * is reentrant: if @p out fills before everything is drained, it returns a * positive count and the caller drains and calls again. * * After @ref zxc_cstream_end returns @c 0, the stream is in DONE state and * any further call returns @c ZXC_ERROR_NULL_INPUT (use @ref * zxc_cstream_free to release). * * @param[in,out] cs Compression stream. * @param[in,out] out Output descriptor. * * @return @c 0 finalisation complete (file is now valid); * @c >0 bytes still pending, drain @p out and call again; * @c <0 a @ref zxc_error_t code. */ ZXC_EXPORT int64_t zxc_cstream_end(zxc_cstream* cs, zxc_outbuf_t* out); /** * @brief Suggested input chunk size for best throughput. * * Equal to the configured block size (default 512 KB). The caller may * supply any input chunk; this is purely a performance hint. * * @param[in] cs Compression stream. * @return Suggested @c in_buf capacity in bytes, or 0 if @p cs is @c NULL. */ ZXC_EXPORT size_t zxc_cstream_in_size(const zxc_cstream* cs); /** * @brief Suggested output chunk size to never trigger a partial drain. * * Sized to hold one full compressed block plus framing overhead. Smaller * outputs work but may force the caller into an extra drain loop. * * @param[in] cs Compression stream. * @return Suggested @c out_buf capacity in bytes, or 0 if @p cs is @c NULL. */ ZXC_EXPORT size_t zxc_cstream_out_size(const zxc_cstream* cs); /* ===== Decompression ================================================= */ /** * @brief Creates a push decompression stream. * * All settings from @p opts are copied into the context. Only * @c checksum_enabled is honoured (controls whether per-block and global * checksums are verified when present). Dictionary options are rejected * (the push-stream format carries no dict_id): passing @c dict / * @c dict_size / @c dict_huf makes creation fail rather than silently * ignoring them. * * @param[in] opts Decompression options, or @c NULL for defaults. * @return Allocated context to be released with @ref zxc_dstream_free, * or @c NULL on allocation failure or dictionary options in @p opts. */ ZXC_EXPORT zxc_dstream* zxc_dstream_create(const zxc_decompress_opts_t* opts); /** * @brief Releases a decompression stream. Safe to call with @c NULL. * * @param[in] ds Stream returned by @ref zxc_dstream_create. */ ZXC_EXPORT void zxc_dstream_free(zxc_dstream* ds); /** * @brief Pushes compressed input and drains decompressed output. * * Internally runs a parser state machine: file header -> per-block * (header + payload + optional checksum) -> EOF block -> optional SEK block -> * file footer. Each call makes as much progress as @p in and @p out allow. * * @par End of stream * When the decoder reaches the file footer and validates it, the stream * enters DONE state. Subsequent calls return @c 0 without producing more * output, even if extra bytes remain in @p in (those trailing bytes are * silently ignored, the caller may use the residual @c in->pos to detect * how much real data was consumed). * * @par Errors * Sticky: once a negative code is returned, further calls keep returning it. * * @param[in,out] ds Decompression stream. * @param[in,out] out Output descriptor; @c pos advanced by produced bytes. * @param[in,out] in Input descriptor; @c pos advanced by consumed bytes. * * @return @c >0 number of decompressed bytes written into @p out this call; * @c 0 stream complete (DONE) or no progress possible (caller should * feed more input); * @c <0 a @ref zxc_error_t code. */ ZXC_EXPORT int64_t zxc_dstream_decompress(zxc_dstream* ds, zxc_outbuf_t* out, zxc_inbuf_t* in); /** * @brief Reports whether the decoder has fully consumed a valid stream. * * Returns @c 1 iff the parser has reached the file footer **and** validated * it. Callers that have finished feeding input use this to detect truncated * streams: if @ref zxc_dstream_decompress returns @c 0 with no output and * @ref zxc_dstream_finished returns @c 0, the input ended prematurely. * * @param[in] ds Decompression stream. * @return @c 1 if DONE, @c 0 otherwise (including errored). */ ZXC_EXPORT int zxc_dstream_finished(const zxc_dstream* ds); /** * @brief Suggested input chunk size for the decompressor. * * @param[in] ds Decompression stream. * @return Suggested @c in_buf capacity in bytes, or 0 if @p ds is @c NULL. */ ZXC_EXPORT size_t zxc_dstream_in_size(const zxc_dstream* ds); /** * @brief Suggested output chunk size for the decompressor. * * Sized to hold at least one full decompressed block. * * @param[in] ds Decompression stream. * @return Suggested @c out_buf capacity in bytes, or 0 if @p ds is @c NULL. */ ZXC_EXPORT size_t zxc_dstream_out_size(const zxc_dstream* ds); /** @} */ /* end of pstream */ #ifdef __cplusplus } #endif #endif /* ZXC_PSTREAM_H */ zxc-0.13.0/include/zxc_seekable.h000066400000000000000000000261061522536370400166460ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file zxc_seekable.h * @brief Seekable compression and random-access decompression API. * * This header provides functions to produce seekable ZXC archives and to * decompress arbitrary byte ranges without reading the entire file. * * A seekable archive embeds a Seek Table block (block_type = @c ZXC_BLOCK_SEK) * after the EOF block, recording the compressed size of every block. * The table is detected at read time by deriving @c num_blocks from the file * footer's total decompressed size and the header's block size, then seeking * backward to validate the SEK block header. * Standard (non-seekable) decompressors ignore the seek table entirely. * * This header is freestanding: it depends only on @c , @c * and the rest of the ZXC public API. It does not pull in @c , so it * is includable from kernel-space or other freestanding environments. * The @c FILE*-based @ref zxc_seekable_open_file entry point lives in the * companion header @c zxc_seekable_file.h. * * @par Creating a seekable archive * @code * zxc_compress_opts_t opts = { .level = 3, .seekable = 1 }; * int64_t csize = zxc_compress(src, src_size, dst, dst_cap, &opts); * @endcode * * @par Random-access decompression (buffer-backed) * @code * zxc_seekable* s = zxc_seekable_open(compressed, csize); * int64_t n = zxc_seekable_decompress_range(s, out, out_cap, offset, len); * zxc_seekable_free(s); * @endcode * * @par Random-access decompression (custom storage) * @code * zxc_reader_t r = { .read_at = my_read_at, .ctx = my_state, .size = total }; * zxc_seekable* s = zxc_seekable_open_reader(&r); * @endcode */ #ifndef ZXC_SEEKABLE_H #define ZXC_SEEKABLE_H #include #include #include "zxc_export.h" #ifdef __cplusplus extern "C" { #endif /** * @defgroup seekable_api Seekable API * @brief Random-access compression and decompression. * @{ */ /* ========================================================================= */ /* Seekable Reader (Random-Access Decompression) */ /* ========================================================================= */ /** * @brief Opaque handle for a seekable ZXC archive. * * Created by zxc_seekable_open(), zxc_seekable_open_reader(), or * zxc_seekable_open_file() (see @c zxc_seekable_file.h). * Must be freed with zxc_seekable_free(). */ typedef struct zxc_seekable_s zxc_seekable; /** * @brief Opens a seekable archive from a memory buffer. * * Parses the seek table from the end of the buffer and builds the internal * block index. The buffer must remain valid for the lifetime of the handle. * * @param[in] src Pointer to the compressed data. * @param[in] src_size Size of the compressed data in bytes. * @return Handle on success, or @c NULL if the buffer does not contain a * valid seekable archive (e.g. missing seek block, bad block type). */ ZXC_EXPORT zxc_seekable* zxc_seekable_open(const void* src, const size_t src_size); /** * @brief Storage-agnostic reader interface for seekable archives. * * Lets the caller plug any backend (mmap, HTTP range requests, S3, a custom * VFS, kernel @c vfs_read, etc.) behind the seekable reader. The reader * exposes positional reads only; no seeking state is implied. * * @par Thread safety * @c read_at MUST be safe to call concurrently from multiple threads when the * resulting handle is used with zxc_seekable_decompress_range_mt(). The * single-threaded path makes no concurrent calls. * * @par Lifetime * Both @c ctx and the backing storage must remain valid for the lifetime of * the returned zxc_seekable handle (until zxc_seekable_free()). */ typedef struct { /** * @brief Reads exactly @c len bytes at @c offset into @c dst. * * @param[in,out] ctx Opaque user context (forwarded from zxc_reader_t::ctx). * @param[out] dst Destination buffer. * @param[in] len Number of bytes to read. * @param[in] offset Byte offset from the start of the archive. * @return Number of bytes read (@c == @c len on success), or a negative * @ref zxc_error_t code on failure. Short reads are treated as * errors by the seekable reader. */ int64_t (*read_at)(void* ctx, void* dst, size_t len, uint64_t offset); /** @brief Opaque user context passed unchanged to @c read_at. */ void* ctx; /** @brief Total size of the compressed archive in bytes. */ uint64_t size; } zxc_reader_t; /** * @brief Opens a seekable archive through a user-supplied reader. * * The reader is invoked to fetch the file header, footer, and seek table at * open time, then again on every block read during decompression. Use this * entry point to back the seekable API with any storage that supports * positional reads (e.g. mmap, HTTP, S3, a kernel file descriptor). * * @param[in] r Reader interface (must remain valid for the handle lifetime). * @return Handle on success, or @c NULL on error. */ ZXC_EXPORT zxc_seekable* zxc_seekable_open_reader(const zxc_reader_t* r); /** * @brief Returns the total number of blocks in the seekable archive. * * @param[in] s Seekable handle. * @return Number of data blocks (excluding EOF). */ ZXC_EXPORT uint32_t zxc_seekable_get_num_blocks(const zxc_seekable* s); /** * @brief Returns the total decompressed size of the seekable archive. * * @param[in] s Seekable handle. * @return Total decompressed size in bytes. */ ZXC_EXPORT uint64_t zxc_seekable_get_decompressed_size(const zxc_seekable* s); /** * @brief Returns the compressed size of a specific block. * * This is the "on-disk" size including block header, payload, and optional * per-block checksum. * * @param[in] s Seekable handle. * @param[in] block_idx Zero-based block index. * @return Compressed block size, or 0 if @p block_idx is out of range. */ ZXC_EXPORT uint32_t zxc_seekable_get_block_comp_size(const zxc_seekable* s, const uint32_t block_idx); /** * @brief Returns the decompressed size of a specific block. * * @param[in] s Seekable handle. * @param[in] block_idx Zero-based block index. * @return Decompressed block size, or 0 if @p block_idx is out of range. */ ZXC_EXPORT uint32_t zxc_seekable_get_block_decomp_size(const zxc_seekable* s, const uint32_t block_idx); /** * @brief Decompresses an arbitrary byte range from the original data. * * Only the blocks overlapping [@p offset, @p offset + @p len) are read and * decompressed. This is the core random-access primitive. * * @param[in,out] s Seekable handle. * @param[out] dst Destination buffer. * @param[in] dst_capacity Capacity of @p dst (must be >= @p len). * @param[in] offset Byte offset into the original uncompressed data. * @param[in] len Number of bytes to decompress. * @return Number of bytes written to @p dst (== @p len on success), * or a negative @ref zxc_error_t code on failure. */ ZXC_EXPORT int64_t zxc_seekable_decompress_range(zxc_seekable* s, void* dst, const size_t dst_capacity, const uint64_t offset, const size_t len); /** * @brief Multi-threaded variant of zxc_seekable_decompress_range(). * * Decompresses blocks in parallel using a fork-join thread pool. Each worker * thread owns its own decompression context and reads compressed data via * @c pread() (POSIX) or @c ReadFile() (Windows) for lock-free concurrent I/O. * * Falls back to single-threaded mode when @p n_threads <= 1 or when the * requested range spans a single block. * * @param[in,out] s Seekable handle. * @param[out] dst Destination buffer. * @param[in] dst_capacity Capacity of @p dst (must be >= @p len). * @param[in] offset Byte offset into the original uncompressed data. * @param[in] len Number of bytes to decompress. * @param[in] n_threads Number of worker threads (0 = auto-detect CPU cores). * @return Number of bytes written to @p dst (== @p len on success), * or a negative @ref zxc_error_t code on failure. */ ZXC_EXPORT int64_t zxc_seekable_decompress_range_mt(zxc_seekable* s, void* dst, const size_t dst_capacity, const uint64_t offset, const size_t len, int n_threads); /** * @brief Frees a seekable handle and all associated resources. * * Safe to call with @c NULL. * * @param[in] s Handle to free. */ ZXC_EXPORT void zxc_seekable_free(zxc_seekable* s); /** * @brief Attach a pre-trained dictionary to a seekable handle. * * The dictionary content and table are copied internally; the caller may free * them after this call returns. Must be called before any * zxc_seekable_decompress_range() call. * * @param[in] s Seekable handle. * @param[in] dict Dictionary content. * @param[in] dict_size Size in bytes (max ZXC_DICT_SIZE_MAX). * @param[in] dict_huf Shared literal Huffman table (128 bytes, see * zxc_dict_huf()), or NULL if the archive was compressed * without one. Must match the compression-time pair: the * archive's dict_id binds (dict, table). * @return @ref ZXC_OK on success, or a negative @ref zxc_error_t code. */ ZXC_EXPORT int zxc_seekable_set_dict(zxc_seekable* s, const void* dict, size_t dict_size, const void* dict_huf); /* ========================================================================= */ /* Seek Table Writer (low-level) */ /* ========================================================================= */ /** * @brief Writes a seek table to the destination buffer. * * This is a low-level helper used internally by the seekable compression * paths. It writes: block_header(8) + N entries(4 each). * Each entry stores only @c comp_size; decompressed sizes are derived at * read time from the file header's block_size. * * @param[out] dst Destination buffer. * @param[in] dst_capacity Capacity of @p dst in bytes. * @param[in] comp_sizes Array of compressed block sizes. * @param[in] num_blocks Number of blocks. * @return Number of bytes written, or a negative @ref zxc_error_t on failure. */ ZXC_EXPORT int64_t zxc_write_seek_table(uint8_t* dst, const size_t dst_capacity, const uint32_t* comp_sizes, const uint32_t num_blocks); /** * @brief Returns the encoded size of a seek table for the given block count. * * @param[in] num_blocks Number of blocks. * @return Total byte size of the seek table. */ ZXC_EXPORT size_t zxc_seek_table_size(const uint32_t num_blocks); /** @} */ /* end of seekable_api */ #ifdef __cplusplus } #endif #endif /* ZXC_SEEKABLE_H */ zxc-0.13.0/include/zxc_stream.h000066400000000000000000000115761522536370400163730ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file zxc_stream.h * @brief @c FILE*-flavored variants of the ZXC API. * * Groups the public entry points that depend on @c so they can be * cleanly excluded from kernel / freestanding builds (which include * @c zxc_buffer.h, @c zxc_pstream.h, and the storage-agnostic part of * @c zxc_seekable.h instead). * * Two subsystems live here: * * 1. **Multi-threaded streaming driver**: reads from a @c FILE* input and * writes compressed (or decompressed) output to a @c FILE*. Internally * the driver uses an asynchronous Producer-Consumer pipeline via a ring * buffer to separate I/O from CPU-intensive work: * - Reader thread: reads chunks from @c f_in. * - Worker threads: compress/decompress chunks in parallel. * - Writer thread: orders the results and writes them to @c f_out. * Functions: @ref zxc_stream_compress, @ref zxc_stream_decompress, * @ref zxc_stream_get_decompressed_size. * * 2. **Seekable @c FILE* open helper**: thin wrapper that adapts a * @c FILE* into a thread-safe @c pread / @c ReadFile-backed * @ref zxc_reader_t and delegates to @ref zxc_seekable_open_reader. * Function: @ref zxc_seekable_open_file. * * @see zxc_buffer.h for the simple one-shot buffer API. * @see zxc_pstream.h for single-threaded push-based streaming. * @see zxc_seekable.h for the storage-agnostic seekable reader. */ #ifndef ZXC_STREAM_H #define ZXC_STREAM_H #include #include #include "zxc_export.h" #include "zxc_opts.h" #include "zxc_seekable.h" #ifdef __cplusplus extern "C" { #endif /** * @defgroup stream_api Streaming API * @brief Multi-threaded, FILE*-based compression and decompression. * @{ */ /** * @brief Compresses data from an input stream to an output stream. * * This function sets up a multi-threaded pipeline: * 1. Reader Thread: Reads chunks from f_in. * 2. Worker Threads: Compress chunks in parallel (LZ77 + Bitpacking). * 3. Writer Thread: Orders the processed chunks and writes them to f_out. * * @param[in] f_in Input file stream (must be opened in "rb" mode). * @param[out] f_out Output file stream (must be opened in "wb" mode). * @param[in] opts Compression options (NULL uses all defaults). * * @return Total compressed bytes written, or a negative zxc_error_t code (e.g., * ZXC_ERROR_IO) if an error occurred. */ ZXC_EXPORT int64_t zxc_stream_compress(FILE* f_in, FILE* f_out, const zxc_compress_opts_t* opts); /** * @brief Decompresses data from an input stream to an output stream. * * Uses the same pipeline architecture as compression to maximize throughput. * * @param[in] f_in Input file stream (must be opened in "rb" mode). * @param[out] f_out Output file stream (must be opened in "wb" mode). * @param[in] opts Decompression options (NULL uses all defaults). * * @return Total decompressed bytes written, or a negative zxc_error_t code (e.g., * ZXC_ERROR_BAD_HEADER) if an error occurred. */ ZXC_EXPORT int64_t zxc_stream_decompress(FILE* f_in, FILE* f_out, const zxc_decompress_opts_t* opts); /** * @brief Returns the decompressed size stored in a ZXC compressed file. * * This function reads the file footer to extract the original uncompressed size * without performing any decompression. The file position is restored after reading. * * @param[in] f_in Input file stream (must be opened in "rb" mode). * * @return The original uncompressed size in bytes, or a negative zxc_error_t code (e.g., * ZXC_ERROR_BAD_MAGIC) if the file is invalid or an I/O error occurred. */ ZXC_EXPORT int64_t zxc_stream_get_decompressed_size(FILE* f_in); /* ========================================================================= */ /* Seekable FILE* open helper */ /* ========================================================================= */ /** * @brief Opens a seekable archive from a @c FILE*. * * Internally builds a @ref zxc_reader_t that performs thread-safe positional * reads (@c pread on POSIX, @c ReadFile + @c OVERLAPPED on Windows) on the * file descriptor extracted from @p f, then delegates to * @ref zxc_seekable_open_reader. The current file position is saved and * restored. The @c FILE* must remain open for the lifetime of the handle. * * Lives here (next to the other @c FILE*-based entry points) rather than in * @c zxc_seekable.h so the latter remains freestanding (kernel-includable). * * @param[in] f File opened in @c "rb" mode (must be seekable, not a pipe). * @return Handle on success (free with @ref zxc_seekable_free), or @c NULL * on error. */ ZXC_EXPORT zxc_seekable* zxc_seekable_open_file(FILE* f); /** @} */ /* end of stream_api */ #ifdef __cplusplus } #endif #endif // ZXC_STREAM_Hzxc-0.13.0/libzxc.pc.in000066400000000000000000000005711522536370400146350ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=${prefix} libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ Name: libzxc Description: High-performance asymmetric lossless compression URL: https://github.com/hellobertrand/zxc License: BSD 3-Clause Version: @PROJECT_VERSION@ Libs: -L${libdir} -lzxc Libs.private: -pthread Cflags: -I${includedir}zxc-0.13.0/meson.build000066400000000000000000000134171522536370400145560ustar00rootroot00000000000000# ZXC - High-performance lossless compression # # Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. # SPDX-License-Identifier: BSD-3-Clause project('zxc', 'c', version : '0.13.0', license : 'BSD-3-Clause', default_options : ['c_std=c17', 'buildtype=release', 'warning_level=2'], meson_version : '>= 0.60.0', ) cc = meson.get_compiler('c') host_cpu = host_machine.cpu_family() # Keep in sync with ZXC_SOVERSION in CMakeLists.txt (bumps only on an ABI break). zxc_soversion = '4' # ============================================================================= # Platform defines # ============================================================================= if host_machine.system() == 'linux' add_project_arguments('-D_GNU_SOURCE', '-D_FILE_OFFSET_BITS=64', '-D_LARGEFILE_SOURCE', language : 'c') elif host_machine.system() == 'darwin' add_project_arguments('-D_GNU_SOURCE', language : 'c') endif # ============================================================================= # Include directories # ============================================================================= inc_public = include_directories('include') inc_private = include_directories('src/lib') rapidhash_inc = include_directories('src/lib/vendors') libzxc_includes = [inc_public, inc_private, rapidhash_inc] # ============================================================================= # SIMD runtime dispatch # ============================================================================= # Compiles compress/decompress/huffman per-ISA so the runtime dispatcher can # route to the fastest implementation available at startup. variant_sources = ['src/lib/zxc_compress.c', 'src/lib/zxc_decompress.c', 'src/lib/zxc_huffman.c'] variant_sets = [['_default', []]] if host_cpu == 'x86_64' variant_sets += [['_sse2', ['-msse2']]] variant_sets += [['_avx2', ['-mavx2', '-mfma', '-mbmi', '-mbmi2', '-mlzcnt']]] variant_sets += [['_avx512', ['-mavx512f', '-mavx512bw', '-mavx512vbmi', '-mavx512vbmi2', '-mbmi', '-mbmi2', '-mlzcnt']]] elif host_cpu == 'aarch64' variant_sets += [['_neon', ['-march=armv8-a+simd']]] elif host_cpu == 'arm' variant_sets += [['_neon', ['-march=armv7-a', '-mfpu=neon']]] endif variant_objects = [] foreach info : variant_sets foreach src : variant_sources obj = static_library( 'zxc' + info[0] + '_' + src.underscorify(), src, c_args : info[1] + ['-DZXC_FUNCTION_SUFFIX=' + info[0]], include_directories : libzxc_includes, pic : true, ) variant_objects += obj.extract_all_objects(recursive : false) endforeach endforeach # ============================================================================= # Core library # ============================================================================= libzxc_sources = files( 'src/lib/zxc_common.c', 'src/lib/zxc_pivco_tables.c', 'src/lib/zxc_dict.c', 'src/lib/zxc_driver.c', 'src/lib/zxc_dispatch.c', 'src/lib/zxc_pstream.c', 'src/lib/zxc_seekable.c', ) thread_dep = dependency('threads') libzxc = library('zxc', libzxc_sources, objects : variant_objects, gnu_symbol_visibility : 'hidden', include_directories : libzxc_includes, dependencies : [thread_dep], install : true, version : meson.project_version(), soversion : zxc_soversion, ) libzxc_dep = declare_dependency( link_with : libzxc, include_directories : inc_public, ) meson.override_dependency('libzxc', libzxc_dep) # Static link target for tests that access internal symbols (same pattern as # CMake's zxc_lib_static and zstd's libzstd_internal_dep). if get_option('default_library') == 'static' libzxc_static = libzxc else libzxc_static = static_library('zxc_static', libzxc_sources, objects : variant_objects, include_directories : libzxc_includes, dependencies : [thread_dep], build_by_default : false, ) endif # ============================================================================= # pkg-config & headers # ============================================================================= pkgconfig = import('pkgconfig') pkgconfig.generate(libzxc, name : 'libzxc', filebase : 'libzxc', description : 'High-performance asymmetric lossless compression', version : meson.project_version(), url : 'https://github.com/hellobertrand/zxc', ) install_headers( 'include/zxc.h', 'include/zxc_buffer.h', 'include/zxc_constants.h', 'include/zxc_dict.h', 'include/zxc_error.h', 'include/zxc_export.h', 'include/zxc_opts.h', 'include/zxc_pstream.h', 'include/zxc_seekable.h', 'include/zxc_stream.h', ) # ============================================================================= # CLI (standalone only, off by default) # ============================================================================= if not meson.is_subproject() and get_option('build_cli') math_dep = cc.find_library('m', required : false) executable('zxc', 'src/cli/main.c', dependencies : [libzxc_dep, math_dep], install : true, ) endif # ============================================================================= # Tests (standalone only) # ============================================================================= if not meson.is_subproject() test_exe = executable('zxc_test', files( 'tests/test_main.c', 'tests/test_common.c', 'tests/test_buffer_api.c', 'tests/test_block_api.c', 'tests/test_context_api.c', 'tests/test_static_ctx.c', 'tests/test_pstream_api.c', 'tests/test_stream_api.c', 'tests/test_seekable.c', 'tests/test_seekable_mt.c', 'tests/test_format.c', 'tests/test_misc.c', 'tests/test_dict.c', ), include_directories : libzxc_includes, link_with : libzxc_static, dependencies : [thread_dep], ) test('zxc_test', test_exe) endif zxc-0.13.0/meson_options.txt000066400000000000000000000001561522536370400160450ustar00rootroot00000000000000option('build_cli', type : 'boolean', value : false, description : 'Build the command-line interface') zxc-0.13.0/sbom.spdx.json.in000066400000000000000000000037531522536370400156330ustar00rootroot00000000000000{ "spdxVersion": "SPDX-2.3", "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "zxc", "documentNamespace": "https://github.com/hellobertrand/zxc/sbom/@VERSION@-@COMMIT@", "creationInfo": { "created": "@CREATED@", "creators": ["Tool: zxc-build-workflow"] }, "packages": [ { "name": "zxc", "SPDXID": "SPDXRef-Package-zxc", "versionInfo": "@VERSION@", "supplier": "Person: Bertrand Lebonnois", "originator": "Person: Bertrand Lebonnois", "downloadLocation": "https://github.com/hellobertrand/zxc", "filesAnalyzed": false, "licenseConcluded": "BSD-3-Clause", "licenseDeclared": "BSD-3-Clause", "copyrightText": "NOASSERTION", "primaryPackagePurpose": "APPLICATION", "externalRefs": [ { "referenceCategory": "PACKAGE-MANAGER", "referenceType": "purl", "referenceLocator": "pkg:github/hellobertrand/zxc@@VERSION@" } ] }, { "name": "rapidhash", "SPDXID": "SPDXRef-Package-rapidhash", "versionInfo": "V3", "supplier": "Person: Nicolas De Carli", "originator": "Person: Nicolas De Carli", "downloadLocation": "https://github.com/Nicoshev/rapidhash", "filesAnalyzed": false, "sourceInfo": "Vendored single-header at src/lib/vendors/rapidhash.h", "licenseConcluded": "MIT", "licenseDeclared": "MIT", "copyrightText": "Copyright (C) 2025 Nicolas De Carli", "primaryPackagePurpose": "SOURCE", "externalRefs": [ { "referenceCategory": "PACKAGE-MANAGER", "referenceType": "purl", "referenceLocator": "pkg:github/Nicoshev/rapidhash@V3" } ] } ], "relationships": [ { "spdxElementId": "SPDXRef-DOCUMENT", "relationshipType": "DESCRIBES", "relatedSpdxElement": "SPDXRef-Package-zxc" }, { "spdxElementId": "SPDXRef-Package-zxc", "relationshipType": "STATIC_LINK", "relatedSpdxElement": "SPDXRef-Package-rapidhash" } ] } zxc-0.13.0/src/000077500000000000000000000000001522536370400131755ustar00rootroot00000000000000zxc-0.13.0/src/cli/000077500000000000000000000000001522536370400137445ustar00rootroot00000000000000zxc-0.13.0/src/cli/main.c000066400000000000000000002226511522536370400150440ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file main.c * @brief Command Line Interface (CLI) entry point for the ZXC compression tool. * * This file handles argument parsing, file I/O setup, platform-specific * compatibility layers (specifically for Windows), and the execution of * compression, decompression, or benchmarking modes. */ #include #include #include #include #include #include #include "../../include/zxc_buffer.h" #include "../../include/zxc_constants.h" #include "../../include/zxc_dict.h" #include "../../include/zxc_error.h" #include "../../include/zxc_stream.h" #define ZXC_STDIO_BUFFER_SIZE (1024 * 1024) #ifdef _WIN32 // Windows Implementation #include #include #include #include // Map POSIX macros to MSVC equivalents #define F_OK 0 #define access _access #define isatty _isatty #define fileno _fileno #define unlink _unlink #define fseeko _fseeki64 #define ftello _ftelli64 /** * @brief Returns the current monotonic time in seconds using Windows * Performance Counter. * @return double Time in seconds. */ static double zxc_now(void) { LARGE_INTEGER frequency; LARGE_INTEGER count; QueryPerformanceFrequency(&frequency); QueryPerformanceCounter(&count); return (double)count.QuadPart / frequency.QuadPart; } struct option { const char* name; int has_arg; int* flag; int val; }; #define no_argument 0 #define required_argument 1 #define optional_argument 2 char* optarg = NULL; int optind = 1; int optopt = 0; /** * @brief Minimal implementation of getopt_long for Windows. * Handles long options (--option[=value]), grouped short options (-dcf), * attached or separate option arguments (-T4 / -T 4), the "--" end-of-options * marker, and a bare "-" left as a positional argument. */ static int getopt_long(int argc, char* const argv[], const char* optstring, const struct option* longopts, const int* longindex) { (void)longindex; static int shortpos = 0; // >0: position within a grouped short-option argv element optarg = NULL; if (optind >= argc) return -1; char* const curr = argv[optind]; if (shortpos == 0) { if (curr[0] != '-' || curr[1] == '\0') return -1; // positional (including "-") if (curr[1] == '-') { if (curr[2] == '\0') { // "--": end of options optind++; return -1; } char* const name_end = strchr(curr + 2, '='); const size_t name_len = name_end ? (size_t)(name_end - (curr + 2)) : strlen(curr + 2); for (const struct option* p = longopts; p && p->name; p++) { if (name_len != strlen(p->name) || strncmp(curr + 2, p->name, name_len) != 0) continue; optind++; if (p->has_arg == required_argument) { if (name_end) optarg = name_end + 1; else if (optind < argc) optarg = argv[optind++]; else return '?'; } else if (p->has_arg == optional_argument && name_end) { optarg = name_end + 1; } if (p->flag) { *p->flag = p->val; return 0; } return p->val; } optind++; return '?'; } shortpos = 1; } const char c = curr[shortpos]; const char* const os = (c != ':') ? strchr(optstring, c) : NULL; if (!os) { optopt = c; if (curr[shortpos + 1] != '\0') { shortpos++; } else { shortpos = 0; optind++; } return '?'; } if (os[1] == ':') { // Option takes an argument: the rest of this element, or (if required) // the next argv element. Optional (::) never consumes a separate element. char* const attached = (curr[shortpos + 1] != '\0') ? curr + shortpos + 1 : NULL; shortpos = 0; optind++; if (attached) { optarg = attached; } else if (os[2] != ':') { if (optind < argc) { optarg = argv[optind++]; } else { optopt = c; return '?'; } } return c; } // Flag option: continue within the cluster if more characters follow if (curr[shortpos + 1] != '\0') { shortpos++; } else { shortpos = 0; optind++; } return c; } #else // POSIX / Linux / macOS Implementation #include #include #include #include #include #include #include #include #include #include #include /** * @brief Returns the current monotonic time in seconds using clock_gettime. * @return double Time in seconds. */ static double zxc_now(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return ts.tv_sec + ts.tv_nsec / 1e9; } #endif /** * @brief Validates and resolves the input file path to prevent directory traversal * and ensure it is a regular file. * * @param[in] path The raw input path from command line. * @param[out] resolved_buffer Buffer to store resolved path (needs sufficient size). * @param[in] buffer_size Size of the resolved_buffer. * @return 0 on success, -1 on error. * * Security note (CWE-23): paths come from the CLI at the user's own privileges, * not from archive content, so traversal is intended (suppressed in .snyk). * True only while the format stores no path; an archive mode restoring embedded paths * would be a real zip slip risk: re-evaluate the ignore then. */ static int zxc_validate_input_path(const char* path, char* resolved_buffer, size_t buffer_size) { #ifdef _WIN32 if (!_fullpath(resolved_buffer, path, buffer_size)) { return -1; } DWORD attr = GetFileAttributesA(resolved_buffer); if (attr == INVALID_FILE_ATTRIBUTES || (attr & FILE_ATTRIBUTE_DIRECTORY)) { // Not a valid file or is a directory errno = (attr == INVALID_FILE_ATTRIBUTES) ? ENOENT : EISDIR; return -1; } return 0; #else char* const res = realpath(path, NULL); if (!res) { // realpath failed (e.g. file does not exist) return -1; } struct stat st; if (stat(res, &st) != 0) { free(res); return -1; } if (!S_ISREG(st.st_mode)) { free(res); errno = EISDIR; // Generic error for non-regular file return -1; } const size_t len = strlen(res); if (len >= buffer_size) { free(res); errno = ENAMETOOLONG; return -1; } memcpy(resolved_buffer, res, len + 1); free(res); return 0; #endif } /** * @brief Validates and resolves the output file path. * * @param[in] path The raw output path. * @param[out] resolved_buffer Buffer to store resolved path. * @param[in] buffer_size Size of the resolved_buffer. * @return 0 on success, -1 on error. */ static int zxc_validate_output_path(const char* path, char* resolved_buffer, size_t buffer_size) { #ifdef _WIN32 if (!_fullpath(resolved_buffer, path, buffer_size)) return -1; DWORD attr = GetFileAttributesA(resolved_buffer); if (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY)) { errno = EISDIR; return -1; } return 0; #else // POSIX output path validation char* const temp_path = strdup(path); if (!temp_path) return -1; char* const temp_path2 = strdup(path); if (!temp_path2) { free(temp_path); return -1; } // Split into dir and base char* const dir = dirname(temp_path); // Note: dirname may modify string or return static const char* const base = basename(temp_path2); char* const resolved_dir = realpath(dir, NULL); if (!resolved_dir) { // Parent directory must exist free(temp_path); free(temp_path2); return -1; } struct stat st; if (stat(resolved_dir, &st) != 0 || !S_ISDIR(st.st_mode)) { free(resolved_dir); free(temp_path); free(temp_path2); errno = EISDIR; return -1; } // Reconstruct valid path: resolved_dir / base // Ensure we don't overflow buffer const int written = snprintf(resolved_buffer, buffer_size, "%s/%s", resolved_dir, base); free(resolved_dir); free(temp_path); free(temp_path2); if (written < 0 || (size_t)written >= buffer_size) { errno = ENAMETOOLONG; return -1; } return 0; #endif } // CLI Logging Helpers static int g_quiet = 0; static int g_verbose = 0; /* Progress display policy (--progress): auto = tty-only heuristic, always = * force (one line per update off-tty), never = disable. -q suppresses all. */ enum { ZXC_PROGRESS_AUTO = 0, ZXC_PROGRESS_ALWAYS, ZXC_PROGRESS_NEVER }; static int g_progress_mode = ZXC_PROGRESS_AUTO; /* Shared literal Huffman table from the -D .zxd file (malloc'd copy), passed * through the compress/decompress opts. NULL when the dict carries none. */ static void* g_dict_huf = NULL; /* Enables printf-style format/argument checking by the compiler on GCC/Clang * (catches format-vs-argument mismatches at every call site); no-op on MSVC. */ #if defined(__GNUC__) || defined(__clang__) #define ZXC_PRINTF_FORMAT(fmt_idx, args_idx) __attribute__((format(printf, fmt_idx, args_idx))) #else #define ZXC_PRINTF_FORMAT(fmt_idx, args_idx) #endif /** * @brief Standard logging function. Respects the global quiet flag. */ static void zxc_log(const char* fmt, ...) ZXC_PRINTF_FORMAT(1, 2); static void zxc_log(const char* fmt, ...) { if (g_quiet) return; va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); } /** * @brief Verbose logging function. Only prints if verbose is enabled and quiet * is disabled. */ static void zxc_log_v(const char* fmt, ...) ZXC_PRINTF_FORMAT(1, 2); static void zxc_log_v(const char* fmt, ...) { if (!g_verbose || g_quiet) return; va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); } // OS-specific helpers for directory checks #ifdef _WIN32 static int zxc_is_directory(const char* path) { DWORD attr = GetFileAttributesA(path); return (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY)); } #else static int zxc_is_directory(const char* path) { struct stat st; if (stat(path, &st) == 0) { return S_ISDIR(st.st_mode); } return 0; } #endif typedef enum { MODE_COMPRESS, MODE_DECOMPRESS, MODE_BENCHMARK, MODE_INTEGRITY, MODE_LIST, MODE_TRAIN_DICT } zxc_mode_t; enum { OPT_VERSION = 1000, OPT_HELP, OPT_TRAIN_DICT, OPT_PROGRESS }; // Forward declaration for recursive mode static int process_single_file(const char* in_path, const char* out_path_override, zxc_mode_t mode, int num_threads, int keep_input, int force, int to_stdout, int checksum, int level, size_t block_size, int json_output, int seekable, const void* dict, size_t dict_size); // Forward declaration for processing directory static int process_directory(const char* dir_path, zxc_mode_t mode, int num_threads, int keep_input, int force, int to_stdout, int checksum, int level, size_t block_size, int json_output, int seekable, const void* dict, size_t dict_size); // OS-specific implementation of directory processing static int process_directory(const char* dir_path, zxc_mode_t mode, int num_threads, int keep_input, int force, int to_stdout, int checksum, int level, size_t block_size, int json_output, int seekable, const void* dict, size_t dict_size) { int overall_ret = 0; #ifdef _WIN32 char search_path[MAX_PATH]; snprintf(search_path, sizeof(search_path), "%s\\*", dir_path); WIN32_FIND_DATAA find_data; HANDLE hFind = FindFirstFileA(search_path, &find_data); if (hFind == INVALID_HANDLE_VALUE) { zxc_log("Error opening directory '%s'\n", dir_path); return 1; } do { if (strcmp(find_data.cFileName, ".") == 0 || strcmp(find_data.cFileName, "..") == 0) { continue; } char full_path[MAX_PATH]; snprintf(full_path, sizeof(full_path), "%s\\%s", dir_path, find_data.cFileName); if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { overall_ret |= process_directory(full_path, mode, num_threads, keep_input, force, to_stdout, checksum, level, block_size, json_output, seekable, dict, dict_size); } else { // Check if it ends with .zxc to skip if compressing to avoid double compression if (mode == MODE_COMPRESS) { const size_t len = strlen(full_path); if (len >= 4 && strcmp(full_path + len - 4, ".zxc") == 0) { continue; // Skip already compressed files in recursive compression } } overall_ret |= process_single_file(full_path, NULL, mode, num_threads, keep_input, force, to_stdout, checksum, level, block_size, json_output, seekable, dict, dict_size); } } while (FindNextFileA(hFind, &find_data) != 0); FindClose(hFind); #else DIR* const dir = opendir(dir_path); if (!dir) { zxc_log("Error opening directory '%s': %s\n", dir_path, strerror(errno)); return 1; } const struct dirent* entry; while ((entry = readdir(dir)) != NULL) { if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } const size_t path_len = strlen(dir_path) + 1 + strlen(entry->d_name) + 1; char* const full_path = malloc(path_len); if (!full_path) { zxc_log("Error allocating memory for path in directory '%s'\n", dir_path); continue; } const int n = snprintf(full_path, path_len, "%s/%s", dir_path, entry->d_name); if (n < 0 || (size_t)n >= path_len) { zxc_log("Error: path too long in directory '%s'\n", dir_path); free(full_path); continue; } struct stat st; if (stat(full_path, &st) == 0) { if (S_ISDIR(st.st_mode)) { overall_ret |= process_directory(full_path, mode, num_threads, keep_input, force, to_stdout, checksum, level, block_size, json_output, seekable, dict, dict_size); } else if (S_ISREG(st.st_mode)) { // Check if it ends with .zxc to skip if compressing to avoid double compression if (mode == MODE_COMPRESS) { const size_t len = strlen(full_path); if (len >= 4 && strcmp(full_path + len - 4, ".zxc") == 0) { free(full_path); continue; // Skip already compressed files in recursive compression } } overall_ret |= process_single_file(full_path, NULL, mode, num_threads, keep_input, force, to_stdout, checksum, level, block_size, json_output, seekable, dict, dict_size); } } free(full_path); } closedir(dir); #endif return overall_ret; } void print_help(const char* app) { printf("Usage: %s [] []...\n\n", app); printf( "Standard Modes:\n" " -z, --compress Compress FILE {default}\n" " -d, --decompress Decompress FILE (or stdin -> stdout)\n" " -l, --list List archive or dictionary info\n" " -t, --test Test compressed FILE integrity\n" " -b, --bench [N] Benchmark in-memory (N=seconds, default 5)\n" " --train Train a dictionary from input FILEs (training samples).\n" " Output via -o (default: ./dictionary_.zxd)\n\n" "Batch Processing:\n" " -m, --multiple Multiple input files\n" " -r, --recursive Operate recursively on directories\n\n" "Special Options:\n" " -V, --version Show version information\n" " -h, --help Show this help message\n\n" "Options:\n" " -1..-7 Compression level {3}\n" " -B, --block-size Block size: 4K..2M, power of 2 {512K}\n" " -T, --threads N Number of threads (0=auto)\n" " -C, --checksum Enable checksum {default}\n" " -N, --no-checksum Disable checksum\n" " -D, --dict FILE Use pre-trained dictionary (.zxd). Required to decompress\n" " an archive that was compressed with a dictionary\n" " -S, --seekable Append seek table for random-access decompression\n" " -o, --output FILE Write output to FILE (else derived from input;\n" " for --train: ./dictionary_.zxd, or a directory)\n" " -k, --keep Keep input file\n" " -f, --force Force overwrite\n" " -c, --stdout Write to stdout\n" " -v, --verbose Verbose mode\n" " -q, --quiet Quiet mode\n" " -j, --json JSON output (benchmark mode)\n" " --progress MODE Progress display: auto, always, never {auto}\n"); } void print_version(void) { printf("ZXC CLI (%zu-bit) v%s, by Bertrand Lebonnois\nBSD 3-Clause License\n", sizeof(void*) * CHAR_BIT, ZXC_LIB_VERSION_STR); } /** * @brief Formats a byte size into human-readable TB/GB/MB/KB/B format (Base 1000). */ static void format_size_decimal(uint64_t bytes, char* buf, size_t buf_size) { const double TB = 1000.0 * 1000.0 * 1000.0 * 1000.0; const double GB = 1000.0 * 1000.0 * 1000.0; const double MB = 1000.0 * 1000.0; const double KB = 1000.0; if ((double)bytes >= TB) snprintf(buf, buf_size, "%.1f TB", (double)bytes / TB); else if ((double)bytes >= GB) snprintf(buf, buf_size, "%.1f GB", (double)bytes / GB); else if ((double)bytes >= MB) snprintf(buf, buf_size, "%.1f MB", (double)bytes / MB); else if ((double)bytes >= KB) snprintf(buf, buf_size, "%.1f KB", (double)bytes / KB); else snprintf(buf, buf_size, "%llu B", (unsigned long long)bytes); } /** * @brief Progress context for CLI progress bar display. */ typedef struct { double start_time; const char* operation; // "Compressing", "Decompressing" or "Testing" uint64_t total_size; // Pre-determined total size (0 if unknown) int to_tty; // stderr is a terminal: rewrite in place; else one line per update double last_draw; // Timestamp of the last repaint (0 = nothing drawn yet) size_t last_len; // Visible length of the last in-place line (for erasing) } progress_ctx_t; /** * @brief Erases an in-place progress line without ANSI escapes. * * Plain "\r" + spaces works on every terminal, including legacy Windows * consoles where "\033[K" prints garbage unless VT processing is enabled. */ static void zxc_progress_clear(size_t len) { fprintf(stderr, "\r%*s\r", (int)len, ""); fflush(stderr); } /** * @brief Progress callback for CLI progress bar. * * The library fires this once per block -- at multi-GB/s rates that is far * more often than a terminal can display, so frames are throttled (100 ms on * a tty, 1 s otherwise) and each frame is emitted as a single write (stderr * is unbuffered: per-character output would be one syscall per character). * * Format: Compressing [=====> ] 45% | 4.5 GB/10.0 GB | 156.0 MB/s | ETA 0:35 */ static void cli_progress_callback(uint64_t bytes_processed, uint64_t bytes_total, const void* user_data) { (void)bytes_total; /* required by zxc_progress_callback_t */ progress_ctx_t* const pctx = (progress_ctx_t*)(uintptr_t)user_data; if (!pctx) return; const double now = zxc_now(); const double interval = pctx->to_tty ? 0.1 : 1.0; if (pctx->last_draw != 0.0 && now - pctx->last_draw < interval) return; pctx->last_draw = now; // Use pre-determined total size from context (not the parameter) const uint64_t total = pctx->total_size; const double elapsed = now - pctx->start_time; // Cumulative throughput double speed_mbps = 0.0; if (elapsed > 0.1) // Avoid division by zero for very fast operations speed_mbps = (double)bytes_processed / (1000.0 * 1000.0) / elapsed; char proc_str[32]; format_size_decimal(bytes_processed, proc_str, sizeof(proc_str)); char text[160]; int n; if (total > 0) { // Known size: percentage bar int percent = (int)((bytes_processed * 100) / total); if (percent > 100) percent = 100; enum { BAR_WIDTH = 20 }; char bar[BAR_WIDTH + 1]; const int filled = (percent * BAR_WIDTH) / 100; for (int i = 0; i < BAR_WIDTH; i++) { if (i < filled) bar[i] = '='; else if (i == filled) bar[i] = '>'; else bar[i] = ' '; } bar[BAR_WIDTH] = '\0'; // Estimated time to completion, from the cumulative throughput char eta[24] = ""; if (speed_mbps > 0.0 && total > bytes_processed) { const long secs = (long)((double)(total - bytes_processed) / (speed_mbps * 1e6)); if (secs >= 3600) snprintf(eta, sizeof(eta), " | ETA %ld:%02ld:%02ld", secs / 3600, (secs / 60) % 60, secs % 60); else snprintf(eta, sizeof(eta), " | ETA %ld:%02ld", secs / 60, secs % 60); } char total_str[32]; format_size_decimal(total, total_str, sizeof(total_str)); n = snprintf(text, sizeof(text), "%s [%s] %d%% | %s/%s | %.1f MB/s%s", pctx->operation, bar, percent, proc_str, total_str, speed_mbps, eta); } else { // Unknown size (stdin): just show bytes processed n = snprintf(text, sizeof(text), "%s %s | %.1f MB/s", pctx->operation, proc_str, speed_mbps); } if (n < 0) return; const size_t tlen = ((size_t)n < sizeof(text)) ? (size_t)n : sizeof(text) - 1; char frame[352]; size_t flen = 0; if (pctx->to_tty) { frame[flen++] = '\r'; memcpy(frame + flen, text, tlen); flen += tlen; // Pad with spaces to erase any residue from a longer previous line size_t visible = tlen; while (visible < pctx->last_len && flen < sizeof(frame)) { frame[flen++] = ' '; visible++; } pctx->last_len = tlen; } else { // Off-tty (--progress=always): plain newline-terminated updates memcpy(frame, text, tlen); flen = tlen; frame[flen++] = '\n'; } fwrite(frame, 1, flen, stderr); fflush(stderr); } /** * @brief Lists the contents of a ZXC archive. * * Reads the file header and footer to display: * - Compressed size * - Uncompressed size * - Compression ratio * - Checksum method * - Filename * * In verbose mode, displays additional header information. * * @param[in] path Path to the ZXC archive file. * @param[in] json_output If 1, output JSON format. * @return 0 on success, 1 on error. */ // Report a .zxd dictionary file: its dict_id (to match against a .zxc's // "Dict ID") and content size. `buf` holds the whole .zxd file. static int zxc_list_dict(const char* path, const uint8_t* buf, size_t buf_size, long long file_size, int json_output) { const void* content = NULL; size_t content_size = 0; uint32_t id = 0; const int rc = zxc_dict_load(buf, buf_size, &content, &content_size, NULL, &id); if (rc != ZXC_OK) { fprintf(stderr, "Error: invalid dictionary '%s': %s\n", path, zxc_error_name(rc)); return 1; } if (json_output) { printf( "{\n" " \"type\": \"dictionary\",\n" " \"filename\": \"%s\",\n" " \"dict_id\": \"0x%08X\",\n" " \"content_size_bytes\": %zu,\n" " \"file_size_bytes\": %lld\n" "}\n", path, id, content_size, file_size); } else { printf( "\n Dictionary file (.zxd)\n" " Dict ID: 0x%08X\n" " Content size: %zu bytes\n" " File: %s\n", id, content_size, path); } return 0; } static int zxc_list_archive(const char* path, int json_output) { char resolved_path[4096]; if (zxc_validate_input_path(path, resolved_path, sizeof(resolved_path)) != 0) { fprintf(stderr, "Error: Invalid input file '%s': %s\n", path, strerror(errno)); return 1; } FILE* f = fopen(resolved_path, "rb"); if (!f) { fprintf(stderr, "Error: Cannot open '%s': %s\n", path, strerror(errno)); return 1; } // Get file size if (fseeko(f, 0, SEEK_END) != 0) { fclose(f); fprintf(stderr, "Error: Cannot seek in file\n"); return 1; } const long long file_size = ftello(f); // A .zxd dictionary file has its own magic word; recognise it and report // its dict_id (for matching against a .zxc's "Dict ID") instead of failing // as a non-archive. The upper bound is the largest possible .zxd file. if (file_size >= (long long)ZXC_DICT_HEADER_SIZE && file_size <= (long long)zxc_dict_save_bound(ZXC_DICT_SIZE_MAX)) { uint8_t probe[ZXC_DICT_HEADER_SIZE]; if (fseeko(f, 0, SEEK_SET) == 0 && fread(probe, 1, ZXC_DICT_HEADER_SIZE, f) == ZXC_DICT_HEADER_SIZE && zxc_dict_get_id(probe, ZXC_DICT_HEADER_SIZE) != 0) { uint8_t* dbuf = (uint8_t*)malloc((size_t)file_size); int r = 1; if (dbuf && fseeko(f, 0, SEEK_SET) == 0 && fread(dbuf, 1, (size_t)file_size, f) == (size_t)file_size) r = zxc_list_dict(path, dbuf, (size_t)file_size, file_size, json_output); else fprintf(stderr, "Error: Cannot read '%s'\n", path); free(dbuf); fclose(f); return r; } fseeko(f, 0, SEEK_SET); } // Use public API to get decompressed size const int64_t uncompressed_size = zxc_stream_get_decompressed_size(f); if (uncompressed_size < 0) { fclose(f); fprintf(stderr, "Error: Not a valid ZXC archive\n"); return 1; } // Read header for format info (rewind after API call) uint8_t header[ZXC_FILE_HEADER_SIZE]; if (fseeko(f, 0, SEEK_SET) != 0 || fread(header, 1, ZXC_FILE_HEADER_SIZE, f) != ZXC_FILE_HEADER_SIZE) { fclose(f); fprintf(stderr, "Error: Cannot read file header\n"); return 1; } // Extract header fields const uint8_t format_version = header[4]; // Block size is stored at offset 5 as a log2 exponent (codes 12..21 = 2^code, // i.e. 4 KB..2 MB). Convert to KB. const uint8_t chunk_code = header[5]; size_t block_size_kb; if (chunk_code >= ZXC_BLOCK_SIZE_MIN_LOG2 && chunk_code <= ZXC_BLOCK_SIZE_MAX_LOG2) { block_size_kb = ((size_t)1U << chunk_code) / 1024; } else { block_size_kb = 0; // unknown / unsupported code } // Read footer for checksum info uint8_t footer[ZXC_FILE_FOOTER_SIZE]; if (fseeko(f, file_size - ZXC_FILE_FOOTER_SIZE, SEEK_SET) != 0 || fread(footer, 1, ZXC_FILE_FOOTER_SIZE, f) != ZXC_FILE_FOOTER_SIZE) { fclose(f); fprintf(stderr, "Error: Cannot read file footer\n"); return 1; } fclose(f); // Parse checksum (if non-zero, checksum was enabled) const uint32_t stored_checksum = footer[8] | ((uint32_t)footer[9] << 8) | ((uint32_t)footer[10] << 16) | ((uint32_t)footer[11] << 24); const char* checksum_method = (stored_checksum != 0) ? "RapidHash" : "-"; // Dictionary ID (from header flag bit 6 + bytes 7-10) const uint32_t dict_id = zxc_get_dict_id(header, ZXC_FILE_HEADER_SIZE); // Calculate ratio (uncompressed / compressed, e.g., 2.5 means 2.5x compression) const double ratio = (file_size > 0) ? ((double)uncompressed_size / (double)file_size) : 0.0; // Format sizes char comp_str[32]; char uncomp_str[32]; char dict_id_str[16]; format_size_decimal((uint64_t)file_size, comp_str, sizeof(comp_str)); format_size_decimal((uint64_t)uncompressed_size, uncomp_str, sizeof(uncomp_str)); if (dict_id) snprintf(dict_id_str, sizeof(dict_id_str), "0x%08X", dict_id); else snprintf(dict_id_str, sizeof(dict_id_str), "-"); if (json_output) { printf( "{\n" " \"filename\": \"%s\",\n" " \"compressed_size_bytes\": %lld,\n" " \"uncompressed_size_bytes\": %lld,\n" " \"compression_ratio\": %.3f,\n" " \"format_version\": %u,\n" " \"block_size_kb\": %zu,\n" " \"checksum_method\": \"%s\",\n" " \"checksum_value\": \"0x%08X\",\n" " \"dict_id\": %s%s%s\n" "}\n", path, file_size, (long long)uncompressed_size, ratio, format_version, block_size_kb, (stored_checksum != 0) ? "RapidHash" : "none", stored_checksum, dict_id ? "\"" : "", dict_id ? dict_id_str : "null", dict_id ? "\"" : ""); } else if (g_verbose) { // Verbose mode: detailed vertical layout printf( "\nFile: %s\n" "-----------------------\n" "Block Format: %u\n" "Block Size: %zu KB\n" "Checksum Method: %s\n", path, format_version, block_size_kb, (stored_checksum != 0) ? "RapidHash" : "None"); if (stored_checksum != 0) printf("Checksum Value: 0x%08X\n", stored_checksum); if (dict_id) printf("Dictionary ID: %s\n", dict_id_str); printf( "-----------------------\n" "Comp. Size: %s\n" "Uncomp. Size: %s\n" "Ratio: %.2f\n", comp_str, uncomp_str, ratio); } else { // Normal mode: table format printf("\n %12s %12s %5s %-10s %-10s %s\n", "Compressed", "Uncompressed", "Ratio", "Checksum", "Dict ID", "Filename"); printf(" %12s %12s %5.2f %-10s %-10s %s\n", comp_str, uncomp_str, ratio, checksum_method, dict_id_str, path); } return 0; } static int process_single_file(const char* in_path, const char* out_path_override, zxc_mode_t mode, int num_threads, int keep_input, int force, int to_stdout, int checksum_enabled, int level, size_t block_size, int json_output, int seekable, const void* dict, size_t dict_size) { FILE* f_in = stdin; FILE* f_out = stdout; char resolved_in_path[4096] = {0}; char out_path[4096] = {0}; char resolved_out_path[4096] = {0}; int use_stdin = 1; int use_stdout = 0; int created_out_file = 0; int overall_ret = 0; if (in_path && strcmp(in_path, "-") != 0) { if (zxc_validate_input_path(in_path, resolved_in_path, sizeof(resolved_in_path)) != 0) { zxc_log("Error: Invalid input file '%s': %s\n", in_path, strerror(errno)); return 1; } f_in = fopen(resolved_in_path, "rb"); if (!f_in) { zxc_log("Error open input %s: %s\n", resolved_in_path, strerror(errno)); return 1; } use_stdin = 0; } else { use_stdin = 1; use_stdout = 1; // Default to stdout if reading from stdin in_path = NULL; } if (mode == MODE_INTEGRITY) { use_stdout = 0; f_out = NULL; } else if (to_stdout) { use_stdout = 1; } else if (out_path_override) { // Explicit -o / positional output: honored for file and stdin input alike const int n = snprintf(out_path, sizeof(out_path), "%s", out_path_override); if (n < 0 || (size_t)n >= sizeof(out_path)) { zxc_log("Error: Output path too long\n"); if (!use_stdin) fclose(f_in); return 1; } use_stdout = 0; } else if (!use_stdin) { // Auto-generate output filename from the input filename if (mode == MODE_COMPRESS) { const int n = snprintf(out_path, sizeof(out_path), "%s.zxc", in_path); if (n < 0 || (size_t)n >= sizeof(out_path)) { zxc_log("Error: Output path too long\n"); fclose(f_in); return 1; } } else { const size_t len = strlen(in_path); if (len > 4 && !strcmp(in_path + len - 4, ".zxc")) { const size_t base_len = len - 4; if (base_len >= sizeof(out_path)) { zxc_log("Error: Output path too long\n"); fclose(f_in); return 1; } memcpy(out_path, in_path, base_len); out_path[base_len] = '\0'; } else { zxc_log("Error: Cannot determine output filename: '%s' does not end with .zxc\n", in_path); fclose(f_in); return 1; } } } // Open output file if not writing to stdout if (!use_stdout && mode != MODE_INTEGRITY) { if (zxc_validate_output_path(out_path, resolved_out_path, sizeof(resolved_out_path)) != 0) { zxc_log("Error: Invalid output path '%s': %s\n", out_path, strerror(errno)); if (!use_stdin) fclose(f_in); return 1; } // Safety check on resolved paths: opening the output with O_TRUNC would // destroy the input if both names refer to the same file if (!use_stdin && strcmp(resolved_in_path, resolved_out_path) == 0) { zxc_log("Error: Input and output files are identical for '%s'.\n", in_path); fclose(f_in); return 1; } if (!force && access(resolved_out_path, F_OK) == 0) { zxc_log("Output exists. Use -f to overwrite '%s'.\n", resolved_out_path); if (!use_stdin) fclose(f_in); return 1; } #ifdef _WIN32 f_out = fopen(resolved_out_path, "wb"); #else // Restrict permissions to 0644 const int fd = open(resolved_out_path, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (fd == -1) { zxc_log("Error creating output %s: %s\n", resolved_out_path, strerror(errno)); if (!use_stdin) fclose(f_in); return 1; } f_out = fdopen(fd, "wb"); #endif if (!f_out) { zxc_log("Error open output %s: %s\n", resolved_out_path, strerror(errno)); if (!use_stdin) fclose(f_in); #ifndef _WIN32 if (fd != -1) close(fd); #endif return 1; } created_out_file = 1; } // Prevent writing binary data to the terminal unless forced if (use_stdout && isatty(fileno(stdout)) && mode == MODE_COMPRESS && !force) { zxc_log( "Refusing to write compressed data to terminal.\n" "For help, type: zxc -h\n"); if (!use_stdin) fclose(f_in); return 1; } // Set stdin/stdout to binary mode if using them #ifdef _WIN32 if (use_stdin) _setmode(_fileno(stdin), _O_BINARY); if (use_stdout) _setmode(_fileno(stdout), _O_BINARY); #else // On POSIX systems, there's no text/binary distinction, but we ensure // no buffering issues occur by using freopen if needed if (use_stdin) { if (!freopen(NULL, "rb", stdin)) zxc_log("Warning: Failed to reopen stdin in binary mode\n"); } if (use_stdout) { if (!freopen(NULL, "wb", stdout)) zxc_log("Warning: Failed to reopen stdout in binary mode\n"); } #endif // Determine if we should show progress bar and get file size // IMPORTANT: This must be done BEFORE setting large buffers with setvbuf // to avoid buffer inconsistency issues when reading the footer int show_progress = 0; uint64_t total_size = 0; const int stderr_tty = isatty(fileno(stderr)) != 0; if (!g_quiet && g_progress_mode != ZXC_PROGRESS_NEVER && (g_progress_mode == ZXC_PROGRESS_ALWAYS || (!use_stdout && !use_stdin && stderr_tty))) { // Get the total size based on mode (only knowable for seekable file input) if (!use_stdin) { if (mode == MODE_COMPRESS) { // Compression: get input file size const long long saved_pos = ftello(f_in); if (saved_pos >= 0 && fseeko(f_in, 0, SEEK_END) == 0) { const long long size = ftello(f_in); if (size > 0) total_size = (uint64_t)size; fseeko(f_in, saved_pos, SEEK_SET); } } else { // Decompression: get decompressed size from footer (BEFORE starting decompression) const int64_t decomp_size = zxc_stream_get_decompressed_size(f_in); if (decomp_size > 0) total_size = (uint64_t)decomp_size; } } // auto: only show progress for files > 1MB; always: unconditionally if (g_progress_mode == ZXC_PROGRESS_ALWAYS || total_size > ZXC_STDIO_BUFFER_SIZE) show_progress = 1; } // Set large buffers for I/O performance (AFTER file size detection) char* b1 = malloc(ZXC_STDIO_BUFFER_SIZE); char* b2 = malloc(ZXC_STDIO_BUFFER_SIZE); if (b1) setvbuf(f_in, b1, _IOFBF, ZXC_STDIO_BUFFER_SIZE); if (f_out && b2) setvbuf(f_out, b2, _IOFBF, ZXC_STDIO_BUFFER_SIZE); if (mode == MODE_COMPRESS) zxc_log_v("Processing %s... (Compression Level %d)\n", in_path ? in_path : "", level); else zxc_log_v("Processing %s...\n", in_path ? in_path : ""); if (g_verbose) zxc_log("Checksum: %s\n", checksum_enabled ? "enabled" : "disabled"); if (g_verbose && seekable) zxc_log("Seekable: enabled\n"); // Prepare progress context progress_ctx_t pctx = {.start_time = zxc_now(), .operation = (mode == MODE_COMPRESS) ? "Compressing" : (mode == MODE_INTEGRITY) ? "Testing" : "Decompressing", .total_size = total_size, .to_tty = stderr_tty, .last_draw = 0.0, .last_len = 0}; const double t0 = zxc_now(); int64_t bytes; if (mode == MODE_COMPRESS) { zxc_compress_opts_t copts = { .n_threads = num_threads, .level = level, .block_size = block_size, .checksum_enabled = checksum_enabled, .seekable = seekable, .dict = dict, .dict_size = dict_size, .dict_huf = g_dict_huf, .progress_cb = show_progress ? &cli_progress_callback : NULL, .user_data = &pctx, }; bytes = zxc_stream_compress(f_in, f_out, &copts); } else { zxc_decompress_opts_t dopts = { .n_threads = num_threads, .checksum_enabled = checksum_enabled, .dict = dict, .dict_size = dict_size, .dict_huf = g_dict_huf, .progress_cb = show_progress ? &cli_progress_callback : NULL, .user_data = &pctx, }; bytes = zxc_stream_decompress(f_in, f_out, &dopts); } const double dt = zxc_now() - t0; // Clear the in-place progress line on completion (off-tty lines end in '\n') if (show_progress && pctx.to_tty) zxc_progress_clear(pctx.last_len); if (!use_stdin) fclose(f_in); else setvbuf(stdin, NULL, _IONBF, 0); // stdio defers write errors to flush/close: a failure here means the // output is truncated even though the codec reported success int write_error = 0; if (created_out_file) { write_error = (fclose(f_out) != 0); } else if (use_stdout) { write_error = (fflush(stdout) != 0); setvbuf(stdout, NULL, _IONBF, 0); } free(b1); free(b2); if (bytes >= 0 && write_error) { zxc_log("Error: %s: write failed: %s\n", created_out_file ? resolved_out_path : "", strerror(errno)); if (created_out_file) unlink(resolved_out_path); return 1; } if (bytes >= 0) { if (mode == MODE_INTEGRITY) { // Test mode: show result if (json_output) { printf( "{\n" " \"filename\": \"%s\",\n" " \"status\": \"ok\",\n" " \"checksum_verified\": %s,\n" " \"time_seconds\": %.6f\n" "}\n", in_path ? in_path : "", checksum_enabled ? "true" : "false", dt); } else if (g_verbose) { printf( "%s: OK\n" " Checksum: %s\n" " Time: %.3fs\n", in_path ? in_path : "", checksum_enabled ? "verified (RapidHash)" : "not verified", dt); } else { printf("%s: OK\n", in_path ? in_path : ""); } } else { zxc_log_v("Processed %lld bytes in %.3fs\n", (long long)bytes, dt); } if (!use_stdin && !use_stdout && !keep_input && !out_path_override && mode != MODE_INTEGRITY) unlink(resolved_in_path); } else { if (mode == MODE_INTEGRITY) { const int err_code = (int)bytes; const char* reason = zxc_error_name(err_code); const int needs_dict = (err_code == ZXC_ERROR_DICT_REQUIRED || err_code == ZXC_ERROR_DICT_MISMATCH); if (json_output) { printf( "{\n" " \"filename\": \"%s\",\n" " \"status\": \"failed\",\n" " \"error\": \"%s\"\n" "}\n", in_path ? in_path : "", reason); } else { fprintf(stderr, "%s: FAILED (%s)\n", in_path ? in_path : "", reason); if (needs_dict) fprintf(stderr, " This archive was compressed with a dictionary; pass it with -D.\n"); } } else { zxc_log("Error: %s: %s\n", in_path ? in_path : "", zxc_error_name((int)bytes)); if (created_out_file) unlink(resolved_out_path); } overall_ret = 1; } return overall_ret; } /** * @brief Main entry point. * Parses arguments and dispatches execution to Benchmark, Compress, or * Decompress modes. */ int main(int argc, char** argv) { zxc_mode_t mode = MODE_COMPRESS; /* When invoked as "unzxc" (typically a symlink to zxc), default to * decompression -- like unzstd / gunzip. An explicit -z/-d/-l/-t/-b below * still overrides this default. */ { const char* prog = (argc > 0 && argv[0]) ? argv[0] : "zxc"; const char* slash = strrchr(prog, '/'); #ifdef _WIN32 const char* bslash = strrchr(prog, '\\'); if (bslash && (!slash || bslash > slash)) slash = bslash; #endif const char* base = slash ? slash + 1 : prog; if (strstr(base, "unzxc")) mode = MODE_DECOMPRESS; } int num_threads = 0; int keep_input = 0; int force = 0; int to_stdout = 0; int bench_seconds = 5; int checksum = -1; int level = 3; int json_output = 0; size_t block_size = 0; int seekable = 0; const char* dict_path = NULL; const char* output_path = NULL; static const struct option long_options[] = {{"train", no_argument, 0, OPT_TRAIN_DICT}, {"progress", required_argument, 0, OPT_PROGRESS}, {"output", required_argument, 0, 'o'}, {"dict", required_argument, 0, 'D'}, {"compress", no_argument, 0, 'z'}, {"decompress", no_argument, 0, 'd'}, {"list", no_argument, 0, 'l'}, {"test", no_argument, 0, 't'}, {"bench", optional_argument, 0, 'b'}, {"threads", required_argument, 0, 'T'}, {"keep", no_argument, 0, 'k'}, {"force", no_argument, 0, 'f'}, {"stdout", no_argument, 0, 'c'}, {"verbose", no_argument, 0, 'v'}, {"quiet", no_argument, 0, 'q'}, {"checksum", no_argument, 0, 'C'}, {"no-checksum", no_argument, 0, 'N'}, {"json", no_argument, 0, 'j'}, {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {"multiple", no_argument, 0, 'm'}, {"recursive", no_argument, 0, 'r'}, {"block-size", required_argument, 0, 'B'}, {"seekable", no_argument, 0, 'S'}, {0, 0, 0, 0}}; int opt; int multiple_mode = 0; int recursive_mode = 0; while ((opt = getopt_long(argc, argv, "1234567b::B:cCdD:fho:jklmrNqST:tvVz", long_options, NULL)) != -1) { switch (opt) { case 'z': mode = MODE_COMPRESS; break; case 'd': mode = MODE_DECOMPRESS; break; case 'l': mode = MODE_LIST; break; case 't': mode = MODE_INTEGRITY; break; case 'b': { mode = MODE_BENCHMARK; const char* bench_arg = optarg; if (!bench_arg && optind < argc && argv[optind][0] >= '1' && argv[optind][0] <= '9') { // Consume the next argument as a duration only if it is all // digits, so a filename like "5samples.bin" is left alone const char* p = argv[optind] + 1; while (*p >= '0' && *p <= '9') p++; if (*p == '\0') bench_arg = argv[optind++]; } if (bench_arg) { char* end = NULL; const long secs = strtol(bench_arg, &end, 10); if (end == bench_arg || *end != '\0' || secs < 1 || secs > 3600) { fprintf(stderr, "Error: duration must be between 1 and 3600 seconds\n"); return 1; } bench_seconds = (int)secs; } break; } case '1': level = 1; break; case '2': level = 2; break; case '3': level = 3; break; case '4': level = 4; break; case '5': level = 5; break; case '6': level = 6; break; case '7': level = 7; break; case 'T': { char* end = NULL; const long threads = strtol(optarg, &end, 10); if (end == optarg || *end != '\0' || threads < 0 || threads > ZXC_MAX_THREADS) { fprintf(stderr, "Error: num_threads must be between 0 and %d\n", ZXC_MAX_THREADS); return 1; } num_threads = (int)threads; break; } case 'k': keep_input = 1; break; case 'f': force = 1; break; case 'c': to_stdout = 1; break; case 'v': g_verbose = 1; break; case 'q': g_quiet = 1; break; case 'C': checksum = 1; break; case 'N': checksum = 0; break; case 'j': json_output = 1; break; case 'm': multiple_mode = 1; break; case 'S': seekable = 1; break; case 'D': dict_path = optarg; break; case OPT_TRAIN_DICT: mode = MODE_TRAIN_DICT; break; case OPT_PROGRESS: if (optarg != NULL && strcmp(optarg, "auto") == 0) g_progress_mode = ZXC_PROGRESS_AUTO; else if (optarg != NULL && strcmp(optarg, "always") == 0) g_progress_mode = ZXC_PROGRESS_ALWAYS; else if (optarg != NULL && strcmp(optarg, "never") == 0) g_progress_mode = ZXC_PROGRESS_NEVER; else { fprintf(stderr, "Error: --progress must be 'auto', 'always' or 'never'\n"); return 1; } break; case 'o': output_path = optarg; break; case 'r': recursive_mode = 1; multiple_mode = 1; // Recursive implies multiple mode for files processing break; case 'B': { char* end = NULL; const long long bs_val = strtoll(optarg, &end, 10); long long multiplier = 1; if (*end == 'k' || *end == 'K') { multiplier = 1024; end++; if (*end == 'b' || *end == 'B') end++; // optional "B" in "KB" } else if (*end == 'm' || *end == 'M') { multiplier = 1024 * 1024; end++; if (*end == 'b' || *end == 'B') end++; // optional "B" in "MB" } // Bound the value before multiplying to avoid signed overflow const long long bs = (bs_val > 0 && bs_val <= (long long)ZXC_BLOCK_SIZE_MAX / multiplier) ? bs_val * multiplier : 0; if (end == optarg || *end != '\0' || bs < ZXC_BLOCK_SIZE_MIN || (bs & (bs - 1)) != 0) { fprintf(stderr, "Error: block-size must be a power of 2 between 4K and 2M\n" " Examples: -B 4K, -B 128K, -B 1M, -B 2M\n"); return 1; } block_size = (size_t)bs; break; } case '?': print_help(argv[0]); return 1; case 'V': print_version(); return 0; case 'h': print_help(argv[0]); return 0; default: return 1; } } // Handle positional arguments for mode selection (e.g., "zxc z file") if (optind < argc && mode != MODE_BENCHMARK) { if (strcmp(argv[optind], "z") == 0) { mode = MODE_COMPRESS; optind++; } else if (strcmp(argv[optind], "d") == 0) { mode = MODE_DECOMPRESS; optind++; } else if (strcmp(argv[optind], "l") == 0 || strcmp(argv[optind], "list") == 0) { mode = MODE_LIST; optind++; } else if (strcmp(argv[optind], "t") == 0 || strcmp(argv[optind], "test") == 0) { mode = MODE_INTEGRITY; optind++; } else if (strcmp(argv[optind], "b") == 0) { mode = MODE_BENCHMARK; optind++; } } if (checksum == -1) { checksum = (mode == MODE_BENCHMARK) ? 0 : 1; } /* Load dictionary file (.zxd) if requested */ void* dict = NULL; size_t dict_size = 0; if (dict_path) { char resolved_dict[4096]; if (zxc_validate_input_path(dict_path, resolved_dict, sizeof(resolved_dict)) != 0) { fprintf(stderr, "Error: invalid dictionary path '%s': %s\n", dict_path, strerror(errno)); return 1; } FILE* f_dict = fopen(resolved_dict, "rb"); if (!f_dict) { fprintf(stderr, "Error: cannot open dictionary '%s': %s\n", dict_path, strerror(errno)); return 1; } fseeko(f_dict, 0, SEEK_END); const long long fsize = ftello(f_dict); fseeko(f_dict, 0, SEEK_SET); if (fsize <= 0 || (size_t)fsize > ZXC_DICT_SIZE_MAX + ZXC_DICT_HEADER_SIZE + ZXC_HUF_TABLE_SIZE) { fprintf(stderr, "Error: dictionary file '%s' has invalid size\n", dict_path); fclose(f_dict); return 1; } uint8_t* zxd_buf = (uint8_t*)malloc((size_t)fsize); if (!zxd_buf || fread(zxd_buf, 1, (size_t)fsize, f_dict) != (size_t)fsize) { fprintf(stderr, "Error: failed to read dictionary '%s'\n", dict_path); free(zxd_buf); fclose(f_dict); return 1; } fclose(f_dict); const void* content = NULL; size_t content_size = 0; const void* huf = NULL; const int rc = zxc_dict_load(zxd_buf, (size_t)fsize, &content, &content_size, &huf, NULL); if (rc != ZXC_OK) { fprintf(stderr, "Error: invalid dictionary '%s': %s\n", dict_path, zxc_error_name(rc)); free(zxd_buf); return 1; } /* content_size is a file-derived length; zxc_dict_load already * validates it, but re-check the untrusted size at the alloc/copy * boundary so the bound governing memcpy is explicit at the sink. */ if (content_size == 0 || content_size > ZXC_DICT_SIZE_MAX) { fprintf(stderr, "Error: invalid dictionary '%s'\n", dict_path); free(zxd_buf); return 1; } dict = malloc(content_size); if (!dict) { free(zxd_buf); return 1; } memcpy(dict, content, content_size); dict_size = content_size; /* Shared literal Huffman table (zero-copy into zxd_buf; .zxd always * carries one, so huf is non-NULL after a successful load). */ if (huf) { g_dict_huf = malloc(ZXC_HUF_TABLE_SIZE); if (!g_dict_huf) { free(dict); free(zxd_buf); return 1; } memcpy(g_dict_huf, huf, ZXC_HUF_TABLE_SIZE); } free(zxd_buf); } /* * Train Dictionary Mode * Reads input files as samples, trains a dictionary, saves as .zxd. */ if (mode == MODE_TRAIN_DICT) { if (optind >= argc) { fprintf(stderr, "Error: --train requires input files as training samples.\n"); free(dict); return 1; } const int n_files = argc - optind; void** samples = (void**)malloc((size_t)n_files * sizeof(void*)); size_t* sample_sizes = (size_t*)malloc((size_t)n_files * sizeof(size_t)); if (!samples || !sample_sizes) { fprintf(stderr, "Error: memory allocation failed\n"); free(samples); free(sample_sizes); free(dict); return 1; } int n_loaded = 0; for (int i = optind; i < argc; i++) { char resolved[4096]; if (zxc_validate_input_path(argv[i], resolved, sizeof(resolved)) != 0) { fprintf(stderr, "Warning: invalid path '%s', skipping\n", argv[i]); continue; } FILE* sf = fopen(resolved, "rb"); if (!sf) { fprintf(stderr, "Warning: cannot open '%s', skipping\n", argv[i]); continue; } fseeko(sf, 0, SEEK_END); size_t sz = (size_t)ftello(sf); fseeko(sf, 0, SEEK_SET); if (sz == 0) { fclose(sf); continue; } uint8_t* buf = (uint8_t*)malloc(sz); if (!buf) { fclose(sf); continue; } const size_t rd = fread(buf, 1, sz, sf); fclose(sf); if (rd != sz) { fprintf(stderr, "Warning: short read on '%s', skipping\n", resolved); free(buf); continue; } samples[n_loaded] = buf; sample_sizes[n_loaded] = sz; n_loaded++; } if (n_loaded == 0) { fprintf(stderr, "Error: no valid samples loaded\n"); free(samples); free(sample_sizes); free(dict); return 1; } size_t dict_cap = ZXC_DICT_SIZE_MAX; if (block_size > 0 && block_size < dict_cap) dict_cap = block_size; uint8_t* dict_buf = (uint8_t*)malloc(dict_cap); if (!dict_buf) { fprintf(stderr, "Error: memory allocation failed\n"); for (int i = 0; i < n_loaded; i++) free(samples[i]); free(samples); free(sample_sizes); free(dict); return 1; } int64_t dict_sz = zxc_train_dict((const void* const*)samples, sample_sizes, (size_t)n_loaded, dict_buf, dict_cap); /* Train the shared literal Huffman table on the same samples (needs * the trained dict for the post-LZ literal distribution). The .zxd * format always carries the table, so a failure here is fatal. */ uint8_t huf_lengths[ZXC_HUF_TABLE_SIZE]; int huf_rc = ZXC_ERROR_NULL_INPUT; if (dict_sz > 0) { huf_rc = zxc_train_dict_huf((const void* const*)samples, sample_sizes, (size_t)n_loaded, dict_buf, (size_t)dict_sz, huf_lengths); } for (int i = 0; i < n_loaded; i++) free(samples[i]); free(samples); free(sample_sizes); if (dict_sz <= 0) { fprintf(stderr, "Error: training failed: %s\n", zxc_error_name((int)dict_sz)); free(dict_buf); free(dict); return 1; } if (huf_rc != ZXC_OK) { fprintf(stderr, "Error: literal table training failed: %s\n", zxc_error_name(huf_rc)); free(dict_buf); free(dict); return 1; } const size_t zxd_bound = zxc_dict_save_bound((size_t)dict_sz); uint8_t* zxd = (uint8_t*)malloc(zxd_bound); if (!zxd) { fprintf(stderr, "Error: memory allocation failed\n"); free(dict_buf); free(dict); return 1; } const int64_t zxd_sz = zxc_dict_save(dict_buf, (size_t)dict_sz, huf_lengths, zxd, zxd_bound); free(dict_buf); if (zxd_sz <= 0) { fprintf(stderr, "Error: dict save failed: %s\n", zxc_error_name((int)zxd_sz)); free(zxd); free(dict); return 1; } /* * Resolve the output path (from -o, optional). With no -o, write the * content-addressable name dictionary_{dict_id:08x}.zxd in the current * directory. If -o names a directory (or ends with a separator), use that * name inside it; otherwise write to the -o path verbatim. The id must be * computed before opening the file so it can name it. */ const uint32_t trained_id = zxc_dict_get_id(zxd, (size_t)zxd_sz); char final_path[4096]; if (!output_path) { snprintf(final_path, sizeof(final_path), "dictionary_%08x.zxd", trained_id); } else { const size_t op_len = strlen(output_path); const int is_dir_target = zxc_is_directory(output_path) || (op_len > 0 && (output_path[op_len - 1] == '/' || output_path[op_len - 1] == '\\')); if (is_dir_target) { const int has_sep = op_len > 0 && (output_path[op_len - 1] == '/' || output_path[op_len - 1] == '\\'); snprintf(final_path, sizeof(final_path), "%s%sdictionary_%08x.zxd", output_path, has_sep ? "" : "/", trained_id); } else { snprintf(final_path, sizeof(final_path), "%s", output_path); } } FILE* out; #ifdef _WIN32 out = fopen(final_path, "wb"); #else { const int fd = open(final_path, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); out = (fd != -1) ? fdopen(fd, "wb") : NULL; } #endif if (!out) { fprintf(stderr, "Error: cannot create '%s': %s\n", final_path, strerror(errno)); free(zxd); free(dict); return 1; } const size_t nwritten = fwrite(zxd, 1, (size_t)zxd_sz, out); const int close_rc = fclose(out); free(zxd); if (nwritten != (size_t)zxd_sz || close_rc != 0) { fprintf(stderr, "Error: failed to write '%s': %s\n", final_path, strerror(errno)); unlink(final_path); free(dict); return 1; } fprintf(stderr, "Trained dictionary: %lld bytes from %d samples -> %s (dict_id: 0x%08X)\n", (long long)dict_sz, n_loaded, final_path, trained_id); free(dict); return 0; } /* * Benchmark Mode * Loads the entire input file into RAM to measure raw algorithm throughput * without disk I/O bottlenecks. */ if (mode == MODE_BENCHMARK) { if (optind >= argc) { zxc_log("Benchmark requires input file.\n"); free(dict); return 1; } const char* in_path = argv[optind]; int ret = 1; uint8_t* ram = NULL; uint8_t* c_dat = NULL; FILE* fm = NULL; char resolved_path[4096]; if (zxc_validate_input_path(in_path, resolved_path, sizeof(resolved_path)) != 0) { zxc_log("Error: Invalid input file '%s': %s\n", in_path, strerror(errno)); free(dict); return 1; } const zxc_compress_opts_t bench_copts = {.n_threads = num_threads, .level = level, .block_size = block_size, .checksum_enabled = checksum, .dict = dict, .dict_size = dict_size, .dict_huf = g_dict_huf}; const zxc_decompress_opts_t bench_dopts = {.n_threads = num_threads, .checksum_enabled = checksum, .dict = dict, .dict_size = dict_size, .dict_huf = g_dict_huf}; FILE* f_in = fopen(resolved_path, "rb"); if (!f_in) goto bench_cleanup; if (fseeko(f_in, 0, SEEK_END) != 0) goto bench_cleanup; const long long fsize = ftello(f_in); if (fsize <= 0) goto bench_cleanup; const size_t in_size = (size_t)fsize; if (fseeko(f_in, 0, SEEK_SET) != 0) goto bench_cleanup; ram = malloc(in_size); if (!ram) goto bench_cleanup; if (fread(ram, 1, in_size, f_in) != in_size) goto bench_cleanup; fclose(f_in); f_in = NULL; if (!json_output) printf( "Input: %s (%zu bytes)\n" "Running for %d seconds (threads: %d)...\n", in_path, in_size, bench_seconds, num_threads); #ifdef _WIN32 if (!json_output) printf("Note: Using tmpfile on Windows (slower than fmemopen).\n"); fm = tmpfile(); if (fm) { fwrite(ram, 1, in_size, fm); rewind(fm); } #else fm = fmemopen(ram, in_size, "rb"); #endif if (!fm) goto bench_cleanup; double best_compress = 1e30; int compress_iters = 0; const double compress_deadline = zxc_now() + (double)bench_seconds; const double compress_start = zxc_now(); while (zxc_now() < compress_deadline) { rewind(fm); const double t0 = zxc_now(); zxc_stream_compress(fm, NULL, &bench_copts); const double dt = zxc_now() - t0; if (dt < best_compress) best_compress = dt; compress_iters++; if (!json_output && !g_quiet) fprintf(stderr, "\rCompressing... %d iters (%.1fs)", compress_iters, zxc_now() - compress_start); } if (!json_output && !g_quiet) zxc_progress_clear(64); fclose(fm); fm = NULL; const uint64_t max_c = zxc_compress_bound(in_size); c_dat = malloc((size_t)max_c); if (!c_dat) goto bench_cleanup; #ifdef _WIN32 FILE* fm_in = tmpfile(); FILE* fm_out = tmpfile(); if (!fm_in || !fm_out) { if (fm_in) fclose(fm_in); if (fm_out) fclose(fm_out); goto bench_cleanup; } fwrite(ram, 1, in_size, fm_in); rewind(fm_in); #else FILE* fm_in = fmemopen(ram, in_size, "rb"); FILE* fm_out = fmemopen(c_dat, max_c, "wb"); if (!fm_in || !fm_out) { if (fm_in) fclose(fm_in); if (fm_out) fclose(fm_out); goto bench_cleanup; } #endif const int64_t c_sz = zxc_stream_compress(fm_in, fm_out, &bench_copts); if (c_sz < 0) { fclose(fm_in); fclose(fm_out); fm_in = NULL; fm_out = NULL; goto bench_cleanup; } #ifdef _WIN32 rewind(fm_out); if (fread(c_dat, 1, (size_t)c_sz, fm_out) != (size_t)c_sz) { fclose(fm_in); fclose(fm_out); fm_in = NULL; fm_out = NULL; goto bench_cleanup; } fclose(fm_in); fclose(fm_out); #else fclose(fm_in); fclose(fm_out); #endif #ifdef _WIN32 FILE* fc = tmpfile(); if (!fc) goto bench_cleanup; fwrite(c_dat, 1, (size_t)c_sz, fc); rewind(fc); #else FILE* fc = fmemopen(c_dat, (size_t)c_sz, "rb"); if (!fc) goto bench_cleanup; #endif double best_decompress = 1e30; int decompress_iters = 0; const double decompress_deadline = zxc_now() + (double)bench_seconds; const double decompress_start = zxc_now(); while (zxc_now() < decompress_deadline) { rewind(fc); const double t0 = zxc_now(); zxc_stream_decompress(fc, NULL, &bench_dopts); const double dt = zxc_now() - t0; if (dt < best_decompress) best_decompress = dt; decompress_iters++; if (!json_output && !g_quiet) fprintf(stderr, "\rDecompressing... %d iters (%.1fs)", decompress_iters, zxc_now() - decompress_start); } if (!json_output && !g_quiet) zxc_progress_clear(64); fclose(fc); const double compress_speed_mbps = (double)in_size / (1000.0 * 1000.0) / best_compress; const double decompress_speed_mbps = (double)in_size / (1000.0 * 1000.0) / best_decompress; const double ratio = (c_sz > 0) ? ((double)in_size / c_sz) : 0.0; if (json_output) printf( "{\n" " \"input_file\": \"%s\",\n" " \"input_size_bytes\": %zu,\n" " \"compressed_size_bytes\": %lld,\n" " \"compression_ratio\": %.3f,\n" " \"duration_seconds\": %d,\n" " \"compress_iterations\": %d,\n" " \"decompress_iterations\": %d,\n" " \"threads\": %d,\n" " \"level\": %d,\n" " \"checksum_enabled\": %s,\n" " \"compress_speed_mbps\": %.3f,\n" " \"decompress_speed_mbps\": %.3f,\n" " \"compress_time_seconds\": %.6f,\n" " \"decompress_time_seconds\": %.6f\n" "}\n", in_path, in_size, (long long)c_sz, ratio, bench_seconds, compress_iters, decompress_iters, num_threads, level, checksum ? "true" : "false", compress_speed_mbps, decompress_speed_mbps, best_compress, best_decompress); else printf( "Compressed: %lld bytes (ratio %.3f)\n" "Compress : %.3f MB/s (%d iters)\n" "Decompress: %.3f MB/s (%d iters)\n", (long long)c_sz, ratio, compress_speed_mbps, compress_iters, decompress_speed_mbps, decompress_iters); ret = 0; bench_cleanup: if (fm) fclose(fm); if (f_in) fclose(f_in); free(ram); free(c_dat); free(dict); return ret; } /* * List Mode * Displays archive information (compressed size, uncompressed size, ratio). */ if (mode == MODE_LIST) { free(dict); if (optind >= argc) { zxc_log("List mode requires input file.\n"); return 1; } int ret = 0; const int num_files = argc - optind; if (json_output && num_files > 1) printf("[\n"); for (int i = optind; i < argc; i++) { const int r = zxc_list_archive(argv[i], json_output); // Keep the JSON array well-formed: a failed entry prints nothing, // so emit an error object in its place if (r != 0 && json_output) printf("{\n \"filename\": \"%s\",\n \"error\": \"cannot list file\"\n}\n", argv[i]); ret |= r; if (json_output && num_files > 1 && i < argc - 1) { printf(",\n"); } } if (json_output && num_files > 1) { printf("]\n"); } return ret; } if (multiple_mode && to_stdout) { zxc_log("Error: cannot write to stdout when using multiple files mode (-m).\n"); free(dict); return 1; } if (multiple_mode && output_path) { zxc_log("Error: cannot use -o with multiple files mode (-m/-r).\n"); free(dict); return 1; } /* * File Processing Mode * Loops over files and determines input/output paths. */ int overall_ret = 0; const int start_optind = optind; // If no files passed but we aren't using stdin, or mode expects files: if (optind >= argc && mode == MODE_INTEGRITY) { zxc_log("Test mode requires at least one input file.\n"); free(dict); return 1; } if (multiple_mode && optind >= argc) { zxc_log("Multiple files mode requires at least one input file.\n"); free(dict); return 1; } // Default to processing at least once (for stdin) if no files are passed and not in a mode that // strictly needs files const int num_files_to_process = (optind < argc) ? (argc - optind) : 1; for (int file_idx = 0; file_idx < num_files_to_process; file_idx++) { const char* current_arg = (optind < argc) ? argv[start_optind + file_idx] : NULL; if (recursive_mode && current_arg && strcmp(current_arg, "-") != 0 && zxc_is_directory(current_arg)) { overall_ret |= process_directory(current_arg, mode, num_threads, keep_input, force, to_stdout, checksum, level, block_size, json_output, seekable, dict, dict_size); } else { // -o takes precedence over a positional OUTPUT-FILE. const char* explicit_out_path = NULL; if (!multiple_mode && !to_stdout) { if (output_path) explicit_out_path = output_path; else if (optind + 1 < argc && current_arg && strcmp(current_arg, "-") != 0) explicit_out_path = argv[start_optind + 1]; } overall_ret |= process_single_file(current_arg, explicit_out_path, mode, num_threads, keep_input, force, to_stdout, checksum, level, block_size, json_output, seekable, dict, dict_size); } if (!multiple_mode) { break; // Standard mode only does the first argument as input } } free(dict); return overall_ret; } zxc-0.13.0/src/lib/000077500000000000000000000000001522536370400137435ustar00rootroot00000000000000zxc-0.13.0/src/lib/vendors/000077500000000000000000000000001522536370400154235ustar00rootroot00000000000000zxc-0.13.0/src/lib/vendors/rapidhash.h000066400000000000000000000514151522536370400175450ustar00rootroot00000000000000/* * rapidhash V3 - Very fast, high quality, platform-independent hashing algorithm. * * Based on 'wyhash', by Wang Yi * * Copyright (C) 2025 Nicolas De Carli * * 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. * * You can contact the author at: * - rapidhash source repository: https://github.com/Nicoshev/rapidhash */ #pragma once /* * Includes. */ #include #include #if defined(_MSC_VER) # include # if defined(_M_X64) && !defined(_M_ARM64EC) # pragma intrinsic(_umul128) # endif #endif /* * C/C++ macros. */ #ifdef _MSC_VER # define RAPIDHASH_ALWAYS_INLINE __forceinline #elif defined(__GNUC__) # define RAPIDHASH_ALWAYS_INLINE inline __attribute__((__always_inline__)) #else # define RAPIDHASH_ALWAYS_INLINE inline #endif #ifdef __cplusplus # define RAPIDHASH_NOEXCEPT noexcept # define RAPIDHASH_CONSTEXPR constexpr # ifndef RAPIDHASH_INLINE # define RAPIDHASH_INLINE RAPIDHASH_ALWAYS_INLINE # endif # if __cplusplus >= 201402L && !defined(_MSC_VER) # define RAPIDHASH_INLINE_CONSTEXPR RAPIDHASH_ALWAYS_INLINE constexpr # else # define RAPIDHASH_INLINE_CONSTEXPR RAPIDHASH_ALWAYS_INLINE # endif #else # define RAPIDHASH_NOEXCEPT # define RAPIDHASH_CONSTEXPR static const # ifndef RAPIDHASH_INLINE # define RAPIDHASH_INLINE static RAPIDHASH_ALWAYS_INLINE # endif # define RAPIDHASH_INLINE_CONSTEXPR RAPIDHASH_INLINE #endif /* * Unrolled macro. * Improves large input speed, but increases code size and worsens small input speed. * * RAPIDHASH_COMPACT: Normal behavior. * RAPIDHASH_UNROLLED: * */ #ifndef RAPIDHASH_UNROLLED # define RAPIDHASH_COMPACT #elif defined(RAPIDHASH_COMPACT) # error "cannot define RAPIDHASH_COMPACT and RAPIDHASH_UNROLLED simultaneously." #endif /* * Protection macro, alters behaviour of rapid_mum multiplication function. * * RAPIDHASH_FAST: Normal behavior, max speed. * RAPIDHASH_PROTECTED: Extra protection against entropy loss. */ #ifndef RAPIDHASH_PROTECTED # define RAPIDHASH_FAST #elif defined(RAPIDHASH_FAST) # error "cannot define RAPIDHASH_PROTECTED and RAPIDHASH_FAST simultaneously." #endif /* * Likely and unlikely macros. */ #if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__) # define _likely_(x) __builtin_expect(x,1) # define _unlikely_(x) __builtin_expect(x,0) #else # define _likely_(x) (x) # define _unlikely_(x) (x) #endif /* * Endianness macros. */ #ifndef RAPIDHASH_LITTLE_ENDIAN # if defined(_WIN32) || defined(__LITTLE_ENDIAN__) || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) # define RAPIDHASH_LITTLE_ENDIAN # elif defined(__BIG_ENDIAN__) || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) # define RAPIDHASH_BIG_ENDIAN # else # warning "could not determine endianness! Falling back to little endian." # define RAPIDHASH_LITTLE_ENDIAN # endif #endif /* * Default secret parameters. */ RAPIDHASH_CONSTEXPR uint64_t rapid_secret[8] = { 0x2d358dccaa6c78a5ull, 0x8bb84b93962eacc9ull, 0x4b33a62ed433d4a3ull, 0x4d5a2da51de1aa47ull, 0xa0761d6478bd642full, 0xe7037ed1a0b428dbull, 0x90ed1765281c388cull, 0xaaaaaaaaaaaaaaaaull}; /* * 64*64 -> 128bit multiply function. * * @param A Address of 64-bit number. * @param B Address of 64-bit number. * * Calculates 128-bit C = *A * *B. * * When RAPIDHASH_FAST is defined: * Overwrites A contents with C's low 64 bits. * Overwrites B contents with C's high 64 bits. * * When RAPIDHASH_PROTECTED is defined: * Xors and overwrites A contents with C's low 64 bits. * Xors and overwrites B contents with C's high 64 bits. */ RAPIDHASH_INLINE_CONSTEXPR void rapid_mum(uint64_t *A, uint64_t *B) RAPIDHASH_NOEXCEPT { #if defined(__SIZEOF_INT128__) __uint128_t r=*A; r*=*B; #ifdef RAPIDHASH_PROTECTED *A^=(uint64_t)r; *B^=(uint64_t)(r>>64); #else *A=(uint64_t)r; *B=(uint64_t)(r>>64); #endif #elif defined(_MSC_VER) && (defined(_WIN64) || defined(_M_HYBRID_CHPE_ARM64)) #if defined(_M_X64) #ifdef RAPIDHASH_PROTECTED uint64_t a, b; a=_umul128(*A,*B,&b); *A^=a; *B^=b; #else *A=_umul128(*A,*B,B); #endif #else #ifdef RAPIDHASH_PROTECTED uint64_t a, b; b = __umulh(*A, *B); a = *A * *B; *A^=a; *B^=b; #else uint64_t c = __umulh(*A, *B); *A = *A * *B; *B = c; #endif #endif #else uint64_t ha=*A>>32, hb=*B>>32, la=(uint32_t)*A, lb=(uint32_t)*B; uint64_t rh=ha*hb, rm0=ha*lb, rm1=hb*la, rl=la*lb, t=rl+(rm0<<32), c=t>32)+(rm1>>32)+c; #ifdef RAPIDHASH_PROTECTED *A^=lo; *B^=hi; #else *A=lo; *B=hi; #endif #endif } /* * Multiply and xor mix function. * * @param A 64-bit number. * @param B 64-bit number. * * Calculates 128-bit C = A * B. * Returns 64-bit xor between high and low 64 bits of C. */ RAPIDHASH_INLINE_CONSTEXPR uint64_t rapid_mix(uint64_t A, uint64_t B) RAPIDHASH_NOEXCEPT { rapid_mum(&A,&B); return A^B; } /* * Read functions. */ #ifdef RAPIDHASH_LITTLE_ENDIAN RAPIDHASH_INLINE uint64_t rapid_read64(const uint8_t *p) RAPIDHASH_NOEXCEPT { uint64_t v; memcpy(&v, p, sizeof(uint64_t)); return v;} RAPIDHASH_INLINE uint64_t rapid_read32(const uint8_t *p) RAPIDHASH_NOEXCEPT { uint32_t v; memcpy(&v, p, sizeof(uint32_t)); return v;} #elif defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__) RAPIDHASH_INLINE uint64_t rapid_read64(const uint8_t *p) RAPIDHASH_NOEXCEPT { uint64_t v; memcpy(&v, p, sizeof(uint64_t)); return __builtin_bswap64(v);} RAPIDHASH_INLINE uint64_t rapid_read32(const uint8_t *p) RAPIDHASH_NOEXCEPT { uint32_t v; memcpy(&v, p, sizeof(uint32_t)); return __builtin_bswap32(v);} #elif defined(_MSC_VER) RAPIDHASH_INLINE uint64_t rapid_read64(const uint8_t *p) RAPIDHASH_NOEXCEPT { uint64_t v; memcpy(&v, p, sizeof(uint64_t)); return _byteswap_uint64(v);} RAPIDHASH_INLINE uint64_t rapid_read32(const uint8_t *p) RAPIDHASH_NOEXCEPT { uint32_t v; memcpy(&v, p, sizeof(uint32_t)); return _byteswap_ulong(v);} #else RAPIDHASH_INLINE uint64_t rapid_read64(const uint8_t *p) RAPIDHASH_NOEXCEPT { uint64_t v; memcpy(&v, p, 8); return (((v >> 56) & 0xff)| ((v >> 40) & 0xff00)| ((v >> 24) & 0xff0000)| ((v >> 8) & 0xff000000)| ((v << 8) & 0xff00000000)| ((v << 24) & 0xff0000000000)| ((v << 40) & 0xff000000000000)| ((v << 56) & 0xff00000000000000)); } RAPIDHASH_INLINE uint64_t rapid_read32(const uint8_t *p) RAPIDHASH_NOEXCEPT { uint32_t v; memcpy(&v, p, 4); return (((v >> 24) & 0xff)| ((v >> 8) & 0xff00)| ((v << 8) & 0xff0000)| ((v << 24) & 0xff000000)); } #endif /* * rapidhash main function. * * @param key Buffer to be hashed. * @param len @key length, in bytes. * @param seed 64-bit seed used to alter the hash result predictably. * @param secret Triplet of 64-bit secrets used to alter hash result predictably. * * Returns a 64-bit hash. */ RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhash_internal(const void *key, size_t len, uint64_t seed, const uint64_t* secret) RAPIDHASH_NOEXCEPT { const uint8_t *p=(const uint8_t *)key; seed ^= rapid_mix(seed ^ secret[2], secret[1]); uint64_t a=0, b=0; size_t i = len; if (_likely_(len <= 16)) { if (len >= 4) { seed ^= len; if (len >= 8) { const uint8_t* plast = p + len - 8; a = rapid_read64(p); b = rapid_read64(plast); } else { const uint8_t* plast = p + len - 4; a = rapid_read32(p); b = rapid_read32(plast); } } else if (len > 0) { a = (((uint64_t)p[0])<<45)|p[len-1]; b = p[len>>1]; } else a = b = 0; } else { if (len > 112) { uint64_t see1 = seed, see2 = seed; uint64_t see3 = seed, see4 = seed; uint64_t see5 = seed, see6 = seed; #ifdef RAPIDHASH_COMPACT do { seed = rapid_mix(rapid_read64(p) ^ secret[0], rapid_read64(p + 8) ^ seed); see1 = rapid_mix(rapid_read64(p + 16) ^ secret[1], rapid_read64(p + 24) ^ see1); see2 = rapid_mix(rapid_read64(p + 32) ^ secret[2], rapid_read64(p + 40) ^ see2); see3 = rapid_mix(rapid_read64(p + 48) ^ secret[3], rapid_read64(p + 56) ^ see3); see4 = rapid_mix(rapid_read64(p + 64) ^ secret[4], rapid_read64(p + 72) ^ see4); see5 = rapid_mix(rapid_read64(p + 80) ^ secret[5], rapid_read64(p + 88) ^ see5); see6 = rapid_mix(rapid_read64(p + 96) ^ secret[6], rapid_read64(p + 104) ^ see6); p += 112; i -= 112; } while(i > 112); #else while (i > 224) { seed = rapid_mix(rapid_read64(p) ^ secret[0], rapid_read64(p + 8) ^ seed); see1 = rapid_mix(rapid_read64(p + 16) ^ secret[1], rapid_read64(p + 24) ^ see1); see2 = rapid_mix(rapid_read64(p + 32) ^ secret[2], rapid_read64(p + 40) ^ see2); see3 = rapid_mix(rapid_read64(p + 48) ^ secret[3], rapid_read64(p + 56) ^ see3); see4 = rapid_mix(rapid_read64(p + 64) ^ secret[4], rapid_read64(p + 72) ^ see4); see5 = rapid_mix(rapid_read64(p + 80) ^ secret[5], rapid_read64(p + 88) ^ see5); see6 = rapid_mix(rapid_read64(p + 96) ^ secret[6], rapid_read64(p + 104) ^ see6); seed = rapid_mix(rapid_read64(p + 112) ^ secret[0], rapid_read64(p + 120) ^ seed); see1 = rapid_mix(rapid_read64(p + 128) ^ secret[1], rapid_read64(p + 136) ^ see1); see2 = rapid_mix(rapid_read64(p + 144) ^ secret[2], rapid_read64(p + 152) ^ see2); see3 = rapid_mix(rapid_read64(p + 160) ^ secret[3], rapid_read64(p + 168) ^ see3); see4 = rapid_mix(rapid_read64(p + 176) ^ secret[4], rapid_read64(p + 184) ^ see4); see5 = rapid_mix(rapid_read64(p + 192) ^ secret[5], rapid_read64(p + 200) ^ see5); see6 = rapid_mix(rapid_read64(p + 208) ^ secret[6], rapid_read64(p + 216) ^ see6); p += 224; i -= 224; } if (i > 112) { seed = rapid_mix(rapid_read64(p) ^ secret[0], rapid_read64(p + 8) ^ seed); see1 = rapid_mix(rapid_read64(p + 16) ^ secret[1], rapid_read64(p + 24) ^ see1); see2 = rapid_mix(rapid_read64(p + 32) ^ secret[2], rapid_read64(p + 40) ^ see2); see3 = rapid_mix(rapid_read64(p + 48) ^ secret[3], rapid_read64(p + 56) ^ see3); see4 = rapid_mix(rapid_read64(p + 64) ^ secret[4], rapid_read64(p + 72) ^ see4); see5 = rapid_mix(rapid_read64(p + 80) ^ secret[5], rapid_read64(p + 88) ^ see5); see6 = rapid_mix(rapid_read64(p + 96) ^ secret[6], rapid_read64(p + 104) ^ see6); p += 112; i -= 112; } #endif seed ^= see1; see2 ^= see3; see4 ^= see5; seed ^= see6; see2 ^= see4; seed ^= see2; } if (i > 16) { seed = rapid_mix(rapid_read64(p) ^ secret[2], rapid_read64(p + 8) ^ seed); if (i > 32) { seed = rapid_mix(rapid_read64(p + 16) ^ secret[2], rapid_read64(p + 24) ^ seed); if (i > 48) { seed = rapid_mix(rapid_read64(p + 32) ^ secret[1], rapid_read64(p + 40) ^ seed); if (i > 64) { seed = rapid_mix(rapid_read64(p + 48) ^ secret[1], rapid_read64(p + 56) ^ seed); if (i > 80) { seed = rapid_mix(rapid_read64(p + 64) ^ secret[2], rapid_read64(p + 72) ^ seed); if (i > 96) { seed = rapid_mix(rapid_read64(p + 80) ^ secret[1], rapid_read64(p + 88) ^ seed); } } } } } } a=rapid_read64(p+i-16) ^ i; b=rapid_read64(p+i-8); } a ^= secret[1]; b ^= seed; rapid_mum(&a, &b); return rapid_mix(a ^ secret[7], b ^ secret[1] ^ i); } /* * rapidhashMicro main function. * * @param key Buffer to be hashed. * @param len @key length, in bytes. * @param seed 64-bit seed used to alter the hash result predictably. * @param secret Triplet of 64-bit secrets used to alter hash result predictably. * * Returns a 64-bit hash. */ RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhashMicro_internal(const void *key, size_t len, uint64_t seed, const uint64_t* secret) RAPIDHASH_NOEXCEPT { const uint8_t *p=(const uint8_t *)key; seed ^= rapid_mix(seed ^ secret[2], secret[1]); uint64_t a=0, b=0; size_t i = len; if (_likely_(len <= 16)) { if (len >= 4) { seed ^= len; if (len >= 8) { const uint8_t* plast = p + len - 8; a = rapid_read64(p); b = rapid_read64(plast); } else { const uint8_t* plast = p + len - 4; a = rapid_read32(p); b = rapid_read32(plast); } } else if (len > 0) { a = (((uint64_t)p[0])<<45)|p[len-1]; b = p[len>>1]; } else a = b = 0; } else { if (i > 80) { uint64_t see1 = seed, see2 = seed; uint64_t see3 = seed, see4 = seed; do { seed = rapid_mix(rapid_read64(p) ^ secret[0], rapid_read64(p + 8) ^ seed); see1 = rapid_mix(rapid_read64(p + 16) ^ secret[1], rapid_read64(p + 24) ^ see1); see2 = rapid_mix(rapid_read64(p + 32) ^ secret[2], rapid_read64(p + 40) ^ see2); see3 = rapid_mix(rapid_read64(p + 48) ^ secret[3], rapid_read64(p + 56) ^ see3); see4 = rapid_mix(rapid_read64(p + 64) ^ secret[4], rapid_read64(p + 72) ^ see4); p += 80; i -= 80; } while(i > 80); seed ^= see1; see2 ^= see3; seed ^= see4; seed ^= see2; } if (i > 16) { seed = rapid_mix(rapid_read64(p) ^ secret[2], rapid_read64(p + 8) ^ seed); if (i > 32) { seed = rapid_mix(rapid_read64(p + 16) ^ secret[2], rapid_read64(p + 24) ^ seed); if (i > 48) { seed = rapid_mix(rapid_read64(p + 32) ^ secret[1], rapid_read64(p + 40) ^ seed); if (i > 64) { seed = rapid_mix(rapid_read64(p + 48) ^ secret[1], rapid_read64(p + 56) ^ seed); } } } } a=rapid_read64(p+i-16) ^ i; b=rapid_read64(p+i-8); } a ^= secret[1]; b ^= seed; rapid_mum(&a, &b); return rapid_mix(a ^ secret[7], b ^ secret[1] ^ i); } /* * rapidhashNano main function. * * @param key Buffer to be hashed. * @param len @key length, in bytes. * @param seed 64-bit seed used to alter the hash result predictably. * @param secret Triplet of 64-bit secrets used to alter hash result predictably. * * Returns a 64-bit hash. */ RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhashNano_internal(const void *key, size_t len, uint64_t seed, const uint64_t* secret) RAPIDHASH_NOEXCEPT { const uint8_t *p=(const uint8_t *)key; seed ^= rapid_mix(seed ^ secret[2], secret[1]); uint64_t a=0, b=0; size_t i = len; if (_likely_(len <= 16)) { if (len >= 4) { seed ^= len; if (len >= 8) { const uint8_t* plast = p + len - 8; a = rapid_read64(p); b = rapid_read64(plast); } else { const uint8_t* plast = p + len - 4; a = rapid_read32(p); b = rapid_read32(plast); } } else if (len > 0) { a = (((uint64_t)p[0])<<45)|p[len-1]; b = p[len>>1]; } else a = b = 0; } else { if (i > 48) { uint64_t see1 = seed, see2 = seed; do { seed = rapid_mix(rapid_read64(p) ^ secret[0], rapid_read64(p + 8) ^ seed); see1 = rapid_mix(rapid_read64(p + 16) ^ secret[1], rapid_read64(p + 24) ^ see1); see2 = rapid_mix(rapid_read64(p + 32) ^ secret[2], rapid_read64(p + 40) ^ see2); p += 48; i -= 48; } while(i > 48); seed ^= see1; seed ^= see2; } if (i > 16) { seed = rapid_mix(rapid_read64(p) ^ secret[2], rapid_read64(p + 8) ^ seed); if (i > 32) { seed = rapid_mix(rapid_read64(p + 16) ^ secret[2], rapid_read64(p + 24) ^ seed); } } a=rapid_read64(p+i-16) ^ i; b=rapid_read64(p+i-8); } a ^= secret[1]; b ^= seed; rapid_mum(&a, &b); return rapid_mix(a ^ secret[7], b ^ secret[1] ^ i); } /* * rapidhash seeded hash function. * * @param key Buffer to be hashed. * @param len @key length, in bytes. * @param seed 64-bit seed used to alter the hash result predictably. * * Calls rapidhash_internal using provided parameters and default secrets. * * Returns a 64-bit hash. */ RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhash_withSeed(const void *key, size_t len, uint64_t seed) RAPIDHASH_NOEXCEPT { return rapidhash_internal(key, len, seed, rapid_secret); } /* * rapidhash general purpose hash function. * * @param key Buffer to be hashed. * @param len @key length, in bytes. * * Calls rapidhash_withSeed using provided parameters and the default seed. * * Returns a 64-bit hash. */ RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhash(const void *key, size_t len) RAPIDHASH_NOEXCEPT { return rapidhash_withSeed(key, len, 0); } /* * rapidhashMicro seeded hash function. * * Designed for HPC and server applications, where cache misses make a noticeable performance detriment. * Clang-18+ compiles it to ~140 instructions without stack usage, both on x86-64 and aarch64. * Faster for sizes up to 512 bytes, just 15%-20% slower for inputs above 1kb. * * @param key Buffer to be hashed. * @param len @key length, in bytes. * @param seed 64-bit seed used to alter the hash result predictably. * * Calls rapidhash_internal using provided parameters and default secrets. * * Returns a 64-bit hash. */ RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhashMicro_withSeed(const void *key, size_t len, uint64_t seed) RAPIDHASH_NOEXCEPT { return rapidhashMicro_internal(key, len, seed, rapid_secret); } /* * rapidhashMicro hash function. * * @param key Buffer to be hashed. * @param len @key length, in bytes. * * Calls rapidhash_withSeed using provided parameters and the default seed. * * Returns a 64-bit hash. */ RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhashMicro(const void *key, size_t len) RAPIDHASH_NOEXCEPT { return rapidhashMicro_withSeed(key, len, 0); } /* * rapidhashNano seeded hash function. * * @param key Buffer to be hashed. * @param len @key length, in bytes. * @param seed 64-bit seed used to alter the hash result predictably. * * Calls rapidhash_internal using provided parameters and default secrets. * * Returns a 64-bit hash. */ RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhashNano_withSeed(const void *key, size_t len, uint64_t seed) RAPIDHASH_NOEXCEPT { return rapidhashNano_internal(key, len, seed, rapid_secret); } /* * rapidhashNano hash function. * * Designed for Mobile and embedded applications, where keeping a small code size is a top priority. * Clang-18+ compiles it to less than 100 instructions without stack usage, both on x86-64 and aarch64. * The fastest for sizes up to 48 bytes, but may be considerably slower for larger inputs. * * @param key Buffer to be hashed. * @param len @key length, in bytes. * * Calls rapidhash_withSeed using provided parameters and the default seed. * * Returns a 64-bit hash. */ RAPIDHASH_INLINE_CONSTEXPR uint64_t rapidhashNano(const void *key, size_t len) RAPIDHASH_NOEXCEPT { return rapidhashNano_withSeed(key, len, 0); } zxc-0.13.0/src/lib/zxc_common.c000066400000000000000000001226201522536370400162660ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file zxc_common.c * @brief Shared library utilities: context management, header I/O, * compress-bound calculation, and error-code name lookup. * * This translation unit contains the functions shared by both the buffer and * streaming APIs. It is linked into every build of libzxc. */ #include "../../include/zxc_buffer.h" #include "../../include/zxc_error.h" #include "zxc_internal.h" /* * ============================================================================ * CONTEXT MANAGEMENT * ============================================================================ */ /** * @brief Allocates memory aligned to the specified boundary. * * Uses `_aligned_malloc` on Windows and `posix_memalign` elsewhere. * * @param[in] size Number of bytes to allocate. * @param[in] alignment Required alignment (must be a power of two). * @return Pointer to the allocated block, or @c NULL on failure. */ void* zxc_aligned_malloc(const size_t size, const size_t alignment) { #if defined(_WIN32) return _aligned_malloc(size, alignment); #else void* ptr = NULL; if (posix_memalign(&ptr, alignment, size) != 0) return NULL; return ptr; #endif } /** * @brief Frees memory previously allocated by zxc_aligned_malloc(). * * @param[in] ptr Pointer returned by zxc_aligned_malloc() (may be @c NULL). */ void zxc_aligned_free(void* ptr) { #if defined(_WIN32) _aligned_free(ptr); #else free(ptr); #endif } /** * @brief Returns @c sizeof(zxc_compress_opts_t) for ABI-safe allocation. * * Public API; see @c zxc_buffer.h. Lets callers (other languages, or a * different library version) size the options struct without knowing its layout. * * @return Size of @ref zxc_compress_opts_t in bytes. */ size_t zxc_compress_opts_size(void) { return sizeof(zxc_compress_opts_t); } /** * @brief Returns @c sizeof(zxc_decompress_opts_t) for ABI-safe allocation. * * Public API; see @c zxc_buffer.h. Lets callers (other languages, or a * different library version) size the options struct without knowing its layout. * * @return Size of @ref zxc_decompress_opts_t in bytes. */ size_t zxc_decompress_opts_size(void) { return sizeof(zxc_decompress_opts_t); } /* * Layout of the persistent buffer carved by every cctx/dctx init: both modes * (compress, decompress) compute the same offset table, used by the workspace * sizer and the in-place init. */ typedef struct { size_t total; /* mode == 0 (decompress) */ size_t off_work; size_t off_lit_dctx; /* mode == 0: scratch for a Huffman-coded GLO token section (enc_litlen == HUFFMAN). */ size_t off_tok_dctx; size_t sz_tok_dctx; /* mode == 0: PivCo decode level scratch (one chunk-sized ping-pong buffer). */ size_t off_pivco_dctx; size_t sz_pivco_dctx; /* mode == 1 (compress) */ size_t off_hash_pos; size_t off_hash_tags; size_t off_chain; size_t off_seq_union; size_t off_extras; size_t off_lit_cctx; /* meaningful only when sz_opt > 0 (level >= ZXC_LEVEL_DENSITY). */ size_t off_opt; /* both modes: [dict | data] concat scratch, present only when dict_size > 0. */ size_t off_dict; /* both modes: dict Huffman tree-at-attach state, present only when dict_size > 0. */ size_t off_dict_huf; /* Sub-buffer sizes (re-used by the partitioning step + zero-init). */ size_t sz_hash_pos; size_t sz_hash_tags; size_t sz_opt; size_t sz_dict; /* 0 = no dictionary buffer. */ size_t max_seq; } zxc_cctx_layout_t; /** * @brief Computes the single-allocation memory layout for a compression / * decompression context. * * Walks the same partition order used by @ref zxc_cctx_init_in_workspace and * records each sub-buffer's offset plus the running @c total, so the sizing * query and the partitioning step share one source of truth and can never * disagree. * * Decompress (@p mode == 0) reserves @c work_buf, @c lit_buffer (both padded * for wild-copy overshoot) and the token / PivCo decode scratch buffers. * Compress (@p mode == 1) reserves the LZ match-finder * tables (hash positions, tags, chain), the sequence / extras / literal buffers * and - only at @c level >= ZXC_LEVEL_DENSITY - the optimal-parser scratch. A * @p dict_size > 0 appends the [dict | data] concat scratch in both modes. * * Every offset is cache-line aligned via @c ZXC_ALIGN_CL. * * @param[in] chunk_size Block size in bytes. * @param[in] mode 1 = compression, 0 = decompression. * @param[in] level Compression level (only consulted when @p mode == 1). * @param[in] dict_size Dictionary prefill size; when > 0 the layout includes * the [dict | data] concat buffer. * @return Fully populated layout; @c .total is the required workspace size. */ /** * @brief Worst-case sequence count for one block. Shared by the compressor's * buffer sizing and the decoder's token scratch: the decode side must * accept exactly what the compress side can emit, so both derive from * this single expression. */ static ZXC_ALWAYS_INLINE size_t zxc_cctx_max_seq(const size_t chunk_size) { return chunk_size / ZXC_LZ_MIN_MATCH_LEN + 16; } /** * @brief Decode-side entropy scratch sizes for one block: token scratch * (worst-case sequence count + wild-read pad) and PivCo ping-pong * scratch. Single definition shared by the layout (full provisioning: * static workspaces) and the lazy heap allocator * (@ref zxc_cctx_alloc_entropy_scratch), so the two can never drift. */ static void zxc_dctx_entropy_sizes(const size_t chunk_size, size_t* RESTRICT sz_tok, size_t* RESTRICT sz_pivco) { *sz_tok = zxc_cctx_max_seq(chunk_size) + ZXC_PAD_SIZE; *sz_pivco = chunk_size + ZXC_PIVCO_SCRATCH_PAD; } static zxc_cctx_layout_t compute_cctx_layout(const size_t chunk_size, const int mode, const int level, const size_t dict_size, const int defer_entropy_scratch) { zxc_cctx_layout_t layout = {0}; const size_t max_seq = zxc_cctx_max_seq(chunk_size); if (mode == 0) { /* Decompress: work_buf + lit_buffer, both padded for wild-copy * overshoot and sized worst-case (chunk_size + ZXC_DECOMPRESS_TAIL_PAD). * lit_buffer is provisioned regardless of level because the decoder cannot * predict the per-block literal encoding (RAW / RLE / HUFFMAN). */ const size_t sz_work = chunk_size + ZXC_DECOMPRESS_TAIL_PAD; const size_t sz_lit = chunk_size + ZXC_PAD_SIZE; layout.off_work = layout.total; layout.total += ZXC_ALIGN_CL(sz_work); layout.off_lit_dctx = layout.total; layout.total += ZXC_ALIGN_CL(sz_lit); /* Token-section decode scratch (level-7 GLO blocks Huffman-code the * token stream) + PivCo ping-pong scratch. The decoder cannot predict * per-block enc_lit/enc_litlen, so static workspaces provision both * up front (no-alloc contract); heap contexts defer them to the first * entropy section instead (~1.2 x chunk_size that L1-5 archives never * pay), see zxc_cctx_alloc_entropy_scratch. */ if (!defer_entropy_scratch) { size_t sz_tok = 0; size_t sz_pivco = 0; zxc_dctx_entropy_sizes(chunk_size, &sz_tok, &sz_pivco); layout.sz_tok_dctx = sz_tok; layout.off_tok_dctx = layout.total; layout.total += ZXC_ALIGN_CL(layout.sz_tok_dctx); layout.sz_pivco_dctx = sz_pivco; layout.off_pivco_dctx = layout.total; layout.total += ZXC_ALIGN_CL(layout.sz_pivco_dctx); } } else { /* Compress: 6 partitions + optional opt_scratch at level >= ZXC_LEVEL_DENSITY. */ const uint32_t offset_bits = zxc_log2_u32((uint32_t)chunk_size); layout.max_seq = max_seq; layout.sz_hash_pos = ZXC_LZ_HASH_SIZE * sizeof(uint32_t); layout.sz_hash_tags = ZXC_LZ_HASH_SIZE * sizeof(uint8_t); const size_t sz_chain = ZXC_LZ_WINDOW_SIZE * sizeof(uint16_t); /* buf_sequences (GHI, level <= ZXC_LEVEL_FAST) aliases buf_offsets + buf_tokens (GLO, * level >= ZXC_LEVEL_DEFAULT). Mutually exclusive per block; sized for the larger. */ const size_t sz_seq_union = layout.max_seq * sizeof(uint32_t); const size_t vbyte_len = (offset_bits + 6) / 7; const size_t sz_extras = layout.max_seq * 2 * vbyte_len; const size_t sz_lit = chunk_size + ZXC_PAD_SIZE; /* opt_scratch (level >= ZXC_LEVEL_DENSITY only): DP arrays for the optimal parser, also * reused transiently as the package-merge scratch for the length-limited * Huffman code-length builder. Sized to the larger of the two demands. * The formula must stay in sync with zxc_estimate_cctx_size() and the * consumer in zxc_compress.c. */ if (level >= ZXC_LEVEL_DENSITY) { const size_t sz_dp = ZXC_ALIGN_CL((chunk_size + 1) * sizeof(uint32_t)); const size_t sz_pl = ZXC_ALIGN_CL((chunk_size + 1) * sizeof(uint16_t)); const size_t sz_po = ZXC_ALIGN_CL((chunk_size + 1) * sizeof(uint16_t)); const size_t n_bm_words = ZXC_BITMAP_WORDS(chunk_size + 1); const size_t sz_bm = ZXC_ALIGN_CL(n_bm_words * sizeof(uint64_t)); const size_t dp_needed = sz_dp + sz_pl + sz_po + sz_bm; layout.sz_opt = (dp_needed > ZXC_HUF_BUILD_SCRATCH_SIZE) ? dp_needed : ZXC_HUF_BUILD_SCRATCH_SIZE; } layout.off_hash_pos = layout.total; layout.total += ZXC_ALIGN_CL(layout.sz_hash_pos); layout.off_hash_tags = layout.total; layout.total += ZXC_ALIGN_CL(layout.sz_hash_tags); layout.off_chain = layout.total; layout.total += ZXC_ALIGN_CL(sz_chain); layout.off_seq_union = layout.total; layout.total += ZXC_ALIGN_CL(sz_seq_union); layout.off_extras = layout.total; layout.total += ZXC_ALIGN_CL(sz_extras); layout.off_lit_cctx = layout.total; layout.total += ZXC_ALIGN_CL(sz_lit); /* opt_scratch is appended last so it is absent for levels 1..5 (zero * waste on the common path) and only inflates the workspace at level 6. */ if (layout.sz_opt) { layout.off_opt = layout.total; layout.total += ZXC_ALIGN_CL(layout.sz_opt); } } /* [dict | data] concat scratch (dict only). Compress chunk_size already * spans [dict | block]; decompress prepends dict to a (chunk + PAD) region. */ if (dict_size > 0) { layout.sz_dict = (mode == 1) ? (chunk_size + ZXC_DECOMPRESS_TAIL_PAD) : (dict_size + chunk_size + ZXC_DECOMPRESS_TAIL_PAD); layout.off_dict = layout.total; layout.total += ZXC_ALIGN_CL(layout.sz_dict); layout.off_dict_huf = layout.total; layout.total += ZXC_ALIGN_CL(sizeof(zxc_dict_huf_state_t)); } return layout; } /** * @brief Returns the workspace byte count required for the given parameters. * * Public contract documented at the declaration in @c zxc_internal.h. Thin * wrapper that returns @c compute_cctx_layout(...).total, or 0 when * @p chunk_size is 0. * * @param[in] chunk_size Block size in bytes. * @param[in] mode 1 = compression, 0 = decompression. * @param[in] level Compression level (only consulted when @p mode == 1). * @param[in] dict_size Dictionary prefill size; > 0 includes the concat buffer. * @return Workspace size in bytes, or 0 if @p chunk_size is 0. */ size_t zxc_cctx_compute_workspace_size(const size_t chunk_size, const int mode, const int level, const size_t dict_size) { if (UNLIKELY(chunk_size == 0)) return 0; return compute_cctx_layout(chunk_size, mode, level, dict_size, 0).total; } /** * @brief Partitions a caller-supplied workspace into a ready-to-use context. * * Public contract (alignment, lifetime, return codes) documented at the * declaration in @c zxc_internal.h. Computes the layout via * @ref compute_cctx_layout, rejects an undersized @p workspace, then carves the * sub-buffers out of it. @c ctx->memory_block stays NULL so @ref zxc_cctx_free * leaves the caller-owned workspace untouched. * * @param[out] ctx Context to initialise. * @param[in] workspace Caller-allocated, cache-line-aligned buffer. * @param[in] workspace_size Capacity of @p workspace in bytes. * @param[in] chunk_size Block size in bytes. * @param[in] mode 1 = compression, 0 = decompression. * @param[in] level Compression level (ignored when @p mode == 0). * @param[in] checksum_enabled Non-zero to enable checksum computation. * @param[in] dict_size Dictionary prefill size; > 0 carves the concat buffer. * @return @ref ZXC_OK, @ref ZXC_ERROR_NULL_INPUT, or @ref ZXC_ERROR_DST_TOO_SMALL. */ int zxc_cctx_init_in_workspace(zxc_cctx_t* RESTRICT ctx, void* RESTRICT workspace, const size_t workspace_size, const size_t chunk_size, const int mode, const int level, const int checksum_enabled, const size_t dict_size, const int defer_entropy_scratch) { if (UNLIKELY(!ctx || !workspace || chunk_size == 0)) return ZXC_ERROR_NULL_INPUT; const zxc_cctx_layout_t layout = compute_cctx_layout(chunk_size, mode, level, dict_size, defer_entropy_scratch); if (UNLIKELY(workspace_size < layout.total)) return ZXC_ERROR_DST_TOO_SMALL; ZXC_MEMSET(ctx, 0, sizeof(zxc_cctx_t)); ctx->checksum_enabled = checksum_enabled; ctx->chunk_size = chunk_size; const uint32_t offset_bits = zxc_log2_u32((uint32_t)chunk_size); ctx->offset_bits = offset_bits; ctx->offset_mask = (uint32_t)((1ULL << offset_bits) - 1); ctx->max_epoch = (uint32_t)(1ULL << (32 - offset_bits)); /* memory_block stays NULL on the static-init path so zxc_cctx_free does * not try to free the caller's workspace. Sub-buffer pointers carry the * partition; ownership is implicit (the caller owns @p workspace). */ uint8_t* const mem = (uint8_t*)workspace; /* Dictionary concat scratch (both modes); init owns dict_size now so callers * no longer assign ctx->dict_size after init. */ ctx->dict_size = dict_size; if (dict_size > 0) { ctx->dict_buffer = mem + layout.off_dict; ctx->dict_buffer_cap = layout.sz_dict; ctx->dict_huf = (zxc_dict_huf_state_t*)(void*)(mem + layout.off_dict_huf); } if (mode == 0) { ctx->work_buf = mem + layout.off_work; ctx->work_buf_cap = chunk_size + ZXC_DECOMPRESS_TAIL_PAD; ctx->lit_buffer = mem + layout.off_lit_dctx; ctx->lit_buffer_cap = chunk_size + ZXC_PAD_SIZE; if (layout.sz_pivco_dctx) { ctx->tok_buffer = mem + layout.off_tok_dctx; ctx->tok_buffer_cap = layout.sz_tok_dctx; ctx->pivco_scratch = mem + layout.off_pivco_dctx; ctx->pivco_scratch_cap = layout.sz_pivco_dctx; } return ZXC_OK; } ctx->hash_table = (uint32_t*)(mem + layout.off_hash_pos); ctx->hash_tags = mem + layout.off_hash_tags; ctx->chain_table = (uint16_t*)(mem + layout.off_chain); ctx->buf_sequences = (uint32_t*)(mem + layout.off_seq_union); ctx->buf_offsets = (uint16_t*)(mem + layout.off_seq_union); ctx->buf_tokens = mem + layout.off_seq_union + layout.max_seq * sizeof(uint16_t); ctx->buf_extras = mem + layout.off_extras; ctx->literals = mem + layout.off_lit_cctx; if (layout.sz_opt) { ctx->opt_scratch = mem + layout.off_opt; ctx->opt_scratch_cap = layout.sz_opt; } ctx->compression_level = level; ctx->epoch = 1; ZXC_MEMSET(ctx->hash_table, 0, layout.sz_hash_pos); ZXC_MEMSET(ctx->hash_tags, 0, layout.sz_hash_tags); return ZXC_OK; } /** * @brief Initialises a compression / decompression context, allocating the * persistent buffer with @c ZXC_ALIGNED_MALLOC. * * Thin wrapper around @ref zxc_cctx_init_in_workspace: sizes the buffer via * @ref zxc_cctx_compute_workspace_size, allocates it, then partitions it. * The pointer is stored in @c ctx->memory_block so @ref zxc_cctx_free can * release it. The static-cctx public API (see @c zxc_buffer.h) bypasses * this wrapper and partitions a caller-supplied workspace directly. * * @param[out] ctx Context to initialise. * @param[in] chunk_size Block size in bytes. * @param[in] mode 1 = compression, 0 = decompression. * @param[in] level Compression level (ignored when @p mode == 0). * @param[in] checksum_enabled Non-zero to enable checksum computation. * @param[in] dict_size Dictionary prefill size. * @return @ref ZXC_OK on success, @ref ZXC_ERROR_MEMORY on allocation failure. */ int zxc_cctx_init(zxc_cctx_t* RESTRICT ctx, const size_t chunk_size, const int mode, const int level, const int checksum_enabled, const size_t dict_size) { if (UNLIKELY(chunk_size == 0)) return ZXC_ERROR_NULL_INPUT; /* Defer the decode-side entropy scratch allocation to the first entropy * section (zxc_cctx_alloc_entropy_scratch) when the caller is a heap context. * Static workspaces must provision it up front (no allocation is ever allowed * later). */ const int defer_entropy = (mode == 0); const size_t total = compute_cctx_layout(chunk_size, mode, level, dict_size, defer_entropy).total; if (UNLIKELY(total == 0)) return ZXC_ERROR_NULL_INPUT; uint8_t* const mem = (uint8_t*)ZXC_ALIGNED_MALLOC(total, ZXC_CACHE_LINE_SIZE); if (UNLIKELY(!mem)) return ZXC_ERROR_MEMORY; const int rc = zxc_cctx_init_in_workspace(ctx, mem, total, chunk_size, mode, level, checksum_enabled, dict_size, defer_entropy); if (UNLIKELY(rc != ZXC_OK)) { // LCOV_EXCL_START ZXC_ALIGNED_FREE(mem); return rc; // LCOV_EXCL_STOP } /* Library-owned buffer: record the allocation so zxc_cctx_free frees it. */ ctx->memory_block = mem; return ZXC_OK; } /** * @brief Lazily allocates the deferred decode-side entropy scratch. * * Public contract at the declaration in @c zxc_internal.h. One aligned block * carries [tok_buffer | pivco_scratch], sized by @ref zxc_dctx_entropy_sizes * (the same source the full-provision layout uses), owned by the context and * released by @ref zxc_cctx_free. */ int zxc_cctx_alloc_entropy_scratch(zxc_cctx_t* ctx) { if (LIKELY(ctx->pivco_scratch != NULL)) return ZXC_OK; size_t sz_tok = 0; size_t sz_pivco = 0; zxc_dctx_entropy_sizes(ctx->chunk_size, &sz_tok, &sz_pivco); const size_t total = ZXC_ALIGN_CL(sz_tok) + ZXC_ALIGN_CL(sz_pivco); uint8_t* const mem = (uint8_t*)ZXC_ALIGNED_MALLOC(total, ZXC_CACHE_LINE_SIZE); if (UNLIKELY(!mem)) return ZXC_ERROR_MEMORY; // LCOV_EXCL_LINE ctx->entropy_block = mem; ctx->tok_buffer = mem; ctx->tok_buffer_cap = sz_tok; ctx->pivco_scratch = mem + ZXC_ALIGN_CL(sz_tok); ctx->pivco_scratch_cap = sz_pivco; return ZXC_OK; } /** * @brief Releases all resources owned by a compression context. * * After this call every pointer inside @p ctx is @c NULL and the context * may be safely re-initialised with zxc_cctx_init(). * * @param[in,out] ctx Context to tear down. */ void zxc_cctx_free(zxc_cctx_t* ctx) { if (ctx->memory_block) { ZXC_ALIGNED_FREE(ctx->memory_block); ctx->memory_block = NULL; } if (ctx->entropy_block) { ZXC_ALIGNED_FREE(ctx->entropy_block); ctx->entropy_block = NULL; } ctx->lit_buffer = NULL; ctx->hash_table = NULL; ctx->hash_tags = NULL; ctx->chain_table = NULL; ctx->buf_sequences = NULL; ctx->buf_tokens = NULL; ctx->buf_offsets = NULL; ctx->buf_extras = NULL; ctx->literals = NULL; ctx->work_buf = NULL; ctx->tok_buffer = NULL; ctx->pivco_scratch = NULL; ctx->opt_scratch = NULL; ctx->dict_buffer = NULL; ctx->dict_huf = NULL; ctx->epoch = 0; ctx->lit_buffer_cap = 0; ctx->work_buf_cap = 0; ctx->tok_buffer_cap = 0; ctx->pivco_scratch_cap = 0; ctx->opt_scratch_cap = 0; ctx->dict_buffer_cap = 0; ctx->dict_size = 0; ctx->dict_huf_tree_ok = 0; ctx->lit_freq_acc = NULL; } /** * @brief Attach the shared dictionary literal Huffman table to a context. * * Validates the 128-byte packed code-lengths header and builds the dictionary's * PivCo tree, canonical codes and code lengths ONCE into the context * (tree-at-attach); per-block encode/estimate/decode reuse them. @p lengths need * only be valid during this call (the tree is a copy). A NULL @p lengths is a no-op. * * @param[in,out] ctx Initialised context to attach the table to. * @param[in] lengths 128-byte packed code lengths, or NULL for a no-op. * @return @ref ZXC_OK on success, @ref ZXC_ERROR_CORRUPT_DATA if @p lengths is * structurally invalid (bad nibble, Kraft inequality). */ int zxc_cctx_attach_dict_huf(zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT lengths) { if (UNLIKELY(!ctx)) return ZXC_ERROR_NULL_INPUT; ctx->dict_huf_tree_ok = 0; if (lengths == NULL) return ZXC_OK; /* Empty (all-zero) table from a low-entropy corpus: treat it as "no shared table". */ int empty = 1; for (size_t i = 0; i < ZXC_HUF_TABLE_SIZE; i++) { if (lengths[i]) { empty = 0; break; } } if (UNLIKELY(empty || ctx->dict_huf == NULL)) return ZXC_OK; /* Tree-at-attach: unpack + build the PivCo tree, codes and decoder tables * once here; the per-block encode/estimate/decode paths reuse them via * the context. */ const int rc = zxc_huf_dict_tree_build(lengths, &ctx->dict_huf->tree, ctx->dict_huf->codes, ctx->dict_huf->code_len, &ctx->dict_huf->dec); if (UNLIKELY(rc != ZXC_OK)) return rc; ctx->dict_huf_tree_ok = 1; return ZXC_OK; } /* * ============================================================================ * HEADER I/O * ============================================================================ */ /** * @brief Serialises a ZXC file header into @p dst. * * Layout (16 bytes): Magic (4) | Version (1) | Chunk (1) | Flags (1) | * Reserved (7) | CRC-16 (2). * * @param[out] dst Destination buffer (>= @ref ZXC_FILE_HEADER_SIZE bytes). * @param[in] dst_capacity Capacity of @p dst. * @param[in] chunk_size Block size (stored as its log2 exponent). * @param[in] has_checksum Non-zero to set the checksum flag. * @param[in] dict_id Dictionary id; when non-zero, sets the dictionary flag * and is stored in the header. * @return Number of bytes written (@ref ZXC_FILE_HEADER_SIZE) on success, * or a negative @ref zxc_error_t code. */ int zxc_write_file_header(uint8_t* RESTRICT dst, const size_t dst_capacity, const size_t chunk_size, const int has_checksum, const uint32_t dict_id) { if (UNLIKELY(dst_capacity < ZXC_FILE_HEADER_SIZE)) return ZXC_ERROR_DST_TOO_SMALL; zxc_store_le32(dst, ZXC_MAGIC_WORD); dst[4] = ZXC_FILE_FORMAT_VERSION; // Block size stored as log2 exponent (e.g. 18 = 256 KB) dst[5] = (uint8_t)zxc_log2_u32((uint32_t)chunk_size); // Flags are at offset 6 uint8_t flags = has_checksum ? (ZXC_FILE_FLAG_HAS_CHECKSUM | ZXC_CHECKSUM_RAPIDHASH) : 0; if (dict_id != 0) flags |= ZXC_FILE_FLAG_HAS_DICTIONARY; dst[6] = flags; // Bytes 7-13: Reserved / dict_id ZXC_MEMSET(dst + 7, 0, 7); if (dict_id != 0) zxc_store_le32(dst + 7, dict_id); // Bytes 14-15: CRC (16-bit) zxc_store_le16(dst + 14, 0); // Zero out before hashing const uint16_t crc = zxc_hash16(dst); zxc_store_le16(dst + 14, crc); return ZXC_FILE_HEADER_SIZE; } /** * @brief Parses and validates a ZXC file header from @p src. * * Checks the magic word, format version, and CRC-16. * * @param[in] src Source buffer (>= @ref ZXC_FILE_HEADER_SIZE bytes). * @param[in] src_size Size of @p src. * @param[out] out_block_size Receives the decoded block size (may be @c NULL). * @param[out] out_has_checksum Receives 1 if checksums are present, 0 otherwise * (may be @c NULL). * @param[out] out_dict_id Receives the dictionary id, or 0 if none * (may be @c NULL). * @return @ref ZXC_OK on success, or a negative @ref zxc_error_t code. */ int zxc_read_file_header(const uint8_t* RESTRICT src, const size_t src_size, size_t* RESTRICT out_block_size, int* RESTRICT out_has_checksum, uint32_t* RESTRICT out_dict_id) { if (UNLIKELY(src_size < ZXC_FILE_HEADER_SIZE)) return ZXC_ERROR_SRC_TOO_SMALL; if (UNLIKELY(zxc_le32(src) != ZXC_MAGIC_WORD)) return ZXC_ERROR_BAD_MAGIC; if (UNLIKELY(src[4] != ZXC_FILE_FORMAT_VERSION)) return ZXC_ERROR_BAD_VERSION; uint8_t temp[ZXC_FILE_HEADER_SIZE]; ZXC_MEMCPY(temp, src, ZXC_FILE_HEADER_SIZE); // Zero out CRC bytes (14-15) before hash check temp[14] = 0; temp[15] = 0; // Header CRC16 (integrity), then the checksum-algorithm id in flags bits 0-3 // (only 0 = RapidHash is defined). CRC is checked first via short-circuit. if (UNLIKELY(zxc_le16(src + 14) != zxc_hash16(temp) || (src[6] & 0x0FU) != ZXC_CHECKSUM_RAPIDHASH)) return ZXC_ERROR_BAD_HEADER; if (out_block_size) { const uint8_t code = src[5]; if (UNLIKELY(code < ZXC_BLOCK_SIZE_MIN_LOG2 || code > ZXC_BLOCK_SIZE_MAX_LOG2)) return ZXC_ERROR_BAD_BLOCK_SIZE; // Exponent encoding: block_size = 2^code (4 KB - 2 MB) *out_block_size = (size_t)1U << code; } // Flags are at offset 6 if (out_has_checksum) *out_has_checksum = (src[6] & ZXC_FILE_FLAG_HAS_CHECKSUM) ? 1 : 0; if (out_dict_id) *out_dict_id = (src[6] & ZXC_FILE_FLAG_HAS_DICTIONARY) ? zxc_le32(src + 7) : 0; return ZXC_OK; } /** * @brief Serialises a block header (8 bytes) into @p dst. * * @param[out] dst Destination buffer (>= @ref ZXC_BLOCK_HEADER_SIZE bytes). * @param[in] dst_capacity Capacity of @p dst. * @param[in] bh Populated block header descriptor. * @return Number of bytes written (@ref ZXC_BLOCK_HEADER_SIZE) on success, * or a negative @ref zxc_error_t code. */ int zxc_write_block_header(uint8_t* RESTRICT dst, const size_t dst_capacity, const zxc_block_header_t* RESTRICT bh) { if (UNLIKELY(dst_capacity < ZXC_BLOCK_HEADER_SIZE)) return ZXC_ERROR_DST_TOO_SMALL; dst[0] = bh->block_type; dst[1] = 0; // Flags not used currently dst[2] = 0; // Reserved zxc_store_le32(dst + 3, bh->comp_size); dst[7] = 0; // Zero before hashing dst[7] = zxc_hash8(dst); // Checksum at the end return ZXC_BLOCK_HEADER_SIZE; } /** * @brief Parses and validates a block header from @p src. * * Validates the 8-bit CRC embedded in the header. * * @param[in] src Source buffer (>= @ref ZXC_BLOCK_HEADER_SIZE bytes). * @param[in] src_size Size of @p src. * @param[out] bh Receives the decoded block header fields. * @return @ref ZXC_OK on success, or a negative @ref zxc_error_t code. */ int zxc_read_block_header(const uint8_t* RESTRICT src, const size_t src_size, zxc_block_header_t* RESTRICT bh) { if (UNLIKELY(src_size < ZXC_BLOCK_HEADER_SIZE)) return ZXC_ERROR_SRC_TOO_SMALL; uint8_t temp[ZXC_BLOCK_HEADER_SIZE]; ZXC_MEMCPY(temp, src, ZXC_BLOCK_HEADER_SIZE); temp[7] = 0; // Zero out checksum byte before hashing if (UNLIKELY(src[7] != zxc_hash8(temp))) return ZXC_ERROR_BAD_HEADER; bh->block_type = src[0]; bh->block_flags = 0; // Flags not used currently bh->reserved = src[2]; bh->comp_size = zxc_le32(src + 3); bh->header_crc = src[7]; return ZXC_OK; } /** * @brief Writes the 12-byte file footer (source size + global checksum). * * @param[out] dst Destination buffer (>= @ref ZXC_FILE_FOOTER_SIZE bytes). * @param[in] dst_capacity Capacity of @p dst. * @param[in] src_size Original uncompressed size in bytes. * @param[in] global_hash Accumulated global checksum value. * @param[in] checksum_enabled Non-zero to write the checksum; zero to zero-fill. * @return Number of bytes written (@ref ZXC_FILE_FOOTER_SIZE) on success, * or a negative @ref zxc_error_t code. */ int zxc_write_file_footer(uint8_t* RESTRICT dst, const size_t dst_capacity, const uint64_t src_size, const uint32_t global_hash, const int checksum_enabled) { if (UNLIKELY(dst_capacity < ZXC_FILE_FOOTER_SIZE)) return ZXC_ERROR_DST_TOO_SMALL; zxc_store_le64(dst, src_size); if (checksum_enabled) { zxc_store_le32(dst + sizeof(uint64_t), global_hash); } else { ZXC_MEMSET(dst + sizeof(uint64_t), 0, sizeof(uint32_t)); } return ZXC_FILE_FOOTER_SIZE; } /** * @brief Serialises a GLO block header followed by its section descriptors. * * @param[out] dst Destination buffer. * @param[in] rem Remaining capacity of @p dst. * @param[in] gh Populated GLO header descriptor. * @param[in] desc Array of @ref ZXC_GLO_SECTIONS section descriptors. * @return Total bytes written on success, or a negative @ref zxc_error_t code. */ int zxc_write_glo_header_and_desc(uint8_t* RESTRICT dst, const size_t rem, const zxc_gnr_header_t* RESTRICT gh, const zxc_section_desc_t desc[ZXC_GLO_SECTIONS]) { const size_t needed = ZXC_GLO_HEADER_BINARY_SIZE + ZXC_GLO_SECTIONS * ZXC_SECTION_DESC_BINARY_SIZE; if (UNLIKELY(rem < needed)) return ZXC_ERROR_DST_TOO_SMALL; zxc_store_le32(dst, gh->n_sequences); zxc_store_le32(dst + 4, gh->n_literals); dst[8] = gh->enc_lit; dst[9] = gh->enc_litlen; dst[10] = gh->enc_mlen; dst[11] = gh->enc_off; zxc_store_le32(dst + 12, 0); uint8_t* p = dst + ZXC_GLO_HEADER_BINARY_SIZE; for (int i = 0; i < ZXC_GLO_SECTIONS; i++) { zxc_store_le64(p, desc[i].sizes); p += ZXC_SECTION_DESC_BINARY_SIZE; } return (int)needed; } /** * @brief Parses a GLO block header and its section descriptors from @p src. * * @param[in] src Source buffer. * @param[in] len Size of @p src. * @param[out] gh Receives the decoded GLO header. * @param[out] desc Receives @ref ZXC_GLO_SECTIONS decoded section descriptors. * @return @ref ZXC_OK on success, or a negative @ref zxc_error_t code. */ int zxc_read_glo_header_and_desc(const uint8_t* RESTRICT src, const size_t len, zxc_gnr_header_t* RESTRICT gh, zxc_section_desc_t desc[ZXC_GLO_SECTIONS]) { const size_t needed = ZXC_GLO_HEADER_BINARY_SIZE + ZXC_GLO_SECTIONS * ZXC_SECTION_DESC_BINARY_SIZE; if (UNLIKELY(len < needed)) return ZXC_ERROR_SRC_TOO_SMALL; gh->n_sequences = zxc_le32(src); gh->n_literals = zxc_le32(src + 4); gh->enc_lit = src[8]; gh->enc_litlen = src[9]; gh->enc_mlen = src[10]; gh->enc_off = src[11]; const uint8_t* p = src + ZXC_GLO_HEADER_BINARY_SIZE; for (int i = 0; i < ZXC_GLO_SECTIONS; i++) { desc[i].sizes = zxc_le64(p); p += ZXC_SECTION_DESC_BINARY_SIZE; } return ZXC_OK; } /** * @brief Serialises a GHI block header followed by its section descriptors. * * @param[out] dst Destination buffer. * @param[in] rem Remaining capacity of @p dst. * @param[in] gh Populated GHI header descriptor. * @param[in] desc Array of @ref ZXC_GHI_SECTIONS section descriptors. * @return Total bytes written on success, or a negative @ref zxc_error_t code. */ int zxc_write_ghi_header_and_desc(uint8_t* RESTRICT dst, const size_t rem, const zxc_gnr_header_t* RESTRICT gh, const zxc_section_desc_t desc[ZXC_GHI_SECTIONS]) { const size_t needed = ZXC_GHI_HEADER_BINARY_SIZE + ZXC_GHI_SECTIONS * ZXC_SECTION_DESC_BINARY_SIZE; if (UNLIKELY(rem < needed)) return ZXC_ERROR_DST_TOO_SMALL; zxc_store_le32(dst, gh->n_sequences); zxc_store_le32(dst + 4, gh->n_literals); dst[8] = gh->enc_lit; dst[9] = gh->enc_litlen; dst[10] = gh->enc_mlen; dst[11] = gh->enc_off; zxc_store_le32(dst + 12, 0); uint8_t* p = dst + ZXC_GHI_HEADER_BINARY_SIZE; for (int i = 0; i < ZXC_GHI_SECTIONS; i++) { zxc_store_le64(p, desc[i].sizes); p += ZXC_SECTION_DESC_BINARY_SIZE; } return (int)needed; } /** * @brief Parses a GHI block header and its section descriptors from @p src. * * @param[in] src Source buffer. * @param[in] len Size of @p src. * @param[out] gh Receives the decoded GHI header. * @param[out] desc Receives @ref ZXC_GHI_SECTIONS decoded section descriptors. * @return @ref ZXC_OK on success, or a negative @ref zxc_error_t code. */ int zxc_read_ghi_header_and_desc(const uint8_t* RESTRICT src, const size_t len, zxc_gnr_header_t* RESTRICT gh, zxc_section_desc_t desc[ZXC_GHI_SECTIONS]) { const size_t needed = ZXC_GHI_HEADER_BINARY_SIZE + ZXC_GHI_SECTIONS * ZXC_SECTION_DESC_BINARY_SIZE; if (UNLIKELY(len < needed)) return ZXC_ERROR_SRC_TOO_SMALL; gh->n_sequences = zxc_le32(src); gh->n_literals = zxc_le32(src + 4); gh->enc_lit = src[8]; gh->enc_litlen = src[9]; gh->enc_mlen = src[10]; gh->enc_off = src[11]; const uint8_t* p = src + ZXC_GHI_HEADER_BINARY_SIZE; for (int i = 0; i < ZXC_GHI_SECTIONS; i++) { desc[i].sizes = zxc_le64(p); p += ZXC_SECTION_DESC_BINARY_SIZE; } return ZXC_OK; } /* * ============================================================================ * COMPRESS BOUND CALCULATION * ============================================================================ */ /** * @brief Returns the maximum compressed size for a given input size. * * The result accounts for the file header, per-block headers, block * checksums, worst-case expansion, EOF block, seekable overhead (SEK * block), and the file footer. * * The block count is derived from @ref ZXC_BLOCK_SIZE_MIN (4 KB) to * guarantee the bound holds for all valid block sizes and seekable mode. * * @param[in] input_size Uncompressed input size in bytes. * @return Upper bound on compressed size, or 0 if @p input_size would overflow. */ uint64_t zxc_compress_bound(const size_t input_size) { // Guard against uint64 overflow when summing per-block overhead // across very large inputs (input_size approaching SIZE_MAX). if (UNLIKELY(input_size > (SIZE_MAX - (SIZE_MAX >> 8)))) return 0; uint64_t n = ((uint64_t)input_size + ZXC_BLOCK_SIZE_MIN - 1) / ZXC_BLOCK_SIZE_MIN; if (n == 0) n = 1; return ZXC_FILE_HEADER_SIZE + (n * (ZXC_BLOCK_HEADER_SIZE + ZXC_BLOCK_CHECKSUM_SIZE + ZXC_BLOCK_FORMAT_OVERHEAD)) + (uint64_t)input_size + ZXC_BLOCK_HEADER_SIZE + /* EOF block */ ZXC_BLOCK_HEADER_SIZE + /* SEK block header (seekable) */ (n * ZXC_SEEK_ENTRY_SIZE) + /* SEK entries: 4 bytes per block */ ZXC_FILE_FOOTER_SIZE; } /** * @brief Returns the maximum compressed size for a single block (no file framing). * * @param[in] input_size Uncompressed block size in bytes * (must be <= @ref ZXC_BLOCK_SIZE_MAX). * @return Upper bound on compressed block size, or 0 if @p input_size is out * of range for the Block API (i.e. exceeds ZXC_BLOCK_SIZE_MAX) or if * the arithmetic would overflow. */ uint64_t zxc_compress_block_bound(const size_t input_size) { // Mirror the Block API contract: src_size must be in [1, ZXC_BLOCK_SIZE_MAX]. // Inputs outside this range cause zxc_compress_block to fail // (NULL_INPUT for 0, BAD_BLOCK_SIZE above MAX), so the bound is undefined. // Returning 0 signals "unusable" upfront. The cap also makes the addition // below trivially overflow-free. if (UNLIKELY(input_size == 0 || input_size > ZXC_BLOCK_SIZE_MAX)) return 0; // Outer block header + payload (worst case: incompressible, raw bytes) // + inner format overhead + optional checksum. return (uint64_t)ZXC_BLOCK_HEADER_SIZE + (uint64_t)input_size + ZXC_BLOCK_FORMAT_OVERHEAD + ZXC_BLOCK_CHECKSUM_SIZE; } /** * @brief Returns the minimum dst_capacity required by zxc_decompress_block(). * * The decoder uses speculative wild-copy writes on its fast path. * Sizing the destination to uncompressed_size + ZXC_PAD_SIZE*66 guarantees * the fast path is always reachable and that tail bounds checks never * spuriously reject the last literals of a valid block. * * Returns 0 if @p uncompressed_size exceeds ZXC_BLOCK_SIZE_MAX (the Block API * limit), or if the arithmetic would overflow. * * @param[in] uncompressed_size Exact decompressed size of the block. * @return Minimum @c dst_capacity in bytes, or 0 if @p uncompressed_size exceeds * @c ZXC_BLOCK_SIZE_MAX. */ uint64_t zxc_decompress_block_bound(const size_t uncompressed_size) { if (UNLIKELY(uncompressed_size > ZXC_BLOCK_SIZE_MAX)) return 0; return (uint64_t)uncompressed_size + ZXC_DECOMPRESS_TAIL_PAD; } /** * @brief Estimates the total buffer bytes allocated inside a cctx for a block. * * Thin wrapper around @ref zxc_cctx_compute_workspace_size for @c mode == 1 * (compress), with @c src_size clamped up to a valid block size via * @ref zxc_block_size_ceil. The opaque wrapper struct allocated by * @ref zxc_create_cctx adds a fixed overhead (< 128 B) that is negligible * next to the per-chunk buffers and is intentionally omitted. * * For @p level >= 6 the figure includes the optimal-parser scratch * (@c opt_scratch, ~8.125 bytes per chunk_size byte) used by the optimal * parser and reused as transient package-merge scratch for the Huffman * code-length builder. * * @param[in] src_size Input size; rounded up to a valid block size. * @param[in] level Compression level (>= 6 includes the optimal-parser scratch). * @return Estimated context buffer size in bytes, or 0 if @p src_size is 0. */ uint64_t zxc_estimate_cctx_size(const size_t src_size, const int level) { if (UNLIKELY(src_size == 0)) return 0; const size_t chunk_size = zxc_block_size_ceil(src_size); return (uint64_t)zxc_cctx_compute_workspace_size(chunk_size, 1, level, 0); } /* * ============================================================================ * ERROR CODE UTILITIES * ============================================================================ */ /** * @brief Returns a human-readable string for the given error code. * * @param[in] code An error code from @ref zxc_error_t (or @ref ZXC_OK). * @return A static string such as @c "ZXC_OK" or @c "ZXC_ERROR_MEMORY". * Returns @c "ZXC_UNKNOWN_ERROR" for unrecognised codes. */ const char* zxc_error_name(const int code) { switch ((zxc_error_t)code) { case ZXC_OK: return "ZXC_OK"; case ZXC_ERROR_MEMORY: return "ZXC_ERROR_MEMORY"; case ZXC_ERROR_DST_TOO_SMALL: return "ZXC_ERROR_DST_TOO_SMALL"; case ZXC_ERROR_SRC_TOO_SMALL: return "ZXC_ERROR_SRC_TOO_SMALL"; case ZXC_ERROR_BAD_MAGIC: return "ZXC_ERROR_BAD_MAGIC"; case ZXC_ERROR_BAD_VERSION: return "ZXC_ERROR_BAD_VERSION"; case ZXC_ERROR_BAD_HEADER: return "ZXC_ERROR_BAD_HEADER"; case ZXC_ERROR_BAD_CHECKSUM: return "ZXC_ERROR_BAD_CHECKSUM"; case ZXC_ERROR_CORRUPT_DATA: return "ZXC_ERROR_CORRUPT_DATA"; case ZXC_ERROR_BAD_OFFSET: return "ZXC_ERROR_BAD_OFFSET"; case ZXC_ERROR_OVERFLOW: return "ZXC_ERROR_OVERFLOW"; case ZXC_ERROR_IO: return "ZXC_ERROR_IO"; case ZXC_ERROR_NULL_INPUT: return "ZXC_ERROR_NULL_INPUT"; case ZXC_ERROR_BAD_BLOCK_TYPE: return "ZXC_ERROR_BAD_BLOCK_TYPE"; case ZXC_ERROR_BAD_BLOCK_SIZE: return "ZXC_ERROR_BAD_BLOCK_SIZE"; case ZXC_ERROR_DICT_REQUIRED: return "ZXC_ERROR_DICT_REQUIRED"; case ZXC_ERROR_DICT_MISMATCH: return "ZXC_ERROR_DICT_MISMATCH"; case ZXC_ERROR_DICT_TOO_LARGE: return "ZXC_ERROR_DICT_TOO_LARGE"; case ZXC_ERROR_BAD_LEVEL: return "ZXC_ERROR_BAD_LEVEL"; default: return "ZXC_UNKNOWN_ERROR"; } } /* * ============================================================================ * LIBRARY INFORMATION * ============================================================================ */ /** * @brief Returns the minimum supported compression level. * * Returns the value of ZXC_LEVEL_FASTEST (currently 1). * This allows integrators to discover the level range at runtime without relying on * compile-time macros alone. */ int zxc_min_level(void) { return ZXC_LEVEL_FASTEST; } /** * @brief Returns the maximum supported compression level. * * Returns the value of ZXC_LEVEL_ULTRA (currently 7). */ int zxc_max_level(void) { return ZXC_LEVEL_ULTRA; } /** * @brief Returns the default compression level. * * Returns the value of ZXC_LEVEL_DEFAULT (currently 3). */ int zxc_default_level(void) { return ZXC_LEVEL_DEFAULT; } /** * @brief Returns the human-readable library version string. * * The returned pointer is a compile-time constant and must not be freed. * Format: "MAJOR.MINOR.PATCH" (e.g. "0.13.0"). */ const char* zxc_version_string(void) { return ZXC_LIB_VERSION_STR; } zxc-0.13.0/src/lib/zxc_compress.c000066400000000000000000003047031522536370400166350ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file zxc_compress.c * @brief Block-level compression: LZ77 parsing, GLO / GHI / RAW encoding, * and the chunk-wrapper entry point. * * Compiled multiple times with different @c ZXC_FUNCTION_SUFFIX values to * produce AVX2, AVX-512, NEON, and scalar variants dispatched at runtime * by @ref zxc_dispatch.c. */ /* * Function Multi-Versioning Support * If ZXC_FUNCTION_SUFFIX is defined (e.g. _avx2, _neon), rename the public * entry point AND the Huffman entry points consumed by this TU. The defines * sit before zxc_internal.h so that the prototypes the header declares are * also rewritten with the suffix, keeping callers and callees consistent. */ #ifdef ZXC_FUNCTION_SUFFIX #define ZXC_CAT_IMPL(x, y) x##y #define ZXC_CAT(x, y) ZXC_CAT_IMPL(x, y) #define zxc_compress_chunk_wrapper ZXC_CAT(zxc_compress_chunk_wrapper, ZXC_FUNCTION_SUFFIX) #define zxc_huf_build_code_lengths ZXC_CAT(zxc_huf_build_code_lengths, ZXC_FUNCTION_SUFFIX) #define zxc_huf_unpack_lengths ZXC_CAT(zxc_huf_unpack_lengths, ZXC_FUNCTION_SUFFIX) #define zxc_huf_calc_size ZXC_CAT(zxc_huf_calc_size, ZXC_FUNCTION_SUFFIX) #define zxc_huf_calc_size_dict ZXC_CAT(zxc_huf_calc_size_dict, ZXC_FUNCTION_SUFFIX) #define zxc_huf_encode_section ZXC_CAT(zxc_huf_encode_section, ZXC_FUNCTION_SUFFIX) #define zxc_huf_encode_section_dict ZXC_CAT(zxc_huf_encode_section_dict, ZXC_FUNCTION_SUFFIX) #endif #include "../../include/zxc_error.h" #include "zxc_internal.h" /** * @brief Computes a hash value for either a 4-byte or 5-byte sequence. * * @param[in] val The 64-bit integer sequence (e.g., 8 bytes read from input stream). * @param[in] use_hash5 Non-zero to use the 5-byte xorshift64* hash (Marsaglia/Vigna), zero for * 4-byte Marsaglia hash. * @return uint32_t A hash value suitable for indexing the match table. */ static ZXC_ALWAYS_INLINE uint32_t zxc_hash_func(const uint64_t val, const int use_hash5) { if (use_hash5) { const uint64_t v5 = val & 0xFFFFFFFFFFULL; return (uint32_t)((v5 * ZXC_LZ_HASH_PRIME2) >> (64 - ZXC_LZ_HASH_BITS)); } else { const uint64_t v4 = val ^ (val >> 15); return ((uint32_t)v4 * ZXC_LZ_HASH_PRIME1) >> (32 - ZXC_LZ_HASH_BITS); } } #if defined(ZXC_USE_AVX2) /** * @brief Reduces a 256-bit integer vector to a single scalar by finding the maximum unsigned 32-bit * integer element. * * This function performs a horizontal reduction across the 8 packed 32-bit unsigned integers * in the source vector to determine the maximum value. * * @param[in] v The 256-bit vector containing 8 unsigned 32-bit integers. * @return The maximum unsigned 32-bit integer found in the vector. */ // codeql[cpp/unused-static-function] : Used conditionally when ZXC_USE_AVX2 is defined static ZXC_ALWAYS_INLINE uint32_t zxc_mm256_reduce_max_epu32(__m256i v) { __m128i vlow = _mm256_castsi256_si128(v); // Extract the lower 128 bits __m128i vhigh = _mm256_extracti128_si256(v, 1); // Extract the upper 128 bits vlow = _mm_max_epu32(vlow, vhigh); // Element-wise max of lower and upper halves __m128i vshuf = _mm_shuffle_epi32(vlow, _MM_SHUFFLE(1, 0, 3, 2)); // Shuffle to swap pairs vlow = _mm_max_epu32(vlow, vshuf); // Max of original and swapped vshuf = _mm_shuffle_epi32(vlow, _MM_SHUFFLE(2, 3, 0, 1)); // Shuffle to bring remaining candidates vlow = _mm_max_epu32(vlow, vshuf); // Final max comparison return (uint32_t)_mm_cvtsi128_si32(vlow); // Extract the scalar result } #endif #if defined(ZXC_USE_SSE2) /** * @brief SSE2 emulation of SSE4.1 @c _mm_blendv_epi8. * * Selects bytes from @p b where the corresponding @p mask byte has its high bit * set, else from @p a. In every call site the mask lanes are full-width compare * results (all-ones or all-zero per element), so a plain bitwise select is exact. * * @param[in] a Lanes chosen where @p mask is clear. * @param[in] b Lanes chosen where @p mask is set. * @param[in] mask Per-byte selector (a full-width compare result). * @return The blended 128-bit vector. */ // codeql[cpp/unused-static-function] : Used conditionally when ZXC_USE_SSE2 is defined static ZXC_ALWAYS_INLINE __m128i zxc_mm_blendv_epi8_sse2(__m128i a, __m128i b, __m128i mask) { return _mm_or_si128(_mm_and_si128(mask, b), _mm_andnot_si128(mask, a)); } /** * @brief SSE2 emulation of SSE4.1 @c _mm_packus_epi32 (saturating u32 -> u16). * * SSE2 only has signed @c _mm_packs_epi32 (saturates to int16). Bias each lane * by -0x8000 so values in [0, 0xFFFF] land in the signed int16 range with no * saturation, pack, then add 0x8000 back per 16-bit lane. Exact for inputs in * [0, 0xFFFF] (all call sites pass match lengths < 2^16). * * @param[in] a Four u32 lanes forming the low half of the result. * @param[in] b Four u32 lanes forming the high half of the result. * @return The eight u16 lanes packed from @p a then @p b. */ // codeql[cpp/unused-static-function] : Used conditionally when ZXC_USE_SSE2 is defined static ZXC_ALWAYS_INLINE __m128i zxc_mm_packus_epi32_sse2(__m128i a, __m128i b) { const __m128i bias32 = _mm_set1_epi32(0x8000); const __m128i bias16 = _mm_set1_epi16((short)0x8000); const __m128i pa = _mm_sub_epi32(a, bias32); const __m128i pb = _mm_sub_epi32(b, bias32); return _mm_add_epi16(_mm_packs_epi32(pa, pb), bias16); } #endif /** * @brief Writes a Prefix Varint encoded value to a buffer. * * This function encodes a 32-bit unsigned integer using Prefix Varint encoding * and writes it to the destination buffer. Unary prefix bits in the first * byte determine the total length (1-5 bytes), allowing for branchless or * predictable decoding. * * Format: * - 0xxxxxxx (1 byte) * - 10xxxxxx ... (2 bytes) * - 110xxxxx ... (3 bytes) * ... * * @param[out] dst Pointer to the destination buffer where the encoded value will be written. * @param[in] val The 32-bit unsigned integer value to encode. * @return The number of bytes written to the destination buffer. */ static ZXC_ALWAYS_INLINE size_t zxc_write_varint(uint8_t* RESTRICT dst, const uint32_t val) { // Refuse to emit varints above ZXC_MAX_VARINT_VALUE: such values are // out-of-spec (block_size_max is 2^21, the largest legitimate varint is // strictly less) and would be rejected by the decoder. For valid inputs // from the Block API (src_size <= ZXC_BLOCK_SIZE_MAX) this never triggers; // it is a defense-in-depth check. Callers must treat a return of 0 as an // encoding error. if (UNLIKELY(val > ZXC_MAX_VARINT_VALUE)) { return 0; } // 1 byte: 0xxxxxxx (7 bits) = 2^7 = 128 if (LIKELY(val < (1U << 7))) { dst[0] = (uint8_t)val; return 1; } // 2 bytes: 10xxxxxx xxxxxxxx (14 bits) = 2^14 = 16384 if (LIKELY(val < (1U << 14))) { dst[0] = (uint8_t)(0x80 | (val & 0x3F)); dst[1] = (uint8_t)(val >> 6); return 2; } // 3 bytes: 110xxxxx xxxxxxxx xxxxxxxx (21 bits) -> max emittable value, // matching ZXC_MAX_VARINT_VALUE = ZXC_BLOCK_SIZE_MAX - 1. dst[0] = (uint8_t)(0xC0 | (val & 0x1F)); dst[1] = (uint8_t)(val >> 5); dst[2] = (uint8_t)(val >> 13); return 3; } /** * @brief Structure representing a match found during compression. * * This structure holds information about a matching sequence found * in the input data during the compression process. * * @param ref Pointer to the reference data where the match was found. * @param len Length of the matching sequence in bytes. * @param backtrack Distance to backtrack from the current position to find the match. */ typedef struct { const uint8_t* ref; uint32_t len; uint32_t backtrack; } zxc_match_t; /** * @brief Finds the best matching sequence for LZ77 compression * * Uses a split hash table layout: * - hash_table[h] : uint32_t position + epoch (128 KB for 15-bit hash) * - hash_tags[h] : uint8_t tag for fast rejection (32 KB, L1-resident) * * @param[in] src Pointer to the start of the source buffer. * @param[in] ip Current input position pointer. * @param[in] iend Pointer to the end of the input buffer. * @param[in] search_limit Pointer to the match finding limit. * @param[in] anchor Pointer to the current anchor position. * @param[in,out] hash_table Pointer to the position table for match finding. * @param[in,out] hash_tags Pointer to the tag table for fast rejection. * @param[in,out] chain_table Pointer to the chain table for collision handling. * @param[in] epoch_mark Current epoch marker for hash table invalidation. * @param[in] offset_mask Mask isolating the position bits in chain/table entries. * @param[in] level Compression level (selects search depth and matcher behaviour). * @param[in] p LZ77 parameters controlling search depth, lazy matching, and stepping. * @param[in] last_off Most recently accepted match offset, used as a repeat-offset * seed probed before the hash-chain walk. Pass 0 to disable (all callers * except the level-6 optimal parser do so). * @return zxc_match_t Structure containing the best match information * (reference pointer, length of the match, and backtrack distance). */ static ZXC_ALWAYS_INLINE zxc_match_t zxc_lz77_find_best_match( const uint8_t* src, const uint8_t* ip, const uint8_t* iend, const uint8_t* search_limit, const uint8_t* anchor, uint32_t* RESTRICT hash_table, uint8_t* RESTRICT hash_tags, uint16_t* RESTRICT chain_table, const uint32_t epoch_mark, const uint32_t offset_mask, const int level, const zxc_lz77_params_t p, const uint32_t last_off) { const int use_hash5 = (level >= 3); // Track the best match found so far. // ref is the pointer to the start of the match in the history buffer, // len is the match length, and backtrack is the distance from ip to ref. // Start with a sentinel length just below the minimum so any valid match will replace it. zxc_match_t best = (zxc_match_t){NULL, ZXC_LZ_MIN_MATCH_LEN - 1, 0}; // Load the 8-byte sequence at the current position. uint64_t cur_val8 = zxc_le64(ip); uint32_t cur_val = (uint32_t)cur_val8; uint32_t h = zxc_hash_func(cur_val8, use_hash5); // 8-bit tag: XOR fold of first 4 bytes for fast rejection const uint8_t cur_tag = (uint8_t)(cur_val ^ (cur_val >> 16)); // Current position in the input buffer expressed as a 32-bit index. const uint32_t cur_pos = (uint32_t)(ip - src); // Tag-first filter on fast levels. const uint8_t stored_tag = hash_tags[h]; uint32_t match_idx; if (level <= ZXC_LEVEL_FAST && stored_tag != cur_tag) { match_idx = 0; } else { const uint32_t raw_head = hash_table[h]; match_idx = ((raw_head & ~offset_mask) == epoch_mark) ? (raw_head & offset_mask) : 0; } // skip_head still drives the chain walk on level >= 3 (advances past the // mismatched head without comparing). On level <= 2 it is always 0 here: // either match_idx == 0 (filter-skip) or stored_tag == cur_tag. const int skip_head = (match_idx != 0) & (stored_tag != cur_tag); // Split table writes hash_table[h] = epoch_mark | cur_pos; hash_tags[h] = cur_tag; // Branchless chain table update const uint32_t dist = cur_pos - match_idx; const uint32_t valid_mask = -((int32_t)((match_idx != 0) & (dist < ZXC_LZ_WINDOW_SIZE))); chain_table[cur_pos & ZXC_LZ_WINDOW_MASK] = (uint16_t)(dist & valid_mask); int attempts = p.search_depth; /* Repeat-offset seed (level-6 parser passes last_off; others pass 0). * Probing the previous offset first often finds the longest match right * away, speeding up the chain walk. It only raises best.len, never lowers * it, so the result is unchanged - this is purely a speed optimization. */ if (last_off != 0U && last_off <= (uint32_t)ZXC_LZ_MAX_DIST && last_off <= cur_pos) { const uint8_t* const rep_ref = src + (cur_pos - last_off); if (zxc_le32(rep_ref) == cur_val) { uint32_t mlen = sizeof(uint32_t); const uint8_t* const limit_8 = iend - sizeof(uint64_t); while (ip + mlen < limit_8) { const uint64_t diff = zxc_le64(ip + mlen) ^ zxc_le64(rep_ref + mlen); if (diff == 0) { mlen += sizeof(uint64_t); } else { mlen += (uint32_t)(zxc_ctz64(diff) >> 3); goto _rep_done; } } while (ip + mlen < iend && rep_ref[mlen] == ip[mlen]) mlen++; _rep_done:; best.len = mlen; best.ref = rep_ref; if (UNLIKELY(best.len >= (uint32_t)p.sufficient_len || ip + best.len >= iend)) goto _finalize_match; } } if (match_idx == 0) goto _finalize_match; // Optimization: If head tag doesn't match, advance immediately without loading the first // mismatch. if (skip_head) { const uint16_t delta = chain_table[match_idx & ZXC_LZ_WINDOW_MASK]; const uint32_t next_idx = match_idx - delta; match_idx = (delta != 0) ? next_idx : 0; attempts--; } while (match_idx > 0) { if (UNLIKELY(attempts-- < 0 || cur_pos - match_idx > ZXC_LZ_MAX_DIST)) break; const uint8_t* ref = src + match_idx; // Load the next chain link early (before the compare) so its address // resolves while we prefetch. const uint16_t delta = chain_table[match_idx & ZXC_LZ_WINDOW_MASK]; const uint32_t next_idx = match_idx - delta; ZXC_PREFETCH_READ(src + next_idx); const uint32_t ref_val = zxc_le32(ref); const int tag_match = (ref_val == cur_val); // Cheap gate: 4-byte tag match, then check the byte past the current // best (the && skips that load unless the tag already matched). const int should_compare = tag_match && (ref[best.len] == ip[best.len]); if (should_compare) { uint32_t mlen = sizeof(uint32_t); // We already know the first 4 bytes match // Fast path: Scalar 64-bit comparison for short matches (=< 64 bytes) // Most matches are short, so this avoids SIMD overhead for common cases const uint8_t* limit_8 = iend - sizeof(uint64_t); const uint8_t* scalar_limit = ip + mlen + 64; if (scalar_limit > limit_8) scalar_limit = limit_8; while (ip + mlen < scalar_limit) { uint64_t diff = zxc_le64(ip + mlen) ^ zxc_le64(ref + mlen); if (diff == 0) mlen += sizeof(uint64_t); else { mlen += (zxc_ctz64(diff) >> 3); goto _match_len_done; } } // Long match path: Use SIMD for matches exceeding 64 bytes #if defined(ZXC_USE_AVX512) const uint8_t* limit_64 = iend - 64; while (ip + mlen < limit_64) { const __m512i v_src = _mm512_loadu_si512((const void*)(ip + mlen)); const __m512i v_ref = _mm512_loadu_si512((const void*)(ref + mlen)); const __mmask64 mask = _mm512_cmpeq_epi8_mask(v_src, v_ref); if (mask == 0xFFFFFFFFFFFFFFFF) mlen += 64; else { mlen += (uint32_t)zxc_ctz64(~mask); goto _match_len_done; } } #elif defined(ZXC_USE_AVX2) const uint8_t* limit_32 = iend - 32; while (ip + mlen < limit_32) { const __m256i v_src = _mm256_loadu_si256((const __m256i*)(ip + mlen)); const __m256i v_ref = _mm256_loadu_si256((const __m256i*)(ref + mlen)); const __m256i v_cmp = _mm256_cmpeq_epi8(v_src, v_ref); const uint32_t mask = (uint32_t)_mm256_movemask_epi8(v_cmp); if (mask == 0xFFFFFFFF) mlen += 32; else { mlen += zxc_ctz32(~mask); goto _match_len_done; } } #elif defined(ZXC_USE_SSE2) const uint8_t* limit_16 = iend - 16; while (ip + mlen < limit_16) { const __m128i v_src = _mm_loadu_si128((const __m128i*)(ip + mlen)); const __m128i v_ref = _mm_loadu_si128((const __m128i*)(ref + mlen)); const __m128i v_cmp = _mm_cmpeq_epi8(v_src, v_ref); const uint32_t mask = (uint32_t)_mm_movemask_epi8(v_cmp); if (mask == 0xFFFFU) mlen += 16; else { // mask != 0xFFFF => a differing byte exists in bits 0..15, // so the lowest set bit of ~mask lies in that range. mlen += zxc_ctz32(~mask); goto _match_len_done; } } #elif defined(ZXC_USE_NEON64) { const uint8_t* limit_32 = iend - 32; while (ip + mlen < limit_32) { const uint8x16_t s0 = vld1q_u8(ip + mlen); const uint8x16_t r0 = vld1q_u8(ref + mlen); const uint8x16_t c0 = vceqq_u8(s0, r0); const uint64_t m0 = vget_lane_u64( vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(c0), 4)), 0); if (UNLIKELY(m0 != ~(uint64_t)0)) { mlen += (uint32_t)(zxc_ctz64(~m0) >> 2); goto _match_len_done; } const uint8x16_t s1 = vld1q_u8(ip + mlen + 16); const uint8x16_t r1 = vld1q_u8(ref + mlen + 16); const uint8x16_t c1 = vceqq_u8(s1, r1); const uint64_t m1 = vget_lane_u64( vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(c1), 4)), 0); if (UNLIKELY(m1 != ~(uint64_t)0)) { mlen += 16 + (uint32_t)(zxc_ctz64(~m1) >> 2); goto _match_len_done; } mlen += 32; } if (ip + mlen < iend - 16) { const uint8x16_t v_src = vld1q_u8(ip + mlen); const uint8x16_t v_ref = vld1q_u8(ref + mlen); const uint8x16_t v_cmp = vceqq_u8(v_src, v_ref); const uint64_t mask = vget_lane_u64( vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(v_cmp), 4)), 0); if (LIKELY(mask == ~(uint64_t)0)) mlen += 16; else { mlen += (uint32_t)(zxc_ctz64(~mask) >> 2); goto _match_len_done; } } } #elif defined(ZXC_USE_NEON32) { const uint8_t* limit_16 = iend - 16; while (ip + mlen < limit_16) { const uint8x16_t v_src = vld1q_u8(ip + mlen); const uint8x16_t v_ref = vld1q_u8(ref + mlen); const uint8x16_t v_cmp = vceqq_u8(v_src, v_ref); uint8x8_t p1 = vpmin_u8(vget_low_u8(v_cmp), vget_high_u8(v_cmp)); uint8x8_t p2 = vpmin_u8(p1, p1); uint8x8_t p3 = vpmin_u8(p2, p2); uint8x8_t p4 = vpmin_u8(p3, p3); uint8_t min_val = vget_lane_u8(p4, 0); if (min_val == 0xFF) mlen += 16; else { uint8x16_t v_diff = vmvnq_u8(v_cmp); uint64_t lo = (uint64_t)vgetq_lane_u32(vreinterpretq_u32_u8(v_diff), 0) | ((uint64_t)vgetq_lane_u32(vreinterpretq_u32_u8(v_diff), 1) << 32); if (lo != 0) mlen += (zxc_ctz64(lo) >> 3); else mlen += 8 + (zxc_ctz64( (uint64_t)vgetq_lane_u32(vreinterpretq_u32_u8(v_diff), 2) | ((uint64_t)vgetq_lane_u32(vreinterpretq_u32_u8(v_diff), 3) << 32)) >> 3); goto _match_len_done; } } } #endif while (ip + mlen < limit_8) { const uint64_t diff = zxc_le64(ip + mlen) ^ zxc_le64(ref + mlen); if (diff == 0) mlen += sizeof(uint64_t); else { mlen += (zxc_ctz64(diff) >> 3); goto _match_len_done; } } while (ip + mlen < iend && ref[mlen] == ip[mlen]) mlen++; _match_len_done:; const int better = (mlen > best.len); best.len = better ? mlen : best.len; best.ref = better ? ref : best.ref; if (UNLIKELY(best.len >= (uint32_t)p.sufficient_len || ip + best.len >= iend)) break; } match_idx = (delta != 0) ? next_idx : 0; } _finalize_match: if (best.ref) { // Backtrack to extend match backwards const uint8_t* b_ip = ip; const uint8_t* b_ref = best.ref; while (b_ip > anchor && b_ref > src && b_ip[-1] == b_ref[-1]) { b_ip--; b_ref--; best.len++; best.backtrack++; } best.ref = b_ref; } if (p.use_lazy && best.ref && best.len < (uint32_t)p.lazy_len_threshold && ip + 1 < search_limit) { // --- Lazy evaluation at ip+1 --- const uint64_t next_val8 = zxc_le64(ip + 1); const uint32_t next_val = (uint32_t)next_val8; const uint32_t h2 = zxc_hash_func(next_val8, use_hash5); const uint8_t next_stored_tag = hash_tags[h2]; const uint32_t next_head = hash_table[h2]; uint32_t next_idx = (next_head & ~offset_mask) == epoch_mark ? (next_head & offset_mask) : 0; const uint8_t next_tag = (uint8_t)(next_val ^ (next_val >> 16)); const int skip_lazy_head = (next_idx > 0 && next_stored_tag != next_tag); uint32_t max_lazy2 = 0; int lazy_att = p.lazy_attempts; int is_lazy_first = 1; while (next_idx > 0) { if (UNLIKELY(lazy_att-- <= 0 || (uint32_t)(ip + 1 - src) - next_idx > ZXC_LZ_MAX_DIST)) break; const uint8_t* ref2 = src + next_idx; if ((!is_lazy_first || !skip_lazy_head) && zxc_le32(ref2) == next_val) { uint32_t l2 = sizeof(uint32_t); const uint8_t* limit = iend - sizeof(uint64_t); while (ip + 1 + l2 < limit) { const uint64_t v1 = zxc_le64(ip + 1 + l2); const uint64_t v2 = zxc_le64(ref2 + l2); if (v1 != v2) { l2 += (uint32_t)(zxc_ctz64(v1 ^ v2) >> 3); goto lazy2_done; } l2 += sizeof(uint64_t); } while (ip + 1 + l2 < iend && ref2[l2] == ip[1 + l2]) l2++; lazy2_done: max_lazy2 = l2 > max_lazy2 ? l2 : max_lazy2; } const uint16_t delta = chain_table[next_idx & ZXC_LZ_WINDOW_MASK]; if (UNLIKELY(delta == 0)) break; next_idx -= delta; is_lazy_first = 0; } // --- Lazy evaluation at ip+2 (computed in parallel, no dependency on lazy 1) --- uint32_t max_lazy3 = 0; if (level >= ZXC_LEVEL_BALANCED && ip + 2 < search_limit) { const uint64_t val3_8 = zxc_le64(ip + 2); const uint32_t val3 = (uint32_t)val3_8; const uint32_t h3 = zxc_hash_func(val3_8, use_hash5); const uint8_t tag3 = hash_tags[h3]; const uint32_t head3 = hash_table[h3]; uint32_t idx3 = (head3 & ~offset_mask) == epoch_mark ? (head3 & offset_mask) : 0; const uint8_t cur_tag3 = (uint8_t)(val3 ^ (val3 >> 16)); const int skip_head3 = (idx3 > 0 && tag3 != cur_tag3); int is_first3 = 1; lazy_att = p.lazy_attempts; while (idx3 > 0) { if (UNLIKELY(lazy_att-- <= 0 || (uint32_t)(ip + 2 - src) - idx3 > ZXC_LZ_MAX_DIST)) break; const uint8_t* ref3 = src + idx3; if ((!is_first3 || !skip_head3) && zxc_le32(ref3) == val3) { uint32_t l3 = sizeof(uint32_t); const uint8_t* limit = iend - sizeof(uint64_t); while (ip + 2 + l3 < limit) { const uint64_t v1 = zxc_le64(ip + 2 + l3); const uint64_t v2 = zxc_le64(ref3 + l3); if (v1 != v2) { l3 += (uint32_t)(zxc_ctz64(v1 ^ v2) >> 3); goto lazy3_done; } l3 += sizeof(uint64_t); } while (ip + 2 + l3 < iend && ref3[l3] == ip[2 + l3]) l3++; lazy3_done: max_lazy3 = l3 > max_lazy3 ? l3 : max_lazy3; } const uint16_t delta = chain_table[idx3 & ZXC_LZ_WINDOW_MASK]; if (UNLIKELY(delta == 0)) break; idx3 -= delta; is_first3 = 0; } } // Single decision: invalidate if either lazy position found a better match if (max_lazy2 > best.len + 1 || max_lazy3 > best.len + 2) best.ref = NULL; } return best; } /** * @brief Update dp[p + L_start .. p + L_end) with a constant transition * cost, in parallel where the target ISA allows. * * For each L in [L_start, L_end), if @p nxt is strictly less than the * current dp[p+L], rewrite dp/parent_len/parent_off in lockstep: same * semantics as the scalar update inside ::zxc_lz77_optimal_parse_glo. * Caller guarantees @p nxt is independent of L (the cost of the L-th * transition does not vary across the requested span). * * Vectorized prologue per ISA, falling through to a scalar tail: * - AVX-512 BW + VL : 16-wide via vpcmpud + vmask{store,storeu}. * Falls back to AVX2 if VL is absent. * - AVX2 : 8-wide via biased vpcmpgt + vpblendvb (no 32-bit * unsigned cmpgt before AVX-512). parent_off is * updated with a packed 8x16 mask + 128-bit blend. * - NEON64 / NEON32 : 4-wide via vcgtq_u32 + vbslq_u32, with vmovn_u32 * to narrow the mask for the 4x16 parent_off update. * * @param[in,out] dp DP cost array; dp[p + L] is relaxed when * @p nxt < dp[p + L]. * @param[in,out] parent_len Backtrack length array, written in lockstep * with @p dp; receives the L of the relaxing * transition. * @param[in,out] parent_off Backtrack offset array, written in lockstep; * receives @p off_biased on relaxation. * @param[in] p Source DP position the transitions originate * from. Indexing into the three arrays is * `p + L`. * @param[in] L Initial L value (start of the span, inclusive). * @param[in] L_end End of the span (exclusive). Must satisfy * @p L_end <= UINT16_MAX so every written length * fits in @c parent_len's @c uint16_t cells. * @param[in] nxt Constant successor cost `dp[p] + transition`, * shared across the [L, L_end) span. * @param[in] off_biased Match offset minus ::ZXC_LZ_OFFSET_BIAS, the * value stored when a transition wins. * @return The first L value not processed (i.e., @p L_end on success). */ // codeql[cpp/unused-static-function]: false positive static ZXC_ALWAYS_INLINE size_t zxc_opt_dp_update_const_cost( uint32_t* RESTRICT dp, uint16_t* RESTRICT parent_len, uint16_t* RESTRICT parent_off, const size_t p, size_t L, const size_t L_end, const uint32_t nxt, const uint16_t off_biased) { #if defined(ZXC_USE_AVX512) && defined(__AVX512VL__) if (L + 16 <= L_end) { const __m512i v_inc = _mm512_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); const __m512i v_nxt = _mm512_set1_epi32((int)nxt); const __m256i v_off = _mm256_set1_epi16((int16_t)off_biased); for (; L + 16 <= L_end; L += 16) { const __m512i v_L_lanes = _mm512_add_epi32(v_inc, _mm512_set1_epi32((int)L)); const __m512i v_dp = _mm512_loadu_si512((const void*)&dp[p + L]); const __mmask16 m = _mm512_cmplt_epu32_mask(v_nxt, v_dp); _mm512_mask_storeu_epi32(&dp[p + L], m, v_nxt); const __m256i v_L_u16 = _mm512_cvtusepi32_epi16(v_L_lanes); _mm256_mask_storeu_epi16((void*)&parent_len[p + L], m, v_L_u16); _mm256_mask_storeu_epi16((void*)&parent_off[p + L], m, v_off); } } #elif defined(ZXC_USE_AVX2) if (L + 8 <= L_end) { const __m256i v_inc = _mm256_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7); const __m256i v_nxt = _mm256_set1_epi32((int)nxt); const __m256i v_bias = _mm256_set1_epi32((int)0x80000000); const __m256i v_nxt_b = _mm256_xor_si256(v_nxt, v_bias); const __m128i v_off = _mm_set1_epi16((int16_t)off_biased); for (; L + 8 <= L_end; L += 8) { const __m256i v_L_lanes = _mm256_add_epi32(v_inc, _mm256_set1_epi32((int)L)); const __m256i v_dp = _mm256_loadu_si256((const __m256i*)&dp[p + L]); /* Unsigned-compare-via-bias trick: * (dp ^ 0x80000000) > (nxt ^ 0x80000000) iff dp > nxt * because XOR with the sign bit maps unsigned ordering to * signed ordering. AVX2 only has signed cmpgt for 32-bit. */ const __m256i v_dp_b = _mm256_xor_si256(v_dp, v_bias); const __m256i v_mask = _mm256_cmpgt_epi32(v_dp_b, v_nxt_b); const __m256i v_dp_new = _mm256_blendv_epi8(v_dp, v_nxt, v_mask); _mm256_storeu_si256((__m256i*)&dp[p + L], v_dp_new); /* Pack 8x int32 mask -> 8x int16 mask with signed saturation: * 0xFFFFFFFF -> 0xFFFF, 0x00000000 -> 0x0000. */ const __m128i v_mask16 = _mm_packs_epi32(_mm256_castsi256_si128(v_mask), _mm256_extracti128_si256(v_mask, 1)); const __m128i v_L_u16 = _mm_packus_epi32(_mm256_castsi256_si128(v_L_lanes), _mm256_extracti128_si256(v_L_lanes, 1)); const __m128i v_pl = _mm_loadu_si128((const __m128i*)&parent_len[p + L]); const __m128i v_pl_new = _mm_blendv_epi8(v_pl, v_L_u16, v_mask16); _mm_storeu_si128((__m128i*)&parent_len[p + L], v_pl_new); const __m128i v_po = _mm_loadu_si128((const __m128i*)&parent_off[p + L]); const __m128i v_po_new = _mm_blendv_epi8(v_po, v_off, v_mask16); _mm_storeu_si128((__m128i*)&parent_off[p + L], v_po_new); } } #elif defined(ZXC_USE_NEON64) || defined(ZXC_USE_NEON32) if (L + 4 <= L_end) { static const uint32_t k_inc_array[4] = {0, 1, 2, 3}; const uint32x4_t v_inc = vld1q_u32(k_inc_array); const uint32x4_t v_nxt = vdupq_n_u32(nxt); const uint16x4_t v_off = vdup_n_u16(off_biased); for (; L + 4 <= L_end; L += 4) { const uint32x4_t v_L_lanes = vaddq_u32(v_inc, vdupq_n_u32((uint32_t)L)); const uint32x4_t v_dp = vld1q_u32(&dp[p + L]); const uint32x4_t v_mask = vcgtq_u32(v_dp, v_nxt); vst1q_u32(&dp[p + L], vbslq_u32(v_mask, v_nxt, v_dp)); const uint16x4_t v_mask16 = vmovn_u32(v_mask); const uint16x4_t v_L_u16 = vqmovn_u32(v_L_lanes); const uint16x4_t v_pl = vld1_u16(&parent_len[p + L]); vst1_u16(&parent_len[p + L], vbsl_u16(v_mask16, v_L_u16, v_pl)); const uint16x4_t v_po = vld1_u16(&parent_off[p + L]); vst1_u16(&parent_off[p + L], vbsl_u16(v_mask16, v_off, v_po)); } } #elif defined(ZXC_USE_SSE2) if (L + 4 <= L_end) { const __m128i v_inc = _mm_setr_epi32(0, 1, 2, 3); const __m128i v_nxt = _mm_set1_epi32((int)nxt); const __m128i v_bias = _mm_set1_epi32((int)0x80000000); const __m128i v_nxt_b = _mm_xor_si128(v_nxt, v_bias); const __m128i v_off = _mm_set1_epi16((short)off_biased); for (; L + 4 <= L_end; L += 4) { const __m128i v_L_lanes = _mm_add_epi32(v_inc, _mm_set1_epi32((int)L)); const __m128i v_dp = _mm_loadu_si128((const __m128i*)&dp[p + L]); /* Unsigned compare via sign-bit bias (SSE2 cmpgt is signed only): * (dp ^ 0x80000000) > (nxt ^ 0x80000000) iff dp > nxt. */ const __m128i v_dp_b = _mm_xor_si128(v_dp, v_bias); const __m128i v_mask = _mm_cmpgt_epi32(v_dp_b, v_nxt_b); const __m128i v_dp_new = zxc_mm_blendv_epi8_sse2(v_dp, v_nxt, v_mask); _mm_storeu_si128((__m128i*)&dp[p + L], v_dp_new); /* Narrow the 4x int32 mask / length lanes to 4x int16 (low 64 bits). * packs: 0xFFFFFFFF -> 0xFFFF, 0 -> 0; packus (SSE2-emulated): u32->u16. */ const __m128i v_mask16 = _mm_packs_epi32(v_mask, v_mask); const __m128i v_L_u16 = zxc_mm_packus_epi32_sse2(v_L_lanes, v_L_lanes); __m128i v_pl = _mm_loadl_epi64((const __m128i*)&parent_len[p + L]); v_pl = zxc_mm_blendv_epi8_sse2(v_pl, v_L_u16, v_mask16); _mm_storel_epi64((__m128i*)&parent_len[p + L], v_pl); __m128i v_po = _mm_loadl_epi64((const __m128i*)&parent_off[p + L]); v_po = zxc_mm_blendv_epi8_sse2(v_po, v_off, v_mask16); _mm_storel_epi64((__m128i*)&parent_off[p + L], v_po); } } #endif /* Scalar tail (and full path on archs without SIMD). * L < L_end <= UINT16_MAX (caller precondition), so the cast is lossless. */ for (; L < L_end; L++) { if (nxt < dp[p + L]) { dp[p + L] = nxt; parent_len[p + L] = (uint16_t)L; parent_off[p + L] = off_biased; } } return L; } /** * @brief Estimate per-block literal cost from a sampled histogram passed * through the actual length-limited Huffman builder. * * Strategy: build a strided sample of @p src (4096 entries), run the same * length-limited Huffman code construction the encoder uses, and report the * sample-weighted average code length. This is the predicted bits/byte * for Huffman-encoded literals on this distribution: no calibration * constants, no per-corpus tuning. The cap at 8 reflects that RAW is * always available at exactly that cost; if Huffman doesn't beat 8 on the * sample, the encoder will pick RAW and 8 is the right price. * * @param[in] src Source buffer for the block. * @param[in] src_sz Length of @p src in bytes. * @param[in] scratch Package-merge scratch (pre-allocated in the cctx for * level >= 6). May be `NULL`, in which case the builder * allocates its own working memory. * @return Estimated literal cost in bits, in `[1, 8]`. */ // codeql[cpp/unused-static-function]: false positive static uint32_t zxc_opt_estimate_lit_bits(const uint8_t* RESTRICT src, const size_t src_sz, void* RESTRICT scratch) { if (UNLIKELY(src_sz < ZXC_OPT_LIT_SAMPLE_MIN)) return CHAR_BIT; uint32_t hist[ZXC_HUF_NUM_SYMBOLS] = {0}; const size_t step = (src_sz > 4096) ? (src_sz >> 12) : 1U; size_t sampled = 0; for (size_t i = 0; i < src_sz; i += step) { hist[src[i]]++; sampled++; } uint8_t code_len[ZXC_HUF_NUM_SYMBOLS]; if (UNLIKELY(zxc_huf_build_code_lengths(hist, code_len, scratch, ZXC_HUF_MAX_CODE_LEN_DENSITY) != ZXC_OK)) return CHAR_BIT; /* Sample-weighted sum of code lengths == predicted total Huffman bits * for the sample. Divide by sample count for bits/byte, rounded up * (DP works in integer bits; rounding up errs on the conservative * side, slightly favoring matches over fractional-cost literals). */ uint64_t total_bits = 0; for (int k = 0; k < ZXC_HUF_NUM_SYMBOLS; k++) { total_bits += (uint64_t)hist[k] * (uint64_t)code_len[k]; } const uint32_t avg = (uint32_t)((total_bits + sampled - 1) / sampled); /* Cap at RAW cost: if Huffman can't beat 8 bits/byte on the sample, * the encoder will pick RAW anyway and 8 is the actual literal cost. */ return (avg < CHAR_BIT) ? avg : CHAR_BIT; } /** * @brief Static price-based optimal LZ77 parser for level 6. * * Forward DP over the block's positions: `dp[p]` = min bit cost to encode * `src[0..p)`. Per-position transitions are * - literal: `dp[p+1] = min(dp[p+1], dp[p] + lit_cost` * - match : `dp[p+L] = min(dp[p+L], dp[p] + match_cost(L))` for L in * `[MIN_MATCH, max_L]` * where `max_L` is the longest match found by ::zxc_lz77_find_best_match at * `p` (with lazy disabled, the DP itself handles position-based * optimization). Backtracking from `dp[src_sz]` reconstructs the * optimal token sequence. * * Complexity guard: ::ZXC_OPT_LONG_MATCH_SKIP causes ::zxc_lz77_find_best_match * to be skipped at positions strictly inside a long match, without this * guard, highly repetitive data (e.g. Lorem-loop with multi-MB matches at * every offset) makes the parser quadratic and unit tests run for minutes. * The inner sub-length update loop visits every L from `MIN_MATCH` to * `max_L`; the skip threshold means each long-match region only pays its * O(L) cost once at the starting position, keeping total work O(N). * * @param[in,out] ctx Compression context. The lazy-allocated * `opt_scratch` field provides the DP arrays; * it is grown on first use and reused on * subsequent blocks. * @param[in] src Source buffer to parse. * @param[in] src_sz Length of @p src in bytes. * @param[in,out] hash_table LZ77 hash table (epoch | position entries). * @param[in,out] hash_tags 8-bit fast-rejection tags paired with @p hash_table. * @param[in,out] chain_table Hash-chain link table (ring buffer). * @param[in] epoch_mark Current epoch shifted into the high bits. * @param[in] offset_mask Mask isolating the position bits in chain entries. * @param[in] level Compression level (used to size the matcher). * @param[out] literals Buffer receiving the gathered literal bytes. * @param[out] buf_tokens Buffer receiving the per-sequence token bytes. * @param[out] buf_offsets Buffer receiving the per-sequence offsets. * @param[out] buf_extras Buffer receiving variable-length overflow data. * @param[out] seq_c_out Number of emitted sequences. * @param[out] lit_c_out Number of literal bytes written into @p literals. * @param[out] extras_sz_out Number of bytes written into @p buf_extras. * @param[out] max_offset_out Largest biased offset emitted (used by the caller * to choose 1-byte vs 2-byte offset encoding). * @return @c ZXC_OK on success, or a negative @ref zxc_error_t. */ static int zxc_lz77_optimal_parse_glo(zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint32_t* RESTRICT hash_table, uint8_t* RESTRICT hash_tags, uint16_t* RESTRICT chain_table, const uint32_t epoch_mark, const uint32_t offset_mask, const int level, uint8_t* RESTRICT literals, uint8_t* RESTRICT buf_tokens, uint16_t* RESTRICT buf_offsets, uint8_t* RESTRICT buf_extras, uint32_t* RESTRICT seq_c_out, size_t* RESTRICT lit_c_out, size_t* RESTRICT extras_sz_out, uint16_t* RESTRICT max_offset_out) { zxc_lz77_params_t lzp_opt = zxc_get_lz77_params(level); lzp_opt.use_lazy = 0; // guard /* When a dictionary is active, src = [dict | block_data]. DP arrays are * indexed relative to the block start (position dict_sz in src). The * variable src_base points to the first block byte for literal copies, * while src remains the base for the match finder (absolute positions). */ const size_t dict_sz = ctx->dict_size; const size_t block_sz = src_sz - dict_sz; const uint8_t* const src_base = src + dict_sz; const uint8_t* const iend = src + src_sz; /* Block too small for any match: emit all as literals. */ if (UNLIKELY(block_sz < ZXC_LZ_SEARCH_MARGIN + 1)) { if (block_sz > 0) ZXC_MEMCPY(literals, src_base, block_sz); *lit_c_out = block_sz; *seq_c_out = 0; *extras_sz_out = 0; *max_offset_out = 0; return 0; } const size_t search_limit_pos = block_sz - ZXC_LZ_SEARCH_MARGIN; const uint8_t* const search_limit = src + search_limit_pos; /* DP arrays carved from ctx->opt_scratch: a single allocation lazy- * grown on the first level-6 call and reused across blocks. Each * sub-buffer is cache-line padded so the next one starts on a 64 B * boundary. The total `needed` matches zxc_estimate_cctx_size() keep * the formula in sync. * * dp : (chunk+1) x uint32_t: min cost to reach position p. * parent_len : (chunk+1) x uint16_t: 0 = literal, >= MIN_MATCH = match. * parent_off : (chunk+1) x uint16_t: biased match offset (distance-1). * match_end_bits : ceil((chunk+1)/64) x uint64_t: 1 bit per position, * set when that position * is the end of a match * on the chosen DP path. * Replaces a forward-order * actions[] stack at 1/64 * the cost. * * The same buffer is reused as transient scratch for the length-limited * Huffman code-length builder (see zxc_opt_estimate_lit_bits below and * the Huffman selection in zxc_encode_block_glo): the package-merge * scratch is needed before the DP runs and again after the parse has * been read out, so the lifetimes never overlap. The capacity is the * larger of the two demands. */ const size_t chunk = ctx->chunk_size; const size_t sz_dp = ZXC_ALIGN_CL((chunk + 1) * sizeof(uint32_t)); const size_t sz_pl = ZXC_ALIGN_CL((chunk + 1) * sizeof(uint16_t)); const size_t sz_po = ZXC_ALIGN_CL((chunk + 1) * sizeof(uint16_t)); const size_t n_bm_words = ZXC_BITMAP_WORDS(chunk + 1); const size_t sz_bm = ZXC_ALIGN_CL(n_bm_words * sizeof(uint64_t)); const size_t dp_needed = sz_dp + sz_pl + sz_po + sz_bm; const size_t needed = (dp_needed > ZXC_HUF_BUILD_SCRATCH_SIZE) ? dp_needed : ZXC_HUF_BUILD_SCRATCH_SIZE; /* opt_scratch is now pre-allocated inside ctx->memory_block by * zxc_cctx_init when level >= ZXC_LEVEL_DENSITY. The formula above must * stay byte-for-byte in sync with the one in zxc_cctx_init() and * zxc_estimate_cctx_size(). */ (void)needed; /* Per-block literal cost (sample only block data, not dict prefix): */ const uint32_t lit_cost = zxc_opt_estimate_lit_bits(src_base, block_sz, ctx->opt_scratch); uint32_t* const dp = (uint32_t*)ctx->opt_scratch; uint16_t* const parent_len = (uint16_t*)(ctx->opt_scratch + sz_dp); uint16_t* const parent_off = (uint16_t*)(ctx->opt_scratch + sz_dp + sz_pl); uint64_t* const match_end_bits = (uint64_t*)(ctx->opt_scratch + sz_dp + sz_pl + sz_po); dp[0] = 0; ZXC_MEMSET(dp + 1, 0xFF, block_sz * sizeof(uint32_t)); ZXC_MEMSET(match_end_bits, 0, sz_bm); /* Forward DP: visit every position, update reachable successors. * `skip_until` skips find_best_match at positions strictly inside the * last long match, the DP transition from the start of the match * already covers dp[p+1..p+L], and re-searching at every intra-match * position is what makes the parser quadratic on repetitive inputs. */ size_t skip_until = 0; /* Rolling repeat-offset seed for find_best_match */ uint32_t last_off = 0; for (size_t p = 0; p < search_limit_pos; p++) { if (UNLIKELY(dp[p] == UINT32_MAX)) continue; /* Literal transition. */ const uint32_t lit_next = dp[p] + lit_cost; if (lit_next < dp[p + 1]) { dp[p + 1] = lit_next; parent_len[p + 1] = 0; } if (p < skip_until) continue; /* Match transition: call find_best_match (no lazy, no backtrack via * anchor=ip). Iterate sub-lengths since any L <= max_L matches at the * same offset and may end at a more useful DP position. * ip uses absolute position (src + dict_sz + p) so match finder * resolves dict references correctly via src as base. */ const uint8_t* ip = src_base + p; const zxc_match_t m = zxc_lz77_find_best_match( src, ip, iend, search_limit, /*anchor=*/ip, hash_table, hash_tags, chain_table, epoch_mark, offset_mask, level, lzp_opt, last_off); if (m.ref) { const uint32_t off = (uint32_t)(ip - m.ref); if (off > 0 && off <= ZXC_LZ_WINDOW_SIZE) { last_off = off; const size_t L_max_raw = (m.len > block_sz - p) ? (block_sz - p) : (size_t)m.len; const size_t L_max = (L_max_raw > UINT16_MAX) ? UINT16_MAX : L_max_raw; /* The L-iteration cost function is piecewise constant in * varint segments. Split the [MIN_MATCH, L_max] span into: * 1. cheap : v < ML_MASK -> cost = base * 2. varint1 : v in [ML_MASK, ML_MASK + 128) -> cost = base + 8 * 3. varint2+: v >= ML_MASK + 128 -> cost = base + 16, +24, ... * * Steps 1 and 2 use constant nxt and are vectorized via * the helper. Step 3 is rare (typical matches are short) * and stays scalar. */ const uint16_t off_biased = (uint16_t)(off - ZXC_LZ_OFFSET_BIAS); const size_t L_max_plus = L_max + 1; size_t L = ZXC_LZ_MIN_MATCH_LEN; /* 1. Cheap range. */ { const size_t L_cheap_end = ZXC_LZ_MIN_MATCH_LEN + ZXC_TOKEN_ML_MASK; const size_t L_end = (L_max_plus < L_cheap_end) ? L_max_plus : L_cheap_end; const uint32_t nxt = dp[p] + ZXC_OPT_MATCH_COST_BASE; L = zxc_opt_dp_update_const_cost(dp, parent_len, parent_off, p, L, L_end, nxt, off_biased); } /* 2. First varint level (1-byte extension). */ if (L < L_max_plus) { const size_t L_v1_end = ZXC_LZ_MIN_MATCH_LEN + ZXC_TOKEN_ML_MASK + 128; const size_t L_end = (L_max_plus < L_v1_end) ? L_max_plus : L_v1_end; const uint32_t nxt = dp[p] + ZXC_OPT_MATCH_COST_BASE + CHAR_BIT; L = zxc_opt_dp_update_const_cost(dp, parent_len, parent_off, p, L, L_end, nxt, off_biased); } /* 3. Higher varint levels: variable cost, kept scalar. * Reached only by L >= ML_MASK + 128 + MIN_MATCH, so the * v >= ML_MASK guard from the original loop is implied. */ for (; L < L_max_plus; L++) { uint32_t cost = ZXC_OPT_MATCH_COST_BASE; uint32_t v = (uint32_t)(L - ZXC_LZ_MIN_MATCH_LEN) - ZXC_TOKEN_ML_MASK; cost += CHAR_BIT; while (v >= 128) { v >>= 7; cost += CHAR_BIT; } const uint32_t nxt = dp[p] + cost; if (nxt < dp[p + L]) { dp[p + L] = nxt; parent_len[p + L] = (uint16_t)L; parent_off[p + L] = off_biased; } } if (UNLIKELY(L_max >= ZXC_OPT_LONG_MATCH_SKIP)) skip_until = p + L_max - 1; } } } /* Tail (last ZXC_LZ_SEARCH_MARGIN bytes) can only be literals: the match finder * stops at search_limit so its 8-byte probe reads stay in bounds. */ for (size_t p = search_limit_pos; p < block_sz; p++) { if (UNLIKELY(dp[p] == UINT32_MAX)) continue; const uint32_t lit_next = dp[p] + lit_cost; if (lit_next < dp[p + 1]) { dp[p + 1] = lit_next; parent_len[p + 1] = 0; } } /* Backtrack from src_sz to 0: only match endpoints are recorded (one bit * per position in match_end_bits). Literals between matches are implicit * runs of unmarked positions and are reconstructed during forward emission * via lit_start tracking, so they need no backtrack storage. */ { size_t pos = block_sz; while (pos > 0) { const uint32_t L = parent_len[pos]; if (L == 0) { pos -= 1; } else { match_end_bits[pos >> 6] |= (uint64_t)1 << (pos & 63); pos -= L; } } } /* Forward emission: walk match_end_bits word-by-word, peeling set bits * with ctzll. Each set bit gives a match endpoint; parent_len/parent_off * at that position recover (length, offset). */ uint32_t seq_c = 0; size_t lit_c = 0; size_t extras_sz = 0; uint16_t max_offset = 0; size_t lit_start = 0; for (size_t word_idx = 0; word_idx < n_bm_words; word_idx++) { uint64_t w = match_end_bits[word_idx]; while (w) { const size_t pos = (word_idx << 6) + (size_t)zxc_ctz64(w); w &= w - 1; const uint32_t L = parent_len[pos]; const uint16_t off_biased = parent_off[pos]; const size_t match_start = pos - L; const size_t LL = match_start - lit_start; if (LL > 0) { ZXC_MEMCPY(literals + lit_c, src_base + lit_start, LL); lit_c += LL; } const uint32_t ll = (uint32_t)LL; const uint32_t ml = L - ZXC_LZ_MIN_MATCH_LEN; const uint8_t ll_code = (ll >= ZXC_TOKEN_LL_MASK) ? ZXC_TOKEN_LL_MASK : (uint8_t)ll; const uint8_t ml_code = (ml >= ZXC_TOKEN_ML_MASK) ? ZXC_TOKEN_ML_MASK : (uint8_t)ml; buf_tokens[seq_c] = (ll_code << ZXC_TOKEN_LIT_BITS) | ml_code; buf_offsets[seq_c] = off_biased; if (off_biased > max_offset) max_offset = off_biased; if (UNLIKELY(ll >= ZXC_TOKEN_LL_MASK)) { const size_t n = zxc_write_varint(buf_extras + extras_sz, ll - ZXC_TOKEN_LL_MASK); if (UNLIKELY(n == 0)) return ZXC_ERROR_OVERFLOW; extras_sz += n; } if (UNLIKELY(ml >= ZXC_TOKEN_ML_MASK)) { const size_t n = zxc_write_varint(buf_extras + extras_sz, ml - ZXC_TOKEN_ML_MASK); if (UNLIKELY(n == 0)) return ZXC_ERROR_OVERFLOW; extras_sz += n; } seq_c++; lit_start = pos; } } /* Tail literals after the last match (or all literals if no match). */ if (lit_start < block_sz) { const size_t tail = block_sz - lit_start; ZXC_MEMCPY(literals + lit_c, src_base + lit_start, tail); lit_c += tail; } *seq_c_out = seq_c; *lit_c_out = lit_c; *extras_sz_out = extras_sz; *max_offset_out = max_offset; return 0; } /** * @brief Seeds the hash/chain tables from dictionary content prepended to @p src. * * When a dictionary is active, @p src is laid out as [dict_content | block_data]. * This function inserts hash entries for dictionary positions [0, dict_size) so * the match finder can reference them during block encoding. * * @param[in] src Source buffer starting with dictionary content. * @param[in] dict_size Size of the dictionary prefix in bytes. * @param[in,out] hash_table Hash table to seed with dictionary positions. * @param[in,out] hash_tags Tag table for fast match rejection. * @param[in,out] chain_table Chain table for collision resolution. * @param[in] epoch_mark Current epoch marker for hash table entries. * @param[in] offset_mask Position mask for epoch/offset encoding. * @param[in] level Compression level (controls hash function variant). */ static void zxc_lz_seed_dict(const uint8_t* RESTRICT src, const size_t dict_size, uint32_t* RESTRICT hash_table, uint8_t* RESTRICT hash_tags, uint16_t* RESTRICT chain_table, const uint32_t epoch_mark, const uint32_t offset_mask, const int level) { if (UNLIKELY(dict_size < ZXC_LZ_MIN_MATCH_LEN)) return; const int use_hash5 = (level >= 3); const size_t limit = dict_size - (ZXC_LZ_MIN_MATCH_LEN - 1); /* Sparse seeding for the first half, dense for the second half. * Positions near the end of the dict produce shorter offsets and are * more likely to yield matches, so they deserve full coverage. */ const size_t half = limit / 2; for (size_t i = 0; i < half; i += 4) { const uint64_t val8 = zxc_le64(src + i); const uint32_t h = zxc_hash_func(val8, use_hash5); const uint32_t cur_pos = (uint32_t)i; const uint8_t tag = (uint8_t)((uint32_t)val8 ^ ((uint32_t)val8 >> 16)); hash_table[h] = epoch_mark | cur_pos; hash_tags[h] = tag; chain_table[cur_pos & ZXC_LZ_WINDOW_MASK] = 0; } for (size_t i = half; i < limit; i++) { const uint64_t val8 = zxc_le64(src + i); const uint32_t h = zxc_hash_func(val8, use_hash5); const uint32_t cur_pos = (uint32_t)i; const uint8_t tag = (uint8_t)((uint32_t)val8 ^ ((uint32_t)val8 >> 16)); const uint32_t raw_head = hash_table[h]; const uint32_t prev_idx = ((raw_head & ~offset_mask) == epoch_mark) ? (raw_head & offset_mask) : 0; hash_table[h] = epoch_mark | cur_pos; hash_tags[h] = tag; const uint32_t dist = cur_pos - prev_idx; const uint32_t valid = -((int32_t)((prev_idx != 0) & (dist < ZXC_LZ_WINDOW_SIZE))); chain_table[cur_pos & ZXC_LZ_WINDOW_MASK] = (uint16_t)(dist & valid); } } /** * @brief Encodes a data block using the General (GLO) compression format. * * This function implements the core LZ77 compression logic. It dynamically * adjusts compression parameters (search depth, lazy matching strategy, and * step skipping) based on the compression level configured in the context. * * **LZ77 Implementation Details:** * 1. **Hash Chain:** Uses a hash table (`ctx->hash_table`) to find potential * match positions. Collisions are handled via a `chain_table`, allowing us to * search deeper into the history for a better match. * 2. **Lazy Matching:** If a match is found, we check the *next* byte to see if * it produces a longer match. If so, we output a literal and take the better * match. This is enabled for levels >= 3. * 3. **Step Skipping:** For lower levels (1-3), we skip bytes when updating the * hash table to increase speed (`step > 1`). For levels 4+, we process every * byte to maximize compression ratio. * 4. **SIMD Match Finding:** Uses AVX2/AVX512/NEON to compare 32/64 bytes at a * time during match length calculation, significantly speeding up long match * verification. * 5. **RLE Detection:** Analyzes literals to see if Run-Length Encoding would * be beneficial (saving > 10% space). * * The encoding process consists of: * 1. **LZ77 Parsing**: The function iterates through the source data, * maintaining a hash chain to find repeated patterns (matches). It supports * "Lazy Matching" for higher compression levels to optimize match selection. * 2. **Sequence Storage**: Matches are converted into sequences consisting of * literal lengths, match lengths, and offsets. * 3. **Bitpacking & Serialization**: The sequences are analyzed to determine * optimal bit-widths. The function then writes the block header, encodes * literals (using Raw or RLE encoding), and bit-packs the sequence streams into * the destination buffer. * * @param[in,out] ctx Pointer to the compression context containing hash tables * and configuration. * @param[in] src Pointer to the input source data. * @param[in] src_sz Size of the input data in bytes. * @param[out] dst Pointer to the destination buffer where compressed data will * be written. * @param[in] dst_cap Maximum capacity of the destination buffer. * @param[out] out_sz [Out] Pointer to a variable that will receive the total size * of the compressed output. * * @return ZXC_OK on success, or a negative zxc_error_t code (e.g., ZXC_ERROR_DST_TOO_SMALL) if an * error occurs (e.g., buffer overflow). */ static int zxc_encode_block_glo(zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, size_t dst_cap, size_t* RESTRICT out_sz) { const int level = ctx->compression_level; const size_t dict_sz = ctx->dict_size; const zxc_lz77_params_t lzp = zxc_get_lz77_params(level); ctx->epoch++; if (UNLIKELY(ctx->epoch >= ctx->max_epoch)) { ZXC_MEMSET(ctx->hash_table, 0, ZXC_LZ_HASH_SIZE * sizeof(uint32_t)); ZXC_MEMSET(ctx->hash_tags, 0, ZXC_LZ_HASH_SIZE * sizeof(uint8_t)); ctx->epoch = 1; } const uint32_t offset_bits = ctx->offset_bits; const uint32_t offset_mask = ctx->offset_mask; const uint32_t epoch_mark = ctx->epoch << offset_bits; if (dict_sz > 0) zxc_lz_seed_dict(src, dict_sz, ctx->hash_table, ctx->hash_tags, ctx->chain_table, epoch_mark, offset_mask, level); const uint8_t* ip = src + dict_sz; const uint8_t* iend = src + src_sz; const uint8_t* anchor = ip; const uint8_t* search_limit = iend - ZXC_LZ_SEARCH_MARGIN; uint32_t* const hash_table = ctx->hash_table; uint8_t* const hash_tags = ctx->hash_tags; uint16_t* const chain_table = ctx->chain_table; uint8_t* const literals = ctx->literals; uint8_t* const buf_tokens = ctx->buf_tokens; uint16_t* const buf_offsets = ctx->buf_offsets; uint8_t* const buf_extras = ctx->buf_extras; uint32_t seq_c = 0; size_t lit_c = 0; size_t extras_sz = 0; uint16_t max_offset = 0; // Track max offset for 1-byte/2-byte mode decision /* Level 6+: price-based optimal parser (fills outputs and skips the * lazy loop + last_lits handling below via `goto parse_done`). */ if (level >= ZXC_LEVEL_DENSITY) { const int rc = zxc_lz77_optimal_parse_glo( ctx, src, src_sz, hash_table, hash_tags, chain_table, epoch_mark, offset_mask, level, literals, buf_tokens, buf_offsets, buf_extras, &seq_c, &lit_c, &extras_sz, &max_offset); if (UNLIKELY(rc != 0)) return rc; goto parse_done; } while (LIKELY(ip < search_limit)) { const size_t dist = (size_t)(ip - anchor); size_t step = lzp.step_base + (dist >> lzp.step_shift); if (UNLIKELY(ip + step >= search_limit)) step = 1; if (LIKELY(ip + step + sizeof(uint64_t) <= iend)) { const uint64_t v_next = zxc_le64(ip + step); // cppcheck-suppress unreadVariable const uint32_t h_next = zxc_hash_func(v_next, 1); ZXC_PREFETCH_READ(&hash_tags[h_next]); ZXC_PREFETCH_READ(&hash_table[h_next]); } const zxc_match_t m = zxc_lz77_find_best_match(src, ip, iend, search_limit, anchor, hash_table, hash_tags, chain_table, epoch_mark, offset_mask, level, lzp, /*last_off=*/0U); if (m.ref) { ip -= m.backtrack; const uint32_t ll = (uint32_t)(ip - anchor); const uint32_t ml = m.len - ZXC_LZ_MIN_MATCH_LEN; const uint32_t off = (uint32_t)(ip - m.ref); if (ll > 0) { if (LIKELY(anchor + ZXC_PAD_SIZE <= iend)) { zxc_copy32(literals + lit_c, anchor); if (UNLIKELY(ll > ZXC_PAD_SIZE)) { ZXC_MEMCPY(literals + lit_c + ZXC_PAD_SIZE, anchor + ZXC_PAD_SIZE, ll - ZXC_PAD_SIZE); } } else { ZXC_MEMCPY(literals + lit_c, anchor, ll); } lit_c += ll; } const uint8_t ll_code = (ll >= ZXC_TOKEN_LL_MASK) ? ZXC_TOKEN_LL_MASK : (uint8_t)ll; const uint8_t ml_code = (ml >= ZXC_TOKEN_ML_MASK) ? ZXC_TOKEN_ML_MASK : (uint8_t)ml; buf_tokens[seq_c] = (ll_code << ZXC_TOKEN_LIT_BITS) | ml_code; buf_offsets[seq_c] = (uint16_t)(off - ZXC_LZ_OFFSET_BIAS); if ((off - ZXC_LZ_OFFSET_BIAS) > max_offset) max_offset = (uint16_t)(off - ZXC_LZ_OFFSET_BIAS); if (ll >= ZXC_TOKEN_LL_MASK) { const size_t n = zxc_write_varint(buf_extras + extras_sz, ll - ZXC_TOKEN_LL_MASK); if (UNLIKELY(n == 0)) return ZXC_ERROR_OVERFLOW; extras_sz += n; } if (ml >= ZXC_TOKEN_ML_MASK) { const size_t n = zxc_write_varint(buf_extras + extras_sz, ml - ZXC_TOKEN_ML_MASK); if (UNLIKELY(n == 0)) return ZXC_ERROR_OVERFLOW; extras_sz += n; } seq_c++; if (m.len > 2 && level > ZXC_LEVEL_BALANCED) { const uint8_t* match_end = ip + m.len; if (match_end < iend - 7) { const uint32_t pos_u = (uint32_t)((match_end - 2) - src); const uint64_t val_u8 = zxc_le64(match_end - 2); const uint32_t val_u = (uint32_t)val_u8; const uint32_t h_u = zxc_hash_func(val_u8, 1); const uint32_t prev_head = hash_table[h_u]; const uint32_t prev_idx = (prev_head & ~offset_mask) == epoch_mark ? (prev_head & offset_mask) : 0; hash_table[h_u] = epoch_mark | pos_u; hash_tags[h_u] = (uint8_t)(val_u ^ (val_u >> 16)); chain_table[pos_u & ZXC_LZ_WINDOW_MASK] = (prev_idx > 0 && (pos_u - prev_idx) < ZXC_LZ_WINDOW_SIZE) ? (uint16_t)(pos_u - prev_idx) : 0; } } ip += m.len; anchor = ip; } else { ip += step; } } const size_t last_lits = iend - anchor; if (last_lits > 0) { ZXC_MEMCPY(literals + lit_c, anchor, last_lits); lit_c += last_lits; } parse_done:; /* Dictionary-table trainer hook: accumulate the REAL post-LZ literal * frequencies (see zxc_train_dict_huf). Cold path, NULL outside training. */ if (UNLIKELY(ctx->lit_freq_acc != NULL)) { for (size_t i = 0; i < lit_c; i++) ctx->lit_freq_acc[literals[i]]++; } // --- RLE ANALYSIS --- size_t rle_size = 0; int enc_lit = ZXC_SECTION_ENCODING_RAW; size_t best_j = lit_c; if (lit_c > 0) { const uint8_t* p = literals; const uint8_t* const p_end = literals + lit_c; const uint8_t* const p_end_4 = p_end - 3; // Safe limit for 4-byte lookahead while (LIKELY(p < p_end)) { const uint8_t b = *p; const uint8_t* run_start = p++; // Fast run counting with early SIMD exit #if defined(ZXC_USE_AVX512) const __m512i vb = _mm512_set1_epi8((char)b); while (p <= p_end - 64) { const __m512i v = _mm512_loadu_si512((const void*)p); const __mmask64 mask = _mm512_cmpeq_epi8_mask(v, vb); if (mask != 0xFFFFFFFFFFFFFFFFULL) { p += (size_t)zxc_ctz64(~mask); goto _run_done; } p += 64; } #elif defined(ZXC_USE_AVX2) const __m256i vb = _mm256_set1_epi8((char)b); while (p <= p_end - 32) { const __m256i v = _mm256_loadu_si256((const __m256i*)p); const uint32_t mask = (uint32_t)_mm256_movemask_epi8(_mm256_cmpeq_epi8(v, vb)); if (mask != 0xFFFFFFFF) { p += zxc_ctz32(~mask); goto _run_done; } p += 32; } #elif defined(ZXC_USE_SSE2) const __m128i vb = _mm_set1_epi8((char)b); while (p <= p_end - 16) { const __m128i v = _mm_loadu_si128((const __m128i*)p); const uint32_t mask = (uint32_t)_mm_movemask_epi8(_mm_cmpeq_epi8(v, vb)); if (mask != 0xFFFFU) { p += zxc_ctz32(~mask); goto _run_done; } p += 16; } #elif defined(ZXC_USE_NEON64) const uint8x16_t vb = vdupq_n_u8(b); while (p <= p_end - 16) { const uint8x16_t v = vld1q_u8(p); const uint8x16_t eq = vceqq_u8(v, vb); /* SHRN nibble-mask: see find_best_match above for rationale. */ const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(eq), 4)), 0); if (LIKELY(mask == ~(uint64_t)0)) { p += 16; } else { p += (size_t)(zxc_ctz64(~mask) >> 2); goto _run_done; } } #elif defined(ZXC_USE_NEON32) uint8x16_t vb = vdupq_n_u8(b); while (p <= p_end - 16) { uint8x16_t v = vld1q_u8(p); uint8x16_t eq = vceqq_u8(v, vb); uint8x16_t not_eq = vmvnq_u8(eq); // 32-bit ARM NEON doesn't always support vgetq_lane_u64 / vreinterpretq_u64_u8 so // we treat the 128-bit vector as 4 x 32-bit lanes */ const uint32x4_t neq32 = vreinterpretq_u32_u8(not_eq); const uint32_t l0 = vgetq_lane_u32(neq32, 0); const uint32_t l1 = vgetq_lane_u32(neq32, 1); const uint64_t lo = ((uint64_t)l1 << 32) | l0; if (lo != 0) { p += (size_t)(zxc_ctz64(lo) >> 3); goto _run_done; } const uint32_t h0 = vgetq_lane_u32(neq32, 2); const uint32_t h1 = vgetq_lane_u32(neq32, 3); const uint64_t hi = ((uint64_t)h1 << 32) | h0; if (hi != 0) { p += 8 + (zxc_ctz64(hi) >> 3); goto _run_done; } p += 16; } #endif while (p < p_end && *p == b) p++; #if defined(ZXC_USE_AVX512) || defined(ZXC_USE_AVX2) || defined(ZXC_USE_NEON64) || \ defined(ZXC_USE_NEON32) || defined(ZXC_USE_SSE2) _run_done:; #endif const size_t run = (size_t)(p - run_start); if (run >= 4) { // RLE run: 2 bytes per 131 values, then remainder // Branchless: full_chunks * 2 + remainder handling const size_t full_chunks = run / 131; const size_t rem = run - full_chunks * 131; // Avoid modulo rle_size += full_chunks * 2; // Remainder: if >= 4 -> 2 bytes (RLE), else 1 + rem (literal) if (rem >= 4) rle_size += 2; else if (rem > 0) rle_size += 1 + rem; } else { // Literal run: scan ahead with fast SIMD lookahead const uint8_t* lit_start = run_start; #if defined(ZXC_USE_AVX512) while (p <= p_end_4 - 64) { const __m512i v0 = _mm512_loadu_si512((const void*)p); const __m512i v1 = _mm512_loadu_si512((const void*)(p + 1)); const uint64_t m = (uint64_t)_mm512_cmpeq_epi8_mask(v0, v1); const uint64_t mask = m & (m >> 1) & (m >> 2); if (LIKELY(mask == 0)) { p += 62; continue; } const unsigned rpos = (unsigned)zxc_ctz64(mask); const unsigned rpairs = (unsigned)zxc_ctz64(~(m >> rpos)); if (UNLIKELY(rpos + rpairs >= 64)) { p += rpos; goto _lit_done; /* run may extend past the window */ } const size_t seg = (size_t)(p + rpos - lit_start); rle_size += seg + ((seg + 127) >> 7); rle_size += 2; /* run in [4,63]: full_chunks=0, rem>=4 */ p += rpos + rpairs + 1; lit_start = p; } #elif defined(ZXC_USE_AVX2) while (p <= p_end_4 - 32) { __m256i v0 = _mm256_loadu_si256((const __m256i*)p); __m256i v1 = _mm256_loadu_si256((const __m256i*)(p + 1)); const uint32_t m = (uint32_t)_mm256_movemask_epi8(_mm256_cmpeq_epi8(v0, v1)); const uint32_t mask = m & (m >> 1) & (m >> 2); if (LIKELY(mask == 0)) { p += 30; continue; } const unsigned rpos = zxc_ctz32(mask); const unsigned rpairs = zxc_ctz32(~(m >> rpos)); if (UNLIKELY(rpos + rpairs >= 32)) { p += rpos; goto _lit_done; /* run may extend past the window */ } const size_t seg = (size_t)(p + rpos - lit_start); rle_size += seg + ((seg + 127) >> 7); rle_size += 2; /* run in [4,31]: full_chunks=0, rem>=4 */ p += rpos + rpairs + 1; lit_start = p; } #elif defined(ZXC_USE_SSE2) while (p <= p_end_4 - 16) { __m128i v0 = _mm_loadu_si128((const __m128i*)p); __m128i v1 = _mm_loadu_si128((const __m128i*)(p + 1)); const uint32_t m = (uint32_t)_mm_movemask_epi8(_mm_cmpeq_epi8(v0, v1)); const uint32_t mask = m & (m >> 1) & (m >> 2); if (LIKELY(mask == 0)) { p += 14; continue; } const unsigned rpos = zxc_ctz32(mask); const unsigned rpairs = zxc_ctz32(~(m >> rpos)); if (UNLIKELY(rpos + rpairs >= 16)) { p += rpos; goto _lit_done; /* run may extend past the window */ } const size_t seg = (size_t)(p + rpos - lit_start); rle_size += seg + ((seg + 127) >> 7); rle_size += 2; /* run in [4,16]: full_chunks=0, rem>=4 */ p += rpos + rpairs + 1; lit_start = p; } #elif defined(ZXC_USE_NEON64) while (p <= p_end_4 - 16) { uint8x16_t v0 = vld1q_u8(p); uint8x16_t v1 = vld1q_u8(p + 1); const uint8x16_t eq = vceqq_u8(v0, v1); const uint64_t m = vget_lane_u64( vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(eq), 4)), 0); const uint64_t mask = m & (m >> 4) & (m >> 8); if (LIKELY(mask == 0)) { p += 14; continue; } const unsigned rpos = (unsigned)(zxc_ctz64(mask) >> 2); const unsigned rpairs = (unsigned)(zxc_ctz64(~(m >> (rpos * 4))) >> 2); if (UNLIKELY(rpos + rpairs >= 16)) { p += rpos; goto _lit_done; /* run may extend past the window */ } const size_t seg = (size_t)(p + rpos - lit_start); rle_size += seg + ((seg + 127) >> 7); rle_size += 2; /* run in [4,16]: full_chunks=0, rem>=4 */ p += rpos + rpairs + 1; lit_start = p; } #elif defined(ZXC_USE_NEON32) while (p <= p_end_4 - 16) { uint8x16_t v0 = vld1q_u8(p); uint8x16_t v1 = vld1q_u8(p + 1); const uint8x16_t pe = vceqq_u8(v0, v1); const uint8x16_t zv = vdupq_n_u8(0); const uint8x16_t eq = vandq_u8(pe, vandq_u8(vextq_u8(pe, zv, 1), vextq_u8(pe, zv, 2))); uint32x4_t eq32 = vreinterpretq_u32_u8(eq); uint32_t l0 = vgetq_lane_u32(eq32, 0); uint32_t l1 = vgetq_lane_u32(eq32, 1); uint64_t lo = ((uint64_t)l1 << 32) | l0; if (lo != 0) { p += (zxc_ctz64(lo) >> 3); goto _lit_done; } uint32_t h0 = vgetq_lane_u32(eq32, 2); uint32_t h1 = vgetq_lane_u32(eq32, 3); uint64_t hi = ((uint64_t)h1 << 32) | h0; if (hi != 0) { p += 8 + (zxc_ctz64(hi) >> 3); goto _lit_done; } p += 14; } #endif while (p < p_end_4) { // Check for RLE opportunity (4 identical bytes) if (UNLIKELY(p[0] == p[1] && p[1] == p[2] && p[2] == p[3])) break; p++; } // Handle remaining bytes near end while (p < p_end) { if (UNLIKELY(p + 3 < p_end && p[0] == p[1] && p[1] == p[2] && p[2] == p[3])) break; p++; } #if defined(ZXC_USE_AVX512) || defined(ZXC_USE_AVX2) || defined(ZXC_USE_NEON64) || \ defined(ZXC_USE_NEON32) || defined(ZXC_USE_SSE2) _lit_done:; #endif const size_t lit_run = (size_t)(p - lit_start); // 1 header per 128 bytes + all data bytes // lit_run + ceil(lit_run / 128) rle_size += lit_run + ((lit_run + 127) >> 7); } } /* RLE over RAW: space-speed J comparison. The per-level premium * reproduces the historical margin exactly below ULTRA. */ const size_t rle_j = rle_size + ZXC_SS_TAX(lit_c, zxc_ss_prem_rle_q8(level)); if (rle_j < best_j) { enc_lit = ZXC_SECTION_ENCODING_RLE; best_j = rle_j; } } /* Level >= 6: also evaluate Huffman as a 3rd literal-encoding candidate. * Build a histogram and length-limited canonical code lengths, compute the * exact PivCo section size (128-byte header + derived node runs), and * switch to HUFFMAN if its J (size + decode tax) beats the current * winner's. */ uint8_t huf_code_len[ZXC_HUF_NUM_SYMBOLS]; size_t huf_total_size = SIZE_MAX; uint32_t lit_freq[ZXC_HUF_NUM_SYMBOLS]; /* valid iff huf_total_size != SIZE_MAX */ if (level >= ZXC_LEVEL_DENSITY && lit_c >= ZXC_HUF_MIN_LITERALS) { uint32_t freq0[ZXC_HUF_NUM_SYMBOLS] = {0}; uint32_t freq1[ZXC_HUF_NUM_SYMBOLS] = {0}; uint32_t freq2[ZXC_HUF_NUM_SYMBOLS] = {0}; uint32_t freq3[ZXC_HUF_NUM_SYMBOLS] = {0}; { size_t i = 0; for (; i + 4 <= lit_c; i += 4) { freq0[literals[i + 0]]++; freq1[literals[i + 1]]++; freq2[literals[i + 2]]++; freq3[literals[i + 3]]++; } for (; i < lit_c; i++) freq0[literals[i]]++; } uint32_t* const freq = lit_freq; for (int k = 0; k < ZXC_HUF_NUM_SYMBOLS; k++) { freq[k] = freq0[k] + freq1[k] + freq2[k] + freq3[k]; } if (zxc_huf_build_code_lengths(freq, huf_code_len, ctx->opt_scratch, zxc_huf_enc_max_code_len(level)) == ZXC_OK) { huf_total_size = zxc_huf_calc_size(freq, huf_code_len, 1); /* Space-speed: the entropy candidate must beat the current winner's * J, paying its own decode tax over the copy path. */ if (huf_total_size != SIZE_MAX) { const size_t huf_j = huf_total_size + ZXC_SS_TAX(lit_c, zxc_ss_prem_huf_q8(level)); if (huf_j < best_j) { enc_lit = ZXC_SECTION_ENCODING_HUFFMAN; best_j = huf_j; } } } } /* Shared dictionary table candidate (level >= 6, dict with table attached): * same bitstream as HUFFMAN but no 128-byte lengths header, so it stays * viable on literal sections far below ZXC_HUF_MIN_LITERALS. Exact size * accounting; invalid (a literal byte without a code) drops the candidate. */ size_t huf_dict_total_size = SIZE_MAX; if (level >= ZXC_LEVEL_DENSITY && ctx->dict_huf_tree_ok && lit_c > 0) { /* The dict candidate runs on sections too small for the per-block * table, so the histogram may not have been built above. */ if (huf_total_size == SIZE_MAX) { ZXC_MEMSET(lit_freq, 0, sizeof(uint32_t) * ZXC_HUF_NUM_SYMBOLS); for (size_t i = 0; i < lit_c; i++) lit_freq[literals[i]]++; } /* zxc_huf_calc_size returns SIZE_MAX for unencodable candidates * (a literal byte without a code in the shared table). */ huf_dict_total_size = zxc_huf_calc_size_dict(lit_freq, ctx->dict_huf->code_len, &ctx->dict_huf->tree); if (huf_dict_total_size != SIZE_MAX) { /* Same J rule against the running winner: vs the per-block Huffman * candidate the equal entropy taxes cancel (pure size comparison), * while RAW/RLE baselines keep their decode-tax edge. */ const size_t huf_dict_j = huf_dict_total_size + ZXC_SS_TAX(lit_c, zxc_ss_prem_huf_q8(level)); /* Last candidate: no need to keep best_j updated past this point. */ if (huf_dict_j < best_j) enc_lit = ZXC_SECTION_ENCODING_HUFFMAN_DICT; } } /* Level-7 (ULTRA): Huffman-code the token byte stream, reusing the literal * codec (tokens are a <=256-symbol byte alphabet). Same J rule as literals * (entropy tax vs the RAW copy); the decoder selects the path from * gh.enc_litlen. opt_scratch * is free here (the parse and literal code-length build are done). */ uint8_t tok_code_len[ZXC_HUF_NUM_SYMBOLS]; uint8_t enc_tok = ZXC_SECTION_ENCODING_RAW; size_t tok_huf_size = 0; uint32_t tfreq[ZXC_HUF_NUM_SYMBOLS]; if (level >= ZXC_LEVEL_ULTRA && seq_c >= ZXC_HUF_MIN_LITERALS) { ZXC_MEMSET(tfreq, 0, sizeof(tfreq)); /* only the ULTRA path reads tfreq */ for (uint32_t i = 0; i < seq_c; i++) tfreq[buf_tokens[i]]++; if (zxc_huf_build_code_lengths(tfreq, tok_code_len, ctx->opt_scratch, zxc_huf_enc_max_code_len(level)) == ZXC_OK) { tok_huf_size = zxc_huf_calc_size(tfreq, tok_code_len, 1); /* Space-speed J comparison (this path is ULTRA-only): the PivCo * token section pays the same decode tax as PivCo literals. */ if (tok_huf_size != SIZE_MAX && tok_huf_size + ZXC_SS_TAX((size_t)seq_c, zxc_ss_prem_huf_q8(level)) < (size_t)seq_c) enc_tok = ZXC_SECTION_ENCODING_HUFFMAN; } } zxc_block_header_t bh = {.block_type = ZXC_BLOCK_GLO}; uint8_t* const p = dst + ZXC_BLOCK_HEADER_SIZE; size_t rem = dst_cap - ZXC_BLOCK_HEADER_SIZE; // Decide offset encoding mode: 1-byte if all offsets <= 255 const int use_8bit_off = (max_offset <= 255) ? 1 : 0; const size_t off_stream_size = use_8bit_off ? seq_c : (seq_c * 2); const zxc_gnr_header_t gh = {.n_sequences = seq_c, .n_literals = (uint32_t)lit_c, .enc_lit = enc_lit, .enc_litlen = enc_tok, .enc_mlen = 0, .enc_off = (uint8_t)use_8bit_off}; zxc_section_desc_t desc[ZXC_GLO_SECTIONS] = {0}; const size_t lit_section_size = (enc_lit == ZXC_SECTION_ENCODING_RLE) ? rle_size : (enc_lit == ZXC_SECTION_ENCODING_HUFFMAN) ? huf_total_size : (enc_lit == ZXC_SECTION_ENCODING_HUFFMAN_DICT) ? huf_dict_total_size : lit_c; desc[0].sizes = (uint64_t)lit_section_size | ((uint64_t)lit_c << 32); const size_t tok_section_size = (enc_tok == ZXC_SECTION_ENCODING_HUFFMAN) ? tok_huf_size : seq_c; desc[1].sizes = (uint64_t)tok_section_size | ((uint64_t)seq_c << 32); desc[2].sizes = (uint64_t)off_stream_size | ((uint64_t)off_stream_size << 32); desc[3].sizes = (uint64_t)extras_sz | ((uint64_t)extras_sz << 32); const int ghs = zxc_write_glo_header_and_desc(p, rem, &gh, desc); if (UNLIKELY(ghs < 0)) return ghs; uint8_t* p_curr = p + ghs; rem -= ghs; // Extract stream sizes once const size_t sz_lit = (size_t)(desc[0].sizes & ZXC_SECTION_SIZE_MASK); const size_t sz_tok = (size_t)(desc[1].sizes & ZXC_SECTION_SIZE_MASK); const size_t sz_off = (size_t)(desc[2].sizes & ZXC_SECTION_SIZE_MASK); const size_t sz_ext = (size_t)(desc[3].sizes & ZXC_SECTION_SIZE_MASK); if (UNLIKELY(rem < sz_lit)) return ZXC_ERROR_DST_TOO_SMALL; if (enc_lit == ZXC_SECTION_ENCODING_HUFFMAN) { const int written = zxc_huf_encode_section(literals, lit_c, lit_freq, huf_code_len, p_curr, rem); if (UNLIKELY(written < 0)) return written; if (UNLIKELY((size_t)written != huf_total_size)) return ZXC_ERROR_DST_TOO_SMALL; p_curr += written; } else if (enc_lit == ZXC_SECTION_ENCODING_HUFFMAN_DICT) { const int written = zxc_huf_encode_section_dict(literals, lit_c, lit_freq, ctx->dict_huf->code_len, &ctx->dict_huf->tree, ctx->dict_huf->codes, p_curr, rem); if (UNLIKELY(written < 0)) return written; if (UNLIKELY((size_t)written != huf_dict_total_size)) return ZXC_ERROR_DST_TOO_SMALL; p_curr += written; } else if (enc_lit == ZXC_SECTION_ENCODING_RLE) { // Write RLE - optimized single-pass encoding const uint8_t* lit_ptr = literals; const uint8_t* const lit_end = literals + lit_c; while (lit_ptr < lit_end) { uint8_t b = *lit_ptr; const uint8_t* run_start = lit_ptr++; // Count run length while (lit_ptr < lit_end && *lit_ptr == b) lit_ptr++; size_t run = (size_t)(lit_ptr - run_start); if (run >= 4) { // RLE runs: emit 2-byte tokens (header + value) while (run >= 4) { size_t chunk = (run > 131) ? 131 : run; *p_curr++ = (uint8_t)(ZXC_LIT_RLE_FLAG | (chunk - 4)); *p_curr++ = b; run -= chunk; } // Leftover < 4 bytes: emit as literal if (run > 0) { *p_curr++ = (uint8_t)(run - 1); ZXC_MEMCPY(p_curr, lit_ptr - run, run); p_curr += run; } } else { // Literal run: scan ahead to find next RLE opportunity const uint8_t* lit_run_start = run_start; while (lit_ptr < lit_end) { // Quick check: need 4 identical bytes to break if (UNLIKELY(lit_ptr + 3 < lit_end && lit_ptr[0] == lit_ptr[1] && lit_ptr[1] == lit_ptr[2] && lit_ptr[2] == lit_ptr[3])) { break; } lit_ptr++; } size_t lit_run = (size_t)(lit_ptr - lit_run_start); const uint8_t* src_ptr = lit_run_start; // Emit literal chunks (max 128 bytes each) while (lit_run > 0) { size_t chunk = (lit_run > 128) ? 128 : lit_run; *p_curr++ = (uint8_t)(chunk - 1); ZXC_MEMCPY(p_curr, src_ptr, chunk); p_curr += chunk; src_ptr += chunk; lit_run -= chunk; } } } } else { ZXC_MEMCPY(p_curr, literals, lit_c); p_curr += lit_c; } rem -= sz_lit; if (UNLIKELY(rem < sz_tok)) return ZXC_ERROR_DST_TOO_SMALL; if (enc_tok == ZXC_SECTION_ENCODING_HUFFMAN) { const int written = zxc_huf_encode_section(buf_tokens, seq_c, tfreq, tok_code_len, p_curr, rem); if (UNLIKELY(written < 0)) return written; if (UNLIKELY((size_t)written != tok_huf_size)) return ZXC_ERROR_DST_TOO_SMALL; p_curr += written; } else { ZXC_MEMCPY(p_curr, buf_tokens, seq_c); p_curr += seq_c; } rem -= sz_tok; if (UNLIKELY(rem < sz_off)) return ZXC_ERROR_DST_TOO_SMALL; if (use_8bit_off) { // Write 1-byte offsets - unroll for better throughput uint32_t i = 0; for (; i + 8 <= seq_c; i += 8) { p_curr[0] = (uint8_t)buf_offsets[i + 0]; p_curr[1] = (uint8_t)buf_offsets[i + 1]; p_curr[2] = (uint8_t)buf_offsets[i + 2]; p_curr[3] = (uint8_t)buf_offsets[i + 3]; p_curr[4] = (uint8_t)buf_offsets[i + 4]; p_curr[5] = (uint8_t)buf_offsets[i + 5]; p_curr[6] = (uint8_t)buf_offsets[i + 6]; p_curr[7] = (uint8_t)buf_offsets[i + 7]; p_curr += 8; } for (; i < seq_c; i++) { *p_curr++ = (uint8_t)buf_offsets[i]; } } else { // Write 2-byte offsets in little-endian order #ifdef ZXC_BIG_ENDIAN for (uint32_t i = 0; i < seq_c; i++) { zxc_store_le16(p_curr, buf_offsets[i]); p_curr += sizeof(uint16_t); } #else ZXC_MEMCPY(p_curr, buf_offsets, seq_c * sizeof(uint16_t)); p_curr += seq_c * sizeof(uint16_t); #endif } rem -= sz_off; if (UNLIKELY(rem < sz_ext)) return ZXC_ERROR_DST_TOO_SMALL; ZXC_MEMCPY(p_curr, buf_extras, extras_sz); p_curr += extras_sz; bh.comp_size = (uint32_t)(p_curr - (dst + ZXC_BLOCK_HEADER_SIZE)); const int hw = zxc_write_block_header(dst, dst_cap, &bh); if (UNLIKELY(hw < 0)) return hw; // Checksum will be appended by the wrapper *out_sz = ZXC_BLOCK_HEADER_SIZE + bh.comp_size; return ZXC_OK; } /** * @brief Encodes a data block using the General High Velocity (GHI) compression format. * * 1. Compression Strategy * It uses an LZ77-based algorithm with a sliding window (64KB) and a hash table/chain table * mechanism. * * 2. Token Format (Fixed-Width) * Unlike the standard GLO block which uses 1-byte tokens (4-bit literal length / 4-bit match * length), GHI uses 4-byte (32-bit) sequence records for better performance on long runs: * Literal Length (LL): 8 bits (stores 0-254; 255 indicates overflow). * Match Length (ML): 8 bits (stores 0-254; 255 indicates overflow). * Offset: 16 bits (supports the full 64KB window). * This format minimizes the number of expensive VByte reads during decompression for common * sequences where lengths are between 16 and 255. * * @param[in,out] ctx Pointer to the compression context containing hash tables * and configuration. * @param[in] src Pointer to the input source data. * @param[in] src_sz Size of the input data in bytes. * @param[out] dst Pointer to the destination buffer where compressed data will * be written. * @param[in] dst_cap Maximum capacity of the destination buffer. * @param[out] out_sz Pointer to a variable that will receive the total size * of the compressed output. * * @return ZXC_OK on success, or a negative zxc_error_t code (e.g., ZXC_ERROR_DST_TOO_SMALL) if an * error occurs (e.g., buffer overflow). */ static int zxc_encode_block_ghi(zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap, size_t* RESTRICT const out_sz) { const int level = ctx->compression_level; const size_t dict_sz = ctx->dict_size; const zxc_lz77_params_t lzp = zxc_get_lz77_params(level); ctx->epoch++; if (UNLIKELY(ctx->epoch >= ctx->max_epoch)) { ZXC_MEMSET(ctx->hash_table, 0, ZXC_LZ_HASH_SIZE * sizeof(uint32_t)); ZXC_MEMSET(ctx->hash_tags, 0, ZXC_LZ_HASH_SIZE * sizeof(uint8_t)); ctx->epoch = 1; } const uint32_t offset_bits = ctx->offset_bits; const uint32_t offset_mask = ctx->offset_mask; const uint32_t epoch_mark = ctx->epoch << offset_bits; if (dict_sz > 0) zxc_lz_seed_dict(src, dict_sz, ctx->hash_table, ctx->hash_tags, ctx->chain_table, epoch_mark, offset_mask, level); const uint8_t* ip = src + dict_sz; const uint8_t* iend = src + src_sz; const uint8_t* anchor = ip; const uint8_t* search_limit = iend - ZXC_LZ_SEARCH_MARGIN; uint32_t* const hash_table = ctx->hash_table; uint8_t* const hash_tags = ctx->hash_tags; uint8_t* const buf_extras = ctx->buf_extras; uint16_t* const chain_table = ctx->chain_table; uint8_t* const literals = ctx->literals; uint32_t* const buf_sequences = ctx->buf_sequences; uint32_t seq_c = 0; size_t extras_c = 0; size_t lit_c = 0; uint16_t max_offset = 0; while (LIKELY(ip < search_limit)) { size_t dist = (size_t)(ip - anchor); size_t step = lzp.step_base + (dist >> lzp.step_shift); if (UNLIKELY(ip + step >= search_limit)) step = 1; ZXC_PREFETCH_READ(ip + step * 4 + ZXC_CACHE_LINE_SIZE); if (LIKELY(ip + step + sizeof(uint64_t) <= iend)) { const uint64_t v_next = zxc_le64(ip + step); // cppcheck-suppress unreadVariable const uint32_t h_next = zxc_hash_func(v_next, 0); ZXC_PREFETCH_READ(&hash_tags[h_next]); ZXC_PREFETCH_READ(&hash_table[h_next]); } const zxc_match_t m = zxc_lz77_find_best_match(src, ip, iend, search_limit, anchor, hash_table, hash_tags, chain_table, epoch_mark, offset_mask, level, lzp, /*last_off=*/0U); if (m.ref) { ip -= m.backtrack; const uint32_t ll = (uint32_t)(ip - anchor); const uint32_t ml = m.len - ZXC_LZ_MIN_MATCH_LEN; const uint32_t off = (uint32_t)(ip - m.ref); if (ll > 0) { if (LIKELY(anchor + ZXC_PAD_SIZE <= iend)) { zxc_copy32(literals + lit_c, anchor); if (UNLIKELY(ll > ZXC_PAD_SIZE)) { ZXC_MEMCPY(literals + lit_c + ZXC_PAD_SIZE, anchor + ZXC_PAD_SIZE, ll - ZXC_PAD_SIZE); } } else { ZXC_MEMCPY(literals + lit_c, anchor, ll); } lit_c += ll; } const uint32_t ll_write = (ll >= ZXC_SEQ_LL_MASK) ? 255U : ll; const uint32_t ml_write = (ml >= ZXC_SEQ_ML_MASK) ? 255U : ml; const uint32_t seq_val = (ll_write << (ZXC_SEQ_ML_BITS + ZXC_SEQ_OFF_BITS)) | (ml_write << ZXC_SEQ_OFF_BITS) | ((off - ZXC_LZ_OFFSET_BIAS) & ZXC_SEQ_OFF_MASK); if ((off - ZXC_LZ_OFFSET_BIAS) > max_offset) max_offset = (uint16_t)(off - ZXC_LZ_OFFSET_BIAS); buf_sequences[seq_c] = seq_val; seq_c++; if (ll >= ZXC_SEQ_LL_MASK) { const size_t n = zxc_write_varint(buf_extras + extras_c, ll - ZXC_SEQ_LL_MASK); if (UNLIKELY(n == 0)) return ZXC_ERROR_OVERFLOW; extras_c += n; } if (ml >= ZXC_SEQ_ML_MASK) { const size_t n = zxc_write_varint(buf_extras + extras_c, ml - ZXC_SEQ_ML_MASK); if (UNLIKELY(n == 0)) return ZXC_ERROR_OVERFLOW; extras_c += n; } ip += m.len; anchor = ip; } else { ip += step; } } const size_t last_lits = iend - anchor; if (last_lits > 0) { ZXC_MEMCPY(literals + lit_c, anchor, last_lits); lit_c += last_lits; } zxc_block_header_t bh = {.block_type = ZXC_BLOCK_GHI}; uint8_t* const p = dst + ZXC_BLOCK_HEADER_SIZE; size_t rem = dst_cap - ZXC_BLOCK_HEADER_SIZE; // Decide offset encoding mode const zxc_gnr_header_t gh = {.n_sequences = seq_c, .n_literals = (uint32_t)lit_c, .enc_lit = ZXC_SECTION_ENCODING_RAW, .enc_litlen = 0, .enc_mlen = 0, .enc_off = (uint8_t)(max_offset <= 255) ? 1 : 0}; zxc_section_desc_t desc[ZXC_GHI_SECTIONS] = {0}; desc[0].sizes = (uint64_t)lit_c | ((uint64_t)lit_c << 32); size_t sz_seqs = seq_c * sizeof(uint32_t); desc[1].sizes = (uint64_t)sz_seqs | ((uint64_t)sz_seqs << 32); desc[2].sizes = (uint64_t)extras_c | ((uint64_t)extras_c << 32); const int ghs = zxc_write_ghi_header_and_desc(p, rem, &gh, desc); if (UNLIKELY(ghs < 0)) return ghs; uint8_t* p_curr = p + ghs; rem -= ghs; // Extract stream sizes once const size_t sz_lit = (size_t)(desc[0].sizes & ZXC_SECTION_SIZE_MASK); const size_t sz_seq = (size_t)(desc[1].sizes & ZXC_SECTION_SIZE_MASK); const size_t sz_ext = (size_t)(desc[2].sizes & ZXC_SECTION_SIZE_MASK); if (UNLIKELY(rem < sz_lit + sz_seq + sz_ext)) return ZXC_ERROR_DST_TOO_SMALL; ZXC_MEMCPY(p_curr, literals, lit_c); p_curr += lit_c; rem -= sz_lit; if (UNLIKELY(rem < sz_seq)) return ZXC_ERROR_DST_TOO_SMALL; // Write sequences in little-endian order #ifdef ZXC_BIG_ENDIAN for (uint32_t i = 0; i < seq_c; i++) { zxc_store_le32(p_curr, buf_sequences[i]); p_curr += sizeof(uint32_t); } #else ZXC_MEMCPY(p_curr, buf_sequences, sz_seq); p_curr += sz_seq; #endif // --- WRITE EXTRAS --- ZXC_MEMCPY(p_curr, buf_extras, sz_ext); p_curr += sz_ext; bh.comp_size = (uint32_t)(p_curr - (dst + ZXC_BLOCK_HEADER_SIZE)); const int hw = zxc_write_block_header(dst, dst_cap, &bh); if (UNLIKELY(hw < 0)) return hw; // Checksum will be appended by the wrapper *out_sz = ZXC_BLOCK_HEADER_SIZE + bh.comp_size; return ZXC_OK; } /** * @brief Encodes a raw data block (uncompressed). * * This function prepares and writes a "RAW" type block into the destination * buffer. It handles the block header and copying of source data; any checksum * is appended separately by the wrapper. * * @param[in] src Pointer to the source data to encode. * @param[in] src_sz Size of the source data in bytes. * @param[out] dst Pointer to the destination buffer. * @param[in] dst_cap Maximum capacity of the destination buffer. * @param[out] out_sz Pointer to a variable receiving the total written size * (header + data). * * @return ZXC_OK on success, or a negative zxc_error_t code (e.g., ZXC_ERROR_DST_TOO_SMALL) if the * destination buffer capacity is insufficient. */ static int zxc_encode_block_raw(const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT const dst, const size_t dst_cap, size_t* RESTRICT const out_sz) { if (UNLIKELY(dst_cap < ZXC_BLOCK_HEADER_SIZE + src_sz)) return ZXC_ERROR_DST_TOO_SMALL; // Compute block RAW zxc_block_header_t bh; bh.block_type = ZXC_BLOCK_RAW; bh.block_flags = 0; // Checksum flag moved to file header bh.reserved = 0; bh.comp_size = (uint32_t)src_sz; const int hw = zxc_write_block_header(dst, dst_cap, &bh); if (UNLIKELY(hw < 0)) return hw; ZXC_MEMCPY(dst + ZXC_BLOCK_HEADER_SIZE, src, src_sz); // Checksum will be appended by the wrapper *out_sz = ZXC_BLOCK_HEADER_SIZE + src_sz; return ZXC_OK; } /** * @brief Compresses one chunk into a single ZXC block (the compression hot path). * * Selects the GHI encoder at level <= 2, otherwise GLO; falls back to a RAW * block when the coded form would not shrink the data. When @c ctx->dict_size * is > 0, @p chunk is the [dict | block] concat and only the block tail counts * toward the expansion check. Appends the per-block checksum when enabled. * * @param[in,out] ctx Compression context (level, dict_size, checksum, buffers). * @param[in] chunk Source bytes ([dict | block] when a dictionary is active). * @param[in] src_sz Length of @p chunk in bytes (includes any dict prefix). * @param[out] dst Destination block buffer. * @param[in] dst_cap Capacity of @p dst in bytes. * @return Compressed block size in bytes on success, or a negative @ref zxc_error_t. */ // cppcheck-suppress unusedFunction int zxc_compress_chunk_wrapper(zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT chunk, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap) { const size_t dict_sz = ctx->dict_size; const size_t block_sz = src_sz - dict_sz; const uint8_t* block_data = chunk + dict_sz; size_t w = 0; int res = ZXC_OK; if (ctx->compression_level <= 2) res = zxc_encode_block_ghi(ctx, chunk, src_sz, dst, dst_cap, &w); else res = zxc_encode_block_glo(ctx, chunk, src_sz, dst, dst_cap, &w); // Check expansion against block data size (excluding dict prefix). if (UNLIKELY(res != ZXC_OK || w >= block_sz)) { res = zxc_encode_block_raw(block_data, block_sz, dst, dst_cap, &w); if (UNLIKELY(res != ZXC_OK)) return res; } if (ctx->checksum_enabled) { // Calculate checksum on the compressed payload (w currently excludes checksum) // Header is at dst, data starts at dst + ZXC_BLOCK_HEADER_SIZE if (UNLIKELY(w < ZXC_BLOCK_HEADER_SIZE || w + ZXC_BLOCK_CHECKSUM_SIZE > dst_cap)) return ZXC_ERROR_OVERFLOW; uint32_t payload_sz = (uint32_t)(w - ZXC_BLOCK_HEADER_SIZE); uint32_t crc = zxc_checksum(dst + ZXC_BLOCK_HEADER_SIZE, payload_sz, ZXC_CHECKSUM_RAPIDHASH); zxc_store_le32(dst + w, crc); w += ZXC_BLOCK_CHECKSUM_SIZE; } return (int)w; } zxc-0.13.0/src/lib/zxc_decompress.c000066400000000000000000003120571522536370400171470ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file zxc_decompress.c * @brief Block-level decompression: GLO / GHI / RAW decoding with * SIMD-accelerated bit-unpacking and overlapping copies. * * Like @ref zxc_compress.c, this file is compiled multiple times with * @c ZXC_FUNCTION_SUFFIX to produce per-ISA variants. */ /* * Function Multi-Versioning Support * If ZXC_FUNCTION_SUFFIX is defined (e.g. _avx2, _neon), rename the public * entry point AND the Huffman decoder consumed by this TU. The defines sit * before zxc_internal.h so that the prototypes the header declares are also * rewritten with the suffix, keeping callers and callees consistent. */ #ifdef ZXC_FUNCTION_SUFFIX #define ZXC_CAT_IMPL(x, y) x##y #define ZXC_CAT(x, y) ZXC_CAT_IMPL(x, y) #define zxc_decompress_chunk_wrapper ZXC_CAT(zxc_decompress_chunk_wrapper, ZXC_FUNCTION_SUFFIX) #define zxc_decompress_chunk_wrapper_dict \ ZXC_CAT(zxc_decompress_chunk_wrapper_dict, ZXC_FUNCTION_SUFFIX) #define zxc_decompress_chunk_wrapper_safe \ ZXC_CAT(zxc_decompress_chunk_wrapper_safe, ZXC_FUNCTION_SUFFIX) #define zxc_huf_decode_section ZXC_CAT(zxc_huf_decode_section, ZXC_FUNCTION_SUFFIX) #define zxc_huf_decode_section_dict ZXC_CAT(zxc_huf_decode_section_dict, ZXC_FUNCTION_SUFFIX) #endif #include "../../include/zxc_error.h" #include "zxc_internal.h" /** * @brief Reads a Prefix Varint encoded integer from a stream. * * This function decodes a 32-bit unsigned integer encoded in Prefix Varint format * from the provided byte stream. Unary prefix bits in the first byte determine * the total length (1-3 bytes). * * Format: * - 1 byte (0xxxxxxx): 7-bit payload (val < 2^7 = 128) * - 2 bytes (10xxxxxx): 14-bit payload (val < 2^14 = 16384) * - 3 bytes (110xxxxx): 21-bit payload (val < 2^21 = 2097152) * * @param[in,out] ptr Pointer to a pointer to the current position in the stream. * @param[in] end Pointer to the end of the readable stream (for bounds checking). * @return The decoded 32-bit integer, or 0 if reading would overflow bounds (safe default). */ static ZXC_ALWAYS_INLINE uint32_t zxc_read_varint(const uint8_t** ptr, const uint8_t* end) { const uint8_t* p = *ptr; // Bounds check: need at least 1 byte if (UNLIKELY(p >= end)) return 0; const uint32_t b0 = p[0]; // 1 Byte: 0xxxxxxx (7 bits) -> val < 128 (2^7) if (LIKELY(b0 < 0x80)) { *ptr = p + 1; return b0; } // 2 Bytes: 10xxxxxx xxxxxxxx (14 bits) -> val < 16384 (2^14) if (LIKELY(b0 < 0xC0)) { if (UNLIKELY(p + 1 >= end)) { *ptr = end; return 0; } *ptr = p + 2; return (b0 & 0x3F) | ((uint32_t)p[1] << 6); } // 3 Bytes: 110xxxxx xxxxxxxx xxxxxxxx (21 bits) -> val < 2097152 (2^21). // This is the largest length a legitimate varint can take: block_size_max // is 2^21 and varint values represent (ll - MASK) or (ml - MASK), which is // always strictly less than block_size_max. if (LIKELY(b0 < 0xE0)) { if (UNLIKELY(p + 2 >= end)) { *ptr = end; return 0; } *ptr = p + 3; return (b0 & 0x1F) | ((uint32_t)p[1] << 5) | ((uint32_t)p[2] << 13); } // extra encoding: out-of-spec for the current format, reject. *ptr = end; return 0; } /** * @brief Shuffle masks for overlapping copies with small offsets (0-15). * * Shared between ARM NEON and x86 SSSE3. Each row defines how to replicate * source bytes to fill 16 bytes when offset < 16. */ #if defined(ZXC_USE_NEON64) || defined(ZXC_USE_NEON32) || defined(ZXC_USE_AVX2) || \ defined(ZXC_USE_AVX512) /** * @brief Precomputed masks for handling overlapping data during decompression. * * This 16x16 lookup table contains 128-bit aligned masks used to efficiently * mask off or combine bytes when processing overlapping copy operations or * boundary conditions in the ZXC decompression algorithm. * * The alignment to 16 bytes ensures compatibility with SIMD instructions * (like SSE/AVX) for optimized memory operations. */ static const ZXC_ALIGN(16) uint8_t zxc_overlap_masks[16][16] = { {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // off=0 (unused) {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // off=1 (RLE handled separately) {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}, // off=2 {0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0}, // off=3 {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3}, // off=4 {0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0}, // off=5 {0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3}, // off=6 {0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1}, // off=7 {0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7}, // off=8 {0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3, 4, 5, 6}, // off=9 {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5}, // off=10 {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 1, 2, 3, 4}, // off=11 {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 1, 2, 3}, // off=12 {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2}, // off=13 {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 1}, // off=14 {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0} // off=15 }; #endif /** * @brief Per-offset store stride for periodic overlap runs: the largest * multiple of @c off that fits in 16 bytes, i.e. `16 - (16 % off)`. * * Advancing the output cursor by a multiple of @c off keeps the 16-byte * pattern vector phase-aligned, so the run is emitted with pure stores of a * single register. Entries 0 and 1 are unused (RLE handled separately). */ static const uint8_t zxc_overlap_strides[16] = {16, 16, 16, 15, 16, 15, 12, 14, 16, 9, 10, 11, 12, 13, 14, 15}; /** * @brief Copies an @p ml-byte LZ run whose pattern repeats with period @p off (2..15). * * Builds the 16-byte periodic pattern `out[i] = dst[-off + (i % off)]` once * (one shuffle on NEON/SSSE3, a wrap-counter byte loop on the SSE2/scalar * tier), then emits 16-byte stores advancing by @ref zxc_overlap_strides so * the pattern never needs re-shuffling. May overshoot up to 15 bytes past * @p ml; the caller must guarantee @ref ZXC_PAD_SIZE bytes of headroom. * * @param[out] dst Output cursor; the run source is `dst - off`. * @param[in] off Back-reference distance, in [2, 15]. * @param[in] ml Run length in bytes (>= 1). */ // codeql[cpp/unused-static-function] : False positive static ZXC_ALWAYS_INLINE void zxc_decode_copy_overlap_run(uint8_t* dst, const uint32_t off, const uint64_t ml) { const size_t stride = zxc_overlap_strides[off]; size_t copied = 0; #if defined(ZXC_USE_NEON64) const uint8x16_t mask = vld1q_u8(zxc_overlap_masks[off]); const uint8x16_t pat = vqtbl1q_u8(vld1q_u8(dst - off), mask); do { vst1q_u8(dst + copied, pat); copied += stride; } while (copied < ml); #elif defined(ZXC_USE_NEON32) uint8x8x2_t src_tbl; src_tbl.val[0] = vld1_u8(dst - off); src_tbl.val[1] = vld1_u8(dst - off + 8); const uint8x8_t pat_lo = vtbl2_u8(src_tbl, vld1_u8(zxc_overlap_masks[off])); const uint8x8_t pat_hi = vtbl2_u8(src_tbl, vld1_u8(zxc_overlap_masks[off] + 8)); do { vst1_u8(dst + copied, pat_lo); vst1_u8(dst + copied + 8, pat_hi); copied += stride; } while (copied < ml); #elif defined(ZXC_USE_AVX2) || defined(ZXC_USE_AVX512) const __m128i mask = _mm_load_si128((const __m128i*)zxc_overlap_masks[off]); const __m128i src_data = _mm_loadu_si128((const __m128i*)(dst - off)); const __m128i pat = _mm_shuffle_epi8(src_data, mask); do { _mm_storeu_si128((__m128i*)(dst + copied), pat); copied += stride; } while (copied < ml); #else // SSE2-only tier and non-SIMD builds: no PSHUFB, build the pattern with a // wrap counter (no per-byte modulo), then store it via zxc_copy16. const uint8_t* src = dst - off; uint8_t pat[16]; uint32_t k = 0; for (size_t i = 0; i < 16; i++) { pat[i] = src[k]; if (++k == off) k = 0; } do { zxc_copy16(dst + copied, pat); copied += stride; } while (copied < ml); #endif } /** * @brief Fills an @p ml-byte single-byte run (LZ offset == 1) with wild stores. * * Splats @p byte into a vector register and emits @ref ZXC_PAD_SIZE-byte * chunks, avoiding a libc memset call on the typically short runs of the hot * path. Like the other run copiers it may **overshoot** up to * @ref ZXC_PAD_SIZE - 1 bytes past @p ml; the caller must guarantee * @ref ZXC_PAD_SIZE bytes of headroom. Falls back to @ref ZXC_MEMSET on * non-SIMD builds. * * @param[out] dst Output cursor. * @param[in] byte Byte value to replicate. * @param[in] ml Run length in bytes (>= 1). */ // codeql[cpp/unused-static-function] : False positive, used in DECODE_SEQ_SAFE/FAST macros static ZXC_ALWAYS_INLINE void zxc_decode_fill_run(uint8_t* dst, const uint8_t byte, const uint64_t ml) { #if defined(ZXC_USE_AVX2) || defined(ZXC_USE_AVX512) const __m256i v = _mm256_set1_epi8((char)byte); _mm256_storeu_si256((__m256i*)dst, v); if (UNLIKELY(ml > ZXC_PAD_SIZE)) { uint8_t* out = dst + ZXC_PAD_SIZE; size_t rem = ml - ZXC_PAD_SIZE; while (rem > ZXC_PAD_SIZE) { _mm256_storeu_si256((__m256i*)out, v); out += ZXC_PAD_SIZE; rem -= ZXC_PAD_SIZE; } _mm256_storeu_si256((__m256i*)out, v); } #elif defined(ZXC_USE_SSE2) const __m128i v = _mm_set1_epi8((char)byte); _mm_storeu_si128((__m128i*)dst, v); _mm_storeu_si128((__m128i*)(dst + 16), v); if (UNLIKELY(ml > ZXC_PAD_SIZE)) { uint8_t* out = dst + ZXC_PAD_SIZE; size_t rem = ml - ZXC_PAD_SIZE; while (rem > ZXC_PAD_SIZE) { _mm_storeu_si128((__m128i*)out, v); _mm_storeu_si128((__m128i*)(out + 16), v); out += ZXC_PAD_SIZE; rem -= ZXC_PAD_SIZE; } _mm_storeu_si128((__m128i*)out, v); _mm_storeu_si128((__m128i*)(out + 16), v); } #elif defined(ZXC_USE_NEON64) || defined(ZXC_USE_NEON32) const uint8x16_t v = vdupq_n_u8(byte); vst1q_u8(dst, v); vst1q_u8(dst + 16, v); if (UNLIKELY(ml > ZXC_PAD_SIZE)) { uint8_t* out = dst + ZXC_PAD_SIZE; size_t rem = ml - ZXC_PAD_SIZE; while (rem > ZXC_PAD_SIZE) { vst1q_u8(out, v); vst1q_u8(out + 16, v); out += ZXC_PAD_SIZE; rem -= ZXC_PAD_SIZE; } vst1q_u8(out, v); vst1q_u8(out + 16, v); } #else ZXC_MEMSET(dst, byte, ml); #endif } /* ========================================================================== * Shared decode macros for the GLO and GHI decoders (fast + safe variants). * Defined at file scope to avoid four identical copies inside each function. * They reference the local names l_ptr, d_ptr, written that every call site * has in scope. #undef-ed at the end of the last consumer. * ========================================================================== */ /** * @brief Copies @p ll literal bytes from @p src to @p dst using 32-byte wild copies. * * Writes in @ref ZXC_PAD_SIZE-byte chunks and may **overshoot** by up to * @ref ZXC_PAD_SIZE - 1 bytes past @p ll; the caller must guarantee @p dst has at * least @ref ZXC_PAD_SIZE bytes of writable headroom (the unrolled loops and the * trailing-literal margins ensure this). Pointers are taken by value and the * caller advances its own cursors by @p ll, keeping them in registers on the hot * path. * * @param[out] dst Output cursor. Must not overlap @p src and must have * @ref ZXC_PAD_SIZE bytes of overshoot headroom. * @param[in] src Literal-stream source. Must not overlap @p dst (RESTRICT). * @param[in] ll Number of literal bytes to copy. */ static ZXC_ALWAYS_INLINE void zxc_decode_copy_literals(uint8_t* RESTRICT dst, const uint8_t* RESTRICT src, const uint64_t ll) { zxc_copy32(dst, src); if (UNLIKELY(ll > ZXC_PAD_SIZE)) { dst += ZXC_PAD_SIZE; src += ZXC_PAD_SIZE; size_t rem = ll - ZXC_PAD_SIZE; while (rem > ZXC_PAD_SIZE) { zxc_copy32(dst, src); dst += ZXC_PAD_SIZE; src += ZXC_PAD_SIZE; rem -= ZXC_PAD_SIZE; } zxc_copy32(dst, src); } } /** * @brief Copies an @p ml-byte LZ match from @c d_ptr-off to @p d_ptr, handling overlap. * * The source @c d_ptr-off may overlap the destination (the LZ repeat case), so the * copy strategy is chosen by back-reference distance: * - @p off >= @ref ZXC_PAD_SIZE : 32-byte wild copies (no overlap within a chunk); * - @p off >= @ref ZXC_PAD_SIZE / 2 : 16-byte wild copies; * - @p off == 1 : single-byte run via @ref zxc_decode_fill_run; * - otherwise (2..15) : pattern-replicating overlap copy. * * Like @ref zxc_decode_copy_literals it may **overshoot** up to @ref ZXC_PAD_SIZE - 1 * bytes past @p ml, so @p d_ptr must have @ref ZXC_PAD_SIZE bytes of headroom. @p d_ptr * is taken by value; the caller advances its cursor by @p ml. * * @param[in,out] d_ptr Output cursor; the match source is @c d_ptr-off. Must have * @ref ZXC_PAD_SIZE bytes of overshoot headroom. * @param[in] off Resolved (bias-removed) back-reference distance, @c >= 1. * @param[in] ml Match length in bytes (@c >= ZXC_LZ_MIN_MATCH_LEN). */ static ZXC_ALWAYS_INLINE void zxc_decode_copy_match(uint8_t* RESTRICT d_ptr, const uint32_t off, const uint64_t ml) { const uint8_t* match_src = d_ptr - off; if (LIKELY(off >= ZXC_PAD_SIZE)) { zxc_copy32(d_ptr, match_src); if (UNLIKELY(ml > ZXC_PAD_SIZE)) { uint8_t* out = d_ptr + ZXC_PAD_SIZE; const uint8_t* ref = match_src + ZXC_PAD_SIZE; size_t rem = ml - ZXC_PAD_SIZE; while (rem > ZXC_PAD_SIZE) { zxc_copy32(out, ref); out += ZXC_PAD_SIZE; ref += ZXC_PAD_SIZE; rem -= ZXC_PAD_SIZE; } zxc_copy32(out, ref); } } else if (off >= (ZXC_PAD_SIZE / 2)) { zxc_copy16(d_ptr, match_src); if (UNLIKELY(ml > (ZXC_PAD_SIZE / 2))) { uint8_t* out = d_ptr + (ZXC_PAD_SIZE / 2); const uint8_t* ref = match_src + (ZXC_PAD_SIZE / 2); size_t rem = ml - (ZXC_PAD_SIZE / 2); while (rem > (ZXC_PAD_SIZE / 2)) { zxc_copy16(out, ref); out += (ZXC_PAD_SIZE / 2); ref += (ZXC_PAD_SIZE / 2); rem -= (ZXC_PAD_SIZE / 2); } zxc_copy16(out, ref); } } else if (off == 1) { zxc_decode_fill_run(d_ptr, match_src[0], ml); } else { zxc_decode_copy_overlap_run(d_ptr, off, ml); } } /** * @brief Exact-size match copy for the tail loops (no overshoot headroom). * * The tail/remaining-sequence loops validate against @c d_end exactly, so the * wild-copy ladder above (which overshoots up to @ref ZXC_PAD_SIZE) is off * limits there. For overlapping matches this expands the run by doubling: * every ZXC_MEMCPY source range ends at or before its destination start, and * nothing is written past @c d_ptr+ml. Since each copied prefix length is a * multiple of @p off, the result is byte-identical to the naive per-byte * copy, in O(log(ml/off)) calls instead of O(ml) iterations. * * @param[in,out] d_ptr Output cursor (match source is @c d_ptr-off). * @param[in] match_src Match source, equal to @c d_ptr-off. * @param[in] off Back-reference distance, @c >= 1. * @param[in] ml Match length in bytes. */ static ZXC_NOINLINE void zxc_decode_copy_match_exact(uint8_t* d_ptr, const uint8_t* match_src, const size_t off, const size_t ml) { if (off >= ml) { ZXC_MEMCPY(d_ptr, match_src, ml); } else if (ml < 16) { for (size_t i = 0; i < ml; i++) d_ptr[i] = match_src[i]; } else { size_t n = off; ZXC_MEMCPY(d_ptr, match_src, n); while (n <= ml - n) { ZXC_MEMCPY(d_ptr + n, d_ptr, n); n <<= 1; } ZXC_MEMCPY(d_ptr + n, d_ptr, ml - n); } } // SAFE version: validates offset against written bytes #define DECODE_SEQ_SAFE(ll, ml, off) \ do { \ zxc_decode_copy_literals(d_ptr, l_ptr, ll); \ l_ptr += ll; \ d_ptr += ll; \ written += ll; \ if (UNLIKELY(off > written)) return ZXC_ERROR_BAD_OFFSET; \ zxc_decode_copy_match(d_ptr, off, ml); \ d_ptr += ml; \ written += ml; \ } while (0) // FAST version: no offset validation (for use after written >= 256 or 65536) #define DECODE_SEQ_FAST(ll, ml, off) \ do { \ zxc_decode_copy_literals(d_ptr, l_ptr, ll); \ l_ptr += ll; \ d_ptr += ll; \ zxc_decode_copy_match(d_ptr, off, ml); \ d_ptr += ml; \ } while (0) /* The section decoders below are outlined COLD on purpose: they run once per * section (a call is noise), and keeping them out of the hot sequence-decode * loop protects its i-cache footprint - inlining them was measured at * ~3-5% slower on levels 3-5, which never take these branches. */ /** * @brief Ensures the entropy decode scratch (tok_buffer + pivco_scratch) is * available before decoding an entropy section. * * Heap contexts defer this scratch to the first entropy section * (@ref zxc_cctx_alloc_entropy_scratch allocates it once); static workspaces * pre-carve it, making this a no-op. The const cast is sound: every context * lives in writable memory (heap allocation or caller workspace) - the * decode chain is const only because steady-state decoding never mutates * the context. * * @param[in] ctx Decompression context (mutated on the first entropy block). * @return @ref ZXC_OK, or @ref ZXC_ERROR_MEMORY on allocation failure. */ static ZXC_NOINLINE ZXC_COLD int zxc_ensure_entropy_scratch(const zxc_cctx_t* RESTRICT ctx) { if (LIKELY(ctx->pivco_scratch != NULL)) return ZXC_OK; return zxc_cctx_alloc_entropy_scratch((zxc_cctx_t*)(uintptr_t)ctx); } static ZXC_NOINLINE ZXC_COLD int zxc_decode_lit_pivco(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT payload, const size_t psize, const size_t required_size) { const int arc = zxc_ensure_entropy_scratch(ctx); if (UNLIKELY(arc != ZXC_OK)) return arc; if (UNLIKELY(ctx->lit_buffer_cap < required_size + ZXC_PAD_SIZE || ctx->pivco_scratch_cap < required_size + ZXC_PIVCO_SCRATCH_PAD)) return ZXC_ERROR_CORRUPT_DATA; return zxc_huf_decode_section(payload, psize, ctx->lit_buffer, required_size, ctx->pivco_scratch); } static ZXC_NOINLINE ZXC_COLD int zxc_decode_lit_pivco_dict(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT payload, const size_t psize, const size_t required_size) { if (UNLIKELY(!ctx->dict_huf_tree_ok)) return ZXC_ERROR_DICT_REQUIRED; const int arc = zxc_ensure_entropy_scratch(ctx); if (UNLIKELY(arc != ZXC_OK)) return arc; if (UNLIKELY(ctx->lit_buffer_cap < required_size + ZXC_PAD_SIZE || ctx->pivco_scratch_cap < required_size + ZXC_PIVCO_SCRATCH_PAD)) return ZXC_ERROR_CORRUPT_DATA; return zxc_huf_decode_section_dict(payload, psize, ctx->lit_buffer, required_size, &ctx->dict_huf->tree, &ctx->dict_huf->dec, ctx->pivco_scratch); } static ZXC_NOINLINE ZXC_COLD int zxc_decode_tok_pivco(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT payload, const size_t psize, const size_t n_tok) { const int arc = zxc_ensure_entropy_scratch(ctx); if (UNLIKELY(arc != ZXC_OK)) return arc; if (UNLIKELY(n_tok + ZXC_PAD_SIZE > ctx->tok_buffer_cap || n_tok + ZXC_PIVCO_SCRATCH_PAD > ctx->pivco_scratch_cap)) return ZXC_ERROR_CORRUPT_DATA; return zxc_huf_decode_section(payload, psize, ctx->tok_buffer, n_tok, ctx->pivco_scratch); } static ZXC_NOINLINE int zxc_decode_block_glo_entropy(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, size_t src_size, uint8_t* RESTRICT dst, size_t dst_capacity); static ZXC_NOINLINE int zxc_decode_block_glo_entropy_dict(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, size_t src_size, uint8_t* RESTRICT dst, size_t dst_capacity); static ZXC_NOINLINE int zxc_decode_block_glo_entropy_safe(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, size_t src_size, uint8_t* RESTRICT dst, size_t dst_capacity); /** * @brief Unified GLO (General Low) block decoder body, shared by the fast, * safe, dictionary and entropy-token variants. * * Decodes a block in the internal GLO format; the decompressed size is derived * from the Section Descriptors in the payload. @p safe, @p has_dict and * @p tok_entropy must be compile-time constants (0 or 1): the 4x-unrolled * loops are duplicated inside @c if(safe)/else branches so each variant keeps * single-assignment @c const save pointers, and after constant propagation * only one branch survives per wrapper (codegen equivalent to a hand-written * pair). * * The @p tok_entropy dimension keeps the token pointer on a SINGLE provenance * per instantiation (in-place @c src tokens vs @c ctx->tok_buffer): the * tok_entropy=0 instantiations tail-call their entropy twin right after the * header parse when they meet enc_litlen == 2. * * @param[in,out] ctx Decompression context (dict buffer, scratch). * @param[in] src Compressed block payload. * @param[in] src_size Size of @p src in bytes. * @param[out] dst Destination buffer for decoded bytes. * @param[in] dst_capacity Capacity of @p dst in bytes. * @param[in] safe Compile-time flag: 1 = strict bounds-checked loop. * @param[in] has_dict Compile-time flag: 1 = resolve matches against a dict prefix. * @param[in] tok_entropy Compile-time flag: 1 = tokens are a PivCo section * decoded into @c ctx->tok_buffer (level 7). * @return Bytes written to @p dst on success, or a negative @ref zxc_error_t. */ static ZXC_ALWAYS_INLINE int zxc_decode_block_glo_impl(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_size, uint8_t* RESTRICT dst, const size_t dst_capacity, const int safe, const int has_dict, const int tok_entropy) { zxc_gnr_header_t gh; /* Constant 0 when !has_dict, so `written` starts at 0 and `dst - dict_size` * folds to `dst` -- pre-dict codegen on the hot path. */ const size_t dict_size = has_dict ? ctx->dict_size : 0; zxc_section_desc_t desc[ZXC_GLO_SECTIONS]; if (UNLIKELY(zxc_read_glo_header_and_desc(src, src_size, &gh, desc) != ZXC_OK)) return ZXC_ERROR_BAD_HEADER; /* Entropy-coded tokens (level 7): tail-call the dedicated instantiation * BEFORE any section work (it restarts from the header, so only the * header parse above is duplicated). Constant flags per instantiation: * exactly one call survives; the RAW-token fast path below keeps t_ptr on * a single provenance (a joined pointer cost ~4% at levels 3-5). */ if (!tok_entropy && UNLIKELY(gh.enc_litlen == ZXC_SECTION_ENCODING_HUFFMAN)) { if (safe) return zxc_decode_block_glo_entropy_safe(ctx, src, src_size, dst, dst_capacity); if (has_dict) return zxc_decode_block_glo_entropy_dict(ctx, src, src_size, dst, dst_capacity); return zxc_decode_block_glo_entropy(ctx, src, src_size, dst, dst_capacity); } const uint8_t* p_data = src + ZXC_GLO_HEADER_BINARY_SIZE + ZXC_GLO_SECTIONS * ZXC_SECTION_DESC_BINARY_SIZE; const uint8_t* p_curr = p_data; // --- Literal Stream Setup --- const uint8_t* l_ptr; const uint8_t* l_end; uint8_t* rle_buf = NULL; size_t lit_stream_size = (size_t)(desc[0].sizes & ZXC_SECTION_SIZE_MASK); if (gh.enc_lit == ZXC_SECTION_ENCODING_HUFFMAN) { /* Wire value 2: PivCo layout. */ const size_t required_size = (size_t)(desc[0].sizes >> 32); if (UNLIKELY(lit_stream_size > (size_t)(src + src_size - p_curr))) return ZXC_ERROR_CORRUPT_DATA; if (required_size == 0) { l_ptr = p_curr; l_end = p_curr; } else { if (UNLIKELY(required_size > dst_capacity || required_size > SIZE_MAX - ZXC_PAD_SIZE)) return ZXC_ERROR_DST_TOO_SMALL; const int rc = zxc_decode_lit_pivco(ctx, p_curr, lit_stream_size, required_size); if (UNLIKELY(rc != ZXC_OK)) return rc; l_ptr = ctx->lit_buffer; l_end = ctx->lit_buffer + required_size; } } else if (gh.enc_lit == ZXC_SECTION_ENCODING_HUFFMAN_DICT) { /* Shared dictionary table: no inline lengths header; the prebuilt * decode table was attached to the context with the dictionary. */ const size_t required_size = (size_t)(desc[0].sizes >> 32); if (UNLIKELY(lit_stream_size > (size_t)(src + src_size - p_curr))) return ZXC_ERROR_CORRUPT_DATA; if (required_size == 0) { l_ptr = p_curr; l_end = p_curr; } else { if (UNLIKELY(required_size > dst_capacity || required_size > SIZE_MAX - ZXC_PAD_SIZE)) return ZXC_ERROR_DST_TOO_SMALL; const int rc = zxc_decode_lit_pivco_dict(ctx, p_curr, lit_stream_size, required_size); if (UNLIKELY(rc != ZXC_OK)) return rc; l_ptr = ctx->lit_buffer; l_end = ctx->lit_buffer + required_size; } } else if (gh.enc_lit == ZXC_SECTION_ENCODING_RLE) { const size_t required_size = (size_t)(desc[0].sizes >> 32); if (required_size > 0) { if (UNLIKELY(required_size > dst_capacity || required_size > SIZE_MAX - ZXC_PAD_SIZE)) return ZXC_ERROR_DST_TOO_SMALL; const size_t alloc_size = required_size + ZXC_PAD_SIZE; /* lit_buffer is pre-allocated to chunk_size + ZXC_PAD_SIZE by * zxc_cctx_init (mode == 0).*/ if (UNLIKELY(ctx->lit_buffer_cap < alloc_size)) return ZXC_ERROR_CORRUPT_DATA; rle_buf = ctx->lit_buffer; if (UNLIKELY(!rle_buf || lit_stream_size > (size_t)(src + src_size - p_curr))) return ZXC_ERROR_CORRUPT_DATA; const uint8_t* r_ptr = p_curr; const uint8_t* r_end = r_ptr + lit_stream_size; uint8_t* w_ptr = rle_buf; const uint8_t* const w_end = rle_buf + required_size; while (r_ptr < r_end && w_ptr < w_end) { uint8_t token = *r_ptr++; if (LIKELY(!(token & ZXC_LIT_RLE_FLAG))) { // Raw copy (most common path): use ZXC_PAD_SIZE-byte wild copies // token is 7-bit (0-127), so len is 1-128 bytes const uint32_t len = (uint32_t)token + 1; if (UNLIKELY(w_ptr + len > w_end || r_ptr + len > r_end)) return ZXC_ERROR_CORRUPT_DATA; // Destination has ZXC_PAD_SIZE bytes of safe overrun space. // Source may not - check before wild copy. // Fast path: source has ZXC_PAD_SIZE-byte read headroom (most common) if (LIKELY(r_ptr + ZXC_PAD_SIZE <= r_end)) { // Single 32-byte copy covers len <= ZXC_PAD_SIZE (most tokens) zxc_copy32(w_ptr, r_ptr); if (UNLIKELY(len > ZXC_PAD_SIZE)) { // Unroll: max len=128, so max 4 copies total // Use unconditional stores with overlap - faster than branches if (len <= 2 * ZXC_PAD_SIZE) { zxc_copy32(w_ptr + len - ZXC_PAD_SIZE, r_ptr + len - ZXC_PAD_SIZE); } else if (len <= 3 * ZXC_PAD_SIZE) { zxc_copy32(w_ptr + ZXC_PAD_SIZE, r_ptr + ZXC_PAD_SIZE); zxc_copy32(w_ptr + len - ZXC_PAD_SIZE, r_ptr + len - ZXC_PAD_SIZE); } else { zxc_copy32(w_ptr + ZXC_PAD_SIZE, r_ptr + ZXC_PAD_SIZE); zxc_copy32(w_ptr + 2 * ZXC_PAD_SIZE, r_ptr + 2 * ZXC_PAD_SIZE); zxc_copy32(w_ptr + len - ZXC_PAD_SIZE, r_ptr + len - ZXC_PAD_SIZE); } } } else { // Near end of source: safe copy (rare cold path) ZXC_MEMCPY(w_ptr, r_ptr, len); } w_ptr += len; r_ptr += len; } else { // RLE run: fill with single byte const uint32_t len = (token & ZXC_LIT_LEN_MASK) + 4; if (UNLIKELY(w_ptr + len > w_end || r_ptr >= r_end)) return ZXC_ERROR_CORRUPT_DATA; ZXC_MEMSET(w_ptr, *r_ptr++, len); w_ptr += len; } } if (UNLIKELY(w_ptr != w_end)) return ZXC_ERROR_CORRUPT_DATA; l_ptr = rle_buf; l_end = rle_buf + required_size; } else { l_ptr = p_curr; l_end = p_curr; } } else if (gh.enc_lit == ZXC_SECTION_ENCODING_RAW) { l_ptr = p_curr; l_end = p_curr + lit_stream_size; } else { return ZXC_ERROR_CORRUPT_DATA; } p_curr += lit_stream_size; // --- Stream Pointers & Validation --- const size_t sz_tokens = (size_t)(desc[1].sizes & ZXC_SECTION_SIZE_MASK); const size_t sz_offsets = (size_t)(desc[2].sizes & ZXC_SECTION_SIZE_MASK); const size_t sz_extras = (size_t)(desc[3].sizes & ZXC_SECTION_SIZE_MASK); // Validate stream sizes match sequence count (early rejection of malformed data) const uint64_t expected_off_size = (gh.enc_off == 1) ? (uint64_t)gh.n_sequences : (uint64_t)gh.n_sequences * 2; /* Offsets/extras follow the on-disk token SECTION; sz_tokens is its size * (== n_sequences when RAW, the Huffman payload size when enc_litlen set). */ const uint8_t* o_ptr = p_curr + sz_tokens; const uint8_t* e_ptr = o_ptr + sz_offsets; const uint8_t* const e_end = e_ptr + sz_extras; // For vbyte overflow detection // Validate streams don't overflow source buffer + match sequence count. if (UNLIKELY((e_end != src + src_size) || (uint64_t)sz_offsets < expected_off_size)) return ZXC_ERROR_CORRUPT_DATA; const uint8_t* RESTRICT t_ptr; if (!tok_entropy) { /* enc_litlen == 2 was re-routed right after the header parse. */ if (UNLIKELY(sz_tokens < gh.n_sequences)) return ZXC_ERROR_CORRUPT_DATA; t_ptr = p_curr; } else { if (UNLIKELY(gh.enc_litlen != ZXC_SECTION_ENCODING_HUFFMAN)) return ZXC_ERROR_CORRUPT_DATA; const int rc = zxc_decode_tok_pivco(ctx, p_curr, sz_tokens, gh.n_sequences); if (UNLIKELY(rc != ZXC_OK)) return rc; t_ptr = ctx->tok_buffer; } uint8_t* d_ptr = dst; const uint8_t* const d_end = dst + dst_capacity; // Destination safe margin for 4x loop: max output without varint extension. // ll_max = 14, ml_max = 14 + 5 = 19, per-seq = 33, 4x = 132. // Add ZXC_PAD_SIZE (32) for the wild zxc_copy32 overshoot + 4 safety = 168. const uint8_t* const d_end_safe = d_end - (132 + ZXC_PAD_SIZE + 4); // Literal stream safe threshold for 4x-unrolled loops. // Without varint extension, max ll per sequence = ZXC_TOKEN_LL_MASK - 1 = 14. // For 4 sequences: 4 * 14 = 56. With this margin, l_ptr checks are only needed // on the cold varint path, keeping the hot path free of l_ptr overhead. const size_t glo_sz_lit = (size_t)(l_end - l_ptr); const size_t glo_margin_4x = 4 * (ZXC_TOKEN_LL_MASK - 1); // 56 const size_t glo_margin_1x = ZXC_TOKEN_LL_MASK - 1; // 14 const uint8_t* const l_end_safe_4x = (glo_sz_lit > glo_margin_4x) ? l_end - glo_margin_4x : l_ptr; const uint8_t* const l_end_safe_1x = (glo_sz_lit > glo_margin_1x) ? l_end - glo_margin_1x : l_ptr; uint32_t n_seq = gh.n_sequences; // Track bytes written for offset validation // For 1-byte offsets (enc_off==1): validate until 256 bytes written (max 8-bit offset) // For 2-byte offsets (enc_off==0): validate until 65536 bytes written (max 16-bit offset) // After threshold, all offsets are guaranteed valid (can't exceed written bytes) // When a dictionary is active, dict_size bytes are logically "already written" // (prepended by the caller), so the SAFE loop may be skipped entirely. size_t written = dict_size; // --- SAFE Loop: offset validation until threshold (4x unroll) --- // For 1-byte offsets: bounds check until 256 bytes written // For 2-byte offsets: bounds check until 65536 bytes written const size_t bounds_threshold = (gh.enc_off == 1) ? (1U << 8) : (1U << 16); if (safe) { /* SAFE variant: save per-batch state so overflow can rollback. */ while (n_seq >= 4 && d_ptr < d_end_safe && l_ptr < l_end_safe_4x && written < bounds_threshold) { const uint8_t* const t_save = t_ptr; const uint8_t* const o_save = o_ptr; const uint8_t* const e_save = e_ptr; uint8_t* const d_save = d_ptr; const uint8_t* const l_save = l_ptr; const size_t w_save = written; uint32_t tokens = zxc_le32(t_ptr); t_ptr += sizeof(uint32_t); uint32_t off1 = ZXC_LZ_OFFSET_BIAS, off2 = ZXC_LZ_OFFSET_BIAS, off3 = ZXC_LZ_OFFSET_BIAS, off4 = ZXC_LZ_OFFSET_BIAS; if (gh.enc_off == 1) { uint32_t offsets = zxc_le32(o_ptr); o_ptr += sizeof(uint32_t); off1 += offsets & 0xFF; off2 += (offsets >> 8) & 0xFF; off3 += (offsets >> 16) & 0xFF; off4 += (offsets >> 24) & 0xFF; } else { uint64_t offsets = zxc_le64(o_ptr); o_ptr += sizeof(uint64_t); off1 += (uint32_t)(offsets & 0xFFFF); off2 += (uint32_t)((offsets >> 16) & 0xFFFF); off3 += (uint32_t)((offsets >> 32) & 0xFFFF); off4 += (uint32_t)((offsets >> 48) & 0xFFFF); } uint64_t ll1 = (tokens & 0x0F0) >> 4; uint64_t ml1 = (tokens & 0x00F); if (UNLIKELY(ll1 == ZXC_TOKEN_LL_MASK)) { ll1 += zxc_read_varint(&e_ptr, e_end); const uint64_t reserve = ((tokens >> 12) & 0xF) + ((tokens >> 20) & 0xF) + (tokens >> 28); if (UNLIKELY(ll1 + reserve > (size_t)(l_end - l_ptr) || ll1 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_safe_4x; } if (UNLIKELY(ml1 == ZXC_TOKEN_ML_MASK)) { ml1 += zxc_read_varint(&e_ptr, e_end); if (UNLIKELY(ll1 + ml1 + ZXC_LZ_MIN_MATCH_LEN + 3U * ZXC_GLO_MAX_INLINE_OUT_PER_SEQ + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_safe_4x; } ml1 += ZXC_LZ_MIN_MATCH_LEN; DECODE_SEQ_SAFE(ll1, ml1, off1); uint64_t ll2 = (tokens & 0x0F000) >> 12; uint64_t ml2 = (tokens & 0x00F00) >> 8; if (UNLIKELY(ll2 == ZXC_TOKEN_LL_MASK)) { ll2 += zxc_read_varint(&e_ptr, e_end); const uint64_t reserve = ((tokens >> 20) & 0xF) + (tokens >> 28); if (UNLIKELY(ll2 + reserve > (size_t)(l_end - l_ptr) || ll2 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_safe_4x; } if (UNLIKELY(ml2 == ZXC_TOKEN_ML_MASK)) { ml2 += zxc_read_varint(&e_ptr, e_end); if (UNLIKELY(ll2 + ml2 + ZXC_LZ_MIN_MATCH_LEN + 2U * ZXC_GLO_MAX_INLINE_OUT_PER_SEQ + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_safe_4x; } ml2 += ZXC_LZ_MIN_MATCH_LEN; DECODE_SEQ_SAFE(ll2, ml2, off2); uint64_t ll3 = (tokens & 0x0F00000) >> 20; uint64_t ml3 = (tokens & 0x00F0000) >> 16; if (UNLIKELY(ll3 == ZXC_TOKEN_LL_MASK)) { ll3 += zxc_read_varint(&e_ptr, e_end); const uint64_t reserve = (tokens >> 28); if (UNLIKELY(ll3 + reserve > (size_t)(l_end - l_ptr) || ll3 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_safe_4x; } if (UNLIKELY(ml3 == ZXC_TOKEN_ML_MASK)) { ml3 += zxc_read_varint(&e_ptr, e_end); if (UNLIKELY(ll3 + ml3 + ZXC_LZ_MIN_MATCH_LEN + ZXC_GLO_MAX_INLINE_OUT_PER_SEQ + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_safe_4x; } ml3 += ZXC_LZ_MIN_MATCH_LEN; DECODE_SEQ_SAFE(ll3, ml3, off3); uint64_t ll4 = (tokens >> 28); uint64_t ml4 = (tokens >> 24) & 0x0F; if (UNLIKELY(ll4 == ZXC_TOKEN_LL_MASK)) { ll4 += zxc_read_varint(&e_ptr, e_end); if (UNLIKELY(ll4 > (size_t)(l_end - l_ptr) || ll4 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_safe_4x; } if (UNLIKELY(ml4 == ZXC_TOKEN_ML_MASK)) { ml4 += zxc_read_varint(&e_ptr, e_end); if (UNLIKELY(ll4 + ml4 + ZXC_LZ_MIN_MATCH_LEN + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_safe_4x; } ml4 += ZXC_LZ_MIN_MATCH_LEN; DECODE_SEQ_SAFE(ll4, ml4, off4); n_seq -= 4; continue; rollback_safe_4x: t_ptr = t_save; o_ptr = o_save; e_ptr = e_save; d_ptr = d_save; l_ptr = l_save; written = w_save; break; } } else { while (n_seq >= 4 && d_ptr < d_end_safe && l_ptr < l_end_safe_4x && written < bounds_threshold) { uint32_t tokens = zxc_le32(t_ptr); t_ptr += sizeof(uint32_t); uint32_t off1 = ZXC_LZ_OFFSET_BIAS; uint32_t off2 = ZXC_LZ_OFFSET_BIAS; uint32_t off3 = ZXC_LZ_OFFSET_BIAS; uint32_t off4 = ZXC_LZ_OFFSET_BIAS; if (gh.enc_off == 1) { // Read 4 x 1-byte offsets uint32_t offsets = zxc_le32(o_ptr); o_ptr += sizeof(uint32_t); off1 += offsets & 0xFF; off2 += (offsets >> 8) & 0xFF; off3 += (offsets >> 16) & 0xFF; off4 += (offsets >> 24) & 0xFF; } else { // Read 4 x 2-byte offsets uint64_t offsets = zxc_le64(o_ptr); o_ptr += sizeof(uint64_t); off1 += (uint32_t)(offsets & 0xFFFF); off2 += (uint32_t)((offsets >> 16) & 0xFFFF); off3 += (uint32_t)((offsets >> 32) & 0xFFFF); off4 += (uint32_t)((offsets >> 48) & 0xFFFF); } uint64_t ll1 = (tokens & 0x0F0) >> 4; uint64_t ml1 = (tokens & 0x00F); if (UNLIKELY(ll1 == ZXC_TOKEN_LL_MASK)) { ll1 += zxc_read_varint(&e_ptr, e_end); const uint64_t reserve = ((tokens >> 12) & 0xF) + ((tokens >> 20) & 0xF) + (tokens >> 28); if (UNLIKELY(ll1 + reserve > (size_t)(l_end - l_ptr) || ll1 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } if (UNLIKELY(ml1 == ZXC_TOKEN_ML_MASK)) { ml1 += zxc_read_varint(&e_ptr, e_end); if (UNLIKELY(ll1 + ml1 + ZXC_LZ_MIN_MATCH_LEN + 3U * ZXC_GLO_MAX_INLINE_OUT_PER_SEQ + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } ml1 += ZXC_LZ_MIN_MATCH_LEN; DECODE_SEQ_SAFE(ll1, ml1, off1); uint64_t ll2 = (tokens & 0x0F000) >> 12; uint64_t ml2 = (tokens & 0x00F00) >> 8; if (UNLIKELY(ll2 == ZXC_TOKEN_LL_MASK)) { ll2 += zxc_read_varint(&e_ptr, e_end); const uint64_t reserve = ((tokens >> 20) & 0xF) + (tokens >> 28); if (UNLIKELY(ll2 + reserve > (size_t)(l_end - l_ptr) || ll2 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } if (UNLIKELY(ml2 == ZXC_TOKEN_ML_MASK)) { ml2 += zxc_read_varint(&e_ptr, e_end); if (UNLIKELY(ll2 + ml2 + ZXC_LZ_MIN_MATCH_LEN + 2U * ZXC_GLO_MAX_INLINE_OUT_PER_SEQ + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } ml2 += ZXC_LZ_MIN_MATCH_LEN; DECODE_SEQ_SAFE(ll2, ml2, off2); uint64_t ll3 = (tokens & 0x0F00000) >> 20; uint64_t ml3 = (tokens & 0x00F0000) >> 16; if (UNLIKELY(ll3 == ZXC_TOKEN_LL_MASK)) { ll3 += zxc_read_varint(&e_ptr, e_end); const uint64_t reserve = (tokens >> 28); if (UNLIKELY(ll3 + reserve > (size_t)(l_end - l_ptr) || ll3 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } if (UNLIKELY(ml3 == ZXC_TOKEN_ML_MASK)) { ml3 += zxc_read_varint(&e_ptr, e_end); if (UNLIKELY(ll3 + ml3 + ZXC_LZ_MIN_MATCH_LEN + ZXC_GLO_MAX_INLINE_OUT_PER_SEQ + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } ml3 += ZXC_LZ_MIN_MATCH_LEN; DECODE_SEQ_SAFE(ll3, ml3, off3); uint64_t ll4 = (tokens >> 28); uint64_t ml4 = (tokens >> 24) & 0x0F; if (UNLIKELY(ll4 == ZXC_TOKEN_LL_MASK)) { ll4 += zxc_read_varint(&e_ptr, e_end); if (UNLIKELY(ll4 > (size_t)(l_end - l_ptr) || ll4 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } if (UNLIKELY(ml4 == ZXC_TOKEN_ML_MASK)) { ml4 += zxc_read_varint(&e_ptr, e_end); if (UNLIKELY(ll4 + ml4 + ZXC_LZ_MIN_MATCH_LEN + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } ml4 += ZXC_LZ_MIN_MATCH_LEN; DECODE_SEQ_SAFE(ll4, ml4, off4); n_seq -= 4; } } // --- FAST Loop: After threshold, no offset validation needed (4x unroll) --- if (safe) { while (n_seq >= 4 && d_ptr < d_end_safe && l_ptr < l_end_safe_4x) { const uint8_t* const t_save = t_ptr; const uint8_t* const o_save = o_ptr; const uint8_t* const e_save = e_ptr; uint8_t* const d_save = d_ptr; const uint8_t* const l_save = l_ptr; uint32_t tokens = zxc_le32(t_ptr); t_ptr += sizeof(uint32_t); uint32_t off1 = ZXC_LZ_OFFSET_BIAS; uint32_t off2 = ZXC_LZ_OFFSET_BIAS; uint32_t off3 = ZXC_LZ_OFFSET_BIAS; uint32_t off4 = ZXC_LZ_OFFSET_BIAS; if (gh.enc_off == 1) { uint32_t offsets = zxc_le32(o_ptr); o_ptr += sizeof(uint32_t); off1 += offsets & 0xFF; off2 += (offsets >> 8) & 0xFF; off3 += (offsets >> 16) & 0xFF; off4 += (offsets >> 24) & 0xFF; } else { uint64_t offsets = zxc_le64(o_ptr); o_ptr += sizeof(uint64_t); off1 += (uint32_t)(offsets & 0xFFFF); off2 += (uint32_t)((offsets >> 16) & 0xFFFF); off3 += (uint32_t)((offsets >> 32) & 0xFFFF); off4 += (uint32_t)((offsets >> 48) & 0xFFFF); } uint64_t ll1 = (tokens & 0x0F0) >> 4; uint64_t ml1 = (tokens & 0x00F); if (UNLIKELY(ll1 == ZXC_TOKEN_LL_MASK)) { ll1 += zxc_read_varint(&e_ptr, e_end); const uint64_t reserve = ((tokens >> 12) & 0xF) + ((tokens >> 20) & 0xF) + (tokens >> 28); if (UNLIKELY(ll1 + reserve > (size_t)(l_end - l_ptr) || ll1 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_fast_4x; } if (UNLIKELY(ml1 == ZXC_TOKEN_ML_MASK)) { ml1 += zxc_read_varint(&e_ptr, e_end); if (UNLIKELY(ll1 + ml1 + ZXC_LZ_MIN_MATCH_LEN + 3U * ZXC_GLO_MAX_INLINE_OUT_PER_SEQ + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_fast_4x; } ml1 += ZXC_LZ_MIN_MATCH_LEN; DECODE_SEQ_FAST(ll1, ml1, off1); uint64_t ll2 = (tokens & 0x0F000) >> 12; uint64_t ml2 = (tokens & 0x00F00) >> 8; if (UNLIKELY(ll2 == ZXC_TOKEN_LL_MASK)) { ll2 += zxc_read_varint(&e_ptr, e_end); const uint64_t reserve = ((tokens >> 20) & 0xF) + (tokens >> 28); if (UNLIKELY(ll2 + reserve > (size_t)(l_end - l_ptr) || ll2 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_fast_4x; } if (UNLIKELY(ml2 == ZXC_TOKEN_ML_MASK)) { ml2 += zxc_read_varint(&e_ptr, e_end); if (UNLIKELY(ll2 + ml2 + ZXC_LZ_MIN_MATCH_LEN + 2U * ZXC_GLO_MAX_INLINE_OUT_PER_SEQ + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_fast_4x; } ml2 += ZXC_LZ_MIN_MATCH_LEN; DECODE_SEQ_FAST(ll2, ml2, off2); uint64_t ll3 = (tokens & 0x0F00000) >> 20; uint64_t ml3 = (tokens & 0x00F0000) >> 16; if (UNLIKELY(ll3 == ZXC_TOKEN_LL_MASK)) { ll3 += zxc_read_varint(&e_ptr, e_end); const uint64_t reserve = (tokens >> 28); if (UNLIKELY(ll3 + reserve > (size_t)(l_end - l_ptr) || ll3 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_fast_4x; } if (UNLIKELY(ml3 == ZXC_TOKEN_ML_MASK)) { ml3 += zxc_read_varint(&e_ptr, e_end); if (UNLIKELY(ll3 + ml3 + ZXC_LZ_MIN_MATCH_LEN + ZXC_GLO_MAX_INLINE_OUT_PER_SEQ + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_fast_4x; } ml3 += ZXC_LZ_MIN_MATCH_LEN; DECODE_SEQ_FAST(ll3, ml3, off3); uint64_t ll4 = (tokens >> 28); uint64_t ml4 = (tokens >> 24) & 0x0F; if (UNLIKELY(ll4 == ZXC_TOKEN_LL_MASK)) { ll4 += zxc_read_varint(&e_ptr, e_end); if (UNLIKELY(ll4 > (size_t)(l_end - l_ptr) || ll4 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_fast_4x; } if (UNLIKELY(ml4 == ZXC_TOKEN_ML_MASK)) { ml4 += zxc_read_varint(&e_ptr, e_end); if (UNLIKELY(ll4 + ml4 + ZXC_LZ_MIN_MATCH_LEN + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_fast_4x; } ml4 += ZXC_LZ_MIN_MATCH_LEN; DECODE_SEQ_FAST(ll4, ml4, off4); n_seq -= 4; continue; rollback_fast_4x: t_ptr = t_save; o_ptr = o_save; e_ptr = e_save; d_ptr = d_save; l_ptr = l_save; break; } } else { while (n_seq >= 4 && d_ptr < d_end_safe && l_ptr < l_end_safe_4x) { uint32_t tokens = zxc_le32(t_ptr); t_ptr += sizeof(uint32_t); uint32_t off1 = ZXC_LZ_OFFSET_BIAS; uint32_t off2 = ZXC_LZ_OFFSET_BIAS; uint32_t off3 = ZXC_LZ_OFFSET_BIAS; uint32_t off4 = ZXC_LZ_OFFSET_BIAS; if (gh.enc_off == 1) { // Read 4 x 1-byte offsets uint32_t offsets = zxc_le32(o_ptr); o_ptr += sizeof(uint32_t); off1 += offsets & 0xFF; off2 += (offsets >> 8) & 0xFF; off3 += (offsets >> 16) & 0xFF; off4 += (offsets >> 24) & 0xFF; } else { // Read 4 x 2-byte offsets uint64_t offsets = zxc_le64(o_ptr); o_ptr += sizeof(uint64_t); off1 += (uint32_t)(offsets & 0xFFFF); off2 += (uint32_t)((offsets >> 16) & 0xFFFF); off3 += (uint32_t)((offsets >> 32) & 0xFFFF); off4 += (uint32_t)((offsets >> 48) & 0xFFFF); } uint64_t ll1 = (tokens & 0x0F0) >> 4; uint64_t ml1 = (tokens & 0x00F); if (UNLIKELY(ll1 == ZXC_TOKEN_LL_MASK)) { ll1 += zxc_read_varint(&e_ptr, e_end); const uint64_t reserve = ((tokens >> 12) & 0xF) + ((tokens >> 20) & 0xF) + (tokens >> 28); if (UNLIKELY(ll1 + reserve > (size_t)(l_end - l_ptr) || ll1 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } if (UNLIKELY(ml1 == ZXC_TOKEN_ML_MASK)) { ml1 += zxc_read_varint(&e_ptr, e_end); if (UNLIKELY(ll1 + ml1 + ZXC_LZ_MIN_MATCH_LEN + 3U * ZXC_GLO_MAX_INLINE_OUT_PER_SEQ + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } ml1 += ZXC_LZ_MIN_MATCH_LEN; DECODE_SEQ_FAST(ll1, ml1, off1); uint64_t ll2 = (tokens & 0x0F000) >> 12; uint64_t ml2 = (tokens & 0x00F00) >> 8; if (UNLIKELY(ll2 == ZXC_TOKEN_LL_MASK)) { ll2 += zxc_read_varint(&e_ptr, e_end); const uint64_t reserve = ((tokens >> 20) & 0xF) + (tokens >> 28); if (UNLIKELY(ll2 + reserve > (size_t)(l_end - l_ptr) || ll2 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } if (UNLIKELY(ml2 == ZXC_TOKEN_ML_MASK)) { ml2 += zxc_read_varint(&e_ptr, e_end); if (UNLIKELY(ll2 + ml2 + ZXC_LZ_MIN_MATCH_LEN + 2U * ZXC_GLO_MAX_INLINE_OUT_PER_SEQ + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } ml2 += ZXC_LZ_MIN_MATCH_LEN; DECODE_SEQ_FAST(ll2, ml2, off2); uint64_t ll3 = (tokens & 0x0F00000) >> 20; uint64_t ml3 = (tokens & 0x00F0000) >> 16; if (UNLIKELY(ll3 == ZXC_TOKEN_LL_MASK)) { ll3 += zxc_read_varint(&e_ptr, e_end); const uint64_t reserve = (tokens >> 28); if (UNLIKELY(ll3 + reserve > (size_t)(l_end - l_ptr) || ll3 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } if (UNLIKELY(ml3 == ZXC_TOKEN_ML_MASK)) { ml3 += zxc_read_varint(&e_ptr, e_end); if (UNLIKELY(ll3 + ml3 + ZXC_LZ_MIN_MATCH_LEN + ZXC_GLO_MAX_INLINE_OUT_PER_SEQ + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } ml3 += ZXC_LZ_MIN_MATCH_LEN; DECODE_SEQ_FAST(ll3, ml3, off3); uint64_t ll4 = (tokens >> 28); uint64_t ml4 = (tokens >> 24) & 0x0F; if (UNLIKELY(ll4 == ZXC_TOKEN_LL_MASK)) { ll4 += zxc_read_varint(&e_ptr, e_end); if (UNLIKELY(ll4 > (size_t)(l_end - l_ptr) || ll4 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } if (UNLIKELY(ml4 == ZXC_TOKEN_ML_MASK)) { ml4 += zxc_read_varint(&e_ptr, e_end); if (UNLIKELY(ll4 + ml4 + ZXC_LZ_MIN_MATCH_LEN + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } ml4 += ZXC_LZ_MIN_MATCH_LEN; DECODE_SEQ_FAST(ll4, ml4, off4); n_seq -= 4; } } // Validate vbyte reads didn't overflow if (UNLIKELY(e_ptr > e_end)) return ZXC_ERROR_CORRUPT_DATA; // --- Remaining 1 sequence (Fast Path) --- while (n_seq > 0 && d_ptr < d_end_safe && l_ptr < l_end_safe_1x) { // Save pointers before reading (in case we need to fall back to Safe Path) const uint8_t* t_save = t_ptr; const uint8_t* o_save = o_ptr; const uint8_t* e_save = e_ptr; uint8_t token = *t_ptr++; uint64_t ll = token >> ZXC_TOKEN_LIT_BITS; uint64_t ml = token & ZXC_TOKEN_ML_MASK; uint32_t offset = ZXC_LZ_OFFSET_BIAS; if (gh.enc_off == 1) { offset += *o_ptr++; // 1-byte offset (biased) } else { offset += zxc_le16(o_ptr); // 2-byte offset (biased) o_ptr += sizeof(uint16_t); } if (UNLIKELY(ll == ZXC_TOKEN_LL_MASK)) { ll += zxc_read_varint(&e_ptr, e_end); if (UNLIKELY(l_ptr + ll > l_end)) { t_ptr = t_save; o_ptr = o_save; e_ptr = e_save; break; } } if (UNLIKELY(ml == ZXC_TOKEN_ML_MASK)) ml += zxc_read_varint(&e_ptr, e_end); ml += ZXC_LZ_MIN_MATCH_LEN; // Check bounds before wild copies - if too close to end, fall back to Safe Path if (UNLIKELY(ll + ml + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) { // Restore pointers and let Safe Path handle this sequence t_ptr = t_save; o_ptr = o_save; e_ptr = e_save; break; } { const uint8_t* src_lit = l_ptr; uint8_t* dst_lit = d_ptr; zxc_copy32(dst_lit, src_lit); if (UNLIKELY(ll > ZXC_PAD_SIZE)) { dst_lit += ZXC_PAD_SIZE; src_lit += ZXC_PAD_SIZE; size_t rem = ll - ZXC_PAD_SIZE; while (rem > ZXC_PAD_SIZE) { zxc_copy32(dst_lit, src_lit); dst_lit += ZXC_PAD_SIZE; src_lit += ZXC_PAD_SIZE; rem -= ZXC_PAD_SIZE; } zxc_copy32(dst_lit, src_lit); } l_ptr += ll; d_ptr += ll; written += ll; } { // Skip check if written >= bounds_threshold (256 for 8-bit, 65536 for 16-bit) if (UNLIKELY(written < bounds_threshold && offset > written)) return ZXC_ERROR_BAD_OFFSET; /* The loop entry check guarantees ll + ml + ZXC_PAD_SIZE bytes of * headroom, so the wild-copy ladder (incl. overlap/fill runs) is safe. */ zxc_decode_copy_match(d_ptr, offset, ml); d_ptr += ml; written += ml; } n_seq--; } // --- Safe Path for Remaining Sequences --- while (n_seq > 0) { uint8_t token = *t_ptr++; uint64_t ll = token >> ZXC_TOKEN_LIT_BITS; uint64_t ml = token & ZXC_TOKEN_ML_MASK; uint32_t offset = ZXC_LZ_OFFSET_BIAS; if (gh.enc_off == 1) { offset += *o_ptr++; // 1-byte offset (biased) } else { offset += zxc_le16(o_ptr); // 2-byte offset (biased) o_ptr += sizeof(uint16_t); } if (UNLIKELY(ll == ZXC_TOKEN_LL_MASK)) ll += zxc_read_varint(&e_ptr, e_end); if (UNLIKELY(ml == ZXC_TOKEN_ML_MASK)) ml += zxc_read_varint(&e_ptr, e_end); ml += ZXC_LZ_MIN_MATCH_LEN; if (UNLIKELY(ll + ml > (size_t)(d_end - d_ptr) || l_ptr + ll > l_end)) return ZXC_ERROR_OVERFLOW; ZXC_MEMCPY(d_ptr, l_ptr, ll); l_ptr += ll; d_ptr += ll; const uint8_t* match_src = d_ptr - offset; if (UNLIKELY(match_src < dst - dict_size)) return ZXC_ERROR_BAD_OFFSET; zxc_decode_copy_match_exact(d_ptr, match_src, offset, ml); d_ptr += ml; n_seq--; } // --- Trailing Literals --- // Copy remaining literals from source stream (literal exhaustion) if (UNLIKELY(l_ptr > l_end)) return ZXC_ERROR_CORRUPT_DATA; const size_t remaining_literals = (size_t)(l_end - l_ptr); if (remaining_literals > 0) { if (UNLIKELY(d_ptr + remaining_literals > d_end)) return ZXC_ERROR_OVERFLOW; ZXC_MEMCPY(d_ptr, l_ptr, remaining_literals); d_ptr += remaining_literals; } return (int)(d_ptr - dst); } /** * @brief Unified GHI (General High) block decoder body, shared by the fast, safe * and dictionary variants. * * Decodes a block in the internal GHI format; the decompressed size is derived * from the Section Descriptors in the payload. @p safe and @p has_dict must be * compile-time constants (0 or 1): the 4x-unrolled loops are duplicated inside * @c if(safe)/else branches so each variant keeps single-assignment @c const * save pointers, and after constant propagation only one branch survives per * wrapper. * * @param[in,out] ctx Decompression context (dict buffer, tables). * @param[in] src Compressed block payload. * @param[in] src_size Size of @p src in bytes. * @param[out] dst Destination buffer for decoded bytes. * @param[in] dst_capacity Capacity of @p dst in bytes. * @param[in] safe Compile-time flag: 1 = strict bounds-checked loop. * @param[in] has_dict Compile-time flag: 1 = resolve matches against a dict prefix. * @return Bytes written to @p dst on success, or a negative @ref zxc_error_t. */ static ZXC_ALWAYS_INLINE int zxc_decode_block_ghi_impl(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_size, uint8_t* RESTRICT dst, const size_t dst_capacity, const int safe, const int has_dict) { zxc_gnr_header_t gh; /* 0 when !has_dict (safe path) -> folds `written`/`dst - dict_size`. */ const size_t dict_size = has_dict ? ctx->dict_size : 0; zxc_section_desc_t desc[ZXC_GHI_SECTIONS]; if (UNLIKELY(zxc_read_ghi_header_and_desc(src, src_size, &gh, desc) != ZXC_OK)) return ZXC_ERROR_BAD_HEADER; const uint8_t* p_curr = src + ZXC_GHI_HEADER_BINARY_SIZE + ZXC_GHI_SECTIONS * ZXC_SECTION_DESC_BINARY_SIZE; // --- Stream Pointers & Validation --- const size_t sz_lit = (uint32_t)desc[0].sizes; const size_t sz_seqs = (uint32_t)desc[1].sizes; const size_t sz_exts = (uint32_t)desc[2].sizes; const uint8_t* l_ptr = p_curr; const uint8_t* l_end = l_ptr + sz_lit; p_curr += sz_lit; const uint8_t* seq_ptr = p_curr; const uint8_t* extras_ptr = p_curr + sz_seqs; const uint8_t* const extras_end = extras_ptr + sz_exts; // Validate streams don't overflow source buffer + // Validate sequence stream size matches sequence count if (UNLIKELY((extras_end != src + src_size) || ((uint64_t)sz_seqs < (uint64_t)gh.n_sequences * 4))) return ZXC_ERROR_CORRUPT_DATA; uint8_t* d_ptr = dst; const uint8_t* const d_end = dst + dst_capacity; const uint8_t* const d_end_safe = d_end - (ZXC_PAD_SIZE * 4); // 128 // Safety margin for 4x unrolled loop: 4 * (ZXC_SEQ_LL_MASK LL + // ZXC_SEQ_ML_MASK+ZXC_LZ_MIN_MATCH_LEN ML) + ZXC_PAD_SIZE Pad = 4 x (255 + 255 + 5) + 32 = 2092 const uint8_t* const d_end_fast = d_end - ZXC_DECOMPRESS_TAIL_PAD; // 2112 // Literal stream safe thresholds for GHI loops. // Without varint extension, max ll per sequence = ZXC_SEQ_LL_MASK - 1 = 254. // For 4 sequences: 4 * 254 = 1016. With this margin, l_ptr checks are only needed // on the cold varint path, keeping the hot path free of l_ptr overhead. const size_t ghi_margin_4x = 4 * (ZXC_SEQ_LL_MASK - 1); // 1016 const size_t ghi_margin_1x = ZXC_SEQ_LL_MASK - 1; // 254 const uint8_t* const l_end_safe_4x = (sz_lit > ghi_margin_4x) ? l_end - ghi_margin_4x : l_ptr; const uint8_t* const l_end_safe_1x = (sz_lit > ghi_margin_1x) ? l_end - ghi_margin_1x : l_ptr; uint32_t n_seq = gh.n_sequences; // Track bytes written for offset validation // For 1-byte offsets (enc_off==1): validate until 256 bytes written (max 8-bit offset) // For 2-byte offsets (enc_off==0): validate until 65536 bytes written (max 16-bit offset) // After threshold, all offsets are guaranteed valid (can't exceed written bytes) // When a dictionary is active, dict_size bytes are logically "already written" // (prepended by the caller), so the SAFE loop may be skipped entirely. size_t written = dict_size; // --- SAFE Loop: offset validation until threshold (4x unroll) --- // Since offset is 16-bit, threshold is 65536. // For 1-byte offsets (enc_off==1): validate until 256 bytes written // For 2-byte offsets (enc_off==0): validate until 65536 bytes written const size_t bounds_threshold = (gh.enc_off == 1) ? (1U << 8) : (1U << 16); if (safe) { /* SAFE variant: save per-batch state so an OVERFLOW can rollback and * hand over to the 1x loop / Safe Path. Wild writes already committed * are deterministically overwritten when the 1x loop replays. */ while (n_seq >= 4 && d_ptr < d_end_fast && l_ptr < l_end_safe_4x && written < bounds_threshold) { const uint8_t* const t_save = seq_ptr; const uint8_t* const e_save = extras_ptr; uint8_t* const d_save = d_ptr; const uint8_t* const l_save = l_ptr; const size_t w_save = written; uint32_t s1 = zxc_le32(seq_ptr); uint32_t s2 = zxc_le32(seq_ptr + sizeof(uint32_t)); uint32_t s3 = zxc_le32(seq_ptr + 2 * sizeof(uint32_t)); uint32_t s4 = zxc_le32(seq_ptr + 3 * sizeof(uint32_t)); seq_ptr += 4 * sizeof(uint32_t); uint64_t ll1 = s1 >> 24; if (UNLIKELY(ll1 == ZXC_SEQ_LL_MASK)) { ll1 += zxc_read_varint(&extras_ptr, extras_end); const uint64_t reserve = (s2 >> 24) + (s3 >> 24) + (s4 >> 24); if (UNLIKELY(ll1 + reserve > (size_t)(l_end - l_ptr) || ll1 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_safe_4x; } uint32_t m1b = (s1 >> 16) & 0xFF; uint64_t ml1 = m1b + ZXC_LZ_MIN_MATCH_LEN; if (UNLIKELY(m1b == ZXC_SEQ_ML_MASK)) { ml1 += zxc_read_varint(&extras_ptr, extras_end); if (UNLIKELY(ll1 + ml1 + 3U * ZXC_GHI_MAX_INLINE_OUT_PER_SEQ + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_safe_4x; } uint32_t off1 = (s1 & 0xFFFF) + ZXC_LZ_OFFSET_BIAS; DECODE_SEQ_SAFE(ll1, ml1, off1); uint64_t ll2 = s2 >> 24; if (UNLIKELY(ll2 == ZXC_SEQ_LL_MASK)) { ll2 += zxc_read_varint(&extras_ptr, extras_end); const uint64_t reserve = (s3 >> 24) + (s4 >> 24); if (UNLIKELY(ll2 + reserve > (size_t)(l_end - l_ptr) || ll2 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_safe_4x; } uint32_t m2b = (s2 >> 16) & 0xFF; uint64_t ml2 = m2b + ZXC_LZ_MIN_MATCH_LEN; if (UNLIKELY(m2b == ZXC_SEQ_ML_MASK)) { ml2 += zxc_read_varint(&extras_ptr, extras_end); if (UNLIKELY(ll2 + ml2 + 2U * ZXC_GHI_MAX_INLINE_OUT_PER_SEQ + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_safe_4x; } uint32_t off2 = (s2 & 0xFFFF) + ZXC_LZ_OFFSET_BIAS; DECODE_SEQ_SAFE(ll2, ml2, off2); uint64_t ll3 = s3 >> 24; if (UNLIKELY(ll3 == ZXC_SEQ_LL_MASK)) { ll3 += zxc_read_varint(&extras_ptr, extras_end); const uint64_t reserve = (s4 >> 24); if (UNLIKELY(ll3 + reserve > (size_t)(l_end - l_ptr) || ll3 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_safe_4x; } uint32_t m3b = (s3 >> 16) & 0xFF; uint64_t ml3 = m3b + ZXC_LZ_MIN_MATCH_LEN; if (UNLIKELY(m3b == ZXC_SEQ_ML_MASK)) { ml3 += zxc_read_varint(&extras_ptr, extras_end); if (UNLIKELY(ll3 + ml3 + ZXC_GHI_MAX_INLINE_OUT_PER_SEQ + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_safe_4x; } uint32_t off3 = (s3 & 0xFFFF) + ZXC_LZ_OFFSET_BIAS; DECODE_SEQ_SAFE(ll3, ml3, off3); uint64_t ll4 = s4 >> 24; if (UNLIKELY(ll4 == ZXC_SEQ_LL_MASK)) { ll4 += zxc_read_varint(&extras_ptr, extras_end); if (UNLIKELY(ll4 > (size_t)(l_end - l_ptr) || ll4 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_safe_4x; } uint32_t m4b = (s4 >> 16) & 0xFF; uint64_t ml4 = m4b + ZXC_LZ_MIN_MATCH_LEN; if (UNLIKELY(m4b == ZXC_SEQ_ML_MASK)) { ml4 += zxc_read_varint(&extras_ptr, extras_end); if (UNLIKELY(ll4 + ml4 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_safe_4x; } uint32_t off4 = (s4 & 0xFFFF) + ZXC_LZ_OFFSET_BIAS; DECODE_SEQ_SAFE(ll4, ml4, off4); n_seq -= 4; continue; rollback_safe_4x: seq_ptr = t_save; extras_ptr = e_save; d_ptr = d_save; l_ptr = l_save; written = w_save; break; } } else { while (n_seq >= 4 && d_ptr < d_end_fast && l_ptr < l_end_safe_4x && written < bounds_threshold) { uint32_t s1 = zxc_le32(seq_ptr); uint32_t s2 = zxc_le32(seq_ptr + sizeof(uint32_t)); uint32_t s3 = zxc_le32(seq_ptr + 2 * sizeof(uint32_t)); uint32_t s4 = zxc_le32(seq_ptr + 3 * sizeof(uint32_t)); seq_ptr += 4 * sizeof(uint32_t); uint64_t ll1 = s1 >> 24; if (UNLIKELY(ll1 == ZXC_SEQ_LL_MASK)) { ll1 += zxc_read_varint(&extras_ptr, extras_end); const uint64_t reserve = (s2 >> 24) + (s3 >> 24) + (s4 >> 24); if (UNLIKELY(ll1 + reserve > (size_t)(l_end - l_ptr) || ll1 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } uint32_t m1b = (s1 >> 16) & 0xFF; uint64_t ml1 = m1b + ZXC_LZ_MIN_MATCH_LEN; if (UNLIKELY(m1b == ZXC_SEQ_ML_MASK)) { ml1 += zxc_read_varint(&extras_ptr, extras_end); if (UNLIKELY(ll1 + ml1 + 3U * ZXC_GHI_MAX_INLINE_OUT_PER_SEQ + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } uint32_t off1 = (s1 & 0xFFFF) + ZXC_LZ_OFFSET_BIAS; DECODE_SEQ_SAFE(ll1, ml1, off1); uint64_t ll2 = s2 >> 24; if (UNLIKELY(ll2 == ZXC_SEQ_LL_MASK)) { ll2 += zxc_read_varint(&extras_ptr, extras_end); const uint64_t reserve = (s3 >> 24) + (s4 >> 24); if (UNLIKELY(ll2 + reserve > (size_t)(l_end - l_ptr) || ll2 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } uint32_t m2b = (s2 >> 16) & 0xFF; uint64_t ml2 = m2b + ZXC_LZ_MIN_MATCH_LEN; if (UNLIKELY(m2b == ZXC_SEQ_ML_MASK)) { ml2 += zxc_read_varint(&extras_ptr, extras_end); if (UNLIKELY(ll2 + ml2 + 2U * ZXC_GHI_MAX_INLINE_OUT_PER_SEQ + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } uint32_t off2 = (s2 & 0xFFFF) + ZXC_LZ_OFFSET_BIAS; DECODE_SEQ_SAFE(ll2, ml2, off2); uint64_t ll3 = s3 >> 24; if (UNLIKELY(ll3 == ZXC_SEQ_LL_MASK)) { ll3 += zxc_read_varint(&extras_ptr, extras_end); const uint64_t reserve = (s4 >> 24); if (UNLIKELY(ll3 + reserve > (size_t)(l_end - l_ptr) || ll3 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } uint32_t m3b = (s3 >> 16) & 0xFF; uint64_t ml3 = m3b + ZXC_LZ_MIN_MATCH_LEN; if (UNLIKELY(m3b == ZXC_SEQ_ML_MASK)) { ml3 += zxc_read_varint(&extras_ptr, extras_end); if (UNLIKELY(ll3 + ml3 + ZXC_GHI_MAX_INLINE_OUT_PER_SEQ + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } uint32_t off3 = (s3 & 0xFFFF) + ZXC_LZ_OFFSET_BIAS; DECODE_SEQ_SAFE(ll3, ml3, off3); uint64_t ll4 = s4 >> 24; if (UNLIKELY(ll4 == ZXC_SEQ_LL_MASK)) { ll4 += zxc_read_varint(&extras_ptr, extras_end); if (UNLIKELY(ll4 > (size_t)(l_end - l_ptr) || ll4 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } uint32_t m4b = (s4 >> 16) & 0xFF; uint64_t ml4 = m4b + ZXC_LZ_MIN_MATCH_LEN; if (UNLIKELY(m4b == ZXC_SEQ_ML_MASK)) { ml4 += zxc_read_varint(&extras_ptr, extras_end); if (UNLIKELY(ll4 + ml4 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } uint32_t off4 = (s4 & 0xFFFF) + ZXC_LZ_OFFSET_BIAS; DECODE_SEQ_SAFE(ll4, ml4, off4); n_seq -= 4; } } // --- SAFE Loop tail: remaining sequences with offset validation (1x) --- while (n_seq > 0 && d_ptr < d_end_safe && written < bounds_threshold) { uint32_t seq = zxc_le32(seq_ptr); seq_ptr += sizeof(uint32_t); uint64_t ll = seq >> 24; if (UNLIKELY(ll == ZXC_SEQ_LL_MASK)) ll += zxc_read_varint(&extras_ptr, extras_end); uint32_t m_bits = (seq >> 16) & 0xFF; uint64_t ml = m_bits + ZXC_LZ_MIN_MATCH_LEN; if (UNLIKELY(m_bits == ZXC_SEQ_ML_MASK)) ml += zxc_read_varint(&extras_ptr, extras_end); uint32_t offset = (seq & 0xFFFF) + ZXC_LZ_OFFSET_BIAS; // Strict bounds check: sequence must fit, AND wild copies must not overshoot // Check both destination (d_ptr) and source literal stream (l_ptr) if (UNLIKELY(ll + ml + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr) || ll + ZXC_PAD_SIZE > (size_t)(l_end - l_ptr))) { // Fallback to exact copy (slow but safe) if (UNLIKELY(d_ptr + ll > d_end || l_ptr + ll > l_end)) return ZXC_ERROR_OVERFLOW; ZXC_MEMCPY(d_ptr, l_ptr, ll); l_ptr += ll; d_ptr += ll; written += ll; if (UNLIKELY(offset > written || d_ptr + ml > d_end)) return ZXC_ERROR_BAD_OFFSET; const uint8_t* match_src = d_ptr - offset; zxc_decode_copy_match_exact(d_ptr, match_src, offset, ml); d_ptr += ml; written += ml; } else { // Safe to process with wild copies DECODE_SEQ_SAFE(ll, ml, offset); } n_seq--; } // --- FAST Loop: After threshold, check large margin to avoid individual bounds checks --- if (safe) { while (n_seq >= 4 && d_ptr < d_end_fast && l_ptr < l_end_safe_4x) { const uint8_t* const t_save = seq_ptr; const uint8_t* const e_save = extras_ptr; uint8_t* const d_save = d_ptr; const uint8_t* const l_save = l_ptr; uint32_t s1 = zxc_le32(seq_ptr); uint32_t s2 = zxc_le32(seq_ptr + sizeof(uint32_t)); uint32_t s3 = zxc_le32(seq_ptr + 2 * sizeof(uint32_t)); uint32_t s4 = zxc_le32(seq_ptr + 3 * sizeof(uint32_t)); seq_ptr += 4 * sizeof(uint32_t); // Prefetch ahead in literal and extras streams to hide memory latency ZXC_PREFETCH_READ(l_ptr + ZXC_CACHE_LINE_SIZE); uint64_t ll1 = s1 >> 24; if (UNLIKELY(ll1 == ZXC_SEQ_LL_MASK)) { ll1 += zxc_read_varint(&extras_ptr, extras_end); const uint64_t reserve = (s2 >> 24) + (s3 >> 24) + (s4 >> 24); if (UNLIKELY(ll1 + reserve > (size_t)(l_end - l_ptr) || ll1 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_fast_4x; } uint32_t m1b = (s1 >> 16) & 0xFF; uint64_t ml1 = m1b + ZXC_LZ_MIN_MATCH_LEN; if (UNLIKELY(m1b == ZXC_SEQ_ML_MASK)) { ml1 += zxc_read_varint(&extras_ptr, extras_end); if (UNLIKELY(ll1 + ml1 + 3U * ZXC_GHI_MAX_INLINE_OUT_PER_SEQ + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_fast_4x; } uint32_t off1 = (s1 & 0xFFFF) + ZXC_LZ_OFFSET_BIAS; DECODE_SEQ_FAST(ll1, ml1, off1); uint64_t ll2 = s2 >> 24; if (UNLIKELY(ll2 == ZXC_SEQ_LL_MASK)) { ll2 += zxc_read_varint(&extras_ptr, extras_end); const uint64_t reserve = (s3 >> 24) + (s4 >> 24); if (UNLIKELY(ll2 + reserve > (size_t)(l_end - l_ptr) || ll2 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_fast_4x; } uint32_t m2b = (s2 >> 16) & 0xFF; uint64_t ml2 = m2b + ZXC_LZ_MIN_MATCH_LEN; if (UNLIKELY(m2b == ZXC_SEQ_ML_MASK)) { ml2 += zxc_read_varint(&extras_ptr, extras_end); if (UNLIKELY(ll2 + ml2 + 2U * ZXC_GHI_MAX_INLINE_OUT_PER_SEQ + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_fast_4x; } uint32_t off2 = (s2 & 0xFFFF) + ZXC_LZ_OFFSET_BIAS; DECODE_SEQ_FAST(ll2, ml2, off2); uint64_t ll3 = s3 >> 24; if (UNLIKELY(ll3 == ZXC_SEQ_LL_MASK)) { ll3 += zxc_read_varint(&extras_ptr, extras_end); const uint64_t reserve = (s4 >> 24); if (UNLIKELY(ll3 + reserve > (size_t)(l_end - l_ptr) || ll3 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_fast_4x; } uint32_t m3b = (s3 >> 16) & 0xFF; uint64_t ml3 = m3b + ZXC_LZ_MIN_MATCH_LEN; if (UNLIKELY(m3b == ZXC_SEQ_ML_MASK)) { ml3 += zxc_read_varint(&extras_ptr, extras_end); if (UNLIKELY(ll3 + ml3 + ZXC_GHI_MAX_INLINE_OUT_PER_SEQ + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_fast_4x; } uint32_t off3 = (s3 & 0xFFFF) + ZXC_LZ_OFFSET_BIAS; DECODE_SEQ_FAST(ll3, ml3, off3); uint64_t ll4 = s4 >> 24; if (UNLIKELY(ll4 == ZXC_SEQ_LL_MASK)) { ll4 += zxc_read_varint(&extras_ptr, extras_end); if (UNLIKELY(ll4 > (size_t)(l_end - l_ptr) || ll4 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_fast_4x; } uint32_t m4b = (s4 >> 16) & 0xFF; uint64_t ml4 = m4b + ZXC_LZ_MIN_MATCH_LEN; if (UNLIKELY(m4b == ZXC_SEQ_ML_MASK)) { ml4 += zxc_read_varint(&extras_ptr, extras_end); if (UNLIKELY(ll4 + ml4 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) goto rollback_fast_4x; } uint32_t off4 = (s4 & 0xFFFF) + ZXC_LZ_OFFSET_BIAS; DECODE_SEQ_FAST(ll4, ml4, off4); n_seq -= 4; continue; rollback_fast_4x: seq_ptr = t_save; extras_ptr = e_save; d_ptr = d_save; l_ptr = l_save; break; } } else { while (n_seq >= 4 && d_ptr < d_end_fast && l_ptr < l_end_safe_4x) { uint32_t s1 = zxc_le32(seq_ptr); uint32_t s2 = zxc_le32(seq_ptr + sizeof(uint32_t)); uint32_t s3 = zxc_le32(seq_ptr + 2 * sizeof(uint32_t)); uint32_t s4 = zxc_le32(seq_ptr + 3 * sizeof(uint32_t)); seq_ptr += 4 * sizeof(uint32_t); // Prefetch ahead in literal and extras streams to hide memory latency ZXC_PREFETCH_READ(l_ptr + ZXC_CACHE_LINE_SIZE); uint64_t ll1 = s1 >> 24; if (UNLIKELY(ll1 == ZXC_SEQ_LL_MASK)) { ll1 += zxc_read_varint(&extras_ptr, extras_end); const uint64_t reserve = (s2 >> 24) + (s3 >> 24) + (s4 >> 24); if (UNLIKELY(ll1 + reserve > (size_t)(l_end - l_ptr) || ll1 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } uint32_t m1b = (s1 >> 16) & 0xFF; uint64_t ml1 = m1b + ZXC_LZ_MIN_MATCH_LEN; if (UNLIKELY(m1b == ZXC_SEQ_ML_MASK)) { ml1 += zxc_read_varint(&extras_ptr, extras_end); if (UNLIKELY(ll1 + ml1 + 3U * ZXC_GHI_MAX_INLINE_OUT_PER_SEQ + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } uint32_t off1 = (s1 & 0xFFFF) + ZXC_LZ_OFFSET_BIAS; DECODE_SEQ_FAST(ll1, ml1, off1); uint64_t ll2 = s2 >> 24; if (UNLIKELY(ll2 == ZXC_SEQ_LL_MASK)) { ll2 += zxc_read_varint(&extras_ptr, extras_end); const uint64_t reserve = (s3 >> 24) + (s4 >> 24); if (UNLIKELY(ll2 + reserve > (size_t)(l_end - l_ptr) || ll2 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } uint32_t m2b = (s2 >> 16) & 0xFF; uint64_t ml2 = m2b + ZXC_LZ_MIN_MATCH_LEN; if (UNLIKELY(m2b == ZXC_SEQ_ML_MASK)) { ml2 += zxc_read_varint(&extras_ptr, extras_end); if (UNLIKELY(ll2 + ml2 + 2U * ZXC_GHI_MAX_INLINE_OUT_PER_SEQ + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } uint32_t off2 = (s2 & 0xFFFF) + ZXC_LZ_OFFSET_BIAS; DECODE_SEQ_FAST(ll2, ml2, off2); uint64_t ll3 = s3 >> 24; if (UNLIKELY(ll3 == ZXC_SEQ_LL_MASK)) { ll3 += zxc_read_varint(&extras_ptr, extras_end); const uint64_t reserve = (s4 >> 24); if (UNLIKELY(ll3 + reserve > (size_t)(l_end - l_ptr) || ll3 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } uint32_t m3b = (s3 >> 16) & 0xFF; uint64_t ml3 = m3b + ZXC_LZ_MIN_MATCH_LEN; if (UNLIKELY(m3b == ZXC_SEQ_ML_MASK)) { ml3 += zxc_read_varint(&extras_ptr, extras_end); if (UNLIKELY(ll3 + ml3 + ZXC_GHI_MAX_INLINE_OUT_PER_SEQ + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } uint32_t off3 = (s3 & 0xFFFF) + ZXC_LZ_OFFSET_BIAS; DECODE_SEQ_FAST(ll3, ml3, off3); uint64_t ll4 = s4 >> 24; if (UNLIKELY(ll4 == ZXC_SEQ_LL_MASK)) { ll4 += zxc_read_varint(&extras_ptr, extras_end); if (UNLIKELY(ll4 > (size_t)(l_end - l_ptr) || ll4 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } uint32_t m4b = (s4 >> 16) & 0xFF; uint64_t ml4 = m4b + ZXC_LZ_MIN_MATCH_LEN; if (UNLIKELY(m4b == ZXC_SEQ_ML_MASK)) { ml4 += zxc_read_varint(&extras_ptr, extras_end); if (UNLIKELY(ll4 + ml4 + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr))) return ZXC_ERROR_OVERFLOW; } uint32_t off4 = (s4 & 0xFFFF) + ZXC_LZ_OFFSET_BIAS; DECODE_SEQ_FAST(ll4, ml4, off4); n_seq -= 4; } } // --- Remaining 1 sequence (Fast Path) --- while (n_seq > 0 && d_ptr < d_end_safe && l_ptr < l_end_safe_1x) { // Save state for fallback const uint8_t* seq_save = seq_ptr; const uint8_t* ext_save = extras_ptr; const uint32_t seq = zxc_le32(seq_ptr); seq_ptr += sizeof(uint32_t); uint64_t ll = seq >> 24; if (UNLIKELY(ll == ZXC_SEQ_LL_MASK)) { ll += zxc_read_varint(&extras_ptr, extras_end); if (UNLIKELY(l_ptr + ll > l_end)) { seq_ptr = seq_save; extras_ptr = ext_save; break; } } uint32_t m_bits = (seq >> 16) & 0xFF; uint64_t ml = m_bits + ZXC_LZ_MIN_MATCH_LEN; if (UNLIKELY(m_bits == ZXC_SEQ_ML_MASK)) ml += zxc_read_varint(&extras_ptr, extras_end); // Strict bounds checks (including wild copy overrun safety) if (UNLIKELY(ll + ml + ZXC_PAD_SIZE > (size_t)(d_end - d_ptr) || ll + ZXC_PAD_SIZE > (size_t)(l_end - l_ptr))) { // Restore state and break to Safe Path seq_ptr = seq_save; extras_ptr = ext_save; break; } uint32_t offset = (seq & 0xFFFF) + ZXC_LZ_OFFSET_BIAS; { const uint8_t* src_lit = l_ptr; uint8_t* dst_lit = d_ptr; zxc_copy32(dst_lit, src_lit); if (UNLIKELY(ll > ZXC_PAD_SIZE)) { dst_lit += ZXC_PAD_SIZE; src_lit += ZXC_PAD_SIZE; size_t rem = ll - ZXC_PAD_SIZE; while (rem > ZXC_PAD_SIZE) { zxc_copy32(dst_lit, src_lit); dst_lit += ZXC_PAD_SIZE; src_lit += ZXC_PAD_SIZE; rem -= ZXC_PAD_SIZE; } zxc_copy32(dst_lit, src_lit); } l_ptr += ll; d_ptr += ll; written += ll; } { // Skip check if written >= bounds_threshold (256 for 8-bit, 65536 for 16-bit) if (UNLIKELY(written < bounds_threshold && offset > written)) return ZXC_ERROR_BAD_OFFSET; /* The loop entry check guarantees ll + ml + ZXC_PAD_SIZE bytes of * headroom, so the wild-copy ladder (incl. overlap/fill runs) is safe. */ zxc_decode_copy_match(d_ptr, offset, ml); d_ptr += ml; written += ml; } n_seq--; } // --- Safe Path for Remaining Sequences --- while (n_seq > 0) { uint32_t seq = zxc_le32(seq_ptr); seq_ptr += sizeof(uint32_t); uint64_t ll = seq >> 24; if (UNLIKELY(ll == ZXC_SEQ_LL_MASK)) ll += zxc_read_varint(&extras_ptr, extras_end); uint32_t m_bits = (seq >> 16) & 0xFF; uint64_t ml = m_bits + ZXC_LZ_MIN_MATCH_LEN; if (UNLIKELY(m_bits == ZXC_SEQ_ML_MASK)) ml += zxc_read_varint(&extras_ptr, extras_end); uint32_t offset = (seq & 0xFFFF) + ZXC_LZ_OFFSET_BIAS; if (UNLIKELY(ll + ml > (size_t)(d_end - d_ptr) || l_ptr + ll > l_end)) return ZXC_ERROR_OVERFLOW; ZXC_MEMCPY(d_ptr, l_ptr, ll); l_ptr += ll; d_ptr += ll; const uint8_t* match_src = d_ptr - offset; if (UNLIKELY(match_src < dst - dict_size)) return ZXC_ERROR_BAD_OFFSET; zxc_decode_copy_match_exact(d_ptr, match_src, offset, ml); d_ptr += ml; n_seq--; } // --- Trailing Literals --- // Copy remaining literals from source stream (literal exhaustion) if (UNLIKELY(l_ptr > l_end)) return ZXC_ERROR_CORRUPT_DATA; const size_t remaining_literals = (size_t)(l_end - l_ptr); if (remaining_literals > 0) { if (UNLIKELY(d_ptr + remaining_literals > d_end)) return ZXC_ERROR_OVERFLOW; ZXC_MEMCPY(d_ptr, l_ptr, remaining_literals); d_ptr += remaining_literals; } return (int)(d_ptr - dst); } /** * Cold specializations for entropy-coded GLO tokens (level 7). Each wrapper fixes * tok_entropy=1 plus one (safe, has_dict) combo as compile-time constants, so the * inlined impl is fully specialized and the RAW-token fast path stays untouched. */ static ZXC_NOINLINE int zxc_decode_block_glo_entropy(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_size, uint8_t* RESTRICT dst, const size_t dst_capacity) { return zxc_decode_block_glo_impl(ctx, src, src_size, dst, dst_capacity, 0, 0, 1); } static ZXC_NOINLINE int zxc_decode_block_glo_entropy_dict(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_size, uint8_t* RESTRICT dst, const size_t dst_capacity) { return zxc_decode_block_glo_impl(ctx, src, src_size, dst, dst_capacity, 0, 1, 1); } static ZXC_NOINLINE int zxc_decode_block_glo_entropy_safe(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_size, uint8_t* RESTRICT dst, const size_t dst_capacity) { return zxc_decode_block_glo_impl(ctx, src, src_size, dst, dst_capacity, 1, 0, 1); } /** * @brief Decode a no-dict GLO block (plain, inlinable path). * * Wrapper over @ref zxc_decode_block_glo_impl with @c safe=0, @c has_dict=0, so * the no-dict chunk wrapper inlines it exactly like the dict-free build. * * @param[in,out] ctx Decompression context. * @param[in] src Compressed GLO block payload. * @param[in] src_size Size of @p src in bytes. * @param[out] dst Destination buffer. * @param[in] dst_capacity Capacity of @p dst in bytes. * @return Bytes written on success, or a negative @ref zxc_error_t. */ static int zxc_decode_block_glo(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_size, uint8_t* RESTRICT dst, const size_t dst_capacity) { return zxc_decode_block_glo_impl(ctx, src, src_size, dst, dst_capacity, 0, 0, 0); } /** * @brief Decode a no-dict GHI block (plain, inlinable path). * * Wrapper over @ref zxc_decode_block_ghi_impl with @c safe=0, @c has_dict=0. * * @param[in,out] ctx Decompression context. * @param[in] src Compressed GHI block payload. * @param[in] src_size Size of @p src in bytes. * @param[out] dst Destination buffer. * @param[in] dst_capacity Capacity of @p dst in bytes. * @return Bytes written on success, or a negative @ref zxc_error_t. */ static int zxc_decode_block_ghi(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_size, uint8_t* RESTRICT dst, const size_t dst_capacity) { return zxc_decode_block_ghi_impl(ctx, src, src_size, dst, dst_capacity, 0, 0); } /** * @brief Decode a GLO block against a dictionary prefix (cold path). * * Wrapper over @ref zxc_decode_block_glo_impl with @c safe=0, @c has_dict=1. * NOINLINE: only reached on the cold dict path (@ref zxc_decompress_chunk_wrapper_dict), * so it never loads into I-cache on a no-dict stream. * * @param[in,out] ctx Decompression context (dict prefix in its buffer). * @param[in] src Compressed GLO block payload. * @param[in] src_size Size of @p src in bytes. * @param[out] dst Destination buffer. * @param[in] dst_capacity Capacity of @p dst in bytes. * @return Bytes written on success, or a negative @ref zxc_error_t. */ static ZXC_NOINLINE int zxc_decode_block_glo_dict(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_size, uint8_t* RESTRICT dst, const size_t dst_capacity) { return zxc_decode_block_glo_impl(ctx, src, src_size, dst, dst_capacity, 0, 1, 0); } /** * @brief Decode a GHI block against a dictionary prefix (cold path). * * Wrapper over @ref zxc_decode_block_ghi_impl with @c safe=0, @c has_dict=1 * (NOINLINE; see @ref zxc_decode_block_glo_dict). * * @param[in,out] ctx Decompression context (dict prefix in its buffer). * @param[in] src Compressed GHI block payload. * @param[in] src_size Size of @p src in bytes. * @param[out] dst Destination buffer. * @param[in] dst_capacity Capacity of @p dst in bytes. * @return Bytes written on success, or a negative @ref zxc_error_t. */ static ZXC_NOINLINE int zxc_decode_block_ghi_dict(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_size, uint8_t* RESTRICT dst, const size_t dst_capacity) { return zxc_decode_block_ghi_impl(ctx, src, src_size, dst, dst_capacity, 0, 1); } /** * @brief Decode a GLO block with the strict-tail safe loop (no wild copies). * * Wrapper over @ref zxc_decode_block_glo_impl with @c safe=1, @c has_dict=0. * The safe path never carries a dict (block_safe routes dict inputs to the * bounce path), so @c has_dict=0 folds the dead dict handling. * * @param[in,out] ctx Decompression context. * @param[in] src Compressed GLO block payload. * @param[in] src_size Size of @p src in bytes. * @param[out] dst Destination buffer (capacity == exact decoded size). * @param[in] dst_capacity Capacity of @p dst in bytes. * @return Bytes written on success, or a negative @ref zxc_error_t. */ static ZXC_NOINLINE int zxc_decode_block_glo_safe(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_size, uint8_t* RESTRICT dst, const size_t dst_capacity) { return zxc_decode_block_glo_impl(ctx, src, src_size, dst, dst_capacity, 1, 0, 0); } /** * @brief Decode a GHI block with the strict-tail safe loop (no wild copies). * * Wrapper over @ref zxc_decode_block_ghi_impl with @c safe=1, @c has_dict=0 * (the strict-tail safe path never carries a dict; see * @ref zxc_decode_block_glo_safe). * * @param[in,out] ctx Decompression context. * @param[in] src Compressed GHI block payload. * @param[in] src_size Size of @p src in bytes. * @param[out] dst Destination buffer (capacity == exact decoded size). * @param[in] dst_capacity Capacity of @p dst in bytes. * @return Bytes written on success, or a negative @ref zxc_error_t. */ static ZXC_NOINLINE int zxc_decode_block_ghi_safe(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_size, uint8_t* RESTRICT dst, const size_t dst_capacity) { return zxc_decode_block_ghi_impl(ctx, src, src_size, dst, dst_capacity, 1, 0); } #undef DECODE_SEQ_FAST #undef DECODE_SEQ_SAFE /** * @brief Shared chunk-decode body: validates the block header, verifies the * optional checksum, then dispatches on block type. * * @p has_dict is a compile-time constant: the no-dict instantiation folds the * GLO/GHI selection to the plain (inlinable) decoders, so * @ref zxc_decompress_chunk_wrapper carries no dict code and matches the * dict-free build; the dict instantiation calls the NOINLINE @c _dict decoders. * * @param[in,out] ctx Decompression context. * @param[in] src Compressed block (header + payload + optional checksum). * @param[in] src_sz Size of @p src in bytes. * @param[out] dst Destination buffer for the decoded block. * @param[in] dst_cap Capacity of @p dst in bytes. * @param[in] has_dict Compile-time flag: 1 = dictionary-aware decoders. * @return Bytes written on success, or a negative @ref zxc_error_t. */ static ZXC_ALWAYS_INLINE int zxc_decompress_chunk_wrapper_body( const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap, const int has_dict) { if (UNLIKELY(src_sz < ZXC_BLOCK_HEADER_SIZE)) return ZXC_ERROR_SRC_TOO_SMALL; const uint8_t type = src[0]; const uint32_t comp_sz = zxc_le32(src + 3); const int has_crc = ctx->checksum_enabled; // Check bounds: Header + Body + Checksum(if any) const size_t expected_sz = (size_t)ZXC_BLOCK_HEADER_SIZE + comp_sz + (has_crc ? ZXC_BLOCK_CHECKSUM_SIZE : 0); if (UNLIKELY(src_sz < expected_sz)) return ZXC_ERROR_SRC_TOO_SMALL; const uint8_t* data = src + ZXC_BLOCK_HEADER_SIZE; if (has_crc) { const uint32_t stored = zxc_le32(data + comp_sz); const uint32_t calc = zxc_checksum(data, comp_sz, ZXC_CHECKSUM_RAPIDHASH); if (UNLIKELY(stored != calc)) return ZXC_ERROR_BAD_CHECKSUM; } int decoded_sz = ZXC_ERROR_BAD_BLOCK_TYPE; switch (type) { case ZXC_BLOCK_GLO: decoded_sz = has_dict ? zxc_decode_block_glo_dict(ctx, data, comp_sz, dst, dst_cap) : zxc_decode_block_glo(ctx, data, comp_sz, dst, dst_cap); break; case ZXC_BLOCK_GHI: decoded_sz = has_dict ? zxc_decode_block_ghi_dict(ctx, data, comp_sz, dst, dst_cap) : zxc_decode_block_ghi(ctx, data, comp_sz, dst, dst_cap); break; case ZXC_BLOCK_RAW: // For RAW blocks, comp_sz == raw_sz (uncompressed data stored as-is) if (UNLIKELY(comp_sz > dst_cap)) return ZXC_ERROR_DST_TOO_SMALL; ZXC_MEMCPY(dst, data, comp_sz); decoded_sz = (int)comp_sz; break; case ZXC_BLOCK_EOF: // EOF should be handled by the dispatcher, not here return ZXC_ERROR_CORRUPT_DATA; default: return ZXC_ERROR_BAD_BLOCK_TYPE; } return decoded_sz; } /** * @brief Public no-dict chunk decoder (decompression hot path). * * Inlines the plain GLO/GHI decoders via @ref zxc_decompress_chunk_wrapper_body * with @c has_dict=0, so it carries no dict code and matches the dict-free build. * * @param[in,out] ctx Decompression context. * @param[in] src Compressed block bytes. * @param[in] src_sz Size of @p src in bytes. * @param[out] dst Destination buffer for the decoded block. * @param[in] dst_cap Capacity of @p dst in bytes. * @return Bytes written on success, or a negative @ref zxc_error_t. */ // cppcheck-suppress unusedFunction int zxc_decompress_chunk_wrapper(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap) { return zxc_decompress_chunk_wrapper_body(ctx, src, src_sz, dst, dst_cap, 0); } /** * @brief Public dictionary chunk decoder. * * Routes through @ref zxc_decompress_chunk_wrapper_body with @c has_dict=1, * which calls the NOINLINE @c _dict decoders (slower: dict back-refs read the * prepended dictionary). Used only when @c ctx->dict_size != 0. * * @param[in,out] ctx Decompression context (dict prefix in its buffer). * @param[in] src Compressed block bytes. * @param[in] src_sz Size of @p src in bytes. * @param[out] dst Destination buffer for the decoded block. * @param[in] dst_cap Capacity of @p dst in bytes. * @return Bytes written on success, or a negative @ref zxc_error_t. */ // cppcheck-suppress unusedFunction int zxc_decompress_chunk_wrapper_dict(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap) { return zxc_decompress_chunk_wrapper_body(ctx, src, src_sz, dst, dst_cap, 1); } /** * @brief Public strict-tail safe chunk decoder (dst_cap == exact decoded size). * * Validates the block header and optional checksum, then decodes via the * @c _safe decoders (no bounce buffer, no tail padding); RAW blocks are copied * directly. Dict inputs are not handled here (the caller routes them to the * bounce-capable path). * * @param[in,out] ctx Decompression context. * @param[in] src Compressed block bytes. * @param[in] src_sz Size of @p src in bytes. * @param[out] dst Destination buffer (capacity == exact decoded size). * @param[in] dst_cap Capacity of @p dst in bytes. * @return Bytes written on success, or a negative @ref zxc_error_t. */ // cppcheck-suppress unusedFunction int zxc_decompress_chunk_wrapper_safe(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap) { if (UNLIKELY(src_sz < ZXC_BLOCK_HEADER_SIZE)) return ZXC_ERROR_SRC_TOO_SMALL; const uint8_t type = src[0]; const uint32_t comp_sz = zxc_le32(src + 3); const int has_crc = ctx->checksum_enabled; const size_t expected_sz = (size_t)ZXC_BLOCK_HEADER_SIZE + comp_sz + (has_crc ? ZXC_BLOCK_CHECKSUM_SIZE : 0); if (UNLIKELY(src_sz < expected_sz)) return ZXC_ERROR_SRC_TOO_SMALL; const uint8_t* data = src + ZXC_BLOCK_HEADER_SIZE; if (has_crc) { const uint32_t stored = zxc_le32(data + comp_sz); const uint32_t calc = zxc_checksum(data, comp_sz, ZXC_CHECKSUM_RAPIDHASH); if (UNLIKELY(stored != calc)) return ZXC_ERROR_BAD_CHECKSUM; } switch (type) { case ZXC_BLOCK_GLO: return zxc_decode_block_glo_safe(ctx, data, comp_sz, dst, dst_cap); case ZXC_BLOCK_GHI: return zxc_decode_block_ghi_safe(ctx, data, comp_sz, dst, dst_cap); case ZXC_BLOCK_RAW: if (UNLIKELY(comp_sz > dst_cap)) return ZXC_ERROR_DST_TOO_SMALL; ZXC_MEMCPY(dst, data, comp_sz); return (int)comp_sz; case ZXC_BLOCK_EOF: return ZXC_ERROR_CORRUPT_DATA; default: return ZXC_ERROR_BAD_BLOCK_TYPE; } } zxc-0.13.0/src/lib/zxc_deps.h000066400000000000000000000074051522536370400157410ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file zxc_deps.h * @brief Single point of override for the C-library dependencies of libzxc. * * The core compression/decompression code only ever reaches the standard * library through the macros and headers declared here. * Freestanding consumers vendor the source tree and replace this file with * an environment-specific version that maps the same macros onto their own * allocator, sort, and basic header set. * * @par Stock (hosted) build * Pulls in @c , @c , @c , @c and * expands the macros to their libc equivalents. * * Per-symbol @c -D overrides are also accepted (each macro is guarded by * an @c ifndef), so vendoring is optional for ad-hoc consumers. */ #ifndef ZXC_DEPS_H #define ZXC_DEPS_H /** * @addtogroup internal * @{ */ /** * @name Standard Headers * @brief Pulled in by the stock libzxc build to provide @c size_t, * @c uintN_t / @c intN_t, @c CHAR_BIT, @c malloc / @c calloc / * @c realloc / @c free, and @c memcpy / @c memset / @c memmove / * @c memcmp. * * Vendored overrides of this file typically substitute @c , * @c , @c , @c , * @c . * @{ */ #include #include #include #include /** @} */ /* end of Standard Headers */ /** * @name Heap Allocator Abstraction * @brief Macros around the libc allocators so non-libc targets (Linux kernel, * embedded freestanding builds, custom arenas) can override them via @c -D * flags **before** including any zxc header, or by vendoring this file. * * @note Aligned allocations go through @ref ZXC_ALIGNED_MALLOC / * @ref ZXC_ALIGNED_FREE (see below), not these. * @{ */ /** @def ZXC_MALLOC * @brief Heap allocator. Default: libc @c malloc. */ #ifndef ZXC_MALLOC #define ZXC_MALLOC(size) malloc(size) #endif /** @def ZXC_CALLOC * @brief Zero-initialised heap allocator. Default: libc @c calloc. */ #ifndef ZXC_CALLOC #define ZXC_CALLOC(nmemb, size) calloc(nmemb, size) #endif /** @def ZXC_REALLOC * @brief In-place / move heap reallocator. Default: libc @c realloc. */ #ifndef ZXC_REALLOC #define ZXC_REALLOC(ptr, size) realloc(ptr, size) #endif /** @def ZXC_FREE * @brief Heap deallocator. Default: libc @c free. */ #ifndef ZXC_FREE #define ZXC_FREE(ptr) free(ptr) #endif /** @} */ /* end of Heap Allocator Abstraction */ /** * @name Aligned Allocator Abstraction * @brief Macros around the cache-line-aligned allocator used for compression * workspace and per-context scratch buffers. * * The default expansion calls the internal helpers @ref zxc_aligned_malloc / * @ref zxc_aligned_free (forward-declared in @c zxc_internal.h, defined in * @c zxc_common.c), which wrap @c _aligned_malloc / @c _aligned_free on * Windows and @c posix_memalign / @c free on POSIX. * * Kernel builds typically map this to the slab allocator: @c kmalloc already * returns @c ARCH_KMALLOC_MINALIGN-aligned memory, which is greater than or * equal to the cache line size on every supported architecture. * @{ */ /** @def ZXC_ALIGNED_MALLOC * @brief Cache-line-aligned allocator. * Default: @c zxc_aligned_malloc (wraps @c posix_memalign / * @c _aligned_malloc). */ #ifndef ZXC_ALIGNED_MALLOC #define ZXC_ALIGNED_MALLOC(size, alignment) zxc_aligned_malloc(size, alignment) #endif /** @def ZXC_ALIGNED_FREE * @brief Counterpart deallocator for @ref ZXC_ALIGNED_MALLOC. */ #ifndef ZXC_ALIGNED_FREE #define ZXC_ALIGNED_FREE(ptr) zxc_aligned_free(ptr) #endif /** @} */ /* end of Aligned Allocator Abstraction */ /** @} */ /* end of addtogroup internal */ #endif /* ZXC_DEPS_H */ zxc-0.13.0/src/lib/zxc_dict.c000066400000000000000000000726451522536370400157340ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file zxc_dict.c * @brief Pre-trained dictionary: ID computation, .zxd serialization, and training. */ #include "../../include/zxc_dict.h" #include "../../include/zxc_buffer.h" #include "zxc_internal.h" /* ------------------------------------------------------------------------- * Dictionary ID * ------------------------------------------------------------------------- */ /** * @brief Computes the dictionary identifier for @p dict (and optional table). * * Public API; see @c zxc_dict.h. One logical checksum over the content bytes, * optionally chained with the packed Huffman lengths so a single id covers * both. Stored in the archive header and re-checked on decode. * * @param[in] dict Dictionary content bytes. * @param[in] dict_size Content length in bytes. * @param[in] huf_lengths Optional packed Huffman lengths (@c ZXC_HUF_TABLE_SIZE * bytes), or NULL to hash the content alone. * @return The 32-bit dictionary id, or 0 if @p dict is NULL or empty. */ uint32_t zxc_dict_id(const void* RESTRICT dict, const size_t dict_size, const void* RESTRICT huf_lengths) { if (UNLIKELY(!dict || dict_size == 0)) return 0; /* One logical hash over the real bytes only: the content checksum seeds * the table checksum (content and table are not contiguous at the API * level, so chaining avoids both a concat copy and a synthetic buffer). */ const uint32_t base = zxc_checksum(dict, dict_size, 0); if (huf_lengths == NULL) return base; return zxc_checksum_seed(huf_lengths, ZXC_HUF_TABLE_SIZE, base, 0); } /* ------------------------------------------------------------------------- * .zxd format: save / load / bound * * Layout (ZXC_DICT_HEADER_SIZE = 16 bytes + content + Huffman table): * 0x00 4 Magic (0x9CB0D1C7 LE) * 0x04 1 Version (1) * 0x05 1 Flags (bits 0-3: checksum algo id, 0=RapidHash; bits 4-7 reserved) * 0x06 2 Content size (u16 LE) * 0x08 4 dict_id (u32 LE; covers content AND the Huffman table -- * see zxc_dict_id) * 0x0C 2 Reserved (0) * 0x0E 2 Header CRC16 (zxc_hash16, computed with bytes 0x0C-0x0F zeroed) * 0x10 N Content bytes * +N 128 Packed Huffman code lengths (always present) * ------------------------------------------------------------------------- */ /** * @brief Extracts the stored @c dict_id from a serialized .zxd buffer. * * Public API; see @c zxc_dict.h. Validates the magic, then reads the id field * straight from the header without recomputing it. * * @param[in] buf Serialized .zxd bytes. * @param[in] buf_size Size of @p buf in bytes. * @return The stored dictionary id, or 0 if @p buf is too small or not a .zxd. */ uint32_t zxc_dict_get_id(const void* buf, const size_t buf_size) { if (UNLIKELY(!buf || buf_size < ZXC_DICT_HEADER_SIZE)) return 0; const uint8_t* p = (const uint8_t*)buf; if (UNLIKELY(zxc_le32(p) != ZXC_DICT_MAGIC)) return 0; return zxc_le32(p + 8); } /** * @brief Worst-case .zxd byte size for a dictionary of @p content_size bytes. * * Public API; see @c zxc_dict.h. Use it to size the buffer passed to * @ref zxc_dict_save. * * @param[in] content_size Dictionary content length in bytes. * @return Required buffer size: header + content + Huffman table. */ size_t zxc_dict_save_bound(const size_t content_size) { return ZXC_DICT_HEADER_SIZE + content_size + ZXC_HUF_TABLE_SIZE; } /** * @brief Serializes dictionary content + Huffman table into the .zxd format. * * Public API; full contract in @c zxc_dict.h. Writes the 16-byte header (magic, * version, sizes, dict_id, header CRC), then the content bytes, then the packed * Huffman lengths. See the on-disk layout note above. * * @param[in] content Dictionary content bytes. * @param[in] content_size Content length (<= @c ZXC_DICT_SIZE_MAX). * @param[in] huf_lengths Packed Huffman lengths (@c ZXC_HUF_TABLE_SIZE bytes). * @param[out] buf Destination .zxd buffer. * @param[in] buf_capacity Capacity of @p buf (>= @ref zxc_dict_save_bound). * @return Bytes written on success, or a negative @ref zxc_error_t * (@ref ZXC_ERROR_NULL_INPUT, @ref ZXC_ERROR_DICT_TOO_LARGE, * @ref ZXC_ERROR_DST_TOO_SMALL). */ int64_t zxc_dict_save(const void* RESTRICT content, const size_t content_size, const void* RESTRICT huf_lengths, void* RESTRICT buf, const size_t buf_capacity) { if (UNLIKELY(!content || content_size == 0 || !huf_lengths)) return ZXC_ERROR_NULL_INPUT; if (UNLIKELY(content_size > ZXC_DICT_SIZE_MAX)) return ZXC_ERROR_DICT_TOO_LARGE; const size_t total = ZXC_DICT_HEADER_SIZE + content_size + ZXC_HUF_TABLE_SIZE; if (UNLIKELY(buf_capacity < total)) return ZXC_ERROR_DST_TOO_SMALL; uint8_t* dst = (uint8_t*)buf; zxc_store_le32(dst + 0, ZXC_DICT_MAGIC); dst[4] = ZXC_DICT_VERSION; dst[5] = 0; /* flags: reserved */ zxc_store_le16(dst + 6, (uint16_t)content_size); zxc_store_le32(dst + 8, zxc_dict_id(content, content_size, (const uint8_t*)huf_lengths)); zxc_store_le32(dst + 12, 0); /* reserved (0x0C) + CRC16 (0x0E), zeroed before CRC */ const uint16_t crc = zxc_hash16(dst); zxc_store_le16(dst + 14, crc); ZXC_MEMCPY(dst + ZXC_DICT_HEADER_SIZE, content, content_size); ZXC_MEMCPY(dst + ZXC_DICT_HEADER_SIZE + content_size, huf_lengths, ZXC_HUF_TABLE_SIZE); return (int64_t)total; } /** * @brief Parses a .zxd buffer, returning in-buffer views of content and table. * * Public API; full contract in @c zxc_dict.h. Validates magic, version, header * CRC and dict_id, then points the out-params at the content and Huffman table * inside @p buf (no copy). The returned pointers alias @p buf and stay valid * only while it does. * * @param[in] buf Serialized .zxd bytes. * @param[in] buf_size Size of @p buf in bytes. * @param[out] content_out Receives a pointer to the content bytes. * @param[out] content_size_out Receives the content length. * @param[out] huf_out Receives a pointer to the Huffman table (optional). * @param[out] dict_id_out Receives the validated dict_id (optional). * @return @ref ZXC_OK, or a negative @ref zxc_error_t (bad magic / version / * header / checksum, or too small). */ int zxc_dict_load(const void* RESTRICT buf, const size_t buf_size, const void** RESTRICT content_out, size_t* RESTRICT content_size_out, const void** RESTRICT huf_out, uint32_t* RESTRICT dict_id_out) { if (UNLIKELY(!buf || !content_out || !content_size_out)) return ZXC_ERROR_NULL_INPUT; if (UNLIKELY(buf_size < ZXC_DICT_HEADER_SIZE)) return ZXC_ERROR_SRC_TOO_SMALL; const uint8_t* src = (const uint8_t*)buf; if (UNLIKELY(zxc_le32(src) != ZXC_DICT_MAGIC)) return ZXC_ERROR_BAD_MAGIC; if (UNLIKELY(src[4] != ZXC_DICT_VERSION)) return ZXC_ERROR_BAD_VERSION; const size_t content_size = zxc_le16(src + 6); if (UNLIKELY(content_size == 0)) return ZXC_ERROR_CORRUPT_DATA; if (UNLIKELY(content_size > ZXC_DICT_SIZE_MAX)) return ZXC_ERROR_DICT_TOO_LARGE; if (UNLIKELY(buf_size < ZXC_DICT_HEADER_SIZE + content_size + ZXC_HUF_TABLE_SIZE)) return ZXC_ERROR_SRC_TOO_SMALL; // LCOV_EXCL_LINE uint8_t temp[ZXC_DICT_HEADER_SIZE]; ZXC_MEMCPY(temp, src, sizeof(temp)); zxc_store_le32(temp + 12, 0); /* reserved (0x0C) + CRC16 (0x0E), zeroed before CRC */ const uint16_t expected_crc = zxc_hash16(temp); if (UNLIKELY(zxc_le16(src + 14) != expected_crc)) return ZXC_ERROR_BAD_HEADER; const uint8_t* content = src + ZXC_DICT_HEADER_SIZE; const uint8_t* huf = content + content_size; /* Verify dict_id matches the (content, table) pair. */ const uint32_t id = zxc_dict_id(content, content_size, huf); if (UNLIKELY(zxc_le32(src + 8) != id)) return ZXC_ERROR_BAD_CHECKSUM; *content_out = content; *content_size_out = content_size; if (huf_out) *huf_out = huf; if (dict_id_out) *dict_id_out = id; return ZXC_OK; } /** * @brief Returns a pointer to the packed Huffman table inside a .zxd buffer. * * Public API; see @c zxc_dict.h. Lightweight accessor (checks magic, version * and sizes) that locates the table without the full validation of * @ref zxc_dict_load. The returned pointer aliases @p buf. * * @param[in] buf Serialized .zxd bytes. * @param[in] buf_size Size of @p buf in bytes. * @return Pointer to the @c ZXC_HUF_TABLE_SIZE-byte table, or NULL if @p buf is * too small or not a valid .zxd. */ const void* zxc_dict_huf(const void* buf, const size_t buf_size) { if (UNLIKELY(!buf || buf_size < ZXC_DICT_HEADER_SIZE)) return NULL; const uint8_t* src = (const uint8_t*)buf; if (UNLIKELY(zxc_le32(src) != ZXC_DICT_MAGIC)) return NULL; if (UNLIKELY(src[4] != ZXC_DICT_VERSION)) return NULL; const size_t content_size = zxc_le16(src + 6); if (UNLIKELY(content_size == 0 || buf_size < ZXC_DICT_HEADER_SIZE + content_size + ZXC_HUF_TABLE_SIZE)) return NULL; // LCOV_EXCL_LINE return src + ZXC_DICT_HEADER_SIZE + content_size; } /* ------------------------------------------------------------------------- * Dictionary training: k-gram frequency selection * * Algorithm: * 1. Concatenate all samples into a corpus. * 2. For each position in the corpus, hash the k-gram (k = MIN_MATCH_LEN) * and count occurrences in a fixed-size hash map. * 3. Walk the corpus, building candidate segments: each starts at a frequent * k-gram and extends while neighbours stay frequent. A segment's score is * the summed frequency of its k-grams (its coverage of the corpus). * 4. Greedily fill the dictionary in descending coverage order, BUT account * for overlap: once a pattern is placed, a single copy serves all future * LZ matches, so its k-grams are zeroed in the frequency table. Segments * whose coverage has since collapsed (mostly already in the dict) are * skipped, so capacity goes to NEW patterns instead of redundant copies. * ------------------------------------------------------------------------- */ /** * @brief Hashes the k-gram at @p p into a frequency-table bucket index. * * Training-internal: a multiplicative hash of the @c ZXC_DICT_KGRAM_LEN-byte * k-gram, folded down to @c ZXC_DICT_HASH_BITS. * * @param[in] p Pointer to at least @c ZXC_DICT_KGRAM_LEN readable bytes. * @return Bucket index in [0, @c ZXC_DICT_HASH_SIZE). */ static uint32_t zxc_dict_hash(const uint8_t* p) { uint32_t v = zxc_le32(p); v ^= (uint32_t)p[4]; return (v * ZXC_LZ_HASH_PRIME1) >> (32 - ZXC_DICT_HASH_BITS); } /** * @brief Segment descriptor for dictionary training, scored by coverage. */ typedef struct { uint32_t offset; /**< Offset of the segment in the corpus. */ uint16_t length; /**< Length of the segment. */ uint32_t score; /**< Summed k-gram frequency (coverage) of the segment. */ } zxc_dict_seg_t; /** * @brief Restore the min-heap property at @p root over the range @p a[0..n). * * Sinks @p a[root] down the binary heap (children at @c 2i+1 / @c 2i+2) until * both children are @c >= it, comparing on @ref zxc_dict_seg_t::score. The loop * is iterative (no recursion), so the call stack stays O(1) regardless of @p n. * * @param[in,out] a Heap-ordered array; @p a[0..n) is treated as the heap. * @param[in] root Index of the element to sift down. Must be @c < n. * @param[in] n Number of valid elements in the heap. * * @note Complexity O(log n). */ static void zxc_dict_sift_down(zxc_dict_seg_t* RESTRICT a, size_t root, const size_t n) { for (;;) { size_t child = 2 * root + 1; if (child >= n) break; if (child + 1 < n && a[child + 1].score < a[child].score) child++; if (a[root].score <= a[child].score) break; const zxc_dict_seg_t t = a[root]; a[root] = a[child]; a[child] = t; root = child; } } /** * @brief Sort @p a[0..n) by @ref zxc_dict_seg_t::score in descending order. * * In-place heapsort: a min-heap is built over the whole array, then each * extracted minimum is swapped to the shrinking tail. Because the smallest * scores accumulate at the end, the array is left in descending order * (largest score at index 0), as required by the dictionary fill step. * * Replaces a libc @c qsort call for two reasons: * - **Freestanding/kernel-safe**: no dependency on @c qsort and no indirect * comparator call (the @c score comparison is inlined in @ref * zxc_dict_sift_down). * - **Deterministic**: ordering is fixed by this code rather than by the * platform's @c qsort, which matters for reproducible dictionary output * across libc implementations. * * Equal scores keep an unspecified-but-deterministic relative order, matching * the previous comparator that returned 0 on ties (heapsort is not stable). * * @param[in,out] a Array of @p n segments, sorted in place. * @param[in] n Number of segments. @c n < 2 is a no-op. * * @note Complexity O(n log n) worst case with no extra allocation. In practice * this matches or beats @c qsort on the sizes seen here (up to ~65536 * segments): eliminating the per-comparison indirect call outweighs * heapsort's weaker cache locality. This is a cold path (dictionary * training), so absolute speed is not critical. */ static void zxc_dict_sort_segs_desc(zxc_dict_seg_t* RESTRICT a, const size_t n) { if (UNLIKELY(n < 2)) return; for (size_t i = n / 2; i-- > 0;) zxc_dict_sift_down(a, i, n); for (size_t end = n; end > 1;) { end--; const zxc_dict_seg_t t = a[0]; a[0] = a[end]; a[end] = t; zxc_dict_sift_down(a, 0, end); } } /** * @brief Trains a raw dictionary from sample buffers by k-gram coverage. * * Public API; full contract in @c zxc_dict.h, algorithm in the note above. * Concatenates the samples, counts sampled k-gram frequencies, builds * coverage-scored candidate segments, then greedily fills @p dict_buf in * descending coverage while zeroing each pick's k-grams (so overlapping * patterns aren't copied twice). Picks are emitted in reverse so the * highest-coverage bytes land at the dict's end (smallest match offsets). * * @param[in] samples Array of @p n_samples sample buffers. * @param[in] sample_sizes Array of @p n_samples sample lengths. * @param[in] n_samples Number of samples. * @param[out] dict_buf Destination for the trained content. * @param[in] dict_capacity Capacity of @p dict_buf (<= @c ZXC_DICT_SIZE_MAX). * @return Trained content length in bytes, or a negative @ref zxc_error_t. */ int64_t zxc_train_dict(const void* const* RESTRICT samples, const size_t* RESTRICT sample_sizes, const size_t n_samples, void* RESTRICT dict_buf, const size_t dict_capacity) { if (UNLIKELY(!samples || !sample_sizes || n_samples == 0 || !dict_buf || dict_capacity == 0)) return ZXC_ERROR_NULL_INPUT; // LCOV_EXCL_LINE if (UNLIKELY(dict_capacity > ZXC_DICT_SIZE_MAX)) return ZXC_ERROR_DICT_TOO_LARGE; /* Step 1: concatenate samples */ size_t corpus_size = 0; for (size_t i = 0; i < n_samples; i++) corpus_size += sample_sizes[i]; if (UNLIKELY(corpus_size < ZXC_DICT_KGRAM_LEN)) return ZXC_ERROR_SRC_TOO_SMALL; uint8_t* corpus = (uint8_t*)ZXC_MALLOC(corpus_size); if (UNLIKELY(!corpus)) return ZXC_ERROR_MEMORY; { size_t pos = 0; for (size_t i = 0; i < n_samples; i++) { if (sample_sizes[i] > 0) ZXC_MEMCPY(corpus + pos, samples[i], sample_sizes[i]); pos += sample_sizes[i]; } } /* Step 2: count k-gram frequencies */ uint16_t* freq = (uint16_t*)ZXC_MALLOC(ZXC_DICT_HASH_SIZE * sizeof(uint16_t)); if (UNLIKELY(!freq)) { // LCOV_EXCL_START ZXC_FREE(corpus); return ZXC_ERROR_MEMORY; // LCOV_EXCL_STOP } ZXC_MEMSET(freq, 0, ZXC_DICT_HASH_SIZE * sizeof(uint16_t)); /* Count k-gram frequencies on a representative sample of positions, not all * of them: counting a large corpus in full saturates the 16-bit counters, * so the segment-extension test never stops and segments balloon into * filler. Sampling keeps counts unsaturated and spread across the corpus. */ const size_t kgram_limit = corpus_size - ZXC_DICT_KGRAM_LEN + 1; size_t freq_stride = kgram_limit / ZXC_DICT_SAMPLE_TARGET; if (freq_stride < 1) freq_stride = 1; for (size_t i = 0; i < kgram_limit; i += freq_stride) { const uint32_t h = zxc_dict_hash(corpus + i); if (freq[h] < UINT16_MAX) freq[h]++; } /* Step 3: build candidate segments, each scored by its coverage. Spread the * candidate starts across the whole corpus: a fixed k-gram stride exhausts * the segment budget within the prefix, leaving a large input's later * content unseen. Segments still extend k-gram by k-gram, so they stay * contiguous. */ const size_t max_segs = corpus_size / ZXC_DICT_KGRAM_LEN; const size_t seg_alloc = (max_segs < ZXC_DICT_MAX_SEGMENTS) ? max_segs : ZXC_DICT_MAX_SEGMENTS; size_t stride = ZXC_DICT_KGRAM_LEN; if (seg_alloc > 0 && corpus_size / seg_alloc > stride) stride = corpus_size / seg_alloc; zxc_dict_seg_t* segs = (zxc_dict_seg_t*)ZXC_MALLOC(seg_alloc * sizeof(zxc_dict_seg_t)); if (UNLIKELY(!segs)) { // LCOV_EXCL_START ZXC_FREE(freq); ZXC_FREE(corpus); return ZXC_ERROR_MEMORY; // LCOV_EXCL_STOP } size_t n_segs = 0; for (size_t i = 0; i + ZXC_DICT_KGRAM_LEN <= corpus_size && n_segs < seg_alloc; i += stride) { const uint32_t h = zxc_dict_hash(corpus + i); const uint16_t f = freq[h]; if (f < 2) continue; /* Extend the segment as long as the next k-gram is also frequent, and * accumulate coverage (summed k-gram frequency) as the score. */ uint32_t coverage = f; size_t end = i + ZXC_DICT_KGRAM_LEN; while (end + ZXC_DICT_KGRAM_LEN <= corpus_size && end - i < 4096) { const uint16_t nf = freq[zxc_dict_hash(corpus + end)]; if (nf < 2) break; coverage += nf; end += ZXC_DICT_KGRAM_LEN; } segs[n_segs].offset = (uint32_t)i; segs[n_segs].length = (uint16_t)(end - i); segs[n_segs].score = coverage; n_segs++; } if (UNLIKELY(n_segs == 0)) { /* No frequent patterns. Use tail of corpus as dict. */ const size_t copy = (corpus_size < dict_capacity) ? corpus_size : dict_capacity; ZXC_MEMCPY(dict_buf, corpus + corpus_size - copy, copy); ZXC_FREE(freq); ZXC_FREE(segs); ZXC_FREE(corpus); return (int64_t)copy; } /* Step 4: pick segments greedily in descending-coverage order, zeroing each * pick's k-grams so overlapping patterns aren't copied twice. Picks are * compacted in place into segs[0..n_sel); placement is step 5. */ zxc_dict_sort_segs_desc(segs, n_segs); uint8_t* out = (uint8_t*)dict_buf; size_t n_sel = 0; size_t total = 0; for (size_t i = 0; i < n_segs && total < dict_capacity; i++) { const size_t seg_off = segs[i].offset; const size_t seg_end = seg_off + segs[i].length; /* Recompute coverage from the decrementing table: skip the segment if * earlier picks have already covered more than half of its k-grams. */ uint32_t cur = 0; for (size_t p = seg_off; p + ZXC_DICT_KGRAM_LEN <= seg_end; p += ZXC_DICT_KGRAM_LEN) cur += freq[zxc_dict_hash(corpus + p)]; if (cur * 2 < segs[i].score) continue; size_t copy = segs[i].length; if (copy > dict_capacity - total) copy = dict_capacity - total; /* One copy in the dictionary serves all future matches: mark this * segment's k-grams as covered so later segments cover new ground. */ for (size_t p = seg_off; p + ZXC_DICT_KGRAM_LEN <= seg_end; p += ZXC_DICT_KGRAM_LEN) freq[zxc_dict_hash(corpus + p)] = 0; /* Record the pick (n_sel <= i, so this never clobbers an unread entry). */ segs[n_sel].offset = (uint32_t)seg_off; segs[n_sel].length = (uint16_t)copy; n_sel++; total += copy; } ZXC_FREE(freq); /* Step 5: emit picks in reverse order so the highest-coverage segment ends * up at the END of the dict. The dict sits just before the data, so bytes * nearer its end have the smallest match offset: cheapest to encode and the * last to leave the 16-bit (65535) offset window. * * No padding: if the picks don't fill the capacity, the dict is just * shorter. The old tail-padding only added low-value bytes that raised * offsets for everything after them. */ size_t filled = 0; for (size_t i = n_sel; i-- > 0;) { ZXC_MEMCPY(out + filled, corpus + segs[i].offset, segs[i].length); filled += segs[i].length; } /* Nothing selected (every segment subsumed by earlier picks): fall back to * the corpus tail so the dict is never empty, like the n_segs == 0 path. */ if (UNLIKELY(filled == 0)) { const size_t tail = (corpus_size < dict_capacity) ? corpus_size : dict_capacity; ZXC_MEMCPY(out, corpus + corpus_size - tail, tail); filled = tail; } ZXC_FREE(segs); ZXC_FREE(corpus); return (int64_t)filled; } /* ------------------------------------------------------------------------- * Shared literal Huffman table training (Tier-2) * * Compresses the training samples with the freshly trained dictionary at * level ZXC_LEVEL_DENSITY and accumulates the frequencies of the REAL * post-LZ literals (via the cctx lit_freq_acc hook in the GLO encoder). * Raw sample bytes are a poor proxy: LZ matches against the dictionary * remove most repeated content, so the literal distribution differs * substantially from the raw byte histogram. * * Samples are sliced into ZXC_DICT_HUF_TRAIN_BLOCK-byte blocks: the small- * block regime is where the shared table pays (per-block table headers are * unaffordable there) and literal density is highest. * ------------------------------------------------------------------------- */ /** * @brief Trains the shared literal Huffman table for a dictionary (Tier-2). * * Public API; see @c zxc_dict.h and the algorithm note above. Compresses the * samples with @p dict at @c ZXC_LEVEL_DENSITY, accumulating the frequencies of * the REAL post-LZ literals (raw bytes are a poor proxy), then builds and packs * length-limited code lengths. Samples are sliced into * @c ZXC_DICT_HUF_TRAIN_BLOCK blocks, sub-sampled to a fixed budget. * * @param[in] samples Array of @p n_samples sample buffers. * @param[in] sample_sizes Array of @p n_samples sample lengths. * @param[in] n_samples Number of samples. * @param[in] dict Trained dictionary content. * @param[in] dict_size Dictionary length (<= @c ZXC_DICT_SIZE_MAX). * @param[out] huf_lengths_out Receives the @c ZXC_HUF_TABLE_SIZE packed lengths. * @return @ref ZXC_OK, or a negative @ref zxc_error_t. */ int zxc_train_dict_huf(const void* const* RESTRICT samples, const size_t* RESTRICT sample_sizes, const size_t n_samples, const void* RESTRICT dict, const size_t dict_size, uint8_t* RESTRICT huf_lengths_out) { if (UNLIKELY(!samples || !sample_sizes || n_samples == 0 || !dict || dict_size == 0 || !huf_lengths_out)) return ZXC_ERROR_NULL_INPUT; if (UNLIKELY(dict_size > ZXC_DICT_SIZE_MAX)) return ZXC_ERROR_DICT_TOO_LARGE; const size_t eff_chunk = zxc_block_size_ceil(dict_size + ZXC_DICT_HUF_TRAIN_BLOCK); zxc_cctx_t cctx; if (UNLIKELY(zxc_cctx_init(&cctx, eff_chunk, 1, ZXC_LEVEL_DENSITY, 0, dict_size) != ZXC_OK)) return ZXC_ERROR_MEMORY; // LCOV_EXCL_LINE uint32_t freq[ZXC_HUF_NUM_SYMBOLS] = {0}; cctx.lit_freq_acc = freq; const size_t out_cap = (size_t)zxc_compress_bound(eff_chunk); uint8_t* out_scratch = (uint8_t*)ZXC_MALLOC(out_cap); if (UNLIKELY(!out_scratch)) { // LCOV_EXCL_START zxc_cctx_free(&cctx); return ZXC_ERROR_MEMORY; // LCOV_EXCL_STOP } /* [dict | slice] concat scratch carved by zxc_cctx_init (mode 1). */ uint8_t* const work = cctx.dict_buffer; ZXC_MEMCPY(work, dict, dict_size); /* Slice stride: process every slice while the corpus fits the budget, * else 1 slice out of `stride` spread evenly across all samples. */ size_t corpus_total = 0; for (size_t s = 0; s < n_samples; s++) corpus_total += sample_sizes[s]; const size_t stride = (corpus_total > ZXC_DICT_HUF_SAMPLE_BUDGET) ? (corpus_total + ZXC_DICT_HUF_SAMPLE_BUDGET - 1) / ZXC_DICT_HUF_SAMPLE_BUDGET : 1; int rc = ZXC_OK; size_t slice_idx = 0; for (size_t s = 0; s < n_samples && rc == ZXC_OK; s++) { const uint8_t* sample = (const uint8_t*)samples[s]; const size_t sample_size = sample_sizes[s]; if (!sample || sample_size == 0) continue; for (size_t off = 0; off < sample_size; off += ZXC_DICT_HUF_TRAIN_BLOCK, slice_idx++) { if (slice_idx % stride != 0) continue; const size_t slice = (sample_size - off < ZXC_DICT_HUF_TRAIN_BLOCK) ? (sample_size - off) : ZXC_DICT_HUF_TRAIN_BLOCK; ZXC_MEMCPY(work + dict_size, sample + off, slice); const int r = zxc_compress_chunk_wrapper(&cctx, work, dict_size + slice, out_scratch, out_cap); if (UNLIKELY(r < 0)) { rc = r; break; } } } if (rc == ZXC_OK) { /* A low-entropy corpus leaves no post-LZ literals: an empty histogram, * not corrupt input. Detect that on the histogram itself (OR-reduce) and * emit an empty all-zero table: every block then falls back to its * per-block table. Inferring "empty" from a build error code instead * would risk masking a genuine failure. */ uint32_t any = 0; for (int i = 0; i < ZXC_HUF_NUM_SYMBOLS; i++) any |= freq[i]; if (any == 0) { ZXC_MEMSET(huf_lengths_out, 0, ZXC_HUF_TABLE_SIZE); } else { /* No coverage smoothing: with 8-bit cap, a code over all 256 symbols * can only be the degenerate all-8-bit code (Kraft equality), * which compresses nothing. Symbols unseen in training * stay code-less; blocks containing one fall back to their per-block * table at compression time (the encoder's validity check). */ uint8_t code_len[ZXC_HUF_NUM_SYMBOLS]; rc = zxc_huf_build_code_lengths(freq, code_len, NULL, ZXC_HUF_MAX_CODE_LEN_DENSITY); if (rc == ZXC_OK) zxc_huf_pack_lengths(code_len, huf_lengths_out); } } ZXC_FREE(out_scratch); zxc_cctx_free(&cctx); return rc; } /* ------------------------------------------------------------------------- * All-in-one convenience: samples -> ready-to-write .zxd bytes * ------------------------------------------------------------------------- */ /** * @brief One-shot training: sample buffers in, ready-to-write .zxd bytes out. * * Public API; see @c zxc_dict.h. Convenience wrapper that trains the content * (@ref zxc_train_dict), then the shared Huffman table from that content * (@ref zxc_train_dict_huf), then serializes both via @ref zxc_dict_save. The * two phases are a real data dependency, hidden behind one call. * * @param[in] samples Array of @p n_samples sample buffers. * @param[in] sample_sizes Array of @p n_samples sample lengths. * @param[in] n_samples Number of samples. * @param[out] zxd_buf Destination .zxd buffer. * @param[in] zxd_capacity Capacity of @p zxd_buf in bytes. * @return Bytes written to @p zxd_buf, or a negative @ref zxc_error_t. */ int64_t zxc_dict_train(const void* const* RESTRICT samples, const size_t* RESTRICT sample_sizes, const size_t n_samples, void* RESTRICT zxd_buf, const size_t zxd_capacity) { if (UNLIKELY(!samples || !sample_sizes || n_samples == 0 || !zxd_buf || zxd_capacity == 0)) return ZXC_ERROR_NULL_INPUT; /* Train the content into a temporary buffer (max-content sized), then the * shared table from that content, then serialize both into zxd_buf. The * two-phase training is a real data dependency (the table needs the trained * content to histogram post-LZ literals); this hides it behind one call. */ uint8_t* content = (uint8_t*)ZXC_MALLOC(ZXC_DICT_SIZE_MAX); if (UNLIKELY(!content)) return ZXC_ERROR_MEMORY; int64_t out; const int64_t content_size = zxc_train_dict(samples, sample_sizes, n_samples, content, ZXC_DICT_SIZE_MAX); if (UNLIKELY(content_size <= 0)) { // LCOV_EXCL_START out = (content_size < 0) ? content_size : ZXC_ERROR_SRC_TOO_SMALL; goto done; // LCOV_EXCL_STOP } { uint8_t huf[ZXC_HUF_TABLE_SIZE]; const int hrc = zxc_train_dict_huf(samples, sample_sizes, n_samples, content, (size_t)content_size, huf); if (UNLIKELY(hrc != ZXC_OK)) { // LCOV_EXCL_START out = hrc; goto done; // LCOV_EXCL_STOP } out = zxc_dict_save(content, (size_t)content_size, huf, zxd_buf, zxd_capacity); } done: ZXC_FREE(content); return out; } zxc-0.13.0/src/lib/zxc_dispatch.c000066400000000000000000002574601522536370400166100ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file zxc_dispatch.c * @brief Runtime CPU feature detection and SIMD dispatch layer. * * Detects AVX2/AVX512/NEON at runtime and routes compress/decompress calls * to the best available implementation via lazy-initialised function pointers. * Also contains the public one-shot buffer API (@ref zxc_compress, * @ref zxc_decompress, @ref zxc_get_decompressed_size). */ #include "../../include/zxc_dict.h" #include "../../include/zxc_error.h" #include "../../include/zxc_seekable.h" #include "zxc_internal.h" /* * ZXC_DISABLE_SIMD => force ZXC_ONLY_DEFAULT so the dispatcher never selects * an AVX2/AVX512/NEON variant. */ #if defined(ZXC_DISABLE_SIMD) && !defined(ZXC_ONLY_DEFAULT) #define ZXC_ONLY_DEFAULT #endif #if defined(_MSC_VER) #include #if defined(_M_X64) #include // _xgetbv (x86-specific header; x64 AVX state check) #endif #endif #if defined(__linux__) && (defined(__arm__) || defined(_M_ARM)) #include #include #endif /* * ============================================================================ * PROTOTYPES FOR MULTI-VERSIONED VARIANTS * ============================================================================ * These are compiled in separate translation units with different flags. */ // Decompression Prototypes int zxc_decompress_chunk_wrapper_default(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap); int zxc_decompress_chunk_wrapper_dict_default(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap); int zxc_decompress_chunk_wrapper_safe_default(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap); #ifndef ZXC_ONLY_DEFAULT #if defined(__x86_64__) || defined(_M_X64) int zxc_decompress_chunk_wrapper_avx2(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap); int zxc_decompress_chunk_wrapper_dict_avx2(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap); int zxc_decompress_chunk_wrapper_avx512(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap); int zxc_decompress_chunk_wrapper_dict_avx512(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap); int zxc_decompress_chunk_wrapper_safe_avx2(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap); int zxc_decompress_chunk_wrapper_safe_avx512(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap); int zxc_decompress_chunk_wrapper_sse2(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap); int zxc_decompress_chunk_wrapper_dict_sse2(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap); int zxc_decompress_chunk_wrapper_safe_sse2(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap); #elif defined(__aarch64__) || defined(_M_ARM64) || defined(__arm__) || defined(_M_ARM) int zxc_decompress_chunk_wrapper_neon(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap); int zxc_decompress_chunk_wrapper_dict_neon(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap); int zxc_decompress_chunk_wrapper_safe_neon(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap); #endif #endif // Compression Prototypes int zxc_compress_chunk_wrapper_default(zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap); // Huffman Prototypes (variant TUs of zxc_huffman.c). The compressor and // decompressor variants resolve their Huffman calls to the matching suffixed // symbol at compile time (zero dispatch overhead in the hot path); the thin // wrappers below expose the un-suffixed names for tests and external callers. int zxc_huf_build_code_lengths_default(const uint32_t* RESTRICT freq, uint8_t* RESTRICT code_len, void* RESTRICT scratch, int max_code_len); size_t zxc_huf_calc_size_default(const uint32_t* RESTRICT freq, const uint8_t* RESTRICT code_len, int with_header); int zxc_huf_encode_section_default(const uint8_t* RESTRICT literals, size_t n_literals, const uint32_t* RESTRICT freq, const uint8_t* RESTRICT code_len, uint8_t* RESTRICT dst, size_t dst_cap); int zxc_huf_decode_section_default(const uint8_t* RESTRICT payload, size_t payload_size, uint8_t* RESTRICT dst, size_t n, uint8_t* RESTRICT scratch); int zxc_huf_encode_section_dict_default(const uint8_t* RESTRICT literals, size_t n_literals, const uint32_t* RESTRICT freq, const uint8_t* RESTRICT code_len, const zxc_pivco_tree_t* RESTRICT tree, const uint32_t* RESTRICT codes, uint8_t* RESTRICT dst, size_t dst_cap); int zxc_huf_decode_section_dict_default(const uint8_t* RESTRICT payload, size_t payload_size, uint8_t* RESTRICT dst, size_t n, const zxc_pivco_tree_t* RESTRICT tree, const zxc_pivco_decode_aux_t* RESTRICT aux, uint8_t* RESTRICT scratch); int zxc_huf_dict_tree_build_default(const uint8_t* RESTRICT packed_lengths, zxc_pivco_tree_t* RESTRICT tree, uint32_t* RESTRICT codes, uint8_t* RESTRICT code_len, zxc_pivco_decode_aux_t* RESTRICT aux); size_t zxc_huf_calc_size_dict_default(const uint32_t* RESTRICT freq, const uint8_t* RESTRICT code_len, const zxc_pivco_tree_t* RESTRICT tree); void zxc_huf_pack_lengths_default(const uint8_t* RESTRICT code_len, uint8_t* RESTRICT out); int zxc_huf_unpack_lengths_default(const uint8_t* RESTRICT in, uint8_t* RESTRICT code_len); #if defined(__x86_64__) || defined(_M_X64) int zxc_compress_chunk_wrapper_avx2(zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap); int zxc_compress_chunk_wrapper_avx512(zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap); int zxc_compress_chunk_wrapper_sse2(zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap); #elif defined(__aarch64__) || defined(_M_ARM64) || defined(__arm__) || defined(_M_ARM) int zxc_compress_chunk_wrapper_neon(zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap); #endif /* * ============================================================================ * CPU DETECTION LOGIC * ============================================================================ */ /** * @enum zxc_cpu_feature_t * @brief Detected CPU SIMD capability level. */ typedef enum { ZXC_CPU_GENERIC = 0, /**< @brief Scalar-only fallback. */ ZXC_CPU_AVX2 = 1, /**< @brief x86-64 AVX2 available. */ ZXC_CPU_AVX512 = 2, /**< @brief x86-64 AVX-512F+BW available. */ ZXC_CPU_NEON = 3, /**< @brief ARM NEON available. */ ZXC_CPU_SSE2 = 4 /**< @brief x86 SSE2 available (no AVX2); x86-64 baseline. */ } zxc_cpu_feature_t; /** * @brief Probes the running CPU for SIMD support. * * Uses CPUID on x86-64 (MSVC and GCC/Clang paths), `getauxval` on * 32-bit ARM Linux, and compile-time constants on AArch64. * * @return The highest @ref zxc_cpu_feature_t level supported. */ // LCOV_EXCL_START static zxc_cpu_feature_t zxc_detect_cpu_features(void) { #ifdef ZXC_ONLY_DEFAULT return ZXC_CPU_GENERIC; #else zxc_cpu_feature_t features = ZXC_CPU_GENERIC; #if defined(__x86_64__) || defined(_M_X64) #if defined(_MSC_VER) // AVX2/AVX512 need OS-enabled YMM/ZMM state: gate on OSXSAVE + XGETBV/XCR0, // not CPUID alone (else a VEX/EVEX op faults #UD when the OS hasn't enabled it). int regs[4]; int sse2 = 0; int avx2 = 0; int avx512 = 0; __cpuid(regs, 1); if (regs[3] & (1 << 26)) sse2 = 1; // SSE2 if (regs[2] & (1 << 27)) { // OSXSAVE const unsigned long long xcr0 = _xgetbv(0); if ((xcr0 & 0x6) == 0x6) { // SSE+YMM enabled __cpuidex(regs, 7, 0); if (regs[1] & (1 << 5)) avx2 = 1; // AVX512 also needs XCR0[5..7] (opmask/ZMM) if ((regs[1] & (1 << 16)) && (regs[1] & (1 << 30)) && (regs[2] & (1 << 6)) && (xcr0 & 0xE0) == 0xE0) avx512 = 1; /* AVX512 tier = F+BW+VBMI2 (variant built with -mavx512vbmi2) */ } } if (avx512) { features = ZXC_CPU_AVX512; } else if (avx2) { features = ZXC_CPU_AVX2; } else if (sse2) { features = ZXC_CPU_SSE2; } #else // GCC/Clang built-in detection __builtin_cpu_init(); if (__builtin_cpu_supports("avx512f") && __builtin_cpu_supports("avx512bw") && __builtin_cpu_supports("avx512vbmi2")) { features = ZXC_CPU_AVX512; } else if (__builtin_cpu_supports("avx2")) { features = ZXC_CPU_AVX2; } else if (__builtin_cpu_supports("sse2")) { features = ZXC_CPU_SSE2; } #endif #elif defined(__aarch64__) || defined(_M_ARM64) // ARM64 usually guarantees NEON features = ZXC_CPU_NEON; #elif defined(__arm__) || defined(_M_ARM) // ARM32 Runtime detection for Linux #if defined(__linux__) const unsigned long hwcaps = getauxval(AT_HWCAP); if (hwcaps & HWCAP_NEON) { features = ZXC_CPU_NEON; } #else // Fallback for non-Linux: rely on compiler flags. // If compiled with -mfpu=neon, we assume target supports it. // Otherwise, safe default is GENERIC. #if defined(__ARM_NEON) features = ZXC_CPU_NEON; #endif #endif #endif return features; #endif } // LCOV_EXCL_STOP /* * ============================================================================ * DISPATCHERS * ============================================================================ * We use a function pointer initialized on first use (lazy initialization). */ /** @brief Function pointer type for the chunk decompressor. */ typedef int (*zxc_decompress_func_t)(const zxc_cctx_t* RESTRICT, const uint8_t* RESTRICT, const size_t, uint8_t* RESTRICT, const size_t); /** @brief Function pointer type for the chunk compressor. */ typedef int (*zxc_compress_func_t)(zxc_cctx_t* RESTRICT, const uint8_t* RESTRICT, const size_t, uint8_t* RESTRICT, const size_t); /** @brief Lazily-resolved pointer to the best decompression variant. */ static ZXC_ATOMIC zxc_decompress_func_t zxc_decompress_ptr = (zxc_decompress_func_t)0; /** @brief Lazily-resolved pointer to the best dict-decompression variant. */ static ZXC_ATOMIC zxc_decompress_func_t zxc_decompress_dict_ptr = (zxc_decompress_func_t)0; /** @brief Lazily-resolved pointer to the best safe-decompression variant. */ static ZXC_ATOMIC zxc_decompress_func_t zxc_decompress_safe_ptr = (zxc_decompress_func_t)0; /** @brief Lazily-resolved pointer to the best compression variant. */ static ZXC_ATOMIC zxc_compress_func_t zxc_compress_ptr = (zxc_compress_func_t)0; /** * @brief First-call initialiser for the decompression dispatcher. * * Detects CPU features, selects the best implementation, stores the * pointer atomically, then tail-calls into it. * * @param[in] ctx Decompression context (its @c dict_size picks the dict variant). * @param[in] src Compressed input chunk. * @param[in] src_sz Size of @p src in bytes. * @param[out] dst Destination buffer for decompressed data. * @param[in] dst_cap Capacity of @p dst in bytes. * @return Result of the selected variant: decompressed size, or negative * @ref zxc_error_t. */ // LCOV_EXCL_START static int zxc_decompress_dispatch_init(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap) { const zxc_cpu_feature_t cpu = zxc_detect_cpu_features(); zxc_decompress_func_t zxc_decompress_ptr_local = NULL; zxc_decompress_func_t zxc_decompress_dict_ptr_local = NULL; #ifndef ZXC_ONLY_DEFAULT #if defined(__x86_64__) || defined(_M_X64) if (cpu == ZXC_CPU_AVX512) { zxc_decompress_ptr_local = zxc_decompress_chunk_wrapper_avx512; zxc_decompress_dict_ptr_local = zxc_decompress_chunk_wrapper_dict_avx512; } else if (cpu == ZXC_CPU_AVX2) { zxc_decompress_ptr_local = zxc_decompress_chunk_wrapper_avx2; zxc_decompress_dict_ptr_local = zxc_decompress_chunk_wrapper_dict_avx2; } else if (cpu == ZXC_CPU_SSE2) { zxc_decompress_ptr_local = zxc_decompress_chunk_wrapper_sse2; zxc_decompress_dict_ptr_local = zxc_decompress_chunk_wrapper_dict_sse2; } else { zxc_decompress_ptr_local = zxc_decompress_chunk_wrapper_default; zxc_decompress_dict_ptr_local = zxc_decompress_chunk_wrapper_dict_default; } #elif defined(__aarch64__) || defined(_M_ARM64) || defined(__arm__) || defined(_M_ARM) // cppcheck-suppress knownConditionTrueFalse if (cpu == ZXC_CPU_NEON) { zxc_decompress_ptr_local = zxc_decompress_chunk_wrapper_neon; zxc_decompress_dict_ptr_local = zxc_decompress_chunk_wrapper_dict_neon; } else { zxc_decompress_ptr_local = zxc_decompress_chunk_wrapper_default; zxc_decompress_dict_ptr_local = zxc_decompress_chunk_wrapper_dict_default; } #else (void)cpu; zxc_decompress_ptr_local = zxc_decompress_chunk_wrapper_default; zxc_decompress_dict_ptr_local = zxc_decompress_chunk_wrapper_dict_default; #endif #else (void)cpu; zxc_decompress_ptr_local = zxc_decompress_chunk_wrapper_default; zxc_decompress_dict_ptr_local = zxc_decompress_chunk_wrapper_dict_default; #endif #if ZXC_USE_C11_ATOMICS atomic_store_explicit(&zxc_decompress_ptr, zxc_decompress_ptr_local, memory_order_release); atomic_store_explicit(&zxc_decompress_dict_ptr, zxc_decompress_dict_ptr_local, memory_order_release); #else zxc_decompress_ptr = zxc_decompress_ptr_local; zxc_decompress_dict_ptr = zxc_decompress_dict_ptr_local; #endif return (ctx->dict_size ? zxc_decompress_dict_ptr_local : zxc_decompress_ptr_local)( ctx, src, src_sz, dst, dst_cap); } // LCOV_EXCL_STOP /** * @brief First-call initialiser for the safe-decompression dispatcher. * * Mirrors @ref zxc_decompress_dispatch_init but selects the `_safe_*` * decoder variants used by @ref zxc_decompress_block_safe. * * @param[in] ctx Decompression context. * @param[in] src Compressed input chunk. * @param[in] src_sz Size of @p src in bytes. * @param[out] dst Destination buffer (strict: exact uncompressed size). * @param[in] dst_cap Capacity of @p dst in bytes. * @return Result of the selected variant: decompressed size, or negative * @ref zxc_error_t. */ // LCOV_EXCL_START static int zxc_decompress_safe_dispatch_init(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap) { const zxc_cpu_feature_t cpu = zxc_detect_cpu_features(); zxc_decompress_func_t zxc_decompress_safe_ptr_local = NULL; #ifndef ZXC_ONLY_DEFAULT #if defined(__x86_64__) || defined(_M_X64) if (cpu == ZXC_CPU_AVX512) zxc_decompress_safe_ptr_local = zxc_decompress_chunk_wrapper_safe_avx512; else if (cpu == ZXC_CPU_AVX2) zxc_decompress_safe_ptr_local = zxc_decompress_chunk_wrapper_safe_avx2; else if (cpu == ZXC_CPU_SSE2) zxc_decompress_safe_ptr_local = zxc_decompress_chunk_wrapper_safe_sse2; else zxc_decompress_safe_ptr_local = zxc_decompress_chunk_wrapper_safe_default; #elif defined(__aarch64__) || defined(_M_ARM64) || defined(__arm__) || defined(_M_ARM) // cppcheck-suppress knownConditionTrueFalse if (cpu == ZXC_CPU_NEON) zxc_decompress_safe_ptr_local = zxc_decompress_chunk_wrapper_safe_neon; else zxc_decompress_safe_ptr_local = zxc_decompress_chunk_wrapper_safe_default; #else (void)cpu; zxc_decompress_safe_ptr_local = zxc_decompress_chunk_wrapper_safe_default; #endif #else (void)cpu; zxc_decompress_safe_ptr_local = zxc_decompress_chunk_wrapper_safe_default; #endif #if ZXC_USE_C11_ATOMICS atomic_store_explicit(&zxc_decompress_safe_ptr, zxc_decompress_safe_ptr_local, memory_order_release); #else zxc_decompress_safe_ptr = zxc_decompress_safe_ptr_local; #endif return zxc_decompress_safe_ptr_local(ctx, src, src_sz, dst, dst_cap); } // LCOV_EXCL_STOP /** * @brief First-call initialiser for the compression dispatcher. * * Detects CPU features, selects the best implementation, stores the * pointer atomically, then tail-calls into it. * * @param[in,out] ctx Compression context. * @param[in] src Uncompressed input chunk. * @param[in] src_sz Size of @p src in bytes. * @param[out] dst Destination buffer for the compressed chunk. * @param[in] dst_cap Capacity of @p dst in bytes. * @return Result of the selected variant: compressed size, or negative * @ref zxc_error_t. */ // LCOV_EXCL_START static int zxc_compress_dispatch_init(zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap) { const zxc_cpu_feature_t cpu = zxc_detect_cpu_features(); zxc_compress_func_t zxc_compress_ptr_local = NULL; #ifndef ZXC_ONLY_DEFAULT #if defined(__x86_64__) || defined(_M_X64) if (cpu == ZXC_CPU_AVX512) zxc_compress_ptr_local = zxc_compress_chunk_wrapper_avx512; else if (cpu == ZXC_CPU_AVX2) zxc_compress_ptr_local = zxc_compress_chunk_wrapper_avx2; else if (cpu == ZXC_CPU_SSE2) zxc_compress_ptr_local = zxc_compress_chunk_wrapper_sse2; else zxc_compress_ptr_local = zxc_compress_chunk_wrapper_default; #elif defined(__aarch64__) || defined(_M_ARM64) || defined(__arm__) || defined(_M_ARM) // cppcheck-suppress knownConditionTrueFalse if (cpu == ZXC_CPU_NEON) zxc_compress_ptr_local = zxc_compress_chunk_wrapper_neon; else zxc_compress_ptr_local = zxc_compress_chunk_wrapper_default; #else (void)cpu; zxc_compress_ptr_local = zxc_compress_chunk_wrapper_default; #endif #else (void)cpu; zxc_compress_ptr_local = zxc_compress_chunk_wrapper_default; #endif #if ZXC_USE_C11_ATOMICS atomic_store_explicit(&zxc_compress_ptr, zxc_compress_ptr_local, memory_order_release); #else zxc_compress_ptr = zxc_compress_ptr_local; #endif return zxc_compress_ptr_local(ctx, src, src_sz, dst, dst_cap); } // LCOV_EXCL_STOP /** * @brief Public decompression dispatcher (calls lazily-resolved implementation). * * @param[in,out] ctx Decompression context. * @param[in] src Compressed input chunk (header + payload + optional checksum). * @param[in] src_sz Size of @p src in bytes. * @param[out] dst Destination buffer for decompressed data. * @param[in] dst_cap Capacity of @p dst. * @return Decompressed size in bytes, or a negative @ref zxc_error_t code. */ int zxc_decompress_chunk_wrapper(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap) { /* dict_size is constant for a stream; this per-block branch (outside the decode * loop) routes to the dict variant only when a dictionary is active, so the * no-dict path runs the dict-free chunk wrapper (identical codegen to main). */ #if ZXC_USE_C11_ATOMICS const zxc_decompress_func_t func = atomic_load_explicit( ctx->dict_size ? &zxc_decompress_dict_ptr : &zxc_decompress_ptr, memory_order_acquire); #else const zxc_decompress_func_t func = ctx->dict_size ? zxc_decompress_dict_ptr : zxc_decompress_ptr; #endif if (UNLIKELY(!func)) return zxc_decompress_dispatch_init(ctx, src, src_sz, dst, dst_cap); return func(ctx, src, src_sz, dst, dst_cap); } /** * @brief Internal safe-decompression dispatcher (strict dst_capacity == uncompressed_size). * * Calls the lazily-resolved `_safe_*` variant, running first-call init if needed. * * @param[in] ctx Decompression context. * @param[in] src Compressed input chunk. * @param[in] src_sz Size of @p src in bytes. * @param[out] dst Destination buffer (capacity == exact uncompressed size). * @param[in] dst_cap Capacity of @p dst in bytes. * @return Decompressed size in bytes, or a negative @ref zxc_error_t. */ static int zxc_decompress_chunk_wrapper_safe_public(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap) { #if ZXC_USE_C11_ATOMICS const zxc_decompress_func_t func = atomic_load_explicit(&zxc_decompress_safe_ptr, memory_order_acquire); #else const zxc_decompress_func_t func = zxc_decompress_safe_ptr; #endif if (UNLIKELY(!func)) return zxc_decompress_safe_dispatch_init(ctx, src, src_sz, dst, dst_cap); return func(ctx, src, src_sz, dst, dst_cap); } /** * @brief Public compression dispatcher (calls lazily-resolved implementation). * * @param[in,out] ctx Compression context. * @param[in] src Uncompressed input chunk. * @param[in] src_sz Size of @p src in bytes. * @param[out] dst Destination buffer for compressed data. * @param[in] dst_cap Capacity of @p dst. * @return Compressed size in bytes, or a negative @ref zxc_error_t code. */ int zxc_compress_chunk_wrapper(zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap) { #if ZXC_USE_C11_ATOMICS const zxc_compress_func_t func = atomic_load_explicit(&zxc_compress_ptr, memory_order_acquire); #else const zxc_compress_func_t func = zxc_compress_ptr; #endif if (UNLIKELY(!func)) return zxc_compress_dispatch_init(ctx, src, src_sz, dst, dst_cap); return func(ctx, src, src_sz, dst, dst_cap); } /* * ============================================================================ * HUFFMAN TRAMPOLINES * ============================================================================ * The Huffman codec is built per-variant (default / avx2 / avx512 / neon) * alongside zxc_compress.c and zxc_decompress.c, so the LZ77 stages and the * Huffman stage in a given variant share the same ISA flags (e.g. -mbmi2 on * the AVX2/AVX512 variants). The compress/decompress variant TUs resolve * their Huffman calls to the matching suffixed symbol at compile time, so * the production hot path has zero dispatch overhead. * * These thin wrappers exist only for tests and external callers that link * against the un-suffixed names. They forward to the default (scalar) variant. */ /** * @brief Build length-limited per-symbol Huffman code lengths from frequencies. * * Un-suffixed entry forwarding to @ref zxc_huf_build_code_lengths_default; full * contract in @c zxc_internal.h. * * @param[in] freq Per-symbol frequency counts. * @param[out] code_len Per-symbol code lengths. * @param[in] scratch Caller-provided build scratch buffer. * @return `ZXC_OK` on success, negative `zxc_error_t` on failure. */ int zxc_huf_build_code_lengths(const uint32_t* RESTRICT freq, uint8_t* RESTRICT code_len, void* RESTRICT scratch, const int max_code_len) { return zxc_huf_build_code_lengths_default(freq, code_len, scratch, max_code_len); } /** @brief Un-suffixed forwarders for the PivCo section codec (tests, tools). */ size_t zxc_huf_calc_size(const uint32_t* RESTRICT freq, const uint8_t* RESTRICT code_len, const int with_header) { return zxc_huf_calc_size_default(freq, code_len, with_header); } int zxc_huf_encode_section(const uint8_t* RESTRICT literals, const size_t n_literals, const uint32_t* RESTRICT freq, const uint8_t* RESTRICT code_len, uint8_t* RESTRICT dst, const size_t dst_cap) { return zxc_huf_encode_section_default(literals, n_literals, freq, code_len, dst, dst_cap); } int zxc_huf_decode_section(const uint8_t* RESTRICT payload, const size_t payload_size, uint8_t* RESTRICT dst, const size_t n, uint8_t* RESTRICT scratch) { return zxc_huf_decode_section_default(payload, payload_size, dst, n, scratch); } int zxc_huf_encode_section_dict(const uint8_t* RESTRICT literals, const size_t n_literals, const uint32_t* RESTRICT freq, const uint8_t* RESTRICT code_len, const zxc_pivco_tree_t* RESTRICT tree, const uint32_t* RESTRICT codes, uint8_t* RESTRICT dst, const size_t dst_cap) { return zxc_huf_encode_section_dict_default(literals, n_literals, freq, code_len, tree, codes, dst, dst_cap); } int zxc_huf_decode_section_dict(const uint8_t* RESTRICT payload, const size_t payload_size, uint8_t* RESTRICT dst, const size_t n, const zxc_pivco_tree_t* RESTRICT tree, const zxc_pivco_decode_aux_t* RESTRICT aux, uint8_t* RESTRICT scratch) { return zxc_huf_decode_section_dict_default(payload, payload_size, dst, n, tree, aux, scratch); } int zxc_huf_dict_tree_build(const uint8_t* RESTRICT packed_lengths, zxc_pivco_tree_t* RESTRICT tree, uint32_t* RESTRICT codes, uint8_t* RESTRICT code_len, zxc_pivco_decode_aux_t* RESTRICT aux) { return zxc_huf_dict_tree_build_default(packed_lengths, tree, codes, code_len, aux); } size_t zxc_huf_calc_size_dict(const uint32_t* RESTRICT freq, const uint8_t* RESTRICT code_len, const zxc_pivco_tree_t* RESTRICT tree) { return zxc_huf_calc_size_dict_default(freq, code_len, tree); } /** * @brief Pack per-symbol code lengths into the 128-byte nibble header. * * Un-suffixed entry forwarding to @ref zxc_huf_pack_lengths_default; full * contract in @c zxc_internal.h. * * @param[in] code_len Per-symbol code lengths (one byte each). * @param[out] out Destination 128-byte packed header. */ void zxc_huf_pack_lengths(const uint8_t* RESTRICT code_len, uint8_t* RESTRICT out) { zxc_huf_pack_lengths_default(code_len, out); } /** * @brief Unpack and validate a 128-byte packed lengths header. * * Un-suffixed entry forwarding to @ref zxc_huf_unpack_lengths_default; full * contract in @c zxc_internal.h. * * @param[in] in 128-byte packed lengths header. * @param[out] code_len Destination per-symbol code lengths. * @return `ZXC_OK` on success, `ZXC_ERROR_CORRUPT_DATA` on invalid lengths. */ int zxc_huf_unpack_lengths(const uint8_t* RESTRICT in, uint8_t* RESTRICT code_len) { return zxc_huf_unpack_lengths_default(in, code_len); } /* * ============================================================================ * PUBLIC UTILITY API * ============================================================================ * These wrapper functions provide a simplified interface by managing context * allocation and looping over blocks. They call the dispatched wrappers above. */ /** * @brief Compresses an entire buffer in one call. * * Manages context allocation internally, loops over blocks, writes the * file header / EOF block / footer, and accumulates the global checksum. * * @param[in] src Uncompressed input data. * @param[in] src_size Size of @p src in bytes. * @param[out] dst Destination buffer (use zxc_compress_bound() to size). * @param[in] dst_capacity Capacity of @p dst. * @param[in] opts Compression options (level, block size, checksum, * dictionary, seekable, threads), or NULL for defaults. * @return Total compressed size in bytes, or a negative @ref zxc_error_t code. */ // cppcheck-suppress unusedFunction int64_t zxc_compress(const void* RESTRICT src, const size_t src_size, void* RESTRICT dst, const size_t dst_capacity, const zxc_compress_opts_t* opts) { if (UNLIKELY(!dst || dst_capacity == 0 || (src_size > 0 && !src))) return ZXC_ERROR_NULL_INPUT; const int checksum_enabled = opts ? opts->checksum_enabled : 0; const int seekable = opts ? opts->seekable : 0; const int level = zxc_level_clamp((opts && opts->level > 0) ? opts->level : ZXC_LEVEL_DEFAULT); const size_t block_size = (opts && opts->block_size > 0) ? opts->block_size : ZXC_BLOCK_SIZE_DEFAULT; const uint8_t* dict = opts ? (const uint8_t*)opts->dict : NULL; const size_t dict_size = (opts && opts->dict) ? opts->dict_size : 0; const uint8_t* dict_huf = (opts && opts->dict) ? (const uint8_t*)opts->dict_huf : NULL; if (UNLIKELY(dict_size > ZXC_DICT_SIZE_MAX)) return ZXC_ERROR_DICT_TOO_LARGE; if (UNLIKELY(!zxc_validate_block_size(block_size))) return ZXC_ERROR_BAD_BLOCK_SIZE; const uint32_t did = (dict && dict_size > 0) ? zxc_dict_id(dict, dict_size, dict_huf) : 0; const uint8_t* ip = (const uint8_t*)src; uint8_t* op = (uint8_t*)dst; const uint8_t* op_start = op; const uint8_t* op_end = op + dst_capacity; uint32_t global_hash = 0; zxc_cctx_t ctx; const size_t eff_chunk = dict_size > 0 ? zxc_block_size_ceil(dict_size + block_size) : block_size; // LCOV_EXCL_START if (UNLIKELY(zxc_cctx_init(&ctx, eff_chunk, 1, level, checksum_enabled, dict_size) != ZXC_OK)) return ZXC_ERROR_MEMORY; // LCOV_EXCL_STOP if (UNLIKELY(zxc_cctx_attach_dict_huf(&ctx, dict_huf) != ZXC_OK)) { // LCOV_EXCL_START zxc_cctx_free(&ctx); return ZXC_ERROR_CORRUPT_DATA; // LCOV_EXCL_STOP } /* Dict input buffer: [dict_content | block_data] for the encoder, carved * into the cctx workspace (NULL when no dictionary is active). */ uint8_t* const dict_input = ctx.dict_buffer; if (dict_input) ZXC_MEMCPY(dict_input, dict, dict_size); const int h_val = zxc_write_file_header(op, (size_t)(op_end - op), block_size, checksum_enabled, did); // LCOV_EXCL_START if (UNLIKELY(h_val < 0)) { zxc_cctx_free(&ctx); return h_val; } // LCOV_EXCL_STOP op += h_val; /* Seekable: dynamic array for per-block compressed sizes */ uint32_t* seek_comp = NULL; uint32_t seek_count = 0; uint32_t seek_cap = 0; if (seekable) { const size_t block_count = src_size / block_size; if (UNLIKELY(block_count > (size_t)UINT32_MAX - 2)) { // LCOV_EXCL_START zxc_cctx_free(&ctx); return ZXC_ERROR_BAD_BLOCK_SIZE; // LCOV_EXCL_STOP } seek_cap = (uint32_t)(block_count + 2); seek_comp = (uint32_t*)ZXC_MALLOC(seek_cap * sizeof(uint32_t)); // LCOV_EXCL_START if (UNLIKELY(!seek_comp)) { zxc_cctx_free(&ctx); return ZXC_ERROR_MEMORY; } // LCOV_EXCL_STOP } size_t pos = 0; while (pos < src_size) { const size_t chunk_len = (src_size - pos > block_size) ? block_size : (src_size - pos); const size_t rem_cap = (size_t)(op_end - op); int res; if (dict_input) { ZXC_MEMCPY(dict_input + dict_size, ip + pos, chunk_len); res = zxc_compress_chunk_wrapper(&ctx, dict_input, dict_size + chunk_len, op, rem_cap); } else { res = zxc_compress_chunk_wrapper(&ctx, ip + pos, chunk_len, op, rem_cap); } if (UNLIKELY(res < 0)) { ZXC_FREE(seek_comp); zxc_cctx_free(&ctx); return res; } if (checksum_enabled) { // Update Global Hash (Rotation + XOR) // Block checksum is at the end of the written block data if (LIKELY(res >= ZXC_GLOBAL_CHECKSUM_SIZE)) { const uint32_t block_hash = zxc_le32(op + res - ZXC_GLOBAL_CHECKSUM_SIZE); global_hash = zxc_hash_combine_rotate(global_hash, block_hash); } } /* Seekable: record compressed block size */ if (seekable) { // LCOV_EXCL_START if (UNLIKELY(seek_count >= seek_cap)) { seek_cap = seek_cap * 2; uint32_t* nc = (uint32_t*)ZXC_REALLOC(seek_comp, seek_cap * sizeof(uint32_t)); if (UNLIKELY(!nc)) { ZXC_FREE(seek_comp); zxc_cctx_free(&ctx); return ZXC_ERROR_MEMORY; } seek_comp = nc; } // LCOV_EXCL_STOP seek_comp[seek_count] = (uint32_t)res; seek_count++; } op += res; pos += chunk_len; } zxc_cctx_free(&ctx); // Write EOF Block const size_t rem_cap = (size_t)(op_end - op); const zxc_block_header_t eof_bh = { .block_type = ZXC_BLOCK_EOF, .block_flags = 0, .reserved = 0, .comp_size = 0}; const int eof_val = zxc_write_block_header(op, rem_cap, &eof_bh); // LCOV_EXCL_START if (UNLIKELY(eof_val < 0)) { ZXC_FREE(seek_comp); return eof_val; } // LCOV_EXCL_STOP op += eof_val; /* Seekable: write seek table between EOF block and footer */ if (seekable && seek_count > 0) { const size_t st_cap = (size_t)(op_end - op); const int64_t st_val = zxc_write_seek_table(op, st_cap, seek_comp, seek_count); ZXC_FREE(seek_comp); if (UNLIKELY(st_val < 0)) return st_val; // LCOV_EXCL_LINE op += st_val; } else { ZXC_FREE(seek_comp); } if (UNLIKELY((size_t)(op_end - op) < ZXC_FILE_FOOTER_SIZE)) return ZXC_ERROR_DST_TOO_SMALL; // LCOV_EXCL_LINE // Write 12-byte Footer: [Source Size (8)] + [Global Hash (4)] const int footer_val = zxc_write_file_footer(op, (size_t)(op_end - op), src_size, global_hash, checksum_enabled); if (UNLIKELY(footer_val < 0)) return footer_val; // LCOV_EXCL_LINE op += footer_val; return (int64_t)(op - op_start); } /* Shared frame decode body used by both zxc_decompress (non-overlapping * src/dst) and zxc_decompress_inplace (single overlapping buffer). No RESTRICT * between src and dst so the overlapping case is well-defined; each per-block * decode still gets disjoint compressed/output regions (guaranteed by the * in-place margin) and its wrapper keeps its own RESTRICT. */ static int64_t zxc_decompress_frame(const uint8_t* src, size_t src_size, uint8_t* dst, size_t dst_capacity, const zxc_decompress_opts_t* opts); /** * @brief Decompresses an entire buffer in one call. * * Validates the file header and footer, loops over compressed blocks, * and verifies the global checksum when enabled. * * @param[in] src Compressed input data. * @param[in] src_size Size of @p src in bytes. * @param[out] dst Destination buffer for decompressed data. * @param[in] dst_capacity Capacity of @p dst. * @param[in] opts Decompression options (checksum verification, * dictionary, threads), or NULL for defaults. * @return Total decompressed size in bytes, or a negative @ref zxc_error_t code. */ // cppcheck-suppress unusedFunction int64_t zxc_decompress(const void* RESTRICT src, const size_t src_size, void* RESTRICT dst, const size_t dst_capacity, const zxc_decompress_opts_t* opts) { if (UNLIKELY(!src || src_size < ZXC_FILE_HEADER_SIZE + ZXC_FILE_FOOTER_SIZE || (!dst && dst_capacity != 0))) return ZXC_ERROR_NULL_INPUT; if (UNLIKELY(!dst || dst_capacity == 0)) { /* Empty-frame case (stored size == 0). */ if (UNLIKELY(zxc_le32(src) != ZXC_MAGIC_WORD)) return ZXC_ERROR_NULL_INPUT; const uint8_t* footer = (const uint8_t*)src + src_size - ZXC_FILE_FOOTER_SIZE; return (zxc_le64(footer) == 0) ? 0 : (int64_t)ZXC_ERROR_DST_TOO_SMALL; } return zxc_decompress_frame((const uint8_t*)src, src_size, (uint8_t*)dst, dst_capacity, opts); } static int64_t zxc_decompress_frame(const uint8_t* src, const size_t src_size, uint8_t* dst, const size_t dst_capacity, const zxc_decompress_opts_t* opts) { const int checksum_enabled = opts ? opts->checksum_enabled : 0; const uint8_t* dict = opts ? (const uint8_t*)opts->dict : NULL; const size_t dict_size = (opts && opts->dict) ? opts->dict_size : 0; const uint8_t* dict_huf = (opts && opts->dict) ? (const uint8_t*)opts->dict_huf : NULL; const uint8_t* ip = src; const uint8_t* ip_end = ip + src_size; uint8_t* op = dst; const uint8_t* op_start = op; const uint8_t* op_end = op + dst_capacity; size_t runtime_chunk_size = 0; zxc_cctx_t ctx; int file_has_checksums = 0; uint32_t header_dict_id = 0; if (UNLIKELY(zxc_read_file_header(ip, src_size, &runtime_chunk_size, &file_has_checksums, &header_dict_id) != ZXC_OK || zxc_cctx_init(&ctx, runtime_chunk_size, 0, 0, file_has_checksums && checksum_enabled, dict_size) != ZXC_OK)) { return ZXC_ERROR_BAD_HEADER; } /* Dictionary validation */ if (header_dict_id != 0) { if (UNLIKELY(!dict || dict_size == 0)) { zxc_cctx_free(&ctx); return ZXC_ERROR_DICT_REQUIRED; } if (UNLIKELY(zxc_dict_id(dict, dict_size, dict_huf) != header_dict_id)) { zxc_cctx_free(&ctx); return ZXC_ERROR_DICT_MISMATCH; } } if (UNLIKELY(zxc_cctx_attach_dict_huf(&ctx, dict_huf) != ZXC_OK)) { // LCOV_EXCL_START zxc_cctx_free(&ctx); return ZXC_ERROR_CORRUPT_DATA; // LCOV_EXCL_STOP } ip += ZXC_FILE_HEADER_SIZE; const size_t work_sz = runtime_chunk_size + ZXC_DECOMPRESS_TAIL_PAD; /* Dict decode buffer: [dict_content | decode_space + PAD], carved into the * cctx workspace (NULL when no dictionary is active). */ uint8_t* const dict_dec = ctx.dict_buffer; if (dict_dec) ZXC_MEMCPY(dict_dec, dict, dict_size); // Block decompression loop uint32_t global_hash = 0; while (ip < ip_end) { const size_t rem_src = (size_t)(ip_end - ip); zxc_block_header_t bh; // Read the block header to determine the compressed size if (UNLIKELY(zxc_read_block_header(ip, rem_src, &bh) != ZXC_OK)) { zxc_cctx_free(&ctx); return ZXC_ERROR_BAD_HEADER; } // Handle EOF block separately (not a real chunk to decompress) if (UNLIKELY(bh.block_type == ZXC_BLOCK_EOF)) { // EOF carries no payload; a non-zero comp_size is a malformed header. if (UNLIKELY(bh.comp_size != 0)) { zxc_cctx_free(&ctx); return ZXC_ERROR_BAD_HEADER; } // Footer is always the last ZXC_FILE_FOOTER_SIZE bytes of the source, // even when a seek table is inserted between EOF block and footer. // LCOV_EXCL_START if (UNLIKELY(src_size < ZXC_FILE_FOOTER_SIZE)) { zxc_cctx_free(&ctx); return ZXC_ERROR_SRC_TOO_SMALL; } // LCOV_EXCL_STOP const uint8_t* const footer = src + src_size - ZXC_FILE_FOOTER_SIZE; // Validate source size matches what we decompressed const uint64_t stored_size = zxc_le64(footer); if (UNLIKELY(stored_size != (uint64_t)(op - op_start))) { zxc_cctx_free(&ctx); return ZXC_ERROR_CORRUPT_DATA; } // Validate global checksum if enabled and file has checksums if (checksum_enabled && file_has_checksums) { const uint32_t stored_hash = zxc_le32(footer + sizeof(uint64_t)); if (UNLIKELY(stored_hash != global_hash)) { zxc_cctx_free(&ctx); return ZXC_ERROR_BAD_CHECKSUM; } } break; // EOF reached, exit loop } int res; const size_t rem_cap = (size_t)(op_end - op); if (dict_dec) { /* Dict path: decode into bounce buffer with dict prefix so match * copies that reference dict content resolve naturally. */ res = zxc_decompress_chunk_wrapper(&ctx, ip, rem_src, dict_dec + dict_size, work_sz); if (LIKELY(res > 0)) { if (UNLIKELY((size_t)res > rem_cap)) { // LCOV_EXCL_START zxc_cctx_free(&ctx); return ZXC_ERROR_DST_TOO_SMALL; // LCOV_EXCL_STOP } ZXC_MEMCPY(op, dict_dec + dict_size, (size_t)res); } } else if (LIKELY(rem_cap >= work_sz)) { // Fast path: decode directly into dst. Cap dst_cap to chunk_size + PAD res = zxc_decompress_chunk_wrapper(&ctx, ip, rem_src, op, work_sz); } else { // Safe path: decode into bounce buffer, then copy exact result. res = zxc_decompress_chunk_wrapper(&ctx, ip, rem_src, ctx.work_buf, ctx.work_buf_cap); if (LIKELY(res > 0)) { // LCOV_EXCL_START if (UNLIKELY((size_t)res > rem_cap)) { zxc_cctx_free(&ctx); return ZXC_ERROR_DST_TOO_SMALL; } // LCOV_EXCL_STOP ZXC_MEMCPY(op, ctx.work_buf, (size_t)res); } } if (UNLIKELY(res < 0)) { zxc_cctx_free(&ctx); return res; } // Update global hash from block checksum if (checksum_enabled && file_has_checksums) { const uint32_t block_hash = zxc_le32(ip + ZXC_BLOCK_HEADER_SIZE + bh.comp_size); global_hash = zxc_hash_combine_rotate(global_hash, block_hash); } ip += ZXC_BLOCK_HEADER_SIZE + bh.comp_size + (file_has_checksums ? ZXC_BLOCK_CHECKSUM_SIZE : 0); op += res; } zxc_cctx_free(&ctx); return (int64_t)(op - op_start); } /** * @brief Single source of truth for the in-place safety margin (bytes over the * decompressed size @p d). * * The write cursor sweeps [0, d) while the read cursor starts flush-right; the * no-overtake invariant `sum_{j<=k} o_j + PAD <= F + 16 + sum_{j (uint64_t)SIZE_MAX || d > (uint64_t)SIZE_MAX - margin)) return 0; return (size_t)(d + margin); } /** * @brief Decompresses in place, inside a single caller-owned buffer. * * The compressed archive of @p comp_size bytes must sit **flush-right** in * @p buffer, i.e. at `buffer + buffer_capacity - comp_size`. Decoding runs * left-to-right into `buffer[0..]`; because ZXC never expands a block and the * buffer carries a one-block + wild-copy margin (see * @ref zxc_decompress_inplace_bound), the write cursor provably never overtakes * the read cursor, so a single allocation replaces the usual input+output pair. * Dictionary archives are supported (they decode through the context's own * bounce buffer, which does not alias @p buffer). * * @param[in,out] buffer Single work buffer holding the flush-right * archive; receives the decompressed output. * @param[in] buffer_capacity Total size of @p buffer in bytes. * @param[in] comp_size Size of the compressed archive in bytes. * @param[in] opts Decompression options, or NULL for defaults. * @return Decompressed size in bytes, or a negative @ref zxc_error_t code * (`ZXC_ERROR_DST_TOO_SMALL` if the buffer lacks the safety margin). */ // cppcheck-suppress unusedFunction int64_t zxc_decompress_inplace(void* buffer, const size_t buffer_capacity, const size_t comp_size, const zxc_decompress_opts_t* opts) { if (UNLIKELY(!buffer || comp_size < ZXC_FILE_HEADER_SIZE + ZXC_FILE_FOOTER_SIZE || comp_size > buffer_capacity)) return ZXC_ERROR_NULL_INPUT; uint8_t* const buf = (uint8_t*)buffer; const uint8_t* const comp = buf + (buffer_capacity - comp_size); /* flush-right */ uint64_t d = 0; uint64_t margin = 0; if (UNLIKELY(zxc_inplace_probe(comp, comp_size, &d, &margin) != ZXC_OK)) return ZXC_ERROR_BAD_HEADER; if (UNLIKELY(d > (uint64_t)buffer_capacity || (uint64_t)buffer_capacity - d < margin)) return ZXC_ERROR_DST_TOO_SMALL; return zxc_decompress_frame(comp, comp_size, buf, buffer_capacity, opts); } /** * @brief Reads the decompressed size from a ZXC-compressed buffer. * * The size is stored in the file footer (last @ref ZXC_FILE_FOOTER_SIZE bytes). * The footer is untrusted input, so the value is checked for plausibility * against the archive itself: every decoded block costs at least * @ref ZXC_BLOCK_HEADER_SIZE compressed bytes and expands to at most one * block size, which bounds the ratio an authentic archive can reach. A size * beyond that bound (a forged footer) returns 0, so callers sizing an output * allocation from this value inherit the check. * * @param[in] src Compressed data. * @param[in] src_size Size of @p src in bytes. * @return Original uncompressed size, or 0 on error or an implausible footer. */ uint64_t zxc_get_decompressed_size(const void* src, const size_t src_size) { if (UNLIKELY(src_size < ZXC_FILE_HEADER_SIZE + ZXC_FILE_FOOTER_SIZE)) return 0; const uint8_t* const p = (const uint8_t*)src; if (UNLIKELY(zxc_le32(p) != ZXC_MAGIC_WORD)) return 0; size_t chunk_size = 0; int has_cs = 0; uint32_t did = 0; if (UNLIKELY(zxc_read_file_header(p, src_size, &chunk_size, &has_cs, &did) != ZXC_OK)) return 0; const uint8_t* const footer = p + src_size - ZXC_FILE_FOOTER_SIZE; const uint64_t d = zxc_le64(footer); /* Plausibility: an archive of src_size bytes cannot carry more blocks * than src_size / ZXC_BLOCK_HEADER_SIZE, and each block decodes to at * most chunk_size bytes. Division form keeps the compare overflow-free * (the usual `(d + chunk - 1) / chunk` ceil would wrap for a forged d * near UINT64_MAX). */ const uint64_t blocks_needed = chunk_size ? d / (uint64_t)chunk_size + (d % (uint64_t)chunk_size != 0) : 0; if (UNLIKELY(blocks_needed > (uint64_t)(src_size / ZXC_BLOCK_HEADER_SIZE))) return 0; return d; } /** * @brief Reads the dictionary id from a compressed archive's file header. * * Public API; see @c zxc_buffer.h. Validates the magic, then returns the * header's @c dict_id field when the dictionary flag is set. Does not decompress. * * @param[in] src Start of the compressed archive (>= @c ZXC_FILE_HEADER_SIZE). * @param[in] src_size Size of @p src in bytes. * @return The dictionary id, or 0 if @p src is invalid or the archive uses no * dictionary. */ // cppcheck-suppress unusedFunction uint32_t zxc_get_dict_id(const void* src, const size_t src_size) { if (UNLIKELY(!src || src_size < ZXC_FILE_HEADER_SIZE)) return 0; const uint8_t* const p = (const uint8_t*)src; if (UNLIKELY(zxc_le32(p) != ZXC_MAGIC_WORD)) return 0; return (p[6] & ZXC_FILE_FLAG_HAS_DICTIONARY) ? zxc_le32(p + 7) : 0; } /* * ============================================================================ * REUSABLE CONTEXT API (Opaque) * ============================================================================ * * Provides heap-allocated, opaque contexts that integrators can reuse across * multiple compress / decompress calls, eliminating per-call malloc/free * overhead. */ /* --- Compression --------------------------------------------------------- */ /** * @brief Opaque reusable compression context (public handle @ref zxc_cctx). * * Wraps one internal @ref zxc_cctx_t plus the sticky options and bookkeeping * needed to reuse buffers across calls and re-init only when the block size * changes. */ struct zxc_cctx_s { zxc_cctx_t inner; /* existing internal context */ int initialized; /* 1 if inner has live allocations */ int owns_workspace; /* 0 = library-allocated (free in zxc_free_cctx), 1 = caller-supplied static workspace (no-op free, block_size pinned at init) */ size_t last_block_size; /* block size used for last init */ /* Sticky options (remembered from create or last compress call). */ int stored_level; int stored_checksum; size_t stored_block_size; }; /** * @brief Creates a reusable compression context. * * Public API; full contract in @c zxc_buffer.h. With non-NULL @p opts the * internal buffers are pre-allocated for the given level / block size / * checksum; with NULL @p opts allocation is deferred to the first * @ref zxc_compress_cctx call. The resolved settings become sticky defaults. * * @param[in] opts Initial compression options, or NULL to defer allocation. * @return A context to release with @ref zxc_free_cctx, or NULL on allocation * failure or invalid @p opts. */ zxc_cctx* zxc_create_cctx(const zxc_compress_opts_t* opts) { zxc_cctx* const cctx = (zxc_cctx*)ZXC_CALLOC(1, sizeof(zxc_cctx)); if (UNLIKELY(!cctx)) return NULL; // LCOV_EXCL_LINE /* Resolve and store sticky defaults. */ cctx->stored_level = zxc_level_clamp((opts && opts->level > 0) ? opts->level : ZXC_LEVEL_DEFAULT); cctx->stored_block_size = (opts && opts->block_size > 0) ? opts->block_size : ZXC_BLOCK_SIZE_DEFAULT; cctx->stored_checksum = opts ? opts->checksum_enabled : 0; if (opts) { // LCOV_EXCL_START if (UNLIKELY(!zxc_validate_block_size(cctx->stored_block_size) || zxc_cctx_init(&cctx->inner, cctx->stored_block_size, 1, cctx->stored_level, cctx->stored_checksum, 0) != ZXC_OK)) { ZXC_FREE(cctx); return NULL; } // LCOV_EXCL_STOP cctx->last_block_size = cctx->stored_block_size; cctx->initialized = 1; } return cctx; } /** * @brief Releases a reusable compression context. * * Public API; see @c zxc_buffer.h. Frees the inner buffers and the handle. * NULL-safe. For a static (caller-workspace) context this is a no-op, since the * caller owns the workspace. * * @param[in] cctx Context from @ref zxc_create_cctx (may be NULL). */ void zxc_free_cctx(zxc_cctx* cctx) { if (UNLIKELY(!cctx)) return; /* Static cctx: handle + inner buffers live inside the caller's workspace, * which we do not own. Free is a no-op; the caller owns the workspace. */ if (cctx->owns_workspace) return; if (cctx->initialized) zxc_cctx_free(&cctx->inner); ZXC_FREE(cctx); } /** * @brief Compresses a whole buffer into a framed archive, reusing @p cctx. * * Public API; full contract in @c zxc_buffer.h. Resolves per-call options over * the context's sticky defaults, re-initialises the inner buffers only when the * block size changes (level / checksum update in place), then writes the file * header, the compressed blocks, the EOF block and the footer. * * @param[in,out] cctx Reusable compression context. * @param[in] src Source bytes. * @param[in] src_size Number of source bytes (must be > 0). * @param[out] dst Destination buffer for the archive. * @param[in] dst_capacity Capacity of @p dst in bytes. * @param[in] opts Per-call option overrides, or NULL for the * context defaults. * @return Archive size in bytes on success, or a negative @ref zxc_error_t. */ int64_t zxc_compress_cctx(zxc_cctx* cctx, const void* RESTRICT src, const size_t src_size, void* RESTRICT dst, const size_t dst_capacity, const zxc_compress_opts_t* opts) { if (UNLIKELY(!cctx)) return ZXC_ERROR_NULL_INPUT; if (UNLIKELY(!src || !dst || src_size == 0 || dst_capacity == 0)) return ZXC_ERROR_NULL_INPUT; const int checksum_enabled = opts ? opts->checksum_enabled : cctx->stored_checksum; const int level = zxc_level_clamp((opts && opts->level > 0) ? opts->level : cctx->stored_level); const size_t block_size = (opts && opts->block_size > 0) ? opts->block_size : cctx->stored_block_size; if (UNLIKELY(!zxc_validate_block_size(block_size))) return ZXC_ERROR_BAD_BLOCK_SIZE; /* Static cctx: block_size is locked at workspace init. Reject any opts * that would force a re-partition, since the workspace cannot grow. * level / checksum_enabled may still vary per call - except a raise into * the optimal-parser tier, whose opt_scratch region a workspace carved at * level < ZXC_LEVEL_DENSITY does not carry. */ if (UNLIKELY(cctx->owns_workspace && block_size != cctx->last_block_size)) return ZXC_ERROR_BAD_BLOCK_SIZE; if (UNLIKELY(cctx->owns_workspace && level >= ZXC_LEVEL_DENSITY && !cctx->inner.opt_scratch)) return ZXC_ERROR_BAD_LEVEL; cctx->stored_level = level; cctx->stored_block_size = block_size; cctx->stored_checksum = checksum_enabled; /* Re-init when block_size changed (it drives buffer sizes), or when a * per-call level raise into the optimal-parser tier requires the * opt_scratch region that inits at level < ZXC_LEVEL_DENSITY do not * allocate (using it NULL would crash). */ if (UNLIKELY(!cctx->initialized || cctx->last_block_size != block_size || (level >= ZXC_LEVEL_DENSITY && !cctx->inner.opt_scratch))) { if (cctx->initialized) { // LCOV_EXCL_START zxc_cctx_free(&cctx->inner); cctx->initialized = 0; // LCOV_EXCL_STOP } // LCOV_EXCL_START if (UNLIKELY(zxc_cctx_init(&cctx->inner, block_size, 1, level, checksum_enabled, 0) != ZXC_OK)) return ZXC_ERROR_MEMORY; // LCOV_EXCL_STOP cctx->last_block_size = block_size; cctx->initialized = 1; } else { /* Same block_size: update level + checksum without realloc. */ cctx->inner.compression_level = level; cctx->inner.checksum_enabled = checksum_enabled; } zxc_cctx_t* const ctx = &cctx->inner; uint8_t* op = (uint8_t*)dst; const uint8_t* const op_start = op; const uint8_t* const op_end = op + dst_capacity; const uint8_t* const ip = (const uint8_t*)src; uint32_t global_hash = 0; const int h_val = zxc_write_file_header(op, (size_t)(op_end - op), block_size, checksum_enabled, 0); if (UNLIKELY(h_val < 0)) return h_val; // LCOV_EXCL_LINE op += h_val; size_t pos = 0; while (pos < src_size) { const size_t chunk_len = (src_size - pos > block_size) ? block_size : (src_size - pos); const size_t rem_cap = (size_t)(op_end - op); const int res = zxc_compress_chunk_wrapper(ctx, ip + pos, chunk_len, op, rem_cap); if (UNLIKELY(res < 0)) return res; if (checksum_enabled) { if (LIKELY(res >= ZXC_GLOBAL_CHECKSUM_SIZE)) { const uint32_t block_hash = zxc_le32(op + res - ZXC_GLOBAL_CHECKSUM_SIZE); global_hash = zxc_hash_combine_rotate(global_hash, block_hash); } } op += res; pos += chunk_len; } /* EOF block */ const size_t rem_cap = (size_t)(op_end - op); const zxc_block_header_t eof_bh = { .block_type = ZXC_BLOCK_EOF, .block_flags = 0, .reserved = 0, .comp_size = 0}; const int eof_val = zxc_write_block_header(op, rem_cap, &eof_bh); if (UNLIKELY(eof_val < 0)) return eof_val; // LCOV_EXCL_LINE op += eof_val; if (UNLIKELY(rem_cap < (size_t)eof_val + ZXC_FILE_FOOTER_SIZE)) return ZXC_ERROR_DST_TOO_SMALL; // LCOV_EXCL_LINE const int footer_val = zxc_write_file_footer(op, (size_t)(op_end - op), src_size, global_hash, checksum_enabled); if (UNLIKELY(footer_val < 0)) return footer_val; // LCOV_EXCL_LINE op += footer_val; return (int64_t)(op - op_start); } /* --- Decompression ------------------------------------------------------- */ /** * @brief Opaque reusable decompression context (public handle @ref zxc_dctx). * * Reuses the internal @ref zxc_cctx_t type for decode, tracking the last block * and dict sizes so the inner buffers are re-carved only when they change. */ struct zxc_dctx_s { zxc_cctx_t inner; /* reuses the same internal context type */ size_t last_block_size; /* block size from last header parse */ size_t last_dict_size; /* dict_size the inner buffer was carved for (drives re-init) */ int initialized; /* 1 if inner has live allocations */ int owns_workspace; /* 0 = library-allocated (free in zxc_free_dctx), 1 = caller-supplied static workspace (no-op free, block_size pinned at init) */ }; /** * @brief Creates a reusable decompression context. * * Public API; see @c zxc_buffer.h. The inner buffers are allocated lazily on * the first decode (sized from the archive header), so this only allocates the * handle itself. * * @return A context to release with @ref zxc_free_dctx, or NULL on allocation * failure. */ zxc_dctx* zxc_create_dctx(void) { zxc_dctx* const dctx = (zxc_dctx*)ZXC_CALLOC(1, sizeof(zxc_dctx)); return dctx; } /** * @brief Releases a reusable decompression context. * * Public API; see @c zxc_buffer.h. Frees the inner buffers and the handle. * NULL-safe; a no-op for a static (caller-workspace) context. * * @param[in] dctx Context from @ref zxc_create_dctx (may be NULL). */ void zxc_free_dctx(zxc_dctx* dctx) { if (UNLIKELY(!dctx)) return; /* Static dctx: handle + inner buffers live inside the caller's workspace, * which we do not own. Free is a no-op; the caller owns the workspace. */ if (dctx->owns_workspace) return; if (dctx->initialized) zxc_cctx_free(&dctx->inner); ZXC_FREE(dctx); } /** * @brief Decompresses a framed archive into @p dst, reusing @p dctx. * * Public API; full contract in @c zxc_buffer.h. Parses the file header, * re-initialises the inner buffers only when the block size changes (or a prior * dict call left a prefix), then decodes each block - straight into @p dst when * the tail padding fits, otherwise through a bounce buffer - and verifies the * footer size and optional checksum. * * @param[in,out] dctx Reusable decompression context. * @param[in] src Compressed archive bytes. * @param[in] src_size Archive size (>= @c ZXC_FILE_HEADER_SIZE). * @param[out] dst Destination for the decompressed output. * @param[in] dst_capacity Capacity of @p dst in bytes. * @param[in] opts Per-call options (e.g. checksum), or NULL. * @return Decompressed size in bytes on success, or a negative @ref zxc_error_t. */ int64_t zxc_decompress_dctx(zxc_dctx* dctx, const void* RESTRICT src, const size_t src_size, void* RESTRICT dst, const size_t dst_capacity, const zxc_decompress_opts_t* opts) { if (UNLIKELY(!dctx || !src || !dst || src_size < ZXC_FILE_HEADER_SIZE)) return ZXC_ERROR_NULL_INPUT; const int checksum_enabled = opts ? opts->checksum_enabled : 0; const uint8_t* ip = (const uint8_t*)src; const uint8_t* const ip_end = ip + src_size; uint8_t* op = (uint8_t*)dst; const uint8_t* const op_start = op; const uint8_t* const op_end = op + dst_capacity; size_t runtime_chunk_size = 0; int file_has_checksums = 0; uint32_t global_hash = 0; if (UNLIKELY(zxc_read_file_header(ip, src_size, &runtime_chunk_size, &file_has_checksums, NULL) != ZXC_OK)) return ZXC_ERROR_BAD_HEADER; /* Static dctx: block_size is locked at workspace init; reject any * archive whose declared block_size would require a re-partition. */ if (UNLIKELY(dctx->owns_workspace && runtime_chunk_size != dctx->last_block_size)) return ZXC_ERROR_BAD_BLOCK_SIZE; /* Re-init when block size changed, or when a prior dict-using call (block * API) left the inner context carrying a dict prefix. */ if (UNLIKELY(!dctx->initialized || dctx->last_block_size != runtime_chunk_size || dctx->last_dict_size != 0)) { if (dctx->initialized) { // LCOV_EXCL_START zxc_cctx_free(&dctx->inner); dctx->initialized = 0; // LCOV_EXCL_STOP } // LCOV_EXCL_START if (UNLIKELY(zxc_cctx_init(&dctx->inner, runtime_chunk_size, 0, 0, file_has_checksums && checksum_enabled, 0) != ZXC_OK)) return ZXC_ERROR_MEMORY; // LCOV_EXCL_STOP dctx->last_block_size = runtime_chunk_size; dctx->last_dict_size = 0; dctx->initialized = 1; } else { dctx->inner.checksum_enabled = file_has_checksums && checksum_enabled; } zxc_cctx_t* const ctx = &dctx->inner; ip += ZXC_FILE_HEADER_SIZE; /* work_buf was pre-sized to runtime_chunk_size + ZXC_DECOMPRESS_TAIL_PAD * inside the matching zxc_cctx_init call above; the re-init guard ensures * it stays in sync when chunk_size changes between calls. */ const size_t work_sz = runtime_chunk_size + ZXC_DECOMPRESS_TAIL_PAD; while (ip < ip_end) { const size_t rem_src = (size_t)(ip_end - ip); zxc_block_header_t bh; if (UNLIKELY(zxc_read_block_header(ip, rem_src, &bh) != ZXC_OK)) return ZXC_ERROR_BAD_HEADER; if (UNLIKELY(bh.block_type == ZXC_BLOCK_EOF)) { if (UNLIKELY(bh.comp_size != 0)) return ZXC_ERROR_BAD_HEADER; if (UNLIKELY(rem_src < ZXC_BLOCK_HEADER_SIZE + ZXC_FILE_FOOTER_SIZE)) return ZXC_ERROR_SRC_TOO_SMALL; const uint8_t* const footer = ip + ZXC_BLOCK_HEADER_SIZE; const uint64_t stored_size = zxc_le64(footer); if (UNLIKELY(stored_size != (uint64_t)(op - op_start))) return ZXC_ERROR_CORRUPT_DATA; if (checksum_enabled && file_has_checksums) { const uint32_t stored_hash = zxc_le32(footer + sizeof(uint64_t)); if (UNLIKELY(stored_hash != global_hash)) return ZXC_ERROR_BAD_CHECKSUM; } break; } const size_t rem_cap = (size_t)(op_end - op); int res; if (LIKELY(rem_cap >= work_sz)) { // Fast path: decode directly into dst (enough padding for wild copies). res = zxc_decompress_chunk_wrapper(ctx, ip, rem_src, op, rem_cap); } else { // Safe path: decode into bounce buffer, then copy exact result. res = zxc_decompress_chunk_wrapper(ctx, ip, rem_src, ctx->work_buf, ctx->work_buf_cap); if (LIKELY(res > 0)) { if (UNLIKELY((size_t)res > rem_cap)) return ZXC_ERROR_DST_TOO_SMALL; // LCOV_EXCL_LINE ZXC_MEMCPY(op, ctx->work_buf, (size_t)res); } } if (UNLIKELY(res < 0)) return res; if (checksum_enabled && file_has_checksums) { const uint32_t block_hash = zxc_le32(ip + ZXC_BLOCK_HEADER_SIZE + bh.comp_size); global_hash = zxc_hash_combine_rotate(global_hash, block_hash); } ip += ZXC_BLOCK_HEADER_SIZE + bh.comp_size + (file_has_checksums ? ZXC_BLOCK_CHECKSUM_SIZE : 0); op += res; } return (int64_t)(op - op_start); } /* ========================================================================= */ /* Block-Level API (no file framing) */ /* ========================================================================= */ /** * @brief Compresses a single block (no file framing), reusing @p cctx. * * Public API; full contract in @c zxc_buffer.h. Produces one format-conformant * block with no header / EOF / footer, so @p src_size must not exceed * @c ZXC_BLOCK_SIZE_MAX (use the frame or streaming APIs for larger inputs). * With a dictionary in @p opts, [dict | block] is assembled in the cctx-owned * bounce buffer before encoding. Inner buffers are re-initialised when the * effective block size changes, or when a per-call level raise into the * optimal-parser tier requires the opt_scratch region; static contexts * reject both cases instead (the workspace cannot grow). * * @param[in,out] cctx Reusable compression context. * @param[in] src Source block bytes. * @param[in] src_size Source length (0 < @p src_size <= @c ZXC_BLOCK_SIZE_MAX). * @param[out] dst Destination buffer for the block payload. * @param[in] dst_capacity Capacity of @p dst in bytes. * @param[in] opts Per-call options (level, dict, ...), or NULL. * @return Block payload size in bytes on success, or a negative @ref zxc_error_t. */ int64_t zxc_compress_block(zxc_cctx* cctx, const void* RESTRICT src, const size_t src_size, void* RESTRICT dst, const size_t dst_capacity, const zxc_compress_opts_t* opts) { if (UNLIKELY(!cctx || !src || !dst || src_size == 0 || dst_capacity == 0)) return ZXC_ERROR_NULL_INPUT; /* Block API processes a single format-conformant block: src_size must not * exceed ZXC_BLOCK_SIZE_MAX. Callers with larger inputs should use the * frame or streaming APIs which chunk transparently. */ if (UNLIKELY(src_size > ZXC_BLOCK_SIZE_MAX)) return ZXC_ERROR_BAD_BLOCK_SIZE; const int checksum_enabled = opts ? opts->checksum_enabled : cctx->stored_checksum; const int level = zxc_level_clamp((opts && opts->level > 0) ? opts->level : cctx->stored_level); /* For block API, block_size == src_size (the caller compresses one block at a time). */ const size_t block_size = (opts && opts->block_size > 0) ? opts->block_size : cctx->stored_block_size; const size_t min_bs = zxc_block_size_ceil(src_size); /* Always ensure internal buffers can hold src_size. * When a dictionary is active, offset_bits must accommodate dict + block. */ const uint8_t* b_dict = opts ? (const uint8_t*)opts->dict : NULL; const size_t b_dict_size = (opts && opts->dict) ? opts->dict_size : 0; const size_t base_block_size = (block_size > min_bs) ? block_size : min_bs; const size_t effective_block_size = b_dict_size > 0 ? zxc_block_size_ceil(b_dict_size + base_block_size) : base_block_size; /* Static cctx: the workspace cannot grow, so reject any request that * would force a re-partition - a different effective block size, or a * per-call level raise into the optimal-parser tier whose opt_scratch * region the workspace does not carry. A heap re-init here would violate * the static API's no-allocation contract, and the replacement workspace * could never be freed (zxc_free_cctx is a no-op for static contexts). */ if (UNLIKELY(cctx->owns_workspace && effective_block_size != cctx->last_block_size)) return ZXC_ERROR_BAD_BLOCK_SIZE; if (UNLIKELY(cctx->owns_workspace && level >= ZXC_LEVEL_DENSITY && !cctx->inner.opt_scratch)) return ZXC_ERROR_BAD_LEVEL; cctx->stored_level = level; cctx->stored_block_size = effective_block_size; cctx->stored_checksum = checksum_enabled; /* Re-init when block_size changed, or when a per-call level raise into * the optimal-parser tier requires the opt_scratch region that inits at * level < ZXC_LEVEL_DENSITY do not allocate (using it NULL would crash). */ if (UNLIKELY(!cctx->initialized || cctx->last_block_size != effective_block_size || (level >= ZXC_LEVEL_DENSITY && !cctx->inner.opt_scratch))) { if (cctx->initialized) { // LCOV_EXCL_START zxc_cctx_free(&cctx->inner); cctx->initialized = 0; // LCOV_EXCL_STOP } // LCOV_EXCL_START if (UNLIKELY(zxc_cctx_init(&cctx->inner, effective_block_size, 1, level, checksum_enabled, b_dict_size) != ZXC_OK)) return ZXC_ERROR_MEMORY; // LCOV_EXCL_STOP cctx->last_block_size = effective_block_size; cctx->initialized = 1; } else { cctx->inner.compression_level = level; cctx->inner.checksum_enabled = checksum_enabled; } cctx->inner.dict_size = b_dict_size; int res; if (b_dict && b_dict_size > 0) { /* [dict | block] assembled in the cctx-owned dict_buffer */ uint8_t* const combined = cctx->inner.dict_buffer; ZXC_MEMCPY(combined, b_dict, b_dict_size); ZXC_MEMCPY(combined + b_dict_size, src, src_size); res = zxc_compress_chunk_wrapper(&cctx->inner, combined, b_dict_size + src_size, (uint8_t*)dst, dst_capacity); } else { res = zxc_compress_chunk_wrapper(&cctx->inner, (const uint8_t*)src, src_size, (uint8_t*)dst, dst_capacity); } if (UNLIKELY(res < 0)) return res; return (int64_t)res; } /** * @brief Decompresses a single block (no file framing), reusing @p dctx. * * Public API; full contract in @c zxc_buffer.h. Decodes one format-conformant * block; the decoded payload cannot exceed @c ZXC_BLOCK_SIZE_MAX, so * @p dst_capacity is bounded by @c ZXC_BLOCK_SIZE_MAX + @c ZXC_DECOMPRESS_TAIL_PAD. * With a dictionary in @p opts the decode runs through the [dict | decode] * bounce buffer; otherwise it goes straight into @p dst when the tail padding * fits, or via @c work_buf when it doesn't. * * @param[in,out] dctx Reusable decompression context. * @param[in] src Compressed block bytes. * @param[in] src_size Source length (>= @c ZXC_BLOCK_HEADER_SIZE). * @param[out] dst Destination for the decoded payload. * @param[in] dst_capacity Capacity of @p dst in bytes. * @param[in] opts Per-call options (dict, checksum), or NULL. * @return Decoded payload size in bytes on success, or a negative @ref zxc_error_t. */ int64_t zxc_decompress_block(zxc_dctx* dctx, const void* RESTRICT src, const size_t src_size, void* RESTRICT dst, const size_t dst_capacity, const zxc_decompress_opts_t* opts) { if (UNLIKELY(!dctx || !src || !dst || src_size < ZXC_BLOCK_HEADER_SIZE || dst_capacity == 0)) return ZXC_ERROR_NULL_INPUT; /* Block API decompresses a single format-conformant block. Decoded payload * cannot exceed ZXC_BLOCK_SIZE_MAX; dst_capacity is bounded accordingly to * include the tail-pad needed for safe wild copies. Callers expecting * larger outputs should use the frame or streaming APIs. */ if (UNLIKELY(dst_capacity > ZXC_BLOCK_SIZE_MAX + ZXC_DECOMPRESS_TAIL_PAD)) return ZXC_ERROR_BAD_BLOCK_SIZE; const int checksum_enabled = opts ? opts->checksum_enabled : 0; const uint8_t* dict = opts ? (const uint8_t*)opts->dict : NULL; const size_t dict_size = (opts && opts->dict) ? opts->dict_size : 0; /* Derive the block_size from dst_capacity (callers know the original size) */ const size_t block_size = zxc_block_size_ceil(dst_capacity); if (UNLIKELY(!dctx->initialized || dctx->last_block_size != block_size || dctx->last_dict_size != dict_size)) { if (dctx->initialized) { zxc_cctx_free(&dctx->inner); dctx->initialized = 0; } // LCOV_EXCL_START if (UNLIKELY(zxc_cctx_init(&dctx->inner, block_size, 0, 0, checksum_enabled, dict_size) != ZXC_OK)) return ZXC_ERROR_MEMORY; // LCOV_EXCL_STOP dctx->last_block_size = block_size; dctx->last_dict_size = dict_size; dctx->initialized = 1; } else { dctx->inner.checksum_enabled = checksum_enabled; } zxc_cctx_t* const ctx = &dctx->inner; ctx->dict_size = dict_size; /* work_buf was pre-sized to block_size + ZXC_DECOMPRESS_TAIL_PAD inside * the matching zxc_cctx_init call above. */ const size_t work_sz = block_size + ZXC_DECOMPRESS_TAIL_PAD; int res; if (dict && dict_size > 0) { /* [dict | decode] assembled in the cctx-owned dict_buffer */ uint8_t* const dec_buf = ctx->dict_buffer; ZXC_MEMCPY(dec_buf, dict, dict_size); res = zxc_decompress_chunk_wrapper(ctx, (const uint8_t*)src, src_size, dec_buf + dict_size, work_sz); if (LIKELY(res > 0)) { if (UNLIKELY((size_t)res > dst_capacity)) return ZXC_ERROR_DST_TOO_SMALL; ZXC_MEMCPY(dst, dec_buf + dict_size, (size_t)res); } } else if (LIKELY(dst_capacity >= work_sz)) { res = zxc_decompress_chunk_wrapper(ctx, (const uint8_t*)src, src_size, (uint8_t*)dst, dst_capacity); } else { /* Bounce through work_buf when output can't absorb wild copies. */ res = zxc_decompress_chunk_wrapper(ctx, (const uint8_t*)src, src_size, ctx->work_buf, ctx->work_buf_cap); if (LIKELY(res > 0)) { if (UNLIKELY((size_t)res > dst_capacity)) return ZXC_ERROR_DST_TOO_SMALL; ZXC_MEMCPY(dst, ctx->work_buf, (size_t)res); } } if (UNLIKELY(res < 0)) return res; return (int64_t)res; } /** * @brief Safe-variant block decompressor: accepts dst_capacity == uncompressed_size. * * Dict inputs and RAW blocks route to @ref zxc_decompress_block; plain GLO/GHI * use the strict safe decoder (no bounce buffer, no +ZXC_DECOMPRESS_TAIL_PAD). * * Public API; full contract in @c zxc_buffer.h. * * @param[in,out] dctx Reusable decompression context. * @param[in] src Compressed block bytes. * @param[in] src_size Source length (>= @c ZXC_BLOCK_HEADER_SIZE). * @param[out] dst Destination for the decoded payload. * @param[in] dst_capacity Exact uncompressed size (<= @c ZXC_BLOCK_SIZE_MAX). * @param[in] opts Per-call options (dict, checksum), or NULL. * @return Decoded payload size in bytes on success, or a negative @ref zxc_error_t. */ int64_t zxc_decompress_block_safe(zxc_dctx* dctx, const void* RESTRICT src, const size_t src_size, void* RESTRICT dst, const size_t dst_capacity, const zxc_decompress_opts_t* opts) { if (UNLIKELY(!dctx || !src || !dst || src_size < ZXC_BLOCK_HEADER_SIZE || dst_capacity == 0)) return ZXC_ERROR_NULL_INPUT; /* Strict-tail variant: dst_capacity matches the exact uncompressed size */ if (UNLIKELY(dst_capacity > ZXC_BLOCK_SIZE_MAX)) return ZXC_ERROR_BAD_BLOCK_SIZE; /* A dict needs the [dict|payload] bounce; route to the bounce-capable path. */ if (opts && opts->dict && opts->dict_size > 0) { return zxc_decompress_block(dctx, src, src_size, dst, dst_capacity, opts); } const uint8_t type = ((const uint8_t*)src)[0]; /* RAW never wild-writes past dst_capacity: route to the existing fast API. */ if (type == ZXC_BLOCK_RAW) { return zxc_decompress_block(dctx, src, src_size, dst, dst_capacity, opts); } /* GLO/GHI: use the strict-tail decoder (no bounce buffer required). */ const int checksum_enabled = opts ? opts->checksum_enabled : 0; const size_t block_size = zxc_block_size_ceil(dst_capacity); if (UNLIKELY(!dctx->initialized || dctx->last_block_size != block_size || dctx->last_dict_size != 0)) { if (dctx->initialized) { zxc_cctx_free(&dctx->inner); dctx->initialized = 0; } // LCOV_EXCL_START if (UNLIKELY(zxc_cctx_init(&dctx->inner, block_size, 0, 0, checksum_enabled, 0) != ZXC_OK)) return ZXC_ERROR_MEMORY; // LCOV_EXCL_STOP dctx->last_block_size = block_size; dctx->last_dict_size = 0; dctx->initialized = 1; } else { dctx->inner.checksum_enabled = checksum_enabled; } dctx->inner.dict_size = 0; const int res = zxc_decompress_chunk_wrapper_safe_public(&dctx->inner, (const uint8_t*)src, src_size, (uint8_t*)dst, dst_capacity); if (UNLIKELY(res < 0)) return res; return (int64_t)res; } /* * ============================================================================ * STATIC CONTEXT API (caller-allocated workspace) * ============================================================================ * Places the public handle struct at the start of the workspace, then carves * the persistent buffer (via zxc_cctx_init_in_workspace) in the remaining * cache-line-aligned tail. The caller owns the whole workspace; free * functions become no-ops via the owns_workspace flag. */ /* Size occupied by the opaque handle at the start of the workspace, rounded * up to a cache-line boundary so the persistent buffer (which expects 64 B * alignment for the hot zones) starts aligned. */ #define ZXC_STATIC_CCTX_HDR_SIZE ZXC_ALIGN_CL(sizeof(struct zxc_cctx_s)) #define ZXC_STATIC_DCTX_HDR_SIZE ZXC_ALIGN_CL(sizeof(struct zxc_dctx_s)) /** * @brief Workspace size needed for a static compression context. * * Public API; see @c zxc_buffer.h. Sum of the cache-line-aligned handle header * and the persistent buffer that @ref zxc_init_static_cctx carves for the given * @p block_size / @p level. Performs no allocation. * * @param[in] block_size Block size the context will be pinned to. * @param[in] level Compression level. * @return Required workspace size in bytes, or 0 if the parameters are invalid. */ size_t zxc_static_cctx_workspace_size(const size_t block_size, const int level) { if (UNLIKELY(!zxc_validate_block_size(block_size))) return 0; if (UNLIKELY(level < ZXC_LEVEL_FASTEST || level > ZXC_LEVEL_ULTRA)) return 0; const size_t inner_sz = zxc_cctx_compute_workspace_size(block_size, 1, level, 0); if (UNLIKELY(inner_sz == 0)) return 0; return ZXC_STATIC_CCTX_HDR_SIZE + inner_sz; } /** * @brief Initialises a compression context inside a caller-supplied workspace. * * Public API; full contract in @c zxc_buffer.h. Places the opaque handle at the * start of @p workspace and carves the persistent buffer in the aligned tail - * no heap allocation. The block size is pinned for the context's lifetime, and * @ref zxc_free_cctx becomes a no-op (the caller owns @p workspace). * * @param[in] workspace Caller buffer (>= @ref zxc_static_cctx_workspace_size). * @param[in] workspace_size Capacity of @p workspace in bytes. * @param[in] opts Compression options (non-NULL: level, block_size, * checksum). * @return A ready context owned by @p workspace, or NULL on invalid input or an * undersized workspace. */ zxc_cctx* zxc_init_static_cctx(void* RESTRICT workspace, const size_t workspace_size, const zxc_compress_opts_t* RESTRICT opts) { if (UNLIKELY(!workspace || !opts)) return NULL; const int level = (opts->level > 0) ? opts->level : ZXC_LEVEL_DEFAULT; const size_t block_size = (opts->block_size > 0) ? opts->block_size : ZXC_BLOCK_SIZE_DEFAULT; const int checksum_enabled = opts->checksum_enabled; if (UNLIKELY(!zxc_validate_block_size(block_size))) return NULL; if (UNLIKELY(level < ZXC_LEVEL_FASTEST || level > ZXC_LEVEL_ULTRA)) return NULL; const size_t inner_sz = zxc_cctx_compute_workspace_size(block_size, 1, level, 0); if (UNLIKELY(inner_sz == 0)) return NULL; if (UNLIKELY(workspace_size < ZXC_STATIC_CCTX_HDR_SIZE + inner_sz)) return NULL; zxc_cctx* const cctx = (zxc_cctx*)workspace; ZXC_MEMSET(cctx, 0, sizeof(*cctx)); uint8_t* const inner_ws = (uint8_t*)workspace + ZXC_STATIC_CCTX_HDR_SIZE; if (UNLIKELY(zxc_cctx_init_in_workspace(&cctx->inner, inner_ws, inner_sz, block_size, 1, level, checksum_enabled, 0, 0) != ZXC_OK)) return NULL; cctx->owns_workspace = 1; cctx->initialized = 1; cctx->last_block_size = block_size; cctx->stored_level = level; cctx->stored_block_size = block_size; cctx->stored_checksum = checksum_enabled; return cctx; } /** * @brief Workspace size needed for a static decompression context. * * Public API; see @c zxc_buffer.h. Sum of the cache-line-aligned handle header * and the persistent buffer that @ref zxc_init_static_dctx carves for the given * @p block_size. Performs no allocation. * * @param[in] block_size Block size the context will be pinned to. * @return Required workspace size in bytes, or 0 if @p block_size is invalid. */ size_t zxc_static_dctx_workspace_size(const size_t block_size) { if (UNLIKELY(!zxc_validate_block_size(block_size))) return 0; const size_t inner_sz = zxc_cctx_compute_workspace_size(block_size, 0, 0, 0); if (UNLIKELY(inner_sz == 0)) return 0; return ZXC_STATIC_DCTX_HDR_SIZE + inner_sz; } /** * @brief Initialises a decompression context inside a caller-supplied workspace. * * Public API; full contract in @c zxc_buffer.h. Places the opaque handle at the * start of @p workspace and carves the persistent buffer in the aligned tail - * no heap allocation. The block size is pinned, so decoded archives must match * it; @ref zxc_free_dctx becomes a no-op (the caller owns @p workspace). * * @param[in] workspace Caller buffer (>= @ref zxc_static_dctx_workspace_size). * @param[in] workspace_size Capacity of @p workspace in bytes. * @param[in] block_size Block size to pin the context to. * @return A ready context owned by @p workspace, or NULL on invalid input or an * undersized workspace. */ zxc_dctx* zxc_init_static_dctx(void* RESTRICT workspace, const size_t workspace_size, const size_t block_size) { if (UNLIKELY(!workspace)) return NULL; if (UNLIKELY(!zxc_validate_block_size(block_size))) return NULL; const size_t inner_sz = zxc_cctx_compute_workspace_size(block_size, 0, 0, 0); if (UNLIKELY(inner_sz == 0)) return NULL; if (UNLIKELY(workspace_size < ZXC_STATIC_DCTX_HDR_SIZE + inner_sz)) return NULL; zxc_dctx* const dctx = (zxc_dctx*)workspace; ZXC_MEMSET(dctx, 0, sizeof(*dctx)); uint8_t* const inner_ws = (uint8_t*)workspace + ZXC_STATIC_DCTX_HDR_SIZE; /* mode == 0 init: checksum_enabled is updated per-call from the file * header flags, so it does not need to be locked at workspace init. */ if (UNLIKELY(zxc_cctx_init_in_workspace(&dctx->inner, inner_ws, inner_sz, block_size, 0, 0, 0, 0, 0) != ZXC_OK)) return NULL; dctx->owns_workspace = 1; dctx->initialized = 1; dctx->last_block_size = block_size; return dctx; } zxc-0.13.0/src/lib/zxc_driver.c000066400000000000000000001453221522536370400162750ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file zxc_driver.c * @brief Userspace @c FILE*-flavored driver: multi-threaded streaming and * the seekable @c FILE* open helper. * * Two distinct subsystems live in this translation unit because they share * the same userspace-only host requirements (@c , threading, and * platform file-descriptor extraction): keeping them together means a * single TU to exclude when building for kernel / freestanding targets. * * 1. Streaming engine: a ring-buffer producer / worker / consumer * pipeline that parallelises block processing over @c FILE* streams. * Public API: @ref zxc_stream_compress, @ref zxc_stream_decompress, * @ref zxc_stream_get_decompressed_size. * * 2. Seekable @c FILE* wrapper: builds a @ref zxc_reader_t whose * @c read_at uses @c pread / @c ReadFile on the file descriptor * extracted from a @c FILE*, then delegates to * @ref zxc_seekable_open_reader. Public API: * @ref zxc_seekable_open_file. */ #include #include #include #include "../../include/zxc_buffer.h" #include "../../include/zxc_dict.h" #include "../../include/zxc_error.h" #include "../../include/zxc_seekable.h" #include "../../include/zxc_stream.h" #include "zxc_internal.h" /* * ============================================================================ * WINDOWS THREADING EMULATION * ============================================================================ * Maps POSIX pthread calls to Windows Native API (CriticalSection, * ConditionVariable, Threads). Allows the same threading logic to compile on * Linux/macOS and Windows. */ #if defined(_WIN32) #include /* _get_osfhandle, _fileno (used by zxc_seekable_open_file) */ #include #include #include #include // Map POSIX file positioning functions to Windows equivalents #define fseeko _fseeki64 #define ftello _ftelli64 /** * @brief Returns the logical-processor count (backs the @c sysconf shim below). * @return Number of processors reported by @c GetSystemInfo. */ static int zxc_get_num_procs(void) { SYSTEM_INFO sysinfo; GetSystemInfo(&sysinfo); return sysinfo.dwNumberOfProcessors; } typedef CRITICAL_SECTION pthread_mutex_t; typedef CONDITION_VARIABLE pthread_cond_t; typedef HANDLE pthread_t; #define pthread_mutex_init(m, a) InitializeCriticalSection(m) #define pthread_mutex_destroy(m) DeleteCriticalSection(m) #define pthread_mutex_lock(m) EnterCriticalSection(m) #define pthread_mutex_unlock(m) LeaveCriticalSection(m) #define pthread_cond_init(c, a) InitializeConditionVariable(c) #define pthread_cond_destroy(c) (void)(0) #define pthread_cond_wait(c, m) SleepConditionVariableCS(c, m, INFINITE) #define pthread_cond_signal(c) WakeConditionVariable(c) #define pthread_cond_broadcast(c) WakeAllConditionVariable(c) /** * @brief Trampoline payload bridging the POSIX @c void*(*)(void*) worker * signature to the @c _beginthreadex entry point. * * Heap-allocated by the @c pthread_create shim and freed by * @ref zxc_win_thread_entry once the captured worker has started. */ typedef struct { void* (*func)(void*); /* worker to invoke */ void* arg; /* argument forwarded to @c func */ } zxc_win_thread_arg_t; /** * @brief @c _beginthreadex entry point: unpacks the trampoline payload, frees * it, then runs the captured POSIX-style worker. * * @param[in] p Heap @ref zxc_win_thread_arg_t handed over by the creator; * ownership transfers to this function. * @return Always 0 (the worker's @c void* result is discarded, as on POSIX). */ static unsigned __stdcall zxc_win_thread_entry(void* p) { zxc_win_thread_arg_t* a = (zxc_win_thread_arg_t*)p; void* (*f)(void*) = a->func; void* arg = a->arg; ZXC_FREE(a); f(arg); return 0; } /** * @brief @c pthread_create shim: spawns @p start_routine(@p arg) via * @c _beginthreadex, matching the POSIX prototype. * * @param[out] thread Receives the thread handle on success. * @param[in] attr Unused (POSIX attribute object); ignored. * @param[in] start_routine Worker to run on the new thread. * @param[in] arg Opaque argument forwarded to @p start_routine. * @return 0 on success, @ref ZXC_ERROR_MEMORY on allocation or spawn failure. */ static int pthread_create(pthread_t* thread, const void* attr, void* (*start_routine)(void*), void* arg) { (void)attr; zxc_win_thread_arg_t* wrapper = ZXC_MALLOC(sizeof(zxc_win_thread_arg_t)); if (UNLIKELY(!wrapper)) return ZXC_ERROR_MEMORY; wrapper->func = start_routine; wrapper->arg = arg; uintptr_t handle = _beginthreadex(NULL, 0, zxc_win_thread_entry, wrapper, 0, NULL); if (UNLIKELY(handle == 0)) { ZXC_FREE(wrapper); return ZXC_ERROR_MEMORY; } *thread = (HANDLE)handle; return 0; } /** * @brief @c pthread_join shim: blocks until @p thread finishes, then closes its * handle. * * @param[in] thread Handle from a successful @c pthread_create. * @param[in] retval Unused (POSIX exit-value out-param); ignored. * @return Always 0. */ static int pthread_join(pthread_t thread, void** retval) { (void)retval; WaitForSingleObject(thread, INFINITE); CloseHandle(thread); return 0; } #define sysconf(x) zxc_get_num_procs() #define _SC_NPROCESSORS_ONLN 0 #else #include #include #endif /* * ============================================================================ * STREAMING ENGINE (Producer / Worker / Consumer) * ============================================================================ * Implements a Ring Buffer architecture to parallelize block processing. */ /** * @enum job_status_t * @brief Represents the lifecycle states of a processing job within the ring * buffer. * * @var JOB_STATUS_FREE * The job slot is empty and available to be filled with new data by the * writer. * @var JOB_STATUS_FILLED * The job slot has been populated with input data and is ready for * processing by a worker. * @var JOB_STATUS_PROCESSED * The worker has finished processing the data; the result is ready to be * consumed/written out. */ typedef enum { JOB_STATUS_FREE, JOB_STATUS_FILLED, JOB_STATUS_PROCESSED } job_status_t; /** * @struct zxc_stream_job_t * @brief Represents a single unit of work (a chunk of data) to be processed. * * This structure holds the input and output buffers for a specific chunk of * data, along with its processing status. It is padded to align with cache * lines to prevent false sharing in a multi-threaded environment. * * @var zxc_stream_job_t::in_buf * Pointer to the buffer containing raw input data. * @var zxc_stream_job_t::in_cap * The total allocated capacity of the input buffer. * @var zxc_stream_job_t::in_sz * The actual size of the valid data currently in the input buffer. * @var zxc_stream_job_t::out_buf * Pointer to the buffer where processed (compressed/decompressed) data is * stored. * @var zxc_stream_job_t::out_cap * The total allocated capacity of the output buffer. * @var zxc_stream_job_t::result_sz * The actual size of the valid data produced in the output buffer. * @var zxc_stream_job_t::job_id * A unique identifier for the job, often used for ordering or debugging. * @var zxc_stream_job_t::status * The current state of this job (Free, Filled, or Processed). * @var zxc_stream_job_t::pad * Padding bytes to ensure the structure size aligns with the cache line * size (@c ZXC_CACHE_LINE_SIZE), minimizing cache contention between threads * accessing adjacent jobs. */ typedef struct { uint8_t* in_buf; size_t in_cap; size_t in_sz; uint8_t* out_buf; size_t out_cap; size_t result_sz; int job_id; ZXC_ATOMIC job_status_t status; // Atomic for lock-free status updates char pad[ZXC_CACHE_LINE_SIZE]; // Prevent False Sharing } zxc_stream_job_t; /** * @typedef zxc_chunk_processor_t * @brief Function pointer type for processing a chunk of data. * * This type defines the signature for internal functions responsible for * processing (compressing or transforming) a specific chunk of input data. * * @param ctx Pointer to the compression context containing state and * configuration. * @param in Pointer to the input data buffer. * @param in_sz Size of the input data in bytes. * @param out Pointer to the output buffer where processed data will be * written. * @param out_cap Capacity of the output buffer in bytes. * * @return The number of bytes written to the output buffer on success, or a * negative error code on failure. */ typedef int (*zxc_chunk_processor_t)(zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT in, const size_t in_sz, uint8_t* RESTRICT out, const size_t out_cap); /** * @struct zxc_stream_ctx_t * @brief The main context structure managing the streaming * compression/decompression state. * * This structure orchestrates the producer-consumer workflow. It manages the * ring buffer of jobs, the worker queue, synchronization primitives (mutexes * and condition variables), and configuration settings for the compression * algorithm. * * @var zxc_stream_ctx_t::jobs * Array of job structures acting as the ring buffer. * @var zxc_stream_ctx_t::ring_size * The total number of slots in the jobs array. * @var zxc_stream_ctx_t::worker_queue * A circular queue containing indices of jobs ready to be picked up by * worker threads. * @var zxc_stream_ctx_t::wq_head * Index of the head of the worker queue (where workers take jobs). * @var zxc_stream_ctx_t::wq_tail * Index of the tail of the worker queue (where the writer adds jobs). * @var zxc_stream_ctx_t::wq_count * Current number of items in the worker queue. * @var zxc_stream_ctx_t::lock * Mutex used to protect access to shared resources (queue indices, status * changes). * @var zxc_stream_ctx_t::cond_reader * Condition variable to signal the output thread (reader) that processed * data is available. * @var zxc_stream_ctx_t::cond_worker * Condition variable to signal worker threads that new work is available. * @var zxc_stream_ctx_t::cond_writer * Condition variable to signal the input thread (writer) that job slots * are free. * @var zxc_stream_ctx_t::shutdown_workers * Flag indicating that worker threads should terminate. * @var zxc_stream_ctx_t::compression_mode * Indicates the operation mode (e.g., compression or decompression). * @var zxc_stream_ctx_t::io_error * Atomic flag to signal if an I/O error occurred during processing. * @var zxc_stream_ctx_t::processor * Function pointer or object responsible for the actual chunk processing * logic. * @var zxc_stream_ctx_t::write_idx * The index of the next job slot to be written to by the main thread. * @var zxc_stream_ctx_t::compression_level * The configured level of compression (trading off speed vs. ratio). * @var zxc_stream_ctx_t::chunk_size * The size of each data chunk to be processed. * @var zxc_stream_ctx_t::checksum_enabled * Flag indicating whether checksum verification/generation is active. * @var zxc_stream_ctx_t::file_has_checksum * Flag indicating whether the input file includes checksums. * @var zxc_stream_ctx_t::progress_cb * Optional callback function for reporting progress during processing. * @var zxc_stream_ctx_t::progress_user_data * User data pointer to be passed to the progress callback function. * @var zxc_stream_ctx_t::total_input_bytes * Total size of the input data in bytes, used for progress tracking. * @var zxc_stream_ctx_t::dict * Pointer to the optional dictionary buffer used to prime * compression/decompression, NULL when no dictionary is in use. * @var zxc_stream_ctx_t::dict_size * Size of the dictionary in bytes, 0 when no dictionary is in use. * @var zxc_stream_ctx_t::dict_huf * Shared dictionary literal Huffman table (128-byte packed code-lengths * header), NULL when absent. */ typedef struct { zxc_stream_job_t* jobs; size_t ring_size; int* worker_queue; int wq_head; int wq_tail; int wq_count; pthread_mutex_t lock; pthread_cond_t cond_reader; pthread_cond_t cond_worker; pthread_cond_t cond_writer; int shutdown_workers; int compression_mode; ZXC_ATOMIC int io_error; zxc_chunk_processor_t processor; int write_idx; int compression_level; size_t chunk_size; int checksum_enabled; int file_has_checksum; zxc_progress_callback_t progress_cb; void* progress_user_data; uint64_t total_input_bytes; const uint8_t* dict; size_t dict_size; const uint8_t* dict_huf; /**< Shared dictionary literal table (128-byte packed code-lengths header), NULL when absent. */ } zxc_stream_ctx_t; /** * @struct writer_args_t * @brief Structure containing arguments for the writer callback function. * * This structure is used to pass necessary context and state information * to the function responsible for writing compressed or decompressed data * to a file stream. * * @var writer_args_t::ctx * Pointer to the ZXC stream context, holding the state of the * compression/decompression stream. * * @var writer_args_t::f * Pointer to the output file stream where data will be written. * * @var writer_args_t::total_bytes * Accumulator for the total number of bytes written to the file so far. * * @var writer_args_t::global_hash * The global hash accumulated during processing. * * @var writer_args_t::bytes_processed * The number of bytes processed so far, used for progress reporting. * * @var writer_args_t::seek_comp * Array of compressed block sizes for seek table construction. * * @var writer_args_t::seek_count * Number of entries in the seek table. * * @var writer_args_t::seek_cap * Capacity of the seek table array. */ typedef struct { zxc_stream_ctx_t* ctx; FILE* f; int64_t total_bytes; uint32_t global_hash; uint64_t bytes_processed; // For progress callback uint32_t* seek_comp; uint32_t seek_count; uint32_t seek_cap; } writer_args_t; /** * @brief Worker thread function for parallel stream processing. * * This function serves as the entry point for worker threads in the ZXC * streaming compression/decompression context. It continuously retrieves jobs * from a shared work queue, processes them using a thread-local compression * context (`zxc_cctx_t`), and signals the writer thread upon completion. * * **Worker Lifecycle & Synchronization:** * 1. **Initialization:** Allocates a thread-local `zxc_cctx_t` to avoid lock * contention during compression/decompression. * 2. **Wait Loop:** Uses `pthread_cond_wait` on `cond_worker` to sleep until a * job is available in the `worker_queue`. * 3. **Job Retrieval:** Dequeues a job ID from the ring buffer. The * `worker_queue` acts as a load balancer. * 4. **Processing:** Calls `ctx->processor` (the compression/decompression * function) on the job's data. This is the CPU-intensive part and runs in * parallel. * 5. **Completion:** Updates `job->status` to `JOB_STATUS_PROCESSED`. * 6. **Signaling:** If the processed job is the *next* one expected by the * writer * (`jid == ctx->write_idx`), it signals `cond_writer`. This optimization * prevents unnecessary wake-ups of the writer thread for out-of-order * completions. * * @param[in] arg A pointer to the shared stream context (`zxc_stream_ctx_t`). * @return Always returns NULL. */ static void* zxc_stream_worker(void* arg) { zxc_stream_ctx_t* const ctx = (zxc_stream_ctx_t*)arg; zxc_cctx_t cctx; const int unified_chk = (ctx->compression_mode == 1) ? ctx->checksum_enabled : (ctx->file_has_checksum && ctx->checksum_enabled); const size_t eff_chunk = (ctx->dict_size > 0 && ctx->compression_mode == 1) ? zxc_block_size_ceil(ctx->dict_size + ctx->chunk_size) : ctx->chunk_size; if (UNLIKELY(zxc_cctx_init(&cctx, eff_chunk, ctx->compression_mode, ctx->compression_level, unified_chk, ctx->dict_size) != ZXC_OK || zxc_cctx_attach_dict_huf(&cctx, ctx->dict_huf) != ZXC_OK)) { // LCOV_EXCL_START zxc_cctx_free(&cctx); pthread_mutex_lock(&ctx->lock); ctx->io_error = 1; pthread_cond_broadcast(&ctx->cond_writer); pthread_cond_broadcast(&ctx->cond_reader); pthread_mutex_unlock(&ctx->lock); return NULL; // LCOV_EXCL_STOP } cctx.compression_level = ctx->compression_level; /* Per-worker dict buffer for assembling [dict | block_data] */ const size_t dsz = ctx->dict_size; uint8_t* const dict_work = cctx.dict_buffer; if (dict_work) ZXC_MEMCPY(dict_work, ctx->dict, dsz); while (1) { zxc_stream_job_t* job = NULL; pthread_mutex_lock(&ctx->lock); while (ctx->wq_count == 0 && !ctx->shutdown_workers) { pthread_cond_wait(&ctx->cond_worker, &ctx->lock); } if (ctx->shutdown_workers && ctx->wq_count == 0) { pthread_mutex_unlock(&ctx->lock); break; } const int jid = ctx->worker_queue[ctx->wq_tail]; ctx->wq_tail = (ctx->wq_tail + 1) % ctx->ring_size; ctx->wq_count--; job = &ctx->jobs[jid]; pthread_mutex_unlock(&ctx->lock); int res; if (dict_work && ctx->compression_mode == 1) { ZXC_MEMCPY(dict_work + dsz, job->in_buf, job->in_sz); res = ctx->processor(&cctx, dict_work, dsz + job->in_sz, job->out_buf, job->out_cap); } else if (dict_work && ctx->compression_mode == 0) { res = ctx->processor(&cctx, job->in_buf, job->in_sz, dict_work + dsz, ctx->chunk_size + ZXC_DECOMPRESS_TAIL_PAD); if (LIKELY(res > 0)) ZXC_MEMCPY(job->out_buf, dict_work + dsz, (size_t)res); } else { res = ctx->processor(&cctx, job->in_buf, job->in_sz, job->out_buf, job->out_cap); } pthread_mutex_lock(&ctx->lock); job->result_sz = UNLIKELY(res < 0) ? 0 : (size_t)res; job->status = JOB_STATUS_PROCESSED; if (UNLIKELY(res < 0)) { ctx->io_error = 1; pthread_cond_broadcast(&ctx->cond_writer); pthread_cond_broadcast(&ctx->cond_reader); } else if (jid == ctx->write_idx) { pthread_cond_signal(&ctx->cond_writer); } pthread_mutex_unlock(&ctx->lock); } zxc_cctx_free(&cctx); return NULL; } /** * @brief Asynchronous writer thread function. * * This function runs as a separate thread responsible for writing processed * data chunks to the output file. It operates on a ring buffer of jobs shared * with the reader and worker threads. * * **Ordering Enforcement:** * The writer MUST write blocks in the exact order they were read. Even if * worker threads finish jobs out of order (e.g., job 2 finishes before job 1), * the writer waits for `ctx->write_idx` (job 1) to be `JOB_STATUS_PROCESSED`. * * **Workflow:** * 1. **Wait:** Sleeps on `cond_writer` until the job at `ctx->write_idx` is * ready. * 2. **Write:** Writes the `out_buf` to the file. * 3. **Release:** Sets the job status to `JOB_STATUS_FREE` and signals * `cond_reader`, allowing the main thread to reuse this slot for new input. * 4. **Advance:** Increments `ctx->write_idx` to wait for the next sequential * block. * * @param[in] arg Pointer to a `writer_args_t` structure containing the stream * context, the output file handle, and a counter for total bytes written. * @return Always returns NULL. */ static void* zxc_async_writer(void* arg) { writer_args_t* const args = (writer_args_t*)arg; zxc_stream_ctx_t* const ctx = args->ctx; while (1) { zxc_stream_job_t* const job = &ctx->jobs[ctx->write_idx]; pthread_mutex_lock(&ctx->lock); while (job->status != JOB_STATUS_PROCESSED && !ctx->io_error) pthread_cond_wait(&ctx->cond_writer, &ctx->lock); const size_t result_sz = job->result_sz; const size_t in_sz = job->in_sz; pthread_mutex_unlock(&ctx->lock); if (result_sz == (size_t)-1) break; if (args->f && result_sz > 0) { if (fwrite(job->out_buf, 1, result_sz, args->f) != result_sz) { pthread_mutex_lock(&ctx->lock); ctx->io_error = 1; pthread_cond_signal(&ctx->cond_reader); pthread_mutex_unlock(&ctx->lock); } else if (ctx->checksum_enabled && ctx->compression_mode == 1) { // Update Global Hash (Rotation + XOR) if (LIKELY(result_sz >= ZXC_GLOBAL_CHECKSUM_SIZE)) { uint32_t block_hash = zxc_le32(job->out_buf + result_sz - ZXC_GLOBAL_CHECKSUM_SIZE); args->global_hash = zxc_hash_combine_rotate(args->global_hash, block_hash); } } } if (UNLIKELY(ctx->io_error)) { pthread_mutex_lock(&ctx->lock); job->status = JOB_STATUS_FREE; pthread_cond_signal(&ctx->cond_reader); pthread_mutex_unlock(&ctx->lock); break; } args->total_bytes += (int64_t)result_sz; /* Seekable: record compressed block size */ if (args->seek_comp && ctx->compression_mode == 1) { if (UNLIKELY(args->seek_count >= args->seek_cap)) { args->seek_cap = args->seek_cap * 2; uint32_t* nc = (uint32_t*)ZXC_REALLOC(args->seek_comp, args->seek_cap * sizeof(uint32_t)); // LCOV_EXCL_START if (UNLIKELY(!nc)) { pthread_mutex_lock(&ctx->lock); ctx->io_error = 1; job->status = JOB_STATUS_FREE; pthread_cond_signal(&ctx->cond_reader); pthread_mutex_unlock(&ctx->lock); break; } // LCOV_EXCL_STOP args->seek_comp = nc; } args->seek_comp[args->seek_count++] = (uint32_t)result_sz; } // Update progress callback if (ctx->progress_cb) { // LCOV_EXCL_START args->bytes_processed += ctx->compression_mode == 1 ? in_sz : result_sz; ctx->progress_cb(args->bytes_processed, ctx->total_input_bytes, ctx->progress_user_data); // LCOV_EXCL_STOP } pthread_mutex_lock(&ctx->lock); job->status = JOB_STATUS_FREE; ctx->write_idx = (ctx->write_idx + 1) % ctx->ring_size; pthread_cond_signal(&ctx->cond_reader); pthread_mutex_unlock(&ctx->lock); } return NULL; } /** * @brief Orchestrates the multithreaded streaming compression or decompression * engine. * * This function initializes the stream context, allocates the necessary ring * buffer memory for jobs and I/O buffers, and spawns the worker threads and the * asynchronous writer thread. It acts as the main "producer" (reader) loop. * * **Architecture: Producer-Consumer with Ring Buffer** * - **Ring Buffer:** A fixed-size array of `zxc_stream_job_t` structures. * - **Producer (Main Thread):** Reads chunks from `f_in` and fills "Free" slots * in the ring buffer. It blocks if no slots are free (backpressure). * - **Workers:** Pick up "Filled" jobs from a queue, process them, and mark * them as "Processed". * - **Consumer (Writer Thread):** Waits for the *next sequential* job to be * "Processed", writes it to `f_out`, and marks the slot as "Free". * * **Double-Buffering & Zero-Copy:** * We allocate `alloc_in` and `alloc_out` buffers for each job. The reader reads * directly into `in_buf`, and the writer writes directly from `out_buf`, * minimizing memory copies. * * @param[in] f_in Input file stream (source). * @param[out] f_out Output file stream (destination). * @param[in] n_threads Worker thread count; 0 or less auto-detects the * number of online processors. * @param[in] mode 1 for compression, 0 for decompression. * @param[in] level Compression level (compression mode only). * @param[in] block_size Block size in bytes (compression mode). * @param[in] checksum_enabled Non-zero to generate / verify checksums. * @param[in] seekable Non-zero to emit a seek table (compression mode). * @param[in] func Chunk processor (compression or decompression). * @param[in] progress_cb Optional progress callback, or NULL. * @param[in] user_data Opaque pointer passed to @p progress_cb. * @param[in] dict Optional dictionary content, or NULL. * @param[in] dict_size Dictionary length in bytes (0 if none). * @param[in] dict_huf Optional shared literal Huffman table, or NULL. * @return Total bytes written to the output on success, or a negative * @ref zxc_error_t code. */ static int64_t zxc_stream_engine_run(FILE* f_in, FILE* f_out, const int n_threads, const int mode, const int level, const size_t block_size, const int checksum_enabled, const int seekable, zxc_chunk_processor_t func, zxc_progress_callback_t progress_cb, void* user_data, const uint8_t* dict, const size_t dict_size, const uint8_t* dict_huf) { zxc_stream_ctx_t ctx; ZXC_MEMSET(&ctx, 0, sizeof(ctx)); size_t runtime_chunk_sz = (block_size > 0) ? block_size : ZXC_BLOCK_SIZE_DEFAULT; int file_has_chk = 0; // Try to get input file size for progress tracking (compression mode only) // For decompression, the CLI precomputes the size and passes it via user_data uint64_t total_file_size = 0; if (mode == 1 && progress_cb) { // LCOV_EXCL_START const long long saved_pos = ftello(f_in); if (saved_pos >= 0 && fseeko(f_in, 0, SEEK_END) == 0) { const long long size = ftello(f_in); if (size > 0) total_file_size = (uint64_t)size; fseeko(f_in, saved_pos, SEEK_SET); } // LCOV_EXCL_STOP } if (mode == 0) { // Decompression Mode: Read and validate file header uint8_t h[ZXC_FILE_HEADER_SIZE]; uint32_t header_dict_id = 0; if (UNLIKELY(fread(h, 1, ZXC_FILE_HEADER_SIZE, f_in) != ZXC_FILE_HEADER_SIZE || zxc_read_file_header(h, ZXC_FILE_HEADER_SIZE, &runtime_chunk_sz, &file_has_chk, &header_dict_id) != ZXC_OK)) return ZXC_ERROR_BAD_HEADER; if (header_dict_id != 0) { if (UNLIKELY(!dict || dict_size == 0)) return ZXC_ERROR_DICT_REQUIRED; if (UNLIKELY(zxc_dict_id(dict, dict_size, dict_huf) != header_dict_id)) return ZXC_ERROR_DICT_MISMATCH; } } int num_threads = (n_threads > 0) ? n_threads : (int)sysconf(_SC_NPROCESSORS_ONLN); if (num_threads > ZXC_MAX_THREADS) num_threads = ZXC_MAX_THREADS; // Reserve 1 thread for Writer/Reader overhead if possible const int num_workers = (num_threads > 1) ? num_threads - 1 : 1; ctx.compression_mode = mode; ctx.processor = func; ctx.io_error = 0; ctx.compression_level = level; ctx.ring_size = (size_t)num_workers * 4U; ctx.chunk_size = runtime_chunk_sz; ctx.checksum_enabled = checksum_enabled; ctx.file_has_checksum = mode == 1 ? checksum_enabled : file_has_chk; ctx.progress_cb = progress_cb; ctx.progress_user_data = user_data; ctx.total_input_bytes = total_file_size; ctx.dict = dict; ctx.dict_size = dict_size; ctx.dict_huf = dict_huf; uint32_t d_global_hash = 0; const uint64_t max_out = zxc_compress_bound(runtime_chunk_sz); const size_t raw_alloc_in = (size_t)((mode ? runtime_chunk_sz : max_out) + ZXC_PAD_SIZE); const size_t alloc_in = (raw_alloc_in + ZXC_ALIGNMENT_MASK) & ~ZXC_ALIGNMENT_MASK; const size_t raw_alloc_out = (size_t)((mode ? max_out : runtime_chunk_sz + ZXC_DECOMPRESS_TAIL_PAD) + ZXC_PAD_SIZE); const size_t alloc_out = (raw_alloc_out + ZXC_ALIGNMENT_MASK) & ~ZXC_ALIGNMENT_MASK; const size_t per_job_sz = sizeof(zxc_stream_job_t) + sizeof(int) + alloc_in + alloc_out; const size_t alloc_size = ctx.ring_size * per_job_sz; uint8_t* const mem_block = ZXC_ALIGNED_MALLOC(alloc_size, ZXC_CACHE_LINE_SIZE); if (UNLIKELY(!mem_block || per_job_sz > SIZE_MAX / ctx.ring_size)) { // LCOV_EXCL_START ZXC_ALIGNED_FREE(mem_block); return ZXC_ERROR_MEMORY; // LCOV_EXCL_STOP } uint8_t* ptr = mem_block; ctx.jobs = (zxc_stream_job_t*)ptr; ptr += ctx.ring_size * sizeof(zxc_stream_job_t); ctx.worker_queue = (int*)ptr; ptr += ctx.ring_size * sizeof(int); uint8_t* buf_in = ptr; ptr += ctx.ring_size * alloc_in; uint8_t* buf_out = ptr; ZXC_MEMSET(mem_block, 0, ctx.ring_size * (sizeof(zxc_stream_job_t) + sizeof(int))); for (size_t i = 0; i < ctx.ring_size; i++) { ctx.jobs[i].job_id = (int)i; ctx.jobs[i].status = JOB_STATUS_FREE; ctx.jobs[i].in_buf = buf_in + (i * alloc_in); ctx.jobs[i].in_cap = alloc_in - ZXC_PAD_SIZE; ctx.jobs[i].in_sz = 0; ctx.jobs[i].out_buf = buf_out + (i * alloc_out); ctx.jobs[i].out_cap = alloc_out - ZXC_PAD_SIZE; ctx.jobs[i].result_sz = 0; } pthread_mutex_init(&ctx.lock, NULL); pthread_cond_init(&ctx.cond_reader, NULL); pthread_cond_init(&ctx.cond_worker, NULL); pthread_cond_init(&ctx.cond_writer, NULL); pthread_t* const workers = ZXC_MALLOC((size_t)num_workers * sizeof(pthread_t)); if (UNLIKELY(!workers)) { // LCOV_EXCL_START ZXC_ALIGNED_FREE(mem_block); return ZXC_ERROR_MEMORY; // LCOV_EXCL_STOP } int started_workers = 0; for (int i = 0; i < num_workers; i++) { if (UNLIKELY(pthread_create(&workers[i], NULL, zxc_stream_worker, &ctx) != 0)) break; started_workers++; } if (UNLIKELY(started_workers == 0)) { // LCOV_EXCL_START pthread_cond_destroy(&ctx.cond_writer); pthread_cond_destroy(&ctx.cond_worker); pthread_cond_destroy(&ctx.cond_reader); pthread_mutex_destroy(&ctx.lock); ZXC_FREE(workers); ZXC_ALIGNED_FREE(mem_block); return ZXC_ERROR_MEMORY; // LCOV_EXCL_STOP } writer_args_t w_args = {&ctx, f_out, 0, 0, 0, NULL, 0, 0}; /* Seekable: allocate initial block-size tracking array */ if (mode == 1 && seekable) { w_args.seek_cap = 64; w_args.seek_comp = (uint32_t*)ZXC_MALLOC(w_args.seek_cap * sizeof(uint32_t)); // LCOV_EXCL_START if (UNLIKELY(!w_args.seek_comp)) { pthread_mutex_lock(&ctx.lock); ctx.shutdown_workers = 1; pthread_cond_broadcast(&ctx.cond_worker); pthread_mutex_unlock(&ctx.lock); for (int i = 0; i < started_workers; i++) pthread_join(workers[i], NULL); pthread_cond_destroy(&ctx.cond_writer); pthread_cond_destroy(&ctx.cond_worker); pthread_cond_destroy(&ctx.cond_reader); pthread_mutex_destroy(&ctx.lock); ZXC_FREE(workers); ZXC_ALIGNED_FREE(mem_block); return ZXC_ERROR_MEMORY; } // LCOV_EXCL_STOP } if (mode == 1 && f_out) { uint8_t h[ZXC_FILE_HEADER_SIZE]; zxc_write_file_header(h, ZXC_FILE_HEADER_SIZE, runtime_chunk_sz, checksum_enabled, (dict && dict_size) ? zxc_dict_id(dict, dict_size, dict_huf) : 0); if (UNLIKELY(fwrite(h, 1, ZXC_FILE_HEADER_SIZE, f_out) != ZXC_FILE_HEADER_SIZE)) ctx.io_error = 1; w_args.total_bytes = ZXC_FILE_HEADER_SIZE; } pthread_t writer_th; if (UNLIKELY(pthread_create(&writer_th, NULL, zxc_async_writer, &w_args) != 0)) { // LCOV_EXCL_START pthread_mutex_lock(&ctx.lock); ctx.shutdown_workers = 1; pthread_cond_broadcast(&ctx.cond_worker); pthread_mutex_unlock(&ctx.lock); for (int i = 0; i < started_workers; i++) pthread_join(workers[i], NULL); pthread_cond_destroy(&ctx.cond_writer); pthread_cond_destroy(&ctx.cond_worker); pthread_cond_destroy(&ctx.cond_reader); pthread_mutex_destroy(&ctx.lock); ZXC_FREE(workers); ZXC_ALIGNED_FREE(mem_block); return ZXC_ERROR_MEMORY; // LCOV_EXCL_STOP } int read_idx = 0; int read_eof = 0; uint64_t total_src_bytes = 0; // Reader Loop: Reads from file, prepares jobs, pushes to worker queue. while (!read_eof && !ctx.io_error) { zxc_stream_job_t* const job = &ctx.jobs[read_idx]; pthread_mutex_lock(&ctx.lock); while (job->status != JOB_STATUS_FREE && !ctx.io_error) pthread_cond_wait(&ctx.cond_reader, &ctx.lock); pthread_mutex_unlock(&ctx.lock); if (UNLIKELY(ctx.io_error)) break; size_t read_sz = 0; if (mode == 1) { read_sz = fread(job->in_buf, 1, runtime_chunk_sz, f_in); total_src_bytes += read_sz; if (UNLIKELY(read_sz == 0)) read_eof = 1; } else { uint8_t bh_buf[ZXC_BLOCK_HEADER_SIZE]; size_t h_read = fread(bh_buf, 1, ZXC_BLOCK_HEADER_SIZE, f_in); if (UNLIKELY(h_read < ZXC_BLOCK_HEADER_SIZE)) { read_eof = 1; } else { zxc_block_header_t bh; if (UNLIKELY(zxc_read_block_header(bh_buf, ZXC_BLOCK_HEADER_SIZE, &bh) != ZXC_OK)) { read_eof = 1; goto _job_prepared; } if (bh.block_type == ZXC_BLOCK_EOF) { if (UNLIKELY(bh.comp_size != 0)) { ctx.io_error = 1; goto _job_prepared; } read_eof = 1; read_sz = 0; goto _job_prepared; } const int has_crc = ctx.file_has_checksum; const size_t checksum_sz = (has_crc ? ZXC_BLOCK_CHECKSUM_SIZE : 0); const size_t body_total = bh.comp_size + checksum_sz; const size_t total_len = ZXC_BLOCK_HEADER_SIZE + body_total; if (UNLIKELY(total_len > job->in_cap)) { ctx.io_error = 1; break; } ZXC_MEMCPY(job->in_buf, bh_buf, ZXC_BLOCK_HEADER_SIZE); // Single fread for body + checksum (reduces syscalls) const size_t body_read = fread(job->in_buf + ZXC_BLOCK_HEADER_SIZE, 1, body_total, f_in); if (UNLIKELY(body_read != body_total)) { ctx.io_error = 1; break; } else if (has_crc) { // Update Global Hash for Decompression const uint32_t b_crc = zxc_le32(job->in_buf + ZXC_BLOCK_HEADER_SIZE + bh.comp_size); d_global_hash = zxc_hash_combine_rotate(d_global_hash, b_crc); } read_sz = ZXC_BLOCK_HEADER_SIZE + body_read; } } _job_prepared: if (UNLIKELY(read_eof && read_sz == 0)) break; job->in_sz = read_sz; pthread_mutex_lock(&ctx.lock); job->status = JOB_STATUS_FILLED; ctx.worker_queue[ctx.wq_head] = read_idx; ctx.wq_head = (ctx.wq_head + 1) % ctx.ring_size; ctx.wq_count++; read_idx = (read_idx + 1) % ctx.ring_size; pthread_cond_signal(&ctx.cond_worker); pthread_mutex_unlock(&ctx.lock); if (UNLIKELY(read_sz < runtime_chunk_sz && mode == 1)) read_eof = 1; } zxc_stream_job_t* const end_job = &ctx.jobs[read_idx]; pthread_mutex_lock(&ctx.lock); while (end_job->status != JOB_STATUS_FREE && !ctx.io_error) pthread_cond_wait(&ctx.cond_reader, &ctx.lock); end_job->result_sz = (size_t)-1; end_job->status = JOB_STATUS_PROCESSED; pthread_cond_broadcast(&ctx.cond_writer); pthread_mutex_unlock(&ctx.lock); pthread_join(writer_th, NULL); pthread_mutex_lock(&ctx.lock); ctx.shutdown_workers = 1; pthread_cond_broadcast(&ctx.cond_worker); pthread_mutex_unlock(&ctx.lock); for (int i = 0; i < started_workers; i++) pthread_join(workers[i], NULL); pthread_cond_destroy(&ctx.cond_writer); pthread_cond_destroy(&ctx.cond_worker); pthread_cond_destroy(&ctx.cond_reader); pthread_mutex_destroy(&ctx.lock); // Write EOF Block + optional Seek Table + Footer if compression and no error if (mode == 1 && !ctx.io_error && w_args.total_bytes >= 0) { /* EOF block */ uint8_t eof_buf[ZXC_BLOCK_HEADER_SIZE]; const zxc_block_header_t eof_bh = { .block_type = ZXC_BLOCK_EOF, .block_flags = 0, .reserved = 0, .comp_size = 0}; zxc_write_block_header(eof_buf, ZXC_BLOCK_HEADER_SIZE, &eof_bh); if (UNLIKELY(f_out && fwrite(eof_buf, 1, ZXC_BLOCK_HEADER_SIZE, f_out) != ZXC_BLOCK_HEADER_SIZE)) ctx.io_error = 1; else w_args.total_bytes += ZXC_BLOCK_HEADER_SIZE; /* Seekable: write SEK block between EOF and footer */ if (!ctx.io_error && w_args.seek_comp && w_args.seek_count > 0) { const size_t st_size = zxc_seek_table_size(w_args.seek_count); uint8_t* const st_buf = (uint8_t*)ZXC_MALLOC(st_size); if (st_buf) { const int64_t st_val = zxc_write_seek_table(st_buf, st_size, w_args.seek_comp, w_args.seek_count); if (st_val > 0 && f_out && fwrite(st_buf, 1, (size_t)st_val, f_out) == (size_t)st_val) w_args.total_bytes += st_val; ZXC_FREE(st_buf); } } /* Footer */ uint8_t footer_buf[ZXC_FILE_FOOTER_SIZE]; zxc_write_file_footer(footer_buf, ZXC_FILE_FOOTER_SIZE, total_src_bytes, w_args.global_hash, checksum_enabled); if (UNLIKELY(f_out && fwrite(footer_buf, 1, ZXC_FILE_FOOTER_SIZE, f_out) != ZXC_FILE_FOOTER_SIZE)) ctx.io_error = 1; else w_args.total_bytes += ZXC_FILE_FOOTER_SIZE; } else if (mode == 0 && !ctx.io_error) { /* * After the EOF block, the stream may contain: * (a) [FOOTER 12B] - no seekable table * (b) [SEK header 8B] [payload] [FOOTER 12B] - seekable archive */ uint8_t peek_buf[ZXC_BLOCK_HEADER_SIZE]; uint8_t footer[ZXC_FILE_FOOTER_SIZE]; if (UNLIKELY(fread(peek_buf, 1, ZXC_BLOCK_HEADER_SIZE, f_in) != ZXC_BLOCK_HEADER_SIZE)) { ctx.io_error = 1; } else { zxc_block_header_t peek_bh; const int is_sek = (zxc_read_block_header(peek_buf, ZXC_BLOCK_HEADER_SIZE, &peek_bh) == ZXC_OK && peek_bh.block_type == ZXC_BLOCK_SEK); if (is_sek) { /* Drain the SEK payload (read + discard) */ size_t remaining = (size_t)peek_bh.comp_size; uint8_t discard[512]; while (remaining > 0 && !ctx.io_error) { const size_t chunk = remaining < sizeof(discard) ? remaining : sizeof(discard); if (UNLIKELY(fread(discard, 1, chunk, f_in) != chunk)) ctx.io_error = 1; remaining -= chunk; } /* Read full 12-byte footer */ if (!ctx.io_error && UNLIKELY(fread(footer, 1, ZXC_FILE_FOOTER_SIZE, f_in) != ZXC_FILE_FOOTER_SIZE)) ctx.io_error = 1; } else { /* peek_buf contains the first 8 bytes of the 12-byte footer. * Read the remaining 4 bytes and assemble. */ ZXC_MEMCPY(footer, peek_buf, ZXC_BLOCK_HEADER_SIZE); const size_t tail = ZXC_FILE_FOOTER_SIZE - ZXC_BLOCK_HEADER_SIZE; /* 4 */ if (UNLIKELY(fread(footer + ZXC_BLOCK_HEADER_SIZE, 1, tail, f_in) != tail)) ctx.io_error = 1; } } /* Verify Footer Content: Source Size and Global Checksum */ if (!ctx.io_error) { int valid = (zxc_le64(footer) == (uint64_t)w_args.total_bytes); if (valid && checksum_enabled && ctx.file_has_checksum) valid = (zxc_le32(footer + sizeof(uint64_t)) == d_global_hash); if (UNLIKELY(!valid)) ctx.io_error = 1; } } ZXC_FREE(w_args.seek_comp); ZXC_FREE(workers); ZXC_ALIGNED_FREE(mem_block); if (UNLIKELY(ctx.io_error)) return ZXC_ERROR_IO; return w_args.total_bytes; } /** * @brief Compresses a @c FILE* stream to another @c FILE* stream. * * Public API; full contract in @c zxc_stream.h. Resolves the options (threads, * level, block size, checksums, seekable, dictionary) with their defaults, then * drives @ref zxc_stream_engine_run in compression mode with the * compress chunk processor. * * @param[in] f_in Input stream (must be non-NULL). * @param[out] f_out Output stream (NULL performs a dry run / size estimate). * @param[in] opts Compression options, or NULL for all defaults. * @return Total bytes written on success, or a negative @ref zxc_error_t. */ int64_t zxc_stream_compress(FILE* f_in, FILE* f_out, const zxc_compress_opts_t* opts) { if (UNLIKELY(!f_in)) return ZXC_ERROR_NULL_INPUT; const int n_threads = opts ? opts->n_threads : 0; const int checksum_enabled = opts ? opts->checksum_enabled : 0; const int seekable = opts ? opts->seekable : 0; const int level = (opts && opts->level > 0) ? opts->level : ZXC_LEVEL_DEFAULT; const size_t block_size = (opts && opts->block_size > 0) ? opts->block_size : ZXC_BLOCK_SIZE_DEFAULT; const uint8_t* dict = opts ? (const uint8_t*)opts->dict : NULL; const size_t dict_size = (opts && opts->dict) ? opts->dict_size : 0; zxc_progress_callback_t cb = opts ? opts->progress_cb : NULL; void* ud = opts ? opts->user_data : NULL; if (UNLIKELY(!zxc_validate_block_size(block_size))) return ZXC_ERROR_BAD_BLOCK_SIZE; if (UNLIKELY(dict_size > ZXC_DICT_SIZE_MAX)) return ZXC_ERROR_DICT_TOO_LARGE; const uint8_t* dict_huf = (opts && opts->dict) ? (const uint8_t*)opts->dict_huf : NULL; return zxc_stream_engine_run(f_in, f_out, n_threads, 1, level, block_size, checksum_enabled, seekable, zxc_compress_chunk_wrapper, cb, ud, dict, dict_size, dict_huf); } /** * @brief Decompresses a @c FILE* stream to another @c FILE* stream. * * Public API; full contract in @c zxc_stream.h. Resolves the options (threads, * checksums, dictionary), then drives @ref zxc_stream_engine_run in * decompression mode with the decompress chunk processor. The block size and * level are recovered from the archive header, not from @p opts. * * @param[in] f_in Input (compressed) stream (must be non-NULL). * @param[out] f_out Output (decompressed) stream. * @param[in] opts Decompression options, or NULL for all defaults. * @return Total bytes written on success, or a negative @ref zxc_error_t. */ int64_t zxc_stream_decompress(FILE* f_in, FILE* f_out, const zxc_decompress_opts_t* opts) { if (UNLIKELY(!f_in)) return ZXC_ERROR_NULL_INPUT; const int n_threads = opts ? opts->n_threads : 0; const int checksum_enabled = opts ? opts->checksum_enabled : 0; const uint8_t* dict = opts ? (const uint8_t*)opts->dict : NULL; const size_t dict_size = (opts && opts->dict) ? opts->dict_size : 0; zxc_progress_callback_t cb = opts ? opts->progress_cb : NULL; void* ud = opts ? opts->user_data : NULL; const uint8_t* dict_huf = (opts && opts->dict) ? (const uint8_t*)opts->dict_huf : NULL; return zxc_stream_engine_run(f_in, f_out, n_threads, 0, 0, 0, checksum_enabled, 0, (zxc_chunk_processor_t)zxc_decompress_chunk_wrapper, cb, ud, dict, dict_size, dict_huf); } /** * @brief Reads the total decompressed size from an archive's footer. * * Public API; see @c zxc_stream.h. Validates the file magic, reads the 64-bit * decompressed-size field from the footer, and restores the caller's original * stream position before returning. Does not decompress any data. * * @param[in] f_in Compressed stream (must be non-NULL and seekable). * @return Decompressed size in bytes, or a negative @ref zxc_error_t * (@ref ZXC_ERROR_BAD_MAGIC, @ref ZXC_ERROR_SRC_TOO_SMALL, * @ref ZXC_ERROR_IO). */ int64_t zxc_stream_get_decompressed_size(FILE* f_in) { if (UNLIKELY(!f_in)) return ZXC_ERROR_NULL_INPUT; const long long saved_pos = ftello(f_in); if (UNLIKELY(saved_pos < 0)) return ZXC_ERROR_IO; // Get file size if (fseeko(f_in, 0, SEEK_END) != 0) return ZXC_ERROR_IO; const long long file_size = ftello(f_in); if (UNLIKELY(file_size < (long long)(ZXC_FILE_HEADER_SIZE + ZXC_FILE_FOOTER_SIZE))) { fseeko(f_in, saved_pos, SEEK_SET); return ZXC_ERROR_SRC_TOO_SMALL; } uint8_t header[ZXC_FILE_HEADER_SIZE]; if (UNLIKELY(fseeko(f_in, 0, SEEK_SET) != 0 || fread(header, 1, ZXC_FILE_HEADER_SIZE, f_in) != ZXC_FILE_HEADER_SIZE)) { fseeko(f_in, saved_pos, SEEK_SET); return ZXC_ERROR_IO; } if (UNLIKELY(zxc_le32(header) != ZXC_MAGIC_WORD)) { fseeko(f_in, saved_pos, SEEK_SET); return ZXC_ERROR_BAD_MAGIC; } uint8_t footer[ZXC_FILE_FOOTER_SIZE]; if (UNLIKELY(fseeko(f_in, file_size - ZXC_FILE_FOOTER_SIZE, SEEK_SET) != 0 || fread(footer, 1, ZXC_FILE_FOOTER_SIZE, f_in) != ZXC_FILE_FOOTER_SIZE)) { fseeko(f_in, saved_pos, SEEK_SET); return ZXC_ERROR_IO; } fseeko(f_in, saved_pos, SEEK_SET); return (int64_t)zxc_le64(footer); } /* * ============================================================================ * SEEKABLE FILE* WRAPPER * ============================================================================ * Adapts a FILE* into a thread-safe zxc_reader_t (pread on POSIX, ReadFile + * OVERLAPPED on Windows) and delegates to zxc_seekable_open_reader. Keeping * this entry point alongside the stream driver, rather than in the kernel- * safe zxc_seekable.c, means zxc_seekable.c stays freestanding. */ #if defined(_WIN32) /** @brief Reader context for the Win32 @c FILE* adapter (OS file handle + size). */ typedef struct { HANDLE handle; /* OS handle from _get_osfhandle(_fileno(f)) */ uint64_t size; /* total file size in bytes */ } zxc_stdio_ctx_t; /** * @brief Thread-safe positioned read backing the seekable @c FILE* reader. * * Win32 implementation: a positioned @c ReadFile via @c OVERLAPPED, so * concurrent worker threads never race on a shared file cursor. * * @param[in] vctx @ref zxc_stdio_ctx_t carrying the file handle. * @param[out] dst Destination buffer (at least @p len bytes). * @param[in] len Number of bytes to read. * @param[in] offset Absolute byte offset to read from. * @return @p len on a full read, otherwise @ref ZXC_ERROR_IO. */ // LCOV_EXCL_START - Windows I/O path, not reachable on POSIX CI static int64_t zxc_stdio_read_at(void* vctx, void* dst, size_t len, uint64_t offset) { zxc_stdio_ctx_t* const ctx = (zxc_stdio_ctx_t*)vctx; OVERLAPPED ov; ZXC_MEMSET(&ov, 0, sizeof(ov)); ov.Offset = (DWORD)(offset & 0xFFFFFFFFu); ov.OffsetHigh = (DWORD)(offset >> 32); DWORD bytes_read = 0; if (!ReadFile(ctx->handle, dst, (DWORD)len, &bytes_read, &ov)) return ZXC_ERROR_IO; return (bytes_read == (DWORD)len) ? (int64_t)len : ZXC_ERROR_IO; } // LCOV_EXCL_STOP #else /* POSIX */ /** @brief Reader context for the POSIX @c FILE* adapter (file descriptor + size). */ typedef struct { int fd; /* descriptor from fileno(f) */ uint64_t size; /* total file size in bytes */ } zxc_stdio_ctx_t; /** * @brief Thread-safe positioned read backing the seekable @c FILE* reader. * * POSIX implementation: a single @c pread, which carries its own offset and so * is safe to call concurrently from multiple worker threads on one descriptor. * * @param[in] vctx @ref zxc_stdio_ctx_t carrying the file descriptor. * @param[out] dst Destination buffer (at least @p len bytes). * @param[in] len Number of bytes to read. * @param[in] offset Absolute byte offset to read from. * @return @p len on a full read, otherwise @ref ZXC_ERROR_IO. */ static int64_t zxc_stdio_read_at(void* vctx, void* dst, size_t len, uint64_t offset) { zxc_stdio_ctx_t* const ctx = (zxc_stdio_ctx_t*)vctx; const ssize_t r = pread(ctx->fd, dst, len, (off_t)offset); return (r == (ssize_t)len) ? (int64_t)len : ZXC_ERROR_IO; } #endif /* _WIN32 */ /** * @brief Opens a seekable archive backed by an open @c FILE*. * * Public API; full contract in @c zxc_stream.h. Snapshots and restores the * file position, measures the file, wraps it in a thread-safe positioned * reader (@c pread on POSIX, @c ReadFile + @c OVERLAPPED on Windows), and * delegates to @ref zxc_seekable_open_reader. The reader context is heap-owned * and handed to the returned handle via @ref zxc_seekable_attach_owned_ctx, so * @ref zxc_seekable_free releases it. * * @param[in] f Open, seekable file handle. * @return A handle to release with @ref zxc_seekable_free, or NULL on bad input, * an I/O error, or a missing / malformed seek table. */ zxc_seekable* zxc_seekable_open_file(FILE* f) { if (UNLIKELY(!f)) return NULL; /* Snapshot the caller's file position so we can restore it. */ const long long saved_pos = ftello(f); if (UNLIKELY(saved_pos < 0)) return NULL; // LCOV_EXCL_LINE // LCOV_EXCL_START - ftello/fseeko failure paths not reachable in CI if (UNLIKELY(fseeko(f, 0, SEEK_END) != 0)) return NULL; const long long file_size = ftello(f); (void)fseeko(f, saved_pos, SEEK_SET); if (UNLIKELY(file_size <= 0)) return NULL; // LCOV_EXCL_STOP zxc_stdio_ctx_t* const ctx = (zxc_stdio_ctx_t*)ZXC_MALLOC(sizeof(*ctx)); if (UNLIKELY(!ctx)) return NULL; // LCOV_EXCL_LINE #if defined(_WIN32) ctx->handle = (HANDLE)(intptr_t)_get_osfhandle(_fileno(f)); // LCOV_EXCL_LINE #else ctx->fd = fileno(f); #endif ctx->size = (uint64_t)file_size; const zxc_reader_t reader = { .read_at = zxc_stdio_read_at, .ctx = ctx, .size = (uint64_t)file_size}; zxc_seekable* const s = zxc_seekable_open_reader(&reader); if (UNLIKELY(!s)) { ZXC_FREE(ctx); return NULL; } /* Hand the ctx lifetime over to the seekable handle. */ zxc_seekable_attach_owned_ctx(s, ctx); return s; } zxc-0.13.0/src/lib/zxc_huffman.c000066400000000000000000002276061522536370400164340ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause * */ /** * @file zxc_huffman.c * @brief Canonical, length-limited Huffman codec in the PivCo layout (GLO * entropy sections, wire values enc 2/3). * * Covers the whole entropy pipeline for GLO literal sections (level >= 6) * and level-7 token sections: * - boundary package-merge -> optimal length-limited code lengths * (<= ZXC_HUF_MAX_CODE_LEN_DENSITY at level 6, <= _ULTRA at level 7); * - 128-byte packed lengths header (4-bit nibbles) pack/unpack + validation; * - exact section sizing for the encoder's space-speed selection * (zxc_huf_calc_size returns SIZE_MAX for unencodable candidates); * - PivCo section encode/decode: the code is classical canonical Huffman, * only the bit LAYOUT differs -- bits are grouped by tree level into * per-node branch runs (see the section banner below and FORMAT.md * section 5.2.1), so decoding runs data-parallel list merges * (NEON TBL / SSSE3 pshufb / AVX-512-VBMI2 vpexpandb kernels, plus flat * subtree unpacking and a leaf-pair fast path) instead of a serial * bit chain. * * Public declarations live in zxc_internal.h; the shuffle-index tables live * in zxc_pivco_tables.c (single TU, shared by all variants); the rest is * private to this translation unit. */ /* * Function Multi-Versioning Support * If ZXC_FUNCTION_SUFFIX is defined (e.g. _avx2, _neon), rename the public * entry points so each variant TU produces its own copy under a unique symbol * (e.g. zxc_huf_decode_section_avx2). The runtime dispatcher in * zxc_compress.c / zxc_decompress.c routes to the matching variant. * * The defines sit before zxc_internal.h so the header's prototypes are * rewritten with the same suffix as the definitions below. */ #ifdef ZXC_FUNCTION_SUFFIX #define ZXC_CAT_IMPL(x, y) x##y #define ZXC_CAT(x, y) ZXC_CAT_IMPL(x, y) #define zxc_huf_build_code_lengths ZXC_CAT(zxc_huf_build_code_lengths, ZXC_FUNCTION_SUFFIX) #define zxc_huf_pack_lengths ZXC_CAT(zxc_huf_pack_lengths, ZXC_FUNCTION_SUFFIX) #define zxc_huf_unpack_lengths ZXC_CAT(zxc_huf_unpack_lengths, ZXC_FUNCTION_SUFFIX) #define zxc_huf_calc_size ZXC_CAT(zxc_huf_calc_size, ZXC_FUNCTION_SUFFIX) #define zxc_huf_calc_size_dict ZXC_CAT(zxc_huf_calc_size_dict, ZXC_FUNCTION_SUFFIX) #define zxc_huf_dict_tree_build ZXC_CAT(zxc_huf_dict_tree_build, ZXC_FUNCTION_SUFFIX) #define zxc_huf_encode_section ZXC_CAT(zxc_huf_encode_section, ZXC_FUNCTION_SUFFIX) #define zxc_huf_encode_section_dict ZXC_CAT(zxc_huf_encode_section_dict, ZXC_FUNCTION_SUFFIX) #define zxc_huf_decode_section ZXC_CAT(zxc_huf_decode_section, ZXC_FUNCTION_SUFFIX) #define zxc_huf_decode_section_dict ZXC_CAT(zxc_huf_decode_section_dict, ZXC_FUNCTION_SUFFIX) #endif #include "../../include/zxc_error.h" #include "zxc_internal.h" #include "zxc_pivco_tables.h" /* =========================================================================== * Length-limited Huffman: boundary package-merge * =========================================================================== * * Builds optimal length-limited Huffman code lengths (max length * ZXC_HUF_MAX_CODE_LEN_ULTRA) on 256-symbol alphabets. Package-merge is run for * ZXC_HUF_MAX_CODE_LEN_ULTRA levels; each level holds up to 2N items (leaves + * paired packages). Selection of the cheapest 2N - 2 items at level * ZXC_HUF_MAX_CODE_LEN_ULTRA gives the appearance count of each leaf, which is * its code length. */ typedef zxc_huf_pm_item_t pm_item_t; typedef struct { uint32_t w; int16_t sym; } pm_leaf_t; typedef zxc_huf_pm_frame_t frame_t; /** * @brief Sort `pm_leaf_t` array by ascending weight, ties broken by ascending symbol. * * Bucket sort on `floor(log2(weight))` (32 buckets), with insertion sort * inside each bucket. Replaces a libc `qsort` call: the comparator's * indirect call dominated, and frequency distributions cluster naturally * across ~10-14 magnitude buckets, so intra-bucket lists stay short and * insertion sort is branch-friendly. Deterministic tie-break on `sym` is * applied inside the insertion sort. * * Precondition: all weights are > 0 (zero-frequency symbols are filtered * by the caller before this runs). * * @param[in,out] leaves Leaf array, sorted in place (ascending weight, then * ascending @c sym on ties). * @param[in] n Number of leaves; @c n < 2 is effectively a no-op. */ static void pm_leaves_sort(pm_leaf_t* RESTRICT leaves, const int n) { /* One bucket per possible value of floor(log2(weight)) for a 32-bit * weight, i.e. 32 buckets. */ enum { NUM_BUCKETS = 32 }; int count[NUM_BUCKETS]; int offset[NUM_BUCKETS + 1]; /* +1 sentinel = n, avoids end-of-bucket branch. */ uint8_t bucket_of[ZXC_HUF_NUM_SYMBOLS]; pm_leaf_t tmp[ZXC_HUF_NUM_SYMBOLS]; ZXC_MEMSET(count, 0, sizeof(count)); for (int i = 0; i < n; i++) { const unsigned b = zxc_log2_u32(leaves[i].w); bucket_of[i] = (uint8_t)b; count[b]++; } int acc = 0; for (int b = 0; b < NUM_BUCKETS; b++) { offset[b] = acc; acc += count[b]; } offset[NUM_BUCKETS] = n; int pos[NUM_BUCKETS]; ZXC_MEMCPY(pos, offset, sizeof(pos)); for (int i = 0; i < n; i++) { tmp[pos[bucket_of[i]]++] = leaves[i]; } for (int b = 0; b < NUM_BUCKETS; b++) { if (count[b] < 2) continue; const int s = offset[b]; const int e = offset[b + 1]; for (int i = s + 1; i < e; i++) { const pm_leaf_t key = tmp[i]; int j = i - 1; while (j >= s && (tmp[j].w > key.w || (tmp[j].w == key.w && tmp[j].sym > key.sym))) { tmp[j + 1] = tmp[j]; j--; } tmp[j + 1] = key; } } ZXC_MEMCPY(leaves, tmp, (size_t)n * sizeof(pm_leaf_t)); } /** * @brief Build length-limited canonical Huffman code lengths. * * Runs the boundary package-merge algorithm capped at `ZXC_HUF_MAX_CODE_LEN_ULTRA`. * Symbols with `freq[i] == 0` get `code_len[i] == 0`; every other symbol * receives a length in `[1, ZXC_HUF_MAX_CODE_LEN_ULTRA]`. The single-present-symbol * case is handled as a degenerate code of length 1. * * @param[in] freq Frequency table indexed by symbol (0..255). * @param[out] code_len Output code-length array, written in full. * @param[in] scratch Optional scratch of `ZXC_HUF_BUILD_SCRATCH_SIZE` bytes * (carved into items / counts / stack regions). If * `NULL`, the function allocates its own working memory * for the duration of the call. * @return `ZXC_OK` on success, `ZXC_ERROR_MEMORY` or `ZXC_ERROR_CORRUPT_DATA` * on failure. */ int zxc_huf_build_code_lengths(const uint32_t* RESTRICT freq, uint8_t* RESTRICT code_len, void* RESTRICT scratch, const int max_code_len) { ZXC_MEMSET(code_len, 0, ZXC_HUF_NUM_SYMBOLS); pm_leaf_t leaves[ZXC_HUF_NUM_SYMBOLS]; int n = 0; for (int i = 0; i < ZXC_HUF_NUM_SYMBOLS; i++) { if (freq[i] > 0) { leaves[n].w = freq[i]; leaves[n].sym = (int16_t)i; n++; } } if (UNLIKELY(n == 0)) return ZXC_ERROR_CORRUPT_DATA; if (n == 1) { code_len[leaves[0].sym] = 1; return ZXC_OK; } pm_leaves_sort(leaves, n); /* Callers pass max_code_len >= 8, and n <= 256 <= 2^max_code_len, so the * length limit is always feasible (a full 8-bit code covers all 256 symbols). */ const int max_per_level = 2 * n; /* Working buffers: either carve from caller-provided scratch (sized for * the worst-case alphabet) or fall back to per-call malloc/free. */ pm_item_t* items; int* counts; frame_t* stack; pm_item_t* owned_items = NULL; int* owned_counts = NULL; frame_t* owned_stack = NULL; if (scratch) { uint8_t* p = (uint8_t*)scratch; items = (pm_item_t*)p; p += (size_t)ZXC_HUF_MAX_CODE_LEN_ULTRA * (size_t)ZXC_HUF_PM_LEVEL_BOUND * sizeof(pm_item_t); p = (uint8_t*)(((uintptr_t)p + 7U) & ~(uintptr_t)7U); counts = (int*)p; ZXC_MEMSET(counts, 0, (size_t)ZXC_HUF_MAX_CODE_LEN_ULTRA * sizeof(int)); p += (size_t)ZXC_HUF_MAX_CODE_LEN_ULTRA * sizeof(int); p = (uint8_t*)(((uintptr_t)p + 7U) & ~(uintptr_t)7U); stack = (frame_t*)p; } else { owned_items = (pm_item_t*)ZXC_MALLOC((size_t)ZXC_HUF_MAX_CODE_LEN_ULTRA * (size_t)max_per_level * sizeof(pm_item_t)); owned_counts = (int*)ZXC_CALLOC((size_t)ZXC_HUF_MAX_CODE_LEN_ULTRA, sizeof(int)); owned_stack = (frame_t*)ZXC_MALLOC((size_t)ZXC_HUF_MAX_CODE_LEN_ULTRA * (size_t)max_per_level * sizeof(frame_t)); if (UNLIKELY(!owned_items || !owned_counts || !owned_stack)) { ZXC_FREE(owned_items); ZXC_FREE(owned_counts); ZXC_FREE(owned_stack); return ZXC_ERROR_MEMORY; } items = owned_items; counts = owned_counts; stack = owned_stack; } #define ITEM(k, i) items[(size_t)(k) * (size_t)max_per_level + (size_t)(i)] /* Level 0 (logical level 1): the leaves themselves, already sorted. */ for (int i = 0; i < n; i++) { ITEM(0, i).weight = leaves[i].w; ITEM(0, i).left = -1; ITEM(0, i).right = -1; ITEM(0, i).sym = leaves[i].sym; } counts[0] = n; /* Levels 1..max_code_len-1: merge sorted leaves with sorted packages from the previous * level. */ for (int k = 1; k < max_code_len; k++) { const int prev = counts[k - 1]; const int packs = prev / 2; int li = 0; int pi = 0; int n_lvl = 0; while (li < n || pi < packs) { const uint32_t wl = (li < n) ? leaves[li].w : UINT32_MAX; const uint32_t wp = (pi < packs) ? (uint32_t)(ITEM(k - 1, 2 * pi).weight + ITEM(k - 1, 2 * pi + 1).weight) : UINT32_MAX; if (wl <= wp && li < n) { ITEM(k, n_lvl).weight = wl; ITEM(k, n_lvl).left = -1; ITEM(k, n_lvl).right = -1; ITEM(k, n_lvl).sym = leaves[li].sym; li++; } else { ITEM(k, n_lvl).weight = wp; ITEM(k, n_lvl).left = (int16_t)(2 * pi); ITEM(k, n_lvl).right = (int16_t)(2 * pi + 1); ITEM(k, n_lvl).sym = -1; pi++; } n_lvl++; } counts[k] = n_lvl; } /* Step 3: take first 2n-2 items at level max_code_len-1; trace back, counting leaf * appearances. */ int n_take = 2 * n - 2; if (n_take > counts[max_code_len - 1]) n_take = counts[max_code_len - 1]; /* Worst case stack depth: (max_code_len * n_take) frames; bounded by * max_code_len * 2n <= ZXC_HUF_MAX_CODE_LEN_ULTRA * 2n. `stack` was set up earlier from * scratch (or the local malloc fallback), sized for the ceiling. */ int sp = 0; for (int i = 0; i < n_take; i++) { stack[sp].lvl = (int8_t)(max_code_len - 1); stack[sp].idx = (int16_t)i; sp++; } while (sp > 0) { frame_t f = stack[--sp]; const pm_item_t* it = &ITEM(f.lvl, f.idx); if (it->sym >= 0) { code_len[it->sym]++; } else { stack[sp].lvl = (int8_t)(f.lvl - 1); stack[sp].idx = it->left; sp++; stack[sp].lvl = (int8_t)(f.lvl - 1); stack[sp].idx = it->right; sp++; } } if (owned_items) { ZXC_FREE(owned_items); ZXC_FREE(owned_counts); ZXC_FREE(owned_stack); } #undef ITEM return ZXC_OK; } /* =========================================================================== * 128-byte length header: 256 x 4-bit lengths, low nibble first. * =========================================================================*/ /** * @brief Pack per-symbol code lengths into the 128-byte (4-bit nibble) header. * * The packing is little-endian within each byte: low nibble holds * `code_len[2*i]`, high nibble holds `code_len[2*i + 1]`. The function * silently truncates any length > 15; callers must enforce the cap of * `ZXC_HUF_MAX_CODE_LEN_ULTRA` (<= 15) before calling. * * @param[in] code_len Per-symbol code lengths (length `ZXC_HUF_NUM_SYMBOLS`). * @param[out] out Output header buffer of `ZXC_HUF_TABLE_SIZE` bytes. */ void zxc_huf_pack_lengths(const uint8_t* RESTRICT code_len, uint8_t* RESTRICT out) { for (int i = 0; i < ZXC_HUF_NUM_SYMBOLS; i += 2) { const uint8_t lo = code_len[i] & 0x0F; const uint8_t hi = code_len[i + 1] & 0x0F; out[i >> 1] = (uint8_t)(lo | (hi << 4)); } } /** * @brief Unpack and structurally validate a 128-byte packed lengths header. * * Inverts ::zxc_huf_pack_lengths and validates the two structural invariants: * no length exceeds `ZXC_HUF_MAX_CODE_LEN_ULTRA`, and at least one symbol is * present. (Kraft consistency is checked later by the tree build.) * * @param[in] in 128-byte packed lengths header. * @param[out] code_len Output code-length array of length `ZXC_HUF_NUM_SYMBOLS`. * @return `ZXC_OK` on success, `ZXC_ERROR_CORRUPT_DATA` if a length is too * large or the table is empty. */ int zxc_huf_unpack_lengths(const uint8_t* RESTRICT in, uint8_t* RESTRICT code_len) { int max_len = 0; int n_present = 0; for (int i = 0; i < ZXC_HUF_NUM_SYMBOLS; i += 2) { const uint8_t b = in[i >> 1]; const uint8_t lo = b & 0x0F; const uint8_t hi = (uint8_t)(b >> 4); code_len[i] = lo; code_len[i + 1] = hi; if (lo > max_len) max_len = lo; if (hi > max_len) max_len = hi; if (lo) n_present++; if (hi) n_present++; } if (UNLIKELY(max_len > ZXC_HUF_MAX_CODE_LEN_ULTRA || n_present == 0)) return ZXC_ERROR_CORRUPT_DATA; return ZXC_OK; } /* =========================================================================== * PivCo-Huffman section codec (enc 2/3) * =========================================================================== * * Layout: [128-byte packed code lengths (literal sections only)] then, for * every EMITTING node of the canonical code tree in BFS order, that node's * run: one branch bit per symbol routed through the node (0 = left child, * 1 = right), in sequence order -- or, for flat subtree roots, D packed bits * per symbol (bit j = branch at relative depth j). Runs are LSB-first within * bytes and byte-padded; descendants of a flat root emit nothing. * * The decoder recovers each node's symbol count for free: the root handles * n symbols, and popcounting a node's bits yields its right child's count. * Reconstruction runs bottom-up, one level at a time: leaves are runs of a * single symbol; an internal node MERGES its two children's sequences under * the control of its bits. Merges are branch-free shuffles (16 outputs per * step on AArch64 via a two-register TBL, 8 on ARMv7 via VTBL4, 64 on * AVX-512-VBMI2 via vpexpandb), which is what makes this layout * decode faster than the serial bit-chain of the classic 4-stream layout on * any target with a 16-byte shuffle. * * Level buffers ping-pong between `scratch` (odd depths) and `dst` (even * depths, final output at depth 0), so one n-sized scratch suffices. Both * buffers need read slack past `n` (speculative 16-byte kernel loads): * ZXC_PAD_SIZE on dst (already true for lit/token buffers), and * ZXC_PIVCO_SCRATCH_PAD on scratch. */ static ZXC_ALWAYS_INLINE int zxc_pivco_popcnt32(const uint32_t v) { #if defined(__GNUC__) || defined(__clang__) return __builtin_popcount(v); #else /* Portable SWAR popcount for MSVC */ uint32_t x = v - ((v >> 1) & 0x55555555U); x = (x & 0x33333333U) + ((x >> 2) & 0x33333333U); x = (x + (x >> 4)) & 0x0F0F0F0FU; return (int)((x * 0x01010101U) >> 24); #endif } static ZXC_ALWAYS_INLINE int zxc_pivco_popcnt64(const uint64_t v) { #if defined(__GNUC__) || defined(__clang__) return __builtin_popcountll(v); #else return zxc_pivco_popcnt32((uint32_t)v) + zxc_pivco_popcnt32((uint32_t)(v >> 32)); #endif } /** * @brief Build the canonical code tree (and codes) from per-symbol lengths. * * Canonical MSB-first assignment: symbols sorted by (length, symbol) receive * sequential codes. Insertion collisions or code-space overflow (malformed * lengths on the decode path) fail with a nonzero return. * * @param[in] code_len Per-symbol code lengths (0 = absent). * @param[out] t Tree + BFS ordering, fully initialised on success. * @param[out] codes Optional per-symbol canonical codes (encoder only). * @return 0 on success, -1 on malformed lengths. */ static int zxc_pivco_tree_build(const uint8_t* RESTRICT code_len, zxc_pivco_tree_t* RESTRICT t, uint32_t* RESTRICT codes) { uint32_t bl_count[ZXC_HUF_MAX_CODE_LEN_ULTRA + 1] = {0}; int n_present = 0; for (int s = 0; s < ZXC_HUF_NUM_SYMBOLS; s++) { const int l = code_len[s]; if (l == 0) continue; if (UNLIKELY(l > ZXC_HUF_MAX_CODE_LEN_ULTRA)) return -1; bl_count[l]++; n_present++; } if (UNLIKELY(n_present == 0)) return -1; if (n_present >= 2) { uint32_t kraft = 0; for (int l = 1; l <= ZXC_HUF_MAX_CODE_LEN_ULTRA; l++) kraft += bl_count[l] << (ZXC_HUF_MAX_CODE_LEN_ULTRA - l); if (UNLIKELY(kraft != (1U << ZXC_HUF_MAX_CODE_LEN_ULTRA))) return -1; } else { /* Degenerate single-symbol table: the format requires the lone symbol * to have code length exactly 1 (FORMAT.md, decoder validation * requirements); the encoder never emits anything else. Reject longer * unary chains. */ if (UNLIKELY(bl_count[1] != 1)) return -1; } uint32_t next_code[ZXC_HUF_MAX_CODE_LEN_ULTRA + 2] = {0}; uint32_t code = 0; for (int l = 1; l <= ZXC_HUF_MAX_CODE_LEN_ULTRA; l++) { code = (code + bl_count[l - 1]) << 1; next_code[l] = code; } t->n_nodes = 1; /* root */ t->nd[0].child[0] = -1; t->nd[0].child[1] = -1; t->nd[0].sym = -1; int max_depth = 0; for (int s = 0; s < ZXC_HUF_NUM_SYMBOLS; s++) { const int l = code_len[s]; if (l == 0) continue; const uint32_t c = next_code[l]++; if (UNLIKELY(c >> l)) return -1; /* code space overflow */ if (codes) codes[s] = c; int cur = 0; for (int d = l - 1; d >= 0; d--) { if (UNLIKELY(t->nd[cur].sym >= 0)) return -1; /* prefix collision */ const int bit = (int)((c >> d) & 1U); int nxt = t->nd[cur].child[bit]; if (nxt < 0) { if (UNLIKELY(t->n_nodes >= ZXC_PIVCO_MAX_NODES)) return -1; nxt = t->n_nodes++; t->nd[nxt].child[0] = -1; t->nd[nxt].child[1] = -1; t->nd[nxt].sym = -1; t->nd[cur].child[bit] = (int16_t)nxt; } cur = nxt; } if (UNLIKELY(t->nd[cur].child[0] >= 0 || t->nd[cur].child[1] >= 0)) return -1; t->nd[cur].sym = (int16_t)s; if (l > max_depth) max_depth = l; } t->max_depth = max_depth; /* BFS order (parents before children, left before right): this is both * the wire order of the node bit runs and the property that makes each * parent's children CONTIGUOUS in the next level's sequence buffer. */ int head = 0; int tail = 0; t->bfs[tail++] = 0; int depth_end = 1; /* index in bfs where the current depth ends */ int depth = 0; t->lvl_start[0] = 0; while (head < tail) { if (head == depth_end) { depth++; t->lvl_start[depth] = (int16_t)head; depth_end = tail; } const int nid = t->bfs[head++]; for (int b = 0; b < 2; b++) { const int ch = t->nd[nid].child[b]; if (ch >= 0) t->bfs[tail++] = (int16_t)ch; } } for (int d = depth + 1; d <= max_depth + 1; d++) t->lvl_start[d] = (int16_t)tail; /* Flat-subtree detection: min/max leaf depth per node in one reverse-BFS * sweep, then maximality by masking descendants of the first flat node on * each root-to-leaf path. */ { int8_t mn[ZXC_PIVCO_MAX_NODES]; int8_t mx[ZXC_PIVCO_MAX_NODES]; for (int i = t->n_nodes - 1; i >= 0; i--) { const int nid = t->bfs[i]; const zxc_pivco_node_t* nd = &t->nd[nid]; if (nd->sym >= 0) { mn[nid] = 0; mx[nid] = 0; } else if (nd->child[0] >= 0 && nd->child[1] >= 0) { const int a = mn[nd->child[0]]; const int b = mn[nd->child[1]]; const int c = mx[nd->child[0]]; const int d2 = mx[nd->child[1]]; mn[nid] = (int8_t)(1 + (a < b ? a : b)); mx[nid] = (int8_t)(1 + (c > d2 ? c : d2)); } else { /* single-child (degenerate) : never flat */ mn[nid] = 0; mx[nid] = (int8_t)ZXC_HUF_MAX_CODE_LEN_ULTRA; } } for (int i = 0; i < t->n_nodes; i++) { const int nid = t->bfs[i]; t->flat_d[nid] = 0; if (i == 0) t->covered[nid] = 0; /* root */ /* Any complete subtree unpacks flat, beating the merge cascade * (FORMAT RULE, both sides): D = 2-6 have SIMD unpackers, D >= 7 the * scalar one. (D = 1 is a leaf pair, handled on the merge path.) */ if (!t->covered[nid] && t->nd[nid].sym < 0 && mn[nid] == mx[nid] && mn[nid] >= 2) t->flat_d[nid] = (uint8_t)mn[nid]; const int ch0 = t->nd[nid].child[0]; const int ch1 = t->nd[nid].child[1]; const uint8_t cov = (uint8_t)(t->covered[nid] || t->flat_d[nid]); if (ch0 >= 0) t->covered[ch0] = cov; if (ch1 >= 0) t->covered[ch1] = cov; } } return 0; } /** * @brief Per-node symbol counts from the histogram (encoder / size estimate). * * Leaf count = freq[sym]; internal count = sum of children, computed in one * reverse-BFS sweep (children precede their parent in that direction). */ static void zxc_pivco_counts(const zxc_pivco_tree_t* RESTRICT t, const uint32_t* RESTRICT freq, uint32_t* RESTRICT count) { for (int i = t->n_nodes - 1; i >= 0; i--) { const int nid = t->bfs[i]; const zxc_pivco_node_t* nd = &t->nd[nid]; if (nd->sym >= 0) { count[nid] = freq[nd->sym]; } else { const uint32_t c0 = nd->child[0] >= 0 ? count[nd->child[0]] : 0; const uint32_t c1 = nd->child[1] >= 0 ? count[nd->child[1]] : 0; count[nid] = c0 + c1; } } } /** * @brief Byte size of one PivCo node's on-wire bit run. * * A wire-visible node emits one of two run kinds, both bit-packed then rounded * up to whole bytes: * - @b Flat root (@p flat_d > 0): a complete subtree of depth @p flat_d, storing * @p count packed codes of @p flat_d bits each: `ceil(count * flat_d / 8)`. * - @b Merge node (@p flat_d == 0): one partition bit per symbol routed through * the node: `ceil(count / 8)`. * * This is THE wire run-boundary rule. It is the single definition shared by the * size estimator (@ref zxc_huf_calc_size), the encoder (zxc_pivco_encode_core), * and the decoder's pass-1 pointer walk (zxc_pivco_decode_core), so the three * cannot drift: the encoder asserts bytes-written == estimate, and the decoder * advances from run to run by this exact arithmetic. * * @param[in] count Number of symbols the node covers (packed codes if flat, * else partition bits). * @param[in] flat_d Flat-subtree depth (bits per packed code), or 0 for a merge * node. * @return Byte length of the node's run (0 when @p count is 0). */ static ZXC_ALWAYS_INLINE size_t zxc_pivco_run_bytes(const uint32_t count, const uint8_t flat_d) { return flat_d ? ((size_t)count * flat_d + 7) / 8 : ((size_t)count + 7) / 8; } size_t zxc_huf_calc_size(const uint32_t* RESTRICT freq, const uint8_t* RESTRICT code_len, const int with_header) { zxc_pivco_tree_t t; if (UNLIKELY(zxc_pivco_tree_build(code_len, &t, NULL) != 0)) return SIZE_MAX; const size_t body = zxc_huf_calc_size_dict(freq, code_len, &t); if (UNLIKELY(body == SIZE_MAX)) return SIZE_MAX; return body + (with_header ? (size_t)ZXC_HUF_TABLE_SIZE : 0); } /** * @brief Sizing core shared with zxc_huf_calc_size: exact payload size of one * PivCo section for a prebuilt tree (tree-at-attach dict sections call * it directly; they carry no inline lengths header). */ size_t zxc_huf_calc_size_dict(const uint32_t* RESTRICT freq, const uint8_t* RESTRICT code_len, const zxc_pivco_tree_t* RESTRICT tree) { /* Encodability is part of the estimate: a histogram symbol without a * code (dict table not covering this block) has no leaf, so the counts * below would silently ignore it and undercount. Such a candidate cannot * be emitted -- report it as unencodable instead. */ for (int k = 0; k < ZXC_HUF_NUM_SYMBOLS; k++) if (UNLIKELY(freq[k] != 0 && code_len[k] == 0)) return SIZE_MAX; uint32_t count[ZXC_PIVCO_MAX_NODES]; zxc_pivco_counts(tree, freq, count); size_t total = 0; for (int i = 0; i < tree->n_nodes; i++) { const int nid = tree->bfs[i]; if (tree->covered[nid] || tree->nd[nid].sym >= 0) continue; total += zxc_pivco_run_bytes(count[nid], tree->flat_d[nid]); } return total; } /** * @brief Shared PivCo section encoder body. * * Walks each input symbol root-to-leaf once, appending its branch bit to the * per-node write cursor; node bit runs land in BFS order, byte-aligned. */ static int zxc_pivco_encode_core(const uint8_t* RESTRICT literals, const size_t n_literals, const uint32_t* RESTRICT freq, const uint8_t* RESTRICT code_len, const zxc_pivco_tree_t* RESTRICT t, const uint32_t* RESTRICT codes, uint8_t* RESTRICT dst, const size_t dst_cap, const int with_header) { if (UNLIKELY(n_literals == 0)) return ZXC_ERROR_CORRUPT_DATA; /* Every literal present in the histogram must have a code (a dict table * may lack codes for symbols unseen in training: the caller then falls * back to a per-block table, but a direct call must fail loudly). */ for (int k = 0; k < ZXC_HUF_NUM_SYMBOLS; k++) if (UNLIKELY(freq[k] != 0 && code_len[k] == 0)) return ZXC_ERROR_CORRUPT_DATA; uint32_t count[ZXC_PIVCO_MAX_NODES]; zxc_pivco_counts(t, freq, count); /* Byte offsets of every wire-visible internal node's run, in BFS order: * bitmap runs (1 bit/symbol) or, for flat roots, packed code runs * (flat_d bits/symbol). Covered nodes have no run. */ uint32_t bit_off[ZXC_PIVCO_MAX_NODES]; size_t payload = 0; for (int i = 0; i < t->n_nodes; i++) { const int nid = t->bfs[i]; if (t->covered[nid] || t->nd[nid].sym >= 0) continue; bit_off[nid] = (uint32_t)payload; payload += zxc_pivco_run_bytes(count[nid], t->flat_d[nid]); } const size_t hdr = with_header ? (size_t)ZXC_HUF_TABLE_SIZE : 0; /* +2: the packed-code emitter uses a 3-byte read-modify-write that may * touch up to 2 bytes past the payload end. */ if (UNLIKELY(hdr + payload + 2 > dst_cap)) return ZXC_ERROR_DST_TOO_SMALL; if (with_header) zxc_huf_pack_lengths(code_len, dst); uint8_t* const out = dst + hdr; ZXC_MEMSET(out, 0, payload + 2); /* Per-node bit cursors; emit MSB-first code bits while descending. At a * flat root, emit the symbol's packed D-bit residual (bit j = branch at * subtree level j) in one shot and stop descending. * Batching the per-bit RMW (read-modify-write) brings nothing: * per-node accumulators would still touch memory once per bit, and flat * roots already absorb the dense levels in one shot. */ uint32_t wpos[ZXC_PIVCO_MAX_NODES]; ZXC_MEMSET(wpos, 0, (size_t)t->n_nodes * sizeof(uint32_t)); for (size_t i = 0; i < n_literals; i++) { const uint8_t s = literals[i]; const uint32_t c = codes[s]; int cur = 0; for (int d = code_len[s] - 1; d >= 0; d--) { const int fd = t->flat_d[cur]; if (fd) { /* residual = low fd bits of the canonical code, bit-reversed * so that packed bit j is the branch taken at level j. */ uint32_t r = 0; for (int j = 0; j < fd; j++) r |= ((c >> (fd - 1 - j)) & 1U) << j; const uint32_t p = wpos[cur]; wpos[cur] += (uint32_t)fd; uint8_t* q = out + bit_off[cur] + (p >> 3); const uint32_t sh = p & 7U; uint32_t w = (uint32_t)q[0] | ((uint32_t)q[1] << 8) | ((uint32_t)q[2] << 16); w |= r << sh; /* fd <= 11, sh <= 7 -> fits 24 bits */ q[0] = (uint8_t)w; q[1] = (uint8_t)(w >> 8); q[2] = (uint8_t)(w >> 16); break; } const uint32_t bit = (c >> d) & 1U; const uint32_t p = wpos[cur]++; out[bit_off[cur] + (p >> 3)] |= (uint8_t)(bit << (p & 7)); cur = t->nd[cur].child[bit]; } } return (int)(hdr + payload); } /** * @brief Encode a literal/token section (PivCo layout, inline lengths header). * * Emits the 128-byte packed code-length header followed by the per-node * branch runs. The caller supplies the histogram and code lengths it already * built for the selection step. * * @param[in] literals Symbols to encode (n_literals bytes). * @param[in] n_literals Symbol count (> 0). * @param[in] freq Symbol histogram of @p literals. * @param[in] code_len Per-symbol code lengths (every symbol with * freq != 0 must have a code). * @param[out] dst Destination buffer. * @param[in] dst_cap Destination capacity in bytes. * @return Encoded size in bytes, or a negative ZXC_ERROR_* value * (ZXC_ERROR_DST_TOO_SMALL, ZXC_ERROR_CORRUPT_DATA). */ int zxc_huf_encode_section(const uint8_t* RESTRICT literals, const size_t n_literals, const uint32_t* RESTRICT freq, const uint8_t* RESTRICT code_len, uint8_t* RESTRICT dst, const size_t dst_cap) { zxc_pivco_tree_t t; uint32_t codes[ZXC_HUF_NUM_SYMBOLS]; if (UNLIKELY(zxc_pivco_tree_build(code_len, &t, codes) != 0)) return ZXC_ERROR_CORRUPT_DATA; return zxc_pivco_encode_core(literals, n_literals, freq, code_len, &t, codes, dst, dst_cap, 1); } /** * @brief Encode a literal section against a shared dictionary table. * * Same payload as @ref zxc_huf_encode_section with the 128-byte lengths * header omitted: @p code_len / @p tree / @p codes come from the dictionary's * shared literal table, prebuilt ONCE at attach by @ref zxc_huf_dict_tree_build * (wire value enc_lit = 3). */ int zxc_huf_encode_section_dict(const uint8_t* RESTRICT literals, const size_t n_literals, const uint32_t* RESTRICT freq, const uint8_t* RESTRICT code_len, const zxc_pivco_tree_t* RESTRICT tree, const uint32_t* RESTRICT codes, uint8_t* RESTRICT dst, const size_t dst_cap) { return zxc_pivco_encode_core(literals, n_literals, freq, code_len, tree, codes, dst, dst_cap, 0); } /** * @brief Precompute the topology-derived decoder tables for @p t. * * Mirrors exactly what zxc_pivco_decode_core builds inline for per-section * trees: the leaf-pair skip flags, and each flat root's packed-code -> symbol * table (bit j of the code = branch taken at subtree level j). Both depend * only on the tree, so a frame-constant (dictionary) tree computes them once * here instead of once per decoded section. */ static void zxc_pivco_decode_aux_build(const zxc_pivco_tree_t* RESTRICT t, zxc_pivco_decode_aux_t* RESTRICT aux) { ZXC_MEMSET(aux->skip, 0, sizeof(aux->skip)); for (int i = 0; i < t->n_nodes; i++) { const zxc_pivco_node_t* nd = &t->nd[t->bfs[i]]; if (nd->sym >= 0) continue; const int ch0 = nd->child[0]; const int ch1 = nd->child[1]; if (ch0 >= 0 && ch1 >= 0 && t->nd[ch0].sym >= 0 && t->nd[ch1].sym >= 0) { aux->skip[ch0] = 1; aux->skip[ch1] = 1; } } /* Flat subtrees have disjoint leaves, so the concatenated tables fit in * ZXC_HUF_NUM_SYMBOLS pool entries (see zxc_pivco_decode_aux_t). */ uint32_t pool_off = 0; for (int i = 0; i < t->n_nodes; i++) { const int nid = t->bfs[i]; if (t->covered[nid] || !t->flat_d[nid]) continue; aux->c2s_off[nid] = (uint16_t)pool_off; uint8_t* const c2s = aux->c2s_pool + pool_off; pool_off += 1U << t->flat_d[nid]; int16_t stk_n[ZXC_HUF_MAX_CODE_LEN_ULTRA + 1]; uint16_t stk_p[ZXC_HUF_MAX_CODE_LEN_ULTRA + 1]; uint8_t stk_l[ZXC_HUF_MAX_CODE_LEN_ULTRA + 1]; int sp = 0; stk_n[0] = (int16_t)nid; stk_p[0] = 0; stk_l[0] = 0; while (sp >= 0) { const int cn = stk_n[sp]; const uint32_t cp = stk_p[sp]; const int cl = stk_l[sp]; sp--; if (t->nd[cn].sym >= 0) { c2s[cp] = (uint8_t)t->nd[cn].sym; continue; } sp++; stk_n[sp] = t->nd[cn].child[0]; stk_p[sp] = (uint16_t)cp; stk_l[sp] = (uint8_t)(cl + 1); sp++; stk_n[sp] = t->nd[cn].child[1]; stk_p[sp] = (uint16_t)(cp | (1U << cl)); stk_l[sp] = (uint8_t)(cl + 1); } } } /** * @brief Prebuild a dict table's decode/encode state (tree-at-attach). * * Unpacks the 128-byte packed lengths and builds the PivCo tree, canonical * codes and decoder tables once; the outputs are frame-constant, so per-block * encode, estimate and decode reuse them instead of rebuilding (the pre-v7 * cached-decode-table form, restored for the PivCo layout). */ int zxc_huf_dict_tree_build(const uint8_t* RESTRICT packed_lengths, zxc_pivco_tree_t* RESTRICT tree, uint32_t* RESTRICT codes, uint8_t* RESTRICT code_len, zxc_pivco_decode_aux_t* RESTRICT aux) { const int rc = zxc_huf_unpack_lengths(packed_lengths, code_len); if (UNLIKELY(rc != ZXC_OK)) return rc; if (UNLIKELY(zxc_pivco_tree_build(code_len, tree, codes) != 0)) return ZXC_ERROR_CORRUPT_DATA; zxc_pivco_decode_aux_build(tree, aux); return ZXC_OK; } /** * @brief Merge two adjacent child sequences under control bits. * * src[0..nl) is the left sequence, src[nl..nl+nr) the right one (children are * contiguous by construction). Control bits are LSB-first; bit 0 takes the * next left element, bit 1 the next right one. Speculative 16-byte loads read * up to 15 bytes past each cursor: callers guarantee read slack after the * level buffers. Writes stay within out[0..nl+nr). */ static ZXC_ALWAYS_INLINE void zxc_pivco_merge(uint8_t* RESTRICT out, const uint8_t* RESTRICT src, const size_t nl, const size_t nr, const uint8_t* RESTRICT bits) { const uint8_t* L = src; const uint8_t* R = src + nl; const size_t n = nl + nr; size_t i = 0; size_t lp = 0; size_t rp = 0; #if defined(ZXC_USE_NEON64) /* 16 outputs per step: two-register TBL over {L[0..15], R[0..15]} with the * signed-index tables (see zxc_pivco_tables.h). */ while (i + 16 <= n) { const uint8_t b0 = bits[i >> 3]; const uint8_t b1 = bits[(i >> 3) + 1]; uint8x16x2_t tb; tb.val[0] = vld1q_u8(L + lp); tb.val[1] = vld1q_u8(R + rp); const int pc0 = zxc_pivco_popcnt32(b0); const uint8x8_t ia = vld1_u8(zxc_pivco_idxa_u8[b0]); const uint8x8_t ib = vld1_u8(zxc_pivco_idxb_pre[pc0][b1]); vst1q_u8(out + i, vqtbl2q_u8(tb, vcombine_u8(ia, ib))); const int pc = pc0 + zxc_pivco_popcnt32(b1); rp += (size_t)pc; lp += (size_t)(16 - pc); i += 16; } #else /* x86 tiers */ #if defined(ZXC_USE_AVX512) && defined(__AVX512VBMI2__) /* 64 outputs per step: the merge IS a pair of byte expands. Bytes of L * fill the 0-bit lanes of the control word, bytes of R the 1-bit lanes * (merge-masked into the first result). Masked loads keep the speculative * reads fault-safe without extra buffer slack. */ while (i + 64 <= n) { uint64_t ctrl; ZXC_MEMCPY(&ctrl, bits + (i >> 3), 8); const int pc = zxc_pivco_popcnt64(ctrl); const __mmask64 lmask = (pc == 0) ? ~(__mmask64)0 : (((__mmask64)1 << (64 - pc)) - 1U); const __mmask64 rmask = (pc == 64) ? ~(__mmask64)0 : (((__mmask64)1 << pc) - 1U); const __m512i vl = _mm512_maskz_loadu_epi8(lmask, (const void*)(L + lp)); const __m512i vr = _mm512_maskz_loadu_epi8(rmask, (const void*)(R + rp)); const __m512i outl = _mm512_maskz_expand_epi8((__mmask64)~ctrl, vl); const __m512i outv = _mm512_mask_expand_epi8(outl, (__mmask64)ctrl, vr); _mm512_storeu_si512((void*)(out + i), outv); rp += (size_t)pc; lp += (size_t)(64 - pc); i += 64; } #endif #if defined(ZXC_USE_AVX512) || defined(ZXC_USE_AVX2) || \ (defined(ZXC_USE_SSE2) && defined(__SSSE3__)) /* 16 outputs/step: pshufb(L, ix+0x70) | pshufb(R, ix-16) selects L[ix] or * R[ix-16] (out-of-range lanes zero out); 32/step unrolls it twice. */ while (i + 32 <= n) { const uint8_t* cb = bits + (i >> 3); const int pcA0 = zxc_pivco_popcnt32(cb[0]); const int pcA = pcA0 + zxc_pivco_popcnt32(cb[1]); const int pcB0 = zxc_pivco_popcnt32(cb[2]); const int pcB = pcB0 + zxc_pivco_popcnt32(cb[3]); const size_t lpB = lp + (size_t)(16 - pcA); const size_t rpB = rp + (size_t)pcA; const __m128i vlA = _mm_loadu_si128((const __m128i*)(const void*)(L + lp)); const __m128i vrA = _mm_loadu_si128((const __m128i*)(const void*)(R + rp)); const __m128i vlB = _mm_loadu_si128((const __m128i*)(const void*)(L + lpB)); const __m128i vrB = _mm_loadu_si128((const __m128i*)(const void*)(R + rpB)); const __m128i iaA = _mm_loadl_epi64((const __m128i*)(const void*)zxc_pivco_idxa_u8[cb[0]]); const __m128i ibA = _mm_loadl_epi64((const __m128i*)(const void*)zxc_pivco_idxb_pre[pcA0][cb[1]]); const __m128i iaB = _mm_loadl_epi64((const __m128i*)(const void*)zxc_pivco_idxa_u8[cb[2]]); const __m128i ibB = _mm_loadl_epi64((const __m128i*)(const void*)zxc_pivco_idxb_pre[pcB0][cb[3]]); const __m128i ixA = _mm_unpacklo_epi64(iaA, ibA); const __m128i ixB = _mm_unpacklo_epi64(iaB, ibB); const __m128i selA = _mm_or_si128(_mm_shuffle_epi8(vlA, _mm_add_epi8(ixA, _mm_set1_epi8(0x70))), _mm_shuffle_epi8(vrA, _mm_sub_epi8(ixA, _mm_set1_epi8(16)))); const __m128i selB = _mm_or_si128(_mm_shuffle_epi8(vlB, _mm_add_epi8(ixB, _mm_set1_epi8(0x70))), _mm_shuffle_epi8(vrB, _mm_sub_epi8(ixB, _mm_set1_epi8(16)))); _mm_storeu_si128((__m128i*)(void*)(out + i), selA); _mm_storeu_si128((__m128i*)(void*)(out + i + 16), selB); lp = lpB + (size_t)(16 - pcB); rp = rpB + (size_t)pcB; i += 32; } while (i + 16 <= n) { const uint8_t b0 = bits[i >> 3]; const uint8_t b1 = bits[(i >> 3) + 1]; const __m128i vl = _mm_loadu_si128((const __m128i*)(const void*)(L + lp)); const __m128i vr = _mm_loadu_si128((const __m128i*)(const void*)(R + rp)); const int pc0 = zxc_pivco_popcnt32(b0); const __m128i ia = _mm_loadl_epi64((const __m128i*)(const void*)zxc_pivco_idxa_u8[b0]); const __m128i ib = _mm_loadl_epi64((const __m128i*)(const void*)zxc_pivco_idxb_pre[pc0][b1]); const __m128i ix = _mm_unpacklo_epi64(ia, ib); const __m128i sell = _mm_shuffle_epi8(vl, _mm_add_epi8(ix, _mm_set1_epi8(0x70))); const __m128i selr = _mm_shuffle_epi8(vr, _mm_sub_epi8(ix, _mm_set1_epi8(16))); _mm_storeu_si128((__m128i*)(void*)(out + i), _mm_or_si128(sell, selr)); const int pc = pc0 + zxc_pivco_popcnt32(b1); rp += (size_t)pc; lp += (size_t)(16 - pc); i += 16; } #elif defined(ZXC_USE_NEON32) /* 8 outputs per step: four-register VTBL over {L[0..7], -, R[0..7], -}. * The shared index tables address a 32-lane view (L lanes 0..15, R lanes * 16..31); an 8-output step only ever references lanes 0..7 and 16..23, * so the two unused d-registers stay undefined-but-harmless zeros. */ while (i + 8 <= n) { const uint8_t b = bits[i >> 3]; uint8x8x4_t tb; tb.val[0] = vld1_u8(L + lp); tb.val[1] = vdup_n_u8(0); tb.val[2] = vld1_u8(R + rp); tb.val[3] = vdup_n_u8(0); vst1_u8(out + i, vtbl4_u8(tb, vld1_u8(zxc_pivco_idxa_u8[b]))); const int pc = zxc_pivco_popcnt32(b); rp += (size_t)pc; lp += (size_t)(8 - pc); i += 8; } #endif #endif /* x86 tiers */ /* Portable 8-output path: byte selects from a 24-byte scratch laid out * as the low half of the 32-lane view the shared index tables assume * (L at offset 0, R at offset 16). */ while (i + 8 <= n) { const uint8_t b = bits[i >> 3]; uint8_t comb[24]; ZXC_MEMCPY(comb, L + lp, 8); ZXC_MEMCPY(comb + 16, R + rp, 8); const uint8_t* ix = zxc_pivco_idxa_u8[b]; for (int j = 0; j < 8; j++) out[i + (size_t)j] = comb[ix[j]]; const int pc = zxc_pivco_popcnt32(b); rp += (size_t)pc; lp += (size_t)(8 - pc); i += 8; } while (i < n) { const int bit = (bits[i >> 3] >> (i & 7)) & 1; out[i++] = bit ? R[rp++] : L[lp++]; } } /** * @brief Decode a flat subtree's packed D-bit code run into symbols. * * codes are packed LSB-first, D in [2, ZXC_HUF_MAX_CODE_LEN_ULTRA]; c2s maps * a packed path to its leaf symbol. SIMD paths cover the frequent D = 2 / 4 * (in-register unpack + single 16-byte table lookup); the scalar bit-reader * handles every D. */ static void zxc_pivco_unpack_flat(uint8_t* RESTRICT out, const size_t n, const int D, const uint8_t* RESTRICT bits, const uint8_t* RESTRICT c2s) { size_t i = 0; #if defined(ZXC_USE_NEON64) if (D == 4) { const uint8x16_t vc2s = vld1q_u8(c2s); /* 16 entries */ static const uint8_t rep2[16] = {0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7}; static const int8_t sh4[16] = {0, -4, 0, -4, 0, -4, 0, -4, 0, -4, 0, -4, 0, -4, 0, -4}; const uint8x16_t vrep = vld1q_u8(rep2); const int8x16_t vsh = vld1q_s8(sh4); const uint8x16_t vmask = vdupq_n_u8(0x0F); while (i + 16 <= n) { uint8x16_t raw = vdupq_n_u8(0); raw = vreinterpretq_u8_u64( vsetq_lane_u64(zxc_le64(bits + ((i * 4) >> 3)), vreinterpretq_u64_u8(raw), 0)); const uint8x16_t rep = vqtbl1q_u8(raw, vrep); const uint8x16_t codes = vandq_u8(vshlq_u8(rep, vsh), vmask); vst1q_u8(out + i, vqtbl1q_u8(vc2s, codes)); i += 16; } } else if (D == 2) { uint8_t c2s16[16]; for (int k = 0; k < 16; k++) c2s16[k] = c2s[k & 3]; const uint8x16_t vc2s = vld1q_u8(c2s16); static const uint8_t rep4[16] = {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3}; static const int8_t sh2[16] = {0, -2, -4, -6, 0, -2, -4, -6, 0, -2, -4, -6, 0, -2, -4, -6}; const uint8x16_t vrep = vld1q_u8(rep4); const int8x16_t vsh = vld1q_s8(sh2); const uint8x16_t vmask = vdupq_n_u8(0x03); while (i + 16 <= n) { uint8x16_t raw = vdupq_n_u8(0); raw = vreinterpretq_u8_u32( vsetq_lane_u32(zxc_le32(bits + ((i * 2) >> 3)), vreinterpretq_u32_u8(raw), 0)); const uint8x16_t rep = vqtbl1q_u8(raw, vrep); const uint8x16_t codes = vandq_u8(vshlq_u8(rep, vsh), vmask); vst1q_u8(out + i, vqtbl1q_u8(vc2s, codes)); i += 16; } } else if (D == 3) { /* Odd D straddle bytes: build a 16-bit window {byte_j,byte_j+1} per lane * (byte_j = Di>>3), right-shift by Di&7, mask D bits. 16/step, two u16x8 * halves sharing the shift vector. D=3: 6 source bytes. */ uint8_t c2s16[16]; for (int k = 0; k < 16; k++) c2s16[k] = c2s[k & 7]; const uint8x16_t vc2s = vld1q_u8(c2s16); /* 8 entries, padded to 16 */ static const uint8_t idxA[16] = {0, 1, 0, 1, 0, 1, 1, 2, 1, 2, 1, 2, 2, 3, 2, 3}; static const uint8_t idxB[16] = {3, 4, 3, 4, 3, 4, 4, 5, 4, 5, 4, 5, 5, 6, 5, 6}; static const int16_t sh8[8] = {0, -3, -6, -1, -4, -7, -2, -5}; const uint8x16_t vidxA = vld1q_u8(idxA); const uint8x16_t vidxB = vld1q_u8(idxB); const int16x8_t vsh = vld1q_s16(sh8); const uint16x8_t vmask = vdupq_n_u16(0x07); while (i + 16 <= n) { const size_t off = (i * 3) >> 3; uint32_t w0; uint16_t w1; ZXC_MEMCPY(&w0, bits + off, 4); ZXC_MEMCPY(&w1, bits + off + 4, 2); /* exactly 6 bytes read */ uint8x16_t raw = vdupq_n_u8(0); raw = vreinterpretq_u8_u32(vsetq_lane_u32(w0, vreinterpretq_u32_u8(raw), 0)); raw = vreinterpretq_u8_u16(vsetq_lane_u16(w1, vreinterpretq_u16_u8(raw), 2)); const uint16x8_t winA = vreinterpretq_u16_u8(vqtbl1q_u8(raw, vidxA)); const uint16x8_t winB = vreinterpretq_u16_u8(vqtbl1q_u8(raw, vidxB)); const uint8x8_t cA = vmovn_u16(vandq_u16(vshlq_u16(winA, vsh), vmask)); const uint8x8_t cB = vmovn_u16(vandq_u16(vshlq_u16(winB, vsh), vmask)); vst1q_u8(out + i, vqtbl1q_u8(vc2s, vcombine_u8(cA, cB))); i += 16; } } else if (D == 5) { /* Same window scheme as D=3; c2s has 32 entries (vqtbl2q). 10 bytes. */ uint8x16x2_t vc2s; vc2s.val[0] = vld1q_u8(c2s); vc2s.val[1] = vld1q_u8(c2s + 16); static const uint8_t idxA[16] = {0, 1, 0, 1, 1, 2, 1, 2, 2, 3, 3, 4, 3, 4, 4, 5}; static const uint8_t idxB[16] = {5, 6, 5, 6, 6, 7, 6, 7, 7, 8, 8, 9, 8, 9, 9, 10}; static const int16_t sh8[8] = {0, -5, -2, -7, -4, -1, -6, -3}; const uint8x16_t vidxA = vld1q_u8(idxA); const uint8x16_t vidxB = vld1q_u8(idxB); const int16x8_t vsh = vld1q_s16(sh8); const uint16x8_t vmask = vdupq_n_u16(0x1F); while (i + 16 <= n) { const size_t off = (i * 5) >> 3; uint64_t w0; uint16_t w1; ZXC_MEMCPY(&w0, bits + off, 8); ZXC_MEMCPY(&w1, bits + off + 8, 2); uint8x16_t raw = vdupq_n_u8(0); raw = vreinterpretq_u8_u64(vsetq_lane_u64(w0, vreinterpretq_u64_u8(raw), 0)); raw = vreinterpretq_u8_u16(vsetq_lane_u16(w1, vreinterpretq_u16_u8(raw), 4)); const uint16x8_t winA = vreinterpretq_u16_u8(vqtbl1q_u8(raw, vidxA)); const uint16x8_t winB = vreinterpretq_u16_u8(vqtbl1q_u8(raw, vidxB)); const uint8x8_t cA = vmovn_u16(vandq_u16(vshlq_u16(winA, vsh), vmask)); const uint8x8_t cB = vmovn_u16(vandq_u16(vshlq_u16(winB, vsh), vmask)); vst1q_u8(out + i, vqtbl2q_u8(vc2s, vcombine_u8(cA, cB))); i += 16; } } else if (D == 6) { /* Same window scheme as D=3; c2s has 64 entries (vqtbl4q). 12 bytes. */ uint8x16x4_t vc2s; vc2s.val[0] = vld1q_u8(c2s); vc2s.val[1] = vld1q_u8(c2s + 16); vc2s.val[2] = vld1q_u8(c2s + 32); vc2s.val[3] = vld1q_u8(c2s + 48); static const uint8_t idxA[16] = {0, 1, 0, 1, 1, 2, 2, 3, 3, 4, 3, 4, 4, 5, 5, 6}; static const uint8_t idxB[16] = {6, 7, 6, 7, 7, 8, 8, 9, 9, 10, 9, 10, 10, 11, 11, 12}; static const int16_t sh8[8] = {0, -6, -4, -2, 0, -6, -4, -2}; const uint8x16_t vidxA = vld1q_u8(idxA); const uint8x16_t vidxB = vld1q_u8(idxB); const int16x8_t vsh = vld1q_s16(sh8); const uint16x8_t vmask = vdupq_n_u16(0x3F); while (i + 16 <= n) { const size_t off = (i * 6) >> 3; uint64_t w0; uint32_t w1; ZXC_MEMCPY(&w0, bits + off, 8); ZXC_MEMCPY(&w1, bits + off + 8, 4); uint8x16_t raw = vdupq_n_u8(0); raw = vreinterpretq_u8_u64(vsetq_lane_u64(w0, vreinterpretq_u64_u8(raw), 0)); raw = vreinterpretq_u8_u32(vsetq_lane_u32(w1, vreinterpretq_u32_u8(raw), 2)); const uint16x8_t winA = vreinterpretq_u16_u8(vqtbl1q_u8(raw, vidxA)); const uint16x8_t winB = vreinterpretq_u16_u8(vqtbl1q_u8(raw, vidxB)); const uint8x8_t cA = vmovn_u16(vandq_u16(vshlq_u16(winA, vsh), vmask)); const uint8x8_t cB = vmovn_u16(vandq_u16(vshlq_u16(winB, vsh), vmask)); vst1q_u8(out + i, vqtbl4q_u8(vc2s, vcombine_u8(cA, cB))); i += 16; } } #elif defined(ZXC_USE_NEON32) /* d-register mirrors of the AArch64 kernels: 8 codes per step, c2s lookup * via VTBL (16-entry table = two d-registers). */ if (D == 4) { uint8x8x2_t vc2s; vc2s.val[0] = vld1_u8(c2s); vc2s.val[1] = vld1_u8(c2s + 8); static const uint8_t rep2[8] = {0, 0, 1, 1, 2, 2, 3, 3}; static const int8_t sh4[8] = {0, -4, 0, -4, 0, -4, 0, -4}; const uint8x8_t vrep = vld1_u8(rep2); const int8x8_t vsh = vld1_s8(sh4); const uint8x8_t vmask = vdup_n_u8(0x0F); while (i + 8 <= n) { uint32_t w; ZXC_MEMCPY(&w, bits + ((i * 4) >> 3), 4); const uint8x8_t raw = vreinterpret_u8_u32(vdup_n_u32(w)); const uint8x8_t rep = vtbl1_u8(raw, vrep); const uint8x8_t codes = vand_u8(vshl_u8(rep, vsh), vmask); vst1_u8(out + i, vtbl2_u8(vc2s, codes)); i += 8; } } else if (D == 2) { uint8_t c2s8[8]; for (int k = 0; k < 8; k++) c2s8[k] = c2s[k & 3]; const uint8x8_t vc2s = vld1_u8(c2s8); static const uint8_t rep4[8] = {0, 0, 0, 0, 1, 1, 1, 1}; static const int8_t sh2[8] = {0, -2, -4, -6, 0, -2, -4, -6}; const uint8x8_t vrep = vld1_u8(rep4); const int8x8_t vsh = vld1_s8(sh2); const uint8x8_t vmask = vdup_n_u8(0x03); while (i + 8 <= n) { const uint16_t w = (uint16_t)((uint16_t)bits[(i * 2) >> 3] | ((uint16_t)bits[((i * 2) >> 3) + 1] << 8)); const uint8x8_t raw = vreinterpret_u8_u16(vdup_n_u16(w)); const uint8x8_t rep = vtbl1_u8(raw, vrep); const uint8x8_t codes = vand_u8(vshl_u8(rep, vsh), vmask); vst1_u8(out + i, vtbl1_u8(vc2s, codes)); i += 8; } } else if (D == 3) { /* 8 codes/step. 3-bit codes straddle bytes, so build a 16-bit window * {byte_j, byte_j+1} per lane (byte_j = 3i>>3) via two VTBLs, then * extract bits [s,s+3) by multiply-by-2^(13-s) + >>13 (no per-lane u16 * variable shift on ARMv7); s_j = 3j&7. 8 codes fit in 3 source bytes. */ const uint8x8_t vc2s = vld1_u8(c2s); /* 8 entries (2^3) */ static const uint8_t idxlo[8] = {0, 1, 0, 1, 0, 1, 1, 2}; static const uint8_t idxhi[8] = {1, 2, 1, 2, 2, 3, 2, 3}; static const uint16_t mul8[8] = {8192, 1024, 128, 4096, 512, 64, 2048, 256}; const uint8x8_t vidxlo = vld1_u8(idxlo); const uint8x8_t vidxhi = vld1_u8(idxhi); const uint16x8_t vmul = vld1q_u16(mul8); const uint16x8_t vmask = vdupq_n_u16(0x07); while (i + 8 <= n) { const size_t off = (i * 3) >> 3; const uint32_t w = (uint32_t)bits[off] | ((uint32_t)bits[off + 1] << 8) | ((uint32_t)bits[off + 2] << 16); /* exactly 3 bytes */ const uint8x8_t raw = vreinterpret_u8_u32(vdup_n_u32(w)); const uint8x8_t wlo = vtbl1_u8(raw, vidxlo); const uint8x8_t whi = vtbl1_u8(raw, vidxhi); const uint16x8_t win = vreinterpretq_u16_u8(vcombine_u8(wlo, whi)); const uint16x8_t codes16 = vandq_u16(vshrq_n_u16(vmulq_u16(win, vmul), 13), vmask); vst1_u8(out + i, vtbl1_u8(vc2s, vmovn_u16(codes16))); i += 8; } } else if (D == 5) { /* Same window scheme as D=3; c2s has 32 entries (VTBL4). 5 source bytes. */ uint8x8x4_t vc2s; vc2s.val[0] = vld1_u8(c2s); vc2s.val[1] = vld1_u8(c2s + 8); vc2s.val[2] = vld1_u8(c2s + 16); vc2s.val[3] = vld1_u8(c2s + 24); static const uint8_t idxlo[8] = {0, 1, 0, 1, 1, 2, 1, 2}; static const uint8_t idxhi[8] = {2, 3, 3, 4, 3, 4, 4, 5}; static const uint16_t mul8[8] = {2048, 64, 512, 16, 128, 1024, 32, 256}; const uint8x8_t vidxlo = vld1_u8(idxlo); const uint8x8_t vidxhi = vld1_u8(idxhi); const uint16x8_t vmul = vld1q_u16(mul8); const uint16x8_t vmask = vdupq_n_u16(0x1F); while (i + 8 <= n) { uint64_t w = 0; ZXC_MEMCPY(&w, bits + ((i * 5) >> 3), 5); const uint8x8_t raw = vcreate_u8(w); const uint16x8_t win = vreinterpretq_u16_u8(vcombine_u8(vtbl1_u8(raw, vidxlo), vtbl1_u8(raw, vidxhi))); const uint8x8_t codes = vmovn_u16(vandq_u16(vshrq_n_u16(vmulq_u16(win, vmul), 11), vmask)); vst1_u8(out + i, vtbl4_u8(vc2s, codes)); i += 8; } } else if (D == 6) { /* Same window scheme; c2s has 64 entries: two VTBL4 halves, select by * code bit 5. 6 source bytes. */ uint8x8x4_t lo, hi; lo.val[0] = vld1_u8(c2s); lo.val[1] = vld1_u8(c2s + 8); lo.val[2] = vld1_u8(c2s + 16); lo.val[3] = vld1_u8(c2s + 24); hi.val[0] = vld1_u8(c2s + 32); hi.val[1] = vld1_u8(c2s + 40); hi.val[2] = vld1_u8(c2s + 48); hi.val[3] = vld1_u8(c2s + 56); static const uint8_t idxlo[8] = {0, 1, 0, 1, 1, 2, 2, 3}; static const uint8_t idxhi[8] = {3, 4, 3, 4, 4, 5, 5, 6}; static const uint16_t mul8[8] = {1024, 16, 64, 256, 1024, 16, 64, 256}; const uint8x8_t vidxlo = vld1_u8(idxlo); const uint8x8_t vidxhi = vld1_u8(idxhi); const uint16x8_t vmul = vld1q_u16(mul8); const uint16x8_t vmask = vdupq_n_u16(0x3F); while (i + 8 <= n) { uint64_t w = 0; ZXC_MEMCPY(&w, bits + ((i * 6) >> 3), 6); const uint8x8_t raw = vcreate_u8(w); const uint16x8_t win = vreinterpretq_u16_u8(vcombine_u8(vtbl1_u8(raw, vidxlo), vtbl1_u8(raw, vidxhi))); const uint8x8_t codes = vmovn_u16(vandq_u16(vshrq_n_u16(vmulq_u16(win, vmul), 10), vmask)); const uint8x8_t sel = vbsl_u8(vcge_u8(codes, vdup_n_u8(32)), vtbl4_u8(hi, vsub_u8(codes, vdup_n_u8(32))), vtbl4_u8(lo, codes)); vst1_u8(out + i, sel); i += 8; } } #elif defined(ZXC_USE_AVX512) || defined(ZXC_USE_AVX2) || \ (defined(ZXC_USE_SSE2) && defined(__SSE4_1__)) if (D == 4) { const __m128i vc2s = _mm_loadu_si128((const __m128i*)(const void*)c2s); const __m128i vrep = _mm_setr_epi8(0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7); const __m128i vmask = _mm_set1_epi8(0x0F); /* even lanes keep low nibble, odd lanes take the high nibble: shift the * replicated bytes right by 4 via a 16-bit shift + odd-lane select. */ const __m128i vodd = _mm_set1_epi16(0x0F00 - 0x0F00 + (short)0xFF00); while (i + 16 <= n) { const __m128i raw = _mm_loadl_epi64((const __m128i*)(const void*)(bits + ((i * 4) >> 3))); const __m128i rep = _mm_shuffle_epi8(raw, vrep); const __m128i hi = _mm_and_si128(_mm_srli_epi16(rep, 4), _mm_set1_epi8(0x0F)); const __m128i lo = _mm_and_si128(rep, vmask); const __m128i codes = _mm_blendv_epi8(lo, hi, vodd); _mm_storeu_si128((__m128i*)(void*)(out + i), _mm_shuffle_epi8(vc2s, codes)); i += 16; } } else if (D == 2) { /* 16 codes from 4 bytes. SSE lacks per-lane byte shifts, so shift via a * per-lane multiply: (b << (6 - 2j)) >> 6 keeps bits [2j+1:2j]. Bytes * are spread to u16 lanes, multiplied by {64,16,4,1}, shifted, then the * two halves are packed back to 16 code bytes. */ uint8_t c2s16[16]; for (int k = 0; k < 16; k++) c2s16[k] = c2s[k & 3]; const __m128i vc2s = _mm_loadu_si128((const __m128i*)(const void*)c2s16); const __m128i vspreadA = _mm_setr_epi8(0, (char)0x80, 0, (char)0x80, 0, (char)0x80, 0, (char)0x80, 1, (char)0x80, 1, (char)0x80, 1, (char)0x80, 1, (char)0x80); const __m128i vspreadB = _mm_setr_epi8(2, (char)0x80, 2, (char)0x80, 2, (char)0x80, 2, (char)0x80, 3, (char)0x80, 3, (char)0x80, 3, (char)0x80, 3, (char)0x80); const __m128i vmul = _mm_setr_epi16(64, 16, 4, 1, 64, 16, 4, 1); while (i + 16 <= n) { const __m128i raw = _mm_cvtsi32_si128( (int)((uint32_t)bits[(i * 2) >> 3] | ((uint32_t)bits[((i * 2) >> 3) + 1] << 8) | ((uint32_t)bits[((i * 2) >> 3) + 2] << 16) | ((uint32_t)bits[((i * 2) >> 3) + 3] << 24))); const __m128i a16 = _mm_srli_epi16(_mm_mullo_epi16(_mm_shuffle_epi8(raw, vspreadA), vmul), 6); const __m128i b16 = _mm_srli_epi16(_mm_mullo_epi16(_mm_shuffle_epi8(raw, vspreadB), vmul), 6); const __m128i codes = _mm_and_si128(_mm_packus_epi16(a16, b16), _mm_set1_epi8(3)); _mm_storeu_si128((__m128i*)(void*)(out + i), _mm_shuffle_epi8(vc2s, codes)); i += 16; } } else if (D == 3) { /* 16-bit window {byte_j,byte_j+1} per lane (byte_j = Di>>3); SSE has no * per-lane u16 shift, so extract bits [s,s+D) of window w by w*2^(16-D-s) * (field lands in the top D bits) then >>(16-D); s_j = Dj&7. D=3: 6 bytes. */ uint8_t c2s16[16]; for (int k = 0; k < 16; k++) c2s16[k] = c2s[k & 7]; const __m128i vc2s = _mm_loadu_si128((const __m128i*)(const void*)c2s16); const __m128i shufA = _mm_setr_epi8(0, 1, 0, 1, 0, 1, 1, 2, 1, 2, 1, 2, 2, 3, 2, 3); const __m128i shufB = _mm_setr_epi8(3, 4, 3, 4, 3, 4, 4, 5, 4, 5, 4, 5, 5, 6, 5, 6); const __m128i vmul = _mm_setr_epi16(8192, 1024, 128, 4096, 512, 64, 2048, 256); while (i + 16 <= n) { const size_t off = (i * 3) >> 3; uint32_t w0; uint16_t w1; ZXC_MEMCPY(&w0, bits + off, 4); ZXC_MEMCPY(&w1, bits + off + 4, 2); /* exactly 6 bytes read */ const __m128i raw = _mm_insert_epi16(_mm_cvtsi32_si128((int)w0), w1, 2); const __m128i cA = _mm_srli_epi16(_mm_mullo_epi16(_mm_shuffle_epi8(raw, shufA), vmul), 13); const __m128i cB = _mm_srli_epi16(_mm_mullo_epi16(_mm_shuffle_epi8(raw, shufB), vmul), 13); const __m128i codes = _mm_and_si128(_mm_packus_epi16(cA, cB), _mm_set1_epi8(7)); _mm_storeu_si128((__m128i*)(void*)(out + i), _mm_shuffle_epi8(vc2s, codes)); i += 16; } } else if (D == 5) { /* c2s has 32 entries: pshufb does 16, so select lo/hi by code bit 4. */ const __m128i vlo = _mm_loadu_si128((const __m128i*)(const void*)c2s); const __m128i vhi = _mm_loadu_si128((const __m128i*)(const void*)(c2s + 16)); const __m128i shufA = _mm_setr_epi8(0, 1, 0, 1, 1, 2, 1, 2, 2, 3, 3, 4, 3, 4, 4, 5); const __m128i shufB = _mm_setr_epi8(5, 6, 5, 6, 6, 7, 6, 7, 7, 8, 8, 9, 8, 9, 9, 10); const __m128i vmul = _mm_setr_epi16(2048, 64, 512, 16, 128, 1024, 32, 256); while (i + 16 <= n) { const size_t off = (i * 5) >> 3; uint16_t w1; ZXC_MEMCPY(&w1, bits + off + 8, 2); /* 10 source bytes */ const __m128i raw = _mm_insert_epi16(_mm_loadl_epi64((const __m128i*)(const void*)(bits + off)), w1, 4); const __m128i cA = _mm_srli_epi16(_mm_mullo_epi16(_mm_shuffle_epi8(raw, shufA), vmul), 11); const __m128i cB = _mm_srli_epi16(_mm_mullo_epi16(_mm_shuffle_epi8(raw, shufB), vmul), 11); const __m128i codes = _mm_and_si128(_mm_packus_epi16(cA, cB), _mm_set1_epi8(0x1F)); const __m128i sel = _mm_blendv_epi8(_mm_shuffle_epi8(vlo, codes), _mm_shuffle_epi8(vhi, codes), _mm_slli_epi16(codes, 3)); _mm_storeu_si128((__m128i*)(void*)(out + i), sel); i += 16; } } else if (D == 6) { /* c2s has 64 entries: 4 pshufb sub-tables, select by code bits 4-5. */ const __m128i t0 = _mm_loadu_si128((const __m128i*)(const void*)c2s); const __m128i t1 = _mm_loadu_si128((const __m128i*)(const void*)(c2s + 16)); const __m128i t2 = _mm_loadu_si128((const __m128i*)(const void*)(c2s + 32)); const __m128i t3 = _mm_loadu_si128((const __m128i*)(const void*)(c2s + 48)); const __m128i shufA = _mm_setr_epi8(0, 1, 0, 1, 1, 2, 2, 3, 3, 4, 3, 4, 4, 5, 5, 6); const __m128i shufB = _mm_setr_epi8(6, 7, 6, 7, 7, 8, 8, 9, 9, 10, 9, 10, 10, 11, 11, 12); const __m128i vmul = _mm_setr_epi16(1024, 16, 64, 256, 1024, 16, 64, 256); while (i + 16 <= n) { const size_t off = (i * 6) >> 3; uint32_t w1; ZXC_MEMCPY(&w1, bits + off + 8, 4); /* 12 source bytes */ const __m128i raw = _mm_insert_epi32( _mm_loadl_epi64((const __m128i*)(const void*)(bits + off)), (int)w1, 2); const __m128i cA = _mm_srli_epi16(_mm_mullo_epi16(_mm_shuffle_epi8(raw, shufA), vmul), 10); const __m128i cB = _mm_srli_epi16(_mm_mullo_epi16(_mm_shuffle_epi8(raw, shufB), vmul), 10); const __m128i codes = _mm_and_si128(_mm_packus_epi16(cA, cB), _mm_set1_epi8(0x3F)); const __m128i m4 = _mm_slli_epi16(codes, 3); const __m128i lo = _mm_blendv_epi8(_mm_shuffle_epi8(t0, codes), _mm_shuffle_epi8(t1, codes), m4); const __m128i hi = _mm_blendv_epi8(_mm_shuffle_epi8(t2, codes), _mm_shuffle_epi8(t3, codes), m4); _mm_storeu_si128((__m128i*)(void*)(out + i), _mm_blendv_epi8(lo, hi, _mm_slli_epi16(codes, 2))); i += 16; } } #endif /* Generic scalar bit-reader (any D). */ { uint64_t bitpos = i * (uint64_t)D; const uint32_t m = (1U << D) - 1U; const size_t fbytes = (size_t)(((uint64_t)n * (uint64_t)D + 7U) >> 3); while (i < n) { const size_t byte = (size_t)(bitpos >> 3); uint32_t w = bits[byte]; if (byte + 1 < fbytes) w |= (uint32_t)bits[byte + 1] << 8; if (D > 8 && byte + 2 < fbytes) w |= (uint32_t)bits[byte + 2] << 16; out[i++] = c2s[(w >> (bitpos & 7U)) & m]; bitpos += (uint64_t)D; } } } /** * @brief Direct emission for a node whose two children are BOTH leaves. * * out[k] = bit_k ? sym1 : sym0, i.e. sym0 ^ (delta & mask(bit_k)). Skips * materialising the two child runs entirely (idea from the PivCo reference * implementation: sequential stores + bit-test/XOR-blend beat a table merge * of two constant runs by 2-3x). */ static ZXC_ALWAYS_INLINE void zxc_pivco_emit_leaf_pair(uint8_t* RESTRICT out, const size_t n, const uint8_t sym0, const uint8_t sym1, const uint8_t* RESTRICT bits) { size_t i = 0; #if defined(ZXC_USE_NEON64) const uint8x16_t vsym0 = vdupq_n_u8(sym0); const uint8x16_t vdelta = vdupq_n_u8((uint8_t)(sym0 ^ sym1)); static const uint8_t rep_idx[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}; static const uint8_t bit_sel[16] = {1, 2, 4, 8, 16, 32, 64, 128, 1, 2, 4, 8, 16, 32, 64, 128}; const uint8x16_t vrep = vld1q_u8(rep_idx); const uint8x16_t vsel = vld1q_u8(bit_sel); while (i + 16 <= n) { uint8x16_t ctrl = vdupq_n_u8(0); ctrl = vsetq_lane_u8(bits[i >> 3], ctrl, 0); ctrl = vsetq_lane_u8(bits[(i >> 3) + 1], ctrl, 1); const uint8x16_t rep = vqtbl1q_u8(ctrl, vrep); const uint8x16_t mask = vtstq_u8(rep, vsel); vst1q_u8(out + i, veorq_u8(vsym0, vandq_u8(vdelta, mask))); i += 16; } #elif defined(ZXC_USE_NEON32) const uint8x8_t vsym0 = vdup_n_u8(sym0); const uint8x8_t vdelta = vdup_n_u8((uint8_t)(sym0 ^ sym1)); static const uint8_t bit_sel8[8] = {1, 2, 4, 8, 16, 32, 64, 128}; const uint8x8_t vsel = vld1_u8(bit_sel8); while (i + 8 <= n) { const uint8x8_t mask = vtst_u8(vdup_n_u8(bits[i >> 3]), vsel); vst1_u8(out + i, veor_u8(vsym0, vand_u8(vdelta, mask))); i += 8; } #elif defined(ZXC_USE_AVX512) || defined(ZXC_USE_AVX2) || \ (defined(ZXC_USE_SSE2) && defined(__SSSE3__)) const __m128i vsym0 = _mm_set1_epi8((char)sym0); const __m128i vdelta = _mm_set1_epi8((char)(uint8_t)(sym0 ^ sym1)); const __m128i vrep = _mm_setr_epi8(0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1); const __m128i vsel = _mm_setr_epi8(1, 2, 4, 8, 16, 32, 64, (char)128, 1, 2, 4, 8, 16, 32, 64, (char)128); while (i + 16 <= n) { const __m128i ctrl = _mm_cvtsi32_si128(bits[i >> 3] | ((int)bits[(i >> 3) + 1] << 8)); const __m128i rep = _mm_shuffle_epi8(ctrl, vrep); /* lanes where the selected bit is set -> 0xFF */ const __m128i mask = _mm_cmpeq_epi8(_mm_and_si128(rep, vsel), vsel); _mm_storeu_si128((__m128i*)(void*)(out + i), _mm_xor_si128(vsym0, _mm_and_si128(vdelta, mask))); i += 16; } #endif while (i < n) { const int bit = (bits[i >> 3] >> (i & 7)) & 1; out[i++] = bit ? sym1 : sym0; } } /** * @brief Shared PivCo section decoder body (code lengths already unpacked). * * Pass 1 (sizing) walks the wire's BFS order once: each internal node's bit * run is located, bounds-checked and popcounted, which yields both children's * symbol counts. Pass 2 rebuilds sequences bottom-up, one level at a time, * ping-ponging between dst (even depths, depth 0 = final output) and scratch * (odd depths); a level's writes never alias its reads. * * @p aux carries the topology-derived tables (leaf-pair skip flags, flat-root * c2s) precomputed at attach for a frame-constant dictionary tree; NULL for * per-section trees, which rebuild them inline below. */ static int zxc_pivco_decode_core(const uint8_t* RESTRICT payload, const size_t payload_size, uint8_t* RESTRICT dst, const size_t n, const zxc_pivco_tree_t* RESTRICT t, const zxc_pivco_decode_aux_t* RESTRICT aux, uint8_t* RESTRICT scratch) { if (UNLIKELY(n == 0)) return ZXC_ERROR_CORRUPT_DATA; /* Pass 1: node counts + bit-run pointers, straight from the wire order. */ uint32_t count[ZXC_PIVCO_MAX_NODES]; const uint8_t* bit_ptr[ZXC_PIVCO_MAX_NODES]; count[0] = (uint32_t)n; const uint8_t* p = payload; const uint8_t* const pend = payload + payload_size; for (int i = 0; i < t->n_nodes; i++) { const int nid = t->bfs[i]; const zxc_pivco_node_t* nd = &t->nd[nid]; if (t->covered[nid] || nd->sym >= 0) continue; const uint32_t c = count[nid]; if (t->flat_d[nid]) { /* Packed-code run: no partition below, nothing to popcount. */ const size_t fbytes = zxc_pivco_run_bytes(c, t->flat_d[nid]); if (UNLIKELY((size_t)(pend - p) < fbytes)) return ZXC_ERROR_CORRUPT_DATA; bit_ptr[nid] = p; p += fbytes; continue; } const size_t nbytes = zxc_pivco_run_bytes(c, 0); /* merge node: c partition bits */ if (UNLIKELY((size_t)(pend - p) < nbytes)) return ZXC_ERROR_CORRUPT_DATA; bit_ptr[nid] = p; /* popcount of the c valid bits = right child's count. Count all but the * last byte fast, then the last byte with its padding masked off: the * 8-byte loop must not swallow the final byte unmasked when nbytes % 8 == 0 * (else set padding bits inflate `ones` past the true child count). */ uint32_t ones = 0; if (nbytes) { const size_t full = nbytes - 1; size_t k = 0; for (; k + 8 <= full; k += 8) { uint64_t w; ZXC_MEMCPY(&w, p + k, 8); ones += (uint32_t)zxc_pivco_popcnt64(w); } for (; k < full; k++) ones += (uint32_t)zxc_pivco_popcnt32(p[k]); uint8_t last = p[full]; if (c & 7U) last &= (uint8_t)((1U << (c & 7U)) - 1U); ones += (uint32_t)zxc_pivco_popcnt32(last); } p += nbytes; if (UNLIKELY(ones > c)) return ZXC_ERROR_CORRUPT_DATA; const int ch0 = nd->child[0]; const int ch1 = nd->child[1]; if (ch1 >= 0) count[ch1] = ones; else if (UNLIKELY(ones != 0)) return ZXC_ERROR_CORRUPT_DATA; if (ch0 >= 0) count[ch0] = c - ones; else if (UNLIKELY(c - ones != 0)) return ZXC_ERROR_CORRUPT_DATA; } /* Per-level sequence offsets (BFS order => children contiguous). */ uint32_t seq_off[ZXC_PIVCO_MAX_NODES]; for (int d = 0; d <= t->max_depth; d++) { uint32_t off = 0; for (int i = t->lvl_start[d]; i < t->lvl_start[d + 1]; i++) { const int nid = t->bfs[i]; if (t->covered[nid]) continue; /* not materialised anywhere */ seq_off[nid] = off; off += count[nid]; } } /* Leaf-pair parents emit both runs directly from their bits (XOR-blend), * so their children never need materialising: flag them for skipping. * A dictionary tree carries these flags precomputed in aux. */ uint8_t skip_local[ZXC_PIVCO_MAX_NODES]; const uint8_t* skip; if (aux) { skip = aux->skip; } else { ZXC_MEMSET(skip_local, 0, sizeof(skip_local)); for (int i = 0; i < t->n_nodes; i++) { const zxc_pivco_node_t* nd = &t->nd[t->bfs[i]]; if (nd->sym >= 0) continue; const int ch0 = nd->child[0]; const int ch1 = nd->child[1]; if (ch0 >= 0 && ch1 >= 0 && t->nd[ch0].sym >= 0 && t->nd[ch1].sym >= 0) { skip_local[ch0] = 1; skip_local[ch1] = 1; } } skip = skip_local; } /* Pass 2: bottom-up level reconstruction. */ for (int d = t->max_depth; d >= 0; d--) { uint8_t* const buf_d = (d & 1) ? scratch : dst; const uint8_t* const buf_c = (d & 1) ? dst : scratch; /* children at d+1 (read-only) */ for (int i = t->lvl_start[d]; i < t->lvl_start[d + 1]; i++) { const int nid = t->bfs[i]; if (t->covered[nid]) continue; /* lives inside a flat root's run */ const zxc_pivco_node_t* nd = &t->nd[nid]; const uint32_t c = count[nid]; if (c == 0 || skip[nid]) continue; if (nd->sym >= 0) { ZXC_MEMSET(buf_d + seq_off[nid], (uint8_t)nd->sym, c); } else if (t->flat_d[nid]) { /* Packed-path -> symbol table (complete subtree of depth D: * 2^D leaves): precomputed at attach for a dictionary tree, * else built here, then unpack the code run directly. */ const int D = t->flat_d[nid]; uint8_t c2s_local[1U << ZXC_HUF_MAX_CODE_LEN_ULTRA]; const uint8_t* c2s; if (aux) { c2s = aux->c2s_pool + aux->c2s_off[nid]; } else { int16_t stk_n[ZXC_HUF_MAX_CODE_LEN_ULTRA + 1]; uint16_t stk_p[ZXC_HUF_MAX_CODE_LEN_ULTRA + 1]; uint8_t stk_l[ZXC_HUF_MAX_CODE_LEN_ULTRA + 1]; int sp = 0; stk_n[0] = (int16_t)nid; stk_p[0] = 0; stk_l[0] = 0; while (sp >= 0) { const int cn = stk_n[sp]; const uint32_t cp = stk_p[sp]; const int cl = stk_l[sp]; sp--; if (t->nd[cn].sym >= 0) { c2s_local[cp] = (uint8_t)t->nd[cn].sym; continue; } sp++; stk_n[sp] = t->nd[cn].child[0]; stk_p[sp] = (uint16_t)cp; stk_l[sp] = (uint8_t)(cl + 1); sp++; stk_n[sp] = t->nd[cn].child[1]; stk_p[sp] = (uint16_t)(cp | (1U << cl)); stk_l[sp] = (uint8_t)(cl + 1); } c2s = c2s_local; } zxc_pivco_unpack_flat(buf_d + seq_off[nid], c, D, bit_ptr[nid], c2s); } else { const int ch0 = nd->child[0]; const int ch1 = nd->child[1]; if (ch0 >= 0 && ch1 >= 0 && t->nd[ch0].sym >= 0 && t->nd[ch1].sym >= 0) { zxc_pivco_emit_leaf_pair(buf_d + seq_off[nid], c, (uint8_t)t->nd[ch0].sym, (uint8_t)t->nd[ch1].sym, bit_ptr[nid]); continue; } const uint32_t nl = ch0 >= 0 ? count[ch0] : 0; const uint32_t src_off = ch0 >= 0 ? seq_off[ch0] : seq_off[nd->child[1]]; zxc_pivco_merge(buf_d + seq_off[nid], buf_c + src_off, nl, c - nl, bit_ptr[nid]); } } } return ZXC_OK; } /** * @brief Decode a literal/token section (PivCo layout, inline lengths header). * * Unpacks and validates the 128-byte code-length header, then runs the * bottom-up merge decoder. * * @param[in] payload Section bytes (header + node runs). * @param[in] payload_size Section size in bytes. * @param[out] dst Receives the @p n decoded symbols; needs * ZXC_PAD_SIZE bytes of write slack past @p n. * @param[in] n Expected symbol count. * @param[in] scratch Level ping-pong buffer with at least * n + ZXC_PIVCO_SCRATCH_PAD bytes. * @return ZXC_OK or ZXC_ERROR_CORRUPT_DATA. */ int zxc_huf_decode_section(const uint8_t* RESTRICT payload, const size_t payload_size, uint8_t* RESTRICT dst, const size_t n, uint8_t* RESTRICT scratch) { if (UNLIKELY(payload_size < (size_t)ZXC_HUF_TABLE_SIZE)) return ZXC_ERROR_CORRUPT_DATA; uint8_t code_len[ZXC_HUF_NUM_SYMBOLS]; const int rc = zxc_huf_unpack_lengths(payload, code_len); if (UNLIKELY(rc != ZXC_OK)) return rc; zxc_pivco_tree_t t; if (UNLIKELY(zxc_pivco_tree_build(code_len, &t, NULL) != 0)) return ZXC_ERROR_CORRUPT_DATA; return zxc_pivco_decode_core(payload + ZXC_HUF_TABLE_SIZE, payload_size - ZXC_HUF_TABLE_SIZE, dst, n, &t, NULL, scratch); } /** * @brief Decode a shared-dictionary literal section (no inline header). * * Same as @ref zxc_huf_decode_section, but against the dictionary's PivCo * @p tree and decoder tables @p aux, prebuilt ONCE at attach time by * @ref zxc_huf_dict_tree_build (wire value enc_lit = 3) -- no per-block * unpack, tree rebuild, or c2s/skip rebuild. */ int zxc_huf_decode_section_dict(const uint8_t* RESTRICT payload, const size_t payload_size, uint8_t* RESTRICT dst, const size_t n, const zxc_pivco_tree_t* RESTRICT tree, const zxc_pivco_decode_aux_t* RESTRICT aux, uint8_t* RESTRICT scratch) { return zxc_pivco_decode_core(payload, payload_size, dst, n, tree, aux, scratch); } zxc-0.13.0/src/lib/zxc_internal.h000066400000000000000000002476251522536370400166340ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file zxc_internal.h * @brief Internal definitions, constants, SIMD helpers, and utility functions. * * This header is **not** part of the public API. It is shared across the * library's translation units and contains: * - Platform detection and SIMD intrinsic includes. * - Compiler-abstraction macros (LIKELY, PREFETCH, MEMCPY, ALIGN, ...). * - Endianness detection and byte-swap helpers. * - File-format constants (magic word, header sizes, block sizes, ...). * - Inline helpers for hashing, endian-safe loads/stores, bit manipulation, * aligned allocation, and bitstream reading. * - Internal function prototypes for chunk-level compression/decompression. * * @warning Do not include this header from user code; use the public headers * zxc_buffer.h or zxc_stream.h instead. */ #ifndef ZXC_INTERNAL_H #define ZXC_INTERNAL_H #include "zxc_deps.h" /* libc deps: , , , , and the ZXC_MALLOC / ZXC_ALIGNED_MALLOC macros. Vendor this file to retarget non-libc environments. */ #include "../../include/zxc_buffer.h" #include "../../include/zxc_constants.h" #include "../../include/zxc_error.h" #include "../../include/zxc_seekable.h" #include "rapidhash.h" #ifdef __cplusplus extern "C" { #endif /** * @defgroup internal Internal Helpers * @brief Platform abstractions, constants, and utility functions (private). * @{ */ /** * @name Atomic Qualifier * @brief Provides a portable atomic / volatile qualifier. * * If C11 atomics are available, @c ZXC_ATOMIC expands to @c _Atomic; * otherwise it falls back to @c volatile. * @{ */ #if !defined(__cplusplus) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && \ !defined(__STDC_NO_ATOMICS__) #include #define ZXC_ATOMIC _Atomic #define ZXC_USE_C11_ATOMICS 1 #else #define ZXC_ATOMIC volatile #define ZXC_USE_C11_ATOMICS 0 #endif /** @} */ /* end of Atomic Qualifier */ /** * @name SIMD Intrinsics & Compiler Macros * @brief Auto-detected SIMD feature macros for x86 (SSE/AVX) and ARM (NEON). * * Depending on the target architecture and compiler flags the following macros * may be defined: * - @c ZXC_USE_AVX512 - AVX-512F + AVX-512BW available. * - @c ZXC_USE_AVX2 - AVX2 available. * - @c ZXC_USE_SSE2 - SSE2 (x86-64 baseline) available. * - @c ZXC_USE_NEON64 - AArch64 NEON available. * - @c ZXC_USE_NEON32 - ARMv7 NEON available. * * Note: @c -mavx2 / @c -mavx512f imply @c __SSE2__, so @c ZXC_USE_SSE2 is * also defined in the AVX variants. The hand-written SIMD code paths therefore * order their preprocessor branches AVX512 -> AVX2 -> SSE2 so the widest * available path wins; the SSE2 branch is the active one only in the dedicated * @c _sse2 variant (no AVX2/AVX512 flags). SSE2 is the x86-64 baseline, so this * tier covers every 64-bit x86 CPU (and i686 with @c -msse2). The handful of * operations that would otherwise require SSE4.1 (@c _mm_max_epu32, * @c _mm_blendv_epi8, @c _mm_packus_epi32) or SSSE3 (@c _mm_shuffle_epi8) are * emulated with pure SSE2 instruction sequences or fall back to scalar code. * * Define @c ZXC_DISABLE_SIMD to gate all hand-written SIMD paths (intrinsics, * inline assembly). Compiler auto-vectorisation is unaffected. * @{ */ #ifndef ZXC_DISABLE_SIMD #if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86) #include #include #if defined(__AVX512F__) && defined(__AVX512BW__) #ifndef ZXC_USE_AVX512 #define ZXC_USE_AVX512 #endif #endif #if defined(__AVX2__) #ifndef ZXC_USE_AVX2 #define ZXC_USE_AVX2 #endif #endif #if defined(__SSE2__) || defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2) #ifndef ZXC_USE_SSE2 #define ZXC_USE_SSE2 #endif #endif #elif (defined(__ARM_NEON) || defined(__ARM_NEON__) || defined(_M_ARM64) || \ defined(ZXC_USE_NEON32) || defined(ZXC_USE_NEON64)) #if !defined(_MSC_VER) #include #endif #include #if defined(__aarch64__) || defined(_M_ARM64) #ifndef ZXC_USE_NEON64 #define ZXC_USE_NEON64 #endif #else #ifndef ZXC_USE_NEON32 #define ZXC_USE_NEON32 #endif #endif #endif #endif /* ZXC_DISABLE_SIMD */ /** @} */ /* end of SIMD Intrinsics */ /** * @name Compiler Abstractions * @brief Portable wrappers for branch hints, prefetch, memory ops, alignment, * and forced inlining. * @{ */ #if defined(__GNUC__) || defined(__clang__) /** @def LIKELY * @brief Branch prediction hint: expression is likely true. * @param x Expression to evaluate. */ #define LIKELY(x) (__builtin_expect(!!(x), 1)) /** @def UNLIKELY * @brief Branch prediction hint: expression is unlikely to be true. * @param x Expression to evaluate. */ #define UNLIKELY(x) (__builtin_expect(!!(x), 0)) /** @def RESTRICT * @brief Pointer aliasing hint (maps to __restrict__). */ #define RESTRICT __restrict__ /** @def ZXC_PREFETCH_READ * @brief Prefetch data for reading. * @param ptr Pointer to data to prefetch. */ #define ZXC_PREFETCH_READ(ptr) __builtin_prefetch((const void*)(ptr), 0, 3) /** @def ZXC_PREFETCH_WRITE * @brief Prefetch data for writing. * @param ptr Pointer to data to prefetch. */ #define ZXC_PREFETCH_WRITE(ptr) __builtin_prefetch((const void*)(ptr), 1, 3) /** @def ZXC_MEMCPY * @brief Optimized memory copy using compiler built-in. */ #define ZXC_MEMCPY(dst, src, n) __builtin_memcpy(dst, src, n) /** @def ZXC_MEMSET * @brief Optimized memory set using compiler built-in. */ #define ZXC_MEMSET(dst, val, n) __builtin_memset(dst, val, n) /** @def ZXC_ALIGN * @brief Specifies memory alignment for a variable or structure. * @param x Alignment boundary in bytes (must be a power of 2). */ #define ZXC_ALIGN(x) __attribute__((aligned(x))) /** @def ZXC_ALWAYS_INLINE * @brief Forces a function to be inlined at all optimization levels. */ #define ZXC_ALWAYS_INLINE inline __attribute__((always_inline)) /** @def ZXC_NOINLINE * @brief Prevents a function from being inlined into its callers. */ #define ZXC_NOINLINE __attribute__((noinline)) /** @def ZXC_COLD * @brief Marks a function as rarely executed: optimized for size and placed * in a cold text section, away from the hot paths' i-cache footprint. * No MSVC equivalent -- expands to nothing there (noinline still keeps * the body out of the caller). */ #define ZXC_COLD __attribute__((cold)) #elif defined(_MSC_VER) #include #if defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64) #include #define ZXC_PREFETCH_READ(ptr) _mm_prefetch((const char*)(ptr), _MM_HINT_T0) #define ZXC_PREFETCH_WRITE(ptr) _mm_prefetch((const char*)(ptr), _MM_HINT_T0) #else #define ZXC_PREFETCH_READ(ptr) __prefetch((const void*)(ptr)) #define ZXC_PREFETCH_WRITE(ptr) __prefetch((const void*)(ptr)) #endif #define LIKELY(x) (x) #define UNLIKELY(x) (x) #define RESTRICT __restrict #pragma intrinsic(memcpy, memset) #define ZXC_MEMCPY(dst, src, n) memcpy(dst, src, n) #define ZXC_MEMSET(dst, val, n) memset(dst, val, n) /** @def ZXC_ALIGN * @brief Specifies memory alignment for a variable or structure (MSVC). * @param x Alignment boundary in bytes (must be a power of 2). */ #define ZXC_ALIGN(x) __declspec(align(x)) /** @def ZXC_ALWAYS_INLINE * @brief Forces a function to be inlined at all optimization levels (MSVC). */ #define ZXC_ALWAYS_INLINE __forceinline /** @def ZXC_NOINLINE * @brief Prevents a function from being inlined into its callers (MSVC). */ #define ZXC_NOINLINE __declspec(noinline) /** @copydoc ZXC_COLD */ #define ZXC_COLD #pragma intrinsic(_BitScanReverse) #else #define LIKELY(x) (x) #define UNLIKELY(x) (x) #define RESTRICT #define ZXC_PREFETCH_READ(ptr) #define ZXC_PREFETCH_WRITE(ptr) #define ZXC_MEMCPY(dst, src, n) memcpy(dst, src, n) #define ZXC_MEMSET(dst, val, n) memset(dst, val, n) /** @def ZXC_ALWAYS_INLINE * @brief Forces a function to be inlined (fallback for non-GCC/Clang/MSVC compilers). */ #define ZXC_ALWAYS_INLINE inline /** @def ZXC_NOINLINE * @brief Prevents inlining (best-effort no-op fallback for unknown compilers). */ #define ZXC_NOINLINE #define ZXC_COLD #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L #include /** @def ZXC_ALIGN * @brief Specifies memory alignment using C11 _Alignas. * @param x Alignment boundary in bytes (must be a power of 2). */ #define ZXC_ALIGN(x) _Alignas(x) #else /** @def ZXC_ALIGN * @brief No-op alignment macro for compilers without alignment support. * @param x Ignored (alignment not supported). */ #define ZXC_ALIGN(x) #endif #endif /** @} */ /* end of Compiler Abstractions */ /* Heap allocator and cache-line-aligned allocator macros are now defined * in @c zxc_deps.h (included at the top of this header), so non-libc * targets can override them by vendoring that single file. */ /** * @name Endianness Detection * @brief Compile-time detection of host byte order. * * Defines exactly one of @c ZXC_LITTLE_ENDIAN or @c ZXC_BIG_ENDIAN. * @{ */ #ifndef ZXC_LITTLE_ENDIAN #if defined(_WIN32) || defined(__LITTLE_ENDIAN__) || \ (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) #define ZXC_LITTLE_ENDIAN #elif defined(__BIG_ENDIAN__) || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) #define ZXC_BIG_ENDIAN #else #warning "Endianness not detected, defaulting to little-endian" #define ZXC_LITTLE_ENDIAN #endif #endif /** @} */ /* end of Endianness Detection */ /** * @name Byte-Swap Helpers * @brief 16/32/64-bit byte-swap macros (only defined under @c ZXC_BIG_ENDIAN). * @{ */ #ifdef ZXC_BIG_ENDIAN #if defined(__GNUC__) || defined(__clang__) #define ZXC_BSWAP16(x) __builtin_bswap16(x) #define ZXC_BSWAP32(x) __builtin_bswap32(x) #define ZXC_BSWAP64(x) __builtin_bswap64(x) #elif defined(_MSC_VER) #define ZXC_BSWAP16(x) _byteswap_ushort(x) #define ZXC_BSWAP32(x) _byteswap_ulong(x) #define ZXC_BSWAP64(x) _byteswap_uint64(x) #else #define ZXC_BSWAP16(x) ((uint16_t)(((x) >> 8) | ((x) << 8))) #define ZXC_BSWAP32(x) \ ((uint32_t)(((x) >> 24) | (((x) >> 8) & 0xFF00) | (((x) << 8) & 0xFF0000) | ((x) << 24))) #define ZXC_BSWAP64(x) \ ((uint64_t)(((uint64_t)ZXC_BSWAP32((uint32_t)(x)) << 32) | ZXC_BSWAP32((uint32_t)((x) >> 32)))) #endif #endif /** @} */ /* end of Byte-Swap Helpers */ /** * @name File Format Constants * @brief Magic words, header sizes, block sizes, and related constants. * @{ */ /** @brief Magic word identifying ZXC files (little-endian 0x9CB02EF5). */ #define ZXC_MAGIC_WORD 0x9CB02EF5U /** @brief Current on-disk file format version. The decoder accepts only this * version; Older versions are rejected with ZXC_ERROR_BAD_VERSION. */ #define ZXC_FILE_FORMAT_VERSION 7 /** @brief Safety padding appended to buffers to tolerate overruns. */ #define ZXC_PAD_SIZE 32 /** * @brief Tail padding required on the decompression destination buffer. * * The decoder's fast path uses speculative wild-copy writes and gates * fast-loop entry on @c d_end - ZXC_DECOMPRESS_TAIL_PAD. Sizing * @c dst_capacity to @c uncompressed_size + ZXC_DECOMPRESS_TAIL_PAD * guarantees the fast path is reachable and that tail bounds checks * never spuriously reject the last literals of a valid block. * * @see zxc_decompress_block_bound() */ #define ZXC_DECOMPRESS_TAIL_PAD (ZXC_PAD_SIZE * 66) /** @brief Assumed CPU cache line size for alignment. */ #define ZXC_CACHE_LINE_SIZE 64 /** @brief Bitmask for cache-line alignment checks. */ #define ZXC_ALIGNMENT_MASK (ZXC_CACHE_LINE_SIZE - 1) /** @brief Round @p x up to the next cache-line boundary. */ #define ZXC_ALIGN_CL(x) (((x) + ZXC_ALIGNMENT_MASK) & ~(size_t)ZXC_ALIGNMENT_MASK) /** * @brief Number of @c uint64_t words needed to hold a bitmap of @p n_bits. * * Equivalent to @c ceil(n_bits / 64). */ #define ZXC_BITMAP_WORDS(n_bits) (((n_bits) + 63) / 64) /** @brief Bit flag in the Flags byte indicating checksum presence (bit 7). */ #define ZXC_FILE_FLAG_HAS_CHECKSUM 0x80U /** @brief Bit flag in the Flags byte indicating a dictionary is required (bit 6). */ #define ZXC_FILE_FLAG_HAS_DICTIONARY 0x40U /** @brief Mask for the checksum algorithm id (bits 0-3). */ #define ZXC_FILE_CHECKSUM_ALGO_MASK 0x0FU /** @brief Magic word identifying ZXC dictionary files (.zxd). */ #define ZXC_DICT_MAGIC 0x9CB0D1C7U /** @brief Current dictionary file format version. A 128-byte packed Huffman * code-lengths table (shared literal table) always follows the * dictionary content. */ #define ZXC_DICT_VERSION 1 /** @brief K-gram length scanned by the dictionary trainer. Aligned on the LZ * minimum match length so trained patterns are matchable at encode time. */ #define ZXC_DICT_KGRAM_LEN ZXC_LZ_MIN_MATCH_LEN /** @brief Address bits for the dictionary trainer's k-gram frequency table. */ #define ZXC_DICT_HASH_BITS 16 /** @brief Maximum number of candidate segments the dictionary trainer keeps. */ #define ZXC_DICT_MAX_SEGMENTS (1U << 16) /** @brief Target number of sampled k-gram positions for the trainer's frequency * estimate. Bounds the count so 16-bit counters stay unsaturated on large * corpora; the trainer strides the corpus to hit roughly this many positions. */ #define ZXC_DICT_SAMPLE_TARGET (1U << 19) /** @brief Number of buckets in the dictionary trainer's frequency table. */ #define ZXC_DICT_HASH_SIZE (1U << ZXC_DICT_HASH_BITS) /** @brief Training block size for the shared-table literal statistics. */ #define ZXC_DICT_HUF_TRAIN_BLOCK 4096U /** @brief Cap on the corpus bytes compressed by the literal-table trainer: the * histogram converges early, so past it slices are strided evenly instead. */ #define ZXC_DICT_HUF_SAMPLE_BUDGET (8U << 20) /** @brief Block header size: Type(1)+Flags(1)+Reserved(1)+CRC(1)+CompSize(4). */ #define ZXC_BLOCK_HEADER_SIZE 8 /** @brief Size of the per-block checksum field in bytes. */ #define ZXC_BLOCK_CHECKSUM_SIZE 4 /** @brief Binary size of a GLO block sub-header. */ #define ZXC_GLO_HEADER_BINARY_SIZE 16 /** @brief Binary size of a GHI block sub-header. */ #define ZXC_GHI_HEADER_BINARY_SIZE 16 /** @brief Worst-case format overhead inside a single block beyond the outer * 8-byte block header and the optional 4-byte checksum. * * Covers the inner GLO/GHI sub-header (16 B) plus four section descriptors * (4 x 8 = 32 B) = 48 B, with a 16 B safety margin for future format * evolution. Used by zxc_compress_block_bound() and zxc_compress_bound() * to size the destination buffer in the worst (incompressible) case. */ #define ZXC_BLOCK_FORMAT_OVERHEAD 64 /** @brief Binary size of a section descriptor (comp_size + raw_size). */ #define ZXC_SECTION_DESC_BINARY_SIZE 8 /** @brief 32-bit mask for extracting sizes from a section descriptor. */ #define ZXC_SECTION_SIZE_MASK 0xFFFFFFFFU /** @brief Number of sections in a GLO block. */ #define ZXC_GLO_SECTIONS 4 /** @brief Number of sections in a GHI block. */ #define ZXC_GHI_SECTIONS 3 /** @brief Checksum algorithm id for RapidHash (default, sole implementation). */ #define ZXC_CHECKSUM_RAPIDHASH 0 /** @brief Size of the global checksum appended after EOF block (4 bytes). */ #define ZXC_GLOBAL_CHECKSUM_SIZE 4 /** @name Seekable Format Constants * @brief Seek table block appended between EOF block and footer. * * The seek table is optional (opt-in at compression time) and allows * random-access decompression by recording per-block compressed and * decompressed sizes. It uses a standard ZXC block header with * @c block_type = @c ZXC_BLOCK_SEK. * * Detection from the end of the file: the reader derives @c num_blocks * from the file footer (total decompressed size) and file header (block size). * It then seeks backward to validate the SEK block header. * @{ */ /** @brief Per-block entry size: comp_size(4) only. decomp_size is derived * from the file header's block_size (all blocks except the last are full). */ #define ZXC_SEEK_ENTRY_SIZE 4 /** @} */ /* end of Seekable Format Constants */ /** @name GLO Token Constants * @brief 4-bit literal length / 4-bit match length / 16-bit offset. * @{ */ /** @brief Bits for Literal Length in a GLO token. */ #define ZXC_TOKEN_LIT_BITS 4 /** @brief Bits for Match Length in a GLO token. */ #define ZXC_TOKEN_ML_BITS 4 /** @brief Mask to extract Literal Length from a GLO token. */ #define ZXC_TOKEN_LL_MASK ((1U << ZXC_TOKEN_LIT_BITS) - 1) /** @brief Mask to extract Match Length from a GLO token. */ #define ZXC_TOKEN_ML_MASK ((1U << ZXC_TOKEN_ML_BITS) - 1) /** @} */ /** @name GHI Sequence Constants * @brief 8-bit literal length / 8-bit match length / 16-bit offset. * @{ */ /** @brief Bits for Literal Length in a GHI sequence. */ #define ZXC_SEQ_LL_BITS 8 /** @brief Bits for Match Length in a GHI sequence. */ #define ZXC_SEQ_ML_BITS 8 /** @brief Bits for Offset in a GHI sequence. */ #define ZXC_SEQ_OFF_BITS 16 /** @brief Mask to extract Literal Length from a GHI sequence. */ #define ZXC_SEQ_LL_MASK ((1U << ZXC_SEQ_LL_BITS) - 1) /** @brief Mask to extract Match Length from a GHI sequence. */ #define ZXC_SEQ_ML_MASK ((1U << ZXC_SEQ_ML_BITS) - 1) /** @brief Mask to extract Offset from a GHI sequence. */ #define ZXC_SEQ_OFF_MASK ((1U << ZXC_SEQ_OFF_BITS) - 1) /** @} */ /** @name Literal Stream Encoding * @{ */ /** @brief Flag bit indicating an RLE run in the literal stream (0x80). */ #define ZXC_LIT_RLE_FLAG 0x80U /** @brief Mask to extract the run/literal length (lower 7 bits). */ #define ZXC_LIT_LEN_MASK (ZXC_LIT_RLE_FLAG - 1) /** @} */ /** @name LZ77 Constants * @brief Hash table geometry, sliding window, and match parameters. * * The hash table uses a split layout with 15-bit addressing (32 768 buckets): * - `hash_table[]`: uint32_t, stores `(epoch << offset_bits) | position` (128 KB). * - `hash_tags[]`: uint8_t, stores an 8-bit tag for fast rejection (32 KB). * Total: 160 KB. The tag table fits in L1 cache, enabling a * "filter-first" access pattern that avoids cold loads into hash_table * on the ~60-75% of lookups where the tag mismatches. * The 64 KB sliding window allows `chain_table` to use `uint16_t`. * @{ */ /** @brief Address bits for the LZ77 hash table (2^15 = 32 768 buckets). */ #define ZXC_LZ_HASH_BITS 15 /** @brief Marsaglia multiplicative hash constant for 4-byte hashing. */ #define ZXC_LZ_HASH_PRIME1 0x2D35182DU /** @brief Marsaglia/Vigna xorshift* multiplier for 5-byte hashing. */ #define ZXC_LZ_HASH_PRIME2 0x2545F4914F6CDD1DULL /** @brief Maximum number of entries in the hash table. */ #define ZXC_LZ_HASH_SIZE (1U << ZXC_LZ_HASH_BITS) /** @brief Sliding window size (64 KB). */ #define ZXC_LZ_WINDOW_SIZE (1U << 16) /** @brief Mask for ring-buffer indexing into chain_table (power-of-two window). */ #define ZXC_LZ_WINDOW_MASK (ZXC_LZ_WINDOW_SIZE - 1U) /** @brief Minimum match length for an LZ77 match. */ #define ZXC_LZ_MIN_MATCH_LEN 5 /** @brief Maximum legitimate value a varint can decode to. * * A varint value represents (ll - MASK) or (ml - MASK) and is therefore always * strictly less than ZXC_BLOCK_SIZE_MAX (enforced by the Block API entry * points). The cap is set to (ZXC_BLOCK_SIZE_MAX - 1), which fits cleanly in a * 3-byte varint (21 bits): the decoder rejects any 4- or 5-byte encoding, and * the encoder refuses to emit values above this bound. Together they bound the * varint surface to exactly the format-defined block size limit. */ #define ZXC_MAX_VARINT_VALUE ((uint32_t)(ZXC_BLOCK_SIZE_MAX - 1U)) /** @brief Maximum decoded output of a single sequence with INLINE ll/ml * (non-varint). Used by 4x decoder bounds checks to reserve space for * subsequent inline sequences in the same batch when the current * sequence has a varint-extended ml. */ #define ZXC_GLO_MAX_INLINE_OUT_PER_SEQ \ ((ZXC_TOKEN_LL_MASK - 1U) + (ZXC_TOKEN_ML_MASK - 1U) + ZXC_LZ_MIN_MATCH_LEN) /* 33 */ #define ZXC_GHI_MAX_INLINE_OUT_PER_SEQ \ ((ZXC_SEQ_LL_MASK - 1U) + (ZXC_SEQ_ML_MASK - 1U) + ZXC_LZ_MIN_MATCH_LEN) /* 513 */ /** @brief Base bias added to encoded offsets (stored = actual - bias). */ #define ZXC_LZ_OFFSET_BIAS 1 /** @brief Maximum allowed offset distance. */ #define ZXC_LZ_MAX_DIST (ZXC_LZ_WINDOW_SIZE - 1) /** @brief Bytes at the block end where match search stops (left as literals). * Equals the 8-byte word the finder reads at each probe, so @c ip+8<=iend. */ #define ZXC_LZ_SEARCH_MARGIN (sizeof(uint64_t)) /** @} */ /** @name Optimal Parser Tuning (level >= 6) * @brief Static prices and complexity guards used by the level-6 optimal * LZ77 parser DP. * @{ */ /** @brief Static price (bits) of a match token before varint extras: 1 byte * token + 2 byte offset. */ #define ZXC_OPT_MATCH_COST_BASE ((uint32_t)(3U * CHAR_BIT)) /** @brief Threshold above which `find_best_match` is skipped at intra-match * positions, keeping the parser O(N) on highly repetitive data. */ #define ZXC_OPT_LONG_MATCH_SKIP ((size_t)256) /** @brief Minimum literal count for the sample-based Huffman cost estimator * used by the optimal parser. Below this, the strided sample is too * small for the resulting code-lengths to be statistically reliable, * so the estimator falls back to RAW cost (8 bits/byte). */ #define ZXC_OPT_LIT_SAMPLE_MIN 1024 /** @} */ /** @name Hash Prime Constants * @brief Mixing primes used by internal hash functions. * @{ */ /** @brief Hash prime 1. */ #define ZXC_HASH_PRIME1 0x9E3779B97F4A7C15ULL /** @brief Hash prime 2. */ #define ZXC_HASH_PRIME2 0xD2D84A61D2D84A61ULL /** @} */ /** @name Huffman Codec Constants * @brief Length-limited canonical Huffman codec for GLO literal sections * (level >= 6) and level-7 token sections, in the PivCo layout. * * On-disk section payload layout (FORMAT.md section 5.2.1): * - @c ZXC_HUF_TABLE_SIZE bytes: @c ZXC_HUF_NUM_SYMBOLS code lengths * packed two per byte (4 bits each). The same packed table is used as the * per-block lengths header (enc_lit=2) and as the shared table carried by * a .zxd dictionary (enc_lit=3, header omitted) -- hence the public * constant. * - Node runs: for every emitting node of the canonical code tree in BFS * order, its branch bits (or packed D-bit residuals for flat subtree * roots), LSB-first, each run padded to a byte boundary. All run sizes * are derived (root count + popcounts), never stored. * @{ */ /** @brief Maximum Huffman code length, in bits: the ceiling the decoder's * on-wire validation accepts (and the level-7 encoder cap). Bounds * the tree depth, hence the merge-level count and every stack array * sized off the code length. The encoder caps codes per level via * ::zxc_huf_enc_max_code_len. */ #define ZXC_HUF_MAX_CODE_LEN_ULTRA 11 /** @brief Encoder code-length cap for levels up to ::ZXC_LEVEL_DENSITY (below * ::ZXC_LEVEL_ULTRA): shallow 8-bit trees decode fastest (fewer merge * levels, denser flat subtrees) -- this cap is the de-facto speed * governor of level 6. */ #define ZXC_HUF_MAX_CODE_LEN_DENSITY 8 /** @brief Alphabet size: one entry per possible byte value. */ #define ZXC_HUF_NUM_SYMBOLS 256 /** @brief Upper bound on PivCo tree nodes (full binary tree over the alphabet). */ #define ZXC_PIVCO_MAX_NODES (2 * ZXC_HUF_NUM_SYMBOLS - 1) /** @brief One PivCo Huffman tree node. */ typedef struct { int16_t child[2]; /* node index, -1 = absent */ int16_t sym; /* >= 0: leaf symbol; -1: internal */ } zxc_pivco_node_t; /** * @brief Canonical Huffman tree in PivCo (level-ordered) form. * * Derived deterministically from the 128-byte packed code lengths by * zxc_huf_dict_tree_build / the section decoders; pure value type (index-based, * no internal pointers), safe to copy or embed. Embedded in ::zxc_cctx_t so a * dictionary's shared table is built ONCE at attach instead of per block. */ typedef struct { zxc_pivco_node_t nd[ZXC_PIVCO_MAX_NODES]; int16_t bfs[ZXC_PIVCO_MAX_NODES]; /* node ids in BFS (== wire) order */ int16_t lvl_start[ZXC_HUF_MAX_CODE_LEN_ULTRA + 2]; int n_nodes; int max_depth; /* Flat-subtree fast path: flat_d[nid] = D (>= 2) when nid roots a MAXIMAL * complete subtree whose leaves all sit exactly D levels below it; such a * node's wire run is its symbols' packed D-bit residual codes instead of * D levels of partition bitmaps (same bit count, decode = unpack+lookup). * covered[nid] = 1 for every strict descendant of a flat root: those nodes * do not exist on the wire nor in the level buffers. Both sides derive * this from the code lengths alone, so nothing is signalled. */ uint8_t flat_d[ZXC_PIVCO_MAX_NODES]; uint8_t covered[ZXC_PIVCO_MAX_NODES]; } zxc_pivco_tree_t; /** * @brief Precomputed decode-side tables derived from a ::zxc_pivco_tree_t. * * Pure functions of the tree topology (no dependence on section data): * @c skip flags the children of leaf-pair parents (emitted directly by the * parent's XOR-blend, never materialised), and @c c2s_pool holds each flat * root's packed-code -> symbol table at @c c2s_off[nid]. Flat subtrees have * disjoint leaves, so the pool never exceeds ZXC_HUF_NUM_SYMBOLS entries; the * +16 slack covers zxc_pivco_unpack_flat's SIMD table loads, which round a * table up to 16 entries. Per-section trees rebuild these inline in * zxc_pivco_decode_core; dictionary trees build them ONCE at attach so the * small-block dict decode path stops repaying the DFS + fills per block. */ typedef struct { uint8_t skip[ZXC_PIVCO_MAX_NODES]; /**< 1 = child of a leaf-pair parent. */ uint16_t c2s_off[ZXC_PIVCO_MAX_NODES]; /**< Flat roots: offset into c2s_pool. */ uint8_t c2s_pool[ZXC_HUF_NUM_SYMBOLS + 16]; /**< Concatenated c2s tables. */ } zxc_pivco_decode_aux_t; /** * @brief Frame-constant dictionary Huffman state, prebuilt once at attach. * * Bundles everything the per-block dict paths reuse: the PivCo @c tree (decoder * + estimator), the canonical @c codes / @c code_len (encoder), and the * decode-side @c dec tables. Carved from the context workspace only when * @c dict_size > 0, so no-dict contexts pay nothing for it. Built by * @ref zxc_huf_dict_tree_build via @c zxc_cctx_attach_dict_huf. */ typedef struct { zxc_pivco_tree_t tree; /**< PivCo tree from the shared literal table. */ uint32_t codes[ZXC_HUF_NUM_SYMBOLS]; /**< Canonical codes (encoder side). */ uint8_t code_len[ZXC_HUF_NUM_SYMBOLS]; /**< Unpacked code lengths. */ zxc_pivco_decode_aux_t dec; /**< Precomputed decoder tables. */ } zxc_dict_huf_state_t; /** @brief RLE margin shift: source of the legacy below-ULTRA premium used by * ::zxc_ss_prem_rle_q8 (256 >> shift reproduces the historical * RLE-vs-RAW margin exactly). */ #define ZXC_RLE_MARGIN_SHIFT 5 /** @brief Huffman margin shift: source of the legacy below-ULTRA premium used * by ::zxc_ss_prem_huf_q8 (the frozen ::ZXC_HUF_MIN_LITERALS floor was * also historically derived from it). */ #define ZXC_HUF_MARGIN_SHIFT 5 /** @name Space-speed section selection * * Section encodings are selected at EVERY level by pricing each candidate * with a Lagrangian cost `J = compressed_size + decode_tax` and taking the * minimum (Kraken-style `J = R + lambda*D` with a linear per-byte * decode-time model). The tax charges the DECODE time a candidate adds over * the RAW copy path, expressed in bytes: * `tax = (n_decoded_bytes * PREM) >> 8`. Only the premium is per-level: * * - Below DENSITY, the premiums reproduce the historical fixed margins * EXACTLY (8 = 3.125% = the old `>> 5` margins), keeping the sub-DENSITY * levels (1-5) output byte-stable. * - At DENSITY and above the physical premiums apply: `PREM_HUF = 4` (1.56% * -- the entropy decoder costs ~0.5 ns/B over a raw copy, so ~1 byte of * size buys ~30 ns of decode; validated on silesia: ~0.8 pt of ratio for * ~3% decode vs the legacy margin) and `PREM_RLE = 1` (0.39%: RLE decodes * at near copy speed, only a token tax to break ties toward RAW). * @{ */ /** @brief Decode tax of a candidate: `n` decoded bytes at premium `prem` (Q8). */ #define ZXC_SS_TAX(n, prem_q8) (((size_t)(n) * (size_t)(prem_q8)) >> 8) /** * @brief Per-level RLE-vs-RAW decode premium (Q8) for the space-speed selector. * * Feeds @ref ZXC_SS_TAX, which charges an RLE candidate `n * premium >> 8` * decode-tax bytes over the RAW copy path. Below @ref ZXC_LEVEL_DENSITY the * premium is `256 >> ZXC_RLE_MARGIN_SHIFT` (8 = 3.125%), reproducing the * historical RLE-vs-RAW margin exactly so those levels keep byte-stable * selection. At @ref ZXC_LEVEL_DENSITY and above the physical premium 1 (0.39%) * applies: RLE decodes at near copy speed, so it needs only a token tax to break * ties toward RAW. * * @param[in] level Compression level. * @return Decode premium in Q8 (RLE decode-tax bytes per 256 decoded bytes). */ static inline int zxc_ss_prem_rle_q8(const int level) { return (level >= ZXC_LEVEL_DENSITY) ? 1 : (256 >> ZXC_RLE_MARGIN_SHIFT); } /** * @brief Per-level Huffman/PivCo-vs-RAW decode premium (Q8) for the space-speed * selector. * * Feeds @ref ZXC_SS_TAX, which charges a Huffman literal (or token) candidate * `n * premium >> 8` decode-tax bytes over the RAW copy path, the level's * lambda folded with the entropy decoder's cost. Below @ref ZXC_LEVEL_DENSITY * the premium is `256 >> ZXC_HUF_MARGIN_SHIFT` (8 = 3.125%), matching the * historical Huffman margin against a RAW baseline. At @ref ZXC_LEVEL_DENSITY * and above the physical premium 4 (1.56%) applies, trading more decode time for * ratio (the entropy decoder costs ~0.5 ns/B over a raw copy; validated on * silesia). * * @param[in] level Compression level. * @return Decode premium in Q8 (Huffman decode-tax bytes per 256 decoded bytes). */ static inline int zxc_ss_prem_huf_q8(const int level) { return (level >= ZXC_LEVEL_DENSITY) ? 4 : (256 >> ZXC_HUF_MARGIN_SHIFT); } /** @} */ /** @brief Absolute floor (in literals) below which a Huffman candidate is * never evaluated. * * Frozen byte-stability threshold. The value was originally derived * from v6 wire geometry (128-byte lengths table + 6-byte sub-stream * sizes) under the pre-Lagrangian `huf_total < baseline * (M-1)/M` * call-site rule; neither input exists in v7 (sub-stream sizes are * derived, selection uses J = size + lambda * tax), but raising or * lowering the floor changes which blocks pick Huffman and therefore * the emitted archive bytes, so it stays frozen at the historical * value rather than being re-derived. */ #define ZXC_HUF_MIN_LITERALS 139 /** @} */ /** @brief Clamps a resolved compression level to the supported ceiling. * * Out-of-range levels above ::ZXC_LEVEL_ULTRA are silently clamped * (never rejected) at every compress entry point, so `level = 99` * behaves as ULTRA across the buffer, context, block and stream APIs * and every language binding inherits the same policy. Levels <= 0 * select the caller's default before this is applied. */ static inline int zxc_level_clamp(const int level) { return (level > ZXC_LEVEL_ULTRA) ? ZXC_LEVEL_ULTRA : level; } /** @brief Encoder Huffman code-length cap for a compression @p level: levels below * ::ZXC_LEVEL_ULTRA use ::ZXC_HUF_MAX_CODE_LEN_DENSITY, ::ZXC_LEVEL_ULTRA uses * the full ::ZXC_HUF_MAX_CODE_LEN_ULTRA ceiling (denser codes, slower decode). * Applies to both literal and token Huffman; the decoder always supports the * ceiling, so lower-level streams decode unchanged. */ static inline int zxc_huf_enc_max_code_len(const int level) { return (level >= ZXC_LEVEL_ULTRA) ? ZXC_HUF_MAX_CODE_LEN_ULTRA : ZXC_HUF_MAX_CODE_LEN_DENSITY; } /** * @brief Boundary package-merge work item. * * Each level holds at most `2 * ZXC_HUF_NUM_SYMBOLS` of these; exposed so * callers can size pre-allocated scratch via ::ZXC_HUF_BUILD_SCRATCH_SIZE. */ typedef struct { uint32_t weight; /**< Accumulated weight (summed frequency) of the package. */ int16_t left; /**< Left child index, or -1 for a leaf. */ int16_t right; /**< Right child index, or -1 for a leaf. */ int16_t sym; /**< Symbol index for a leaf, or -1 for an internal node. */ } zxc_huf_pm_item_t; /** @brief Trace-back stack frame for the package-merge code-length recovery. */ typedef struct { int8_t lvl; /**< Package-merge level being traced back. */ int16_t idx; /**< Item index within that level. */ } zxc_huf_pm_frame_t; /** @brief Per-level item bound: at most leaves + paired packages from the * previous level. */ #define ZXC_HUF_PM_LEVEL_BOUND (2 * ZXC_HUF_NUM_SYMBOLS) /** @brief Worst-case scratch size (bytes) for ::zxc_huf_build_code_lengths. * Carved by the function into items / counts / stack regions; sized * for the worst-case alphabet (n = `ZXC_HUF_NUM_SYMBOLS`). Includes * a small alignment slack between regions. */ #define ZXC_HUF_BUILD_SCRATCH_SIZE \ ((size_t)ZXC_HUF_MAX_CODE_LEN_ULTRA * (size_t)ZXC_HUF_PM_LEVEL_BOUND * \ sizeof(zxc_huf_pm_item_t) + \ 8U + (size_t)ZXC_HUF_MAX_CODE_LEN_ULTRA * sizeof(int) + 8U + \ (size_t)ZXC_HUF_MAX_CODE_LEN_ULTRA * (size_t)ZXC_HUF_PM_LEVEL_BOUND * \ sizeof(zxc_huf_pm_frame_t)) /** @} */ /** @name Block Size Helpers * @brief Runtime helpers for variable block sizes. * @{ */ /** * @brief Integer log-base-2 for a 32-bit value. * @param v Must be a power of two (returns 0 for zero). * @return Floor of log2(v). */ static ZXC_ALWAYS_INLINE uint32_t zxc_log2_u32(const uint32_t v) { #ifdef _MSC_VER unsigned long index; return (v == 0) ? 0 : (_BitScanReverse(&index, v) ? index : 0); #else return (v == 0) ? 0 : (uint32_t)(31 - __builtin_clz(v)); #endif } /** * @brief Branchless bit_ceil: smallest power of two >= v, clamped to ZXC_BLOCK_SIZE_MIN. * @param[in] v Input size (must be > 0). * @return Smallest power of two >= @p v, clamped up to @ref ZXC_BLOCK_SIZE_MIN. */ static ZXC_ALWAYS_INLINE size_t zxc_block_size_ceil(const size_t v) { uint64_t x = (uint64_t)v - 1; x |= x >> 1; x |= x >> 2; x |= x >> 4; x |= x >> 8; x |= x >> 16; x |= x >> 32; x++; const size_t bs = (size_t)x; return (bs < ZXC_BLOCK_SIZE_MIN) ? ZXC_BLOCK_SIZE_MIN : bs; } /** * @brief Validates a block size. * Must be a power of two in [ZXC_BLOCK_SIZE_MIN, ZXC_BLOCK_SIZE_MAX]. * @param[in] bs Block size to validate. * @return 1 if valid, 0 otherwise. */ static ZXC_ALWAYS_INLINE int zxc_validate_block_size(const size_t bs) { return bs >= ZXC_BLOCK_SIZE_MIN && bs <= ZXC_BLOCK_SIZE_MAX && (bs & (bs - 1)) == 0; } /** @} */ /** @} */ /* end of File Format Constants */ /** * @struct zxc_lz77_params_t * @brief Search parameters for LZ77 compression levels. * * Each compression level maps to a specific set of parameters that control the * trade-off between compression speed and ratio. Higher search depths and lazy * matching improve ratio at the expense of throughput; larger step values * accelerate literal scanning but may miss short matches. */ typedef struct { /** Maximum number of candidates explored in the hash chain per position. * Higher values find better matches but increase CPU cost linearly. */ int search_depth; /** "Good enough" match length: once a match reaches this threshold the * chain walk stops immediately, avoiding wasted effort on an already * excellent match. */ int sufficient_len; /** Enable lazy matching. When set, after finding a match at position * @c ip the compressor probes @c ip+1 (and @c ip+2 for level >= 4) to * see if a longer match exists. If so, a literal is emitted and the * better match is taken instead. Improves ratio but costs extra work. */ int use_lazy; /** Maximum number of candidates explored during lazy evaluation (same * semantics as @ref search_depth but applied to the ip+1 / ip+2 probes). * Only meaningful when @ref use_lazy is non-zero. */ int lazy_attempts; /** Skip lazy evaluation when the current match length already reaches * this threshold: a match this long is unlikely to be beaten at the * next byte. Set to 0 when @ref use_lazy is disabled. */ int lazy_len_threshold; /** Base step size when advancing through unmatched literals. * 1 = test every byte (best ratio), 4 = skip aggressively (fastest). */ uint32_t step_base; /** Acceleration factor for step size: @c step = step_base + (distance >> step_shift). * A larger value keeps the step conservative (grows slowly with distance); * a smaller value ramps up quickly, skipping more in long literal runs. */ uint32_t step_shift; } zxc_lz77_params_t; /** * @brief Retrieves LZ77 compression parameters based on the specified compression level. * * This inline function returns the appropriate LZ77 parameters configuration * for the given compression level. * * @param[in] level The compression level to use for determining LZ77 parameters. * @return zxc_lz77_params_t The LZ77 parameters structure corresponding to the specified level. */ static ZXC_ALWAYS_INLINE zxc_lz77_params_t zxc_get_lz77_params(const int level) { if (level >= ZXC_LEVEL_ULTRA) return (zxc_lz77_params_t){128, 256, 0, 0, 0, 1, 8}; // search_depth, sufficient_len, use_lazy, lazy_attempts, lazy_len_threshold, step_base, // step_shift static const zxc_lz77_params_t table[7] = { {3, 16, 0, 0, 0, 4, 4}, // fallback {3, 16, 0, 0, 0, 4, 4}, // level 1 {3, 18, 0, 0, 0, 3, 6}, // level 2 {3, 16, 1, 4, 128, 1, 4}, // level 3 {3, 18, 1, 4, 128, 1, 5}, // level 4 {64, 256, 1, 16, 128, 1, 8}, // level 5 {64, 256, 0, 0, 0, 1, 8} // level 6 }; return table[level < ZXC_LEVEL_FASTEST ? ZXC_LEVEL_FASTEST : level]; } /** * @enum zxc_block_type_t * @brief Defines the different types of data blocks supported by the ZXC * format. * * This enumeration categorizes blocks based on the compression strategy * applied: * - `ZXC_BLOCK_RAW` (0): No compression. Used when data is incompressible (high * entropy) or when compression would expand the data size. * - `ZXC_BLOCK_GLO` (1): General-purpose compression (LZ77 + Bitpacking). This * is the default for most data (text, binaries, JSON, etc.). Includes 4 sections descriptors. * - `ZXC_BLOCK_GHI` (2): General-purpose high-velocity mode using LZ77 with advanced * techniques (lazy matching, step skipping) for maximum ratio. Includes 3 sections descriptors. * - `ZXC_BLOCK_SEK` (254): Seek table block. Contains per-block compressed/decompressed sizes * for random-access decompression. Placed between EOF block and file footer. * - `ZXC_BLOCK_EOF` (255): End of file marker. */ typedef enum { ZXC_BLOCK_RAW = 0, ZXC_BLOCK_GLO = 1, ZXC_BLOCK_GHI = 2, ZXC_BLOCK_SEK = 254, ZXC_BLOCK_EOF = 255 } zxc_block_type_t; /** * @enum zxc_section_encoding_t * @brief Specifies the encoding methods used for internal data sections. * * These modes determine how specific components (like literals, match lengths, * or offsets) are stored within a block. * - `ZXC_SECTION_ENCODING_RAW`: Data is stored uncompressed. * - `ZXC_SECTION_ENCODING_RLE`: Run-Length Encoding. * - `ZXC_SECTION_ENCODING_HUFFMAN`: canonical Huffman in the PivCo layout * (level-ordered branch runs, max 11-bit codes -- FORMAT.md section 5.2.1). * Valid for the literal stream (`enc_lit`, level >= 6) and the token * stream (`enc_litlen`, level 7) of GLO blocks. * - `ZXC_SECTION_ENCODING_HUFFMAN_DICT`: same payload as HUFFMAN but the * 128-byte code-lengths header is omitted: codes come from the shared * table carried by the dictionary (.zxd). Only valid for `enc_lit` of GLO * blocks in dictionary-compressed archives; requires the same dictionary * (content + table, bound by dict_id) at decode time. */ typedef enum { ZXC_SECTION_ENCODING_RAW = 0, ZXC_SECTION_ENCODING_RLE = 1, ZXC_SECTION_ENCODING_HUFFMAN = 2, ZXC_SECTION_ENCODING_HUFFMAN_DICT = 3 } zxc_section_encoding_t; /** * @struct zxc_gnr_header_t * @brief Header specific to General (LZ-based) compression blocks. * * This header follows the main block header when the block type is GLO/GHI. It * describes the layout of sequences and literals. * * @var zxc_gnr_header_t::n_sequences * The total count of LZ sequences in the block. * @var zxc_gnr_header_t::n_literals * The total count of literal bytes. * @var zxc_gnr_header_t::enc_lit * Encoding method used for the literal stream. * @var zxc_gnr_header_t::enc_litlen * Encoding method used for the literal lengths stream. * @var zxc_gnr_header_t::enc_mlen * Encoding method used for the match lengths stream. * @var zxc_gnr_header_t::enc_off * Encoding method used for the offset stream. */ typedef struct { uint32_t n_sequences; // Number of sequences uint32_t n_literals; // Number of literals uint8_t enc_lit; // Literal encoding uint8_t enc_litlen; // Literal lengths encoding uint8_t enc_mlen; // Match lengths encoding uint8_t enc_off; // Offset encoding (Unused in Token format, kept for alignment) } zxc_gnr_header_t; /** * @struct zxc_section_desc_t * @brief Describes the size attributes of a specific data section. * * Used to track the compressed and uncompressed sizes of sub-components * (e.g., a literal stream or offset stream) within a block. */ typedef struct { uint64_t sizes; /**< Packed sizes: compressed size (low 32 bits) | raw size (high 32 bits). */ } zxc_section_desc_t; /** * ============================================================================ * MEMORY & ENDIANNESS HELPERS * ============================================================================ * Functions to handle unaligned memory access and Little Endian conversion. */ /** * @brief Reads a 16-bit unsigned integer from memory in little-endian format. * * This function interprets the bytes at the given memory address as a * little-endian 16-bit integer, regardless of the host system's endianness. * It is marked as always inline for performance critical paths. * * @param[in] p Pointer to the memory location to read from. * @return The 16-bit unsigned integer value read from memory. */ static ZXC_ALWAYS_INLINE uint16_t zxc_le16(const void* p) { uint16_t v; ZXC_MEMCPY(&v, p, sizeof(v)); #ifdef ZXC_BIG_ENDIAN return ZXC_BSWAP16(v); #else return v; #endif } /** * @brief Reads a 32-bit unsigned integer from memory in little-endian format. * * This function interprets the bytes at the given pointer address as a * little-endian 32-bit integer, regardless of the host system's endianness. * It is marked as always inline for performance critical paths. * * @param[in] p Pointer to the memory location to read from. * @return The 32-bit unsigned integer value read from memory. */ static ZXC_ALWAYS_INLINE uint32_t zxc_le32(const void* p) { uint32_t v; ZXC_MEMCPY(&v, p, sizeof(v)); #ifdef ZXC_BIG_ENDIAN return ZXC_BSWAP32(v); #else return v; #endif } /** * @brief Reads a 64-bit unsigned integer from memory in little-endian format. * * This function interprets the bytes at the given memory address as a * little-endian 64-bit integer, regardless of the host system's endianness. * It is marked as always inline for performance critical paths. * * @param[in] p Pointer to the memory location to read from. * @return The 64-bit unsigned integer value read from memory. */ static ZXC_ALWAYS_INLINE uint64_t zxc_le64(const void* p) { uint64_t v; ZXC_MEMCPY(&v, p, sizeof(v)); #ifdef ZXC_BIG_ENDIAN return ZXC_BSWAP64(v); #else return v; #endif } /** * @brief Stores a 16-bit integer in memory using little-endian byte order. * * This function copies the value of a 16-bit unsigned integer to the specified * memory location. It uses memcpy to avoid strict aliasing violations and * potential unaligned access issues. * * @note This function assumes the system is little-endian or that the compiler * optimizes the memcpy to a store instruction that handles endianness if necessary * (though the implementation shown is a direct copy). * * @param[out] p Pointer to the destination memory where the value will be stored. * Must point to a valid memory region of at least 2 bytes. * @param[in] v The 16-bit unsigned integer value to store. */ static ZXC_ALWAYS_INLINE void zxc_store_le16(void* p, const uint16_t v) { #ifdef ZXC_BIG_ENDIAN const uint16_t s = ZXC_BSWAP16(v); ZXC_MEMCPY(p, &s, sizeof(s)); #else ZXC_MEMCPY(p, &v, sizeof(v)); #endif } /** * @brief Stores a 32-bit unsigned integer in little-endian format at the specified memory location. * * This function writes the 32-bit value `v` to the memory pointed to by `p`. * It uses `ZXC_MEMCPY` to ensure safe memory access, avoiding potential alignment issues * that could occur with direct pointer casting on some architectures. * * @note This function is marked as `ZXC_ALWAYS_INLINE` to minimize function call overhead. * * @param[out] p Pointer to the destination memory where the value will be stored. * @param[in] v The 32-bit unsigned integer value to store. */ static ZXC_ALWAYS_INLINE void zxc_store_le32(void* p, const uint32_t v) { #ifdef ZXC_BIG_ENDIAN const uint32_t s = ZXC_BSWAP32(v); ZXC_MEMCPY(p, &s, sizeof(s)); #else ZXC_MEMCPY(p, &v, sizeof(v)); #endif } /** * @brief Stores a 64-bit unsigned integer in little-endian format at the specified memory location. * * This function copies the 64-bit value `v` to the memory pointed to by `p`. * It uses `ZXC_MEMCPY` to ensure safe memory access, avoiding potential alignment issues * that might occur with direct pointer dereferencing on some architectures. * * @note This function assumes the system is little-endian or that the compiler optimizes * the memcpy to a store instruction that handles endianness correctly if `ZXC_MEMCPY` * is defined appropriately. * * @param[out] p Pointer to the destination memory where the value will be stored. * @param[in] v The 64-bit unsigned integer value to store. */ static ZXC_ALWAYS_INLINE void zxc_store_le64(void* p, const uint64_t v) { #ifdef ZXC_BIG_ENDIAN const uint64_t s = ZXC_BSWAP64(v); ZXC_MEMCPY(p, &s, sizeof(s)); #else ZXC_MEMCPY(p, &v, sizeof(v)); #endif } /** * @brief Computes the 1-byte checksum for block headers. * * Implementation based on Marsaglia's Xorshift (PRNG) principles. * * @param[in] p Pointer to the input data to be hashed (8 bytes) * @return uint8_t The computed hash value. */ static ZXC_ALWAYS_INLINE uint8_t zxc_hash8(const uint8_t* p) { const uint64_t v = zxc_le64(p); uint64_t h = v ^ ZXC_HASH_PRIME1; h ^= h << 13; h ^= h >> 7; h ^= h << 17; return (uint8_t)((h >> 32) ^ h); } /** * @brief Computes the 2-byte checksum for file headers. * * This function generates a hash value by reading data from the given pointer. * The result is a 16-bit hash. * Implementation based on Marsaglia's Xorshift (PRNG) principles. * * @param[in] p Pointer to the input data to be hashed (16 bytes) * @return uint16_t The computed hash value. */ static ZXC_ALWAYS_INLINE uint16_t zxc_hash16(const uint8_t* p) { const uint64_t v1 = zxc_le64(p); const uint64_t v2 = zxc_le64(p + 8); uint64_t h = v1 ^ v2 ^ ZXC_HASH_PRIME2; h ^= h << 13; h ^= h >> 7; h ^= h << 17; const uint32_t res = (uint32_t)((h >> 32) ^ h); return (uint16_t)((res >> 16) ^ res); } /** * @brief Copies 16 bytes from the source memory location to the destination memory location. * * This function is forced to be inlined and uses SIMD intrinsics when available. * SSE2 on x86/x64, NEON on ARM, or memcpy as fallback. * * @param[out] dst Pointer to the destination memory block. * @param[in] src Pointer to the source memory block. */ static ZXC_ALWAYS_INLINE void zxc_copy16(void* dst, const void* src) { #if defined(ZXC_USE_AVX2) || defined(ZXC_USE_AVX512) || defined(ZXC_USE_SSE2) // x86 SSE2/AVX2/AVX512: Single 128-bit unaligned load/store _mm_storeu_si128((__m128i*)dst, _mm_loadu_si128((const __m128i*)src)); #elif defined(ZXC_USE_NEON64) || defined(ZXC_USE_NEON32) vst1q_u8((uint8_t*)dst, vld1q_u8((const uint8_t*)src)); #else ZXC_MEMCPY(dst, src, 16); #endif } /** * @brief Copies 32 bytes from source to destination using SIMD when available. * * Uses AVX2 on x86, NEON on ARM64/ARM32, or two 16-byte copies as fallback. * * @param[out] dst Pointer to the destination memory block. * @param[in] src Pointer to the source memory block. */ static ZXC_ALWAYS_INLINE void zxc_copy32(void* dst, const void* src) { #if defined(ZXC_USE_AVX2) || defined(ZXC_USE_AVX512) // AVX2/AVX512: Single 256-bit (32 byte) unaligned load/store _mm256_storeu_si256((__m256i*)dst, _mm256_loadu_si256((const __m256i*)src)); #elif defined(ZXC_USE_SSE2) // SSE2: Two 128-bit (16 byte) unaligned load/stores (no 256-bit regs) _mm_storeu_si128((__m128i*)dst, _mm_loadu_si128((const __m128i*)src)); _mm_storeu_si128((__m128i*)((uint8_t*)dst + 16), _mm_loadu_si128((const __m128i*)((const uint8_t*)src + 16))); #elif defined(ZXC_USE_NEON64) || defined(ZXC_USE_NEON32) // NEON: Two 128-bit (16 byte) unaligned load/stores vst1q_u8((uint8_t*)dst, vld1q_u8((const uint8_t*)src)); vst1q_u8((uint8_t*)dst + 16, vld1q_u8((const uint8_t*)src + 16)); #else ZXC_MEMCPY(dst, src, 32); #endif } /** * @brief Counts trailing zeros in a 32-bit unsigned integer. * * This function returns the number of contiguous zero bits starting from the * least significant bit (LSB). If the input is 0, it returns 32. * * It utilizes compiler-specific built-ins for GCC/Clang (`__builtin_ctz`) and * MSVC (`_BitScanForward`) for optimal performance. If no supported compiler * is detected, it falls back to a portable De Bruijn sequence implementation. * * @param[in] x The 32-bit unsigned integer to scan. * @return The number of trailing zeros (0-32). */ static ZXC_ALWAYS_INLINE int zxc_ctz32(const uint32_t x) { if (x == 0) return 32; #if defined(__GNUC__) || defined(__clang__) return __builtin_ctz(x); #elif defined(_MSC_VER) unsigned long r; _BitScanForward(&r, x); return (int)r; #else // Fallback De Bruijn (32 bits) static const int DeBruijn32[32] = {0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9}; return DeBruijn32[((uint32_t)((x & (0U - x)) * 0x077CB531U)) >> 27]; #endif } /** * @brief Counts the number of trailing zeros in a 64-bit unsigned integer. * * This function determines the number of zero bits following the least significant * one bit in the binary representation of `x`. * * @param[in] x The 64-bit unsigned integer to scan. * @return The number of trailing zeros. Returns 64 if `x` is 0. * * @note This implementation uses compiler built-ins for GCC/Clang (`__builtin_ctzll`) * and MSVC (`_BitScanForward64`) when available for optimal performance. * It falls back to a De Bruijn sequence multiplication method for other compilers. */ static ZXC_ALWAYS_INLINE int zxc_ctz64(const uint64_t x) { if (x == 0) return 64; #if defined(__GNUC__) || defined(__clang__) return __builtin_ctzll(x); #elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64)) unsigned long r; _BitScanForward64(&r, x); return (int)r; #elif defined(_MSC_VER) // Use two 32-bit scans to avoid fragile 64-bit De Bruijn multiplication. unsigned long r; const uint32_t lo = (uint32_t)x; if (_BitScanForward(&r, lo)) return (int)r; _BitScanForward(&r, (uint32_t)(x >> 32)); return 32 + (int)r; #else // Fallback De Bruijn for non-GCC/non-MSVC compilers static const int Debruijn64[64] = { 0, 1, 48, 2, 57, 49, 28, 3, 61, 58, 50, 42, 38, 29, 17, 4, 62, 55, 59, 36, 53, 51, 43, 22, 45, 39, 33, 30, 24, 18, 12, 5, 63, 47, 56, 27, 60, 41, 37, 16, 54, 35, 52, 21, 44, 32, 23, 11, 46, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6}; return Debruijn64[((x & (0ULL - x)) * 0x03F79D71B4CA8B09ULL) >> 58]; #endif } /** * @brief Allocates aligned memory in a cross-platform manner. * * This function provides a unified interface for allocating memory with a specific * alignment requirement. It wraps `_aligned_malloc` for Windows * environments and `posix_memalign` for POSIX-compliant systems. * * @param[in] size The size of the memory block to allocate, in bytes. * @param[in] alignment The alignment value, which must be a power of two and a multiple * of `sizeof(void *)`. * @return A pointer to the allocated memory block, or NULL if the allocation fails. * The returned pointer must be freed using the corresponding aligned free function. */ void* zxc_aligned_malloc(const size_t size, const size_t alignment); /** * @brief Frees memory previously allocated with an aligned allocation function. * * This function provides a cross-platform wrapper for freeing aligned memory. * On Windows, it calls `_aligned_free`. * On other platforms, it falls back to the standard `free` function. * * @param[in] ptr A pointer to the memory block to be freed. If ptr is NULL, no operation is * performed. */ void zxc_aligned_free(void* ptr); /* * ============================================================================ * COMPRESSION CONTEXT & STRUCTS * ============================================================================ */ /* * INTERNAL API * ------------ */ /** * @brief Calculates a 32-bit hash for a given input buffer. * @param[in] input Pointer to the data buffer. * @param[in] len Length of the data in bytes. * @param[in] hash_method Checksum algorithm identifier (e.g., ZXC_CHECKSUM_RAPIDHASH). * @return The calculated 32-bit hash value. */ static ZXC_ALWAYS_INLINE uint32_t zxc_checksum(const void* RESTRICT input, const size_t len, const uint8_t hash_method) { (void)hash_method; /* single algorithm for now; extend when adding more */ const uint64_t hash = rapidhash(input, len); return (uint32_t)(hash ^ (hash >> (sizeof(uint32_t) * CHAR_BIT))); } /** * @brief Seeded variant of @ref zxc_checksum, for chaining a hash over * non-contiguous buffers: `zxc_checksum_seed(b, bn, zxc_checksum(a, an, m), m)` * hashes each byte once without a concat copy. * @param[in] input Pointer to the data buffer. * @param[in] len Length of the data in bytes. * @param[in] seed Previous 32-bit checksum to chain from. * @param[in] hash_method Checksum algorithm identifier (e.g., ZXC_CHECKSUM_RAPIDHASH). * @return The calculated 32-bit hash value. */ static ZXC_ALWAYS_INLINE uint32_t zxc_checksum_seed(const void* RESTRICT input, const size_t len, const uint32_t seed, const uint8_t hash_method) { (void)hash_method; /* single algorithm for now; extend when adding more */ const uint64_t hash = rapidhash_withSeed(input, len, seed); return (uint32_t)(hash ^ (hash >> (sizeof(uint32_t) * CHAR_BIT))); } /** * @brief Combines a running hash with a new block hash using rotate-left and XOR. * * This function updates a global checksum by rotating the current hash left by 1 bit * (with wraparound) and XORing with the new block hash. This provides a simple but * effective rolling hash that depends on the order of blocks. * * Formula: result = ((hash << 1) | (hash >> 31)) ^ block_hash * * @param[in] hash The current running hash value. * @param[in] block_hash The hash of the new block to combine. * @return The updated combined hash value. */ static ZXC_ALWAYS_INLINE uint32_t zxc_hash_combine_rotate(const uint32_t hash, const uint32_t block_hash) { return ((hash << 1) | (hash >> 31)) ^ block_hash; } /** * @brief Writes a generic header and section descriptors to a destination * buffer. * * Serializes the `zxc_gnr_header_t` and an array of 4 section descriptors. * * @param[out] dst Pointer to the destination buffer. * @param[in] rem The remaining space in the destination buffer. * @param[in] gh Pointer to the generic header structure to write. * @param[in] desc Array of 4 section descriptors to write. * @return int The number of bytes written, or a negative error code if the buffer * is too small. */ int zxc_write_glo_header_and_desc(uint8_t* RESTRICT dst, const size_t rem, const zxc_gnr_header_t* RESTRICT gh, const zxc_section_desc_t desc[ZXC_GLO_SECTIONS]); /** * @brief Reads a generic header and section descriptors from a source buffer. * * Deserializes data into a `zxc_gnr_header_t` and an array of 4 section * descriptors. * * @param[in] src Pointer to the source buffer. * @param[in] len The length of the source buffer available for reading. * @param[out] gh Pointer to the generic header structure to populate. * @param[out] desc Array of 4 section descriptors to populate. * * @return int Returns ZXC_OK on success, or a negative zxc_error_t code on failure. */ int zxc_read_glo_header_and_desc(const uint8_t* RESTRICT src, const size_t len, zxc_gnr_header_t* RESTRICT gh, zxc_section_desc_t desc[ZXC_GLO_SECTIONS]); /** * @brief Writes a record header and description to the destination buffer. * * @param dst Pointer to the destination buffer where the header and description will be written. * @param rem Remaining size available in the destination buffer. * @param gh Pointer to the GNR header structure containing header information. * @param desc Array of 3 section descriptors to be written along with the header. * * @return int Returns the number of bytes written on success, or a negative error code on failure. */ int zxc_write_ghi_header_and_desc(uint8_t* RESTRICT dst, const size_t rem, const zxc_gnr_header_t* RESTRICT gh, const zxc_section_desc_t desc[ZXC_GHI_SECTIONS]); /** * @brief Reads a record header and section descriptors from a buffer. * * This function parses the source buffer to extract a general header and * up to three section descriptors from a ZXC record. * * @param[in] src Pointer to the source buffer containing the record data. * @param[in] len Length of the source buffer in bytes. * @param[out] gh Pointer to a zxc_gnr_header_t structure to store the parsed header. * @param[out] desc Array of 3 zxc_section_desc_t structures to store the parsed section * descriptors. * * @return int Returns ZXC_OK on success, or a negative zxc_error_t code on failure. */ int zxc_read_ghi_header_and_desc(const uint8_t* RESTRICT src, const size_t len, zxc_gnr_header_t* RESTRICT gh, zxc_section_desc_t desc[ZXC_GHI_SECTIONS]); /* ============================================================================ * Huffman codec for the GLO literal stream (level >= 6). * * On-disk layout, decoder geometry and tunables: see * @ref ZXC_HUF_MAX_CODE_LEN_ULTRA and the surrounding "Huffman Codec Constants" * group above. * ============================================================================ */ /** * @brief Build length-limited canonical Huffman code lengths from a frequency table. * * Uses the boundary package-merge algorithm capped at `ZXC_HUF_MAX_CODE_LEN_ULTRA`. * Symbols with `freq[i] == 0` get `code_len[i] == 0`; others receive a value * in `[1, ZXC_HUF_MAX_CODE_LEN_ULTRA]`. * * @param[in] freq Frequency table of length `ZXC_HUF_NUM_SYMBOLS`. * @param[out] code_len Output code-length array of length `ZXC_HUF_NUM_SYMBOLS`. * @param[in] scratch Optional caller-owned scratch buffer of at least * ::ZXC_HUF_BUILD_SCRATCH_SIZE bytes. If `NULL`, the * function allocates its own working memory and frees * it before returning. * @param[in] max_code_len Code-length ceiling in `[1, ZXC_HUF_MAX_CODE_LEN_ULTRA]`; * package-merge is run for this many levels (see * ::zxc_huf_enc_max_code_len for the per-level value). * @return `ZXC_OK` on success, negative `zxc_error_t` code on failure. */ int zxc_huf_build_code_lengths(const uint32_t* RESTRICT freq, uint8_t* RESTRICT code_len, void* RESTRICT scratch, int max_code_len); /** * @brief Pack per-symbol code lengths into the 128-byte (4-bit nibble) header. */ void zxc_huf_pack_lengths(const uint8_t* RESTRICT code_len, uint8_t* RESTRICT out); /** * @brief Unpack and structurally validate a 128-byte packed lengths header. * @return `ZXC_OK` on success, `ZXC_ERROR_CORRUPT_DATA` on invalid lengths. */ int zxc_huf_unpack_lengths(const uint8_t* RESTRICT in, uint8_t* RESTRICT code_len); /* -------------------------------------------------------------------------- * PivCo-Huffman section codec (enc 2/3) * * Same code bits as canonical Huffman for the same lengths, reordered by tree * LEVEL: the payload is, for each internal node in BFS order, that node's * branch bits (one bit per symbol routed through it, LSB-first, each node * byte-aligned). No size fields anywhere: the decoder derives every node's * run length from the root count and popcounts. Decoding is bottom-up level * merges (shuffle-parallel, no gather). * -------------------------------------------------------------------------- */ /** @brief Extra scratch slack required past `n` by the PivCo decoder. */ #define ZXC_PIVCO_SCRATCH_PAD 32 /** @brief Exact encoded size (bytes) of a PivCo section for this histogram and * code lengths; includes the 128-byte lengths header when @p with_header. */ size_t zxc_huf_calc_size(const uint32_t* RESTRICT freq, const uint8_t* RESTRICT code_len, int with_header); /** @brief Encode a PivCo literal section (128-byte lengths header + payload). */ int zxc_huf_encode_section(const uint8_t* RESTRICT literals, size_t n_literals, const uint32_t* RESTRICT freq, const uint8_t* RESTRICT code_len, uint8_t* RESTRICT dst, size_t dst_cap); /** @brief Unpack a dict table's 128-byte packed lengths and prebuild its PivCo * tree, canonical codes, code lengths and decoder tables (tree-at-attach). * All outputs are frame-constant; per-block encode/estimate/decode then skip * the rebuild. */ int zxc_huf_dict_tree_build(const uint8_t* RESTRICT packed_lengths, zxc_pivco_tree_t* RESTRICT tree, uint32_t* RESTRICT codes, uint8_t* RESTRICT code_len, zxc_pivco_decode_aux_t* RESTRICT aux); /** @brief zxc_huf_calc_size for a dict section: prebuilt @p tree, no header. */ size_t zxc_huf_calc_size_dict(const uint32_t* RESTRICT freq, const uint8_t* RESTRICT code_len, const zxc_pivco_tree_t* RESTRICT tree); /** @brief Encode a PivCo section against a prebuilt dict tree/codes (no header). */ int zxc_huf_encode_section_dict(const uint8_t* RESTRICT literals, size_t n_literals, const uint32_t* RESTRICT freq, const uint8_t* RESTRICT code_len, const zxc_pivco_tree_t* RESTRICT tree, const uint32_t* RESTRICT codes, uint8_t* RESTRICT dst, size_t dst_cap); /** @brief Decode a PivCo literal section into @p dst (exactly @p n bytes). * @p dst needs ZXC_PAD_SIZE slack, @p scratch at least n + ZXC_PIVCO_SCRATCH_PAD. */ int zxc_huf_decode_section(const uint8_t* RESTRICT payload, size_t payload_size, uint8_t* RESTRICT dst, size_t n, uint8_t* RESTRICT scratch); /** @brief Decode a PivCo dict section against a prebuilt dict @p tree and its * attach-time decoder tables @p aux. */ int zxc_huf_decode_section_dict(const uint8_t* RESTRICT payload, size_t payload_size, uint8_t* RESTRICT dst, size_t n, const zxc_pivco_tree_t* RESTRICT tree, const zxc_pivco_decode_aux_t* RESTRICT aux, uint8_t* RESTRICT scratch); /* --------------------------------------------------------------------------- * Compression / decompression context. * * The context owns the working buffers (hash table, sequence buffers, scratch * memory) that the encoder and decoder reuse across blocks. It used to be * exposed via zxc_sans_io.h, but no consumer outside of the library itself * needs to drive it directly - the public buffer / streaming / seekable APIs * already provide opaque wrappers (`zxc_create_cctx` / `zxc_create_dctx`). * Keeping the layout private lets us evolve the buffer layout (cache-line * placement, additional scratch arenas) without breaking the ABI. * --------------------------------------------------------------------------- */ /** * @struct zxc_cctx_t * @brief Compression / decompression context. * * Holds the buffers reused across blocks to avoid repeated allocations. * * **Key fields:** * - @c hash_table: epoch-tagged positions (`ZXC_LZ_HASH_SIZE` * 4 bytes). * - @c hash_tags: 8-bit tags for fast match rejection * (`ZXC_LZ_HASH_SIZE` * 1 byte). * - @c chain_table: collision chain storing the *previous* occurrence of a * hash, forming a linked list per bucket and enabling history traversal. * - @c epoch: drives "lazy hash table invalidation". Instead of memset-ing * the hash table for every block, we store `(epoch << offset_bits) | offset`; an * entry whose stored epoch differs from `ctx->epoch` is treated as empty. */ typedef struct { /* Hot zone: random access / high frequency. * Kept at the start to ensure they reside in the first cache line (64 bytes). */ uint32_t* hash_table; /**< Hash table for LZ77 match positions (epoch|pos). */ uint8_t* hash_tags; /**< Split tag table for fast match rejection (8-bit tags). */ uint16_t* chain_table; /**< Chain table for collision resolution. */ void* memory_block; /**< Single allocation block owner. */ uint32_t epoch; /**< Current epoch for lazy hash table invalidation. */ /* Warm zone: sequential access per sequence. */ uint32_t* buf_sequences; /**< Buffer for sequence records (packed: LL(8)|ML(8)|Offset(16)). */ uint8_t* buf_tokens; /**< Buffer for token sequences. */ uint16_t* buf_offsets; /**< Buffer for offsets. */ uint8_t* buf_extras; /**< Buffer for extra lengths (vbytes for LL/ML). */ uint8_t* literals; /**< Buffer for literal bytes. */ /* Cold zone: configuration / scratch / resizeable. */ uint8_t* lit_buffer; /**< Scratch buffer for literals (RLE / Huffman). */ size_t lit_buffer_cap; /**< Current capacity of the scratch buffer. */ uint8_t* work_buf; /**< Padded scratch buffer for buffer-API decompression. */ size_t work_buf_cap; /**< Capacity of the work buffer. */ uint8_t* tok_buffer; /**< Decode scratch for a Huffman-coded GLO token section (enc_litlen == HUFFMAN); NULL on compress. Heap decode contexts defer it (with pivco_scratch) to the first entropy section, see entropy_block. */ size_t tok_buffer_cap; /**< Capacity of tok_buffer in bytes. */ uint8_t* pivco_scratch; /**< Level ping-pong scratch for PivCo decode. */ size_t pivco_scratch_cap; /**< Capacity of pivco_scratch in bytes. */ void* entropy_block; /**< Lazy allocation backing tok_buffer + pivco_scratch (heap decode contexts, first entropy block only). NULL on compress contexts and static workspaces. Freed by zxc_cctx_free. */ uint8_t* opt_scratch; /**< Optimal-parser DP scratch (level >= 6 only, lazy-allocated, packs dp/parent_len/parent_off/actions). Also reused as transient scratch for the length-limited Huffman code-length builder. */ size_t opt_scratch_cap; /**< Current capacity of opt_scratch in bytes. */ int checksum_enabled; /**< 1 if checksum calculation/verification is enabled. */ int compression_level; /**< Compression level. */ size_t dict_size; /**< Dictionary prefill size (0 = no dictionary). */ uint8_t* dict_buffer; /**< [dict | data] concat scratch carved from memory_block when dict_size > 0 (NULL otherwise). */ size_t dict_buffer_cap; /**< Capacity of dict_buffer in bytes (0 = none). */ zxc_dict_huf_state_t* dict_huf; /**< Tree-at-attach state (PivCo tree + codes + code lengths), carved from the workspace only when dict_size > 0 (NULL otherwise); built once by zxc_cctx_attach_dict_huf. Valid iff dict_huf_tree_ok. */ int dict_huf_tree_ok; /**< 1 when *dict_huf is built and valid. */ uint32_t* lit_freq_acc; /**< Trainer hook: when non-NULL, the GLO encoder accumulates post-LZ literal byte frequencies here (256 entries). NULL outside dictionary training. */ /* Block-size derived parameters (computed once at init). */ size_t chunk_size; /**< Effective block size in bytes. */ uint32_t offset_bits; /**< log2(chunk_size) - governs epoch_mark shift. */ uint32_t offset_mask; /**< (1U << offset_bits) - 1 */ uint32_t max_epoch; /**< 1U << (32 - offset_bits) */ } zxc_cctx_t; /** * @brief Initialises a ZXC compression / decompression context in place. * * Allocates the internal buffers (hash table, sequence buffers, scratch) sized * for @p chunk_size and the requested @p mode. * * @param[out] ctx Context to initialise. * @param[in] chunk_size Block size driving buffer sizing. * @param[in] mode 1 for compression, 0 for decompression. * @param[in] level Compression level (ignored when @p mode == 0). * @param[in] checksum_enabled Non-zero to enable checksum computation. * @param[in] dict_size Dictionary prefill size; when > 0 an extra * [dict | data] concat buffer is carved into the * workspace and @c ctx->dict_buffer is set. * * @return @c ZXC_OK on success, or a negative @ref zxc_error_t code (notably * @c ZXC_ERROR_MEMORY on allocation failure). */ int zxc_cctx_init(zxc_cctx_t* ctx, const size_t chunk_size, const int mode, const int level, const int checksum_enabled, const size_t dict_size); /** * @brief Attach the shared dictionary literal table to an initialised context. * * Validates the 128-byte packed code-lengths header and builds the PivCo tree, * canonical codes and decoder tables ONCE into the context (tree-at-attach); * per-block encode/estimate/decode reuse them. @p lengths need only be valid * during this call (everything is copied into the context workspace). A NULL * @p lengths is a no-op. * * @return @ref ZXC_OK on success, @ref ZXC_ERROR_CORRUPT_DATA if the lengths * header is structurally invalid (bad nibble, Kraft inequality). */ int zxc_cctx_attach_dict_huf(zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT lengths); /** * @brief Returns the byte count that @ref zxc_cctx_init would allocate for * the given parameters. * * Used by the static-cctx public API to size a caller-supplied workspace * before calling @ref zxc_cctx_init_in_workspace. * * @param[in] chunk_size Block size in bytes (must satisfy * @ref zxc_validate_block_size). * @param[in] mode 1 = compression, 0 = decompression. * @param[in] level Compression level (only consulted when @p mode == 1). * @param[in] dict_size Dictionary prefill size; when > 0 the figure includes * the [dict | data] concat buffer. * @return Size in bytes, or 0 if the parameters are invalid. */ size_t zxc_cctx_compute_workspace_size(const size_t chunk_size, const int mode, const int level, const size_t dict_size); /** * @brief Initialises a compression / decompression context inside a * caller-supplied workspace. * * Identical to @ref zxc_cctx_init except that the persistent buffer is * carved out of @p workspace instead of being @c ZXC_ALIGNED_MALLOC'd * internally. @p workspace must be cache-line aligned and at least as * large as @ref zxc_cctx_compute_workspace_size for the same parameters. * * The caller owns @p workspace and must keep it alive for the lifetime of * @p ctx. @ref zxc_cctx_free becomes a no-op for contexts initialised * this way (the workspace is not freed by the library). * * @param[out] ctx Context to initialise (zeroed on entry). * @param[in] workspace Caller-allocated, cache-line-aligned buffer. * @param[in] workspace_size Capacity of @p workspace in bytes. * @param[in] chunk_size Block size in bytes. * @param[in] mode 1 = compression, 0 = decompression. * @param[in] level Compression level (ignored when @p mode == 0). * @param[in] checksum_enabled Non-zero to enable checksum computation. * @param[in] dict_size Dictionary prefill size; when > 0 the workspace * must include the [dict | data] concat buffer and * @c ctx->dict_buffer is set into it. * @param[in] defer_entropy_scratch Non-zero (heap decode contexts only) to * leave the tok/PivCo decode scratch out of the * partition; it is then lazily allocated by * @ref zxc_cctx_alloc_entropy_scratch on the * first entropy section. Static workspaces must * pass 0 (no-allocation contract). * @return @c ZXC_OK on success, @c ZXC_ERROR_DST_TOO_SMALL if the workspace * is too small, or another negative @ref zxc_error_t. */ int zxc_cctx_init_in_workspace(zxc_cctx_t* RESTRICT ctx, void* RESTRICT workspace, const size_t workspace_size, const size_t chunk_size, const int mode, const int level, const int checksum_enabled, const size_t dict_size, const int defer_entropy_scratch); /** * @brief Lazily allocates the decode-side entropy scratch (tok_buffer + * pivco_scratch) for a heap context initialised with deferral. * * No-op when the scratch is already present (static workspaces pre-carve it; * subsequent entropy blocks reuse the first allocation). The block is owned * by the context (@c entropy_block) and released by @ref zxc_cctx_free. * * @return @ref ZXC_OK, or @ref ZXC_ERROR_MEMORY on allocation failure. */ int zxc_cctx_alloc_entropy_scratch(zxc_cctx_t* ctx); /** * @brief Releases the internal buffers owned by a context. * * Does NOT free @p ctx itself - the caller owns the struct storage. The * context may safely be re-initialised with zxc_cctx_init() afterwards. * * @param[in,out] ctx Context whose buffers should be released. */ void zxc_cctx_free(zxc_cctx_t* ctx); /** * @brief Internal wrapper function to decompress a single chunk of data. * * This function handles the decompression of a specific chunk from the source * buffer into the destination buffer using the provided compression context. It * serves as an abstraction layer over the core decompression logic. * * @param[in,out] ctx Pointer to the ZXC compression context structure containing * internal state and configuration. * @param[in] src Pointer to the source buffer containing compressed data. * @param[in] src_sz Size of the compressed data in the source buffer (in bytes). * @param[out] dst Pointer to the destination buffer where decompressed data will * be written. * @param[in] dst_cap Capacity of the destination buffer (maximum bytes that can be * written). * * @return int Returns ZXC_OK on success, or a negative zxc_error_t code on failure. * Specific error codes depend on the underlying ZXC * implementation. */ int zxc_decompress_chunk_wrapper(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap); int zxc_decompress_chunk_wrapper_dict(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap); /** * @brief Wraps the internal chunk compression logic. * * This function acts as a wrapper to compress a single chunk of data using the * provided compression context. It handles the interaction with the underlying * compression algorithm for a specific block of memory. * * @param[in,out] ctx Pointer to the ZXC compression context containing configuration * and state. * @param[in] chunk Pointer to the source buffer containing the raw data to * compress. * @param[in] src_sz The size of the source chunk in bytes. * @param[out] dst Pointer to the destination buffer where compressed data will be * written. * @param[in] dst_cap The capacity of the destination buffer (maximum bytes to write). * * @return int The number of bytes written to the destination buffer on success, * or a negative error code on failure. */ int zxc_compress_chunk_wrapper(zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT chunk, const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap); /* --------------------------------------------------------------------------- * Internal frame primitives. * * Read/write the ZXC file header, block header, and file footer. These were * previously exposed via zxc_sans_io.h but no in-tree consumer outside of the * library implementation needs them, and exposing them freezes on-disk layout * details (block_flags layout, footer composition) that we want to keep free * to evolve until the format is declared stable. * --------------------------------------------------------------------------- */ /** * @brief On-disk header structure for a ZXC block (8 bytes, little-endian). * * @c raw_size is not stored in the header; decoders derive it from Section * Descriptors within the compressed payload. */ typedef struct { uint8_t block_type; /**< Block type (see @ref zxc_block_type_t). */ uint8_t block_flags; /**< Flags (e.g., checksum presence). */ uint8_t reserved; /**< Reserved for future protocol extensions. */ uint8_t header_crc; /**< Header integrity checksum (1 byte). */ uint32_t comp_size; /**< Compressed size excluding this header. */ } zxc_block_header_t; /** * @brief Writes the standard ZXC file header into @p dst. * * Stores the magic word (little-endian) and the version number into the * provided buffer, after checking that it has sufficient capacity. * * @param[out] dst Destination buffer. * @param[in] dst_capacity Total capacity of @p dst in bytes. * @param[in] chunk_size Block size to encode in the header. * @param[in] has_checksum Non-zero if the checksum bit must be set. * @param[in] dict_id Dictionary ID (0 = no dictionary). * * @return Number of bytes written (@c ZXC_FILE_HEADER_SIZE) on success, * or @c ZXC_ERROR_DST_TOO_SMALL if @p dst_capacity is insufficient. */ int zxc_write_file_header(uint8_t* RESTRICT dst, const size_t dst_capacity, const size_t chunk_size, const int has_checksum, const uint32_t dict_id); /** * @brief Validates and reads the ZXC file header from @p src. * * Checks that the source buffer is large enough to contain a ZXC file header * and that the magic word and version number match the expected format. * * @param[in] src Pointer to the source buffer. * @param[in] src_size Size of the source buffer in bytes. * @param[out] out_block_size Optional pointer that receives the recommended * block size. May be @c NULL. * @param[out] out_has_checksum Optional pointer that receives the checksum * flag. May be @c NULL. * @param[out] out_dict_id Optional pointer that receives the dictionary * ID (0 if none). May be @c NULL. * * @return @c ZXC_OK on success, or a negative error code (e.g. * @c ZXC_ERROR_SRC_TOO_SMALL, @c ZXC_ERROR_BAD_MAGIC, * @c ZXC_ERROR_BAD_VERSION). */ int zxc_read_file_header(const uint8_t* RESTRICT src, const size_t src_size, size_t* out_block_size, int* out_has_checksum, uint32_t* out_dict_id); /** * @brief Encodes a block header into @p dst. * * Serialises the contents of a @ref zxc_block_header_t structure into a byte * array in little-endian format, after checking that @p dst has sufficient * capacity. * * @param[out] dst Destination buffer. * @param[in] dst_capacity Total capacity of @p dst in bytes. * @param[in] bh Source block header structure to serialise. * * @return Number of bytes written (@c ZXC_BLOCK_HEADER_SIZE) on success, * or @c ZXC_ERROR_DST_TOO_SMALL if @p dst_capacity is insufficient. */ int zxc_write_block_header(uint8_t* RESTRICT dst, const size_t dst_capacity, const zxc_block_header_t* bh); /** * @brief Reads and parses a ZXC block header from @p src. * * Extracts the block type, flags, reserved fields, and compressed size from * the first @c ZXC_BLOCK_HEADER_SIZE bytes of @p src. Multi-byte fields are * decoded as little-endian. * * @param[in] src Source buffer holding the encoded block header. * @param[in] src_size Size of @p src in bytes. * @param[out] bh Block header structure populated with the parsed data. * * @return @c ZXC_OK on success, or @c ZXC_ERROR_SRC_TOO_SMALL if @p src is * smaller than @c ZXC_BLOCK_HEADER_SIZE. */ int zxc_read_block_header(const uint8_t* RESTRICT src, const size_t src_size, zxc_block_header_t* bh); /** * @brief Writes the ZXC file footer into @p dst. * * The footer stores the original uncompressed size and an optional global * checksum. It is always @c ZXC_FILE_FOOTER_SIZE (12) bytes long. * * @param[out] dst Destination buffer. * @param[in] dst_capacity Total capacity of @p dst in bytes. * @param[in] src_size Original uncompressed size of the data. * @param[in] global_hash Global checksum hash (used only when * @p checksum_enabled is non-zero). * @param[in] checksum_enabled Non-zero if the checksum should be emitted. * * @return Number of bytes written (@c ZXC_FILE_FOOTER_SIZE) on success, * or @c ZXC_ERROR_DST_TOO_SMALL on failure. */ int zxc_write_file_footer(uint8_t* RESTRICT dst, const size_t dst_capacity, const uint64_t src_size, const uint32_t global_hash, const int checksum_enabled); /* --------------------------------------------------------------------------- * Seekable cross-TU hooks (defined in zxc_seekable.c, consumed by the * FILE*-flavored open helper in zxc_driver.c). * ------------------------------------------------------------------------- */ /** * @brief Hands ownership of a heap-allocated reader context to a seekable * handle. The context will be released via @c ZXC_FREE when * @ref zxc_seekable_free is called on @p s. * * Safe to call exactly once per handle. Intended for thin wrappers that * build a @ref zxc_reader_t over their own allocated state * (@ref zxc_seekable_open_file) and need that state to outlive the call * site. * * @param[in,out] s Seekable handle returned by @ref zxc_seekable_open_reader. * @param[in] ctx Pointer previously returned by @c ZXC_MALLOC / @c ZXC_CALLOC. */ void zxc_seekable_attach_owned_ctx(zxc_seekable* s, void* ctx); /** @} */ /* end of internal */ #ifdef __cplusplus } #endif #endif // ZXC_INTERNAL_Hzxc-0.13.0/src/lib/zxc_pivco_tables.c000066400000000000000000002635561522536370400174660ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file zxc_pivco_tables.c * @brief Shuffle-index tables for the PivCo merge kernels (single definition). * * Generated data - see zxc_pivco_tables.h for the layout contract. Compiled * once, outside the per-architecture variants. */ #include "zxc_pivco_tables.h" const uint8_t zxc_pivco_idxa_u8[256][8] = { {0, 1, 2, 3, 4, 5, 6, 7}, {16, 0, 1, 2, 3, 4, 5, 6}, {0, 16, 1, 2, 3, 4, 5, 6}, {16, 17, 0, 1, 2, 3, 4, 5}, {0, 1, 16, 2, 3, 4, 5, 6}, {16, 0, 17, 1, 2, 3, 4, 5}, {0, 16, 17, 1, 2, 3, 4, 5}, {16, 17, 18, 0, 1, 2, 3, 4}, {0, 1, 2, 16, 3, 4, 5, 6}, {16, 0, 1, 17, 2, 3, 4, 5}, {0, 16, 1, 17, 2, 3, 4, 5}, {16, 17, 0, 18, 1, 2, 3, 4}, {0, 1, 16, 17, 2, 3, 4, 5}, {16, 0, 17, 18, 1, 2, 3, 4}, {0, 16, 17, 18, 1, 2, 3, 4}, {16, 17, 18, 19, 0, 1, 2, 3}, {0, 1, 2, 3, 16, 4, 5, 6}, {16, 0, 1, 2, 17, 3, 4, 5}, {0, 16, 1, 2, 17, 3, 4, 5}, {16, 17, 0, 1, 18, 2, 3, 4}, {0, 1, 16, 2, 17, 3, 4, 5}, {16, 0, 17, 1, 18, 2, 3, 4}, {0, 16, 17, 1, 18, 2, 3, 4}, {16, 17, 18, 0, 19, 1, 2, 3}, {0, 1, 2, 16, 17, 3, 4, 5}, {16, 0, 1, 17, 18, 2, 3, 4}, {0, 16, 1, 17, 18, 2, 3, 4}, {16, 17, 0, 18, 19, 1, 2, 3}, {0, 1, 16, 17, 18, 2, 3, 4}, {16, 0, 17, 18, 19, 1, 2, 3}, {0, 16, 17, 18, 19, 1, 2, 3}, {16, 17, 18, 19, 20, 0, 1, 2}, {0, 1, 2, 3, 4, 16, 5, 6}, {16, 0, 1, 2, 3, 17, 4, 5}, {0, 16, 1, 2, 3, 17, 4, 5}, {16, 17, 0, 1, 2, 18, 3, 4}, {0, 1, 16, 2, 3, 17, 4, 5}, {16, 0, 17, 1, 2, 18, 3, 4}, {0, 16, 17, 1, 2, 18, 3, 4}, {16, 17, 18, 0, 1, 19, 2, 3}, {0, 1, 2, 16, 3, 17, 4, 5}, {16, 0, 1, 17, 2, 18, 3, 4}, {0, 16, 1, 17, 2, 18, 3, 4}, {16, 17, 0, 18, 1, 19, 2, 3}, {0, 1, 16, 17, 2, 18, 3, 4}, {16, 0, 17, 18, 1, 19, 2, 3}, {0, 16, 17, 18, 1, 19, 2, 3}, {16, 17, 18, 19, 0, 20, 1, 2}, {0, 1, 2, 3, 16, 17, 4, 5}, {16, 0, 1, 2, 17, 18, 3, 4}, {0, 16, 1, 2, 17, 18, 3, 4}, {16, 17, 0, 1, 18, 19, 2, 3}, {0, 1, 16, 2, 17, 18, 3, 4}, {16, 0, 17, 1, 18, 19, 2, 3}, {0, 16, 17, 1, 18, 19, 2, 3}, {16, 17, 18, 0, 19, 20, 1, 2}, {0, 1, 2, 16, 17, 18, 3, 4}, {16, 0, 1, 17, 18, 19, 2, 3}, {0, 16, 1, 17, 18, 19, 2, 3}, {16, 17, 0, 18, 19, 20, 1, 2}, {0, 1, 16, 17, 18, 19, 2, 3}, {16, 0, 17, 18, 19, 20, 1, 2}, {0, 16, 17, 18, 19, 20, 1, 2}, {16, 17, 18, 19, 20, 21, 0, 1}, {0, 1, 2, 3, 4, 5, 16, 6}, {16, 0, 1, 2, 3, 4, 17, 5}, {0, 16, 1, 2, 3, 4, 17, 5}, {16, 17, 0, 1, 2, 3, 18, 4}, {0, 1, 16, 2, 3, 4, 17, 5}, {16, 0, 17, 1, 2, 3, 18, 4}, {0, 16, 17, 1, 2, 3, 18, 4}, {16, 17, 18, 0, 1, 2, 19, 3}, {0, 1, 2, 16, 3, 4, 17, 5}, {16, 0, 1, 17, 2, 3, 18, 4}, {0, 16, 1, 17, 2, 3, 18, 4}, {16, 17, 0, 18, 1, 2, 19, 3}, {0, 1, 16, 17, 2, 3, 18, 4}, {16, 0, 17, 18, 1, 2, 19, 3}, {0, 16, 17, 18, 1, 2, 19, 3}, {16, 17, 18, 19, 0, 1, 20, 2}, {0, 1, 2, 3, 16, 4, 17, 5}, {16, 0, 1, 2, 17, 3, 18, 4}, {0, 16, 1, 2, 17, 3, 18, 4}, {16, 17, 0, 1, 18, 2, 19, 3}, {0, 1, 16, 2, 17, 3, 18, 4}, {16, 0, 17, 1, 18, 2, 19, 3}, {0, 16, 17, 1, 18, 2, 19, 3}, {16, 17, 18, 0, 19, 1, 20, 2}, {0, 1, 2, 16, 17, 3, 18, 4}, {16, 0, 1, 17, 18, 2, 19, 3}, {0, 16, 1, 17, 18, 2, 19, 3}, {16, 17, 0, 18, 19, 1, 20, 2}, {0, 1, 16, 17, 18, 2, 19, 3}, {16, 0, 17, 18, 19, 1, 20, 2}, {0, 16, 17, 18, 19, 1, 20, 2}, {16, 17, 18, 19, 20, 0, 21, 1}, {0, 1, 2, 3, 4, 16, 17, 5}, {16, 0, 1, 2, 3, 17, 18, 4}, {0, 16, 1, 2, 3, 17, 18, 4}, {16, 17, 0, 1, 2, 18, 19, 3}, {0, 1, 16, 2, 3, 17, 18, 4}, {16, 0, 17, 1, 2, 18, 19, 3}, {0, 16, 17, 1, 2, 18, 19, 3}, {16, 17, 18, 0, 1, 19, 20, 2}, {0, 1, 2, 16, 3, 17, 18, 4}, {16, 0, 1, 17, 2, 18, 19, 3}, {0, 16, 1, 17, 2, 18, 19, 3}, {16, 17, 0, 18, 1, 19, 20, 2}, {0, 1, 16, 17, 2, 18, 19, 3}, {16, 0, 17, 18, 1, 19, 20, 2}, {0, 16, 17, 18, 1, 19, 20, 2}, {16, 17, 18, 19, 0, 20, 21, 1}, {0, 1, 2, 3, 16, 17, 18, 4}, {16, 0, 1, 2, 17, 18, 19, 3}, {0, 16, 1, 2, 17, 18, 19, 3}, {16, 17, 0, 1, 18, 19, 20, 2}, {0, 1, 16, 2, 17, 18, 19, 3}, {16, 0, 17, 1, 18, 19, 20, 2}, {0, 16, 17, 1, 18, 19, 20, 2}, {16, 17, 18, 0, 19, 20, 21, 1}, {0, 1, 2, 16, 17, 18, 19, 3}, {16, 0, 1, 17, 18, 19, 20, 2}, {0, 16, 1, 17, 18, 19, 20, 2}, {16, 17, 0, 18, 19, 20, 21, 1}, {0, 1, 16, 17, 18, 19, 20, 2}, {16, 0, 17, 18, 19, 20, 21, 1}, {0, 16, 17, 18, 19, 20, 21, 1}, {16, 17, 18, 19, 20, 21, 22, 0}, {0, 1, 2, 3, 4, 5, 6, 16}, {16, 0, 1, 2, 3, 4, 5, 17}, {0, 16, 1, 2, 3, 4, 5, 17}, {16, 17, 0, 1, 2, 3, 4, 18}, {0, 1, 16, 2, 3, 4, 5, 17}, {16, 0, 17, 1, 2, 3, 4, 18}, {0, 16, 17, 1, 2, 3, 4, 18}, {16, 17, 18, 0, 1, 2, 3, 19}, {0, 1, 2, 16, 3, 4, 5, 17}, {16, 0, 1, 17, 2, 3, 4, 18}, {0, 16, 1, 17, 2, 3, 4, 18}, {16, 17, 0, 18, 1, 2, 3, 19}, {0, 1, 16, 17, 2, 3, 4, 18}, {16, 0, 17, 18, 1, 2, 3, 19}, {0, 16, 17, 18, 1, 2, 3, 19}, {16, 17, 18, 19, 0, 1, 2, 20}, {0, 1, 2, 3, 16, 4, 5, 17}, {16, 0, 1, 2, 17, 3, 4, 18}, {0, 16, 1, 2, 17, 3, 4, 18}, {16, 17, 0, 1, 18, 2, 3, 19}, {0, 1, 16, 2, 17, 3, 4, 18}, {16, 0, 17, 1, 18, 2, 3, 19}, {0, 16, 17, 1, 18, 2, 3, 19}, {16, 17, 18, 0, 19, 1, 2, 20}, {0, 1, 2, 16, 17, 3, 4, 18}, {16, 0, 1, 17, 18, 2, 3, 19}, {0, 16, 1, 17, 18, 2, 3, 19}, {16, 17, 0, 18, 19, 1, 2, 20}, {0, 1, 16, 17, 18, 2, 3, 19}, {16, 0, 17, 18, 19, 1, 2, 20}, {0, 16, 17, 18, 19, 1, 2, 20}, {16, 17, 18, 19, 20, 0, 1, 21}, {0, 1, 2, 3, 4, 16, 5, 17}, {16, 0, 1, 2, 3, 17, 4, 18}, {0, 16, 1, 2, 3, 17, 4, 18}, {16, 17, 0, 1, 2, 18, 3, 19}, {0, 1, 16, 2, 3, 17, 4, 18}, {16, 0, 17, 1, 2, 18, 3, 19}, {0, 16, 17, 1, 2, 18, 3, 19}, {16, 17, 18, 0, 1, 19, 2, 20}, {0, 1, 2, 16, 3, 17, 4, 18}, {16, 0, 1, 17, 2, 18, 3, 19}, {0, 16, 1, 17, 2, 18, 3, 19}, {16, 17, 0, 18, 1, 19, 2, 20}, {0, 1, 16, 17, 2, 18, 3, 19}, {16, 0, 17, 18, 1, 19, 2, 20}, {0, 16, 17, 18, 1, 19, 2, 20}, {16, 17, 18, 19, 0, 20, 1, 21}, {0, 1, 2, 3, 16, 17, 4, 18}, {16, 0, 1, 2, 17, 18, 3, 19}, {0, 16, 1, 2, 17, 18, 3, 19}, {16, 17, 0, 1, 18, 19, 2, 20}, {0, 1, 16, 2, 17, 18, 3, 19}, {16, 0, 17, 1, 18, 19, 2, 20}, {0, 16, 17, 1, 18, 19, 2, 20}, {16, 17, 18, 0, 19, 20, 1, 21}, {0, 1, 2, 16, 17, 18, 3, 19}, {16, 0, 1, 17, 18, 19, 2, 20}, {0, 16, 1, 17, 18, 19, 2, 20}, {16, 17, 0, 18, 19, 20, 1, 21}, {0, 1, 16, 17, 18, 19, 2, 20}, {16, 0, 17, 18, 19, 20, 1, 21}, {0, 16, 17, 18, 19, 20, 1, 21}, {16, 17, 18, 19, 20, 21, 0, 22}, {0, 1, 2, 3, 4, 5, 16, 17}, {16, 0, 1, 2, 3, 4, 17, 18}, {0, 16, 1, 2, 3, 4, 17, 18}, {16, 17, 0, 1, 2, 3, 18, 19}, {0, 1, 16, 2, 3, 4, 17, 18}, {16, 0, 17, 1, 2, 3, 18, 19}, {0, 16, 17, 1, 2, 3, 18, 19}, {16, 17, 18, 0, 1, 2, 19, 20}, {0, 1, 2, 16, 3, 4, 17, 18}, {16, 0, 1, 17, 2, 3, 18, 19}, {0, 16, 1, 17, 2, 3, 18, 19}, {16, 17, 0, 18, 1, 2, 19, 20}, {0, 1, 16, 17, 2, 3, 18, 19}, {16, 0, 17, 18, 1, 2, 19, 20}, {0, 16, 17, 18, 1, 2, 19, 20}, {16, 17, 18, 19, 0, 1, 20, 21}, {0, 1, 2, 3, 16, 4, 17, 18}, {16, 0, 1, 2, 17, 3, 18, 19}, {0, 16, 1, 2, 17, 3, 18, 19}, {16, 17, 0, 1, 18, 2, 19, 20}, {0, 1, 16, 2, 17, 3, 18, 19}, {16, 0, 17, 1, 18, 2, 19, 20}, {0, 16, 17, 1, 18, 2, 19, 20}, {16, 17, 18, 0, 19, 1, 20, 21}, {0, 1, 2, 16, 17, 3, 18, 19}, {16, 0, 1, 17, 18, 2, 19, 20}, {0, 16, 1, 17, 18, 2, 19, 20}, {16, 17, 0, 18, 19, 1, 20, 21}, {0, 1, 16, 17, 18, 2, 19, 20}, {16, 0, 17, 18, 19, 1, 20, 21}, {0, 16, 17, 18, 19, 1, 20, 21}, {16, 17, 18, 19, 20, 0, 21, 22}, {0, 1, 2, 3, 4, 16, 17, 18}, {16, 0, 1, 2, 3, 17, 18, 19}, {0, 16, 1, 2, 3, 17, 18, 19}, {16, 17, 0, 1, 2, 18, 19, 20}, {0, 1, 16, 2, 3, 17, 18, 19}, {16, 0, 17, 1, 2, 18, 19, 20}, {0, 16, 17, 1, 2, 18, 19, 20}, {16, 17, 18, 0, 1, 19, 20, 21}, {0, 1, 2, 16, 3, 17, 18, 19}, {16, 0, 1, 17, 2, 18, 19, 20}, {0, 16, 1, 17, 2, 18, 19, 20}, {16, 17, 0, 18, 1, 19, 20, 21}, {0, 1, 16, 17, 2, 18, 19, 20}, {16, 0, 17, 18, 1, 19, 20, 21}, {0, 16, 17, 18, 1, 19, 20, 21}, {16, 17, 18, 19, 0, 20, 21, 22}, {0, 1, 2, 3, 16, 17, 18, 19}, {16, 0, 1, 2, 17, 18, 19, 20}, {0, 16, 1, 2, 17, 18, 19, 20}, {16, 17, 0, 1, 18, 19, 20, 21}, {0, 1, 16, 2, 17, 18, 19, 20}, {16, 0, 17, 1, 18, 19, 20, 21}, {0, 16, 17, 1, 18, 19, 20, 21}, {16, 17, 18, 0, 19, 20, 21, 22}, {0, 1, 2, 16, 17, 18, 19, 20}, {16, 0, 1, 17, 18, 19, 20, 21}, {0, 16, 1, 17, 18, 19, 20, 21}, {16, 17, 0, 18, 19, 20, 21, 22}, {0, 1, 16, 17, 18, 19, 20, 21}, {16, 0, 17, 18, 19, 20, 21, 22}, {0, 16, 17, 18, 19, 20, 21, 22}, {16, 17, 18, 19, 20, 21, 22, 23}, }; const uint8_t zxc_pivco_idxb_pre[9][256][8] = { { {8, 9, 10, 11, 12, 13, 14, 15}, {16, 8, 9, 10, 11, 12, 13, 14}, {8, 16, 9, 10, 11, 12, 13, 14}, {16, 17, 8, 9, 10, 11, 12, 13}, {8, 9, 16, 10, 11, 12, 13, 14}, {16, 8, 17, 9, 10, 11, 12, 13}, {8, 16, 17, 9, 10, 11, 12, 13}, {16, 17, 18, 8, 9, 10, 11, 12}, {8, 9, 10, 16, 11, 12, 13, 14}, {16, 8, 9, 17, 10, 11, 12, 13}, {8, 16, 9, 17, 10, 11, 12, 13}, {16, 17, 8, 18, 9, 10, 11, 12}, {8, 9, 16, 17, 10, 11, 12, 13}, {16, 8, 17, 18, 9, 10, 11, 12}, {8, 16, 17, 18, 9, 10, 11, 12}, {16, 17, 18, 19, 8, 9, 10, 11}, {8, 9, 10, 11, 16, 12, 13, 14}, {16, 8, 9, 10, 17, 11, 12, 13}, {8, 16, 9, 10, 17, 11, 12, 13}, {16, 17, 8, 9, 18, 10, 11, 12}, {8, 9, 16, 10, 17, 11, 12, 13}, {16, 8, 17, 9, 18, 10, 11, 12}, {8, 16, 17, 9, 18, 10, 11, 12}, {16, 17, 18, 8, 19, 9, 10, 11}, {8, 9, 10, 16, 17, 11, 12, 13}, {16, 8, 9, 17, 18, 10, 11, 12}, {8, 16, 9, 17, 18, 10, 11, 12}, {16, 17, 8, 18, 19, 9, 10, 11}, {8, 9, 16, 17, 18, 10, 11, 12}, {16, 8, 17, 18, 19, 9, 10, 11}, {8, 16, 17, 18, 19, 9, 10, 11}, {16, 17, 18, 19, 20, 8, 9, 10}, {8, 9, 10, 11, 12, 16, 13, 14}, {16, 8, 9, 10, 11, 17, 12, 13}, {8, 16, 9, 10, 11, 17, 12, 13}, {16, 17, 8, 9, 10, 18, 11, 12}, {8, 9, 16, 10, 11, 17, 12, 13}, {16, 8, 17, 9, 10, 18, 11, 12}, {8, 16, 17, 9, 10, 18, 11, 12}, {16, 17, 18, 8, 9, 19, 10, 11}, {8, 9, 10, 16, 11, 17, 12, 13}, {16, 8, 9, 17, 10, 18, 11, 12}, {8, 16, 9, 17, 10, 18, 11, 12}, {16, 17, 8, 18, 9, 19, 10, 11}, {8, 9, 16, 17, 10, 18, 11, 12}, {16, 8, 17, 18, 9, 19, 10, 11}, {8, 16, 17, 18, 9, 19, 10, 11}, {16, 17, 18, 19, 8, 20, 9, 10}, {8, 9, 10, 11, 16, 17, 12, 13}, {16, 8, 9, 10, 17, 18, 11, 12}, {8, 16, 9, 10, 17, 18, 11, 12}, {16, 17, 8, 9, 18, 19, 10, 11}, {8, 9, 16, 10, 17, 18, 11, 12}, {16, 8, 17, 9, 18, 19, 10, 11}, {8, 16, 17, 9, 18, 19, 10, 11}, {16, 17, 18, 8, 19, 20, 9, 10}, {8, 9, 10, 16, 17, 18, 11, 12}, {16, 8, 9, 17, 18, 19, 10, 11}, {8, 16, 9, 17, 18, 19, 10, 11}, {16, 17, 8, 18, 19, 20, 9, 10}, {8, 9, 16, 17, 18, 19, 10, 11}, {16, 8, 17, 18, 19, 20, 9, 10}, {8, 16, 17, 18, 19, 20, 9, 10}, {16, 17, 18, 19, 20, 21, 8, 9}, {8, 9, 10, 11, 12, 13, 16, 14}, {16, 8, 9, 10, 11, 12, 17, 13}, {8, 16, 9, 10, 11, 12, 17, 13}, {16, 17, 8, 9, 10, 11, 18, 12}, {8, 9, 16, 10, 11, 12, 17, 13}, {16, 8, 17, 9, 10, 11, 18, 12}, {8, 16, 17, 9, 10, 11, 18, 12}, {16, 17, 18, 8, 9, 10, 19, 11}, {8, 9, 10, 16, 11, 12, 17, 13}, {16, 8, 9, 17, 10, 11, 18, 12}, {8, 16, 9, 17, 10, 11, 18, 12}, {16, 17, 8, 18, 9, 10, 19, 11}, {8, 9, 16, 17, 10, 11, 18, 12}, {16, 8, 17, 18, 9, 10, 19, 11}, {8, 16, 17, 18, 9, 10, 19, 11}, {16, 17, 18, 19, 8, 9, 20, 10}, {8, 9, 10, 11, 16, 12, 17, 13}, {16, 8, 9, 10, 17, 11, 18, 12}, {8, 16, 9, 10, 17, 11, 18, 12}, {16, 17, 8, 9, 18, 10, 19, 11}, {8, 9, 16, 10, 17, 11, 18, 12}, {16, 8, 17, 9, 18, 10, 19, 11}, {8, 16, 17, 9, 18, 10, 19, 11}, {16, 17, 18, 8, 19, 9, 20, 10}, {8, 9, 10, 16, 17, 11, 18, 12}, {16, 8, 9, 17, 18, 10, 19, 11}, {8, 16, 9, 17, 18, 10, 19, 11}, {16, 17, 8, 18, 19, 9, 20, 10}, {8, 9, 16, 17, 18, 10, 19, 11}, {16, 8, 17, 18, 19, 9, 20, 10}, {8, 16, 17, 18, 19, 9, 20, 10}, {16, 17, 18, 19, 20, 8, 21, 9}, {8, 9, 10, 11, 12, 16, 17, 13}, {16, 8, 9, 10, 11, 17, 18, 12}, {8, 16, 9, 10, 11, 17, 18, 12}, {16, 17, 8, 9, 10, 18, 19, 11}, {8, 9, 16, 10, 11, 17, 18, 12}, {16, 8, 17, 9, 10, 18, 19, 11}, {8, 16, 17, 9, 10, 18, 19, 11}, {16, 17, 18, 8, 9, 19, 20, 10}, {8, 9, 10, 16, 11, 17, 18, 12}, {16, 8, 9, 17, 10, 18, 19, 11}, {8, 16, 9, 17, 10, 18, 19, 11}, {16, 17, 8, 18, 9, 19, 20, 10}, {8, 9, 16, 17, 10, 18, 19, 11}, {16, 8, 17, 18, 9, 19, 20, 10}, {8, 16, 17, 18, 9, 19, 20, 10}, {16, 17, 18, 19, 8, 20, 21, 9}, {8, 9, 10, 11, 16, 17, 18, 12}, {16, 8, 9, 10, 17, 18, 19, 11}, {8, 16, 9, 10, 17, 18, 19, 11}, {16, 17, 8, 9, 18, 19, 20, 10}, {8, 9, 16, 10, 17, 18, 19, 11}, {16, 8, 17, 9, 18, 19, 20, 10}, {8, 16, 17, 9, 18, 19, 20, 10}, {16, 17, 18, 8, 19, 20, 21, 9}, {8, 9, 10, 16, 17, 18, 19, 11}, {16, 8, 9, 17, 18, 19, 20, 10}, {8, 16, 9, 17, 18, 19, 20, 10}, {16, 17, 8, 18, 19, 20, 21, 9}, {8, 9, 16, 17, 18, 19, 20, 10}, {16, 8, 17, 18, 19, 20, 21, 9}, {8, 16, 17, 18, 19, 20, 21, 9}, {16, 17, 18, 19, 20, 21, 22, 8}, {8, 9, 10, 11, 12, 13, 14, 16}, {16, 8, 9, 10, 11, 12, 13, 17}, {8, 16, 9, 10, 11, 12, 13, 17}, {16, 17, 8, 9, 10, 11, 12, 18}, {8, 9, 16, 10, 11, 12, 13, 17}, {16, 8, 17, 9, 10, 11, 12, 18}, {8, 16, 17, 9, 10, 11, 12, 18}, {16, 17, 18, 8, 9, 10, 11, 19}, {8, 9, 10, 16, 11, 12, 13, 17}, {16, 8, 9, 17, 10, 11, 12, 18}, {8, 16, 9, 17, 10, 11, 12, 18}, {16, 17, 8, 18, 9, 10, 11, 19}, {8, 9, 16, 17, 10, 11, 12, 18}, {16, 8, 17, 18, 9, 10, 11, 19}, {8, 16, 17, 18, 9, 10, 11, 19}, {16, 17, 18, 19, 8, 9, 10, 20}, {8, 9, 10, 11, 16, 12, 13, 17}, {16, 8, 9, 10, 17, 11, 12, 18}, {8, 16, 9, 10, 17, 11, 12, 18}, {16, 17, 8, 9, 18, 10, 11, 19}, {8, 9, 16, 10, 17, 11, 12, 18}, {16, 8, 17, 9, 18, 10, 11, 19}, {8, 16, 17, 9, 18, 10, 11, 19}, {16, 17, 18, 8, 19, 9, 10, 20}, {8, 9, 10, 16, 17, 11, 12, 18}, {16, 8, 9, 17, 18, 10, 11, 19}, {8, 16, 9, 17, 18, 10, 11, 19}, {16, 17, 8, 18, 19, 9, 10, 20}, {8, 9, 16, 17, 18, 10, 11, 19}, {16, 8, 17, 18, 19, 9, 10, 20}, {8, 16, 17, 18, 19, 9, 10, 20}, {16, 17, 18, 19, 20, 8, 9, 21}, {8, 9, 10, 11, 12, 16, 13, 17}, {16, 8, 9, 10, 11, 17, 12, 18}, {8, 16, 9, 10, 11, 17, 12, 18}, {16, 17, 8, 9, 10, 18, 11, 19}, {8, 9, 16, 10, 11, 17, 12, 18}, {16, 8, 17, 9, 10, 18, 11, 19}, {8, 16, 17, 9, 10, 18, 11, 19}, {16, 17, 18, 8, 9, 19, 10, 20}, {8, 9, 10, 16, 11, 17, 12, 18}, {16, 8, 9, 17, 10, 18, 11, 19}, {8, 16, 9, 17, 10, 18, 11, 19}, {16, 17, 8, 18, 9, 19, 10, 20}, {8, 9, 16, 17, 10, 18, 11, 19}, {16, 8, 17, 18, 9, 19, 10, 20}, {8, 16, 17, 18, 9, 19, 10, 20}, {16, 17, 18, 19, 8, 20, 9, 21}, {8, 9, 10, 11, 16, 17, 12, 18}, {16, 8, 9, 10, 17, 18, 11, 19}, {8, 16, 9, 10, 17, 18, 11, 19}, {16, 17, 8, 9, 18, 19, 10, 20}, {8, 9, 16, 10, 17, 18, 11, 19}, {16, 8, 17, 9, 18, 19, 10, 20}, {8, 16, 17, 9, 18, 19, 10, 20}, {16, 17, 18, 8, 19, 20, 9, 21}, {8, 9, 10, 16, 17, 18, 11, 19}, {16, 8, 9, 17, 18, 19, 10, 20}, {8, 16, 9, 17, 18, 19, 10, 20}, {16, 17, 8, 18, 19, 20, 9, 21}, {8, 9, 16, 17, 18, 19, 10, 20}, {16, 8, 17, 18, 19, 20, 9, 21}, {8, 16, 17, 18, 19, 20, 9, 21}, {16, 17, 18, 19, 20, 21, 8, 22}, {8, 9, 10, 11, 12, 13, 16, 17}, {16, 8, 9, 10, 11, 12, 17, 18}, {8, 16, 9, 10, 11, 12, 17, 18}, {16, 17, 8, 9, 10, 11, 18, 19}, {8, 9, 16, 10, 11, 12, 17, 18}, {16, 8, 17, 9, 10, 11, 18, 19}, {8, 16, 17, 9, 10, 11, 18, 19}, {16, 17, 18, 8, 9, 10, 19, 20}, {8, 9, 10, 16, 11, 12, 17, 18}, {16, 8, 9, 17, 10, 11, 18, 19}, {8, 16, 9, 17, 10, 11, 18, 19}, {16, 17, 8, 18, 9, 10, 19, 20}, {8, 9, 16, 17, 10, 11, 18, 19}, {16, 8, 17, 18, 9, 10, 19, 20}, {8, 16, 17, 18, 9, 10, 19, 20}, {16, 17, 18, 19, 8, 9, 20, 21}, {8, 9, 10, 11, 16, 12, 17, 18}, {16, 8, 9, 10, 17, 11, 18, 19}, {8, 16, 9, 10, 17, 11, 18, 19}, {16, 17, 8, 9, 18, 10, 19, 20}, {8, 9, 16, 10, 17, 11, 18, 19}, {16, 8, 17, 9, 18, 10, 19, 20}, {8, 16, 17, 9, 18, 10, 19, 20}, {16, 17, 18, 8, 19, 9, 20, 21}, {8, 9, 10, 16, 17, 11, 18, 19}, {16, 8, 9, 17, 18, 10, 19, 20}, {8, 16, 9, 17, 18, 10, 19, 20}, {16, 17, 8, 18, 19, 9, 20, 21}, {8, 9, 16, 17, 18, 10, 19, 20}, {16, 8, 17, 18, 19, 9, 20, 21}, {8, 16, 17, 18, 19, 9, 20, 21}, {16, 17, 18, 19, 20, 8, 21, 22}, {8, 9, 10, 11, 12, 16, 17, 18}, {16, 8, 9, 10, 11, 17, 18, 19}, {8, 16, 9, 10, 11, 17, 18, 19}, {16, 17, 8, 9, 10, 18, 19, 20}, {8, 9, 16, 10, 11, 17, 18, 19}, {16, 8, 17, 9, 10, 18, 19, 20}, {8, 16, 17, 9, 10, 18, 19, 20}, {16, 17, 18, 8, 9, 19, 20, 21}, {8, 9, 10, 16, 11, 17, 18, 19}, {16, 8, 9, 17, 10, 18, 19, 20}, {8, 16, 9, 17, 10, 18, 19, 20}, {16, 17, 8, 18, 9, 19, 20, 21}, {8, 9, 16, 17, 10, 18, 19, 20}, {16, 8, 17, 18, 9, 19, 20, 21}, {8, 16, 17, 18, 9, 19, 20, 21}, {16, 17, 18, 19, 8, 20, 21, 22}, {8, 9, 10, 11, 16, 17, 18, 19}, {16, 8, 9, 10, 17, 18, 19, 20}, {8, 16, 9, 10, 17, 18, 19, 20}, {16, 17, 8, 9, 18, 19, 20, 21}, {8, 9, 16, 10, 17, 18, 19, 20}, {16, 8, 17, 9, 18, 19, 20, 21}, {8, 16, 17, 9, 18, 19, 20, 21}, {16, 17, 18, 8, 19, 20, 21, 22}, {8, 9, 10, 16, 17, 18, 19, 20}, {16, 8, 9, 17, 18, 19, 20, 21}, {8, 16, 9, 17, 18, 19, 20, 21}, {16, 17, 8, 18, 19, 20, 21, 22}, {8, 9, 16, 17, 18, 19, 20, 21}, {16, 8, 17, 18, 19, 20, 21, 22}, {8, 16, 17, 18, 19, 20, 21, 22}, {16, 17, 18, 19, 20, 21, 22, 23}, }, { {7, 8, 9, 10, 11, 12, 13, 14}, {17, 7, 8, 9, 10, 11, 12, 13}, {7, 17, 8, 9, 10, 11, 12, 13}, {17, 18, 7, 8, 9, 10, 11, 12}, {7, 8, 17, 9, 10, 11, 12, 13}, {17, 7, 18, 8, 9, 10, 11, 12}, {7, 17, 18, 8, 9, 10, 11, 12}, {17, 18, 19, 7, 8, 9, 10, 11}, {7, 8, 9, 17, 10, 11, 12, 13}, {17, 7, 8, 18, 9, 10, 11, 12}, {7, 17, 8, 18, 9, 10, 11, 12}, {17, 18, 7, 19, 8, 9, 10, 11}, {7, 8, 17, 18, 9, 10, 11, 12}, {17, 7, 18, 19, 8, 9, 10, 11}, {7, 17, 18, 19, 8, 9, 10, 11}, {17, 18, 19, 20, 7, 8, 9, 10}, {7, 8, 9, 10, 17, 11, 12, 13}, {17, 7, 8, 9, 18, 10, 11, 12}, {7, 17, 8, 9, 18, 10, 11, 12}, {17, 18, 7, 8, 19, 9, 10, 11}, {7, 8, 17, 9, 18, 10, 11, 12}, {17, 7, 18, 8, 19, 9, 10, 11}, {7, 17, 18, 8, 19, 9, 10, 11}, {17, 18, 19, 7, 20, 8, 9, 10}, {7, 8, 9, 17, 18, 10, 11, 12}, {17, 7, 8, 18, 19, 9, 10, 11}, {7, 17, 8, 18, 19, 9, 10, 11}, {17, 18, 7, 19, 20, 8, 9, 10}, {7, 8, 17, 18, 19, 9, 10, 11}, {17, 7, 18, 19, 20, 8, 9, 10}, {7, 17, 18, 19, 20, 8, 9, 10}, {17, 18, 19, 20, 21, 7, 8, 9}, {7, 8, 9, 10, 11, 17, 12, 13}, {17, 7, 8, 9, 10, 18, 11, 12}, {7, 17, 8, 9, 10, 18, 11, 12}, {17, 18, 7, 8, 9, 19, 10, 11}, {7, 8, 17, 9, 10, 18, 11, 12}, {17, 7, 18, 8, 9, 19, 10, 11}, {7, 17, 18, 8, 9, 19, 10, 11}, {17, 18, 19, 7, 8, 20, 9, 10}, {7, 8, 9, 17, 10, 18, 11, 12}, {17, 7, 8, 18, 9, 19, 10, 11}, {7, 17, 8, 18, 9, 19, 10, 11}, {17, 18, 7, 19, 8, 20, 9, 10}, {7, 8, 17, 18, 9, 19, 10, 11}, {17, 7, 18, 19, 8, 20, 9, 10}, {7, 17, 18, 19, 8, 20, 9, 10}, {17, 18, 19, 20, 7, 21, 8, 9}, {7, 8, 9, 10, 17, 18, 11, 12}, {17, 7, 8, 9, 18, 19, 10, 11}, {7, 17, 8, 9, 18, 19, 10, 11}, {17, 18, 7, 8, 19, 20, 9, 10}, {7, 8, 17, 9, 18, 19, 10, 11}, {17, 7, 18, 8, 19, 20, 9, 10}, {7, 17, 18, 8, 19, 20, 9, 10}, {17, 18, 19, 7, 20, 21, 8, 9}, {7, 8, 9, 17, 18, 19, 10, 11}, {17, 7, 8, 18, 19, 20, 9, 10}, {7, 17, 8, 18, 19, 20, 9, 10}, {17, 18, 7, 19, 20, 21, 8, 9}, {7, 8, 17, 18, 19, 20, 9, 10}, {17, 7, 18, 19, 20, 21, 8, 9}, {7, 17, 18, 19, 20, 21, 8, 9}, {17, 18, 19, 20, 21, 22, 7, 8}, {7, 8, 9, 10, 11, 12, 17, 13}, {17, 7, 8, 9, 10, 11, 18, 12}, {7, 17, 8, 9, 10, 11, 18, 12}, {17, 18, 7, 8, 9, 10, 19, 11}, {7, 8, 17, 9, 10, 11, 18, 12}, {17, 7, 18, 8, 9, 10, 19, 11}, {7, 17, 18, 8, 9, 10, 19, 11}, {17, 18, 19, 7, 8, 9, 20, 10}, {7, 8, 9, 17, 10, 11, 18, 12}, {17, 7, 8, 18, 9, 10, 19, 11}, {7, 17, 8, 18, 9, 10, 19, 11}, {17, 18, 7, 19, 8, 9, 20, 10}, {7, 8, 17, 18, 9, 10, 19, 11}, {17, 7, 18, 19, 8, 9, 20, 10}, {7, 17, 18, 19, 8, 9, 20, 10}, {17, 18, 19, 20, 7, 8, 21, 9}, {7, 8, 9, 10, 17, 11, 18, 12}, {17, 7, 8, 9, 18, 10, 19, 11}, {7, 17, 8, 9, 18, 10, 19, 11}, {17, 18, 7, 8, 19, 9, 20, 10}, {7, 8, 17, 9, 18, 10, 19, 11}, {17, 7, 18, 8, 19, 9, 20, 10}, {7, 17, 18, 8, 19, 9, 20, 10}, {17, 18, 19, 7, 20, 8, 21, 9}, {7, 8, 9, 17, 18, 10, 19, 11}, {17, 7, 8, 18, 19, 9, 20, 10}, {7, 17, 8, 18, 19, 9, 20, 10}, {17, 18, 7, 19, 20, 8, 21, 9}, {7, 8, 17, 18, 19, 9, 20, 10}, {17, 7, 18, 19, 20, 8, 21, 9}, {7, 17, 18, 19, 20, 8, 21, 9}, {17, 18, 19, 20, 21, 7, 22, 8}, {7, 8, 9, 10, 11, 17, 18, 12}, {17, 7, 8, 9, 10, 18, 19, 11}, {7, 17, 8, 9, 10, 18, 19, 11}, {17, 18, 7, 8, 9, 19, 20, 10}, {7, 8, 17, 9, 10, 18, 19, 11}, {17, 7, 18, 8, 9, 19, 20, 10}, {7, 17, 18, 8, 9, 19, 20, 10}, {17, 18, 19, 7, 8, 20, 21, 9}, {7, 8, 9, 17, 10, 18, 19, 11}, {17, 7, 8, 18, 9, 19, 20, 10}, {7, 17, 8, 18, 9, 19, 20, 10}, {17, 18, 7, 19, 8, 20, 21, 9}, {7, 8, 17, 18, 9, 19, 20, 10}, {17, 7, 18, 19, 8, 20, 21, 9}, {7, 17, 18, 19, 8, 20, 21, 9}, {17, 18, 19, 20, 7, 21, 22, 8}, {7, 8, 9, 10, 17, 18, 19, 11}, {17, 7, 8, 9, 18, 19, 20, 10}, {7, 17, 8, 9, 18, 19, 20, 10}, {17, 18, 7, 8, 19, 20, 21, 9}, {7, 8, 17, 9, 18, 19, 20, 10}, {17, 7, 18, 8, 19, 20, 21, 9}, {7, 17, 18, 8, 19, 20, 21, 9}, {17, 18, 19, 7, 20, 21, 22, 8}, {7, 8, 9, 17, 18, 19, 20, 10}, {17, 7, 8, 18, 19, 20, 21, 9}, {7, 17, 8, 18, 19, 20, 21, 9}, {17, 18, 7, 19, 20, 21, 22, 8}, {7, 8, 17, 18, 19, 20, 21, 9}, {17, 7, 18, 19, 20, 21, 22, 8}, {7, 17, 18, 19, 20, 21, 22, 8}, {17, 18, 19, 20, 21, 22, 23, 7}, {7, 8, 9, 10, 11, 12, 13, 17}, {17, 7, 8, 9, 10, 11, 12, 18}, {7, 17, 8, 9, 10, 11, 12, 18}, {17, 18, 7, 8, 9, 10, 11, 19}, {7, 8, 17, 9, 10, 11, 12, 18}, {17, 7, 18, 8, 9, 10, 11, 19}, {7, 17, 18, 8, 9, 10, 11, 19}, {17, 18, 19, 7, 8, 9, 10, 20}, {7, 8, 9, 17, 10, 11, 12, 18}, {17, 7, 8, 18, 9, 10, 11, 19}, {7, 17, 8, 18, 9, 10, 11, 19}, {17, 18, 7, 19, 8, 9, 10, 20}, {7, 8, 17, 18, 9, 10, 11, 19}, {17, 7, 18, 19, 8, 9, 10, 20}, {7, 17, 18, 19, 8, 9, 10, 20}, {17, 18, 19, 20, 7, 8, 9, 21}, {7, 8, 9, 10, 17, 11, 12, 18}, {17, 7, 8, 9, 18, 10, 11, 19}, {7, 17, 8, 9, 18, 10, 11, 19}, {17, 18, 7, 8, 19, 9, 10, 20}, {7, 8, 17, 9, 18, 10, 11, 19}, {17, 7, 18, 8, 19, 9, 10, 20}, {7, 17, 18, 8, 19, 9, 10, 20}, {17, 18, 19, 7, 20, 8, 9, 21}, {7, 8, 9, 17, 18, 10, 11, 19}, {17, 7, 8, 18, 19, 9, 10, 20}, {7, 17, 8, 18, 19, 9, 10, 20}, {17, 18, 7, 19, 20, 8, 9, 21}, {7, 8, 17, 18, 19, 9, 10, 20}, {17, 7, 18, 19, 20, 8, 9, 21}, {7, 17, 18, 19, 20, 8, 9, 21}, {17, 18, 19, 20, 21, 7, 8, 22}, {7, 8, 9, 10, 11, 17, 12, 18}, {17, 7, 8, 9, 10, 18, 11, 19}, {7, 17, 8, 9, 10, 18, 11, 19}, {17, 18, 7, 8, 9, 19, 10, 20}, {7, 8, 17, 9, 10, 18, 11, 19}, {17, 7, 18, 8, 9, 19, 10, 20}, {7, 17, 18, 8, 9, 19, 10, 20}, {17, 18, 19, 7, 8, 20, 9, 21}, {7, 8, 9, 17, 10, 18, 11, 19}, {17, 7, 8, 18, 9, 19, 10, 20}, {7, 17, 8, 18, 9, 19, 10, 20}, {17, 18, 7, 19, 8, 20, 9, 21}, {7, 8, 17, 18, 9, 19, 10, 20}, {17, 7, 18, 19, 8, 20, 9, 21}, {7, 17, 18, 19, 8, 20, 9, 21}, {17, 18, 19, 20, 7, 21, 8, 22}, {7, 8, 9, 10, 17, 18, 11, 19}, {17, 7, 8, 9, 18, 19, 10, 20}, {7, 17, 8, 9, 18, 19, 10, 20}, {17, 18, 7, 8, 19, 20, 9, 21}, {7, 8, 17, 9, 18, 19, 10, 20}, {17, 7, 18, 8, 19, 20, 9, 21}, {7, 17, 18, 8, 19, 20, 9, 21}, {17, 18, 19, 7, 20, 21, 8, 22}, {7, 8, 9, 17, 18, 19, 10, 20}, {17, 7, 8, 18, 19, 20, 9, 21}, {7, 17, 8, 18, 19, 20, 9, 21}, {17, 18, 7, 19, 20, 21, 8, 22}, {7, 8, 17, 18, 19, 20, 9, 21}, {17, 7, 18, 19, 20, 21, 8, 22}, {7, 17, 18, 19, 20, 21, 8, 22}, {17, 18, 19, 20, 21, 22, 7, 23}, {7, 8, 9, 10, 11, 12, 17, 18}, {17, 7, 8, 9, 10, 11, 18, 19}, {7, 17, 8, 9, 10, 11, 18, 19}, {17, 18, 7, 8, 9, 10, 19, 20}, {7, 8, 17, 9, 10, 11, 18, 19}, {17, 7, 18, 8, 9, 10, 19, 20}, {7, 17, 18, 8, 9, 10, 19, 20}, {17, 18, 19, 7, 8, 9, 20, 21}, {7, 8, 9, 17, 10, 11, 18, 19}, {17, 7, 8, 18, 9, 10, 19, 20}, {7, 17, 8, 18, 9, 10, 19, 20}, {17, 18, 7, 19, 8, 9, 20, 21}, {7, 8, 17, 18, 9, 10, 19, 20}, {17, 7, 18, 19, 8, 9, 20, 21}, {7, 17, 18, 19, 8, 9, 20, 21}, {17, 18, 19, 20, 7, 8, 21, 22}, {7, 8, 9, 10, 17, 11, 18, 19}, {17, 7, 8, 9, 18, 10, 19, 20}, {7, 17, 8, 9, 18, 10, 19, 20}, {17, 18, 7, 8, 19, 9, 20, 21}, {7, 8, 17, 9, 18, 10, 19, 20}, {17, 7, 18, 8, 19, 9, 20, 21}, {7, 17, 18, 8, 19, 9, 20, 21}, {17, 18, 19, 7, 20, 8, 21, 22}, {7, 8, 9, 17, 18, 10, 19, 20}, {17, 7, 8, 18, 19, 9, 20, 21}, {7, 17, 8, 18, 19, 9, 20, 21}, {17, 18, 7, 19, 20, 8, 21, 22}, {7, 8, 17, 18, 19, 9, 20, 21}, {17, 7, 18, 19, 20, 8, 21, 22}, {7, 17, 18, 19, 20, 8, 21, 22}, {17, 18, 19, 20, 21, 7, 22, 23}, {7, 8, 9, 10, 11, 17, 18, 19}, {17, 7, 8, 9, 10, 18, 19, 20}, {7, 17, 8, 9, 10, 18, 19, 20}, {17, 18, 7, 8, 9, 19, 20, 21}, {7, 8, 17, 9, 10, 18, 19, 20}, {17, 7, 18, 8, 9, 19, 20, 21}, {7, 17, 18, 8, 9, 19, 20, 21}, {17, 18, 19, 7, 8, 20, 21, 22}, {7, 8, 9, 17, 10, 18, 19, 20}, {17, 7, 8, 18, 9, 19, 20, 21}, {7, 17, 8, 18, 9, 19, 20, 21}, {17, 18, 7, 19, 8, 20, 21, 22}, {7, 8, 17, 18, 9, 19, 20, 21}, {17, 7, 18, 19, 8, 20, 21, 22}, {7, 17, 18, 19, 8, 20, 21, 22}, {17, 18, 19, 20, 7, 21, 22, 23}, {7, 8, 9, 10, 17, 18, 19, 20}, {17, 7, 8, 9, 18, 19, 20, 21}, {7, 17, 8, 9, 18, 19, 20, 21}, {17, 18, 7, 8, 19, 20, 21, 22}, {7, 8, 17, 9, 18, 19, 20, 21}, {17, 7, 18, 8, 19, 20, 21, 22}, {7, 17, 18, 8, 19, 20, 21, 22}, {17, 18, 19, 7, 20, 21, 22, 23}, {7, 8, 9, 17, 18, 19, 20, 21}, {17, 7, 8, 18, 19, 20, 21, 22}, {7, 17, 8, 18, 19, 20, 21, 22}, {17, 18, 7, 19, 20, 21, 22, 23}, {7, 8, 17, 18, 19, 20, 21, 22}, {17, 7, 18, 19, 20, 21, 22, 23}, {7, 17, 18, 19, 20, 21, 22, 23}, {17, 18, 19, 20, 21, 22, 23, 24}, }, { {6, 7, 8, 9, 10, 11, 12, 13}, {18, 6, 7, 8, 9, 10, 11, 12}, {6, 18, 7, 8, 9, 10, 11, 12}, {18, 19, 6, 7, 8, 9, 10, 11}, {6, 7, 18, 8, 9, 10, 11, 12}, {18, 6, 19, 7, 8, 9, 10, 11}, {6, 18, 19, 7, 8, 9, 10, 11}, {18, 19, 20, 6, 7, 8, 9, 10}, {6, 7, 8, 18, 9, 10, 11, 12}, {18, 6, 7, 19, 8, 9, 10, 11}, {6, 18, 7, 19, 8, 9, 10, 11}, {18, 19, 6, 20, 7, 8, 9, 10}, {6, 7, 18, 19, 8, 9, 10, 11}, {18, 6, 19, 20, 7, 8, 9, 10}, {6, 18, 19, 20, 7, 8, 9, 10}, {18, 19, 20, 21, 6, 7, 8, 9}, {6, 7, 8, 9, 18, 10, 11, 12}, {18, 6, 7, 8, 19, 9, 10, 11}, {6, 18, 7, 8, 19, 9, 10, 11}, {18, 19, 6, 7, 20, 8, 9, 10}, {6, 7, 18, 8, 19, 9, 10, 11}, {18, 6, 19, 7, 20, 8, 9, 10}, {6, 18, 19, 7, 20, 8, 9, 10}, {18, 19, 20, 6, 21, 7, 8, 9}, {6, 7, 8, 18, 19, 9, 10, 11}, {18, 6, 7, 19, 20, 8, 9, 10}, {6, 18, 7, 19, 20, 8, 9, 10}, {18, 19, 6, 20, 21, 7, 8, 9}, {6, 7, 18, 19, 20, 8, 9, 10}, {18, 6, 19, 20, 21, 7, 8, 9}, {6, 18, 19, 20, 21, 7, 8, 9}, {18, 19, 20, 21, 22, 6, 7, 8}, {6, 7, 8, 9, 10, 18, 11, 12}, {18, 6, 7, 8, 9, 19, 10, 11}, {6, 18, 7, 8, 9, 19, 10, 11}, {18, 19, 6, 7, 8, 20, 9, 10}, {6, 7, 18, 8, 9, 19, 10, 11}, {18, 6, 19, 7, 8, 20, 9, 10}, {6, 18, 19, 7, 8, 20, 9, 10}, {18, 19, 20, 6, 7, 21, 8, 9}, {6, 7, 8, 18, 9, 19, 10, 11}, {18, 6, 7, 19, 8, 20, 9, 10}, {6, 18, 7, 19, 8, 20, 9, 10}, {18, 19, 6, 20, 7, 21, 8, 9}, {6, 7, 18, 19, 8, 20, 9, 10}, {18, 6, 19, 20, 7, 21, 8, 9}, {6, 18, 19, 20, 7, 21, 8, 9}, {18, 19, 20, 21, 6, 22, 7, 8}, {6, 7, 8, 9, 18, 19, 10, 11}, {18, 6, 7, 8, 19, 20, 9, 10}, {6, 18, 7, 8, 19, 20, 9, 10}, {18, 19, 6, 7, 20, 21, 8, 9}, {6, 7, 18, 8, 19, 20, 9, 10}, {18, 6, 19, 7, 20, 21, 8, 9}, {6, 18, 19, 7, 20, 21, 8, 9}, {18, 19, 20, 6, 21, 22, 7, 8}, {6, 7, 8, 18, 19, 20, 9, 10}, {18, 6, 7, 19, 20, 21, 8, 9}, {6, 18, 7, 19, 20, 21, 8, 9}, {18, 19, 6, 20, 21, 22, 7, 8}, {6, 7, 18, 19, 20, 21, 8, 9}, {18, 6, 19, 20, 21, 22, 7, 8}, {6, 18, 19, 20, 21, 22, 7, 8}, {18, 19, 20, 21, 22, 23, 6, 7}, {6, 7, 8, 9, 10, 11, 18, 12}, {18, 6, 7, 8, 9, 10, 19, 11}, {6, 18, 7, 8, 9, 10, 19, 11}, {18, 19, 6, 7, 8, 9, 20, 10}, {6, 7, 18, 8, 9, 10, 19, 11}, {18, 6, 19, 7, 8, 9, 20, 10}, {6, 18, 19, 7, 8, 9, 20, 10}, {18, 19, 20, 6, 7, 8, 21, 9}, {6, 7, 8, 18, 9, 10, 19, 11}, {18, 6, 7, 19, 8, 9, 20, 10}, {6, 18, 7, 19, 8, 9, 20, 10}, {18, 19, 6, 20, 7, 8, 21, 9}, {6, 7, 18, 19, 8, 9, 20, 10}, {18, 6, 19, 20, 7, 8, 21, 9}, {6, 18, 19, 20, 7, 8, 21, 9}, {18, 19, 20, 21, 6, 7, 22, 8}, {6, 7, 8, 9, 18, 10, 19, 11}, {18, 6, 7, 8, 19, 9, 20, 10}, {6, 18, 7, 8, 19, 9, 20, 10}, {18, 19, 6, 7, 20, 8, 21, 9}, {6, 7, 18, 8, 19, 9, 20, 10}, {18, 6, 19, 7, 20, 8, 21, 9}, {6, 18, 19, 7, 20, 8, 21, 9}, {18, 19, 20, 6, 21, 7, 22, 8}, {6, 7, 8, 18, 19, 9, 20, 10}, {18, 6, 7, 19, 20, 8, 21, 9}, {6, 18, 7, 19, 20, 8, 21, 9}, {18, 19, 6, 20, 21, 7, 22, 8}, {6, 7, 18, 19, 20, 8, 21, 9}, {18, 6, 19, 20, 21, 7, 22, 8}, {6, 18, 19, 20, 21, 7, 22, 8}, {18, 19, 20, 21, 22, 6, 23, 7}, {6, 7, 8, 9, 10, 18, 19, 11}, {18, 6, 7, 8, 9, 19, 20, 10}, {6, 18, 7, 8, 9, 19, 20, 10}, {18, 19, 6, 7, 8, 20, 21, 9}, {6, 7, 18, 8, 9, 19, 20, 10}, {18, 6, 19, 7, 8, 20, 21, 9}, {6, 18, 19, 7, 8, 20, 21, 9}, {18, 19, 20, 6, 7, 21, 22, 8}, {6, 7, 8, 18, 9, 19, 20, 10}, {18, 6, 7, 19, 8, 20, 21, 9}, {6, 18, 7, 19, 8, 20, 21, 9}, {18, 19, 6, 20, 7, 21, 22, 8}, {6, 7, 18, 19, 8, 20, 21, 9}, {18, 6, 19, 20, 7, 21, 22, 8}, {6, 18, 19, 20, 7, 21, 22, 8}, {18, 19, 20, 21, 6, 22, 23, 7}, {6, 7, 8, 9, 18, 19, 20, 10}, {18, 6, 7, 8, 19, 20, 21, 9}, {6, 18, 7, 8, 19, 20, 21, 9}, {18, 19, 6, 7, 20, 21, 22, 8}, {6, 7, 18, 8, 19, 20, 21, 9}, {18, 6, 19, 7, 20, 21, 22, 8}, {6, 18, 19, 7, 20, 21, 22, 8}, {18, 19, 20, 6, 21, 22, 23, 7}, {6, 7, 8, 18, 19, 20, 21, 9}, {18, 6, 7, 19, 20, 21, 22, 8}, {6, 18, 7, 19, 20, 21, 22, 8}, {18, 19, 6, 20, 21, 22, 23, 7}, {6, 7, 18, 19, 20, 21, 22, 8}, {18, 6, 19, 20, 21, 22, 23, 7}, {6, 18, 19, 20, 21, 22, 23, 7}, {18, 19, 20, 21, 22, 23, 24, 6}, {6, 7, 8, 9, 10, 11, 12, 18}, {18, 6, 7, 8, 9, 10, 11, 19}, {6, 18, 7, 8, 9, 10, 11, 19}, {18, 19, 6, 7, 8, 9, 10, 20}, {6, 7, 18, 8, 9, 10, 11, 19}, {18, 6, 19, 7, 8, 9, 10, 20}, {6, 18, 19, 7, 8, 9, 10, 20}, {18, 19, 20, 6, 7, 8, 9, 21}, {6, 7, 8, 18, 9, 10, 11, 19}, {18, 6, 7, 19, 8, 9, 10, 20}, {6, 18, 7, 19, 8, 9, 10, 20}, {18, 19, 6, 20, 7, 8, 9, 21}, {6, 7, 18, 19, 8, 9, 10, 20}, {18, 6, 19, 20, 7, 8, 9, 21}, {6, 18, 19, 20, 7, 8, 9, 21}, {18, 19, 20, 21, 6, 7, 8, 22}, {6, 7, 8, 9, 18, 10, 11, 19}, {18, 6, 7, 8, 19, 9, 10, 20}, {6, 18, 7, 8, 19, 9, 10, 20}, {18, 19, 6, 7, 20, 8, 9, 21}, {6, 7, 18, 8, 19, 9, 10, 20}, {18, 6, 19, 7, 20, 8, 9, 21}, {6, 18, 19, 7, 20, 8, 9, 21}, {18, 19, 20, 6, 21, 7, 8, 22}, {6, 7, 8, 18, 19, 9, 10, 20}, {18, 6, 7, 19, 20, 8, 9, 21}, {6, 18, 7, 19, 20, 8, 9, 21}, {18, 19, 6, 20, 21, 7, 8, 22}, {6, 7, 18, 19, 20, 8, 9, 21}, {18, 6, 19, 20, 21, 7, 8, 22}, {6, 18, 19, 20, 21, 7, 8, 22}, {18, 19, 20, 21, 22, 6, 7, 23}, {6, 7, 8, 9, 10, 18, 11, 19}, {18, 6, 7, 8, 9, 19, 10, 20}, {6, 18, 7, 8, 9, 19, 10, 20}, {18, 19, 6, 7, 8, 20, 9, 21}, {6, 7, 18, 8, 9, 19, 10, 20}, {18, 6, 19, 7, 8, 20, 9, 21}, {6, 18, 19, 7, 8, 20, 9, 21}, {18, 19, 20, 6, 7, 21, 8, 22}, {6, 7, 8, 18, 9, 19, 10, 20}, {18, 6, 7, 19, 8, 20, 9, 21}, {6, 18, 7, 19, 8, 20, 9, 21}, {18, 19, 6, 20, 7, 21, 8, 22}, {6, 7, 18, 19, 8, 20, 9, 21}, {18, 6, 19, 20, 7, 21, 8, 22}, {6, 18, 19, 20, 7, 21, 8, 22}, {18, 19, 20, 21, 6, 22, 7, 23}, {6, 7, 8, 9, 18, 19, 10, 20}, {18, 6, 7, 8, 19, 20, 9, 21}, {6, 18, 7, 8, 19, 20, 9, 21}, {18, 19, 6, 7, 20, 21, 8, 22}, {6, 7, 18, 8, 19, 20, 9, 21}, {18, 6, 19, 7, 20, 21, 8, 22}, {6, 18, 19, 7, 20, 21, 8, 22}, {18, 19, 20, 6, 21, 22, 7, 23}, {6, 7, 8, 18, 19, 20, 9, 21}, {18, 6, 7, 19, 20, 21, 8, 22}, {6, 18, 7, 19, 20, 21, 8, 22}, {18, 19, 6, 20, 21, 22, 7, 23}, {6, 7, 18, 19, 20, 21, 8, 22}, {18, 6, 19, 20, 21, 22, 7, 23}, {6, 18, 19, 20, 21, 22, 7, 23}, {18, 19, 20, 21, 22, 23, 6, 24}, {6, 7, 8, 9, 10, 11, 18, 19}, {18, 6, 7, 8, 9, 10, 19, 20}, {6, 18, 7, 8, 9, 10, 19, 20}, {18, 19, 6, 7, 8, 9, 20, 21}, {6, 7, 18, 8, 9, 10, 19, 20}, {18, 6, 19, 7, 8, 9, 20, 21}, {6, 18, 19, 7, 8, 9, 20, 21}, {18, 19, 20, 6, 7, 8, 21, 22}, {6, 7, 8, 18, 9, 10, 19, 20}, {18, 6, 7, 19, 8, 9, 20, 21}, {6, 18, 7, 19, 8, 9, 20, 21}, {18, 19, 6, 20, 7, 8, 21, 22}, {6, 7, 18, 19, 8, 9, 20, 21}, {18, 6, 19, 20, 7, 8, 21, 22}, {6, 18, 19, 20, 7, 8, 21, 22}, {18, 19, 20, 21, 6, 7, 22, 23}, {6, 7, 8, 9, 18, 10, 19, 20}, {18, 6, 7, 8, 19, 9, 20, 21}, {6, 18, 7, 8, 19, 9, 20, 21}, {18, 19, 6, 7, 20, 8, 21, 22}, {6, 7, 18, 8, 19, 9, 20, 21}, {18, 6, 19, 7, 20, 8, 21, 22}, {6, 18, 19, 7, 20, 8, 21, 22}, {18, 19, 20, 6, 21, 7, 22, 23}, {6, 7, 8, 18, 19, 9, 20, 21}, {18, 6, 7, 19, 20, 8, 21, 22}, {6, 18, 7, 19, 20, 8, 21, 22}, {18, 19, 6, 20, 21, 7, 22, 23}, {6, 7, 18, 19, 20, 8, 21, 22}, {18, 6, 19, 20, 21, 7, 22, 23}, {6, 18, 19, 20, 21, 7, 22, 23}, {18, 19, 20, 21, 22, 6, 23, 24}, {6, 7, 8, 9, 10, 18, 19, 20}, {18, 6, 7, 8, 9, 19, 20, 21}, {6, 18, 7, 8, 9, 19, 20, 21}, {18, 19, 6, 7, 8, 20, 21, 22}, {6, 7, 18, 8, 9, 19, 20, 21}, {18, 6, 19, 7, 8, 20, 21, 22}, {6, 18, 19, 7, 8, 20, 21, 22}, {18, 19, 20, 6, 7, 21, 22, 23}, {6, 7, 8, 18, 9, 19, 20, 21}, {18, 6, 7, 19, 8, 20, 21, 22}, {6, 18, 7, 19, 8, 20, 21, 22}, {18, 19, 6, 20, 7, 21, 22, 23}, {6, 7, 18, 19, 8, 20, 21, 22}, {18, 6, 19, 20, 7, 21, 22, 23}, {6, 18, 19, 20, 7, 21, 22, 23}, {18, 19, 20, 21, 6, 22, 23, 24}, {6, 7, 8, 9, 18, 19, 20, 21}, {18, 6, 7, 8, 19, 20, 21, 22}, {6, 18, 7, 8, 19, 20, 21, 22}, {18, 19, 6, 7, 20, 21, 22, 23}, {6, 7, 18, 8, 19, 20, 21, 22}, {18, 6, 19, 7, 20, 21, 22, 23}, {6, 18, 19, 7, 20, 21, 22, 23}, {18, 19, 20, 6, 21, 22, 23, 24}, {6, 7, 8, 18, 19, 20, 21, 22}, {18, 6, 7, 19, 20, 21, 22, 23}, {6, 18, 7, 19, 20, 21, 22, 23}, {18, 19, 6, 20, 21, 22, 23, 24}, {6, 7, 18, 19, 20, 21, 22, 23}, {18, 6, 19, 20, 21, 22, 23, 24}, {6, 18, 19, 20, 21, 22, 23, 24}, {18, 19, 20, 21, 22, 23, 24, 25}, }, { {5, 6, 7, 8, 9, 10, 11, 12}, {19, 5, 6, 7, 8, 9, 10, 11}, {5, 19, 6, 7, 8, 9, 10, 11}, {19, 20, 5, 6, 7, 8, 9, 10}, {5, 6, 19, 7, 8, 9, 10, 11}, {19, 5, 20, 6, 7, 8, 9, 10}, {5, 19, 20, 6, 7, 8, 9, 10}, {19, 20, 21, 5, 6, 7, 8, 9}, {5, 6, 7, 19, 8, 9, 10, 11}, {19, 5, 6, 20, 7, 8, 9, 10}, {5, 19, 6, 20, 7, 8, 9, 10}, {19, 20, 5, 21, 6, 7, 8, 9}, {5, 6, 19, 20, 7, 8, 9, 10}, {19, 5, 20, 21, 6, 7, 8, 9}, {5, 19, 20, 21, 6, 7, 8, 9}, {19, 20, 21, 22, 5, 6, 7, 8}, {5, 6, 7, 8, 19, 9, 10, 11}, {19, 5, 6, 7, 20, 8, 9, 10}, {5, 19, 6, 7, 20, 8, 9, 10}, {19, 20, 5, 6, 21, 7, 8, 9}, {5, 6, 19, 7, 20, 8, 9, 10}, {19, 5, 20, 6, 21, 7, 8, 9}, {5, 19, 20, 6, 21, 7, 8, 9}, {19, 20, 21, 5, 22, 6, 7, 8}, {5, 6, 7, 19, 20, 8, 9, 10}, {19, 5, 6, 20, 21, 7, 8, 9}, {5, 19, 6, 20, 21, 7, 8, 9}, {19, 20, 5, 21, 22, 6, 7, 8}, {5, 6, 19, 20, 21, 7, 8, 9}, {19, 5, 20, 21, 22, 6, 7, 8}, {5, 19, 20, 21, 22, 6, 7, 8}, {19, 20, 21, 22, 23, 5, 6, 7}, {5, 6, 7, 8, 9, 19, 10, 11}, {19, 5, 6, 7, 8, 20, 9, 10}, {5, 19, 6, 7, 8, 20, 9, 10}, {19, 20, 5, 6, 7, 21, 8, 9}, {5, 6, 19, 7, 8, 20, 9, 10}, {19, 5, 20, 6, 7, 21, 8, 9}, {5, 19, 20, 6, 7, 21, 8, 9}, {19, 20, 21, 5, 6, 22, 7, 8}, {5, 6, 7, 19, 8, 20, 9, 10}, {19, 5, 6, 20, 7, 21, 8, 9}, {5, 19, 6, 20, 7, 21, 8, 9}, {19, 20, 5, 21, 6, 22, 7, 8}, {5, 6, 19, 20, 7, 21, 8, 9}, {19, 5, 20, 21, 6, 22, 7, 8}, {5, 19, 20, 21, 6, 22, 7, 8}, {19, 20, 21, 22, 5, 23, 6, 7}, {5, 6, 7, 8, 19, 20, 9, 10}, {19, 5, 6, 7, 20, 21, 8, 9}, {5, 19, 6, 7, 20, 21, 8, 9}, {19, 20, 5, 6, 21, 22, 7, 8}, {5, 6, 19, 7, 20, 21, 8, 9}, {19, 5, 20, 6, 21, 22, 7, 8}, {5, 19, 20, 6, 21, 22, 7, 8}, {19, 20, 21, 5, 22, 23, 6, 7}, {5, 6, 7, 19, 20, 21, 8, 9}, {19, 5, 6, 20, 21, 22, 7, 8}, {5, 19, 6, 20, 21, 22, 7, 8}, {19, 20, 5, 21, 22, 23, 6, 7}, {5, 6, 19, 20, 21, 22, 7, 8}, {19, 5, 20, 21, 22, 23, 6, 7}, {5, 19, 20, 21, 22, 23, 6, 7}, {19, 20, 21, 22, 23, 24, 5, 6}, {5, 6, 7, 8, 9, 10, 19, 11}, {19, 5, 6, 7, 8, 9, 20, 10}, {5, 19, 6, 7, 8, 9, 20, 10}, {19, 20, 5, 6, 7, 8, 21, 9}, {5, 6, 19, 7, 8, 9, 20, 10}, {19, 5, 20, 6, 7, 8, 21, 9}, {5, 19, 20, 6, 7, 8, 21, 9}, {19, 20, 21, 5, 6, 7, 22, 8}, {5, 6, 7, 19, 8, 9, 20, 10}, {19, 5, 6, 20, 7, 8, 21, 9}, {5, 19, 6, 20, 7, 8, 21, 9}, {19, 20, 5, 21, 6, 7, 22, 8}, {5, 6, 19, 20, 7, 8, 21, 9}, {19, 5, 20, 21, 6, 7, 22, 8}, {5, 19, 20, 21, 6, 7, 22, 8}, {19, 20, 21, 22, 5, 6, 23, 7}, {5, 6, 7, 8, 19, 9, 20, 10}, {19, 5, 6, 7, 20, 8, 21, 9}, {5, 19, 6, 7, 20, 8, 21, 9}, {19, 20, 5, 6, 21, 7, 22, 8}, {5, 6, 19, 7, 20, 8, 21, 9}, {19, 5, 20, 6, 21, 7, 22, 8}, {5, 19, 20, 6, 21, 7, 22, 8}, {19, 20, 21, 5, 22, 6, 23, 7}, {5, 6, 7, 19, 20, 8, 21, 9}, {19, 5, 6, 20, 21, 7, 22, 8}, {5, 19, 6, 20, 21, 7, 22, 8}, {19, 20, 5, 21, 22, 6, 23, 7}, {5, 6, 19, 20, 21, 7, 22, 8}, {19, 5, 20, 21, 22, 6, 23, 7}, {5, 19, 20, 21, 22, 6, 23, 7}, {19, 20, 21, 22, 23, 5, 24, 6}, {5, 6, 7, 8, 9, 19, 20, 10}, {19, 5, 6, 7, 8, 20, 21, 9}, {5, 19, 6, 7, 8, 20, 21, 9}, {19, 20, 5, 6, 7, 21, 22, 8}, {5, 6, 19, 7, 8, 20, 21, 9}, {19, 5, 20, 6, 7, 21, 22, 8}, {5, 19, 20, 6, 7, 21, 22, 8}, {19, 20, 21, 5, 6, 22, 23, 7}, {5, 6, 7, 19, 8, 20, 21, 9}, {19, 5, 6, 20, 7, 21, 22, 8}, {5, 19, 6, 20, 7, 21, 22, 8}, {19, 20, 5, 21, 6, 22, 23, 7}, {5, 6, 19, 20, 7, 21, 22, 8}, {19, 5, 20, 21, 6, 22, 23, 7}, {5, 19, 20, 21, 6, 22, 23, 7}, {19, 20, 21, 22, 5, 23, 24, 6}, {5, 6, 7, 8, 19, 20, 21, 9}, {19, 5, 6, 7, 20, 21, 22, 8}, {5, 19, 6, 7, 20, 21, 22, 8}, {19, 20, 5, 6, 21, 22, 23, 7}, {5, 6, 19, 7, 20, 21, 22, 8}, {19, 5, 20, 6, 21, 22, 23, 7}, {5, 19, 20, 6, 21, 22, 23, 7}, {19, 20, 21, 5, 22, 23, 24, 6}, {5, 6, 7, 19, 20, 21, 22, 8}, {19, 5, 6, 20, 21, 22, 23, 7}, {5, 19, 6, 20, 21, 22, 23, 7}, {19, 20, 5, 21, 22, 23, 24, 6}, {5, 6, 19, 20, 21, 22, 23, 7}, {19, 5, 20, 21, 22, 23, 24, 6}, {5, 19, 20, 21, 22, 23, 24, 6}, {19, 20, 21, 22, 23, 24, 25, 5}, {5, 6, 7, 8, 9, 10, 11, 19}, {19, 5, 6, 7, 8, 9, 10, 20}, {5, 19, 6, 7, 8, 9, 10, 20}, {19, 20, 5, 6, 7, 8, 9, 21}, {5, 6, 19, 7, 8, 9, 10, 20}, {19, 5, 20, 6, 7, 8, 9, 21}, {5, 19, 20, 6, 7, 8, 9, 21}, {19, 20, 21, 5, 6, 7, 8, 22}, {5, 6, 7, 19, 8, 9, 10, 20}, {19, 5, 6, 20, 7, 8, 9, 21}, {5, 19, 6, 20, 7, 8, 9, 21}, {19, 20, 5, 21, 6, 7, 8, 22}, {5, 6, 19, 20, 7, 8, 9, 21}, {19, 5, 20, 21, 6, 7, 8, 22}, {5, 19, 20, 21, 6, 7, 8, 22}, {19, 20, 21, 22, 5, 6, 7, 23}, {5, 6, 7, 8, 19, 9, 10, 20}, {19, 5, 6, 7, 20, 8, 9, 21}, {5, 19, 6, 7, 20, 8, 9, 21}, {19, 20, 5, 6, 21, 7, 8, 22}, {5, 6, 19, 7, 20, 8, 9, 21}, {19, 5, 20, 6, 21, 7, 8, 22}, {5, 19, 20, 6, 21, 7, 8, 22}, {19, 20, 21, 5, 22, 6, 7, 23}, {5, 6, 7, 19, 20, 8, 9, 21}, {19, 5, 6, 20, 21, 7, 8, 22}, {5, 19, 6, 20, 21, 7, 8, 22}, {19, 20, 5, 21, 22, 6, 7, 23}, {5, 6, 19, 20, 21, 7, 8, 22}, {19, 5, 20, 21, 22, 6, 7, 23}, {5, 19, 20, 21, 22, 6, 7, 23}, {19, 20, 21, 22, 23, 5, 6, 24}, {5, 6, 7, 8, 9, 19, 10, 20}, {19, 5, 6, 7, 8, 20, 9, 21}, {5, 19, 6, 7, 8, 20, 9, 21}, {19, 20, 5, 6, 7, 21, 8, 22}, {5, 6, 19, 7, 8, 20, 9, 21}, {19, 5, 20, 6, 7, 21, 8, 22}, {5, 19, 20, 6, 7, 21, 8, 22}, {19, 20, 21, 5, 6, 22, 7, 23}, {5, 6, 7, 19, 8, 20, 9, 21}, {19, 5, 6, 20, 7, 21, 8, 22}, {5, 19, 6, 20, 7, 21, 8, 22}, {19, 20, 5, 21, 6, 22, 7, 23}, {5, 6, 19, 20, 7, 21, 8, 22}, {19, 5, 20, 21, 6, 22, 7, 23}, {5, 19, 20, 21, 6, 22, 7, 23}, {19, 20, 21, 22, 5, 23, 6, 24}, {5, 6, 7, 8, 19, 20, 9, 21}, {19, 5, 6, 7, 20, 21, 8, 22}, {5, 19, 6, 7, 20, 21, 8, 22}, {19, 20, 5, 6, 21, 22, 7, 23}, {5, 6, 19, 7, 20, 21, 8, 22}, {19, 5, 20, 6, 21, 22, 7, 23}, {5, 19, 20, 6, 21, 22, 7, 23}, {19, 20, 21, 5, 22, 23, 6, 24}, {5, 6, 7, 19, 20, 21, 8, 22}, {19, 5, 6, 20, 21, 22, 7, 23}, {5, 19, 6, 20, 21, 22, 7, 23}, {19, 20, 5, 21, 22, 23, 6, 24}, {5, 6, 19, 20, 21, 22, 7, 23}, {19, 5, 20, 21, 22, 23, 6, 24}, {5, 19, 20, 21, 22, 23, 6, 24}, {19, 20, 21, 22, 23, 24, 5, 25}, {5, 6, 7, 8, 9, 10, 19, 20}, {19, 5, 6, 7, 8, 9, 20, 21}, {5, 19, 6, 7, 8, 9, 20, 21}, {19, 20, 5, 6, 7, 8, 21, 22}, {5, 6, 19, 7, 8, 9, 20, 21}, {19, 5, 20, 6, 7, 8, 21, 22}, {5, 19, 20, 6, 7, 8, 21, 22}, {19, 20, 21, 5, 6, 7, 22, 23}, {5, 6, 7, 19, 8, 9, 20, 21}, {19, 5, 6, 20, 7, 8, 21, 22}, {5, 19, 6, 20, 7, 8, 21, 22}, {19, 20, 5, 21, 6, 7, 22, 23}, {5, 6, 19, 20, 7, 8, 21, 22}, {19, 5, 20, 21, 6, 7, 22, 23}, {5, 19, 20, 21, 6, 7, 22, 23}, {19, 20, 21, 22, 5, 6, 23, 24}, {5, 6, 7, 8, 19, 9, 20, 21}, {19, 5, 6, 7, 20, 8, 21, 22}, {5, 19, 6, 7, 20, 8, 21, 22}, {19, 20, 5, 6, 21, 7, 22, 23}, {5, 6, 19, 7, 20, 8, 21, 22}, {19, 5, 20, 6, 21, 7, 22, 23}, {5, 19, 20, 6, 21, 7, 22, 23}, {19, 20, 21, 5, 22, 6, 23, 24}, {5, 6, 7, 19, 20, 8, 21, 22}, {19, 5, 6, 20, 21, 7, 22, 23}, {5, 19, 6, 20, 21, 7, 22, 23}, {19, 20, 5, 21, 22, 6, 23, 24}, {5, 6, 19, 20, 21, 7, 22, 23}, {19, 5, 20, 21, 22, 6, 23, 24}, {5, 19, 20, 21, 22, 6, 23, 24}, {19, 20, 21, 22, 23, 5, 24, 25}, {5, 6, 7, 8, 9, 19, 20, 21}, {19, 5, 6, 7, 8, 20, 21, 22}, {5, 19, 6, 7, 8, 20, 21, 22}, {19, 20, 5, 6, 7, 21, 22, 23}, {5, 6, 19, 7, 8, 20, 21, 22}, {19, 5, 20, 6, 7, 21, 22, 23}, {5, 19, 20, 6, 7, 21, 22, 23}, {19, 20, 21, 5, 6, 22, 23, 24}, {5, 6, 7, 19, 8, 20, 21, 22}, {19, 5, 6, 20, 7, 21, 22, 23}, {5, 19, 6, 20, 7, 21, 22, 23}, {19, 20, 5, 21, 6, 22, 23, 24}, {5, 6, 19, 20, 7, 21, 22, 23}, {19, 5, 20, 21, 6, 22, 23, 24}, {5, 19, 20, 21, 6, 22, 23, 24}, {19, 20, 21, 22, 5, 23, 24, 25}, {5, 6, 7, 8, 19, 20, 21, 22}, {19, 5, 6, 7, 20, 21, 22, 23}, {5, 19, 6, 7, 20, 21, 22, 23}, {19, 20, 5, 6, 21, 22, 23, 24}, {5, 6, 19, 7, 20, 21, 22, 23}, {19, 5, 20, 6, 21, 22, 23, 24}, {5, 19, 20, 6, 21, 22, 23, 24}, {19, 20, 21, 5, 22, 23, 24, 25}, {5, 6, 7, 19, 20, 21, 22, 23}, {19, 5, 6, 20, 21, 22, 23, 24}, {5, 19, 6, 20, 21, 22, 23, 24}, {19, 20, 5, 21, 22, 23, 24, 25}, {5, 6, 19, 20, 21, 22, 23, 24}, {19, 5, 20, 21, 22, 23, 24, 25}, {5, 19, 20, 21, 22, 23, 24, 25}, {19, 20, 21, 22, 23, 24, 25, 26}, }, { {4, 5, 6, 7, 8, 9, 10, 11}, {20, 4, 5, 6, 7, 8, 9, 10}, {4, 20, 5, 6, 7, 8, 9, 10}, {20, 21, 4, 5, 6, 7, 8, 9}, {4, 5, 20, 6, 7, 8, 9, 10}, {20, 4, 21, 5, 6, 7, 8, 9}, {4, 20, 21, 5, 6, 7, 8, 9}, {20, 21, 22, 4, 5, 6, 7, 8}, {4, 5, 6, 20, 7, 8, 9, 10}, {20, 4, 5, 21, 6, 7, 8, 9}, {4, 20, 5, 21, 6, 7, 8, 9}, {20, 21, 4, 22, 5, 6, 7, 8}, {4, 5, 20, 21, 6, 7, 8, 9}, {20, 4, 21, 22, 5, 6, 7, 8}, {4, 20, 21, 22, 5, 6, 7, 8}, {20, 21, 22, 23, 4, 5, 6, 7}, {4, 5, 6, 7, 20, 8, 9, 10}, {20, 4, 5, 6, 21, 7, 8, 9}, {4, 20, 5, 6, 21, 7, 8, 9}, {20, 21, 4, 5, 22, 6, 7, 8}, {4, 5, 20, 6, 21, 7, 8, 9}, {20, 4, 21, 5, 22, 6, 7, 8}, {4, 20, 21, 5, 22, 6, 7, 8}, {20, 21, 22, 4, 23, 5, 6, 7}, {4, 5, 6, 20, 21, 7, 8, 9}, {20, 4, 5, 21, 22, 6, 7, 8}, {4, 20, 5, 21, 22, 6, 7, 8}, {20, 21, 4, 22, 23, 5, 6, 7}, {4, 5, 20, 21, 22, 6, 7, 8}, {20, 4, 21, 22, 23, 5, 6, 7}, {4, 20, 21, 22, 23, 5, 6, 7}, {20, 21, 22, 23, 24, 4, 5, 6}, {4, 5, 6, 7, 8, 20, 9, 10}, {20, 4, 5, 6, 7, 21, 8, 9}, {4, 20, 5, 6, 7, 21, 8, 9}, {20, 21, 4, 5, 6, 22, 7, 8}, {4, 5, 20, 6, 7, 21, 8, 9}, {20, 4, 21, 5, 6, 22, 7, 8}, {4, 20, 21, 5, 6, 22, 7, 8}, {20, 21, 22, 4, 5, 23, 6, 7}, {4, 5, 6, 20, 7, 21, 8, 9}, {20, 4, 5, 21, 6, 22, 7, 8}, {4, 20, 5, 21, 6, 22, 7, 8}, {20, 21, 4, 22, 5, 23, 6, 7}, {4, 5, 20, 21, 6, 22, 7, 8}, {20, 4, 21, 22, 5, 23, 6, 7}, {4, 20, 21, 22, 5, 23, 6, 7}, {20, 21, 22, 23, 4, 24, 5, 6}, {4, 5, 6, 7, 20, 21, 8, 9}, {20, 4, 5, 6, 21, 22, 7, 8}, {4, 20, 5, 6, 21, 22, 7, 8}, {20, 21, 4, 5, 22, 23, 6, 7}, {4, 5, 20, 6, 21, 22, 7, 8}, {20, 4, 21, 5, 22, 23, 6, 7}, {4, 20, 21, 5, 22, 23, 6, 7}, {20, 21, 22, 4, 23, 24, 5, 6}, {4, 5, 6, 20, 21, 22, 7, 8}, {20, 4, 5, 21, 22, 23, 6, 7}, {4, 20, 5, 21, 22, 23, 6, 7}, {20, 21, 4, 22, 23, 24, 5, 6}, {4, 5, 20, 21, 22, 23, 6, 7}, {20, 4, 21, 22, 23, 24, 5, 6}, {4, 20, 21, 22, 23, 24, 5, 6}, {20, 21, 22, 23, 24, 25, 4, 5}, {4, 5, 6, 7, 8, 9, 20, 10}, {20, 4, 5, 6, 7, 8, 21, 9}, {4, 20, 5, 6, 7, 8, 21, 9}, {20, 21, 4, 5, 6, 7, 22, 8}, {4, 5, 20, 6, 7, 8, 21, 9}, {20, 4, 21, 5, 6, 7, 22, 8}, {4, 20, 21, 5, 6, 7, 22, 8}, {20, 21, 22, 4, 5, 6, 23, 7}, {4, 5, 6, 20, 7, 8, 21, 9}, {20, 4, 5, 21, 6, 7, 22, 8}, {4, 20, 5, 21, 6, 7, 22, 8}, {20, 21, 4, 22, 5, 6, 23, 7}, {4, 5, 20, 21, 6, 7, 22, 8}, {20, 4, 21, 22, 5, 6, 23, 7}, {4, 20, 21, 22, 5, 6, 23, 7}, {20, 21, 22, 23, 4, 5, 24, 6}, {4, 5, 6, 7, 20, 8, 21, 9}, {20, 4, 5, 6, 21, 7, 22, 8}, {4, 20, 5, 6, 21, 7, 22, 8}, {20, 21, 4, 5, 22, 6, 23, 7}, {4, 5, 20, 6, 21, 7, 22, 8}, {20, 4, 21, 5, 22, 6, 23, 7}, {4, 20, 21, 5, 22, 6, 23, 7}, {20, 21, 22, 4, 23, 5, 24, 6}, {4, 5, 6, 20, 21, 7, 22, 8}, {20, 4, 5, 21, 22, 6, 23, 7}, {4, 20, 5, 21, 22, 6, 23, 7}, {20, 21, 4, 22, 23, 5, 24, 6}, {4, 5, 20, 21, 22, 6, 23, 7}, {20, 4, 21, 22, 23, 5, 24, 6}, {4, 20, 21, 22, 23, 5, 24, 6}, {20, 21, 22, 23, 24, 4, 25, 5}, {4, 5, 6, 7, 8, 20, 21, 9}, {20, 4, 5, 6, 7, 21, 22, 8}, {4, 20, 5, 6, 7, 21, 22, 8}, {20, 21, 4, 5, 6, 22, 23, 7}, {4, 5, 20, 6, 7, 21, 22, 8}, {20, 4, 21, 5, 6, 22, 23, 7}, {4, 20, 21, 5, 6, 22, 23, 7}, {20, 21, 22, 4, 5, 23, 24, 6}, {4, 5, 6, 20, 7, 21, 22, 8}, {20, 4, 5, 21, 6, 22, 23, 7}, {4, 20, 5, 21, 6, 22, 23, 7}, {20, 21, 4, 22, 5, 23, 24, 6}, {4, 5, 20, 21, 6, 22, 23, 7}, {20, 4, 21, 22, 5, 23, 24, 6}, {4, 20, 21, 22, 5, 23, 24, 6}, {20, 21, 22, 23, 4, 24, 25, 5}, {4, 5, 6, 7, 20, 21, 22, 8}, {20, 4, 5, 6, 21, 22, 23, 7}, {4, 20, 5, 6, 21, 22, 23, 7}, {20, 21, 4, 5, 22, 23, 24, 6}, {4, 5, 20, 6, 21, 22, 23, 7}, {20, 4, 21, 5, 22, 23, 24, 6}, {4, 20, 21, 5, 22, 23, 24, 6}, {20, 21, 22, 4, 23, 24, 25, 5}, {4, 5, 6, 20, 21, 22, 23, 7}, {20, 4, 5, 21, 22, 23, 24, 6}, {4, 20, 5, 21, 22, 23, 24, 6}, {20, 21, 4, 22, 23, 24, 25, 5}, {4, 5, 20, 21, 22, 23, 24, 6}, {20, 4, 21, 22, 23, 24, 25, 5}, {4, 20, 21, 22, 23, 24, 25, 5}, {20, 21, 22, 23, 24, 25, 26, 4}, {4, 5, 6, 7, 8, 9, 10, 20}, {20, 4, 5, 6, 7, 8, 9, 21}, {4, 20, 5, 6, 7, 8, 9, 21}, {20, 21, 4, 5, 6, 7, 8, 22}, {4, 5, 20, 6, 7, 8, 9, 21}, {20, 4, 21, 5, 6, 7, 8, 22}, {4, 20, 21, 5, 6, 7, 8, 22}, {20, 21, 22, 4, 5, 6, 7, 23}, {4, 5, 6, 20, 7, 8, 9, 21}, {20, 4, 5, 21, 6, 7, 8, 22}, {4, 20, 5, 21, 6, 7, 8, 22}, {20, 21, 4, 22, 5, 6, 7, 23}, {4, 5, 20, 21, 6, 7, 8, 22}, {20, 4, 21, 22, 5, 6, 7, 23}, {4, 20, 21, 22, 5, 6, 7, 23}, {20, 21, 22, 23, 4, 5, 6, 24}, {4, 5, 6, 7, 20, 8, 9, 21}, {20, 4, 5, 6, 21, 7, 8, 22}, {4, 20, 5, 6, 21, 7, 8, 22}, {20, 21, 4, 5, 22, 6, 7, 23}, {4, 5, 20, 6, 21, 7, 8, 22}, {20, 4, 21, 5, 22, 6, 7, 23}, {4, 20, 21, 5, 22, 6, 7, 23}, {20, 21, 22, 4, 23, 5, 6, 24}, {4, 5, 6, 20, 21, 7, 8, 22}, {20, 4, 5, 21, 22, 6, 7, 23}, {4, 20, 5, 21, 22, 6, 7, 23}, {20, 21, 4, 22, 23, 5, 6, 24}, {4, 5, 20, 21, 22, 6, 7, 23}, {20, 4, 21, 22, 23, 5, 6, 24}, {4, 20, 21, 22, 23, 5, 6, 24}, {20, 21, 22, 23, 24, 4, 5, 25}, {4, 5, 6, 7, 8, 20, 9, 21}, {20, 4, 5, 6, 7, 21, 8, 22}, {4, 20, 5, 6, 7, 21, 8, 22}, {20, 21, 4, 5, 6, 22, 7, 23}, {4, 5, 20, 6, 7, 21, 8, 22}, {20, 4, 21, 5, 6, 22, 7, 23}, {4, 20, 21, 5, 6, 22, 7, 23}, {20, 21, 22, 4, 5, 23, 6, 24}, {4, 5, 6, 20, 7, 21, 8, 22}, {20, 4, 5, 21, 6, 22, 7, 23}, {4, 20, 5, 21, 6, 22, 7, 23}, {20, 21, 4, 22, 5, 23, 6, 24}, {4, 5, 20, 21, 6, 22, 7, 23}, {20, 4, 21, 22, 5, 23, 6, 24}, {4, 20, 21, 22, 5, 23, 6, 24}, {20, 21, 22, 23, 4, 24, 5, 25}, {4, 5, 6, 7, 20, 21, 8, 22}, {20, 4, 5, 6, 21, 22, 7, 23}, {4, 20, 5, 6, 21, 22, 7, 23}, {20, 21, 4, 5, 22, 23, 6, 24}, {4, 5, 20, 6, 21, 22, 7, 23}, {20, 4, 21, 5, 22, 23, 6, 24}, {4, 20, 21, 5, 22, 23, 6, 24}, {20, 21, 22, 4, 23, 24, 5, 25}, {4, 5, 6, 20, 21, 22, 7, 23}, {20, 4, 5, 21, 22, 23, 6, 24}, {4, 20, 5, 21, 22, 23, 6, 24}, {20, 21, 4, 22, 23, 24, 5, 25}, {4, 5, 20, 21, 22, 23, 6, 24}, {20, 4, 21, 22, 23, 24, 5, 25}, {4, 20, 21, 22, 23, 24, 5, 25}, {20, 21, 22, 23, 24, 25, 4, 26}, {4, 5, 6, 7, 8, 9, 20, 21}, {20, 4, 5, 6, 7, 8, 21, 22}, {4, 20, 5, 6, 7, 8, 21, 22}, {20, 21, 4, 5, 6, 7, 22, 23}, {4, 5, 20, 6, 7, 8, 21, 22}, {20, 4, 21, 5, 6, 7, 22, 23}, {4, 20, 21, 5, 6, 7, 22, 23}, {20, 21, 22, 4, 5, 6, 23, 24}, {4, 5, 6, 20, 7, 8, 21, 22}, {20, 4, 5, 21, 6, 7, 22, 23}, {4, 20, 5, 21, 6, 7, 22, 23}, {20, 21, 4, 22, 5, 6, 23, 24}, {4, 5, 20, 21, 6, 7, 22, 23}, {20, 4, 21, 22, 5, 6, 23, 24}, {4, 20, 21, 22, 5, 6, 23, 24}, {20, 21, 22, 23, 4, 5, 24, 25}, {4, 5, 6, 7, 20, 8, 21, 22}, {20, 4, 5, 6, 21, 7, 22, 23}, {4, 20, 5, 6, 21, 7, 22, 23}, {20, 21, 4, 5, 22, 6, 23, 24}, {4, 5, 20, 6, 21, 7, 22, 23}, {20, 4, 21, 5, 22, 6, 23, 24}, {4, 20, 21, 5, 22, 6, 23, 24}, {20, 21, 22, 4, 23, 5, 24, 25}, {4, 5, 6, 20, 21, 7, 22, 23}, {20, 4, 5, 21, 22, 6, 23, 24}, {4, 20, 5, 21, 22, 6, 23, 24}, {20, 21, 4, 22, 23, 5, 24, 25}, {4, 5, 20, 21, 22, 6, 23, 24}, {20, 4, 21, 22, 23, 5, 24, 25}, {4, 20, 21, 22, 23, 5, 24, 25}, {20, 21, 22, 23, 24, 4, 25, 26}, {4, 5, 6, 7, 8, 20, 21, 22}, {20, 4, 5, 6, 7, 21, 22, 23}, {4, 20, 5, 6, 7, 21, 22, 23}, {20, 21, 4, 5, 6, 22, 23, 24}, {4, 5, 20, 6, 7, 21, 22, 23}, {20, 4, 21, 5, 6, 22, 23, 24}, {4, 20, 21, 5, 6, 22, 23, 24}, {20, 21, 22, 4, 5, 23, 24, 25}, {4, 5, 6, 20, 7, 21, 22, 23}, {20, 4, 5, 21, 6, 22, 23, 24}, {4, 20, 5, 21, 6, 22, 23, 24}, {20, 21, 4, 22, 5, 23, 24, 25}, {4, 5, 20, 21, 6, 22, 23, 24}, {20, 4, 21, 22, 5, 23, 24, 25}, {4, 20, 21, 22, 5, 23, 24, 25}, {20, 21, 22, 23, 4, 24, 25, 26}, {4, 5, 6, 7, 20, 21, 22, 23}, {20, 4, 5, 6, 21, 22, 23, 24}, {4, 20, 5, 6, 21, 22, 23, 24}, {20, 21, 4, 5, 22, 23, 24, 25}, {4, 5, 20, 6, 21, 22, 23, 24}, {20, 4, 21, 5, 22, 23, 24, 25}, {4, 20, 21, 5, 22, 23, 24, 25}, {20, 21, 22, 4, 23, 24, 25, 26}, {4, 5, 6, 20, 21, 22, 23, 24}, {20, 4, 5, 21, 22, 23, 24, 25}, {4, 20, 5, 21, 22, 23, 24, 25}, {20, 21, 4, 22, 23, 24, 25, 26}, {4, 5, 20, 21, 22, 23, 24, 25}, {20, 4, 21, 22, 23, 24, 25, 26}, {4, 20, 21, 22, 23, 24, 25, 26}, {20, 21, 22, 23, 24, 25, 26, 27}, }, { {3, 4, 5, 6, 7, 8, 9, 10}, {21, 3, 4, 5, 6, 7, 8, 9}, {3, 21, 4, 5, 6, 7, 8, 9}, {21, 22, 3, 4, 5, 6, 7, 8}, {3, 4, 21, 5, 6, 7, 8, 9}, {21, 3, 22, 4, 5, 6, 7, 8}, {3, 21, 22, 4, 5, 6, 7, 8}, {21, 22, 23, 3, 4, 5, 6, 7}, {3, 4, 5, 21, 6, 7, 8, 9}, {21, 3, 4, 22, 5, 6, 7, 8}, {3, 21, 4, 22, 5, 6, 7, 8}, {21, 22, 3, 23, 4, 5, 6, 7}, {3, 4, 21, 22, 5, 6, 7, 8}, {21, 3, 22, 23, 4, 5, 6, 7}, {3, 21, 22, 23, 4, 5, 6, 7}, {21, 22, 23, 24, 3, 4, 5, 6}, {3, 4, 5, 6, 21, 7, 8, 9}, {21, 3, 4, 5, 22, 6, 7, 8}, {3, 21, 4, 5, 22, 6, 7, 8}, {21, 22, 3, 4, 23, 5, 6, 7}, {3, 4, 21, 5, 22, 6, 7, 8}, {21, 3, 22, 4, 23, 5, 6, 7}, {3, 21, 22, 4, 23, 5, 6, 7}, {21, 22, 23, 3, 24, 4, 5, 6}, {3, 4, 5, 21, 22, 6, 7, 8}, {21, 3, 4, 22, 23, 5, 6, 7}, {3, 21, 4, 22, 23, 5, 6, 7}, {21, 22, 3, 23, 24, 4, 5, 6}, {3, 4, 21, 22, 23, 5, 6, 7}, {21, 3, 22, 23, 24, 4, 5, 6}, {3, 21, 22, 23, 24, 4, 5, 6}, {21, 22, 23, 24, 25, 3, 4, 5}, {3, 4, 5, 6, 7, 21, 8, 9}, {21, 3, 4, 5, 6, 22, 7, 8}, {3, 21, 4, 5, 6, 22, 7, 8}, {21, 22, 3, 4, 5, 23, 6, 7}, {3, 4, 21, 5, 6, 22, 7, 8}, {21, 3, 22, 4, 5, 23, 6, 7}, {3, 21, 22, 4, 5, 23, 6, 7}, {21, 22, 23, 3, 4, 24, 5, 6}, {3, 4, 5, 21, 6, 22, 7, 8}, {21, 3, 4, 22, 5, 23, 6, 7}, {3, 21, 4, 22, 5, 23, 6, 7}, {21, 22, 3, 23, 4, 24, 5, 6}, {3, 4, 21, 22, 5, 23, 6, 7}, {21, 3, 22, 23, 4, 24, 5, 6}, {3, 21, 22, 23, 4, 24, 5, 6}, {21, 22, 23, 24, 3, 25, 4, 5}, {3, 4, 5, 6, 21, 22, 7, 8}, {21, 3, 4, 5, 22, 23, 6, 7}, {3, 21, 4, 5, 22, 23, 6, 7}, {21, 22, 3, 4, 23, 24, 5, 6}, {3, 4, 21, 5, 22, 23, 6, 7}, {21, 3, 22, 4, 23, 24, 5, 6}, {3, 21, 22, 4, 23, 24, 5, 6}, {21, 22, 23, 3, 24, 25, 4, 5}, {3, 4, 5, 21, 22, 23, 6, 7}, {21, 3, 4, 22, 23, 24, 5, 6}, {3, 21, 4, 22, 23, 24, 5, 6}, {21, 22, 3, 23, 24, 25, 4, 5}, {3, 4, 21, 22, 23, 24, 5, 6}, {21, 3, 22, 23, 24, 25, 4, 5}, {3, 21, 22, 23, 24, 25, 4, 5}, {21, 22, 23, 24, 25, 26, 3, 4}, {3, 4, 5, 6, 7, 8, 21, 9}, {21, 3, 4, 5, 6, 7, 22, 8}, {3, 21, 4, 5, 6, 7, 22, 8}, {21, 22, 3, 4, 5, 6, 23, 7}, {3, 4, 21, 5, 6, 7, 22, 8}, {21, 3, 22, 4, 5, 6, 23, 7}, {3, 21, 22, 4, 5, 6, 23, 7}, {21, 22, 23, 3, 4, 5, 24, 6}, {3, 4, 5, 21, 6, 7, 22, 8}, {21, 3, 4, 22, 5, 6, 23, 7}, {3, 21, 4, 22, 5, 6, 23, 7}, {21, 22, 3, 23, 4, 5, 24, 6}, {3, 4, 21, 22, 5, 6, 23, 7}, {21, 3, 22, 23, 4, 5, 24, 6}, {3, 21, 22, 23, 4, 5, 24, 6}, {21, 22, 23, 24, 3, 4, 25, 5}, {3, 4, 5, 6, 21, 7, 22, 8}, {21, 3, 4, 5, 22, 6, 23, 7}, {3, 21, 4, 5, 22, 6, 23, 7}, {21, 22, 3, 4, 23, 5, 24, 6}, {3, 4, 21, 5, 22, 6, 23, 7}, {21, 3, 22, 4, 23, 5, 24, 6}, {3, 21, 22, 4, 23, 5, 24, 6}, {21, 22, 23, 3, 24, 4, 25, 5}, {3, 4, 5, 21, 22, 6, 23, 7}, {21, 3, 4, 22, 23, 5, 24, 6}, {3, 21, 4, 22, 23, 5, 24, 6}, {21, 22, 3, 23, 24, 4, 25, 5}, {3, 4, 21, 22, 23, 5, 24, 6}, {21, 3, 22, 23, 24, 4, 25, 5}, {3, 21, 22, 23, 24, 4, 25, 5}, {21, 22, 23, 24, 25, 3, 26, 4}, {3, 4, 5, 6, 7, 21, 22, 8}, {21, 3, 4, 5, 6, 22, 23, 7}, {3, 21, 4, 5, 6, 22, 23, 7}, {21, 22, 3, 4, 5, 23, 24, 6}, {3, 4, 21, 5, 6, 22, 23, 7}, {21, 3, 22, 4, 5, 23, 24, 6}, {3, 21, 22, 4, 5, 23, 24, 6}, {21, 22, 23, 3, 4, 24, 25, 5}, {3, 4, 5, 21, 6, 22, 23, 7}, {21, 3, 4, 22, 5, 23, 24, 6}, {3, 21, 4, 22, 5, 23, 24, 6}, {21, 22, 3, 23, 4, 24, 25, 5}, {3, 4, 21, 22, 5, 23, 24, 6}, {21, 3, 22, 23, 4, 24, 25, 5}, {3, 21, 22, 23, 4, 24, 25, 5}, {21, 22, 23, 24, 3, 25, 26, 4}, {3, 4, 5, 6, 21, 22, 23, 7}, {21, 3, 4, 5, 22, 23, 24, 6}, {3, 21, 4, 5, 22, 23, 24, 6}, {21, 22, 3, 4, 23, 24, 25, 5}, {3, 4, 21, 5, 22, 23, 24, 6}, {21, 3, 22, 4, 23, 24, 25, 5}, {3, 21, 22, 4, 23, 24, 25, 5}, {21, 22, 23, 3, 24, 25, 26, 4}, {3, 4, 5, 21, 22, 23, 24, 6}, {21, 3, 4, 22, 23, 24, 25, 5}, {3, 21, 4, 22, 23, 24, 25, 5}, {21, 22, 3, 23, 24, 25, 26, 4}, {3, 4, 21, 22, 23, 24, 25, 5}, {21, 3, 22, 23, 24, 25, 26, 4}, {3, 21, 22, 23, 24, 25, 26, 4}, {21, 22, 23, 24, 25, 26, 27, 3}, {3, 4, 5, 6, 7, 8, 9, 21}, {21, 3, 4, 5, 6, 7, 8, 22}, {3, 21, 4, 5, 6, 7, 8, 22}, {21, 22, 3, 4, 5, 6, 7, 23}, {3, 4, 21, 5, 6, 7, 8, 22}, {21, 3, 22, 4, 5, 6, 7, 23}, {3, 21, 22, 4, 5, 6, 7, 23}, {21, 22, 23, 3, 4, 5, 6, 24}, {3, 4, 5, 21, 6, 7, 8, 22}, {21, 3, 4, 22, 5, 6, 7, 23}, {3, 21, 4, 22, 5, 6, 7, 23}, {21, 22, 3, 23, 4, 5, 6, 24}, {3, 4, 21, 22, 5, 6, 7, 23}, {21, 3, 22, 23, 4, 5, 6, 24}, {3, 21, 22, 23, 4, 5, 6, 24}, {21, 22, 23, 24, 3, 4, 5, 25}, {3, 4, 5, 6, 21, 7, 8, 22}, {21, 3, 4, 5, 22, 6, 7, 23}, {3, 21, 4, 5, 22, 6, 7, 23}, {21, 22, 3, 4, 23, 5, 6, 24}, {3, 4, 21, 5, 22, 6, 7, 23}, {21, 3, 22, 4, 23, 5, 6, 24}, {3, 21, 22, 4, 23, 5, 6, 24}, {21, 22, 23, 3, 24, 4, 5, 25}, {3, 4, 5, 21, 22, 6, 7, 23}, {21, 3, 4, 22, 23, 5, 6, 24}, {3, 21, 4, 22, 23, 5, 6, 24}, {21, 22, 3, 23, 24, 4, 5, 25}, {3, 4, 21, 22, 23, 5, 6, 24}, {21, 3, 22, 23, 24, 4, 5, 25}, {3, 21, 22, 23, 24, 4, 5, 25}, {21, 22, 23, 24, 25, 3, 4, 26}, {3, 4, 5, 6, 7, 21, 8, 22}, {21, 3, 4, 5, 6, 22, 7, 23}, {3, 21, 4, 5, 6, 22, 7, 23}, {21, 22, 3, 4, 5, 23, 6, 24}, {3, 4, 21, 5, 6, 22, 7, 23}, {21, 3, 22, 4, 5, 23, 6, 24}, {3, 21, 22, 4, 5, 23, 6, 24}, {21, 22, 23, 3, 4, 24, 5, 25}, {3, 4, 5, 21, 6, 22, 7, 23}, {21, 3, 4, 22, 5, 23, 6, 24}, {3, 21, 4, 22, 5, 23, 6, 24}, {21, 22, 3, 23, 4, 24, 5, 25}, {3, 4, 21, 22, 5, 23, 6, 24}, {21, 3, 22, 23, 4, 24, 5, 25}, {3, 21, 22, 23, 4, 24, 5, 25}, {21, 22, 23, 24, 3, 25, 4, 26}, {3, 4, 5, 6, 21, 22, 7, 23}, {21, 3, 4, 5, 22, 23, 6, 24}, {3, 21, 4, 5, 22, 23, 6, 24}, {21, 22, 3, 4, 23, 24, 5, 25}, {3, 4, 21, 5, 22, 23, 6, 24}, {21, 3, 22, 4, 23, 24, 5, 25}, {3, 21, 22, 4, 23, 24, 5, 25}, {21, 22, 23, 3, 24, 25, 4, 26}, {3, 4, 5, 21, 22, 23, 6, 24}, {21, 3, 4, 22, 23, 24, 5, 25}, {3, 21, 4, 22, 23, 24, 5, 25}, {21, 22, 3, 23, 24, 25, 4, 26}, {3, 4, 21, 22, 23, 24, 5, 25}, {21, 3, 22, 23, 24, 25, 4, 26}, {3, 21, 22, 23, 24, 25, 4, 26}, {21, 22, 23, 24, 25, 26, 3, 27}, {3, 4, 5, 6, 7, 8, 21, 22}, {21, 3, 4, 5, 6, 7, 22, 23}, {3, 21, 4, 5, 6, 7, 22, 23}, {21, 22, 3, 4, 5, 6, 23, 24}, {3, 4, 21, 5, 6, 7, 22, 23}, {21, 3, 22, 4, 5, 6, 23, 24}, {3, 21, 22, 4, 5, 6, 23, 24}, {21, 22, 23, 3, 4, 5, 24, 25}, {3, 4, 5, 21, 6, 7, 22, 23}, {21, 3, 4, 22, 5, 6, 23, 24}, {3, 21, 4, 22, 5, 6, 23, 24}, {21, 22, 3, 23, 4, 5, 24, 25}, {3, 4, 21, 22, 5, 6, 23, 24}, {21, 3, 22, 23, 4, 5, 24, 25}, {3, 21, 22, 23, 4, 5, 24, 25}, {21, 22, 23, 24, 3, 4, 25, 26}, {3, 4, 5, 6, 21, 7, 22, 23}, {21, 3, 4, 5, 22, 6, 23, 24}, {3, 21, 4, 5, 22, 6, 23, 24}, {21, 22, 3, 4, 23, 5, 24, 25}, {3, 4, 21, 5, 22, 6, 23, 24}, {21, 3, 22, 4, 23, 5, 24, 25}, {3, 21, 22, 4, 23, 5, 24, 25}, {21, 22, 23, 3, 24, 4, 25, 26}, {3, 4, 5, 21, 22, 6, 23, 24}, {21, 3, 4, 22, 23, 5, 24, 25}, {3, 21, 4, 22, 23, 5, 24, 25}, {21, 22, 3, 23, 24, 4, 25, 26}, {3, 4, 21, 22, 23, 5, 24, 25}, {21, 3, 22, 23, 24, 4, 25, 26}, {3, 21, 22, 23, 24, 4, 25, 26}, {21, 22, 23, 24, 25, 3, 26, 27}, {3, 4, 5, 6, 7, 21, 22, 23}, {21, 3, 4, 5, 6, 22, 23, 24}, {3, 21, 4, 5, 6, 22, 23, 24}, {21, 22, 3, 4, 5, 23, 24, 25}, {3, 4, 21, 5, 6, 22, 23, 24}, {21, 3, 22, 4, 5, 23, 24, 25}, {3, 21, 22, 4, 5, 23, 24, 25}, {21, 22, 23, 3, 4, 24, 25, 26}, {3, 4, 5, 21, 6, 22, 23, 24}, {21, 3, 4, 22, 5, 23, 24, 25}, {3, 21, 4, 22, 5, 23, 24, 25}, {21, 22, 3, 23, 4, 24, 25, 26}, {3, 4, 21, 22, 5, 23, 24, 25}, {21, 3, 22, 23, 4, 24, 25, 26}, {3, 21, 22, 23, 4, 24, 25, 26}, {21, 22, 23, 24, 3, 25, 26, 27}, {3, 4, 5, 6, 21, 22, 23, 24}, {21, 3, 4, 5, 22, 23, 24, 25}, {3, 21, 4, 5, 22, 23, 24, 25}, {21, 22, 3, 4, 23, 24, 25, 26}, {3, 4, 21, 5, 22, 23, 24, 25}, {21, 3, 22, 4, 23, 24, 25, 26}, {3, 21, 22, 4, 23, 24, 25, 26}, {21, 22, 23, 3, 24, 25, 26, 27}, {3, 4, 5, 21, 22, 23, 24, 25}, {21, 3, 4, 22, 23, 24, 25, 26}, {3, 21, 4, 22, 23, 24, 25, 26}, {21, 22, 3, 23, 24, 25, 26, 27}, {3, 4, 21, 22, 23, 24, 25, 26}, {21, 3, 22, 23, 24, 25, 26, 27}, {3, 21, 22, 23, 24, 25, 26, 27}, {21, 22, 23, 24, 25, 26, 27, 28}, }, { {2, 3, 4, 5, 6, 7, 8, 9}, {22, 2, 3, 4, 5, 6, 7, 8}, {2, 22, 3, 4, 5, 6, 7, 8}, {22, 23, 2, 3, 4, 5, 6, 7}, {2, 3, 22, 4, 5, 6, 7, 8}, {22, 2, 23, 3, 4, 5, 6, 7}, {2, 22, 23, 3, 4, 5, 6, 7}, {22, 23, 24, 2, 3, 4, 5, 6}, {2, 3, 4, 22, 5, 6, 7, 8}, {22, 2, 3, 23, 4, 5, 6, 7}, {2, 22, 3, 23, 4, 5, 6, 7}, {22, 23, 2, 24, 3, 4, 5, 6}, {2, 3, 22, 23, 4, 5, 6, 7}, {22, 2, 23, 24, 3, 4, 5, 6}, {2, 22, 23, 24, 3, 4, 5, 6}, {22, 23, 24, 25, 2, 3, 4, 5}, {2, 3, 4, 5, 22, 6, 7, 8}, {22, 2, 3, 4, 23, 5, 6, 7}, {2, 22, 3, 4, 23, 5, 6, 7}, {22, 23, 2, 3, 24, 4, 5, 6}, {2, 3, 22, 4, 23, 5, 6, 7}, {22, 2, 23, 3, 24, 4, 5, 6}, {2, 22, 23, 3, 24, 4, 5, 6}, {22, 23, 24, 2, 25, 3, 4, 5}, {2, 3, 4, 22, 23, 5, 6, 7}, {22, 2, 3, 23, 24, 4, 5, 6}, {2, 22, 3, 23, 24, 4, 5, 6}, {22, 23, 2, 24, 25, 3, 4, 5}, {2, 3, 22, 23, 24, 4, 5, 6}, {22, 2, 23, 24, 25, 3, 4, 5}, {2, 22, 23, 24, 25, 3, 4, 5}, {22, 23, 24, 25, 26, 2, 3, 4}, {2, 3, 4, 5, 6, 22, 7, 8}, {22, 2, 3, 4, 5, 23, 6, 7}, {2, 22, 3, 4, 5, 23, 6, 7}, {22, 23, 2, 3, 4, 24, 5, 6}, {2, 3, 22, 4, 5, 23, 6, 7}, {22, 2, 23, 3, 4, 24, 5, 6}, {2, 22, 23, 3, 4, 24, 5, 6}, {22, 23, 24, 2, 3, 25, 4, 5}, {2, 3, 4, 22, 5, 23, 6, 7}, {22, 2, 3, 23, 4, 24, 5, 6}, {2, 22, 3, 23, 4, 24, 5, 6}, {22, 23, 2, 24, 3, 25, 4, 5}, {2, 3, 22, 23, 4, 24, 5, 6}, {22, 2, 23, 24, 3, 25, 4, 5}, {2, 22, 23, 24, 3, 25, 4, 5}, {22, 23, 24, 25, 2, 26, 3, 4}, {2, 3, 4, 5, 22, 23, 6, 7}, {22, 2, 3, 4, 23, 24, 5, 6}, {2, 22, 3, 4, 23, 24, 5, 6}, {22, 23, 2, 3, 24, 25, 4, 5}, {2, 3, 22, 4, 23, 24, 5, 6}, {22, 2, 23, 3, 24, 25, 4, 5}, {2, 22, 23, 3, 24, 25, 4, 5}, {22, 23, 24, 2, 25, 26, 3, 4}, {2, 3, 4, 22, 23, 24, 5, 6}, {22, 2, 3, 23, 24, 25, 4, 5}, {2, 22, 3, 23, 24, 25, 4, 5}, {22, 23, 2, 24, 25, 26, 3, 4}, {2, 3, 22, 23, 24, 25, 4, 5}, {22, 2, 23, 24, 25, 26, 3, 4}, {2, 22, 23, 24, 25, 26, 3, 4}, {22, 23, 24, 25, 26, 27, 2, 3}, {2, 3, 4, 5, 6, 7, 22, 8}, {22, 2, 3, 4, 5, 6, 23, 7}, {2, 22, 3, 4, 5, 6, 23, 7}, {22, 23, 2, 3, 4, 5, 24, 6}, {2, 3, 22, 4, 5, 6, 23, 7}, {22, 2, 23, 3, 4, 5, 24, 6}, {2, 22, 23, 3, 4, 5, 24, 6}, {22, 23, 24, 2, 3, 4, 25, 5}, {2, 3, 4, 22, 5, 6, 23, 7}, {22, 2, 3, 23, 4, 5, 24, 6}, {2, 22, 3, 23, 4, 5, 24, 6}, {22, 23, 2, 24, 3, 4, 25, 5}, {2, 3, 22, 23, 4, 5, 24, 6}, {22, 2, 23, 24, 3, 4, 25, 5}, {2, 22, 23, 24, 3, 4, 25, 5}, {22, 23, 24, 25, 2, 3, 26, 4}, {2, 3, 4, 5, 22, 6, 23, 7}, {22, 2, 3, 4, 23, 5, 24, 6}, {2, 22, 3, 4, 23, 5, 24, 6}, {22, 23, 2, 3, 24, 4, 25, 5}, {2, 3, 22, 4, 23, 5, 24, 6}, {22, 2, 23, 3, 24, 4, 25, 5}, {2, 22, 23, 3, 24, 4, 25, 5}, {22, 23, 24, 2, 25, 3, 26, 4}, {2, 3, 4, 22, 23, 5, 24, 6}, {22, 2, 3, 23, 24, 4, 25, 5}, {2, 22, 3, 23, 24, 4, 25, 5}, {22, 23, 2, 24, 25, 3, 26, 4}, {2, 3, 22, 23, 24, 4, 25, 5}, {22, 2, 23, 24, 25, 3, 26, 4}, {2, 22, 23, 24, 25, 3, 26, 4}, {22, 23, 24, 25, 26, 2, 27, 3}, {2, 3, 4, 5, 6, 22, 23, 7}, {22, 2, 3, 4, 5, 23, 24, 6}, {2, 22, 3, 4, 5, 23, 24, 6}, {22, 23, 2, 3, 4, 24, 25, 5}, {2, 3, 22, 4, 5, 23, 24, 6}, {22, 2, 23, 3, 4, 24, 25, 5}, {2, 22, 23, 3, 4, 24, 25, 5}, {22, 23, 24, 2, 3, 25, 26, 4}, {2, 3, 4, 22, 5, 23, 24, 6}, {22, 2, 3, 23, 4, 24, 25, 5}, {2, 22, 3, 23, 4, 24, 25, 5}, {22, 23, 2, 24, 3, 25, 26, 4}, {2, 3, 22, 23, 4, 24, 25, 5}, {22, 2, 23, 24, 3, 25, 26, 4}, {2, 22, 23, 24, 3, 25, 26, 4}, {22, 23, 24, 25, 2, 26, 27, 3}, {2, 3, 4, 5, 22, 23, 24, 6}, {22, 2, 3, 4, 23, 24, 25, 5}, {2, 22, 3, 4, 23, 24, 25, 5}, {22, 23, 2, 3, 24, 25, 26, 4}, {2, 3, 22, 4, 23, 24, 25, 5}, {22, 2, 23, 3, 24, 25, 26, 4}, {2, 22, 23, 3, 24, 25, 26, 4}, {22, 23, 24, 2, 25, 26, 27, 3}, {2, 3, 4, 22, 23, 24, 25, 5}, {22, 2, 3, 23, 24, 25, 26, 4}, {2, 22, 3, 23, 24, 25, 26, 4}, {22, 23, 2, 24, 25, 26, 27, 3}, {2, 3, 22, 23, 24, 25, 26, 4}, {22, 2, 23, 24, 25, 26, 27, 3}, {2, 22, 23, 24, 25, 26, 27, 3}, {22, 23, 24, 25, 26, 27, 28, 2}, {2, 3, 4, 5, 6, 7, 8, 22}, {22, 2, 3, 4, 5, 6, 7, 23}, {2, 22, 3, 4, 5, 6, 7, 23}, {22, 23, 2, 3, 4, 5, 6, 24}, {2, 3, 22, 4, 5, 6, 7, 23}, {22, 2, 23, 3, 4, 5, 6, 24}, {2, 22, 23, 3, 4, 5, 6, 24}, {22, 23, 24, 2, 3, 4, 5, 25}, {2, 3, 4, 22, 5, 6, 7, 23}, {22, 2, 3, 23, 4, 5, 6, 24}, {2, 22, 3, 23, 4, 5, 6, 24}, {22, 23, 2, 24, 3, 4, 5, 25}, {2, 3, 22, 23, 4, 5, 6, 24}, {22, 2, 23, 24, 3, 4, 5, 25}, {2, 22, 23, 24, 3, 4, 5, 25}, {22, 23, 24, 25, 2, 3, 4, 26}, {2, 3, 4, 5, 22, 6, 7, 23}, {22, 2, 3, 4, 23, 5, 6, 24}, {2, 22, 3, 4, 23, 5, 6, 24}, {22, 23, 2, 3, 24, 4, 5, 25}, {2, 3, 22, 4, 23, 5, 6, 24}, {22, 2, 23, 3, 24, 4, 5, 25}, {2, 22, 23, 3, 24, 4, 5, 25}, {22, 23, 24, 2, 25, 3, 4, 26}, {2, 3, 4, 22, 23, 5, 6, 24}, {22, 2, 3, 23, 24, 4, 5, 25}, {2, 22, 3, 23, 24, 4, 5, 25}, {22, 23, 2, 24, 25, 3, 4, 26}, {2, 3, 22, 23, 24, 4, 5, 25}, {22, 2, 23, 24, 25, 3, 4, 26}, {2, 22, 23, 24, 25, 3, 4, 26}, {22, 23, 24, 25, 26, 2, 3, 27}, {2, 3, 4, 5, 6, 22, 7, 23}, {22, 2, 3, 4, 5, 23, 6, 24}, {2, 22, 3, 4, 5, 23, 6, 24}, {22, 23, 2, 3, 4, 24, 5, 25}, {2, 3, 22, 4, 5, 23, 6, 24}, {22, 2, 23, 3, 4, 24, 5, 25}, {2, 22, 23, 3, 4, 24, 5, 25}, {22, 23, 24, 2, 3, 25, 4, 26}, {2, 3, 4, 22, 5, 23, 6, 24}, {22, 2, 3, 23, 4, 24, 5, 25}, {2, 22, 3, 23, 4, 24, 5, 25}, {22, 23, 2, 24, 3, 25, 4, 26}, {2, 3, 22, 23, 4, 24, 5, 25}, {22, 2, 23, 24, 3, 25, 4, 26}, {2, 22, 23, 24, 3, 25, 4, 26}, {22, 23, 24, 25, 2, 26, 3, 27}, {2, 3, 4, 5, 22, 23, 6, 24}, {22, 2, 3, 4, 23, 24, 5, 25}, {2, 22, 3, 4, 23, 24, 5, 25}, {22, 23, 2, 3, 24, 25, 4, 26}, {2, 3, 22, 4, 23, 24, 5, 25}, {22, 2, 23, 3, 24, 25, 4, 26}, {2, 22, 23, 3, 24, 25, 4, 26}, {22, 23, 24, 2, 25, 26, 3, 27}, {2, 3, 4, 22, 23, 24, 5, 25}, {22, 2, 3, 23, 24, 25, 4, 26}, {2, 22, 3, 23, 24, 25, 4, 26}, {22, 23, 2, 24, 25, 26, 3, 27}, {2, 3, 22, 23, 24, 25, 4, 26}, {22, 2, 23, 24, 25, 26, 3, 27}, {2, 22, 23, 24, 25, 26, 3, 27}, {22, 23, 24, 25, 26, 27, 2, 28}, {2, 3, 4, 5, 6, 7, 22, 23}, {22, 2, 3, 4, 5, 6, 23, 24}, {2, 22, 3, 4, 5, 6, 23, 24}, {22, 23, 2, 3, 4, 5, 24, 25}, {2, 3, 22, 4, 5, 6, 23, 24}, {22, 2, 23, 3, 4, 5, 24, 25}, {2, 22, 23, 3, 4, 5, 24, 25}, {22, 23, 24, 2, 3, 4, 25, 26}, {2, 3, 4, 22, 5, 6, 23, 24}, {22, 2, 3, 23, 4, 5, 24, 25}, {2, 22, 3, 23, 4, 5, 24, 25}, {22, 23, 2, 24, 3, 4, 25, 26}, {2, 3, 22, 23, 4, 5, 24, 25}, {22, 2, 23, 24, 3, 4, 25, 26}, {2, 22, 23, 24, 3, 4, 25, 26}, {22, 23, 24, 25, 2, 3, 26, 27}, {2, 3, 4, 5, 22, 6, 23, 24}, {22, 2, 3, 4, 23, 5, 24, 25}, {2, 22, 3, 4, 23, 5, 24, 25}, {22, 23, 2, 3, 24, 4, 25, 26}, {2, 3, 22, 4, 23, 5, 24, 25}, {22, 2, 23, 3, 24, 4, 25, 26}, {2, 22, 23, 3, 24, 4, 25, 26}, {22, 23, 24, 2, 25, 3, 26, 27}, {2, 3, 4, 22, 23, 5, 24, 25}, {22, 2, 3, 23, 24, 4, 25, 26}, {2, 22, 3, 23, 24, 4, 25, 26}, {22, 23, 2, 24, 25, 3, 26, 27}, {2, 3, 22, 23, 24, 4, 25, 26}, {22, 2, 23, 24, 25, 3, 26, 27}, {2, 22, 23, 24, 25, 3, 26, 27}, {22, 23, 24, 25, 26, 2, 27, 28}, {2, 3, 4, 5, 6, 22, 23, 24}, {22, 2, 3, 4, 5, 23, 24, 25}, {2, 22, 3, 4, 5, 23, 24, 25}, {22, 23, 2, 3, 4, 24, 25, 26}, {2, 3, 22, 4, 5, 23, 24, 25}, {22, 2, 23, 3, 4, 24, 25, 26}, {2, 22, 23, 3, 4, 24, 25, 26}, {22, 23, 24, 2, 3, 25, 26, 27}, {2, 3, 4, 22, 5, 23, 24, 25}, {22, 2, 3, 23, 4, 24, 25, 26}, {2, 22, 3, 23, 4, 24, 25, 26}, {22, 23, 2, 24, 3, 25, 26, 27}, {2, 3, 22, 23, 4, 24, 25, 26}, {22, 2, 23, 24, 3, 25, 26, 27}, {2, 22, 23, 24, 3, 25, 26, 27}, {22, 23, 24, 25, 2, 26, 27, 28}, {2, 3, 4, 5, 22, 23, 24, 25}, {22, 2, 3, 4, 23, 24, 25, 26}, {2, 22, 3, 4, 23, 24, 25, 26}, {22, 23, 2, 3, 24, 25, 26, 27}, {2, 3, 22, 4, 23, 24, 25, 26}, {22, 2, 23, 3, 24, 25, 26, 27}, {2, 22, 23, 3, 24, 25, 26, 27}, {22, 23, 24, 2, 25, 26, 27, 28}, {2, 3, 4, 22, 23, 24, 25, 26}, {22, 2, 3, 23, 24, 25, 26, 27}, {2, 22, 3, 23, 24, 25, 26, 27}, {22, 23, 2, 24, 25, 26, 27, 28}, {2, 3, 22, 23, 24, 25, 26, 27}, {22, 2, 23, 24, 25, 26, 27, 28}, {2, 22, 23, 24, 25, 26, 27, 28}, {22, 23, 24, 25, 26, 27, 28, 29}, }, { {1, 2, 3, 4, 5, 6, 7, 8}, {23, 1, 2, 3, 4, 5, 6, 7}, {1, 23, 2, 3, 4, 5, 6, 7}, {23, 24, 1, 2, 3, 4, 5, 6}, {1, 2, 23, 3, 4, 5, 6, 7}, {23, 1, 24, 2, 3, 4, 5, 6}, {1, 23, 24, 2, 3, 4, 5, 6}, {23, 24, 25, 1, 2, 3, 4, 5}, {1, 2, 3, 23, 4, 5, 6, 7}, {23, 1, 2, 24, 3, 4, 5, 6}, {1, 23, 2, 24, 3, 4, 5, 6}, {23, 24, 1, 25, 2, 3, 4, 5}, {1, 2, 23, 24, 3, 4, 5, 6}, {23, 1, 24, 25, 2, 3, 4, 5}, {1, 23, 24, 25, 2, 3, 4, 5}, {23, 24, 25, 26, 1, 2, 3, 4}, {1, 2, 3, 4, 23, 5, 6, 7}, {23, 1, 2, 3, 24, 4, 5, 6}, {1, 23, 2, 3, 24, 4, 5, 6}, {23, 24, 1, 2, 25, 3, 4, 5}, {1, 2, 23, 3, 24, 4, 5, 6}, {23, 1, 24, 2, 25, 3, 4, 5}, {1, 23, 24, 2, 25, 3, 4, 5}, {23, 24, 25, 1, 26, 2, 3, 4}, {1, 2, 3, 23, 24, 4, 5, 6}, {23, 1, 2, 24, 25, 3, 4, 5}, {1, 23, 2, 24, 25, 3, 4, 5}, {23, 24, 1, 25, 26, 2, 3, 4}, {1, 2, 23, 24, 25, 3, 4, 5}, {23, 1, 24, 25, 26, 2, 3, 4}, {1, 23, 24, 25, 26, 2, 3, 4}, {23, 24, 25, 26, 27, 1, 2, 3}, {1, 2, 3, 4, 5, 23, 6, 7}, {23, 1, 2, 3, 4, 24, 5, 6}, {1, 23, 2, 3, 4, 24, 5, 6}, {23, 24, 1, 2, 3, 25, 4, 5}, {1, 2, 23, 3, 4, 24, 5, 6}, {23, 1, 24, 2, 3, 25, 4, 5}, {1, 23, 24, 2, 3, 25, 4, 5}, {23, 24, 25, 1, 2, 26, 3, 4}, {1, 2, 3, 23, 4, 24, 5, 6}, {23, 1, 2, 24, 3, 25, 4, 5}, {1, 23, 2, 24, 3, 25, 4, 5}, {23, 24, 1, 25, 2, 26, 3, 4}, {1, 2, 23, 24, 3, 25, 4, 5}, {23, 1, 24, 25, 2, 26, 3, 4}, {1, 23, 24, 25, 2, 26, 3, 4}, {23, 24, 25, 26, 1, 27, 2, 3}, {1, 2, 3, 4, 23, 24, 5, 6}, {23, 1, 2, 3, 24, 25, 4, 5}, {1, 23, 2, 3, 24, 25, 4, 5}, {23, 24, 1, 2, 25, 26, 3, 4}, {1, 2, 23, 3, 24, 25, 4, 5}, {23, 1, 24, 2, 25, 26, 3, 4}, {1, 23, 24, 2, 25, 26, 3, 4}, {23, 24, 25, 1, 26, 27, 2, 3}, {1, 2, 3, 23, 24, 25, 4, 5}, {23, 1, 2, 24, 25, 26, 3, 4}, {1, 23, 2, 24, 25, 26, 3, 4}, {23, 24, 1, 25, 26, 27, 2, 3}, {1, 2, 23, 24, 25, 26, 3, 4}, {23, 1, 24, 25, 26, 27, 2, 3}, {1, 23, 24, 25, 26, 27, 2, 3}, {23, 24, 25, 26, 27, 28, 1, 2}, {1, 2, 3, 4, 5, 6, 23, 7}, {23, 1, 2, 3, 4, 5, 24, 6}, {1, 23, 2, 3, 4, 5, 24, 6}, {23, 24, 1, 2, 3, 4, 25, 5}, {1, 2, 23, 3, 4, 5, 24, 6}, {23, 1, 24, 2, 3, 4, 25, 5}, {1, 23, 24, 2, 3, 4, 25, 5}, {23, 24, 25, 1, 2, 3, 26, 4}, {1, 2, 3, 23, 4, 5, 24, 6}, {23, 1, 2, 24, 3, 4, 25, 5}, {1, 23, 2, 24, 3, 4, 25, 5}, {23, 24, 1, 25, 2, 3, 26, 4}, {1, 2, 23, 24, 3, 4, 25, 5}, {23, 1, 24, 25, 2, 3, 26, 4}, {1, 23, 24, 25, 2, 3, 26, 4}, {23, 24, 25, 26, 1, 2, 27, 3}, {1, 2, 3, 4, 23, 5, 24, 6}, {23, 1, 2, 3, 24, 4, 25, 5}, {1, 23, 2, 3, 24, 4, 25, 5}, {23, 24, 1, 2, 25, 3, 26, 4}, {1, 2, 23, 3, 24, 4, 25, 5}, {23, 1, 24, 2, 25, 3, 26, 4}, {1, 23, 24, 2, 25, 3, 26, 4}, {23, 24, 25, 1, 26, 2, 27, 3}, {1, 2, 3, 23, 24, 4, 25, 5}, {23, 1, 2, 24, 25, 3, 26, 4}, {1, 23, 2, 24, 25, 3, 26, 4}, {23, 24, 1, 25, 26, 2, 27, 3}, {1, 2, 23, 24, 25, 3, 26, 4}, {23, 1, 24, 25, 26, 2, 27, 3}, {1, 23, 24, 25, 26, 2, 27, 3}, {23, 24, 25, 26, 27, 1, 28, 2}, {1, 2, 3, 4, 5, 23, 24, 6}, {23, 1, 2, 3, 4, 24, 25, 5}, {1, 23, 2, 3, 4, 24, 25, 5}, {23, 24, 1, 2, 3, 25, 26, 4}, {1, 2, 23, 3, 4, 24, 25, 5}, {23, 1, 24, 2, 3, 25, 26, 4}, {1, 23, 24, 2, 3, 25, 26, 4}, {23, 24, 25, 1, 2, 26, 27, 3}, {1, 2, 3, 23, 4, 24, 25, 5}, {23, 1, 2, 24, 3, 25, 26, 4}, {1, 23, 2, 24, 3, 25, 26, 4}, {23, 24, 1, 25, 2, 26, 27, 3}, {1, 2, 23, 24, 3, 25, 26, 4}, {23, 1, 24, 25, 2, 26, 27, 3}, {1, 23, 24, 25, 2, 26, 27, 3}, {23, 24, 25, 26, 1, 27, 28, 2}, {1, 2, 3, 4, 23, 24, 25, 5}, {23, 1, 2, 3, 24, 25, 26, 4}, {1, 23, 2, 3, 24, 25, 26, 4}, {23, 24, 1, 2, 25, 26, 27, 3}, {1, 2, 23, 3, 24, 25, 26, 4}, {23, 1, 24, 2, 25, 26, 27, 3}, {1, 23, 24, 2, 25, 26, 27, 3}, {23, 24, 25, 1, 26, 27, 28, 2}, {1, 2, 3, 23, 24, 25, 26, 4}, {23, 1, 2, 24, 25, 26, 27, 3}, {1, 23, 2, 24, 25, 26, 27, 3}, {23, 24, 1, 25, 26, 27, 28, 2}, {1, 2, 23, 24, 25, 26, 27, 3}, {23, 1, 24, 25, 26, 27, 28, 2}, {1, 23, 24, 25, 26, 27, 28, 2}, {23, 24, 25, 26, 27, 28, 29, 1}, {1, 2, 3, 4, 5, 6, 7, 23}, {23, 1, 2, 3, 4, 5, 6, 24}, {1, 23, 2, 3, 4, 5, 6, 24}, {23, 24, 1, 2, 3, 4, 5, 25}, {1, 2, 23, 3, 4, 5, 6, 24}, {23, 1, 24, 2, 3, 4, 5, 25}, {1, 23, 24, 2, 3, 4, 5, 25}, {23, 24, 25, 1, 2, 3, 4, 26}, {1, 2, 3, 23, 4, 5, 6, 24}, {23, 1, 2, 24, 3, 4, 5, 25}, {1, 23, 2, 24, 3, 4, 5, 25}, {23, 24, 1, 25, 2, 3, 4, 26}, {1, 2, 23, 24, 3, 4, 5, 25}, {23, 1, 24, 25, 2, 3, 4, 26}, {1, 23, 24, 25, 2, 3, 4, 26}, {23, 24, 25, 26, 1, 2, 3, 27}, {1, 2, 3, 4, 23, 5, 6, 24}, {23, 1, 2, 3, 24, 4, 5, 25}, {1, 23, 2, 3, 24, 4, 5, 25}, {23, 24, 1, 2, 25, 3, 4, 26}, {1, 2, 23, 3, 24, 4, 5, 25}, {23, 1, 24, 2, 25, 3, 4, 26}, {1, 23, 24, 2, 25, 3, 4, 26}, {23, 24, 25, 1, 26, 2, 3, 27}, {1, 2, 3, 23, 24, 4, 5, 25}, {23, 1, 2, 24, 25, 3, 4, 26}, {1, 23, 2, 24, 25, 3, 4, 26}, {23, 24, 1, 25, 26, 2, 3, 27}, {1, 2, 23, 24, 25, 3, 4, 26}, {23, 1, 24, 25, 26, 2, 3, 27}, {1, 23, 24, 25, 26, 2, 3, 27}, {23, 24, 25, 26, 27, 1, 2, 28}, {1, 2, 3, 4, 5, 23, 6, 24}, {23, 1, 2, 3, 4, 24, 5, 25}, {1, 23, 2, 3, 4, 24, 5, 25}, {23, 24, 1, 2, 3, 25, 4, 26}, {1, 2, 23, 3, 4, 24, 5, 25}, {23, 1, 24, 2, 3, 25, 4, 26}, {1, 23, 24, 2, 3, 25, 4, 26}, {23, 24, 25, 1, 2, 26, 3, 27}, {1, 2, 3, 23, 4, 24, 5, 25}, {23, 1, 2, 24, 3, 25, 4, 26}, {1, 23, 2, 24, 3, 25, 4, 26}, {23, 24, 1, 25, 2, 26, 3, 27}, {1, 2, 23, 24, 3, 25, 4, 26}, {23, 1, 24, 25, 2, 26, 3, 27}, {1, 23, 24, 25, 2, 26, 3, 27}, {23, 24, 25, 26, 1, 27, 2, 28}, {1, 2, 3, 4, 23, 24, 5, 25}, {23, 1, 2, 3, 24, 25, 4, 26}, {1, 23, 2, 3, 24, 25, 4, 26}, {23, 24, 1, 2, 25, 26, 3, 27}, {1, 2, 23, 3, 24, 25, 4, 26}, {23, 1, 24, 2, 25, 26, 3, 27}, {1, 23, 24, 2, 25, 26, 3, 27}, {23, 24, 25, 1, 26, 27, 2, 28}, {1, 2, 3, 23, 24, 25, 4, 26}, {23, 1, 2, 24, 25, 26, 3, 27}, {1, 23, 2, 24, 25, 26, 3, 27}, {23, 24, 1, 25, 26, 27, 2, 28}, {1, 2, 23, 24, 25, 26, 3, 27}, {23, 1, 24, 25, 26, 27, 2, 28}, {1, 23, 24, 25, 26, 27, 2, 28}, {23, 24, 25, 26, 27, 28, 1, 29}, {1, 2, 3, 4, 5, 6, 23, 24}, {23, 1, 2, 3, 4, 5, 24, 25}, {1, 23, 2, 3, 4, 5, 24, 25}, {23, 24, 1, 2, 3, 4, 25, 26}, {1, 2, 23, 3, 4, 5, 24, 25}, {23, 1, 24, 2, 3, 4, 25, 26}, {1, 23, 24, 2, 3, 4, 25, 26}, {23, 24, 25, 1, 2, 3, 26, 27}, {1, 2, 3, 23, 4, 5, 24, 25}, {23, 1, 2, 24, 3, 4, 25, 26}, {1, 23, 2, 24, 3, 4, 25, 26}, {23, 24, 1, 25, 2, 3, 26, 27}, {1, 2, 23, 24, 3, 4, 25, 26}, {23, 1, 24, 25, 2, 3, 26, 27}, {1, 23, 24, 25, 2, 3, 26, 27}, {23, 24, 25, 26, 1, 2, 27, 28}, {1, 2, 3, 4, 23, 5, 24, 25}, {23, 1, 2, 3, 24, 4, 25, 26}, {1, 23, 2, 3, 24, 4, 25, 26}, {23, 24, 1, 2, 25, 3, 26, 27}, {1, 2, 23, 3, 24, 4, 25, 26}, {23, 1, 24, 2, 25, 3, 26, 27}, {1, 23, 24, 2, 25, 3, 26, 27}, {23, 24, 25, 1, 26, 2, 27, 28}, {1, 2, 3, 23, 24, 4, 25, 26}, {23, 1, 2, 24, 25, 3, 26, 27}, {1, 23, 2, 24, 25, 3, 26, 27}, {23, 24, 1, 25, 26, 2, 27, 28}, {1, 2, 23, 24, 25, 3, 26, 27}, {23, 1, 24, 25, 26, 2, 27, 28}, {1, 23, 24, 25, 26, 2, 27, 28}, {23, 24, 25, 26, 27, 1, 28, 29}, {1, 2, 3, 4, 5, 23, 24, 25}, {23, 1, 2, 3, 4, 24, 25, 26}, {1, 23, 2, 3, 4, 24, 25, 26}, {23, 24, 1, 2, 3, 25, 26, 27}, {1, 2, 23, 3, 4, 24, 25, 26}, {23, 1, 24, 2, 3, 25, 26, 27}, {1, 23, 24, 2, 3, 25, 26, 27}, {23, 24, 25, 1, 2, 26, 27, 28}, {1, 2, 3, 23, 4, 24, 25, 26}, {23, 1, 2, 24, 3, 25, 26, 27}, {1, 23, 2, 24, 3, 25, 26, 27}, {23, 24, 1, 25, 2, 26, 27, 28}, {1, 2, 23, 24, 3, 25, 26, 27}, {23, 1, 24, 25, 2, 26, 27, 28}, {1, 23, 24, 25, 2, 26, 27, 28}, {23, 24, 25, 26, 1, 27, 28, 29}, {1, 2, 3, 4, 23, 24, 25, 26}, {23, 1, 2, 3, 24, 25, 26, 27}, {1, 23, 2, 3, 24, 25, 26, 27}, {23, 24, 1, 2, 25, 26, 27, 28}, {1, 2, 23, 3, 24, 25, 26, 27}, {23, 1, 24, 2, 25, 26, 27, 28}, {1, 23, 24, 2, 25, 26, 27, 28}, {23, 24, 25, 1, 26, 27, 28, 29}, {1, 2, 3, 23, 24, 25, 26, 27}, {23, 1, 2, 24, 25, 26, 27, 28}, {1, 23, 2, 24, 25, 26, 27, 28}, {23, 24, 1, 25, 26, 27, 28, 29}, {1, 2, 23, 24, 25, 26, 27, 28}, {23, 1, 24, 25, 26, 27, 28, 29}, {1, 23, 24, 25, 26, 27, 28, 29}, {23, 24, 25, 26, 27, 28, 29, 30}, }, { {0, 1, 2, 3, 4, 5, 6, 7}, {24, 0, 1, 2, 3, 4, 5, 6}, {0, 24, 1, 2, 3, 4, 5, 6}, {24, 25, 0, 1, 2, 3, 4, 5}, {0, 1, 24, 2, 3, 4, 5, 6}, {24, 0, 25, 1, 2, 3, 4, 5}, {0, 24, 25, 1, 2, 3, 4, 5}, {24, 25, 26, 0, 1, 2, 3, 4}, {0, 1, 2, 24, 3, 4, 5, 6}, {24, 0, 1, 25, 2, 3, 4, 5}, {0, 24, 1, 25, 2, 3, 4, 5}, {24, 25, 0, 26, 1, 2, 3, 4}, {0, 1, 24, 25, 2, 3, 4, 5}, {24, 0, 25, 26, 1, 2, 3, 4}, {0, 24, 25, 26, 1, 2, 3, 4}, {24, 25, 26, 27, 0, 1, 2, 3}, {0, 1, 2, 3, 24, 4, 5, 6}, {24, 0, 1, 2, 25, 3, 4, 5}, {0, 24, 1, 2, 25, 3, 4, 5}, {24, 25, 0, 1, 26, 2, 3, 4}, {0, 1, 24, 2, 25, 3, 4, 5}, {24, 0, 25, 1, 26, 2, 3, 4}, {0, 24, 25, 1, 26, 2, 3, 4}, {24, 25, 26, 0, 27, 1, 2, 3}, {0, 1, 2, 24, 25, 3, 4, 5}, {24, 0, 1, 25, 26, 2, 3, 4}, {0, 24, 1, 25, 26, 2, 3, 4}, {24, 25, 0, 26, 27, 1, 2, 3}, {0, 1, 24, 25, 26, 2, 3, 4}, {24, 0, 25, 26, 27, 1, 2, 3}, {0, 24, 25, 26, 27, 1, 2, 3}, {24, 25, 26, 27, 28, 0, 1, 2}, {0, 1, 2, 3, 4, 24, 5, 6}, {24, 0, 1, 2, 3, 25, 4, 5}, {0, 24, 1, 2, 3, 25, 4, 5}, {24, 25, 0, 1, 2, 26, 3, 4}, {0, 1, 24, 2, 3, 25, 4, 5}, {24, 0, 25, 1, 2, 26, 3, 4}, {0, 24, 25, 1, 2, 26, 3, 4}, {24, 25, 26, 0, 1, 27, 2, 3}, {0, 1, 2, 24, 3, 25, 4, 5}, {24, 0, 1, 25, 2, 26, 3, 4}, {0, 24, 1, 25, 2, 26, 3, 4}, {24, 25, 0, 26, 1, 27, 2, 3}, {0, 1, 24, 25, 2, 26, 3, 4}, {24, 0, 25, 26, 1, 27, 2, 3}, {0, 24, 25, 26, 1, 27, 2, 3}, {24, 25, 26, 27, 0, 28, 1, 2}, {0, 1, 2, 3, 24, 25, 4, 5}, {24, 0, 1, 2, 25, 26, 3, 4}, {0, 24, 1, 2, 25, 26, 3, 4}, {24, 25, 0, 1, 26, 27, 2, 3}, {0, 1, 24, 2, 25, 26, 3, 4}, {24, 0, 25, 1, 26, 27, 2, 3}, {0, 24, 25, 1, 26, 27, 2, 3}, {24, 25, 26, 0, 27, 28, 1, 2}, {0, 1, 2, 24, 25, 26, 3, 4}, {24, 0, 1, 25, 26, 27, 2, 3}, {0, 24, 1, 25, 26, 27, 2, 3}, {24, 25, 0, 26, 27, 28, 1, 2}, {0, 1, 24, 25, 26, 27, 2, 3}, {24, 0, 25, 26, 27, 28, 1, 2}, {0, 24, 25, 26, 27, 28, 1, 2}, {24, 25, 26, 27, 28, 29, 0, 1}, {0, 1, 2, 3, 4, 5, 24, 6}, {24, 0, 1, 2, 3, 4, 25, 5}, {0, 24, 1, 2, 3, 4, 25, 5}, {24, 25, 0, 1, 2, 3, 26, 4}, {0, 1, 24, 2, 3, 4, 25, 5}, {24, 0, 25, 1, 2, 3, 26, 4}, {0, 24, 25, 1, 2, 3, 26, 4}, {24, 25, 26, 0, 1, 2, 27, 3}, {0, 1, 2, 24, 3, 4, 25, 5}, {24, 0, 1, 25, 2, 3, 26, 4}, {0, 24, 1, 25, 2, 3, 26, 4}, {24, 25, 0, 26, 1, 2, 27, 3}, {0, 1, 24, 25, 2, 3, 26, 4}, {24, 0, 25, 26, 1, 2, 27, 3}, {0, 24, 25, 26, 1, 2, 27, 3}, {24, 25, 26, 27, 0, 1, 28, 2}, {0, 1, 2, 3, 24, 4, 25, 5}, {24, 0, 1, 2, 25, 3, 26, 4}, {0, 24, 1, 2, 25, 3, 26, 4}, {24, 25, 0, 1, 26, 2, 27, 3}, {0, 1, 24, 2, 25, 3, 26, 4}, {24, 0, 25, 1, 26, 2, 27, 3}, {0, 24, 25, 1, 26, 2, 27, 3}, {24, 25, 26, 0, 27, 1, 28, 2}, {0, 1, 2, 24, 25, 3, 26, 4}, {24, 0, 1, 25, 26, 2, 27, 3}, {0, 24, 1, 25, 26, 2, 27, 3}, {24, 25, 0, 26, 27, 1, 28, 2}, {0, 1, 24, 25, 26, 2, 27, 3}, {24, 0, 25, 26, 27, 1, 28, 2}, {0, 24, 25, 26, 27, 1, 28, 2}, {24, 25, 26, 27, 28, 0, 29, 1}, {0, 1, 2, 3, 4, 24, 25, 5}, {24, 0, 1, 2, 3, 25, 26, 4}, {0, 24, 1, 2, 3, 25, 26, 4}, {24, 25, 0, 1, 2, 26, 27, 3}, {0, 1, 24, 2, 3, 25, 26, 4}, {24, 0, 25, 1, 2, 26, 27, 3}, {0, 24, 25, 1, 2, 26, 27, 3}, {24, 25, 26, 0, 1, 27, 28, 2}, {0, 1, 2, 24, 3, 25, 26, 4}, {24, 0, 1, 25, 2, 26, 27, 3}, {0, 24, 1, 25, 2, 26, 27, 3}, {24, 25, 0, 26, 1, 27, 28, 2}, {0, 1, 24, 25, 2, 26, 27, 3}, {24, 0, 25, 26, 1, 27, 28, 2}, {0, 24, 25, 26, 1, 27, 28, 2}, {24, 25, 26, 27, 0, 28, 29, 1}, {0, 1, 2, 3, 24, 25, 26, 4}, {24, 0, 1, 2, 25, 26, 27, 3}, {0, 24, 1, 2, 25, 26, 27, 3}, {24, 25, 0, 1, 26, 27, 28, 2}, {0, 1, 24, 2, 25, 26, 27, 3}, {24, 0, 25, 1, 26, 27, 28, 2}, {0, 24, 25, 1, 26, 27, 28, 2}, {24, 25, 26, 0, 27, 28, 29, 1}, {0, 1, 2, 24, 25, 26, 27, 3}, {24, 0, 1, 25, 26, 27, 28, 2}, {0, 24, 1, 25, 26, 27, 28, 2}, {24, 25, 0, 26, 27, 28, 29, 1}, {0, 1, 24, 25, 26, 27, 28, 2}, {24, 0, 25, 26, 27, 28, 29, 1}, {0, 24, 25, 26, 27, 28, 29, 1}, {24, 25, 26, 27, 28, 29, 30, 0}, {0, 1, 2, 3, 4, 5, 6, 24}, {24, 0, 1, 2, 3, 4, 5, 25}, {0, 24, 1, 2, 3, 4, 5, 25}, {24, 25, 0, 1, 2, 3, 4, 26}, {0, 1, 24, 2, 3, 4, 5, 25}, {24, 0, 25, 1, 2, 3, 4, 26}, {0, 24, 25, 1, 2, 3, 4, 26}, {24, 25, 26, 0, 1, 2, 3, 27}, {0, 1, 2, 24, 3, 4, 5, 25}, {24, 0, 1, 25, 2, 3, 4, 26}, {0, 24, 1, 25, 2, 3, 4, 26}, {24, 25, 0, 26, 1, 2, 3, 27}, {0, 1, 24, 25, 2, 3, 4, 26}, {24, 0, 25, 26, 1, 2, 3, 27}, {0, 24, 25, 26, 1, 2, 3, 27}, {24, 25, 26, 27, 0, 1, 2, 28}, {0, 1, 2, 3, 24, 4, 5, 25}, {24, 0, 1, 2, 25, 3, 4, 26}, {0, 24, 1, 2, 25, 3, 4, 26}, {24, 25, 0, 1, 26, 2, 3, 27}, {0, 1, 24, 2, 25, 3, 4, 26}, {24, 0, 25, 1, 26, 2, 3, 27}, {0, 24, 25, 1, 26, 2, 3, 27}, {24, 25, 26, 0, 27, 1, 2, 28}, {0, 1, 2, 24, 25, 3, 4, 26}, {24, 0, 1, 25, 26, 2, 3, 27}, {0, 24, 1, 25, 26, 2, 3, 27}, {24, 25, 0, 26, 27, 1, 2, 28}, {0, 1, 24, 25, 26, 2, 3, 27}, {24, 0, 25, 26, 27, 1, 2, 28}, {0, 24, 25, 26, 27, 1, 2, 28}, {24, 25, 26, 27, 28, 0, 1, 29}, {0, 1, 2, 3, 4, 24, 5, 25}, {24, 0, 1, 2, 3, 25, 4, 26}, {0, 24, 1, 2, 3, 25, 4, 26}, {24, 25, 0, 1, 2, 26, 3, 27}, {0, 1, 24, 2, 3, 25, 4, 26}, {24, 0, 25, 1, 2, 26, 3, 27}, {0, 24, 25, 1, 2, 26, 3, 27}, {24, 25, 26, 0, 1, 27, 2, 28}, {0, 1, 2, 24, 3, 25, 4, 26}, {24, 0, 1, 25, 2, 26, 3, 27}, {0, 24, 1, 25, 2, 26, 3, 27}, {24, 25, 0, 26, 1, 27, 2, 28}, {0, 1, 24, 25, 2, 26, 3, 27}, {24, 0, 25, 26, 1, 27, 2, 28}, {0, 24, 25, 26, 1, 27, 2, 28}, {24, 25, 26, 27, 0, 28, 1, 29}, {0, 1, 2, 3, 24, 25, 4, 26}, {24, 0, 1, 2, 25, 26, 3, 27}, {0, 24, 1, 2, 25, 26, 3, 27}, {24, 25, 0, 1, 26, 27, 2, 28}, {0, 1, 24, 2, 25, 26, 3, 27}, {24, 0, 25, 1, 26, 27, 2, 28}, {0, 24, 25, 1, 26, 27, 2, 28}, {24, 25, 26, 0, 27, 28, 1, 29}, {0, 1, 2, 24, 25, 26, 3, 27}, {24, 0, 1, 25, 26, 27, 2, 28}, {0, 24, 1, 25, 26, 27, 2, 28}, {24, 25, 0, 26, 27, 28, 1, 29}, {0, 1, 24, 25, 26, 27, 2, 28}, {24, 0, 25, 26, 27, 28, 1, 29}, {0, 24, 25, 26, 27, 28, 1, 29}, {24, 25, 26, 27, 28, 29, 0, 30}, {0, 1, 2, 3, 4, 5, 24, 25}, {24, 0, 1, 2, 3, 4, 25, 26}, {0, 24, 1, 2, 3, 4, 25, 26}, {24, 25, 0, 1, 2, 3, 26, 27}, {0, 1, 24, 2, 3, 4, 25, 26}, {24, 0, 25, 1, 2, 3, 26, 27}, {0, 24, 25, 1, 2, 3, 26, 27}, {24, 25, 26, 0, 1, 2, 27, 28}, {0, 1, 2, 24, 3, 4, 25, 26}, {24, 0, 1, 25, 2, 3, 26, 27}, {0, 24, 1, 25, 2, 3, 26, 27}, {24, 25, 0, 26, 1, 2, 27, 28}, {0, 1, 24, 25, 2, 3, 26, 27}, {24, 0, 25, 26, 1, 2, 27, 28}, {0, 24, 25, 26, 1, 2, 27, 28}, {24, 25, 26, 27, 0, 1, 28, 29}, {0, 1, 2, 3, 24, 4, 25, 26}, {24, 0, 1, 2, 25, 3, 26, 27}, {0, 24, 1, 2, 25, 3, 26, 27}, {24, 25, 0, 1, 26, 2, 27, 28}, {0, 1, 24, 2, 25, 3, 26, 27}, {24, 0, 25, 1, 26, 2, 27, 28}, {0, 24, 25, 1, 26, 2, 27, 28}, {24, 25, 26, 0, 27, 1, 28, 29}, {0, 1, 2, 24, 25, 3, 26, 27}, {24, 0, 1, 25, 26, 2, 27, 28}, {0, 24, 1, 25, 26, 2, 27, 28}, {24, 25, 0, 26, 27, 1, 28, 29}, {0, 1, 24, 25, 26, 2, 27, 28}, {24, 0, 25, 26, 27, 1, 28, 29}, {0, 24, 25, 26, 27, 1, 28, 29}, {24, 25, 26, 27, 28, 0, 29, 30}, {0, 1, 2, 3, 4, 24, 25, 26}, {24, 0, 1, 2, 3, 25, 26, 27}, {0, 24, 1, 2, 3, 25, 26, 27}, {24, 25, 0, 1, 2, 26, 27, 28}, {0, 1, 24, 2, 3, 25, 26, 27}, {24, 0, 25, 1, 2, 26, 27, 28}, {0, 24, 25, 1, 2, 26, 27, 28}, {24, 25, 26, 0, 1, 27, 28, 29}, {0, 1, 2, 24, 3, 25, 26, 27}, {24, 0, 1, 25, 2, 26, 27, 28}, {0, 24, 1, 25, 2, 26, 27, 28}, {24, 25, 0, 26, 1, 27, 28, 29}, {0, 1, 24, 25, 2, 26, 27, 28}, {24, 0, 25, 26, 1, 27, 28, 29}, {0, 24, 25, 26, 1, 27, 28, 29}, {24, 25, 26, 27, 0, 28, 29, 30}, {0, 1, 2, 3, 24, 25, 26, 27}, {24, 0, 1, 2, 25, 26, 27, 28}, {0, 24, 1, 2, 25, 26, 27, 28}, {24, 25, 0, 1, 26, 27, 28, 29}, {0, 1, 24, 2, 25, 26, 27, 28}, {24, 0, 25, 1, 26, 27, 28, 29}, {0, 24, 25, 1, 26, 27, 28, 29}, {24, 25, 26, 0, 27, 28, 29, 30}, {0, 1, 2, 24, 25, 26, 27, 28}, {24, 0, 1, 25, 26, 27, 28, 29}, {0, 24, 1, 25, 26, 27, 28, 29}, {24, 25, 0, 26, 27, 28, 29, 30}, {0, 1, 24, 25, 26, 27, 28, 29}, {24, 0, 25, 26, 27, 28, 29, 30}, {0, 24, 25, 26, 27, 28, 29, 30}, {24, 25, 26, 27, 28, 29, 30, 31}, }, }; zxc-0.13.0/src/lib/zxc_pivco_tables.h000066400000000000000000000030761522536370400174600ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file zxc_pivco_tables.h * @brief Shuffle-index tables for the PivCo merge routines (declarations). * * The definitions live in zxc_pivco_tables.c, compiled ONCE outside the * per-architecture function-multiversioning variants: the tables are pure * data, so duplicating them per variant would only bloat the binary * (22 KB x number of variants). * * For a control byte b (bits LSB-first; bit==0 -> next element of L, bit==1 -> * next of R), over a 32-lane view {L[0..15], R[0..15]} (L lanes 0..15, * R lanes 16..31): * * - zxc_pivco_idxa_u8[b][j]: FINAL lane for output j of the first half of a * 16-output merge step. Also serves the portable 8-output merge routine (a * 24-byte scratch with R copied at offset 16). * * - zxc_pivco_idxb_pre[pc0][b][j]: FINAL lane for output 8+j, pre-offset for * pc0 = popcount(first control byte): L entries already skip the * (8 - pc0) left elements consumed by the first half, R entries the pc0 * right ones. Indexing the row by pc0 folds this fixup into a single * table load. */ #ifndef ZXC_PIVCO_TABLES_H #define ZXC_PIVCO_TABLES_H #include #if defined(__GNUC__) || defined(__clang__) #define ZXC_HIDDEN __attribute__((visibility("hidden"))) #else #define ZXC_HIDDEN #endif extern ZXC_HIDDEN const uint8_t zxc_pivco_idxa_u8[256][8]; extern ZXC_HIDDEN const uint8_t zxc_pivco_idxb_pre[9][256][8]; #endif /* ZXC_PIVCO_TABLES_H */ zxc-0.13.0/src/lib/zxc_pstream.c000066400000000000000000001333551522536370400164600ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file zxc_pstream.c * @brief Push-based, single-threaded streaming driver implementation. * * See zxc_pstream.h for the public contract. The implementation composes * the public block API (@ref zxc_compress_block / @ref zxc_decompress_block) * with the public sans-IO header helpers (@ref zxc_write_file_header / * footer, @c zxc_read_*); the only internal dependency is on shared * constants and the global-hash combine inline, pulled from zxc_internal.h. * * Both compression and decompression are structured as resumable state * machines driven by caller-provided input/output buffers * (@ref zxc_inbuf_t / @ref zxc_outbuf_t). Each call advances as much as * possible without blocking and returns a status indicating whether the * caller should drain @p out, supply more @p in, or finalise the stream. */ #include "../../include/zxc_pstream.h" #include "../../include/zxc_buffer.h" #include "../../include/zxc_constants.h" #include "../../include/zxc_error.h" #include "zxc_internal.h" /* ===================================================================== */ /* Compression */ /* ===================================================================== */ /** * @enum cstream_state_t * @brief Lifecycle states of the push compression stream. * * The compression state machine alternates between *staging* a chunk of * output bytes into the @c pending buffer and *draining* those bytes into * the caller's @ref zxc_outbuf_t. Forward progress is therefore always * either consuming from @c in or producing into @c out. * * @var cstream_state_t::CS_INIT * Initial state; nothing has been emitted yet. * @var cstream_state_t::CS_DRAIN_HEADER * File header staged in @c pending; draining to @p out, then transitions * to @c CS_ACCUMULATE. * @var cstream_state_t::CS_ACCUMULATE * Copying input bytes into the internal block accumulator until it is * full (or the stream is finalised). * @var cstream_state_t::CS_DRAIN_BLOCK * A full data block was just compressed; draining it to @p out, then * back to @c CS_ACCUMULATE. * @var cstream_state_t::CS_DRAIN_LAST * Draining the final partial block produced inside @ref zxc_cstream_end; * transitions to @c CS_DRAIN_EOF. * @var cstream_state_t::CS_DRAIN_EOF * Draining the EOF block; transitions to @c CS_DRAIN_FOOTER. * @var cstream_state_t::CS_DRAIN_FOOTER * Draining the file footer; transitions to @c CS_DONE. * @var cstream_state_t::CS_DONE * Finalisation complete; further @c _compress / @c _end calls are * rejected. * @var cstream_state_t::CS_ERRORED * Sticky error state; subsequent calls return the latched error code. */ typedef enum { CS_INIT = 0, CS_DRAIN_HEADER, CS_ACCUMULATE, CS_DRAIN_BLOCK, CS_DRAIN_LAST, CS_DRAIN_EOF, CS_DRAIN_FOOTER, CS_DONE, CS_ERRORED } cstream_state_t; /** * @struct zxc_cstream_s * @brief Internal state of a push compression stream. * * Owns three buffers: a fixed-size input accumulator (@c in_block, sized * to one block), a variable-size output staging area (@c pending, holding * the file header, one compressed block, the EOF block, or the file * footer), and the underlying compression context (@c cctx). * * @var zxc_cstream_s::opts * Compression options (copied from the caller at creation time). * @var zxc_cstream_s::cctx * Underlying single-block compression context. * @var zxc_cstream_s::block_size * Target block size in bytes; cached from @c opts.block_size. * @var zxc_cstream_s::in_block * Heap buffer of capacity @c block_size used to accumulate one full * uncompressed block before invoking the block compressor. * @var zxc_cstream_s::in_used * Number of valid bytes currently held in @c in_block (in * [0, @c block_size]). * @var zxc_cstream_s::pending * Heap buffer holding the next chunk of output bytes to emit * (header, compressed block, EOF marker or footer). * @var zxc_cstream_s::pending_cap * Allocated capacity of @c pending. * @var zxc_cstream_s::pending_len * Total valid bytes currently staged in @c pending. * @var zxc_cstream_s::pending_pos * Bytes already copied from @c pending to the caller's output buffer. * Drain is complete when @c pending_pos == @c pending_len. * @var zxc_cstream_s::total_in * Running count of uncompressed bytes consumed; written into the file * footer. * @var zxc_cstream_s::global_hash * Rolling per-block-trailer hash; written into the file footer when * checksums are enabled. * @var zxc_cstream_s::state * Current state machine position (see @ref cstream_state_t). * @var zxc_cstream_s::error_code * Sticky error code; valid only when @c state is @c CS_ERRORED. */ struct zxc_cstream_s { zxc_compress_opts_t opts; zxc_cctx* cctx; size_t block_size; uint8_t* in_block; size_t in_used; uint8_t* pending; size_t pending_cap; size_t pending_len; size_t pending_pos; uint64_t total_in; uint32_t global_hash; cstream_state_t state; int error_code; }; /** * @brief Latches a sticky error on the compression stream. * * Stores @p code in @c cs->error_code, transitions @c cs->state to * @c CS_ERRORED, and returns @p code. Once errored, subsequent * @ref zxc_cstream_compress / @ref zxc_cstream_end calls return the same * code without performing further work. * * @param[in,out] cs Compression stream. * @param[in] code Negative @ref zxc_error_t value to latch. * @return @p code (always negative). */ static int cs_set_error(zxc_cstream* cs, const int code) { cs->error_code = code; cs->state = CS_ERRORED; return code; } /** * @brief Compresses one block from @p src into @c cs->pending. * * Grows @c pending to @ref zxc_compress_block_bound if needed and updates * bookkeeping (@c total_in, @c global_hash). When file-level checksums are * enabled, folds the block trailer into the rolling @c global_hash. * @p src is either the @c in_block accumulator (via * @ref cs_compress_one_block) or the caller's input buffer directly (the * full-block fast path in @ref zxc_cstream_compress, which skips the * staging memcpy). * * @pre @c len > 0. * * @param[in,out] cs Compression stream. * @param[in] src Block bytes to compress (only [src, src+len) is read). * @param[in] len Block length in bytes. * @return @ref ZXC_OK on success, negative @ref zxc_error_t on failure. */ static int cs_compress_block_from(zxc_cstream* cs, const uint8_t* RESTRICT src, const size_t len) { const uint64_t bound = zxc_compress_block_bound(len); // LCOV_EXCL_START if (UNLIKELY(bound == 0 || bound > SIZE_MAX)) return ZXC_ERROR_OVERFLOW; if (UNLIKELY(bound > cs->pending_cap)) { uint8_t* nb = (uint8_t*)ZXC_REALLOC(cs->pending, (size_t)bound); if (UNLIKELY(!nb)) return ZXC_ERROR_MEMORY; cs->pending = nb; cs->pending_cap = (size_t)bound; } // LCOV_EXCL_STOP const int64_t csize = zxc_compress_block(cs->cctx, src, len, cs->pending, cs->pending_cap, &cs->opts); if (UNLIKELY(csize < 0)) return (int)csize; // LCOV_EXCL_LINE cs->pending_len = (size_t)csize; cs->pending_pos = 0; cs->total_in += len; /* If checksums are on, the block trailer is the last ZXC_BLOCK_CHECKSUM_SIZE * bytes of pending; fold it into the rolling global hash. */ if (cs->opts.checksum_enabled && cs->pending_len >= ZXC_BLOCK_CHECKSUM_SIZE) { const uint32_t bh = zxc_le32(cs->pending + cs->pending_len - ZXC_BLOCK_CHECKSUM_SIZE); cs->global_hash = zxc_hash_combine_rotate(cs->global_hash, bh); } return ZXC_OK; } /** * @brief Compresses the accumulated (full or partial) block. * * Convenience wrapper around @ref cs_compress_block_from for the staging * path: compresses @c cs->in_block[0..in_used) into @c cs->pending and, on * success, resets @c in_used to 0 so the accumulator is ready for the next * block. On failure the accumulator is left untouched. * * @pre @c cs->in_used > 0. * * @param[in,out] cs Compression stream. * @return @ref ZXC_OK on success, negative @ref zxc_error_t on failure. */ static int cs_compress_one_block(zxc_cstream* cs) { const int rc = cs_compress_block_from(cs, cs->in_block, cs->in_used); if (LIKELY(rc == ZXC_OK)) cs->in_used = 0; return rc; } /** * @brief Drains staged output bytes into the caller's output buffer. * * Copies as many bytes as possible from * @c cs->pending[pending_pos..pending_len) into * @c out->dst[pos..size), advancing both cursors. * * @param[in,out] cs Compression stream. * @param[in,out] out Caller output buffer. * @return Non-zero once the @c pending buffer has been fully drained * (@c pending_pos == @c pending_len), zero otherwise. */ static int cs_drain_pending(zxc_cstream* cs, zxc_outbuf_t* out) { const size_t avail_out = out->size - out->pos; const size_t avail_pen = cs->pending_len - cs->pending_pos; const size_t n = avail_out < avail_pen ? avail_out : avail_pen; if (n) { ZXC_MEMCPY((uint8_t*)out->dst + out->pos, cs->pending + cs->pending_pos, n); out->pos += n; cs->pending_pos += n; } return cs->pending_pos == cs->pending_len; } /** * @brief Allocates and initialises a push compression stream. * * Copies @p opts into the new context, applying defaults for any zero-valued * field (@c level -> @ref ZXC_LEVEL_DEFAULT, @c block_size -> * @ref ZXC_BLOCK_SIZE_DEFAULT; levels above @ref ZXC_LEVEL_ULTRA are * clamped). Forces single-threaded operation (@c n_threads = 0) and * disables progress callbacks and seekable framing (those modes belong to * the @c FILE*-based pipeline). Dictionary options are REJECTED (the * push-stream format carries no dict_id, so a dictionary would produce * undecodable archives): passing any of @c dict / @c dict_size / @c dict_huf * returns @c NULL rather than silently dropping them. Pre-sizes the * @c pending buffer so that the file header / footer paths never need a * realloc. * * @param[in] opts Compression options, or @c NULL for full defaults. * @return New stream owned by the caller, or @c NULL on allocation * failure / invalid option values (including dictionary options). */ zxc_cstream* zxc_cstream_create(const zxc_compress_opts_t* opts) { zxc_cstream* cs = (zxc_cstream*)ZXC_CALLOC(1, sizeof(*cs)); if (UNLIKELY(!cs)) return NULL; // LCOV_EXCL_LINE if (opts) cs->opts = *opts; /* The push-stream format carries no dict_id, so a dictionary here would * produce archives that decode wrong (or not at all) elsewhere. */ if (UNLIKELY(cs->opts.dict || cs->opts.dict_size || cs->opts.dict_huf)) { ZXC_FREE(cs); return NULL; } if (cs->opts.level == 0) cs->opts.level = ZXC_LEVEL_DEFAULT; cs->opts.level = zxc_level_clamp(cs->opts.level); if (cs->opts.block_size == 0) cs->opts.block_size = ZXC_BLOCK_SIZE_DEFAULT; /* n_threads is ignored on this single-threaded path. */ cs->opts.n_threads = 0; cs->opts.progress_cb = NULL; cs->opts.user_data = NULL; cs->opts.seekable = 0; cs->block_size = cs->opts.block_size; cs->cctx = zxc_create_cctx(&cs->opts); // LCOV_EXCL_START if (UNLIKELY(!cs->cctx)) { ZXC_FREE(cs); return NULL; } cs->in_block = (uint8_t*)ZXC_MALLOC(cs->block_size); if (UNLIKELY(!cs->in_block)) { zxc_free_cctx(cs->cctx); ZXC_FREE(cs); return NULL; } // LCOV_EXCL_STOP /* Pre-size pending so the file header path never needs realloc. */ cs->pending_cap = ZXC_FILE_HEADER_SIZE > ZXC_FILE_FOOTER_SIZE ? ZXC_FILE_HEADER_SIZE : ZXC_FILE_FOOTER_SIZE; cs->pending = (uint8_t*)ZXC_MALLOC(cs->pending_cap); // LCOV_EXCL_START if (UNLIKELY(!cs->pending)) { ZXC_FREE(cs->in_block); zxc_free_cctx(cs->cctx); ZXC_FREE(cs); return NULL; } // LCOV_EXCL_STOP cs->state = CS_INIT; return cs; } /** * @brief Stages the 16-byte file header into the @c pending buffer. * * @param[in,out] cs Compression stream. * @return @ref ZXC_OK on success, negative @ref zxc_error_t on failure. */ static int cs_stage_file_header(zxc_cstream* cs) { const int w = zxc_write_file_header(cs->pending, cs->pending_cap, cs->block_size, cs->opts.checksum_enabled, 0); if (UNLIKELY(w < 0)) return w; // LCOV_EXCL_LINE cs->pending_len = (size_t)w; cs->pending_pos = 0; return ZXC_OK; } /** * @brief Stages the 8-byte EOF block into the @c pending buffer. * * The EOF block is a regular block header with @c block_type set to * @ref ZXC_BLOCK_EOF and @c comp_size = 0; it carries no payload. * * @param[in,out] cs Compression stream. * @return @ref ZXC_OK on success, negative @ref zxc_error_t on failure. */ static int cs_stage_eof(zxc_cstream* cs) { // LCOV_EXCL_START if (UNLIKELY(ZXC_BLOCK_HEADER_SIZE > cs->pending_cap)) { uint8_t* nb = (uint8_t*)ZXC_REALLOC(cs->pending, ZXC_BLOCK_HEADER_SIZE); if (UNLIKELY(!nb)) return ZXC_ERROR_MEMORY; cs->pending = nb; cs->pending_cap = ZXC_BLOCK_HEADER_SIZE; } // LCOV_EXCL_STOP const zxc_block_header_t eof = { .block_type = (uint8_t)ZXC_BLOCK_EOF, .block_flags = 0, .reserved = 0, .header_crc = 0, .comp_size = 0, }; const int w = zxc_write_block_header(cs->pending, cs->pending_cap, &eof); if (UNLIKELY(w < 0)) return w; // LCOV_EXCL_LINE cs->pending_len = (size_t)w; cs->pending_pos = 0; return ZXC_OK; } /** * @brief Stages the 12-byte file footer into the @c pending buffer. * * The footer carries the total uncompressed input size and (when checksums * are enabled) the global rolling hash accumulated across all data blocks. * * @param[in,out] cs Compression stream. * @return @ref ZXC_OK on success, negative @ref zxc_error_t on failure. */ static int cs_stage_footer(zxc_cstream* cs) { // LCOV_EXCL_START if (UNLIKELY(ZXC_FILE_FOOTER_SIZE > cs->pending_cap)) { uint8_t* nb = (uint8_t*)ZXC_REALLOC(cs->pending, ZXC_FILE_FOOTER_SIZE); if (UNLIKELY(!nb)) return ZXC_ERROR_MEMORY; cs->pending = nb; cs->pending_cap = ZXC_FILE_FOOTER_SIZE; } // LCOV_EXCL_STOP const int w = zxc_write_file_footer(cs->pending, cs->pending_cap, cs->total_in, cs->global_hash, cs->opts.checksum_enabled); if (UNLIKELY(w < 0)) return w; // LCOV_EXCL_LINE cs->pending_len = (size_t)w; cs->pending_pos = 0; return ZXC_OK; } /** * @brief Releases a compression stream and all internal buffers. * * Safe to call with @c NULL. * * @param[in,out] cs Stream returned by @ref zxc_cstream_create. */ void zxc_cstream_free(zxc_cstream* cs) { if (!cs) return; ZXC_FREE(cs->pending); ZXC_FREE(cs->in_block); zxc_free_cctx(cs->cctx); ZXC_FREE(cs); } /** * @brief Returns the suggested input chunk size (configured block size). * * @param[in] cs Compression stream. * @return Block size in bytes, or 0 if @p cs is @c NULL. */ size_t zxc_cstream_in_size(const zxc_cstream* cs) { return cs ? cs->block_size : 0; } /** * @brief Returns the suggested output chunk size. * * Sized to hold one full compressed block plus framing overhead, i.e. * @ref zxc_compress_block_bound applied to the configured block size. * Falls back to @c block_size when the bound overflows @c size_t. * * @param[in] cs Compression stream. * @return Suggested output buffer capacity in bytes, or 0 if @p cs is @c NULL. */ size_t zxc_cstream_out_size(const zxc_cstream* cs) { if (UNLIKELY(!cs)) return 0; // LCOV_EXCL_LINE const uint64_t b = zxc_compress_block_bound(cs->block_size); return (b == 0 || b > SIZE_MAX) ? cs->block_size : (size_t)b; } /** * @brief Push-side entry point: feeds input and drains compressed output. * * Drives the @ref cstream_state_t machine: emits the file header on the * first call, then accumulates input until a block is full, compresses it, * and drains the result into @p out. Each call makes as much progress as * either buffer allows; the function is fully reentrant. See the public * contract in @ref zxc_cstream_compress for full semantics. * * The terminal states (@c CS_DRAIN_LAST, @c CS_DRAIN_EOF, @c CS_DRAIN_FOOTER, * @c CS_DONE, @c CS_ERRORED) are owned by @ref zxc_cstream_end; reaching * them here yields @ref ZXC_ERROR_NULL_INPUT. * * @param[in,out] cs Compression stream. * @param[in,out] out Caller output buffer. * @param[in,out] in Caller input buffer. * @return @c 0 if @p in fully consumed and nothing pending, * @c >0 number of bytes still pending (drain @p out then call again), * @c <0 a @ref zxc_error_t code. */ int64_t zxc_cstream_compress(zxc_cstream* cs, zxc_outbuf_t* out, zxc_inbuf_t* in) { if (UNLIKELY(!cs || !out || !in || in->pos > in->size || out->pos > out->size || (in->size > in->pos && !in->src) || (out->size > out->pos && !out->dst) || cs->state == CS_DONE)) { return ZXC_ERROR_NULL_INPUT; } if (UNLIKELY(cs->state == CS_ERRORED)) return cs->error_code; for (;;) { switch (cs->state) { case CS_INIT: { const int rc = cs_stage_file_header(cs); if (UNLIKELY(rc < 0)) return cs_set_error(cs, rc); // LCOV_EXCL_LINE cs->state = CS_DRAIN_HEADER; break; } case CS_DRAIN_HEADER: case CS_DRAIN_BLOCK: { if (!cs_drain_pending(cs, out)) return (int64_t)(cs->pending_len - cs->pending_pos); cs->state = CS_ACCUMULATE; break; } case CS_ACCUMULATE: { const size_t avail_in = in->size - in->pos; if (cs->in_used == 0 && avail_in >= cs->block_size) { const int rc = cs_compress_block_from(cs, (const uint8_t*)in->src + in->pos, cs->block_size); if (UNLIKELY(rc < 0)) return cs_set_error(cs, rc); // LCOV_EXCL_LINE in->pos += cs->block_size; cs->state = CS_DRAIN_BLOCK; break; } const size_t room = cs->block_size - cs->in_used; const size_t n = avail_in < room ? avail_in : room; if (n) { ZXC_MEMCPY(cs->in_block + cs->in_used, (const uint8_t*)in->src + in->pos, n); in->pos += n; cs->in_used += n; } if (cs->in_used == cs->block_size) { const int rc = cs_compress_one_block(cs); if (UNLIKELY(rc < 0)) return cs_set_error(cs, rc); // LCOV_EXCL_LINE cs->state = CS_DRAIN_BLOCK; break; } /* Block not yet full either in is empty or we made no progress. */ return 0; } case CS_DRAIN_LAST: case CS_DRAIN_EOF: case CS_DRAIN_FOOTER: case CS_DONE: case CS_ERRORED: /* These states are owned by _end(). */ return ZXC_ERROR_NULL_INPUT; } } } /** * @brief Finalises the stream: residual block (if any), EOF, and footer. * * Continues the same state machine as @ref zxc_cstream_compress through the * terminal states (@c CS_DRAIN_LAST -> @c CS_DRAIN_EOF -> @c CS_DRAIN_FOOTER * -> @c CS_DONE). Reentrant: when @p out fills mid-drain, returns the * number of bytes still pending and resumes from where it left off on the * next call. See the public contract in @ref zxc_cstream_end. * * @param[in,out] cs Compression stream. * @param[in,out] out Caller output buffer. * @return @c 0 once finalisation is complete (stream is now in DONE state), * @c >0 number of bytes still pending (drain and call again), * @c <0 a @ref zxc_error_t code. */ int64_t zxc_cstream_end(zxc_cstream* cs, zxc_outbuf_t* out) { if (UNLIKELY(!cs || !out || cs->state == CS_DONE)) return ZXC_ERROR_NULL_INPUT; if (UNLIKELY(cs->state == CS_ERRORED)) return cs->error_code; for (;;) { switch (cs->state) { case CS_INIT: { /* _end before any input, still need to emit file header. */ const int rc = cs_stage_file_header(cs); if (UNLIKELY(rc < 0)) return cs_set_error(cs, rc); // LCOV_EXCL_LINE cs->state = CS_DRAIN_HEADER; break; } case CS_DRAIN_HEADER: case CS_DRAIN_BLOCK: { if (!cs_drain_pending(cs, out)) return (int64_t)(cs->pending_len - cs->pending_pos); cs->state = CS_ACCUMULATE; break; } case CS_ACCUMULATE: { /* Compress the residual partial block (if any), then EOF + footer. */ if (cs->in_used > 0) { const int rc = cs_compress_one_block(cs); if (UNLIKELY(rc < 0)) return cs_set_error(cs, rc); // LCOV_EXCL_LINE cs->state = CS_DRAIN_LAST; break; } /* No residual data: go straight to EOF. */ { const int rc = cs_stage_eof(cs); if (UNLIKELY(rc < 0)) return cs_set_error(cs, rc); // LCOV_EXCL_LINE cs->state = CS_DRAIN_EOF; break; } } case CS_DRAIN_LAST: { if (!cs_drain_pending(cs, out)) return (int64_t)(cs->pending_len - cs->pending_pos); /* After last data block -> EOF. */ const int rc = cs_stage_eof(cs); if (UNLIKELY(rc < 0)) return cs_set_error(cs, rc); // LCOV_EXCL_LINE cs->state = CS_DRAIN_EOF; break; } case CS_DRAIN_EOF: { if (!cs_drain_pending(cs, out)) return (int64_t)(cs->pending_len - cs->pending_pos); const int rc = cs_stage_footer(cs); if (UNLIKELY(rc < 0)) return cs_set_error(cs, rc); // LCOV_EXCL_LINE cs->state = CS_DRAIN_FOOTER; break; } case CS_DRAIN_FOOTER: { if (!cs_drain_pending(cs, out)) return (int64_t)(cs->pending_len - cs->pending_pos); cs->state = CS_DONE; return 0; } case CS_DONE: case CS_ERRORED: return cs->state == CS_ERRORED ? cs->error_code : 0; // LCOV_EXCL_LINE } } } /* ===================================================================== */ /* Decompression */ /* ===================================================================== */ /** * @enum dstream_state_t * @brief Lifecycle states of the push decompression stream. * * The decompressor implements a frame-aware parser: file header -> N * (data block header + payload [+ optional checksum]) -> EOF block -> * optional SEK index block -> file footer. The states alternate between * *pulling* fixed- or variable-sized chunks from the caller's input, and * *emitting* the corresponding decoded output. * * @var dstream_state_t::DS_NEED_FILE_HEADER * Pulling the 16-byte file header into @c scratch. * @var dstream_state_t::DS_NEED_BLOCK_HEADER * Pulling an 8-byte block header into @c scratch. * @var dstream_state_t::DS_NEED_BLOCK_PAYLOAD * Pulling a data block payload (and optional checksum) into @c payload. * @var dstream_state_t::DS_DECODE_BLOCK * Calling the underlying block decoder on the accumulated payload. * @var dstream_state_t::DS_EMIT_DECODED * Draining decoded bytes from @c decoded into @p out. * @var dstream_state_t::DS_PEEK_TAIL * Just past the EOF block: read 8 bytes and peek to disambiguate * between an optional SEK index block and the file footer. * @var dstream_state_t::DS_DRAIN_SEK_PAYLOAD * The peeked 8 bytes were a SEK header; skipping its payload bytes. * @var dstream_state_t::DS_NEED_FOOTER_FULL * Pulling the full 12-byte file footer (used after a SEK block). * @var dstream_state_t::DS_NEED_FOOTER_REST * The 8 peeked bytes were the head of the footer; pulling the * remaining 4 bytes. * @var dstream_state_t::DS_VALIDATE_FOOTER * Validating @c total_out and the optional global hash. * @var dstream_state_t::DS_DONE * Stream fully consumed and validated. * @var dstream_state_t::DS_ERRORED * Sticky error state; subsequent calls return the latched code. */ typedef enum { DS_NEED_FILE_HEADER = 0, DS_NEED_BLOCK_HEADER, DS_NEED_BLOCK_PAYLOAD, DS_DECODE_BLOCK, DS_EMIT_DECODED, DS_PEEK_TAIL, DS_DRAIN_SEK_PAYLOAD, DS_NEED_FOOTER_FULL, DS_NEED_FOOTER_REST, DS_VALIDATE_FOOTER, DS_DONE, DS_ERRORED } dstream_state_t; /** * @struct zxc_dstream_s * @brief Internal state of a push decompression stream. * * Owns three accumulator regions: a fixed-size on-stack-style @c scratch * buffer for headers/footers/peeks, a heap @c payload buffer for variable * block payloads, and a heap @c decoded buffer that holds one decompressed * block before it is emitted to the caller. * * @var zxc_dstream_s::opts * Decompression options (copied at creation time). * @var zxc_dstream_s::inner * Underlying single-block decompression context. * @var zxc_dstream_s::inner_initialized * Non-zero once @c inner has been initialised; gates the matching * @ref zxc_cctx_free call at teardown. * @var zxc_dstream_s::block_size * Block size declared by the file header; 0 until the header is parsed. * @var zxc_dstream_s::file_has_checksum * File-level checksum flag declared by the file header. * @var zxc_dstream_s::scratch * Generic 32-byte accumulator for fixed-size frames (file header, block * header, footer); comfortably holds the largest (16-byte file header). * @var zxc_dstream_s::scratch_used * Number of bytes currently held in @c scratch. * @var zxc_dstream_s::scratch_need * Target number of bytes for the current accumulation phase. * @var zxc_dstream_s::payload * Heap buffer holding one block: header + compressed payload + optional * checksum. * @var zxc_dstream_s::payload_cap * Allocated capacity of @c payload. * @var zxc_dstream_s::payload_used * Number of valid bytes currently in @c payload. * @var zxc_dstream_s::payload_need * Target byte count for the current payload phase * (= header size + comp_size + checksum size). * @var zxc_dstream_s::decoded * Heap buffer holding the decoded output of one block (sized for the * wild-copy fast path: @c block_size + @ref ZXC_DECOMPRESS_TAIL_PAD). * @var zxc_dstream_s::decoded_cap * Allocated capacity of @c decoded. * @var zxc_dstream_s::decoded_size * Real number of decoded bytes in @c decoded after the last decode. * @var zxc_dstream_s::decoded_pos * Bytes already emitted from @c decoded; drain complete when * @c decoded_pos == @c decoded_size. * @var zxc_dstream_s::cur_bh * Parsed block header for the block currently being processed. * @var zxc_dstream_s::sek_remaining * Bytes left to skip from a SEK block payload (only used in * @c DS_DRAIN_SEK_PAYLOAD). * @var zxc_dstream_s::total_out * Cumulative decompressed output size; cross-checked against the * file footer. * @var zxc_dstream_s::global_hash * Rolling per-block-trailer hash; cross-checked against the file * footer when checksums are enabled. * @var zxc_dstream_s::state * Current state machine position (see @ref dstream_state_t). * @var zxc_dstream_s::error_code * Sticky error code; valid only when @c state is @c DS_ERRORED. */ struct zxc_dstream_s { zxc_decompress_opts_t opts; zxc_cctx_t inner; int inner_initialized; size_t block_size; int file_has_checksum; uint8_t scratch[32]; size_t scratch_used; size_t scratch_need; uint8_t* payload; size_t payload_cap; size_t payload_used; size_t payload_need; uint8_t* decoded; size_t decoded_cap; size_t decoded_size; size_t decoded_pos; zxc_block_header_t cur_bh; size_t sek_remaining; uint64_t total_out; uint32_t global_hash; dstream_state_t state; int error_code; }; /** * @brief Latches a sticky error on the decompression stream. * * Stores @p code in @c ds->error_code, transitions @c ds->state to * @c DS_ERRORED, and returns @p code. Once errored, subsequent * @ref zxc_dstream_decompress calls return the same code without * performing further work. * * @param[in,out] ds Decompression stream. * @param[in] code Negative @ref zxc_error_t value to latch. * @return @p code (always negative). */ static int ds_set_error(zxc_dstream* ds, const int code) { ds->error_code = code; ds->state = DS_ERRORED; return code; } /** * @brief Pulls up to @c (scratch_need - scratch_used) bytes from @p in. * * Used to accumulate fixed-size frames (file header, block header, footer, * EOF tail peek) into the inline @c scratch buffer. * * @param[in,out] ds Decompression stream. * @param[in,out] in Caller input buffer; @c pos is advanced. * @return @c 1 once @c scratch holds exactly @c scratch_need bytes, * @c 0 otherwise (need more input). */ static int ds_pull_scratch(zxc_dstream* ds, zxc_inbuf_t* in) { const size_t want = ds->scratch_need - ds->scratch_used; const size_t avail = in->size - in->pos; const size_t n = want < avail ? want : avail; if (n) { ZXC_MEMCPY(ds->scratch + ds->scratch_used, (const uint8_t*)in->src + in->pos, n); in->pos += n; ds->scratch_used += n; } return ds->scratch_used == ds->scratch_need; } /** * @brief Same as @ref ds_pull_scratch but pulls into the heap @c payload buffer. * * Used to accumulate the variable-size compressed block payload * (header + comp_size [+ checksum]). * * @param[in,out] ds Decompression stream. * @param[in,out] in Caller input buffer; @c pos is advanced. * @return @c 1 once @c payload holds exactly @c payload_need bytes, * @c 0 otherwise (need more input). */ static int ds_pull_payload(zxc_dstream* ds, zxc_inbuf_t* in) { const size_t want = ds->payload_need - ds->payload_used; const size_t avail = in->size - in->pos; const size_t n = want < avail ? want : avail; if (n) { ZXC_MEMCPY(ds->payload + ds->payload_used, (const uint8_t*)in->src + in->pos, n); in->pos += n; ds->payload_used += n; } return ds->payload_used == ds->payload_need; } /** * @brief Allocates and initialises a push decompression stream. * * Copies @p opts into the new context. The internal multi-threading, * progress-callback, and seekable knobs are forced off (those modes belong * to the @c FILE*-based pipeline). @c block_size, @c file_has_checksum, * and the @c payload / @c decoded buffers are filled in lazily once the * file header is parsed. * * @param[in] opts Decompression options, or @c NULL for full defaults. * @return New stream owned by the caller, or @c NULL on allocation failure. */ zxc_dstream* zxc_dstream_create(const zxc_decompress_opts_t* opts) { zxc_dstream* ds = (zxc_dstream*)ZXC_CALLOC(1, sizeof(*ds)); if (UNLIKELY(!ds)) return NULL; // LCOV_EXCL_LINE if (opts) ds->opts = *opts; if (UNLIKELY(ds->opts.dict || ds->opts.dict_size || ds->opts.dict_huf)) { ZXC_FREE(ds); return NULL; } ds->opts.n_threads = 0; ds->opts.progress_cb = NULL; ds->opts.user_data = NULL; ds->state = DS_NEED_FILE_HEADER; ds->scratch_need = ZXC_FILE_HEADER_SIZE; return ds; } /** * @brief Releases a decompression stream and all internal buffers. * * Safe to call with @c NULL. * * @param[in,out] ds Stream returned by @ref zxc_dstream_create. */ void zxc_dstream_free(zxc_dstream* ds) { if (UNLIKELY(!ds)) return; // LCOV_EXCL_LINE ZXC_FREE(ds->payload); ZXC_FREE(ds->decoded); if (ds->inner_initialized) zxc_cctx_free(&ds->inner); ZXC_FREE(ds); } /** * @brief Returns 1 iff the stream has reached @c DS_DONE. * * @param[in] ds Decompression stream. * @return @c 1 if DONE, @c 0 otherwise (including errored). */ int zxc_dstream_finished(const zxc_dstream* ds) { return (ds && ds->state == DS_DONE) ? 1 : 0; } /** * @brief Returns the suggested input chunk size for the decompressor. * * Before the file header is parsed the call returns * @ref ZXC_BLOCK_SIZE_DEFAULT; afterwards it returns the maximal compressed * block size derived from the negotiated @c block_size. * * @param[in] ds Decompression stream. * @return Suggested input buffer capacity in bytes, or 0 if @p ds is @c NULL. */ size_t zxc_dstream_in_size(const zxc_dstream* ds) { if (UNLIKELY(!ds)) return 0; // LCOV_EXCL_LINE if (ds->block_size == 0) return ZXC_BLOCK_SIZE_DEFAULT; const uint64_t b = zxc_compress_block_bound(ds->block_size); return (b == 0 || b > SIZE_MAX) ? ds->block_size : (size_t)b; } /** * @brief Returns the suggested output chunk size for the decompressor. * * Equals the negotiated @c block_size; before the file header is parsed, * returns @ref ZXC_BLOCK_SIZE_DEFAULT. * * @param[in] ds Decompression stream. * @return Suggested output buffer capacity in bytes, or 0 if @p ds is @c NULL. */ size_t zxc_dstream_out_size(const zxc_dstream* ds) { if (UNLIKELY(!ds)) return 0; // LCOV_EXCL_LINE return ds->block_size == 0 ? ZXC_BLOCK_SIZE_DEFAULT : ds->block_size; } /** * @brief Drains @c ds->decoded[decoded_pos..decoded_size) into @p out. * * Updates @c ds->total_out by the number of bytes copied and, when * @p produced is non-NULL, accumulates the same count into @c *produced * (used by the outer state machine to compute the per-call return value). * * @param[in,out] ds Decompression stream. * @param[in,out] out Caller output buffer. * @param[in,out] produced Optional running count of bytes produced this call. * @return @c 1 once @c decoded is fully drained, @c 0 otherwise. */ static int ds_drain_decoded(zxc_dstream* ds, zxc_outbuf_t* out, size_t* produced) { const size_t avail_out = out->size - out->pos; const size_t avail_dec = ds->decoded_size - ds->decoded_pos; const size_t n = avail_out < avail_dec ? avail_out : avail_dec; if (n) { ZXC_MEMCPY((uint8_t*)out->dst + out->pos, ds->decoded + ds->decoded_pos, n); out->pos += n; ds->decoded_pos += n; ds->total_out += n; if (produced) *produced += n; } return ds->decoded_pos == ds->decoded_size; } /** * @brief Handles the @c DS_NEED_FILE_HEADER state. * * Pulls the 16-byte file header into @c scratch, parses it via * @ref zxc_read_file_header, and lazily allocates the @c payload and * @c decoded buffers (sized from the negotiated @c block_size). The * @c decoded buffer is over-allocated by @ref ZXC_DECOMPRESS_TAIL_PAD bytes to * absorb wild-copy overflow and give the decoder's 4x ML bounds checks their * required tail headroom. Initialises the underlying * decompression context and transitions to @c DS_NEED_BLOCK_HEADER. * * @param[in,out] ds Decompression stream. * @param[in,out] in Caller input buffer. * @return @c 1 if more input is needed, @c 0 to continue the outer loop, * negative @ref zxc_error_t on validation/allocation failure. */ static int ds_handle_need_file_header(zxc_dstream* ds, zxc_inbuf_t* in) { if (!ds_pull_scratch(ds, in)) return 1; size_t bs = 0; int has_csum = 0; const int rc = zxc_read_file_header(ds->scratch, ds->scratch_used, &bs, &has_csum, NULL); if (UNLIKELY(rc != ZXC_OK)) return ds_set_error(ds, rc); // LCOV_EXCL_LINE ds->block_size = bs; ds->file_has_checksum = has_csum; /* Allocate payload + decoded buffers now that block_size is known. */ const uint64_t pb = zxc_compress_block_bound(ds->block_size); // LCOV_EXCL_START if (UNLIKELY(pb == 0 || pb > SIZE_MAX)) return ds_set_error(ds, ZXC_ERROR_OVERFLOW); // LCOV_EXCL_STOP ds->payload_cap = (size_t)pb; ds->payload = (uint8_t*)ZXC_MALLOC(ds->payload_cap); ds->decoded_cap = ds->block_size + ZXC_DECOMPRESS_TAIL_PAD; ds->decoded = (uint8_t*)ZXC_MALLOC(ds->decoded_cap); // LCOV_EXCL_START if (UNLIKELY(!ds->payload || !ds->decoded)) return ds_set_error(ds, ZXC_ERROR_MEMORY); if (UNLIKELY(zxc_cctx_init(&ds->inner, ds->block_size, 0, 0, ds->file_has_checksum && ds->opts.checksum_enabled, 0) != ZXC_OK)) { return ds_set_error(ds, ZXC_ERROR_MEMORY); } // LCOV_EXCL_STOP ds->inner_initialized = 1; ds->state = DS_NEED_BLOCK_HEADER; ds->scratch_used = 0; ds->scratch_need = ZXC_BLOCK_HEADER_SIZE; return 0; } /** * @brief Handles the @c DS_NEED_BLOCK_HEADER state. * * Pulls 8 bytes into @c scratch and parses them as a block header. If the * block is an EOF block, transitions to @c DS_PEEK_TAIL to disambiguate * between an optional SEK index and the file footer. Otherwise, validates * the announced @c comp_size against the @c payload buffer capacity, copies * the parsed header into @c payload (the underlying decoder expects header * + body + optional checksum as a single contiguous frame), and transitions * to @c DS_NEED_BLOCK_PAYLOAD. * * @param[in,out] ds Decompression stream. * @param[in,out] in Caller input buffer. * @return @c 1 if more input is needed, @c 0 to continue the outer loop, * negative @ref zxc_error_t on validation/allocation failure. */ static int ds_handle_need_block_header(zxc_dstream* ds, zxc_inbuf_t* in) { if (!ds_pull_scratch(ds, in)) return 1; const int rc = zxc_read_block_header(ds->scratch, ds->scratch_used, &ds->cur_bh); if (UNLIKELY(rc != ZXC_OK)) return ds_set_error(ds, rc); // LCOV_EXCL_LINE if (ds->cur_bh.block_type == (uint8_t)ZXC_BLOCK_EOF) { /* EOF block: comp_size must be 0; no payload, no checksum. */ if (UNLIKELY(ds->cur_bh.comp_size != 0)) return ds_set_error(ds, ZXC_ERROR_BAD_BLOCK_SIZE); ds->state = DS_PEEK_TAIL; ds->scratch_used = 0; ds->scratch_need = ZXC_BLOCK_HEADER_SIZE; /* sniff */ return 0; } /* Normal data block: read comp_size [+ ZXC_BLOCK_CHECKSUM_SIZE if file-level checksums]. */ const uint64_t need = (uint64_t)ds->cur_bh.comp_size + (ds->file_has_checksum ? (uint64_t)ZXC_BLOCK_CHECKSUM_SIZE : 0u); if (UNLIKELY(need > ds->payload_cap)) return ds_set_error(ds, ZXC_ERROR_BAD_BLOCK_SIZE); /* Feed the full block (header + payload + opt csum) to zxc_decompress_block, * so prefix with the 8-byte header we just parsed. */ ZXC_MEMCPY(ds->payload, ds->scratch, ZXC_BLOCK_HEADER_SIZE); ds->payload_used = ZXC_BLOCK_HEADER_SIZE; ds->payload_need = (size_t)need + ZXC_BLOCK_HEADER_SIZE; // LCOV_EXCL_START if (UNLIKELY(ds->payload_need > ds->payload_cap)) { /* grow */ uint8_t* nb = (uint8_t*)ZXC_REALLOC(ds->payload, ds->payload_need); if (UNLIKELY(!nb)) return ds_set_error(ds, ZXC_ERROR_MEMORY); ds->payload = nb; ds->payload_cap = ds->payload_need; } // LCOV_EXCL_STOP ds->state = DS_NEED_BLOCK_PAYLOAD; return 0; } /** * @brief Push-side entry point: feeds compressed input and drains decoded output. * * Drives the @ref dstream_state_t machine: file header, then per-block * (header + payload + optional checksum) -> decode -> emit, repeated until * the EOF block, optional SEK index block, and file footer have been parsed * and validated. Each call makes as much progress as @p in and @p out * allow; the function is fully reentrant. See the public contract in * @ref zxc_dstream_decompress for full semantics. * * @par End of stream * Once @c DS_VALIDATE_FOOTER succeeds, the stream is in @c DS_DONE; further * calls return @c 0 without consuming input. * * @par Errors * On any negative return the stream becomes errored (sticky); subsequent * calls keep returning the same code until @ref zxc_dstream_free. * * @param[in,out] ds Decompression stream. * @param[in,out] out Caller output buffer. * @param[in,out] in Caller input buffer. * @return @c >0 number of decompressed bytes written into @p out this call, * @c 0 stream complete (DONE) or no progress possible, * @c <0 a @ref zxc_error_t code. */ int64_t zxc_dstream_decompress(zxc_dstream* ds, zxc_outbuf_t* out, zxc_inbuf_t* in) { if (UNLIKELY(!ds || !out || !in || in->pos > in->size || out->pos > out->size || (in->size > in->pos && !in->src) || (out->size > out->pos && !out->dst))) { return ZXC_ERROR_NULL_INPUT; } if (UNLIKELY(ds->state == DS_ERRORED)) return ds->error_code; if (UNLIKELY(ds->state == DS_DONE)) return 0; size_t produced = 0; for (;;) { switch (ds->state) { case DS_NEED_FILE_HEADER: { const int rc = ds_handle_need_file_header(ds, in); if (rc == 1) return (int64_t)produced; if (rc < 0) return rc; break; } case DS_NEED_BLOCK_HEADER: { const int rc = ds_handle_need_block_header(ds, in); if (rc == 1) return (int64_t)produced; if (rc < 0) return rc; break; } case DS_NEED_BLOCK_PAYLOAD: { if (!ds_pull_payload(ds, in)) return (int64_t)produced; ds->state = DS_DECODE_BLOCK; break; } case DS_DECODE_BLOCK: { const int direct = (out->size - out->pos) >= ds->decoded_cap; uint8_t* const ddst = direct ? (uint8_t*)out->dst + out->pos : ds->decoded; const int dsz = zxc_decompress_chunk_wrapper( &ds->inner, ds->payload, ds->payload_used, ddst, ds->decoded_cap); if (UNLIKELY(dsz < 0)) return ds_set_error(ds, dsz); /* If file-level checksum verification is enabled, fold this * block's trailer into the rolling global hash (last * ZXC_BLOCK_CHECKSUM_SIZE bytes of the *raw* block). */ if (ds->opts.checksum_enabled && ds->file_has_checksum && ds->payload_used >= ZXC_BLOCK_CHECKSUM_SIZE) { const uint32_t bh = zxc_le32(ds->payload + ds->payload_used - ZXC_BLOCK_CHECKSUM_SIZE); ds->global_hash = zxc_hash_combine_rotate(ds->global_hash, bh); } if (direct) { out->pos += (size_t)dsz; produced += (size_t)dsz; ds->total_out += (size_t)dsz; ds->decoded_size = 0; ds->decoded_pos = 0; ds->state = DS_NEED_BLOCK_HEADER; ds->scratch_used = 0; ds->scratch_need = ZXC_BLOCK_HEADER_SIZE; break; } ds->decoded_size = (size_t)dsz; ds->decoded_pos = 0; ds->state = DS_EMIT_DECODED; break; } case DS_EMIT_DECODED: { const int done = ds_drain_decoded(ds, out, &produced); if (!done) return (int64_t)produced; ds->state = DS_NEED_BLOCK_HEADER; ds->scratch_used = 0; ds->scratch_need = ZXC_BLOCK_HEADER_SIZE; break; } case DS_PEEK_TAIL: { if (!ds_pull_scratch(ds, in)) return (int64_t)produced; /* Try to interpret as a block header (SEK). */ zxc_block_header_t peek; const int sek_rc = zxc_read_block_header(ds->scratch, ds->scratch_used, &peek); if (sek_rc == ZXC_OK && peek.block_type == (uint8_t)ZXC_BLOCK_SEK) { /* SEK block: skip its payload (peek.comp_size bytes). */ ds->sek_remaining = (size_t)peek.comp_size; ds->state = DS_DRAIN_SEK_PAYLOAD; break; } /* Not SEK -> these 8 bytes are the first 8 of the 12-byte footer. */ ds->state = DS_NEED_FOOTER_REST; ds->scratch_need = ZXC_FILE_FOOTER_SIZE; /* keep first 8, want 4 more */ break; } case DS_DRAIN_SEK_PAYLOAD: { const size_t avail = in->size - in->pos; const size_t n = avail < ds->sek_remaining ? avail : ds->sek_remaining; in->pos += n; ds->sek_remaining -= n; if (ds->sek_remaining > 0) return (int64_t)produced; ds->state = DS_NEED_FOOTER_FULL; ds->scratch_used = 0; ds->scratch_need = ZXC_FILE_FOOTER_SIZE; break; } case DS_NEED_FOOTER_REST: case DS_NEED_FOOTER_FULL: { if (!ds_pull_scratch(ds, in)) return (int64_t)produced; ds->state = DS_VALIDATE_FOOTER; break; } case DS_VALIDATE_FOOTER: { const uint64_t declared = zxc_le64(ds->scratch); if (UNLIKELY(declared != ds->total_out)) return ds_set_error(ds, ZXC_ERROR_CORRUPT_DATA); if (ds->opts.checksum_enabled && ds->file_has_checksum) { const uint32_t fh = zxc_le32(ds->scratch + sizeof(uint64_t)); if (UNLIKELY(fh != ds->global_hash)) return ds_set_error(ds, ZXC_ERROR_BAD_CHECKSUM); } ds->state = DS_DONE; return (int64_t)produced; } case DS_DONE: case DS_ERRORED: return ds->state == DS_ERRORED ? ds->error_code : (int64_t)produced; } } } zxc-0.13.0/src/lib/zxc_seekable.c000066400000000000000000001357051522536370400165610ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file zxc_seekable.c * @brief Seekable archive reader (random-access decompression) and seek table writer. * * The seek table is a standard ZXC block (type = ZXC_BLOCK_SEK) appended * between the EOF block and the file footer. It records the compressed size * of every block (decompressed sizes are derived from the header's block_size), * enabling O(1) lookup + O(block_size) decompression for any byte range. * * On-disk layout of a SEK block: * * [Block Header (8B)] block_type=SEK, block_flags=0, comp_size=N*4 * [N x Entry (4B)] comp_size(u32 LE) per block * * Detection from end of file: * 1. Read file header (first 16 bytes) => block_size * 2. Read file footer (last 12 bytes) => total_decompressed_size * 3. Derive num_blocks = ceil(total_decomp / block_size) * 4. Compute seek block size, read backward to the block header * 5. Validate block_type == ZXC_BLOCK_SEK */ #include "../../include/zxc_seekable.h" #include "../../include/zxc_dict.h" #include "../../include/zxc_error.h" #include "zxc_internal.h" /* ========================================================================= */ /* Platform Threading Layer */ /* ========================================================================= */ // LCOV_EXCL_START - Windows platform layer, not reachable on POSIX CI #if defined(_WIN32) #include /* _beginthreadex */ #include /* Map POSIX threading primitives to Windows equivalents */ typedef HANDLE zxc_thread_t; /** * @brief Trampoline payload bridging the POSIX-style @c void*(*)(void*) worker * signature to the Win32 @c _beginthreadex entry point. * * Heap-allocated by @ref zxc_seek_thread_create and freed by * @ref zxc_seek_thread_entry once the captured callback has started. */ typedef struct { void* (*func)(void*); /* worker to invoke */ void* arg; /* argument forwarded to @c func */ } zxc_seek_thread_arg_t; /** * @brief @c _beginthreadex entry point: unpacks the trampoline payload, frees * it, then runs the captured POSIX-style worker. * * @param[in] p Heap @ref zxc_seek_thread_arg_t handed over by the creator; * ownership transfers to this function. * @return Always 0 (the worker's @c void* result is discarded, matching the * POSIX path which also ignores it). */ static unsigned __stdcall zxc_seek_thread_entry(void* p) { zxc_seek_thread_arg_t* a = (zxc_seek_thread_arg_t*)p; void* (*f)(void*) = a->func; void* arg = a->arg; ZXC_FREE(a); f(arg); return 0; } /** * @brief Spawns a thread running @p fn(@p arg), abstracting @c _beginthreadex. * * Allocates a @ref zxc_seek_thread_arg_t trampoline so the Win32 entry-point * signature can carry a POSIX-style worker; the trampoline is freed by the * thread itself (or here, on a launch failure). * * @param[out] t Receives the created thread handle on success. * @param[in] fn Worker to run on the new thread. * @param[in] arg Opaque argument forwarded to @p fn. * @return 0 on success, @ref ZXC_ERROR_MEMORY on allocation or spawn failure. */ static int zxc_seek_thread_create(zxc_thread_t* t, void* (*fn)(void*), void* arg) { zxc_seek_thread_arg_t* wrapper = ZXC_MALLOC(sizeof(zxc_seek_thread_arg_t)); if (UNLIKELY(!wrapper)) return ZXC_ERROR_MEMORY; wrapper->func = fn; wrapper->arg = arg; uintptr_t handle = _beginthreadex(NULL, 0, zxc_seek_thread_entry, wrapper, 0, NULL); if (UNLIKELY(handle == 0)) { ZXC_FREE(wrapper); return ZXC_ERROR_MEMORY; } *t = (HANDLE)handle; return 0; } /** * @brief Blocks until thread @p t finishes, then releases its handle. * @param[in] t Handle from a successful @ref zxc_seek_thread_create. */ static void zxc_seek_thread_join(zxc_thread_t t) { WaitForSingleObject(t, INFINITE); CloseHandle(t); } /** * @brief Returns the number of logical processors reported by the OS. * @return Online processor count (always >= 1 in practice). */ static int zxc_seek_get_num_procs(void) { SYSTEM_INFO si; GetSystemInfo(&si); return (int)si.dwNumberOfProcessors; } // LCOV_EXCL_STOP #else /* POSIX */ #include #include typedef pthread_t zxc_thread_t; /** * @brief Spawns a thread running @p fn(@p arg) via @c pthread_create. * @param[out] t Receives the created thread handle on success. * @param[in] fn Worker to run on the new thread. * @param[in] arg Opaque argument forwarded to @p fn. * @return 0 on success, @ref ZXC_ERROR_MEMORY if the thread cannot be created. */ static int zxc_seek_thread_create(zxc_thread_t* t, void* (*fn)(void*), void* arg) { return pthread_create(t, NULL, fn, arg) == 0 ? 0 : ZXC_ERROR_MEMORY; } /** * @brief Blocks until thread @p t finishes (its @c void* result is discarded). * @param[in] t Handle from a successful @ref zxc_seek_thread_create. */ static void zxc_seek_thread_join(zxc_thread_t t) { pthread_join(t, NULL); } /** * @brief Returns the number of online logical processors. * @return @c _SC_NPROCESSORS_ONLN, clamped to a minimum of 1 if the query fails. */ static int zxc_seek_get_num_procs(void) { const long n = sysconf(_SC_NPROCESSORS_ONLN); return (n > 0) ? (int)n : 1; } #endif /* _WIN32 */ /* ========================================================================= */ /* Seek Table Writer */ /* ========================================================================= */ /** * @brief Byte size of a seek table holding @p num_blocks entries. * * Public API (declared in @c zxc_seekable.h): one block header plus * @p num_blocks fixed-size entries. Use it to size the destination buffer * before @ref zxc_write_seek_table. * * @param[in] num_blocks Number of block entries the table will hold. * @return Total table size in bytes (block header + entries). */ size_t zxc_seek_table_size(const uint32_t num_blocks) { return ZXC_BLOCK_HEADER_SIZE + (size_t)num_blocks * ZXC_SEEK_ENTRY_SIZE; } /** * @brief Serialises a seek table (a @c ZXC_BLOCK_SEK block) into @p dst. * * Public API; full contract in @c zxc_seekable.h. Emits the standard ZXC block * header followed by one little-endian @c u32 compressed-size entry per block. * * @param[out] dst Destination buffer. * @param[in] dst_capacity Capacity of @p dst in bytes. * @param[in] comp_sizes Array of @p num_blocks compressed block sizes. * @param[in] num_blocks Number of blocks (and entries) to write. * @return Bytes written on success, or a negative @ref zxc_error_t * (@ref ZXC_ERROR_OVERFLOW, @ref ZXC_ERROR_DST_TOO_SMALL, * @ref ZXC_ERROR_NULL_INPUT). */ int64_t zxc_write_seek_table(uint8_t* dst, const size_t dst_capacity, const uint32_t* comp_sizes, const uint32_t num_blocks) { if (UNLIKELY(num_blocks > UINT32_MAX / ZXC_SEEK_ENTRY_SIZE)) return ZXC_ERROR_OVERFLOW; const size_t total = zxc_seek_table_size(num_blocks); if (UNLIKELY(dst_capacity < total)) return ZXC_ERROR_DST_TOO_SMALL; if (UNLIKELY(!dst || !comp_sizes)) return ZXC_ERROR_NULL_INPUT; const uint32_t payload_size = num_blocks * ZXC_SEEK_ENTRY_SIZE; /* Write standard ZXC block header */ const zxc_block_header_t bh = { .block_type = ZXC_BLOCK_SEK, .block_flags = 0, .reserved = 0, .comp_size = payload_size}; const int hdr_res = zxc_write_block_header(dst, dst_capacity, &bh); if (UNLIKELY(hdr_res < 0)) return hdr_res; uint8_t* p = dst + hdr_res; /* Write entries: comp_size(4) only */ for (uint32_t i = 0; i < num_blocks; i++) { zxc_store_le32(p, comp_sizes[i]); p += sizeof(uint32_t); } return (int64_t)(p - dst); } /* ========================================================================= */ /* Seekable Reader (Opaque Handle) */ /* ========================================================================= */ struct zxc_seekable_s { /* Source - exactly one of {src, reader.read_at} is set. The FILE* * variant (see zxc_seekable_file.c) routes through the reader callback * by wrapping pread() in its own ctx; from this struct's perspective it * is indistinguishable from any other caller-supplied reader. */ const uint8_t* src; uint64_t src_size; zxc_reader_t reader; /* user-supplied callback reader; read_at == NULL when unused */ /* Heap-allocated reader context owned by the seekable handle, freed in * zxc_seekable_free. Set by thin wrappers (e.g. zxc_seekable_open_file) * via zxc_seekable_attach_owned_ctx. NULL when the caller manages * reader.ctx lifetime themselves. */ void* owned_reader_ctx; /* Parsed seek table */ uint32_t num_blocks; uint32_t* comp_sizes; /* array[num_blocks] */ uint64_t* comp_offsets; /* prefix-sum: byte offset in compressed file per block */ uint64_t total_decomp; /* total decompressed size (from footer) */ /* File header info - block_size is always a power of 2 in [4KB, 2MB], * fits in 21 bits. */ uint32_t block_size; int file_has_checksums; uint32_t expected_dict_id; /* dict_id from the file header; 0 = no dictionary */ /* Reusable decompression context (single-threaded path only) */ zxc_cctx_t dctx; int dctx_initialized; /* Dictionary (owned copy, freed in zxc_seekable_free). */ uint8_t* dict; size_t dict_size; /* Shared literal Huffman table (owned copy; meaningful when has_dict_huf). */ uint8_t dict_huf[ZXC_HUF_TABLE_SIZE]; int has_dict_huf; }; /** * @brief Parses the seek table from raw bytes at the end of the archive. * * Detection (backward from end): * 1. Read file header => block_size * 2. Read file footer => total_decomp_size * 3. Derive num_blocks = ceil(total_decomp_size / block_size) * 4. Compute expected seek block position, validate block_type == SEK * 5. Read comp_sizes; derive decomp_sizes from block_size * * @param[in] data Pointer to the whole in-memory archive. * @param[in] data_size Archive size in bytes. * @return A newly allocated handle (free via @ref zxc_seekable_free), or NULL * if the buffer is too small or the seek table is missing / malformed. */ static zxc_seekable* zxc_seekable_parse(const uint8_t* data, const size_t data_size) { /* Minimum: file_header(16) + eof_block(8) + seek_block_header(8) * + file_footer(12) = 44 */ const size_t MIN_SEEKABLE_SIZE = ZXC_FILE_HEADER_SIZE + ZXC_BLOCK_HEADER_SIZE + ZXC_BLOCK_HEADER_SIZE + ZXC_FILE_FOOTER_SIZE; if (UNLIKELY(data_size < MIN_SEEKABLE_SIZE)) return NULL; /* Step 1: validate file header => block_size */ size_t block_size_sz = 0; int file_has_chk = 0; uint32_t header_dict_id = 0; if (UNLIKELY(zxc_read_file_header(data, data_size, &block_size_sz, &file_has_chk, &header_dict_id) != ZXC_OK)) return NULL; // LCOV_EXCL_LINE const uint32_t block_size = (uint32_t)block_size_sz; if (UNLIKELY(block_size == 0)) return NULL; // LCOV_EXCL_LINE /* Step 2: read total decompressed size from the file footer */ const uint8_t* const footer_ptr = data + data_size - ZXC_FILE_FOOTER_SIZE; const uint64_t total_decomp = zxc_le64(footer_ptr); /* A value of 0 means empty file - no seek table */ if (UNLIKELY(total_decomp == 0)) return NULL; /* Step 3: derive num_blocks = ceil(total_decomp / block_size) */ const uint64_t num_blocks_64 = (total_decomp + block_size - 1) / block_size; if (UNLIKELY(num_blocks_64 > UINT32_MAX)) return NULL; const uint32_t num_blocks = (uint32_t)num_blocks_64; /* Step 4: compute seek block position and validate. */ const uint64_t entries_total_64 = num_blocks_64 * ZXC_SEEK_ENTRY_SIZE; if (UNLIKELY(entries_total_64 > SIZE_MAX - ZXC_BLOCK_HEADER_SIZE)) return NULL; const size_t entries_total = (size_t)entries_total_64; const size_t seek_block_total = ZXC_BLOCK_HEADER_SIZE + entries_total; if (UNLIKELY(seek_block_total + ZXC_FILE_FOOTER_SIZE > data_size)) return NULL; const uint8_t* const seek_block_start = data + data_size - ZXC_FILE_FOOTER_SIZE - seek_block_total; if (UNLIKELY(seek_block_start < data)) return NULL; /* Read and validate SEK block header */ zxc_block_header_t bh; if (UNLIKELY(zxc_read_block_header(seek_block_start, seek_block_total, &bh) != ZXC_OK)) return NULL; if (UNLIKELY(bh.block_type != ZXC_BLOCK_SEK)) return NULL; if (UNLIKELY(bh.comp_size != (uint32_t)entries_total)) return NULL; /* Step 5: allocate handle and parse entries */ zxc_seekable* const s = (zxc_seekable*)ZXC_CALLOC(1, sizeof(zxc_seekable)); // LCOV_EXCL_START if (UNLIKELY(!s)) return NULL; // LCOV_EXCL_STOP s->num_blocks = num_blocks; s->block_size = block_size; s->file_has_checksums = file_has_chk; s->expected_dict_id = header_dict_id; s->src = data; s->src_size = (uint64_t)data_size; /* Allocate arrays */ s->comp_sizes = (uint32_t*)ZXC_CALLOC(num_blocks, sizeof(uint32_t)); s->comp_offsets = (uint64_t*)ZXC_CALLOC((size_t)num_blocks + 1, sizeof(uint64_t)); // LCOV_EXCL_START if (UNLIKELY(!s->comp_sizes || !s->comp_offsets)) { zxc_seekable_free(s); return NULL; } // LCOV_EXCL_STOP s->total_decomp = total_decomp; /* Parse comp_sizes and build compressed prefix sums. * Validate each comp_size against data_size to prevent prefix-sum overflow * and out-of-bounds reads during decompression. */ const uint8_t* ep = seek_block_start + ZXC_BLOCK_HEADER_SIZE; uint64_t comp_acc = ZXC_FILE_HEADER_SIZE; /* blocks start after file header */ for (uint32_t i = 0; i < num_blocks; i++) { s->comp_sizes[i] = zxc_le32(ep); ep += sizeof(uint32_t); /* Reject entries below minimum (block header) or larger than the file */ if (UNLIKELY(s->comp_sizes[i] < ZXC_BLOCK_HEADER_SIZE || s->comp_sizes[i] > (uint64_t)data_size)) { zxc_seekable_free(s); return NULL; } s->comp_offsets[i] = comp_acc; comp_acc += s->comp_sizes[i]; /* Reject if cumulative offset exceeds file size (inconsistent table) */ if (UNLIKELY(comp_acc > (uint64_t)data_size)) { // LCOV_EXCL_START zxc_seekable_free(s); return NULL; // LCOV_EXCL_STOP } } s->comp_offsets[num_blocks] = comp_acc; /* Verify prefix-sum lands exactly at the EOF block position. * Expected layout: [header 16][data blocks][EOF 8][SEK block][footer 12] * So comp_acc (end of data blocks) + EOF(8) == seek_block_start. */ const uint64_t expected_eof_offset = (uint64_t)(seek_block_start - data) - ZXC_BLOCK_HEADER_SIZE; if (UNLIKELY(comp_acc != expected_eof_offset)) { // LCOV_EXCL_START zxc_seekable_free(s); return NULL; // LCOV_EXCL_STOP } /* Validate that an actual EOF block header exists at the computed offset */ if (UNLIKELY(comp_acc + ZXC_BLOCK_HEADER_SIZE > data_size)) { // LCOV_EXCL_START zxc_seekable_free(s); return NULL; // LCOV_EXCL_STOP } zxc_block_header_t eof_bh; if (UNLIKELY(zxc_read_block_header(data + comp_acc, ZXC_BLOCK_HEADER_SIZE, &eof_bh) != ZXC_OK || eof_bh.block_type != ZXC_BLOCK_EOF)) { // LCOV_EXCL_START zxc_seekable_free(s); return NULL; // LCOV_EXCL_STOP } return s; } /** * @brief Opens a seekable archive held entirely in a memory buffer. * * Public API; see @c zxc_seekable.h. Thin guard around * @ref zxc_seekable_parse, which detects and validates the trailing seek table. * * @param[in] src Pointer to the whole compressed archive. * @param[in] src_size Archive size in bytes. * @return A handle to release with @ref zxc_seekable_free, or NULL on bad input * or a missing / malformed seek table. */ zxc_seekable* zxc_seekable_open(const void* src, const size_t src_size) { if (UNLIKELY(!src || src_size == 0)) return NULL; return zxc_seekable_parse((const uint8_t*)src, src_size); } /* zxc_seekable_open_file (FILE* variant) lives in zxc_seekable_file.c. It * builds a zxc_reader_t over pread() and delegates to * zxc_seekable_open_reader below, keeping this translation unit free of any * dependency. */ /** * @brief Opens a seekable archive over a caller-supplied random-access reader. * * Public API; see @c zxc_seekable.h. Reads the file header, footer and seek * block through @p r->read_at (the FILE* variant wraps @c pread this way), * validates the SEK block, and builds the per-block compressed-offset prefix * sums. Unlike @ref zxc_seekable_open the archive is never mapped whole; only * the metadata is read up front. * * @param[in] r Reader descriptor (@c read_at and @c size must be set). * @return A handle to release with @ref zxc_seekable_free, or NULL on bad input, * a short read, or a malformed seek table. */ zxc_seekable* zxc_seekable_open_reader(const zxc_reader_t* r) { if (UNLIKELY(!r || !r->read_at || r->size == 0)) return NULL; /* Minimum: file_header(16) + eof_block(8) + seek_block_header(8) * + file_footer(12) = 44 */ const uint64_t MIN_SEEKABLE_SIZE = ZXC_FILE_HEADER_SIZE + ZXC_BLOCK_HEADER_SIZE + ZXC_BLOCK_HEADER_SIZE + ZXC_FILE_FOOTER_SIZE; if (UNLIKELY(r->size < MIN_SEEKABLE_SIZE)) return NULL; /* Read file header => block_size */ uint8_t header[ZXC_FILE_HEADER_SIZE]; if (UNLIKELY(r->read_at(r->ctx, header, ZXC_FILE_HEADER_SIZE, 0) != (int64_t)ZXC_FILE_HEADER_SIZE)) return NULL; size_t bs_sz = 0; int fhc = 0; uint32_t header_dict_id = 0; if (UNLIKELY(zxc_read_file_header(header, ZXC_FILE_HEADER_SIZE, &bs_sz, &fhc, &header_dict_id) != ZXC_OK)) return NULL; // LCOV_EXCL_LINE const uint32_t bs = (uint32_t)bs_sz; if (UNLIKELY(bs == 0)) return NULL; /* Read footer => total_decomp_size */ uint8_t footer_buf[ZXC_FILE_FOOTER_SIZE]; if (UNLIKELY(r->read_at(r->ctx, footer_buf, ZXC_FILE_FOOTER_SIZE, r->size - ZXC_FILE_FOOTER_SIZE) != (int64_t)ZXC_FILE_FOOTER_SIZE)) return NULL; const uint64_t total_decomp = zxc_le64(footer_buf); if (UNLIKELY(total_decomp == 0)) return NULL; /* Derive num_blocks = ceil(total_decomp / block_size) */ const uint64_t num_blocks_64 = (total_decomp + bs - 1) / bs; if (UNLIKELY(num_blocks_64 > UINT32_MAX)) return NULL; const uint32_t num_blocks = (uint32_t)num_blocks_64; /* Guard against size_t multiplication overflow */ const uint64_t entries_total_64 = (uint64_t)num_blocks * ZXC_SEEK_ENTRY_SIZE; if (UNLIKELY(entries_total_64 > SIZE_MAX - ZXC_BLOCK_HEADER_SIZE)) return NULL; /* Read the full seek block */ const size_t seek_block_total = ZXC_BLOCK_HEADER_SIZE + (size_t)entries_total_64; if (UNLIKELY(seek_block_total + ZXC_FILE_FOOTER_SIZE > r->size)) return NULL; uint8_t* const seek_buf = (uint8_t*)ZXC_MALLOC(seek_block_total); if (UNLIKELY(!seek_buf)) return NULL; const uint64_t seek_offset = r->size - ZXC_FILE_FOOTER_SIZE - (uint64_t)seek_block_total; if (UNLIKELY(r->read_at(r->ctx, seek_buf, seek_block_total, seek_offset) != (int64_t)seek_block_total)) { // LCOV_EXCL_START ZXC_FREE(seek_buf); return NULL; // LCOV_EXCL_STOP } /* Validate SEK block header */ zxc_block_header_t bh; if (UNLIKELY(zxc_read_block_header(seek_buf, seek_block_total, &bh) != ZXC_OK) || bh.block_type != ZXC_BLOCK_SEK || bh.comp_size != (uint32_t)entries_total_64) { // LCOV_EXCL_START ZXC_FREE(seek_buf); return NULL; // LCOV_EXCL_STOP } /* Build seekable handle */ zxc_seekable* const s = (zxc_seekable*)ZXC_CALLOC(1, sizeof(zxc_seekable)); if (UNLIKELY(!s)) { // LCOV_EXCL_START ZXC_FREE(seek_buf); return NULL; // LCOV_EXCL_STOP } s->reader = *r; s->src = NULL; s->src_size = r->size; s->num_blocks = num_blocks; s->block_size = bs; s->file_has_checksums = fhc; s->expected_dict_id = header_dict_id; s->comp_sizes = (uint32_t*)ZXC_CALLOC(num_blocks, sizeof(uint32_t)); s->comp_offsets = (uint64_t*)ZXC_CALLOC((size_t)num_blocks + 1, sizeof(uint64_t)); if (UNLIKELY(!s->comp_sizes || !s->comp_offsets)) { // LCOV_EXCL_START ZXC_FREE(seek_buf); zxc_seekable_free(s); return NULL; // LCOV_EXCL_STOP } s->total_decomp = total_decomp; /* Parse comp_sizes and build prefix sums; validate against archive size. */ const uint8_t* ep = seek_buf + ZXC_BLOCK_HEADER_SIZE; uint64_t comp_acc = ZXC_FILE_HEADER_SIZE; for (uint32_t i = 0; i < num_blocks; i++) { s->comp_sizes[i] = zxc_le32(ep); ep += sizeof(uint32_t); if (UNLIKELY(s->comp_sizes[i] < ZXC_BLOCK_HEADER_SIZE || s->comp_sizes[i] > r->size)) { // LCOV_EXCL_START ZXC_FREE(seek_buf); zxc_seekable_free(s); return NULL; // LCOV_EXCL_STOP } s->comp_offsets[i] = comp_acc; comp_acc += s->comp_sizes[i]; if (UNLIKELY(comp_acc > r->size)) { // LCOV_EXCL_START ZXC_FREE(seek_buf); zxc_seekable_free(s); return NULL; // LCOV_EXCL_STOP } } s->comp_offsets[num_blocks] = comp_acc; ZXC_FREE(seek_buf); return s; } /** * @brief Number of blocks in the archive. * @param[in] s Seekable handle (may be NULL). * @return Block count, or 0 if @p s is NULL. */ uint32_t zxc_seekable_get_num_blocks(const zxc_seekable* s) { return s ? s->num_blocks : 0; } /** * @brief Total decompressed size of the archive. * @param[in] s Seekable handle (may be NULL). * @return Decompressed size in bytes, or 0 if @p s is NULL. */ uint64_t zxc_seekable_get_decompressed_size(const zxc_seekable* s) { return s ? s->total_decomp : 0; } /** * @brief Compressed byte size of a given block. * @param[in] s Seekable handle (may be NULL). * @param[in] block_idx Zero-based block index. * @return Compressed size in bytes, or 0 if @p s is NULL or @p block_idx is * out of range. */ uint32_t zxc_seekable_get_block_comp_size(const zxc_seekable* s, const uint32_t block_idx) { if (UNLIKELY(!s || block_idx >= s->num_blocks)) return 0; return s->comp_sizes[block_idx]; } /** * @brief Decompressed byte size of a given block. * * Every block decompresses to @c block_size except the last, which holds the * remainder of @c total_decomp. * * @param[in] s Seekable handle (may be NULL). * @param[in] block_idx Zero-based block index. * @return Decompressed size in bytes, or 0 if @p s is NULL or @p block_idx is * out of range. */ uint32_t zxc_seekable_get_block_decomp_size(const zxc_seekable* s, const uint32_t block_idx) { if (UNLIKELY(!s || block_idx >= s->num_blocks)) return 0; const uint64_t start = (uint64_t)block_idx * (uint64_t)s->block_size; const uint64_t remaining = s->total_decomp - start; return (remaining >= (uint64_t)s->block_size) ? s->block_size : (uint32_t)remaining; } /* ========================================================================= */ /* Random-Access Decompression */ /* ========================================================================= */ /** * @brief Maps a decompressed @p offset to its containing block index (O(1)). * @param[in] block_size Fixed decompressed block size (a power of two). * @param[in] offset Absolute decompressed byte offset. * @return Zero-based index of the block that holds @p offset. */ static uint32_t zxc_seek_find_block(const uint32_t block_size, const uint64_t offset) { return (uint32_t)(offset / (uint64_t)block_size); } /** * @brief Decompressed start offset of block @p idx (O(1)). * @param[in] block_size Fixed decompressed block size. * @param[in] idx Zero-based block index. * @return Absolute decompressed byte offset where block @p idx begins. */ static uint64_t zxc_seek_decomp_offset(const uint32_t block_size, const uint32_t idx) { return (uint64_t)idx * (uint64_t)block_size; } /** * @brief Decompressed size of block @p idx (O(1)). * * Returns @p block_size for every block except the last, which holds the * remainder of @p total_decomp. * * @param[in] block_size Fixed decompressed block size. * @param[in] total_decomp Total decompressed archive size. * @param[in] idx Zero-based block index. * @return Decompressed byte size of block @p idx. */ static uint32_t zxc_seek_decomp_size(const uint32_t block_size, const uint64_t total_decomp, const uint32_t idx) { const uint64_t start = (uint64_t)idx * (uint64_t)block_size; const uint64_t remaining = total_decomp - start; return (remaining >= (uint64_t)block_size) ? block_size : (uint32_t)remaining; } /** * @brief Reads a compressed block into @p buf from the memory buffer or reader. * * Single-threaded path: copies from @c s->src in buffer mode, otherwise calls * @c s->reader.read_at (which also backs the FILE* variant). * * @param[in] s Seekable handle. * @param[in] block_idx Zero-based block index to read. * @param[out] buf Destination buffer. * @param[in] buf_cap Capacity of @p buf in bytes. * @return The block's compressed byte count on success, or a negative * @ref zxc_error_t (@ref ZXC_ERROR_DST_TOO_SMALL, * @ref ZXC_ERROR_SRC_TOO_SMALL, @ref ZXC_ERROR_IO). */ static int zxc_seek_read_block(const zxc_seekable* s, const uint32_t block_idx, uint8_t* buf, const size_t buf_cap) { const uint64_t off = s->comp_offsets[block_idx]; const uint32_t csz = s->comp_sizes[block_idx]; if (UNLIKELY(csz > buf_cap)) return ZXC_ERROR_DST_TOO_SMALL; if (s->src) { /* Buffer mode */ if (UNLIKELY(off + csz > s->src_size)) return ZXC_ERROR_SRC_TOO_SMALL; ZXC_MEMCPY(buf, s->src + off, csz); } else if (s->reader.read_at) { /* Caller-supplied reader (also covers the FILE* variant, which * provides a pread-backed callback from zxc_seekable_file.c). */ const int64_t r = s->reader.read_at(s->reader.ctx, buf, csz, off); if (UNLIKELY(r != (int64_t)csz)) return (r < 0) ? (int)r : ZXC_ERROR_IO; } else { return ZXC_ERROR_NULL_INPUT; // LCOV_EXCL_LINE } return (int)csz; } /** * @brief Decompresses the byte range [@p offset, @p offset + @p len) into @p dst. * * Public API; full contract in @c zxc_seekable.h. Maps the range to its block * span via O(1) division, decodes each covered block through a reusable, * lazily-initialised, dictionary-aware context, and copies out only the * requested sub-range. Single-threaded; see @ref zxc_seekable_decompress_range_mt * for the parallel variant. * * @param[in,out] s Seekable handle (carries the reusable context). * @param[out] dst Destination buffer. * @param[in] dst_capacity Capacity of @p dst (must be >= @p len). * @param[in] offset Absolute decompressed start offset. * @param[in] len Number of decompressed bytes to produce. * @return @p len on success, or a negative @ref zxc_error_t. */ int64_t zxc_seekable_decompress_range(zxc_seekable* s, void* dst, const size_t dst_capacity, const uint64_t offset, const size_t len) { if (UNLIKELY(len == 0)) return 0; if (UNLIKELY(!s || !dst)) return ZXC_ERROR_NULL_INPUT; if (UNLIKELY(dst_capacity < len)) return ZXC_ERROR_DST_TOO_SMALL; if (UNLIKELY(offset + len > s->total_decomp)) return ZXC_ERROR_SRC_TOO_SMALL; if (UNLIKELY(s->expected_dict_id != 0 && (!s->dict || s->dict_size == 0))) return ZXC_ERROR_DICT_REQUIRED; /* Initialize decompression context on first use */ if (!s->dctx_initialized) { // LCOV_EXCL_START if (UNLIKELY(zxc_cctx_init(&s->dctx, (size_t)s->block_size, 0, 0, 0, s->dict_size) != ZXC_OK)) return ZXC_ERROR_MEMORY; // LCOV_EXCL_STOP if (UNLIKELY(zxc_cctx_attach_dict_huf(&s->dctx, s->has_dict_huf ? s->dict_huf : NULL) != ZXC_OK)) { // LCOV_EXCL_START zxc_cctx_free(&s->dctx); return ZXC_ERROR_CORRUPT_DATA; // LCOV_EXCL_STOP } s->dctx_initialized = 1; if (s->dict_size > 0) ZXC_MEMCPY(s->dctx.dict_buffer, s->dict, s->dict_size); } s->dctx.dict_size = s->dict_size; /* work_buf is pre-sized to block_size + ZXC_DECOMPRESS_TAIL_PAD by the * matching zxc_cctx_init above. */ const size_t work_sz = (size_t)s->block_size + ZXC_DECOMPRESS_TAIL_PAD; /* Find block range - O(1) division */ const uint32_t blk_start = zxc_seek_find_block(s->block_size, offset); const uint32_t blk_end = zxc_seek_find_block(s->block_size, offset + len - 1); uint8_t* out = (uint8_t*)dst; size_t remaining = len; /* Allocate read buffer for compressed blocks */ size_t max_comp = 0; for (uint32_t bi = blk_start; bi <= blk_end; bi++) { if (s->comp_sizes[bi] > max_comp) max_comp = s->comp_sizes[bi]; } uint8_t* const read_buf = (uint8_t*)ZXC_MALLOC(max_comp + ZXC_PAD_SIZE); if (UNLIKELY(!read_buf)) return ZXC_ERROR_MEMORY; // LCOV_EXCL_LINE for (uint32_t bi = blk_start; bi <= blk_end; bi++) { /* Read compressed block data */ const int read_res = zxc_seek_read_block(s, bi, read_buf, max_comp + ZXC_PAD_SIZE); if (UNLIKELY(read_res < 0)) { // LCOV_EXCL_START ZXC_FREE(read_buf); return read_res; // LCOV_EXCL_STOP } /* Decompress the block: when a dictionary is active, decode into the * cctx-owned dict_buffer (which has dict content prepended) so that * match copies referencing dictionary bytes resolve naturally. */ uint8_t* dec_dst = s->dctx.dict_buffer ? s->dctx.dict_buffer + s->dict_size : s->dctx.work_buf; const int dec_res = zxc_decompress_chunk_wrapper(&s->dctx, read_buf, (size_t)read_res, dec_dst, work_sz); if (UNLIKELY(dec_res < 0)) { // LCOV_EXCL_START ZXC_FREE(read_buf); return dec_res; // LCOV_EXCL_STOP } /* Calculate which portion of this block's decompressed data we need */ const uint64_t blk_decomp_start = zxc_seek_decomp_offset(s->block_size, bi); const size_t skip = (offset > blk_decomp_start) ? (size_t)(offset - blk_decomp_start) : 0; if (UNLIKELY((size_t)dec_res < skip)) { // LCOV_EXCL_START ZXC_FREE(read_buf); return ZXC_ERROR_CORRUPT_DATA; // LCOV_EXCL_STOP } const size_t avail = (size_t)dec_res - skip; const size_t copy = (avail < remaining) ? avail : remaining; ZXC_MEMCPY(out, dec_dst + skip, copy); out += copy; remaining -= copy; } ZXC_FREE(read_buf); return (int64_t)len; } /* ========================================================================= */ /* Multi-Threaded Random-Access Decompression (Fork-Join) */ /* ========================================================================= */ /** * @brief Per-block job descriptor for multi-threaded decompression. * * Each worker thread receives a pointer to one of these, performs the read + * decompress + memcpy sequence, and writes the result code into @c result. * The main thread inspects @c result after join. */ typedef struct { const zxc_seekable* s; /* shared handle (read-only) */ uint32_t block_idx; /* block to decompress */ uint8_t* dst; /* output pointer within caller's buffer */ size_t skip; /* bytes to skip at start of decompressed block */ size_t copy_len; /* bytes to copy into dst */ int result; /* 0 = OK, < 0 = error */ } zxc_seek_mt_job_t; /** * @brief Thread-safe block read backing the multi-threaded path. * * Like @ref zxc_seek_read_block but safe to call concurrently: buffer mode uses * @c memcpy on const data, reader mode relies on a positioned (pread-style) * callback that carries its own offset. * * @param[in] s Seekable handle (read-only). * @param[in] block_idx Zero-based block index to read. * @param[out] buf Destination buffer. * @param[in] buf_cap Capacity of @p buf in bytes. * @return The block's compressed byte count on success, or a negative * @ref zxc_error_t. */ static int zxc_seek_read_block_mt(const zxc_seekable* s, const uint32_t block_idx, uint8_t* buf, const size_t buf_cap) { const uint64_t off = s->comp_offsets[block_idx]; const uint32_t csz = s->comp_sizes[block_idx]; if (UNLIKELY(csz > buf_cap)) return ZXC_ERROR_DST_TOO_SMALL; if (s->src) { /* Buffer mode - memcpy is inherently thread-safe on const data */ if (UNLIKELY(off + csz > s->src_size)) return ZXC_ERROR_SRC_TOO_SMALL; ZXC_MEMCPY(buf, s->src + off, csz); } else if (s->reader.read_at) { /* Reader callback - caller-supplied read_at must be thread-safe. * The FILE* variant (zxc_seekable_file.c) installs a pread-backed * callback that is naturally thread-safe. */ const int64_t r = s->reader.read_at(s->reader.ctx, buf, csz, off); if (UNLIKELY(r != (int64_t)csz)) return (r < 0) ? (int)r : ZXC_ERROR_IO; } else { return ZXC_ERROR_NULL_INPUT; // LCOV_EXCL_LINE } return (int)csz; } /** * @struct zxc_seek_mt_stripe_t * @brief Per-thread stripe descriptor for multi-threaded decompression. * * Each worker owns the job subset {first, first+stride, first+2*stride, ...} * of the shared @c jobs array and reuses one decompression context, one * dictionary copy and one read buffer across all of them, amortising what * would otherwise be per-block costs (context init, dict memcpy, malloc and * a thread spawn per block). Stripes are pairwise disjoint, so workers * touch distinct jobs and distinct output ranges and need no * synchronisation beyond the final join. * * Written by @ref zxc_seekable_decompress_range_mt before the fork phase and * read-only for the worker (@ref zxc_seek_mt_worker); results travel through * the jobs themselves (@c zxc_seek_mt_job_t::result). * * @var zxc_seek_mt_stripe_t::jobs * Job array shared by all workers; this worker only reads/writes the * entries of its own stripe. * @var zxc_seek_mt_stripe_t::num_jobs * Total number of jobs in @c jobs (stripe iteration bound). * @var zxc_seek_mt_stripe_t::first * Index of this worker's first job (equals its worker index, in * [0, @c stride)). * @var zxc_seek_mt_stripe_t::stride * Stripe step between consecutive jobs of this worker; equals the * worker-thread count. */ typedef struct { zxc_seek_mt_job_t* jobs; uint32_t num_jobs; uint32_t first; uint32_t stride; } zxc_seek_mt_stripe_t; /** * @brief Marks every job of a stripe with @p code (setup-failure path). * * @param[in,out] st Stripe whose jobs to mark. * @param[in] code Negative @ref zxc_error_t value. */ static void zxc_seek_mt_fail_stripe(zxc_seek_mt_stripe_t* st, const int code) { for (uint32_t i = st->first; i < st->num_jobs; i += st->stride) st->jobs[i].result = code; } /** * @brief Worker thread entry point for multi-threaded seekable decompression. * * Sets up its context, dictionary copy and read buffer once, then for each * block of its stripe: read (thread-safe pread), decompress, copy the * requested sub-range into the caller's output. The dict prefix survives * across blocks because the decoder never writes below its dst. * * Each job's outcome goes into its @c result (read by the main thread after * join); on error the worker abandons the rest of its stripe. * * @param[in,out] arg Pointer to this worker's @ref zxc_seek_mt_stripe_t. * @return Always NULL (result codes are reported via the jobs). */ static void* zxc_seek_mt_worker(void* arg) { zxc_seek_mt_stripe_t* const st = (zxc_seek_mt_stripe_t*)arg; zxc_seek_mt_job_t* const jobs = st->jobs; const zxc_seekable* const s = jobs[st->first].s; /* Thread-local decompression context (mode=0 for decompress-only) */ zxc_cctx_t dctx; if (UNLIKELY(zxc_cctx_init(&dctx, (size_t)s->block_size, 0, 0, 0, s->dict_size) != ZXC_OK)) { // LCOV_EXCL_START zxc_seek_mt_fail_stripe(st, ZXC_ERROR_MEMORY); return NULL; // LCOV_EXCL_STOP } if (UNLIKELY(zxc_cctx_attach_dict_huf(&dctx, s->has_dict_huf ? s->dict_huf : NULL) != ZXC_OK)) { // LCOV_EXCL_START zxc_cctx_free(&dctx); zxc_seek_mt_fail_stripe(st, ZXC_ERROR_CORRUPT_DATA); return NULL; // LCOV_EXCL_STOP } const size_t work_sz = (size_t)s->block_size + ZXC_DECOMPRESS_TAIL_PAD; uint8_t* const dict_work = dctx.dict_buffer; if (dict_work) ZXC_MEMCPY(dict_work, s->dict, s->dict_size); /* Read buffer sized for the largest compressed block of the stripe. */ size_t max_csz = 0; for (uint32_t i = st->first; i < st->num_jobs; i += st->stride) { const uint32_t csz = s->comp_sizes[jobs[i].block_idx]; if (csz > max_csz) max_csz = csz; } uint8_t* const read_buf = (uint8_t*)ZXC_MALLOC(max_csz + ZXC_PAD_SIZE); if (UNLIKELY(!read_buf)) { // LCOV_EXCL_START zxc_cctx_free(&dctx); zxc_seek_mt_fail_stripe(st, ZXC_ERROR_MEMORY); return NULL; // LCOV_EXCL_STOP } for (uint32_t i = st->first; i < st->num_jobs; i += st->stride) { zxc_seek_mt_job_t* const job = &jobs[i]; const int read_res = zxc_seek_read_block_mt(s, job->block_idx, read_buf, max_csz + ZXC_PAD_SIZE); if (UNLIKELY(read_res < 0)) { // LCOV_EXCL_START job->result = read_res; break; // LCOV_EXCL_STOP } /* Decompress: use dict bounce buffer when dictionary is active */ uint8_t* dec_dst = dict_work ? dict_work + s->dict_size : dctx.work_buf; const int dec_res = zxc_decompress_chunk_wrapper(&dctx, read_buf, (size_t)read_res, dec_dst, work_sz); if (UNLIKELY(dec_res < 0)) { // LCOV_EXCL_START job->result = dec_res; break; // LCOV_EXCL_STOP } if (UNLIKELY((size_t)dec_res < job->skip + job->copy_len)) { // LCOV_EXCL_START job->result = ZXC_ERROR_CORRUPT_DATA; break; // LCOV_EXCL_STOP } /* Copy the requested portion directly into the caller's output buffer */ ZXC_MEMCPY(job->dst, dec_dst + job->skip, job->copy_len); job->result = 0; } ZXC_FREE(read_buf); zxc_cctx_free(&dctx); return NULL; } /** * @brief Multi-threaded variant of @ref zxc_seekable_decompress_range. * * Public API; full contract in @c zxc_seekable.h. Plans one job per covered * block (each with its own thread-local context and read buffer) and runs them * fork-join in waves of up to @p n_threads. Falls back to the single-threaded * path for trivial spans. @p n_threads == 0 auto-detects the core count. * * @param[in,out] s Seekable handle (read-only during the parallel phase). * @param[out] dst Destination buffer. * @param[in] dst_capacity Capacity of @p dst (must be >= @p len). * @param[in] offset Absolute decompressed start offset. * @param[in] len Number of decompressed bytes to produce. * @param[in] n_threads Worker thread count; 0 = auto-detect. * @return @p len on success, or the first negative @ref zxc_error_t observed. */ int64_t zxc_seekable_decompress_range_mt(zxc_seekable* s, void* dst, const size_t dst_capacity, const uint64_t offset, const size_t len, int n_threads) { if (UNLIKELY(len == 0)) return 0; if (UNLIKELY(!s || !dst)) return ZXC_ERROR_NULL_INPUT; if (UNLIKELY(dst_capacity < len)) return ZXC_ERROR_DST_TOO_SMALL; if (UNLIKELY(offset + len > s->total_decomp)) return ZXC_ERROR_SRC_TOO_SMALL; if (UNLIKELY(s->expected_dict_id != 0 && (!s->dict || s->dict_size == 0))) return ZXC_ERROR_DICT_REQUIRED; /* Find block range - O(1) division */ const uint32_t blk_start = zxc_seek_find_block(s->block_size, offset); const uint32_t blk_end = zxc_seek_find_block(s->block_size, offset + len - 1); const uint32_t num_jobs = blk_end - blk_start + 1; /* Auto-detect thread count (0 = use all available cores) */ if (n_threads == 0) n_threads = zxc_seek_get_num_procs(); /* Fallback to single-threaded path for trivial cases */ if (n_threads <= 1 || num_jobs <= 1) { return zxc_seekable_decompress_range(s, dst, dst_capacity, offset, len); } /* Cap threads to number of blocks and max limit */ if ((uint32_t)n_threads > num_jobs) n_threads = (int)num_jobs; if (n_threads > ZXC_MAX_THREADS) n_threads = ZXC_MAX_THREADS; /* Allocate job descriptors */ zxc_seek_mt_job_t* const jobs = (zxc_seek_mt_job_t*)ZXC_CALLOC(num_jobs, sizeof(zxc_seek_mt_job_t)); if (UNLIKELY(!jobs)) return ZXC_ERROR_MEMORY; // LCOV_EXCL_LINE /* Plan jobs: compute skip, copy_len, and dst pointer for each block */ uint8_t* out = (uint8_t*)dst; size_t remaining = len; for (uint32_t i = 0; i < num_jobs; i++) { const uint32_t bi = blk_start + i; const uint64_t blk_decomp_start = zxc_seek_decomp_offset(s->block_size, bi); const size_t skip = (offset > blk_decomp_start) ? (size_t)(offset - blk_decomp_start) : 0; const size_t blk_decomp_sz = zxc_seek_decomp_size(s->block_size, s->total_decomp, bi); if (UNLIKELY(blk_decomp_sz < skip)) { // LCOV_EXCL_START ZXC_FREE(jobs); return ZXC_ERROR_CORRUPT_DATA; // LCOV_EXCL_STOP } const size_t avail = blk_decomp_sz - skip; const size_t copy = (avail < remaining) ? avail : remaining; jobs[i].s = s; jobs[i].block_idx = bi; jobs[i].dst = out; jobs[i].skip = skip; jobs[i].copy_len = copy; jobs[i].result = 0; out += copy; remaining -= copy; } /* Launch one persistent worker per thread */ zxc_thread_t* const threads = (zxc_thread_t*)ZXC_MALLOC((size_t)n_threads * sizeof(zxc_thread_t)); zxc_seek_mt_stripe_t* const stripes = (zxc_seek_mt_stripe_t*)ZXC_MALLOC((size_t)n_threads * sizeof(zxc_seek_mt_stripe_t)); if (UNLIKELY(!threads || !stripes)) { // LCOV_EXCL_START ZXC_FREE(threads); ZXC_FREE(stripes); ZXC_FREE(jobs); return ZXC_ERROR_MEMORY; // LCOV_EXCL_STOP } int launched = 0; for (int t = 0; t < n_threads; t++) { stripes[t].jobs = jobs; stripes[t].num_jobs = num_jobs; stripes[t].first = (uint32_t)t; stripes[t].stride = (uint32_t)n_threads; if (UNLIKELY(zxc_seek_thread_create(&threads[t], zxc_seek_mt_worker, &stripes[t]) != 0)) { // LCOV_EXCL_START /* Failed to create thread - mark its stripe as errored; already * launched workers keep running and are joined below. */ zxc_seek_mt_fail_stripe(&stripes[t], ZXC_ERROR_MEMORY); continue; // LCOV_EXCL_STOP } launched++; threads[launched - 1] = threads[t]; } /* Join phase */ for (int t = 0; t < launched; t++) zxc_seek_thread_join(threads[t]); ZXC_FREE(threads); ZXC_FREE(stripes); /* Report the first error in job order, if any. */ int64_t result = (int64_t)len; for (uint32_t i = 0; i < num_jobs; i++) { if (jobs[i].result < 0) { result = (int64_t)jobs[i].result; break; } } ZXC_FREE(jobs); return result; } /** * @brief Releases a seekable handle and every resource it owns. * * Public API; see @c zxc_seekable.h. Tears down the reusable context, the seek * arrays (comp sizes / offsets), the owned dictionary copy and any attached * reader context. NULL-safe. * * @param[in] s Seekable handle to release (may be NULL). */ void zxc_seekable_free(zxc_seekable* s) { if (UNLIKELY(!s)) return; if (s->dctx_initialized) zxc_cctx_free(&s->dctx); ZXC_FREE(s->dict); ZXC_FREE(s->comp_sizes); ZXC_FREE(s->comp_offsets); ZXC_FREE(s->owned_reader_ctx); ZXC_FREE(s); } /** * @brief Installs the dictionary needed to decode a dict-compressed archive. * * Public API; full contract in @c zxc_seekable.h. Validates the dict_id against * the file header, then takes an owned copy of @p dict (and the optional shared * literal Huffman table @p dict_huf). Drops any context already built so the * [dict | decode] bounce buffer is re-carved on the next decompress. * * @param[in,out] s Seekable handle. * @param[in] dict Dictionary bytes. * @param[in] dict_size Dictionary length (<= @c ZXC_DICT_SIZE_MAX). * @param[in] dict_huf Optional shared literal Huffman table, or NULL. * @return @ref ZXC_OK, or a negative @ref zxc_error_t * (@ref ZXC_ERROR_DICT_TOO_LARGE, @ref ZXC_ERROR_DICT_MISMATCH, ...). */ int zxc_seekable_set_dict(zxc_seekable* s, const void* dict, const size_t dict_size, const void* dict_huf) { if (UNLIKELY(!s || !dict || dict_size == 0)) return ZXC_ERROR_NULL_INPUT; if (UNLIKELY(dict_size > ZXC_DICT_SIZE_MAX)) return ZXC_ERROR_DICT_TOO_LARGE; if (UNLIKELY(s->expected_dict_id != 0 && zxc_dict_id(dict, dict_size, (const uint8_t*)dict_huf) != s->expected_dict_id)) return ZXC_ERROR_DICT_MISMATCH; ZXC_FREE(s->dict); s->dict = NULL; s->dict_size = 0; s->has_dict_huf = 0; s->dict = (uint8_t*)ZXC_MALLOC(dict_size); if (UNLIKELY(!s->dict)) return ZXC_ERROR_MEMORY; ZXC_MEMCPY(s->dict, dict, dict_size); s->dict_size = dict_size; if (dict_huf) { ZXC_MEMCPY(s->dict_huf, dict_huf, ZXC_HUF_TABLE_SIZE); s->has_dict_huf = 1; } /* The [dict | decode] bounce buffer is carved into the dctx workspace. * Drop any context built without it (or for a different dict size) so it is * re-carved with the new dict on the next decompress. */ if (s->dctx_initialized) { zxc_cctx_free(&s->dctx); s->dctx_initialized = 0; } return ZXC_OK; } /** * @brief Transfers ownership of a heap reader context to the handle. * * Cross-TU hook (declared in @c zxc_internal.h): @p ctx is released via * @c ZXC_FREE when @ref zxc_seekable_free runs. Used by * @ref zxc_seekable_open_file so its allocated reader state outlives the open * call. NULL-safe on @p s. * * @param[in,out] s Seekable handle (may be NULL). * @param[in] ctx Heap pointer to hand over; freed by @ref zxc_seekable_free. */ void zxc_seekable_attach_owned_ctx(zxc_seekable* s, void* ctx) { if (s) s->owned_reader_ctx = ctx; } zxc-0.13.0/tests/000077500000000000000000000000001522536370400135505ustar00rootroot00000000000000zxc-0.13.0/tests/format/000077500000000000000000000000001522536370400150405ustar00rootroot00000000000000zxc-0.13.0/tests/format/README.md000066400000000000000000000110421522536370400163150ustar00rootroot00000000000000# Golden-File Format Conformance Suite Byte-frozen reference archives that pin the **ZXC on-disk wire format** (`docs/FORMAT.md`, format version 6). Unlike `conformance/`, which checks that a decoder produces the right *output*, this suite asserts the exact *bytes* and *structure* of compressed files, field by field. ## Contents ``` golden_cases.h Single source of truth: input generators + per-file expectations gen_golden.c Maintainer tool that (re)produces golden/*.zxc deterministically test_golden.c Structural validator (ctest target: `format_golden`) golden/*.zxc The frozen archives — never edit by hand golden.sha256 Byte-stability manifest (sha256sum format) ``` ## The corpus Each file maps onto sections of `docs/FORMAT.md §5` and the integrity fields: | File | Exercises | |----------------------------|-----------------------------------------------------------------| | `01_empty_eof_only.zxc` | EOF block only + footer (zero-length source) | | `02_block_raw.zxc` | RAW block (incompressible input) | | `03_block_ghi.zxc` | GHI block (level <= 2) | | `04_block_glo.zxc` | GLO block (level >= 3) | | `05_block_glo_huffman.zxc` | GLO block with the Huffman literal section (`enc_lit == 2`, §5.2.1) | | `06_checksum_per_block.zxc`| Per-block checksum + non-zero global stream hash | | `07_multiple_blocks.zxc` | Multiple data blocks → rolling global hash (§7.3) | | `08_seekable_table.zxc` | SEK seek-table block (§5.5) | | `09_block_dict.zxc` | Raw in-memory dictionary (no shared table): `HAS_DICTIONARY` flag + content-only `dict_id` (§3.1, §12.3) | | `10_glo_offset16.zxc` | GLO block with 16-bit offsets (`enc_off == 0`, large-distance matches) | | `11_glo_rle.zxc` | GLO block with RLE literal encoding (`enc_lit == 1`) | | `12_glo_huffman_dict.zxc` | Dictionary archive with the shared-table Huffman literal section (`enc_lit == 3`, §5.2.2) | ## What the validator checks For every file, `test_golden.c` walks the bytes and verifies, against `docs/FORMAT.md`: - **File header (§3):** magic, version, chunk-size code, flags (incl. `HAS_CHECKSUM` / `HAS_DICTIONARY`), the reserved bytes — or the `dict_id` when a dictionary is used — and the 16-bit header CRC (`zxc_hash16`). - **Each block header (§4):** type, flags, reserved byte, `comp_size` bounds, and the 8-bit header CRC (`zxc_hash8`). - **Every payload type (§5):** RAW; GLO/GHI header + section descriptors (sizes must tile the payload); the Huffman literal section flag. - **EOF block (§5.4):** `comp_size == 0`. - **Optional SEK block (§5.5):** type, CRC8, `comp_size == n_blocks * 4`, and each entry equal to the physical size of its data block. - **Per-block checksum (§7.2):** recomputed (`zxc_checksum`) over the compressed payload and matched against the trailing 4 bytes. - **Global stream hash (§7.3):** reconstructed with `zxc_hash_combine_rotate` from the per-block checksums and matched against the footer. - **File footer (§8):** trailing 12 bytes, original source size, global hash. Each file is also decompressed and compared byte-for-byte against its deterministically regenerated input. ## Running ```sh cmake -S . -B build cmake --build build --target zxc_format_golden_test ctest --test-dir build -R format_golden --output-on-failure ``` ## Byte stability `golden.sha256` is the frozen reference. The [`Golden Format Stability`](../../.github/workflows/golden.yml) CI job runs `sha256sum -c` against it, so **any single changed byte in any golden file fails CI**. The job also checks that the file set and the manifest stay in sync. ## Regenerating (intentional format changes only) The golden files are static artifacts; CI never re-compresses them. Regenerate only when the wire format changes on purpose: ```sh cmake --build build --target zxc_golden_gen ./build/zxc_golden_gen tests/format/golden sha256sum tests/format/golden/*.zxc | sort -k2 > tests/format/golden.sha256 ``` Then review the binary diff carefully and commit the `.zxc` files together with the refreshed `golden.sha256` in the same change. To add a new case, append an entry to `GOLDEN_CASES[]` in `golden_cases.h` (input generator + options + expectations), regenerate, and refresh the manifest. ## License BSD-3-Clause. Same as the ZXC library. zxc-0.13.0/tests/format/gen_golden.c000066400000000000000000000072321522536370400173110ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /* * Golden-file generator. * * Regenerates every tests/format/golden/.zxc from the deterministic * specifications in golden_cases.h. This is a MAINTAINER tool, not part of the * normal test run: CI never re-compresses the golden files, it only validates * and hashes the frozen bytes (see test_golden.c and .github/workflows/golden.yml). * * Usage: * zxc_golden_gen # defaults to "tests/format/golden" * * After regenerating, refresh the byte-stability manifest from the repo root: * sha256sum tests/format/golden/[asterisk].zxc > tests/format/golden.sha256 * * Review the resulting binary diff carefully: any change here is, by design, a * wire-format change that must be intentional. */ #include #include #include #include #include #ifdef _MSC_VER #include #include #endif #include "../../include/zxc_buffer.h" #include "../../include/zxc_error.h" #include "golden_cases.h" /* Open a file for binary writing with owner-only permissions (0600), mirroring * tests/test_common.c::create_restricted_file. Avoids fopen()'s world-writable * 0666 default flagged by CodeQL. */ static FILE *open_restricted_wb(const char *path) { #ifdef _MSC_VER int fd = -1; _sopen_s(&fd, path, _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE); return fd >= 0 ? _fdopen(fd, "wb") : NULL; #else const int fd = open(path, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR); return fd >= 0 ? fdopen(fd, "wb") : NULL; #endif } static int write_file(const char *path, const uint8_t *data, size_t size) { FILE *f = open_restricted_wb(path); if (!f) { fprintf(stderr, " cannot open %s for writing\n", path); return -1; } if (size && fwrite(data, 1, size, f) != size) { fprintf(stderr, " short write to %s\n", path); fclose(f); return -1; } fclose(f); return 0; } int main(int argc, char **argv) { const char *dir = (argc > 1) ? argv[1] : "tests/format/golden"; int failures = 0; printf("Generating %zu golden files into %s/\n", (size_t)GOLDEN_CASE_COUNT, dir); for (size_t i = 0; i < GOLDEN_CASE_COUNT; i++) { const golden_case_t *gc = &GOLDEN_CASES[i]; uint8_t *input = NULL; size_t in_size = gc->make_input(&input); size_t cap = (size_t)zxc_compress_bound(in_size) + 4096; uint8_t *out = (uint8_t *)malloc(cap); if (!out) { fprintf(stderr, " OOM for %s\n", gc->name); free(input); failures++; continue; } zxc_compress_opts_t opts = gc->opts; if (gc->use_dict_huf) opts.dict_huf = gc_dict_huf_table(); int64_t csize = zxc_compress(input, in_size, out, cap, &opts); if (csize <= 0) { fprintf(stderr, " FAIL: compress(%s) -> %s\n", gc->name, zxc_error_name((int)csize)); failures++; } else { char path[1024]; snprintf(path, sizeof path, "%s/%s.zxc", dir, gc->name); if (write_file(path, out, (size_t)csize) == 0) printf(" wrote %-14s %8lld bytes\n", gc->name, (long long)csize); else failures++; } free(out); free(input); } if (failures) { fprintf(stderr, "Generation FAILED (%d error(s)).\n", failures); return 1; } printf("Done. Remember to refresh tests/format/golden.sha256.\n"); return 0; } zxc-0.13.0/tests/format/golden.sha256000066400000000000000000000025541522536370400172500ustar00rootroot00000000000000be33ea8c1ab9b7a1f3c27422a07e1c68a692e31ad70708670c3232c8b2b2ed34 tests/format/golden/01_empty_eof_only.zxc de8acc8befc30037e6cd5240da7607277b0aef17c157d252f49b1d568441ca52 tests/format/golden/02_block_raw.zxc f687667eb43e3d399b38a0a88470df48e78f1c3541026bd0cfed09533001ed06 tests/format/golden/03_block_ghi.zxc e649ef6031af453a5ed7d0f1eb8c83b666ea36d94fcae24d09ffedc6a3dc2338 tests/format/golden/04_block_glo.zxc 4c51f01cf8e4d372377ecd8bfb0860647f5af74292992a5162de6e2737cec613 tests/format/golden/05_block_glo_huffman.zxc b4d4ffabb8a33245e335d5e5bc9f8066845de7a088a2745bd6af9ced153f0afe tests/format/golden/06_checksum_per_block.zxc f62c9a2af87b00b21114cd1df44d9a55f51a424b1dd3a35d1b7598e9b49986ae tests/format/golden/07_multiple_blocks.zxc e1607574afb153e0a1744e26111d7c79cf1139ceea05bd57ee784c7b40945c43 tests/format/golden/08_seekable_table.zxc 4e7e69900df44d763ca81b0bbde156b5f5131bbb01a9bf7463d97b2b04afa318 tests/format/golden/09_block_dict.zxc 2804841a960698105b2223b5a64007daf2dd185c8ee9057a37aaff25af983741 tests/format/golden/10_glo_offset16.zxc a6656cb14626336f462a8841dd08ae955889a40ed75db0cb68e3d8a8dc45fb0b tests/format/golden/11_glo_rle.zxc 3c3d2f971ea81fc8161d4a05683714bd4b4c574982b1c1b7e1e76caff9b2eff1 tests/format/golden/12_glo_huffman_dict.zxc c591faabceac76ff90f6f8863652ac0be57858a893fd90801350a043302e838d tests/format/golden/13_glo_huffman_wide.zxc zxc-0.13.0/tests/format/golden/000077500000000000000000000000001522536370400163105ustar00rootroot00000000000000zxc-0.13.0/tests/format/golden/01_empty_eof_only.zxc000066400000000000000000000000441522536370400223640ustar00rootroot00000000000000.]zxc-0.13.0/tests/format/golden/02_block_raw.zxc000066400000000000000000000100541522536370400213020ustar00rootroot00000000000000.]|qcoߞpaC Qz#t\bjvԕ>+cPYsaSW aeP"båĮ*h6QЖ|"eTri1hY,ehoM`'.xZ\y4iQcRΎx:FLG^QazbVK^fBANSncW~$l~(CU6lp\ӯP߫c9Mxu"ܒ><XFkRΥݽy/[Ed})mkKП~Q:('2k- ѝ43dBMJ 2H~|>u*VJ~{{xoӼ9"J+=  wlx} Y Tɉ#t<8H3yd&H ^i%_JNjszet}0M?va %&a̪Ar[qmk.nߟ!DXH{ܵ[^;s (\G@f;>JY"cǏHDgTnXPQ5瞽 99MlO=@NdL]1) kQEMLga^[Ae{\h6N[򄇵kf?b9@ 6 V$= *P-$﯇rcRDEN5H%h Zd@DOpfpB95pţ|i>y6O$1mQfc,e2CwТ.(}]JI(,.|Z> OeU8\Ҧb+W=UG<@2h/WEh-|caNLy0>CbDne-hEY~Ll>)pʢǞTp$$N">ώO4AGݶ>C!ũcܩv?e.T pibds';[lZFH]ujݐx)YI%5҅9rB-p ~&y g8畒ZUvt< AD\#K`!Rυ|C}w{Mtb3YW]G5|wS#kPj9Ӑ؍Y]}3؛*ZjWF`t64Ѽ繁P+8z+՜I>&0] #p~kl+CHv^1Ss4LkZ^EU߼b/~@ s'Tj#oB~[aвRz%UI{ 'L&D-r w c+H}t~o2=7iq]yYOM jkB s>}L 1a7=~r˹puTo_H{_ Vu1.2ߥ 93_+]ʄ8Lz${İbߟ)p QJSBesHӥr=>0ǧvœEǐM n_no7m{Rr8h -f]tlkХ 9X!}EZ+E p|y-v&E嘜KsyrI-{`mp'vJL~mZ1`uJ?|Y?q'-j1m]x&~۵xs;e7iaMQ&y"N: HBAz]*Xز|F`F V&*.]țsVS;]KV+4uz|ݶ"hnÒ?T;r@v[mе5%70u˺/xURDqHj,^L,y $nM>Bު?UqivܱR'n>xa z~搭ʵ9, :V~H-r;dQ1q&=zUie 7yȖj/'v׻y]_᭄i>BWs4rυUGp\VrE7zb!f~*L|bUxCf;zO|AHHzd?P?Ӈ毨t= dp(zs*"kUVardXv!t!sf,330&p!lrA?\]Fe \!^ɐ]l`|}}P4UF1H]Sunסg,D1eep +A*` HG}>Cح̍3T+\5Ѐ94yx`nb}Q'[ۙvTg(N0˔m[RqݙM1^p4 VAY;*1T¸_&H={O_Я-M3/ٛ?'Q&M!0 QE=icb+Ie .Ù^+b,-"a`Or[Ɖ AIh"4ݨW1Ǣ%CIH )7&89)hnN! 39dkuF q>`J'= i NͦXtAIhe7f3 ~NXSWRL=Z&MZ$ /ݝJ4K\/riHCyWk[r]Gc ` &O|*rQc5˦*1dTe?Xm58yD7%A=WwW8Hh*,s?NI8C#Nr Ԡ^/ijgow9 b-^svиh(PLM76[Kɪ8_^_f3ΦZ J g~§R -%pETMJCAфm9[Vة`~ Wh'Mr}(>RQ Ra  5Ko5ֈd.*oFɈSM6NC͎v{fmQGz]1w_ޟQHL-o/X_լ/G5gzn8kQ$~]e3>} 9@tAg?gpΣ`O%Bó+'I1r.d7Bx{g;h֩0t5_p՚iTc$D–M<j0hѠjhy>~'$Wv/2gC֖{C/yeȘQ|}Eϡ][apt(nQu[L:F$C5AE#r3Nɻzxc-0.13.0/tests/format/golden/03_block_ghi.zxc000066400000000000000000000003251522536370400212610ustar00rootroot00000000000000.]/{{{the quick brown fox jumps over the lazy dog. ZXC compresses repeated patterns efficiently and decompresses them very fast. z{z zxc-0.13.0/tests/format/golden/04_block_glo.zxc000066400000000000000000000003341522536370400212740ustar00rootroot00000000000000.]{{{the quick brown fox jumps over the lazy dog. ZXC compresses repeated patterns efficiently and decompresses them very fast. zl} zxc-0.13.0/tests/format/golden/05_block_glo_huffman.zxc000066400000000000000000000132541522536370400230060ustar00rootroot00000000000000.]g444gg 2C4!2RLa|IM{AL cB " v3" TY<Aj )b"-\AJKuBhgȣ-#dTxjYi#B"S c B\ "H3D hp`A$ޏT )xOCE#B/3" (NH5]` %Ҭp|D 9NѠ9Ě 0$" I Yib7㢂ȃp*P0AR;c\ ,ENi}KXTā@A)i*XL7耂 c\(KbNp'%TN*5|TA n +]RƢĵ23,甏(( ɱZ@T8 )6i *Q2a4J6&"ɹ6PP Ec:12"ꕔVn^vBC7e)K.B (3Z.P >I 0,,=AJ2)AAKbMYt*>d8RU2BJxX~zrH,z+E~lHLFfk#4٫b@T0Z1M4b5tJҟ4q }p3)G`3hdVjqd+?{>XB&ډŠ3X(5b"n`ģ \8#)AHWXHi`̩v /#@,;!M [e q0VYuVM^G g"xW6quc`<"^MNWka(')x,s5vQ]\§2fp 3(#M#lFlF2cx`G |'Z=>- %UD!W-e"PbĠ?\_SȂG$NbiJX0DvIApI)얇,#C5+Z:oF*X $(EId4#ŻTڊ)L6 ̪No0E\nm#&dW]~S͆"SH'[HYMGKHpdPËgHe:ieA=11p}-`h9`-BۀT@2e#z[GDئ4J=FL^Lj tˀ1ahBfT%(zl H +%JP f1C$1/@y.( үA %Tdȁ"$`bOkԄ3f¦u8X\" eɵJPd*Fq&5((kGv W9GC W:Z7!ZM(6qHHKtz)dK)T;4dHRXˇsOMA*>K⫃ͩCAUtY~\ *B rD!)/@sAuVc=vb@X&!"Md!Nƌ`@,BH YGt(;%O71DJQ0L @!GAtBgj!M 0!Lay# 1 )GcE IkaalANc;F/\R2 c$*"1p3Wh`t'b 0ѝSb K`uPOZ(RkI<2 ̓N8v 3!Ǟe&D \; { ("TSە=Y,iT WV`2 T#2C42fl@?h(#H$ i \|GLuZnH E*VpKɠU_>tlր&qXV>niE3ӈmY"hYҸsΝ"7∠"bX]sŐܕYWd6ū ?of75 \`ekB:u%𳃜{K>;<߭oh, Lv_[2-ah/=hk@\ G1{ֆ4X,G8oGҕFτM,TljԦ$€!$.Â$;QF/N)yBZ͔er.[#Ƈ?NfI[*+ʛf1c=Tf6=onDvDƔI x礼Jh w "E= ($Jh@$a bBPh*>bH q(0VRv\)$_B@ *HY z`zb< qD  IRI {v%BB dM *A2FF$;3$'P`L=uu XN6 s  8fhD$ H0rQ8c6I~dvδ )CH2ADlD/e(" )` S SDLwt)~}f=^'cV*CofOc~ךBïY-[mA.]۵#a~ҷ/_oOQoJ^ d]/qD:$" "8 Mg ,ȷȲ~-d1%#Tc33C%sdsӄ3sӣC##Cs#53C3Ctec3c#4s$#d#ec43#3S#t#C7scscCScC3Cc%sFDtde3Cs6Tcsc#s#3BoQuaIg] c#w [t es e  =u, ^pz=> f T @n  =# IzlEwN) E ) ~ &  9s a ~ 54I `& i 9 +  _lqqB4 6I?_ ]#S N|i Qw(*`o'O  ?Ju)(SbW  *5v T,*+jw# ;&q'6,g "" [* +e$ T)9 ,e6[% )+G # H`!U* %p-A 9)T2-0# 2;.5Y =-&;(M,N {+"47# >+g%H25$3*@1v 9 $.&TB**') Gi, *C/ /5 g4//g"AVAX }2` Q=%u&I8 *%_O $ %;)EFC %- "&--iaC , (4  I & nZ -A 1# !$EE ;%?E)rS*  0M?  @zxc-0.13.0/tests/format/golden/06_checksum_per_block.zxc000066400000000000000000000003401522536370400231620ustar00rootroot00000000000000.>]{{{the quick brown fox jumps over the lazy dog. ZXC compresses repeated patterns efficiently and decompresses them very fast. zl}' 'zxc-0.13.0/tests/format/golden/07_multiple_blocks.zxc000066400000000000000000000021611522536370400225340ustar00rootroot00000000000000. ?{{{the quick brown fox jumps over the lazy dog. ZXC compresses repeated patterns efficiently and decompresses them very fast. zl=G1pppzy dog. ZXC compresses repeated patterns efficiently and dethem very fast. the quick brown fox jumps over the la.z,&= t{{{rns efficiently and decompresses them very fast. the quick brown fox jumps over the lazy dog. ZXC compresses repeated pattezl=vA .ppp very fast. the quick brown fox jumps over the lazy dog. ZXC compresses repeated patterns efficiently and dethemO.z]=\@3kkk over the lazy dog. ZXC compresses repeated patterns efficiently and dethem very fast.quick brown fox jumps.[z8={{{peated patterns efficiently and decompresses them very fast. the quick brown fox jumps over the lazy dog. ZXC compresses rezl ~O SLzxc-0.13.0/tests/format/golden/08_seekable_table.zxc000066400000000000000000000022211522536370400222640ustar00rootroot00000000000000. ?{{{the quick brown fox jumps over the lazy dog. ZXC compresses repeated patterns efficiently and decompresses them very fast. zl=G1pppzy dog. ZXC compresses repeated patterns efficiently and dethem very fast. the quick brown fox jumps over the la.z,&= t{{{rns efficiently and decompresses them very fast. the quick brown fox jumps over the lazy dog. ZXC compresses repeated pattezl=vA .ppp very fast. the quick brown fox jumps over the lazy dog. ZXC compresses repeated patterns efficiently and dethemO.z]=\@3kkk over the lazy dog. ZXC compresses repeated patterns efficiently and dethem very fast.quick brown fox jumps.[z8={{{peated patterns efficiently and decompresses them very fast. the quick brown fox jumps over the lazy dog. ZXC compresses rezl ~O% SLzxc-0.13.0/tests/format/golden/09_block_dict.zxc000066400000000000000000000001611522536370400214410ustar00rootroot00000000000000.@8E  4242/profile eqs@=zxc-0.13.0/tests/format/golden/10_glo_offset16.zxc000066400000000000000000000101431522536370400216330ustar00rootroot00000000000000.]7cL(6O8Hrnd:p~>3@L5Juʅތ>l3RBn6 4kf:H𐽳y"K*{%IEB&|X(C#NMv@sVo(kx*,+:D|tqŚ(`a?|[dGC솊q^rI!=vXߔhhc=o=`qX.KS_ e0݋0i3-5d4XaZHt>lnU/qAC65rt Oz`v xM `JPD;v<)R7u? ABy΢+N@p,m [ GԢ\Dp|.S%XB;Oc# (0\w7 ԑ*h78mH .qk \ΞIv=܃п`LaX& Xhw|Q<\T@./7Tc߲긌_s6{8]@qG^p5|gVRRGX*C%Dv`cVBme$H_KחdO?JC8Jm`UKsȀ 0Tyu]S=d2f vQ&J^&۷ RBHFK^RG%Xf0LLieN1բRÿMk^lwRdyqrF-LzȱQ"$)/db1zH] `H#mv@alj'K &Ov.d{ +fǜa[5> 9|OB]IZ`X gd֫P͸B* 1w:0F62! 'gV@mn#h% ^T,5 !caUiY{X)!zݬlW2 7,TAnvK : +`=(95K/ :H{*DlFq|zL(6O8Hrnd:p~>3@L5Juʅތ>l3RBn6 4kf:H𐽳y"K*{%IEB&|X(C#NMv@sVo(kx*,+:D|tqŚ(`a?|[dGC솊q^rI!=vXߔhhc=o=`qX.KS_ e0݋0i3-5d4XaZHt>lnU/qAC65rt Oz`v xM `JPD;v<)R7u? ABy΢+N@p,m [ GԢ\Dp|.S%XB;Oc# (0\w7 ԑ*h78mH .qk \ΞIv=܃п`LaX& Xhw|Q<\T@./7Tc߲긌_s6{8]@qG^p5|gVRRGX*C%Dv`cVBme$H_KחdO?JC8Jm`UKsȀ 0Tyu]S=d2f vQ&J^&۷ RBHFK^RG%Xf0LLieN1բRÿMk^lwRdyqrF-LzȱQ"$)/db1zH] `H#mv@alj'K &Ov.d{ +fǜa[5> 9|OB]IZ`X gd֫P͸B* 1w:0F62! 'gV@mn#h% ^T,5 !caUiY{X)!zݬlW2 7,TAnvK : +`=(95K/ :H{*DlFq|zL(6O8Hrnd:p~>3@L5Juʅތ>l3RBn6 4kf:H𐽳y"K*{%IEB&|X(C#NMv@sVo(kx*,+:D|tqŚ(`a?|[dGC솊q^rI!=vXߔhhc=o=`qX.KS_ e0݋0i3-5d4XaZHt>lnU/qAC65rt Oz`v xM `JPD;v<)R7u? ABy΢+N@p,m [ GԢ\Dp|.S%XB;Oc# (0\w7 ԑ*h78mH .qk \ΞIv=܃п`LaX& Xhw|Q<\T@./7Tc߲긌_s6{8]@qG^p5|gVRRGX*C%Dv`cVBme$H_KחdO?JC8Jm`UKsȀ 0Tyu]S=d2f vQ&J^&۷ RBHFK^RG%Xf0LLieN1բRÿMk^lwRdyqrF-LzȱQ"$)/db1zH] `H#mv@alj'K &Ov.d{ +fǜa[5> 9|OB]IZ`X gd֫P͸B* 1w:0F62! 'gV@mn#h% ^T,5 !caUiY{X)!zݬlW2 7,TAnvK : +`=(95K/ :H{*DlFq|zL(6O8Hrnd:p~>3@L5Juʅތ>l3RBn6 4kf:H𐽳y"K*{%IEB&|X(C#NMv@sVo(kx*,+:D|tqŚ(`a?|[dGC솊q^rI!=vXߔhhc=o=`qX.KS_ e0݋0i3-5d4XaZHt>lnU/qAC65rt Oz`v xM `JPD;v<)R7u? ABy΢+N@p,m [ GԢ\Dp|.S%XB;Oc# (0\w7 ԑ*h78mH .qk \ΞIv=܃п`LaX& Xhw|Q<\T@./7Tc߲긌_s6{8]@qG^p5|gVRRGX*C%Dv`cVBme$H_KחdO?JC8Jm`UKsȀ 0Tyu]S=d2f vQ&J^&۷ RBHFK^RG%Xf0LLieN1բRÿMk^lwRdyqrF-LzȱQ"$)/db1zH] `H#mv@alj'K &Ov.d{ +fǜa[5> 9|OB]IZ`X gd֫P͸B* 1w:0F62! 'gV@mn#h% ^T,5 !caUiY{X)!zݬlW2 7,TAnvK : +`=(95K/ :H{*DlFq|z?? zxc-0.13.0/tests/format/golden/11_glo_rle.zxc000066400000000000000000000166561522536370400210000ustar00rootroot00000000000000.]wubbuuOπ# ߀rԀ΀H8؀&T>DRՀ0;8g"뀪Xjڀ[&UK=ȀA97`쀪$Ve}ƀ:M̀ۀ!Ҁ3€.Z%L Ndk|񴀪〪рqKx+h/P}:6o˹* s]瞀}Ѐ4 :._ހÀ'iWmG݀pwBӀS ^_(. H F1퀪b)Q7䫀Qt }?zִt8fl^E;"MyyRa)%HiWYrCBL^"4'Q 5O vh=M2XD^\aasR)\ *M(zKV"}wXQNjec5RdFHRe-ECI(DU tPO˲Y-#l&tCh_;,B(fQ͹z"M4 9|B rӥVJ߫Y4gfC$U芄cTM0Rb*)zJFGGI]SBls](-~Y6#;?a;[Y>-wUl >=<xf2C)`:nb\>16^|00I@b# ~t n H>  R; d !?x EF6 { u;L+ Z b ?W+b+ S q'  f"6^ f1h6 l"  t 1  &f  'u",1TE   |Zh3 V3CA +A XoK  s ;r *e[u /S6?J 2 '~ } C P$h  9M4x ` -y +S+ D v F X?D,-K `): J+|- 0^ %3 f IV @^? , t jiyyC  ;^B 4{A s5  B:^Z "TIS ^ 6F6i3QOcB @[L!E;5" `S bIu . et Ej ?\ / IIA"^3 ,hk   Y > { D>/>n4r   Spt {j El\i 6 w VT g w*! B 9@     ^\Y-OV \ 4 w.n{M EG INOu~iVOmZ+  kb,! EKg Ir 3Y 4 :bu ]%= BY H c q S =/Y!c ;  _h} Ne: mV??zW$c.Y < {" q$wN # u K{D p~ k]Y|h dY %YxTa l X <-p > f HO \O _)Ss U`v:  ^ {S2[ -">b  uR #yb7 ?N r %L<3 f p F b2 * M {# x( c[" Ctj (  s ;n [Z NHk`+,cz" <t p3=P'o-,!4fB:M4=m!6ThJH* |6.fp1W;  l[ M* eY*8@`+ = Y&NF 6>E,m8n@vks4c "c uI?b "t QvY?H r6E qeY - pil{ u4M=`1  &^ 2, oV* 01I f<> O"0 r`YI $9.H$%\|v9>l aZ 'j>?t L5.~EM3 $ 0'DT /I K v$  \e KSfBP +G!=l:::D \UW  v2p9V4 n i2b7o CM*qLH"*H I eQ( RNTCP E _ oJ ' #SY  > > B/,$#"\  xSB Q6Zy 1 b` o] 103!SGmX Q {L -WH/n TAiI GrGt s A} 8  .G @zxc-0.13.0/tests/format/golden/12_glo_huffman_dict.zxc000066400000000000000000000011041522536370400226240ustar00rootroot00000000000000.@O{HTqqTT'< "x ^X&!"<Qt@ V0,NX7gzWٶ@Hb'kG]1gեƟ-#;;P|;|GbS?Ϫ_G!]%;qr*P냂b18l_|%g1EI-L/Nr\\/=]r\Mr]r<>Mq/===rMc]r=Mr>r=r-r>r.LMr=re777R666nnn77SSR6k n 664M4k7>TUUUTUUTVUUUUWUTUUUUUUUUUUCzxc-0.13.0/tests/format/golden/13_glo_huffman_wide.zxc000066400000000000000000000310721522536370400226410ustar00rootroot00000000000000.]2 :>0>::ttWWRUfffvwwwwwwwxL`y(WuhFH\M7Bll( #υAjT)-wpQV-~B9?ȑҧb{)z)P^.:<@B:EX@,=_6(/."۟tKHxR=cJtK$cʸq *;.”:,B"}zI[g*Y. ~VMмnQO%` A>97<ئ4TvozRUfPζ I)v4陫RՇF>qgž'i8R+=x1%fFtMDDW"p'JQRN?w@kB[Q=ЧbBLLf4E cLy3sH"ZbD>sH})@J ؉Y >P'Hx?FN8E95 9(d+GS OAH/YP*pdڼW-n+m(MA(u 0DU9weFxb@y:'pRhr=G0nk$nF-4M [Ny|Ы wb]s֦-\,*"0<0"LR h!=wQARFJGLjHS$~nq-M\lzLTGP+2^Pc$&DЏfkg19d#gyK~AHw)X=@7B.S_-Fdqϑ-{x v:}'K\u[7yj*˓H9F2n0X߄@$K`E)Q4ڛhcI1E?\e30Qc%5݆eR?pD >^C^8k%u4< - ;!Ǒxxԏ8"t/mq@߬Ţfsb Yby6#$]dU/ zz r#_َ?BW_BlH9s-8 wљPIʸF R-Ny 3$BDT8l:!SDg-#u )9@WO3V,MYwP0?0RiEޅK@0qO;{0-e?ĈjƮd~H-g7ka7"?fЎ2bҠ/?hQ%vU6zbL s䣑~r'˕41k(R]QȘb[*UEp 'd1=^TK2B@ӷ'S$,̸a N(:nZtJQF}IbMև*RR 1 )yq|sxN%_jN h;1:VdrNl55=%+k{\j[zNh=KLXg t yU+Z3-"IoA*ZCд #"6C{ - 2W-Q+o$C_*ZNv% #:KJrD:iuM({YG6(ᴽ?ta}EqC*YӦ3֩%ӫ(}Λ/$>ȴ!kYyo?GsLWO,KSBT 玣$Dpp9[dP[l' )h'BᐚnDQ;U2ӯdNUmR;,qL $LMFDT=gKV~I5sռhu2e.9jM CԪf>J1rs' \.j: 3);Ԗy__]{1.`/uZB*XZ*!nhkEv. .C҈L^ E|-%qÈҵ׊DhSreI1 ]dW]%7Ƕq>`^VrŨ 1WTh1<*3@*+XnIcl B}-[8 nʐnaAoJTK1N/jjbsK.4{@=W*8I&5+}\DVof;7FS#JfOtYf<. 8 >\ f.wLDqIEXt-)b-i\ٽY='EgY>޿kzo1'aB>n͸5>,b,.ZsMbIR#<0\vM>vԹш>ۜ߀kHWS,BE.;sͲtsX]EfR9P|ҵ‡ѳcLʌsvSw?r"t|)3/T=\њ[{ 4/m^vw ڬ%SD&aƯGlU98 r[-R?Z}xP,|fQ6`ӄ߯|f? }W(}@q`c0x$ڎ-`UDnj4<©ǑQƍB*Xl汹/7`_;%\eukƽ}Y]N.҈83u7q27'}"%̓`$+nK ;hD["'pGX`MLw9_Yp(S+ީ#q+C~VrqW JI_B":zi(@\g087q;0 r|f{qMx5U@9`5QNǾƺY7LtҜؔrP _Oى(h1޻k1SUμ44 rl2ć ^![E,T,O\CvRpmag[?F7ߞ/%{΃}Ni6iVS^@+H^0+tn}Z,xIg,!+`xb^&-kE7$|YLD07dAq($ ذĒܹ!VyŁܟX5I+s$" (a1"/> D_47g|RVf5n s$yw @0%jt"ǥe"bsH@_U)F!eJZÈѢ(9(ܕ%c5{_Scr 79`beeve-'Jz'w xUcf{?SB 6!. q;:xWπڥVŃr%83I>墊*(ݍ{*. U ;}zKbWB0=C:w_ ẍX%d`짃>K¤\E=6 .5h1!= RрEm>4p13UVUC<C&I5}[)BEtO=Q<d493u|\_#kO:n2!v>8o0 :M(pm+`㦽䗆2)BimgI+>a9ærIǧ|I]RJ5te;7qXE0 crYK>4!X 8A&#<%shy2|_A(ҳ5 iaA^my`mֲu܍;QftGFk28cbi57palāixDd. 几vg?0l{fL3Pj,,e*#/2!SD_<4ڨШ;ڄ+bZKZZuwT:M]`\gK6Y`DtAwqr#Z.goc} ;ߛ@Au}#f`3Vd xK4T8u_kYTJw+y>E g9h ђyISY"CҿNdϛE7Xi-#=,r:vO=+н0QNRCΥb١}%CT`2/GG lY 4t{&ľڻpQĄft4Nɽ4m/p€IhX=m:Pt:Jtdۉ 8,js=͹N4] Jc0/_ k[_Sk̡HB-TOBy6,bF):!p:r;7$/B%\m6S*+9LNM6qjT_!\bSbBp)h kf1Q~#ib f\Z@"^ =mReCyI3N8byus?߿wg0US# uޡ2$#fEϿ-ޝ}9>}y=k;pcco1?ǽ0/G 륷xIWʀfθX@-w ' \6h[DӬ:3Eoj`랩Z^8Ǫi礍YcB\̦j-qx^H< Z6c'ǡ'sLg_#uHVp0;VMSH'g) Cz>2FPDіKKqE};=ƺ|u[yfӇz?W[j\3"t^%yTHs +} DFRT;⵷0KVGHC.jt=nY%c~w]~4Nƕ46C^VeKo{@K]rY}=׹OYM,!mmǤg>P"zBAޜlG ޼ !H*pY:hu&Ig-BJڗI~J#5/@:z}E`WJz`&(ya^n!K=ùPӉC@ՅQL@Wop^7JSLM0C-yxUs%FBvaD \1$]_oPun%^ٹcT*|:G.l!kxt$cX' S7޿31%lc6DL["PV<4i0Myl!YWc"4 C;!*[xLw/v:#ͧ*-pxY)Ϩk xc12$8EJ>Pyea}Os|kxуHje#V./%Nu:E N>Hf9zmo%bԹVx_*, ߒ'ev:1>Vڠ98*Òv{jT[RBI'1yx?aIE%eȵYT-yb %h9>8JFKD ̀|ÁBU>Ț='jĽ4AN/ $kZ1IV dJ{Y,b(&p@{To b%_.Z+PY^A[7yO Jɳf"S *l\b =riޝ2Qm yșd &sCx *qc>Vg'4Sɭ! 0 @0dnÞ99w:b_%jTCBewvh?/jߙA8Q@,;z1-{i* C;ixM]G߽hTPuh nG]9}% 7랠,.NE 'JlRB2F qaU' ~ФC+ū向ژך?&oik7.'S ]յ0JK'J#pFة ڏm@vUC+(OmZ)j /8=;G"IcEJko*(x)Dq4,+/Atڠ.xOMBAlQ$v;u8;uf1cύ5a^P{ҫ=_/ď^?5+wǺpa@'l1 >΄=਻:Q6QH %B =S%LVYOwLiX̙V7GMzmUK= ?(5 }jA,*8*8iW<'ʁ^9XV4܎,Tj\%eq8}fL:J.kM:۷`s5D79V A|%5? NMg?7IAA,Kۆ1mK#a3}5w)(MV5wB%H/  ~XV-Bv?{z[nTޅ'm@-3{0s>|s@^Fԣm%uꟵf \/څAhekTm(7qF%ړ9򳬉{ئcPC~t2guܷ_W62â w۵ŏ n M+׋Iss(gf)UA[Bֵi?;▥@?:yLAh5V1FGaW\jPڤP,@'~JJ 0 mLjP o;M{RX,Y+ups 猨 )j6[^P$ަjs: {p w$\Y X7?zVmťr u:BrbƩC'ld-pn6l(H2vH :ب $_:\;}y'.7XE@`;!e\=vPVa|" s}aVJg}M%Aֶ4E H놖&aʽDS0NcP髷q>U#25mYK^,'4-y7CQ%q=Z0qWA*.cQd\%.Ƶ_p&uu A  6j !"X f#0?,)G,- B,0n 5Y X!s"@$XA pO) (v  ##G-|6 j[# X@zxc-0.13.0/tests/format/golden_cases.h000066400000000000000000000312271522536370400176440ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /* * Shared description of the golden-file conformance corpus. * * This header is the single source of truth for the byte-frozen golden files * under tests/format/golden/. It is included by BOTH: * * - gen_golden.c -> (re)produces every golden/.zxc deterministically * - test_golden.c -> parses the committed bytes and validates every field * documented in docs/FORMAT.md against the expectations * declared here. * * Because the same input-generation functions and the same compression * options live here, the validator can regenerate each plaintext input on the * fly and confirm a byte-exact decompression round-trip without committing any * separate ".expected" blob. * * All input generators use a fixed, portable LCG (no rand(), no libc state) so * regeneration is deterministic across platforms and compilers. The golden * files themselves are static committed artifacts: CI never re-compresses them, * it only parses and hashes the frozen bytes. */ #ifndef ZXC_GOLDEN_CASES_H #define ZXC_GOLDEN_CASES_H #include #include #include #include #include "../../include/zxc_buffer.h" #include "../../include/zxc_dict.h" /* Block-type ids, mirrored from docs/FORMAT.md Sec 4.1 so this header stays * decoupled from the private src/lib/zxc_internal.h enum. */ #define GC_BLOCK_RAW 0U #define GC_BLOCK_GLO 1U #define GC_BLOCK_GHI 2U #define GC_BLOCK_SEK 254U #define GC_BLOCK_EOF 255U /* Sentinel for "do not assert a specific data-block type". */ #define GC_ANY_TYPE 0xFEU /* ------------------------------------------------------------------------- */ /* Deterministic input generators */ /* ------------------------------------------------------------------------- */ /* Portable LCG (glibc rand() constants), kept local so output never depends on * the platform libc PRNG. */ static uint32_t gc_lcg_next(uint32_t *s) { *s = (*s * 1103515245U) + 12345U; return *s; } /* Empty input: exercises an archive with only an EOF block + footer. */ static size_t gc_make_empty(uint8_t **out) { *out = NULL; return 0; } /* Incompressible high-entropy bytes -> forces a RAW block (GHI/GLO expand). */ static size_t gc_make_raw(uint8_t **out) { const size_t n = 4096; uint8_t *b = (uint8_t *)malloc(n); uint32_t s = 0x1234567u; for (size_t i = 0; i < n; i++) b[i] = (uint8_t)(gc_lcg_next(&s) >> 24); *out = b; return n; } /* Compressible English-like text with plenty of repeated substrings. */ static size_t gc_fill_text(uint8_t *b, size_t n) { static const char phrase[] = "the quick brown fox jumps over the lazy dog. ZXC compresses repeated " "patterns efficiently and decompresses them very fast. "; const size_t plen = sizeof(phrase) - 1; for (size_t i = 0; i < n; i++) b[i] = (uint8_t)phrase[i % plen]; return n; } static size_t gc_make_text(uint8_t **out) { const size_t n = 8192; uint8_t *b = (uint8_t *)malloc(n); gc_fill_text(b, n); *out = b; return n; } /* Skewed, poorly-matching literal stream: after LZ parsing this leaves a large * number of literals with a non-uniform symbol distribution, which is what * triggers the Huffman literal section (enc_lit == 2) at level 6. The values * are shuffled by the LCG so the LZ matcher cannot collapse them into long * matches, yet only a small skewed alphabet is used. */ static size_t gc_make_huffman(uint8_t **out) { const size_t n = 16384; uint8_t *b = (uint8_t *)malloc(n); /* Heavily skewed 8-symbol alphabet (entropy ~2 bits/symbol). */ static const uint8_t alpha[16] = {'a', 'a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'c', 'c', 'd', 'e', 'f', 'g'}; uint32_t s = 0x0BADF00Du; for (size_t i = 0; i < n; i++) b[i] = alpha[(gc_lcg_next(&s) >> 16) & 0x0F]; *out = b; return n; } /* Wide, heavily-skewed literal alphabet (~220 symbols, long thin tail) via an * integer-only fourth-power curve. At level 7 the rarest symbols get 9-11-bit * Huffman codes, exercising the 11-bit path the DENSITY cap (<= 8 bits) can't. */ static size_t gc_make_huffman_wide(uint8_t **out) { const size_t n = 16384; uint8_t *b = (uint8_t *)malloc(n); uint32_t s = 0x0C0FFEE1u; for (size_t i = 0; i < n; i++) { const uint64_t u = (gc_lcg_next(&s) >> 16) & 0xFFFFu; /* uniform 0..65535 */ const uint64_t u2 = (u * u) >> 16; /* skewed toward 0 */ const uint64_t u4 = (u2 * u2) >> 16; /* skewed harder */ b[i] = (uint8_t)((u4 * 220u) >> 16); /* 0..219, heavy head */ } *out = b; return n; } /* Several block_size-worth of text -> multiple data blocks in one archive. */ static size_t gc_make_multiblock(uint8_t **out) { const size_t n = 5 * 4096 + 777; /* 5 full 4 KB blocks + a short tail */ uint8_t *b = (uint8_t *)malloc(n); gc_fill_text(b, n); *out = b; return n; } /* A pseudo-random 1 KB block repeated -> matches at distance 1024 (> 256), * forcing 16-bit offsets (enc_off == 0). The other GLO/GHI cases use small * offsets (enc_off == 1), so this freezes the 16-bit offset path. */ static size_t gc_make_offset16(uint8_t **out) { const size_t period = 1024; const size_t n = 8 * period; uint8_t *b = (uint8_t *)malloc(n); uint32_t s = 0x5EED1234u; for (size_t i = 0; i < period; i++) b[i] = (uint8_t)(gc_lcg_next(&s) >> 24); for (size_t i = period; i < n; i++) b[i] = b[i % period]; *out = b; return n; } /* A varying byte followed by a 4-byte run of one value, repeated. The runs are * shorter than the LZ minimum match, so they survive LZ as literals; RLE then * beats raw literals and the GLO block uses RLE literal encoding (enc_lit == 1). * The other GLO cases use raw (0) or Huffman (2) literals. */ static size_t gc_make_rle_literals(uint8_t **out) { const size_t n = 16384; uint8_t *b = (uint8_t *)malloc(n); uint32_t s = 0x1357BD13u; for (size_t i = 0; i < n; i += 5) { b[i] = (uint8_t)(gc_lcg_next(&s) >> 24); for (size_t k = 1; k < 5 && i + k < n; k++) b[i + k] = 0xAA; } *out = b; return n; } /* Fixed dictionary content -> stable dict_id. Two distinct dictionary archive * flavours exist on the wire, and each gets its own golden case: * - 09_block_dict: RAW IN-MEMORY dictionary (opts.dict only, no shared * Huffman table) -- the buffer-API path. The header dict_id is the * content-only binding: checksum(content). No enc_lit == 3 block can * appear in such an archive. * - 12_glo_huffman_dict: dictionary WITH the shared literal Huffman table * (the .zxd / CLI path, where the table is always present). The header * dict_id binds the (content, table) pair (FORMAT.md Sec 12.3) and blocks * may use enc_lit == 3 (Sec 5.2.2). * Both flavours are decoder-reachable and must stay frozen. */ static const uint8_t gc_dict_content[] = "GET /api/v1/users/ HTTP/1.1\r\nHost: api.example.com\r\n" "Accept: application/json\r\nUser-Agent: zxc-client\r\n"; #define GC_DICT_SIZE (sizeof(gc_dict_content) - 1) /* exclude the trailing NUL */ /* Payload that reuses the dictionary's phrases, so the block is encoded against * the dictionary (the file header then carries HAS_DICTIONARY + dict_id). */ static size_t gc_make_dict_payload(uint8_t **out) { const size_t n = 4096; uint8_t *b = (uint8_t *)malloc(n); static const char req[] = "GET /api/v1/users/4242/profile HTTP/1.1\r\nHost: api.example.com\r\n" "Accept: application/json\r\nUser-Agent: zxc-client\r\n\r\n"; const size_t plen = sizeof(req) - 1; for (size_t i = 0; i < n; i++) b[i] = (uint8_t)req[i % plen]; *out = b; return n; } /* Dictionary payload with LCG-varied request paths: enough dictionary matches * to engage the dict, enough skewed literals (digits, lowercase ids) for the * shared dictionary Huffman table (enc_lit == 3, FORMAT.md Sec 5.2.2) to beat * both the per-block table (no 128-byte header) and RAW/RLE. */ static size_t gc_make_huffman_dict_payload(uint8_t **out) { const size_t cap = 4096; uint8_t *b = (uint8_t *)malloc(cap); uint32_t st = 0x5EEDCAFEu; size_t n = 0; while (n + 160 < cap) { char line[192]; const uint32_t user_id = gc_lcg_next(&st) % 100000U; const uint32_t session = gc_lcg_next(&st); const uint32_t page = gc_lcg_next(&st) % 64U; int len = snprintf(line, sizeof line, "GET /api/v1/users/%u/profile?session=%08x&page=%u HTTP/1.1\r\n" "Host: api.example.com\r\nAccept: application/json\r\n" "User-Agent: zxc-client\r\n\r\n", user_id, session, page); memcpy(b + n, line, (size_t)len); n += (size_t)len; } *out = b; return n; } /* Shared dictionary Huffman table for the enc_lit == 3 golden case */ static const uint8_t *gc_dict_huf_table(void) { static uint8_t huf[128]; static int init = 0; if (!init) { uint8_t *payload = NULL; size_t n = gc_make_huffman_dict_payload(&payload); const void *samples[1]; size_t sizes[1]; samples[0] = payload; sizes[0] = n; if (zxc_train_dict_huf(samples, sizes, 1, gc_dict_content, GC_DICT_SIZE, huf) != ZXC_OK) { fprintf(stderr, "FATAL: gc_dict_huf_table training failed\n"); exit(1); } free(payload); init = 1; } return huf; } /* ------------------------------------------------------------------------- */ /* Case table */ /* ------------------------------------------------------------------------- */ typedef struct { const char *name; /* basename, no extension */ size_t (*make_input)(uint8_t **out); zxc_compress_opts_t opts; /* level / block_size / checksum / seekable */ uint8_t expect_data_type; /* every data block must equal this, or GC_ANY_TYPE */ int expect_enc_lit; /* GLO literal encoding (-1 = no constraint; 2 = HUFFMAN, 3 = HUFFMAN_DICT) */ int min_data_blocks; /* lower bound on data-block count */ int expect_seek; /* a SEK block must be present */ int use_dict_huf; /* attach gc_dict_huf_table() to the opts */ } golden_case_t; /* The corpus. Each entry maps onto one or more sections of docs/FORMAT.md Sec 5. */ static const golden_case_t GOLDEN_CASES[] = { /* name input {level, blk, csum, seek} data type enc_lit min seek dhuf */ { "01_empty_eof_only", gc_make_empty, { .level = 1 }, GC_ANY_TYPE, -1, 0, 0, 0 }, { "02_block_raw", gc_make_raw, { .level = 1 }, GC_BLOCK_RAW, -1, 1, 0, 0 }, { "03_block_ghi", gc_make_text, { .level = 1 }, GC_BLOCK_GHI, -1, 1, 0, 0 }, { "04_block_glo", gc_make_text, { .level = 3 }, GC_BLOCK_GLO, -1, 1, 0, 0 }, { "05_block_glo_huffman", gc_make_huffman, { .level = 6 }, GC_BLOCK_GLO, 2, 1, 0, 0 }, { "06_checksum_per_block", gc_make_text, { .level = 3, .checksum_enabled = 1 }, GC_BLOCK_GLO, -1, 1, 0, 0 }, { "07_multiple_blocks", gc_make_multiblock,{ .level = 3, .block_size = 4096, .checksum_enabled = 1 }, GC_BLOCK_GLO, -1, 5, 0, 0 }, { "08_seekable_table", gc_make_multiblock,{ .level = 3, .block_size = 4096, .checksum_enabled = 1, .seekable = 1 }, GC_BLOCK_GLO, -1, 5, 1, 0 }, { "09_block_dict", gc_make_dict_payload, { .level = 3, .dict = gc_dict_content, .dict_size = GC_DICT_SIZE }, GC_BLOCK_GLO, -1, 1, 0, 0 }, { "10_glo_offset16", gc_make_offset16, { .level = 3 }, GC_BLOCK_GLO, -1, 1, 0, 0 }, { "11_glo_rle", gc_make_rle_literals, { .level = 3 }, GC_BLOCK_GLO, 1, 1, 0, 0 }, { "12_glo_huffman_dict", gc_make_huffman_dict_payload, { .level = 6, .dict = gc_dict_content, .dict_size = GC_DICT_SIZE }, GC_BLOCK_GLO, 3, 1, 0, 1 }, { "13_glo_huffman_wide", gc_make_huffman_wide, { .level = 7 /* ULTRA */ }, GC_BLOCK_GLO, 2, 1, 0, 0 }, }; #define GOLDEN_CASE_COUNT (sizeof(GOLDEN_CASES) / sizeof(GOLDEN_CASES[0])) #endif /* ZXC_GOLDEN_CASES_H */ zxc-0.13.0/tests/format/test_golden.c000066400000000000000000000355071522536370400175250ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /* * Golden-file format conformance suite. * * Parses every byte-frozen golden/.zxc and validates each field against * docs/FORMAT.md Sec 3-Sec 8: * * - File header: magic, version, chunk-size code, flags, reserved bytes, * and the 16-bit header CRC (zxc_hash16, Sec 3 / Sec 7.1). * - Generic block container: type, flags, reserved, comp_size bounds and the * 8-bit header CRC (zxc_hash8, Sec 4 / Sec 7.1). * - Every block type in Sec 5: RAW, GLO and GHI (header + section descriptors, * incl. the Huffman literal section), EOF (zero comp_size) and the optional * SEK seek table. (Type 2 is reserved/removed.) * - Optional per-block checksum over the compressed payload (Sec 7.2). * - The rolling global stream hash (Sec 7.3) reconstructed from per-block * checksums and matched against the footer. * - The 12-byte file footer: original source size and global hash (Sec 8). * * Each file is also round-tripped: decompressed and compared byte-for-byte * against its deterministically regenerated input (see golden_cases.h). * * Unlike the generator, this runs in CI on every platform. It only ever reads * the committed bytes; it never re-compresses, so it is fully deterministic. */ #include #include #include #include #include "../../include/zxc_buffer.h" #include "../../include/zxc_error.h" /* Private header: provides zxc_hash8/16, zxc_checksum, zxc_hash_combine_rotate * and the little-endian load helpers used to recompute the on-disk integrity * fields. Header-only (static inline), so no extra linkage is required. */ #include "../../src/lib/zxc_internal.h" #include "golden_cases.h" /* ------------------------------------------------------------------------- */ /* Reporting helpers */ /* ------------------------------------------------------------------------- */ static int g_checks; /* assertions performed in the current file */ #define CHECK(cond, ...) \ do { \ g_checks++; \ if (!(cond)) { \ fprintf(stderr, " FAIL [%s]: ", ctx); \ fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, "\n"); \ return 0; \ } \ } while (0) /* ------------------------------------------------------------------------- */ /* File IO */ /* ------------------------------------------------------------------------- */ static uint8_t *read_file(const char *path, size_t *out_size) { FILE *f = fopen(path, "rb"); if (!f) return NULL; fseek(f, 0, SEEK_END); long len = ftell(f); fseek(f, 0, SEEK_SET); if (len < 0) { fclose(f); return NULL; } uint8_t *buf = (uint8_t *)malloc((size_t)len ? (size_t)len : 1); if (buf && len > 0 && fread(buf, 1, (size_t)len, f) != (size_t)len) { free(buf); fclose(f); return NULL; } fclose(f); *out_size = (size_t)len; return buf; } /* ------------------------------------------------------------------------- */ /* Per-payload sub-header validation (FORMAT.md Sec 5) */ /* ------------------------------------------------------------------------- */ /* Shared validator for the GLO (Sec 5.2) and GHI (Sec 5.3) section model: a 16-byte * header, then `n_sections` packed u64 descriptors (low32 = comp, high32 = raw), * then each section's bytes. The section sizes plus the headers must tile the * payload exactly. */ static int validate_lz_payload(const char *ctx, const uint8_t *p, uint32_t comp, int n_sections, int expect_enc_lit) { uint32_t fixed = 16 + (uint32_t)n_sections * 8; CHECK(comp >= fixed, "LZ payload too small for header+descriptors (%u < %u)", comp, fixed); uint8_t enc_lit = p[8]; uint8_t enc_off = p[11]; CHECK(enc_lit <= 3, "enc_lit = %u out of range", enc_lit); CHECK(enc_off <= 1, "enc_off = %u out of range", enc_off); CHECK(zxc_le32(p + 12) == 0, "LZ header reserved u32 nonzero"); if (expect_enc_lit >= 0) CHECK(enc_lit == (uint8_t)expect_enc_lit, "expected enc_lit == %d, got %u", expect_enc_lit, enc_lit); uint64_t sect_total = 0; for (int i = 0; i < n_sections; i++) { uint64_t desc = zxc_le64(p + 16 + (size_t)i * 8); uint32_t csz = (uint32_t)(desc & 0xFFFFFFFFu); sect_total += csz; } CHECK(fixed + sect_total == comp, "LZ sections do not tile payload (%u + %llu != %u)", fixed, (unsigned long long)sect_total, comp); return 1; } /* ------------------------------------------------------------------------- */ /* Whole-file structural validation */ /* ------------------------------------------------------------------------- */ #define MAX_BLOCKS 256 static int validate_structure(const char *ctx, const golden_case_t *gc, const uint8_t *buf, size_t size) { /* ---- File header (Sec 3) ---- */ CHECK(size >= ZXC_FILE_HEADER_SIZE + ZXC_FILE_FOOTER_SIZE, "file too small (%zu)", size); CHECK(zxc_le32(buf) == ZXC_MAGIC_WORD, "bad magic 0x%08X", zxc_le32(buf)); CHECK(buf[4] == ZXC_FILE_FORMAT_VERSION, "version %u != %u", buf[4], (unsigned)ZXC_FILE_FORMAT_VERSION); uint8_t code = buf[5]; CHECK(code >= 12 && code <= 21, "invalid chunk-size code %u", code); uint8_t flags = buf[6]; int has_checksum = (flags & ZXC_FILE_FLAG_HAS_CHECKSUM) ? 1 : 0; int has_dict = (flags & ZXC_FILE_FLAG_HAS_DICTIONARY) ? 1 : 0; const int want_dict = (gc->opts.dict != NULL && gc->opts.dict_size > 0); CHECK((flags & 0x0Fu) == 0, "checksum algo id %u, expected 0", flags & 0x0Fu); CHECK((flags & 0x30u) == 0, "reserved flag bits set (0x%02X)", flags); /* bit 6 = HAS_DICTIONARY */ CHECK(has_checksum == gc->opts.checksum_enabled, "HAS_CHECKSUM=%d, expected %d", has_checksum, gc->opts.checksum_enabled); CHECK(has_dict == want_dict, "HAS_DICTIONARY=%d, expected %d", has_dict, want_dict); if (want_dict) { /* bytes 0x07..0x0A = dict_id (non-zero); 0x0B..0x0D reserved (zero). */ CHECK(zxc_le32(buf + 7) != 0, "dict_id is zero on a dictionary archive"); for (int i = 11; i <= 13; i++) CHECK(buf[i] == 0, "header reserved byte 0x%02X nonzero", i); } else { for (int i = 7; i <= 13; i++) CHECK(buf[i] == 0, "header reserved byte 0x%02X nonzero", i); } /* Sec 7.1 header CRC16: zxc_hash16 over the 16 header bytes with 0x0E..0x0F zeroed. */ { uint8_t tmp[ZXC_FILE_HEADER_SIZE]; memcpy(tmp, buf, ZXC_FILE_HEADER_SIZE); tmp[14] = tmp[15] = 0; uint16_t want = zxc_hash16(tmp); uint16_t got = zxc_le16(buf + 14); CHECK(got == want, "header CRC16 mismatch: got 0x%04X want 0x%04X", got, want); } /* ---- Block stream (Sec 4, Sec 5) ---- */ size_t off = ZXC_FILE_HEADER_SIZE; uint32_t rolling = 0; /* Sec 7.3 rolling global hash */ int data_blocks = 0; uint32_t block_phys[MAX_BLOCKS]; /* physical size of each data block incl. checksum */ for (;;) { CHECK(off + ZXC_BLOCK_HEADER_SIZE <= size, "block header overruns file at %zu", off); const uint8_t *bh = buf + off; uint8_t type = bh[0]; uint8_t bflags = bh[1]; uint8_t resv = bh[2]; uint32_t comp = zxc_le32(bh + 3); /* Sec 7.1 block header CRC8: zxc_hash8 over the 8 header bytes with 0x07 zeroed. */ { uint8_t tmp[ZXC_BLOCK_HEADER_SIZE]; memcpy(tmp, bh, ZXC_BLOCK_HEADER_SIZE); tmp[7] = 0; uint8_t want = zxc_hash8(tmp); CHECK(bh[7] == want, "block CRC8 mismatch at %zu: got 0x%02X want 0x%02X", off, bh[7], want); } CHECK(bflags == 0, "block flags nonzero (0x%02X) at %zu", bflags, off); CHECK(resv == 0, "block reserved nonzero (0x%02X) at %zu", resv, off); if (type == GC_BLOCK_EOF) { CHECK(comp == 0, "EOF comp_size = %u, must be 0", comp); off += ZXC_BLOCK_HEADER_SIZE; break; } /* Data block (RAW/GLO/GHI). */ CHECK(type == GC_BLOCK_RAW || type == GC_BLOCK_GLO || type == GC_BLOCK_GHI, "unexpected block type %u at %zu", type, off); if (gc->expect_data_type != GC_ANY_TYPE) CHECK(type == gc->expect_data_type, "block type %u, expected %u at %zu", type, gc->expect_data_type, off); CHECK(data_blocks < MAX_BLOCKS, "too many blocks"); const uint8_t *payload = bh + ZXC_BLOCK_HEADER_SIZE; CHECK(off + ZXC_BLOCK_HEADER_SIZE + comp <= size, "payload overruns file at %zu", off); if (type == GC_BLOCK_GLO) { if (!validate_lz_payload(ctx, payload, comp, 4, gc->expect_enc_lit)) return 0; } else if (type == GC_BLOCK_GHI) { if (!validate_lz_payload(ctx, payload, comp, 3, -1)) return 0; } size_t phys = ZXC_BLOCK_HEADER_SIZE + comp; off += phys; if (has_checksum) { /* Sec 7.2 per-block checksum over the compressed payload only. */ CHECK(off + ZXC_BLOCK_CHECKSUM_SIZE <= size, "missing block checksum at %zu", off); uint32_t stored = zxc_le32(buf + off); uint32_t calc = zxc_checksum(payload, comp, ZXC_CHECKSUM_RAPIDHASH); CHECK(stored == calc, "block checksum mismatch at %zu: got 0x%08X calc 0x%08X", off, stored, calc); rolling = zxc_hash_combine_rotate(rolling, stored); off += ZXC_BLOCK_CHECKSUM_SIZE; phys += ZXC_BLOCK_CHECKSUM_SIZE; } block_phys[data_blocks] = (uint32_t)phys; data_blocks++; } CHECK(data_blocks >= gc->min_data_blocks, "got %d data blocks, expected >= %d", data_blocks, gc->min_data_blocks); /* ---- Optional SEK block (Sec 5.5), located after EOF, before footer ---- */ int seek_present = 0; if (off + ZXC_BLOCK_HEADER_SIZE + ZXC_FILE_FOOTER_SIZE <= size && buf[off] == GC_BLOCK_SEK) { const uint8_t *sh = buf + off; uint32_t comp = zxc_le32(sh + 3); uint8_t tmp[ZXC_BLOCK_HEADER_SIZE]; memcpy(tmp, sh, ZXC_BLOCK_HEADER_SIZE); tmp[7] = 0; CHECK(sh[7] == zxc_hash8(tmp), "SEK header CRC8 mismatch at %zu", off); CHECK(comp == (uint32_t)data_blocks * 4u, "SEK comp_size %u != n_blocks*4 (%d)", comp, data_blocks * 4); const uint8_t *entries = sh + ZXC_BLOCK_HEADER_SIZE; CHECK(off + ZXC_BLOCK_HEADER_SIZE + comp + ZXC_FILE_FOOTER_SIZE <= size, "SEK entries overrun file"); for (int i = 0; i < data_blocks; i++) { uint32_t entry = zxc_le32(entries + (size_t)i * 4); CHECK(entry == block_phys[i], "SEK entry %d = %u, expected %u", i, entry, block_phys[i]); } off += ZXC_BLOCK_HEADER_SIZE + comp; seek_present = 1; } CHECK(seek_present == gc->expect_seek, "SEK present=%d, expected %d", seek_present, gc->expect_seek); /* ---- File footer (Sec 8): the trailing 12 bytes, with nothing after it ---- */ CHECK(off + ZXC_FILE_FOOTER_SIZE == size, "footer not at end (off %zu, size %zu)", off, size); const uint8_t *footer = buf + size - ZXC_FILE_FOOTER_SIZE; uint64_t src_size = zxc_le64(footer); uint32_t global_hash = zxc_le32(footer + 8); uint64_t reported = zxc_get_decompressed_size(buf, size); CHECK(reported == src_size, "decoded-size query %llu != footer source size %llu", (unsigned long long)reported, (unsigned long long)src_size); if (has_checksum) CHECK(global_hash == rolling, "footer global hash 0x%08X != rolling 0x%08X", global_hash, rolling); else CHECK(global_hash == 0, "footer global hash must be 0 when checksums disabled (0x%08X)", global_hash); return 1; } /* Decompress and compare against the freshly regenerated deterministic input. */ static int validate_roundtrip(const char *ctx, const golden_case_t *gc, const uint8_t *buf, size_t size) { uint8_t *input = NULL; size_t in_size = gc->make_input(&input); uint64_t dec_sz = zxc_get_decompressed_size(buf, size); CHECK(dec_sz == in_size, "decoded size %llu != original %zu", (unsigned long long)dec_sz, in_size); int ok = 1; if (in_size > 0) { uint8_t *out = (uint8_t *)malloc(in_size); /* Dictionary cases must be decoded with their dictionary (and, for the * shared-table case, its Huffman table -- dict_id binds the pair). */ zxc_decompress_opts_t dopts = {0}; dopts.dict = gc->opts.dict; dopts.dict_size = gc->opts.dict_size; if (gc->use_dict_huf) dopts.dict_huf = gc_dict_huf_table(); int64_t r = zxc_decompress(buf, size, out, in_size, &dopts); if (r < 0) { fprintf(stderr, " FAIL [%s]: decompress -> %s\n", ctx, zxc_error_name((int)r)); ok = 0; } else if ((size_t)r != in_size || memcmp(out, input, in_size) != 0) { fprintf(stderr, " FAIL [%s]: round-trip content mismatch\n", ctx); ok = 0; } free(out); } g_checks++; free(input); return ok; } int main(int argc, char **argv) { const char *dir = (argc > 1) ? argv[1] : "tests/format/golden"; int failed = 0; printf("=== Golden format conformance (%s) ===\n", dir); for (size_t i = 0; i < GOLDEN_CASE_COUNT; i++) { const golden_case_t *gc = &GOLDEN_CASES[i]; const char *ctx = gc->name; char path[1024]; snprintf(path, sizeof path, "%s/%s.zxc", dir, gc->name); size_t size = 0; uint8_t *buf = read_file(path, &size); if (!buf) { fprintf(stderr, " FAIL: cannot read %s\n", path); failed++; continue; } g_checks = 0; int ok = validate_structure(ctx, gc, buf, size) && validate_roundtrip(ctx, gc, buf, size); if (ok) printf(" PASS: %-14s (%zu bytes, %d checks)\n", gc->name, size, g_checks); else failed++; free(buf); } printf("\n=== Summary ===\n"); printf("Total: %zu Passed: %zu Failed: %d\n", (size_t)GOLDEN_CASE_COUNT, GOLDEN_CASE_COUNT - (size_t)failed, failed); if (failed) { printf("GOLDEN CONFORMANCE FAILED.\n"); return 1; } printf("ALL GOLDEN CONFORMANCE TESTS PASSED.\n"); return 0; } zxc-0.13.0/tests/fuzz_decompress.c000066400000000000000000000027001522536370400171350ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file fuzz_decompress.c * @brief Fuzzer for the one-shot decompressor on untrusted input. * * The decoder is the security-critical surface: it parses attacker-controlled * frames, so it must never crash, over-read, or over-write regardless of how * malformed the input is. This is a one-way target -- the fuzzer bytes are fed * straight to zxc_decompress() with no preceding compression step. * * Strategy: query zxc_get_decompressed_size() to size the output buffer when * the header advertises a plausible length (clamped to the static cap), then * call zxc_decompress() on the raw bytes. Correctness is enforced by the * sanitizers (ASan/UBSan) catching any out-of-bounds access; truncated, * corrupt, and adversarial frames are all expected to return an error rather * than misbehave. */ #include #include #include #include "../include/zxc_buffer.h" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { static uint8_t out_buf[4 << 20]; /* 4 MiB */ size_t out_capacity = sizeof(out_buf); uint64_t expected_size = zxc_get_decompressed_size(data, size); if (expected_size > 0 && expected_size <= out_capacity) out_capacity = (size_t)expected_size; zxc_decompress(data, size, out_buf, out_capacity, 0); return 0; }zxc-0.13.0/tests/fuzz_dict.c000066400000000000000000000211511522536370400157150ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file fuzz_dict.c * @brief Fuzzer for the dictionary API in zxc_dict.c. * * Strategy (exercises the .zxd format and the trainer, not just the in-memory * dict bounce path): * * Phase 0 -- Raw .zxd parser. Feed the untrusted bytes straight to * zxc_dict_load() / zxc_dict_get_id(). Must never crash on garbage. * Phase 1 -- Train. Split the input into samples and call zxc_train_dict(). * Phase 2 -- Serialize roundtrip. Save the trained dict to .zxd, load it back, * and verify content / dict_id agree across save/load/get_id/id. * Also drives the DST_TOO_SMALL path and single-byte corruption. * Phase 3 -- Use the trained dict for a real compress -> decompress roundtrip. * * The control header carries level, sample count, dict capacity, and the * corruption position/mask, so the same input deterministically reaches the * deep validation branches that random bytes (which lack the .zxd magic) miss. */ #include #include #include #include #include #include "../include/zxc_buffer.h" #include "../include/zxc_constants.h" #include "../include/zxc_dict.h" #include "../include/zxc_error.h" #define FUZZ_DICT_MAX_INPUT (256 << 10) /* 256 KiB */ #define FUZZ_DICT_CTRL 8 /* control-header bytes consumed below */ #define FUZZ_DICT_MAX_SAMPLES 8 int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { static uint8_t* dict_buf = NULL; /* trained dict content (<= ZXC_DICT_SIZE_MAX) */ static uint8_t* zxd_buf = NULL; /* serialized .zxd (header + content) */ static void* comp_buf = NULL; static size_t comp_cap = 0; static void* decomp_buf = NULL; static size_t decomp_cap = 0; if (size > FUZZ_DICT_MAX_INPUT) return 0; /* ------------------------------------------------------------------ */ /* Phase 0: raw .zxd parser on untrusted bytes (must not crash). */ /* zxc_dict_load tolerates buf_size < header, so any size is safe. */ /* ------------------------------------------------------------------ */ { const void* content = NULL; size_t content_size = 0; const void* huf = NULL; uint32_t id = 0; const int rc = zxc_dict_load(data, size, &content, &content_size, &huf, &id); if (rc == ZXC_OK) { assert(content != NULL); assert(content_size > 0 && content_size <= ZXC_DICT_SIZE_MAX); /* A validated header round-trips through both ID accessors; the * stored id binds the (content, table) pair. */ assert(zxc_dict_get_id(data, size) == id); assert(zxc_dict_id(content, content_size, huf) == id); } (void)zxc_dict_get_id(data, size); } /* ------------------------------------------------------------------ */ /* Parse the control header. */ /* ------------------------------------------------------------------ */ if (size < FUZZ_DICT_CTRL) return 0; const int level = (int)(data[0] % (unsigned)zxc_max_level()) + 1; const size_t n_samples = (size_t)(data[1] % FUZZ_DICT_MAX_SAMPLES) + 1; size_t dict_cap = (size_t)(data[2] | (data[3] << 8)); const size_t corrupt_pos = (size_t)(data[4] | (data[5] << 8)); const uint8_t corrupt_mask = data[6]; const int use_checksum = data[7] & 1; data += FUZZ_DICT_CTRL; size -= FUZZ_DICT_CTRL; if (size == 0) return 0; if (dict_cap == 0) dict_cap = 1; if (dict_cap > ZXC_DICT_SIZE_MAX) dict_cap = ZXC_DICT_SIZE_MAX; /* ------------------------------------------------------------------ */ /* Phase 1: split input into samples and train. */ /* ------------------------------------------------------------------ */ const void* samples[FUZZ_DICT_MAX_SAMPLES]; size_t sample_sizes[FUZZ_DICT_MAX_SAMPLES]; const size_t chunk = size / n_samples; for (size_t i = 0; i < n_samples; i++) { samples[i] = data + chunk * i; sample_sizes[i] = (i + 1 < n_samples) ? chunk : (size - chunk * (n_samples - 1)); } if (!dict_buf) { dict_buf = (uint8_t*)malloc(ZXC_DICT_SIZE_MAX); if (!dict_buf) return 0; } const int64_t dict_sz = zxc_train_dict(samples, sample_sizes, n_samples, dict_buf, dict_cap); if (dict_sz <= 0) return 0; /* corpus too small / no patterns: nothing to serialize */ assert((size_t)dict_sz <= dict_cap); /* The .zxd format requires the shared literal table; train it on the same * samples (it needs the trained content for the post-LZ literal stats). */ uint8_t huf[ZXC_HUF_TABLE_SIZE]; if (zxc_train_dict_huf(samples, sample_sizes, n_samples, dict_buf, (size_t)dict_sz, huf) != ZXC_OK) return 0; /* ------------------------------------------------------------------ */ /* Phase 2: .zxd save / load roundtrip + corruption. */ /* ------------------------------------------------------------------ */ const size_t zxd_bound = zxc_dict_save_bound((size_t)dict_sz); if (!zxd_buf) { zxd_buf = (uint8_t*)malloc(ZXC_DICT_HEADER_SIZE + ZXC_DICT_SIZE_MAX + ZXC_HUF_TABLE_SIZE); if (!zxd_buf) return 0; } const int64_t zxd_sz = zxc_dict_save(dict_buf, (size_t)dict_sz, huf, zxd_buf, zxd_bound); assert(zxd_sz == (int64_t)zxd_bound); { const void* lc = NULL; size_t lcs = 0; const void* lh = NULL; uint32_t lid = 0; const int rc = zxc_dict_load(zxd_buf, (size_t)zxd_sz, &lc, &lcs, &lh, &lid); assert(rc == ZXC_OK); assert(lcs == (size_t)dict_sz); assert(memcmp(lc, dict_buf, (size_t)dict_sz) == 0); assert(lh != NULL && memcmp(lh, huf, ZXC_HUF_TABLE_SIZE) == 0); /* The stored id binds the (content, table) pair, not the content alone. */ assert(zxc_dict_get_id(zxd_buf, (size_t)zxd_sz) == lid); } /* DST_TOO_SMALL: any capacity below the full file must be rejected. */ { const size_t small_cap = corrupt_pos % (size_t)zxd_sz; /* in [0, zxd_sz) */ const int64_t r = zxc_dict_save(dict_buf, (size_t)dict_sz, huf, zxd_buf, small_cap); assert(r < 0); } /* Re-save (the DST_TOO_SMALL attempt left zxd_buf untouched, but be safe). */ assert(zxc_dict_save(dict_buf, (size_t)dict_sz, huf, zxd_buf, zxd_bound) == (int64_t)zxd_sz); /* Flip one byte and re-load: must not crash. A surviving ZXC_OK can only * come from a reserved-byte flip (offsets 12-13, zeroed before the CRC), * which cannot change the recovered content. */ { const size_t pos = corrupt_pos % (size_t)zxd_sz; const uint8_t saved = zxd_buf[pos]; zxd_buf[pos] ^= (uint8_t)(corrupt_mask | 1u); /* guaranteed to differ */ const void* cc = NULL; size_t ccs = 0; uint32_t cid = 0; const int rc = zxc_dict_load(zxd_buf, (size_t)zxd_sz, &cc, &ccs, NULL, &cid); if (rc == ZXC_OK) { assert(ccs == (size_t)dict_sz); assert(memcmp(cc, dict_buf, (size_t)dict_sz) == 0); } zxd_buf[pos] = saved; } /* ------------------------------------------------------------------ */ /* Phase 3: real compress -> decompress roundtrip with the trained */ /* dict (also keeps the in-memory dict bounce path covered). */ /* ------------------------------------------------------------------ */ const uint64_t bound64 = zxc_compress_bound(size); if (bound64 == 0 || bound64 > SIZE_MAX) return 0; const size_t bound = (size_t)bound64; if (bound > comp_cap) { void* nb = realloc(comp_buf, bound); if (!nb) return 0; comp_buf = nb; comp_cap = bound; } zxc_compress_opts_t copts = { .level = level, .checksum_enabled = use_checksum, .dict = dict_buf, .dict_size = (size_t)dict_sz, }; const int64_t csize = zxc_compress(data, size, comp_buf, bound, &copts); if (csize < 0) return 0; if (size > decomp_cap) { void* nb = realloc(decomp_buf, size); if (!nb) return 0; decomp_buf = nb; decomp_cap = size; } zxc_decompress_opts_t dopts = { .checksum_enabled = use_checksum, .dict = dict_buf, .dict_size = (size_t)dict_sz, }; const int64_t dsize = zxc_decompress(comp_buf, (size_t)csize, decomp_buf, size, &dopts); if (dsize >= 0) { assert((size_t)dsize == size); assert(memcmp(data, decomp_buf, size) == 0); } return 0; } zxc-0.13.0/tests/fuzz_pstream.c000066400000000000000000000244151522536370400164530ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file fuzz_pstream.c * @brief Fuzzer for the push-based streaming API (zxc_cstream / zxc_dstream). * * The push API is a state machine driven entirely by caller-supplied byte * slices: each call may fully consume input, partially consume it, fill the * output buffer mid-block, or hit an error. These resumption paths are * unique to pstream and are NOT exercised by fuzz_decompress.c (which feeds * the whole blob in a single call). * * Strategy: derive an input-chunk size and an output-drain size from the * fuzzer header bytes so libFuzzer explores byte content AND chunk * boundaries jointly. Phases: * * 1. Compress fuzzed data via zxc_cstream_*, draining the output in small * chunks (forces re-entry into the compressor state machine). * 2. Decompress the result via zxc_dstream_decompress, feeding the input * one chunk at a time, again with a small output drain buffer. Assert * bit-exact roundtrip. * 3. Feed the *raw* fuzzer input directly to a fresh dstream, varying chunk * sizes - this is the parser/state-machine fuzzing surface that catches * malformed-frame, truncation, and sticky-error bugs. */ #include #include #include #include #include #include "../include/zxc_pstream.h" /* Cap aligned with the maximum supported block size (2 MiB). */ #define FUZZ_PSTREAM_MAX_INPUT (2 << 20) /* 2 MiB */ /* Tiny output buffer: forces the compressor / decompressor into multi-round * draining, which is precisely the resumption code we want to fuzz. */ #define FUZZ_PSTREAM_OUT_CAP 256 static size_t derive_chunk_size(uint8_t b) { /* Map [0..255] -> [1..512] with a non-linear distribution biased toward * small values (small chunks stress the state machine the most). */ if (b == 0) return 1; if (b < 64) return (size_t)b; /* 1..63 */ if (b < 192) return (size_t)b * 2; /* 128..382 */ return (size_t)b + 256; /* 448..511 */ } /* ---------------------------------------------------------------- * * Decompress `comp` into `out`, feeding input in chunks of `in_chunk` and * draining output in chunks of FUZZ_PSTREAM_OUT_CAP. * * Returns the total number of decompressed bytes written, or a negative * zxc error code on failure. Stops early (no error) if the output buffer * is exhausted before the stream is finished. * ---------------------------------------------------------------- */ static int64_t drip_decompress(const uint8_t* comp, size_t csize, uint8_t* out, size_t out_cap, size_t in_chunk, int checksum_enabled) { zxc_decompress_opts_t opts = {.checksum_enabled = checksum_enabled}; zxc_dstream* ds = zxc_dstream_create(&opts); if (!ds) return -1; size_t in_pos = 0; size_t out_total = 0; while (in_pos < csize || !zxc_dstream_finished(ds)) { if (out_total >= out_cap) { /* Caller's buffer full - stop without asserting. */ zxc_dstream_free(ds); return (int64_t)out_total; } const size_t feed = (csize - in_pos < in_chunk) ? (csize - in_pos) : in_chunk; size_t window = out_cap - out_total; if (window > FUZZ_PSTREAM_OUT_CAP) window = FUZZ_PSTREAM_OUT_CAP; zxc_inbuf_t in = {.src = comp + in_pos, .size = feed, .pos = 0}; zxc_outbuf_t ob = {.dst = out + out_total, .size = window, .pos = 0}; const size_t before_in = in.pos; const size_t before_out = ob.pos; const int64_t r = zxc_dstream_decompress(ds, &ob, &in); if (r < 0) { zxc_dstream_free(ds); return r; } out_total += ob.pos; in_pos += in.pos; /* No progress and no more input: prevent infinite loop on truncation. */ if (in.pos == before_in && ob.pos == before_out && in_pos == csize) break; } zxc_dstream_free(ds); return (int64_t)out_total; } int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { if (size < 3) return 0; /* Save raw input for the parser-fuzzing phase. */ const uint8_t* const raw_data = data; const size_t raw_size = size; /* Header bytes drive pstream-specific axes. data[0] is reserved (the * compression level is intentionally fixed to 1, see copts below); it * is still consumed so corpus byte offsets stay stable. */ const int checksum_enabled = data[1] & 1; const size_t in_chunk = derive_chunk_size(data[2]); data += 3; size -= 3; if (size == 0 || size > FUZZ_PSTREAM_MAX_INPUT) return 0; /* Persistent buffers - reused across iterations to reduce allocator * pressure (same pattern as fuzz_seekable.c). */ static uint8_t* comp_buf = NULL; static size_t comp_cap = 0; static uint8_t* round_buf = NULL; static size_t round_cap = 0; /* ------------------------------------------------------------------ */ /* Phase 1: Push compression in chunks */ /* ------------------------------------------------------------------ */ /* Level forced to 1: pstream's state-machine surface (framing, drain, * resumption, sticky errors) is independent of compression strength, * and buffer-API fuzzers (roundtrip, decompress) already exercise the * level dimension. Cheaper compression = more iterations/sec. */ zxc_compress_opts_t copts = { .level = 1, .checksum_enabled = checksum_enabled, }; zxc_cstream* cs = zxc_cstream_create(&copts); if (!cs) return 0; /* Sized so the compressor never runs out - bound is a safe upper limit. */ /* Use the public buffer-API bound function via a local extern declaration * to avoid pulling zxc_buffer.h (kept narrow on purpose). */ extern uint64_t zxc_compress_bound(size_t input_size); const uint64_t bound64 = zxc_compress_bound(size); if (bound64 == 0 || bound64 > SIZE_MAX) { zxc_cstream_free(cs); return 0; } const size_t bound = (size_t)bound64; if (bound > comp_cap) { void* nb = realloc(comp_buf, bound); if (!nb) { zxc_cstream_free(cs); return 0; } comp_buf = (uint8_t*)nb; comp_cap = bound; } size_t comp_len = 0; size_t in_pos = 0; /* Feed input in `in_chunk`-sized slices. */ while (in_pos < size) { const size_t feed = (size - in_pos < in_chunk) ? (size - in_pos) : in_chunk; zxc_inbuf_t in = {.src = data + in_pos, .size = feed, .pos = 0}; /* Drain repeatedly until this slice is fully consumed. */ for (;;) { size_t window = bound - comp_len; if (window > FUZZ_PSTREAM_OUT_CAP) window = FUZZ_PSTREAM_OUT_CAP; if (window == 0) { /* Bound exhausted - shouldn't happen with zxc_compress_bound. */ zxc_cstream_free(cs); return 0; } zxc_outbuf_t ob = {.dst = comp_buf + comp_len, .size = window, .pos = 0}; const int64_t r = zxc_cstream_compress(cs, &ob, &in); if (r < 0) { zxc_cstream_free(cs); return 0; } comp_len += ob.pos; if (in.pos == in.size && r == 0) break; } in_pos += in.pos; } /* Finalize: zxc_cstream_end may report >0 pending bytes; drain until 0. */ for (;;) { size_t window = bound - comp_len; if (window > FUZZ_PSTREAM_OUT_CAP) window = FUZZ_PSTREAM_OUT_CAP; if (window == 0) { zxc_cstream_free(cs); return 0; } zxc_outbuf_t ob = {.dst = comp_buf + comp_len, .size = window, .pos = 0}; const int64_t pending = zxc_cstream_end(cs, &ob); if (pending < 0) { zxc_cstream_free(cs); return 0; } comp_len += ob.pos; if (pending == 0) break; } zxc_cstream_free(cs); /* ------------------------------------------------------------------ */ /* Phase 2: Push decompression with chunked feeding + roundtrip */ /* ------------------------------------------------------------------ */ if (size > round_cap) { void* nb = realloc(round_buf, size); if (!nb) return 0; round_buf = (uint8_t*)nb; round_cap = size; } const int64_t dsize = drip_decompress(comp_buf, comp_len, round_buf, size, in_chunk, checksum_enabled); if (dsize >= 0) { assert((size_t)dsize == size); assert(memcmp(data, round_buf, size) == 0); } /* ------------------------------------------------------------------ */ /* Phase 3: Parser fuzzing - feed raw input directly */ /* */ /* This is where malformed-frame and truncation bugs live. We don't */ /* care about the result, only that no UB / crash occurs. */ /* ------------------------------------------------------------------ */ if (raw_size > 0) { /* Output is discarded - a single small stack buffer suffices and * keeps the parser's resumption code (small ob.size) under fuzz. */ uint8_t scratch[FUZZ_PSTREAM_OUT_CAP]; const size_t parser_chunk = derive_chunk_size(raw_data[raw_size - 1]); zxc_decompress_opts_t opts = {.checksum_enabled = checksum_enabled}; zxc_dstream* ds = zxc_dstream_create(&opts); if (ds) { size_t pos = 0; int rounds = 0; while (pos < raw_size && rounds < 1024) { const size_t feed = (raw_size - pos < parser_chunk) ? (raw_size - pos) : parser_chunk; zxc_inbuf_t in = {.src = raw_data + pos, .size = feed, .pos = 0}; zxc_outbuf_t ob = {.dst = scratch, .size = sizeof scratch, .pos = 0}; const size_t before_in = in.pos; const size_t before_out = ob.pos; const int64_t r = zxc_dstream_decompress(ds, &ob, &in); pos += in.pos; if (r < 0) break; if (in.pos == before_in && ob.pos == before_out) break; rounds++; } zxc_dstream_free(ds); } } return 0; } zxc-0.13.0/tests/fuzz_roundtrip.c000066400000000000000000000044421522536370400170240ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file fuzz_roundtrip.c * @brief Fuzzer for the one-shot compress -> decompress roundtrip. * * Asserts the core invariant of a lossless codec: decompressing what the * compressor produced must reproduce the original bytes exactly. The fuzzer * data is the payload to compress (never untrusted decoder input -- that is * fuzz_decompress.c's job), so this target hunts for encoder/decoder * mismatches rather than malformed-frame handling. * * Strategy: derive the compression level from the first byte so libFuzzer * explores every level, compress the fuzzed bytes, then decompress into an * exact-size buffer and assert a bit-exact roundtrip. Buffers are reused * across iterations to keep allocator pressure low. */ #include #include #include #include #include #include "../include/zxc_buffer.h" #define FUZZ_ROUNDTRIP_MAX_INPUT (4 << 20) /* 4 MiB */ int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { static void* comp_buf = NULL; static size_t comp_cap = 0; static void* decomp_buf = NULL; static size_t decomp_cap = 0; if (size > FUZZ_ROUNDTRIP_MAX_INPUT) return 0; const uint64_t bound64 = zxc_compress_bound(size); if (bound64 == 0 || bound64 > SIZE_MAX) return 0; const size_t bound = (size_t)bound64; if (bound > comp_cap) { void* new_buf = realloc(comp_buf, bound); if (!new_buf) return 0; comp_buf = new_buf; comp_cap = bound; } const int level = size > 0 ? (data[0] % (unsigned)zxc_max_level()) + 1 : 1; zxc_compress_opts_t copts = {.level = level}; const int64_t csize = zxc_compress(data, size, comp_buf, bound, &copts); if (csize < 0) return 0; if (size == 0) return 0; if (size > decomp_cap) { void* new_buf = realloc(decomp_buf, size); if (!new_buf) return 0; decomp_buf = new_buf; decomp_cap = size; } const int64_t dsize = zxc_decompress(comp_buf, (size_t)csize, decomp_buf, size, NULL); if (dsize >= 0) { assert((size_t)dsize == size); assert(memcmp(data, decomp_buf, size) == 0); } return 0; }zxc-0.13.0/tests/fuzz_seekable.c000066400000000000000000000144601522536370400165520ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file fuzz_seekable.c * @brief Fuzzer for the seekable random-access decompression API. * * Strategy: compress fuzzed input with seekable=1, then exercise the full * seekable read path (open, metadata getters, single-threaded decompress, * multi-threaded decompress) and verify data integrity. */ #include #include #include #include #include #include "../include/zxc_buffer.h" #include "../include/zxc_seekable.h" #define FUZZ_SEEKABLE_MAX_INPUT (4 << 20) /* 4 MiB */ int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { if (size < 2) return 0; /* Save original input for phase 7 (raw parser fuzzing) */ const uint8_t* const raw_data = data; const size_t raw_size = size; /* Use first byte as level, second as flags */ const int level = (data[0] % 5) + 1; const int use_checksum = data[1] & 1; const int use_mt = data[1] & 2; data += 2; size -= 2; if (size == 0 || size > FUZZ_SEEKABLE_MAX_INPUT) return 0; /* Persistent buffers - reused across iterations to reduce allocator pressure */ static uint8_t* comp_buf = NULL; static size_t comp_cap = 0; static uint8_t* decomp_buf = NULL; static size_t decomp_cap = 0; /* ------------------------------------------------------------------ */ /* Phase 1: Compress with seekable=1 */ /* ------------------------------------------------------------------ */ const uint64_t bound64 = zxc_compress_bound(size); if (bound64 == 0 || bound64 > SIZE_MAX) return 0; const size_t bound = (size_t)bound64; if (bound > comp_cap) { void* new_buf = realloc(comp_buf, bound); if (!new_buf) return 0; comp_buf = (uint8_t*)new_buf; comp_cap = bound; } zxc_compress_opts_t copts = { .level = level, .checksum_enabled = use_checksum, .seekable = 1, }; const int64_t csize = zxc_compress(data, size, comp_buf, bound, &copts); if (csize < 0) return 0; /* ------------------------------------------------------------------ */ /* Phase 2: Open seekable handle */ /* ------------------------------------------------------------------ */ zxc_seekable* s = zxc_seekable_open(comp_buf, (size_t)csize); if (!s) return 0; /* ------------------------------------------------------------------ */ /* Phase 3: Exercise metadata getters */ /* ------------------------------------------------------------------ */ const uint32_t num_blocks = zxc_seekable_get_num_blocks(s); const uint64_t total_decomp = zxc_seekable_get_decompressed_size(s); assert(total_decomp == size); for (uint32_t i = 0; i < num_blocks; i++) { const uint32_t csz = zxc_seekable_get_block_comp_size(s, i); const uint32_t dsz = zxc_seekable_get_block_decomp_size(s, i); assert(csz > 0); assert(dsz > 0); (void)csz; (void)dsz; } /* Out-of-range access should return 0 */ assert(zxc_seekable_get_block_comp_size(s, num_blocks) == 0); assert(zxc_seekable_get_block_decomp_size(s, num_blocks) == 0); /* ------------------------------------------------------------------ */ /* Phase 4: Full decompression via seekable range */ /* ------------------------------------------------------------------ */ if (size > decomp_cap) { void* new_buf = realloc(decomp_buf, size); if (!new_buf) { zxc_seekable_free(s); return 0; } decomp_buf = (uint8_t*)new_buf; decomp_cap = size; } int64_t dec_result; if (use_mt && size > 4096) { dec_result = zxc_seekable_decompress_range_mt(s, decomp_buf, size, 0, size, 2); } else { dec_result = zxc_seekable_decompress_range(s, decomp_buf, size, 0, size); } assert(dec_result == (int64_t)size); assert(memcmp(data, decomp_buf, size) == 0); /* ------------------------------------------------------------------ */ /* Phase 5: Partial range decompression (sub-block extraction) */ /* ------------------------------------------------------------------ */ if (size >= 4) { /* Extract a range from the middle */ const size_t off = size / 4; const size_t len = size / 2; dec_result = zxc_seekable_decompress_range(s, decomp_buf, len, off, len); assert(dec_result == (int64_t)len); assert(memcmp(data + off, decomp_buf, len) == 0); } /* ------------------------------------------------------------------ */ /* Phase 6: Edge cases */ /* ------------------------------------------------------------------ */ /* Zero-length read */ dec_result = zxc_seekable_decompress_range(s, decomp_buf, size, 0, 0); assert(dec_result == 0); /* Out-of-bounds read */ dec_result = zxc_seekable_decompress_range(s, decomp_buf, size, total_decomp, 1); assert(dec_result < 0); /* NULL handle */ assert(zxc_seekable_get_num_blocks(NULL) == 0); assert(zxc_seekable_get_decompressed_size(NULL) == 0); /* ------------------------------------------------------------------ */ /* Cleanup */ /* ------------------------------------------------------------------ */ zxc_seekable_free(s); /* ------------------------------------------------------------------ */ /* Phase 7: Fuzz the parser with raw data (malformed seekable archives) */ /* ------------------------------------------------------------------ */ zxc_seekable* s2 = zxc_seekable_open(raw_data, raw_size); if (s2) { /* If it parsed, try a read - should not crash */ const uint64_t td = zxc_seekable_get_decompressed_size(s2); if (td > 0 && td <= FUZZ_SEEKABLE_MAX_INPUT) { uint8_t* tmp = (uint8_t*)malloc((size_t)td); if (tmp) { zxc_seekable_decompress_range(s2, tmp, (size_t)td, 0, (size_t)td); free(tmp); } } zxc_seekable_free(s2); } return 0; } zxc-0.13.0/tests/test_block_api.c000066400000000000000000000573451522536370400167140ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ #include "test_common.h" int test_block_api() { printf("=== TEST: Unit - Block API (zxc_compress_block/zxc_decompress_block) ===\n"); const size_t src_size = 128 * 1024; // 128 KB int result = 0; /* All resources initialized to NULL for centralized cleanup. */ uint8_t* src = NULL; uint8_t* compressed = NULL; uint8_t* decompressed = NULL; zxc_cctx* cctx = NULL; zxc_dctx* dctx = NULL; src = malloc(src_size); if (!src) goto cleanup; gen_lz_data(src, src_size); // 1. zxc_compress_block_bound const uint64_t block_bound = zxc_compress_block_bound(src_size); const uint64_t file_bound = zxc_compress_bound(src_size); if (block_bound == 0 || block_bound >= file_bound) { printf("Failed: block_bound=%llu should be >0 and < file_bound=%llu\n", (unsigned long long)block_bound, (unsigned long long)file_bound); goto cleanup; } printf(" [PASS] block_bound=%llu < file_bound=%llu\n", (unsigned long long)block_bound, (unsigned long long)file_bound); // 2. Allocate buffers and contexts compressed = malloc((size_t)block_bound); decompressed = malloc(src_size); cctx = zxc_create_cctx(NULL); dctx = zxc_create_dctx(); if (!compressed || !decompressed || !cctx || !dctx) { printf("Failed: Allocation failed\n"); goto cleanup; } // 3. Compress block (no checksum) zxc_compress_opts_t copts = {.level = 3, .checksum_enabled = 0}; int64_t csize = zxc_compress_block(cctx, src, src_size, compressed, (size_t)block_bound, &copts); if (csize <= 0) { printf("Failed: zxc_compress_block returned %lld\n", (long long)csize); goto cleanup; } printf(" [PASS] Compress block: %zu -> %lld bytes\n", src_size, (long long)csize); // 4. Decompress block (no checksum) zxc_decompress_opts_t dopts = {.checksum_enabled = 0}; int64_t dsize = zxc_decompress_block(dctx, compressed, (size_t)csize, decompressed, src_size, &dopts); if (dsize != (int64_t)src_size) { printf("Failed: zxc_decompress_block returned %lld, expected %zu\n", (long long)dsize, src_size); goto cleanup; } if (memcmp(src, decompressed, src_size) != 0) { printf("Failed: Content mismatch after block decompression\n"); goto cleanup; } printf(" [PASS] Decompress block: roundtrip OK (no checksum)\n"); // 5. With checksum enabled copts.checksum_enabled = 1; csize = zxc_compress_block(cctx, src, src_size, compressed, (size_t)block_bound, &copts); if (csize <= 0) { printf("Failed: zxc_compress_block with checksum returned %lld\n", (long long)csize); goto cleanup; } dopts.checksum_enabled = 1; dsize = zxc_decompress_block(dctx, compressed, (size_t)csize, decompressed, src_size, &dopts); if (dsize != (int64_t)src_size || memcmp(src, decompressed, src_size) != 0) { printf("Failed: Roundtrip with checksum failed\n"); goto cleanup; } printf(" [PASS] Decompress block: roundtrip OK (with checksum)\n"); // 6. Error cases if (zxc_compress_block(NULL, src, src_size, compressed, (size_t)block_bound, &copts) >= 0) { printf("Failed: Should fail with NULL cctx\n"); goto cleanup; } if (zxc_compress_block(cctx, NULL, src_size, compressed, (size_t)block_bound, &copts) >= 0) { printf("Failed: Should fail with NULL src\n"); goto cleanup; } if (zxc_decompress_block(NULL, compressed, (size_t)csize, decompressed, src_size, &dopts) >= 0) { printf("Failed: Should fail with NULL dctx\n"); goto cleanup; } printf(" [PASS] Error cases handled correctly\n"); // 7. Context reuse across multiple blocks for (int i = 0; i < 3; i++) { gen_lz_data(src, src_size); src[0] = (uint8_t)i; copts.checksum_enabled = 0; csize = zxc_compress_block(cctx, src, src_size, compressed, (size_t)block_bound, &copts); if (csize <= 0) { printf("Failed: Reuse iteration %d compress failed\n", i); goto cleanup; } dopts.checksum_enabled = 0; dsize = zxc_decompress_block(dctx, compressed, (size_t)csize, decompressed, src_size, &dopts); if (dsize != (int64_t)src_size || memcmp(src, decompressed, src_size) != 0) { printf("Failed: Reuse iteration %d roundtrip failed\n", i); goto cleanup; } } printf(" [PASS] Context reuse: 3 independent blocks OK\n"); // 8. Auto-resize: src_size > block_size must succeed (ZXC auto-sizes) { zxc_compress_opts_t guard_opts = {.level = 3, .block_size = 4096, .checksum_enabled = 0}; int64_t guard_rc = zxc_compress_block(cctx, src, src_size, compressed, (size_t)block_bound, &guard_opts); if (guard_rc <= 0) { printf("Failed: src_size > block_size should auto-resize, got %lld\n", (long long)guard_rc); goto cleanup; } printf(" [PASS] Auto-resize: src_size > block_size succeeded\n"); } printf("PASS\n\n"); result = 1; cleanup: zxc_free_cctx(cctx); /* safe with NULL */ zxc_free_dctx(dctx); /* safe with NULL */ free(compressed); free(decompressed); free(src); return result; } /* Roundtrip helper: compress src into a newly-malloc'd buffer; return compressed size (or <0). */ static int64_t sbs_compress(const uint8_t* src, size_t src_size, int level, int checksum, uint8_t** out_buf, size_t* out_cap) { const uint64_t cbound = zxc_compress_block_bound(src_size); uint8_t* buf = (uint8_t*)malloc((size_t)cbound); if (!buf) return -1; zxc_cctx* cctx = zxc_create_cctx(NULL); zxc_compress_opts_t co = {.level = level, .checksum_enabled = checksum, .block_size = src_size}; int64_t csz = zxc_compress_block(cctx, src, src_size, buf, (size_t)cbound, &co); zxc_free_cctx(cctx); if (csz <= 0) { free(buf); return csz; } *out_buf = buf; *out_cap = (size_t)cbound; return csz; } int test_decompress_block_safe() { printf("=== TEST: Unit - zxc_decompress_block_safe ===\n"); const size_t sizes[] = {4 * 1024, 64 * 1024, 256 * 1024, 2 * 1024 * 1024}; const int levels[] = {1, 3, 5, 6}; /* 1. Roundtrip with dst_capacity == uncompressed_size at multiple sizes & levels. */ for (size_t si = 0; si < sizeof(sizes) / sizeof(sizes[0]); si++) { for (size_t li = 0; li < sizeof(levels) / sizeof(levels[0]); li++) { for (int checksum = 0; checksum <= 1; checksum++) { const size_t n = sizes[si]; const int lvl = levels[li]; uint8_t* src = (uint8_t*)malloc(n); if (!src) { printf("Failed: malloc src\n"); return 0; } gen_lz_data(src, n); uint8_t* comp = NULL; size_t comp_cap = 0; int64_t csz = sbs_compress(src, n, lvl, checksum, &comp, &comp_cap); if (csz <= 0) { printf("Failed: compress (n=%zu lvl=%d chk=%d) -> %lld\n", n, lvl, checksum, (long long)csz); free(src); free(comp); return 0; } uint8_t* dst = (uint8_t*)malloc(n); /* tight: no tail pad */ if (!dst) { free(src); free(comp); return 0; } zxc_dctx* dctx = zxc_create_dctx(); zxc_decompress_opts_t dopts = {.checksum_enabled = checksum}; int64_t dsz = zxc_decompress_block_safe(dctx, comp, (size_t)csz, dst, n, &dopts); int ok = (dsz == (int64_t)n) && memcmp(src, dst, n) == 0; zxc_free_dctx(dctx); free(dst); free(comp); free(src); if (!ok) { printf("Failed: safe roundtrip n=%zu lvl=%d chk=%d -> dsz=%lld\n", n, lvl, checksum, (long long)dsz); return 0; } } } } printf(" [PASS] safe roundtrip across sizes/levels/checksum\n"); /* 2. Bit-identical vs zxc_decompress_block on the same compressed payload. */ { const size_t n = 128 * 1024; uint8_t* src = (uint8_t*)malloc(n); gen_lz_data(src, n); uint8_t* comp = NULL; size_t comp_cap = 0; int64_t csz = sbs_compress(src, n, 3, 0, &comp, &comp_cap); const uint64_t dbound = zxc_decompress_block_bound(n); uint8_t* dst_fast = (uint8_t*)malloc((size_t)dbound); uint8_t* dst_safe = (uint8_t*)malloc(n); zxc_dctx* dctx1 = zxc_create_dctx(); zxc_dctx* dctx2 = zxc_create_dctx(); int64_t r1 = zxc_decompress_block(dctx1, comp, (size_t)csz, dst_fast, (size_t)dbound, NULL); int64_t r2 = zxc_decompress_block_safe(dctx2, comp, (size_t)csz, dst_safe, n, NULL); int ok = (r1 == (int64_t)n) && (r2 == (int64_t)n) && memcmp(dst_fast, dst_safe, n) == 0; zxc_free_dctx(dctx1); zxc_free_dctx(dctx2); free(dst_safe); free(dst_fast); free(comp); free(src); if (!ok) { printf("Failed: safe/fast not bit-identical: r1=%lld r2=%lld\n", (long long)r1, (long long)r2); return 0; } printf(" [PASS] bit-identical output vs fast path\n"); } /* 3. dst_capacity < uncompressed_size -> negative error. */ { const size_t n = 64 * 1024; uint8_t* src = (uint8_t*)malloc(n); gen_lz_data(src, n); uint8_t* comp = NULL; size_t comp_cap = 0; int64_t csz = sbs_compress(src, n, 3, 0, &comp, &comp_cap); uint8_t* dst = (uint8_t*)malloc(n); zxc_dctx* dctx = zxc_create_dctx(); int64_t r = zxc_decompress_block_safe(dctx, comp, (size_t)csz, dst, n - 128, NULL); int ok = (r < 0); zxc_free_dctx(dctx); free(dst); free(comp); free(src); if (!ok) { printf("Failed: expected negative error for undersized dst, got %lld\n", (long long)r); return 0; } printf(" [PASS] negative error on dst_capacity < uncompressed_size\n"); } /* 4. Literal-heavy input: would trip OVERFLOW in the fast path when * dst_capacity == uncompressed_size; must succeed with the safe API. */ { const size_t n = 32 * 1024; uint8_t* src = (uint8_t*)malloc(n); gen_random_data(src, n); /* random data -> heavy literal runs, varint-prone */ uint8_t* comp = NULL; size_t comp_cap = 0; int64_t csz = sbs_compress(src, n, 3, 0, &comp, &comp_cap); if (csz <= 0) { printf("Failed: compress literal-heavy\n"); return 0; } uint8_t* dst = (uint8_t*)malloc(n); zxc_dctx* dctx = zxc_create_dctx(); int64_t r = zxc_decompress_block_safe(dctx, comp, (size_t)csz, dst, n, NULL); int ok = (r == (int64_t)n) && memcmp(src, dst, n) == 0; zxc_free_dctx(dctx); free(dst); free(comp); free(src); if (!ok) { printf("Failed: literal-heavy safe decode: r=%lld\n", (long long)r); return 0; } printf(" [PASS] literal-heavy tail decodes into tight dst\n"); } /* 5. Corrupted stream returns a negative error and does not crash. */ { const size_t n = 16 * 1024; uint8_t* src = (uint8_t*)malloc(n); gen_lz_data(src, n); uint8_t* comp = NULL; size_t comp_cap = 0; int64_t csz = sbs_compress(src, n, 3, 1, &comp, &comp_cap); /* with checksum */ /* Flip a byte in the payload to corrupt. */ comp[ZXC_BLOCK_HEADER_SIZE + (csz - ZXC_BLOCK_HEADER_SIZE) / 2] ^= 0xA5; uint8_t* dst = (uint8_t*)malloc(n); zxc_dctx* dctx = zxc_create_dctx(); zxc_decompress_opts_t opts = {.checksum_enabled = 1}; int64_t r = zxc_decompress_block_safe(dctx, comp, (size_t)csz, dst, n, &opts); int ok = (r < 0); zxc_free_dctx(dctx); free(dst); free(comp); free(src); if (!ok) { printf("Failed: corrupted stream should fail, got %lld\n", (long long)r); return 0; } printf(" [PASS] corrupted stream -> negative error (no crash)\n"); } printf("PASS\n\n"); return 1; } int test_decompress_block_bound() { printf("=== TEST: Unit - zxc_decompress_block_bound ===\n"); /* 1. Sanity: helper must return more than the input (tail pad > 0). */ { const size_t n = 4096; const uint64_t b = zxc_decompress_block_bound(n); if (b <= n) { printf("Failed: bound(%zu)=%llu must exceed input (tail pad missing)\n", n, (unsigned long long)b); return 0; } /* Pad is a fixed margin, so the delta must be constant. */ const uint64_t pad = b - n; const uint64_t b2 = zxc_decompress_block_bound(n * 4); if (b2 - n * 4 != pad) { printf("Failed: tail pad must be constant, got %llu vs %llu\n", (unsigned long long)pad, (unsigned long long)(b2 - n * 4)); return 0; } printf(" [PASS] bound(n) = n + %llu (constant tail pad)\n", (unsigned long long)pad); } /* 2. Overflow: huge input must return 0. */ { if (zxc_decompress_block_bound(SIZE_MAX) != 0) { printf("Failed: bound(SIZE_MAX) must return 0 on overflow\n"); return 0; } printf(" [PASS] bound(SIZE_MAX) -> 0 (overflow guard)\n"); } /* 3. Edge: bound(0) must still return a valid non-zero pad. */ { if (zxc_decompress_block_bound(0) == 0) { printf("Failed: bound(0) must be > 0 (tail pad always required)\n"); return 0; } printf(" [PASS] bound(0) > 0\n"); } /* 4. Functional: a roundtrip using bound-sized dst must succeed. */ { const size_t src_size = 64 * 1024; uint8_t* src = malloc(src_size); if (!src) return 0; gen_lz_data(src, src_size); const uint64_t cbound = zxc_compress_block_bound(src_size); uint8_t* compressed = malloc((size_t)cbound); const uint64_t dbound = zxc_decompress_block_bound(src_size); uint8_t* decompressed = malloc((size_t)dbound); zxc_cctx* cctx = zxc_create_cctx(NULL); zxc_dctx* dctx = zxc_create_dctx(); int ok = 0; if (compressed && decompressed && cctx && dctx) { zxc_compress_opts_t copts = {.level = 3}; int64_t csize = zxc_compress_block(cctx, src, src_size, compressed, (size_t)cbound, &copts); if (csize > 0) { int64_t dsize = zxc_decompress_block(dctx, compressed, (size_t)csize, decompressed, (size_t)dbound, NULL); ok = (dsize == (int64_t)src_size) && memcmp(src, decompressed, src_size) == 0; } } zxc_free_cctx(cctx); zxc_free_dctx(dctx); free(decompressed); free(compressed); free(src); if (!ok) { printf("Failed: roundtrip with bound-sized dst failed\n"); return 0; } printf(" [PASS] roundtrip into bound-sized dst OK\n"); } printf("PASS\n\n"); return 1; } /** * @brief Stress-test the block API with boundary sizes across all levels. * * Tests zxc_compress_block / zxc_decompress_block with input sizes carefully * chosen to land near internal buffer limits (search_limit, page boundaries). * * This test covers: * - Sizes near the LZ match-finder safety margin (8-20 bytes) * - Odd sizes that stress alignment assumptions * - Data patterns that trigger each block type encoder (GLO, GHI, RAW) * - All compression levels */ int test_block_api_boundary_sizes() { printf("=== TEST: Block API - Boundary Sizes ===\n"); /* Edge-case sizes: near search_limit (iend-8), near page boundaries, odd, * and large block sizes (128KB - 2MB) */ const size_t sizes[] = { 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19, 20, 23, 24, 25, /* Near search_limit margin */ 31, 32, 33, 48, 63, 64, 65, /* Cache line edges */ 100, 127, 128, 129, 255, 256, 257, /* Byte boundary edges */ 511, 512, 513, 1023, 1024, 1025, /* 1 KB edges */ 4095, 4096, 4097, /* Page boundary */ 8191, 8192, 8193, /* 2-page boundary */ 16383, 16384, 16385, /* 4-page boundary */ 65535, 65536, 65537, /* 64 KB boundary */ 128 * 1024, 128 * 1024 + 1, /* 128 KB block */ 256 * 1024, 256 * 1024 - 1, /* 256 KB block */ 512 * 1024, /* 512 KB block */ 1024 * 1024, /* 1 MB */ 2 * 1024 * 1024, /* 2 MB max block */ }; const int num_sizes = (int)(sizeof(sizes) / sizeof(sizes[0])); /* Data generators: each triggers a different encoder path */ typedef void (*gen_fn)(uint8_t*, size_t); const struct { const char* name; gen_fn gen; } patterns[] = { {"LZ (GLO/GHI)", gen_lz_data}, {"Random (RAW)", gen_random_data}, {"Numeric (integers)", gen_num_data}, }; const int num_patterns = (int)(sizeof(patterns) / sizeof(patterns[0])); const size_t max_size = sizes[num_sizes - 1]; uint8_t* src = malloc(max_size); const uint64_t bound = zxc_compress_block_bound(max_size); uint8_t* compressed = malloc((size_t)bound); uint8_t* decompressed = malloc(max_size); zxc_cctx* cctx = zxc_create_cctx(NULL); zxc_dctx* dctx = zxc_create_dctx(); if (!src || !compressed || !decompressed || !cctx || !dctx) { printf(" [FAIL] allocation failed\n"); free(src); free(compressed); free(decompressed); zxc_free_cctx(cctx); zxc_free_dctx(dctx); return 0; } int failures = 0; for (int p = 0; p < num_patterns; p++) { /* Generate data once at max size; smaller tests use prefix */ patterns[p].gen(src, max_size); for (int lvl = 1; lvl <= 5; lvl++) { for (int s = 0; s < num_sizes; s++) { const size_t sz = sizes[s]; /* gen_num_data fills whole 4-byte integers; skip tiny/unaligned * sizes so the buffer is fully initialized before round-trip. */ if (p == 2 && (sz < 16 || sz % 4 != 0)) continue; zxc_compress_opts_t copts = {.level = lvl, .checksum_enabled = 0}; const int64_t csize = zxc_compress_block( cctx, src, sz, compressed, (size_t)bound, &copts); if (csize <= 0) { /* RAW fallback or incompressible is OK, but actual errors are not */ if (csize < 0 && csize != ZXC_ERROR_DST_TOO_SMALL) { printf(" [FAIL] %s lvl=%d sz=%zu: compress error %lld\n", patterns[p].name, lvl, sz, (long long)csize); failures++; } continue; } zxc_decompress_opts_t dopts = {.checksum_enabled = 0}; const int64_t dsize = zxc_decompress_block( dctx, compressed, (size_t)csize, decompressed, sz, &dopts); if (dsize != (int64_t)sz) { printf(" [FAIL] %s lvl=%d sz=%zu: decompress returned %lld (expected %zu)\n", patterns[p].name, lvl, sz, (long long)dsize, sz); failures++; continue; } if (memcmp(src, decompressed, sz) != 0) { printf(" [FAIL] %s lvl=%d sz=%zu: content mismatch\n", patterns[p].name, lvl, sz); failures++; } } } } free(src); free(compressed); free(decompressed); zxc_free_cctx(cctx); zxc_free_dctx(dctx); if (failures > 0) { printf(" [FAIL] %d sub-tests failed\n", failures); return 0; } printf(" [PASS] All boundary sizes passed (%d patterns x 5 levels x %d sizes)\n", num_patterns, num_sizes); return 1; } /* * Regression test: the Block API enforces the format-level block size cap * (ZXC_BLOCK_SIZE_MAX = 2 MiB). * * Inputs larger than ZXC_BLOCK_SIZE_MAX must be rejected upfront with * ZXC_ERROR_BAD_BLOCK_SIZE; callers with bigger payloads must use the frame * or streaming APIs which chunk transparently. The same cap bounds the * varint value space, neutralizing wrap-around attacks downstream. */ int test_block_api_large_block_varint() { printf("=== TEST: Block API - Reject blocks > ZXC_BLOCK_SIZE_MAX ===\n"); const struct { size_t size; const char* name; } cases[] = { {3 * 1024 * 1024, "3 MiB"}, {4 * 1024 * 1024, "4 MiB"}, {8 * 1024 * 1024, "8 MiB"}, }; const int num_cases = (int)(sizeof(cases) / sizeof(cases[0])); int failures = 0; for (int i = 0; i < num_cases; i++) { const size_t sz = cases[i].size; uint8_t* src = malloc(sz); const uint64_t bound = zxc_compress_block_bound(ZXC_BLOCK_SIZE_MAX); uint8_t* compressed = bound ? malloc((size_t)bound) : NULL; uint8_t* decompressed = malloc(sz); zxc_cctx* cctx = zxc_create_cctx(NULL); zxc_dctx* dctx = zxc_create_dctx(); if (!src || !compressed || !decompressed || !cctx || !dctx) { printf(" [FAIL] %s: allocation failed\n", cases[i].name); failures++; goto per_case_cleanup; } gen_lz_data(src, sz); zxc_compress_opts_t copts = {.level = 1, .checksum_enabled = 1}; const int64_t csize = zxc_compress_block(cctx, src, sz, compressed, (size_t)bound, &copts); if (csize != ZXC_ERROR_BAD_BLOCK_SIZE) { printf(" [FAIL] %s compress: expected ZXC_ERROR_BAD_BLOCK_SIZE (%d), got %lld\n", cases[i].name, ZXC_ERROR_BAD_BLOCK_SIZE, (long long)csize); failures++; goto per_case_cleanup; } zxc_decompress_opts_t dopts = {.checksum_enabled = 1}; const int64_t dsize = zxc_decompress_block(dctx, src, sz, decompressed, sz, &dopts); if (dsize != ZXC_ERROR_BAD_BLOCK_SIZE) { printf(" [FAIL] %s decompress: expected ZXC_ERROR_BAD_BLOCK_SIZE (%d), got %lld\n", cases[i].name, ZXC_ERROR_BAD_BLOCK_SIZE, (long long)dsize); failures++; goto per_case_cleanup; } const int64_t dssize = zxc_decompress_block_safe(dctx, src, sz, decompressed, sz, &dopts); if (dssize != ZXC_ERROR_BAD_BLOCK_SIZE) { printf(" [FAIL] %s decompress_safe: expected ZXC_ERROR_BAD_BLOCK_SIZE (%d), got %lld\n", cases[i].name, ZXC_ERROR_BAD_BLOCK_SIZE, (long long)dssize); failures++; goto per_case_cleanup; } printf(" [PASS] %s correctly rejected by compress, decompress, and decompress_safe\n", cases[i].name); per_case_cleanup: free(src); free(compressed); free(decompressed); zxc_free_cctx(cctx); zxc_free_dctx(dctx); } if (failures > 0) { printf("FAILED: %d sub-tests failed\n", failures); return 0; } printf("PASS\n\n"); return 1; } zxc-0.13.0/tests/test_buffer_api.c000066400000000000000000001033251522536370400170610ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ #include "test_common.h" // Checks that the utility function calculates a sufficient size int test_max_compressed_size_logic() { printf("=== TEST: Unit - zxc_compress_bound ===\n"); // Case 1: 0 bytes (must at least contain the header) size_t sz0 = (size_t)zxc_compress_bound(0); if (sz0 == 0) { printf("Failed: Size for 0 bytes should not be 0 (headers required)\n"); return 0; } // Case 2: Small input size_t input_val = 100; size_t sz100 = (size_t)zxc_compress_bound(input_val); if (sz100 < input_val) { printf("Failed: Output buffer size (%zu) too small for input (%zu)\n", sz100, input_val); return 0; } // Case 3: Consistency (size should not decrease arbitrarily) if (zxc_compress_bound(2000) < zxc_compress_bound(1000)) { printf("Failed: Max size function is not monotonic\n"); return 0; } printf("PASS\n\n"); return 1; } // Checks the buffer-based API (zxc_compress / zxc_decompress) int test_buffer_api() { printf("=== TEST: Unit - Buffer API (zxc_compress/zxc_decompress) ===\n"); size_t src_size = 128 * 1024; uint8_t* src = malloc(src_size); gen_lz_data(src, src_size); // 1. Calculate max compressed size size_t max_dst_size = (size_t)zxc_compress_bound(src_size); uint8_t* compressed = malloc(max_dst_size); int checksum_enabled = 1; // 2. Compress zxc_compress_opts_t _co23 = {.level = 3, .checksum_enabled = checksum_enabled}; int64_t compressed_size = zxc_compress(src, src_size, compressed, max_dst_size, &_co23); if (compressed_size <= 0) { printf("Failed: zxc_compress returned %lld\n", (long long)compressed_size); free(src); free(compressed); return 0; } printf("Compressed %zu bytes to %lld bytes\n", src_size, (long long)compressed_size); // 3. Decompress uint8_t* decompressed = malloc(src_size); zxc_decompress_opts_t _do24 = {.checksum_enabled = checksum_enabled}; int64_t decompressed_size = zxc_decompress(compressed, (size_t)compressed_size, decompressed, src_size, &_do24); if (decompressed_size != (int64_t)src_size) { printf("Failed: zxc_decompress returned %lld, expected %zu\n", (long long)decompressed_size, src_size); free(src); free(compressed); free(decompressed); return 0; } // 4. Verify content if (memcmp(src, decompressed, src_size) != 0) { printf("Failed: Content mismatch after decompression\n"); free(src); free(compressed); free(decompressed); return 0; } // 5. Test error case: Destination too small size_t small_capacity = (size_t)(compressed_size / 2); zxc_compress_opts_t _co25 = {.level = 3, .checksum_enabled = checksum_enabled}; int64_t small_res = zxc_compress(src, src_size, compressed, small_capacity, &_co25); if (small_res >= 0) { printf("Failed: zxc_compress should fail with small buffer (returned %lld)\n", (long long)small_res); free(src); free(compressed); free(decompressed); return 0; } printf("PASS\n\n"); free(src); free(compressed); free(decompressed); return 1; } // Test zxc_get_decompressed_size int test_get_decompressed_size() { printf("=== TEST: Unit - zxc_get_decompressed_size ===\n"); // 1. Compress some data, then check decompressed size size_t src_size = 64 * 1024; uint8_t* src = malloc(src_size); gen_lz_data(src, src_size); size_t max_dst = (size_t)zxc_compress_bound(src_size); uint8_t* compressed = malloc(max_dst); zxc_compress_opts_t _co29 = {.level = 3, .checksum_enabled = 0}; int64_t comp_size = zxc_compress(src, src_size, compressed, max_dst, &_co29); if (comp_size <= 0) { printf("Failed: Compression returned 0\n"); free(src); free(compressed); return 0; } size_t reported = (size_t)zxc_get_decompressed_size(compressed, comp_size); if (reported != src_size) { printf("Failed: Expected %zu, got %zu\n", src_size, reported); free(src); free(compressed); return 0; } printf(" [PASS] Valid compressed data\n"); // 2. Too-small buffer if (zxc_get_decompressed_size(compressed, 4) != 0) { printf("Failed: Should return 0 for too-small buffer\n"); free(src); free(compressed); return 0; } printf(" [PASS] Too-small buffer\n"); // 3. Invalid magic word uint8_t bad_buf[64] = {0}; if (zxc_get_decompressed_size(bad_buf, sizeof(bad_buf)) != 0) { printf("Failed: Should return 0 for invalid magic\n"); free(src); free(compressed); return 0; } printf(" [PASS] Invalid magic word\n"); // 4. Forged footer: an implausible size (far beyond what the archive's // block count could decode to) must return 0, not drive a huge // allocation in callers that size buffers from this value. uint8_t* forged = malloc((size_t)comp_size); memcpy(forged, compressed, (size_t)comp_size); uint8_t* footer_size = forged + comp_size - ZXC_FILE_FOOTER_SIZE; for (int i = 0; i < 8; i++) footer_size[i] = 0xFF; // size = 2^64 - 1 if (zxc_get_decompressed_size(forged, (size_t)comp_size) != 0) { printf("Failed: Should return 0 for a forged (implausible) footer size\n"); free(forged); free(src); free(compressed); return 0; } free(forged); printf(" [PASS] Forged footer size rejected\n"); printf("PASS\n\n"); free(src); free(compressed); return 1; } int test_buffer_error_codes() { printf("=== TEST: Unit - Buffer API Error Codes ===\n"); /* ------------------------------------------------------------------ */ /* zxc_compress error paths */ /* ------------------------------------------------------------------ */ // 1. NULL src zxc_compress_opts_t _co30 = {.level = 3, .checksum_enabled = 0}; int64_t r = zxc_compress(NULL, 100, (void*)1, 100, &_co30); if (r != ZXC_ERROR_NULL_INPUT) { printf(" [FAIL] NULL src: expected %d, got %lld\n", ZXC_ERROR_NULL_INPUT, (long long)r); return 0; } printf(" [PASS] zxc_compress NULL src -> ZXC_ERROR_NULL_INPUT\n"); // 2. NULL dst zxc_compress_opts_t _co31 = {.level = 3, .checksum_enabled = 0}; r = zxc_compress((void*)1, 100, NULL, 100, &_co31); if (r != ZXC_ERROR_NULL_INPUT) { printf(" [FAIL] NULL dst: expected %d, got %lld\n", ZXC_ERROR_NULL_INPUT, (long long)r); return 0; } printf(" [PASS] zxc_compress NULL dst -> ZXC_ERROR_NULL_INPUT\n"); // 3. src_size == 0 produces a valid empty frame (header + EOF + footer) uint8_t dummy[16]; uint8_t empty_dst[64]; zxc_compress_opts_t _co32 = {.level = 3, .checksum_enabled = 0}; r = zxc_compress(NULL, 0, empty_dst, sizeof(empty_dst), &_co32); if (r <= 0) { printf(" [FAIL] src_size==0: expected valid frame, got %lld\n", (long long)r); return 0; } uint64_t orig = zxc_get_decompressed_size(empty_dst, (size_t)r); if (orig != 0) { printf(" [FAIL] src_size==0: decompressed size %llu != 0\n", (unsigned long long)orig); return 0; } printf(" [PASS] zxc_compress src_size==0 -> valid empty frame (%lld bytes)\n", (long long)r); // 4. dst_capacity == 0 zxc_compress_opts_t _co33 = {.level = 3, .checksum_enabled = 0}; r = zxc_compress(dummy, sizeof(dummy), dummy, 0, &_co33); if (r != ZXC_ERROR_NULL_INPUT) { printf(" [FAIL] dst_cap==0: expected %d, got %lld\n", ZXC_ERROR_NULL_INPUT, (long long)r); return 0; } printf(" [PASS] zxc_compress dst_capacity==0 -> ZXC_ERROR_NULL_INPUT\n"); // 5. dst too small for file header (< 16 bytes) { uint8_t src[64]; uint8_t dst[8]; // Too small for file header (16 bytes) gen_lz_data(src, sizeof(src)); zxc_compress_opts_t _co34 = {.level = 3, .checksum_enabled = 0}; r = zxc_compress(src, sizeof(src), dst, sizeof(dst), &_co34); if (r >= 0) { printf(" [FAIL] dst too small for header: expected < 0, got %lld\n", (long long)r); return 0; } } printf(" [PASS] zxc_compress dst too small for header -> negative\n"); // 6. dst too small for data (fits header but not chunk) { const size_t src_sz = 4096; uint8_t* src = malloc(src_sz); const size_t small_dst = 128; uint8_t* dst = malloc(small_dst); gen_lz_data(src, src_sz); zxc_compress_opts_t _co35 = {.level = 3, .checksum_enabled = 0}; r = zxc_compress(src, src_sz, dst, small_dst, &_co35); if (r >= 0) { printf(" [FAIL] dst too small for chunk: expected < 0, got %lld\n", (long long)r); free(src); free(dst); return 0; } free(src); free(dst); } printf(" [PASS] zxc_compress dst too small for chunk -> negative\n"); // 7. dst too small for EOF + footer { // Compress first to find the exact compressed size, then retry with // just enough for the data blocks but not for the EOF + footer. const size_t src_sz = 256; uint8_t* src = malloc(src_sz); gen_lz_data(src, src_sz); const size_t full_cap = (size_t)zxc_compress_bound(src_sz); uint8_t* full_dst = malloc(full_cap); zxc_compress_opts_t _co36 = {.level = 3, .checksum_enabled = 0}; const int64_t full_sz = zxc_compress(src, src_sz, full_dst, full_cap, &_co36); if (full_sz <= 0) { printf(" [SKIP] Cannot prepare for EOF test\n"); free(src); free(full_dst); } else { // EOF header(8) + footer(12) = 20 bytes at the end. // Try with a buffer that's just a few bytes too small. const size_t tight = (size_t)full_sz - 5; uint8_t* tight_dst = malloc(tight); zxc_compress_opts_t _co37 = {.level = 3, .checksum_enabled = 0}; r = zxc_compress(src, src_sz, tight_dst, tight, &_co37); if (r >= 0) { printf(" [FAIL] dst too small for EOF+footer: expected < 0, got %lld\n", (long long)r); free(src); free(full_dst); free(tight_dst); return 0; } free(src); free(full_dst); free(tight_dst); } } printf(" [PASS] zxc_compress dst too small for EOF+footer -> negative\n"); /* ------------------------------------------------------------------ */ /* zxc_decompress error paths */ /* ------------------------------------------------------------------ */ // 8. NULL src zxc_decompress_opts_t _do38 = {.checksum_enabled = 0}; r = zxc_decompress(NULL, 100, (void*)1, 100, &_do38); if (r != ZXC_ERROR_NULL_INPUT) { printf(" [FAIL] decompress NULL src: expected %d, got %lld\n", ZXC_ERROR_NULL_INPUT, (long long)r); return 0; } printf(" [PASS] zxc_decompress NULL src -> ZXC_ERROR_NULL_INPUT\n"); // 9. NULL dst zxc_decompress_opts_t _do39 = {.checksum_enabled = 0}; r = zxc_decompress((void*)1, 100, NULL, 100, &_do39); if (r != ZXC_ERROR_NULL_INPUT) { printf(" [FAIL] decompress NULL dst: expected %d, got %lld\n", ZXC_ERROR_NULL_INPUT, (long long)r); return 0; } printf(" [PASS] zxc_decompress NULL dst -> ZXC_ERROR_NULL_INPUT\n"); // 10. src too small for file header { uint8_t tiny[4] = {0}; uint8_t out[64]; zxc_decompress_opts_t _do40 = {.checksum_enabled = 0}; r = zxc_decompress(tiny, sizeof(tiny), out, sizeof(out), &_do40); if (r != ZXC_ERROR_NULL_INPUT) { printf(" [FAIL] src too small: expected %d, got %lld\n", ZXC_ERROR_NULL_INPUT, (long long)r); return 0; } } printf(" [PASS] zxc_decompress src too small -> ZXC_ERROR_NULL_INPUT\n"); // 11. Bad file header (invalid magic) { uint8_t bad_src[64]; memset(bad_src, 0, sizeof(bad_src)); uint8_t out[64]; zxc_decompress_opts_t _do41 = {.checksum_enabled = 0}; r = zxc_decompress(bad_src, sizeof(bad_src), out, sizeof(out), &_do41); if (r != ZXC_ERROR_BAD_HEADER) { printf(" [FAIL] bad magic: expected %d, got %lld\n", ZXC_ERROR_BAD_HEADER, (long long)r); return 0; } } printf(" [PASS] zxc_decompress bad magic -> ZXC_ERROR_BAD_HEADER\n"); // Prepare a valid compressed buffer for subsequent decompress error tests const size_t test_src_sz = 1024; uint8_t* test_src = malloc(test_src_sz); gen_lz_data(test_src, test_src_sz); const size_t comp_cap = (size_t)zxc_compress_bound(test_src_sz); uint8_t* comp_buf = malloc(comp_cap); zxc_compress_opts_t _co42 = {.level = 3, .checksum_enabled = 1}; const int64_t comp_sz = zxc_compress(test_src, test_src_sz, comp_buf, comp_cap, &_co42); if (comp_sz <= 0) { printf(" [FAIL] Could not prepare compressed data\n"); free(test_src); free(comp_buf); return 0; } // 12. Corrupt block header (damage the first block header byte after file header) { uint8_t* corrupt = malloc((size_t)comp_sz); memcpy(corrupt, comp_buf, (size_t)comp_sz); // Corrupt the block type byte at offset ZXC_FILE_HEADER_SIZE corrupt[ZXC_FILE_HEADER_SIZE] = 0xFF; // Invalid block type uint8_t* out = malloc(test_src_sz); zxc_decompress_opts_t _do43 = {.checksum_enabled = 1}; r = zxc_decompress(corrupt, (size_t)comp_sz, out, test_src_sz, &_do43); if (r >= 0) { printf(" [FAIL] corrupt block header: expected < 0, got %lld\n", (long long)r); free(corrupt); free(out); free(test_src); free(comp_buf); return 0; } free(corrupt); free(out); } printf(" [PASS] zxc_decompress corrupt block header -> negative\n"); // 13. Truncated at EOF (missing footer) { // Find the EOF block: it ends with the footer(12 bytes) // Truncate so the footer is missing const size_t trunc_sz = (size_t)comp_sz - ZXC_FILE_FOOTER_SIZE + 2; // Cut most of footer uint8_t* out = malloc(test_src_sz); zxc_decompress_opts_t _do44 = {.checksum_enabled = 1}; r = zxc_decompress(comp_buf, trunc_sz, out, test_src_sz, &_do44); if (r >= 0) { printf(" [FAIL] truncated footer: expected < 0, got %lld\n", (long long)r); free(out); free(test_src); free(comp_buf); return 0; } free(out); } printf(" [PASS] zxc_decompress truncated footer -> negative\n"); // 14. Stored size mismatch (corrupt the source size in footer) { uint8_t* corrupt = malloc((size_t)comp_sz); memcpy(corrupt, comp_buf, (size_t)comp_sz); // Footer is at end: last 12 bytes = [src_size(8)] + [global_hash(4)] // Corrupt the source size field (add 1 to the first byte) const size_t footer_offset = (size_t)comp_sz - ZXC_FILE_FOOTER_SIZE; corrupt[footer_offset] ^= 0x01; // Flip a bit in the stored source size uint8_t* out = malloc(test_src_sz); zxc_decompress_opts_t _do45 = {.checksum_enabled = 1}; r = zxc_decompress(corrupt, (size_t)comp_sz, out, test_src_sz, &_do45); if (r >= 0) { printf(" [FAIL] size mismatch: expected < 0, got %lld\n", (long long)r); free(corrupt); free(out); free(test_src); free(comp_buf); return 0; } free(corrupt); free(out); } printf(" [PASS] zxc_decompress stored size mismatch -> negative\n"); // 15. Global checksum failure (corrupt the global hash in footer) { uint8_t* corrupt = malloc((size_t)comp_sz); memcpy(corrupt, comp_buf, (size_t)comp_sz); // Global hash is the last 4 bytes of the file corrupt[comp_sz - 1] ^= 0xFF; uint8_t* out = malloc(test_src_sz); zxc_decompress_opts_t _do46 = {.checksum_enabled = 1}; r = zxc_decompress(corrupt, (size_t)comp_sz, out, test_src_sz, &_do46); if (r != ZXC_ERROR_BAD_CHECKSUM) { printf(" [FAIL] bad global checksum: expected %d, got %lld\n", ZXC_ERROR_BAD_CHECKSUM, (long long)r); free(corrupt); free(out); free(test_src); free(comp_buf); return 0; } free(corrupt); free(out); } printf(" [PASS] zxc_decompress global checksum -> ZXC_ERROR_BAD_CHECKSUM\n"); // 16. dst too small for decompression { uint8_t* out = malloc(test_src_sz / 4); // Way too small zxc_decompress_opts_t _do47 = {.checksum_enabled = 0}; r = zxc_decompress(comp_buf, (size_t)comp_sz, out, test_src_sz / 4, &_do47); if (r >= 0) { printf(" [FAIL] dst too small for decompress: expected < 0, got %lld\n", (long long)r); free(out); free(test_src); free(comp_buf); return 0; } free(out); } printf(" [PASS] zxc_decompress dst too small -> negative\n"); free(test_src); free(comp_buf); printf("PASS\n\n"); return 1; } // Tests the dst=NULL / dst_capacity=0 short-circuit in zxc_decompress: // allowed only when the compressed frame's stored size is 0. int test_decompress_empty_frame_null_dst() { printf("=== TEST: Unit - Decompress empty frame with NULL/zero dst ===\n"); /* 1. Produce a valid empty frame via zxc_compress(NULL, 0, ...). */ uint8_t empty_frame[64]; int64_t comp_sz = zxc_compress(NULL, 0, empty_frame, sizeof(empty_frame), NULL); if (comp_sz <= 0) { printf(" [FAIL] empty compress: got %lld\n", (long long)comp_sz); return 0; } /* 2. dst=NULL, dst_capacity=0 on empty frame -> 0 */ int64_t r = zxc_decompress(empty_frame, (size_t)comp_sz, NULL, 0, NULL); if (r != 0) { printf(" [FAIL] NULL dst + 0 cap on empty frame: expected 0, got %lld\n", (long long)r); return 0; } printf(" [PASS] NULL dst + 0 cap on empty frame -> 0\n"); /* 3. dst=valid, dst_capacity=0 on empty frame -> 0 */ uint8_t dummy; r = zxc_decompress(empty_frame, (size_t)comp_sz, &dummy, 0, NULL); if (r != 0) { printf(" [FAIL] valid dst + 0 cap on empty frame: expected 0, got %lld\n", (long long)r); return 0; } printf(" [PASS] valid dst + 0 cap on empty frame -> 0\n"); /* 4. dst=NULL, dst_capacity=0 on non-empty frame -> ZXC_ERROR_DST_TOO_SMALL */ uint8_t payload[128] = "hello"; uint8_t non_empty_frame[256]; int64_t ne_sz = zxc_compress(payload, sizeof(payload), non_empty_frame, sizeof(non_empty_frame), NULL); if (ne_sz <= 0) { printf(" [FAIL] non-empty compress: got %lld\n", (long long)ne_sz); return 0; } r = zxc_decompress(non_empty_frame, (size_t)ne_sz, NULL, 0, NULL); if (r != ZXC_ERROR_DST_TOO_SMALL) { printf(" [FAIL] NULL dst + 0 cap on non-empty frame: expected %d, got %lld\n", ZXC_ERROR_DST_TOO_SMALL, (long long)r); return 0; } printf(" [PASS] NULL dst + 0 cap on non-empty frame -> DST_TOO_SMALL\n"); /* 5. dst=NULL, dst_capacity=0 on bad magic -> ZXC_ERROR_NULL_INPUT */ uint8_t bad_magic[64]; memcpy(bad_magic, empty_frame, (size_t)comp_sz); bad_magic[0] ^= 0xFF; r = zxc_decompress(bad_magic, (size_t)comp_sz, NULL, 0, NULL); if (r != ZXC_ERROR_NULL_INPUT) { printf(" [FAIL] NULL dst + 0 cap + bad magic: expected %d, got %lld\n", ZXC_ERROR_NULL_INPUT, (long long)r); return 0; } printf(" [PASS] NULL dst + 0 cap + bad magic -> NULL_INPUT\n"); /* 6. Caller bug: dst=NULL with non-zero capacity -> ZXC_ERROR_NULL_INPUT */ r = zxc_decompress(empty_frame, (size_t)comp_sz, NULL, 100, NULL); if (r != ZXC_ERROR_NULL_INPUT) { printf(" [FAIL] NULL dst + cap>0: expected %d, got %lld\n", ZXC_ERROR_NULL_INPUT, (long long)r); return 0; } printf(" [PASS] NULL dst + cap>0 -> NULL_INPUT\n"); printf("PASS\n\n"); return 1; } // Tests the buffer API scratch buffer (work_buf) used to safely absorb // zxc_copy32 wild-copy overshoot during decompression. int test_buffer_api_scratch_buf() { printf("=== TEST: Unit - Buffer API Scratch Buffer (work_buf) ===\n"); // 1. Small data roundtrip (177 bytes) { const size_t sz = 177; uint8_t src[177]; gen_lz_data(src, sz); const size_t comp_cap = (size_t)zxc_compress_bound(sz); uint8_t* comp = malloc(comp_cap); zxc_compress_opts_t _co58 = {.level = 3, .checksum_enabled = 0}; const int64_t comp_sz = zxc_compress(src, sz, comp, comp_cap, &_co58); if (comp_sz <= 0) { printf(" [FAIL] compress 177B\n"); free(comp); return 0; } uint8_t dec[177]; zxc_decompress_opts_t _do59 = {.checksum_enabled = 0}; const int64_t dec_sz = zxc_decompress(comp, (size_t)comp_sz, dec, sz, &_do59); if (dec_sz != (int64_t)sz || memcmp(src, dec, sz) != 0) { printf(" [FAIL] roundtrip 177B\n"); free(comp); return 0; } free(comp); printf(" [PASS] small data roundtrip (177 bytes)\n"); } // 2. Exact-fit destination (dst_capacity == decompressed size, no slack) { const size_t sz = 1024; uint8_t* src = malloc(sz); gen_lz_data(src, sz); const size_t comp_cap = (size_t)zxc_compress_bound(sz); uint8_t* comp = malloc(comp_cap); zxc_compress_opts_t _co60 = {.level = 1, .checksum_enabled = 1}; const int64_t comp_sz = zxc_compress(src, sz, comp, comp_cap, &_co60); if (comp_sz <= 0) { printf(" [FAIL] compress 1KB\n"); free(src); free(comp); return 0; } uint8_t* dec = malloc(sz); // exactly sz, no extra room zxc_decompress_opts_t _do61 = {.checksum_enabled = 1}; const int64_t dec_sz = zxc_decompress(comp, (size_t)comp_sz, dec, sz, &_do61); if (dec_sz != (int64_t)sz || memcmp(src, dec, sz) != 0) { printf(" [FAIL] exact-fit 1KB\n"); free(src); free(comp); free(dec); return 0; } free(src); free(comp); free(dec); printf(" [PASS] exact-fit destination (1KB)\n"); } // 3. Tiny data (1 byte) { const uint8_t src = 0x42; const size_t comp_cap = (size_t)zxc_compress_bound(1); uint8_t* comp = malloc(comp_cap); zxc_compress_opts_t _co62 = {.level = 1, .checksum_enabled = 0}; const int64_t comp_sz = zxc_compress(&src, 1, comp, comp_cap, &_co62); if (comp_sz <= 0) { printf(" [FAIL] compress 1B\n"); free(comp); return 0; } uint8_t dec = 0; zxc_decompress_opts_t _do63 = {.checksum_enabled = 0}; const int64_t dec_sz = zxc_decompress(comp, (size_t)comp_sz, &dec, 1, &_do63); if (dec_sz != 1 || dec != 0x42) { printf(" [FAIL] roundtrip 1B\n"); free(comp); return 0; } free(comp); printf(" [PASS] tiny data roundtrip (1 byte)\n"); } // 4. Malformed input must not crash (safe error return) { uint8_t garbage[64]; for (int i = 0; i < 64; i++) garbage[i] = (uint8_t)(i * 37); uint8_t out[256]; zxc_decompress_opts_t _do64 = {.checksum_enabled = 0}; const int64_t r = zxc_decompress(garbage, sizeof(garbage), out, sizeof(out), &_do64); if (r >= 0) { printf(" [FAIL] malformed input should return < 0\n"); return 0; } printf(" [PASS] malformed input -> error %lld (no crash)\n", (long long)r); } // 5. Destination too small { const size_t sz = 512; uint8_t* src = malloc(sz); gen_lz_data(src, sz); const size_t comp_cap = (size_t)zxc_compress_bound(sz); uint8_t* comp = malloc(comp_cap); zxc_compress_opts_t _co65 = {.level = 1, .checksum_enabled = 0}; const int64_t comp_sz = zxc_compress(src, sz, comp, comp_cap, &_co65); if (comp_sz <= 0) { printf(" [FAIL] compress 512B\n"); free(src); free(comp); return 0; } uint8_t tiny_dst[8]; zxc_decompress_opts_t _do66 = {.checksum_enabled = 0}; const int64_t r = zxc_decompress(comp, (size_t)comp_sz, tiny_dst, sizeof(tiny_dst), &_do66); if (r >= 0) { printf(" [FAIL] dst too small should return < 0\n"); free(src); free(comp); return 0; } free(src); free(comp); printf(" [PASS] zxc_decompress dst too small -> negative\n"); } printf("PASS\n\n"); return 1; } // Tests that the two decompression paths in zxc_decompress() produce // identical results: // - Fast path: rem_cap >= runtime_chunk_size + ZXC_PAD_SIZE // -> decompress directly into dst (enough padding for wild copies). // - Safe path: rem_cap < runtime_chunk_size + ZXC_PAD_SIZE // -> decompress into bounce buffer (work_buf), then memcpy exact result. // int test_decompress_fast_vs_safe_path() { printf("=== TEST: Unit - Decompress Fast Path vs Safe Path ===\n"); // Use a multi-block input: ZXC_BLOCK_SIZE_DEFAULT + extra so we get at least 2 blocks. // Block size = 256KB (ZXC_BLOCK_SIZE_DEFAULT). Second block is small. const size_t src_sz = ZXC_BLOCK_SIZE_DEFAULT + 4096; // 256KB + 4KB -> 2 blocks uint8_t* src = malloc(src_sz); if (!src) return 0; gen_lz_data(src, src_sz); const size_t comp_cap = (size_t)zxc_compress_bound(src_sz); uint8_t* comp = malloc(comp_cap); zxc_compress_opts_t _co67 = {.level = 3, .checksum_enabled = 1}; const int64_t comp_sz = zxc_compress(src, src_sz, comp, comp_cap, &_co67); if (comp_sz <= 0) { printf(" [FAIL] compression failed\n"); free(src); free(comp); return 0; } // ----- Sub-test 1: Fast path ----- // Provide a very large dst buffer so all chunks decompress directly into // dst (rem_cap >= runtime_chunk_size + ZXC_PAD_SIZE at every iteration). { const size_t big_cap = src_sz + ZXC_BLOCK_SIZE_DEFAULT; // way more than enough uint8_t* dst = malloc(big_cap); zxc_decompress_opts_t _do68 = {.checksum_enabled = 1}; const int64_t dec_sz = zxc_decompress(comp, (size_t)comp_sz, dst, big_cap, &_do68); if (dec_sz != (int64_t)src_sz) { printf(" [FAIL] fast path size: expected %zu, got %lld\n", src_sz, (long long)dec_sz); free(dst); free(src); free(comp); return 0; } if (memcmp(src, dst, src_sz) != 0) { printf(" [FAIL] fast path content mismatch\n"); free(dst); free(src); free(comp); return 0; } free(dst); printf(" [PASS] fast path (oversized dst)\n"); } // ----- Sub-test 2: Safe path (exact-fit) ----- // Provide dst_capacity == src_sz exactly. After the first 256KB block is // written, rem_cap for the second block (4KB) is exactly 4KB which is // < runtime_chunk_size (256KB) + ZXC_PAD_SIZE (32). This forces the // safe path (bounce buffer) for the second block. { uint8_t* dst = malloc(src_sz); // no slack at all zxc_decompress_opts_t _do69 = {.checksum_enabled = 1}; const int64_t dec_sz = zxc_decompress(comp, (size_t)comp_sz, dst, src_sz, &_do69); if (dec_sz != (int64_t)src_sz) { printf(" [FAIL] safe path size: expected %zu, got %lld\n", src_sz, (long long)dec_sz); free(dst); free(src); free(comp); return 0; } if (memcmp(src, dst, src_sz) != 0) { printf(" [FAIL] safe path content mismatch\n"); free(dst); free(src); free(comp); return 0; } free(dst); printf(" [PASS] safe path (exact-fit dst)\n"); } // ----- Sub-test 3: Boundary ----- // dst_capacity = src_sz + ZXC_PAD_SIZE - 1 (just below the fast path // threshold for the LAST chunk). The last chunk should still fall into // the safe path here. { const size_t tight_cap = src_sz + ZXC_PAD_SIZE - 1; uint8_t* dst = malloc(tight_cap); zxc_decompress_opts_t _do70 = {.checksum_enabled = 1}; const int64_t dec_sz = zxc_decompress(comp, (size_t)comp_sz, dst, tight_cap, &_do70); if (dec_sz != (int64_t)src_sz) { printf(" [FAIL] boundary size: expected %zu, got %lld\n", src_sz, (long long)dec_sz); free(dst); free(src); free(comp); return 0; } if (memcmp(src, dst, src_sz) != 0) { printf(" [FAIL] boundary content mismatch\n"); free(dst); free(src); free(comp); return 0; } free(dst); printf(" [PASS] boundary (dst = src_sz + PAD - 1)\n"); } // ----- Sub-test 4: Safe path with dst too small ----- // The safe path detects that the decompressed chunk doesn't fit and // returns ZXC_ERROR_DST_TOO_SMALL (covers the res > rem_cap guard). { const size_t tiny_cap = ZXC_BLOCK_SIZE_DEFAULT / 2; // Enough for half a block uint8_t* dst = malloc(tiny_cap); zxc_decompress_opts_t _do71 = {.checksum_enabled = 0}; const int64_t dec_sz = zxc_decompress(comp, (size_t)comp_sz, dst, tiny_cap, &_do71); if (dec_sz >= 0) { printf(" [FAIL] safe path dst-too-small should fail, got %lld\n", (long long)dec_sz); free(dst); free(src); free(comp); return 0; } free(dst); printf(" [PASS] safe path dst too small -> negative\n"); } free(src); free(comp); printf("PASS\n\n"); return 1; } /* In-place decompression: compressed placed flush-right in one buffer, decoded * left-to-right into the same buffer. Covers a compressible input, a * high-entropy input whose flush-right archive OVERLAPS the output region * (the core in-place invariant), and the too-small-buffer rejection. */ static int inplace_case(const char* label, const uint8_t* orig, size_t n, int level, int checksum) { const size_t cbound = (size_t)zxc_compress_bound(n); uint8_t* comp = (uint8_t*)malloc(cbound); if (!comp) return 0; zxc_compress_opts_t co = {.level = level, .checksum_enabled = checksum}; const int64_t c = zxc_compress(orig, n, comp, cbound, &co); if (c <= 0) { printf("Failed [%s]: compress -> %lld\n", label, (long long)c); free(comp); return 0; } const size_t csz = (size_t)c; const size_t need = zxc_decompress_inplace_bound(comp, csz); if (need == 0 || need < n) { printf("Failed [%s]: bound %zu\n", label, need); free(comp); return 0; } uint8_t* buf = (uint8_t*)malloc(need); if (!buf) { free(comp); return 0; } memset(buf, 0xCC, need); memcpy(buf + (need - csz), comp, csz); /* flush-right */ zxc_decompress_opts_t dop = {.checksum_enabled = checksum}; const int64_t d = zxc_decompress_inplace(buf, need, csz, &dop); if (d < 0 || (size_t)d != n || memcmp(buf, orig, n) != 0) { printf("Failed [%s]: inplace ret=%lld want=%zu%s\n", label, (long long)d, n, (d == (int64_t)n) ? " (MISMATCH)" : ""); free(comp); free(buf); return 0; } /* one byte short of the required margin must be rejected, never corrupt. */ const size_t tight = need - 1; if (tight >= csz) { uint8_t* b2 = (uint8_t*)malloc(tight); if (b2) { memcpy(b2 + (tight - csz), comp, csz); const int64_t r = zxc_decompress_inplace(b2, tight, csz, &dop); if (r != ZXC_ERROR_DST_TOO_SMALL) { printf("Failed [%s]: undersized buffer not rejected (%lld)\n", label, (long long)r); free(b2); free(comp); free(buf); return 0; } free(b2); } } const size_t comp_start = need - csz; printf(" [PASS] %s (n=%zu, comp=%zu, comp@%zu %s)\n", label, n, csz, comp_start, comp_start < n ? "OVERLAPS output" : "in margin"); free(comp); free(buf); return 1; } int test_decompress_inplace(void) { printf("=== TEST: Unit - In-place decompression (single buffer) ===\n"); const size_t N = 2 * 1024 * 1024; uint8_t* a = (uint8_t*)malloc(N); if (!a) return 0; int ok = 1; gen_lz_data(a, N); ok &= inplace_case("compressible L3", a, N, 3, 1); ok &= inplace_case("compressible L6", a, N, 6, 0); /* high-entropy: comp_size ~ N so the flush-right archive sits INSIDE the * output region -> the write cursor sweeps through the compressed bytes. */ gen_random_data(a, N); ok &= inplace_case("random L1 (overlap)", a, N, 1, 0); ok &= inplace_case("random L7 (overlap)", a, N, 7, 1); /* bound on garbage must be 0. */ uint8_t junk[64]; memset(junk, 0x5A, sizeof(junk)); if (zxc_decompress_inplace_bound(junk, sizeof(junk)) != 0) { printf("Failed: bound on garbage != 0\n"); ok = 0; } free(a); if (!ok) return 0; printf("PASS\n\n"); return 1; } zxc-0.13.0/tests/test_cli.sh000077500000000000000000001431521522536370400157230ustar00rootroot00000000000000#!/bin/bash # ZXC - High-performance lossless compression # # Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. # SPDX-License-Identifier: BSD-3-Clause # # Test script for ZXC CLI # Usage: ./tests/test_cli.sh [path_to_zxc_binary] set -e # Default binary path ZXC_BIN=${1:-"../build/zxc"} # Test Directory (to isolate test files) TEST_DIR="./test_tmp" mkdir -p "$TEST_DIR" # Test Files TEST_FILE="$TEST_DIR/testdata" TEST_FILE_XC="${TEST_FILE}.zxc" TEST_FILE_DEC="${TEST_FILE}.dec" PIPE_XC="$TEST_DIR/test_pipe.zxc" PIPE_DEC="$TEST_DIR/test_pipe.dec" # Variables for checking file existence TEST_FILE_XC_BASH="$TEST_FILE_XC" TEST_FILE_DEC_BASH="${TEST_FILE}.dec" # Arguments passed to ZXC (relative to test_tmp) TEST_FILE_ARG="$TEST_FILE" TEST_FILE_XC_ARG="$TEST_FILE_XC" RED='\033[0;31m' GREEN='\033[0;32m' NC='\033[0m' # No Color log_pass() { echo -e "${GREEN}[PASS]${NC} $1" } log_fail() { echo -e "${RED}[FAIL]${NC} $1" exit 1 } # cleanup on exit cleanup() { echo "Cleaning up..." rm -rf "$TEST_DIR" } trap cleanup EXIT # 0. Check binary if [[ ! -f "$ZXC_BIN" ]]; then log_fail "Binary not found at $ZXC_BIN. Please build the project first." fi echo "Using binary: $ZXC_BIN" # 1. Generate Test File (Lorem Ipsum) echo "Generating test file..." cat > "$TEST_FILE" < "${TEST_FILE}.tmp" mv "${TEST_FILE}.tmp" "$TEST_FILE" done # Pristine copy: sections that consume or delete TEST_FILE restore it from here cp "$TEST_FILE" "${TEST_FILE}.orig" FILE_SIZE=$(wc -c < "$TEST_FILE" | tr -d ' ') echo "Test file generated: $TEST_FILE ($FILE_SIZE bytes)" # Helper: Wait for file to be ready and readable wait_for_file() { local file="$1" local retries=10 local count=0 # On Windows, file locking can cause race conditions immediately after creation. while [[ $count -lt $retries ]]; do if [[ -f "$file" ]]; then # Try to read a byte to ensure it's not exclusively locked if head -c 1 "$file" >/dev/null 2>&1; then return 0 fi fi sleep 1 count=$((count + 1)) done echo "Timeout waiting for file '$file' to be readable." ls -l "$file" 2>/dev/null || echo "File not found in ls." return 1 } # 2. Basic Round-Trip echo "Testing Basic Round-Trip..." if ! "$ZXC_BIN" -z -k "$TEST_FILE_ARG"; then log_fail "Compression command failed (exit code $?)" fi # Wait for output if ! wait_for_file "$TEST_FILE_XC_BASH"; then log_fail "Compression succeeded but output file '$TEST_FILE_XC_BASH' is not accessible." fi # Decompress to stdout echo "Attempting decompression of: $TEST_FILE_XC_ARG" if ! "$ZXC_BIN" -d -c "$TEST_FILE_XC_ARG" > "$TEST_FILE_DEC_BASH"; then echo "Decompress to stdout failed. Retrying once..." sleep 1 if ! "$ZXC_BIN" -d -c "$TEST_FILE_XC_ARG" > "$TEST_FILE_DEC_BASH"; then log_fail "Decompression to stdout failed." fi fi # Decompress to file rm -f "$TEST_FILE" sleep 1 if ! "$ZXC_BIN" -d -k "$TEST_FILE_XC_ARG"; then echo "Decompress to file failed. Retrying once..." sleep 1 if ! "$ZXC_BIN" -d -k "$TEST_FILE_XC_ARG"; then log_fail "Decompression to file failed." fi fi if ! wait_for_file "$TEST_FILE"; then log_fail "Decompression failed to recreate original file." fi mv "$TEST_FILE" "$TEST_FILE_DEC_BASH" # Restore source from the pristine copy for a valid comparison cp "${TEST_FILE}.orig" "$TEST_FILE" if cmp -s "$TEST_FILE" "$TEST_FILE_DEC_BASH"; then log_pass "Basic Round-Trip" else log_fail "Basic Round-Trip content mismatch" fi # 3. Piping echo "Testing Piping..." rm -f "$PIPE_XC" "$PIPE_DEC" cat "$TEST_FILE" | "$ZXC_BIN" > "$PIPE_XC" if [[ ! -s "$PIPE_XC" ]]; then log_fail "Piping compression failed (empty output)" fi cat "$PIPE_XC" | "$ZXC_BIN" -d > "$PIPE_DEC" if [[ ! -s "$PIPE_DEC" ]]; then log_fail "Piping decompression failed (empty output)" fi if cmp -s "$TEST_FILE" "$PIPE_DEC"; then log_pass "Piping" else log_fail "Piping content mismatch" fi # 4. Flags echo "Testing Flags..." # Level "$ZXC_BIN" -1 -k -f "$TEST_FILE_ARG" if [[ ! -f "$TEST_FILE_XC_BASH" ]]; then log_fail "Level 1 flag failed"; fi log_pass "Flag -1" # Force Overwrite (-f) touch "$TEST_DIR/out.zxc" touch "${TEST_FILE_XC_BASH}" set +e "$ZXC_BIN" -z -k "$TEST_FILE_ARG" > /dev/null 2>&1 RET=$? set -e if [[ $RET -eq 0 ]]; then log_fail "Should have failed to overwrite without -f" else log_pass "Overwrite protection verified" fi "$ZXC_BIN" -z -k -f "$TEST_FILE_ARG" if [[ $? -eq 0 ]]; then log_pass "Force overwrite (-f)" else log_fail "Force overwrite failed" fi # 5. Benchmark echo "Testing Benchmark..." "$ZXC_BIN" -b1 "$TEST_FILE_ARG" > /dev/null if [[ $? -eq 0 ]]; then log_pass "Benchmark mode" else log_fail "Benchmark mode failed" fi # 6. Error Handling echo "Testing Error Handling..." set +e "$ZXC_BIN" "nonexistent_file" > /dev/null 2>&1 RET=$? set -e if [[ $RET -ne 0 ]]; then log_pass "Missing file error handled" else log_fail "Missing file should return error" fi # 7. Version echo "Testing Version..." OUT_VER=$("$ZXC_BIN" -V) if [[ "$OUT_VER" == *"ZXC CLI"* && "$OUT_VER" =~ v[0-9]+\.[0-9]+\.[0-9]+ ]]; then log_pass "Version flag" else log_fail "Version flag failed" fi # 8. Checksum echo "Testing Checksum..." "$ZXC_BIN" -C -k -f "$TEST_FILE_ARG" if [[ ! -f "$TEST_FILE_XC_BASH" ]]; then log_fail "Checksum compression failed"; fi rm -f "$TEST_FILE" "$ZXC_BIN" -d "$TEST_FILE_XC_ARG" if [[ ! -f "$TEST_FILE" ]]; then log_fail "Checksum decompression failed"; fi log_pass "Checksum enabled (-C)" "$ZXC_BIN" -N -k -f "$TEST_FILE_ARG" if [[ ! -f "$TEST_FILE_XC_BASH" ]]; then log_fail "No-Checksum compression failed"; fi rm -f "$TEST_FILE" "$ZXC_BIN" -d "$TEST_FILE_XC_ARG" if [[ ! -f "$TEST_FILE" ]]; then log_fail "No-Checksum decompression failed"; fi log_pass "Checksum disabled (-N)" # 9. Integrity Test (-t) echo "Testing Integrity Check (-t)..." "$ZXC_BIN" -z -k -f -C "$TEST_FILE_ARG" # Valid file should pass and show "OK" OUT=$("$ZXC_BIN" -t "$TEST_FILE_XC_ARG") if [[ "$OUT" == *": OK"* ]]; then log_pass "Integrity check passed on valid file" else log_fail "Integrity check failed on valid file (expected OK output)" fi # Verbose test mode OUT=$("$ZXC_BIN" -t -v "$TEST_FILE_XC_ARG") if [[ "$OUT" == *": OK"* ]] && [[ "$OUT" == *"Checksum:"* ]]; then log_pass "Integrity check verbose mode" else log_fail "Integrity check verbose mode failed" fi # Corrupt file should fail and show "FAILED" # Corrupt a byte in the middle of the file (after header) printf '\xff' | dd of="$TEST_FILE_XC_ARG" bs=1 seek=100 count=1 conv=notrunc 2>/dev/null set +e OUT=$("$ZXC_BIN" -t "$TEST_FILE_XC_ARG" 2>&1) RET=$? set -e if [[ $RET -ne 0 ]] && [[ "$OUT" == *": FAILED"* ]]; then log_pass "Integrity check correctly failed on corrupt file" else log_fail "Integrity check PASSED on corrupt file (False Negative)" fi # 10. Global Checksum Integrity echo "Testing Global Checksum Integrity..." "$ZXC_BIN" -z -k -f -C "$TEST_FILE_ARG" # Corrupt the last byte (part of Global Checksum) FILE_SZ=$(wc -c < "$TEST_FILE_XC_ARG" | tr -d ' ') LAST_BYTE_OFFSET=$((FILE_SZ - 1)) printf '\x00' | dd of="$TEST_FILE_XC_ARG" bs=1 seek=$LAST_BYTE_OFFSET count=1 conv=notrunc 2>/dev/null set +e OUT=$("$ZXC_BIN" -t "$TEST_FILE_XC_ARG" 2>&1) RET=$? set -e if [[ $RET -ne 0 ]] && [[ "$OUT" == *": FAILED"* ]]; then log_pass "Integrity check correctly failed on corrupt Global Checksum" else log_fail "Integrity check PASSED on corrupt Global Checksum (False Negative)" fi # Ensure no output file is created if [[ -f "${TEST_FILE}.zxc.zxc" ]] || [[ -f "${TEST_FILE}.zxc.dec" ]]; then log_fail "Integrity check created output file unexpectedly" fi # 11. List Command (-l) echo "Testing List Command (-l)..." "$ZXC_BIN" -z -k -f -C "$TEST_FILE_ARG" # Normal list mode OUT=$("$ZXC_BIN" -l "$TEST_FILE_XC_ARG") if [[ "$OUT" == *"Compressed"* ]] && [[ "$OUT" == *"Uncompressed"* ]] && [[ "$OUT" == *"Ratio"* ]]; then log_pass "List command basic output" else log_fail "List command basic output failed" fi # Verbose list mode OUT=$("$ZXC_BIN" -l -v "$TEST_FILE_XC_ARG") if [[ "$OUT" == *"Block Format:"* ]] && [[ "$OUT" == *"Block Size:"* ]] && [[ "$OUT" == *"Checksum Method:"* ]]; then log_pass "List command verbose output" else log_fail "List command verbose output failed" fi # List with invalid file should fail set +e "$ZXC_BIN" -l "nonexistent_file" > /dev/null 2>&1 RET=$? set -e if [[ $RET -ne 0 ]]; then log_pass "List command error handling" else log_fail "List command should fail on nonexistent file" fi # 12. Compression Levels (All) echo "Testing All Compression Levels..." for LEVEL in 1 2 3 4 5 6 7; do "$ZXC_BIN" -$LEVEL -c -k "$TEST_FILE_ARG" > "$TEST_DIR/test_lvl${LEVEL}.zxc" if [[ ! -f "$TEST_DIR/test_lvl${LEVEL}.zxc" ]]; then log_fail "Compression level $LEVEL failed" fi # Decompress and verify "$ZXC_BIN" -d -c "$TEST_DIR/test_lvl${LEVEL}.zxc" > "$TEST_DIR/test_lvl${LEVEL}.dec" if ! cmp -s "$TEST_FILE" "$TEST_DIR/test_lvl${LEVEL}.dec"; then log_fail "Level $LEVEL decompression mismatch" fi SIZE=$(wc -c < "$TEST_DIR/test_lvl${LEVEL}.zxc" | tr -d ' ') log_pass "Level $LEVEL (Size: $SIZE bytes)" done # 13. Data Type Tests echo "Testing Different Data Types..." # 13.1 Highly Repetitive Text (Best Compression) echo " Testing repetitive data..." yes "Lorem ipsum dolor sit amet" | head -n 1000 > "$TEST_DIR/test_repetitive.txt" "$ZXC_BIN" -3 -k "$TEST_DIR/test_repetitive.txt" if [[ ! -f "$TEST_DIR/test_repetitive.txt.zxc" ]]; then log_fail "Repetitive data compression failed" fi "$ZXC_BIN" -d -c "$TEST_DIR/test_repetitive.txt.zxc" > "$TEST_DIR/test_repetitive.dec" if cmp -s "$TEST_DIR/test_repetitive.txt" "$TEST_DIR/test_repetitive.dec"; then SIZE_ORIG=$(wc -c < "$TEST_DIR/test_repetitive.txt" | tr -d ' ') SIZE_COMP=$(wc -c < "$TEST_DIR/test_repetitive.txt.zxc" | tr -d ' ') RATIO=$((SIZE_ORIG / SIZE_COMP)) log_pass "Repetitive text (Ratio: ${RATIO}:1)" else log_fail "Repetitive data round-trip failed" fi # 13.2 Binary Zeros (Highly Compressible) echo " Testing binary zeros..." dd if=/dev/zero bs=1024 count=100 of="$TEST_DIR/test_zeros.bin" 2>/dev/null "$ZXC_BIN" -3 -k "$TEST_DIR/test_zeros.bin" if [[ ! -f "$TEST_DIR/test_zeros.bin.zxc" ]]; then log_fail "Binary zeros compression failed" fi "$ZXC_BIN" -d -c "$TEST_DIR/test_zeros.bin.zxc" > "$TEST_DIR/test_zeros.dec" if cmp -s "$TEST_DIR/test_zeros.bin" "$TEST_DIR/test_zeros.dec"; then SIZE_ORIG=$(wc -c < "$TEST_DIR/test_zeros.bin" | tr -d ' ') SIZE_COMP=$(wc -c < "$TEST_DIR/test_zeros.bin.zxc" | tr -d ' ') RATIO=$((SIZE_ORIG / SIZE_COMP)) log_pass "Binary zeros (Ratio: ${RATIO}:1)" else log_fail "Binary zeros round-trip failed" fi # 13.3 Random Data (Incompressible - Should use RAW blocks) echo " Testing random data (incompressible)..." dd if=/dev/urandom bs=1024 count=100 of="$TEST_DIR/test_random.bin" 2>/dev/null "$ZXC_BIN" -3 -k "$TEST_DIR/test_random.bin" if [[ ! -f "$TEST_DIR/test_random.bin.zxc" ]]; then log_fail "Random data compression failed" fi "$ZXC_BIN" -d -c "$TEST_DIR/test_random.bin.zxc" > "$TEST_DIR/test_random.dec" if cmp -s "$TEST_DIR/test_random.bin" "$TEST_DIR/test_random.dec"; then SIZE_ORIG=$(wc -c < "$TEST_DIR/test_random.bin" | tr -d ' ') SIZE_COMP=$(wc -c < "$TEST_DIR/test_random.bin.zxc" | tr -d ' ') # Random data should expand slightly (RAW blocks + headers) if [[ $SIZE_COMP -le $((SIZE_ORIG + 200)) ]]; then log_pass "Random data (RAW blocks, minimal expansion)" else log_fail "Random data expanded too much" fi else log_fail "Random data round-trip failed" fi # 14. Large File Test (Performance Validation) echo "Testing Large Files..." dd if=/dev/zero bs=1M count=10 of="$TEST_DIR/test_large.bin" 2>/dev/null if ! "$ZXC_BIN" -3 -k "$TEST_DIR/test_large.bin"; then log_fail "Large file compression failed" fi if ! "$ZXC_BIN" -d -c "$TEST_DIR/test_large.bin.zxc" > "$TEST_DIR/test_large.dec"; then log_fail "Large file decompression failed" fi if cmp -s "$TEST_DIR/test_large.bin" "$TEST_DIR/test_large.dec"; then log_pass "Large file (10 MB) round-trip" else log_fail "Large file content mismatch" fi # 15. One-Pass Pipe Round-Trip (Critical for Streaming) echo "Testing One-Pass Pipe Round-Trip..." cat "$TEST_FILE" | "$ZXC_BIN" -c | "$ZXC_BIN" -dc > "$TEST_DIR/test_pipe_onepass.dec" if cmp -s "$TEST_FILE" "$TEST_DIR/test_pipe_onepass.dec"; then log_pass "One-pass pipe round-trip" else log_fail "One-pass pipe round-trip content mismatch" fi # 16. Empty File Edge Case echo "Testing Empty File..." touch "$TEST_DIR/test_empty.txt" "$ZXC_BIN" -3 -k "$TEST_DIR/test_empty.txt" if [[ ! -f "$TEST_DIR/test_empty.txt.zxc" ]]; then log_fail "Empty file compression failed" fi "$ZXC_BIN" -d -c "$TEST_DIR/test_empty.txt.zxc" > "$TEST_DIR/test_empty.dec" if [[ ! -s "$TEST_DIR/test_empty.dec" ]]; then log_pass "Empty file round-trip" else log_fail "Empty file produced non-empty output" fi # 17. Stdin Detection (No -c flag needed for compression) echo "Testing Stdin Auto-Detection..." cat "$TEST_FILE" | "$ZXC_BIN" > "$TEST_DIR/test_stdin_auto.zxc" 2>/dev/null if [[ -s "$TEST_DIR/test_stdin_auto.zxc" ]]; then "$ZXC_BIN" -d -c "$TEST_DIR/test_stdin_auto.zxc" > "$TEST_DIR/test_stdin_auto.dec" if cmp -s "$TEST_FILE" "$TEST_DIR/test_stdin_auto.dec"; then log_pass "Stdin auto-detection (compression)" else log_fail "Stdin auto-detection content mismatch" fi else log_fail "Stdin auto-detection failed (empty output)" fi # 18. Keep Source File (-k) echo "Testing Keep Source (-k)..." cp "$TEST_FILE" "$TEST_DIR/test_keep.txt" "$ZXC_BIN" -k "$TEST_DIR/test_keep.txt" if [[ -f "$TEST_DIR/test_keep.txt" ]] && [[ -f "$TEST_DIR/test_keep.txt.zxc" ]]; then log_pass "Keep source file (-k)" else log_fail "Keep source file failed (source was deleted)" fi # 19. Multi-Threading Tests (-T) echo "Testing Multi-Threading..." # 19.1 Single Thread (baseline) echo " Testing single thread (-T1)..." "$ZXC_BIN" -3 -T1 -c -k "$TEST_FILE_ARG" > "$TEST_DIR/test_T1.zxc" if [[ ! -f "$TEST_DIR/test_T1.zxc" ]]; then log_fail "Single thread compression failed" fi "$ZXC_BIN" -d -c "$TEST_DIR/test_T1.zxc" > "$TEST_DIR/test_T1.dec" if cmp -s "$TEST_FILE" "$TEST_DIR/test_T1.dec"; then SIZE_T1=$(wc -c < "$TEST_DIR/test_T1.zxc" | tr -d ' ') log_pass "Single thread (-T1, Size: $SIZE_T1)" else log_fail "Single thread round-trip failed" fi # 19.2 Multi-Thread (2 threads) echo " Testing 2 threads (-T2)..." "$ZXC_BIN" -3 -T2 -c -k "$TEST_FILE_ARG" > "$TEST_DIR/test_T2.zxc" if [[ ! -f "$TEST_DIR/test_T2.zxc" ]]; then log_fail "2-thread compression failed" fi "$ZXC_BIN" -d -c "$TEST_DIR/test_T2.zxc" > "$TEST_DIR/test_T2.dec" if cmp -s "$TEST_FILE" "$TEST_DIR/test_T2.dec"; then SIZE_T2=$(wc -c < "$TEST_DIR/test_T2.zxc" | tr -d ' ') log_pass "2 threads (-T2, Size: $SIZE_T2)" else log_fail "2-thread round-trip failed" fi # 19.3 Multi-Thread (all threads) echo " Testing all threads (-T0)..." "$ZXC_BIN" -3 -T0 -c -k "$TEST_FILE_ARG" > "$TEST_DIR/test_T0.zxc" if [[ ! -f "$TEST_DIR/test_T0.zxc" ]]; then log_fail "all-thread compression failed" fi "$ZXC_BIN" -d -c "$TEST_DIR/test_T0.zxc" > "$TEST_DIR/test_T0.dec" if cmp -s "$TEST_FILE" "$TEST_DIR/test_T0.dec"; then SIZE_T0=$(wc -c < "$TEST_DIR/test_T0.zxc" | tr -d ' ') log_pass "all threads (-T0, Size: $SIZE_T0)" else log_fail "all-thread round-trip failed" fi # 19.4 Verify determinism: All outputs should decompress to same content echo " Verifying thread-count independence..." if cmp -s "$TEST_DIR/test_T1.dec" "$TEST_DIR/test_T2.dec" && cmp -s "$TEST_DIR/test_T2.dec" "$TEST_DIR/test_T0.dec"; then log_pass "Deterministic output (all thread counts produce valid results)" else log_fail "Thread outputs differ (non-deterministic)" fi # 19.5 Cross-compatibility: File compressed with -T2 should decompress without -T flag echo " Testing cross-compatibility..." "$ZXC_BIN" -d -c "$TEST_DIR/test_T2.zxc" > "$TEST_DIR/test_T2_compat.dec" if cmp -s "$TEST_FILE" "$TEST_DIR/test_T2_compat.dec"; then log_pass "Cross-compatible decompression" else log_fail "Multi-threaded file not compatible with standard decompression" fi # 19.6 Large file with threads (performance validation) echo " Testing large file with threads..." dd if=/dev/zero bs=1M count=5 of="$TEST_DIR/test_large_mt.bin" 2>/dev/null "$ZXC_BIN" -3 -T4 -c -k "$TEST_DIR/test_large_mt.bin" > "$TEST_DIR/test_large_mt.zxc" "$ZXC_BIN" -d -c "$TEST_DIR/test_large_mt.zxc" > "$TEST_DIR/test_large_mt.dec" if cmp -s "$TEST_DIR/test_large_mt.bin" "$TEST_DIR/test_large_mt.dec"; then log_pass "Large file multi-threading" else log_fail "Large file multi-threading round-trip failed" fi # 20. JSON Output Tests echo "Testing JSON Output (-j, --json)..." # 20.1 List mode with JSON (single file) echo " Testing list mode with JSON (single file)..." "$ZXC_BIN" -z -k -f -C "$TEST_FILE_ARG" JSON_OUT=$("$ZXC_BIN" -l -j "$TEST_FILE_XC_ARG") if [[ "$JSON_OUT" == *'"filename"'* ]] && \ [[ "$JSON_OUT" == *'"compressed_size_bytes"'* ]] && \ [[ "$JSON_OUT" == *'"uncompressed_size_bytes"'* ]] && \ [[ "$JSON_OUT" == *'"compression_ratio"'* ]] && \ [[ "$JSON_OUT" == *'"format_version"'* ]] && \ [[ "$JSON_OUT" == *'"checksum_method"'* ]]; then log_pass "List mode JSON output (single file)" else log_fail "List mode JSON output missing expected fields" fi # 20.2 List mode with JSON (multiple files) echo " Testing list mode with JSON (multiple files)..." cp "$TEST_FILE_XC_ARG" "$TEST_DIR/test2.zxc" JSON_OUT=$("$ZXC_BIN" -l -j "$TEST_FILE_XC_ARG" "$TEST_DIR/test2.zxc") if [[ "$JSON_OUT" == "["* ]] && \ [[ "$JSON_OUT" == *"]" ]] && \ [[ "$JSON_OUT" == *'"filename"'* ]] && \ [[ "$JSON_OUT" == *","* ]]; then log_pass "List mode JSON output (multiple files - array)" else log_fail "List mode JSON output should produce array for multiple files" fi # 20.3 Benchmark mode with JSON echo " Testing benchmark mode with JSON..." JSON_OUT=$("$ZXC_BIN" -b1 -j "$TEST_FILE_ARG" 2>/dev/null) if [[ "$JSON_OUT" == *'"input_file"'* ]] && \ [[ "$JSON_OUT" == *'"input_size_bytes"'* ]] && \ [[ "$JSON_OUT" == *'"compressed_size_bytes"'* ]] && \ [[ "$JSON_OUT" == *'"compression_ratio"'* ]] && \ [[ "$JSON_OUT" == *'"duration_seconds"'* ]] && \ [[ "$JSON_OUT" == *'"compress_iterations"'* ]] && \ [[ "$JSON_OUT" == *'"decompress_iterations"'* ]] && \ [[ "$JSON_OUT" == *'"threads"'* ]] && \ [[ "$JSON_OUT" == *'"level"'* ]] && \ [[ "$JSON_OUT" == *'"checksum_enabled"'* ]] && \ [[ "$JSON_OUT" == *'"compress_speed_mbps"'* ]] && \ [[ "$JSON_OUT" == *'"decompress_speed_mbps"'* ]]; then log_pass "Benchmark mode JSON output" else log_fail "Benchmark mode JSON output missing expected fields" fi # 20.4 Integrity check with JSON (valid file) echo " Testing integrity check with JSON (valid file)..." "$ZXC_BIN" -z -k -f -C "$TEST_FILE_ARG" JSON_OUT=$("$ZXC_BIN" -t -j "$TEST_FILE_XC_ARG") if [[ "$JSON_OUT" == *'"filename"'* ]] && \ [[ "$JSON_OUT" == *'"status": "ok"'* ]] && \ [[ "$JSON_OUT" == *'"checksum_verified"'* ]]; then log_pass "Integrity check JSON output (valid file)" else log_fail "Integrity check JSON output missing expected fields" fi # 20.5 Integrity check with JSON (corrupt file) echo " Testing integrity check with JSON (corrupt file)..." "$ZXC_BIN" -z -k -f -C "$TEST_FILE_ARG" # Corrupt a byte printf '\xff' | dd of="$TEST_FILE_XC_ARG" bs=1 seek=100 count=1 conv=notrunc 2>/dev/null set +e JSON_OUT=$("$ZXC_BIN" -t -j "$TEST_FILE_XC_ARG" 2>&1) RET=$? set -e if [[ $RET -ne 0 ]] && \ [[ "$JSON_OUT" == *'"filename"'* ]] && \ [[ "$JSON_OUT" == *'"status": "failed"'* ]] && \ [[ "$JSON_OUT" == *'"error"'* ]]; then log_pass "Integrity check JSON output (corrupt file)" else log_fail "Integrity check JSON output for corrupt file incorrect" fi # 20.6 Verify JSON is parseable (if jq is available) echo " Checking JSON validity (if jq available)..." if command -v jq &> /dev/null; then "$ZXC_BIN" -z -k -f -C "$TEST_FILE_ARG" JSON_OUT=$("$ZXC_BIN" -l -j "$TEST_FILE_XC_ARG") if echo "$JSON_OUT" | jq . > /dev/null 2>&1; then log_pass "JSON output is valid (verified with jq)" else log_fail "JSON output is not valid JSON" fi else echo " [SKIP] jq not available, skipping JSON validation" fi # 21. Multiple Mode (-m) Tests echo "Testing Multiple Mode (-m)..." # 21.1 Compress multiple files echo " Testing compress multiple files..." cp "$TEST_FILE" "$TEST_DIR/multi1.txt" cp "$TEST_FILE" "$TEST_DIR/multi2.txt" "$ZXC_BIN" -m -3 "$TEST_DIR/multi1.txt" "$TEST_DIR/multi2.txt" if [[ -f "$TEST_DIR/multi1.txt.zxc" ]] && [[ -f "$TEST_DIR/multi2.txt.zxc" ]]; then log_pass "Compress multiple files (-m)" else log_fail "Compress multiple files failed" fi # 21.2 Decompress multiple files echo " Testing decompress multiple files..." rm -f "$TEST_DIR/multi1.txt" "$TEST_DIR/multi2.txt" "$ZXC_BIN" -d -m "$TEST_DIR/multi1.txt.zxc" "$TEST_DIR/multi2.txt.zxc" if cmp -s "$TEST_FILE" "$TEST_DIR/multi1.txt" && cmp -s "$TEST_FILE" "$TEST_DIR/multi2.txt"; then log_pass "Decompress multiple files (-d -m)" else log_fail "Decompress multiple files failed (content mismatch or missing files)" fi # 21.3 Error on multiple and stdout echo " Testing stdout restriction with multiple mode..." set +e "$ZXC_BIN" -m -c "$TEST_DIR/multi1.txt" "$TEST_DIR/multi2.txt" > /dev/null 2>&1 RET=$? set -e if [[ $RET -ne 0 ]]; then log_pass "Stdout rejected with multiple mode" else log_fail "Stdout should be rejected with multiple mode" fi # 22. Recursive Mode (-r) Tests echo "Testing Recursive Mode (-r)..." # Create a nested directory structure mkdir -p "$TEST_DIR/rec_test/subdir1" mkdir -p "$TEST_DIR/rec_test/subdir2" cp "$TEST_FILE" "$TEST_DIR/rec_test/fileA.txt" cp "$TEST_FILE" "$TEST_DIR/rec_test/subdir1/fileB.txt" cp "$TEST_FILE" "$TEST_DIR/rec_test/subdir2/fileC.txt" # 22.1 Compress recursively echo " Testing compress recursive directory..." "$ZXC_BIN" -r -3 "$TEST_DIR/rec_test" if [[ -f "$TEST_DIR/rec_test/fileA.txt.zxc" ]] && \ [[ -f "$TEST_DIR/rec_test/subdir1/fileB.txt.zxc" ]] && \ [[ -f "$TEST_DIR/rec_test/subdir2/fileC.txt.zxc" ]]; then log_pass "Compress recursive directory (-r)" else log_fail "Compress recursive directory failed" fi # 22.2 Decompress recursively echo " Testing decompress recursive directory..." rm -f "$TEST_DIR/rec_test/fileA.txt" "$TEST_DIR/rec_test/subdir1/fileB.txt" "$TEST_DIR/rec_test/subdir2/fileC.txt" "$ZXC_BIN" -d -r "$TEST_DIR/rec_test" if cmp -s "$TEST_FILE" "$TEST_DIR/rec_test/fileA.txt" && \ cmp -s "$TEST_FILE" "$TEST_DIR/rec_test/subdir1/fileB.txt" && \ cmp -s "$TEST_FILE" "$TEST_DIR/rec_test/subdir2/fileC.txt"; then log_pass "Decompress recursive directory (-d -r)" else log_fail "Decompress recursive directory failed (content mismatch or missing files)" fi # 23. Block Size Tests (-B) echo "Testing Block Size (-B)..." # 23.1 Round-trip with different block sizes for BS in 4K 64K 512K 1M; do echo " Testing block size -B $BS..." "$ZXC_BIN" -3 -B "$BS" -c -k "$TEST_FILE_ARG" > "$TEST_DIR/test_bs_${BS}.zxc" if [[ ! -s "$TEST_DIR/test_bs_${BS}.zxc" ]]; then log_fail "Block size $BS compression produced empty output" fi "$ZXC_BIN" -d -c "$TEST_DIR/test_bs_${BS}.zxc" > "$TEST_DIR/test_bs_${BS}.dec" if cmp -s "$TEST_FILE" "$TEST_DIR/test_bs_${BS}.dec"; then SIZE_BS=$(wc -c < "$TEST_DIR/test_bs_${BS}.zxc" | tr -d ' ') log_pass "Block size -B $BS (Size: $SIZE_BS)" else log_fail "Block size $BS round-trip failed" fi done # 23.2 Test suffix formats (KB, M, MB) echo " Testing block size suffix formats..." "$ZXC_BIN" -3 -B 128KB -c -k "$TEST_FILE_ARG" > "$TEST_DIR/test_bs_128KB.zxc" "$ZXC_BIN" -d -c "$TEST_DIR/test_bs_128KB.zxc" > "$TEST_DIR/test_bs_128KB.dec" if cmp -s "$TEST_FILE" "$TEST_DIR/test_bs_128KB.dec"; then log_pass "Block size suffix KB" else log_fail "Block size suffix KB round-trip failed" fi "$ZXC_BIN" -3 -B 2MB -c -k "$TEST_FILE_ARG" > "$TEST_DIR/test_bs_2MB.zxc" "$ZXC_BIN" -d -c "$TEST_DIR/test_bs_2MB.zxc" > "$TEST_DIR/test_bs_2MB.dec" if cmp -s "$TEST_FILE" "$TEST_DIR/test_bs_2MB.dec"; then log_pass "Block size suffix MB" else log_fail "Block size suffix MB round-trip failed" fi # 23.3 Error: invalid block size (not power of 2) echo " Testing invalid block size..." set +e "$ZXC_BIN" -3 -B 100K "$TEST_FILE_ARG" > /dev/null 2>&1 RET=$? set -e if [[ $RET -ne 0 ]]; then log_pass "Invalid block size rejected (100K)" else log_fail "Invalid block size should be rejected (100K is not power of 2)" fi # 23.4 Error: block size too small set +e "$ZXC_BIN" -3 -B 2K "$TEST_FILE_ARG" > /dev/null 2>&1 RET=$? set -e if [[ $RET -ne 0 ]]; then log_pass "Block size too small rejected (2K)" else log_fail "Block size 2K should be rejected (min is 4K)" fi # 23.5 Error: block size too large set +e "$ZXC_BIN" -3 -B 4M "$TEST_FILE_ARG" > /dev/null 2>&1 RET=$? set -e if [[ $RET -ne 0 ]]; then log_pass "Block size too large rejected (4M)" else log_fail "Block size 4M should be rejected (max is 2M)" fi # 24. Seekable Format (-S) echo "Testing Seekable Format (-S)..." # 24.1 Basic seekable round-trip echo " Testing basic seekable round-trip..." rm -f "$TEST_FILE_XC_ARG" "$TEST_FILE_DEC_BASH" "$ZXC_BIN" -S -c "$TEST_FILE_ARG" > "$TEST_FILE_XC_ARG" if [[ ! -s "$TEST_FILE_XC_ARG" ]]; then log_fail "Seekable compression failed" fi "$ZXC_BIN" -d -c "$TEST_FILE_XC_ARG" > "$TEST_FILE_DEC_BASH" if cmp -s "$TEST_FILE" "$TEST_FILE_DEC_BASH"; then log_pass "Seekable basic round-trip (-S)" else log_fail "Seekable decompression mismatch" fi # 24.2 Seekable file must be larger than normal (SEK block overhead) echo " Testing seekable overhead..." "$ZXC_BIN" -3 -c -k "$TEST_FILE_ARG" > "$TEST_DIR/normal.zxc" "$ZXC_BIN" -3 -S -c -k "$TEST_FILE_ARG" > "$TEST_DIR/seekable.zxc" SIZE_NORMAL=$(wc -c < "$TEST_DIR/normal.zxc" | tr -d ' ') SIZE_SEEKABLE=$(wc -c < "$TEST_DIR/seekable.zxc" | tr -d ' ') if [[ "$SIZE_SEEKABLE" -gt "$SIZE_NORMAL" ]]; then OVERHEAD=$((SIZE_SEEKABLE - SIZE_NORMAL)) log_pass "Seekable overhead verified (+${OVERHEAD} bytes)" else log_fail "Seekable file should be larger than normal (SEK block missing?)" fi # 24.3 Seekable with small block size (many blocks = larger seek table) echo " Testing seekable with small blocks (-B 4K)..." "$ZXC_BIN" -3 -S -B 4K -c -k "$TEST_FILE_ARG" > "$TEST_DIR/seekable_4k.zxc" "$ZXC_BIN" -d -c "$TEST_DIR/seekable_4k.zxc" > "$TEST_DIR/seekable_4k.dec" if cmp -s "$TEST_FILE" "$TEST_DIR/seekable_4k.dec"; then SIZE_4K=$(wc -c < "$TEST_DIR/seekable_4k.zxc" | tr -d ' ') # With 4K blocks, overhead should be larger than with default 256K blocks if [[ "$SIZE_4K" -gt "$SIZE_SEEKABLE" ]]; then log_pass "Seekable small blocks (-B 4K, Size: $SIZE_4K, more entries)" else log_pass "Seekable small blocks (-B 4K, Size: $SIZE_4K)" fi else log_fail "Seekable small blocks round-trip failed" fi # 24.4 Seekable with checksum echo " Testing seekable + checksum (-S -C)..." "$ZXC_BIN" -3 -S -C -c -k "$TEST_FILE_ARG" > "$TEST_DIR/seekable_chk.zxc" "$ZXC_BIN" -d -c "$TEST_DIR/seekable_chk.zxc" > "$TEST_DIR/seekable_chk.dec" if cmp -s "$TEST_FILE" "$TEST_DIR/seekable_chk.dec"; then log_pass "Seekable + checksum (-S -C)" else log_fail "Seekable + checksum round-trip failed" fi # 24.5 Seekable with multi-threading echo " Testing seekable + threads (-S -T2)..." "$ZXC_BIN" -3 -S -T2 -c -k "$TEST_FILE_ARG" > "$TEST_DIR/seekable_mt.zxc" "$ZXC_BIN" -d -c "$TEST_DIR/seekable_mt.zxc" > "$TEST_DIR/seekable_mt.dec" if cmp -s "$TEST_FILE" "$TEST_DIR/seekable_mt.dec"; then log_pass "Seekable + multi-threading (-S -T2)" else log_fail "Seekable + multi-threading round-trip failed" fi # 24.6 Seekable across all levels echo " Testing seekable across all levels..." SEEK_ALL_OK=1 for LEVEL in 1 2 3 4 5 6 7; do "$ZXC_BIN" -$LEVEL -S -c -k "$TEST_FILE_ARG" > "$TEST_DIR/seekable_lvl${LEVEL}.zxc" "$ZXC_BIN" -d -c "$TEST_DIR/seekable_lvl${LEVEL}.zxc" > "$TEST_DIR/seekable_lvl${LEVEL}.dec" if ! cmp -s "$TEST_FILE" "$TEST_DIR/seekable_lvl${LEVEL}.dec"; then SEEK_ALL_OK=0 log_fail "Seekable level $LEVEL round-trip failed" fi done if [[ "$SEEK_ALL_OK" -eq 1 ]]; then log_pass "Seekable across all levels (1-7)" fi # 24.7 Seekable pipe round-trip (no fseeko - validates SEK skip on stdin) echo " Testing seekable pipe round-trip..." cat "$TEST_FILE" | "$ZXC_BIN" -S -c | "$ZXC_BIN" -dc > "$TEST_DIR/seekable_pipe.dec" if cmp -s "$TEST_FILE" "$TEST_DIR/seekable_pipe.dec"; then log_pass "Seekable pipe round-trip" else log_fail "Seekable pipe round-trip content mismatch" fi # 24.8 Seekable + no-checksum echo " Testing seekable + no-checksum (-S -N)..." "$ZXC_BIN" -3 -S -N -c -k "$TEST_FILE_ARG" > "$TEST_DIR/seekable_nochk.zxc" "$ZXC_BIN" -d -c "$TEST_DIR/seekable_nochk.zxc" > "$TEST_DIR/seekable_nochk.dec" if cmp -s "$TEST_FILE" "$TEST_DIR/seekable_nochk.dec"; then log_pass "Seekable + no-checksum (-S -N)" else log_fail "Seekable + no-checksum round-trip failed" fi # 24.9 List command on seekable archive echo " Testing list command on seekable archive..." "$ZXC_BIN" -3 -S -C -k -f "$TEST_FILE_ARG" OUT=$("$ZXC_BIN" -l "$TEST_FILE_XC_ARG") if [[ "$OUT" == *"Compressed"* ]] && [[ "$OUT" == *"Uncompressed"* ]]; then log_pass "List command on seekable archive" else log_fail "List command on seekable archive failed" fi # 25. Dictionary Tests (-D) echo "Testing Dictionary (-D)..." # 25.1 Train a dictionary using --train echo " Training dictionary from test data..." # Create a few sample files for training for i in 1 2 3 4 5; do cp "$TEST_FILE" "$TEST_DIR/sample_${i}.txt" done DICT_FILE="$TEST_DIR/test.zxd" "$ZXC_BIN" --train -o "$DICT_FILE" "$TEST_DIR"/sample_*.txt 2>/dev/null if [[ ! -f "$DICT_FILE" ]]; then log_fail "Dictionary training failed" fi log_pass "Dictionary trained via --train" # 25.2 Round-trip with dictionary echo " Testing dict round-trip..." "$ZXC_BIN" -3 -D "$DICT_FILE" -c -k "$TEST_FILE_ARG" > "$TEST_DIR/test_dict.zxc" if [[ ! -s "$TEST_DIR/test_dict.zxc" ]]; then log_fail "Dict compression failed" fi "$ZXC_BIN" -d -D "$DICT_FILE" -c "$TEST_DIR/test_dict.zxc" > "$TEST_DIR/test_dict.dec" if cmp -s "$TEST_FILE" "$TEST_DIR/test_dict.dec"; then log_pass "Dict round-trip (-D)" else log_fail "Dict round-trip content mismatch" fi # 25.3 List shows dict_id echo " Testing list with dict_id..." OUT=$("$ZXC_BIN" -l "$TEST_DIR/test_dict.zxc") if [[ "$OUT" == *"Dict ID"* ]] && [[ "$OUT" == *"0x"* ]]; then log_pass "List shows dict_id" else log_fail "List should show dict_id column with 0x value" fi # 25.4 List without dict shows dash echo " Testing list without dict shows dash..." "$ZXC_BIN" -3 -c -k "$TEST_FILE_ARG" > "$TEST_DIR/test_nodict2.zxc" OUT=$("$ZXC_BIN" -l "$TEST_DIR/test_nodict2.zxc") if [[ "$OUT" == *"Dict ID"* ]] && [[ "$OUT" == *" - "* ]]; then log_pass "List without dict shows dash" else log_fail "List without dict should show dash in Dict ID column" fi # 25.5 JSON list shows dict_id field echo " Testing JSON list with dict_id..." JSON_OUT=$("$ZXC_BIN" -l -j "$TEST_DIR/test_dict.zxc") if [[ "$JSON_OUT" == *'"dict_id"'* ]] && [[ "$JSON_OUT" == *"0x"* ]]; then log_pass "JSON list shows dict_id" else log_fail "JSON list should contain dict_id field" fi # 25.6 Decompressing a dict archive without -D must fail (the dictionary is mandatory). echo " Testing decompress without required dict..." set +e "$ZXC_BIN" -d -c "$TEST_DIR/test_dict.zxc" > /dev/null 2>&1 RET=$? set -e if [[ $RET -ne 0 ]]; then log_pass "Decompress without -D correctly fails" else log_fail "Decompress of a dict archive without -D should fail" fi # 25.7 Dict with seekable echo " Testing dict + seekable (-D -S)..." "$ZXC_BIN" -3 -D "$DICT_FILE" -S -c -k "$TEST_FILE_ARG" > "$TEST_DIR/test_dict_seek.zxc" "$ZXC_BIN" -d -D "$DICT_FILE" -c "$TEST_DIR/test_dict_seek.zxc" > "$TEST_DIR/test_dict_seek.dec" if cmp -s "$TEST_FILE" "$TEST_DIR/test_dict_seek.dec"; then log_pass "Dict + seekable (-D -S)" else log_fail "Dict + seekable round-trip failed" fi # 25.8 Dict with all levels echo " Testing dict across all levels..." DICT_ALL_OK=1 for LEVEL in 1 2 3 4 5 6 7; do "$ZXC_BIN" -$LEVEL -D "$DICT_FILE" -c -k "$TEST_FILE_ARG" > "$TEST_DIR/test_dict_lvl${LEVEL}.zxc" "$ZXC_BIN" -d -D "$DICT_FILE" -c "$TEST_DIR/test_dict_lvl${LEVEL}.zxc" > "$TEST_DIR/test_dict_lvl${LEVEL}.dec" if ! cmp -s "$TEST_FILE" "$TEST_DIR/test_dict_lvl${LEVEL}.dec"; then DICT_ALL_OK=0 log_fail "Dict level $LEVEL round-trip failed" fi done if [[ "$DICT_ALL_OK" -eq 1 ]]; then log_pass "Dict across all levels (1-7)" fi # 25.9 Invalid dict file should fail echo " Testing invalid dict file..." echo "not a valid dict" > "$TEST_DIR/bad.zxd" set +e "$ZXC_BIN" -3 -D "$TEST_DIR/bad.zxd" -c "$TEST_FILE_ARG" > /dev/null 2>&1 RET=$? set -e if [[ $RET -ne 0 ]]; then log_pass "Invalid dict file rejected" else log_fail "Invalid dict file should be rejected" fi # 25.10 Train to a directory -> dictionary_.zxd echo " Testing train-to-directory naming..." AUTO_DIR="$TEST_DIR/auto" mkdir -p "$AUTO_DIR" "$ZXC_BIN" --train -o "$AUTO_DIR/" "$TEST_DIR"/sample_*.txt 2>/dev/null ZXD_NAME=$(ls "$AUTO_DIR" | grep -E '^dictionary_[0-9a-f]{8}\.zxd$' || true) if [[ -n "$ZXD_NAME" ]]; then log_pass "Train to directory names dict dictionary_.zxd ($ZXD_NAME)" else log_fail "Train to directory should create dictionary_.zxd" fi # 25.11 -D is mandatory on decompression: no auto-lookup, even if the .zxd sits # right next to the archive. echo " Testing -D is mandatory (no auto-lookup)..." "$ZXC_BIN" -3 -D "$AUTO_DIR/$ZXD_NAME" -B 4K -c -k "$TEST_FILE_ARG" > "$AUTO_DIR/payload.zxc" set +e "$ZXC_BIN" -d -c "$AUTO_DIR/payload.zxc" > /dev/null 2>&1 # .zxd present, but no -D RET=$? set -e if [[ $RET -ne 0 ]]; then log_pass "Decompress without -D fails even with .zxd present (no auto-lookup)" else log_fail "Decompress without -D should fail (auto-lookup must be removed)" fi "$ZXC_BIN" -d -D "$AUTO_DIR/$ZXD_NAME" -c "$AUTO_DIR/payload.zxc" > "$AUTO_DIR/payload.dec" 2>/dev/null if cmp -s "$TEST_FILE" "$AUTO_DIR/payload.dec"; then log_pass "Decompress with -D succeeds" else log_fail "Decompress with -D failed to recreate original" fi # 26. unzxc Alias (argv[0]-based mode detection) echo "Testing unzxc alias..." # "unzxc" is a symlink to zxc that defaults to decompression (like unzstd / # gunzip). Create a local symlink to the binary under test and exercise it. # Symlinks are POSIX-only; skip gracefully where they are unsupported. ZXC_ABS=$(cd "$(dirname "$ZXC_BIN")" && pwd)/$(basename "$ZXC_BIN") UNZXC_BIN="$TEST_DIR/unzxc" if ln -sf "$ZXC_ABS" "$UNZXC_BIN" 2>/dev/null && [[ -L "$UNZXC_BIN" ]]; then # A known archive to decode through the alias. "$ZXC_BIN" -z -c -k "$TEST_FILE_ARG" > "$TEST_DIR/alias.zxc" # 26.1 unzxc with no mode flag must decompress (equivalent to zxc -d). if "$UNZXC_BIN" -c "$TEST_DIR/alias.zxc" > "$TEST_DIR/alias.dec" 2>/dev/null \ && cmp -s "$TEST_FILE" "$TEST_DIR/alias.dec"; then log_pass "unzxc defaults to decompression" else log_fail "unzxc should decompress by default (like zxc -d)" fi # 26.2 An explicit -z on the unzxc-named binary overrides back to compression. # If the override were broken, unzxc would try to *decode* the plaintext input # and fail, so the round-trip below would not match. set +e "$UNZXC_BIN" -z -c "$TEST_FILE_ARG" > "$TEST_DIR/alias_z.zxc" 2>/dev/null "$ZXC_BIN" -d -c "$TEST_DIR/alias_z.zxc" > "$TEST_DIR/alias_z.dec" 2>/dev/null set -e if cmp -s "$TEST_FILE" "$TEST_DIR/alias_z.dec"; then log_pass "unzxc -z overrides back to compression" else log_fail "unzxc -z should compress (explicit flag must win)" fi else echo " [SKIP] symlinks unsupported here, skipping unzxc alias test" fi # 27. Multiple input files (-m) and native Windows wildcard expansion (setargv.obj) echo "Testing multiple-file mode (-m) and wildcard expansion..." MGLOB_DIR="$TEST_DIR/mglob" mkdir -p "$MGLOB_DIR" for n in 1 2 3; do printf 'multi-file payload %s — lorem ipsum dolor sit amet\n' "$n" > "$MGLOB_DIR/part_${n}.txt" done # 27.1 Cross-platform: the shell expands the glob; -m must compress every file to # .zxc (keeping inputs with -k). Then verify one round-trip is valid. "$ZXC_BIN" -k -m "$MGLOB_DIR"/part_*.txt >/dev/null 2>&1 || true if [[ -f "$MGLOB_DIR/part_1.txt.zxc" ]] && [[ -f "$MGLOB_DIR/part_2.txt.zxc" ]] \ && [[ -f "$MGLOB_DIR/part_3.txt.zxc" ]]; then if "$ZXC_BIN" -d -c "$MGLOB_DIR/part_2.txt.zxc" 2>/dev/null | cmp -s - "$MGLOB_DIR/part_2.txt"; then log_pass "multiple-file mode (-m) compresses every file" else log_fail "-m produced a .zxc that does not round-trip" fi else log_fail "-m did not produce a .zxc for every input file" fi # 27.2 Native Windows wildcard expansion (setargv.obj): cmd/PowerShell don't glob, # so the binary must. Pass a *quoted* pattern so the shell does NOT expand it — # only the linked setargv.obj should. POSIX programs don't self-glob, so this # sub-check is Windows-only (skipped elsewhere, like the unzxc symlink test). case "$(uname -s 2>/dev/null)" in MINGW*|MSYS*|CYGWIN*) ZXC_ABS_G=$(cd "$(dirname "$ZXC_BIN")" && pwd)/$(basename "$ZXC_BIN") rm -f "$MGLOB_DIR"/*.zxc ( cd "$MGLOB_DIR" && "$ZXC_ABS_G" -k -m 'part_*.txt' >/dev/null 2>&1 ) || true if [[ -f "$MGLOB_DIR/part_1.txt.zxc" ]] && [[ -f "$MGLOB_DIR/part_2.txt.zxc" ]] \ && [[ -f "$MGLOB_DIR/part_3.txt.zxc" ]]; then log_pass "native Windows wildcard expansion (setargv.obj)" else log_fail "literal 'part_*.txt' was not expanded by the binary (setargv.obj missing?)" fi ;; *) echo " [SKIP] native wildcard expansion is Windows-only (POSIX shells glob themselves)" ;; esac # 28. Output Option (-o / --output / positional OUTPUT-FILE) echo "Testing Output Option (-o)..." # 28.1 -o on compression: output goes to the named file and the input is KEPT # (auto-deletion only applies when the output name is auto-derived) cp "$TEST_FILE" "$TEST_DIR/o_src.txt" "$ZXC_BIN" -f -o "$TEST_DIR/o_out.zxc" "$TEST_DIR/o_src.txt" if [[ -s "$TEST_DIR/o_out.zxc" ]] && [[ -f "$TEST_DIR/o_src.txt" ]]; then "$ZXC_BIN" -d -c "$TEST_DIR/o_out.zxc" > "$TEST_DIR/o_out.dec" if cmp -s "$TEST_FILE" "$TEST_DIR/o_out.dec"; then log_pass "Compress with -o (named output, input kept)" else log_fail "Compress with -o round-trip mismatch" fi else log_fail "Compress with -o failed (missing output or input was deleted)" fi # 28.2 -o on decompression "$ZXC_BIN" -d -f -o "$TEST_DIR/o_dec.txt" "$TEST_DIR/o_out.zxc" if cmp -s "$TEST_FILE" "$TEST_DIR/o_dec.txt"; then log_pass "Decompress with -o" else log_fail "Decompress with -o failed" fi # 28.3 Positional OUTPUT-FILE (zxc -d INPUT OUTPUT) "$ZXC_BIN" -d -f "$TEST_DIR/o_out.zxc" "$TEST_DIR/o_pos.txt" if cmp -s "$TEST_FILE" "$TEST_DIR/o_pos.txt"; then log_pass "Positional OUTPUT-FILE" else log_fail "Positional OUTPUT-FILE failed" fi # 28.4 -o with stdin input, both directions # (regression: -o used to be silently ignored when reading from stdin) "$ZXC_BIN" -f -o "$TEST_DIR/o_stdin.zxc" < "$TEST_FILE" if [[ ! -s "$TEST_DIR/o_stdin.zxc" ]]; then log_fail "Compress from stdin with -o produced no output" fi "$ZXC_BIN" -d -f -o "$TEST_DIR/o_stdin.dec" < "$TEST_DIR/o_stdin.zxc" if cmp -s "$TEST_FILE" "$TEST_DIR/o_stdin.dec"; then log_pass "stdin + -o (compress and decompress)" else log_fail "stdin + -o round-trip mismatch" fi # 28.5 Identical input/output must be rejected with the input left intact, # even when spelled differently (regression: raw string comparison let # './file' alias 'file' and O_TRUNC destroyed the input) cp "$TEST_DIR/o_out.zxc" "$TEST_DIR/ident.zxc" SZ_BEFORE=$(wc -c < "$TEST_DIR/ident.zxc" | tr -d ' ') set +e "$ZXC_BIN" -d -f -o "$TEST_DIR/./ident.zxc" "$TEST_DIR/ident.zxc" > /dev/null 2>&1 RET=$? set -e SZ_AFTER=$(wc -c < "$TEST_DIR/ident.zxc" | tr -d ' ') if [[ $RET -ne 0 ]] && [[ "$SZ_BEFORE" == "$SZ_AFTER" ]]; then log_pass "Identical input/output rejected, input intact" else log_fail "Identical input/output should be rejected without touching the input" fi # 28.6 -o combined with -m must be rejected set +e "$ZXC_BIN" -m -f -o "$TEST_DIR/x.zxc" "$TEST_DIR/multi1.txt" "$TEST_DIR/multi2.txt" > /dev/null 2>&1 RET=$? set -e if [[ $RET -ne 0 ]]; then log_pass "-o rejected with multiple mode (-m)" else log_fail "-o with -m should be rejected" fi # 29. Default Input Deletion (gzip-like behavior) echo "Testing Default Input Deletion..." cp "$TEST_FILE" "$TEST_DIR/del_me.txt" "$ZXC_BIN" -f "$TEST_DIR/del_me.txt" if [[ ! -f "$TEST_DIR/del_me.txt" ]] && [[ -f "$TEST_DIR/del_me.txt.zxc" ]]; then log_pass "Compression deletes input by default" else log_fail "Compression without -k should delete the input" fi "$ZXC_BIN" -d -f "$TEST_DIR/del_me.txt.zxc" if [[ -f "$TEST_DIR/del_me.txt" ]] && [[ ! -f "$TEST_DIR/del_me.txt.zxc" ]] \ && cmp -s "$TEST_FILE" "$TEST_DIR/del_me.txt"; then log_pass "Decompression deletes archive by default" else log_fail "Decompression without -k should delete the archive and restore the file" fi # 30. Long Options and Option Parsing echo "Testing Long Options and Parsing..." # 30.1 Long-option round-trip (--compress/--decompress/--keep/--force/--stdout) "$ZXC_BIN" --compress --keep --force --stdout "$TEST_FILE_ARG" > "$TEST_DIR/long.zxc" "$ZXC_BIN" --decompress --stdout "$TEST_DIR/long.zxc" > "$TEST_DIR/long.dec" if cmp -s "$TEST_FILE" "$TEST_DIR/long.dec"; then log_pass "Long options round-trip (--compress/--decompress)" else log_fail "Long options round-trip failed" fi # 30.2 Long options with values: separate (--threads 2) and '=' (--block-size=64K) "$ZXC_BIN" --threads 2 --block-size=64K --keep --force --stdout "$TEST_FILE_ARG" > "$TEST_DIR/long2.zxc" "$ZXC_BIN" -d -c "$TEST_DIR/long2.zxc" > "$TEST_DIR/long2.dec" if cmp -s "$TEST_FILE" "$TEST_DIR/long2.dec"; then log_pass "Long options with values (--threads N, --block-size=64K)" else log_fail "Long options with values failed" fi # 30.3 --list / --test / --version / --help / -h OUT_L=$("$ZXC_BIN" --list "$TEST_DIR/long.zxc") OUT_T=$("$ZXC_BIN" --test "$TEST_DIR/long.zxc") OUT_V=$("$ZXC_BIN" --version) OUT_H=$("$ZXC_BIN" --help) OUT_H2=$("$ZXC_BIN" -h) if [[ "$OUT_L" == *"Compressed"* ]] && [[ "$OUT_T" == *": OK"* ]] \ && [[ "$OUT_V" == *"ZXC CLI"* ]] && [[ "$OUT_H" == *"Usage:"* ]] \ && [[ "$OUT_H2" == *"Usage:"* ]]; then log_pass "Long mode options (--list/--test/--version/--help)" else log_fail "Long mode options failed" fi # 30.4 Grouped short flags (-zkf) cp "$TEST_FILE" "$TEST_DIR/group.txt" "$ZXC_BIN" -zkf "$TEST_DIR/group.txt" if [[ -f "$TEST_DIR/group.txt" ]] && [[ -f "$TEST_DIR/group.txt.zxc" ]]; then log_pass "Grouped short flags (-zkf)" else log_fail "Grouped short flags failed (-zkf should behave like -z -k -f)" fi # 30.5 "--" end-of-options marker with a dash-prefixed filename echo "dash-file payload" > "$TEST_DIR/-dash.txt" ZXC_ABS_P=$(cd "$(dirname "$ZXC_BIN")" && pwd)/$(basename "$ZXC_BIN") ( cd "$TEST_DIR" && "$ZXC_ABS_P" -z -k -f -- "-dash.txt" ) 2>/dev/null || true if [[ -f "$TEST_DIR/-dash.txt.zxc" ]]; then log_pass "'--' end-of-options marker" else log_fail "'--' should stop option parsing (dash-prefixed file)" fi # 30.6 "-" as explicit stdin marker "$ZXC_BIN" -d -c - < "$TEST_DIR/long.zxc" > "$TEST_DIR/dash_stdin.dec" if cmp -s "$TEST_FILE" "$TEST_DIR/dash_stdin.dec"; then log_pass "'-' explicit stdin marker" else log_fail "'-' stdin marker failed" fi # 30.7 Invalid option values must be rejected # (regressions: trailing junk accepted, signed overflow, atoi fallback to 0) set +e "$ZXC_BIN" -T abc "$TEST_FILE_ARG" > /dev/null 2>&1; RET_T=$? "$ZXC_BIN" -B 4Kxyz "$TEST_FILE_ARG" > /dev/null 2>&1; RET_B1=$? "$ZXC_BIN" -B 9223372036854775807K "$TEST_FILE_ARG" > /dev/null 2>&1; RET_B2=$? set -e if [[ $RET_T -ne 0 ]] && [[ $RET_B1 -ne 0 ]] && [[ $RET_B2 -ne 0 ]]; then log_pass "Invalid option values rejected (-T abc, -B 4Kxyz, -B overflow)" else log_fail "Invalid option values should be rejected" fi # 30.8 -b must not swallow a digit-leading filename as the duration set +e ERR_B=$("$ZXC_BIN" -b 5nonexistent.bin 2>&1) RET=$? set -e if [[ $RET -ne 0 ]] && [[ "$ERR_B" == *"Invalid input file"* ]]; then log_pass "-b leaves digit-leading filenames alone" else log_fail "-b consumed a digit-leading filename as duration" fi # 31. JSON List Robustness # (regression: a failed entry printed nothing, leaving stray commas in the array) echo "Testing JSON list with a failing entry..." set +e JSON_OUT=$("$ZXC_BIN" -l -j "$TEST_DIR/long.zxc" "$TEST_DIR/does_not_exist.zxc" "$TEST_DIR/long2.zxc" 2>/dev/null) RET=$? set -e if [[ $RET -ne 0 ]] && [[ "$JSON_OUT" == "["* ]] && [[ "$JSON_OUT" == *"]" ]] \ && [[ "$JSON_OUT" == *'"error"'* ]]; then log_pass "JSON list stays an array when one entry fails (exit code still non-zero)" else log_fail "JSON list with a failing entry produced malformed output or wrong exit code" fi if command -v jq &> /dev/null; then if echo "$JSON_OUT" | jq . > /dev/null 2>&1; then log_pass "JSON list with failing entry is valid JSON (verified with jq)" else log_fail "JSON list with failing entry is not valid JSON" fi fi # 32. Benchmark with Dictionary # (regression: the dictionary was freed before the benchmark used it) echo "Testing benchmark with dictionary (-b -D)..." set +e "$ZXC_BIN" -q -b1 -D "$DICT_FILE" "$TEST_FILE_ARG" > /dev/null 2>&1 RET=$? set -e if [[ $RET -eq 0 ]]; then log_pass "Benchmark with dictionary (-b1 -D)" else log_fail "Benchmark with dictionary failed" fi # 33. Deferred Write Error (Linux-only: /dev/full reports ENOSPC on flush) # (regression: fclose/fflush failures were ignored, truncated output # was reported as success and the input file deleted) # Cygwin/MSYS also expose /dev/full, but a NATIVE Windows child does not # inherit its ENOSPC semantics (writes succeed), so require real Linux. if [[ "$(uname -s 2>/dev/null)" == "Linux" ]] && [[ -w /dev/full ]]; then set +e "$ZXC_BIN" -c -k -f "$TEST_FILE_ARG" > /dev/full 2>/dev/null RET=$? set -e if [[ $RET -ne 0 ]]; then log_pass "Deferred write error detected (full device)" else log_fail "Write to full device should fail" fi else echo " [SKIP] deferred write-error check requires Linux /dev/full" fi # 34. Progress Display (--progress) echo "Testing Progress Display (--progress)..." # 34.1 --progress=always emits updates on a non-tty stderr (file input, known size) "$ZXC_BIN" --progress=always -f -k -c "$TEST_FILE_ARG" > /dev/null 2> "$TEST_DIR/prog1.err" if grep -q "Compressing \[" "$TEST_DIR/prog1.err" && grep -q "MB/s" "$TEST_DIR/prog1.err"; then log_pass "--progress=always emits updates off-tty" else log_fail "--progress=always should emit progress lines on stderr" fi # 34.2 --progress=always with stdin input (unknown total size) "$ZXC_BIN" --progress=always -f -o "$TEST_DIR/prog_stdin.zxc" < "$TEST_FILE" 2> "$TEST_DIR/prog2.err" if grep -q "Compressing" "$TEST_DIR/prog2.err"; then log_pass "--progress=always works with stdin (unknown size)" else log_fail "--progress=always with stdin should report bytes processed" fi # 34.3 --progress=never and auto off-tty stay silent "$ZXC_BIN" --progress=never -f -k -c "$TEST_FILE_ARG" > /dev/null 2> "$TEST_DIR/prog3.err" "$ZXC_BIN" -f -k -c "$TEST_FILE_ARG" > /dev/null 2> "$TEST_DIR/prog4.err" if [[ ! -s "$TEST_DIR/prog3.err" ]] && [[ ! -s "$TEST_DIR/prog4.err" ]]; then log_pass "--progress=never and auto off-tty are silent" else log_fail "No progress output expected (never / auto off-tty)" fi # 34.4 Invalid --progress value rejected set +e "$ZXC_BIN" --progress=bogus "$TEST_FILE_ARG" > /dev/null 2>&1 RET=$? set -e if [[ $RET -ne 0 ]]; then log_pass "--progress rejects invalid values" else log_fail "--progress must reject invalid values" fi # 34.5 -t with --progress=always labels the operation "Testing" "$ZXC_BIN" -z -k -f "$TEST_FILE_ARG" "$ZXC_BIN" -t --progress=always "$TEST_FILE_XC_ARG" > /dev/null 2> "$TEST_DIR/prog5.err" if grep -q "Testing" "$TEST_DIR/prog5.err"; then log_pass "-t progress labeled 'Testing'" else log_fail "-t progress should be labeled 'Testing'" fi echo "All tests passed!" exit 0 zxc-0.13.0/tests/test_common.c000066400000000000000000000171231522536370400162470ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ #include "test_common.h" /* Deterministic, platform-independent test PRNG (splitmix64) */ static uint64_t zxc_test_rng_state = 0x9E3779B97F4A7C15ULL; void zxc_test_srand(uint64_t seed) { zxc_test_rng_state = seed; } uint32_t zxc_test_rand(void) { uint64_t z = (zxc_test_rng_state += 0x9E3779B97F4A7C15ULL); z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9ULL; z = (z ^ (z >> 27)) * 0x94D049BB133111EBULL; z = z ^ (z >> 31); return (uint32_t)(z >> 32); } FILE* create_restricted_file(const char* path) { #ifdef _MSC_VER int fd = -1; _sopen_s(&fd, path, _O_CREAT | _O_WRONLY | _O_TRUNC, _SH_DENYNO, _S_IREAD | _S_IWRITE); return fd >= 0 ? _fdopen(fd, "w") : NULL; #else const int fd = open(path, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR); return fd >= 0 ? fdopen(fd, "w") : NULL; #endif } // Generates a buffer of random data (To force RAW) void gen_random_data(uint8_t* const buf, const size_t size) { for (size_t i = 0; i < size; i++) buf[i] = zxc_test_rand() & 0xFF; } // Generates repetitive data (To force GLO/GHI/LZ) void gen_lz_data(uint8_t* const buf, const size_t size) { const char* const pattern = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod " "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim " "veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea " "commodo consequat. Duis aute irure dolor in reprehenderit in voluptate " "velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint " "occaecat cupidatat non proident, sunt in culpa qui officia deserunt " "mollit anim id est laborum."; const size_t pat_len = strlen(pattern); for (size_t i = 0; i < size; i++) buf[i] = pattern[i % pat_len]; } // Generates a regular numeric sequence (integer-pattern round-trip input) void gen_num_data(uint8_t* const buf, const size_t size) { // Fill with 32-bit integers uint32_t* const ptr = (uint32_t*)buf; const size_t count = size / 4; uint32_t val = 0; for (size_t i = 0; i < count; i++) { // Arithmetic sequence: 0, 100, 200... // Deltas are constant (100): a smooth, highly compressible integer sequence ptr[i] = val; val += 100; } } // Generates numeric sequence with 0 deltas (all identical) void gen_num_data_zero(uint8_t* const buf, const size_t size) { uint32_t* const ptr = (uint32_t*)buf; const size_t count = size / 4; for (size_t i = 0; i < count; i++) { ptr[i] = 42; } } // Generates numeric data with alternating small deltas (+1, -1) void gen_num_data_small(uint8_t* const buf, const size_t size) { uint32_t* const ptr = (uint32_t*)buf; const size_t count = size / 4; uint32_t val = 1000; for (size_t i = 0; i < count; i++) { ptr[i] = val; val += (i % 2 == 0) ? 1 : -1; } } // Generates numeric data with very large deltas to maximize bit width void gen_num_data_large(uint8_t* const buf, const size_t size) { uint32_t* const ptr = (uint32_t*)buf; const size_t count = size / 4; for (size_t i = 0; i < count; i++) { // Alternate between 0 and 0xFFFFFFFF (delta is huge) ptr[i] = (i % 2 == 0) ? 0 : 0xFFFFFFFF; } } void gen_binary_data(uint8_t* const buf, const size_t size) { // Pattern with problematic bytes that could be corrupted in text mode: // 0x0A (LF), 0x0D (CR), 0x00 (NULL), 0x1A (EOF/CTRL-Z), 0xFF const uint8_t pattern[] = { 0x5A, 0x58, 0x43, 0x00, // "ZXC" + NULL 0x0A, 0x0D, 0x0A, 0x00, // LF, CR, LF, NULL 0xFF, 0xFE, 0x0A, 0x0D, // High bytes + LF/CR 0x1A, 0x00, 0x0A, 0x0D, // EOF marker + NULL + LF/CR 0x00, 0x00, 0x0A, 0x0A, // Multiple NULLs and LFs }; const size_t pat_len = sizeof(pattern); for (size_t i = 0; i < size; i++) { buf[i] = pattern[i % pat_len]; } } // Generates data with small offsets (<=255 bytes) to force 1-byte offset encoding // This creates short repeating patterns with matches very close to each other void gen_small_offset_data(uint8_t* const buf, const size_t size) { // Create short repeating patterns with very short distances. // LZ will match at offset=5 (< 255), exercising 8-bit offset encoding. const uint8_t pattern[] = "ABCDE"; for (size_t i = 0; i < size; i++) { buf[i] = pattern[i % 5]; } } // Generates data with large offsets (>255 bytes) to force 2-byte offset encoding // This creates patterns where matches are far apart void gen_large_offset_data(uint8_t* const buf, const size_t size) { // First 300 bytes: unique random data (no matches possible) for (size_t i = 0; i < 300 && i < size; i++) { buf[i] = (uint8_t)((i * 7 + 13) % 256); } // Then: repeat patterns from the beginning (offset > 255) for (size_t i = 300; i < size; i++) { buf[i] = buf[i - 300]; // Offset of 300 bytes (requires 2-byte encoding) } } void fill_seek_data(uint8_t* buf, size_t size, uint8_t seed) { for (size_t i = 0; i < size; i++) { buf[i] = (uint8_t)(seed + (i * 17) + (i >> 8)); } } // Generic Round-Trip test function (Compress -> Decompress -> Compare) int test_round_trip(const char* test_name, const uint8_t* input, size_t size, int level, int checksum_enabled) { printf("=== TEST: %s (Sz: %zu, Lvl: %d, CRC: %s) ===\n", test_name, size, level, checksum_enabled ? "Enabled" : "Disabled"); FILE* const f_in = tmpfile(); FILE* const f_comp = tmpfile(); FILE* const f_decomp = tmpfile(); if (!f_in || !f_comp || !f_decomp) { perror("tmpfile"); if (f_in) fclose(f_in); if (f_comp) fclose(f_comp); if (f_decomp) fclose(f_decomp); return 0; } fwrite(input, 1, size, f_in); fseek(f_in, 0, SEEK_SET); zxc_compress_opts_t _sco1 = { .n_threads = 1, .level = level, .checksum_enabled = checksum_enabled}; if (zxc_stream_compress(f_in, f_comp, &_sco1) < 0) { printf("Compression Failed!\n"); fclose(f_in); fclose(f_comp); fclose(f_decomp); return 0; } long comp_size = ftell(f_comp); printf("Compressed Size: %ld (Ratio: %.2f)\n", comp_size, (double)size / (comp_size > 0 ? comp_size : 1)); fseek(f_comp, 0, SEEK_SET); zxc_decompress_opts_t _sdo2 = {.n_threads = 1, .checksum_enabled = checksum_enabled}; if (zxc_stream_decompress(f_comp, f_decomp, &_sdo2) < 0) { printf("Decompression Failed!\n"); fclose(f_in); fclose(f_comp); fclose(f_decomp); return 0; } long decomp_size = ftell(f_decomp); if (decomp_size != (long)size) { printf("Size Mismatch! Expected %zu, got %ld\n", size, decomp_size); fclose(f_in); fclose(f_comp); fclose(f_decomp); return 0; } fseek(f_decomp, 0, SEEK_SET); uint8_t* out_buf = malloc(size > 0 ? size : 1); if (fread(out_buf, 1, size, f_decomp) != size) { printf("Read validation failed (incomplete read)!\n"); free(out_buf); fclose(f_in); fclose(f_comp); fclose(f_decomp); return 0; } if (size > 0 && memcmp(input, out_buf, size) != 0) { printf("Data Mismatch (Content Corruption)!\n"); free(out_buf); fclose(f_in); fclose(f_comp); fclose(f_decomp); return 0; } printf("PASS\n\n"); free(out_buf); fclose(f_in); fclose(f_comp); fclose(f_decomp); return 1; } zxc-0.13.0/tests/test_common.h000066400000000000000000000164631522536370400162620ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ #ifndef ZXC_TEST_COMMON_H #define ZXC_TEST_COMMON_H #include #include #include #include #include #ifdef _MSC_VER #include #include #endif #include "../include/zxc_buffer.h" #include "../include/zxc_error.h" #include "../include/zxc_pstream.h" #include "../include/zxc_seekable.h" #include "../include/zxc_stream.h" #include "../src/lib/zxc_internal.h" /* Declarative test-table entry: TEST_CASE(test_foo) -> { "test_foo", test_foo } */ #define TEST_CASE(fn) { #fn, fn } /* --- IO helper ---------------------------------------------------------- */ /* Creates a temporary file with restricted permissions (0600). * Returns a FILE* opened for writing, or NULL on failure. */ FILE* create_restricted_file(const char* path); /* --- Deterministic PRNG ------------------------------------------------- */ /* Test-only pseudorandom generator (splitmix64). Used purely to synthesize * test payloads. */ void zxc_test_srand(uint64_t seed); uint32_t zxc_test_rand(void); /* --- Data generators ---------------------------------------------------- */ void gen_random_data(uint8_t* buf, size_t size); void gen_lz_data(uint8_t* buf, size_t size); void gen_num_data(uint8_t* buf, size_t size); void gen_num_data_zero(uint8_t* buf, size_t size); void gen_num_data_small(uint8_t* buf, size_t size); void gen_num_data_large(uint8_t* buf, size_t size); void gen_binary_data(uint8_t* buf, size_t size); void gen_small_offset_data(uint8_t* buf, size_t size); void gen_large_offset_data(uint8_t* buf, size_t size); void fill_seek_data(uint8_t* buf, size_t size, uint8_t seed); /* Generic streaming round-trip check (compress, decompress, compare). * Returns 1 on success, 0 on failure. */ int test_round_trip(const char* test_name, const uint8_t* input, size_t size, int level, int checksum_enabled); /* --- Test function prototypes ------------------------------------------ */ /* Buffer API */ int test_buffer_api(void); int test_buffer_api_scratch_buf(void); int test_buffer_error_codes(void); int test_get_decompressed_size(void); int test_decompress_inplace(void); int test_decompress_fast_vs_safe_path(void); int test_max_compressed_size_logic(void); int test_decompress_empty_frame_null_dst(void); /* Block API */ int test_block_api(void); int test_block_api_boundary_sizes(void); int test_block_api_large_block_varint(void); int test_decompress_block_safe(void); int test_decompress_block_bound(void); /* Context API */ int test_opaque_context_api(void); int test_cctx_level_raise_reinit(void); int test_estimate_cctx_size(void); /* Static Context API */ int test_static_ctx_roundtrip_all_levels(void); int test_static_ctx_size_query(void); int test_static_ctx_workspace_too_small(void); int test_static_ctx_block_size_locked(void); int test_static_ctx_level_raise_rejected(void); int test_static_ctx_null_inputs(void); /* Stream API */ int test_null_output_decompression(void); int test_invalid_arguments(void); int test_truncated_input(void); int test_io_failures(void); int test_thread_params(void); int test_multithread_roundtrip(void); int test_stream_get_decompressed_size_errors(void); int test_stream_engine_errors(void); /* Push Streaming API (zxc_pstream.h) */ int test_pstream_roundtrip_basic(void); int test_pstream_roundtrip_no_checksum(void); int test_pstream_roundtrip_levels(void); int test_pstream_tiny_chunks(void); int test_pstream_drip_one_byte(void); int test_pstream_empty_input(void); int test_pstream_large_random(void); int test_pstream_compatible_with_buffer_api(void); int test_pstream_decompress_compatible_with_buffer_api(void); int test_pstream_invalid_args(void); int test_pstream_truncated_input(void); int test_pstream_corrupted_magic(void); int test_pstream_decode_seekable_archive(void); int test_pstream_compress_after_end_rejected(void); int test_pstream_compress_drain_block_resume(void); /* Stream round-trip coverage (patterns x sizes x levels x checksum) */ int test_roundtrip_raw_random(void); int test_roundtrip_ghi_text(void); int test_roundtrip_glo_text(void); int test_roundtrip_num_seq(void); int test_roundtrip_num_zero(void); int test_roundtrip_num_small(void); int test_roundtrip_num_large(void); int test_roundtrip_small_50(void); int test_roundtrip_empty(void); int test_roundtrip_1byte(void); int test_roundtrip_1byte_crc(void); int test_roundtrip_large_15mb_lz(void); int test_roundtrip_large_15mb_num(void); int test_roundtrip_checksum_off(void); int test_roundtrip_checksum_on(void); int test_roundtrip_level1(void); int test_roundtrip_level2(void); int test_roundtrip_level3(void); int test_roundtrip_level4(void); int test_roundtrip_level5(void); int test_roundtrip_level6(void); int test_roundtrip_binary(void); int test_roundtrip_binary_crc(void); int test_roundtrip_binary_small(void); int test_roundtrip_offset8_small(void); int test_roundtrip_offset8_lvl5(void); int test_roundtrip_offset16_large(void); int test_roundtrip_offset16_lvl5(void); int test_roundtrip_offset_mixed(void); /* Seekable (single-threaded) */ int test_seekable_table_sizes(void); int test_seekable_table_write(void); int test_seekable_roundtrip(void); int test_seekable_open_query(void); int test_seekable_random_access(void); int test_seekable_non_seekable_reject(void); int test_seekable_single_block(void); int test_seekable_all_levels(void); int test_seekable_many_blocks(void); int test_seekable_open_file(void); int test_seekable_open_reader(void); int test_seekable_open_reader_mt(void); int test_seekable_cross_boundary(void); int test_seekable_truncated_input(void); int test_seekable_corrupted_sek(void); int test_seekable_range_out_of_bounds(void); int test_seekable_dst_too_small(void); int test_seekable_empty_file(void); int test_seekable_no_checksum(void); int test_seekable_with_checksum(void); int test_seekable_work_buf_tail_pad(void); /* Seekable (multi-threaded) */ int test_seekable_mt_roundtrip(void); int test_seekable_mt_single_block(void); int test_seekable_mt_random_access(void); int test_seekable_mt_full_file(void); /* Format (on-disk) */ int test_huffman_codec(void); int test_huffman_codec_dict(void); int test_huffman_single_symbol_validation(void); int test_eof_block_structure(void); int test_header_checksum(void); int test_global_checksum_order(void); int test_chunk_size_code(void); /* Misc */ int test_error_name(void); int test_library_info_api(void); /* Dictionary */ int test_dict_zxd_roundtrip(void); int test_dict_id_deterministic(void); int test_dict_get_id_apis(void); int test_dict_buffer_roundtrip(void); int test_dict_block_roundtrip(void); int test_dict_block_safe_roundtrip(void); int test_dict_mismatch_error(void); int test_dict_required_error(void); int test_dict_no_dict_compat(void); int test_dict_stream_roundtrip(void); int test_dict_large_dict_roundtrip(void); int test_dict_safe_loop_backref(void); int test_dict_seekable_roundtrip(void); int test_dict_train_roundtrip(void); int test_dict_train_no_frequent_patterns(void); int test_dict_seekable_mt_roundtrip(void); int test_dict_stream_dict_id_checks(void); int test_dict_seekable_dict_id_checks(void); int test_dict_huf_zxd_roundtrip(void); int test_dict_huf_table_roundtrip(void); int test_dict_huf_degenerate_corpus(void); #endif /* ZXC_TEST_COMMON_H */ zxc-0.13.0/tests/test_context_api.c000066400000000000000000000240361522536370400172750ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ #include "test_common.h" int test_opaque_context_api() { printf("=== TEST: Opaque Context API (zxc_create_cctx / zxc_create_dctx) ===\n"); /* 1. NULL context -> ZXC_ERROR_NULL_INPUT */ { uint8_t d[64]; zxc_compress_opts_t co = {.level = 3, .checksum_enabled = 0}; if (zxc_compress_cctx(NULL, d, sizeof(d), d, sizeof(d), &co) != ZXC_ERROR_NULL_INPUT) { printf(" [FAIL] compress_cctx NULL ctx\n"); return 0; } zxc_decompress_opts_t do_ = {.checksum_enabled = 0}; if (zxc_decompress_dctx(NULL, d, sizeof(d), d, sizeof(d), &do_) != ZXC_ERROR_NULL_INPUT) { printf(" [FAIL] decompress_dctx NULL ctx\n"); return 0; } printf(" [PASS] NULL context -> ZXC_ERROR_NULL_INPUT\n"); } /* 2. Create with eager init, multi-call reuse, free */ zxc_compress_opts_t create_opts = {.level = 3, .checksum_enabled = 0}; zxc_cctx* cctx = zxc_create_cctx(&create_opts); zxc_dctx* dctx = zxc_create_dctx(); if (!cctx || !dctx) { printf(" [FAIL] create returned NULL\n"); zxc_free_cctx(cctx); zxc_free_dctx(dctx); return 0; } const size_t src_sz = 8192; uint8_t* src = malloc(src_sz); const size_t comp_cap = (size_t)zxc_compress_bound(src_sz); uint8_t* comp = malloc(comp_cap); uint8_t* dec = malloc(src_sz); /* 3. Three calls with the SAME cctx: level 1, 3, 5 */ for (int lvl = 1; lvl <= 5; lvl += 2) { gen_lz_data(src, src_sz); zxc_compress_opts_t co = {.level = lvl, .checksum_enabled = (lvl == 3)}; const int64_t csz = zxc_compress_cctx(cctx, src, src_sz, comp, comp_cap, &co); if (csz <= 0) { printf(" [FAIL] compress_cctx level %d returned %lld\n", lvl, (long long)csz); goto fail; } zxc_decompress_opts_t do_ = {.checksum_enabled = (lvl == 3)}; const int64_t dsz = zxc_decompress_dctx(dctx, comp, (size_t)csz, dec, src_sz, &do_); if (dsz != (int64_t)src_sz || memcmp(src, dec, src_sz) != 0) { printf(" [FAIL] roundtrip level %d (dsz=%lld)\n", lvl, (long long)dsz); goto fail; } } printf(" [PASS] Multi-call reuse (level 1, 3, 5)\n"); /* 4. Free is safe to call multiple times / on NULL */ zxc_free_cctx(cctx); cctx = NULL; zxc_free_cctx(NULL); /* no-op */ zxc_free_dctx(dctx); dctx = NULL; zxc_free_dctx(NULL); printf(" [PASS] Free + double-free + NULL safety\n"); free(src); free(comp); free(dec); printf("PASS\n\n"); return 1; fail: zxc_free_cctx(cctx); zxc_free_dctx(dctx); free(src); free(comp); free(dec); return 0; } /* Regression: a cctx created below ZXC_LEVEL_DENSITY has no opt_scratch; a * per-call level raise into the optimal-parser tier must re-init the inner * buffers instead of dereferencing the NULL scratch (crash before the fix). * Also covers the new out-of-range level validation (ZXC_ERROR_BAD_LEVEL). */ int test_cctx_level_raise_reinit() { printf("=== TEST: Opaque Context API - level raise re-init + level validation ===\n"); const size_t src_sz = 8192; uint8_t* src = malloc(src_sz); const size_t comp_cap = (size_t)zxc_compress_bound(src_sz); uint8_t* comp = malloc(comp_cap); uint8_t* dec = malloc(src_sz); zxc_cctx* cctx = NULL; zxc_dctx* dctx = NULL; if (!src || !comp || !dec) goto fail; gen_lz_data(src, src_sz); /* 1. Eager init at level 3 (no opt_scratch), then raise to 7 in place. */ zxc_compress_opts_t create_opts = {.level = 3, .checksum_enabled = 0}; cctx = zxc_create_cctx(&create_opts); dctx = zxc_create_dctx(); if (!cctx || !dctx) { printf(" [FAIL] create returned NULL\n"); goto fail; } zxc_compress_opts_t raise = {.level = ZXC_LEVEL_ULTRA, .checksum_enabled = 0}; const int64_t csz = zxc_compress_cctx(cctx, src, src_sz, comp, comp_cap, &raise); if (csz <= 0) { printf(" [FAIL] level-7 raise on level-3 cctx returned %lld\n", (long long)csz); goto fail; } const int64_t dsz = zxc_decompress_dctx(dctx, comp, (size_t)csz, dec, src_sz, NULL); if (dsz != (int64_t)src_sz || memcmp(src, dec, src_sz) != 0) { printf(" [FAIL] roundtrip after level raise (dsz=%lld)\n", (long long)dsz); goto fail; } printf(" [PASS] level 3 -> 7 raise re-inits and roundtrips\n"); /* 2. Same raise through the block API on a fresh low-level cctx. */ zxc_free_cctx(cctx); cctx = zxc_create_cctx(&create_opts); if (!cctx) { printf(" [FAIL] re-create returned NULL\n"); goto fail; } zxc_compress_opts_t raise6 = {.level = ZXC_LEVEL_DENSITY, .checksum_enabled = 0}; const int64_t bsz = zxc_compress_block(cctx, src, src_sz, comp, comp_cap, &raise6); if (bsz <= 0) { printf(" [FAIL] level-6 block raise returned %lld\n", (long long)bsz); goto fail; } printf(" [PASS] level 3 -> 6 raise through the block API\n"); /* 3. Out-of-range levels are silently clamped to ULTRA: level 99 must * produce the exact archive level 7 produces, on every entry point. */ zxc_compress_opts_t lvl7 = {.level = ZXC_LEVEL_ULTRA, .checksum_enabled = 0}; zxc_compress_opts_t lvl99 = {.level = 99, .checksum_enabled = 0}; uint8_t* comp7 = malloc(comp_cap); if (!comp7) goto fail; const int64_t c7 = zxc_compress(src, src_sz, comp7, comp_cap, &lvl7); const int64_t c99 = zxc_compress(src, src_sz, comp, comp_cap, &lvl99); if (c7 <= 0 || c99 != c7 || memcmp(comp, comp7, (size_t)c7) != 0) { printf(" [FAIL] zxc_compress(level=99) must clamp to ULTRA (c7=%lld c99=%lld)\n", (long long)c7, (long long)c99); free(comp7); goto fail; } free(comp7); if (zxc_compress_cctx(cctx, src, src_sz, comp, comp_cap, &lvl99) != c7) { printf(" [FAIL] zxc_compress_cctx(level=99) must clamp to ULTRA\n"); goto fail; } if (zxc_compress_block(cctx, src, src_sz, comp, comp_cap, &lvl99) <= 0) { printf(" [FAIL] zxc_compress_block(level=99) must clamp to ULTRA\n"); goto fail; } zxc_cctx* cctx99 = zxc_create_cctx(&lvl99); if (!cctx99) { printf(" [FAIL] zxc_create_cctx(level=99) must clamp, not fail\n"); goto fail; } zxc_free_cctx(cctx99); printf(" [PASS] level 99 silently clamped to ULTRA across entry points\n"); zxc_free_cctx(cctx); zxc_free_dctx(dctx); free(src); free(comp); free(dec); printf("PASS\n\n"); return 1; fail: zxc_free_cctx(cctx); zxc_free_dctx(dctx); free(src); free(comp); free(dec); return 0; } int test_estimate_cctx_size() { printf("=== TEST: Unit - zxc_estimate_cctx_size ===\n"); const int LVL = 3; /* 1. Zero input returns zero. */ if (zxc_estimate_cctx_size(0, LVL) != 0) { printf(" [FAIL] estimate(0) must return 0\n"); return 0; } printf(" [PASS] estimate(0) == 0\n"); /* 2. Non-zero input returns non-zero estimate. */ const uint64_t e1k = zxc_estimate_cctx_size(1024, LVL); if (e1k == 0) { printf(" [FAIL] estimate(1 KiB) must be > 0\n"); return 0; } printf(" [PASS] estimate(1 KiB) = %llu bytes\n", (unsigned long long)e1k); /* 3. Sizes below ZXC_BLOCK_SIZE_MIN collapse to the same estimate. */ if (zxc_estimate_cctx_size(512, LVL) != e1k || zxc_estimate_cctx_size(4096, LVL) != e1k) { printf(" [FAIL] estimates below MIN must round to ZXC_BLOCK_SIZE_MIN\n"); return 0; } printf(" [PASS] estimate rounds sub-MIN inputs to the same value\n"); /* 4. Monotonic: estimate grows with src_size across block_size tiers. */ const uint64_t e64k = zxc_estimate_cctx_size(64 * 1024, LVL); const uint64_t e1m = zxc_estimate_cctx_size(1024 * 1024, LVL); const uint64_t e8m = zxc_estimate_cctx_size(8 * 1024 * 1024, LVL); if (!(e1k <= e64k && e64k <= e1m && e1m <= e8m)) { printf(" [FAIL] estimates must be monotonic: %llu, %llu, %llu, %llu\n", (unsigned long long)e1k, (unsigned long long)e64k, (unsigned long long)e1m, (unsigned long long)e8m); return 0; } printf(" [PASS] monotonic: 1K=%llu, 64K=%llu, 1M=%llu, 8M=%llu\n", (unsigned long long)e1k, (unsigned long long)e64k, (unsigned long long)e1m, (unsigned long long)e8m); /* 5. Sanity: estimate for a large block must exceed the block itself. */ if (e1m < 1024 * 1024) { printf(" [FAIL] estimate(1 MiB)=%llu should exceed 1 MiB\n", (unsigned long long)e1m); return 0; } printf(" [PASS] estimate exceeds raw block size (context overhead present)\n"); /* 6. Sub-linear scaling: doubling src_size must roughly double the estimate, * bounded above by 10x factor (chain 2x, everything else 1x). */ if (e8m < 4 * e1m || e8m > 12 * e1m) { printf(" [FAIL] 8x src_size yields %.2fx memory (expected ~8x)\n", (double)e8m / (double)e1m); return 0; } printf(" [PASS] scaling: 8x src_size -> %.2fx memory\n", (double)e8m / (double)e1m); /* 7. Level 6 includes the optimal-parser scratch peak (~18 x chunk_size) * on top of the persistent cctx, so it must exceed the level-3 figure * by at least one chunk_size worth of bytes. */ const uint64_t e1m_l3 = zxc_estimate_cctx_size(1024 * 1024, 3); const uint64_t e1m_l6 = zxc_estimate_cctx_size(1024 * 1024, 6); if (e1m_l6 <= e1m_l3 + (1024 * 1024)) { printf(" [FAIL] level 6 must add optimal-parser scratch: l3=%llu l6=%llu\n", (unsigned long long)e1m_l3, (unsigned long long)e1m_l6); return 0; } printf(" [PASS] level 6 vs level 3 at 1 MiB: l3=%llu l6=%llu (delta=%llu)\n", (unsigned long long)e1m_l3, (unsigned long long)e1m_l6, (unsigned long long)(e1m_l6 - e1m_l3)); printf("PASS\n\n"); return 1; } zxc-0.13.0/tests/test_dict.c000066400000000000000000001521251522536370400157040ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ #include "test_common.h" #include "../include/zxc_dict.h" /* Build a valid 128-byte packed lengths table from arbitrary content bytes * (the .zxd format requires one). */ static void build_test_huf_lengths(const void* data, size_t n, uint8_t out[ZXC_HUF_TABLE_SIZE]) { uint32_t freq[ZXC_HUF_NUM_SYMBOLS] = {0}; const uint8_t* p = (const uint8_t*)data; for (size_t i = 0; i < n; i++) freq[p[i]]++; uint8_t code_len[ZXC_HUF_NUM_SYMBOLS]; zxc_huf_build_code_lengths(freq, code_len, NULL, ZXC_HUF_MAX_CODE_LEN_DENSITY); zxc_huf_pack_lengths(code_len, out); } static void gen_dict_friendly_data(uint8_t* buf, size_t size, const uint8_t* dict, size_t dict_size) { for (size_t i = 0; i < size; i++) { if (i % 7 < 5 && dict_size > 5) { size_t off = (i * 31) % (dict_size - 5); buf[i] = dict[off + (i % 5)]; } else { buf[i] = (uint8_t)(i ^ (i >> 8)); } } } int test_dict_zxd_roundtrip(void) { printf("=== TEST: Dict - .zxd save/load roundtrip ===\n"); const char* content = "hello dict content for testing zxd format!"; const size_t content_size = strlen(content); uint8_t huf[ZXC_HUF_TABLE_SIZE]; build_test_huf_lengths(content, content_size, huf); size_t bound = zxc_dict_save_bound(content_size); uint8_t* zxd = (uint8_t*)malloc(bound); int64_t written = zxc_dict_save(content, content_size, huf, zxd, bound); if (written < 0) { printf(" [FAIL] zxc_dict_save returned %lld\n", (long long)written); free(zxd); return 0; } const void* loaded_content = NULL; size_t loaded_size = 0; const void* loaded_huf = NULL; uint32_t loaded_id = 0; int rc = zxc_dict_load(zxd, (size_t)written, &loaded_content, &loaded_size, &loaded_huf, &loaded_id); if (rc != ZXC_OK) { printf(" [FAIL] zxc_dict_load returned %d (%s)\n", rc, zxc_error_name(rc)); free(zxd); return 0; } if (loaded_size != content_size || memcmp(loaded_content, content, content_size) != 0) { printf(" [FAIL] content mismatch after load\n"); free(zxd); return 0; } /* The id covers (content, table): nonzero, matches the stored header id, * and differs from the content-only id. */ if (loaded_id == 0 || loaded_id != zxc_dict_get_id(zxd, (size_t)written) || loaded_id == zxc_dict_id(content, content_size, NULL)) { printf(" [FAIL] dict_id binding incorrect: got %u\n", loaded_id); free(zxd); return 0; } /* The folded load and the standalone accessor must both return the exact * table bytes we stored, and agree with each other. */ const void* huf_acc = zxc_dict_huf(zxd, (size_t)written); if (!loaded_huf || loaded_huf != huf_acc || memcmp(loaded_huf, huf, ZXC_HUF_TABLE_SIZE) != 0) { printf(" [FAIL] dict_load table out-param / zxc_dict_huf mismatch\n"); free(zxd); return 0; } free(zxd); printf("PASS\n\n"); return 1; } int test_dict_id_deterministic(void) { printf("=== TEST: Dict - dict_id is deterministic ===\n"); const char* data = "some repeatable dictionary content"; size_t size = strlen(data); uint32_t id1 = zxc_dict_id(data, size, NULL); uint32_t id2 = zxc_dict_id(data, size, NULL); if (id1 != id2 || id1 == 0) { printf(" [FAIL] dict_id not deterministic or zero: %u vs %u\n", id1, id2); return 0; } uint32_t id_null = zxc_dict_id(NULL, 0, NULL); if (id_null != 0) { printf(" [FAIL] dict_id(NULL, 0) should be 0, got %u\n", id_null); return 0; } printf("PASS\n\n"); return 1; } int test_dict_get_id_apis(void) { printf("=== TEST: Dict - zxc_get_dict_id / zxc_dict_get_id ===\n"); const uint8_t dict[] = "dictionary content for get_id test"; const size_t dict_size = sizeof(dict) - 1; const uint32_t expected_id = zxc_dict_id(dict, dict_size, NULL); /* Compress with dict and verify zxc_get_dict_id reads it back */ const uint8_t src[] = "some data to compress with dict for id test purposes"; const size_t src_size = sizeof(src) - 1; size_t comp_bound = (size_t)zxc_compress_bound(src_size); uint8_t* compressed = (uint8_t*)malloc(comp_bound); zxc_compress_opts_t copts = {.level = 1, .dict = dict, .dict_size = dict_size}; int64_t comp_size = zxc_compress(src, src_size, compressed, comp_bound, &copts); if (comp_size <= 0) { printf(" [FAIL] compress returned %lld\n", (long long)comp_size); free(compressed); return 0; } uint32_t got_id = zxc_get_dict_id(compressed, (size_t)comp_size); if (got_id != expected_id) { printf(" [FAIL] zxc_get_dict_id: got 0x%08X, expected 0x%08X\n", got_id, expected_id); free(compressed); return 0; } printf(" [PASS] zxc_get_dict_id returns 0x%08X\n", got_id); /* Compress without dict: should return 0 */ zxc_compress_opts_t copts2 = {.level = 1}; int64_t comp2 = zxc_compress(src, src_size, compressed, comp_bound, &copts2); if (comp2 > 0 && zxc_get_dict_id(compressed, (size_t)comp2) != 0) { printf(" [FAIL] zxc_get_dict_id should return 0 for no-dict file\n"); free(compressed); return 0; } printf(" [PASS] zxc_get_dict_id returns 0 for no-dict file\n"); free(compressed); /* Save to .zxd and verify zxc_dict_get_id matches what load reports */ uint8_t huf[ZXC_HUF_TABLE_SIZE]; build_test_huf_lengths(dict, dict_size, huf); size_t zxd_bound = zxc_dict_save_bound(dict_size); uint8_t* zxd = (uint8_t*)malloc(zxd_bound); int64_t zxd_size = zxc_dict_save(dict, dict_size, huf, zxd, zxd_bound); if (zxd_size <= 0) { printf(" [FAIL] zxc_dict_save returned %lld\n", (long long)zxd_size); free(zxd); return 0; } const void* lc = NULL; size_t lcs = 0; uint32_t loaded_id = 0; uint32_t zxd_id = zxc_dict_get_id(zxd, (size_t)zxd_size); if (zxd_id == 0 || zxc_dict_load(zxd, (size_t)zxd_size, &lc, &lcs, NULL, &loaded_id) != ZXC_OK || loaded_id != zxd_id) { printf(" [FAIL] zxc_dict_get_id: got 0x%08X, load id 0x%08X\n", zxd_id, loaded_id); free(zxd); return 0; } printf(" [PASS] zxc_dict_get_id returns 0x%08X\n", zxd_id); /* Invalid buffer should return 0 */ if (zxc_dict_get_id("bad", 3) != 0) { printf(" [FAIL] zxc_dict_get_id should return 0 for invalid buffer\n"); free(zxd); return 0; } printf(" [PASS] zxc_dict_get_id returns 0 for invalid buffer\n"); free(zxd); printf("PASS\n\n"); return 1; } int test_dict_buffer_roundtrip(void) { printf("=== TEST: Dict - buffer API roundtrip (all levels) ===\n"); const uint8_t dict_content[] = "The quick brown fox jumps over the lazy dog. " "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " "Pack my box with five dozen liquor jugs. " "How vexingly quick daft zebras jump!"; const size_t dict_size = sizeof(dict_content) - 1; const size_t src_size = 4096; uint8_t* src = (uint8_t*)malloc(src_size); gen_dict_friendly_data(src, src_size, dict_content, dict_size); size_t comp_bound = (size_t)zxc_compress_bound(src_size); uint8_t* compressed = (uint8_t*)malloc(comp_bound); uint8_t* decompressed = (uint8_t*)malloc(src_size); for (int level = 1; level <= 6; level++) { zxc_compress_opts_t copts = { .level = level, .checksum_enabled = 1, .dict = dict_content, .dict_size = dict_size, }; int64_t comp_size = zxc_compress(src, src_size, compressed, comp_bound, &copts); if (comp_size <= 0) { printf(" [FAIL] level %d: compress returned %lld\n", level, (long long)comp_size); free(src); free(compressed); free(decompressed); return 0; } zxc_decompress_opts_t dopts = { .checksum_enabled = 1, .dict = dict_content, .dict_size = dict_size, }; int64_t dec_size = zxc_decompress(compressed, (size_t)comp_size, decompressed, src_size, &dopts); if (dec_size != (int64_t)src_size) { printf(" [FAIL] level %d: decompress returned %lld, expected %zu\n", level, (long long)dec_size, src_size); free(src); free(compressed); free(decompressed); return 0; } if (memcmp(src, decompressed, src_size) != 0) { printf(" [FAIL] level %d: content mismatch\n", level); free(src); free(compressed); free(decompressed); return 0; } printf(" [PASS] level %d: %zu -> %lld bytes\n", level, src_size, (long long)comp_size); } free(src); free(compressed); free(decompressed); printf("PASS\n\n"); return 1; } int test_dict_block_roundtrip(void) { printf("=== TEST: Dict - block API roundtrip (all levels) ===\n"); const uint8_t dict_content[] = "The quick brown fox jumps over the lazy dog. " "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " "Pack my box with five dozen liquor jugs."; const size_t dict_size = sizeof(dict_content) - 1; const size_t src_size = 4096; uint8_t* src = (uint8_t*)malloc(src_size); gen_dict_friendly_data(src, src_size, dict_content, dict_size); const size_t comp_bound = (size_t)zxc_compress_block_bound(src_size); uint8_t* compressed = (uint8_t*)malloc(comp_bound); uint8_t* decompressed = (uint8_t*)malloc(src_size); zxc_cctx* cctx = zxc_create_cctx(NULL); zxc_dctx* dctx = zxc_create_dctx(); int result = 0; if (!src || !compressed || !decompressed || !cctx || !dctx) { printf(" [FAIL] allocation failed\n"); goto cleanup; } for (int level = 1; level <= 6; level++) { zxc_compress_opts_t copts = { .level = level, .checksum_enabled = 1, .dict = dict_content, .dict_size = dict_size, }; int64_t comp_size = zxc_compress_block(cctx, src, src_size, compressed, comp_bound, &copts); if (comp_size <= 0) { printf(" [FAIL] level %d: compress_block returned %lld\n", level, (long long)comp_size); goto cleanup; } zxc_decompress_opts_t dopts = { .checksum_enabled = 1, .dict = dict_content, .dict_size = dict_size, }; int64_t dec_size = zxc_decompress_block(dctx, compressed, (size_t)comp_size, decompressed, src_size, &dopts); if (dec_size != (int64_t)src_size || memcmp(src, decompressed, src_size) != 0) { printf(" [FAIL] level %d: block roundtrip mismatch (dec_size=%lld)\n", level, (long long)dec_size); goto cleanup; } printf(" [PASS] level %d: %zu -> %lld bytes\n", level, src_size, (long long)comp_size); } result = 1; cleanup: zxc_free_cctx(cctx); /* safe with NULL */ zxc_free_dctx(dctx); /* safe with NULL */ free(src); free(compressed); free(decompressed); if (result) printf("PASS\n\n"); return result; } /* zxc_decompress_block_safe (exact-fit dst) must honor opts->dict; it used to * ignore it (BAD_OFFSET on dict back-refs). Real shuffled-pattern dict matches. */ int test_dict_block_safe_roundtrip(void) { printf("=== TEST: Dict - block_safe (strict-tail) with real dict back-refs ===\n"); enum { NPAT = 256, PLEN = 40 }; const size_t dict_size = (size_t)NPAT * PLEN; uint8_t* dict = (uint8_t*)malloc(dict_size); for (int i = 0; i < NPAT; i++) { uint32_t x = (uint32_t)i * 2654435761u; for (int j = 0; j < PLEN; j++) { x = x * 1103515245u + 12345u; dict[(size_t)i * PLEN + j] = (uint8_t)(x >> 16); } } int order[NPAT]; for (int i = 0; i < NPAT; i++) order[i] = i; uint32_t s = 777u; for (int i = NPAT - 1; i > 0; i--) { s = s * 1103515245u + 12345u; int j = (int)(s % (uint32_t)(i + 1)); int tmp = order[i]; order[i] = order[j]; order[j] = tmp; } const size_t src_size = (size_t)NPAT * (PLEN + 1); uint8_t* src = (uint8_t*)malloc(src_size); for (int k = 0; k < NPAT; k++) { memcpy(src + (size_t)k * (PLEN + 1), dict + (size_t)order[k] * PLEN, PLEN); src[(size_t)k * (PLEN + 1) + PLEN] = (uint8_t)k; } const size_t comp_bound = (size_t)zxc_compress_block_bound(src_size); uint8_t* compressed = (uint8_t*)malloc(comp_bound); uint8_t* decompressed = (uint8_t*)malloc(src_size); zxc_cctx* cctx = zxc_create_cctx(NULL); zxc_dctx* dctx = zxc_create_dctx(); int result = 0; if (!dict || !src || !compressed || !decompressed || !cctx || !dctx) { printf(" [FAIL] allocation failed\n"); goto cleanup; } for (int level = 1; level <= 6; level++) { zxc_compress_opts_t copts = {.level = level, .dict = dict, .dict_size = dict_size}; int64_t comp_size = zxc_compress_block(cctx, src, src_size, compressed, comp_bound, &copts); if (comp_size <= 0) { printf(" [FAIL] level %d: compress_block returned %lld (%s)\n", level, (long long)comp_size, zxc_error_name((int)comp_size)); goto cleanup; } /* Strict-tail decode: EXACT dst_capacity == src_size, WITH the dict. */ zxc_decompress_opts_t dopts = {.dict = dict, .dict_size = dict_size}; int64_t dec_size = zxc_decompress_block_safe(dctx, compressed, (size_t)comp_size, decompressed, src_size, &dopts); if (dec_size != (int64_t)src_size || memcmp(src, decompressed, src_size) != 0) { printf(" [FAIL] level %d: dec_size=%lld err=%s\n", level, (long long)dec_size, dec_size < 0 ? zxc_error_name((int)dec_size) : "content mismatch"); goto cleanup; } printf(" [PASS] level %d (%lld -> %zu)\n", level, (long long)comp_size, src_size); } result = 1; cleanup: zxc_free_cctx(cctx); zxc_free_dctx(dctx); free(dict); free(src); free(compressed); free(decompressed); if (result) printf("PASS\n\n"); return result; } int test_dict_mismatch_error(void) { printf("=== TEST: Dict - dict_id mismatch error ===\n"); const uint8_t dict[] = "correct dictionary content"; const uint8_t wrong_dict[] = "wrong dictionary contentz"; const size_t dict_size = sizeof(dict) - 1; const uint8_t src[] = "some data to compress with dict"; const size_t src_size = sizeof(src) - 1; size_t comp_bound = (size_t)zxc_compress_bound(src_size); uint8_t* compressed = (uint8_t*)malloc(comp_bound); zxc_compress_opts_t copts = {.level = 3, .dict = dict, .dict_size = dict_size}; int64_t comp_size = zxc_compress(src, src_size, compressed, comp_bound, &copts); if (comp_size <= 0) { printf(" [FAIL] compress failed: %lld\n", (long long)comp_size); free(compressed); return 0; } uint8_t decompressed[256]; zxc_decompress_opts_t dopts = {.dict = wrong_dict, .dict_size = sizeof(wrong_dict) - 1}; int64_t rc = zxc_decompress(compressed, (size_t)comp_size, decompressed, sizeof(decompressed), &dopts); if (rc != ZXC_ERROR_DICT_MISMATCH) { printf(" [FAIL] expected DICT_MISMATCH, got %lld (%s)\n", (long long)rc, zxc_error_name((int)rc)); free(compressed); return 0; } free(compressed); printf("PASS\n\n"); return 1; } int test_dict_required_error(void) { printf("=== TEST: Dict - dict required error ===\n"); const uint8_t dict[] = "required dictionary"; const size_t dict_size = sizeof(dict) - 1; const uint8_t src[] = "data needing a dict"; const size_t src_size = sizeof(src) - 1; size_t comp_bound = (size_t)zxc_compress_bound(src_size); uint8_t* compressed = (uint8_t*)malloc(comp_bound); zxc_compress_opts_t copts = {.level = 3, .dict = dict, .dict_size = dict_size}; int64_t comp_size = zxc_compress(src, src_size, compressed, comp_bound, &copts); if (comp_size <= 0) { printf(" [FAIL] compress failed: %lld\n", (long long)comp_size); free(compressed); return 0; } uint8_t decompressed[256]; zxc_decompress_opts_t dopts = {0}; int64_t rc = zxc_decompress(compressed, (size_t)comp_size, decompressed, sizeof(decompressed), &dopts); if (rc != ZXC_ERROR_DICT_REQUIRED) { printf(" [FAIL] expected DICT_REQUIRED, got %lld (%s)\n", (long long)rc, zxc_error_name((int)rc)); free(compressed); return 0; } free(compressed); printf("PASS\n\n"); return 1; } int test_dict_no_dict_compat(void) { printf("=== TEST: Dict - no-dict files decompress normally ===\n"); const uint8_t src[] = "data compressed without any dictionary at all, just normal data"; const size_t src_size = sizeof(src) - 1; size_t comp_bound = (size_t)zxc_compress_bound(src_size); uint8_t* compressed = (uint8_t*)malloc(comp_bound); zxc_compress_opts_t copts = {.level = 3, .checksum_enabled = 1}; int64_t comp_size = zxc_compress(src, src_size, compressed, comp_bound, &copts); if (comp_size <= 0) { printf(" [FAIL] compress failed\n"); free(compressed); return 0; } uint8_t decompressed[256]; zxc_decompress_opts_t dopts = {.checksum_enabled = 1}; int64_t dec_size = zxc_decompress(compressed, (size_t)comp_size, decompressed, sizeof(decompressed), &dopts); if (dec_size != (int64_t)src_size || memcmp(src, decompressed, src_size) != 0) { printf(" [FAIL] roundtrip without dict failed\n"); free(compressed); return 0; } free(compressed); printf("PASS\n\n"); return 1; } int test_dict_stream_roundtrip(void) { printf("=== TEST: Dict - stream API roundtrip ===\n"); const uint8_t dict_content[] = "The quick brown fox jumps over the lazy dog. " "Lorem ipsum dolor sit amet, consectetur adipiscing elit."; const size_t dict_size = sizeof(dict_content) - 1; const size_t src_size = 8192; uint8_t* src = (uint8_t*)malloc(src_size); gen_dict_friendly_data(src, src_size, dict_content, dict_size); FILE* f_src = tmpfile(); FILE* f_comp = tmpfile(); FILE* f_dec = tmpfile(); if (!f_src || !f_comp || !f_dec) { printf(" [FAIL] tmpfile() failed\n"); free(src); return 0; } fwrite(src, 1, src_size, f_src); rewind(f_src); zxc_compress_opts_t copts = { .level = ZXC_LEVEL_DEFAULT, .checksum_enabled = 1, .dict = dict_content, .dict_size = dict_size, }; int64_t comp_sz = zxc_stream_compress(f_src, f_comp, &copts); if (comp_sz <= 0) { printf(" [FAIL] stream_compress returned %lld\n", (long long)comp_sz); fclose(f_src); fclose(f_comp); fclose(f_dec); free(src); return 0; } rewind(f_comp); zxc_decompress_opts_t dopts = { .checksum_enabled = 1, .dict = dict_content, .dict_size = dict_size, }; int64_t dec_sz = zxc_stream_decompress(f_comp, f_dec, &dopts); if (dec_sz != (int64_t)src_size) { printf(" [FAIL] stream_decompress returned %lld, expected %zu\n", (long long)dec_sz, src_size); fclose(f_src); fclose(f_comp); fclose(f_dec); free(src); return 0; } rewind(f_dec); uint8_t* result = (uint8_t*)malloc(src_size); const size_t rd = fread(result, 1, src_size, f_dec); int ok = (rd == src_size && memcmp(src, result, src_size) == 0); fclose(f_src); fclose(f_comp); fclose(f_dec); free(result); free(src); if (!ok) { printf(" [FAIL] content mismatch\n"); return 0; } printf("PASS\n\n"); return 1; } int test_dict_large_dict_roundtrip(void) { printf("=== TEST: Dict - large dict (32KB) with small blocks (4KB) ===\n"); uint8_t* dict = (uint8_t*)malloc(32768); for (size_t i = 0; i < 32768; i++) dict[i] = (uint8_t)(i * 7 + 13); const size_t dict_size = 32768; const size_t src_size = 4096; uint8_t* src = (uint8_t*)malloc(src_size); gen_dict_friendly_data(src, src_size, dict, dict_size); size_t comp_bound = (size_t)zxc_compress_bound(src_size); uint8_t* compressed = (uint8_t*)malloc(comp_bound); uint8_t* decompressed = (uint8_t*)malloc(src_size); for (int level = 1; level <= 6; level++) { zxc_compress_opts_t copts = { .level = level, .checksum_enabled = 1, .dict = dict, .dict_size = dict_size, }; int64_t comp_size = zxc_compress(src, src_size, compressed, comp_bound, &copts); if (comp_size <= 0) { printf(" [FAIL] level %d: compress returned %lld (%s)\n", level, (long long)comp_size, zxc_error_name((int)comp_size)); free(src); free(compressed); free(decompressed); free(dict); return 0; } zxc_decompress_opts_t dopts = {.checksum_enabled = 1, .dict = dict, .dict_size = dict_size}; int64_t dec_size = zxc_decompress(compressed, (size_t)comp_size, decompressed, src_size, &dopts); if (dec_size != (int64_t)src_size || memcmp(src, decompressed, src_size) != 0) { printf(" [FAIL] level %d: dec_size=%lld err=%s\n", level, (long long)dec_size, dec_size < 0 ? zxc_error_name((int)dec_size) : "content mismatch"); free(src); free(compressed); free(decompressed); free(dict); return 0; } printf(" [PASS] level %d\n", level); } free(src); free(compressed); free(decompressed); free(dict); printf("PASS\n\n"); return 1; } /* * Regression test for the GLO SAFE-loop dictionary back-reference path. * * The other dict roundtrip tests use gen_dict_friendly_data(), which scatters * dict-derived BYTES but never forms long matches, so the decoder never emits a * dictionary back-reference. This test forges many small *matches* into the dict * (distinct 40-byte patterns in shuffled order, each separated by a literal so * they don't merge). Dict (10KB) + payload (10KB) stay under 64KB, so every * sequence is validated by the SAFE 4x loop's `off > written` check -- which is * only correct because `written` is seeded with dict_size. If that seeding were * dropped (written = 0), these dict back-refs would be wrongly rejected * (BAD_OFFSET). See project_dict_written_floor. */ int test_dict_safe_loop_backref(void) { printf("=== TEST: Dict - many small back-refs in the SAFE 4x loop ===\n"); enum { NPAT = 256, PLEN = 40 }; const size_t dict_size = (size_t)NPAT * PLEN; uint8_t* dict = (uint8_t*)malloc(dict_size); for (int i = 0; i < NPAT; i++) { uint32_t x = (uint32_t)i * 2654435761u; for (int j = 0; j < PLEN; j++) { x = x * 1103515245u + 12345u; dict[(size_t)i * PLEN + j] = (uint8_t)(x >> 16); } } int order[NPAT]; for (int i = 0; i < NPAT; i++) order[i] = i; uint32_t s = 12345u; for (int i = NPAT - 1; i > 0; i--) { s = s * 1103515245u + 12345u; int j = (int)(s % (uint32_t)(i + 1)); int tmp = order[i]; order[i] = order[j]; order[j] = tmp; } const size_t src_size = (size_t)NPAT * (PLEN + 1); uint8_t* src = (uint8_t*)malloc(src_size); for (int k = 0; k < NPAT; k++) { memcpy(src + (size_t)k * (PLEN + 1), dict + (size_t)order[k] * PLEN, PLEN); src[(size_t)k * (PLEN + 1) + PLEN] = (uint8_t)k; // literal separator (anti-merge) } size_t comp_bound = (size_t)zxc_compress_bound(src_size); uint8_t* compressed = (uint8_t*)malloc(comp_bound); uint8_t* decompressed = (uint8_t*)malloc(src_size); int ok = 1; for (int level = 1; level <= 6 && ok; level++) { zxc_compress_opts_t copts = {.level = level, .dict = dict, .dict_size = dict_size}; int64_t comp_size = zxc_compress(src, src_size, compressed, comp_bound, &copts); if (comp_size <= 0) { printf(" [FAIL] level %d: compress returned %lld (%s)\n", level, (long long)comp_size, zxc_error_name((int)comp_size)); ok = 0; break; } zxc_decompress_opts_t dopts = {.dict = dict, .dict_size = dict_size}; int64_t dec_size = zxc_decompress(compressed, (size_t)comp_size, decompressed, src_size, &dopts); if (dec_size != (int64_t)src_size || memcmp(src, decompressed, src_size) != 0) { printf(" [FAIL] level %d: dec_size=%lld err=%s\n", level, (long long)dec_size, dec_size < 0 ? zxc_error_name((int)dec_size) : "content mismatch"); ok = 0; break; } printf(" [PASS] level %d (%lld -> %zu)\n", level, (long long)comp_size, src_size); } free(src); free(compressed); free(decompressed); free(dict); if (ok) printf("PASS\n\n"); return ok; } int test_dict_train_roundtrip(void) { printf("=== TEST: Dict - train then compress/decompress ===\n"); const char* json_samples[] = { "{\"id\":1,\"name\":\"alice\",\"email\":\"alice@example.com\",\"active\":true}", "{\"id\":2,\"name\":\"bob\",\"email\":\"bob@example.com\",\"active\":false}", "{\"id\":3,\"name\":\"carol\",\"email\":\"carol@example.com\",\"active\":true}", "{\"id\":4,\"name\":\"dave\",\"email\":\"dave@example.com\",\"active\":true}", "{\"id\":5,\"name\":\"eve\",\"email\":\"eve@example.com\",\"active\":false}", "{\"id\":6,\"name\":\"frank\",\"email\":\"frank@example.com\",\"active\":true}", "{\"id\":7,\"name\":\"grace\",\"email\":\"grace@example.com\",\"active\":false}", "{\"id\":8,\"name\":\"hank\",\"email\":\"hank@example.com\",\"active\":true}", }; const size_t n_samples = sizeof(json_samples) / sizeof(json_samples[0]); const void* sample_ptrs[8]; size_t sample_sizes[8]; for (size_t i = 0; i < n_samples; i++) { sample_ptrs[i] = json_samples[i]; sample_sizes[i] = strlen(json_samples[i]); } uint8_t dict_buf[4096]; int64_t dict_sz = zxc_train_dict(sample_ptrs, sample_sizes, n_samples, dict_buf, sizeof(dict_buf)); if (dict_sz <= 0) { printf(" [FAIL] train_dict returned %lld\n", (long long)dict_sz); return 0; } printf(" trained dict: %lld bytes\n", (long long)dict_sz); const char* test_input = "{\"id\":99,\"name\":\"zara\",\"email\":\"zara@example.com\",\"active\":true}"; const size_t src_size = strlen(test_input); size_t comp_bound = (size_t)zxc_compress_bound(src_size); uint8_t* compressed = (uint8_t*)malloc(comp_bound); zxc_compress_opts_t copts = { .level = ZXC_LEVEL_DEFAULT, .checksum_enabled = 1, .dict = dict_buf, .dict_size = (size_t)dict_sz, }; int64_t comp_size = zxc_compress(test_input, src_size, compressed, comp_bound, &copts); if (comp_size <= 0) { printf(" [FAIL] compress returned %lld\n", (long long)comp_size); free(compressed); return 0; } zxc_compress_opts_t copts_nodict = {.level = ZXC_LEVEL_DEFAULT, .checksum_enabled = 1}; uint8_t* comp_nodict = (uint8_t*)malloc(comp_bound); int64_t comp_nodict_sz = zxc_compress(test_input, src_size, comp_nodict, comp_bound, &copts_nodict); printf(" with dict: %lld bytes, without: %lld bytes (input: %zu)\n", (long long)comp_size, (long long)comp_nodict_sz, src_size); free(comp_nodict); uint8_t decompressed[256]; zxc_decompress_opts_t dopts = { .checksum_enabled = 1, .dict = dict_buf, .dict_size = (size_t)dict_sz, }; int64_t dec_size = zxc_decompress(compressed, (size_t)comp_size, decompressed, sizeof(decompressed), &dopts); free(compressed); if (dec_size != (int64_t)src_size || memcmp(test_input, decompressed, src_size) != 0) { printf(" [FAIL] roundtrip mismatch\n"); return 0; } printf("PASS\n\n"); return 1; } int test_dict_train_no_frequent_patterns(void) { printf("=== TEST: Dict - train fallback when no frequent k-grams ===\n"); /* A strictly increasing byte sequence has all-distinct 5-grams, so no * k-gram repeats and the trainer finds zero scorable segments. This forces * the n_segs == 0 fallback: copy the tail of the corpus into the dict. */ uint8_t corpus[64]; for (size_t i = 0; i < sizeof(corpus); i++) corpus[i] = (uint8_t)i; const void* sample_ptrs[1] = {corpus}; const size_t sample_sizes[1] = {sizeof(corpus)}; /* Case 1: capacity >= corpus_size -> copy == corpus_size, dict == whole corpus. */ uint8_t dict_big[256]; int64_t sz = zxc_train_dict(sample_ptrs, sample_sizes, 1, dict_big, sizeof(dict_big)); if (sz != (int64_t)sizeof(corpus)) { printf(" [FAIL] expected %zu bytes (full corpus), got %lld\n", sizeof(corpus), (long long)sz); return 0; } if (memcmp(dict_big, corpus, sizeof(corpus)) != 0) { printf(" [FAIL] dict content does not match corpus tail\n"); return 0; } printf(" [PASS] full-corpus fallback (%lld bytes)\n", (long long)sz); /* Case 2: capacity < corpus_size -> copy == capacity, dict == last `cap` bytes. */ const size_t cap = 16; uint8_t dict_small[16]; sz = zxc_train_dict(sample_ptrs, sample_sizes, 1, dict_small, cap); if (sz != (int64_t)cap) { printf(" [FAIL] expected %zu bytes (capped), got %lld\n", cap, (long long)sz); return 0; } if (memcmp(dict_small, corpus + sizeof(corpus) - cap, cap) != 0) { printf(" [FAIL] capped dict does not match corpus tail\n"); return 0; } printf(" [PASS] capped tail fallback (%lld bytes)\n", (long long)sz); printf("PASS\n\n"); return 1; } int test_dict_seekable_roundtrip(void) { printf("=== TEST: Dict - seekable API roundtrip ===\n"); const uint8_t dict_content[] = "The quick brown fox jumps over the lazy dog. " "Lorem ipsum dolor sit amet, consectetur adipiscing elit."; const size_t dict_size = sizeof(dict_content) - 1; const size_t src_size = 8192; uint8_t* src = (uint8_t*)malloc(src_size); gen_dict_friendly_data(src, src_size, dict_content, dict_size); size_t comp_bound = (size_t)zxc_compress_bound(src_size); uint8_t* compressed = (uint8_t*)malloc(comp_bound); zxc_compress_opts_t copts = { .level = ZXC_LEVEL_DEFAULT, .checksum_enabled = 1, .seekable = 1, .dict = dict_content, .dict_size = dict_size, }; int64_t comp_size = zxc_compress(src, src_size, compressed, comp_bound, &copts); if (comp_size <= 0) { printf(" [FAIL] compress returned %lld\n", (long long)comp_size); free(src); free(compressed); return 0; } zxc_seekable* s = zxc_seekable_open(compressed, (size_t)comp_size); if (!s) { printf(" [FAIL] seekable_open returned NULL\n"); free(src); free(compressed); return 0; } int rc = zxc_seekable_set_dict(s, dict_content, dict_size, NULL); if (rc != ZXC_OK) { printf(" [FAIL] seekable_set_dict returned %d\n", rc); zxc_seekable_free(s); free(src); free(compressed); return 0; } uint8_t* decompressed = (uint8_t*)malloc(src_size); int64_t dec_size = zxc_seekable_decompress_range(s, decompressed, src_size, 0, src_size); if (dec_size != (int64_t)src_size) { printf(" [FAIL] decompress_range returned %lld, expected %zu\n", (long long)dec_size, src_size); zxc_seekable_free(s); free(src); free(compressed); free(decompressed); return 0; } int ok = (memcmp(src, decompressed, src_size) == 0); zxc_seekable_free(s); free(decompressed); free(src); free(compressed); if (!ok) { printf(" [FAIL] content mismatch\n"); return 0; } printf("PASS\n\n"); return 1; } int test_dict_seekable_mt_roundtrip(void) { printf("=== TEST: Dict - seekable MT roundtrip ===\n"); const uint8_t dict_content[] = "The quick brown fox jumps over the lazy dog. " "Lorem ipsum dolor sit amet, consectetur adipiscing elit."; const size_t dict_size = sizeof(dict_content) - 1; /* Use 32KB of data with 4KB blocks = 8 blocks, enough for MT */ const size_t src_size = 32768; uint8_t* src = (uint8_t*)malloc(src_size); gen_dict_friendly_data(src, src_size, dict_content, dict_size); size_t comp_bound = (size_t)zxc_compress_bound(src_size); uint8_t* compressed = (uint8_t*)malloc(comp_bound); zxc_compress_opts_t copts = { .level = ZXC_LEVEL_DEFAULT, .block_size = 4096, .checksum_enabled = 1, .seekable = 1, .dict = dict_content, .dict_size = dict_size, }; int64_t comp_size = zxc_compress(src, src_size, compressed, comp_bound, &copts); if (comp_size <= 0) { printf(" [FAIL] compress returned %lld\n", (long long)comp_size); free(src); free(compressed); return 0; } zxc_seekable* s = zxc_seekable_open(compressed, (size_t)comp_size); if (!s) { printf(" [FAIL] seekable_open returned NULL\n"); free(src); free(compressed); return 0; } zxc_seekable_set_dict(s, dict_content, dict_size, NULL); /* Full range MT decompress */ uint8_t* decompressed = (uint8_t*)malloc(src_size); int64_t dec_size = zxc_seekable_decompress_range_mt(s, decompressed, src_size, 0, src_size, 4); if (dec_size != (int64_t)src_size) { printf(" [FAIL] decompress_range_mt returned %lld (%s)\n", (long long)dec_size, dec_size < 0 ? zxc_error_name((int)dec_size) : "size mismatch"); zxc_seekable_free(s); free(src); free(compressed); free(decompressed); return 0; } int ok = (memcmp(src, decompressed, src_size) == 0); if (!ok) { for (size_t i = 0; i < src_size; i++) { if (src[i] != decompressed[i]) { printf(" [FAIL] content mismatch at byte %zu\n", i); break; } } } /* Also test a sub-range across block boundaries */ if (ok) { int64_t sub = zxc_seekable_decompress_range_mt(s, decompressed, 8192, 4000, 8192, 4); ok = (sub == 8192 && memcmp(src + 4000, decompressed, 8192) == 0); if (!ok) printf(" [FAIL] sub-range MT mismatch\n"); } zxc_seekable_free(s); free(decompressed); free(src); free(compressed); if (!ok) return 0; printf("PASS\n\n"); return 1; } static const uint8_t k_dict_a[] = "The quick brown fox jumps over the lazy dog. " "Lorem ipsum dolor sit amet, consectetur adipiscing elit."; static const uint8_t k_dict_b[] = "A completely unrelated dictionary payload hashing to a different dict_id value."; // Compress `src` with dict A to a tmpfile, then try to stream-decompress it with // `dec_dict` (NULL = none) and assert the decoder returns `want_err`. static int stream_dict_error_case(const char* label, const uint8_t* dec_dict, size_t dec_dict_size, int64_t want_err) { const size_t src_size = 8192; uint8_t* src = (uint8_t*)malloc(src_size); gen_dict_friendly_data(src, src_size, k_dict_a, sizeof(k_dict_a) - 1); FILE* f_src = tmpfile(); FILE* f_comp = tmpfile(); FILE* f_dec = tmpfile(); int ok = 1; if (!f_src || !f_comp || !f_dec) { printf(" [FAIL] %s: tmpfile() failed\n", label); ok = 0; } if (ok) { fwrite(src, 1, src_size, f_src); rewind(f_src); zxc_compress_opts_t copts = {.level = ZXC_LEVEL_DEFAULT, .checksum_enabled = 1, .dict = k_dict_a, .dict_size = sizeof(k_dict_a) - 1}; if (zxc_stream_compress(f_src, f_comp, &copts) <= 0) { printf(" [FAIL] %s: stream_compress failed\n", label); ok = 0; } } if (ok) { rewind(f_comp); zxc_decompress_opts_t dopts = { .checksum_enabled = 1, .dict = dec_dict, .dict_size = dec_dict_size}; int64_t rc = zxc_stream_decompress(f_comp, f_dec, &dopts); if (rc != want_err) { printf(" [FAIL] %s: expected %s, got %lld (%s)\n", label, zxc_error_name((int)want_err), (long long)rc, zxc_error_name((int)rc)); ok = 0; } } if (f_src) fclose(f_src); if (f_comp) fclose(f_comp); if (f_dec) fclose(f_dec); free(src); return ok; } int test_dict_stream_dict_id_checks(void) { printf("=== TEST: Dict - stream decode rejects missing/wrong dict ===\n"); int ok = stream_dict_error_case("missing dict", NULL, 0, ZXC_ERROR_DICT_REQUIRED); ok &= stream_dict_error_case("wrong dict", k_dict_b, sizeof(k_dict_b) - 1, ZXC_ERROR_DICT_MISMATCH); if (!ok) return 0; printf("PASS\n\n"); return 1; } int test_dict_seekable_dict_id_checks(void) { printf("=== TEST: Dict - seekable decode rejects missing/wrong dict ===\n"); const size_t src_size = 8192; uint8_t* src = (uint8_t*)malloc(src_size); gen_dict_friendly_data(src, src_size, k_dict_a, sizeof(k_dict_a) - 1); size_t comp_bound = (size_t)zxc_compress_bound(src_size); uint8_t* compressed = (uint8_t*)malloc(comp_bound); zxc_compress_opts_t copts = {.level = ZXC_LEVEL_DEFAULT, .checksum_enabled = 1, .seekable = 1, .dict = k_dict_a, .dict_size = sizeof(k_dict_a) - 1}; int64_t comp_size = zxc_compress(src, src_size, compressed, comp_bound, &copts); uint8_t* out = (uint8_t*)malloc(src_size); int ok = 1; if (comp_size <= 0) { printf(" [FAIL] seekable compress failed\n"); ok = 0; } // 1. Wrong dict via set_dict must be rejected up front. if (ok) { zxc_seekable* s = zxc_seekable_open(compressed, (size_t)comp_size); if (!s) { printf(" [FAIL] seekable_open returned NULL\n"); ok = 0; } else { int rc = zxc_seekable_set_dict(s, k_dict_b, sizeof(k_dict_b) - 1, NULL); if (rc != ZXC_ERROR_DICT_MISMATCH) { printf(" [FAIL] set_dict(wrong): expected DICT_MISMATCH, got %d (%s)\n", rc, zxc_error_name(rc)); ok = 0; } zxc_seekable_free(s); } } // 2. Decoding without any dict must be rejected, not silently corrupt // (single-threaded and multi-threaded entry points). if (ok) { zxc_seekable* s = zxc_seekable_open(compressed, (size_t)comp_size); if (!s) { printf(" [FAIL] seekable_open returned NULL\n"); ok = 0; } else { int64_t st = zxc_seekable_decompress_range(s, out, src_size, 0, src_size); int64_t mt = zxc_seekable_decompress_range_mt(s, out, src_size, 0, src_size, 4); if (st != ZXC_ERROR_DICT_REQUIRED || mt != ZXC_ERROR_DICT_REQUIRED) { printf(" [FAIL] no-dict decode: expected DICT_REQUIRED, got st=%lld mt=%lld\n", (long long)st, (long long)mt); ok = 0; } zxc_seekable_free(s); } } // 3. Correct dict still works (guard against over-rejection). if (ok) { zxc_seekable* s = zxc_seekable_open(compressed, (size_t)comp_size); if (s && zxc_seekable_set_dict(s, k_dict_a, sizeof(k_dict_a) - 1, NULL) == ZXC_OK && zxc_seekable_decompress_range(s, out, src_size, 0, src_size) == (int64_t)src_size && memcmp(src, out, src_size) == 0) { // expected } else { printf(" [FAIL] correct dict roundtrip regressed\n"); ok = 0; } zxc_seekable_free(s); } free(out); free(src); free(compressed); if (!ok) return 0; printf("PASS\n\n"); return 1; } /* --------------------------------------------------------------------------- * Shared literal Huffman table (dict_huf) * ------------------------------------------------------------------------- */ /* Deterministic structured-text generator (LCG): dict-trainable patterns with * a skewed literal distribution so the shared table has something to win on. */ static uint32_t huf_lcg(uint32_t* s) { *s = *s * 1664525u + 1013904223u; return *s >> 16; } static size_t gen_structured_sample(uint8_t* buf, size_t cap, uint32_t seed) { static const char* actions[] = {"login", "logout", "refresh", "checkout"}; static const char* services[] = {"auth-svc", "billing-svc", "gateway"}; uint32_t s = seed; size_t n = 0; while (n + 96 < cap) { n += (size_t)snprintf((char*)buf + n, cap - n, "ts=2026-06-10T12:%02u:%02u service=%s action=%s user=%u " "latency_ms=%u status=%u\n", huf_lcg(&s) % 60, huf_lcg(&s) % 60, services[huf_lcg(&s) % 3], actions[huf_lcg(&s) % 4], huf_lcg(&s) % 100000, huf_lcg(&s) % 2000, (huf_lcg(&s) % 5) ? 200u : 500u); } return n; } int test_dict_huf_zxd_roundtrip(void) { printf("=== TEST: Dict - .zxd create (one-call == primitives) / load / corruption ===\n"); enum { NS = 6, SCAP = 16384 }; uint8_t* bufs[NS]; const void* samples[NS]; size_t sizes[NS]; for (int i = 0; i < NS; i++) { bufs[i] = (uint8_t*)malloc(SCAP); sizes[i] = gen_structured_sample(bufs[i], SCAP, 0x1000u + (uint32_t)i); samples[i] = bufs[i]; } int ok = 0; uint8_t huf[ZXC_HUF_TABLE_SIZE]; /* Same capacity as zxc_dict_train uses internally, so the primitive path * trains identical content and the byte-identity comparison is exact. */ uint8_t* dict_buf = (uint8_t*)malloc(ZXC_DICT_SIZE_MAX); uint8_t* zxd = NULL; uint8_t* zxd_one = NULL; do { /* Primitive 3-step pipeline. */ const int64_t dsz = zxc_train_dict(samples, sizes, NS, dict_buf, ZXC_DICT_SIZE_MAX); if (dsz <= 0) { printf(" [FAIL] train_dict: %lld\n", (long long)dsz); break; } const int hrc = zxc_train_dict_huf(samples, sizes, NS, dict_buf, (size_t)dsz, huf); if (hrc != ZXC_OK) { printf(" [FAIL] train_dict_huf: %s\n", zxc_error_name(hrc)); break; } const size_t bound = zxc_dict_save_bound((size_t)dsz); zxd = (uint8_t*)malloc(bound); const int64_t zsz = zxc_dict_save(dict_buf, (size_t)dsz, huf, zxd, bound); if (zsz <= 0) { printf(" [FAIL] dict_save: %lld\n", (long long)zsz); break; } /* One-call creator must produce byte-identical .zxd output (the * trainers are deterministic). */ const size_t one_bound = zxc_dict_save_bound(ZXC_DICT_SIZE_MAX); zxd_one = (uint8_t*)malloc(one_bound); const int64_t one_sz = zxc_dict_train(samples, sizes, NS, zxd_one, one_bound); if (one_sz != zsz || memcmp(zxd_one, zxd, (size_t)zsz) != 0) { printf(" [FAIL] zxc_dict_train (%lld B) != 3-step pipeline (%lld B)\n", (long long)one_sz, (long long)zsz); break; } /* Folded load yields content + table + id in one call; the table must * match both the trained bytes and the standalone accessor. */ const void* content = NULL; size_t csz = 0; const void* table = NULL; uint32_t id = 0; if (zxc_dict_load(zxd, (size_t)zsz, &content, &csz, &table, &id) != ZXC_OK || csz != (size_t)dsz || memcmp(content, dict_buf, csz) != 0) { printf(" [FAIL] load of table-carrying .zxd\n"); break; } if (!table || table != zxc_dict_huf(zxd, (size_t)zsz) || memcmp(table, huf, ZXC_HUF_TABLE_SIZE) != 0) { printf(" [FAIL] dict_load table out-param / zxc_dict_huf mismatch\n"); break; } /* The id must bind the table: different from the content-only id. */ if (id == zxc_dict_id(dict_buf, (size_t)dsz, NULL)) { printf(" [FAIL] dict_id does not cover the table\n"); break; } /* The format requires the table: NULL lengths must be refused. */ uint8_t* zxd2 = (uint8_t*)malloc(bound); const int64_t z2 = zxc_dict_save(dict_buf, (size_t)dsz, NULL, zxd2, bound); free(zxd2); if (z2 != ZXC_ERROR_NULL_INPUT) { printf(" [FAIL] table-less save accepted: %lld\n", (long long)z2); break; } /* Corrupting the stored table must break the covering id. */ zxd[zsz - 1] ^= 0xA5; if (zxc_dict_load(zxd, (size_t)zsz, &content, &csz, NULL, &id) != ZXC_ERROR_BAD_CHECKSUM) { printf(" [FAIL] corrupted table not rejected\n"); break; } ok = 1; } while (0); free(zxd); free(zxd_one); free(dict_buf); for (int i = 0; i < NS; i++) free(bufs[i]); if (!ok) return 0; printf("PASS\n\n"); return 1; } int test_dict_huf_table_roundtrip(void) { printf("=== TEST: Dict - shared-table compression roundtrip + id binding ===\n"); enum { NS = 6, SCAP = 16384, HCAP = 32768 }; uint8_t* bufs[NS]; const void* samples[NS]; size_t sizes[NS]; for (int i = 0; i < NS; i++) { bufs[i] = (uint8_t*)malloc(SCAP); sizes[i] = gen_structured_sample(bufs[i], SCAP, 0x2000u + (uint32_t)i); samples[i] = bufs[i]; } uint8_t* heldout = (uint8_t*)malloc(HCAP); const size_t hsz = gen_structured_sample(heldout, HCAP, 0xBEEFu); int ok = 0; uint8_t dict_buf[8192]; uint8_t huf[ZXC_HUF_TABLE_SIZE]; uint8_t *c1 = NULL, *c2 = NULL, *out = NULL; do { const int64_t dsz = zxc_train_dict(samples, sizes, NS, dict_buf, sizeof(dict_buf)); if (dsz <= 0 || zxc_train_dict_huf(samples, sizes, NS, dict_buf, (size_t)dsz, huf) != ZXC_OK) { printf(" [FAIL] training\n"); break; } const size_t cap = (size_t)zxc_compress_bound(hsz); c1 = (uint8_t*)malloc(cap); c2 = (uint8_t*)malloc(cap); out = (uint8_t*)malloc(hsz + 64); zxc_compress_opts_t o1 = { .level = 6, .block_size = 4096, .dict = dict_buf, .dict_size = (size_t)dsz}; zxc_compress_opts_t o2 = {.level = 6, .block_size = 4096, .dict = dict_buf, .dict_size = (size_t)dsz, .dict_huf = huf}; const int64_t s1 = zxc_compress(heldout, hsz, c1, cap, &o1); const int64_t s2 = zxc_compress(heldout, hsz, c2, cap, &o2); if (s1 <= 0 || s2 <= 0) { printf(" [FAIL] compress: %lld / %lld\n", (long long)s1, (long long)s2); break; } /* Exact size accounting makes the shared table a strict improvement * on this skewed corpus; never larger by construction. */ if (s2 > s1) { printf(" [FAIL] shared table grew the archive: %lld > %lld\n", (long long)s2, (long long)s1); break; } zxc_decompress_opts_t d2 = { .dict = dict_buf, .dict_size = (size_t)dsz, .dict_huf = huf}; const int64_t r2 = zxc_decompress(c2, (size_t)s2, out, hsz + 64, &d2); if (r2 != (int64_t)hsz || memcmp(out, heldout, hsz) != 0) { printf(" [FAIL] roundtrip with table: %lld\n", (long long)r2); break; } /* id binding, both directions: table archive without the table, and * table-less archive with it, must both be rejected as MISMATCH. */ zxc_decompress_opts_t d_no = {.dict = dict_buf, .dict_size = (size_t)dsz}; if (zxc_decompress(c2, (size_t)s2, out, hsz + 64, &d_no) != ZXC_ERROR_DICT_MISMATCH) { printf(" [FAIL] table archive accepted without table\n"); break; } if (zxc_decompress(c1, (size_t)s1, out, hsz + 64, &d2) != ZXC_ERROR_DICT_MISMATCH) { printf(" [FAIL] table-less archive accepted with table\n"); break; } ok = 1; } while (0); free(c1); free(c2); free(out); free(heldout); for (int i = 0; i < NS; i++) free(bufs[i]); if (!ok) return 0; printf("PASS\n\n"); return 1; } /* A degenerate training corpus (a single repeated byte, a zero-filled region) * is matched in full against the trained dict, so there are no post-LZ literals * to histogram. Training must emit a legitimately empty (all-zero) shared table * rather than failing, and that .zxd must remain usable for a compress/ * decompress roundtrip (the empty table is treated as "no shared table"). */ int test_dict_huf_degenerate_corpus(void) { printf("=== TEST: Dict - degenerate corpus -> empty shared table ===\n"); enum { SAMPLE_SIZE = 4096 }; const uint8_t fills[2] = {(uint8_t)'A', 0x00}; uint8_t* sample = (uint8_t*)malloc(SAMPLE_SIZE); uint8_t* comp = NULL; uint8_t* out = NULL; int ok = 1; for (size_t f = 0; f < sizeof(fills) && ok; f++) { memset(sample, fills[f], SAMPLE_SIZE); const void* samples[1] = {sample}; const size_t sizes[1] = {SAMPLE_SIZE}; uint8_t dict_buf[8192]; uint8_t huf[ZXC_HUF_TABLE_SIZE]; ok = 0; do { const int64_t dsz = zxc_train_dict(samples, sizes, 1, dict_buf, sizeof(dict_buf)); if (dsz <= 0) { printf(" [FAIL] fill 0x%02X: train_dict: %lld\n", fills[f], (long long)dsz); break; } /* Previously returned ZXC_ERROR_CORRUPT_DATA on an empty histogram. */ const int hrc = zxc_train_dict_huf(samples, sizes, 1, dict_buf, (size_t)dsz, huf); if (hrc != ZXC_OK) { printf(" [FAIL] fill 0x%02X: train_dict_huf on empty histogram: %s\n", fills[f], zxc_error_name(hrc)); break; } /* It must be the empty (all-zero) table, not a built code. */ int empty = 1; for (int i = 0; i < ZXC_HUF_TABLE_SIZE; i++) { if (huf[i]) { empty = 0; break; } } if (!empty) { printf(" [FAIL] fill 0x%02X: expected an all-zero shared table\n", fills[f]); break; } /* The empty-table .zxd must serialize and load cleanly. */ const size_t bound = zxc_dict_save_bound((size_t)dsz); uint8_t* zxd = (uint8_t*)malloc(bound); const int64_t zsz = zxc_dict_save(dict_buf, (size_t)dsz, huf, zxd, bound); const void* content = NULL; size_t csz = 0; const void* table = NULL; uint32_t id = 0; const int lrc = (zsz > 0) ? zxc_dict_load(zxd, (size_t)zsz, &content, &csz, &table, &id) : (int)zsz; free(zxd); if (zsz <= 0 || lrc != ZXC_OK) { printf(" [FAIL] fill 0x%02X: empty-table .zxd save=%lld load=%d\n", fills[f], (long long)zsz, lrc); break; } /* Volet 2: the empty table must be usable end-to-end. */ const size_t cap = (size_t)zxc_compress_bound(SAMPLE_SIZE); comp = (uint8_t*)malloc(cap); out = (uint8_t*)malloc(SAMPLE_SIZE + 64); zxc_compress_opts_t co = {.level = 6, .block_size = 4096, .dict = dict_buf, .dict_size = (size_t)dsz, .dict_huf = huf}; const int64_t csize = zxc_compress(sample, SAMPLE_SIZE, comp, cap, &co); if (csize <= 0) { printf(" [FAIL] fill 0x%02X: compress with empty-table dict: %lld\n", fills[f], (long long)csize); break; } zxc_decompress_opts_t deo = { .dict = dict_buf, .dict_size = (size_t)dsz, .dict_huf = huf}; const int64_t dsize = zxc_decompress(comp, (size_t)csize, out, SAMPLE_SIZE + 64, &deo); if (dsize != (int64_t)SAMPLE_SIZE || memcmp(out, sample, SAMPLE_SIZE) != 0) { printf(" [FAIL] fill 0x%02X: roundtrip with empty-table dict: %lld\n", fills[f], (long long)dsize); break; } ok = 1; } while (0); free(comp); comp = NULL; free(out); out = NULL; } free(sample); if (!ok) return 0; printf("PASS\n\n"); return 1; } zxc-0.13.0/tests/test_format.c000066400000000000000000000534631522536370400162560ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ #include "test_common.h" /* Round-trip the Huffman codec over a few representative literal distributions. */ static int huf_roundtrip_case(const char* label, const uint8_t* literals, size_t n) { uint32_t freq[ZXC_HUF_NUM_SYMBOLS] = {0}; for (size_t i = 0; i < n; i++) freq[literals[i]]++; uint8_t code_len[ZXC_HUF_NUM_SYMBOLS]; if (zxc_huf_build_code_lengths(freq, code_len, NULL, ZXC_HUF_MAX_CODE_LEN_DENSITY) != ZXC_OK) { printf("Failed [%s]: build_code_lengths\n", label); return 0; } /* Validate the lengths-limit invariant. */ for (int i = 0; i < ZXC_HUF_NUM_SYMBOLS; i++) { if (code_len[i] > ZXC_HUF_MAX_CODE_LEN_ULTRA) { printf("Failed [%s]: code_len[%d] = %d > %d\n", label, i, code_len[i], ZXC_HUF_MAX_CODE_LEN_ULTRA); return 0; } } /* Worst-case payload size: 128-byte header + packed codes + per-node pad. */ const size_t cap = ZXC_HUF_TABLE_SIZE + 2 * n + 4096; uint8_t* enc = (uint8_t*)malloc(cap); uint8_t* dec = (uint8_t*)malloc(n + ZXC_PAD_SIZE); uint8_t* scr = (uint8_t*)malloc(n + ZXC_PIVCO_SCRATCH_PAD); int ok = 0; if (!enc || !dec || !scr) { printf("Failed [%s]: alloc\n", label); goto done; } const int written = zxc_huf_encode_section(literals, n, freq, code_len, enc, cap); if (written < 0) { printf("Failed [%s]: encode_section -> %d\n", label, written); goto done; } /* The Lagrangian selector prices candidates with zxc_huf_calc_size; it must * predict the EXACT encoded size (with the 128-byte lengths header). */ const size_t est = zxc_huf_calc_size(freq, code_len, 1); if (est != (size_t)written) { printf("Failed [%s]: calc_size %zu != encoded %d\n", label, est, written); goto done; } const int rc = zxc_huf_decode_section(enc, (size_t)written, dec, n, scr); if (rc != ZXC_OK) { printf("Failed [%s]: decode_section -> %d\n", label, rc); goto done; } if (memcmp(literals, dec, n) != 0) { printf("Failed [%s]: roundtrip mismatch\n", label); goto done; } printf(" [PASS] %s (n=%zu, encoded=%d B, ratio=%.1f%%)\n", label, n, written, 100.0 * (double)written / (double)n); ok = 1; done: free(enc); free(dec); free(scr); return ok; } int test_huffman_codec() { printf("=== TEST: Unit - Huffman Codec (build/encode/decode roundtrip) ===\n"); const size_t N = 8192; uint8_t* buf = (uint8_t*)malloc(N); if (!buf) return 0; /* Case 1: heavily skewed (90% one byte, 10% noise). */ for (size_t i = 0; i < N; i++) buf[i] = (zxc_test_rand() % 10 == 0) ? (uint8_t)(zxc_test_rand() & 0xFF) : 'A'; if (!huf_roundtrip_case("Skewed (90% 'A')", buf, N)) { free(buf); return 0; } /* Case 2: uniform random - Huffman should be near no-op (~1 byte/sym). */ for (size_t i = 0; i < N; i++) buf[i] = (uint8_t)(zxc_test_rand() & 0xFF); if (!huf_roundtrip_case("Uniform random", buf, N)) { free(buf); return 0; } /* Case 3: two-symbol alphabet - best case, ~1 bit/symbol. */ for (size_t i = 0; i < N; i++) buf[i] = (zxc_test_rand() & 1) ? 'X' : 'Y'; if (!huf_roundtrip_case("Two-symbol alphabet", buf, N)) { free(buf); return 0; } /* Case 4: single-symbol - degenerate but must still roundtrip. */ for (size_t i = 0; i < N; i++) buf[i] = 'Z'; if (!huf_roundtrip_case("Single-symbol", buf, N)) { free(buf); return 0; } /* Case 5: small block (just above the min-literals threshold). */ for (size_t i = 0; i < ZXC_HUF_MIN_LITERALS; i++) buf[i] = (zxc_test_rand() % 4 == 0) ? (uint8_t)(zxc_test_rand() & 0xFF) : 'k'; if (!huf_roundtrip_case("Small block at threshold", buf, ZXC_HUF_MIN_LITERALS)) { free(buf); return 0; } free(buf); printf("PASS\n\n"); return 1; } /* Round-trip the shared-table (dictionary) Huffman section codec: encode with * external code lengths and NO 128-byte header, decode through a prebuilt * table -- the enc_lit == 3 wire path (FORMAT.md Sec 5.2.2). */ static int huf_dict_roundtrip_case(const char* label, const uint8_t* literals, size_t n) { uint32_t freq[ZXC_HUF_NUM_SYMBOLS] = {0}; for (size_t i = 0; i < n; i++) freq[literals[i]]++; uint8_t code_len[ZXC_HUF_NUM_SYMBOLS]; if (zxc_huf_build_code_lengths(freq, code_len, NULL, ZXC_HUF_MAX_CODE_LEN_DENSITY) != ZXC_OK) { printf("Failed [%s]: build_code_lengths\n", label); return 0; } /* Round the lengths through the 128-byte packed form, as a .zxd does. */ uint8_t packed[ZXC_HUF_TABLE_SIZE]; uint8_t unpacked[ZXC_HUF_NUM_SYMBOLS]; zxc_huf_pack_lengths(code_len, packed); if (zxc_huf_unpack_lengths(packed, unpacked) != ZXC_OK || memcmp(code_len, unpacked, sizeof(code_len)) != 0) { printf("Failed [%s]: pack/unpack lengths roundtrip\n", label); return 0; } /* Tree-at-attach: prebuild the shared table's tree/codes/decoder tables * once, as zxc_cctx_attach_dict_huf does; the dict codec entry points * take them. */ zxc_pivco_tree_t tree; zxc_pivco_decode_aux_t aux; uint32_t codes[ZXC_HUF_NUM_SYMBOLS]; uint8_t tree_len[ZXC_HUF_NUM_SYMBOLS]; if (zxc_huf_dict_tree_build(packed, &tree, codes, tree_len, &aux) != ZXC_OK || memcmp(tree_len, code_len, sizeof(tree_len)) != 0) { printf("Failed [%s]: dict_tree_build\n", label); return 0; } const size_t cap = ZXC_HUF_TABLE_SIZE + 2 * n + 4096; uint8_t* enc = (uint8_t*)malloc(cap); uint8_t* enc_blk = (uint8_t*)malloc(cap); uint8_t* dec = (uint8_t*)malloc(n + ZXC_PAD_SIZE); uint8_t* scr = (uint8_t*)malloc(n + ZXC_PIVCO_SCRATCH_PAD); if (!enc || !enc_blk || !dec || !scr) { printf("Failed [%s]: alloc\n", label); goto fail; } const int written = zxc_huf_encode_section_dict(literals, n, freq, code_len, &tree, codes, enc, cap); if (written < 0) { printf("Failed [%s]: encode_section_dict -> %d\n", label, written); goto fail; } /* Header-less variant of the size estimator (with_header = 0) must match. */ if (zxc_huf_calc_size(freq, code_len, 0) != (size_t)written) { printf("Failed [%s]: dict calc_size != encoded\n", label); goto fail; } /* Same lengths, same bitstreams: the dict section must be exactly the * per-block section minus its 128-byte lengths header. */ const int written_blk = zxc_huf_encode_section(literals, n, freq, code_len, enc_blk, cap); if (written_blk != written + (int)ZXC_HUF_TABLE_SIZE || memcmp(enc, enc_blk + ZXC_HUF_TABLE_SIZE, (size_t)written) != 0) { printf("Failed [%s]: dict section != per-block section minus header\n", label); goto fail; } if (zxc_huf_decode_section_dict(enc, (size_t)written, dec, n, &tree, &aux, scr) != ZXC_OK || memcmp(literals, dec, n) != 0) { printf("Failed [%s]: decode_section_dict roundtrip mismatch\n", label); goto fail; } /* Error paths: truncated payload, undersized dst_cap. */ if (zxc_huf_decode_section_dict(enc, 0, dec, n, &tree, &aux, scr) == ZXC_OK) { printf("Failed [%s]: truncated payload accepted\n", label); goto fail; } if (zxc_huf_encode_section_dict(literals, n, freq, code_len, &tree, codes, enc, 4) != ZXC_ERROR_DST_TOO_SMALL) { printf("Failed [%s]: undersized dst_cap not rejected\n", label); goto fail; } free(enc); free(enc_blk); free(dec); free(scr); printf(" [PASS] %s (n=%zu, encoded=%d B, header saved=%d B)\n", label, n, written, (int)ZXC_HUF_TABLE_SIZE); return 1; fail: free(enc); free(enc_blk); free(dec); free(scr); return 0; } int test_huffman_codec_dict() { printf("=== TEST: Unit - Huffman Codec, shared dictionary table (enc_lit == 3) ===\n"); const size_t N = 8192; uint8_t* buf = (uint8_t*)malloc(N); if (!buf) return 0; /* Skewed text-like distribution: the shared-table sweet spot. */ for (size_t i = 0; i < N; i++) buf[i] = (zxc_test_rand() % 10 == 0) ? (uint8_t)(zxc_test_rand() & 0x7F) : 'A'; if (!huf_dict_roundtrip_case("Skewed (90% 'A')", buf, N)) { free(buf); return 0; } /* Two-symbol alphabet: ~1 bit/symbol, headerless gain is maximal. */ for (size_t i = 0; i < N; i++) buf[i] = (zxc_test_rand() & 1) ? 'X' : 'Y'; if (!huf_dict_roundtrip_case("Two-symbol alphabet", buf, N)) { free(buf); return 0; } /* Small block: where the 128-byte header would dominate per-block cost. */ for (size_t i = 0; i < ZXC_HUF_MIN_LITERALS; i++) buf[i] = (zxc_test_rand() % 4 == 0) ? (uint8_t)('a' + (zxc_test_rand() % 26)) : 'k'; if (!huf_dict_roundtrip_case("Small block at threshold", buf, ZXC_HUF_MIN_LITERALS)) { free(buf); return 0; } /* A literal with NO code in the shared table must be refused by the * encoder (the validity check that triggers the per-block fallback). */ { uint32_t freq[ZXC_HUF_NUM_SYMBOLS] = {0}; for (size_t i = 0; i < 256; i++) buf[i] = (zxc_test_rand() & 1) ? 'X' : 'Y'; for (size_t i = 0; i < 256; i++) freq[buf[i]]++; uint8_t code_len[ZXC_HUF_NUM_SYMBOLS]; if (zxc_huf_build_code_lengths(freq, code_len, NULL, ZXC_HUF_MAX_CODE_LEN_DENSITY) != ZXC_OK) { free(buf); return 0; } buf[100] = '!'; /* unseen in training: no code assigned */ freq['!']++; /* keep the histogram in sync with the mutated buffer */ uint8_t enc[1024]; zxc_pivco_tree_t tree; zxc_pivco_decode_aux_t aux; uint32_t codes[ZXC_HUF_NUM_SYMBOLS]; uint8_t packed[ZXC_HUF_TABLE_SIZE]; uint8_t tree_len[ZXC_HUF_NUM_SYMBOLS]; zxc_huf_pack_lengths(code_len, packed); if (zxc_huf_dict_tree_build(packed, &tree, codes, tree_len, &aux) != ZXC_OK) { printf("Failed: dict_tree_build (code-less literal case)\n"); free(buf); return 0; } if (zxc_huf_encode_section_dict(buf, 256, freq, code_len, &tree, codes, enc, sizeof(enc)) != ZXC_ERROR_CORRUPT_DATA) { printf("Failed: code-less literal not rejected by encode_section_dict\n"); free(buf); return 0; } printf(" [PASS] code-less literal rejected (per-block fallback trigger)\n"); } free(buf); printf("PASS\n\n"); return 1; } /* Regression: a degenerate single-symbol table must carry code_len == 1 * (FORMAT.md, decoder validation requirements). The v6 decoder rejected a * lone symbol with a longer length; the v7 rewrite briefly accepted it. */ int test_huffman_single_symbol_validation() { printf("=== TEST: Unit - Huffman single-symbol table validation ===\n"); zxc_pivco_tree_t tree; zxc_pivco_decode_aux_t aux; uint32_t codes[ZXC_HUF_NUM_SYMBOLS]; uint8_t tree_len[ZXC_HUF_NUM_SYMBOLS]; uint8_t code_len[ZXC_HUF_NUM_SYMBOLS]; uint8_t packed[ZXC_HUF_TABLE_SIZE]; /* Lone symbol with code length 1: the only legal degenerate form. */ memset(code_len, 0, sizeof(code_len)); code_len['A'] = 1; zxc_huf_pack_lengths(code_len, packed); if (zxc_huf_dict_tree_build(packed, &tree, codes, tree_len, &aux) != ZXC_OK) { printf("Failed: single symbol with code_len=1 must be accepted\n"); return 0; } printf(" [PASS] single symbol, code_len=1 accepted\n"); /* Lone symbol with any longer length is declared corrupt by the format. */ for (int len = 2; len <= ZXC_HUF_MAX_CODE_LEN_ULTRA; len++) { memset(code_len, 0, sizeof(code_len)); code_len['A'] = (uint8_t)len; zxc_huf_pack_lengths(code_len, packed); if (zxc_huf_dict_tree_build(packed, &tree, codes, tree_len, &aux) != ZXC_ERROR_CORRUPT_DATA) { printf("Failed: single symbol with code_len=%d must be rejected\n", len); return 0; } } printf(" [PASS] single symbol, code_len 2..%d rejected\n", ZXC_HUF_MAX_CODE_LEN_ULTRA); printf("PASS\n\n"); return 1; } // Checks that the EOF block is correctly appended int test_eof_block_structure() { printf("=== TEST: Unit - EOF Block Structure ===\n"); const char* input = "test"; size_t src_size = 4; size_t max_dst_size = (size_t)zxc_compress_bound(src_size); uint8_t* compressed = malloc(max_dst_size); if (!compressed) return 0; zxc_compress_opts_t _co26 = {.level = 1, .checksum_enabled = 0}; int64_t comp_size = zxc_compress(input, src_size, compressed, max_dst_size, &_co26); if (comp_size <= 0) { printf("Failed: Compression returned 0\n"); free(compressed); return 0; } // Validating Footer and EOF Block // Total Overhead: 12 bytes (Footer) + 8 bytes (EOF Header) = 20 bytes if (comp_size < 20) { printf("Failed: Compressed size too small for Footer + EOF (%lld)\n", (long long)comp_size); free(compressed); return 0; } // 1. Verify 12-byte Footer // Structure: [SrcSize (8)] + [Hash (4)] const uint8_t* footer_ptr = compressed + comp_size - 12; uint32_t f_src_low = zxc_le32(footer_ptr); // Should be 4 uint32_t f_src_high = zxc_le32(footer_ptr + 4); // Should be 0 uint32_t f_hash = zxc_le32(footer_ptr + 8); // Should be 0 (checksum disabled) if (f_src_low != 4 || f_src_high != 0 || f_hash != 0) { printf("Failed: Footer mismatch. Src: %u, Hash: %u\n", f_src_low, f_hash); free(compressed); return 0; } // 2. Verify EOF Block Header (8 bytes) // Should be immediately before the footer const uint8_t* eof_ptr = compressed + comp_size - 20; uint8_t expected[8] = {0xFF, 0, 0, 0, 0, 0, 0, 0}; expected[7] = zxc_hash8(expected); if (memcmp(eof_ptr, expected, 8) != 0) { printf( "Failed: EOF block mismatch.\nExpected: %02X %02X %02X ... %02X\nGot: %02X %02X " "%02X ... %02X\n", expected[0], expected[1], expected[2], expected[7], eof_ptr[0], eof_ptr[1], eof_ptr[2], eof_ptr[7]); free(compressed); return 0; } printf("PASS\n\n"); free(compressed); return 1; } int test_header_checksum() { printf("Running test_header_checksum...\n"); uint8_t header_buf[ZXC_BLOCK_HEADER_SIZE]; zxc_block_header_t bh_in = {.block_type = ZXC_BLOCK_GLO, .block_flags = 0, .reserved = 0, .header_crc = 0, .comp_size = 1024}; // 1. Write Header if (zxc_write_block_header(header_buf, ZXC_BLOCK_HEADER_SIZE, &bh_in) != ZXC_BLOCK_HEADER_SIZE) { printf(" [FAIL] zxc_write_block_header failed\n"); return 0; } // Verify manually that checksum byte is non-zero (highly likely) if (header_buf[7] == 0) { // It's technically possible but very unlikely with a good hash printf(" [WARN] Checksum is 0 (unlikely but possible)\n"); } // 2. Read Header (Valid) zxc_block_header_t bh_out; if (zxc_read_block_header(header_buf, ZXC_BLOCK_HEADER_SIZE, &bh_out) != 0) { printf(" [FAIL] zxc_read_block_header failed on valid input\n"); return 0; } if (bh_out.block_type != bh_in.block_type || bh_out.comp_size != bh_in.comp_size || bh_out.header_crc != header_buf[7]) { printf(" [FAIL] Read data mismatch\n"); return 0; } // 3. Corrupt Header Checksum uint8_t original_crc = header_buf[7]; header_buf[7] = ~original_crc; // Flip bits if (zxc_read_block_header(header_buf, ZXC_BLOCK_HEADER_SIZE, &bh_out) == 0) { printf(" [FAIL] zxc_read_block_header should have failed on corrupted CRC\n"); return 0; } header_buf[7] = original_crc; // Restore // 4. Corrupt Header Content header_buf[0] = ZXC_BLOCK_RAW; // Change type if (zxc_read_block_header(header_buf, ZXC_BLOCK_HEADER_SIZE, &bh_out) == 0) { printf(" [FAIL] zxc_read_block_header should have failed on corrupted content\n"); return 0; } printf("PASS\n\n"); return 1; } // 5. Test Global Checksum Order Sensitivity // Ensures that swapping two blocks (even if valid individually) triggers a global checksum failure. int test_global_checksum_order() { printf("TEST: Global Checksum Order Sensitivity... "); // 1. Create input data withDISTINCT patterns for 2 blocks (so blocks are different) // ZXC_BLOCK_SIZE_DEFAULT is 256KB. We need > 256KB. Let's use 600KB. size_t input_sz = 600 * 1024; uint8_t* val_buf = malloc(input_sz); if (!val_buf) return 0; // Fill Block 1 with 0xAA, Block 2 with 0xBB, Block 3 with 0xCC... memset(val_buf, 0xAA, 256 * 1024); memset(val_buf + 256 * 1024, 0xBB, 256 * 1024); memset(val_buf + 512 * 1024, 0xCC, input_sz - 512 * 1024); FILE* f_in = tmpfile(); FILE* f_comp = tmpfile(); fwrite(val_buf, 1, input_sz, f_in); rewind(f_in); // 2. Compress with Checksum Enabled zxc_compress_opts_t _sco27 = {.n_threads = 1, .level = 1, .checksum_enabled = 1}; zxc_stream_compress(f_in, f_comp, &_sco27); // 3. Read compressed data to memory long comp_sz = ftell(f_comp); rewind(f_comp); uint8_t* comp_buf = malloc((size_t)comp_sz); if (fread(comp_buf, 1, comp_sz, f_comp) != (size_t)comp_sz) { printf("[FAIL] Failed to read compressed data\n"); free(val_buf); free(comp_buf); fclose(f_in); fclose(f_comp); return 0; } // 4. Parse Blocks to identify Block 1 and Block 2 // File Header: ZXC_FILE_HEADER_SIZE bytes size_t off1 = ZXC_FILE_HEADER_SIZE; // Parse Block 1 Header zxc_block_header_t bh1; zxc_read_block_header(comp_buf + off1, ZXC_BLOCK_HEADER_SIZE, &bh1); size_t len1 = ZXC_BLOCK_HEADER_SIZE + bh1.comp_size + ZXC_BLOCK_CHECKSUM_SIZE; size_t off2 = off1 + len1; // Parse Block 2 Header zxc_block_header_t bh2; zxc_read_block_header(comp_buf + off2, ZXC_BLOCK_HEADER_SIZE, &bh2); size_t len2 = ZXC_BLOCK_HEADER_SIZE + bh2.comp_size + ZXC_BLOCK_CHECKSUM_SIZE; // Ensure we have at least 2 full blocks + EOF + Global Checksum if (off2 + len2 > (size_t)comp_sz) { printf("[FAIL] Compressed size too small for test\n"); free(val_buf); free(comp_buf); fclose(f_in); fclose(f_comp); return 0; } // 5. Swap Block 1 and Block 2 // To safely swap, we need a new buffer uint8_t* swapped_buf = malloc((size_t)comp_sz); // Copy File Header // Copy File Header memcpy(swapped_buf, comp_buf, ZXC_FILE_HEADER_SIZE); size_t w_off = ZXC_FILE_HEADER_SIZE; // Write Block 2 first memcpy(swapped_buf + w_off, comp_buf + off2, len2); w_off += len2; // Write Block 1 second memcpy(swapped_buf + w_off, comp_buf + off1, len1); w_off += len1; // Write remaining data (EOF block + Global Checksum) size_t remaining_off = off2 + len2; size_t remaining_len = comp_sz - remaining_off; memcpy(swapped_buf + w_off, comp_buf + remaining_off, remaining_len); // 6. Write to File for Decompression FILE* f_bad = tmpfile(); fwrite(swapped_buf, 1, (size_t)comp_sz, f_bad); rewind(f_bad); // 7. Attempt Decompression FILE* f_out = tmpfile(); zxc_decompress_opts_t _sdo28 = {.n_threads = 1, .checksum_enabled = 1}; int64_t res = zxc_stream_decompress(f_bad, f_out, &_sdo28); fclose(f_in); fclose(f_comp); fclose(f_bad); fclose(f_out); free(val_buf); free(comp_buf); free(swapped_buf); if (res >= 0) { printf(" [FAIL] zxc_stream_decompress unexpectedly succeeded on swapped blocks\n"); return 0; } printf("PASS\n\n"); return 1; } /* Builds a header with the given chunk-size code, fixes the CRC16, and returns * zxc_read_file_header's verdict (block_size out via *bs). */ static int chunk_code_verdict(uint8_t code, size_t *bs) { uint8_t hdr[ZXC_FILE_HEADER_SIZE]; memset(hdr, 0, sizeof(hdr)); hdr[0] = 0xF5; hdr[1] = 0x2E; hdr[2] = 0xB0; hdr[3] = 0x9C; // magic (LE) hdr[4] = ZXC_FILE_FORMAT_VERSION; // version hdr[5] = code; // chunk-size code hdr[6] = 0; // flags: no checksum uint16_t crc = zxc_hash16(hdr); // bytes 14-15 already 0 hdr[14] = (uint8_t)(crc & 0xFF); hdr[15] = (uint8_t)(crc >> 8); int has_checksum = -1; *bs = 0; return zxc_read_file_header(hdr, sizeof(hdr), bs, &has_checksum, NULL); } int test_chunk_size_code() { printf("=== TEST: Chunk-size code validation ===\n"); size_t bs = 0; // Valid exponent code 19 -> 512 KB. int rc = chunk_code_verdict(19, &bs); if (rc != ZXC_OK || bs != 512 * 1024) { printf(" [FAIL] code 19: rc=%d (%s), block_size=%zu\n", rc, zxc_error_name(rc), bs); return 0; } printf(" [PASS] Code 19 -> 512 KB\n"); // Legacy code 64 was accepted in v5 (mapped to 256 KB) but is removed in v6. rc = chunk_code_verdict(64, &bs); if (rc != ZXC_ERROR_BAD_BLOCK_SIZE) { printf(" [FAIL] legacy code 64: expected %d, got %d\n", ZXC_ERROR_BAD_BLOCK_SIZE, rc); return 0; } printf(" [PASS] Legacy code 64 rejected (ZXC_ERROR_BAD_BLOCK_SIZE)\n"); // Out-of-range code 99 -> rejected. rc = chunk_code_verdict(99, &bs); if (rc != ZXC_ERROR_BAD_BLOCK_SIZE) { printf(" [FAIL] code 99: expected %d, got %d\n", ZXC_ERROR_BAD_BLOCK_SIZE, rc); return 0; } printf(" [PASS] Code 99 rejected\n"); printf("PASS\n\n"); return 1; } zxc-0.13.0/tests/test_main.c000066400000000000000000000207731522536370400157100ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ #include "test_common.h" typedef int (*test_fn_t)(void); typedef struct { const char* name; test_fn_t fn; } test_entry_t; static const test_entry_t g_tests[] = { /* --- Streaming round-trip coverage (patterns x sizes x levels x checksum) --- */ TEST_CASE(test_roundtrip_raw_random), TEST_CASE(test_roundtrip_ghi_text), TEST_CASE(test_roundtrip_glo_text), TEST_CASE(test_roundtrip_num_seq), TEST_CASE(test_roundtrip_num_zero), TEST_CASE(test_roundtrip_num_small), TEST_CASE(test_roundtrip_num_large), TEST_CASE(test_roundtrip_small_50), TEST_CASE(test_roundtrip_empty), TEST_CASE(test_roundtrip_1byte), TEST_CASE(test_roundtrip_1byte_crc), TEST_CASE(test_roundtrip_large_15mb_lz), TEST_CASE(test_roundtrip_large_15mb_num), TEST_CASE(test_roundtrip_checksum_off), TEST_CASE(test_roundtrip_checksum_on), TEST_CASE(test_roundtrip_level1), TEST_CASE(test_roundtrip_level2), TEST_CASE(test_roundtrip_level3), TEST_CASE(test_roundtrip_level4), TEST_CASE(test_roundtrip_level5), TEST_CASE(test_roundtrip_level6), TEST_CASE(test_roundtrip_binary), TEST_CASE(test_roundtrip_binary_crc), TEST_CASE(test_roundtrip_binary_small), TEST_CASE(test_roundtrip_offset8_small), TEST_CASE(test_roundtrip_offset8_lvl5), TEST_CASE(test_roundtrip_offset16_large), TEST_CASE(test_roundtrip_offset16_lvl5), TEST_CASE(test_roundtrip_offset_mixed), /* --- Buffer API --- */ TEST_CASE(test_buffer_api), TEST_CASE(test_buffer_api_scratch_buf), TEST_CASE(test_buffer_error_codes), TEST_CASE(test_decompress_inplace), TEST_CASE(test_get_decompressed_size), TEST_CASE(test_decompress_fast_vs_safe_path), TEST_CASE(test_max_compressed_size_logic), TEST_CASE(test_decompress_empty_frame_null_dst), /* --- Block API --- */ TEST_CASE(test_block_api), TEST_CASE(test_block_api_boundary_sizes), TEST_CASE(test_block_api_large_block_varint), TEST_CASE(test_decompress_block_bound), TEST_CASE(test_decompress_block_safe), /* --- Context API --- */ TEST_CASE(test_opaque_context_api), TEST_CASE(test_cctx_level_raise_reinit), TEST_CASE(test_estimate_cctx_size), /* --- Static Context API --- */ TEST_CASE(test_static_ctx_size_query), TEST_CASE(test_static_ctx_workspace_too_small), TEST_CASE(test_static_ctx_block_size_locked), TEST_CASE(test_static_ctx_level_raise_rejected), TEST_CASE(test_static_ctx_null_inputs), TEST_CASE(test_static_ctx_roundtrip_all_levels), /* --- Stream API --- */ TEST_CASE(test_null_output_decompression), TEST_CASE(test_invalid_arguments), TEST_CASE(test_truncated_input), TEST_CASE(test_io_failures), TEST_CASE(test_thread_params), TEST_CASE(test_multithread_roundtrip), TEST_CASE(test_stream_get_decompressed_size_errors), TEST_CASE(test_stream_engine_errors), /* --- Push Streaming API --- */ TEST_CASE(test_pstream_roundtrip_basic), TEST_CASE(test_pstream_roundtrip_no_checksum), TEST_CASE(test_pstream_roundtrip_levels), TEST_CASE(test_pstream_tiny_chunks), TEST_CASE(test_pstream_drip_one_byte), TEST_CASE(test_pstream_empty_input), TEST_CASE(test_pstream_large_random), TEST_CASE(test_pstream_compatible_with_buffer_api), TEST_CASE(test_pstream_decompress_compatible_with_buffer_api), TEST_CASE(test_pstream_invalid_args), TEST_CASE(test_pstream_truncated_input), TEST_CASE(test_pstream_corrupted_magic), TEST_CASE(test_pstream_decode_seekable_archive), TEST_CASE(test_pstream_compress_after_end_rejected), TEST_CASE(test_pstream_compress_drain_block_resume), /* --- Format (on-disk) --- */ TEST_CASE(test_huffman_codec), TEST_CASE(test_huffman_codec_dict), TEST_CASE(test_huffman_single_symbol_validation), TEST_CASE(test_eof_block_structure), TEST_CASE(test_header_checksum), TEST_CASE(test_global_checksum_order), TEST_CASE(test_chunk_size_code), /* --- Misc --- */ TEST_CASE(test_error_name), TEST_CASE(test_library_info_api), /* --- Dictionary --- */ TEST_CASE(test_dict_zxd_roundtrip), TEST_CASE(test_dict_id_deterministic), TEST_CASE(test_dict_get_id_apis), TEST_CASE(test_dict_buffer_roundtrip), TEST_CASE(test_dict_block_roundtrip), TEST_CASE(test_dict_block_safe_roundtrip), TEST_CASE(test_dict_mismatch_error), TEST_CASE(test_dict_required_error), TEST_CASE(test_dict_no_dict_compat), TEST_CASE(test_dict_stream_roundtrip), TEST_CASE(test_dict_large_dict_roundtrip), TEST_CASE(test_dict_safe_loop_backref), TEST_CASE(test_dict_seekable_roundtrip), TEST_CASE(test_dict_train_roundtrip), TEST_CASE(test_dict_train_no_frequent_patterns), TEST_CASE(test_dict_seekable_mt_roundtrip), TEST_CASE(test_dict_stream_dict_id_checks), TEST_CASE(test_dict_seekable_dict_id_checks), TEST_CASE(test_dict_huf_zxd_roundtrip), TEST_CASE(test_dict_huf_table_roundtrip), TEST_CASE(test_dict_huf_degenerate_corpus), /* --- Seekable (single-threaded) --- */ TEST_CASE(test_seekable_table_sizes), TEST_CASE(test_seekable_table_write), TEST_CASE(test_seekable_roundtrip), TEST_CASE(test_seekable_open_query), TEST_CASE(test_seekable_random_access), TEST_CASE(test_seekable_non_seekable_reject), TEST_CASE(test_seekable_single_block), TEST_CASE(test_seekable_all_levels), TEST_CASE(test_seekable_many_blocks), TEST_CASE(test_seekable_open_file), TEST_CASE(test_seekable_open_reader), /* --- Seekable MT --- */ TEST_CASE(test_seekable_mt_roundtrip), TEST_CASE(test_seekable_mt_single_block), TEST_CASE(test_seekable_mt_random_access), TEST_CASE(test_seekable_mt_full_file), TEST_CASE(test_seekable_open_reader_mt), /* --- Seekable edge cases --- */ TEST_CASE(test_seekable_cross_boundary), TEST_CASE(test_seekable_truncated_input), TEST_CASE(test_seekable_corrupted_sek), TEST_CASE(test_seekable_range_out_of_bounds), TEST_CASE(test_seekable_dst_too_small), TEST_CASE(test_seekable_empty_file), TEST_CASE(test_seekable_no_checksum), TEST_CASE(test_seekable_with_checksum), TEST_CASE(test_seekable_work_buf_tail_pad), }; static const size_t g_tests_count = sizeof(g_tests) / sizeof(g_tests[0]); static void print_usage(const char* argv0) { printf("Usage: %s [options] [filter]\n", argv0); printf(" filter substring matched against test names (e.g. '%s block_api')\n", argv0); printf(" -e, --exact N run only the test whose name exactly matches N\n"); printf(" --list print all test names and exit\n"); printf(" -h, --help show this help\n"); printf("With no argument, runs every test.\n"); } int main(int argc, char** argv) { zxc_test_srand(42); // Fixed seed; deterministic across platforms (see test_common.h) const char* filter = NULL; int exact = 0; { int ai = 1; while (ai < argc) { const char* a = argv[ai]; if (strcmp(a, "-h") == 0 || strcmp(a, "--help") == 0) { print_usage(argv[0]); return 0; } if (strcmp(a, "--list") == 0) { for (size_t k = 0; k < g_tests_count; k++) printf("%s\n", g_tests[k].name); return 0; } if (strcmp(a, "-e") == 0 || strcmp(a, "--exact") == 0) { exact = 1; if (ai + 1 >= argc) { printf("error: %s requires a test name\n", a); return 1; } filter = argv[ai + 1]; ai += 2; continue; } filter = a; ai += 1; } } int total_failures = 0; int ran = 0; for (size_t i = 0; i < g_tests_count; i++) { if (filter) { const int match = exact ? (strcmp(g_tests[i].name, filter) == 0) : (strstr(g_tests[i].name, filter) != NULL); if (!match) continue; } if (!g_tests[i].fn()) total_failures++; ran++; } if (filter && ran == 0) { printf("No tests matched filter \"%s\"%s.\n", filter, exact ? " (exact)" : ""); return 1; } if (total_failures > 0) { printf("FAILED: %d tests failed.\n", total_failures); return 1; } printf("ALL TESTS PASSED SUCCESSFULLY.\n"); return 0; } zxc-0.13.0/tests/test_misc.c000066400000000000000000000076701522536370400157200ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ #include "test_common.h" int test_error_name() { printf("--- Test: zxc_error_name ---\n"); struct { int code; const char* expected; } cases[] = { {ZXC_OK, "ZXC_OK"}, {ZXC_ERROR_MEMORY, "ZXC_ERROR_MEMORY"}, {ZXC_ERROR_DST_TOO_SMALL, "ZXC_ERROR_DST_TOO_SMALL"}, {ZXC_ERROR_SRC_TOO_SMALL, "ZXC_ERROR_SRC_TOO_SMALL"}, {ZXC_ERROR_BAD_MAGIC, "ZXC_ERROR_BAD_MAGIC"}, {ZXC_ERROR_BAD_VERSION, "ZXC_ERROR_BAD_VERSION"}, {ZXC_ERROR_BAD_HEADER, "ZXC_ERROR_BAD_HEADER"}, {ZXC_ERROR_BAD_CHECKSUM, "ZXC_ERROR_BAD_CHECKSUM"}, {ZXC_ERROR_CORRUPT_DATA, "ZXC_ERROR_CORRUPT_DATA"}, {ZXC_ERROR_BAD_OFFSET, "ZXC_ERROR_BAD_OFFSET"}, {ZXC_ERROR_OVERFLOW, "ZXC_ERROR_OVERFLOW"}, {ZXC_ERROR_IO, "ZXC_ERROR_IO"}, {ZXC_ERROR_NULL_INPUT, "ZXC_ERROR_NULL_INPUT"}, {ZXC_ERROR_BAD_BLOCK_TYPE, "ZXC_ERROR_BAD_BLOCK_TYPE"}, {ZXC_ERROR_BAD_BLOCK_SIZE, "ZXC_ERROR_BAD_BLOCK_SIZE"}, {ZXC_ERROR_DICT_REQUIRED, "ZXC_ERROR_DICT_REQUIRED"}, {ZXC_ERROR_DICT_MISMATCH, "ZXC_ERROR_DICT_MISMATCH"}, {ZXC_ERROR_DICT_TOO_LARGE, "ZXC_ERROR_DICT_TOO_LARGE"}, }; const int n = sizeof(cases) / sizeof(cases[0]); for (int i = 0; i < n; i++) { const char* name = zxc_error_name(cases[i].code); if (strcmp(name, cases[i].expected) != 0) { printf(" [FAIL] zxc_error_name(%d) = \"%s\", expected \"%s\"\n", cases[i].code, name, cases[i].expected); return 0; } } printf(" [PASS] All %d known error codes\n", n); // Unknown codes should return "ZXC_UNKNOWN_ERROR" const char* unk = zxc_error_name(-999); if (strcmp(unk, "ZXC_UNKNOWN_ERROR") != 0) { printf(" [FAIL] zxc_error_name(-999) = \"%s\", expected \"ZXC_UNKNOWN_ERROR\"\n", unk); return 0; } unk = zxc_error_name(42); if (strcmp(unk, "ZXC_UNKNOWN_ERROR") != 0) { printf(" [FAIL] zxc_error_name(42) = \"%s\", expected \"ZXC_UNKNOWN_ERROR\"\n", unk); return 0; } printf(" [PASS] Unknown error codes\n"); printf("PASS\n\n"); return 1; } int test_library_info_api() { printf("=== TEST: Unit - Library Info API (zxc_min/max/default_level, zxc_version_string) ===\n"); // 1. Min level must match compile-time constant int min = zxc_min_level(); if (min != ZXC_LEVEL_FASTEST) { printf("Failed: zxc_min_level() returned %d, expected %d\n", min, ZXC_LEVEL_FASTEST); return 0; } printf(" [PASS] zxc_min_level() == %d\n", min); // 2. Max level must match compile-time constant int max = zxc_max_level(); if (max != ZXC_LEVEL_ULTRA) { printf("Failed: zxc_max_level() returned %d, expected %d\n", max, ZXC_LEVEL_ULTRA); return 0; } printf(" [PASS] zxc_max_level() == %d\n", max); // 3. Default level must be within [min, max] int def = zxc_default_level(); if (def < min || def > max) { printf("Failed: zxc_default_level() returned %d, not in [%d, %d]\n", def, min, max); return 0; } if (def != ZXC_LEVEL_DEFAULT) { printf("Failed: zxc_default_level() returned %d, expected %d\n", def, ZXC_LEVEL_DEFAULT); return 0; } printf(" [PASS] zxc_default_level() == %d\n", def); // 4. Version string must be non-NULL and match compile-time version const char* ver = zxc_version_string(); if (!ver) { printf("Failed: zxc_version_string() returned NULL\n"); return 0; } if (strcmp(ver, ZXC_LIB_VERSION_STR) != 0) { printf("Failed: zxc_version_string() returned \"%s\", expected \"%s\"\n", ver, ZXC_LIB_VERSION_STR); return 0; } printf(" [PASS] zxc_version_string() == \"%s\"\n", ver); printf("PASS\n\n"); return 1; } zxc-0.13.0/tests/test_pstream_api.c000066400000000000000000000614141522536370400172650ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause * * Tests for the push-based streaming API (zxc_pstream.h). * * Coverage focuses on: * - Round-trip correctness across patterns / sizes / chunk granularities. * - Wire-compatibility with the buffer API (push-compressed blob must be * decodable by zxc_decompress(), and vice-versa). * - Pathological caller behaviour (one-byte chunks, empty input, tiny * output buffers). * - Error handling on truncated / corrupted streams. */ #include "test_common.h" /* ---- helpers ---------------------------------------------------------- */ /* Push-compress src/src_size with given chunk sizes through the pstream * API and return a malloc'd blob (caller frees) of size *out_size. * Returns NULL on failure. */ static uint8_t* pstream_compress_in_chunks(const uint8_t* src, size_t src_size, size_t in_chunk, size_t out_chunk, int level, int checksum_enabled, size_t* out_size) { const zxc_compress_opts_t opts = {.level = level, .checksum_enabled = checksum_enabled}; zxc_cstream* cs = zxc_cstream_create(&opts); if (!cs) return NULL; size_t cap = 1024; size_t used = 0; uint8_t* blob = (uint8_t*)malloc(cap); uint8_t* obuf = (uint8_t*)malloc(out_chunk); if (!blob || !obuf) { free(blob); free(obuf); zxc_cstream_free(cs); return NULL; } /* Append helper */ #define APPEND_FROM(buf, n) \ do { \ if (used + (n) > cap) { \ while (used + (n) > cap) cap *= 2; \ uint8_t* nb = (uint8_t*)realloc(blob, cap); \ if (!nb) { \ free(blob); \ free(obuf); \ zxc_cstream_free(cs); \ return NULL; \ } \ blob = nb; \ } \ memcpy(blob + used, (buf), (n)); \ used += (n); \ } while (0) /* Phase 1: feed input in fixed chunks, draining as needed. */ size_t off = 0; zxc_outbuf_t out = {obuf, out_chunk, 0}; while (off < src_size) { const size_t n = (src_size - off) < in_chunk ? (src_size - off) : in_chunk; zxc_inbuf_t in = {src + off, n, 0}; while (in.pos < in.size) { const int64_t r = zxc_cstream_compress(cs, &out, &in); if (r < 0) { free(blob); free(obuf); zxc_cstream_free(cs); return NULL; } if (out.pos > 0) { APPEND_FROM(obuf, out.pos); out.pos = 0; } } off += n; } /* Phase 2: finalise. */ int64_t pending; do { pending = zxc_cstream_end(cs, &out); if (pending < 0) { free(blob); free(obuf); zxc_cstream_free(cs); return NULL; } if (out.pos > 0) { APPEND_FROM(obuf, out.pos); out.pos = 0; } } while (pending > 0); #undef APPEND_FROM free(obuf); zxc_cstream_free(cs); *out_size = used; return blob; } /* Push-decompress an entire blob in given chunk sizes; returns malloc'd * decoded buffer (caller frees) of size *out_size, or NULL on failure. */ static uint8_t* pstream_decompress_in_chunks(const uint8_t* src, size_t src_size, size_t in_chunk, size_t out_chunk, int checksum_enabled, size_t* out_size) { const zxc_decompress_opts_t opts = {.checksum_enabled = checksum_enabled}; zxc_dstream* ds = zxc_dstream_create(&opts); if (!ds) return NULL; size_t cap = 1024; size_t used = 0; uint8_t* dec = (uint8_t*)malloc(cap); uint8_t* obuf = (uint8_t*)malloc(out_chunk); if (!dec || !obuf) { free(dec); free(obuf); zxc_dstream_free(ds); return NULL; } /* Drive the loop until we have nothing more to feed *and* the decoder * makes no further progress (output drained, no input left to consume). */ size_t off = 0; zxc_outbuf_t out = {obuf, out_chunk, 0}; for (;;) { const size_t remaining = src_size - off; const size_t n = remaining < in_chunk ? remaining : in_chunk; zxc_inbuf_t in = {n ? src + off : NULL, n, 0}; const int64_t r = zxc_dstream_decompress(ds, &out, &in); if (r < 0) { free(dec); free(obuf); zxc_dstream_free(ds); return NULL; } if (out.pos > 0) { if (used + out.pos > cap) { while (used + out.pos > cap) cap *= 2; uint8_t* nb = (uint8_t*)realloc(dec, cap); if (!nb) { free(dec); free(obuf); zxc_dstream_free(ds); return NULL; } dec = nb; } memcpy(dec + used, obuf, out.pos); used += out.pos; out.pos = 0; } off += in.pos; /* Termination: no input left to feed and decoder produced nothing * AND consumed nothing -> it is either DONE or stalled. */ if (off >= src_size && in.pos == 0 && r == 0) break; } /* Reject truncated streams: if we ran out of input but the parser never * reached the validated footer, treat it as failure. */ if (!zxc_dstream_finished(ds)) { free(dec); free(obuf); zxc_dstream_free(ds); return NULL; } free(obuf); zxc_dstream_free(ds); *out_size = used; return dec; } /* End-to-end roundtrip with given chunk sizes; asserts the decoded blob * matches the input byte-for-byte. Returns 1 on success. */ static int do_roundtrip(const char* label, const uint8_t* src, size_t size, size_t in_chunk, size_t out_chunk, int level, int checksum_enabled) { size_t comp_size = 0; uint8_t* comp = pstream_compress_in_chunks(src, size, in_chunk, out_chunk, level, checksum_enabled, &comp_size); if (!comp) { printf(" [%s] compress failed\n", label); return 0; } size_t dec_size = 0; uint8_t* dec = pstream_decompress_in_chunks(comp, comp_size, in_chunk, out_chunk, checksum_enabled, &dec_size); if (!dec) { printf(" [%s] decompress failed (comp_size=%zu)\n", label, comp_size); free(comp); return 0; } if (dec_size != size || (size > 0 && memcmp(dec, src, size) != 0)) { printf(" [%s] mismatch (orig=%zu, dec=%zu)\n", label, size, dec_size); free(comp); free(dec); return 0; } free(comp); free(dec); return 1; } /* ---- tests ------------------------------------------------------------ */ int test_pstream_roundtrip_basic(void) { printf("=== TEST: PStream Roundtrip Basic (lz_data, 64 KiB) ===\n"); const size_t size = 64 * 1024; uint8_t* src = (uint8_t*)malloc(size); if (!src) return 0; gen_lz_data(src, size); const int ok = do_roundtrip("default 64KiB chunks", src, size, 64 * 1024, 64 * 1024, 3, 1); free(src); if (ok) printf("PASS\n\n"); return ok; } int test_pstream_roundtrip_no_checksum(void) { printf("=== TEST: PStream Roundtrip (checksum disabled) ===\n"); const size_t size = 80 * 1024; uint8_t* src = (uint8_t*)malloc(size); if (!src) return 0; gen_lz_data(src, size); const int ok = do_roundtrip("no csum", src, size, 32 * 1024, 32 * 1024, 3, 0); free(src); if (ok) printf("PASS\n\n"); return ok; } int test_pstream_roundtrip_levels(void) { printf("=== TEST: PStream Roundtrip across levels 1..5 ===\n"); const size_t size = 70 * 1024; uint8_t* src = (uint8_t*)malloc(size); if (!src) return 0; gen_lz_data(src, size); int ok = 1; char label[32]; for (int lvl = 1; lvl <= 5 && ok; lvl++) { snprintf(label, sizeof label, "level=%d csum=1", lvl); ok &= do_roundtrip(label, src, size, 16 * 1024, 16 * 1024, lvl, 1); } free(src); if (ok) printf("PASS\n\n"); return ok; } int test_pstream_tiny_chunks(void) { printf("=== TEST: PStream tiny IO chunks (in=137, out=53) ===\n"); /* Stresses the state machine with output buffers so small they force * partial drains in the middle of every block. */ const size_t size = 40 * 1024; uint8_t* src = (uint8_t*)malloc(size); if (!src) return 0; gen_lz_data(src, size); const int ok = do_roundtrip("tiny", src, size, 137, 53, 3, 1); free(src); if (ok) printf("PASS\n\n"); return ok; } int test_pstream_drip_one_byte(void) { printf("=== TEST: PStream 1-byte input chunks (worst-case feeder) ===\n"); const size_t size = 8 * 1024; uint8_t* src = (uint8_t*)malloc(size); if (!src) return 0; gen_lz_data(src, size); const int ok = do_roundtrip("1B", src, size, 1, 4096, 3, 1); free(src); if (ok) printf("PASS\n\n"); return ok; } int test_pstream_empty_input(void) { printf("=== TEST: PStream empty input -> valid empty file ===\n"); size_t comp_size = 0; uint8_t* comp = pstream_compress_in_chunks(NULL, 0, 4096, 4096, 3, 1, &comp_size); if (!comp) { printf("compress NULL/0 failed\n"); return 0; } /* Should at least be: file header (16) + EOF block (8) + footer (12) = 36 bytes. */ if (comp_size < 36) { printf("expected >=36 bytes, got %zu\n", comp_size); free(comp); return 0; } size_t dec_size = 0; uint8_t* dec = pstream_decompress_in_chunks(comp, comp_size, 4096, 4096, 1, &dec_size); if (!dec) { printf("decompress failed\n"); free(comp); return 0; } if (dec_size != 0) { printf("expected 0 decoded bytes, got %zu\n", dec_size); free(comp); free(dec); return 0; } free(comp); free(dec); printf("PASS (comp_size=%zu)\n\n", comp_size); return 1; } int test_pstream_large_random(void) { printf("=== TEST: PStream large mixed (1.5 MiB) ===\n"); const size_t size = 1500 * 1024; uint8_t* src = (uint8_t*)malloc(size); if (!src) return 0; gen_lz_data(src, size); const int ok = do_roundtrip("1.5MiB / level5", src, size, 13 * 1024, 7 * 1024, 5, 1); free(src); if (ok) printf("PASS\n\n"); return ok; } int test_pstream_compatible_with_buffer_api(void) { printf("=== TEST: pstream-compressed blob decodable by zxc_decompress() ===\n"); const size_t size = 100 * 1024; uint8_t* src = (uint8_t*)malloc(size); if (!src) return 0; gen_lz_data(src, size); size_t comp_size = 0; uint8_t* comp = pstream_compress_in_chunks(src, size, 8192, 8192, 3, 1, &comp_size); if (!comp) { free(src); return 0; } /* Decode with the one-shot buffer API. */ uint8_t* dec = (uint8_t*)malloc(size); if (!dec) { free(src); free(comp); return 0; } const zxc_decompress_opts_t dopts = {.checksum_enabled = 1}; const int64_t dsz = zxc_decompress(comp, comp_size, dec, size, &dopts); const int ok = (dsz == (int64_t)size && memcmp(dec, src, size) == 0); if (!ok) { printf("FAIL: dsz=%lld, expected %zu\n", (long long)dsz, size); } free(src); free(comp); free(dec); if (ok) printf("PASS\n\n"); return ok; } int test_pstream_decompress_compatible_with_buffer_api(void) { printf("=== TEST: buffer-API blob decodable by zxc_dstream ===\n"); const size_t size = 100 * 1024; uint8_t* src = (uint8_t*)malloc(size); if (!src) return 0; gen_lz_data(src, size); /* Compress with the one-shot buffer API. */ const uint64_t bound = zxc_compress_bound(size); uint8_t* comp = (uint8_t*)malloc((size_t)bound); if (!comp) { free(src); return 0; } const zxc_compress_opts_t copts = {.level = 3, .checksum_enabled = 1}; const int64_t csz = zxc_compress(src, size, comp, (size_t)bound, &copts); if (csz <= 0) { free(src); free(comp); return 0; } /* Decode with the streaming push API (small chunks). */ size_t dec_size = 0; uint8_t* dec = pstream_decompress_in_chunks(comp, (size_t)csz, 511, 7 * 1024, 1, &dec_size); const int ok = (dec && dec_size == size && memcmp(dec, src, size) == 0); if (!ok) { printf("FAIL: dec_size=%zu, expected %zu\n", dec_size, size); } free(src); free(comp); free(dec); if (ok) printf("PASS\n\n"); return ok; } int test_pstream_invalid_args(void) { printf("=== TEST: PStream invalid arguments ===\n"); /* NULL inputs must return ZXC_ERROR_NULL_INPUT, never crash. */ if (zxc_cstream_compress(NULL, NULL, NULL) != ZXC_ERROR_NULL_INPUT) return 0; if (zxc_cstream_end(NULL, NULL) != ZXC_ERROR_NULL_INPUT) return 0; if (zxc_dstream_decompress(NULL, NULL, NULL) != ZXC_ERROR_NULL_INPUT) return 0; if (zxc_cstream_in_size(NULL) != 0) return 0; if (zxc_cstream_out_size(NULL) != 0) return 0; if (zxc_dstream_in_size(NULL) != 0) return 0; if (zxc_dstream_out_size(NULL) != 0) return 0; /* Dictionary options are rejected at create time (the push-stream format * carries no dict_id): a dictionary must never be silently dropped. */ { static const uint8_t d[4] = {1, 2, 3, 4}; zxc_compress_opts_t copts = {.level = 3, .dict = d, .dict_size = sizeof(d)}; if (zxc_cstream_create(&copts) != NULL) { printf(" [FAIL] cstream_create must reject dict opts\n"); return 0; } zxc_decompress_opts_t dopts_dict = {.dict = d, .dict_size = sizeof(d)}; if (zxc_dstream_create(&dopts_dict) != NULL) { printf(" [FAIL] dstream_create must reject dict opts\n"); return 0; } } /* Malformed descriptors must be rejected before any helper does an * unchecked memcpy() that could underflow size_t arithmetic or * dereference NULL. */ { const zxc_decompress_opts_t dopts = {0}; zxc_dstream* ds = zxc_dstream_create(&dopts); if (!ds) return 0; uint8_t buf[16] = {0}; zxc_outbuf_t good_out = {buf, sizeof buf, 0}; /* in->pos > in->size */ zxc_inbuf_t bad1 = {buf, 4, 5}; if (zxc_dstream_decompress(ds, &good_out, &bad1) != ZXC_ERROR_NULL_INPUT) { zxc_dstream_free(ds); return 0; } /* out->pos > out->size */ zxc_outbuf_t bad_out = {buf, 4, 5}; zxc_inbuf_t empty_in = {NULL, 0, 0}; if (zxc_dstream_decompress(ds, &bad_out, &empty_in) != ZXC_ERROR_NULL_INPUT) { zxc_dstream_free(ds); return 0; } /* in claims bytes but src is NULL */ zxc_inbuf_t bad2 = {NULL, 16, 0}; if (zxc_dstream_decompress(ds, &good_out, &bad2) != ZXC_ERROR_NULL_INPUT) { zxc_dstream_free(ds); return 0; } /* out claims capacity but dst is NULL */ zxc_outbuf_t bad_out2 = {NULL, 16, 0}; if (zxc_dstream_decompress(ds, &bad_out2, &empty_in) != ZXC_ERROR_NULL_INPUT) { zxc_dstream_free(ds); return 0; } zxc_dstream_free(ds); } /* free(NULL) is a no-op, must not crash. */ zxc_cstream_free(NULL); zxc_dstream_free(NULL); printf("PASS\n\n"); return 1; } int test_pstream_truncated_input(void) { printf("=== TEST: PStream truncated input -> error ===\n"); const size_t size = 64 * 1024; uint8_t* src = (uint8_t*)malloc(size); if (!src) return 0; gen_lz_data(src, size); size_t comp_size = 0; uint8_t* comp = pstream_compress_in_chunks(src, size, 4096, 4096, 3, 1, &comp_size); if (!comp || comp_size < 30) { free(src); free(comp); return 0; } /* Cut off the last 5 bytes (truncated footer). */ const size_t trunc_size = comp_size - 5; size_t dec_size = 0; uint8_t* dec = pstream_decompress_in_chunks(comp, trunc_size, 1024, 1024, 1, &dec_size); /* Either the helper returns NULL (error during decode), or it returns a * partial buffer that doesn't match, in both cases, NOT a clean success. */ const int ok = (dec == NULL) || (dec_size != size); free(src); free(comp); free(dec); if (ok) printf("PASS\n\n"); return ok; } int test_pstream_corrupted_magic(void) { printf("=== TEST: PStream corrupted file magic -> ZXC_ERROR_BAD_MAGIC ===\n"); /* Hand-craft 16 bogus bytes of "file header". */ uint8_t junk[16]; for (int i = 0; i < 16; i++) junk[i] = (uint8_t)(0xAA ^ i); const zxc_decompress_opts_t opts = {0}; zxc_dstream* ds = zxc_dstream_create(&opts); if (!ds) return 0; uint8_t out_buf[64]; zxc_outbuf_t out = {out_buf, sizeof out_buf, 0}; zxc_inbuf_t in = {junk, sizeof junk, 0}; const int64_t r = zxc_dstream_decompress(ds, &out, &in); int ok = (r == ZXC_ERROR_BAD_MAGIC); if (!ok) printf("FAIL: expected ZXC_ERROR_BAD_MAGIC (-4), got %lld\n", (long long)r); /* Sticky error: subsequent call returns the same code. */ zxc_inbuf_t empty = {NULL, 0, 0}; if (ok && zxc_dstream_decompress(ds, &out, &empty) != ZXC_ERROR_BAD_MAGIC) { printf("FAIL: error not sticky\n"); ok = 0; } zxc_dstream_free(ds); if (ok) printf("PASS\n\n"); return ok; } /* Decompress a SEEKABLE archive through the pstream API: after the EOF block * the decoder peeks 8 bytes, recognises a SEK block, and skips its payload * in DS_DRAIN_SEK_PAYLOAD before consuming the file footer. */ int test_pstream_decode_seekable_archive(void) { printf("=== TEST: PStream decodes seekable archive (DS_DRAIN_SEK_PAYLOAD) ===\n"); const size_t size = 96 * 1024; /* > one default block to force >1 SEK entry */ uint8_t* src = (uint8_t*)malloc(size); if (!src) return 0; gen_lz_data(src, size); /* Compress with seekable=1 via the buffer API (pstream cstream forces * seekable=0, so we need another producer to emit a SEK block). */ const uint64_t bound = zxc_compress_bound(size); uint8_t* comp = (uint8_t*)malloc((size_t)bound); if (!comp) { free(src); return 0; } const zxc_compress_opts_t copts = { .level = 3, .checksum_enabled = 1, .seekable = 1, .block_size = 32 * 1024}; const int64_t comp_size = zxc_compress(src, size, comp, (size_t)bound, &copts); if (comp_size <= 0) { printf("FAIL: zxc_compress(seekable=1) returned %lld\n", (long long)comp_size); free(src); free(comp); return 0; } /* Drive the seekable blob through zxc_dstream_decompress and check that * decoded bytes match the source. We feed the input in 4 KB chunks and * receive the output via 8 KB chunks: this stresses the SEK skip across * multiple calls (DS_DRAIN_SEK_PAYLOAD returns when the input is exhausted * mid-skip). */ const zxc_decompress_opts_t dopts = {.checksum_enabled = 1}; zxc_dstream* ds = zxc_dstream_create(&dopts); if (!ds) { free(src); free(comp); return 0; } uint8_t* dec = (uint8_t*)malloc(size); /* Zero-initialised: the decoder writes the first `out.pos` bytes per call * but cppcheck cannot see through the pointer chain in zxc_outbuf_t. */ uint8_t out_chunk[8192] = {0}; if (!dec) { zxc_dstream_free(ds); free(src); free(comp); return 0; } size_t dec_used = 0; size_t in_off = 0; const size_t in_step = 4096; int ok = 1; while (in_off < (size_t)comp_size && !zxc_dstream_finished(ds)) { const size_t n = ((size_t)comp_size - in_off) < in_step ? ((size_t)comp_size - in_off) : in_step; zxc_inbuf_t in = {comp + in_off, n, 0}; zxc_outbuf_t out = {out_chunk, sizeof out_chunk, 0}; const int64_t r = zxc_dstream_decompress(ds, &out, &in); if (r < 0) { printf("FAIL: zxc_dstream_decompress returned %lld\n", (long long)r); ok = 0; break; } if (dec_used + out.pos > size) { printf("FAIL: decoded bytes exceed source size\n"); ok = 0; break; } memcpy(dec + dec_used, out_chunk, out.pos); dec_used += out.pos; in_off += in.pos; if (in.pos == 0 && out.pos == 0) break; /* no progress */ } if (ok && !zxc_dstream_finished(ds)) { printf("FAIL: decoder did not finalise after consuming seekable input\n"); ok = 0; } if (ok && (dec_used != size || memcmp(dec, src, size) != 0)) { printf("FAIL: decoded payload does not match source\n"); ok = 0; } zxc_dstream_free(ds); free(src); free(comp); free(dec); if (ok) printf("PASS\n\n"); return ok; } /* Calling _compress() after _end() has started transitioning to a drain-tail * state must return ZXC_ERROR_NULL_INPUT. Covers the * `case CS_DRAIN_LAST/CS_DRAIN_EOF/CS_DRAIN_FOOTER/CS_ERRORED:` branch in * zxc_cstream_compress(). */ int test_pstream_compress_after_end_rejected(void) { printf("=== TEST: PStream _compress() after _end() -> ZXC_ERROR_NULL_INPUT ===\n"); const zxc_compress_opts_t opts = {.level = 3}; zxc_cstream* cs = zxc_cstream_create(&opts); if (!cs) return 0; /* Feed a few bytes so _end has actual residual data to flush. */ uint8_t src[64]; for (size_t i = 0; i < sizeof src; i++) src[i] = (uint8_t)i; uint8_t obuf[1024]; zxc_inbuf_t in = {src, sizeof src, 0}; zxc_outbuf_t out = {obuf, sizeof obuf, 0}; if (zxc_cstream_compress(cs, &out, &in) < 0) { zxc_cstream_free(cs); return 0; } /* Tiny output buffer so _end returns >0 (still pending) and the stream * is parked in CS_DRAIN_LAST / CS_DRAIN_EOF / CS_DRAIN_FOOTER. */ uint8_t tiny[4]; zxc_outbuf_t tiny_out = {tiny, sizeof tiny, 0}; const int64_t pending = zxc_cstream_end(cs, &tiny_out); if (pending <= 0) { printf("FAIL: expected _end() to return >0 with tiny output, got %lld\n", (long long)pending); zxc_cstream_free(cs); return 0; } /* Now state contains {CS_DRAIN_LAST, CS_DRAIN_EOF, CS_DRAIN_FOOTER}. _compress * must reject. */ zxc_inbuf_t more = {src, sizeof src, 0}; zxc_outbuf_t out2 = {obuf, sizeof obuf, 0}; const int64_t r = zxc_cstream_compress(cs, &out2, &more); const int ok = (r == ZXC_ERROR_NULL_INPUT); if (!ok) printf("FAIL: expected ZXC_ERROR_NULL_INPUT (-12), got %lld\n", (long long)r); zxc_cstream_free(cs); if (ok) printf("PASS\n\n"); return ok; } /* When _compress() fills a block but the caller's output buffer is too small * to drain it in one call, the next _compress() resumes at CS_DRAIN_BLOCK. * This test exercises that resume path inside zxc_cstream_compress (distinct * from the CS_DRAIN_BLOCK case in _end). */ int test_pstream_compress_drain_block_resume(void) { printf("=== TEST: PStream _compress() resumes CS_DRAIN_BLOCK across calls ===\n"); /* Small block size so we trigger the block boundary quickly. */ const zxc_compress_opts_t opts = {.level = 3, .block_size = 4096}; zxc_cstream* cs = zxc_cstream_create(&opts); if (!cs) return 0; /* Enough input for one full block (+ a little) but no more. */ const size_t src_size = 4096 + 256; uint8_t* src = (uint8_t*)malloc(src_size); if (!src) { zxc_cstream_free(cs); return 0; } gen_lz_data(src, src_size); /* Output buffer smaller than one compressed block: forces partial drains * and re-entries into CS_DRAIN_BLOCK. Zero-initialised to silence the * cppcheck false-positive that fires when an uninit array's address is * stored in zxc_outbuf_t.dst. */ uint8_t obuf[37] = {0}; zxc_inbuf_t in = {src, src_size, 0}; int saw_pending_drain = 0; int ok = 1; /* Drive the loop until input is exhausted; if any call returns >0 it * means the next call will hit CS_DRAIN_BLOCK in the switch. */ while (in.pos < in.size) { zxc_outbuf_t out = {obuf, sizeof obuf, 0}; const int64_t r = zxc_cstream_compress(cs, &out, &in); if (r < 0) { printf("FAIL: _compress returned %lld\n", (long long)r); ok = 0; break; } if (r > 0) saw_pending_drain = 1; } if (ok && !saw_pending_drain) { printf("FAIL: expected at least one >0 return to exercise CS_DRAIN_BLOCK resume\n"); ok = 0; } zxc_cstream_free(cs); free(src); if (ok) printf("PASS\n\n"); return ok; } zxc-0.13.0/tests/test_seekable.c000066400000000000000000001331631522536370400165350ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ #include "test_common.h" #include "../include/zxc_stream.h" int test_seekable_table_sizes() { printf("=== TEST: Seekable - Table Sizes ===\n"); /* block_header(8) + 10*4 = 48 */ if (zxc_seek_table_size(10) != 48) { printf("Failed: size for 10 blocks\n"); return 0; } /* block_header(8) + 0 = 8 */ if (zxc_seek_table_size(0) != 8) { printf("Failed: zero blocks size\n"); return 0; } printf("PASS\n\n"); return 1; } int test_seekable_table_write() { printf("=== TEST: Seekable - Table Write/Validate ===\n"); const uint32_t comp[] = {100, 200, 150}; const size_t sz = zxc_seek_table_size(3); uint8_t* buf = malloc(sz); if (!buf) return 0; const int64_t written = zxc_write_seek_table(buf, sz, comp, 3); if (written != (int64_t)sz) { printf("Failed: write size mismatch\n"); free(buf); return 0; } /* Validate block_type == SEK in the block header */ if (buf[0] != ZXC_BLOCK_SEK) { printf("Failed: bad block_type (%u)\n", buf[0]); free(buf); return 0; } /* Validate first comp_size entry (after 8-byte block header) */ if (zxc_le32(buf + 8) != 100) { printf("Failed: bad first comp_size\n"); free(buf); return 0; } free(buf); printf("PASS\n\n"); return 1; } int test_seekable_roundtrip() { printf("=== TEST: Seekable - Compress/Decompress Roundtrip ===\n"); const size_t SRC_SIZE = 256 * 1024; uint8_t* src = malloc(SRC_SIZE); if (!src) return 0; fill_seek_data(src, SRC_SIZE, 42); const size_t dst_cap = (size_t)zxc_compress_bound(SRC_SIZE) + 1024; uint8_t* dst = malloc(dst_cap); uint8_t* dec = malloc(SRC_SIZE); if (!dst || !dec) { free(src); free(dst); free(dec); return 0; } zxc_compress_opts_t opts = {.level = ZXC_LEVEL_DEFAULT, .block_size = 64 * 1024, .checksum_enabled = 1, .seekable = 1}; const int64_t csize = zxc_compress(src, SRC_SIZE, dst, dst_cap, &opts); if (csize <= 0) { printf("Failed: compress\n"); free(src); free(dst); free(dec); return 0; } /* Sub-test A: single block (data < block_size) roundtrip */ { const size_t SMALL = 60 * 1024; /* fits in one 64KB block */ memset(dec, 0, SMALL); zxc_compress_opts_t opts_a = {.level = ZXC_LEVEL_DEFAULT, .block_size = 64 * 1024, .checksum_enabled = 1, .seekable = 0}; const int64_t a_csize = zxc_compress(src, SMALL, dst, dst_cap, &opts_a); if (a_csize <= 0) { printf("Failed: single-block 64KB compress (%lld)\n", (long long)a_csize); free(src); free(dst); free(dec); return 0; } zxc_decompress_opts_t ad = {.checksum_enabled = 1}; const int64_t a_dsize = zxc_decompress(dst, (size_t)a_csize, dec, SMALL, &ad); if (a_dsize != (int64_t)SMALL || memcmp(src, dec, SMALL) != 0) { printf("Failed: single-block 64KB roundtrip (dsize=%lld)\n", (long long)a_dsize); if (a_dsize == (int64_t)SMALL) { for (size_t i = 0; i < SMALL; i++) { if (src[i] != dec[i]) { printf(" first diff at byte %zu: src=0x%02x dec=0x%02x\n", i, src[i], dec[i]); break; } } } free(src); free(dst); free(dec); return 0; } printf(" sub-test A (single block, 60KB): OK\n"); } /* Sub-test B: exactly 2 blocks (128KB with 64KB block_size) */ { const size_t TWO = 128 * 1024; memset(dec, 0, TWO); zxc_compress_opts_t opts_b = {.level = ZXC_LEVEL_DEFAULT, .block_size = 64 * 1024, .checksum_enabled = 1, .seekable = 0}; const int64_t b_csize = zxc_compress(src, TWO, dst, dst_cap, &opts_b); if (b_csize <= 0) { printf("Failed: 2-block 64KB compress (%lld)\n", (long long)b_csize); free(src); free(dst); free(dec); return 0; } zxc_decompress_opts_t bd = {.checksum_enabled = 1}; const int64_t b_dsize = zxc_decompress(dst, (size_t)b_csize, dec, TWO, &bd); if (b_dsize != (int64_t)TWO || memcmp(src, dec, TWO) != 0) { printf("Failed: 2-block 64KB roundtrip (dsize=%lld)\n", (long long)b_dsize); if (b_dsize == (int64_t)TWO) { for (size_t i = 0; i < TWO; i++) { if (src[i] != dec[i]) { printf(" first diff at byte %zu: src=0x%02x dec=0x%02x\n", i, src[i], dec[i]); break; } } } free(src); free(dst); free(dec); return 0; } printf(" sub-test B (2 blocks, 128KB): OK\n"); } /* Sub-test C: full 256KB (4 blocks x 64KB) */ { memset(dec, 0, SRC_SIZE); zxc_compress_opts_t opts_ns = {.level = ZXC_LEVEL_DEFAULT, .block_size = 64 * 1024, .checksum_enabled = 1, .seekable = 0}; const int64_t ns_csize = zxc_compress(src, SRC_SIZE, dst, dst_cap, &opts_ns); if (ns_csize <= 0) { printf("Failed: non-seekable compress (%lld)\n", (long long)ns_csize); free(src); free(dst); free(dec); return 0; } zxc_decompress_opts_t nd = {.checksum_enabled = 1}; const int64_t ns_dsize = zxc_decompress(dst, (size_t)ns_csize, dec, SRC_SIZE, &nd); if (ns_dsize != (int64_t)SRC_SIZE || memcmp(src, dec, SRC_SIZE) != 0) { printf("Failed: non-seekable 64KB block_size roundtrip (dsize=%lld)\n", (long long)ns_dsize); if (ns_dsize == (int64_t)SRC_SIZE) { for (size_t i = 0; i < SRC_SIZE; i++) { if (src[i] != dec[i]) { printf(" first diff at byte %zu: src=0x%02x dec=0x%02x\n", i, src[i], dec[i]); break; } } } free(src); free(dst); free(dec); return 0; } printf(" sub-test C (4 blocks, 256KB): OK\n"); } /* Re-compress with seekable=1 for the actual test */ memset(dec, 0, SRC_SIZE); const int64_t csize2 = zxc_compress(src, SRC_SIZE, dst, dst_cap, &opts); if (csize2 <= 0) { printf("Failed: seekable re-compress (%lld)\n", (long long)csize2); free(src); free(dst); free(dec); return 0; } /* Full decompression with standard API (backward compatibility) */ zxc_decompress_opts_t dopts = {.checksum_enabled = 1}; const int64_t dsize = zxc_decompress(dst, (size_t)csize2, dec, SRC_SIZE, &dopts); if (dsize != (int64_t)SRC_SIZE || memcmp(src, dec, SRC_SIZE) != 0) { printf("Failed: decompress mismatch (csize=%lld dsize=%lld expected=%zu)\n", (long long)csize2, (long long)dsize, SRC_SIZE); if (dsize == (int64_t)SRC_SIZE) { for (size_t i = 0; i < SRC_SIZE; i++) { if (src[i] != dec[i]) { printf(" first diff at byte %zu: src=0x%02x dec=0x%02x\n", i, src[i], dec[i]); break; } } } free(src); free(dst); free(dec); return 0; } free(src); free(dst); free(dec); printf("PASS\n\n"); return 1; } int test_seekable_open_query() { printf("=== TEST: Seekable - Open and Query ===\n"); const size_t SRC_SIZE = 200 * 1024; uint8_t* src = malloc(SRC_SIZE); if (!src) return 0; fill_seek_data(src, SRC_SIZE, 99); const size_t dst_cap = (size_t)zxc_compress_bound(SRC_SIZE) + 1024; uint8_t* dst = malloc(dst_cap); if (!dst) { free(src); return 0; } zxc_compress_opts_t opts = {.level = 2, .block_size = 64 * 1024, .seekable = 1}; const int64_t csize = zxc_compress(src, SRC_SIZE, dst, dst_cap, &opts); if (csize <= 0) { printf("Failed: compress\n"); free(src); free(dst); return 0; } zxc_seekable* s = zxc_seekable_open(dst, (size_t)csize); if (!s) { printf("Failed: open\n"); free(src); free(dst); return 0; } const uint32_t nb = zxc_seekable_get_num_blocks(s); if (nb < 3) { printf("Failed: expected >= 3 blocks, got %u\n", nb); zxc_seekable_free(s); free(src); free(dst); return 0; } const uint64_t total = zxc_seekable_get_decompressed_size(s); if (total != SRC_SIZE) { printf("Failed: decomp size\n"); zxc_seekable_free(s); free(src); free(dst); return 0; } uint64_t sum = 0; for (uint32_t i = 0; i < nb; i++) { sum += zxc_seekable_get_block_decomp_size(s, i); if (zxc_seekable_get_block_comp_size(s, i) == 0) { printf("Failed: zero comp size\n"); zxc_seekable_free(s); free(src); free(dst); return 0; } } if (sum != SRC_SIZE) { printf("Failed: block sizes sum\n"); zxc_seekable_free(s); free(src); free(dst); return 0; } zxc_seekable_free(s); free(src); free(dst); printf("PASS\n\n"); return 1; } int test_seekable_random_access() { printf("=== TEST: Seekable - Random Access ===\n"); const size_t SRC_SIZE = 300 * 1024; uint8_t* src = malloc(SRC_SIZE); if (!src) return 0; fill_seek_data(src, SRC_SIZE, 77); const size_t dst_cap = (size_t)zxc_compress_bound(SRC_SIZE) + 1024; uint8_t* dst = malloc(dst_cap); if (!dst) { free(src); return 0; } zxc_compress_opts_t opts = {.level = 3, .block_size = 64 * 1024, .seekable = 1}; const int64_t csize = zxc_compress(src, SRC_SIZE, dst, dst_cap, &opts); if (csize <= 0) { printf("Failed: compress\n"); free(src); free(dst); return 0; } zxc_seekable* s = zxc_seekable_open(dst, (size_t)csize); if (!s) { printf("Failed: open\n"); free(src); free(dst); return 0; } /* First 1000 bytes */ uint8_t out1[1000]; int64_t r = zxc_seekable_decompress_range(s, out1, 1000, 0, 1000); if (r != 1000 || memcmp(src, out1, 1000) != 0) { printf("Failed: first 1000 bytes (r=%lld)\n", (long long)r); if (r == 1000) { for (int i = 0; i < 1000; i++) { if (src[i] != out1[i]) { printf(" first diff at byte %d: src=0x%02x out=0x%02x\n", i, src[i], out1[i]); break; } } } zxc_seekable_free(s); free(src); free(dst); return 0; } /* Middle range spanning multiple blocks */ const uint64_t off = 100 * 1024; const size_t len = 80 * 1024; uint8_t* out2 = malloc(len); if (!out2) { zxc_seekable_free(s); free(src); free(dst); return 0; } r = zxc_seekable_decompress_range(s, out2, len, off, len); if (r != (int64_t)len || memcmp(src + off, out2, len) != 0) { printf("Failed: mid-range\n"); free(out2); zxc_seekable_free(s); free(src); free(dst); return 0; } free(out2); /* Last bytes */ uint8_t out3[512]; r = zxc_seekable_decompress_range(s, out3, 512, SRC_SIZE - 512, 512); if (r != 512 || memcmp(src + SRC_SIZE - 512, out3, 512) != 0) { printf("Failed: tail\n"); zxc_seekable_free(s); free(src); free(dst); return 0; } /* Entire file */ uint8_t* out4 = malloc(SRC_SIZE); if (!out4) { zxc_seekable_free(s); free(src); free(dst); return 0; } r = zxc_seekable_decompress_range(s, out4, SRC_SIZE, 0, SRC_SIZE); if (r != (int64_t)SRC_SIZE || memcmp(src, out4, SRC_SIZE) != 0) { printf("Failed: full range\n"); free(out4); zxc_seekable_free(s); free(src); free(dst); return 0; } free(out4); /* Zero length */ uint8_t dummy; r = zxc_seekable_decompress_range(s, &dummy, 1, 0, 0); if (r != 0) { printf("Failed: zero-length\n"); zxc_seekable_free(s); free(src); free(dst); return 0; } zxc_seekable_free(s); free(src); free(dst); printf("PASS\n\n"); return 1; } int test_seekable_non_seekable_reject() { printf("=== TEST: Seekable - Non-Seekable Archive Rejected ===\n"); const size_t SRC_SIZE = 10000; uint8_t* src = malloc(SRC_SIZE); if (!src) return 0; fill_seek_data(src, SRC_SIZE, 11); const size_t dst_cap = (size_t)zxc_compress_bound(SRC_SIZE); uint8_t* dst = malloc(dst_cap); if (!dst) { free(src); return 0; } zxc_compress_opts_t opts = {.level = 1, .seekable = 0}; const int64_t csize = zxc_compress(src, SRC_SIZE, dst, dst_cap, &opts); if (csize <= 0) { printf("Failed: compress\n"); free(src); free(dst); return 0; } zxc_seekable* s = zxc_seekable_open(dst, (size_t)csize); if (s != NULL) { printf("Failed: expected NULL for non-seekable\n"); zxc_seekable_free(s); free(src); free(dst); return 0; } free(src); free(dst); printf("PASS\n\n"); return 1; } int test_seekable_single_block() { printf("=== TEST: Seekable - Single Block ===\n"); const size_t SRC_SIZE = 1024; uint8_t* src = malloc(SRC_SIZE); if (!src) return 0; fill_seek_data(src, SRC_SIZE, 55); const size_t dst_cap = (size_t)zxc_compress_bound(SRC_SIZE) + 256; uint8_t* dst = malloc(dst_cap); if (!dst) { free(src); return 0; } zxc_compress_opts_t opts = {.level = 1, .seekable = 1}; const int64_t csize = zxc_compress(src, SRC_SIZE, dst, dst_cap, &opts); if (csize <= 0) { printf("Failed: compress\n"); free(src); free(dst); return 0; } zxc_seekable* s = zxc_seekable_open(dst, (size_t)csize); if (!s) { printf("Failed: open\n"); free(src); free(dst); return 0; } if (zxc_seekable_get_num_blocks(s) != 1) { printf("Failed: expected 1 block\n"); zxc_seekable_free(s); free(src); free(dst); return 0; } uint8_t out[100]; int64_t r = zxc_seekable_decompress_range(s, out, 100, 500, 100); if (r != 100 || memcmp(src + 500, out, 100) != 0) { printf("Failed: range data\n"); zxc_seekable_free(s); free(src); free(dst); return 0; } zxc_seekable_free(s); free(src); free(dst); printf("PASS\n\n"); return 1; } int test_seekable_all_levels() { printf("=== TEST: Seekable - All Compression Levels ===\n"); const size_t SRC_SIZE = 128 * 1024; uint8_t* src = malloc(SRC_SIZE); if (!src) return 0; fill_seek_data(src, SRC_SIZE, 33); const size_t dst_cap = (size_t)zxc_compress_bound(SRC_SIZE) + 1024; uint8_t* dst = malloc(dst_cap); uint8_t* dec = malloc(SRC_SIZE); if (!dst || !dec) { free(src); free(dst); free(dec); return 0; } for (int lvl = 1; lvl <= 5; lvl++) { zxc_compress_opts_t opts = {.level = lvl, .block_size = 32 * 1024, .seekable = 1}; const int64_t csize = zxc_compress(src, SRC_SIZE, dst, dst_cap, &opts); if (csize <= 0) { printf("Failed: compress level %d\n", lvl); free(src); free(dst); free(dec); return 0; } zxc_seekable* s = zxc_seekable_open(dst, (size_t)csize); if (!s) { printf("Failed: open level %d\n", lvl); free(src); free(dst); free(dec); return 0; } int64_t r = zxc_seekable_decompress_range(s, dec, SRC_SIZE, 0, SRC_SIZE); if (r != (int64_t)SRC_SIZE || memcmp(src, dec, SRC_SIZE) != 0) { printf("Failed: level %d data mismatch (r=%lld expected=%zu)\n", lvl, (long long)r, SRC_SIZE); if (r == (int64_t)SRC_SIZE) { for (size_t i = 0; i < SRC_SIZE; i++) { if (src[i] != dec[i]) { printf(" first diff at byte %zu: src=0x%02x dec=0x%02x\n", i, src[i], dec[i]); break; } } } zxc_seekable_free(s); free(src); free(dst); free(dec); return 0; } zxc_seekable_free(s); } free(src); free(dst); free(dec); printf("PASS\n\n"); return 1; } int test_seekable_many_blocks() { printf("=== TEST: Seekable - Many Small Blocks ===\n"); /* Use minimum block size (4KB) with 256KB data => 64 blocks. * This stresses the seekable block tracking array (dispatch lines 410-424) * and ensures the seek table handles high block counts correctly. */ const size_t SRC_SIZE = 256 * 1024; uint8_t* src = malloc(SRC_SIZE); if (!src) return 0; fill_seek_data(src, SRC_SIZE, 77); const size_t dst_cap = (size_t)zxc_compress_bound(SRC_SIZE) + 4096; uint8_t* dst = malloc(dst_cap); uint8_t* dec = malloc(SRC_SIZE); if (!dst || !dec) { free(src); free(dst); free(dec); return 0; } /* Compress with minimum block_size = 4096 */ zxc_compress_opts_t opts = {.level = ZXC_LEVEL_DEFAULT, .block_size = 4096, .checksum_enabled = 1, .seekable = 1}; const int64_t csize = zxc_compress(src, SRC_SIZE, dst, dst_cap, &opts); if (csize <= 0) { printf("Failed: compress (%lld)\n", (long long)csize); free(src); free(dst); free(dec); return 0; } /* Open and verify block count = 64 */ zxc_seekable* s = zxc_seekable_open(dst, (size_t)csize); if (!s) { printf("Failed: open\n"); free(src); free(dst); free(dec); return 0; } const uint32_t n_blocks = zxc_seekable_get_num_blocks(s); if (n_blocks != 64) { printf("Failed: expected 64 blocks, got %u\n", n_blocks); zxc_seekable_free(s); free(src); free(dst); free(dec); return 0; } /* Full decompress via seekable API */ int64_t r = zxc_seekable_decompress_range(s, dec, SRC_SIZE, 0, SRC_SIZE); if (r != (int64_t)SRC_SIZE || memcmp(src, dec, SRC_SIZE) != 0) { printf("Failed: full decompress mismatch (r=%lld)\n", (long long)r); zxc_seekable_free(s); free(src); free(dst); free(dec); return 0; } /* Random access: read 100 bytes from the middle of block 32 */ const uint64_t mid_off = 32 * 4096 + 2000; uint8_t spot[100]; r = zxc_seekable_decompress_range(s, spot, 100, mid_off, 100); if (r != 100 || memcmp(src + mid_off, spot, 100) != 0) { printf("Failed: random access at offset %llu\n", (unsigned long long)mid_off); zxc_seekable_free(s); free(src); free(dst); free(dec); return 0; } /* Cross-block read: span block boundary (last 50B of block 15 + first 50B of block 16) */ const uint64_t cross_off = 16 * 4096 - 50; uint8_t cross[100]; r = zxc_seekable_decompress_range(s, cross, 100, cross_off, 100); if (r != 100 || memcmp(src + cross_off, cross, 100) != 0) { printf("Failed: cross-block read at offset %llu\n", (unsigned long long)cross_off); zxc_seekable_free(s); free(src); free(dst); free(dec); return 0; } zxc_seekable_free(s); free(src); free(dst); free(dec); printf("PASS\n\n"); return 1; } int test_seekable_open_file() { printf("=== TEST: Seekable - Open File ===\n"); /* Compress seekable data into a buffer, write to tmpfile, then open via file API */ const size_t SRC_SIZE = 128 * 1024; uint8_t* src = malloc(SRC_SIZE); if (!src) return 0; fill_seek_data(src, SRC_SIZE, 99); const size_t dst_cap = (size_t)zxc_compress_bound(SRC_SIZE) + 1024; uint8_t* dst = malloc(dst_cap); uint8_t* dec = malloc(SRC_SIZE); if (!dst || !dec) { free(src); free(dst); free(dec); return 0; } zxc_compress_opts_t opts = {.level = ZXC_LEVEL_DEFAULT, .block_size = 32 * 1024, .checksum_enabled = 1, .seekable = 1}; const int64_t csize = zxc_compress(src, SRC_SIZE, dst, dst_cap, &opts); if (csize <= 0) { printf("Failed: compress (%lld)\n", (long long)csize); free(src); free(dst); free(dec); return 0; } /* Write compressed data to a temp file */ FILE* tf = tmpfile(); if (!tf) { printf("Failed: tmpfile\n"); free(src); free(dst); free(dec); return 0; } if (fwrite(dst, 1, (size_t)csize, tf) != (size_t)csize) { printf("Failed: fwrite\n"); fclose(tf); free(src); free(dst); free(dec); return 0; } fflush(tf); /* Open via file API */ zxc_seekable* s = zxc_seekable_open_file(tf); if (!s) { printf("Failed: zxc_seekable_open_file returned NULL\n"); fclose(tf); free(src); free(dst); free(dec); return 0; } /* Verify block count: 128KB / 32KB = 4 blocks */ const uint32_t n_blocks = zxc_seekable_get_num_blocks(s); if (n_blocks != 4) { printf("Failed: expected 4 blocks, got %u\n", n_blocks); zxc_seekable_free(s); fclose(tf); free(src); free(dst); free(dec); return 0; } /* Full decompress from file */ int64_t r = zxc_seekable_decompress_range(s, dec, SRC_SIZE, 0, SRC_SIZE); if (r != (int64_t)SRC_SIZE || memcmp(src, dec, SRC_SIZE) != 0) { printf("Failed: full decompress from file (r=%lld)\n", (long long)r); zxc_seekable_free(s); fclose(tf); free(src); free(dst); free(dec); return 0; } /* Random access from file: read 200 bytes spanning block boundary */ const uint64_t cross_off = 32 * 1024 - 100; /* last 100B of block 0 + first 100B of block 1 */ uint8_t cross[200]; r = zxc_seekable_decompress_range(s, cross, 200, cross_off, 200); if (r != 200 || memcmp(src + cross_off, cross, 200) != 0) { printf("Failed: cross-block read from file\n"); zxc_seekable_free(s); fclose(tf); free(src); free(dst); free(dec); return 0; } zxc_seekable_free(s); fclose(tf); /* NULL input rejection */ if (zxc_seekable_open_file(NULL) != NULL) { printf("Failed: NULL not rejected\n"); free(src); free(dst); free(dec); return 0; } free(src); free(dst); free(dec); printf("PASS\n\n"); return 1; } /* Cross-boundary range: decompresses bytes that span exactly two blocks */ int test_seekable_cross_boundary() { printf("=== TEST: Seekable - Cross-Boundary Range ===\n"); const size_t BLK = 64 * 1024; const size_t SRC_SIZE = BLK * 4; /* 4 blocks */ uint8_t* src = malloc(SRC_SIZE); if (!src) return 0; fill_seek_data(src, SRC_SIZE, 123); const size_t dst_cap = (size_t)zxc_compress_bound(SRC_SIZE) + 1024; uint8_t* dst = malloc(dst_cap); if (!dst) { free(src); return 0; } zxc_compress_opts_t opts = {.level = 3, .block_size = BLK, .seekable = 1}; const int64_t csize = zxc_compress(src, SRC_SIZE, dst, dst_cap, &opts); if (csize <= 0) { printf("Failed: compress\n"); free(src); free(dst); return 0; } zxc_seekable* s = zxc_seekable_open(dst, (size_t)csize); if (!s) { printf("Failed: open\n"); free(src); free(dst); return 0; } /* Read 200 bytes starting 100 bytes before the block 0/1 boundary */ const uint64_t boundary = BLK; const uint64_t off = boundary - 100; const size_t len = 200; uint8_t out[200]; int64_t r = zxc_seekable_decompress_range(s, out, sizeof(out), off, len); if (r != (int64_t)len) { printf("Failed: cross-boundary range returned %lld\n", (long long)r); zxc_seekable_free(s); free(src); free(dst); return 0; } if (memcmp(out, src + off, len) != 0) { printf("Failed: cross-boundary data mismatch\n"); zxc_seekable_free(s); free(src); free(dst); return 0; } /* Also test a range spanning 3 blocks */ const uint64_t off3 = BLK * 2 - 100; const size_t len3 = BLK + 200; uint8_t* out3 = malloc(len3); if (!out3) { zxc_seekable_free(s); free(src); free(dst); return 0; } r = zxc_seekable_decompress_range(s, out3, len3, off3, len3); if (r != (int64_t)len3 || memcmp(out3, src + off3, len3) != 0) { printf("Failed: 3-block span mismatch\n"); free(out3); zxc_seekable_free(s); free(src); free(dst); return 0; } free(out3); zxc_seekable_free(s); free(src); free(dst); printf("PASS\n\n"); return 1; } /* Open with truncated data should return NULL */ int test_seekable_truncated_input() { printf("=== TEST: Seekable - Truncated Input Rejected ===\n"); const size_t SRC_SIZE = 64 * 1024; uint8_t* src = malloc(SRC_SIZE); if (!src) return 0; fill_seek_data(src, SRC_SIZE, 44); const size_t dst_cap = (size_t)zxc_compress_bound(SRC_SIZE) + 256; uint8_t* dst = malloc(dst_cap); if (!dst) { free(src); return 0; } zxc_compress_opts_t opts = {.level = 1, .seekable = 1}; const int64_t csize = zxc_compress(src, SRC_SIZE, dst, dst_cap, &opts); if (csize <= 0) { printf("Failed: compress\n"); free(src); free(dst); return 0; } /* Truncate to half */ zxc_seekable* s = zxc_seekable_open(dst, (size_t)(csize / 2)); if (s != NULL) { printf("Failed: should reject truncated data\n"); zxc_seekable_free(s); free(src); free(dst); return 0; } /* Truncate to just header */ s = zxc_seekable_open(dst, 16); if (s != NULL) { printf("Failed: should reject header-only data\n"); zxc_seekable_free(s); free(src); free(dst); return 0; } /* Zero bytes */ s = zxc_seekable_open(dst, 0); if (s != NULL) { printf("Failed: should reject zero-length data\n"); zxc_seekable_free(s); free(src); free(dst); return 0; } free(src); free(dst); printf("PASS\n\n"); return 1; } /* Corrupted SEK block: ensure no crash (no UB) */ int test_seekable_corrupted_sek() { printf("=== TEST: Seekable - Corrupted SEK Block ===\n"); const size_t SRC_SIZE = 64 * 1024; uint8_t* src = malloc(SRC_SIZE); if (!src) return 0; fill_seek_data(src, SRC_SIZE, 66); const size_t dst_cap = (size_t)zxc_compress_bound(SRC_SIZE) + 256; uint8_t* dst = malloc(dst_cap); if (!dst) { free(src); return 0; } zxc_compress_opts_t opts = {.level = 1, .seekable = 1}; const int64_t csize = zxc_compress(src, SRC_SIZE, dst, dst_cap, &opts); if (csize <= 0) { printf("Failed: compress\n"); free(src); free(dst); return 0; } /* Corrupt a byte in the SEK payload area (before footer) */ uint8_t* corrupt = malloc((size_t)csize); if (!corrupt) { free(src); free(dst); return 0; } memcpy(corrupt, dst, (size_t)csize); corrupt[csize - 14] ^= 0xFF; zxc_seekable* s = zxc_seekable_open(corrupt, (size_t)csize); /* May succeed or fail - just ensure no crash */ if (s) { uint8_t out[100]; (void)zxc_seekable_decompress_range(s, out, sizeof(out), 0, 100); zxc_seekable_free(s); } free(corrupt); free(src); free(dst); printf("PASS\n\n"); return 1; } /* Range beyond file end should return error */ int test_seekable_range_out_of_bounds() { printf("=== TEST: Seekable - Out-of-Bounds Range ===\n"); const size_t SRC_SIZE = 32 * 1024; uint8_t* src = malloc(SRC_SIZE); if (!src) return 0; fill_seek_data(src, SRC_SIZE, 22); const size_t dst_cap = (size_t)zxc_compress_bound(SRC_SIZE) + 256; uint8_t* dst = malloc(dst_cap); if (!dst) { free(src); return 0; } zxc_compress_opts_t opts = {.level = 1, .seekable = 1}; const int64_t csize = zxc_compress(src, SRC_SIZE, dst, dst_cap, &opts); if (csize <= 0) { printf("Failed: compress\n"); free(src); free(dst); return 0; } zxc_seekable* s = zxc_seekable_open(dst, (size_t)csize); if (!s) { printf("Failed: open\n"); free(src); free(dst); return 0; } uint8_t out[256]; /* offset past EOF */ int64_t r = zxc_seekable_decompress_range(s, out, sizeof(out), SRC_SIZE + 100, 100); if (r > 0) { printf("Failed: should reject offset past EOF (got %lld)\n", (long long)r); zxc_seekable_free(s); free(src); free(dst); return 0; } /* offset valid but length extends past EOF */ r = zxc_seekable_decompress_range(s, out, sizeof(out), SRC_SIZE - 50, 200); if (r > 0) { printf("Failed: should reject range extending past EOF (got %lld)\n", (long long)r); zxc_seekable_free(s); free(src); free(dst); return 0; } zxc_seekable_free(s); free(src); free(dst); printf("PASS\n\n"); return 1; } /* dst_capacity too small for requested range */ int test_seekable_dst_too_small() { printf("=== TEST: Seekable - Dst Too Small ===\n"); const size_t SRC_SIZE = 32 * 1024; uint8_t* src = malloc(SRC_SIZE); if (!src) return 0; fill_seek_data(src, SRC_SIZE, 91); const size_t dst_cap = (size_t)zxc_compress_bound(SRC_SIZE) + 256; uint8_t* dst = malloc(dst_cap); if (!dst) { free(src); return 0; } zxc_compress_opts_t opts = {.level = 1, .seekable = 1}; const int64_t csize = zxc_compress(src, SRC_SIZE, dst, dst_cap, &opts); if (csize <= 0) { printf("Failed: compress\n"); free(src); free(dst); return 0; } zxc_seekable* s = zxc_seekable_open(dst, (size_t)csize); if (!s) { printf("Failed: open\n"); free(src); free(dst); return 0; } uint8_t out[10]; int64_t r = zxc_seekable_decompress_range(s, out, 10, 0, 1000); if (r > 0) { printf("Failed: should reject insufficient dst capacity (got %lld)\n", (long long)r); zxc_seekable_free(s); free(src); free(dst); return 0; } zxc_seekable_free(s); free(src); free(dst); printf("PASS\n\n"); return 1; } /* Empty file with seekable=1 */ /* Empty file with seekable=1: buffer API rejects NULL src, verify graceful rejection. * Also verify via streaming API (which supports empty files). */ int test_seekable_empty_file() { printf("=== TEST: Seekable - Empty File ===\n"); const size_t dst_cap = (size_t)zxc_compress_bound(0) + 256; uint8_t* dst = malloc(dst_cap); if (!dst) return 0; /* Buffer API: NULL src with size 0 produces a valid empty frame */ zxc_compress_opts_t opts = {.level = 3, .seekable = 1}; const int64_t csize = zxc_compress(NULL, 0, dst, dst_cap, &opts); if (csize <= 0) { printf("Failed: expected valid empty frame (got %lld)\n", (long long)csize); free(dst); return 0; } const uint64_t orig = zxc_get_decompressed_size(dst, (size_t)csize); if (orig != 0) { printf("Failed: empty frame size should be 0 (got %llu)\n", (unsigned long long)orig); free(dst); return 0; } /* Streaming API: empty file via tmpfile() should work */ FILE* fin = tmpfile(); FILE* fout = tmpfile(); if (fin && fout) { int64_t stream_sz = zxc_stream_compress(fin, fout, &opts); if (stream_sz < 0) { printf("Failed: stream compress empty (got %lld)\n", (long long)stream_sz); fclose(fin); fclose(fout); free(dst); return 0; } /* Decompress the stream output */ rewind(fout); FILE* fdec = tmpfile(); if (fdec) { zxc_decompress_opts_t dopts = {.checksum_enabled = 0}; int64_t dsz = zxc_stream_decompress(fout, fdec, &dopts); if (dsz != 0) { printf("Failed: stream decompress empty should return 0 (got %lld)\n", (long long)dsz); fclose(fin); fclose(fout); fclose(fdec); free(dst); return 0; } fclose(fdec); } fclose(fin); fclose(fout); } free(dst); printf("PASS\n\n"); return 1; } /* Seekable without checksum (seekable=1, checksum_enabled=0) */ int test_seekable_no_checksum() { printf("=== TEST: Seekable - No Checksum ===\n"); const size_t SRC_SIZE = 256 * 1024; uint8_t* src = malloc(SRC_SIZE); if (!src) return 0; fill_seek_data(src, SRC_SIZE, 31); const size_t dst_cap = (size_t)zxc_compress_bound(SRC_SIZE) + 1024; uint8_t* dst = malloc(dst_cap); if (!dst) { free(src); return 0; } zxc_compress_opts_t opts = { .level = 3, .block_size = 64 * 1024, .seekable = 1, .checksum_enabled = 0}; const int64_t csize = zxc_compress(src, SRC_SIZE, dst, dst_cap, &opts); if (csize <= 0) { printf("Failed: compress\n"); free(src); free(dst); return 0; } zxc_seekable* s = zxc_seekable_open(dst, (size_t)csize); if (!s) { printf("Failed: open\n"); free(src); free(dst); return 0; } uint8_t out[512]; const uint64_t off = 64 * 1024 + 100; int64_t r = zxc_seekable_decompress_range(s, out, sizeof(out), off, 512); if (r != 512 || memcmp(out, src + off, 512) != 0) { printf("Failed: no-checksum range mismatch\n"); zxc_seekable_free(s); free(src); free(dst); return 0; } /* Full decompress also works */ uint8_t* full = malloc(SRC_SIZE); if (!full) { zxc_seekable_free(s); free(src); free(dst); return 0; } zxc_decompress_opts_t dopts = {.checksum_enabled = 0}; int64_t dsize = zxc_decompress(dst, (size_t)csize, full, SRC_SIZE, &dopts); if (dsize != (int64_t)SRC_SIZE || memcmp(src, full, SRC_SIZE) != 0) { printf("Failed: full decompress mismatch\n"); free(full); zxc_seekable_free(s); free(src); free(dst); return 0; } free(full); zxc_seekable_free(s); free(src); free(dst); printf("PASS\n\n"); return 1; } /* ========================================================================= */ /* Reader-callback API (zxc_seekable_open_reader) */ /* ========================================================================= */ /* In-memory reader: counts invocations so we can assert lazy I/O. */ typedef struct { const uint8_t* data; uint64_t size; int call_count; uint64_t bytes_read; } reader_test_ctx_t; static int64_t reader_test_read_at(void* ctx, void* dst, size_t len, uint64_t offset) { reader_test_ctx_t* m = (reader_test_ctx_t*)ctx; m->call_count++; if (offset > m->size || len > m->size - offset) return -1; memcpy(dst, m->data + offset, len); m->bytes_read += (uint64_t)len; return (int64_t)len; } /* Thread-safe in-memory reader for the MT test. Holds no mutable state, so * concurrent invocations from worker threads are race-free by construction. */ typedef struct { const uint8_t* data; uint64_t size; } reader_test_ctx_mt_t; // cppcheck-suppress constParameterCallback static int64_t reader_test_read_at_mt(void* ctx, void* dst, size_t len, uint64_t offset) { const reader_test_ctx_mt_t* m = (const reader_test_ctx_mt_t*)ctx; if (offset > m->size || len > m->size - offset) return -1; memcpy(dst, m->data + offset, len); return (int64_t)len; } /* Reader that always reports a short read - must be rejected at open(). */ static int64_t reader_short_read_at(void* ctx, void* dst, size_t len, uint64_t offset) { (void)ctx; (void)dst; (void)offset; return (int64_t)(len > 0 ? len - 1 : 0); } /* Reader that returns a negative error code - must propagate. */ static int64_t reader_error_read_at(void* ctx, void* dst, size_t len, uint64_t offset) { (void)ctx; (void)dst; (void)len; (void)offset; return ZXC_ERROR_IO; } /* Open and roundtrip via a user-supplied callback reader (single-threaded). */ int test_seekable_open_reader() { printf("=== TEST: Seekable - Open Reader Callback ===\n"); const size_t SRC_SIZE = 256 * 1024; uint8_t* src = malloc(SRC_SIZE); if (!src) return 0; fill_seek_data(src, SRC_SIZE, 211); const size_t dst_cap = (size_t)zxc_compress_bound(SRC_SIZE) + 1024; uint8_t* dst = malloc(dst_cap); uint8_t* dec = malloc(SRC_SIZE); if (!dst || !dec) { free(src); free(dst); free(dec); return 0; } zxc_compress_opts_t opts = {.level = 3, .block_size = 64 * 1024, .checksum_enabled = 1, .seekable = 1}; const int64_t csize = zxc_compress(src, SRC_SIZE, dst, dst_cap, &opts); if (csize <= 0) { printf("Failed: compress (%lld)\n", (long long)csize); free(src); free(dst); free(dec); return 0; } /* Open through the reader callback */ reader_test_ctx_t mctx = { .data = dst, .size = (uint64_t)csize, .call_count = 0, .bytes_read = 0 }; zxc_reader_t r = { .read_at = reader_test_read_at, .ctx = &mctx, .size = (uint64_t)csize }; zxc_seekable* s = zxc_seekable_open_reader(&r); if (!s) { printf("Failed: zxc_seekable_open_reader returned NULL\n"); free(src); free(dst); free(dec); return 0; } /* open() should perform exactly 3 reads: header, footer, seek block. */ if (mctx.call_count != 3) { printf("Failed: expected 3 reads at open, got %d\n", mctx.call_count); zxc_seekable_free(s); free(src); free(dst); free(dec); return 0; } /* 256KB / 64KB = 4 blocks */ if (zxc_seekable_get_num_blocks(s) != 4) { printf("Failed: expected 4 blocks, got %u\n", zxc_seekable_get_num_blocks(s)); zxc_seekable_free(s); free(src); free(dst); free(dec); return 0; } if (zxc_seekable_get_decompressed_size(s) != SRC_SIZE) { printf("Failed: bad total decompressed size\n"); zxc_seekable_free(s); free(src); free(dst); free(dec); return 0; } /* Full-range decompress */ int64_t n = zxc_seekable_decompress_range(s, dec, SRC_SIZE, 0, SRC_SIZE); if (n != (int64_t)SRC_SIZE || memcmp(src, dec, SRC_SIZE) != 0) { printf("Failed: full range mismatch (n=%lld)\n", (long long)n); zxc_seekable_free(s); free(src); free(dst); free(dec); return 0; } /* Cross-block sub-range */ const uint64_t off = 64 * 1024 - 200; const size_t len = 400; uint8_t cross[400]; n = zxc_seekable_decompress_range(s, cross, len, off, len); if (n != (int64_t)len || memcmp(cross, src + off, len) != 0) { printf("Failed: cross-block sub-range mismatch\n"); zxc_seekable_free(s); free(src); free(dst); free(dec); return 0; } // Lazy I/O: a sub-range inside block 2 only must trigger exactly 1 // extra read. read_at is invoked from inside decompress_range via a // function pointer cppcheck cannot follow, so it folds the before/after // comparison to a constant; suppress the two resulting false positives. const int before = mctx.call_count; n = zxc_seekable_decompress_range(s, cross, len, 128 * 1024 + 10, len); // cppcheck-suppress duplicateExpression const int delta = mctx.call_count - before; // cppcheck-suppress knownConditionTrueFalse if (n != (int64_t)len || delta != 1) { printf("Failed: single-block read should trigger 1 read_at (got %d)\n", delta); zxc_seekable_free(s); free(src); free(dst); free(dec); return 0; } zxc_seekable_free(s); /* NULL reader rejection */ if (zxc_seekable_open_reader(NULL) != NULL) { printf("Failed: NULL reader not rejected\n"); free(src); free(dst); free(dec); return 0; } /* Missing read_at rejection */ zxc_reader_t bad = { .read_at = NULL, .ctx = NULL, .size = 100 }; if (zxc_seekable_open_reader(&bad) != NULL) { printf("Failed: NULL read_at not rejected\n"); free(src); free(dst); free(dec); return 0; } /* Zero size rejection */ zxc_reader_t empty = { .read_at = reader_test_read_at, .ctx = &mctx, .size = 0 }; if (zxc_seekable_open_reader(&empty) != NULL) { printf("Failed: zero size not rejected\n"); free(src); free(dst); free(dec); return 0; } /* Short read at open: must reject */ zxc_reader_t short_r = { .read_at = reader_short_read_at, .ctx = NULL, .size = (uint64_t)csize }; if (zxc_seekable_open_reader(&short_r) != NULL) { printf("Failed: short-read reader not rejected\n"); free(src); free(dst); free(dec); return 0; } /* Negative return at open: must reject */ zxc_reader_t err_r = { .read_at = reader_error_read_at, .ctx = NULL, .size = (uint64_t)csize }; if (zxc_seekable_open_reader(&err_r) != NULL) { printf("Failed: error-returning reader not rejected\n"); free(src); free(dst); free(dec); return 0; } free(src); free(dst); free(dec); printf("PASS\n\n"); return 1; } /* Multi-threaded decompression through a reader callback. * The callback only does memcpy on const data, so it is naturally thread-safe. */ int test_seekable_open_reader_mt() { printf("=== TEST: Seekable - Open Reader Callback (MT) ===\n"); const size_t SRC_SIZE = 1 * 1024 * 1024; /* 1 MB => 16 x 64KB blocks */ uint8_t* src = malloc(SRC_SIZE); if (!src) return 0; fill_seek_data(src, SRC_SIZE, 137); const size_t dst_cap = (size_t)zxc_compress_bound(SRC_SIZE) + 1024; uint8_t* dst = malloc(dst_cap); uint8_t* dec = malloc(SRC_SIZE); if (!dst || !dec) { free(src); free(dst); free(dec); return 0; } zxc_compress_opts_t opts = {.level = 3, .block_size = 64 * 1024, .checksum_enabled = 1, .seekable = 1}; const int64_t csize = zxc_compress(src, SRC_SIZE, dst, dst_cap, &opts); if (csize <= 0) { printf("Failed: compress (%lld)\n", (long long)csize); free(src); free(dst); free(dec); return 0; } /* Use the stateless reader: worker threads call it concurrently. */ reader_test_ctx_mt_t mctx = { .data = dst, .size = (uint64_t)csize }; zxc_reader_t r = { .read_at = reader_test_read_at_mt, .ctx = &mctx, .size = (uint64_t)csize }; zxc_seekable* s = zxc_seekable_open_reader(&r); if (!s) { printf("Failed: open_reader\n"); free(src); free(dst); free(dec); return 0; } int64_t n = zxc_seekable_decompress_range_mt(s, dec, SRC_SIZE, 0, SRC_SIZE, 4); if (n != (int64_t)SRC_SIZE || memcmp(src, dec, SRC_SIZE) != 0) { printf("Failed: MT full range mismatch (n=%lld)\n", (long long)n); zxc_seekable_free(s); free(src); free(dst); free(dec); return 0; } zxc_seekable_free(s); free(src); free(dst); free(dec); printf("PASS\n\n"); return 1; } /* Seekable with checksum (seekable=1, checksum_enabled=1) */ int test_seekable_with_checksum() { printf("=== TEST: Seekable - With Checksum ===\n"); const size_t SRC_SIZE = 256 * 1024; uint8_t* src = malloc(SRC_SIZE); if (!src) return 0; fill_seek_data(src, SRC_SIZE, 47); const size_t dst_cap = (size_t)zxc_compress_bound(SRC_SIZE) + 1024; uint8_t* dst = malloc(dst_cap); if (!dst) { free(src); return 0; } zxc_compress_opts_t opts = { .level = 3, .block_size = 64 * 1024, .seekable = 1, .checksum_enabled = 1}; const int64_t csize = zxc_compress(src, SRC_SIZE, dst, dst_cap, &opts); if (csize <= 0) { printf("Failed: compress\n"); free(src); free(dst); return 0; } /* Seekable random access */ zxc_seekable* s = zxc_seekable_open(dst, (size_t)csize); if (!s) { printf("Failed: open\n"); free(src); free(dst); return 0; } /* Range from block 0 */ uint8_t out[512]; int64_t r = zxc_seekable_decompress_range(s, out, sizeof(out), 0, 512); if (r != 512 || memcmp(out, src, 512) != 0) { printf("Failed: checksum range head mismatch\n"); zxc_seekable_free(s); free(src); free(dst); return 0; } /* Range spanning blocks 1-2 */ const uint64_t off = 64 * 1024 + 100; r = zxc_seekable_decompress_range(s, out, sizeof(out), off, 512); if (r != 512 || memcmp(out, src + off, 512) != 0) { printf("Failed: checksum range mid mismatch\n"); zxc_seekable_free(s); free(src); free(dst); return 0; } /* Full decompress with checksum verification */ uint8_t* full = malloc(SRC_SIZE); if (!full) { zxc_seekable_free(s); free(src); free(dst); return 0; } zxc_decompress_opts_t dopts = {.checksum_enabled = 1}; int64_t dsize = zxc_decompress(dst, (size_t)csize, full, SRC_SIZE, &dopts); if (dsize != (int64_t)SRC_SIZE || memcmp(src, full, SRC_SIZE) != 0) { printf("Failed: full decompress with checksum mismatch\n"); free(full); zxc_seekable_free(s); free(src); free(dst); return 0; } free(full); zxc_seekable_free(s); free(src); free(dst); printf("PASS\n\n"); return 1; } int test_seekable_work_buf_tail_pad(void) { printf("=== TEST: Seekable - full-size blocks at default block_size ===\n"); const size_t SRC_SIZE = 1536 * 1024; const size_t BLK = 512 * 1024; uint8_t* const src = (uint8_t*)malloc(SRC_SIZE); if (!src) { printf("Failed: malloc src\n"); return 0; } gen_lz_data(src, SRC_SIZE); const size_t dst_cap = (size_t)zxc_compress_bound(SRC_SIZE) + 4096; uint8_t* const dst = (uint8_t*)malloc(dst_cap); uint8_t* const dec = (uint8_t*)malloc(SRC_SIZE); if (!dst || !dec) { printf("Failed: malloc dst/dec\n"); free(src); free(dst); free(dec); return 0; } zxc_compress_opts_t opts = {.level = ZXC_LEVEL_DEFAULT, .block_size = BLK, .checksum_enabled = 1, .seekable = 1}; const int64_t csize = zxc_compress(src, SRC_SIZE, dst, dst_cap, &opts); if (csize <= 0) { printf("Failed: compress returned %lld\n", (long long)csize); free(src); free(dst); free(dec); return 0; } zxc_seekable* const s = zxc_seekable_open(dst, (size_t)csize); if (!s) { printf("Failed: zxc_seekable_open returned NULL\n"); free(src); free(dst); free(dec); return 0; } if (zxc_seekable_get_num_blocks(s) < 2) { printf("Failed: need >= 2 full blocks, got %u\n", zxc_seekable_get_num_blocks(s)); zxc_seekable_free(s); free(src); free(dst); free(dec); return 0; } const int64_t r = zxc_seekable_decompress_range(s, dec, SRC_SIZE, 0, SRC_SIZE); if (r != (int64_t)SRC_SIZE || memcmp(src, dec, SRC_SIZE) != 0) { printf("Failed: full-range decompress returned %lld (expected %zu)\n", (long long)r, SRC_SIZE); zxc_seekable_free(s); free(src); free(dst); free(dec); return 0; } const uint64_t mid_off = 200 * 1024; const size_t mid_len = 100 * 1024; uint8_t* const mid = (uint8_t*)malloc(mid_len); if (!mid) { printf("Failed: malloc mid\n"); zxc_seekable_free(s); free(src); free(dst); free(dec); return 0; } const int64_t r2 = zxc_seekable_decompress_range(s, mid, mid_len, mid_off, mid_len); if (r2 != (int64_t)mid_len || memcmp(src + mid_off, mid, mid_len) != 0) { printf("Failed: mid-block decompress returned %lld (expected %zu)\n", (long long)r2, mid_len); free(mid); zxc_seekable_free(s); free(src); free(dst); free(dec); return 0; } free(mid); zxc_seekable_free(s); free(src); free(dst); free(dec); printf("PASS\n\n"); return 1; } zxc-0.13.0/tests/test_seekable_mt.c000066400000000000000000000130631522536370400172310ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ #include "test_common.h" int test_seekable_mt_roundtrip() { printf("=== TEST: Seekable MT - Roundtrip (4 threads) ===\n"); const size_t SRC_SIZE = 512 * 1024; uint8_t* src = malloc(SRC_SIZE); if (!src) return 0; fill_seek_data(src, SRC_SIZE, 55); const size_t dst_cap = (size_t)zxc_compress_bound(SRC_SIZE) + 1024; uint8_t* dst = malloc(dst_cap); uint8_t* dec = malloc(SRC_SIZE); if (!dst || !dec) { free(src); free(dst); free(dec); return 0; } /* Compress with small blocks to create many blocks */ zxc_compress_opts_t opts = {.level = 3, .block_size = 64 * 1024, .seekable = 1}; const int64_t csize = zxc_compress(src, SRC_SIZE, dst, dst_cap, &opts); if (csize <= 0) { printf("Failed: compress\n"); free(src); free(dst); free(dec); return 0; } zxc_seekable* s = zxc_seekable_open(dst, (size_t)csize); if (!s) { printf("Failed: open\n"); free(src); free(dst); free(dec); return 0; } /* Full decompression with 4 threads */ int64_t r = zxc_seekable_decompress_range_mt(s, dec, SRC_SIZE, 0, SRC_SIZE, 4); if (r != (int64_t)SRC_SIZE || memcmp(src, dec, SRC_SIZE) != 0) { printf("Failed: MT decompress mismatch\n"); zxc_seekable_free(s); free(src); free(dst); free(dec); return 0; } zxc_seekable_free(s); free(src); free(dst); free(dec); printf("PASS\n\n"); return 1; } int test_seekable_mt_single_block() { printf("=== TEST: Seekable MT - Single Block Fallback ===\n"); const size_t SRC_SIZE = 32 * 1024; uint8_t* src = malloc(SRC_SIZE); if (!src) return 0; fill_seek_data(src, SRC_SIZE, 88); const size_t dst_cap = (size_t)zxc_compress_bound(SRC_SIZE) + 1024; uint8_t* dst = malloc(dst_cap); uint8_t* dec = malloc(SRC_SIZE); if (!dst || !dec) { free(src); free(dst); free(dec); return 0; } /* Compress with block_size >= SRC_SIZE => only 1 block */ zxc_compress_opts_t opts = {.level = 3, .block_size = 64 * 1024, .seekable = 1}; const int64_t csize = zxc_compress(src, SRC_SIZE, dst, dst_cap, &opts); if (csize <= 0) { printf("Failed: compress\n"); free(src); free(dst); free(dec); return 0; } zxc_seekable* s = zxc_seekable_open(dst, (size_t)csize); if (!s) { printf("Failed: open\n"); free(src); free(dst); free(dec); return 0; } /* Should fallback to ST since only 1 block */ int64_t r = zxc_seekable_decompress_range_mt(s, dec, SRC_SIZE, 0, SRC_SIZE, 4); if (r != (int64_t)SRC_SIZE || memcmp(src, dec, SRC_SIZE) != 0) { printf("Failed: single-block MT mismatch\n"); zxc_seekable_free(s); free(src); free(dst); free(dec); return 0; } zxc_seekable_free(s); free(src); free(dst); free(dec); printf("PASS\n\n"); return 1; } int test_seekable_mt_random_access() { printf("=== TEST: Seekable MT - Random Access ===\n"); const size_t SRC_SIZE = 512 * 1024; uint8_t* src = malloc(SRC_SIZE); if (!src) return 0; fill_seek_data(src, SRC_SIZE, 11); const size_t dst_cap = (size_t)zxc_compress_bound(SRC_SIZE) + 1024; uint8_t* dst = malloc(dst_cap); if (!dst) { free(src); return 0; } zxc_compress_opts_t opts = {.level = 3, .block_size = 64 * 1024, .seekable = 1}; const int64_t csize = zxc_compress(src, SRC_SIZE, dst, dst_cap, &opts); if (csize <= 0) { printf("Failed: compress\n"); free(src); free(dst); return 0; } zxc_seekable* s = zxc_seekable_open(dst, (size_t)csize); if (!s) { printf("Failed: open\n"); free(src); free(dst); return 0; } /* Mid-range spanning 3+ blocks with MT */ const uint64_t off = 100 * 1024; const size_t len = 200 * 1024; uint8_t* out = malloc(len); if (!out) { zxc_seekable_free(s); free(src); free(dst); return 0; } int64_t r = zxc_seekable_decompress_range_mt(s, out, len, off, len, 4); if (r != (int64_t)len || memcmp(src + off, out, len) != 0) { printf("Failed: MT random access mismatch\n"); free(out); zxc_seekable_free(s); free(src); free(dst); return 0; } free(out); zxc_seekable_free(s); free(src); free(dst); printf("PASS\n\n"); return 1; } int test_seekable_mt_full_file() { printf("=== TEST: Seekable MT - Full File (auto threads) ===\n"); const size_t SRC_SIZE = 1024 * 1024; /* 1 MB */ uint8_t* src = malloc(SRC_SIZE); if (!src) return 0; fill_seek_data(src, SRC_SIZE, 7); const size_t dst_cap = (size_t)zxc_compress_bound(SRC_SIZE) + 1024; uint8_t* dst = malloc(dst_cap); uint8_t* dec = malloc(SRC_SIZE); if (!dst || !dec) { free(src); free(dst); free(dec); return 0; } /* 16 blocks of 64K */ zxc_compress_opts_t opts = {.level = 3, .block_size = 64 * 1024, .seekable = 1}; const int64_t csize = zxc_compress(src, SRC_SIZE, dst, dst_cap, &opts); if (csize <= 0) { printf("Failed: compress\n"); free(src); free(dst); free(dec); return 0; } zxc_seekable* s = zxc_seekable_open(dst, (size_t)csize); if (!s) { printf("Failed: open\n"); free(src); free(dst); free(dec); return 0; } /* Decompress with auto-detect threads (n_threads=0) */ int64_t r = zxc_seekable_decompress_range_mt(s, dec, SRC_SIZE, 0, SRC_SIZE, 0); if (r != (int64_t)SRC_SIZE || memcmp(src, dec, SRC_SIZE) != 0) { printf("Failed: MT full file mismatch\n"); zxc_seekable_free(s); free(src); free(dst); free(dec); return 0; } zxc_seekable_free(s); free(src); free(dst); free(dec); printf("PASS\n\n"); return 1; } zxc-0.13.0/tests/test_static_ctx.c000066400000000000000000000316711522536370400171300ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ #include "test_common.h" #if defined(_WIN32) #include static void* test_aligned_alloc(size_t alignment, size_t size) { return _aligned_malloc(size, alignment); } static void test_aligned_free(void* p) { _aligned_free(p); } #else static void* test_aligned_alloc(size_t alignment, size_t size) { void* p = NULL; if (posix_memalign(&p, alignment, size) != 0) return NULL; return p; } static void test_aligned_free(void* p) { free(p); } #endif /* Helper: produce a deterministic compressible payload. */ static void fill_payload(uint8_t* dst, size_t n) { for (size_t i = 0; i < n; ++i) dst[i] = (uint8_t)((i * 31u) ^ (i >> 8)); } /* Roundtrip through a caller-allocated cctx + dctx workspace, at every level. */ int test_static_ctx_roundtrip_all_levels(void) { printf("=== TEST: Static Context API - roundtrip at every level ===\n"); const size_t block_size = 64 * 1024; const size_t src_sz = 200 * 1024; /* spans multiple blocks */ uint8_t* const src = (uint8_t*)malloc(src_sz); if (!src) { printf(" [FAIL] malloc(src)\n"); return 0; } fill_payload(src, src_sz); const size_t cap = (size_t)zxc_compress_bound(src_sz); uint8_t* const enc = (uint8_t*)malloc(cap); uint8_t* const dec = (uint8_t*)malloc(src_sz); if (!enc || !dec) { printf(" [FAIL] malloc(enc/dec)\n"); free(src); free(enc); free(dec); return 0; } for (int lvl = zxc_min_level(); lvl <= zxc_max_level(); ++lvl) { /* Size the cctx workspace exactly. */ const size_t cctx_ws_sz = zxc_static_cctx_workspace_size(block_size, lvl); if (cctx_ws_sz == 0) { printf(" [FAIL] level %d: cctx_ws_sz == 0\n", lvl); goto fail; } void* const cctx_ws = test_aligned_alloc(64, cctx_ws_sz); if (!cctx_ws) { printf(" [FAIL] level %d: aligned_alloc(cctx_ws)\n", lvl); goto fail; } zxc_compress_opts_t copts = { .level = lvl, .block_size = block_size, .checksum_enabled = 1}; zxc_cctx* const cctx = zxc_init_static_cctx(cctx_ws, cctx_ws_sz, &copts); if (!cctx) { printf(" [FAIL] level %d: zxc_init_static_cctx returned NULL\n", lvl); test_aligned_free(cctx_ws); goto fail; } const int64_t csz = zxc_compress_cctx(cctx, src, src_sz, enc, cap, NULL); zxc_free_cctx(cctx); /* no-op for static */ test_aligned_free(cctx_ws); if (csz <= 0) { printf(" [FAIL] level %d: compress returned %lld\n", lvl, (long long)csz); goto fail; } /* Size + init the dctx workspace. */ const size_t dctx_ws_sz = zxc_static_dctx_workspace_size(block_size); if (dctx_ws_sz == 0) { printf(" [FAIL] level %d: dctx_ws_sz == 0\n", lvl); goto fail; } void* const dctx_ws = test_aligned_alloc(64, dctx_ws_sz); if (!dctx_ws) { printf(" [FAIL] level %d: aligned_alloc(dctx_ws)\n", lvl); goto fail; } zxc_dctx* const dctx = zxc_init_static_dctx(dctx_ws, dctx_ws_sz, block_size); if (!dctx) { printf(" [FAIL] level %d: zxc_init_static_dctx returned NULL\n", lvl); test_aligned_free(dctx_ws); goto fail; } zxc_decompress_opts_t dopts = {.checksum_enabled = 1}; const int64_t dsz = zxc_decompress_dctx(dctx, enc, (size_t)csz, dec, src_sz, &dopts); zxc_free_dctx(dctx); /* no-op for static */ test_aligned_free(dctx_ws); if (dsz != (int64_t)src_sz || memcmp(src, dec, src_sz) != 0) { printf(" [FAIL] level %d: roundtrip mismatch (dsz=%lld)\n", lvl, (long long)dsz); goto fail; } printf(" [PASS] level %d: roundtrip OK (%lld bytes -> %lld bytes)\n", lvl, (long long)src_sz, (long long)csz); } free(src); free(enc); free(dec); return 1; fail: free(src); free(enc); free(dec); return 0; } /* Verify the workspace sizers reject invalid inputs and accept the smallest * valid configuration. */ int test_static_ctx_size_query(void) { printf("=== TEST: Static Context API - workspace_size queries ===\n"); /* Invalid: zero block_size. */ if (zxc_static_cctx_workspace_size(0, ZXC_LEVEL_DEFAULT) != 0) { printf(" [FAIL] cctx_size(0) should be 0\n"); return 0; } if (zxc_static_dctx_workspace_size(0) != 0) { printf(" [FAIL] dctx_size(0) should be 0\n"); return 0; } /* Invalid: non-power-of-two block_size. */ if (zxc_static_cctx_workspace_size(63 * 1024, ZXC_LEVEL_DEFAULT) != 0) { printf(" [FAIL] cctx_size(non-pow2) should be 0\n"); return 0; } /* Invalid: out-of-range level. */ if (zxc_static_cctx_workspace_size(64 * 1024, 0) != 0) { printf(" [FAIL] cctx_size(level=0) should be 0\n"); return 0; } if (zxc_static_cctx_workspace_size(64 * 1024, 99) != 0) { printf(" [FAIL] cctx_size(level=99) should be 0\n"); return 0; } /* Valid sizes are strictly increasing across levels (level 6 adds opt_scratch). */ const size_t s3 = zxc_static_cctx_workspace_size(64 * 1024, 3); const size_t s5 = zxc_static_cctx_workspace_size(64 * 1024, 5); const size_t s6 = zxc_static_cctx_workspace_size(64 * 1024, 6); if (s3 == 0 || s5 == 0 || s6 == 0) { printf(" [FAIL] one of s3/s5/s6 is 0\n"); return 0; } if (s3 != s5) { printf(" [FAIL] s3 (%zu) should equal s5 (%zu); only level 6 adds opt_scratch\n", s3, s5); return 0; } if (s6 <= s5) { printf(" [FAIL] s6 (%zu) should exceed s5 (%zu)\n", s6, s5); return 0; } printf(" [PASS] level-1..5 share workspace size; level 6 adds opt_scratch\n"); return 1; } /* Verify that init returns NULL when the workspace is too small. */ int test_static_ctx_workspace_too_small(void) { printf("=== TEST: Static Context API - workspace too small ===\n"); const size_t block_size = 64 * 1024; const size_t needed = zxc_static_cctx_workspace_size(block_size, ZXC_LEVEL_DEFAULT); /* Provide exactly one byte less than needed. */ void* const ws = test_aligned_alloc(64, needed); if (!ws) { printf(" [FAIL] aligned_alloc\n"); return 0; } zxc_compress_opts_t opts = {.level = ZXC_LEVEL_DEFAULT, .block_size = block_size}; if (zxc_init_static_cctx(ws, needed - 1, &opts) != NULL) { printf(" [FAIL] init should reject undersized workspace\n"); test_aligned_free(ws); return 0; } /* Same size: should succeed. */ zxc_cctx* const cctx = zxc_init_static_cctx(ws, needed, &opts); if (!cctx) { printf(" [FAIL] init should accept exact-sized workspace\n"); test_aligned_free(ws); return 0; } zxc_free_cctx(cctx); /* no-op */ test_aligned_free(ws); printf(" [PASS] init rejects undersized, accepts exact-sized workspace\n"); return 1; } /* Verify the block_size lock on a static cctx: compressing with a different * block_size in opts must return ZXC_ERROR_BAD_BLOCK_SIZE without crashing. */ int test_static_ctx_block_size_locked(void) { printf("=== TEST: Static Context API - block_size lock ===\n"); const size_t pinned_bs = 64 * 1024; const size_t ws_sz = zxc_static_cctx_workspace_size(pinned_bs, ZXC_LEVEL_DEFAULT); void* const ws = test_aligned_alloc(64, ws_sz); if (!ws) { printf(" [FAIL] aligned_alloc\n"); return 0; } zxc_compress_opts_t opts = {.level = ZXC_LEVEL_DEFAULT, .block_size = pinned_bs}; zxc_cctx* const cctx = zxc_init_static_cctx(ws, ws_sz, &opts); if (!cctx) { printf(" [FAIL] init_static_cctx\n"); test_aligned_free(ws); return 0; } /* A subsequent compress with a different block_size must fail. */ uint8_t src[256] = {0}; uint8_t dst[1024]; zxc_compress_opts_t opts2 = {.level = ZXC_LEVEL_DEFAULT, .block_size = pinned_bs * 2}; const int64_t rc = zxc_compress_cctx(cctx, src, sizeof(src), dst, sizeof(dst), &opts2); if (rc != ZXC_ERROR_BAD_BLOCK_SIZE) { printf(" [FAIL] expected ZXC_ERROR_BAD_BLOCK_SIZE, got %lld\n", (long long)rc); zxc_free_cctx(cctx); test_aligned_free(ws); return 0; } /* Same block_size still works. */ const int64_t rc2 = zxc_compress_cctx(cctx, src, sizeof(src), dst, sizeof(dst), NULL); if (rc2 <= 0) { printf(" [FAIL] same-block_size compress returned %lld\n", (long long)rc2); zxc_free_cctx(cctx); test_aligned_free(ws); return 0; } zxc_free_cctx(cctx); test_aligned_free(ws); printf(" [PASS] mismatched block_size rejected; matching one accepted\n"); return 1; } /* Regression: a static cctx carved below ZXC_LEVEL_DENSITY has no opt_scratch * and its workspace cannot grow. A per-call raise into the optimal-parser tier * must be rejected with ZXC_ERROR_BAD_LEVEL - before the fix it silently * heap-allocated a replacement workspace (violating the no-allocation * contract) and leaked it, or crashed on the NULL scratch via the frame API. */ int test_static_ctx_level_raise_rejected(void) { printf("=== TEST: Static Context API - level raise rejected ===\n"); const size_t pinned_bs = 64 * 1024; const size_t ws_sz = zxc_static_cctx_workspace_size(pinned_bs, ZXC_LEVEL_DEFAULT); void* const ws = test_aligned_alloc(64, ws_sz); if (!ws) { printf(" [FAIL] aligned_alloc\n"); return 0; } zxc_compress_opts_t opts = {.level = ZXC_LEVEL_DEFAULT, .block_size = pinned_bs}; zxc_cctx* const cctx = zxc_init_static_cctx(ws, ws_sz, &opts); if (!cctx) { printf(" [FAIL] init_static_cctx\n"); test_aligned_free(ws); return 0; } uint8_t src[256] = {0}; uint8_t dst[1024]; /* Frame API: raise to 7 must fail cleanly. */ zxc_compress_opts_t raise = {.level = ZXC_LEVEL_ULTRA, .block_size = pinned_bs}; const int64_t rc = zxc_compress_cctx(cctx, src, sizeof(src), dst, sizeof(dst), &raise); if (rc != ZXC_ERROR_BAD_LEVEL) { printf(" [FAIL] frame raise: expected ZXC_ERROR_BAD_LEVEL, got %lld\n", (long long)rc); goto fail; } /* Block API: raise to 6 must fail cleanly too. */ zxc_compress_opts_t raise6 = {.level = ZXC_LEVEL_DENSITY, .block_size = pinned_bs}; const int64_t rc2 = zxc_compress_block(cctx, src, sizeof(src), dst, sizeof(dst), &raise6); if (rc2 != ZXC_ERROR_BAD_LEVEL) { printf(" [FAIL] block raise: expected ZXC_ERROR_BAD_LEVEL, got %lld\n", (long long)rc2); goto fail; } /* The context is still usable at its pinned level afterwards. */ const int64_t rc3 = zxc_compress_cctx(cctx, src, sizeof(src), dst, sizeof(dst), NULL); if (rc3 <= 0) { printf(" [FAIL] pinned-level compress after rejection returned %lld\n", (long long)rc3); goto fail; } /* A static workspace carved AT the dense tier accepts its own level. */ { const size_t ws7_sz = zxc_static_cctx_workspace_size(pinned_bs, ZXC_LEVEL_ULTRA); void* const ws7 = test_aligned_alloc(64, ws7_sz); if (!ws7) { printf(" [FAIL] aligned_alloc (level 7 ws)\n"); goto fail; } zxc_compress_opts_t opts7 = {.level = ZXC_LEVEL_ULTRA, .block_size = pinned_bs}; zxc_cctx* const cctx7 = zxc_init_static_cctx(ws7, ws7_sz, &opts7); if (!cctx7 || zxc_compress_cctx(cctx7, src, sizeof(src), dst, sizeof(dst), &opts7) <= 0) { printf(" [FAIL] level-7 static cctx should compress at level 7\n"); test_aligned_free(ws7); goto fail; } test_aligned_free(ws7); } zxc_free_cctx(cctx); test_aligned_free(ws); printf(" [PASS] dense-tier raise rejected, pinned level still works\n"); return 1; fail: zxc_free_cctx(cctx); test_aligned_free(ws); return 0; } /* Verify NULL inputs are gracefully rejected. */ int test_static_ctx_null_inputs(void) { printf("=== TEST: Static Context API - NULL inputs ===\n"); zxc_compress_opts_t opts = {.level = 3, .block_size = 4096}; if (zxc_init_static_cctx(NULL, 65536, &opts) != NULL) { printf(" [FAIL] init_static_cctx(NULL workspace) should fail\n"); return 0; } uint8_t ws[16384]; if (zxc_init_static_cctx(ws, sizeof(ws), NULL) != NULL) { printf(" [FAIL] init_static_cctx(NULL opts) should fail\n"); return 0; } if (zxc_init_static_dctx(NULL, 65536, 4096) != NULL) { printf(" [FAIL] init_static_dctx(NULL workspace) should fail\n"); return 0; } /* zxc_free_*ctx must accept a NULL pointer (idempotency). */ zxc_free_cctx(NULL); zxc_free_dctx(NULL); printf(" [PASS] NULL inputs rejected; NULL free is idempotent\n"); return 1; } zxc-0.13.0/tests/test_stream_api.c000066400000000000000000001002021522536370400170720ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ #include "test_common.h" // Checks that the stream decompression can accept NULL output (Integrity Check Mode) int test_null_output_decompression() { printf("=== TEST: Unit - NULL Output Decompression (Integrity Check) ===\n"); size_t size = 64 * 1024; uint8_t* input = malloc(size); if (!input) return 0; gen_lz_data(input, size); FILE* f_in = tmpfile(); FILE* f_comp = tmpfile(); if (!f_in || !f_comp) { if (f_in) fclose(f_in); if (f_comp) fclose(f_comp); free(input); return 0; } fwrite(input, 1, size, f_in); fseek(f_in, 0, SEEK_SET); // Compress with checksum zxc_compress_opts_t _sco3 = {.n_threads = 1, .level = 3, .checksum_enabled = 1}; if (zxc_stream_compress(f_in, f_comp, &_sco3) < 0) { printf("Compression Failed!\n"); fclose(f_in); fclose(f_comp); free(input); return 0; } fseek(f_comp, 0, SEEK_SET); // Decompress with NULL output // This should return the decompressed size but write nothing zxc_decompress_opts_t _sdo4 = {.n_threads = 1, .checksum_enabled = 1}; int64_t d_sz = zxc_stream_decompress(f_comp, NULL, &_sdo4); if (d_sz != (int64_t)size) { printf("Failed: Expected size %zu, got %lld\n", size, (long long)d_sz); fclose(f_in); fclose(f_comp); free(input); return 0; } printf("PASS\n\n"); fclose(f_in); fclose(f_comp); free(input); return 1; } // Checks API robustness against invalid arguments int test_invalid_arguments() { printf("=== TEST: Unit - Invalid Arguments ===\n"); FILE* f = tmpfile(); if (!f) return 0; FILE* f_valid = tmpfile(); if (!f_valid) { fclose(f); return 0; } // Prepare a valid compressed stream for decompression tests zxc_compress_opts_t _sco5 = {.n_threads = 1, .level = 1, .checksum_enabled = 0}; zxc_stream_compress(f, f_valid, &_sco5); rewind(f_valid); // 1. Input NULL -> Must fail zxc_compress_opts_t _sco6 = {.n_threads = 1, .level = 5, .checksum_enabled = 0}; if (zxc_stream_compress(NULL, f, &_sco6) >= 0) { printf("Failed: Should return < 0 when Input is NULL\n"); fclose(f); return 0; } // 2. Output NULL -> Must SUCCEED (Benchmark / Dry-Run Mode) zxc_compress_opts_t _sco7 = {.n_threads = 1, .level = 5, .checksum_enabled = 0}; if (zxc_stream_compress(f, NULL, &_sco7) < 0) { printf("Failed: Should allow NULL Output (Benchmark mode support)\n"); fclose(f); return 0; } // 3. Decompression Input NULL -> Must fail zxc_decompress_opts_t _sdo8 = {.n_threads = 1, .checksum_enabled = 0}; if (zxc_stream_decompress(NULL, f, &_sdo8) >= 0) { printf("Failed: Decompress should return < 0 when Input is NULL\n"); fclose(f); return 0; } // 3b. Decompression Output NULL -> Must SUCCEED (Benchmark mode) zxc_decompress_opts_t _sdo9 = {.n_threads = 1, .checksum_enabled = 0}; if (zxc_stream_decompress(f_valid, NULL, &_sdo9) < 0) { printf("Failed: Decompress should allow NULL Output (Benchmark mode support)\n"); fclose(f_valid); return 0; } // 4. zxc_compress NULL checks zxc_compress_opts_t _co10 = {.level = 3, .checksum_enabled = 0}; if (zxc_compress(NULL, 100, (void*)1, 100, &_co10) >= 0) { printf("Failed: zxc_compress should return < 0 when src is NULL\n"); fclose(f); return 0; } zxc_compress_opts_t _co11 = {.level = 3, .checksum_enabled = 0}; if (zxc_compress((void*)1, 100, NULL, 100, &_co11) >= 0) { printf("Failed: zxc_compress should return < 0 when dst is NULL\n"); fclose(f); return 0; } // 5. zxc_decompress NULL checks zxc_decompress_opts_t _do12 = {.checksum_enabled = 0}; if (zxc_decompress(NULL, 100, (void*)1, 100, &_do12) >= 0) { printf("Failed: zxc_decompress should return < 0 when src is NULL\n"); fclose(f); return 0; } zxc_decompress_opts_t _do13 = {.checksum_enabled = 0}; if (zxc_decompress((void*)1, 100, NULL, 100, &_do13) >= 0) { printf("Failed: zxc_decompress should return < 0 when dst is NULL\n"); fclose(f); return 0; } // 6. zxc_compress_bound overflow check if (zxc_compress_bound(SIZE_MAX) != 0) { printf("Failed: zxc_compress_bound should return 0 on overflow\n"); fclose(f); return 0; } printf("PASS\n\n"); fclose(f); return 1; } // Checks behavior with truncated compressed input int test_truncated_input() { printf("=== TEST: Unit - Truncated Input (Stream) ===\n"); const size_t SRC_SIZE = 1024; uint8_t src[1024]; gen_lz_data(src, SRC_SIZE); size_t cap = (size_t)zxc_compress_bound(SRC_SIZE); uint8_t* compressed = malloc(cap); uint8_t* decomp_buf = malloc(SRC_SIZE); if (!compressed || !decomp_buf) { free(compressed); free(decomp_buf); return 0; } zxc_compress_opts_t _co14 = {.level = 3, .checksum_enabled = 1}; int64_t comp_sz = zxc_compress(src, SRC_SIZE, compressed, cap, &_co14); if (comp_sz <= 0) { printf("Prepare failed\n"); free(compressed); free(decomp_buf); return 0; } // Try decompressing with progressively cropped size // 1. Cut off the Footer (last ZXC_FILE_FOOTER_SIZE bytes) if (comp_sz > ZXC_FILE_FOOTER_SIZE) { zxc_decompress_opts_t _do15 = {.checksum_enabled = 1}; if (zxc_decompress(compressed, (size_t)(comp_sz - ZXC_FILE_FOOTER_SIZE), decomp_buf, SRC_SIZE, &_do15) >= 0) { printf("Failed: Should fail when footer is missing\n"); free(compressed); free(decomp_buf); return 0; } } // 2. Cut off half the file zxc_decompress_opts_t _do16 = {.checksum_enabled = 1}; if (zxc_decompress(compressed, (size_t)(comp_sz / 2), decomp_buf, SRC_SIZE, &_do16) >= 0) { printf("Failed: Should fail when stream is truncated by half\n"); free(compressed); free(decomp_buf); return 0; } // 3. Cut off just 1 byte zxc_decompress_opts_t _do17 = {.checksum_enabled = 1}; if (zxc_decompress(compressed, (size_t)(comp_sz - 1), decomp_buf, SRC_SIZE, &_do17) >= 0) { printf("Failed: Should fail when stream is truncated by 1 byte\n"); free(compressed); free(decomp_buf); return 0; } printf("PASS\n\n"); free(compressed); free(decomp_buf); return 1; } // Checks behavior if writing fails int test_io_failures() { printf("=== TEST: Unit - I/O Failures ===\n"); FILE* f_in = tmpfile(); if (!f_in) return 0; // Create a dummy file to simulate failure // Open it in "rb" (read-only) and pass it as "wb" output file. // fwrite should return 0 and trigger the error. const char* bad_filename = "zxc_test_readonly.tmp"; FILE* f_dummy = create_restricted_file(bad_filename); if (f_dummy) fclose(f_dummy); FILE* f_out = fopen(bad_filename, "rb"); if (!f_out) { perror("fopen readonly"); fclose(f_in); return 0; } // Write some data to input fputs("test data to compress", f_in); fseek(f_in, 0, SEEK_SET); // This should fail cleanly (return < 0) because writing to f_out is impossible zxc_compress_opts_t _sco18 = {.n_threads = 1, .level = 5, .checksum_enabled = 0}; if (zxc_stream_compress(f_in, f_out, &_sco18) >= 0) { printf("Failed: Should detect write error on read-only stream\n"); fclose(f_in); fclose(f_out); remove(bad_filename); return 0; } printf("PASS\n\n"); fclose(f_in); fclose(f_out); remove(bad_filename); return 1; } // Checks thread selector behavior int test_thread_params() { printf("=== TEST: Unit - Thread Parameters ===\n"); FILE* f_in = tmpfile(); FILE* f_out = tmpfile(); if (!f_in || !f_out) { if (f_in) fclose(f_in); if (f_out) fclose(f_out); return 0; } // Test with 0 (Auto) and negative value - must not crash zxc_compress_opts_t _sco19 = {.n_threads = 0, .level = 5, .checksum_enabled = 0}; zxc_stream_compress(f_in, f_out, &_sco19); fseek(f_in, 0, SEEK_SET); fseek(f_out, 0, SEEK_SET); zxc_compress_opts_t _sco20 = {.n_threads = -5, .level = 5, .checksum_enabled = 0}; zxc_stream_compress(f_in, f_out, &_sco20); printf("PASS (No crash observed)\n\n"); fclose(f_in); fclose(f_out); return 1; } // Multi-threaded round-trip test for TSan coverage int test_multithread_roundtrip() { printf("=== TEST: Multi-Thread Round-Trip (TSan Coverage) ===\n"); const size_t SIZE = 4 * 1024 * 1024; // 4MB to ensure multiple chunks const int ITERATIONS = 3; // Multiple runs increase race detection int result = 0; uint8_t* input = malloc(SIZE); uint8_t* output = malloc(SIZE); if (!input || !output) goto cleanup; gen_lz_data(input, SIZE); for (int iter = 0; iter < ITERATIONS; iter++) { FILE* f_in = tmpfile(); FILE* f_comp = tmpfile(); FILE* f_decomp = tmpfile(); if (!f_in || !f_comp || !f_decomp) { if (f_in) fclose(f_in); if (f_comp) fclose(f_comp); if (f_decomp) fclose(f_decomp); goto cleanup; } fwrite(input, 1, SIZE, f_in); fseek(f_in, 0, SEEK_SET); // Vary thread count: 2, 4, 8 int num_threads = 2 << iter; zxc_compress_opts_t _sco21 = {.n_threads = num_threads, .level = 3, .checksum_enabled = 1}; if (zxc_stream_compress(f_in, f_comp, &_sco21) < 0) { printf("Compression failed (threads=%d)!\n", num_threads); fclose(f_in); fclose(f_comp); fclose(f_decomp); goto cleanup; } fseek(f_comp, 0, SEEK_SET); zxc_decompress_opts_t _sdo22 = {.n_threads = num_threads, .checksum_enabled = 1}; if (zxc_stream_decompress(f_comp, f_decomp, &_sdo22) < 0) { printf("Decompression failed (threads=%d)!\n", num_threads); fclose(f_in); fclose(f_comp); fclose(f_decomp); goto cleanup; } long decomp_size = ftell(f_decomp); fseek(f_decomp, 0, SEEK_SET); if (decomp_size != (long)SIZE || fread(output, 1, SIZE, f_decomp) != SIZE || memcmp(input, output, SIZE) != 0) { printf("Verification failed (threads=%d)!\n", num_threads); fclose(f_in); fclose(f_comp); fclose(f_decomp); goto cleanup; } fclose(f_in); fclose(f_comp); fclose(f_decomp); printf(" Iteration %d: PASS (%d threads)\n", iter + 1, num_threads); } printf("PASS (3 iterations, 2/4/8 threads)\n\n"); result = 1; cleanup: free(input); free(output); return result; } int test_stream_get_decompressed_size_errors() { printf("=== TEST: Unit - zxc_stream_get_decompressed_size Error Codes ===\n"); // 1. NULL FILE* int64_t r = zxc_stream_get_decompressed_size(NULL); if (r != ZXC_ERROR_NULL_INPUT) { printf(" [FAIL] NULL FILE*: expected %d, got %lld\n", ZXC_ERROR_NULL_INPUT, (long long)r); return 0; } printf(" [PASS] NULL FILE* -> ZXC_ERROR_NULL_INPUT\n"); // 2. File too small (less than header + footer) { FILE* f = tmpfile(); if (!f) { printf(" [SKIP] tmpfile failed\n"); return 0; } fwrite("tiny", 1, 4, f); fseek(f, 0, SEEK_SET); r = zxc_stream_get_decompressed_size(f); if (r != ZXC_ERROR_SRC_TOO_SMALL) { printf(" [FAIL] file too small: expected %d, got %lld\n", ZXC_ERROR_SRC_TOO_SMALL, (long long)r); fclose(f); return 0; } fclose(f); } printf(" [PASS] file too small -> ZXC_ERROR_SRC_TOO_SMALL\n"); // 3. Bad magic word { FILE* f = tmpfile(); if (!f) { printf(" [SKIP] tmpfile failed\n"); return 0; } // Write enough bytes but with wrong magic uint8_t garbage[ZXC_FILE_HEADER_SIZE + ZXC_FILE_FOOTER_SIZE]; memset(garbage, 0, sizeof(garbage)); fwrite(garbage, 1, sizeof(garbage), f); fseek(f, 0, SEEK_SET); r = zxc_stream_get_decompressed_size(f); if (r != ZXC_ERROR_BAD_MAGIC) { printf(" [FAIL] bad magic: expected %d, got %lld\n", ZXC_ERROR_BAD_MAGIC, (long long)r); fclose(f); return 0; } fclose(f); } printf(" [PASS] bad magic -> ZXC_ERROR_BAD_MAGIC\n"); // 4. Valid file returns correct size { // Create a valid compressed file in memory const size_t src_sz = 512; uint8_t* src = malloc(src_sz); gen_lz_data(src, src_sz); const size_t cap = (size_t)zxc_compress_bound(src_sz); uint8_t* comp = malloc(cap); zxc_compress_opts_t _co48 = {.level = 3, .checksum_enabled = 0}; int64_t comp_sz = zxc_compress(src, src_sz, comp, cap, &_co48); if (comp_sz <= 0) { printf(" [SKIP] compress failed\n"); free(src); free(comp); return 0; } FILE* f = tmpfile(); fwrite(comp, 1, (size_t)comp_sz, f); fseek(f, 0, SEEK_SET); r = zxc_stream_get_decompressed_size(f); if (r != (int64_t)src_sz) { printf(" [FAIL] valid file: expected %zu, got %lld\n", src_sz, (long long)r); fclose(f); free(src); free(comp); return 0; } fclose(f); free(src); free(comp); } printf(" [PASS] valid file -> correct size\n"); printf("PASS\n\n"); return 1; } int test_stream_engine_errors() { printf("=== TEST: Unit - Stream Engine Error Codes ===\n"); // 1. zxc_stream_compress with NULL f_in { FILE* f_out = tmpfile(); zxc_compress_opts_t _sco49 = {.n_threads = 1, .level = 3, .checksum_enabled = 0}; int64_t r = zxc_stream_compress(NULL, f_out, &_sco49); if (r != ZXC_ERROR_NULL_INPUT) { printf(" [FAIL] compress NULL f_in: expected %d, got %lld\n", ZXC_ERROR_NULL_INPUT, (long long)r); if (f_out) fclose(f_out); return 0; } if (f_out) fclose(f_out); } printf(" [PASS] zxc_stream_compress NULL f_in -> ZXC_ERROR_NULL_INPUT\n"); // 2. zxc_stream_decompress with NULL f_in { FILE* f_out = tmpfile(); zxc_decompress_opts_t _sdo50 = {.n_threads = 1, .checksum_enabled = 0}; int64_t r = zxc_stream_decompress(NULL, f_out, &_sdo50); if (r != ZXC_ERROR_NULL_INPUT) { printf(" [FAIL] decompress NULL f_in: expected %d, got %lld\n", ZXC_ERROR_NULL_INPUT, (long long)r); if (f_out) fclose(f_out); return 0; } if (f_out) fclose(f_out); } printf(" [PASS] zxc_stream_decompress NULL f_in -> ZXC_ERROR_NULL_INPUT\n"); // 3. zxc_stream_decompress with bad header (invalid file) { FILE* f_in = tmpfile(); FILE* f_out = tmpfile(); if (!f_in || !f_out) { if (f_in) fclose(f_in); if (f_out) fclose(f_out); printf(" [SKIP] tmpfile failed\n"); return 0; } // Write garbage data (bad magic) uint8_t garbage[64]; memset(garbage, 0xAA, sizeof(garbage)); fwrite(garbage, 1, sizeof(garbage), f_in); fseek(f_in, 0, SEEK_SET); zxc_decompress_opts_t _sdo51 = {.n_threads = 1, .checksum_enabled = 0}; int64_t r = zxc_stream_decompress(f_in, f_out, &_sdo51); if (r != ZXC_ERROR_BAD_HEADER) { printf(" [FAIL] bad header: expected %d, got %lld\n", ZXC_ERROR_BAD_HEADER, (long long)r); fclose(f_in); fclose(f_out); return 0; } fclose(f_in); fclose(f_out); } printf(" [PASS] zxc_stream_decompress bad header -> ZXC_ERROR_BAD_HEADER\n"); // 4. Stream decompress with corrupted footer (stored size mismatch) { // First, create a valid compressed stream const size_t src_sz = 4096; uint8_t* src = malloc(src_sz); gen_lz_data(src, src_sz); FILE* f_comp_in = tmpfile(); FILE* f_comp_out = tmpfile(); fwrite(src, 1, src_sz, f_comp_in); fseek(f_comp_in, 0, SEEK_SET); zxc_compress_opts_t _sco52 = {.n_threads = 1, .level = 3, .checksum_enabled = 1}; int64_t comp_sz = zxc_stream_compress(f_comp_in, f_comp_out, &_sco52); fclose(f_comp_in); if (comp_sz <= 0) { printf(" [SKIP] stream compress failed\n"); fclose(f_comp_out); free(src); return 0; } // Read the compressed data, corrupt the footer source size, rewrite fseek(f_comp_out, 0, SEEK_END); const long comp_file_sz = ftell(f_comp_out); uint8_t* comp_data = malloc(comp_file_sz); fseek(f_comp_out, 0, SEEK_SET); if (fread(comp_data, 1, comp_file_sz, f_comp_out) != (size_t)comp_file_sz) { printf(" [FAIL] fread failed\n"); fclose(f_comp_out); free(comp_data); free(src); return 0; } fclose(f_comp_out); // Corrupt the stored source size in footer (last 12 bytes: [src_size(8)] + [hash(4)]) const size_t footer_off = comp_file_sz - ZXC_FILE_FOOTER_SIZE; comp_data[footer_off] ^= 0x01; // Flip a bit in stored source size FILE* f_corrupt = tmpfile(); FILE* f_dec_out = tmpfile(); fwrite(comp_data, 1, comp_file_sz, f_corrupt); fseek(f_corrupt, 0, SEEK_SET); free(comp_data); zxc_decompress_opts_t _sdo53 = {.n_threads = 1, .checksum_enabled = 1}; int64_t r = zxc_stream_decompress(f_corrupt, f_dec_out, &_sdo53); fclose(f_corrupt); fclose(f_dec_out); free(src); if (r >= 0) { printf(" [FAIL] corrupt footer size: expected < 0, got %lld\n", (long long)r); return 0; } } printf(" [PASS] zxc_stream_decompress corrupt footer -> negative\n"); // 5. Stream decompress with corrupted global checksum { const size_t src_sz = 4096; uint8_t* src = malloc(src_sz); gen_lz_data(src, src_sz); FILE* f_comp_in = tmpfile(); FILE* f_comp_out = tmpfile(); fwrite(src, 1, src_sz, f_comp_in); fseek(f_comp_in, 0, SEEK_SET); zxc_compress_opts_t _sco54 = {.n_threads = 1, .level = 3, .checksum_enabled = 1}; int64_t comp_sz = zxc_stream_compress(f_comp_in, f_comp_out, &_sco54); fclose(f_comp_in); if (comp_sz <= 0) { printf(" [SKIP] stream compress failed\n"); fclose(f_comp_out); free(src); return 0; } fseek(f_comp_out, 0, SEEK_END); const long comp_file_sz = ftell(f_comp_out); uint8_t* comp_data = malloc(comp_file_sz); fseek(f_comp_out, 0, SEEK_SET); if (fread(comp_data, 1, comp_file_sz, f_comp_out) != (size_t)comp_file_sz) { printf(" [FAIL] fread failed\n"); fclose(f_comp_out); free(comp_data); free(src); return 0; } fclose(f_comp_out); // Corrupt the global checksum (last 4 bytes) comp_data[comp_file_sz - 1] ^= 0xFF; FILE* f_corrupt = tmpfile(); FILE* f_dec_out = tmpfile(); fwrite(comp_data, 1, comp_file_sz, f_corrupt); fseek(f_corrupt, 0, SEEK_SET); free(comp_data); zxc_decompress_opts_t _sdo55 = {.n_threads = 1, .checksum_enabled = 1}; int64_t r = zxc_stream_decompress(f_corrupt, f_dec_out, &_sdo55); fclose(f_corrupt); fclose(f_dec_out); free(src); if (r >= 0) { printf(" [FAIL] corrupt global checksum: expected < 0, got %lld\n", (long long)r); return 0; } } printf(" [PASS] zxc_stream_decompress corrupt checksum -> negative\n"); // 6. Stream decompress truncated file (missing EOF + footer) { const size_t src_sz = 4096; uint8_t* src = malloc(src_sz); gen_lz_data(src, src_sz); FILE* f_comp_in = tmpfile(); FILE* f_comp_out = tmpfile(); fwrite(src, 1, src_sz, f_comp_in); fseek(f_comp_in, 0, SEEK_SET); zxc_compress_opts_t _sco56 = {.n_threads = 1, .level = 3, .checksum_enabled = 0}; int64_t comp_sz = zxc_stream_compress(f_comp_in, f_comp_out, &_sco56); fclose(f_comp_in); free(src); if (comp_sz <= 0) { printf(" [SKIP] stream compress failed\n"); fclose(f_comp_out); return 0; } fseek(f_comp_out, 0, SEEK_END); const long comp_file_sz = ftell(f_comp_out); // Truncate: remove the EOF block header + footer const long trunc_sz = comp_file_sz - (ZXC_BLOCK_HEADER_SIZE + ZXC_FILE_FOOTER_SIZE); uint8_t* comp_data = malloc(trunc_sz); fseek(f_comp_out, 0, SEEK_SET); if (fread(comp_data, 1, trunc_sz, f_comp_out) != (size_t)trunc_sz) { printf(" [FAIL] fread failed\n"); fclose(f_comp_out); free(comp_data); return 0; } fclose(f_comp_out); FILE* f_corrupt = tmpfile(); FILE* f_dec_out = tmpfile(); fwrite(comp_data, 1, trunc_sz, f_corrupt); fseek(f_corrupt, 0, SEEK_SET); free(comp_data); zxc_decompress_opts_t _sdo57 = {.n_threads = 1, .checksum_enabled = 0}; int64_t r = zxc_stream_decompress(f_corrupt, f_dec_out, &_sdo57); fclose(f_corrupt); fclose(f_dec_out); // Should fail: missing EOF/footer means io_error or bad read if (r >= 0) { printf(" [FAIL] truncated stream: expected < 0, got %lld\n", (long long)r); return 0; } } printf(" [PASS] zxc_stream_decompress truncated -> negative\n"); // 7. Stream decompress with mid-block body truncation { const size_t src_sz = 64 * 1024; uint8_t* src = malloc(src_sz); gen_lz_data(src, src_sz); FILE* f_comp_in = tmpfile(); FILE* f_comp_out = tmpfile(); fwrite(src, 1, src_sz, f_comp_in); fseek(f_comp_in, 0, SEEK_SET); zxc_compress_opts_t sco_mb = {.n_threads = 1, .level = 3, .checksum_enabled = 0}; int64_t comp_sz = zxc_stream_compress(f_comp_in, f_comp_out, &sco_mb); fclose(f_comp_in); free(src); if (comp_sz <= 0) { printf(" [SKIP] stream compress failed\n"); fclose(f_comp_out); return 0; } fseek(f_comp_out, 0, SEEK_END); const long comp_file_sz = ftell(f_comp_out); // Truncate mid-block: keep header + first block header + partial body const long trunc_sz = ZXC_FILE_HEADER_SIZE + ZXC_BLOCK_HEADER_SIZE + 16; if (trunc_sz < comp_file_sz) { uint8_t* comp_data = malloc(trunc_sz); fseek(f_comp_out, 0, SEEK_SET); if (fread(comp_data, 1, trunc_sz, f_comp_out) == (size_t)trunc_sz) { FILE* f_trunc = tmpfile(); FILE* f_dec_out = tmpfile(); fwrite(comp_data, 1, trunc_sz, f_trunc); fseek(f_trunc, 0, SEEK_SET); zxc_decompress_opts_t sdo_mb = {.n_threads = 1, .checksum_enabled = 0}; int64_t r = zxc_stream_decompress(f_trunc, f_dec_out, &sdo_mb); fclose(f_trunc); fclose(f_dec_out); if (r >= 0) { printf(" [FAIL] mid-block truncated: expected < 0, got %lld\n", (long long)r); free(comp_data); fclose(f_comp_out); return 0; } } free(comp_data); } fclose(f_comp_out); } printf(" [PASS] zxc_stream_decompress mid-block truncated -> negative\n"); // 8. Streaming fwrite error: compress real data, then decompress to a read-only file { const size_t src_sz = 64 * 1024; uint8_t* src = malloc(src_sz); gen_lz_data(src, src_sz); FILE* f_comp_in = tmpfile(); FILE* f_comp_out = tmpfile(); fwrite(src, 1, src_sz, f_comp_in); fseek(f_comp_in, 0, SEEK_SET); free(src); zxc_compress_opts_t sco_io = {.n_threads = 1, .level = 1, .checksum_enabled = 0}; int64_t comp_sz = zxc_stream_compress(f_comp_in, f_comp_out, &sco_io); fclose(f_comp_in); if (comp_sz <= 0) { printf(" [SKIP] compress failed\n"); fclose(f_comp_out); return 0; } fseek(f_comp_out, 0, SEEK_SET); // Open a read-only file as the output: fwrite will fail const char* ro_file = "zxc_test_stream_readonly.tmp"; FILE* f_ro = create_restricted_file(ro_file); if (f_ro) fclose(f_ro); FILE* f_bad_out = fopen(ro_file, "rb"); if (f_bad_out) { zxc_decompress_opts_t sdo_io = {.n_threads = 1, .checksum_enabled = 0}; int64_t r = zxc_stream_decompress(f_comp_out, f_bad_out, &sdo_io); fclose(f_bad_out); if (r >= 0) { printf(" [FAIL] fwrite error: expected < 0, got %lld\n", (long long)r); fclose(f_comp_out); remove(ro_file); return 0; } } fclose(f_comp_out); remove(ro_file); } printf(" [PASS] zxc_stream_decompress fwrite error -> negative\n"); // 9. Multi-threaded streaming I/O failure (writer fwrite error with multiple workers) { const size_t src_sz = 256 * 1024; uint8_t* src = malloc(src_sz); gen_lz_data(src, src_sz); FILE* f_comp_in = tmpfile(); FILE* f_comp_out = tmpfile(); fwrite(src, 1, src_sz, f_comp_in); fseek(f_comp_in, 0, SEEK_SET); free(src); zxc_compress_opts_t sco_mt = {.n_threads = 4, .level = 1, .checksum_enabled = 0}; int64_t comp_sz = zxc_stream_compress(f_comp_in, f_comp_out, &sco_mt); fclose(f_comp_in); if (comp_sz <= 0) { printf(" [SKIP] mt compress failed\n"); fclose(f_comp_out); return 0; } fseek(f_comp_out, 0, SEEK_SET); const char* ro_file2 = "zxc_test_stream_mt_readonly.tmp"; FILE* f_ro2 = create_restricted_file(ro_file2); if (f_ro2) fclose(f_ro2); FILE* f_bad_out2 = fopen(ro_file2, "rb"); if (f_bad_out2) { zxc_decompress_opts_t sdo_mt = {.n_threads = 4, .checksum_enabled = 0}; int64_t r = zxc_stream_decompress(f_comp_out, f_bad_out2, &sdo_mt); fclose(f_bad_out2); if (r >= 0) { printf(" [FAIL] mt fwrite error: expected < 0, got %lld\n", (long long)r); fclose(f_comp_out); remove(ro_file2); return 0; } } fclose(f_comp_out); remove(ro_file2); } printf(" [PASS] zxc_stream_decompress mt fwrite error -> negative\n"); printf("PASS\n\n"); return 1; } /* ======================================================================== */ /* Streaming round-trip suite */ /* */ /* Historical coverage: patterns x levels x checksum. Each case is its own */ /* named entry so CTest can schedule and report them individually. */ /* ======================================================================== */ /* Thin wrapper around test_round_trip: malloc, generate, run, free. */ #define RT_WRAPPER(fn_name, label, gen, size_expr, level_val, crc_val) \ int fn_name(void) { \ const size_t _sz = (size_expr); \ uint8_t* _buf = malloc(_sz > 0 ? _sz : 1); \ if (!_buf) return 0; \ gen(_buf, _sz); \ const int _ok = test_round_trip((label), _buf, _sz, (level_val), \ (crc_val)); \ free(_buf); \ return _ok; \ } #define RT_BUF (256 * 1024) #define RT_LARGE (15 * 1024 * 1024) /* Encoder path coverage */ RT_WRAPPER(test_roundtrip_raw_random, "RAW Block (Random Data)", gen_random_data, RT_BUF, 3, 0) RT_WRAPPER(test_roundtrip_ghi_text, "GHI Block (Text Pattern)", gen_lz_data, RT_BUF, 2, 0) RT_WRAPPER(test_roundtrip_glo_text, "GLO Block (Text Pattern)", gen_lz_data, RT_BUF, 4, 0) RT_WRAPPER(test_roundtrip_num_seq, "Numeric data (integer sequence)", gen_num_data, RT_BUF, 3, 0) RT_WRAPPER(test_roundtrip_num_zero, "Numeric data (zero deltas)", gen_num_data_zero, RT_BUF, 3, 0) RT_WRAPPER(test_roundtrip_num_small, "Numeric data (small deltas)", gen_num_data_small, RT_BUF, 3, 0) RT_WRAPPER(test_roundtrip_num_large, "Numeric data (large deltas)", gen_num_data_large, RT_BUF, 3, 0) /* Size edge cases */ RT_WRAPPER(test_roundtrip_small_50, "Small Input (50 bytes)", gen_random_data, 50, 3, 0) RT_WRAPPER(test_roundtrip_empty, "Empty Input (0 bytes)", gen_random_data, 0, 3, 0) RT_WRAPPER(test_roundtrip_1byte, "1-byte Input", gen_random_data, 1, 3, 0) RT_WRAPPER(test_roundtrip_1byte_crc, "1-byte Input (with checksum)", gen_random_data, 1, 3, 1) RT_WRAPPER(test_roundtrip_large_15mb_lz, "Large File (15MB Multi-Block)", gen_lz_data, RT_LARGE, 3, 1) RT_WRAPPER(test_roundtrip_large_15mb_num, "Large numeric file (15MB Multi-Block)", gen_num_data, RT_LARGE, 3, 1) /* Checksum coverage */ RT_WRAPPER(test_roundtrip_checksum_off, "Checksum Disabled", gen_lz_data, RT_BUF, 3, 0) RT_WRAPPER(test_roundtrip_checksum_on, "Checksum Enabled", gen_lz_data, RT_BUF, 31, 1) /* Per-level coverage */ RT_WRAPPER(test_roundtrip_level1, "Level 1", gen_lz_data, RT_BUF, 1, 1) RT_WRAPPER(test_roundtrip_level2, "Level 2", gen_lz_data, RT_BUF, 2, 1) RT_WRAPPER(test_roundtrip_level3, "Level 3", gen_lz_data, RT_BUF, 3, 1) RT_WRAPPER(test_roundtrip_level4, "Level 4", gen_lz_data, RT_BUF, 4, 1) RT_WRAPPER(test_roundtrip_level5, "Level 5", gen_lz_data, RT_BUF, 5, 1) RT_WRAPPER(test_roundtrip_level6, "Level 6 (Huffman literals)", gen_lz_data, RT_BUF, 6, 1) /* Binary data preservation */ RT_WRAPPER(test_roundtrip_binary, "Binary Data (0x00, 0x0A, 0x0D, 0xFF)", gen_binary_data, RT_BUF, 3, 0) RT_WRAPPER(test_roundtrip_binary_crc, "Binary Data with Checksum", gen_binary_data, RT_BUF, 3, 1) RT_WRAPPER(test_roundtrip_binary_small, "Small Binary Data (128 bytes)", gen_binary_data, 128, 3, 0) /* Repetitive pattern / offset encoding */ RT_WRAPPER(test_roundtrip_offset8_small, "8-bit Offsets (Small Pattern)", gen_small_offset_data, RT_BUF, 3, 1) RT_WRAPPER(test_roundtrip_offset8_lvl5, "8-bit Offsets (Level 5)", gen_small_offset_data, RT_BUF, 5, 1) RT_WRAPPER(test_roundtrip_offset16_large, "16-bit Offsets (Large Distance)", gen_large_offset_data, RT_BUF, 3, 1) RT_WRAPPER(test_roundtrip_offset16_lvl5, "16-bit Offsets (Level 5)", gen_large_offset_data, RT_BUF, 5, 1) /* Mixed offsets: two generators populate two halves of the buffer. */ int test_roundtrip_offset_mixed(void) { uint8_t* buf = malloc(RT_BUF); if (!buf) return 0; gen_small_offset_data(buf, RT_BUF / 2); gen_large_offset_data(buf + RT_BUF / 2, RT_BUF / 2); const int ok = test_round_trip("Mixed Offsets (Hybrid)", buf, RT_BUF, 3, 1); free(buf); return ok; } zxc-0.13.0/wrappers/000077500000000000000000000000001522536370400142515ustar00rootroot00000000000000zxc-0.13.0/wrappers/go/000077500000000000000000000000001522536370400146565ustar00rootroot00000000000000zxc-0.13.0/wrappers/go/README.md000066400000000000000000000042371522536370400161430ustar00rootroot00000000000000# ZXC Go Bindings High-performance Go bindings for the **ZXC** asymmetric compressor, optimised for **fast decompression**. Designed for *Write Once, Read Many* workloads like ML datasets, game assets, and caches. ## Features - **Blazing fast decompression** - ZXC is specifically optimised for read-heavy workloads. - **Buffer API** - compress/decompress `[]byte` slices in a single call. - **Streaming API** - multi-threaded file compression/decompression. - **Functional options** - clean, composable configuration (`WithLevel`, `WithChecksum`, `WithThreads`). - **Typed errors** - sentinel error values for every ZXC error code. ## Prerequisites Build the ZXC core library as a static library: ```bash cd /path/to/zxc cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF \ -DZXC_BUILD_CLI=OFF -DZXC_BUILD_TESTS=OFF cmake --build build ``` ## Installation (from source) ```bash git clone https://github.com/hellobertrand/zxc.git cd zxc/wrappers/go CGO_ENABLED=1 CGO_LDFLAGS="-L../../build -lzxc -lpthread" go build ./... ``` ## Quick Start ```go package main import ( "fmt" "log" zxc "github.com/hellobertrand/zxc/wrappers/go" ) func main() { data := []byte("Hello, ZXC from Go!") // Compress compressed, err := zxc.Compress(data, zxc.WithLevel(zxc.LevelDefault)) if err != nil { log.Fatal(err) } fmt.Printf("Compressed %d => %d bytes\n", len(data), len(compressed)) // Decompress original, err := zxc.Decompress(compressed) if err != nil { log.Fatal(err) } fmt.Printf("Decompressed: %s\n", original) } ``` ## Streaming Files ```go // Multi-threaded file compression n, err := zxc.CompressFile("input.bin", "output.zxc", zxc.WithLevel(zxc.LevelCompact), zxc.WithThreads(4), zxc.WithChecksum(true), ) // Decompress n, err = zxc.DecompressFile("output.zxc", "restored.bin") ``` ## Testing ```bash cd zxc/wrappers/go CGO_ENABLED=1 CGO_LDFLAGS="-L../../build -lzxc -lpthread" go test -v -count=1 ./... ``` ## Benchmarks ```bash CGO_ENABLED=1 CGO_LDFLAGS="-L../../build -lzxc -lpthread" go test -bench=. -benchmem ./... ``` ## License BSD-3-Clause - see [LICENSE](../../LICENSE). zxc-0.13.0/wrappers/go/go.mod000066400000000000000000000000711522536370400157620ustar00rootroot00000000000000module github.com/hellobertrand/zxc/wrappers/go go 1.21 zxc-0.13.0/wrappers/go/zxc.go000066400000000000000000000247731522536370400160260ustar00rootroot00000000000000/* ZXC - High-performance lossless compression Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. SPDX-License-Identifier: BSD-3-Clause */ // Package zxc provides Go bindings to the ZXC high-performance lossless // compression library. // // ZXC is optimised for fast decompression speed: it is designed for // "Write Once, Read Many" workloads such as ML datasets, game assets, // firmware images and caches. // // # Quick Start // // compressed, err := zxc.Compress(data) // if err != nil { log.Fatal(err) } // // original, err := zxc.Decompress(compressed) // if err != nil { log.Fatal(err) } // // # Streaming (file-based) // // n, err := zxc.CompressFile("input.bin", "output.zxc") // n, err = zxc.DecompressFile("output.zxc", "restored.bin") package zxc /* #cgo CFLAGS: -I${SRCDIR}/../../include -DZXC_STATIC_DEFINE #cgo LDFLAGS: -lpthread #include #include #include "zxc.h" */ import "C" import ( "errors" "fmt" ) // ============================================================================ // Compression Levels // ============================================================================ // Level represents a ZXC compression level. type Level int const ( // LevelFastest provides the fastest compression, best for real-time // applications (level 1). LevelFastest Level = C.ZXC_LEVEL_FASTEST // LevelFast provides fast compression, good for real-time applications // (level 2). LevelFast Level = C.ZXC_LEVEL_FAST // LevelDefault is the recommended default: ratio > LZ4, decode speed > LZ4 // (level 3). LevelDefault Level = C.ZXC_LEVEL_DEFAULT // LevelBalanced provides good ratio with good decode speed (level 4). LevelBalanced Level = C.ZXC_LEVEL_BALANCED // LevelCompact provides high density: storage, firmware and assets // (level 5). LevelCompact Level = C.ZXC_LEVEL_COMPACT // LevelDensity provides high density: Huffman-coded literals on top // of LevelCompact plus a price-based optimal LZ77 parser (level 6). LevelDensity Level = C.ZXC_LEVEL_DENSITY // LevelUltra provides maximum density: Huffman-coded literals and sequence // tokens with a deep parse. Slowest compression, best ratio (level 7). LevelUltra Level = C.ZXC_LEVEL_ULTRA ) // AllLevels returns all available compression levels from fastest to most // compact. func AllLevels() []Level { return []Level{LevelFastest, LevelFast, LevelDefault, LevelBalanced, LevelCompact, LevelDensity, LevelUltra} } // ============================================================================ // Version // ============================================================================ const ( // VersionMajor is the major version of the underlying C library. VersionMajor = C.ZXC_VERSION_MAJOR // VersionMinor is the minor version of the underlying C library. VersionMinor = C.ZXC_VERSION_MINOR // VersionPatch is the patch version of the underlying C library. VersionPatch = C.ZXC_VERSION_PATCH ) // Version returns the library version as (major, minor, patch). func Version() (int, int, int) { return int(VersionMajor), int(VersionMinor), int(VersionPatch) } // VersionString returns the library version as a "major.minor.patch" string, // computed from the compile-time constants in the C header. // // For the version reported by the dynamically-linked native library (useful // for detecting ABI mismatch), call [LibraryVersion]. func VersionString() string { return fmt.Sprintf("%d.%d.%d", VersionMajor, VersionMinor, VersionPatch) } // LibraryVersion returns the version string reported by the linked native // libzxc (e.g. "0.13.0"). func LibraryVersion() string { return C.GoString(C.zxc_version_string()) } // MinLevel returns the minimum supported compression level (currently 1). func MinLevel() int { return int(C.zxc_min_level()) } // MaxLevel returns the maximum supported compression level (currently 7). func MaxLevel() int { return int(C.zxc_max_level()) } // DefaultLevel returns the default compression level (currently 3). func DefaultLevel() int { return int(C.zxc_default_level()) } // ============================================================================ // Error Handling // ============================================================================ // Error represents a ZXC library error. type Error struct { Code int Name string } func (e *Error) Error() string { return fmt.Sprintf("zxc: %s (code %d)", e.Name, e.Code) } // Sentinel errors for each ZXC error code. var ( ErrMemory = &Error{Code: int(C.ZXC_ERROR_MEMORY), Name: "memory allocation failed"} ErrDstTooSmall = &Error{Code: int(C.ZXC_ERROR_DST_TOO_SMALL), Name: "destination buffer too small"} ErrSrcTooSmall = &Error{Code: int(C.ZXC_ERROR_SRC_TOO_SMALL), Name: "source buffer too small"} ErrBadMagic = &Error{Code: int(C.ZXC_ERROR_BAD_MAGIC), Name: "invalid magic word"} ErrBadVersion = &Error{Code: int(C.ZXC_ERROR_BAD_VERSION), Name: "unsupported format version"} ErrBadHeader = &Error{Code: int(C.ZXC_ERROR_BAD_HEADER), Name: "corrupted header"} ErrBadChecksum = &Error{Code: int(C.ZXC_ERROR_BAD_CHECKSUM), Name: "checksum verification failed"} ErrCorruptData = &Error{Code: int(C.ZXC_ERROR_CORRUPT_DATA), Name: "corrupted compressed data"} ErrBadOffset = &Error{Code: int(C.ZXC_ERROR_BAD_OFFSET), Name: "invalid match offset"} ErrOverflow = &Error{Code: int(C.ZXC_ERROR_OVERFLOW), Name: "buffer overflow detected"} ErrIO = &Error{Code: int(C.ZXC_ERROR_IO), Name: "I/O error"} ErrNullInput = &Error{Code: int(C.ZXC_ERROR_NULL_INPUT), Name: "null input pointer"} ErrBadBlockType = &Error{Code: int(C.ZXC_ERROR_BAD_BLOCK_TYPE), Name: "unknown block type"} ErrBadBlockSize = &Error{Code: int(C.ZXC_ERROR_BAD_BLOCK_SIZE), Name: "invalid block size"} ErrDictRequired = &Error{Code: int(C.ZXC_ERROR_DICT_REQUIRED), Name: "archive requires a dictionary"} ErrDictMismatch = &Error{Code: int(C.ZXC_ERROR_DICT_MISMATCH), Name: "dictionary ID mismatch"} ErrDictTooLarge = &Error{Code: int(C.ZXC_ERROR_DICT_TOO_LARGE), Name: "dictionary exceeds maximum size"} ErrBadLevel = &Error{Code: int(C.ZXC_ERROR_BAD_LEVEL), Name: "compression level out of range"} ErrInvalidData = errors.New("zxc: invalid compressed data") // ErrBadHufTable is returned when a shared literal Huffman table does not // have the required [HufTableSize] length. ErrBadHufTable = errors.New("zxc: shared Huffman table must be HufTableSize bytes") // ErrDictUnsupported is returned by the push streaming API when dictionary // options are supplied: the push-stream format carries no dictionary ID, so // dictionary compression would produce undecodable archives. ErrDictUnsupported = errors.New("zxc: dictionaries are not supported by the push streaming API") ) // errorFromCode converts a negative C error code to a Go error. func errorFromCode(code C.int64_t) error { switch int(code) { case int(C.ZXC_ERROR_MEMORY): return ErrMemory case int(C.ZXC_ERROR_DST_TOO_SMALL): return ErrDstTooSmall case int(C.ZXC_ERROR_SRC_TOO_SMALL): return ErrSrcTooSmall case int(C.ZXC_ERROR_BAD_MAGIC): return ErrBadMagic case int(C.ZXC_ERROR_BAD_VERSION): return ErrBadVersion case int(C.ZXC_ERROR_BAD_HEADER): return ErrBadHeader case int(C.ZXC_ERROR_BAD_CHECKSUM): return ErrBadChecksum case int(C.ZXC_ERROR_CORRUPT_DATA): return ErrCorruptData case int(C.ZXC_ERROR_BAD_OFFSET): return ErrBadOffset case int(C.ZXC_ERROR_OVERFLOW): return ErrOverflow case int(C.ZXC_ERROR_IO): return ErrIO case int(C.ZXC_ERROR_NULL_INPUT): return ErrNullInput case int(C.ZXC_ERROR_BAD_BLOCK_TYPE): return ErrBadBlockType case int(C.ZXC_ERROR_BAD_BLOCK_SIZE): return ErrBadBlockSize case int(C.ZXC_ERROR_DICT_REQUIRED): return ErrDictRequired case int(C.ZXC_ERROR_DICT_MISMATCH): return ErrDictMismatch case int(C.ZXC_ERROR_DICT_TOO_LARGE): return ErrDictTooLarge case int(C.ZXC_ERROR_BAD_LEVEL): return ErrBadLevel default: return fmt.Errorf("zxc: unknown error (code %d)", int(code)) } } // ============================================================================ // Options (functional options pattern) // ============================================================================ // options holds all configurable parameters. type options struct { level Level checksum bool seekable bool threads int dict []byte dictHuf []byte // levelSet / checksumSet record whether the caller supplied the option // explicitly, so per-call options can fall back to context-creation values // instead of silently overriding them with defaults (see Cctx). levelSet bool checksumSet bool } func defaultOptions() options { return options{ level: LevelDefault, checksum: false, threads: 0, // auto-detect } } // Option configures compression or decompression. type Option func(*options) // WithLevel sets the compression level. func WithLevel(l Level) Option { return func(o *options) { o.level = l o.levelSet = true } } // WithChecksum enables checksum computation and verification. func WithChecksum(enabled bool) Option { return func(o *options) { o.checksum = enabled o.checksumSet = true } } // WithThreads sets the number of worker threads for streaming operations. // A value of 0 means auto-detect the CPU core count. func WithThreads(n int) Option { return func(o *options) { o.threads = n } } // WithSeekable enables seek table generation for random-access decompression. func WithSeekable(enabled bool) Option { return func(o *options) { o.seekable = enabled } } // WithDict sets a pre-trained dictionary used for compression or // decompression. The same dictionary content must be supplied to both // Compress and Decompress (the dictionary ID stored in the archive header is // validated against it). An empty slice clears the dictionary. // // Train a dictionary with [TrainDict] or load one from a .zxd file with // [DictLoad]. The content size must not exceed [DictSizeMax]. func WithDict(dict []byte) Option { return func(o *options) { o.dict = dict } } // WithDictionary attaches a [Dictionary] (content + shared table) in one call. func WithDictionary(d *Dictionary) Option { return func(o *options) { o.dict = d.Content() o.dictHuf = d.Huf() } } // WithDictHuf sets the dictionary's shared literal Huffman table // ([HufTableSize] bytes, from [TrainDictHuf] or [DictHuf]). It is ignored // without [WithDict]. The archive's dictionary ID binds the (dict, table) // pair, so the same table must be supplied at decompression time. func WithDictHuf(huf []byte) Option { return func(o *options) { o.dictHuf = huf } } func applyOptions(opts []Option) options { o := defaultOptions() for _, fn := range opts { fn(&o) } return o } zxc-0.13.0/wrappers/go/zxc_block.go000066400000000000000000000151471522536370400171730ustar00rootroot00000000000000/* ZXC - High-performance lossless compression Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. SPDX-License-Identifier: BSD-3-Clause */ package zxc /* #include "zxc.h" */ import "C" import ( "unsafe" ) // ============================================================================ // Block API (single block, no file framing) // ============================================================================ // CompressBlockBound returns the maximum compressed size for a single block // of inputSize bytes (no file framing, no EOF, no footer). func CompressBlockBound(inputSize int) uint64 { if inputSize < 0 { return 0 } return uint64(C.zxc_compress_block_bound(C.size_t(inputSize))) } // DecompressBlockBound returns the minimum destination buffer size required // by [Dctx.DecompressBlock] for a block of uncompressedSize bytes. // // The fast decoder uses speculative wild-copy writes and needs a small tail // pad beyond the declared uncompressed size. Callers that cannot oversize // their destination buffer should use [Dctx.DecompressBlockSafe] instead. func DecompressBlockBound(uncompressedSize int) uint64 { if uncompressedSize < 0 { return 0 } return uint64(C.zxc_decompress_block_bound(C.size_t(uncompressedSize))) } // EstimateCctxSize returns an accurate estimate of the memory a compression // context reserves when compressing a single block of srcSize bytes at the // given compression level via [Cctx.CompressBlock]. // // The estimate covers all per-chunk working buffers (chain table, literals, // sequence/token/offset/extras buffers) plus the fixed hash tables and // cache-line alignment padding. At level 6+ it also accounts for the // optimal-parser scratch. It scales roughly linearly with srcSize and is // intended for integrators that need an accurate memory budget. // // Returns 0 if srcSize is 0 or negative. func EstimateCctxSize(srcSize, level int) uint64 { if srcSize <= 0 { return 0 } return uint64(C.zxc_estimate_cctx_size(C.size_t(srcSize), C.int(level))) } // Cctx is a reusable compression context for the Block API. // // It wraps an opaque C handle that is freed by [Cctx.Close]. Create with // [NewCctx] and always defer a call to Close to release the native memory. type Cctx struct { ptr *C.zxc_cctx // Creation-time settings, used as the fallback for CompressBlock calls // that do not override them explicitly. level Level checksum bool } // NewCctx creates a new compression context. // // Options [WithLevel] and [WithChecksum] are supported at creation time and // become the defaults for every [Cctx.CompressBlock] call that does not // override them per call. func NewCctx(opts ...Option) (*Cctx, error) { o := applyOptions(opts) var copts C.zxc_compress_opts_t copts.level = C.int(o.level) if o.checksum { copts.checksum_enabled = 1 } // Block size is optional; 0 lets the library pick the default. ptr := C.zxc_create_cctx(&copts) if ptr == nil { return nil, ErrMemory } return &Cctx{ptr: ptr, level: o.level, checksum: o.checksum}, nil } // Close releases the native resources held by the context. Safe to call // multiple times. func (c *Cctx) Close() error { if c == nil || c.ptr == nil { return nil } C.zxc_free_cctx(c.ptr) c.ptr = nil return nil } // CompressBlock compresses a single block using the context. Output format // is [block_header(8B) + payload (+ optional checksum 4B)]. Use // [CompressBlockBound] to size dst. // // Per-call [WithLevel] / [WithChecksum] override the values given to // [NewCctx]; when omitted, the creation-time settings apply. // [WithDict]/[WithDictHuf] are not wired to the block API and are ignored; // use the buffer API ([Compress]/[Decompress]) for dictionary compression. func (c *Cctx) CompressBlock(src, dst []byte, opts ...Option) (int, error) { if c == nil || c.ptr == nil { return 0, ErrNullInput } if len(src) == 0 { return 0, ErrSrcTooSmall } if len(dst) == 0 { return 0, ErrDstTooSmall } // Merge per-call options over the creation-time settings: the C side // always receives a non-NULL opts struct, so its own stored-level // fallback never triggers and the merge must happen here. o := applyOptions(opts) if !o.levelSet { o.level = c.level } if !o.checksumSet { o.checksum = c.checksum } var copts C.zxc_compress_opts_t copts.level = C.int(o.level) if o.checksum { copts.checksum_enabled = 1 } n := C.zxc_compress_block( c.ptr, unsafe.Pointer(&src[0]), C.size_t(len(src)), unsafe.Pointer(&dst[0]), C.size_t(len(dst)), &copts, ) if n < 0 { return 0, errorFromCode(n) } return int(n), nil } // Dctx is a reusable decompression context for the Block API. type Dctx struct { ptr *C.zxc_dctx } // NewDctx creates a new decompression context. func NewDctx() (*Dctx, error) { ptr := C.zxc_create_dctx() if ptr == nil { return nil, ErrMemory } return &Dctx{ptr: ptr}, nil } // Close releases the native resources held by the context. func (d *Dctx) Close() error { if d == nil || d.ptr == nil { return nil } C.zxc_free_dctx(d.ptr) d.ptr = nil return nil } // DecompressBlock decompresses a single block produced by // [Cctx.CompressBlock]. // // dst should be at least [DecompressBlockBound](uncompressedSize) to enable // the fast decode path. For a strictly-sized destination buffer use // [Dctx.DecompressBlockSafe] instead. func (d *Dctx) DecompressBlock(src, dst []byte, opts ...Option) (int, error) { if d == nil || d.ptr == nil { return 0, ErrNullInput } if len(src) == 0 { return 0, ErrSrcTooSmall } if len(dst) == 0 { return 0, ErrDstTooSmall } o := applyOptions(opts) var dopts C.zxc_decompress_opts_t if o.checksum { dopts.checksum_enabled = 1 } n := C.zxc_decompress_block( d.ptr, unsafe.Pointer(&src[0]), C.size_t(len(src)), unsafe.Pointer(&dst[0]), C.size_t(len(dst)), &dopts, ) if n < 0 { return 0, errorFromCode(n) } return int(n), nil } // DecompressBlockSafe is a strict-sized variant of [Dctx.DecompressBlock]: // it accepts a destination buffer sized exactly to the uncompressed length, // with no tail-pad required. Slightly slower than the fast path; output is // bit-identical. func (d *Dctx) DecompressBlockSafe(src, dst []byte, opts ...Option) (int, error) { if d == nil || d.ptr == nil { return 0, ErrNullInput } if len(src) == 0 { return 0, ErrSrcTooSmall } if len(dst) == 0 { return 0, ErrDstTooSmall } o := applyOptions(opts) var dopts C.zxc_decompress_opts_t if o.checksum { dopts.checksum_enabled = 1 } n := C.zxc_decompress_block_safe( d.ptr, unsafe.Pointer(&src[0]), C.size_t(len(src)), unsafe.Pointer(&dst[0]), C.size_t(len(dst)), &dopts, ) if n < 0 { return 0, errorFromCode(n) } return int(n), nil } zxc-0.13.0/wrappers/go/zxc_buffer.go000066400000000000000000000132371522536370400173500ustar00rootroot00000000000000/* ZXC - High-performance lossless compression Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. SPDX-License-Identifier: BSD-3-Clause */ package zxc /* #include "zxc.h" */ import "C" import ( "math" "runtime" "unsafe" ) // ============================================================================ // Buffer API // ============================================================================ // CompressBound returns the maximum compressed size for an input of the given // size. Use this to pre-allocate output buffers. func CompressBound(inputSize int) uint64 { return uint64(C.zxc_compress_bound(C.size_t(inputSize))) } // Compress compresses data using the ZXC algorithm. // // Options: [WithLevel], [WithChecksum]. // // out, err := zxc.Compress(data, zxc.WithLevel(zxc.LevelCompact)) func Compress(data []byte, opts ...Option) ([]byte, error) { o := applyOptions(opts) bound := CompressBound(len(data)) dst := make([]byte, bound) var copts C.zxc_compress_opts_t copts.level = C.int(o.level) if o.checksum { copts.checksum_enabled = 1 } var pinner runtime.Pinner defer pinner.Unpin() if err := setCompressDict(&copts, o, &pinner); err != nil { return nil, err } // &data[0] panics on an empty slice; pass a valid non-nil pointer instead // (the C side reads 0 bytes) so empty input yields a minimal 36-byte frame. var dummy [1]byte srcPtr := unsafe.Pointer(&dummy[0]) if len(data) > 0 { srcPtr = unsafe.Pointer(&data[0]) } written := C.zxc_compress( srcPtr, C.size_t(len(data)), unsafe.Pointer(&dst[0]), C.size_t(bound), &copts, ) if written < 0 { return nil, errorFromCode(written) } if written == 0 { return nil, ErrInvalidData } return dst[:int(written)], nil } // CompressTo compresses data into a pre-allocated output buffer. // Returns the number of bytes written. func CompressTo(data []byte, output []byte, opts ...Option) (int, error) { if len(data) == 0 { return 0, ErrSrcTooSmall } if len(output) == 0 { return 0, ErrDstTooSmall } o := applyOptions(opts) var copts C.zxc_compress_opts_t copts.level = C.int(o.level) if o.checksum { copts.checksum_enabled = 1 } var pinner runtime.Pinner defer pinner.Unpin() if err := setCompressDict(&copts, o, &pinner); err != nil { return 0, err } written := C.zxc_compress( unsafe.Pointer(&data[0]), C.size_t(len(data)), unsafe.Pointer(&output[0]), C.size_t(len(output)), &copts, ) if written < 0 { return 0, errorFromCode(written) } if written == 0 { return 0, ErrInvalidData } return int(written), nil } // DecompressedSize returns the original uncompressed size stored in the // compressed data footer. Returns 0, ErrInvalidData if the data is too small // or invalid. func DecompressedSize(data []byte) (uint64, error) { if len(data) == 0 { return 0, ErrInvalidData } size := C.zxc_get_decompressed_size( unsafe.Pointer(&data[0]), C.size_t(len(data)), ) if size == 0 { return 0, ErrInvalidData } return uint64(size), nil } // Decompress decompresses ZXC-compressed data. // // The output size is read from the compressed data footer. For pre-allocated // buffers, use [DecompressTo]. // // Options: [WithChecksum]. func Decompress(data []byte, opts ...Option) ([]byte, error) { if len(data) == 0 { return nil, ErrInvalidData } o := applyOptions(opts) var dopts C.zxc_decompress_opts_t if o.checksum { dopts.checksum_enabled = 1 } var pinner runtime.Pinner defer pinner.Unpin() if err := setDecompressDict(&dopts, o, &pinner); err != nil { return nil, err } size := uint64(C.zxc_get_decompressed_size( unsafe.Pointer(&data[0]), C.size_t(len(data)), )) // The footer value is plausibility-checked in C (a forged size returns 0); // only guard what Go's make() cannot represent. if size > math.MaxInt { return nil, ErrInvalidData } if size == 0 { // Either a valid empty-payload archive, or invalid input (bad header, // or a footer whose size failed the C-side plausibility check). Let // the C decoder decide: it returns 0 for a valid empty archive, or a // negative error code otherwise. Decode into a zero-length buffer; // DST_TOO_SMALL here can only mean the footer contradicts the payload // (the caller supplied no buffer), so report it as invalid data. var dummy [1]byte written := C.zxc_decompress( unsafe.Pointer(&data[0]), C.size_t(len(data)), unsafe.Pointer(&dummy[0]), C.size_t(0), &dopts, ) if written < 0 { if int(written) == int(C.ZXC_ERROR_DST_TOO_SMALL) { return nil, ErrInvalidData } return nil, errorFromCode(written) } if written != 0 { return nil, ErrInvalidData } return []byte{}, nil } dst := make([]byte, size) written := C.zxc_decompress( unsafe.Pointer(&data[0]), C.size_t(len(data)), unsafe.Pointer(&dst[0]), C.size_t(size), &dopts, ) if written < 0 { return nil, errorFromCode(written) } if uint64(written) != size { return nil, ErrInvalidData } return dst[:int(written)], nil } // DecompressTo decompresses data into a pre-allocated output buffer. // Returns the number of bytes written. func DecompressTo(data []byte, output []byte, opts ...Option) (int, error) { if len(data) == 0 { return 0, ErrInvalidData } if len(output) == 0 { return 0, ErrDstTooSmall } o := applyOptions(opts) var dopts C.zxc_decompress_opts_t if o.checksum { dopts.checksum_enabled = 1 } var pinner runtime.Pinner defer pinner.Unpin() if err := setDecompressDict(&dopts, o, &pinner); err != nil { return 0, err } written := C.zxc_decompress( unsafe.Pointer(&data[0]), C.size_t(len(data)), unsafe.Pointer(&output[0]), C.size_t(len(output)), &dopts, ) if written < 0 { return 0, errorFromCode(written) } return int(written), nil } zxc-0.13.0/wrappers/go/zxc_dict.go000066400000000000000000000236311522536370400170210ustar00rootroot00000000000000/* ZXC - High-performance lossless compression Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. SPDX-License-Identifier: BSD-3-Clause */ package zxc /* #include #include "zxc.h" */ import "C" import ( "runtime" "unsafe" ) // ============================================================================ // Dictionary API (pre-trained dictionaries) // ============================================================================ // DictSizeMax is the maximum dictionary content size in bytes (65535). const DictSizeMax = int(C.ZXC_DICT_SIZE_MAX) // HufTableSize is the size in bytes of the shared literal Huffman table // carried by a .zxd file (packed 4-bit code lengths for 256 symbols). const HufTableSize = int(C.ZXC_HUF_TABLE_SIZE) // setCompressDict points copts at the dictionary content from o when one is // configured. The dict byte is pinned for the duration of the C call so that // the Go pointer stored inside the (Go-allocated) opts struct satisfies the // cgo pointer-passing rules; callers must keep pinner alive until the C call // returns. // // The shared Huffman table length is validated here: the C library reads a // fixed ZXC_HUF_TABLE_SIZE bytes from dict_huf, so a shorter slice would be // read out of bounds. func setCompressDict(copts *C.zxc_compress_opts_t, o options, pinner *runtime.Pinner) error { if len(o.dict) == 0 { return nil } pinner.Pin(&o.dict[0]) copts.dict = unsafe.Pointer(&o.dict[0]) copts.dict_size = C.size_t(len(o.dict)) if len(o.dictHuf) > 0 { if len(o.dictHuf) != HufTableSize { return ErrBadHufTable } pinner.Pin(&o.dictHuf[0]) copts.dict_huf = unsafe.Pointer(&o.dictHuf[0]) } return nil } // setDecompressDict mirrors setCompressDict for decompression options. func setDecompressDict(dopts *C.zxc_decompress_opts_t, o options, pinner *runtime.Pinner) error { if len(o.dict) == 0 { return nil } pinner.Pin(&o.dict[0]) dopts.dict = unsafe.Pointer(&o.dict[0]) dopts.dict_size = C.size_t(len(o.dict)) if len(o.dictHuf) > 0 { if len(o.dictHuf) != HufTableSize { return ErrBadHufTable } pinner.Pin(&o.dictHuf[0]) dopts.dict_huf = unsafe.Pointer(&o.dictHuf[0]) } return nil } // pinSamples builds C-allocated pointer/size arrays that reference the // sample buffers directly: each sample is pinned via pinner, which makes it // legal (Go >= 1.21) to store its address in C memory for the duration of // the call — training never copies the corpus. Empty samples point at a // pinned placeholder byte so the C side never sees NULL. The returned // arrays must be freed with C.free; the pins are released by the caller's // pinner.Unpin. func pinSamples(samples [][]byte, pinner *runtime.Pinner) (cptrs, csizes unsafe.Pointer) { n := len(samples) cptrs = C.malloc(C.size_t(n) * C.size_t(unsafe.Sizeof(uintptr(0)))) csizes = C.malloc(C.size_t(n) * C.size_t(unsafe.Sizeof(C.size_t(0)))) ptrSlice := unsafe.Slice((*unsafe.Pointer)(cptrs), n) sizeSlice := unsafe.Slice((*C.size_t)(csizes), n) placeholder := new(byte) pinner.Pin(placeholder) for i, s := range samples { sizeSlice[i] = C.size_t(len(s)) if len(s) == 0 { ptrSlice[i] = unsafe.Pointer(placeholder) continue } pinner.Pin(&s[0]) ptrSlice[i] = unsafe.Pointer(&s[0]) } return cptrs, csizes } // TrainDict trains a dictionary from a corpus of samples. // // It analyses the samples to select byte sequences that maximise LZ77 match // coverage, returning raw dictionary content suitable for [WithDict], // [DictSave], or [DictID]. maxSize caps the trained dictionary size; values // <= 0 or greater than [DictSizeMax] are clamped to [DictSizeMax]. // // At least one non-empty sample is required. func TrainDict(samples [][]byte, maxSize int) ([]byte, error) { if len(samples) == 0 { return nil, ErrSrcTooSmall } if maxSize <= 0 || maxSize > DictSizeMax { maxSize = DictSizeMax } var pinner runtime.Pinner defer pinner.Unpin() cptrs, csizes := pinSamples(samples, &pinner) defer C.free(cptrs) defer C.free(csizes) dict := make([]byte, DictSizeMax) written := C.zxc_train_dict( (*unsafe.Pointer)(cptrs), (*C.size_t)(csizes), C.size_t(len(samples)), unsafe.Pointer(&dict[0]), C.size_t(maxSize), ) if written < 0 { return nil, errorFromCode(written) } if written == 0 { return nil, ErrInvalidData } return dict[:int(written)], nil } // DictID computes the deterministic 32-bit ID of raw dictionary content. // Returns 0 for empty content. func DictID(content []byte) uint32 { if len(content) == 0 { return 0 } return uint32(C.zxc_dict_id(unsafe.Pointer(&content[0]), C.size_t(len(content)), nil)) } // GetDictID returns the dictionary ID recorded in a compressed .zxc archive // header, or 0 if the archive was not compressed with a dictionary (or is too // small / invalid). func GetDictID(archive []byte) uint32 { if len(archive) == 0 { return 0 } return uint32(C.zxc_get_dict_id(unsafe.Pointer(&archive[0]), C.size_t(len(archive)))) } // DictGetID returns the dictionary ID stored in a serialized .zxd file buffer, // or 0 if the buffer is not a valid .zxd file. func DictGetID(zxd []byte) uint32 { if len(zxd) == 0 { return 0 } return uint32(C.zxc_dict_get_id(unsafe.Pointer(&zxd[0]), C.size_t(len(zxd)))) } // DictSave serializes dictionary content and its shared literal Huffman // table ([HufTableSize] bytes, from [TrainDictHuf]) into the .zxd file // format. The stored dictionary ID covers both content and table. func DictSave(content, hufLengths []byte) ([]byte, error) { if len(content) == 0 { return nil, ErrSrcTooSmall } if len(hufLengths) != HufTableSize { return nil, ErrInvalidData } bound := uint64(C.zxc_dict_save_bound(C.size_t(len(content)))) buf := make([]byte, bound) n := C.zxc_dict_save( unsafe.Pointer(&content[0]), C.size_t(len(content)), unsafe.Pointer(&hufLengths[0]), unsafe.Pointer(&buf[0]), C.size_t(bound), ) if n < 0 { return nil, errorFromCode(n) } return buf[:int(n)], nil } // TrainDictHuf trains the shared literal Huffman table for an // already-trained dictionary (see [TrainDict]). It compresses the samples // with the dictionary and derives canonical code lengths from the real // post-LZ literal distribution. The returned [HufTableSize]-byte table is // required by [DictSave] and can be attached with [WithDictHuf]. func TrainDictHuf(samples [][]byte, dict []byte) ([]byte, error) { if len(samples) == 0 || len(dict) == 0 { return nil, ErrSrcTooSmall } var pinner runtime.Pinner defer pinner.Unpin() cptrs, csizes := pinSamples(samples, &pinner) defer C.free(cptrs) defer C.free(csizes) huf := make([]byte, HufTableSize) rc := C.zxc_train_dict_huf( (*unsafe.Pointer)(cptrs), (*C.size_t)(csizes), C.size_t(len(samples)), unsafe.Pointer(&dict[0]), C.size_t(len(dict)), (*C.uint8_t)(unsafe.Pointer(&huf[0])), ) if rc != C.ZXC_OK { return nil, errorFromCode(C.int64_t(rc)) } return huf, nil } // DictHuf returns a copy of the shared literal Huffman table stored in a // .zxd file buffer, or nil if the buffer is not a valid .zxd file. func DictHuf(zxd []byte) []byte { if len(zxd) == 0 { return nil } p := C.zxc_dict_huf(unsafe.Pointer(&zxd[0]), C.size_t(len(zxd))) if p == nil { return nil } return C.GoBytes(p, C.int(HufTableSize)) } // DictLoad validates a .zxd file buffer and returns a copy of its dictionary // content along with the dictionary ID. Prefer [LoadDictionary] for the full // (content, table, id) bundle. func DictLoad(zxd []byte) (content []byte, id uint32, err error) { d, err := LoadDictionary(zxd) if err != nil { return nil, 0, err } return d.Content(), d.ID(), nil } // Dictionary bundles a trained dictionary's LZ-window content and its shared // literal Huffman table, so callers never juggle the pair by hand. // // Create one with [TrainDictionary] (from samples) or [LoadDictionary] (from // .zxd bytes); attach it with [WithDictionary] or [Seekable.SetDictionary]. type Dictionary struct { content []byte huf []byte // HufTableSize bytes id uint32 } // TrainDictionary trains a complete dictionary (content + shared table) from // a corpus of samples in one call. func TrainDictionary(samples [][]byte) (*Dictionary, error) { if len(samples) == 0 { return nil, ErrSrcTooSmall } var pinner runtime.Pinner defer pinner.Unpin() cptrs, csizes := pinSamples(samples, &pinner) defer C.free(cptrs) defer C.free(csizes) cap := uint64(C.zxc_dict_save_bound(C.size_t(DictSizeMax))) zxd := make([]byte, cap) written := C.zxc_dict_train( (*unsafe.Pointer)(cptrs), (*C.size_t)(csizes), C.size_t(len(samples)), unsafe.Pointer(&zxd[0]), C.size_t(cap), ) if written <= 0 { if written < 0 { return nil, errorFromCode(written) } return nil, ErrInvalidData } return LoadDictionary(zxd[:int(written)]) } // LoadDictionary parses .zxd bytes into an owned Dictionary. func LoadDictionary(zxd []byte) (*Dictionary, error) { if len(zxd) == 0 { return nil, ErrSrcTooSmall } var contentPtr unsafe.Pointer var contentSize C.size_t var hufPtr unsafe.Pointer var dictID C.uint32_t rc := C.zxc_dict_load( unsafe.Pointer(&zxd[0]), C.size_t(len(zxd)), &contentPtr, &contentSize, &hufPtr, &dictID, ) if rc < 0 { return nil, errorFromCode(C.int64_t(rc)) } // The pointers alias into zxd (zero-copy); copy into Go-owned memory so // the result is independent of the input buffer's lifetime. return &Dictionary{ content: C.GoBytes(contentPtr, C.int(contentSize)), huf: C.GoBytes(hufPtr, C.int(HufTableSize)), id: uint32(dictID), }, nil } // Save serializes the dictionary back to .zxd file bytes. func (d *Dictionary) Save() ([]byte, error) { return DictSave(d.content, d.huf) } // ID returns the dictionary ID binding the (content, table) pair, as recorded // in .zxd files and archive headers. func (d *Dictionary) ID() uint32 { return d.id } // Content returns the raw LZ-window content bytes. func (d *Dictionary) Content() []byte { return d.content } // Huf returns the 128-byte shared literal Huffman table. func (d *Dictionary) Huf() []byte { return d.huf } zxc-0.13.0/wrappers/go/zxc_dict_test.go000066400000000000000000000151231522536370400200550ustar00rootroot00000000000000/* ZXC - High-performance lossless compression Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. SPDX-License-Identifier: BSD-3-Clause */ package zxc import ( "bytes" "fmt" "os" "path/filepath" "testing" ) // dictSamples returns a small corpus of similar JSON-like records that share // enough structure for the trainer to extract a useful dictionary. func dictSamples() [][]byte { samples := make([][]byte, 0, 64) for i := 0; i < 64; i++ { s := fmt.Sprintf( `{"id":%d,"type":"user","name":"alice_%d","email":"alice%d@example.com","active":true,"role":"member"}`, i, i, i, ) samples = append(samples, []byte(s)) } return samples } func trainTestDict(t *testing.T) []byte { t.Helper() dict, err := TrainDict(dictSamples(), 0) if err != nil { t.Fatalf("TrainDict: %v", err) } if len(dict) == 0 { t.Fatalf("TrainDict returned empty dictionary") } return dict } func TestTrainDictNonEmpty(t *testing.T) { dict := trainTestDict(t) if len(dict) > DictSizeMax { t.Fatalf("dict size %d exceeds max %d", len(dict), DictSizeMax) } if DictID(dict) == 0 { t.Fatalf("DictID(content) = 0, want non-zero") } } func TestTrainDictEmptySamples(t *testing.T) { if _, err := TrainDict(nil, 0); err == nil { t.Fatalf("TrainDict(nil) should fail") } } func TestCompressDecompressWithDict(t *testing.T) { dict := trainTestDict(t) payload := []byte(`{"id":7,"type":"user","name":"alice_7","email":"alice7@example.com","active":true,"role":"member"}`) comp, err := Compress(payload, WithDict(dict)) if err != nil { t.Fatalf("Compress(WithDict): %v", err) } got, err := Decompress(comp, WithDict(dict)) if err != nil { t.Fatalf("Decompress(WithDict): %v", err) } if !bytes.Equal(got, payload) { t.Fatalf("roundtrip mismatch: got %q want %q", got, payload) } } func TestDecompressDictArchiveWithoutDictFails(t *testing.T) { dict := trainTestDict(t) payload := []byte(`{"id":3,"type":"user","name":"alice_3","email":"alice3@example.com","active":true,"role":"member"}`) comp, err := Compress(payload, WithDict(dict)) if err != nil { t.Fatalf("Compress(WithDict): %v", err) } if _, err := Decompress(comp); err == nil { t.Fatalf("Decompress without dict should fail on a dict-compressed archive") } } func TestDictIDConsistency(t *testing.T) { dict := trainTestDict(t) payload := []byte(`{"id":42,"type":"user","name":"alice_42","email":"alice42@example.com","active":true,"role":"member"}`) contentID := DictID(dict) if contentID == 0 { t.Fatalf("DictID(content) = 0") } comp, err := Compress(payload, WithDict(dict)) if err != nil { t.Fatalf("Compress(WithDict): %v", err) } if archiveID := GetDictID(comp); archiveID != contentID { t.Fatalf("GetDictID(archive) = %d, want %d", archiveID, contentID) } huf, err := TrainDictHuf(dictSamples(), dict) if err != nil { t.Fatalf("TrainDictHuf: %v", err) } zxd, err := DictSave(dict, huf) if err != nil { t.Fatalf("DictSave: %v", err) } // The .zxd id binds (content, table): non-zero and distinct from the // content-only id. if zxdID := DictGetID(zxd); zxdID == 0 || zxdID == contentID { t.Fatalf("DictGetID(.zxd) = %d, want non-zero and != content id %d", zxdID, contentID) } if got := DictHuf(zxd); !bytes.Equal(got, huf) { t.Fatalf("DictHuf(.zxd) mismatch") } } func TestDictSaveLoadRoundtrip(t *testing.T) { dict := trainTestDict(t) huf, err := TrainDictHuf(dictSamples(), dict) if err != nil { t.Fatalf("TrainDictHuf: %v", err) } zxd, err := DictSave(dict, huf) if err != nil { t.Fatalf("DictSave: %v", err) } content, id, err := DictLoad(zxd) if err != nil { t.Fatalf("DictLoad: %v", err) } if !bytes.Equal(content, dict) { t.Fatalf("DictLoad content mismatch (len got %d want %d)", len(content), len(dict)) } if id != DictGetID(zxd) { t.Fatalf("DictLoad id = %d, want %d", id, DictGetID(zxd)) } } func TestDictLoadInvalid(t *testing.T) { if _, _, err := DictLoad([]byte("not a zxd file")); err == nil { t.Fatalf("DictLoad on garbage should fail") } } func TestSeekableSetDictRange(t *testing.T) { dict := trainTestDict(t) // Build a payload large enough to span multiple seekable blocks so the // range decode exercises real block boundaries. var buf bytes.Buffer for i := 0; buf.Len() < 256*1024; i++ { fmt.Fprintf(&buf, `{"id":%d,"type":"user","name":"alice_%d","email":"alice%d@example.com","active":true,"role":"member"}`+"\n", i, i, i, ) } payload := buf.Bytes() dir := t.TempDir() in := filepath.Join(dir, "input.bin") out := filepath.Join(dir, "archive.zxc") if err := os.WriteFile(in, payload, 0o644); err != nil { t.Fatalf("write input: %v", err) } if _, err := CompressFile(in, out, WithSeekable(true), WithDict(dict)); err != nil { t.Fatalf("CompressFile(seekable, dict): %v", err) } s, err := Open(out) if err != nil { t.Fatalf("Open: %v", err) } defer s.Close() if err := s.SetDict(dict, nil); err != nil { t.Fatalf("SetDict: %v", err) } // Decode a range that starts partway through the stream. const offset = 100 * 1024 const length = 50 * 1024 dst := make([]byte, length) n, err := s.DecompressRange(dst, offset, length) if err != nil { t.Fatalf("DecompressRange: %v", err) } if n != length { t.Fatalf("DecompressRange n = %d, want %d", n, length) } if !bytes.Equal(dst, payload[offset:offset+length]) { t.Fatalf("DecompressRange content mismatch") } } func TestDictionaryObjectRoundtrip(t *testing.T) { d, err := TrainDictionary(dictSamples()) if err != nil { t.Fatalf("TrainDictionary: %v", err) } if len(d.Content()) == 0 || len(d.Huf()) != HufTableSize || d.ID() == 0 { t.Fatalf("Dictionary fields: content=%d huf=%d id=%d", len(d.Content()), len(d.Huf()), d.ID()) } // Save/Load preserves the bundle and the pair-binding id. zxd, err := d.Save() if err != nil { t.Fatalf("Save: %v", err) } d2, err := LoadDictionary(zxd) if err != nil { t.Fatalf("LoadDictionary: %v", err) } if !bytes.Equal(d.Content(), d2.Content()) || !bytes.Equal(d.Huf(), d2.Huf()) || d.ID() != d2.ID() { t.Fatalf("Save/Load bundle mismatch") } if DictGetID(zxd) != d.ID() { t.Fatalf("DictGetID(.zxd) = %d, want %d", DictGetID(zxd), d.ID()) } // One-call attach round-trip + id binding negative. payload := dictSamples()[2] comp, err := Compress(payload, WithLevel(LevelDensity), WithDictionary(d)) if err != nil { t.Fatalf("Compress(WithDictionary): %v", err) } out, err := Decompress(comp, WithDictionary(d)) if err != nil || !bytes.Equal(out, payload) { t.Fatalf("Decompress(WithDictionary): %v", err) } if _, err := Decompress(comp, WithDict(d.Content())); err == nil { t.Fatalf("content-only decode of a pair-bound archive should fail") } } zxc-0.13.0/wrappers/go/zxc_dup_unix.go000066400000000000000000000025101522536370400177220ustar00rootroot00000000000000//go:build unix package zxc /* #include #include */ import "C" import ( "fmt" "os" "runtime" ) // C mode strings - allocated once, never freed (intentional; they live for the // process lifetime and avoid per-call C.CString/C.free overhead). var ( cModeRead = C.CString("rb") cModeWrite = C.CString("wb") ) // dupFileRead duplicates a Go *os.File's fd and wraps it in a C FILE* for // reading. The returned FILE* owns its own fd and must be closed with // C.fclose(). func dupFileRead(f *os.File) (*C.FILE, error) { fd := C.int(f.Fd()) dupFd := C.dup(fd) runtime.KeepAlive(f) if dupFd < 0 { return nil, fmt.Errorf("zxc: dup failed for read fd") } cFile := C.fdopen(dupFd, cModeRead) if cFile == nil { C.close(dupFd) return nil, fmt.Errorf("zxc: fdopen failed for read fd") } return cFile, nil } // dupFileWrite duplicates a Go *os.File's fd and wraps it in a C FILE* for // writing. The returned FILE* owns its own fd and must be closed with // C.fclose(). func dupFileWrite(f *os.File) (*C.FILE, error) { fd := C.int(f.Fd()) dupFd := C.dup(fd) runtime.KeepAlive(f) if dupFd < 0 { return nil, fmt.Errorf("zxc: dup failed for write fd") } cFile := C.fdopen(dupFd, cModeWrite) if cFile == nil { C.close(dupFd) return nil, fmt.Errorf("zxc: fdopen failed for write fd") } return cFile, nil } zxc-0.13.0/wrappers/go/zxc_dup_windows.go000066400000000000000000000041411522536370400204330ustar00rootroot00000000000000//go:build windows package zxc /* #include #include #include */ import "C" import ( "fmt" "os" "runtime" "syscall" ) // C mode strings - allocated once, never freed (intentional; they live for the // process lifetime and avoid per-call C.CString/C.free overhead). var ( cModeRead = C.CString("rb") cModeWrite = C.CString("wb") ) // dupHandleToFile duplicates the Windows HANDLE owned by f, wraps the // duplicate as a CRT fd, then opens a C FILE* over it. The FILE* owns the // whole chain: fclose() closes the CRT fd, which closes the duplicated // HANDLE. Go's *os.File and its HANDLE are never touched. // // Duplicating the HANDLE first (rather than _open_osfhandle on Go's HANDLE // followed by _dup) matters: _open_osfhandle transfers HANDLE ownership to // the CRT fd, which then could never be closed without also closing Go's // HANDLE — permanently leaking one slot of the bounded CRT fd table per call. func dupHandleToFile(f *os.File, oflag C.int, mode *C.char, what string) (*C.FILE, error) { proc, _ := syscall.GetCurrentProcess() var dup syscall.Handle err := syscall.DuplicateHandle( proc, syscall.Handle(f.Fd()), proc, &dup, 0, false, syscall.DUPLICATE_SAME_ACCESS, ) runtime.KeepAlive(f) if err != nil { return nil, fmt.Errorf("zxc: DuplicateHandle failed for %s fd: %w", what, err) } crtFd := C._open_osfhandle(C.intptr_t(dup), oflag) if crtFd < 0 { syscall.CloseHandle(dup) return nil, fmt.Errorf("zxc: _open_osfhandle failed for %s fd", what) } cFile := C._fdopen(crtFd, mode) if cFile == nil { C._close(crtFd) // also closes the duplicated HANDLE return nil, fmt.Errorf("zxc: _fdopen failed for %s fd", what) } return cFile, nil } // dupFileRead converts a Go *os.File to an independently-owned C FILE* for // reading on Windows. func dupFileRead(f *os.File) (*C.FILE, error) { return dupHandleToFile(f, C._O_RDONLY, cModeRead, "read") } // dupFileWrite converts a Go *os.File to an independently-owned C FILE* for // writing on Windows. func dupFileWrite(f *os.File) (*C.FILE, error) { return dupHandleToFile(f, C._O_WRONLY, cModeWrite, "write") } zxc-0.13.0/wrappers/go/zxc_file.go000066400000000000000000000101271522536370400170110ustar00rootroot00000000000000/* ZXC - High-performance lossless compression Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. SPDX-License-Identifier: BSD-3-Clause */ package zxc /* #include #include "zxc_stream.h" */ import "C" import ( "fmt" "os" "runtime" ) // ============================================================================ // Streaming API (file-based) // ============================================================================ // CompressFile compresses src to dst using multi-threaded streaming. // // This is the recommended method for large files. It uses an asynchronous // pipeline with separate reader, worker, and writer threads. // // Options: [WithLevel], [WithChecksum], [WithThreads]. // // n, err := zxc.CompressFile("input.bin", "output.zxc", // zxc.WithLevel(zxc.LevelCompact), zxc.WithThreads(4)) func CompressFile(input, output string, opts ...Option) (int64, error) { o := applyOptions(opts) fIn, err := os.Open(input) if err != nil { return 0, fmt.Errorf("zxc: open input: %w", err) } defer fIn.Close() fOut, err := os.Create(output) if err != nil { return 0, fmt.Errorf("zxc: create output: %w", err) } defer fOut.Close() // Duplicate file descriptors so the C FILE* owns its own fd. cIn, err := dupFileRead(fIn) if err != nil { return 0, err } defer C.fclose(cIn) cOut, err := dupFileWrite(fOut) if err != nil { return 0, err } outClosed := false defer func() { if !outClosed { C.fclose(cOut) } }() var copts C.zxc_compress_opts_t copts.n_threads = C.int(o.threads) copts.level = C.int(o.level) if o.checksum { copts.checksum_enabled = 1 } if o.seekable { copts.seekable = 1 } var pinner runtime.Pinner defer pinner.Unpin() if err := setCompressDict(&copts, o, &pinner); err != nil { return 0, err } result := C.zxc_stream_compress(cIn, cOut, &copts) // fclose flushes the last stdio buffer of compressed output; a failure // here (ENOSPC, EIO) means the archive tail never reached the OS, so it // must be reported instead of being swallowed by a deferred close. outClosed = true closeRC := C.fclose(cOut) if result < 0 { return 0, errorFromCode(result) } if closeRC != 0 { return 0, ErrIO } return int64(result), nil } // FileDecompressedSize reads the original uncompressed size from the footer // of a ZXC compressed file without performing decompression. The file is // opened in "rb" mode; the underlying file position is restored internally. func FileDecompressedSize(path string) (int64, error) { f, err := os.Open(path) if err != nil { return 0, fmt.Errorf("zxc: open input: %w", err) } defer f.Close() cf, err := dupFileRead(f) if err != nil { return 0, err } defer C.fclose(cf) result := C.zxc_stream_get_decompressed_size(cf) if result < 0 { return 0, errorFromCode(result) } return int64(result), nil } // DecompressFile decompresses src to dst using multi-threaded streaming. // // Options: [WithChecksum], [WithThreads]. // // n, err := zxc.DecompressFile("compressed.zxc", "output.bin") func DecompressFile(input, output string, opts ...Option) (int64, error) { o := applyOptions(opts) fIn, err := os.Open(input) if err != nil { return 0, fmt.Errorf("zxc: open input: %w", err) } defer fIn.Close() fOut, err := os.Create(output) if err != nil { return 0, fmt.Errorf("zxc: create output: %w", err) } defer fOut.Close() cIn, err := dupFileRead(fIn) if err != nil { return 0, err } defer C.fclose(cIn) cOut, err := dupFileWrite(fOut) if err != nil { return 0, err } outClosed := false defer func() { if !outClosed { C.fclose(cOut) } }() var dopts C.zxc_decompress_opts_t dopts.n_threads = C.int(o.threads) if o.checksum { dopts.checksum_enabled = 1 } var pinner runtime.Pinner defer pinner.Unpin() if err := setDecompressDict(&dopts, o, &pinner); err != nil { return 0, err } result := C.zxc_stream_decompress(cIn, cOut, &dopts) // See CompressFile: the fclose flush result is part of the success path. outClosed = true closeRC := C.fclose(cOut) if result < 0 { return 0, errorFromCode(result) } if closeRC != 0 { return 0, ErrIO } return int64(result), nil } zxc-0.13.0/wrappers/go/zxc_io.go000066400000000000000000000150271522536370400165050ustar00rootroot00000000000000/* ZXC - High-performance lossless compression Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. SPDX-License-Identifier: BSD-3-Clause */ package zxc import ( "io" ) // ============================================================================ // io.Reader / io.Writer adapters over the push streaming API // ============================================================================ // // These wrappers turn a [CStream] / [DStream] pair into the standard Go // streaming interfaces (io.Reader, io.Writer, io.Closer) so ZXC can be plugged // into pipelines that expect them — notably OCI-style content stores, tar // pipelines, HTTP transports, etc. // Magic word identifying a ZXC file frame: little-endian 0x9CB02EF5. var zxcMagicLE = [4]byte{0xF5, 0x2E, 0xB0, 0x9C} // DetectZxc reports whether data starts with the ZXC file magic word. // // Useful for content-type sniffing in containers / object stores that need to // decide which decoder to dispatch (e.g. OCI media-type negotiation). The // check is cheap and side-effect free; it does not validate the rest of the // header or the footer. func DetectZxc(data []byte) bool { if len(data) < 4 { return false } return data[0] == zxcMagicLE[0] && data[1] == zxcMagicLE[1] && data[2] == zxcMagicLE[2] && data[3] == zxcMagicLE[3] } // ---------------------------------------------------------------------------- // Writer // ---------------------------------------------------------------------------- // Writer is an [io.WriteCloser] that compresses bytes written to it and // forwards the resulting ZXC frame to an underlying writer. // // The caller MUST call [Writer.Close] to flush the residual block, EOF marker // and footer; closing the underlying writer is the caller's responsibility. // // Writer is not safe for concurrent use. type Writer struct { dst io.Writer cs *CStream outBuf []byte err error } // NewWriter returns a [Writer] that compresses into w using a single-threaded // push pipeline. // // Honoured options: [WithLevel], [WithChecksum]. [WithThreads] and // [WithSeekable] are ignored (push API is single-threaded). func NewWriter(w io.Writer, opts ...Option) (*Writer, error) { cs, err := NewCStream(opts...) if err != nil { return nil, err } return &Writer{ dst: w, cs: cs, outBuf: make([]byte, cs.OutSize()), }, nil } // Write compresses p and forwards the produced bytes to the underlying writer. // Returns the number of bytes consumed from p (always len(p) on success). func (w *Writer) Write(p []byte) (int, error) { if w.err != nil { return 0, w.err } if w.cs == nil { return 0, ErrNullInput } total := 0 for len(p) > 0 { consumed, produced, pending, err := w.cs.Compress(w.outBuf, p) if err != nil { w.err = err return total, err } if produced > 0 { if _, werr := w.dst.Write(w.outBuf[:produced]); werr != nil { w.err = werr return total, werr } } total += consumed p = p[consumed:] // Defensive: the encoder reported no input progress, no output and // nothing pending while input remains. This should be unreachable // with a healthy stream; returning nil here would silently drop the // rest of p, violating the io.Writer contract. if consumed == 0 && produced == 0 && pending == 0 { w.err = io.ErrShortWrite return total, io.ErrShortWrite } } return total, nil } // Close flushes any buffered data, writes the EOF block + footer, and releases // the underlying CStream. The wrapped io.Writer is NOT closed. // // Safe to call multiple times; subsequent calls are no-ops. func (w *Writer) Close() error { if w.cs == nil { return nil } defer func() { _ = w.cs.Close() w.cs = nil }() if w.err != nil { return w.err } for { produced, pending, err := w.cs.End(w.outBuf) if err != nil { w.err = err return err } if produced > 0 { if _, werr := w.dst.Write(w.outBuf[:produced]); werr != nil { w.err = werr return werr } } if pending == 0 { return nil } } } // ---------------------------------------------------------------------------- // Reader // ---------------------------------------------------------------------------- // Reader is an [io.ReadCloser] that decompresses a ZXC frame read from an // underlying reader. // // Reader is not safe for concurrent use. type Reader struct { src io.Reader ds *DStream inBuf []byte inPos int inLen int err error eof bool // src returned io.EOF } // NewReader returns a [Reader] that decompresses from r. // // Honoured options: [WithChecksum]. [WithThreads] is ignored. func NewReader(r io.Reader, opts ...Option) (*Reader, error) { ds, err := NewDStream(opts...) if err != nil { return nil, err } return &Reader{ src: r, ds: ds, inBuf: make([]byte, ds.InSize()), }, nil } // Read decompresses bytes into p. Returns io.EOF after the footer has been // validated; returns io.ErrUnexpectedEOF if the underlying reader is drained // before the footer is reached. func (r *Reader) Read(p []byte) (int, error) { if r.err != nil { return 0, r.err } if r.ds == nil { return 0, ErrNullInput } if len(p) == 0 { return 0, nil } for { if r.ds.Finished() { r.err = io.EOF return 0, io.EOF } // Try to decompress whatever is currently buffered (or drain mode // when src is at EOF). if r.inPos < r.inLen || r.eof { consumed, produced, derr := r.ds.Decompress(p, r.inBuf[r.inPos:r.inLen]) if derr != nil { r.err = derr return 0, derr } r.inPos += consumed if produced > 0 { return produced, nil } if consumed == 0 { // No forward progress with current input. if r.eof { if r.ds.Finished() { r.err = io.EOF return 0, io.EOF } r.err = io.ErrUnexpectedEOF return 0, io.ErrUnexpectedEOF } // fall through to refill } else { // Bytes were consumed but no output yet — loop and try again. continue } } // Refill: shift consumed bytes out, then read more. if r.inPos > 0 { r.inLen = copy(r.inBuf, r.inBuf[r.inPos:r.inLen]) r.inPos = 0 } n, rerr := r.src.Read(r.inBuf[r.inLen:]) r.inLen += n if rerr == io.EOF { r.eof = true } else if rerr != nil { r.err = rerr return 0, rerr } if n == 0 && !r.eof { // Underlying reader returned (0, nil): treat as a benign retry by // returning to the caller with no output. return 0, nil } } } // Close releases the underlying [DStream]. The wrapped io.Reader is NOT // closed. Safe to call multiple times. func (r *Reader) Close() error { if r.ds == nil { return nil } err := r.ds.Close() r.ds = nil return err } zxc-0.13.0/wrappers/go/zxc_regression_test.go000066400000000000000000000062021522536370400213100ustar00rootroot00000000000000package zxc import ( "bytes" "encoding/binary" "errors" "testing" ) // Regression tests for wrapper-review fixes: create-time option inheritance // in the block API, footer plausibility validation, pstream dictionary // rejection, dictionary error-code mapping, and Huffman-table length checks. func TestFixCctxStickyLevel(t *testing.T) { src := bytes.Repeat([]byte("the quick brown fox jumps over the lazy dog 0123456789 "), 2000) dst := make([]byte, CompressBlockBound(len(src))) cUltra, err := NewCctx(WithLevel(LevelUltra)) if err != nil { t.Fatal(err) } defer cUltra.Close() nDefault := 0 { c, _ := NewCctx(WithLevel(LevelDefault)) defer c.Close() nDefault, err = c.CompressBlock(src, dst, WithLevel(LevelDefault)) if err != nil { t.Fatal(err) } } nExplicitUltra := 0 { c, _ := NewCctx() defer c.Close() nExplicitUltra, err = c.CompressBlock(src, dst, WithLevel(LevelUltra)) if err != nil { t.Fatal(err) } } // No per-call options: must inherit the create-time ULTRA level. nInherited, err := cUltra.CompressBlock(src, dst) if err != nil { t.Fatal(err) } if nInherited != nExplicitUltra { t.Fatalf("create-time level not inherited: got %d, explicit ultra %d, default %d", nInherited, nExplicitUltra, nDefault) } if nInherited == nDefault && nDefault != nExplicitUltra { t.Fatalf("no-opts call silently used LevelDefault") } } func TestFixDecompressCraftedFooter(t *testing.T) { comp, err := Compress([]byte("hello world hello world")) if err != nil { t.Fatal(err) } // Footer = original_size(8) + checksum(4); patch original_size to 2^64-1. binary.LittleEndian.PutUint64(comp[len(comp)-12:], ^uint64(0)) defer func() { if r := recover(); r != nil { t.Fatalf("Decompress panicked on crafted footer: %v", r) } }() if _, err := Decompress(comp); !errors.Is(err, ErrInvalidData) { t.Fatalf("want ErrInvalidData, got %v", err) } } func TestFixPstreamDictRejected(t *testing.T) { if _, err := NewCStream(WithDict([]byte("abc"))); !errors.Is(err, ErrDictUnsupported) { t.Fatalf("NewCStream(WithDict): want ErrDictUnsupported, got %v", err) } if _, err := NewDStream(WithDict([]byte("abc"))); !errors.Is(err, ErrDictUnsupported) { t.Fatalf("NewDStream(WithDict): want ErrDictUnsupported, got %v", err) } } func TestFixDictErrorCodes(t *testing.T) { dict := bytes.Repeat([]byte("sample dictionary content for zxc "), 100) payload := bytes.Repeat([]byte("sample dictionary content for zxc payload"), 50) comp, err := Compress(payload, WithDict(dict)) if err != nil { t.Fatal(err) } if _, err := Decompress(comp); !errors.Is(err, ErrDictRequired) { t.Fatalf("want ErrDictRequired, got %v", err) } wrong := bytes.Repeat([]byte("a completely different dictionary "), 100) if _, err := Decompress(comp, WithDict(wrong)); !errors.Is(err, ErrDictMismatch) { t.Fatalf("want ErrDictMismatch, got %v", err) } } func TestFixHufTableValidation(t *testing.T) { dict := bytes.Repeat([]byte("sample dictionary content for zxc "), 100) if _, err := Compress([]byte("data"), WithDict(dict), WithDictHuf([]byte("short"))); !errors.Is(err, ErrBadHufTable) { t.Fatalf("want ErrBadHufTable, got %v", err) } } zxc-0.13.0/wrappers/go/zxc_seekable.go000066400000000000000000000242241522536370400176500ustar00rootroot00000000000000/* ZXC - High-performance lossless compression Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. SPDX-License-Identifier: BSD-3-Clause */ package zxc /* #include #include #include #include "zxc_seekable.h" #include "zxc_stream.h" #include "zxc_error.h" // Forward declaration of the Go-side trampoline (generated by //export). extern int64_t zxcGoSeekableReadAt(void *ctx, void *dst, size_t len, uint64_t offset); // Opens a seekable archive through a stack-built zxc_reader_t. The ctx is a // uintptr-encoded runtime/cgo.Handle for the Go-side reader, and read_at is // the Go-exported trampoline above. static zxc_seekable* zxcGoOpenReader(uintptr_t ctx, uint64_t size) { zxc_reader_t r; r.read_at = zxcGoSeekableReadAt; r.ctx = (void *)ctx; r.size = size; return zxc_seekable_open_reader(&r); } */ import "C" import ( "io" "runtime" "runtime/cgo" "unsafe" ) // ============================================================================ // Seekable API (random-access decompression, single-threaded) // ============================================================================ // Seekable is a handle to a seekable ZXC archive. // // Build one with [Open] (file-backed) or [OpenBytes] (in-memory). Always // defer a call to [Seekable.Close] to release the native resources. // // A Seekable handle is single-threaded: callers must not invoke its methods // from more than one goroutine concurrently. type Seekable struct { ptr *C.zxc_seekable file *C.FILE // set when opened via Open srcData []byte // in-memory source (OpenBytes); pinned for the handle's life pinner runtime.Pinner rhandle cgo.Handle // valid when opened via OpenReader; zero otherwise hasRH bool // true when rhandle is set (cgo.Handle 0 is itself valid) } // Open opens a seekable archive from a file path. // // The file is opened in binary read mode through the C runtime and remains // open for the lifetime of the returned handle. Close releases both the // archive handle and the underlying FILE*. func Open(path string) (*Seekable, error) { cpath := C.CString(path) defer C.free(unsafe.Pointer(cpath)) mode := C.CString("rb") defer C.free(unsafe.Pointer(mode)) f, err := C.fopen(cpath, mode) if f == nil { if err != nil { return nil, err } return nil, ErrIO } ptr := C.zxc_seekable_open_file(f) if ptr == nil { C.fclose(f) return nil, ErrInvalidData } return &Seekable{ptr: ptr, file: f}, nil } // OpenBytes opens a seekable archive from an in-memory buffer. // // The library requires the source buffer to remain valid for the lifetime // of the handle, so data is pinned (not copied) until [Seekable.Close] — // opening a large archive costs no extra memory. The caller must not // modify data while the handle is open. func OpenBytes(data []byte) (*Seekable, error) { if len(data) == 0 { return nil, ErrSrcTooSmall } // Pinning makes it legal for C to retain the pointer beyond the call // (cgo pointer rules, Go >= 1.21) and keeps the backing array alive. s := &Seekable{} s.pinner.Pin(&data[0]) ptr := C.zxc_seekable_open(unsafe.Pointer(&data[0]), C.size_t(len(data))) if ptr == nil { s.pinner.Unpin() return nil, ErrInvalidData } s.ptr = ptr s.srcData = data return s, nil } // OpenReader opens a seekable archive backed by a user-supplied // [io.ReaderAt]. Use this to plug any storage that supports positional // reads behind the seekable API: an *os.File, an *bytes.Reader, an HTTP // range-request client, an S3 object, a custom VFS. // // size must be the total compressed-archive size in bytes (use // f.Stat().Size() for an *os.File). The library invokes ReadAt three // times during the call (header, footer, seek table) and once per block // during subsequent DecompressRange calls. // // The caller's reader must remain valid until [Seekable.Close] is called. // ReadAt may be invoked from any goroutine but never concurrently in the // current single-threaded API; a future multi-threaded entry point will // require ReadAt to be safe for concurrent use. func OpenReader(r io.ReaderAt, size int64) (*Seekable, error) { if r == nil { return nil, ErrNullInput } if size <= 0 { return nil, ErrSrcTooSmall } h := cgo.NewHandle(r) ptr := C.zxcGoOpenReader(C.uintptr_t(h), C.uint64_t(size)) if ptr == nil { h.Delete() return nil, ErrInvalidData } return &Seekable{ptr: ptr, rhandle: h, hasRH: true}, nil } // The C->Go read trampoline (zxcGoSeekableReadAt) lives in // zxc_seekable_export.go; see the comment there for why it is kept out of this // file's preamble. The forward declaration above (in the cgo preamble) is what // lets zxcGoOpenReader take its address. // Close releases the native resources associated with the handle. Safe to // call multiple times; subsequent calls are no-ops. func (s *Seekable) Close() error { if s == nil || s.ptr == nil { return nil } // Free the C handle first so any in-flight read_at callbacks complete // before we release the underlying Go reader handle. C.zxc_seekable_free(s.ptr) s.ptr = nil if s.file != nil { C.fclose(s.file) s.file = nil } if s.srcData != nil { s.pinner.Unpin() s.srcData = nil } if s.hasRH { s.rhandle.Delete() s.hasRH = false } return nil } // NumBlocks returns the total number of data blocks in the archive // (excluding the EOF marker block). Returns 0 if the handle has been // closed. func (s *Seekable) NumBlocks() uint32 { if s == nil || s.ptr == nil { return 0 } return uint32(C.zxc_seekable_get_num_blocks(s.ptr)) } // DecompressedSize returns the total decompressed size of the archive in // bytes. Returns 0 if the handle has been closed. func (s *Seekable) DecompressedSize() uint64 { if s == nil || s.ptr == nil { return 0 } return uint64(C.zxc_seekable_get_decompressed_size(s.ptr)) } // BlockCompressedSize returns the on-disk size of a specific block (block // header + payload + optional per-block checksum). The second return value // is false if blockIdx is out of range or the handle has been closed. func (s *Seekable) BlockCompressedSize(blockIdx uint32) (uint32, bool) { if s == nil || s.ptr == nil || blockIdx >= s.NumBlocks() { return 0, false } return uint32(C.zxc_seekable_get_block_comp_size(s.ptr, C.uint32_t(blockIdx))), true } // BlockDecompressedSize returns the decompressed size of a specific block. // The second return value is false if blockIdx is out of range or the // handle has been closed. func (s *Seekable) BlockDecompressedSize(blockIdx uint32) (uint32, bool) { if s == nil || s.ptr == nil || blockIdx >= s.NumBlocks() { return 0, false } return uint32(C.zxc_seekable_get_block_decomp_size(s.ptr, C.uint32_t(blockIdx))), true } // DecompressRange decompresses length bytes starting at offset (in the // original uncompressed byte stream) into dst. // // Only the blocks overlapping the requested range are read and // decompressed. dst must be at least length bytes long. Returns the number // of bytes written, which equals length on success. func (s *Seekable) DecompressRange(dst []byte, offset uint64, length int) (int, error) { if s == nil || s.ptr == nil { return 0, ErrNullInput } if length < 0 { return 0, ErrInvalidData } if len(dst) < length { return 0, ErrDstTooSmall } var dptr unsafe.Pointer if len(dst) > 0 { dptr = unsafe.Pointer(&dst[0]) } res := C.zxc_seekable_decompress_range( s.ptr, dptr, C.size_t(len(dst)), C.uint64_t(offset), C.size_t(length), ) if res < 0 { return 0, errorFromCode(res) } return int(res), nil } // SetDictionary attaches a [Dictionary] (content + shared table) in one call. func (s *Seekable) SetDictionary(d *Dictionary) error { return s.SetDict(d.Content(), d.Huf()) } // SetDict attaches a pre-trained dictionary to the seekable handle so that // subsequent [Seekable.DecompressRange] calls can decode blocks that were // compressed with that dictionary. It must be called before decoding any // range from a dictionary-compressed archive. // // The dictionary content must match the one used at compression time; its ID // is validated against the archive header. When the archive was compressed // with a shared literal Huffman table, pass the same table as hufLengths // ([HufTableSize] bytes, see [DictHuf]); pass nil otherwise. Both buffers // are copied internally by the library. Passing an empty dict is an error. func (s *Seekable) SetDict(dict, hufLengths []byte) error { if s == nil || s.ptr == nil { return ErrNullInput } if len(dict) == 0 { return ErrSrcTooSmall } if len(hufLengths) > 0 && len(hufLengths) != HufTableSize { // The C library reads a fixed ZXC_HUF_TABLE_SIZE bytes from the table. return ErrBadHufTable } // The library copies both buffers internally, so the Go pointers only // need to stay valid for the duration of the call — passing them as // direct call arguments satisfies the cgo pointer rules with no copy. var huf unsafe.Pointer if len(hufLengths) > 0 { huf = unsafe.Pointer(&hufLengths[0]) } rc := C.zxc_seekable_set_dict(s.ptr, unsafe.Pointer(&dict[0]), C.size_t(len(dict)), huf) if rc < 0 { return errorFromCode(C.int64_t(rc)) } return nil } // ============================================================================ // Low-level seek table helpers // ============================================================================ // SeekTableSize returns the encoded byte size of a seek table covering // numBlocks data blocks. Use this to size a destination buffer for // [WriteSeekTable]. func SeekTableSize(numBlocks uint32) int { return int(C.zxc_seek_table_size(C.uint32_t(numBlocks))) } // WriteSeekTable writes a seek table (header + entries) into dst. // compSizes is the slice of per-block on-disk compressed sizes, in order. // // Most callers do not need this directly: the file and streaming APIs // already emit a seek table when [WithSeekable] is enabled. func WriteSeekTable(dst []byte, compSizes []uint32) (int, error) { if len(dst) == 0 { return 0, ErrDstTooSmall } if len(compSizes) == 0 { return 0, ErrInvalidData } res := C.zxc_write_seek_table( (*C.uint8_t)(unsafe.Pointer(&dst[0])), C.size_t(len(dst)), (*C.uint32_t)(unsafe.Pointer(&compSizes[0])), C.uint32_t(len(compSizes)), ) if res < 0 { return 0, errorFromCode(res) } return int(res), nil } zxc-0.13.0/wrappers/go/zxc_seekable_export.go000066400000000000000000000035411522536370400212500ustar00rootroot00000000000000/* ZXC - High-performance lossless compression Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. SPDX-License-Identifier: BSD-3-Clause */ package zxc /* #include #include #include "zxc_error.h" */ import "C" import ( "errors" "io" "runtime/cgo" "unsafe" ) // zxcGoSeekableReadAt is the C->Go trampoline invoked by the library for // every positional read against a user-supplied io.ReaderAt. It must not // panic across the FFI boundary; any error is mapped to ZXC_ERROR_IO. // // This trampoline lives in its own file, whose preamble contains no // declaration of zxcGoSeekableReadAt. cgo copies the preamble of any file // carrying an //export directive into _cgo_export.c, right before the // generated declaration that marks the symbol __declspec(dllexport) on // Windows. A hand-written forward declaration in that same preamble would // then precede the dllexport one, which clang on windows/arm64 rejects // ("redeclaration ... cannot add 'dllexport' attribute"). The forward // declaration needed by the reader glue stays in zxc_seekable.go — a file // without //export, so its preamble never reaches _cgo_export.c. // //export zxcGoSeekableReadAt func zxcGoSeekableReadAt(ctx unsafe.Pointer, dst unsafe.Pointer, length C.size_t, offset C.uint64_t) C.int64_t { h := cgo.Handle(uintptr(ctx)) reader, ok := h.Value().(io.ReaderAt) if !ok || dst == nil { return C.int64_t(C.ZXC_ERROR_IO) } // Map the C-owned destination region as a Go slice without copying. buf := unsafe.Slice((*byte)(dst), int(length)) n, err := reader.ReadAt(buf, int64(offset)) // io.ReaderAt explicitly allows (n == len(buf), io.EOF) when the read // ends exactly at EOF — which the footer and seek-table reads always do. if n != int(length) || (err != nil && !errors.Is(err, io.EOF)) { return C.int64_t(C.ZXC_ERROR_IO) } return C.int64_t(length) } zxc-0.13.0/wrappers/go/zxc_seekable_test.go000066400000000000000000000137371522536370400207160ustar00rootroot00000000000000/* ZXC - High-performance lossless compression Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. SPDX-License-Identifier: BSD-3-Clause */ package zxc import ( "bytes" "io" "os" "path/filepath" "sync/atomic" "testing" ) func buildSeekableArchive(t *testing.T, payload []byte) string { t.Helper() dir := t.TempDir() in := filepath.Join(dir, "input.bin") out := filepath.Join(dir, "archive.zxc") if err := os.WriteFile(in, payload, 0o644); err != nil { t.Fatalf("write input: %v", err) } if _, err := CompressFile(in, out, WithSeekable(true), WithChecksum(true)); err != nil { t.Fatalf("CompressFile(seekable): %v", err) } return out } func TestSeekableOpenAndQuery(t *testing.T) { payload := bytes.Repeat([]byte("ZXCseekable_"), 8192) // ~96 KiB path := buildSeekableArchive(t, payload) s, err := Open(path) if err != nil { t.Fatalf("Open: %v", err) } defer s.Close() if got := s.DecompressedSize(); got != uint64(len(payload)) { t.Fatalf("DecompressedSize = %d, want %d", got, len(payload)) } if s.NumBlocks() == 0 { t.Fatalf("NumBlocks = 0, want >= 1") } if cs, ok := s.BlockCompressedSize(0); !ok || cs == 0 { t.Fatalf("BlockCompressedSize(0) = %d ok=%v", cs, ok) } if ds, ok := s.BlockDecompressedSize(0); !ok || ds == 0 { t.Fatalf("BlockDecompressedSize(0) = %d ok=%v", ds, ok) } if _, ok := s.BlockCompressedSize(s.NumBlocks()); ok { t.Fatalf("BlockCompressedSize(out-of-range) should fail") } } func TestSeekableDecompressRange(t *testing.T) { payload := make([]byte, 64*1024) for i := range payload { payload[i] = byte(i * 31) } path := buildSeekableArchive(t, payload) s, err := Open(path) if err != nil { t.Fatalf("Open: %v", err) } defer s.Close() // Full range. full := make([]byte, len(payload)) n, err := s.DecompressRange(full, 0, len(payload)) if err != nil { t.Fatalf("DecompressRange full: %v", err) } if n != len(payload) || !bytes.Equal(full, payload) { t.Fatalf("full range mismatch (n=%d)", n) } // Mid-range. const off, length = 1024, 8192 mid := make([]byte, length) n, err = s.DecompressRange(mid, off, length) if err != nil { t.Fatalf("DecompressRange mid: %v", err) } if n != length || !bytes.Equal(mid, payload[off:off+length]) { t.Fatalf("mid range mismatch (n=%d)", n) } } func TestSeekableOpenBytes(t *testing.T) { payload := bytes.Repeat([]byte("memseekable_"), 4096) path := buildSeekableArchive(t, payload) data, err := os.ReadFile(path) if err != nil { t.Fatalf("read archive: %v", err) } s, err := OpenBytes(data) if err != nil { t.Fatalf("OpenBytes: %v", err) } defer s.Close() if got := s.DecompressedSize(); got != uint64(len(payload)) { t.Fatalf("DecompressedSize = %d, want %d", got, len(payload)) } out := make([]byte, len(payload)) if _, err := s.DecompressRange(out, 0, len(payload)); err != nil { t.Fatalf("DecompressRange: %v", err) } if !bytes.Equal(out, payload) { t.Fatalf("payload mismatch after OpenBytes round-trip") } } func TestSeekableInvalidBytes(t *testing.T) { if _, err := OpenBytes(make([]byte, 32)); err == nil { t.Fatalf("OpenBytes on garbage should fail") } if _, err := OpenBytes(nil); err == nil { t.Fatalf("OpenBytes(nil) should fail") } } // countingReaderAt wraps bytes.Reader and counts ReadAt invocations so // tests can assert lazy I/O at open and per-block reads. type countingReaderAt struct { inner io.ReaderAt calls int64 } func (c *countingReaderAt) ReadAt(p []byte, off int64) (int, error) { atomic.AddInt64(&c.calls, 1) return c.inner.ReadAt(p, off) } func TestSeekableOpenReader(t *testing.T) { payload := make([]byte, 256*1024) for i := range payload { payload[i] = byte(i*7) ^ 0x5A } path := buildSeekableArchive(t, payload) data, err := os.ReadFile(path) if err != nil { t.Fatalf("read archive: %v", err) } cr := &countingReaderAt{inner: bytes.NewReader(data)} s, err := OpenReader(cr, int64(len(data))) if err != nil { t.Fatalf("OpenReader: %v", err) } defer s.Close() // open_reader should have done exactly 3 reads: header, footer, seek // table. if got := atomic.LoadInt64(&cr.calls); got != 3 { t.Fatalf("open phase calls = %d, want 3", got) } if got := s.DecompressedSize(); got != uint64(len(payload)) { t.Fatalf("DecompressedSize = %d, want %d", got, len(payload)) } // Full-range round-trip. out := make([]byte, len(payload)) if _, err := s.DecompressRange(out, 0, len(payload)); err != nil { t.Fatalf("DecompressRange full: %v", err) } if !bytes.Equal(out, payload) { t.Fatalf("payload mismatch after full DecompressRange") } // Sub-range within a single block: must trigger exactly one extra read. before := atomic.LoadInt64(&cr.calls) chunk := make([]byte, 1024) if _, err := s.DecompressRange(chunk, 100, 1024); err != nil { t.Fatalf("DecompressRange sub: %v", err) } if !bytes.Equal(chunk, payload[100:1124]) { t.Fatalf("sub-range mismatch") } if delta := atomic.LoadInt64(&cr.calls) - before; delta != 1 { t.Fatalf("single-block sub-range should trigger 1 read, got %d", delta) } } func TestSeekableOpenReaderRejectsNilAndZero(t *testing.T) { if _, err := OpenReader(nil, 100); err == nil { t.Fatalf("OpenReader(nil, 100) should fail") } if _, err := OpenReader(bytes.NewReader([]byte{1, 2, 3}), 0); err == nil { t.Fatalf("OpenReader(r, 0) should fail") } } func TestSeekableOpenReaderRejectsGarbage(t *testing.T) { // 64 bytes of zeros pass the minimum-size check but parse as invalid. // The cgo handle must still be released (no leak in steady state). garbage := bytes.NewReader(make([]byte, 64)) if _, err := OpenReader(garbage, 64); err == nil { t.Fatalf("OpenReader on garbage should fail") } } func TestSeekTableSizeAndWrite(t *testing.T) { compSizes := []uint32{128, 256, 200, 4} sz := SeekTableSize(uint32(len(compSizes))) if sz == 0 { t.Fatalf("SeekTableSize = 0") } buf := make([]byte, sz) n, err := WriteSeekTable(buf, compSizes) if err != nil { t.Fatalf("WriteSeekTable: %v", err) } if n != sz { t.Fatalf("WriteSeekTable wrote %d bytes, want %d", n, sz) } } zxc-0.13.0/wrappers/go/zxc_stream.go000066400000000000000000000155201522536370400173670ustar00rootroot00000000000000/* ZXC - High-performance lossless compression Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. SPDX-License-Identifier: BSD-3-Clause */ package zxc /* #include #include "zxc.h" */ import "C" import ( "runtime" "unsafe" ) // ============================================================================ // Push Streaming API (single-threaded, caller-driven) // ============================================================================ // CStream is a push-based, single-threaded compression stream — the Go // counterpart of the C [zxc_cstream]. Use it to integrate ZXC into event // loops, callback-driven libraries, or non-blocking network protocols where // the [CompressFile] FILE*-based pipeline is not appropriate. // // CStream is not safe for concurrent use; one goroutine per stream. type CStream struct { ptr *C.zxc_cstream } // NewCStream creates a push compression stream. // // Honoured options: [WithLevel], [WithChecksum]. [WithThreads] and // [WithSeekable] are ignored (the push API is single-threaded and never // emits a seek table). [WithDict]/[WithDictionary] return // [ErrDictUnsupported]: the push-stream format carries no dictionary ID, so // dictionary compression would produce undecodable archives. func NewCStream(opts ...Option) (*CStream, error) { o := applyOptions(opts) if len(o.dict) > 0 || len(o.dictHuf) > 0 { return nil, ErrDictUnsupported } var copts C.zxc_compress_opts_t copts.level = C.int(o.level) if o.checksum { copts.checksum_enabled = 1 } ptr := C.zxc_cstream_create(&copts) if ptr == nil { return nil, ErrMemory } return &CStream{ptr: ptr}, nil } // Close releases the stream and all internal buffers. Safe to call multiple // times. func (c *CStream) Close() error { if c == nil || c.ptr == nil { return nil } C.zxc_cstream_free(c.ptr) c.ptr = nil return nil } // InSize returns the suggested input chunk size for best throughput. func (c *CStream) InSize() int { if c == nil || c.ptr == nil { return 0 } return int(C.zxc_cstream_in_size(c.ptr)) } // OutSize returns the suggested output chunk size to never trigger a partial // drain. func (c *CStream) OutSize() int { if c == nil || c.ptr == nil { return 0 } return int(C.zxc_cstream_out_size(c.ptr)) } // Compress pushes bytes from in into the stream and writes compressed bytes // to out. Returns: // // - consumed: bytes read from in. // - produced: bytes written into out. // - pending: bytes still staged inside the stream (drain out and call // again with the same or new in to continue). // // A return of (consumed, produced, 0, nil) means in was fully consumed and // no compressed bytes remain pending. Calling with an empty in is valid // (drain-only mode). func (c *CStream) Compress(out, in []byte) (consumed, produced int, pending int64, err error) { if c == nil || c.ptr == nil { return 0, 0, 0, ErrNullInput } var pinner runtime.Pinner defer pinner.Unpin() var inBuf C.zxc_inbuf_t if len(in) > 0 { pinner.Pin(&in[0]) inBuf.src = unsafe.Pointer(&in[0]) } inBuf.size = C.size_t(len(in)) var outBuf C.zxc_outbuf_t if len(out) > 0 { pinner.Pin(&out[0]) outBuf.dst = unsafe.Pointer(&out[0]) } outBuf.size = C.size_t(len(out)) r := C.zxc_cstream_compress(c.ptr, &outBuf, &inBuf) if r < 0 { return 0, 0, 0, errorFromCode(r) } return int(inBuf.pos), int(outBuf.pos), int64(r), nil } // End finalises the stream by flushing the residual block, then writing the // EOF block and the file footer to out. Call repeatedly while pending > 0, // draining out between calls. // // After End returns (produced, 0, nil), the stream is in DONE state; further // calls return ErrNullInput. Always call [CStream.Close] to release the // native resources. func (c *CStream) End(out []byte) (produced int, pending int64, err error) { if c == nil || c.ptr == nil { return 0, 0, ErrNullInput } var pinner runtime.Pinner defer pinner.Unpin() var outBuf C.zxc_outbuf_t if len(out) > 0 { pinner.Pin(&out[0]) outBuf.dst = unsafe.Pointer(&out[0]) } outBuf.size = C.size_t(len(out)) r := C.zxc_cstream_end(c.ptr, &outBuf) if r < 0 { return 0, 0, errorFromCode(r) } return int(outBuf.pos), int64(r), nil } // DStream is a push-based, single-threaded decompression stream — the Go // counterpart of the C [zxc_dstream]. type DStream struct { ptr *C.zxc_dstream } // NewDStream creates a push decompression stream. // // Honoured options: [WithChecksum]. [WithThreads] is ignored. // [WithDict]/[WithDictionary] return [ErrDictUnsupported] (see [NewCStream]). func NewDStream(opts ...Option) (*DStream, error) { o := applyOptions(opts) if len(o.dict) > 0 || len(o.dictHuf) > 0 { return nil, ErrDictUnsupported } var dopts C.zxc_decompress_opts_t if o.checksum { dopts.checksum_enabled = 1 } ptr := C.zxc_dstream_create(&dopts) if ptr == nil { return nil, ErrMemory } return &DStream{ptr: ptr}, nil } // Close releases the stream and all internal buffers. Safe to call multiple // times. func (d *DStream) Close() error { if d == nil || d.ptr == nil { return nil } C.zxc_dstream_free(d.ptr) d.ptr = nil return nil } // InSize returns the suggested input chunk size for the decompressor. func (d *DStream) InSize() int { if d == nil || d.ptr == nil { return 0 } return int(C.zxc_dstream_in_size(d.ptr)) } // OutSize returns the suggested output chunk size for the decompressor. func (d *DStream) OutSize() int { if d == nil || d.ptr == nil { return 0 } return int(C.zxc_dstream_out_size(d.ptr)) } // Finished reports whether the decoder has reached and validated the file // footer. Useful to detect truncated streams: if the input source is // drained and Finished returns false, the stream ended prematurely. func (d *DStream) Finished() bool { if d == nil || d.ptr == nil { return false } return C.zxc_dstream_finished(d.ptr) != 0 } // Decompress pushes compressed bytes from in into the stream and writes // decompressed bytes to out. Returns: // // - consumed: bytes read from in. // - produced: bytes written into out. // // A return of (0, 0, nil) when in is non-empty means no progress could be // made: either the parser is waiting for more input (feed more), or the // stream has reached DONE state (any trailing bytes in in are ignored). // Use [DStream.Finished] to disambiguate. func (d *DStream) Decompress(out, in []byte) (consumed, produced int, err error) { if d == nil || d.ptr == nil { return 0, 0, ErrNullInput } var pinner runtime.Pinner defer pinner.Unpin() var inBuf C.zxc_inbuf_t if len(in) > 0 { pinner.Pin(&in[0]) inBuf.src = unsafe.Pointer(&in[0]) } inBuf.size = C.size_t(len(in)) var outBuf C.zxc_outbuf_t if len(out) > 0 { pinner.Pin(&out[0]) outBuf.dst = unsafe.Pointer(&out[0]) } outBuf.size = C.size_t(len(out)) r := C.zxc_dstream_decompress(d.ptr, &outBuf, &inBuf) if r < 0 { return 0, 0, errorFromCode(r) } return int(inBuf.pos), int(outBuf.pos), nil } zxc-0.13.0/wrappers/go/zxc_test.go000066400000000000000000000414101522536370400170500ustar00rootroot00000000000000/* ZXC - High-performance lossless compression Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. SPDX-License-Identifier: BSD-3-Clause */ package zxc import ( "bytes" "fmt" "io" "os" "path/filepath" "testing" ) // ============================================================================ // Buffer API Tests // ============================================================================ func TestRoundtrip(t *testing.T) { data := []byte("Hello, ZXC! This is a test of the Go wrapper.") compressed, err := Compress(data) if err != nil { t.Fatalf("Compress: %v", err) } decompressed, err := Decompress(compressed) if err != nil { t.Fatalf("Decompress: %v", err) } if !bytes.Equal(data, decompressed) { t.Fatal("roundtrip mismatch") } } func TestAllLevels(t *testing.T) { data := []byte("Test data with repetition: CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC") for _, level := range AllLevels() { compressed, err := Compress(data, WithLevel(level)) if err != nil { t.Fatalf("Compress at level %d: %v", level, err) } decompressed, err := Decompress(compressed) if err != nil { t.Fatalf("Decompress at level %d: %v", level, err) } if !bytes.Equal(data, decompressed) { t.Fatalf("roundtrip mismatch at level %d", level) } } } func TestLargeData(t *testing.T) { // 1 MB of compressible data data := make([]byte, 1024*1024) for i := range data { data[i] = byte((i % 256) ^ ((i / 256) % 256)) } compressed, err := Compress(data) if err != nil { t.Fatalf("Compress: %v", err) } if len(compressed) >= len(data) { t.Fatalf("expected compression, got %d >= %d", len(compressed), len(data)) } decompressed, err := Decompress(compressed) if err != nil { t.Fatalf("Decompress: %v", err) } if !bytes.Equal(data, decompressed) { t.Fatal("large data roundtrip mismatch") } } func TestDecompressedSize(t *testing.T) { data := []byte("Hello, world! Testing DecompressedSize function.") compressed, err := Compress(data) if err != nil { t.Fatalf("Compress: %v", err) } size, err := DecompressedSize(compressed) if err != nil { t.Fatalf("DecompressedSize: %v", err) } if size != uint64(len(data)) { t.Fatalf("DecompressedSize = %d, want %d", size, len(data)) } } func TestCompressBound(t *testing.T) { bound := CompressBound(1024) if bound <= 1024 { t.Fatalf("CompressBound(1024) = %d, want > 1024", bound) } } func TestInvalidData(t *testing.T) { _, err := Decompress([]byte("not valid zxc data")) if err == nil { t.Fatal("expected error on invalid data") } } func TestEmptyInput(t *testing.T) { // Empty input produces a valid minimal frame (header + EOF + footer) that // round-trips back to empty. for _, in := range [][]byte{nil, {}} { compressed, err := Compress(in) if err != nil { t.Fatalf("Compress(empty) error: %v", err) } if len(compressed) == 0 { t.Fatal("Compress(empty) should produce a non-empty frame") } out, err := Decompress(compressed) if err != nil { t.Fatalf("Decompress(empty frame) error: %v", err) } if len(out) != 0 { t.Fatalf("empty must round-trip to empty, got %d bytes", len(out)) } } } func TestChecksumRoundtrip(t *testing.T) { data := []byte("Test with checksum enabled for data integrity verification") compressed, err := Compress(data, WithChecksum(true)) if err != nil { t.Fatalf("Compress with checksum: %v", err) } decompressed, err := Decompress(compressed, WithChecksum(true)) if err != nil { t.Fatalf("Decompress with checksum: %v", err) } if !bytes.Equal(data, decompressed) { t.Fatal("checksum roundtrip mismatch") } } func TestCompressTo(t *testing.T) { data := []byte("Testing CompressTo with pre-allocated buffer") output := make([]byte, CompressBound(len(data))) n, err := CompressTo(data, output) if err != nil { t.Fatalf("CompressTo: %v", err) } decompressed, err := Decompress(output[:n]) if err != nil { t.Fatalf("Decompress: %v", err) } if !bytes.Equal(data, decompressed) { t.Fatal("CompressTo roundtrip mismatch") } } func TestVersion(t *testing.T) { major, minor, patch := Version() s := VersionString() if s == "" { t.Fatal("VersionString returned empty") } expected := fmt.Sprintf("%d.%d.%d", major, minor, patch) if s != expected { t.Fatalf("VersionString() = %q, want %q", s, expected) } t.Logf("ZXC version: %s", s) } func TestErrorMessages(t *testing.T) { errors := []*Error{ ErrMemory, ErrDstTooSmall, ErrCorruptData, ErrBadChecksum, } for _, e := range errors { msg := e.Error() if msg == "" { t.Fatalf("expected non-empty error message for code %d", e.Code) } } } // ============================================================================ // Streaming API Tests // ============================================================================ func tempPath(t *testing.T, name string) string { t.Helper() dir := filepath.Join(os.TempDir(), "zxc_go_test") if err := os.MkdirAll(dir, 0o755); err != nil { t.Fatalf("MkdirAll: %v", err) } return filepath.Join(dir, name) } func TestFileRoundtrip(t *testing.T) { inputPath := tempPath(t, "roundtrip_input.bin") compressedPath := tempPath(t, "roundtrip_compressed.zxc") outputPath := tempPath(t, "roundtrip_output.bin") defer os.Remove(inputPath) defer os.Remove(compressedPath) defer os.Remove(outputPath) // 64 KB of compressible data data := make([]byte, 64*1024) for i := range data { data[i] = byte((i % 256) ^ ((i / 256) % 256)) } if err := os.WriteFile(inputPath, data, 0o644); err != nil { t.Fatalf("WriteFile: %v", err) } csize, err := CompressFile(inputPath, compressedPath) if err != nil { t.Fatalf("CompressFile: %v", err) } if csize <= 0 { t.Fatalf("CompressFile returned %d bytes", csize) } dsize, err := DecompressFile(compressedPath, outputPath) if err != nil { t.Fatalf("DecompressFile: %v", err) } if dsize != int64(len(data)) { t.Fatalf("DecompressFile: got %d bytes, want %d", dsize, len(data)) } result, err := os.ReadFile(outputPath) if err != nil { t.Fatalf("ReadFile: %v", err) } if !bytes.Equal(data, result) { t.Fatal("file roundtrip mismatch") } } func TestFileAllLevels(t *testing.T) { inputPath := tempPath(t, "levels_input.bin") defer os.Remove(inputPath) data := make([]byte, 32*1024) for i := range data { data[i] = byte((i % 256) ^ ((i / 256) % 256)) } if err := os.WriteFile(inputPath, data, 0o644); err != nil { t.Fatalf("WriteFile: %v", err) } for _, level := range AllLevels() { compressedPath := tempPath(t, "levels_compressed.zxc") outputPath := tempPath(t, "levels_output.bin") _, err := CompressFile(inputPath, compressedPath, WithLevel(level)) if err != nil { t.Fatalf("CompressFile at level %d: %v", level, err) } _, err = DecompressFile(compressedPath, outputPath) if err != nil { t.Fatalf("DecompressFile at level %d: %v", level, err) } result, err := os.ReadFile(outputPath) if err != nil { t.Fatalf("ReadFile: %v", err) } if !bytes.Equal(data, result) { t.Fatalf("file roundtrip mismatch at level %d", level) } os.Remove(compressedPath) os.Remove(outputPath) } } func TestFileMultithreaded(t *testing.T) { inputPath := tempPath(t, "mt_input.bin") compressedPath := tempPath(t, "mt_compressed.zxc") outputPath := tempPath(t, "mt_output.bin") defer os.Remove(inputPath) defer os.Remove(compressedPath) defer os.Remove(outputPath) // 1 MB of data data := make([]byte, 1024*1024) for i := range data { data[i] = byte((i % 256) ^ ((i / 256) % 256)) } if err := os.WriteFile(inputPath, data, 0o644); err != nil { t.Fatalf("WriteFile: %v", err) } for _, threads := range []int{1, 2, 4} { _, err := CompressFile(inputPath, compressedPath, WithThreads(threads)) if err != nil { t.Fatalf("CompressFile with %d threads: %v", threads, err) } dsize, err := DecompressFile(compressedPath, outputPath, WithThreads(threads)) if err != nil { t.Fatalf("DecompressFile with %d threads: %v", threads, err) } if dsize != int64(len(data)) { t.Fatalf("got %d bytes with %d threads, want %d", dsize, threads, len(data)) } result, err := os.ReadFile(outputPath) if err != nil { t.Fatalf("ReadFile: %v", err) } if !bytes.Equal(data, result) { t.Fatalf("mismatch with %d threads", threads) } } } // ============================================================================ // Push Streaming API Tests // ============================================================================ // pstreamRoundtrip drives a CStream / DStream pair end-to-end and returns // the decoded bytes. func pstreamRoundtrip(t *testing.T, data []byte, copts, dopts []Option) []byte { t.Helper() cs, err := NewCStream(copts...) if err != nil { t.Fatalf("NewCStream: %v", err) } defer cs.Close() outCap := cs.OutSize() if outCap < 64 { outCap = 64 } out := make([]byte, outCap) var compressed bytes.Buffer cursor := 0 for cursor < len(data) { consumed, produced, _, err := cs.Compress(out, data[cursor:]) if err != nil { t.Fatalf("CStream.Compress: %v", err) } cursor += consumed compressed.Write(out[:produced]) } for { produced, pending, err := cs.End(out) if err != nil { t.Fatalf("CStream.End: %v", err) } compressed.Write(out[:produced]) if pending == 0 { break } } ds, err := NewDStream(dopts...) if err != nil { t.Fatalf("NewDStream: %v", err) } defer ds.Close() dout := make([]byte, 64*1024) var decompressed bytes.Buffer src := compressed.Bytes() cursor = 0 for cursor < len(src) && !ds.Finished() { consumed, produced, err := ds.Decompress(dout, src[cursor:]) if err != nil { t.Fatalf("DStream.Decompress: %v", err) } cursor += consumed decompressed.Write(dout[:produced]) if consumed == 0 && produced == 0 { break } } for { _, produced, err := ds.Decompress(dout, nil) if err != nil { t.Fatalf("DStream.Decompress (drain): %v", err) } decompressed.Write(dout[:produced]) if produced == 0 { break } } if !ds.Finished() { t.Fatalf("DStream did not finalise (truncated stream?)") } return decompressed.Bytes() } func TestPStreamSmallRoundtrip(t *testing.T) { data := []byte("Hello pstream! Round-trip through the Go push API.") got := pstreamRoundtrip(t, data, nil, nil) if !bytes.Equal(got, data) { t.Fatalf("mismatch: got %d bytes, want %d", len(got), len(data)) } } func TestPStreamWithChecksum(t *testing.T) { data := make([]byte, 32*1024) for i := range data { data[i] = byte(i % 251) } copts := []Option{WithChecksum(true)} dopts := []Option{WithChecksum(true)} got := pstreamRoundtrip(t, data, copts, dopts) if !bytes.Equal(got, data) { t.Fatalf("checksum roundtrip mismatch (%d vs %d bytes)", len(got), len(data)) } } func TestPStreamMultiBlock(t *testing.T) { data := make([]byte, 512*1024) for i := range data { data[i] = byte((i * 7) % 256) } got := pstreamRoundtrip(t, data, nil, nil) if !bytes.Equal(got, data) { t.Fatalf("multi-block roundtrip mismatch (%d vs %d bytes)", len(got), len(data)) } } func TestPStreamSizeHints(t *testing.T) { cs, err := NewCStream() if err != nil { t.Fatalf("NewCStream: %v", err) } defer cs.Close() if cs.InSize() == 0 || cs.OutSize() == 0 { t.Fatalf("cstream size hints should be non-zero") } ds, err := NewDStream() if err != nil { t.Fatalf("NewDStream: %v", err) } defer ds.Close() if ds.InSize() == 0 || ds.OutSize() == 0 { t.Fatalf("dstream size hints should be non-zero") } if ds.Finished() { t.Fatalf("dstream should not be finished before input") } } // ============================================================================ // io.Reader / io.Writer adapter tests // ============================================================================ func ioRoundtrip(t *testing.T, data []byte, copts, dopts []Option) []byte { t.Helper() var buf bytes.Buffer w, err := NewWriter(&buf, copts...) if err != nil { t.Fatalf("NewWriter: %v", err) } n, err := w.Write(data) if err != nil { t.Fatalf("Writer.Write: %v", err) } if n != len(data) { t.Fatalf("Writer.Write short: %d/%d", n, len(data)) } if err := w.Close(); err != nil { t.Fatalf("Writer.Close: %v", err) } r, err := NewReader(&buf, dopts...) if err != nil { t.Fatalf("NewReader: %v", err) } defer r.Close() got, err := io.ReadAll(r) if err != nil { t.Fatalf("ReadAll: %v", err) } return got } func TestWriterReaderSmallRoundtrip(t *testing.T) { data := []byte("Hello io.Writer / io.Reader bridge over ZXC.") got := ioRoundtrip(t, data, nil, nil) if !bytes.Equal(got, data) { t.Fatalf("mismatch: got %q want %q", got, data) } } func TestWriterReaderLargeRoundtrip(t *testing.T) { data := make([]byte, 2*1024*1024) for i := range data { data[i] = byte((i * 13) % 251) } got := ioRoundtrip(t, data, nil, nil) if !bytes.Equal(got, data) { t.Fatalf("large roundtrip mismatch (%d vs %d)", len(got), len(data)) } } func TestWriterReaderManySmallWrites(t *testing.T) { var buf bytes.Buffer w, err := NewWriter(&buf) if err != nil { t.Fatalf("NewWriter: %v", err) } want := make([]byte, 0, 64*1024) for i := 0; i < 4096; i++ { chunk := []byte{byte(i), byte(i >> 8), byte(i ^ 0x5A)} want = append(want, chunk...) if _, err := w.Write(chunk); err != nil { t.Fatalf("Write(%d): %v", i, err) } } if err := w.Close(); err != nil { t.Fatalf("Writer.Close: %v", err) } r, err := NewReader(&buf) if err != nil { t.Fatalf("NewReader: %v", err) } defer r.Close() got, err := io.ReadAll(r) if err != nil { t.Fatalf("ReadAll: %v", err) } if !bytes.Equal(got, want) { t.Fatalf("many-writes mismatch (%d vs %d)", len(got), len(want)) } } func TestWriterReaderWithChecksum(t *testing.T) { data := make([]byte, 32*1024) for i := range data { data[i] = byte(i) } got := ioRoundtrip(t, data, []Option{WithChecksum(true)}, []Option{WithChecksum(true)}) if !bytes.Equal(got, data) { t.Fatalf("checksum roundtrip mismatch") } } func TestWriterCloseIsIdempotent(t *testing.T) { var buf bytes.Buffer w, err := NewWriter(&buf) if err != nil { t.Fatalf("NewWriter: %v", err) } if _, err := w.Write([]byte("hello")); err != nil { t.Fatalf("Write: %v", err) } if err := w.Close(); err != nil { t.Fatalf("Close 1: %v", err) } if err := w.Close(); err != nil { t.Fatalf("Close 2: %v", err) } } func TestReaderCloseIsIdempotent(t *testing.T) { var buf bytes.Buffer w, _ := NewWriter(&buf) w.Write([]byte("xxx")) w.Close() r, err := NewReader(&buf) if err != nil { t.Fatalf("NewReader: %v", err) } if err := r.Close(); err != nil { t.Fatalf("Close 1: %v", err) } if err := r.Close(); err != nil { t.Fatalf("Close 2: %v", err) } } func TestReaderTruncatedFrame(t *testing.T) { var buf bytes.Buffer w, _ := NewWriter(&buf) payload := bytes.Repeat([]byte("ABCDEFGH"), 4096) if _, err := w.Write(payload); err != nil { t.Fatalf("Write: %v", err) } if err := w.Close(); err != nil { t.Fatalf("Close: %v", err) } // Truncate before the footer to force a premature EOF. full := buf.Bytes() truncated := full[:len(full)/2] r, err := NewReader(bytes.NewReader(truncated)) if err != nil { t.Fatalf("NewReader: %v", err) } defer r.Close() _, err = io.ReadAll(r) if err == nil { t.Fatal("expected error reading truncated frame, got nil") } } // ============================================================================ // DetectZxc // ============================================================================ func TestDetectZxc(t *testing.T) { // Real ZXC frame must be detected. compressed, err := Compress([]byte("magic-detection-input")) if err != nil { t.Fatalf("Compress: %v", err) } if !DetectZxc(compressed) { t.Fatal("DetectZxc returned false for a valid ZXC frame") } // Same goes for an io.Writer-produced frame. var buf bytes.Buffer w, _ := NewWriter(&buf) w.Write([]byte("hi")) w.Close() if !DetectZxc(buf.Bytes()) { t.Fatal("DetectZxc returned false for an io.Writer-produced frame") } // Negative cases. cases := [][]byte{ nil, {}, {0xF5, 0x2E, 0xB0}, // too short {0x00, 0x00, 0x00, 0x00}, []byte("not a zxc frame at all"), } for _, c := range cases { if DetectZxc(c) { t.Fatalf("DetectZxc returned true for non-ZXC input %x", c) } } } // ============================================================================ // Benchmarks // ============================================================================ func BenchmarkCompress(b *testing.B) { data := make([]byte, 1024*1024) for i := range data { data[i] = byte((i % 256) ^ ((i / 256) % 256)) } b.SetBytes(int64(len(data))) b.ResetTimer() for i := 0; i < b.N; i++ { _, err := Compress(data) if err != nil { b.Fatal(err) } } } func BenchmarkDecompress(b *testing.B) { data := make([]byte, 1024*1024) for i := range data { data[i] = byte((i % 256) ^ ((i / 256) % 256)) } compressed, err := Compress(data) if err != nil { b.Fatal(err) } b.SetBytes(int64(len(data))) b.ResetTimer() for i := 0; i < b.N; i++ { _, err := Decompress(compressed) if err != nil { b.Fatal(err) } } } zxc-0.13.0/wrappers/nodejs/000077500000000000000000000000001522536370400155335ustar00rootroot00000000000000zxc-0.13.0/wrappers/nodejs/.gitignore000066400000000000000000000000471522536370400175240ustar00rootroot00000000000000node_modules/ build/ prebuilds/ *.node zxc-0.13.0/wrappers/nodejs/.nvmrc000066400000000000000000000000021522536370400166510ustar00rootroot0000000000000022zxc-0.13.0/wrappers/nodejs/CMakeLists.txt000066400000000000000000000027741522536370400203050ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.14) project(zxc_nodejs C CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # ---- Core ZXC library ---- add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../ ${CMAKE_CURRENT_BINARY_DIR}/zxc_core_build) # ---- Node.js addon ---- include_directories(${CMAKE_JS_INC}) file(GLOB SOURCE_FILES "src/zxc_addon.cc") add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC}) set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node" ) target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../include ${CMAKE_JS_INC} ) # Generate node.lib on Windows (cmake-js provides .def but not .lib) if(MSVC AND CMAKE_JS_NODELIB_DEF AND CMAKE_JS_NODELIB_TARGET) execute_process(COMMAND ${CMAKE_AR} /def:${CMAKE_JS_NODELIB_DEF} /out:${CMAKE_JS_NODELIB_TARGET} ${CMAKE_STATIC_LINKER_FLAGS}) endif() target_link_libraries(${PROJECT_NAME} PRIVATE zxc_lib ${CMAKE_JS_LIB} ) # node-addon-api (header-only, no exceptions by default) execute_process( COMMAND node -p "require('node-addon-api').include" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE NAPI_INCLUDE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE ) # Strip surrounding quotes if present string(REPLACE "\"" "" NAPI_INCLUDE_DIR ${NAPI_INCLUDE_DIR}) target_include_directories(${PROJECT_NAME} PRIVATE ${NAPI_INCLUDE_DIR}) target_compile_definitions(${PROJECT_NAME} PRIVATE NAPI_VERSION=8) zxc-0.13.0/wrappers/nodejs/README.md000066400000000000000000000050031522536370400170100ustar00rootroot00000000000000# ZXC Node.js Bindings High-performance Node.js bindings for the **ZXC** asymmetric compressor, optimized for **fast decompression**. Designed for *Write Once, Read Many* workloads like ML datasets, game assets, and caches. ## Features - **Blazing fast decompression** - ZXC is specifically optimized for read-heavy workloads. - **Native N-API addon** - compiled C code via cmake-js for maximum performance. - **Buffer support** - works directly with Node.js `Buffer` objects. - **TypeScript declarations** - full type definitions included. ## Installation (from source) ```bash git clone https://github.com/hellobertrand/zxc.git cd zxc/wrappers/nodejs npm install ``` ### Prerequisites - Node.js >= 16.0.0 - CMake >= 3.14 - A C17/C++17 compiler (GCC, Clang, or MSVC) ## Usage ```javascript const zxc = require('zxc-compress'); // Compress const data = Buffer.from('Hello, World!'.repeat(1_000)); const compressed = zxc.compress(data, { level: zxc.LEVEL_DEFAULT }); // Decompress (auto-detects size) const decompressed = zxc.decompress(compressed); console.log(`Original: ${data.length} bytes`); console.log(`Compressed: ${compressed.length} bytes`); console.log(`Ratio: ${(compressed.length / data.length * 100).toFixed(1)}%`); ``` ## API ### `compress(data, options?)` Compress a Buffer. | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `data` | `Buffer` | - | Input data | | `options.level` | `number` | `LEVEL_DEFAULT` | Compression level (1–7) | | `options.checksum` | `boolean` | `false` | Enable checksum | Returns: `Buffer` - compressed data. ### `decompress(data, options?)` Decompress a ZXC compressed Buffer. | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `data` | `Buffer` | - | Compressed data | | `options.size` | `number` | auto | Expected decompressed size | | `options.checksum` | `boolean` | `false` | Verify checksum | Returns: `Buffer` - decompressed data. ### `compressBound(inputSize)` Returns the maximum compressed size for a given input size. ### `getDecompressedSize(data)` Returns the original size from a ZXC compressed buffer (reads footer only). ### Constants | Constant | Value | Description | |----------|-------|-------------| | `LEVEL_FASTEST` | 1 | Fastest compression | | `LEVEL_FAST` | 2 | Fast compression | | `LEVEL_DEFAULT` | 3 | Recommended balance | | `LEVEL_BALANCED` | 4 | Good ratio, good speed | | `LEVEL_COMPACT` | 5 | Highest density | ## Testing ```bash npm test ``` ## License BSD-3-Clause zxc-0.13.0/wrappers/nodejs/lib/000077500000000000000000000000001522536370400163015ustar00rootroot00000000000000zxc-0.13.0/wrappers/nodejs/lib/index.d.ts000066400000000000000000000336261522536370400202140ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** Fastest compression, best for real-time applications. */ export const LEVEL_FASTEST: number; /** Fast compression, good for real-time applications. */ export const LEVEL_FAST: number; /** Recommended: ratio > LZ4, decode speed > LZ4. */ export const LEVEL_DEFAULT: number; /** Good ratio, good decode speed. */ export const LEVEL_BALANCED: number; /** High density. Best for storage/firmware/assets. */ export const LEVEL_COMPACT: number; /** High density: Huffman-coded literals on top of COMPACT. */ export const LEVEL_DENSITY: number; /** Maximum density: Huffman-coded literals and sequence tokens (level 7 / ULTRA). */ export const LEVEL_ULTRA: number; /** Memory allocation failure. */ export const ERROR_MEMORY: number; /** Destination buffer too small. */ export const ERROR_DST_TOO_SMALL: number; /** Source buffer too small or truncated input. */ export const ERROR_SRC_TOO_SMALL: number; /** Invalid magic word in file header. */ export const ERROR_BAD_MAGIC: number; /** Unsupported file format version. */ export const ERROR_BAD_VERSION: number; /** Corrupted or invalid header (CRC mismatch). */ export const ERROR_BAD_HEADER: number; /** Block or global checksum verification failed. */ export const ERROR_BAD_CHECKSUM: number; /** Corrupted compressed data. */ export const ERROR_CORRUPT_DATA: number; /** Invalid match offset during decompression. */ export const ERROR_BAD_OFFSET: number; /** Buffer overflow detected during processing. */ export const ERROR_OVERFLOW: number; /** Read/write/seek failure on file. */ export const ERROR_IO: number; /** Required input pointer is NULL. */ export const ERROR_NULL_INPUT: number; /** Unknown or unexpected block type. */ export const ERROR_BAD_BLOCK_TYPE: number; /** Invalid block size. */ export const ERROR_BAD_BLOCK_SIZE: number; /** File requires a dictionary but none was provided. */ export const ERROR_DICT_REQUIRED: number; /** Provided dictionary ID does not match the file header. */ export const ERROR_DICT_MISMATCH: number; /** Dictionary exceeds maximum allowed size. */ export const ERROR_DICT_TOO_LARGE: number; export const ERROR_BAD_LEVEL: number; export interface CompressOptions { /** Compression level (1-7). Defaults to LEVEL_DEFAULT. */ level?: number; /** Enable checksum verification. Defaults to false. */ checksum?: boolean; /** Enable seek table for random-access decompression. Defaults to false. */ seekable?: boolean; /** Pre-trained dictionary: a {@link Dictionary} instance, or raw content * bytes. Defaults to none. */ dict?: Dictionary | Buffer | Uint8Array; /** Shared literal Huffman table (128 bytes, from {@link trainDictHuf} or * {@link dictHuf}). Ignored without `dict`, and ignored when `dict` is a * {@link Dictionary} (which carries its own table). */ dictHuf?: Buffer | Uint8Array; } export interface DecompressOptions { /** Expected decompressed size. If omitted, read from header. */ size?: number; /** Enable checksum verification. Defaults to false. */ checksum?: boolean; /** Pre-trained dictionary: a {@link Dictionary} instance, or raw content * bytes. Required when the archive references a dictionary. */ dict?: Dictionary | Buffer | Uint8Array; /** Shared literal Huffman table (128 bytes) when the archive was compressed * with one (the dictionary ID binds the pair). Ignored when `dict` is a * {@link Dictionary}. */ dictHuf?: Buffer | Uint8Array; } /** * Returns the maximum compressed size for a given input size. * Useful for pre-allocating output buffers. */ export function compressBound(inputSize: number): number; /** * Compress a Buffer using the ZXC algorithm. */ export function compress(data: Buffer, options?: CompressOptions): Buffer; /** * Returns the original decompressed size from a ZXC compressed buffer. * Reads the footer without performing decompression. */ export function getDecompressedSize(data: Buffer): number; /** * Decompress a ZXC compressed Buffer. */ export function decompress(data: Buffer, options?: DecompressOptions): Buffer; /** * Returns a human-readable name for a given error code. */ export function errorName(code: number): string; // ---------- Pre-trained dictionary support ---------- /** Result of {@link dictLoad}. */ export interface LoadedDict { /** Raw dictionary content bytes. */ content: Buffer; /** 128-byte shared literal Huffman table. */ huf: Buffer; /** 32-bit dictionary ID (binds the (content, table) pair). */ id: number; } /** * A trained dictionary: LZ-window content plus its shared literal Huffman * table, bundled as one object. Pass an instance as `options.dict` to * {@link compress} / {@link decompress}, or to `Seekable#setDict`. */ export class Dictionary { constructor(content: Buffer, huf: Buffer, id: number); /** Train a complete dictionary (content + shared table) from samples. */ static train(samples: Array): Dictionary; /** Parse `.zxd` bytes into a Dictionary (owned copies). */ static load(zxd: Buffer): Dictionary; /** Serialize back to `.zxd` file bytes. */ save(): Buffer; /** Raw LZ-window content bytes. */ content: Buffer; /** 128-byte shared literal Huffman table. */ huf: Buffer; /** Dictionary ID binding the (content, table) pair. */ id: number; } /** * Train a pre-trained dictionary from a corpus of sample buffers. * Improves compression ratio on small, similar payloads. * * @param samples - Non-empty array of representative sample buffers. * @param maxSize - Maximum dictionary content size in bytes (defaults to 65535). * @returns Raw dictionary content suitable for `CompressOptions.dict`. */ export function trainDict(samples: Array, maxSize?: number): Buffer; /** Compute the deterministic 32-bit dictionary ID for raw dictionary content. */ export function dictId(content: Buffer): number; /** * Returns the dictionary ID referenced by a `.zxc` archive header, or 0 if the * archive does not require a dictionary. */ export function getDictId(archive: Buffer): number; /** * Returns the dictionary ID stored in a `.zxd` dictionary file, or 0 if the * buffer is not a valid `.zxd` file. */ export function dictGetId(zxd: Buffer): number; /** * Serialize dictionary content and its shared literal Huffman table * (128 bytes, from {@link trainDictHuf}) into the `.zxd` file format. * The stored dictionary ID covers both content and table. */ export function dictSave(content: Buffer, hufLengths: Buffer): Buffer; /** * Train the shared literal Huffman table for an already-trained dictionary. * Returns the 128-byte packed table required by {@link dictSave} and usable * as `CompressOptions.dictHuf` / `DecompressOptions.dictHuf`. */ export function trainDictHuf(samples: Array, dict: Buffer): Buffer; /** * Return the 128-byte shared Huffman table stored in a `.zxd` file, or `null` * if the buffer is not a valid `.zxd` file. */ export function dictHuf(zxd: Buffer): Buffer | null; /** Load and validate a `.zxd` dictionary file. */ export function dictLoad(zxd: Buffer): LoadedDict; /** Returns the minimum supported compression level (currently 1). */ export function minLevel(): number; /** Returns the maximum supported compression level (currently 7). */ export function maxLevel(): number; /** Returns the default compression level (currently 3). */ export function defaultLevel(): number; /** * Returns the version string reported by the linked native libzxc * (e.g. "0.13.0"). Distinct from the npm package version. */ export function libraryVersion(): string; export interface CStreamOptions { /** Compression level (1-7). Defaults to LEVEL_DEFAULT. */ level?: number; /** Enable per-block and global checksums. Defaults to false. */ checksum?: boolean; /** Block size in bytes (0 = default 512 KB). Power of 2, 4 KB – 2 MB. */ blockSize?: number; } /** * Push-based, single-threaded compression stream. * * The Node.js counterpart of the C `zxc_cstream`. Each call to * `compress(buf)` returns the compressed bytes produced from that input * (may be empty if the bytes fit in the internal block accumulator). * Always call `end()` to flush the residual block, EOF marker and footer. */ export class CStream { constructor(options?: CStreamOptions); /** Push input and return any compressed bytes produced this call. */ compress(data: Buffer): Buffer; /** Finalise the stream: residual block + EOF + file footer. */ end(): Buffer; /** Release native resources. Idempotent. */ close(): void; /** Suggested input chunk size in bytes. */ inSize(): number; /** Suggested output chunk size in bytes. */ outSize(): number; } export interface DStreamOptions { /** Verify per-block and global checksums when present. Defaults to false. */ checksum?: boolean; } /** * Push-based, single-threaded decompression stream. */ export class DStream { constructor(options?: DStreamOptions); /** Push compressed bytes and return any decompressed bytes produced. */ decompress(data: Buffer): Buffer; /** True once the decoder has reached and validated the file footer. */ finished(): boolean; /** Release native resources. Idempotent. */ close(): void; /** Suggested input chunk size in bytes. */ inSize(): number; /** Suggested output chunk size in bytes. */ outSize(): number; } // ---------- stream.Transform adapters ---------- import { Transform, TransformOptions } from 'node:stream'; /** Returns true if `buf` starts with the ZXC file magic word. */ export function detectZxc(buf: Buffer | Uint8Array): boolean; export interface CompressStreamOptions extends TransformOptions, CStreamOptions {} export interface DecompressStreamOptions extends TransformOptions, DStreamOptions {} /** * `stream.Transform` that compresses bytes through a ZXC frame. Designed to * be piped between any Node Readable/Writable (fs, http, tar-stream, OCI * registry clients, etc.). Mirrors `zlib.createGzip()` ergonomics. */ export class CompressStream extends Transform { constructor(options?: CompressStreamOptions); } /** * `stream.Transform` that decompresses a ZXC frame. Emits `'error'` with * `code === 'ZXC_TRUNCATED'` if the input ends before the footer. */ export class DecompressStream extends Transform { constructor(options?: DecompressStreamOptions); } export function createCompressStream(options?: CompressStreamOptions): CompressStream; export function createDecompressStream(options?: DecompressStreamOptions): DecompressStream; // ---------- Seekable random-access decompression ---------- /** * Storage-agnostic reader interface for {@link Seekable.constructor}. * * Supply this when the archive is too large to fit in a Buffer, or when * it lives behind a custom backend (file descriptor, HTTP range, S3, etc.). * `readAt` is invoked synchronously on the calling thread; do not perform * async I/O here. Use `fs.readSync(fd, buf, 0, buf.length, offset)` or * similar. */ export interface SeekableReader { /** Total size of the compressed archive in bytes. */ size: number; /** * Fill `buf` with `buf.length` bytes starting at `offset` in the * archive. Throwing causes the surrounding Seekable operation to * fail with an I/O error. */ readAt(buf: Buffer, offset: number): void; } /** * Handle on a seekable ZXC archive. * * Two source modes: * - **Buffer**: pass a compressed Buffer carrying an embedded seek table * (produced by `compress(..., { seekable: true })`). The Buffer is * copied into the addon's heap. * - **Reader callback**: pass a {@link SeekableReader} to back the archive * with any storage that supports positional reads (file descriptor, * HTTP range, S3, custom VFS). `readAt` runs synchronously on the * calling thread; this binding does not support multi-threaded * decompression when a reader callback is used. * * Call `close()` to release native resources. * * A Seekable handle is single-threaded - do not share it across worker * threads. */ export class Seekable { constructor(compressed: Buffer); constructor(reader: SeekableReader); /** Total number of data blocks (excluding the EOF marker block). */ numBlocks(): number; /** Total decompressed size of the archive in bytes. */ decompressedSize(): number; /** * On-disk compressed size of a specific block (block header + * payload + optional per-block checksum). Returns `null` if * `blockIdx` is out of range. */ blockCompressedSize(blockIdx: number): number | null; /** * Decompressed size of a specific block, or `null` if `blockIdx` is * out of range. */ blockDecompressedSize(blockIdx: number): number | null; /** * Decompress `length` bytes starting at `offset` (in the original * uncompressed byte stream). Only the blocks overlapping the * requested range are read. */ decompressRange(offset: number, length: number): Buffer; /** * Attach a pre-trained dictionary to this handle. Must be called before * any `decompressRange` call when the archive was compressed with a * dictionary. The content is copied internally. */ setDict(dict: Buffer | Uint8Array, dictHuf?: Buffer | Uint8Array): void; /** Release native resources. Idempotent. */ close(): void; } /** * Encoded byte size of a seek table covering `numBlocks` data blocks. * Use this to size a destination buffer for {@link writeSeekTable}. */ export function seekTableSize(numBlocks: number): number; /** * Low-level: write a seek table (header + entries) for the given * per-block on-disk compressed sizes. Most callers do not need this - * the buffer and streaming APIs emit a seek table automatically when * `seekable: true` is set. */ export function writeSeekTable(compSizes: number[]): Buffer; zxc-0.13.0/wrappers/nodejs/lib/index.js000066400000000000000000000500331522536370400177470ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ 'use strict'; const { Transform } = require('node:stream'); const native = require('../build/Release/zxc_nodejs.node'); // Re-export compression level constants const LEVEL_FASTEST = native.LEVEL_FASTEST; const LEVEL_FAST = native.LEVEL_FAST; const LEVEL_DEFAULT = native.LEVEL_DEFAULT; const LEVEL_BALANCED = native.LEVEL_BALANCED; const LEVEL_COMPACT = native.LEVEL_COMPACT; const LEVEL_DENSITY = native.LEVEL_DENSITY; const LEVEL_ULTRA = native.LEVEL_ULTRA; // Re-export error constants const ERROR_MEMORY = native.ERROR_MEMORY; const ERROR_DST_TOO_SMALL = native.ERROR_DST_TOO_SMALL; const ERROR_SRC_TOO_SMALL = native.ERROR_SRC_TOO_SMALL; const ERROR_BAD_MAGIC = native.ERROR_BAD_MAGIC; const ERROR_BAD_VERSION = native.ERROR_BAD_VERSION; const ERROR_BAD_HEADER = native.ERROR_BAD_HEADER; const ERROR_BAD_CHECKSUM = native.ERROR_BAD_CHECKSUM; const ERROR_CORRUPT_DATA = native.ERROR_CORRUPT_DATA; const ERROR_BAD_OFFSET = native.ERROR_BAD_OFFSET; const ERROR_OVERFLOW = native.ERROR_OVERFLOW; const ERROR_IO = native.ERROR_IO; const ERROR_NULL_INPUT = native.ERROR_NULL_INPUT; const ERROR_BAD_BLOCK_TYPE = native.ERROR_BAD_BLOCK_TYPE; const ERROR_BAD_BLOCK_SIZE = native.ERROR_BAD_BLOCK_SIZE; const ERROR_DICT_REQUIRED = native.ERROR_DICT_REQUIRED; const ERROR_DICT_MISMATCH = native.ERROR_DICT_MISMATCH; const ERROR_DICT_TOO_LARGE = native.ERROR_DICT_TOO_LARGE; const ERROR_BAD_LEVEL = native.ERROR_BAD_LEVEL; /** * Returns the minimum supported compression level (currently 1). * @returns {number} */ function minLevel() { return native.minLevel(); } /** * Returns the maximum supported compression level (currently 7). * @returns {number} */ function maxLevel() { return native.maxLevel(); } /** * Returns the default compression level (currently 3). * @returns {number} */ function defaultLevel() { return native.defaultLevel(); } /** * Returns the version string reported by the linked native libzxc * (e.g. "0.13.0"). Distinct from the npm package version. * @returns {string} */ function libraryVersion() { return native.libraryVersion(); } /** * Returns a human-readable name for a given error code. * * @param {number} code - ZXC error code. * @returns {string} Error name (e.g., "ZXC_ERROR_DST_TOO_SMALL"). */ function errorName(code) { if (typeof code !== 'number') { throw new TypeError('code must be a number'); } return native.errorName(code); } /** * Returns the maximum compressed size for a given input size. * Useful for pre-allocating output buffers. * * @param {number} inputSize - Size of the input data in bytes. * @returns {number} Maximum required buffer size in bytes. */ function compressBound(inputSize) { if (typeof inputSize !== 'number' || inputSize < 0) { throw new TypeError('inputSize must be a non-negative number'); } return native.compressBound(inputSize); } /** * Normalize an optional dictionary value into a Buffer (or undefined). * Accepts a Buffer or Uint8Array; rejects other truthy types. * @param {Buffer|Uint8Array|undefined|null} dict * @returns {Buffer|undefined} */ function toDictBuffer(dict) { if (dict === undefined || dict === null) return undefined; if (Buffer.isBuffer(dict)) return dict; if (dict instanceof Uint8Array) return Buffer.from(dict.buffer, dict.byteOffset, dict.byteLength); throw new TypeError('dict must be a Buffer or Uint8Array'); } /** * Train a pre-trained dictionary from a corpus of sample Buffers. * * @param {Array} samples - Non-empty array of sample buffers. * @param {number} [maxSize=65535] - Maximum dictionary content size in bytes. * @returns {Buffer} Raw dictionary content suitable for `options.dict`. */ function trainDict(samples, maxSize = native.DICT_SIZE_MAX) { if (!Array.isArray(samples) || samples.length === 0) { throw new TypeError('samples must be a non-empty array of Buffers'); } const bufs = samples.map((s) => { if (Buffer.isBuffer(s)) return s; if (s instanceof Uint8Array) return Buffer.from(s.buffer, s.byteOffset, s.byteLength); throw new TypeError('samples entries must be Buffers or Uint8Arrays'); }); return native.trainDict(bufs, maxSize); } /** * Compute the 32-bit dictionary ID for raw dictionary content. * @param {Buffer} content * @returns {number} */ function dictId(content) { if (!Buffer.isBuffer(content)) { throw new TypeError('content must be a Buffer'); } return native.dictId(content); } /** * Returns the dictionary ID referenced by a `.zxc` archive, or 0 if none. * @param {Buffer} archive * @returns {number} */ function getDictId(archive) { if (!Buffer.isBuffer(archive)) { throw new TypeError('archive must be a Buffer'); } return native.getDictId(archive); } /** * Returns the dictionary ID stored in a `.zxd` dictionary file, or 0 if invalid. * @param {Buffer} zxd * @returns {number} */ function dictGetId(zxd) { if (!Buffer.isBuffer(zxd)) { throw new TypeError('zxd must be a Buffer'); } return native.dictGetId(zxd); } /** * Serialize dictionary content and its shared literal Huffman table * (128 bytes, from {@link trainDictHuf}) into the `.zxd` file format. * The stored dictionary ID covers both content and table. * @param {Buffer} content * @param {Buffer} hufLengths - 128-byte packed code-lengths table. * @returns {Buffer} The encoded `.zxd` file. */ function dictSave(content, hufLengths) { if (!Buffer.isBuffer(content) || !Buffer.isBuffer(hufLengths)) { throw new TypeError('content and hufLengths must be Buffers'); } return native.dictSave(content, hufLengths); } /** * Train the shared literal Huffman table for an already-trained dictionary. * Compresses the samples with the dictionary and derives canonical code * lengths from the real post-LZ literal distribution. * @param {Buffer[]} samples - Training corpus (typically the same as {@link trainDict}). * @param {Buffer} dict - Trained dictionary content. * @returns {Buffer} 128-byte packed table for {@link dictSave} / `options.dictHuf`. */ function trainDictHuf(samples, dict) { if (!Array.isArray(samples)) { throw new TypeError('samples must be an array of Buffers'); } if (!Buffer.isBuffer(dict)) { throw new TypeError('dict must be a Buffer'); } const bufs = samples.map((s, i) => { if (Buffer.isBuffer(s)) return s; if (s instanceof Uint8Array) return Buffer.from(s.buffer, s.byteOffset, s.byteLength); throw new TypeError(`samples[${i}] must be a Buffer or Uint8Array`); }); return native.trainDictHuf(bufs, dict); } /** * Return the 128-byte shared Huffman table stored in a `.zxd` file, * or `null` if the buffer is not a valid `.zxd` file. * @param {Buffer} zxd * @returns {Buffer | null} */ function dictHuf(zxd) { if (!Buffer.isBuffer(zxd)) { throw new TypeError('zxd must be a Buffer'); } return native.dictHuf(zxd); } /** * A trained dictionary: LZ-window content plus its shared literal Huffman * table, bundled so callers never juggle the pair by hand. * * Create with {@link Dictionary.train} (from samples) or * {@link Dictionary.load} (from `.zxd` bytes), then pass the instance as * `options.dict` to {@link compress} / {@link decompress}, or to * `Seekable#setDict`. */ class Dictionary { /** * @param {Buffer} content - Raw LZ-window content bytes. * @param {Buffer} huf - 128-byte shared literal Huffman table. * @param {number} id - Dictionary ID binding the (content, table) pair. */ constructor(content, huf, id) { if (!Buffer.isBuffer(content) || !Buffer.isBuffer(huf) || huf.length !== 128) { throw new TypeError('Dictionary requires (content: Buffer, huf: 128-byte Buffer)'); } this.content = content; this.huf = huf; this.id = id >>> 0; } /** * Train a complete dictionary (content + shared table) from samples. * @param {Array} samples * @returns {Dictionary} */ static train(samples) { if (!Array.isArray(samples)) { throw new TypeError('samples must be an array of Buffers'); } const bufs = samples.map((s, i) => { if (Buffer.isBuffer(s)) return s; if (s instanceof Uint8Array) return Buffer.from(s.buffer, s.byteOffset, s.byteLength); throw new TypeError(`samples[${i}] must be a Buffer or Uint8Array`); }); return Dictionary.load(native.dictTrain(bufs)); } /** * Parse `.zxd` bytes into a Dictionary (owned copies). * @param {Buffer} zxd * @returns {Dictionary} */ static load(zxd) { if (!Buffer.isBuffer(zxd)) { throw new TypeError('zxd must be a Buffer'); } const { content, huf, id } = native.dictLoad(zxd); return new Dictionary(content, huf, id); } /** * Serialize back to `.zxd` file bytes. * @returns {Buffer} */ save() { return native.dictSave(this.content, this.huf); } } /** * Accept a Dictionary or raw (dict, dictHuf) pieces in options. * @returns {{dict: Buffer|undefined, dictHuf: Buffer|undefined}} */ function _splitDictOption(options) { if (options.dict instanceof Dictionary) { return { dict: options.dict.content, dictHuf: options.dict.huf }; } return { dict: toDictBuffer(options.dict), dictHuf: toDictBuffer(options.dictHuf) }; } /** * Load and validate a `.zxd` dictionary file. * @param {Buffer} zxd * @returns {{ content: Buffer, huf: Buffer, id: number }} */ function dictLoad(zxd) { if (!Buffer.isBuffer(zxd)) { throw new TypeError('zxd must be a Buffer'); } return native.dictLoad(zxd); } /** * Compress a Buffer using the ZXC algorithm. * * @param {Buffer} data - Buffer to compress. * @param {object} [options] - Compression options. * @param {number} [options.level=LEVEL_DEFAULT] - Compression level (1-7). * @param {boolean} [options.checksum=false] - Enable checksum verification. * @param {boolean} [options.seekable=false] - Enable seek table for random-access decompression. * @param {Buffer|Uint8Array} [options.dict] - Pre-trained dictionary content (raw bytes). * @returns {Buffer} Compressed data. */ function compress(data, options = {}) { if (!Buffer.isBuffer(data)) { throw new TypeError('data must be a Buffer'); } const level = options.level ?? LEVEL_DEFAULT; const checksum = options.checksum ?? false; const seekable = options.seekable ?? false; const { dict, dictHuf } = _splitDictOption(options); return native.compress(data, level, checksum, seekable, dict, dictHuf); } /** * Returns the original decompressed size from a ZXC compressed buffer. * Reads the footer without performing decompression. * * @param {Buffer} data - Compressed data buffer. * @returns {number} Original uncompressed size in bytes, or 0 if invalid. */ function getDecompressedSize(data) { if (!Buffer.isBuffer(data)) { throw new TypeError('data must be a Buffer'); } return native.getDecompressedSize(data); } /** * Decompress a ZXC compressed Buffer. * * @param {Buffer} data - Compressed data. * @param {object} [options] - Decompression options. * @param {number} [options.size] - Expected decompressed size. If omitted, read from header. * @param {boolean} [options.checksum=false] - Enable checksum verification. * @param {Buffer|Uint8Array} [options.dict] - Pre-trained dictionary content (raw bytes). * @returns {Buffer} Decompressed data. */ function decompress(data, options = {}) { if (!Buffer.isBuffer(data)) { throw new TypeError('data must be a Buffer'); } const checksum = options.checksum ?? false; const { dict, dictHuf } = _splitDictOption(options); let size = options.size; if (size === undefined) { size = getDecompressedSize(data); } return native.decompress(data, size, checksum, dict, dictHuf); } /** * Push-based, single-threaded compression stream. * * The Node.js counterpart of the C `zxc_cstream`. Use it when you cannot * block on a `FILE*` (event loops, web frameworks, network protocols). * * @example * const cs = new zxc.CStream({ level: zxc.LEVEL_DEFAULT, checksum: true }); * const chunks = []; * for await (const part of source) chunks.push(cs.compress(part)); * chunks.push(cs.end()); * cs.close(); * sink.write(Buffer.concat(chunks)); */ const CStream = native.CStream; /** * Push-based, single-threaded decompression stream. * * @example * const ds = new zxc.DStream({ checksum: true }); * for await (const part of compressed) sink.write(ds.decompress(part)); * if (!ds.finished()) throw new Error('truncated'); * ds.close(); */ const DStream = native.DStream; /** * Handle on a seekable ZXC archive, suitable for random-access * decompression of byte ranges. * * The constructor takes a compressed Buffer (must carry an embedded seek * table - produce one by passing `{ seekable: true }` to `compress`). The * Buffer is copied into the addon's heap and kept alive for the lifetime * of the handle; call `.close()` to release both the native handle and * the copy. * * @example * const s = new zxc.Seekable(compressedBuf); * try { * const slice = s.decompressRange(1024, 4096); * console.log(`first 16 bytes: ${slice.subarray(0, 16).toString('hex')}`); * } finally { * s.close(); * } */ const Seekable = native.Seekable; /** * Encoded byte size of a seek table covering `numBlocks` data blocks. * Use this to size a destination buffer for {@link writeSeekTable}. * @param {number} numBlocks * @returns {number} */ function seekTableSize(numBlocks) { if (typeof numBlocks !== 'number' || numBlocks < 0) { throw new TypeError('numBlocks must be a non-negative number'); } return native.seekTableSize(numBlocks); } /** * Low-level: write a seek table (header + entries) for the given * per-block on-disk compressed sizes. * * Most callers do not need this directly; the buffer and streaming APIs * emit a seek table when `seekable: true` is set. * * @param {number[]} compSizes - Per-block compressed sizes, in order. * @returns {Buffer} The encoded seek table. */ function writeSeekTable(compSizes) { if (!Array.isArray(compSizes) || compSizes.length === 0) { throw new TypeError('compSizes must be a non-empty array of numbers'); } return native.writeSeekTable(compSizes); } // ============================================================================= // stream.Transform adapters over the push streaming API // ============================================================================= /** * Returns true if `buf` starts with the ZXC file magic word. * * Useful for content-type sniffing in containers / object stores that need * to dispatch on media type (e.g. OCI). Cheap and side-effect free; does * not validate the rest of the header or the footer. * * Magic word identifying a ZXC file frame: little-endian 0x9CB02EF5. * * @param {Buffer|Uint8Array} buf * @returns {boolean} */ function detectZxc(buf) { if (!buf || buf.length < 4) return false; return buf[0] === 0xF5 && buf[1] === 0x2E && buf[2] === 0xB0 && buf[3] === 0x9C; } /** * A Node.js `stream.Transform` that compresses bytes through a ZXC frame. * * @example * const fs = require('node:fs'); * const { pipeline } = require('node:stream/promises'); * await pipeline( * fs.createReadStream('input.bin'), * zxc.createCompressStream({ level: zxc.LEVEL_DEFAULT }), * fs.createWriteStream('output.zxc'), * ); */ class CompressStream extends Transform { constructor(options = {}) { const { level, checksum, blockSize, ...transformOpts } = options; super(transformOpts); this._cs = new CStream({ ...(level === undefined ? {} : { level }), ...(checksum === undefined ? {} : { checksum }), ...(blockSize === undefined ? {} : { blockSize }), }); } _transform(chunk, encoding, callback) { try { const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, typeof encoding === 'string' ? encoding : 'utf8'); const out = this._cs.compress(buf); if (out.length > 0) this.push(out); callback(); } catch (err) { callback(err); } } _flush(callback) { try { const tail = this._cs.end(); if (tail.length > 0) this.push(tail); this._cs.close(); callback(); } catch (err) { callback(err); } } _destroy(err, callback) { this._cs.close(); callback(err); } } /** * A Node.js `stream.Transform` that decompresses a ZXC frame. * * Emits `'error'` with code `'ZXC_TRUNCATED'` if the input ends before the * footer is reached. * * @example * const fs = require('node:fs'); * const { pipeline } = require('node:stream/promises'); * await pipeline( * fs.createReadStream('input.zxc'), * zxc.createDecompressStream(), * fs.createWriteStream('output.bin'), * ); */ class DecompressStream extends Transform { constructor(options = {}) { const { checksum, ...transformOpts } = options; super(transformOpts); this._ds = new DStream({ ...(checksum === undefined ? {} : { checksum }), }); } _transform(chunk, encoding, callback) { try { const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, typeof encoding === 'string' ? encoding : 'utf8'); const out = this._ds.decompress(buf); if (out.length > 0) this.push(out); callback(); } catch (err) { callback(err); } } _flush(callback) { try { if (!this._ds.finished()) { const err = new Error('zxc: input drained before footer (truncated frame)'); err.code = 'ZXC_TRUNCATED'; this._ds.close(); callback(err); return; } this._ds.close(); callback(); } catch (err) { callback(err); } } _destroy(err, callback) { this._ds.close(); callback(err); } } /** * Factory matching the `zlib.createGzip()` convention. * @param {object} [options] * @returns {CompressStream} */ function createCompressStream(options) { return new CompressStream(options); } /** * Factory matching the `zlib.createGunzip()` convention. * @param {object} [options] * @returns {DecompressStream} */ function createDecompressStream(options) { return new DecompressStream(options); } module.exports = { // Functions compress, decompress, compressBound, getDecompressedSize, // Dictionary API trainDict, dictId, getDictId, dictGetId, dictSave, dictLoad, trainDictHuf, dictHuf, Dictionary, // Push streaming classes CStream, DStream, // Seekable random-access decompression Seekable, seekTableSize, writeSeekTable, // stream.Transform adapters CompressStream, DecompressStream, createCompressStream, createDecompressStream, detectZxc, // Library info helpers minLevel, maxLevel, defaultLevel, libraryVersion, // Constants LEVEL_FASTEST, LEVEL_FAST, LEVEL_DEFAULT, LEVEL_BALANCED, LEVEL_COMPACT, LEVEL_DENSITY, LEVEL_ULTRA, // Error handling errorName, ERROR_MEMORY, ERROR_DST_TOO_SMALL, ERROR_SRC_TOO_SMALL, ERROR_BAD_MAGIC, ERROR_BAD_VERSION, ERROR_BAD_HEADER, ERROR_BAD_CHECKSUM, ERROR_CORRUPT_DATA, ERROR_BAD_OFFSET, ERROR_OVERFLOW, ERROR_IO, ERROR_NULL_INPUT, ERROR_BAD_BLOCK_TYPE, ERROR_BAD_BLOCK_SIZE, ERROR_DICT_REQUIRED, ERROR_DICT_MISMATCH, ERROR_DICT_TOO_LARGE, ERROR_BAD_LEVEL, }; zxc-0.13.0/wrappers/nodejs/package-lock.json000066400000000000000000001610501522536370400207520ustar00rootroot00000000000000{ "name": "zxc-compress", "version": "0.13.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "zxc-compress", "version": "0.13.0", "hasInstallScript": true, "license": "BSD-3-Clause", "dependencies": { "cmake-js": "^8.0.0", "node-addon-api": "^8.5.0" }, "devDependencies": { "vitest": "^4.0.0" }, "engines": { "node": ">=16.0.0" } }, "node_modules/@emnapi/core": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", "dev": true, "license": "MIT", "optional": true, "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", "dev": true, "license": "MIT", "optional": true, "dependencies": { "tslib": "^2.4.0" } }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", "dev": true, "license": "MIT", "optional": true, "dependencies": { "tslib": "^2.4.0" } }, "node_modules/@isaacs/fs-minipass": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", "license": "ISC", "dependencies": { "minipass": "^7.0.4" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true, "license": "MIT" }, "node_modules/@napi-rs/wasm-runtime": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", "dev": true, "license": "MIT", "optional": true, "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "node_modules/@oxc-project/types": { "version": "0.137.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz", "integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==", "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" } }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.3.tgz", "integrity": "sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "android" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-darwin-arm64": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.3.tgz", "integrity": "sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-darwin-x64": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.3.tgz", "integrity": "sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-freebsd-x64": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.3.tgz", "integrity": "sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "freebsd" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.3.tgz", "integrity": "sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==", "cpu": [ "arm" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.3.tgz", "integrity": "sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==", "cpu": [ "arm64" ], "dev": true, "libc": [ "glibc" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-arm64-musl": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.3.tgz", "integrity": "sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==", "cpu": [ "arm64" ], "dev": true, "libc": [ "musl" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.3.tgz", "integrity": "sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==", "cpu": [ "ppc64" ], "dev": true, "libc": [ "glibc" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.3.tgz", "integrity": "sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==", "cpu": [ "s390x" ], "dev": true, "libc": [ "glibc" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-x64-gnu": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.3.tgz", "integrity": "sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==", "cpu": [ "x64" ], "dev": true, "libc": [ "glibc" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-x64-musl": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.3.tgz", "integrity": "sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==", "cpu": [ "x64" ], "dev": true, "libc": [ "musl" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-openharmony-arm64": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.3.tgz", "integrity": "sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "openharmony" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-wasm32-wasi": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.3.tgz", "integrity": "sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==", "cpu": [ "wasm32" ], "dev": true, "license": "MIT", "optional": true, "dependencies": { "@emnapi/core": "1.11.1", "@emnapi/runtime": "1.11.1", "@napi-rs/wasm-runtime": "^1.1.6" }, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.3.tgz", "integrity": "sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-win32-x64-msvc": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.3.tgz", "integrity": "sha512-OL4OMk7UPXOeVGGd3qo5zJyPIljf4AFgk5QAkPPS+OoLuOOozhuaQGC18MxVTnw/06q93gShAJzlwnSCY9YtqA==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/pluginutils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", "dev": true, "license": "MIT" }, "node_modules/@standard-schema/spec": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "dev": true, "license": "MIT" }, "node_modules/@tybys/wasm-util": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", "dev": true, "license": "MIT", "optional": true, "dependencies": { "tslib": "^2.4.0" } }, "node_modules/@types/chai": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", "dev": true, "license": "MIT", "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "node_modules/@types/deep-eql": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", "dev": true, "license": "MIT" }, "node_modules/@types/estree": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "dev": true, "license": "MIT" }, "node_modules/@vitest/expect": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz", "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.9", "@vitest/utils": "4.1.9", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/mocker": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz", "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==", "dev": true, "license": "MIT", "dependencies": { "@vitest/spy": "4.1.9", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "msw": { "optional": true }, "vite": { "optional": true } } }, "node_modules/@vitest/pretty-format": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz", "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==", "dev": true, "license": "MIT", "dependencies": { "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz", "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==", "dev": true, "license": "MIT", "dependencies": { "@vitest/utils": "4.1.9", "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz", "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==", "dev": true, "license": "MIT", "dependencies": { "@vitest/pretty-format": "4.1.9", "@vitest/utils": "4.1.9", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/spy": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz", "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==", "dev": true, "license": "MIT", "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/utils": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz", "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==", "dev": true, "license": "MIT", "dependencies": { "@vitest/pretty-format": "4.1.9", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, "license": "MIT", "engines": { "node": ">=12" } }, "node_modules/chai": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", "dev": true, "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/chownr": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", "license": "BlueOak-1.0.0", "engines": { "node": ">=18" } }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" }, "engines": { "node": ">=12" } }, "node_modules/cmake-js": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/cmake-js/-/cmake-js-8.0.0.tgz", "integrity": "sha512-YbUP88RDwCvoQkZhRtGURYm9RIpWdtvZuhT87fKNoLjk8kIFIFeARpKfuZQGdwfH99GZpUmqSfcDrK62X7lTgg==", "license": "MIT", "dependencies": { "debug": "^4.4.3", "fs-extra": "^11.3.3", "node-api-headers": "^1.8.0", "rc": "1.2.8", "semver": "^7.7.3", "tar": "^7.5.6", "url-join": "^4.0.1", "which": "^6.0.0", "yargs": "^17.7.2" }, "bin": { "cmake-js": "bin/cmake-js" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, "engines": { "node": ">=7.0.0" } }, "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, "license": "MIT" }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" }, "engines": { "node": ">=6.0" }, "peerDependenciesMeta": { "supports-color": { "optional": true } } }, "node_modules/deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "license": "MIT", "engines": { "node": ">=4.0.0" } }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true, "license": "Apache-2.0", "engines": { "node": ">=8" } }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT" }, "node_modules/es-module-lexer": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", "dev": true, "license": "MIT" }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, "license": "MIT", "dependencies": { "@types/estree": "^1.0.0" } }, "node_modules/expect-type": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", "dev": true, "license": "Apache-2.0", "engines": { "node": ">=12.0.0" } }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" }, "peerDependencies": { "picomatch": "^3 || ^4" }, "peerDependenciesMeta": { "picomatch": { "optional": true } } }, "node_modules/fs-extra": { "version": "11.3.4", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" }, "engines": { "node": ">=14.14" } }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, "node_modules/ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "license": "ISC" }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/isexe": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", "license": "BlueOak-1.0.0", "engines": { "node": ">=20" } }, "node_modules/jsonfile": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "node_modules/lightningcss": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", "dev": true, "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" }, "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "node_modules/lightningcss-android-arm64": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", "cpu": [ "arm64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "android" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-darwin-arm64": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", "cpu": [ "arm64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "darwin" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-darwin-x64": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", "cpu": [ "x64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "darwin" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-freebsd-x64": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", "cpu": [ "x64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "freebsd" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-linux-arm-gnueabihf": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", "cpu": [ "arm" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-linux-arm64-gnu": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", "cpu": [ "arm64" ], "dev": true, "libc": [ "glibc" ], "license": "MPL-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-linux-arm64-musl": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", "cpu": [ "arm64" ], "dev": true, "libc": [ "musl" ], "license": "MPL-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-linux-x64-gnu": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", "cpu": [ "x64" ], "dev": true, "libc": [ "glibc" ], "license": "MPL-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-linux-x64-musl": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", "cpu": [ "x64" ], "dev": true, "libc": [ "musl" ], "license": "MPL-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-win32-arm64-msvc": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", "cpu": [ "arm64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "win32" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-win32-x64-msvc": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", "cpu": [ "x64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "win32" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/minipass": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" } }, "node_modules/minizlib": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", "license": "MIT", "dependencies": { "minipass": "^7.1.2" }, "engines": { "node": ">= 18" } }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, "node_modules/nanoid": { "version": "3.3.15", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, "engines": { "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, "node_modules/node-addon-api": { "version": "8.9.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.9.0.tgz", "integrity": "sha512-ekZMeaaIzSQTSpr7X2X3iJM7lTzgnx8ahAG9pJfT/7+14mlEM8ZYQ9cgCDvSSRbReFK0oHli3WrZdCiRsgAT9Q==", "license": "MIT", "engines": { "node": "^18 || ^20 || >= 21" } }, "node_modules/node-api-headers": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/node-api-headers/-/node-api-headers-1.8.0.tgz", "integrity": "sha512-jfnmiKWjRAGbdD1yQS28bknFM1tbHC1oucyuMPjmkEs+kpiu76aRs40WlTmBmyEgzDM76ge1DQ7XJ3R5deiVjQ==", "license": "MIT" }, "node_modules/obug": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", "dev": true, "funding": [ "https://github.com/sponsors/sxzz", "https://opencollective.com/debug" ], "license": "MIT" }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true, "license": "MIT" }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, "license": "ISC" }, "node_modules/picomatch": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/postcss": { "version": "8.5.16", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz", "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==", "dev": true, "funding": [ { "type": "opencollective", "url": "https://opencollective.com/postcss/" }, { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/postcss" }, { "type": "github", "url": "https://github.com/sponsors/ai" } ], "license": "MIT", "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" } }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "cli.js" } }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/rolldown": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.3.tgz", "integrity": "sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==", "dev": true, "license": "MIT", "dependencies": { "@oxc-project/types": "=0.137.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { "rolldown": "bin/cli.mjs" }, "engines": { "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.1.3", "@rolldown/binding-darwin-arm64": "1.1.3", "@rolldown/binding-darwin-x64": "1.1.3", "@rolldown/binding-freebsd-x64": "1.1.3", "@rolldown/binding-linux-arm-gnueabihf": "1.1.3", "@rolldown/binding-linux-arm64-gnu": "1.1.3", "@rolldown/binding-linux-arm64-musl": "1.1.3", "@rolldown/binding-linux-ppc64-gnu": "1.1.3", "@rolldown/binding-linux-s390x-gnu": "1.1.3", "@rolldown/binding-linux-x64-gnu": "1.1.3", "@rolldown/binding-linux-x64-musl": "1.1.3", "@rolldown/binding-openharmony-arm64": "1.1.3", "@rolldown/binding-wasm32-wasi": "1.1.3", "@rolldown/binding-win32-arm64-msvc": "1.1.3", "@rolldown/binding-win32-x64-msvc": "1.1.3" } }, "node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "license": "ISC", "bin": { "semver": "bin/semver.js" }, "engines": { "node": ">=10" } }, "node_modules/siginfo": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", "dev": true, "license": "ISC" }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true, "license": "MIT" }, "node_modules/std-env": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", "dev": true, "license": "MIT" }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/tar": { "version": "7.5.16", "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.16.tgz", "integrity": "sha512-56adEpPMouktRlBLXiaYFFzZ/3+JXa8P9n7WbR+ibIjtviN55mEaOkiysCnPnWm+7kkui1Dn8J9l+g6zV8731w==", "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" }, "engines": { "node": ">=18" } }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "dev": true, "license": "MIT" }, "node_modules/tinyexec": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz", "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==", "dev": true, "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/tinyglobby": { "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" }, "engines": { "node": ">=12.0.0" }, "funding": { "url": "https://github.com/sponsors/SuperchupuDev" } }, "node_modules/tinyrainbow": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", "dev": true, "license": "MIT", "engines": { "node": ">=14.0.0" } }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, "license": "0BSD", "optional": true }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "license": "MIT", "engines": { "node": ">= 10.0.0" } }, "node_modules/url-join": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", "license": "MIT" }, "node_modules/vite": { "version": "8.1.2", "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.2.tgz", "integrity": "sha512-6YYPbRXTxx6bRXmOn7XdnQAy5DQNHhDgtjhDHI13oe4pY93kkcdGJWxpGwOm++/Wh0QpQhDrpIoVMrmrsI5AGQ==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.16", "rolldown": "~1.1.3", "tinyglobby": "^0.2.17" }, "bin": { "vite": "bin/vite.js" }, "engines": { "node": "^20.19.0 || >=22.12.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.3.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, "@vitejs/devtools": { "optional": true }, "esbuild": { "optional": true }, "jiti": { "optional": true }, "less": { "optional": true }, "sass": { "optional": true }, "sass-embedded": { "optional": true }, "stylus": { "optional": true }, "sugarss": { "optional": true }, "terser": { "optional": true }, "tsx": { "optional": true }, "yaml": { "optional": true } } }, "node_modules/vitest": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz", "integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==", "dev": true, "license": "MIT", "dependencies": { "@vitest/expect": "4.1.9", "@vitest/mocker": "4.1.9", "@vitest/pretty-format": "4.1.9", "@vitest/runner": "4.1.9", "@vitest/snapshot": "4.1.9", "@vitest/spy": "4.1.9", "@vitest/utils": "4.1.9", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.1.0", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "bin": { "vitest": "vitest.mjs" }, "engines": { "node": "^20.0.0 || ^22.0.0 || >=24.0.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.1.9", "@vitest/browser-preview": "4.1.9", "@vitest/browser-webdriverio": "4.1.9", "@vitest/coverage-istanbul": "4.1.9", "@vitest/coverage-v8": "4.1.9", "@vitest/ui": "4.1.9", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "@edge-runtime/vm": { "optional": true }, "@opentelemetry/api": { "optional": true }, "@types/node": { "optional": true }, "@vitest/browser-playwright": { "optional": true }, "@vitest/browser-preview": { "optional": true }, "@vitest/browser-webdriverio": { "optional": true }, "@vitest/coverage-istanbul": { "optional": true }, "@vitest/coverage-v8": { "optional": true }, "@vitest/ui": { "optional": true }, "happy-dom": { "optional": true }, "jsdom": { "optional": true }, "vite": { "optional": false } } }, "node_modules/which": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", "license": "ISC", "dependencies": { "isexe": "^4.0.0" }, "bin": { "node-which": "bin/which.js" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/why-is-node-running": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, "license": "MIT", "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" }, "engines": { "node": ">=8" } }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/yallist": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", "license": "BlueOak-1.0.0", "engines": { "node": ">=18" } }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "license": "MIT", "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" }, "engines": { "node": ">=12" } }, "node_modules/yargs-parser": { "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "license": "ISC", "engines": { "node": ">=12" } } } } zxc-0.13.0/wrappers/nodejs/package.json000066400000000000000000000020211522536370400200140ustar00rootroot00000000000000{ "name": "zxc-compress", "version": "0.13.0", "description": "ZXC: High-performance asymmetric lossless compression built for ultra-fast decode - Node.js native addon", "main": "lib/index.js", "types": "lib/index.d.ts", "files": [ "lib/", "src/", "CMakeLists.txt" ], "scripts": { "install": "cmake-js compile", "build": "cmake-js compile", "rebuild": "cmake-js rebuild", "test": "vitest run" }, "keywords": [ "compression", "decompression", "lossless", "zxc", "performance", "lz77" ], "author": "Bertrand Lebonnois", "license": "BSD-3-Clause", "repository": { "type": "git", "url": "https://github.com/hellobertrand/zxc.git", "directory": "wrappers/nodejs" }, "homepage": "https://github.com/hellobertrand/zxc", "engines": { "node": ">=16.0.0" }, "dependencies": { "node-addon-api": "^8.5.0", "cmake-js": "^8.0.0" }, "devDependencies": { "vitest": "^4.0.0" }, "binary": { "napi_versions": [ 8 ] } }zxc-0.13.0/wrappers/nodejs/src/000077500000000000000000000000001522536370400163225ustar00rootroot00000000000000zxc-0.13.0/wrappers/nodejs/src/zxc_addon.cc000066400000000000000000001354501522536370400206120ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include extern "C" { #include "zxc.h" #include "zxc_seekable.h" } // ============================================================================= // compressBound(inputSize: number): number // ============================================================================= static Napi::Value CompressBound(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); if (info.Length() < 1 || !info[0].IsNumber()) { Napi::TypeError::New(env, "Expected a number (inputSize)").ThrowAsJavaScriptException(); return env.Undefined(); } uint64_t input_size = info[0].As().Int64Value(); uint64_t bound = zxc_compress_bound(static_cast(input_size)); return Napi::Number::New(env, static_cast(bound)); } // ============================================================================= // compress(buffer: Buffer, level?: number, checksum?: boolean, seekable?: boolean, // dict?: Buffer): Buffer // ============================================================================= static Napi::Value Compress(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); if (info.Length() < 1 || !info[0].IsBuffer()) { Napi::TypeError::New(env, "Expected a Buffer as first argument") .ThrowAsJavaScriptException(); return env.Undefined(); } Napi::Buffer src_buf = info[0].As>(); size_t src_size = src_buf.Length(); static const uint8_t kEmptySrc = 0; const void* src = src_size > 0 ? static_cast(src_buf.Data()) : &kEmptySrc; int level = ZXC_LEVEL_DEFAULT; if (info.Length() >= 2 && info[1].IsNumber()) { level = info[1].As().Int32Value(); } int checksum = 0; if (info.Length() >= 3 && info[2].IsBoolean()) { checksum = info[2].As().Value() ? 1 : 0; } int seekable = 0; if (info.Length() >= 4 && info[3].IsBoolean()) { seekable = info[3].As().Value() ? 1 : 0; } const void* dict = nullptr; size_t dict_size = 0; if (info.Length() >= 5 && info[4].IsBuffer()) { Napi::Buffer dict_buf = info[4].As>(); if (dict_buf.Length() > 0) { dict = dict_buf.Data(); dict_size = dict_buf.Length(); } } const void* dict_huf = nullptr; if (info.Length() >= 6 && info[5].IsBuffer()) { Napi::Buffer huf_buf = info[5].As>(); if (huf_buf.Length() != ZXC_HUF_TABLE_SIZE) { Napi::TypeError::New(env, "dictHuf must be exactly 128 bytes") .ThrowAsJavaScriptException(); return env.Undefined(); } dict_huf = huf_buf.Data(); } uint64_t bound = zxc_compress_bound(src_size); // Uninitialised native scratch instead of a bound-size JS Buffer: the // bound is >= src_size, and a JS allocation of that size would be // registered as external GC memory only to be discarded after the // copy-slice below. std::unique_ptr dst(new (std::nothrow) uint8_t[static_cast(bound)]); if (!dst) { Napi::Error::New(env, "zxc: allocation failed for compression scratch") .ThrowAsJavaScriptException(); return env.Undefined(); } zxc_compress_opts_t opts = {0}; opts.level = level; opts.checksum_enabled = checksum; opts.seekable = seekable; opts.dict = dict; opts.dict_size = dict_size; opts.dict_huf = dict_huf; int64_t nwritten = zxc_compress(src, src_size, dst.get(), static_cast(bound), &opts); if (nwritten < 0) { auto err_code = static_cast(nwritten); Napi::Error err = Napi::Error::New(env, zxc_error_name(err_code)); err.Set("code", Napi::Number::New(env, err_code)); err.ThrowAsJavaScriptException(); return env.Undefined(); } // Return a slice of the buffer with the actual size return Napi::Buffer::Copy(env, dst.get(), static_cast(nwritten)); } // ============================================================================= // decompress(buffer: Buffer, decompressSize: number, checksum?: boolean, // dict?: Buffer): Buffer // ============================================================================= static Napi::Value Decompress(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); if (info.Length() < 2 || !info[0].IsBuffer() || !info[1].IsNumber()) { Napi::TypeError::New(env, "Expected (Buffer, number) as arguments") .ThrowAsJavaScriptException(); return env.Undefined(); } Napi::Buffer src_buf = info[0].As>(); const void* src = src_buf.Data(); size_t src_size = src_buf.Length(); size_t decompress_size = static_cast(info[1].As().Int64Value()); int checksum = 0; if (info.Length() >= 3 && info[2].IsBoolean()) { checksum = info[2].As().Value() ? 1 : 0; } const void* dict = nullptr; size_t dict_size = 0; if (info.Length() >= 4 && info[3].IsBuffer()) { Napi::Buffer dict_buf = info[3].As>(); if (dict_buf.Length() > 0) { dict = dict_buf.Data(); dict_size = dict_buf.Length(); } } const void* dict_huf = nullptr; if (info.Length() >= 5 && info[4].IsBuffer()) { Napi::Buffer huf_buf = info[4].As>(); if (huf_buf.Length() != ZXC_HUF_TABLE_SIZE) { Napi::TypeError::New(env, "dictHuf must be exactly 128 bytes") .ThrowAsJavaScriptException(); return env.Undefined(); } dict_huf = huf_buf.Data(); } Napi::Buffer dst_buf = Napi::Buffer::New(env, decompress_size); static uint8_t kEmptyDst = 0; void* dst = decompress_size > 0 ? static_cast(dst_buf.Data()) : static_cast(&kEmptyDst); zxc_decompress_opts_t dopts = {0}; dopts.checksum_enabled = checksum; dopts.dict = dict; dopts.dict_size = dict_size; dopts.dict_huf = dict_huf; int64_t nwritten = zxc_decompress(src, src_size, dst, decompress_size, &dopts); if (nwritten < 0) { auto err_code = static_cast(nwritten); Napi::Error err = Napi::Error::New(env, zxc_error_name(err_code)); err.Set("code", Napi::Number::New(env, err_code)); err.ThrowAsJavaScriptException(); return env.Undefined(); } // decompress_size is an upper bound from the caller: when fewer bytes // were written, slice to the actual size; napi buffers are allocated // uninitialized, so returning the full-length buffer would expose stale // heap contents past nwritten. if (static_cast(nwritten) == decompress_size) return dst_buf; return Napi::Buffer::Copy(env, dst_buf.Data(), static_cast(nwritten)); } // ============================================================================= // getDecompressedSize(buffer: Buffer): number // ============================================================================= static Napi::Value GetDecompressedSize(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); if (info.Length() < 1 || !info[0].IsBuffer()) { Napi::TypeError::New(env, "Expected a Buffer as first argument") .ThrowAsJavaScriptException(); return env.Undefined(); } Napi::Buffer src_buf = info[0].As>(); uint64_t size = zxc_get_decompressed_size(src_buf.Data(), src_buf.Length()); return Napi::Number::New(env, static_cast(size)); } // ============================================================================= // Dictionary API // ============================================================================= static Napi::Value ThrowZxcError(Napi::Env env, int code); // trainDict(samples: Buffer[], maxSize?: number): Buffer static Napi::Value TrainDict(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); if (info.Length() < 1 || !info[0].IsArray()) { Napi::TypeError::New(env, "Expected an array of Buffers (samples)") .ThrowAsJavaScriptException(); return env.Undefined(); } Napi::Array arr = info[0].As(); uint32_t n = arr.Length(); if (n == 0) { Napi::TypeError::New(env, "samples must be a non-empty array") .ThrowAsJavaScriptException(); return env.Undefined(); } std::vector samples(n); std::vector sizes(n); for (uint32_t i = 0; i < n; ++i) { Napi::Value v = arr.Get(i); if (!v.IsBuffer()) { Napi::TypeError::New(env, "samples entries must be Buffers") .ThrowAsJavaScriptException(); return env.Undefined(); } Napi::Buffer b = v.As>(); samples[i] = b.Data(); sizes[i] = b.Length(); } size_t capacity = ZXC_DICT_SIZE_MAX; if (info.Length() >= 2 && info[1].IsNumber()) { int64_t requested = info[1].As().Int64Value(); if (requested > 0 && static_cast(requested) < capacity) { capacity = static_cast(requested); } } std::vector dict_buf(capacity); int64_t r = zxc_train_dict(samples.data(), sizes.data(), n, dict_buf.data(), capacity); if (r < 0) { return ThrowZxcError(env, static_cast(r)); } return Napi::Buffer::Copy(env, dict_buf.data(), static_cast(r)); } // dictId(content: Buffer): number static Napi::Value DictId(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); if (info.Length() < 1 || !info[0].IsBuffer()) { Napi::TypeError::New(env, "Expected a Buffer (content)").ThrowAsJavaScriptException(); return env.Undefined(); } Napi::Buffer b = info[0].As>(); uint32_t id = zxc_dict_id(b.Data(), b.Length(), nullptr); return Napi::Number::New(env, static_cast(id)); } // getDictId(archive: Buffer): number (from .zxc) static Napi::Value GetDictId(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); if (info.Length() < 1 || !info[0].IsBuffer()) { Napi::TypeError::New(env, "Expected a Buffer (archive)").ThrowAsJavaScriptException(); return env.Undefined(); } Napi::Buffer b = info[0].As>(); uint32_t id = zxc_get_dict_id(b.Data(), b.Length()); return Napi::Number::New(env, static_cast(id)); } // dictGetId(zxd: Buffer): number (from .zxd) static Napi::Value DictGetId(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); if (info.Length() < 1 || !info[0].IsBuffer()) { Napi::TypeError::New(env, "Expected a Buffer (.zxd)").ThrowAsJavaScriptException(); return env.Undefined(); } Napi::Buffer b = info[0].As>(); uint32_t id = zxc_dict_get_id(b.Data(), b.Length()); return Napi::Number::New(env, static_cast(id)); } // dictSave(content: Buffer, hufLengths: Buffer): Buffer (.zxd) static Napi::Value DictSave(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); if (info.Length() < 2 || !info[0].IsBuffer() || !info[1].IsBuffer()) { Napi::TypeError::New(env, "Expected (content: Buffer, hufLengths: Buffer)") .ThrowAsJavaScriptException(); return env.Undefined(); } Napi::Buffer b = info[0].As>(); Napi::Buffer huf = info[1].As>(); if (huf.Length() != ZXC_HUF_TABLE_SIZE) { Napi::TypeError::New(env, "hufLengths must be exactly 128 bytes") .ThrowAsJavaScriptException(); return env.Undefined(); } size_t cap = zxc_dict_save_bound(b.Length()); std::vector out(cap); int64_t r = zxc_dict_save(b.Data(), b.Length(), huf.Data(), out.data(), cap); if (r < 0) { return ThrowZxcError(env, static_cast(r)); } return Napi::Buffer::Copy(env, out.data(), static_cast(r)); } // trainDictHuf(samples: Buffer[], dict: Buffer): Buffer (128 bytes) static Napi::Value TrainDictHuf(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); if (info.Length() < 2 || !info[0].IsArray() || !info[1].IsBuffer()) { Napi::TypeError::New(env, "Expected (samples: Buffer[], dict: Buffer)") .ThrowAsJavaScriptException(); return env.Undefined(); } Napi::Array arr = info[0].As(); uint32_t n = arr.Length(); if (n == 0) { Napi::TypeError::New(env, "samples must be a non-empty array") .ThrowAsJavaScriptException(); return env.Undefined(); } std::vector samples(n); std::vector sizes(n); for (uint32_t i = 0; i < n; ++i) { Napi::Value v = arr.Get(i); if (!v.IsBuffer()) { Napi::TypeError::New(env, "samples entries must be Buffers") .ThrowAsJavaScriptException(); return env.Undefined(); } Napi::Buffer b = v.As>(); samples[i] = b.Data(); sizes[i] = b.Length(); } Napi::Buffer dict = info[1].As>(); std::array huf; int r = zxc_train_dict_huf(samples.data(), sizes.data(), n, dict.Data(), dict.Length(), huf.data()); if (r < 0) { return ThrowZxcError(env, r); } return Napi::Buffer::Copy(env, huf.data(), huf.size()); } // dictHuf(zxd: Buffer): Buffer | null (128-byte shared Huffman table) static Napi::Value DictHuf(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); if (info.Length() < 1 || !info[0].IsBuffer()) { Napi::TypeError::New(env, "Expected a Buffer (.zxd)").ThrowAsJavaScriptException(); return env.Undefined(); } Napi::Buffer b = info[0].As>(); const void* huf = zxc_dict_huf(b.Data(), b.Length()); if (!huf) return env.Null(); return Napi::Buffer::Copy(env, static_cast(huf), ZXC_HUF_TABLE_SIZE); } // dictLoad(zxd: Buffer): { content: Buffer, huf: Buffer, id: number } static Napi::Value DictLoad(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); if (info.Length() < 1 || !info[0].IsBuffer()) { Napi::TypeError::New(env, "Expected a Buffer (.zxd)").ThrowAsJavaScriptException(); return env.Undefined(); } Napi::Buffer b = info[0].As>(); const void* content = nullptr; size_t content_size = 0; const void* huf = nullptr; uint32_t dict_id = 0; int r = zxc_dict_load(b.Data(), b.Length(), &content, &content_size, &huf, &dict_id); if (r < 0) { return ThrowZxcError(env, r); } // content/huf point INTO b's data (zero-copy); copy into new Buffers. Napi::Object result = Napi::Object::New(env); result.Set("content", Napi::Buffer::Copy( env, static_cast(content), content_size)); result.Set("huf", Napi::Buffer::Copy(env, static_cast(huf), ZXC_HUF_TABLE_SIZE)); result.Set("id", Napi::Number::New(env, static_cast(dict_id))); return result; } // dictTrain(samples: Buffer[]): Buffer (one-call .zxd creation) static Napi::Value DictTrain(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); if (info.Length() < 1 || !info[0].IsArray()) { Napi::TypeError::New(env, "Expected an array of Buffers (samples)") .ThrowAsJavaScriptException(); return env.Undefined(); } Napi::Array arr = info[0].As(); uint32_t n = arr.Length(); if (n == 0) { Napi::TypeError::New(env, "samples must be a non-empty array") .ThrowAsJavaScriptException(); return env.Undefined(); } std::vector samples(n); std::vector sizes(n); for (uint32_t i = 0; i < n; ++i) { Napi::Value v = arr.Get(i); if (!v.IsBuffer()) { Napi::TypeError::New(env, "samples entries must be Buffers") .ThrowAsJavaScriptException(); return env.Undefined(); } Napi::Buffer b = v.As>(); samples[i] = b.Data(); sizes[i] = b.Length(); } const size_t cap = zxc_dict_save_bound(ZXC_DICT_SIZE_MAX); std::vector zxd(cap); int64_t r = zxc_dict_train(samples.data(), sizes.data(), n, zxd.data(), cap); if (r <= 0) { return ThrowZxcError(env, static_cast(r)); } return Napi::Buffer::Copy(env, zxd.data(), static_cast(r)); } // ============================================================================= // minLevel(): number // maxLevel(): number // defaultLevel(): number // libraryVersion(): string // ============================================================================= static Napi::Value MinLevel(const Napi::CallbackInfo& info) { return Napi::Number::New(info.Env(), zxc_min_level()); } static Napi::Value MaxLevel(const Napi::CallbackInfo& info) { return Napi::Number::New(info.Env(), zxc_max_level()); } static Napi::Value DefaultLevel(const Napi::CallbackInfo& info) { return Napi::Number::New(info.Env(), zxc_default_level()); } static Napi::Value LibraryVersion(const Napi::CallbackInfo& info) { const char* v = zxc_version_string(); return Napi::String::New(info.Env(), v ? v : ""); } // ============================================================================= // Push Streaming API (single-threaded, caller-driven) // ============================================================================= // // The C contract drives input/output via mutable structs and reentrant // calls. We expose this to JS as two classes (CStream / DStream) whose // methods take a Buffer in and return a Buffer out, hiding the loop. A // thin Buffer over std::vector growing on demand is used to // accumulate the output across multiple drain rounds. static Napi::Value ThrowZxcError(Napi::Env env, int code) { Napi::Error err = Napi::Error::New(env, zxc_error_name(code)); err.Set("code", Napi::Number::New(env, code)); err.ThrowAsJavaScriptException(); return env.Undefined(); } /* Grow `out` so at least `out_len + want` bytes are addressable. Doubles when * possible, caps at vector::max_size(), and throws if the request can't fit. */ static bool GrowOutput(Napi::Env env, std::vector& out, size_t out_len, size_t want) { const size_t cap = out.max_size(); if (want > cap - out_len) { Napi::Error::New(env, "output buffer size overflow") .ThrowAsJavaScriptException(); return false; } const size_t needed = out_len + want; size_t new_size = (out.size() > cap / 2) ? cap : out.size() * 2; if (new_size < needed) new_size = needed; out.resize(new_size); return true; } class CStreamWrap : public Napi::ObjectWrap { public: static Napi::Function GetClass(Napi::Env env) { return DefineClass(env, "CStream", { InstanceMethod("compress", &CStreamWrap::Compress), InstanceMethod("end", &CStreamWrap::End), InstanceMethod("close", &CStreamWrap::Close), InstanceMethod("inSize", &CStreamWrap::InSize), InstanceMethod("outSize", &CStreamWrap::OutSize), }); } explicit CStreamWrap(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { Napi::Env env = info.Env(); zxc_compress_opts_t opts = {0}; opts.level = ZXC_LEVEL_DEFAULT; if (info.Length() >= 1 && info[0].IsObject()) { Napi::Object o = info[0].As(); if (o.Has("level") && o.Get("level").IsNumber()) { opts.level = o.Get("level").As().Int32Value(); } if (o.Has("checksum") && o.Get("checksum").IsBoolean()) { opts.checksum_enabled = o.Get("checksum").As().Value() ? 1 : 0; } if (o.Has("blockSize") && o.Get("blockSize").IsNumber()) { opts.block_size = static_cast(o.Get("blockSize").As().Int64Value()); } } cs_ = zxc_cstream_create(&opts); if (!cs_) { Napi::Error::New(env, "zxc_cstream_create failed").ThrowAsJavaScriptException(); } } ~CStreamWrap() { if (cs_) zxc_cstream_free(cs_); } private: zxc_cstream* cs_ = nullptr; // Reusable staging buffer: sized on first use and kept across calls, so // the malloc + value-initialising memset of a full block (~512 KB) is // paid once per stream instead of once per streamed chunk. std::vector stage_; bool requireOpen(Napi::Env env) { if (!cs_) { Napi::Error::New(env, "CStream is closed").ThrowAsJavaScriptException(); return false; } return true; } /* Drains from `cs` until either a stop condition is reached. The * caller-supplied `step` decides whether one iteration of * zxc_cstream_compress / _end has fully drained. */ Napi::Value DrainCompress(Napi::Env env, const uint8_t* src, size_t srcLen) { size_t cap = zxc_cstream_out_size(cs_); if (cap < 4096) cap = 4096; if (stage_.size() < cap) stage_.resize(cap); size_t out_len = 0; zxc_inbuf_t in = {src, srcLen, 0}; for (;;) { size_t want = zxc_cstream_out_size(cs_); if (want < 4096) want = 4096; if (stage_.size() - out_len < want) { if (!GrowOutput(env, stage_, out_len, want)) return env.Undefined(); } zxc_outbuf_t obuf = {stage_.data() + out_len, stage_.size() - out_len, 0}; int64_t r = zxc_cstream_compress(cs_, &obuf, &in); out_len += obuf.pos; if (r < 0) return ThrowZxcError(env, static_cast(r)); if (r == 0 && in.pos == in.size) break; } return Napi::Buffer::Copy(env, stage_.data(), out_len); } Napi::Value DrainEnd(Napi::Env env) { size_t cap = zxc_cstream_out_size(cs_); if (cap < 4096) cap = 4096; if (stage_.size() < cap) stage_.resize(cap); size_t out_len = 0; for (;;) { if (stage_.size() - out_len < 4096) { if (!GrowOutput(env, stage_, out_len, 4096)) return env.Undefined(); } zxc_outbuf_t obuf = {stage_.data() + out_len, stage_.size() - out_len, 0}; int64_t r = zxc_cstream_end(cs_, &obuf); out_len += obuf.pos; if (r < 0) return ThrowZxcError(env, static_cast(r)); if (r == 0) break; } return Napi::Buffer::Copy(env, stage_.data(), out_len); } Napi::Value Compress(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); if (!requireOpen(env)) return env.Undefined(); if (info.Length() < 1 || !info[0].IsBuffer()) { Napi::TypeError::New(env, "Expected a Buffer").ThrowAsJavaScriptException(); return env.Undefined(); } Napi::Buffer buf = info[0].As>(); return DrainCompress(env, buf.Data(), buf.Length()); } Napi::Value End(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); if (!requireOpen(env)) return env.Undefined(); return DrainEnd(env); } Napi::Value Close(const Napi::CallbackInfo& info) { if (cs_) { zxc_cstream_free(cs_); cs_ = nullptr; } // Release the staging memory too; the JS wrapper object may outlive // the stream by a long time. std::vector().swap(stage_); return info.Env().Undefined(); } Napi::Value InSize(const Napi::CallbackInfo& info) { return Napi::Number::New(info.Env(), static_cast(cs_ ? zxc_cstream_in_size(cs_) : 0)); } Napi::Value OutSize(const Napi::CallbackInfo& info) { return Napi::Number::New(info.Env(), static_cast(cs_ ? zxc_cstream_out_size(cs_) : 0)); } }; class DStreamWrap : public Napi::ObjectWrap { public: static Napi::Function GetClass(Napi::Env env) { return DefineClass(env, "DStream", { InstanceMethod("decompress", &DStreamWrap::Decompress), InstanceMethod("close", &DStreamWrap::Close), InstanceMethod("finished", &DStreamWrap::Finished), InstanceMethod("inSize", &DStreamWrap::InSize), InstanceMethod("outSize", &DStreamWrap::OutSize), }); } explicit DStreamWrap(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { Napi::Env env = info.Env(); zxc_decompress_opts_t opts = {0}; if (info.Length() >= 1 && info[0].IsObject()) { Napi::Object o = info[0].As(); if (o.Has("checksum") && o.Get("checksum").IsBoolean()) { opts.checksum_enabled = o.Get("checksum").As().Value() ? 1 : 0; } } ds_ = zxc_dstream_create(&opts); if (!ds_) { Napi::Error::New(env, "zxc_dstream_create failed").ThrowAsJavaScriptException(); } } ~DStreamWrap() { if (ds_) zxc_dstream_free(ds_); } private: zxc_dstream* ds_ = nullptr; // Reusable staging buffer; see CStreamWrap::stage_. std::vector stage_; bool requireOpen(Napi::Env env) { if (!ds_) { Napi::Error::New(env, "DStream is closed").ThrowAsJavaScriptException(); return false; } return true; } Napi::Value Decompress(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); if (!requireOpen(env)) return env.Undefined(); if (info.Length() < 1 || !info[0].IsBuffer()) { Napi::TypeError::New(env, "Expected a Buffer").ThrowAsJavaScriptException(); return env.Undefined(); } Napi::Buffer buf = info[0].As>(); size_t cap = zxc_dstream_out_size(ds_); if (cap < 4096) cap = 4096; if (stage_.size() < cap) stage_.resize(cap); size_t out_len = 0; zxc_inbuf_t in = {buf.Data(), buf.Length(), 0}; for (;;) { size_t want = zxc_dstream_out_size(ds_); if (want < 4096) want = 4096; if (stage_.size() - out_len < want) { if (!GrowOutput(env, stage_, out_len, want)) return env.Undefined(); } zxc_outbuf_t obuf = {stage_.data() + out_len, stage_.size() - out_len, 0}; zxc_inbuf_t empty_in = {nullptr, 0, 0}; zxc_inbuf_t* cur_in = (in.pos < in.size) ? &in : &empty_in; const size_t before_in = cur_in->pos; const size_t before_out = obuf.pos; const int64_t r = zxc_dstream_decompress(ds_, &obuf, cur_in); out_len += obuf.pos; if (r < 0) return ThrowZxcError(env, static_cast(r)); /* Keep draining even after input is exhausted; stop only when * no progress was made (no input consumed AND no output produced). */ if (cur_in->pos == before_in && obuf.pos == before_out) break; } return Napi::Buffer::Copy(env, stage_.data(), out_len); } Napi::Value Finished(const Napi::CallbackInfo& info) { return Napi::Boolean::New(info.Env(), ds_ ? zxc_dstream_finished(ds_) != 0 : false); } Napi::Value Close(const Napi::CallbackInfo& info) { if (ds_) { zxc_dstream_free(ds_); ds_ = nullptr; } std::vector().swap(stage_); return info.Env().Undefined(); } Napi::Value InSize(const Napi::CallbackInfo& info) { return Napi::Number::New(info.Env(), static_cast(ds_ ? zxc_dstream_in_size(ds_) : 0)); } Napi::Value OutSize(const Napi::CallbackInfo& info) { return Napi::Number::New(info.Env(), static_cast(ds_ ? zxc_dstream_out_size(ds_) : 0)); } }; // ============================================================================= // errorName(code: number): string // ============================================================================= static Napi::Value ErrorName(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); if (info.Length() < 1 || !info[0].IsNumber()) { Napi::TypeError::New(env, "Expected a number (error code)").ThrowAsJavaScriptException(); return env.Undefined(); } int code = info[0].As().Int32Value(); const char* name = zxc_error_name(code); return Napi::String::New(env, name); } // ============================================================================= // Seekable API: random-access decompression (single-threaded) // ============================================================================= class SeekableWrap : public Napi::ObjectWrap { public: static Napi::Function GetClass(Napi::Env env) { return DefineClass( env, "Seekable", { InstanceMethod("numBlocks", &SeekableWrap::NumBlocks), InstanceMethod("decompressedSize", &SeekableWrap::DecompressedSize), InstanceMethod("blockCompressedSize", &SeekableWrap::BlockCompressedSize), InstanceMethod("blockDecompressedSize", &SeekableWrap::BlockDecompressedSize), InstanceMethod("decompressRange", &SeekableWrap::DecompressRange), InstanceMethod("setDict", &SeekableWrap::SetDict), InstanceMethod("close", &SeekableWrap::Close), }); } explicit SeekableWrap(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { Napi::Env env = info.Env(); if (info.Length() < 1) { Napi::TypeError::New(env, "Expected a Buffer or { size, readAt } object") .ThrowAsJavaScriptException(); return; } // Reader-callback form: { size: number, readAt: (buf, offset) => void }. // Single-threaded only: the JS callback is invoked synchronously on // the calling thread. if (info[0].IsObject() && !info[0].IsBuffer()) { Napi::Object opts = info[0].As(); if (!opts.Has("size") || !opts.Has("readAt") || !opts.Get("size").IsNumber() || !opts.Get("readAt").IsFunction()) { Napi::TypeError::New(env, "Reader object must have { size: number, readAt: function }") .ThrowAsJavaScriptException(); return; } int64_t sz = opts.Get("size").As().Int64Value(); if (sz <= 0) { Napi::TypeError::New(env, "size must be > 0") .ThrowAsJavaScriptException(); return; } env_ = env; read_at_ref_ = Napi::Persistent(opts.Get("readAt").As()); zxc_reader_t r; r.read_at = &SeekableWrap::ReadAtTrampoline; r.ctx = this; r.size = static_cast(sz); s_ = zxc_seekable_open_reader(&r); if (!s_) { read_at_ref_.Reset(); Napi::Error::New(env, "zxc_seekable_open_reader failed (invalid archive?)") .ThrowAsJavaScriptException(); } return; } if (!info[0].IsBuffer()) { Napi::TypeError::New(env, "Expected a Buffer or { size, readAt } object") .ThrowAsJavaScriptException(); return; } Napi::Buffer src_buf = info[0].As>(); if (src_buf.Length() == 0) { Napi::TypeError::New(env, "Seekable buffer must be non-empty") .ThrowAsJavaScriptException(); return; } // Copy the source so the JS-side Buffer doesn't have to outlive us. src_.assign(src_buf.Data(), src_buf.Data() + src_buf.Length()); s_ = zxc_seekable_open(src_.data(), src_.size()); if (!s_) { Napi::Error::New(env, "zxc_seekable_open failed (invalid archive?)") .ThrowAsJavaScriptException(); } } ~SeekableWrap() { if (s_) zxc_seekable_free(s_); if (!read_at_ref_.IsEmpty()) read_at_ref_.Reset(); } private: zxc_seekable* s_ = nullptr; std::vector src_; // Reader-callback state. read_at_ref_.IsEmpty() == true when not in // reader mode. Napi::FunctionReference read_at_ref_; Napi::Env env_{nullptr}; // True while a native call that may re-enter JS (via ReadAtTrampoline) // is on the stack. Guards close()/decompressRange() against reentrant // calls from the readAt callback, which would free or corrupt the // handle the C library is still using. bool in_native_call_ = false; // C trampoline invoked by the library for every positional read against // a user-supplied JS `readAt`. Always called on the V8 main thread, // never use this binding with the multi-threaded decompress_range_mt // entry point. The whole body is guarded: any C++ exception (from the // JS callback, buffer allocation, or env teardown) is swallowed and // surfaced as ZXC_ERROR_IO so it never unwinds through the C frames. static int64_t ReadAtTrampoline(void* ctx, void* dst, size_t len, uint64_t offset) { auto* self = static_cast(ctx); if (!self || self->read_at_ref_.IsEmpty()) return ZXC_ERROR_IO; try { Napi::Env env = self->env_; Napi::HandleScope scope(env); // Allocate a fresh JS Buffer of `len` bytes for the callback to // fill, then memcpy back into `dst`. This avoids exposing the // C-side pointer to JS lifetime hazards. Napi::Buffer jsbuf = Napi::Buffer::New(env, len); self->read_at_ref_.Call({jsbuf, Napi::Number::New(env, static_cast(offset))}); // The callback may have detached/transferred the ArrayBuffer, // in which case Data() is null. if (!jsbuf.Data()) return ZXC_ERROR_IO; std::memcpy(dst, jsbuf.Data(), len); return static_cast(len); } catch (const std::exception&) { return ZXC_ERROR_IO; } } bool requireOpen(Napi::Env env) { if (!s_) { Napi::Error::New(env, "Seekable is closed").ThrowAsJavaScriptException(); return false; } return true; } Napi::Value NumBlocks(const Napi::CallbackInfo& info) { if (!requireOpen(info.Env())) return info.Env().Undefined(); return Napi::Number::New(info.Env(), zxc_seekable_get_num_blocks(s_)); } Napi::Value DecompressedSize(const Napi::CallbackInfo& info) { if (!requireOpen(info.Env())) return info.Env().Undefined(); uint64_t v = zxc_seekable_get_decompressed_size(s_); return Napi::Number::New(info.Env(), static_cast(v)); } Napi::Value BlockCompressedSize(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); if (!requireOpen(env)) return env.Undefined(); if (info.Length() < 1 || !info[0].IsNumber()) { Napi::TypeError::New(env, "Expected a block index") .ThrowAsJavaScriptException(); return env.Undefined(); } uint32_t idx = info[0].As().Uint32Value(); if (idx >= zxc_seekable_get_num_blocks(s_)) return env.Null(); return Napi::Number::New(env, zxc_seekable_get_block_comp_size(s_, idx)); } Napi::Value BlockDecompressedSize(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); if (!requireOpen(env)) return env.Undefined(); if (info.Length() < 1 || !info[0].IsNumber()) { Napi::TypeError::New(env, "Expected a block index") .ThrowAsJavaScriptException(); return env.Undefined(); } uint32_t idx = info[0].As().Uint32Value(); if (idx >= zxc_seekable_get_num_blocks(s_)) return env.Null(); return Napi::Number::New(env, zxc_seekable_get_block_decomp_size(s_, idx)); } Napi::Value DecompressRange(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); if (!requireOpen(env)) return env.Undefined(); if (info.Length() < 2 || !info[0].IsNumber() || !info[1].IsNumber()) { Napi::TypeError::New(env, "Expected (offset: number, length: number)") .ThrowAsJavaScriptException(); return env.Undefined(); } uint64_t offset = static_cast(info[0].As().Int64Value()); int64_t length = info[1].As().Int64Value(); if (length < 0) { Napi::TypeError::New(env, "length must be non-negative") .ThrowAsJavaScriptException(); return env.Undefined(); } if (in_native_call_) { Napi::Error::New(env, "reentrant decompressRange from readAt callback") .ThrowAsJavaScriptException(); return env.Undefined(); } Napi::Buffer out = Napi::Buffer::New(env, static_cast(length)); if (length == 0) return out; in_native_call_ = true; int64_t r = zxc_seekable_decompress_range(s_, out.Data(), static_cast(length), offset, static_cast(length)); in_native_call_ = false; if (r < 0) { return ThrowZxcError(env, static_cast(r)); } // On success the library fills the whole range; slice defensively if // fewer bytes were produced (napi buffers are uninitialized). if (r == length) return out; return Napi::Buffer::Copy(env, out.Data(), static_cast(r)); } Napi::Value SetDict(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); if (!requireOpen(env)) return env.Undefined(); if (info.Length() < 1 || !info[0].IsBuffer()) { Napi::TypeError::New(env, "Expected a Buffer (dict)").ThrowAsJavaScriptException(); return env.Undefined(); } Napi::Buffer dict_buf = info[0].As>(); const void* dict_huf = nullptr; if (info.Length() >= 2 && info[1].IsBuffer()) { Napi::Buffer huf_buf = info[1].As>(); if (huf_buf.Length() != ZXC_HUF_TABLE_SIZE) { Napi::TypeError::New(env, "dictHuf must be exactly 128 bytes") .ThrowAsJavaScriptException(); return env.Undefined(); } dict_huf = huf_buf.Data(); } int r = zxc_seekable_set_dict(s_, dict_buf.Data(), dict_buf.Length(), dict_huf); if (r < 0) { return ThrowZxcError(env, r); } return env.Undefined(); } Napi::Value Close(const Napi::CallbackInfo& info) { if (in_native_call_) { Napi::Error::New(info.Env(), "cannot close Seekable from inside its readAt callback") .ThrowAsJavaScriptException(); return info.Env().Undefined(); } if (s_) { zxc_seekable_free(s_); s_ = nullptr; } return info.Env().Undefined(); } }; // ============================================================================= // seekTableSize(numBlocks: number): number // ============================================================================= static Napi::Value SeekTableSize(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); if (info.Length() < 1 || !info[0].IsNumber()) { Napi::TypeError::New(env, "Expected a number (numBlocks)") .ThrowAsJavaScriptException(); return env.Undefined(); } uint32_t n = info[0].As().Uint32Value(); return Napi::Number::New(env, static_cast(zxc_seek_table_size(n))); } // ============================================================================= // writeSeekTable(compSizes: number[]): Buffer // ============================================================================= static Napi::Value WriteSeekTable(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); if (info.Length() < 1 || !info[0].IsArray()) { Napi::TypeError::New(env, "Expected an array of per-block compressed sizes") .ThrowAsJavaScriptException(); return env.Undefined(); } Napi::Array arr = info[0].As(); uint32_t n = arr.Length(); if (n == 0) { Napi::TypeError::New(env, "compSizes must be non-empty") .ThrowAsJavaScriptException(); return env.Undefined(); } std::vector sizes(n); for (uint32_t i = 0; i < n; ++i) { Napi::Value v = arr.Get(i); if (!v.IsNumber()) { Napi::TypeError::New(env, "compSizes entries must be numbers") .ThrowAsJavaScriptException(); return env.Undefined(); } sizes[i] = v.As().Uint32Value(); } size_t sz = zxc_seek_table_size(n); Napi::Buffer out = Napi::Buffer::New(env, sz); int64_t r = zxc_write_seek_table(out.Data(), sz, sizes.data(), n); if (r < 0) { return ThrowZxcError(env, static_cast(r)); } // On success the table fills the buffer exactly; slice only if it ever // came back shorter (napi buffers are uninitialized past r). if (static_cast(r) == sz) return out; return Napi::Buffer::Copy(env, out.Data(), static_cast(r)); } // ============================================================================= // Module initialization // ============================================================================= static Napi::Object Init(Napi::Env env, Napi::Object exports) { // Functions exports.Set("compressBound", Napi::Function::New(env, CompressBound, "compressBound")); exports.Set("compress", Napi::Function::New(env, Compress, "compress")); exports.Set("decompress", Napi::Function::New(env, Decompress, "decompress")); exports.Set("getDecompressedSize", Napi::Function::New(env, GetDecompressedSize, "getDecompressedSize")); exports.Set("errorName", Napi::Function::New(env, ErrorName, "errorName")); // Dictionary API exports.Set("trainDict", Napi::Function::New(env, TrainDict, "trainDict")); exports.Set("dictId", Napi::Function::New(env, DictId, "dictId")); exports.Set("getDictId", Napi::Function::New(env, GetDictId, "getDictId")); exports.Set("dictGetId", Napi::Function::New(env, DictGetId, "dictGetId")); exports.Set("dictSave", Napi::Function::New(env, DictSave, "dictSave")); exports.Set("trainDictHuf", Napi::Function::New(env, TrainDictHuf, "trainDictHuf")); exports.Set("dictHuf", Napi::Function::New(env, DictHuf, "dictHuf")); exports.Set("dictLoad", Napi::Function::New(env, DictLoad, "dictLoad")); exports.Set("dictTrain", Napi::Function::New(env, DictTrain, "dictTrain")); exports.Set("DICT_SIZE_MAX", Napi::Number::New(env, ZXC_DICT_SIZE_MAX)); // Library info helpers exports.Set("minLevel", Napi::Function::New(env, MinLevel, "minLevel")); exports.Set("maxLevel", Napi::Function::New(env, MaxLevel, "maxLevel")); exports.Set("defaultLevel", Napi::Function::New(env, DefaultLevel, "defaultLevel")); exports.Set("libraryVersion", Napi::Function::New(env, LibraryVersion, "libraryVersion")); // Push streaming classes exports.Set("CStream", CStreamWrap::GetClass(env)); exports.Set("DStream", DStreamWrap::GetClass(env)); // Seekable random-access decompression exports.Set("Seekable", SeekableWrap::GetClass(env)); exports.Set("seekTableSize", Napi::Function::New(env, SeekTableSize, "seekTableSize")); exports.Set("writeSeekTable", Napi::Function::New(env, WriteSeekTable, "writeSeekTable")); // Compression level constants exports.Set("LEVEL_FASTEST", Napi::Number::New(env, ZXC_LEVEL_FASTEST)); exports.Set("LEVEL_FAST", Napi::Number::New(env, ZXC_LEVEL_FAST)); exports.Set("LEVEL_DEFAULT", Napi::Number::New(env, ZXC_LEVEL_DEFAULT)); exports.Set("LEVEL_BALANCED", Napi::Number::New(env, ZXC_LEVEL_BALANCED)); exports.Set("LEVEL_COMPACT", Napi::Number::New(env, ZXC_LEVEL_COMPACT)); exports.Set("LEVEL_DENSITY", Napi::Number::New(env, ZXC_LEVEL_DENSITY)); exports.Set("LEVEL_ULTRA", Napi::Number::New(env, ZXC_LEVEL_ULTRA)); // Error constants exports.Set("ERROR_MEMORY", Napi::Number::New(env, ZXC_ERROR_MEMORY)); exports.Set("ERROR_DST_TOO_SMALL", Napi::Number::New(env, ZXC_ERROR_DST_TOO_SMALL)); exports.Set("ERROR_SRC_TOO_SMALL", Napi::Number::New(env, ZXC_ERROR_SRC_TOO_SMALL)); exports.Set("ERROR_BAD_MAGIC", Napi::Number::New(env, ZXC_ERROR_BAD_MAGIC)); exports.Set("ERROR_BAD_VERSION", Napi::Number::New(env, ZXC_ERROR_BAD_VERSION)); exports.Set("ERROR_BAD_HEADER", Napi::Number::New(env, ZXC_ERROR_BAD_HEADER)); exports.Set("ERROR_BAD_CHECKSUM", Napi::Number::New(env, ZXC_ERROR_BAD_CHECKSUM)); exports.Set("ERROR_CORRUPT_DATA", Napi::Number::New(env, ZXC_ERROR_CORRUPT_DATA)); exports.Set("ERROR_BAD_OFFSET", Napi::Number::New(env, ZXC_ERROR_BAD_OFFSET)); exports.Set("ERROR_OVERFLOW", Napi::Number::New(env, ZXC_ERROR_OVERFLOW)); exports.Set("ERROR_IO", Napi::Number::New(env, ZXC_ERROR_IO)); exports.Set("ERROR_NULL_INPUT", Napi::Number::New(env, ZXC_ERROR_NULL_INPUT)); exports.Set("ERROR_BAD_BLOCK_TYPE", Napi::Number::New(env, ZXC_ERROR_BAD_BLOCK_TYPE)); exports.Set("ERROR_BAD_BLOCK_SIZE", Napi::Number::New(env, ZXC_ERROR_BAD_BLOCK_SIZE)); exports.Set("ERROR_DICT_REQUIRED", Napi::Number::New(env, ZXC_ERROR_DICT_REQUIRED)); exports.Set("ERROR_DICT_MISMATCH", Napi::Number::New(env, ZXC_ERROR_DICT_MISMATCH)); exports.Set("ERROR_DICT_TOO_LARGE", Napi::Number::New(env, ZXC_ERROR_DICT_TOO_LARGE)); exports.Set("ERROR_BAD_LEVEL", Napi::Number::New(env, ZXC_ERROR_BAD_LEVEL)); return exports; } NODE_API_MODULE(zxc_nodejs, Init) zxc-0.13.0/wrappers/nodejs/test/000077500000000000000000000000001522536370400165125ustar00rootroot00000000000000zxc-0.13.0/wrappers/nodejs/test/zxc.dict.test.js000066400000000000000000000163251522536370400215630ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ 'use strict'; const zxc = require('../lib/index'); // ============================================================================= // Pre-trained dictionary support // ============================================================================= // Build a set of similar samples that share a lot of structure, so a trained // dictionary has something to capture. function buildSamples(count = 8) { const samples = []; for (let i = 0; i < count; i++) { const obj = { id: 1000 + i, type: 'user-record', createdAt: '2026-06-05T00:00:00Z', status: 'active', roles: ['reader', 'writer', 'admin'], payload: `the quick brown fox jumps over the lazy dog #${i}`, }; samples.push(Buffer.from(JSON.stringify(obj))); } return samples; } describe('trainDict', () => { test('produces non-empty dictionary content from similar samples', () => { const dict = zxc.trainDict(buildSamples(8)); expect(Buffer.isBuffer(dict)).toBe(true); expect(dict.length).toBeGreaterThan(0); expect(dict.length).toBeLessThanOrEqual(65535); }); test('honors a smaller maxSize cap', () => { const dict = zxc.trainDict(buildSamples(8), 1024); expect(dict.length).toBeLessThanOrEqual(1024); }); test('rejects non-array / empty input', () => { expect(() => zxc.trainDict('nope')).toThrow(TypeError); expect(() => zxc.trainDict([])).toThrow(TypeError); }); }); describe('dictionary compress/decompress roundtrip', () => { const dict = zxc.trainDict(buildSamples(8)); const original = Buffer.from( JSON.stringify({ id: 2000, type: 'user-record', createdAt: '2026-06-05T00:00:00Z', status: 'active', roles: ['reader', 'writer', 'admin'], payload: 'the quick brown fox jumps over the lazy dog #new', }), ); test('compress with {dict} then decompress with {dict} equals original', () => { const compressed = zxc.compress(original, { dict }); const restored = zxc.decompress(compressed, { dict }); expect(restored).toEqual(original); }); test('archive references the dictionary id', () => { const compressed = zxc.compress(original, { dict }); expect(zxc.getDictId(compressed)).toBe(zxc.dictId(dict)); expect(zxc.getDictId(compressed)).not.toBe(0); }); test('decompressing a dict archive without the dictionary throws', () => { const compressed = zxc.compress(original, { dict }); expect(() => zxc.decompress(compressed)).toThrow(); }); test('a non-dictionary archive reports dict id 0', () => { const compressed = zxc.compress(original); expect(zxc.getDictId(compressed)).toBe(0); }); }); describe('dictionary id consistency', () => { const dict = zxc.trainDict(buildSamples(8)); const huf = zxc.trainDictHuf(buildSamples(8), dict); test('dictId(content) === getDictId(archive); .zxd id binds the table', () => { const original = Buffer.from('the quick brown fox jumps over the lazy dog'.repeat(4)); const compressed = zxc.compress(original, { dict }); const zxd = zxc.dictSave(dict, huf); const idContent = zxc.dictId(dict); const idArchive = zxc.getDictId(compressed); const idZxd = zxc.dictGetId(zxd); expect(idContent).toBe(idArchive); // The .zxd id covers (content, table): non-zero, distinct from the // content-only id. expect(idZxd).not.toBe(0); expect(idZxd).not.toBe(idContent); expect(zxc.dictHuf(zxd)).toEqual(huf); }); }); describe('dictSave / dictLoad', () => { const dict = zxc.trainDict(buildSamples(8)); const huf = zxc.trainDictHuf(buildSamples(8), dict); test('roundtrips content and id', () => { const zxd = zxc.dictSave(dict, huf); expect(Buffer.isBuffer(zxd)).toBe(true); expect(zxd.length).toBeGreaterThan(dict.length); const loaded = zxc.dictLoad(zxd); expect(loaded.content).toEqual(dict); expect(loaded.id).toBe(zxc.dictGetId(zxd)); }); test('dictLoad rejects garbage', () => { expect(() => zxc.dictLoad(Buffer.from('not a zxd file at all'))).toThrow(); }); }); describe('Dictionary object', () => { test('train / save / load roundtrip and compress with {dict: Dictionary}', () => { const d = zxc.Dictionary.train(buildSamples(8)); expect(Buffer.isBuffer(d.content)).toBe(true); expect(d.content.length).toBeGreaterThan(0); expect(d.huf.length).toBe(128); expect(d.id).not.toBe(0); // save() -> load() preserves the (content, table, id) triple. const reloaded = zxc.Dictionary.load(d.save()); expect(reloaded.content).toEqual(d.content); expect(reloaded.huf).toEqual(d.huf); expect(reloaded.id).toBe(d.id); // One-call train matches the primitive 2-step training. expect(d.content).toEqual(zxc.trainDict(buildSamples(8))); expect(d.huf).toEqual(zxc.trainDictHuf(buildSamples(8), d.content)); const original = Buffer.from( 'the quick brown fox jumps over the lazy dog #obj'.repeat(4), ); const compressed = zxc.compress(original, { dict: d }); expect(zxc.decompress(compressed, { dict: d })).toEqual(original); // The archive id binds (content, table): decoding with the raw // content but no table is rejected. expect(() => zxc.decompress(compressed, { dict: d.content })).toThrow(); }); }); describe('seekable with dictionary', () => { const dict = zxc.trainDict(buildSamples(8)); // A payload large enough to span multiple blocks, full of dictionary-ish // patterns so the dictionary is actually engaged. function buildPayload() { const parts = []; // > 512 KB so the seekable archive spans multiple blocks, exercising // cross-block random access with a dictionary attached. for (let i = 0; i < 24000; i++) { parts.push(`the quick brown fox jumps over the lazy dog #${i} active reader writer admin\n`); } return Buffer.from(parts.join('')); } test('setDict + decompressRange roundtrip on a dict-compressed seekable archive', () => { const payload = buildPayload(); const compressed = zxc.compress(payload, { seekable: true, dict }); expect(zxc.getDictId(compressed)).toBe(zxc.dictId(dict)); const s = new zxc.Seekable(compressed); try { expect(s.numBlocks()).toBeGreaterThan(1); s.setDict(dict); // Full-range round trip. const full = s.decompressRange(0, payload.length); expect(full).toEqual(payload); // A sub-range somewhere in the middle, crossing a block boundary. const off = 600000; const len = 4096; const slice = s.decompressRange(off, len); expect(slice).toEqual(payload.subarray(off, off + len)); } finally { s.close(); } }); }); zxc-0.13.0/wrappers/nodejs/test/zxc.seekable.test.js000066400000000000000000000140711522536370400224070ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ 'use strict'; const zxc = require('../lib/index'); // ============================================================================= // Seekable random-access decompression // ============================================================================= function buildPayload(size) { const buf = Buffer.alloc(size); for (let i = 0; i < size; i++) buf[i] = (i * 31) & 0xFF; return buf; } function buildSeekableArchive(payload) { return zxc.compress(payload, { seekable: true, checksum: true }); } describe('Seekable: queries', () => { const payload = buildPayload(64 * 1024); const compressed = buildSeekableArchive(payload); test('reports total decompressed size and at least one block', () => { const s = new zxc.Seekable(compressed); try { expect(s.decompressedSize()).toBe(payload.length); expect(s.numBlocks()).toBeGreaterThanOrEqual(1); } finally { s.close(); } }); test('reports per-block sizes and rejects out-of-range indices', () => { const s = new zxc.Seekable(compressed); try { const numBlocks = s.numBlocks(); expect(s.blockCompressedSize(0)).toBeGreaterThan(0); expect(s.blockDecompressedSize(0)).toBeGreaterThan(0); expect(s.blockCompressedSize(numBlocks)).toBeNull(); expect(s.blockDecompressedSize(numBlocks)).toBeNull(); } finally { s.close(); } }); }); describe('Seekable: decompressRange', () => { const payload = buildPayload(64 * 1024); const compressed = buildSeekableArchive(payload); test('full-range round trip', () => { const s = new zxc.Seekable(compressed); try { const out = s.decompressRange(0, payload.length); expect(out.length).toBe(payload.length); expect(out.equals(payload)).toBe(true); } finally { s.close(); } }); test('mid-range slice is byte-exact', () => { const s = new zxc.Seekable(compressed); try { const off = 1024; const len = 8192; const out = s.decompressRange(off, len); expect(out.length).toBe(len); expect(out.equals(payload.subarray(off, off + len))).toBe(true); } finally { s.close(); } }); test('zero-length range returns empty buffer', () => { const s = new zxc.Seekable(compressed); try { const out = s.decompressRange(0, 0); expect(out.length).toBe(0); } finally { s.close(); } }); }); describe('Seekable: error handling', () => { test('constructor throws on garbage buffer', () => { expect(() => new zxc.Seekable(Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]))).toThrow(); }); test('constructor throws on empty buffer', () => { expect(() => new zxc.Seekable(Buffer.alloc(0))).toThrow(); }); test('methods after close throw', () => { const s = new zxc.Seekable(buildSeekableArchive(buildPayload(4096))); s.close(); expect(() => s.numBlocks()).toThrow(); // close is idempotent expect(() => s.close()).not.toThrow(); }); }); describe('Seekable: reader callback', () => { const payload = buildPayload(128 * 1024); const compressed = buildSeekableArchive(payload); test('roundtrip via in-memory readAt', () => { let calls = 0; const reader = { size: compressed.length, readAt(dst, offset) { calls++; compressed.copy(dst, 0, offset, offset + dst.length); }, }; const s = new zxc.Seekable(reader); try { // 3 reads at open: header, footer, seek table. expect(calls).toBe(3); expect(s.decompressedSize()).toBe(payload.length); const out = s.decompressRange(0, payload.length); expect(Buffer.compare(out, payload)).toBe(0); const before = calls; const chunk = s.decompressRange(2048, 1024); expect(Buffer.compare(chunk, payload.subarray(2048, 2048 + 1024))).toBe(0); // Single-block sub-range must trigger exactly one extra read. expect(calls - before).toBe(1); } finally { s.close(); } }); test('readAt throwing maps to decompress_range error', () => { let attempted = 0; const reader = { size: compressed.length, readAt(dst, offset) { attempted++; if (attempted > 3) throw new Error('boom'); compressed.copy(dst, 0, offset, offset + dst.length); }, }; const s = new zxc.Seekable(reader); try { expect(() => s.decompressRange(0, payload.length)).toThrow(); } finally { s.close(); } }); test('rejects missing size / readAt', () => { expect(() => new zxc.Seekable({})).toThrow(); expect(() => new zxc.Seekable({ size: 100 })).toThrow(); expect(() => new zxc.Seekable({ readAt: () => {} })).toThrow(); expect(() => new zxc.Seekable({ size: 0, readAt: () => {} })).toThrow(); }); test('rejects garbage reader', () => { const reader = { size: 64, readAt(dst /* , offset */) { dst.fill(0); }, }; expect(() => new zxc.Seekable(reader)).toThrow(); }); }); describe('Seekable: low-level seek table helpers', () => { test('seekTableSize / writeSeekTable round trip', () => { const compSizes = [128, 256, 200, 4]; const sz = zxc.seekTableSize(compSizes.length); expect(sz).toBeGreaterThan(0); const buf = zxc.writeSeekTable(compSizes); expect(buf.length).toBe(sz); }); test('writeSeekTable rejects empty array', () => { expect(() => zxc.writeSeekTable([])).toThrow(); }); }); zxc-0.13.0/wrappers/nodejs/test/zxc.streams.test.js000066400000000000000000000111141522536370400223050ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause * * Tests for the stream.Transform adapters and detectZxc helper. */ 'use strict'; const { Readable, PassThrough } = require('node:stream'); const { pipeline } = require('node:stream/promises'); const zxc = require('../lib/index'); async function gather(stream) { const chunks = []; for await (const c of stream) chunks.push(c); return Buffer.concat(chunks); } async function roundtripPipeline(data, opts = {}) { const compressed = await gather( Readable.from([data]).pipe(zxc.createCompressStream(opts)), ); const decompressed = await gather( Readable.from([compressed]).pipe(zxc.createDecompressStream(opts)), ); return decompressed; } describe('CompressStream / DecompressStream roundtrip', () => { test('small buffer', async () => { const data = Buffer.from('Hello stream.Transform bridge over ZXC!'); const got = await roundtripPipeline(data); expect(Buffer.compare(got, data)).toBe(0); }); test('large 2 MB buffer', async () => { const data = Buffer.alloc(2 * 1024 * 1024); for (let i = 0; i < data.length; i++) data[i] = (i * 13) % 251; const got = await roundtripPipeline(data); expect(got.length).toBe(data.length); expect(Buffer.compare(got, data)).toBe(0); }); test('many small chunks fed sequentially', async () => { const want = Buffer.alloc(64 * 1024); for (let i = 0; i < want.length; i++) want[i] = (i ^ 0x5A) & 0xFF; // Feed 257-byte chunks to exercise buffering boundaries. const chunks = []; for (let i = 0; i < want.length; i += 257) { chunks.push(want.subarray(i, Math.min(i + 257, want.length))); } const compressed = await gather( Readable.from(chunks).pipe(zxc.createCompressStream()), ); const got = await gather( Readable.from([compressed]).pipe(zxc.createDecompressStream()), ); expect(Buffer.compare(got, want)).toBe(0); }); test('with checksum option', async () => { const data = Buffer.alloc(32 * 1024); for (let i = 0; i < data.length; i++) data[i] = i & 0xFF; const got = await roundtripPipeline(data, { checksum: true }); expect(Buffer.compare(got, data)).toBe(0); }); test('via pipeline()', async () => { const data = Buffer.from('pipeline integration smoke test'.repeat(100)); const intermediate = new PassThrough(); const finalSink = new PassThrough(); const collected = gather(finalSink); await pipeline( Readable.from([data]), zxc.createCompressStream(), intermediate, zxc.createDecompressStream(), finalSink, ); expect(Buffer.compare(await collected, data)).toBe(0); }); }); describe('DecompressStream error paths', () => { test('truncated frame emits ZXC_TRUNCATED', async () => { const data = Buffer.alloc(32 * 1024, 0x41); const compressed = await gather( Readable.from([data]).pipe(zxc.createCompressStream()), ); const truncated = compressed.subarray(0, Math.floor(compressed.length / 2)); await expect( gather(Readable.from([truncated]).pipe(zxc.createDecompressStream())), ).rejects.toMatchObject({ code: 'ZXC_TRUNCATED' }); }); }); describe('detectZxc', () => { test('detects a frame produced by compress()', () => { const frame = zxc.compress(Buffer.from('sniff me')); expect(zxc.detectZxc(frame)).toBe(true); }); test('detects a frame produced by CompressStream', async () => { const frame = await gather( Readable.from([Buffer.from('hi')]).pipe(zxc.createCompressStream()), ); expect(zxc.detectZxc(frame)).toBe(true); }); test.each([ ['empty', Buffer.alloc(0)], ['too short', Buffer.from([0xF5, 0x2E, 0xB0])], ['zeros', Buffer.alloc(4)], ['random text', Buffer.from('not a zxc frame at all')], ])('rejects %s', (_name, buf) => { expect(zxc.detectZxc(buf)).toBe(false); }); test('accepts Uint8Array', () => { const frame = zxc.compress(Buffer.from('x')); const u8 = new Uint8Array(frame); expect(zxc.detectZxc(u8)).toBe(true); }); test('returns false for null/undefined', () => { expect(zxc.detectZxc(null)).toBe(false); expect(zxc.detectZxc(undefined)).toBe(false); }); }); zxc-0.13.0/wrappers/nodejs/test/zxc.test.js000066400000000000000000000234231522536370400206360ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ 'use strict'; const zxc = require('../lib/index'); // ============================================================================= // Roundtrip tests // ============================================================================= describe('compress/decompress roundtrip', () => { const testCases = [ { name: 'normal data', data: Buffer.from('hello world'.repeat(10)) }, { name: 'single byte', data: Buffer.from('a') }, { name: 'empty buffer', data: Buffer.alloc(0) }, { name: 'large 10 MB', data: Buffer.alloc(10_000_000, 0x42) }, ]; for (const { name, data } of testCases) { test(`roundtrip: ${name}`, () => { for (let level = zxc.LEVEL_FASTEST; level <= zxc.LEVEL_ULTRA; level++) { const compressed = zxc.compress(data, { level }); const size = zxc.getDecompressedSize(compressed); const decompressed = zxc.decompress(compressed, { size }); expect(decompressed.length).toBe(data.length); expect(Buffer.compare(decompressed, data)).toBe(0); } }); } test('roundtrip with auto-size detection', () => { const data = Buffer.from('auto size detection test'.repeat(100)); const compressed = zxc.compress(data); const decompressed = zxc.decompress(compressed); expect(decompressed.length).toBe(data.length); expect(Buffer.compare(decompressed, data)).toBe(0); }); test('oversized size hint returns exactly the real payload', () => { // Regression: the full hint-sized buffer used to be returned, with an // uninitialized (allocUnsafe) tail past the real decompressed size. const data = Buffer.from('oversized hint test'.repeat(50)); const compressed = zxc.compress(data); const decompressed = zxc.decompress(compressed, { size: data.length + 4096 }); expect(decompressed.length).toBe(data.length); expect(Buffer.compare(decompressed, data)).toBe(0); }); }); // ============================================================================= // compressBound // ============================================================================= describe('compressBound', () => { test('returns a value >= input size', () => { const bound = zxc.compressBound(1024); expect(bound).toBeGreaterThanOrEqual(1024); }); test('returns 0-based bound for size 0', () => { const bound = zxc.compressBound(0); expect(bound).toBeGreaterThanOrEqual(0); }); }); // ============================================================================= // Corruption detection // ============================================================================= describe('corruption detection', () => { test('detects corrupted data with checksum', () => { const data = Buffer.from('hello world'.repeat(10)); const compressed = zxc.compress(data, { checksum: true }); // Corrupt last byte const corrupted = Buffer.from(compressed); corrupted[corrupted.length - 1] ^= 0x01; expect(() => { zxc.decompress(corrupted, { size: data.length, checksum: true }); }).toThrow(); }); test('throws on truncated compressed input', () => { expect(() => { zxc.decompress(Buffer.from([0x01, 0x02, 0x03]), { size: 100 }); }).toThrow(); }); }); // ============================================================================= // Invalid input types // ============================================================================= describe('invalid input handling', () => { test('compress rejects non-Buffer', () => { expect(() => zxc.compress('string')).toThrow(TypeError); expect(() => zxc.compress(123)).toThrow(TypeError); expect(() => zxc.compress(null)).toThrow(TypeError); }); test('decompress rejects non-Buffer', () => { expect(() => zxc.decompress('string')).toThrow(TypeError); expect(() => zxc.decompress(123)).toThrow(TypeError); }); test('getDecompressedSize rejects non-Buffer', () => { expect(() => zxc.getDecompressedSize('string')).toThrow(TypeError); }); test('compressBound rejects non-number', () => { expect(() => zxc.compressBound('abc')).toThrow(TypeError); }); }); // ============================================================================= // Constants // ============================================================================= describe('constants', () => { test('compression levels are defined', () => { expect(zxc.LEVEL_FASTEST).toBe(1); expect(zxc.LEVEL_FAST).toBe(2); expect(zxc.LEVEL_DEFAULT).toBe(3); expect(zxc.LEVEL_BALANCED).toBe(4); expect(zxc.LEVEL_COMPACT).toBe(5); expect(zxc.LEVEL_DENSITY).toBe(6); expect(zxc.LEVEL_ULTRA).toBe(7); }); }); // ============================================================================= // Error Handling // ============================================================================= describe('error handling', () => { test('error constants are defined', () => { expect(zxc.ERROR_MEMORY).toBe(-1); expect(zxc.ERROR_DST_TOO_SMALL).toBe(-2); expect(zxc.ERROR_SRC_TOO_SMALL).toBe(-3); expect(zxc.ERROR_BAD_MAGIC).toBe(-4); expect(zxc.ERROR_BAD_VERSION).toBe(-5); expect(zxc.ERROR_BAD_HEADER).toBe(-6); expect(zxc.ERROR_BAD_CHECKSUM).toBe(-7); expect(zxc.ERROR_CORRUPT_DATA).toBe(-8); expect(zxc.ERROR_BAD_OFFSET).toBe(-9); expect(zxc.ERROR_OVERFLOW).toBe(-10); expect(zxc.ERROR_IO).toBe(-11); expect(zxc.ERROR_NULL_INPUT).toBe(-12); expect(zxc.ERROR_BAD_BLOCK_TYPE).toBe(-13); expect(zxc.ERROR_BAD_BLOCK_SIZE).toBe(-14); }); test('errorName works', () => { expect(zxc.errorName(zxc.ERROR_DST_TOO_SMALL)).toBe('ZXC_ERROR_DST_TOO_SMALL'); expect(zxc.errorName(-999)).toBe('ZXC_UNKNOWN_ERROR'); }); test('thrown errors have code property', () => { try { zxc.decompress(Buffer.from([0x01, 0x02, 0x03]), { size: 100 }); throw new Error('Should have thrown'); } catch (err) { expect(err.code).toBe(zxc.ERROR_NULL_INPUT); expect(err.message).toBe('ZXC_ERROR_NULL_INPUT'); } }); test('detects bad magic with correct code', () => { try { // Provide enough bytes (16 for header) but fake magic const fakeHeader = Buffer.alloc(32); fakeHeader.write('FAKE', 0, 'utf8'); zxc.decompress(fakeHeader, { size: 100 }); throw new Error('Should have thrown'); } catch (err) { expect(err.code).toBe(zxc.ERROR_BAD_HEADER); } }); }); // ============================================================================= // Push Streaming API (zxc.CStream / zxc.DStream) // ============================================================================= function pstreamRoundtrip(data, { level, checksum } = {}) { const cs = new zxc.CStream({ level, checksum }); const compressedChunks = []; // Feed in 17-byte slices to exercise the buffering/state machine. const step = Math.max(1, Math.min(17, data.length)); for (let i = 0; i < data.length; i += step) { compressedChunks.push(cs.compress(data.subarray(i, i + step))); } compressedChunks.push(cs.end()); cs.close(); const compressed = Buffer.concat(compressedChunks); const ds = new zxc.DStream({ checksum }); const outChunks = []; const dstep = Math.max(1, Math.min(31, compressed.length)); for (let i = 0; i < compressed.length; i += dstep) { outChunks.push(ds.decompress(compressed.subarray(i, i + dstep))); } expect(ds.finished()).toBe(true); ds.close(); return Buffer.concat(outChunks); } describe('pstream roundtrip', () => { test('small payload', () => { const data = Buffer.from('Hello pstream! Round-trip through the Node.js push API.'); expect(pstreamRoundtrip(data).equals(data)).toBe(true); }); test('with checksum', () => { const data = Buffer.alloc(32 * 1024); for (let i = 0; i < data.length; i++) data[i] = i % 251; expect(pstreamRoundtrip(data, { checksum: true }).equals(data)).toBe(true); }); test('multi-block (>512 KB)', () => { const data = Buffer.alloc(1536 * 1024); for (let i = 0; i < data.length; i++) data[i] = (i * 7) % 256; expect(pstreamRoundtrip(data).equals(data)).toBe(true); }); }); describe('pstream lifecycle', () => { test('size hints non-zero', () => { const cs = new zxc.CStream(); expect(cs.inSize()).toBeGreaterThan(0); expect(cs.outSize()).toBeGreaterThan(0); cs.close(); const ds = new zxc.DStream(); expect(ds.inSize()).toBeGreaterThan(0); expect(ds.outSize()).toBeGreaterThan(0); expect(ds.finished()).toBe(false); ds.close(); }); test('use after close throws', () => { const cs = new zxc.CStream(); cs.close(); expect(() => cs.compress(Buffer.from('x'))).toThrow(/closed/); const ds = new zxc.DStream(); ds.close(); expect(() => ds.decompress(Buffer.from('x'))).toThrow(/closed/); }); test('truncated stream not finished', () => { const cs = new zxc.CStream(); const compressed = Buffer.concat([cs.compress(Buffer.from('hello '.repeat(1000))), cs.end()]); cs.close(); const ds = new zxc.DStream(); ds.decompress(compressed.subarray(0, compressed.length - 5)); expect(ds.finished()).toBe(false); ds.close(); }); }); zxc-0.13.0/wrappers/nodejs/vitest.config.mjs000066400000000000000000000002131522536370400210240ustar00rootroot00000000000000import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { globals: true, testTimeout: 30000, }, }); zxc-0.13.0/wrappers/python/000077500000000000000000000000001522536370400155725ustar00rootroot00000000000000zxc-0.13.0/wrappers/python/.gitignore000066400000000000000000000014271522536370400175660ustar00rootroot00000000000000# Editor temporary/working/backup files # ######################################### .#* [#]*# *~ *$ *.bak *.diff .idea/ *.iml *.ipr *.iws *.org .project pmip *.rej .settings/ .*.sw[nop] .sw[nop] *.tmp *.vim .vscode tags cscope.out # gnu global GPATH GRTAGS GSYMS GTAGS .cache .mypy_cache/ # Compiled source # ################### *.a *.com *.class *.dll *.exe *.o *.o.d *.py[ocd] *.so *.mod # Compiled source # ################### build build-* !requirements/build-* _build _version.py # Egg metadata *.egg-info venv/ # OS generated files # ###################### .DS_Store* .VolumeIcon.icns .fseventsd Icon? .gdb_history ehthumbs.db Thumbs.db .directory # pytest generated files # ########################## /.pytest_cache # benchmarking # ########################## heaptrack* *.out zxc-0.13.0/wrappers/python/CMakeLists.txt000066400000000000000000000010751522536370400203350ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.20) project(zxc_python C) find_package(Python COMPONENTS Development.Module REQUIRED) # ---- Core ---- add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../ ${CMAKE_CURRENT_BINARY_DIR}/zxc_core_build) # ---- Python extension ---- Python_add_library(_zxc MODULE src/zxc/_zxc.c) # Without "lib" prefix set_target_properties(_zxc PROPERTIES PREFIX "") target_include_directories(_zxc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../include ) target_link_libraries(_zxc PRIVATE zxc_lib) install(TARGETS _zxc DESTINATION zxc)zxc-0.13.0/wrappers/python/README.md000066400000000000000000000013751522536370400170570ustar00rootroot00000000000000# ZXC Python Bindings High-performance Python bindings for the **ZXC** asymmetric compressor, optimized for **fast decompression**. Designed for *Write Once, Read Many* workloads like ML datasets, game assets, and caches. ## Features - **Blazing fast decompression** - ZXC is specifically optimized for read-heavy workloads. - **Buffer protocol support** - works with `bytes`, `bytearray`, `memoryview`, and even NumPy arrays. - **Releases the GIL** during compression/decompression - true parallelism with Python threads. - **Stream helpers** - compress/decompress file-like objects. ## Installation (from source) ```bash git clone https://github.com/hellobertrand/zxc.git cd zxc/wrappers/python python -m venv .venv source .venv/bin/activate pip install .zxc-0.13.0/wrappers/python/pyproject.toml000066400000000000000000000011651522536370400205110ustar00rootroot00000000000000[build-system] requires = ["scikit-build-core>=0.9", "setuptools_scm[toml]>=7.0"] build-backend = "scikit_build_core.build" [project] name = "zxc-compress" dynamic = ["version"] description = "ZXC: Package for high-performance asymmetric lossless compression built for ultra-fast decode" readme = "README.md" license = "BSD-3-Clause" [project.urls] Homepage = "https://github.com/hellobertrand/zxc" [tool.scikit-build] metadata.version.provider = "scikit_build_core.metadata.setuptools_scm" wheel.packages = ["src/zxc"] [tool.setuptools_scm] root = "../../" version_scheme = "only-version" local_scheme = "no-local-version"zxc-0.13.0/wrappers/python/requirements/000077500000000000000000000000001522536370400203155ustar00rootroot00000000000000zxc-0.13.0/wrappers/python/requirements/build-deps.in000066400000000000000000000001041522536370400226700ustar00rootroot00000000000000scikit-build-core>=0.9 setuptools_scm[toml]>=7.0 setuptools==83.0.0 zxc-0.13.0/wrappers/python/requirements/build-deps.txt000066400000000000000000000031571522536370400231140ustar00rootroot00000000000000# This file was autogenerated by uv via the following command: # uv pip compile --universal --generate-hashes --allow-unsafe --python-version 3.12 --output-file requirements/build-deps.txt requirements/build-deps.in packaging==26.2 \ --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \ --hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661 # via # scikit-build-core # setuptools-scm # vcs-versioning pathspec==1.1.1 \ --hash=sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a \ --hash=sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189 # via scikit-build-core scikit-build-core==0.12.2 \ --hash=sha256:562e0bbc9de1a354c87825ccf732080268d6582a0200f648e8c4a2dcb1e3736d \ --hash=sha256:6ea4730da400f9a998ec3287bd3ebc1d751fe45ad0a93451bead8618adbc02b1 # via -r requirements/build-deps.in setuptools==83.0.0 \ --hash=sha256:025bccbbf0fa05b6192bc64ae1e7b16e001fd6d6d4d5de03c97b1c1ade523bef \ --hash=sha256:29b23c360f22f414dc7336bb39178cc7bcbf6021ed2733cde173f09dba19abb3 # via # -r requirements/build-deps.in # setuptools-scm setuptools-scm==10.2.0 \ --hash=sha256:6cc5ac7da8e54d74e43503be2fa023eb7a1ca820d5c318e6ae20efc48110d6c7 \ --hash=sha256:ec8ea1738b92e42146a46e29a0e9de9ad462744c63cf9778677b95dfd605adde # via -r requirements/build-deps.in vcs-versioning==2.2.2 \ --hash=sha256:4ac4ded78720cdb4d0291ae58ace87e1e9201912e1023f3029c6cce5c9152cfb \ --hash=sha256:fe7fb216f8780a5516e7864a9333aacb1b70015b087a9be3918da76ef2760808 # via setuptools-scm zxc-0.13.0/wrappers/python/requirements/build.in000066400000000000000000000000761522536370400217470ustar00rootroot00000000000000cibuildwheel==4.1.0 setuptools-scm==10.2.0 setuptools==83.0.0 zxc-0.13.0/wrappers/python/requirements/build.txt000066400000000000000000000067331522536370400221660ustar00rootroot00000000000000# This file was autogenerated by uv via the following command: # uv pip compile --universal --generate-hashes --allow-unsafe --python-version 3.12 --output-file requirements/build.txt requirements/build.in bashlex==0.18 \ --hash=sha256:5bb03a01c6d5676338c36fd1028009c8ad07e7d61d8a1ce3f513b7fff52796ee \ --hash=sha256:91d73a23a3e51711919c1c899083890cdecffc91d8c088942725ac13e9dcfffa # via cibuildwheel bracex==2.6 \ --hash=sha256:0b0049264e7340b3ec782b5cb99beb325f36c3782a32e36e876452fd49a09952 \ --hash=sha256:98f1347cd77e22ee8d967a30ad4e310b233f7754dbf31ff3fceb76145ba47dc7 # via cibuildwheel build==1.5.0 \ --hash=sha256:13f3eecb844759ab66efec90ca17639bbf14dc06cb2fdf37a9010322d9c50a6f \ --hash=sha256:302c22c3ba2a0fd5f3911918651341ebb3896176cbdec15bd421f80b1afc7647 # via cibuildwheel certifi==2026.4.22 \ --hash=sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a \ --hash=sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580 # via cibuildwheel cibuildwheel==4.1.0 \ --hash=sha256:aac6f526c0c06039feedd09f9bbceb989ab244ef0543eaea84c8abdeafb81858 \ --hash=sha256:b9dabe7713f16aec4e7064d031daa2da010e7431d58964e0b97b3eb346952454 # via -r requirements/build.in colorama==0.4.6 ; os_name == 'nt' \ --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 # via build dependency-groups==1.3.1 \ --hash=sha256:51aeaa0dfad72430fcfb7bcdbefbd75f3792e5919563077f30bc0d73f4493030 \ --hash=sha256:78078301090517fd938c19f64a53ce98c32834dfe0dee6b88004a569a6adfefd # via cibuildwheel filelock==3.29.0 \ --hash=sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90 \ --hash=sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258 # via cibuildwheel humanize==4.15.0 \ --hash=sha256:1dd098483eb1c7ee8e32eb2e99ad1910baefa4b75c3aff3a82f4d78688993b10 \ --hash=sha256:b1186eb9f5a9749cd9cb8565aee77919dd7c8d076161cf44d70e59e3301e1769 # via cibuildwheel packaging==26.2 \ --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \ --hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661 # via # build # cibuildwheel # dependency-groups # setuptools-scm # vcs-versioning platformdirs==4.9.6 \ --hash=sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a \ --hash=sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917 # via cibuildwheel pyproject-hooks==1.2.0 \ --hash=sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8 \ --hash=sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913 # via build setuptools==83.0.0 \ --hash=sha256:025bccbbf0fa05b6192bc64ae1e7b16e001fd6d6d4d5de03c97b1c1ade523bef \ --hash=sha256:29b23c360f22f414dc7336bb39178cc7bcbf6021ed2733cde173f09dba19abb3 # via # -r requirements/build.in # setuptools-scm setuptools-scm==10.2.0 \ --hash=sha256:6cc5ac7da8e54d74e43503be2fa023eb7a1ca820d5c318e6ae20efc48110d6c7 \ --hash=sha256:ec8ea1738b92e42146a46e29a0e9de9ad462744c63cf9778677b95dfd605adde # via -r requirements/build.in vcs-versioning==2.2.2 \ --hash=sha256:4ac4ded78720cdb4d0291ae58ace87e1e9201912e1023f3029c6cce5c9152cfb \ --hash=sha256:fe7fb216f8780a5516e7864a9333aacb1b70015b087a9be3918da76ef2760808 # via setuptools-scm zxc-0.13.0/wrappers/python/requirements/sdist.in000066400000000000000000000000151522536370400217670ustar00rootroot00000000000000build==1.5.0 zxc-0.13.0/wrappers/python/requirements/sdist.txt000066400000000000000000000020231522536370400222010ustar00rootroot00000000000000# This file was autogenerated by uv via the following command: # uv pip compile --universal --generate-hashes --python-version 3.12 --output-file requirements/sdist.txt requirements/sdist.in build==1.5.0 \ --hash=sha256:13f3eecb844759ab66efec90ca17639bbf14dc06cb2fdf37a9010322d9c50a6f \ --hash=sha256:302c22c3ba2a0fd5f3911918651341ebb3896176cbdec15bd421f80b1afc7647 # via -r requirements/sdist.in colorama==0.4.6 ; os_name == 'nt' \ --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 # via build packaging==26.2 \ --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \ --hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661 # via build pyproject-hooks==1.2.0 \ --hash=sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8 \ --hash=sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913 # via build zxc-0.13.0/wrappers/python/requirements/test.in000066400000000000000000000000161522536370400216210ustar00rootroot00000000000000pytest==9.1.1 zxc-0.13.0/wrappers/python/requirements/test.txt000066400000000000000000000026531522536370400220430ustar00rootroot00000000000000# This file was autogenerated by uv via the following command: # uv pip compile --universal --generate-hashes --python-version 3.12 --output-file requirements/test.txt requirements/test.in colorama==0.4.6 ; sys_platform == 'win32' \ --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 # via pytest iniconfig==2.3.0 \ --hash=sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730 \ --hash=sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12 # via pytest packaging==26.2 \ --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \ --hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661 # via pytest pluggy==1.6.0 \ --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \ --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 # via pytest pygments==2.20.0 \ --hash=sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f \ --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176 # via pytest pytest==9.1.1 \ --hash=sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313 \ --hash=sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c # via -r requirements/test.in zxc-0.13.0/wrappers/python/src/000077500000000000000000000000001522536370400163615ustar00rootroot00000000000000zxc-0.13.0/wrappers/python/src/zxc/000077500000000000000000000000001522536370400171655ustar00rootroot00000000000000zxc-0.13.0/wrappers/python/src/zxc/__init__.py000066400000000000000000000670631522536370400213120ustar00rootroot00000000000000""" ZXC - High-performance lossless compression Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. SPDX-License-Identifier: BSD-3-Clause """ from __future__ import annotations import io as _io from ._zxc import ( pyzxc_compress, pyzxc_decompress, pyzxc_stream_compress, pyzxc_stream_decompress, pyzxc_get_decompressed_size, pyzxc_min_level, pyzxc_max_level, pyzxc_default_level, pyzxc_version_string, pyzxc_train_dict, pyzxc_dict_id, pyzxc_get_dict_id, pyzxc_dict_get_id, pyzxc_dict_save, pyzxc_dict_load, pyzxc_train_dict_huf, pyzxc_dict_huf, pyzxc_dict_train, pyzxc_seekable_set_dict, pyzxc_cstream_create, pyzxc_cstream_compress, pyzxc_cstream_end, pyzxc_cstream_in_size, pyzxc_cstream_out_size, pyzxc_cstream_free, pyzxc_dstream_create, pyzxc_dstream_decompress, pyzxc_dstream_finished, pyzxc_dstream_in_size, pyzxc_dstream_out_size, pyzxc_dstream_free, pyzxc_seekable_open, pyzxc_seekable_open_reader, pyzxc_seekable_num_blocks, pyzxc_seekable_decompressed_size, pyzxc_seekable_block_comp_size, pyzxc_seekable_block_decomp_size, pyzxc_seekable_decompress_range, pyzxc_seekable_free, pyzxc_seek_table_size, pyzxc_write_seek_table, LEVEL_FASTEST, LEVEL_FAST, LEVEL_DEFAULT, LEVEL_BALANCED, LEVEL_COMPACT, LEVEL_DENSITY, LEVEL_ULTRA, ERROR_MEMORY, ERROR_DST_TOO_SMALL, ERROR_SRC_TOO_SMALL, ERROR_BAD_MAGIC, ERROR_BAD_VERSION, ERROR_BAD_HEADER, ERROR_BAD_CHECKSUM, ERROR_CORRUPT_DATA, ERROR_BAD_OFFSET, ERROR_OVERFLOW, ERROR_IO, ERROR_NULL_INPUT, ERROR_BAD_BLOCK_TYPE, ERROR_BAD_BLOCK_SIZE, ERROR_DICT_REQUIRED, ERROR_DICT_MISMATCH, ERROR_DICT_TOO_LARGE, ERROR_BAD_LEVEL, ) try: from ._version import __version__ except ImportError: __version__ = "0.0.0-dev" __all__ = [ # Functions "compress", "decompress", "stream_compress", "stream_decompress", "get_decompressed_size", # Dictionary "train_dict", "dict_id", "get_dict_id", "dict_get_id", "dict_save", "dict_load", "train_dict_huf", "dict_huf", "Dictionary", # Push streaming "CStream", "DStream", # Seekable random-access decompression "Seekable", "seek_table_size", "write_seek_table", # io.RawIOBase adapters "ZxcReader", "ZxcWriter", "detect_zxc", # Library info helpers "min_level", "max_level", "default_level", "library_version", # Constants "LEVEL_FASTEST", "LEVEL_FAST", "LEVEL_DEFAULT", "LEVEL_BALANCED", "LEVEL_COMPACT", "LEVEL_DENSITY", "LEVEL_ULTRA", # Error Constants "ERROR_MEMORY", "ERROR_DST_TOO_SMALL", "ERROR_SRC_TOO_SMALL", "ERROR_BAD_MAGIC", "ERROR_BAD_VERSION", "ERROR_BAD_HEADER", "ERROR_BAD_CHECKSUM", "ERROR_CORRUPT_DATA", "ERROR_BAD_OFFSET", "ERROR_OVERFLOW", "ERROR_IO", "ERROR_NULL_INPUT", "ERROR_BAD_BLOCK_TYPE", "ERROR_BAD_BLOCK_SIZE", "ERROR_DICT_REQUIRED", "ERROR_DICT_MISMATCH", "ERROR_DICT_TOO_LARGE", "ERROR_BAD_LEVEL", ] def min_level() -> int: """Return the minimum supported compression level (currently 1).""" return pyzxc_min_level() def max_level() -> int: """Return the maximum supported compression level (currently 7).""" return pyzxc_max_level() def default_level() -> int: """Return the default compression level (currently 3).""" return pyzxc_default_level() def library_version() -> str: """Return the version string reported by the linked native libzxc (e.g. ``"0.13.0"``). Distinct from the Python package ``__version__``.""" return pyzxc_version_string() def train_dict(samples, max_size: int = 65535) -> bytes: """Train a dictionary from a corpus of sample buffers. Args: samples: A sequence (list/tuple) of bytes-like objects to train from. max_size: Maximum dictionary size in bytes (<= 65535). Returns: Raw dictionary content bytes, suitable for ``compress(..., dict=...)`` or serialization with :func:`dict_save`. """ return pyzxc_train_dict(samples, max_size) def dict_id(content: bytes) -> int: """Return the 32-bit dictionary ID for raw dictionary *content*. Returns 0 if *content* is empty. """ return pyzxc_dict_id(content) def get_dict_id(archive: bytes) -> int: """Return the dictionary ID a ``.zxc`` *archive* requires (0 if none). Only the first 16 bytes of the archive are needed. """ return pyzxc_get_dict_id(archive) def dict_get_id(zxd: bytes) -> int: """Return the dictionary ID stored in a ``.zxd`` file (0 if not valid).""" return pyzxc_dict_get_id(zxd) def dict_save(content: bytes, huf_lengths: bytes) -> bytes: """Serialize dictionary *content* and its shared literal Huffman table (128 bytes, from :func:`train_dict_huf`) into ``.zxd`` file bytes. The stored dictionary ID covers both content and table. """ return pyzxc_dict_save(content, huf_lengths) def train_dict_huf(samples, dict: bytes) -> bytes: """Train the shared literal Huffman table for an already-trained *dict*. Compresses *samples* with the dictionary and derives canonical code lengths from the real post-LZ literal distribution. Returns the 128-byte packed table required by :func:`dict_save` and usable as the ``dict_huf`` argument of :func:`compress` / :func:`decompress`. """ return pyzxc_train_dict_huf(samples, dict) def dict_huf(zxd: bytes): """Return the 128-byte shared Huffman table stored in a ``.zxd`` file, or ``None`` if the buffer is not a valid ``.zxd`` file.""" return pyzxc_dict_huf(zxd) def dict_load(zxd: bytes): """Parse a ``.zxd`` file and return ``(content, dict_id)``. The returned content is an owned copy of the dictionary bytes. Prefer :class:`Dictionary` (``Dictionary.load(zxd)``) for the full (content, table, id) bundle. """ content, _huf, dict_id = pyzxc_dict_load(zxd) return content, dict_id class Dictionary: """A trained dictionary: LZ-window content plus its shared literal Huffman table, bundled so callers never juggle the pair by hand. Create with :meth:`Dictionary.train` (from samples) or :meth:`Dictionary.load` (from ``.zxd`` bytes), then pass the instance as the ``dict`` argument of :func:`compress` / :func:`decompress` / :func:`stream_compress` or :meth:`Seekable.set_dict`. """ __slots__ = ("content", "huf", "id") def __init__(self, content: bytes, huf: bytes, id: int): if len(huf) != 128: raise ValueError("huf must be exactly 128 bytes") self.content = content self.huf = huf self.id = id @classmethod def train(cls, samples) -> "Dictionary": """Train a complete dictionary (content + shared table) from samples.""" return cls.load(pyzxc_dict_train(samples)) @classmethod def load(cls, zxd: bytes) -> "Dictionary": """Parse ``.zxd`` bytes into a Dictionary (owned copies).""" content, huf, dict_id = pyzxc_dict_load(zxd) return cls(content, huf, dict_id) def save(self) -> bytes: """Serialize back to ``.zxd`` file bytes.""" return dict_save(self.content, self.huf) def __eq__(self, other): return (isinstance(other, Dictionary) and self.content == other.content and self.huf == other.huf) def __repr__(self): return f"Dictionary(id=0x{self.id:08X}, content={len(self.content)} bytes)" def _split_dict_arg(dict, dict_huf): """Accept either a Dictionary or raw (content, table) pieces.""" if isinstance(dict, Dictionary): return dict.content, dict.huf return dict, dict_huf def compress(data, level = LEVEL_DEFAULT, checksum = False, dict = None, dict_huf = None) -> bytes: """Compress a bytes object. Args: data: Bytes-like object to compress. level: Compression level. Use constants like LEVEL_FASTEST, LEVEL_DEFAULT, etc. Values above LEVEL_ULTRA are silently clamped; values <= 0 select the default. checksum: If True, append a checksum for integrity verification. dict: Optional pre-trained dictionary content (bytes) to prime the compressor. Must be passed (matching by ID) to `decompress`. dict_huf: Optional shared literal Huffman table (128 bytes, from `train_dict_huf` or `dict_huf`). Ignored without `dict`; becomes part of the archive's dictionary binding. Returns: Compressed bytes. Note: This function operates entirely in-memory. For streaming files, use `stream_compress`. """ dict, dict_huf = _split_dict_arg(dict, dict_huf) return pyzxc_compress(data, level, checksum, dict, dict_huf) def get_decompressed_size(data: bytes) -> int: """Get the original decompressed size of a ZXC compressed buffer. Args: data (bytes): Compressed bytes buffer. Returns: int: Original uncompressed size in bytes, or 0 if the buffer is invalid or too small. Note: This function does not decompress the data, it only reads the footer for size info. """ return pyzxc_get_decompressed_size(data) def decompress(data, decompress_size=None, checksum=False, dict=None, dict_huf=None) -> bytes: """Decompress a bytes object. Args: data: Compressed bytes. decompress_size: Expected size. If None, read from the archive footer. checksum: If True, verify the checksum appended during compression. dict: Pre-trained dictionary content (bytes) required if the archive was compressed with one. Must match the dictionary ID stored in the archive header. dict_huf: Shared literal Huffman table (128 bytes) when the archive was compressed with one (the dictionary ID binds the pair). Returns: Decompressed bytes. """ if decompress_size is None: decompress_size = get_decompressed_size(data) dict, dict_huf = _split_dict_arg(dict, dict_huf) return pyzxc_decompress(data, decompress_size, checksum, dict, dict_huf) def stream_compress(src, dst, n_threads=0, level=LEVEL_DEFAULT, checksum=False, seekable=False, dict=None, dict_huf=None) -> int: """Compress data from src to dst (file-like objects). Args: src: Readable file-like object with `fileno()` support (e.g., open file). dst: Writable file-like object with `fileno()` support. n_threads: Number of threads to use for compression. 0 uses default. level: Compression level. Use constants like LEVEL_FASTEST, LEVEL_DEFAULT, etc. checksum: If True, append a checksum for integrity verification. seekable: If True, append a seek table for random-access decompression. dict: Optional pre-trained dictionary content (bytes). The archive records its dictionary ID; decompression must supply the same dict. dict_huf: Optional shared literal Huffman table (128 bytes) trained alongside the dictionary; ignored without `dict`. The dictionary ID binds the (dict, table) pair. Returns: Number of bytes written to `dst`. Note: In-memory streams like `io.BytesIO` are not supported. Use the in-memory `compress`/`decompress` functions for buffers. """ if not hasattr(src, "fileno") or not hasattr(dst, "fileno"): raise ValueError("src and dst must be open file-like objects") if not src.readable(): raise ValueError("Source file must be readable") if not dst.writable(): raise ValueError("Destination file must be writable") # CRITICAL: Flush Python buffers before passing FDs to C # to prevent data reordering/corruption. if hasattr(src, "flush"): src.flush() if hasattr(dst, "flush"): dst.flush() dict, dict_huf = _split_dict_arg(dict, dict_huf) return pyzxc_stream_compress(src, dst, n_threads, level, checksum, seekable, dict, dict_huf) def stream_decompress(src, dst, n_threads=0, checksum=False) -> int: """Decompress data from src to dst (file-like objects). Args: src: Readable file-like object with `fileno()` support. dst: Writable file-like object with `fileno()` support. n_threads: Number of threads to use for decompression. 0 uses default. checksum: If True, verify the checksum appended during compression. Returns: Number of bytes written to `dst`. Note: In-memory streams like `io.BytesIO` are not supported. Use the in-memory `compress`/`decompress` functions for buffers. """ if not hasattr(src, "fileno") or not hasattr(dst, "fileno"): raise ValueError("src and dst must be open file-like objects") if not src.readable(): raise ValueError("Source file must be readable") if not dst.writable(): raise ValueError("Destination file must be writable") # CRITICAL: Flush Python buffers before passing FDs to C # to prevent data reordering/corruption. if hasattr(src, "flush"): src.flush() if hasattr(dst, "flush"): dst.flush() return pyzxc_stream_decompress(src, dst, n_threads, checksum) class Seekable: """Random-access decompression of a seekable ZXC archive. Opens a seekable archive (one created with ``seekable=True``) and lets you decompress arbitrary byte ranges without reading the whole file. Two construction modes: **From bytes** (zero-copy: the buffer is pinned, not copied — do not mutate it while the handle is open; a ``bytearray`` source cannot be resized until :meth:`close`):: with zxc.Seekable(compressed_bytes) as s: chunk = s.decompress_range(offset, length) **From a reader object** with ``size`` (int) and ``read_at(length, offset) -> bytes`` attributes:: class MyReader: size = total_compressed_bytes def read_at(self, length, offset): ... # return `length` bytes from `offset` with zxc.Seekable(MyReader()) as s: chunk = s.decompress_range(0, s.decompressed_size) """ __slots__ = ("_handle",) def __init__(self, source): if isinstance(source, (bytes, bytearray, memoryview)): self._handle = pyzxc_seekable_open(source) elif hasattr(source, "size") and hasattr(source, "read_at"): self._handle = pyzxc_seekable_open_reader(source) else: raise TypeError( "expected bytes/bytearray/memoryview or an object with " "'size' (int) and 'read_at(length, offset)' attributes" ) def _ensure_open(self) -> None: if self._handle is None: raise ValueError("Seekable is closed") @property def num_blocks(self) -> int: self._ensure_open() return pyzxc_seekable_num_blocks(self._handle) @property def decompressed_size(self) -> int: self._ensure_open() return pyzxc_seekable_decompressed_size(self._handle) def block_compressed_size(self, block_idx: int): self._ensure_open() return pyzxc_seekable_block_comp_size(self._handle, block_idx) def block_decompressed_size(self, block_idx: int): self._ensure_open() return pyzxc_seekable_block_decomp_size(self._handle, block_idx) def set_dict(self, dict: bytes, dict_huf: bytes | None = None) -> None: """Attach a pre-trained dictionary to this seekable handle. Required before :meth:`decompress_range` when the archive was compressed with a dictionary. The content is copied internally, so *dict* may be freed after this call. """ self._ensure_open() dict, dict_huf = _split_dict_arg(dict, dict_huf) pyzxc_seekable_set_dict(self._handle, dict, dict_huf) def decompress_range(self, offset: int, length: int, *, n_threads: int = 0) -> bytes: self._ensure_open() return pyzxc_seekable_decompress_range(self._handle, offset, length, n_threads) def close(self) -> None: if self._handle is not None: pyzxc_seekable_free(self._handle) self._handle = None def __enter__(self): return self def __exit__(self, exc_type, exc, tb): self.close() return False def __del__(self): try: self.close() except Exception: # Destructors must not raise: this is best-effort cleanup only. return def seek_table_size(num_blocks: int) -> int: """Return the encoded byte size of a seek table for *num_blocks* blocks.""" return pyzxc_seek_table_size(num_blocks) def write_seek_table(comp_sizes: list) -> bytes: """Write a raw seek table from a list of per-block compressed sizes. Low-level helper — most callers should simply pass ``seekable=True`` to :func:`stream_compress` instead. """ return pyzxc_write_seek_table(comp_sizes) class CStream: """Push-based, single-threaded compression stream. The Python counterpart of the C ``zxc_cstream``. Use this when the multi-threaded ``stream_compress`` (which takes ``FILE*``) is not appropriate - e.g. async event loops, in-memory streams (``io.BytesIO``), network protocols, or callback-driven libraries. The stream is not thread-safe. Each call to :meth:`compress` returns the compressed bytes produced from the input fed in this call (may be empty if the input was small enough to stay inside the internal block accumulator). :meth:`end` flushes the residual block, the EOF marker and the file footer; you MUST call it to produce a valid ZXC archive. Example:: cs = zxc.CStream(level=zxc.LEVEL_DEFAULT, checksum=True) chunks = [cs.compress(part) for part in source] chunks.append(cs.end()) archive = b"".join(chunks) cs.close() Supports the context-manager protocol:: with zxc.CStream(level=3) as cs: out = cs.compress(data) + cs.end() """ __slots__ = ("_handle",) def __init__(self, level: int = LEVEL_DEFAULT, checksum: bool = False, block_size: int = 0): self._handle = pyzxc_cstream_create(level, checksum, block_size) def compress(self, data) -> bytes: """Push *data* into the stream and return any compressed bytes produced this call. May return ``b""`` if the input fit entirely into the internal block accumulator. The returned bytes must be written verbatim to the sink in order. """ if self._handle is None: raise ValueError("CStream is closed") return pyzxc_cstream_compress(self._handle, data) def end(self) -> bytes: """Finalise the stream and return the trailing bytes (residual block + EOF + file footer). Call exactly once after the last :meth:`compress`. """ if self._handle is None: raise ValueError("CStream is closed") return pyzxc_cstream_end(self._handle) @property def in_size(self) -> int: """Suggested input chunk size (block size, in bytes).""" if self._handle is None: return 0 return pyzxc_cstream_in_size(self._handle) @property def out_size(self) -> int: """Suggested output chunk size to never trigger a partial drain.""" if self._handle is None: return 0 return pyzxc_cstream_out_size(self._handle) def close(self) -> None: """Release native resources. Idempotent.""" if self._handle is not None: pyzxc_cstream_free(self._handle) self._handle = None def __enter__(self): return self def __exit__(self, exc_type, exc, tb): self.close() return False def __del__(self): try: self.close() except Exception: pass class DStream: """Push-based, single-threaded decompression stream. The Python counterpart of the C ``zxc_dstream``. Feed compressed bytes via :meth:`decompress`; each call returns the decompressed bytes produced so far. After all input has been fed, :attr:`finished` becomes ``True`` once the file footer is validated. Example:: ds = zxc.DStream(checksum=True) out = b"".join(ds.decompress(chunk) for chunk in compressed_chunks) if not ds.finished: raise ValueError("truncated stream") ds.close() """ __slots__ = ("_handle",) def __init__(self, checksum: bool = False): self._handle = pyzxc_dstream_create(checksum) def decompress(self, data) -> bytes: """Push *data* and return the decompressed bytes produced. May return ``b""`` if the parser is still waiting for more input (e.g. mid-header). """ if self._handle is None: raise ValueError("DStream is closed") return pyzxc_dstream_decompress(self._handle, data) @property def finished(self) -> bool: """``True`` once the decoder has reached and validated the file footer. Useful to detect truncated input.""" if self._handle is None: return False return pyzxc_dstream_finished(self._handle) @property def in_size(self) -> int: """Suggested input chunk size for the decompressor.""" if self._handle is None: return 0 return pyzxc_dstream_in_size(self._handle) @property def out_size(self) -> int: """Suggested output chunk size for the decompressor.""" if self._handle is None: return 0 return pyzxc_dstream_out_size(self._handle) def close(self) -> None: """Release native resources. Idempotent.""" if self._handle is not None: pyzxc_dstream_free(self._handle) self._handle = None def __enter__(self): return self def __exit__(self, exc_type, exc, tb): self.close() return False def __del__(self): try: self.close() except Exception: pass # ============================================================================ # io.RawIOBase adapters over the push streaming API # ============================================================================ # # Mirror of the wrappers shipped by the Go and Rust bindings: turn a # CStream / DStream pair into Python's standard binary file-like protocol so # ZXC can be plugged into pipelines that expect it — tarfile, requests, # oras-py / OCI registry clients, etc. # # Wrap with ``io.BufferedReader`` / ``io.BufferedWriter`` for buffering when # performance matters. class ZxcReader(_io.RawIOBase): """Decompresses a ZXC frame read from a binary file-like object. Implements the standard :class:`io.RawIOBase` interface so it can be plugged into any code that expects a readable binary stream. Raises :class:`OSError` with ``errno=None`` if the underlying source is drained before the ZXC footer is reached (truncated frame). Example:: with open("data.zxc", "rb") as f, zxc.ZxcReader(f) as r: payload = r.read() The wrapped reader is **not** closed by :meth:`close`. """ def __init__(self, fileobj, *, checksum: bool = False, buffer_size: int = 0): super().__init__() if not hasattr(fileobj, "read"): raise TypeError("fileobj must have a .read() method") self._src = fileobj self._ds = DStream(checksum=checksum) if buffer_size <= 0: buffer_size = self._ds.in_size self._bufsize = buffer_size self._pending = b"" # decompressed bytes not yet returned self._eof_src = False # True once src.read() returned empty bytes def readable(self) -> bool: return True def readinto(self, b) -> int | None: if self.closed: raise ValueError("I/O operation on closed reader") n_out = len(b) if n_out == 0: return 0 while not self._pending: if self._ds.finished: return 0 inbuf = b"" if not self._eof_src: chunk = self._src.read(self._bufsize) if chunk is None: # Non-blocking source with no data available right now # (RawIOBase semantics) — not EOF; propagate the "would # block" condition to the caller. return None if not chunk: self._eof_src = True else: inbuf = chunk produced = self._ds.decompress(inbuf) if produced: self._pending = produced break if self._eof_src: if self._ds.finished: return 0 raise OSError("zxc: input drained before footer (truncated stream)") take = min(n_out, len(self._pending)) b[:take] = self._pending[:take] self._pending = self._pending[take:] return take def close(self) -> None: if self.closed: return try: self._ds.close() finally: super().close() class ZxcWriter(_io.RawIOBase): """Compresses bytes written to it and forwards the ZXC frame to a binary file-like object. Implements the standard :class:`io.RawIOBase` interface. The frame is finalised (residual block, EOF marker, footer) when :meth:`close` is called. **You must close the writer to obtain a valid archive** — closing the wrapped file before the writer leaves a truncated frame on disk. Use the context-manager form to make this automatic:: with open("out.zxc", "wb") as f, zxc.ZxcWriter(f) as w: w.write(payload) The wrapped writer is **not** closed by :meth:`close`. """ def __init__(self, fileobj, *, level: int = LEVEL_DEFAULT, checksum: bool = False): super().__init__() if not hasattr(fileobj, "write"): raise TypeError("fileobj must have a .write() method") self._dst = fileobj self._cs = CStream(level=level, checksum=checksum) self._finalized = False def writable(self) -> bool: return True def write(self, b) -> int: if self.closed: raise ValueError("I/O operation on closed writer") if self._finalized: raise ValueError("ZxcWriter already finalised") mv = memoryview(b) if mv.nbytes == 0: return 0 # The C layer accepts any C-contiguous buffer directly and copies it # into its block accumulator, so no intermediate bytes() copy is # needed here. if not mv.c_contiguous: mv = memoryview(bytes(mv)) chunk = self._cs.compress(mv) if chunk: self._dst.write(chunk) return mv.nbytes def close(self) -> None: if self.closed: return try: if not self._finalized: tail = self._cs.end() if tail: self._dst.write(tail) self._finalized = True finally: self._cs.close() super().close() # Magic word identifying a ZXC file frame: little-endian 0x9CB02EF5. _ZXC_MAGIC_LE = b"\xf5\x2e\xb0\x9c" def detect_zxc(data) -> bool: """Return ``True`` if *data* starts with the ZXC file magic word. Useful for content-type sniffing in containers / object stores that need to dispatch on media type (e.g. OCI). Cheap and side-effect free; does not validate the rest of the header or the footer. """ if data is None: return False mv = memoryview(data) return len(mv) >= 4 and bytes(mv[:4]) == _ZXC_MAGIC_LEzxc-0.13.0/wrappers/python/src/zxc/__init__.pyi000066400000000000000000000131171522536370400214520ustar00rootroot00000000000000""" ZXC - High-performance lossless compression Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. SPDX-License-Identifier: BSD-3-Clause """ from typing import Protocol, Optional # ---------- constants ---------- LEVEL_FASTEST: int LEVEL_FAST: int LEVEL_DEFAULT: int LEVEL_BALANCED: int LEVEL_COMPACT: int LEVEL_DENSITY: int LEVEL_ULTRA: int # ---------- error codes ---------- ERROR_MEMORY: int ERROR_DST_TOO_SMALL: int ERROR_SRC_TOO_SMALL: int ERROR_BAD_MAGIC: int ERROR_BAD_VERSION: int ERROR_BAD_HEADER: int ERROR_BAD_CHECKSUM: int ERROR_CORRUPT_DATA: int ERROR_BAD_OFFSET: int ERROR_OVERFLOW: int ERROR_IO: int ERROR_NULL_INPUT: int ERROR_BAD_BLOCK_TYPE: int ERROR_BAD_BLOCK_SIZE: int ERROR_DICT_REQUIRED: int ERROR_DICT_MISMATCH: int ERROR_DICT_TOO_LARGE: int ERROR_BAD_LEVEL: int # ---------- types ---------- class FileLike(Protocol): """File-like object with a file descriptor. Note: This excludes in-memory streams like io.BytesIO. Use real file objects or objects wrapping OS file descriptors. """ def fileno(self) -> int: ... def readable(self) -> bool: ... def writable(self) -> bool: ... # ---------- functions ---------- def compress( data: bytes, level: int = LEVEL_DEFAULT, checksum: bool = False, dict: Optional[bytes] = None, dict_huf: Optional[bytes] = None ) -> bytes: ... def decompress( data: bytes, decompress_size: Optional[int] = None, checksum: bool = False, dict: Optional[bytes] = None, dict_huf: Optional[bytes] = None ) -> bytes: ... def stream_compress( src: FileLike, dst: FileLike, n_threads: int = 0, level: int = LEVEL_DEFAULT, checksum: bool = False, seekable: bool = False, dict: Optional[bytes] = None, dict_huf: Optional[bytes] = None ) -> int: ... def stream_decompress( src: FileLike, dst: FileLike, n_threads: int = 0, checksum: bool = False ) -> int: ... def get_decompressed_size(data: bytes) -> int: ... # ---------- pre-trained dictionaries ---------- def train_dict(samples: list[bytes], max_size: int = 65535) -> bytes: ... def train_dict_huf(samples: list[bytes], dict: bytes) -> bytes: ... def dict_id(content: bytes) -> int: ... def get_dict_id(archive: bytes) -> int: ... def dict_get_id(zxd: bytes) -> int: ... def dict_save(content: bytes, huf_lengths: bytes) -> bytes: ... def dict_load(zxd: bytes) -> tuple[bytes, int]: ... def dict_huf(zxd: bytes) -> Optional[bytes]: ... class Dictionary: content: bytes huf: bytes id: int def __init__(self, content: bytes, huf: bytes, id: int) -> None: ... @classmethod def train(cls, samples: list[bytes]) -> "Dictionary": ... @classmethod def load(cls, zxd: bytes) -> "Dictionary": ... def save(self) -> bytes: ... def min_level() -> int: ... def max_level() -> int: ... def default_level() -> int: ... def library_version() -> str: ... # ---------- seekable random-access decompression ---------- class Seekable: """Random-access decompression of a seekable ZXC archive.""" def __init__(self, source: bytes | bytearray | memoryview | object) -> None: ... @property def num_blocks(self) -> int: ... @property def decompressed_size(self) -> int: ... def block_compressed_size(self, block_idx: int) -> Optional[int]: ... def block_decompressed_size(self, block_idx: int) -> Optional[int]: ... def set_dict(self, dict: bytes, dict_huf: Optional[bytes] = None) -> None: ... def decompress_range(self, offset: int, length: int, *, n_threads: int = 0) -> bytes: ... def close(self) -> None: ... def __enter__(self) -> "Seekable": ... def __exit__(self, exc_type, exc, tb) -> bool: ... def seek_table_size(num_blocks: int) -> int: ... def write_seek_table(comp_sizes: list[int]) -> bytes: ... # ---------- push streaming ---------- class CStream: """Push-based, single-threaded compression stream.""" def __init__(self, level: int = LEVEL_DEFAULT, checksum: bool = False, block_size: int = 0) -> None: ... def compress(self, data: bytes) -> bytes: ... def end(self) -> bytes: ... @property def in_size(self) -> int: ... @property def out_size(self) -> int: ... def close(self) -> None: ... def __enter__(self) -> "CStream": ... def __exit__(self, exc_type, exc, tb) -> bool: ... class DStream: """Push-based, single-threaded decompression stream.""" def __init__(self, checksum: bool = False) -> None: ... def decompress(self, data: bytes) -> bytes: ... @property def finished(self) -> bool: ... @property def in_size(self) -> int: ... @property def out_size(self) -> int: ... def close(self) -> None: ... def __enter__(self) -> "DStream": ... def __exit__(self, exc_type, exc, tb) -> bool: ... # ---------- io.RawIOBase adapters ---------- import io as _io from typing import IO def detect_zxc(data: bytes) -> bool: ... class ZxcReader(_io.RawIOBase): """Decompresses a ZXC frame read from a binary file-like object.""" def __init__( self, fileobj: IO[bytes], *, checksum: bool = False, buffer_size: int = 0, ) -> None: ... def readable(self) -> bool: ... def readinto(self, b: bytearray) -> Optional[int]: ... def close(self) -> None: ... class ZxcWriter(_io.RawIOBase): """Compresses bytes written to it into a binary file-like object.""" def __init__( self, fileobj: IO[bytes], *, level: int = LEVEL_DEFAULT, checksum: bool = False, ) -> None: ... def writable(self) -> bool: ... def write(self, b: bytes) -> int: ... def close(self) -> None: ...zxc-0.13.0/wrappers/python/src/zxc/_zxc.c000066400000000000000000001750441522536370400203070ustar00rootroot00000000000000/* * Copyright (c) 2025-2026, Bertrand Lebonnois * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. */ #define PY_SSIZE_T_CLEAN #include #include "zxc.h" #include "zxc_seekable.h" #include "zxc_stream.h" #define Py_Return_Errno(err) \ do { \ PyErr_SetFromErrno(err); \ return NULL; \ } while (0) #define Py_Return_Err(err, str) \ do { \ PyErr_SetString(err, str); \ return NULL; \ } while (0) // ============================================================================= // Platform // ============================================================================= static inline int zxc_dup(int fd) { #ifdef _WIN32 return _dup(fd); #else return dup(fd); #endif } static inline FILE* zxc_fdopen(int fd, const char* mode) { #ifdef _WIN32 return _fdopen(fd, mode); #else return fdopen(fd, mode); #endif } static inline int zxc_close(int fd) { #ifdef _WIN32 return _close(fd); #else return close(fd); #endif } static int grow_output(uint8_t** buf, size_t* cap, size_t out_len, size_t want) { const size_t limit = (size_t)PY_SSIZE_T_MAX; if (out_len > limit || want > limit - out_len) return -1; const size_t needed = out_len + want; size_t new_cap = (*cap > limit / 2) ? limit : (*cap * 2); if (new_cap < needed) new_cap = needed; uint8_t* nb = (uint8_t*)realloc(*buf, new_cap); if (!nb) return -2; *buf = nb; *cap = new_cap; return 0; } // ============================================================================= // Wrapper functions // ============================================================================= static PyObject* pyzxc_compress(PyObject* self, PyObject* args, PyObject* kwargs); static PyObject* pyzxc_decompress(PyObject* self, PyObject* args, PyObject* kwargs); static PyObject* pyzxc_stream_compress(PyObject* self, PyObject* args, PyObject* kwargs); static PyObject* pyzxc_stream_decompress(PyObject* self, PyObject* args, PyObject* kwargs); static PyObject* pyzxc_get_decompressed_size(PyObject* self, PyObject* arg); static PyObject* pyzxc_min_level(PyObject* self, PyObject* args); static PyObject* pyzxc_max_level(PyObject* self, PyObject* args); static PyObject* pyzxc_default_level(PyObject* self, PyObject* args); static PyObject* pyzxc_version_string(PyObject* self, PyObject* args); static PyObject* pyzxc_train_dict(PyObject* self, PyObject* args, PyObject* kwargs); static PyObject* pyzxc_dict_id(PyObject* self, PyObject* arg); static PyObject* pyzxc_get_dict_id(PyObject* self, PyObject* arg); static PyObject* pyzxc_dict_get_id(PyObject* self, PyObject* arg); static PyObject* pyzxc_dict_save(PyObject* self, PyObject* args); static PyObject* pyzxc_train_dict_huf(PyObject* self, PyObject* args, PyObject* kwargs); static PyObject* pyzxc_dict_huf(PyObject* self, PyObject* arg); static PyObject* pyzxc_dict_train(PyObject* self, PyObject* args, PyObject* kwargs); static PyObject* pyzxc_dict_load(PyObject* self, PyObject* arg); static PyObject* pyzxc_seekable_set_dict(PyObject* self, PyObject* args); static PyObject* pyzxc_cstream_create(PyObject* self, PyObject* args, PyObject* kwargs); static PyObject* pyzxc_cstream_compress(PyObject* self, PyObject* args, PyObject* kwargs); static PyObject* pyzxc_cstream_end(PyObject* self, PyObject* args, PyObject* kwargs); static PyObject* pyzxc_cstream_in_size(PyObject* self, PyObject* args); static PyObject* pyzxc_cstream_out_size(PyObject* self, PyObject* args); static PyObject* pyzxc_cstream_free(PyObject* self, PyObject* args); static PyObject* pyzxc_dstream_create(PyObject* self, PyObject* args, PyObject* kwargs); static PyObject* pyzxc_dstream_decompress(PyObject* self, PyObject* args, PyObject* kwargs); static PyObject* pyzxc_dstream_finished(PyObject* self, PyObject* args); static PyObject* pyzxc_dstream_in_size(PyObject* self, PyObject* args); static PyObject* pyzxc_dstream_out_size(PyObject* self, PyObject* args); static PyObject* pyzxc_dstream_free(PyObject* self, PyObject* args); static PyObject* pyzxc_seekable_open(PyObject* self, PyObject* args); static PyObject* pyzxc_seekable_open_reader(PyObject* self, PyObject* args); static PyObject* pyzxc_seekable_num_blocks(PyObject* self, PyObject* args); static PyObject* pyzxc_seekable_decompressed_size(PyObject* self, PyObject* args); static PyObject* pyzxc_seekable_block_comp_size(PyObject* self, PyObject* args); static PyObject* pyzxc_seekable_block_decomp_size(PyObject* self, PyObject* args); static PyObject* pyzxc_seekable_decompress_range(PyObject* self, PyObject* args, PyObject* kwargs); static PyObject* pyzxc_seekable_free(PyObject* self, PyObject* args); static PyObject* pyzxc_seek_table_size(PyObject* self, PyObject* args); static PyObject* pyzxc_write_seek_table(PyObject* self, PyObject* args); // ============================================================================= // Initialize python module // ============================================================================= PyDoc_STRVAR( zxc_doc, "ZXC: High-performance, lossless asymmetric compression.\n" "\n" "Functions:\n" " compress(data: bytes, level: int = LEVEL_DEFAULT, checksum: bool = False) -> bytes\n" " Compress a bytes object.\n" "\n" " decompress(data: bytes, decompress_size: int, checksum: bool = False) -> bytes\n" " Decompress a bytes object to its original size.\n" "\n" " stream_compress(src: file-like, dst: file-like, n_threads: int = 0, level: int = " "LEVEL_DEFAULT, checksum: bool = False) -> None\n" " Compress data from a readable file-like object to a writable file-like object.\n" "\n" " stream_decompress(src: file-like, dst: file-like, n_threads: int = 0, checksum: bool = " "False) -> None\n" " Decompress data from a readable file-like object to a writable file-like object.\n" "\n" "Notes:\n" " - File-like objects must support fileno(), readable(), and writable().\n" " - Stream functions release the GIL for multi-threaded compression/decompression.\n"); static PyMethodDef zxc_methods[] = { {"pyzxc_compress", (PyCFunction)pyzxc_compress, METH_VARARGS | METH_KEYWORDS, NULL}, {"pyzxc_decompress", (PyCFunction)pyzxc_decompress, METH_VARARGS | METH_KEYWORDS, NULL}, {"pyzxc_stream_compress", (PyCFunction)pyzxc_stream_compress, METH_VARARGS | METH_KEYWORDS, NULL}, {"pyzxc_stream_decompress", (PyCFunction)pyzxc_stream_decompress, METH_VARARGS | METH_KEYWORDS, NULL}, {"pyzxc_get_decompressed_size", (PyCFunction)pyzxc_get_decompressed_size, METH_O, NULL}, {"pyzxc_min_level", (PyCFunction)pyzxc_min_level, METH_NOARGS, NULL}, {"pyzxc_max_level", (PyCFunction)pyzxc_max_level, METH_NOARGS, NULL}, {"pyzxc_default_level", (PyCFunction)pyzxc_default_level, METH_NOARGS, NULL}, {"pyzxc_version_string", (PyCFunction)pyzxc_version_string, METH_NOARGS, NULL}, /* Dictionary API. */ {"pyzxc_train_dict", (PyCFunction)pyzxc_train_dict, METH_VARARGS | METH_KEYWORDS, NULL}, {"pyzxc_dict_id", (PyCFunction)pyzxc_dict_id, METH_O, NULL}, {"pyzxc_get_dict_id", (PyCFunction)pyzxc_get_dict_id, METH_O, NULL}, {"pyzxc_dict_get_id", (PyCFunction)pyzxc_dict_get_id, METH_O, NULL}, {"pyzxc_dict_save", (PyCFunction)pyzxc_dict_save, METH_VARARGS, NULL}, {"pyzxc_train_dict_huf", (PyCFunction)pyzxc_train_dict_huf, METH_VARARGS | METH_KEYWORDS, NULL}, {"pyzxc_dict_huf", (PyCFunction)pyzxc_dict_huf, METH_O, NULL}, {"pyzxc_dict_train", (PyCFunction)pyzxc_dict_train, METH_VARARGS | METH_KEYWORDS, NULL}, {"pyzxc_dict_load", (PyCFunction)pyzxc_dict_load, METH_O, NULL}, /* Push streaming API (single-threaded, caller-driven). */ {"pyzxc_cstream_create", (PyCFunction)pyzxc_cstream_create, METH_VARARGS | METH_KEYWORDS, NULL}, {"pyzxc_cstream_compress", (PyCFunction)pyzxc_cstream_compress, METH_VARARGS | METH_KEYWORDS, NULL}, {"pyzxc_cstream_end", (PyCFunction)pyzxc_cstream_end, METH_VARARGS | METH_KEYWORDS, NULL}, {"pyzxc_cstream_in_size", (PyCFunction)pyzxc_cstream_in_size, METH_O, NULL}, {"pyzxc_cstream_out_size", (PyCFunction)pyzxc_cstream_out_size, METH_O, NULL}, {"pyzxc_cstream_free", (PyCFunction)pyzxc_cstream_free, METH_O, NULL}, {"pyzxc_dstream_create", (PyCFunction)pyzxc_dstream_create, METH_VARARGS | METH_KEYWORDS, NULL}, {"pyzxc_dstream_decompress", (PyCFunction)pyzxc_dstream_decompress, METH_VARARGS | METH_KEYWORDS, NULL}, {"pyzxc_dstream_finished", (PyCFunction)pyzxc_dstream_finished, METH_O, NULL}, {"pyzxc_dstream_in_size", (PyCFunction)pyzxc_dstream_in_size, METH_O, NULL}, {"pyzxc_dstream_out_size", (PyCFunction)pyzxc_dstream_out_size, METH_O, NULL}, {"pyzxc_dstream_free", (PyCFunction)pyzxc_dstream_free, METH_O, NULL}, /* Seekable random-access decompression API. */ {"pyzxc_seekable_open", (PyCFunction)pyzxc_seekable_open, METH_O, NULL}, {"pyzxc_seekable_open_reader", (PyCFunction)pyzxc_seekable_open_reader, METH_O, NULL}, {"pyzxc_seekable_num_blocks", (PyCFunction)pyzxc_seekable_num_blocks, METH_O, NULL}, {"pyzxc_seekable_decompressed_size", (PyCFunction)pyzxc_seekable_decompressed_size, METH_O, NULL}, {"pyzxc_seekable_block_comp_size", (PyCFunction)pyzxc_seekable_block_comp_size, METH_VARARGS, NULL}, {"pyzxc_seekable_block_decomp_size", (PyCFunction)pyzxc_seekable_block_decomp_size, METH_VARARGS, NULL}, {"pyzxc_seekable_decompress_range", (PyCFunction)pyzxc_seekable_decompress_range, METH_VARARGS | METH_KEYWORDS, NULL}, {"pyzxc_seekable_free", (PyCFunction)pyzxc_seekable_free, METH_O, NULL}, {"pyzxc_seekable_set_dict", (PyCFunction)pyzxc_seekable_set_dict, METH_VARARGS, NULL}, {"pyzxc_seek_table_size", (PyCFunction)pyzxc_seek_table_size, METH_O, NULL}, {"pyzxc_write_seek_table", (PyCFunction)pyzxc_write_seek_table, METH_O, NULL}, {NULL, NULL, 0, NULL} // sentinel }; static struct PyModuleDef zxc_module = {PyModuleDef_HEAD_INIT, "_zxc", zxc_doc, 0, zxc_methods}; PyMODINIT_FUNC PyInit__zxc(void) { PyObject* m = PyModule_Create(&zxc_module); if (!m) return NULL; PyModule_AddIntConstant(m, "LEVEL_FASTEST", ZXC_LEVEL_FASTEST); PyModule_AddIntConstant(m, "LEVEL_FAST", ZXC_LEVEL_FAST); PyModule_AddIntConstant(m, "LEVEL_DEFAULT", ZXC_LEVEL_DEFAULT); PyModule_AddIntConstant(m, "LEVEL_BALANCED", ZXC_LEVEL_BALANCED); PyModule_AddIntConstant(m, "LEVEL_COMPACT", ZXC_LEVEL_COMPACT); PyModule_AddIntConstant(m, "LEVEL_DENSITY", ZXC_LEVEL_DENSITY); PyModule_AddIntConstant(m, "LEVEL_ULTRA", ZXC_LEVEL_ULTRA); /* Error Enums */ PyModule_AddIntConstant(m, "ERROR_MEMORY", ZXC_ERROR_MEMORY); PyModule_AddIntConstant(m, "ERROR_DST_TOO_SMALL", ZXC_ERROR_DST_TOO_SMALL); PyModule_AddIntConstant(m, "ERROR_SRC_TOO_SMALL", ZXC_ERROR_SRC_TOO_SMALL); PyModule_AddIntConstant(m, "ERROR_BAD_MAGIC", ZXC_ERROR_BAD_MAGIC); PyModule_AddIntConstant(m, "ERROR_BAD_VERSION", ZXC_ERROR_BAD_VERSION); PyModule_AddIntConstant(m, "ERROR_BAD_HEADER", ZXC_ERROR_BAD_HEADER); PyModule_AddIntConstant(m, "ERROR_BAD_CHECKSUM", ZXC_ERROR_BAD_CHECKSUM); PyModule_AddIntConstant(m, "ERROR_CORRUPT_DATA", ZXC_ERROR_CORRUPT_DATA); PyModule_AddIntConstant(m, "ERROR_BAD_OFFSET", ZXC_ERROR_BAD_OFFSET); PyModule_AddIntConstant(m, "ERROR_OVERFLOW", ZXC_ERROR_OVERFLOW); PyModule_AddIntConstant(m, "ERROR_IO", ZXC_ERROR_IO); PyModule_AddIntConstant(m, "ERROR_NULL_INPUT", ZXC_ERROR_NULL_INPUT); PyModule_AddIntConstant(m, "ERROR_BAD_BLOCK_TYPE", ZXC_ERROR_BAD_BLOCK_TYPE); PyModule_AddIntConstant(m, "ERROR_BAD_BLOCK_SIZE", ZXC_ERROR_BAD_BLOCK_SIZE); PyModule_AddIntConstant(m, "ERROR_DICT_REQUIRED", ZXC_ERROR_DICT_REQUIRED); PyModule_AddIntConstant(m, "ERROR_DICT_MISMATCH", ZXC_ERROR_DICT_MISMATCH); PyModule_AddIntConstant(m, "ERROR_DICT_TOO_LARGE", ZXC_ERROR_DICT_TOO_LARGE); PyModule_AddIntConstant(m, "ERROR_BAD_LEVEL", ZXC_ERROR_BAD_LEVEL); return m; } // ============================================================================= // Functions definitions // ============================================================================= static PyObject* pyzxc_compress(PyObject* self, PyObject* args, PyObject* kwargs) { (void)self; Py_buffer view; int level = ZXC_LEVEL_DEFAULT; int checksum = 0; Py_buffer dict_view = {0}; PyObject* dict_obj = NULL; int have_dict = 0; PyObject* dict_huf_obj = NULL; uint8_t huf_local[ZXC_HUF_TABLE_SIZE]; const void* dict_huf = NULL; static char* kwlist[] = {"data", "level", "checksum", "dict", "dict_huf", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y*|ipOO", kwlist, &view, &level, &checksum, &dict_obj, &dict_huf_obj)) { return NULL; } if (view.itemsize != 1) { PyBuffer_Release(&view); PyErr_SetString(PyExc_TypeError, "expected a byte buffer (itemsize==1)"); return NULL; } if (dict_obj && dict_obj != Py_None) { if (PyObject_GetBuffer(dict_obj, &dict_view, PyBUF_SIMPLE) < 0) { PyBuffer_Release(&view); return NULL; } have_dict = 1; } if (dict_huf_obj && dict_huf_obj != Py_None) { Py_buffer hv; if (PyObject_GetBuffer(dict_huf_obj, &hv, PyBUF_SIMPLE) < 0) { PyBuffer_Release(&view); if (have_dict) PyBuffer_Release(&dict_view); return NULL; } if (hv.len != ZXC_HUF_TABLE_SIZE) { PyBuffer_Release(&hv); PyBuffer_Release(&view); if (have_dict) PyBuffer_Release(&dict_view); PyErr_SetString(PyExc_ValueError, "dict_huf must be exactly 128 bytes"); return NULL; } memcpy(huf_local, hv.buf, ZXC_HUF_TABLE_SIZE); PyBuffer_Release(&hv); dict_huf = huf_local; } size_t src_size = (size_t)view.len; uint64_t bound = zxc_compress_bound(src_size); PyObject* out = PyBytes_FromStringAndSize(NULL, (Py_ssize_t)bound); if (!out) { PyBuffer_Release(&view); if (have_dict) PyBuffer_Release(&dict_view); return NULL; } char* dst = PyBytes_AsString(out); // Return a pointer to the contents int64_t nwritten; // The number of bytes written to dst zxc_compress_opts_t copts = {0}; copts.level = level; copts.checksum_enabled = checksum; if (have_dict) { copts.dict = dict_view.buf; copts.dict_size = (size_t)dict_view.len; copts.dict_huf = dict_huf; } Py_BEGIN_ALLOW_THREADS nwritten = zxc_compress(view.buf, // Source buffer src_size, // Source size dst, // Destination buffer bound, // Destination capacity &copts // Options ); Py_END_ALLOW_THREADS PyBuffer_Release(&view); if (have_dict) PyBuffer_Release(&dict_view); if (nwritten < 0) { Py_DECREF(out); Py_Return_Err(PyExc_RuntimeError, zxc_error_name((int)nwritten)); } if (_PyBytes_Resize(&out, (Py_ssize_t)nwritten) < 0) // Realloc return NULL; return out; } static PyObject* pyzxc_get_decompressed_size(PyObject* self, PyObject* arg) { (void)self; Py_buffer view; if (PyObject_GetBuffer(arg, &view, PyBUF_SIMPLE) < 0) return NULL; uint64_t n = zxc_get_decompressed_size(view.buf, (size_t)view.len); PyBuffer_Release(&view); return Py_BuildValue("K", n); } static PyObject* pyzxc_decompress(PyObject* self, PyObject* args, PyObject* kwargs) { (void)self; Py_buffer view; int checksum = 0; Py_buffer dict_view = {0}; PyObject* dict_obj = NULL; int have_dict = 0; Py_ssize_t decompress_size; PyObject* dict_huf_obj = NULL; uint8_t huf_local[ZXC_HUF_TABLE_SIZE]; const void* dict_huf = NULL; static char* kwlist[] = {"data", "decompress_size", "checksum", "dict", "dict_huf", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y*n|pOO", kwlist, &view, &decompress_size, &checksum, &dict_obj, &dict_huf_obj)) { return NULL; } if (view.itemsize != 1) { PyBuffer_Release(&view); PyErr_SetString(PyExc_TypeError, "expected a byte buffer (itemsize==1)"); return NULL; } if (decompress_size < 0) { PyBuffer_Release(&view); PyErr_SetString(PyExc_ValueError, "decompress_size must be non-negative"); return NULL; } if (dict_obj && dict_obj != Py_None) { if (PyObject_GetBuffer(dict_obj, &dict_view, PyBUF_SIMPLE) < 0) { PyBuffer_Release(&view); return NULL; } have_dict = 1; } if (dict_huf_obj && dict_huf_obj != Py_None) { Py_buffer hv; if (PyObject_GetBuffer(dict_huf_obj, &hv, PyBUF_SIMPLE) < 0) { PyBuffer_Release(&view); if (have_dict) PyBuffer_Release(&dict_view); return NULL; } if (hv.len != ZXC_HUF_TABLE_SIZE) { PyBuffer_Release(&hv); PyBuffer_Release(&view); if (have_dict) PyBuffer_Release(&dict_view); PyErr_SetString(PyExc_ValueError, "dict_huf must be exactly 128 bytes"); return NULL; } memcpy(huf_local, hv.buf, ZXC_HUF_TABLE_SIZE); PyBuffer_Release(&hv); dict_huf = huf_local; } size_t src_size = (size_t)view.len; PyObject* out = PyBytes_FromStringAndSize(NULL, (Py_ssize_t)decompress_size); if (!out) { PyBuffer_Release(&view); if (have_dict) PyBuffer_Release(&dict_view); return NULL; } char* dst = PyBytes_AsString(out); // Return a pointer to the contents int64_t nwritten; // The number of bytes written to dst zxc_decompress_opts_t dopts = {0}; dopts.checksum_enabled = checksum; if (have_dict) { dopts.dict = dict_view.buf; dopts.dict_size = (size_t)dict_view.len; dopts.dict_huf = dict_huf; } Py_BEGIN_ALLOW_THREADS nwritten = zxc_decompress(view.buf, // Source buffer src_size, // Source size dst, // Destination buffer decompress_size, // Destination capacity &dopts // Options ); Py_END_ALLOW_THREADS PyBuffer_Release(&view); if (have_dict) PyBuffer_Release(&dict_view); if (nwritten < 0) { Py_DECREF(out); Py_Return_Err(PyExc_RuntimeError, zxc_error_name((int)nwritten)); } /* decompress_size is an upper bound from the caller; shrink to the bytes * actually written so no uninitialized tail is ever exposed. */ if (nwritten != (int64_t)decompress_size && _PyBytes_Resize(&out, (Py_ssize_t)nwritten) < 0) return NULL; return out; } static PyObject* pyzxc_stream_compress(PyObject* self, PyObject* args, PyObject* kwargs) { (void)self; PyObject *src; PyObject *dst; int nthreads = 0; int level = ZXC_LEVEL_DEFAULT; int checksum = 0; int seekable = 0; Py_buffer dict_view = {0}; PyObject* dict_obj = NULL; int have_dict = 0; static char* kwlist[] = {"src", "dst", "n_threads", "level", "checksum", "seekable", "dict", "dict_huf", NULL}; PyObject* dict_huf_obj = NULL; uint8_t huf_local[ZXC_HUF_TABLE_SIZE]; const void* dict_huf = NULL; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|iippOO", kwlist, &src, &dst, &nthreads, &level, &checksum, &seekable, &dict_obj, &dict_huf_obj)) { return NULL; } if (dict_obj && dict_obj != Py_None) { if (PyObject_GetBuffer(dict_obj, &dict_view, PyBUF_SIMPLE) < 0) return NULL; have_dict = 1; } if (dict_huf_obj && dict_huf_obj != Py_None) { Py_buffer hv; if (PyObject_GetBuffer(dict_huf_obj, &hv, PyBUF_SIMPLE) < 0) { if (have_dict) PyBuffer_Release(&dict_view); return NULL; } if (hv.len != ZXC_HUF_TABLE_SIZE) { PyBuffer_Release(&hv); if (have_dict) PyBuffer_Release(&dict_view); PyErr_SetString(PyExc_ValueError, "dict_huf must be exactly 128 bytes"); return NULL; } memcpy(huf_local, hv.buf, ZXC_HUF_TABLE_SIZE); PyBuffer_Release(&hv); dict_huf = huf_local; } int src_fd = PyObject_AsFileDescriptor(src); int dst_fd = PyObject_AsFileDescriptor(dst); if (src_fd == -1 || dst_fd == -1) { if (have_dict) PyBuffer_Release(&dict_view); Py_Return_Err(PyExc_RuntimeError, "couldn't get file descriptor"); } int src_dup = zxc_dup(src_fd); if (src_dup == -1) { if (have_dict) PyBuffer_Release(&dict_view); Py_Return_Errno(PyExc_OSError); } int dst_dup = zxc_dup(dst_fd); if (dst_dup == -1) { zxc_close(src_dup); if (have_dict) PyBuffer_Release(&dict_view); Py_Return_Errno(PyExc_OSError); } FILE* fsrc = zxc_fdopen(src_dup, "rb"); if (!fsrc) { zxc_close(src_dup); zxc_close(dst_dup); if (have_dict) PyBuffer_Release(&dict_view); Py_Return_Errno(PyExc_OSError); } FILE* fdst = zxc_fdopen(dst_dup, "wb"); if (!fdst) { fclose(fsrc); zxc_close(dst_dup); if (have_dict) PyBuffer_Release(&dict_view); Py_Return_Errno(PyExc_OSError); } int64_t nwritten; zxc_compress_opts_t scopts = {0}; scopts.n_threads = nthreads; scopts.level = level; scopts.checksum_enabled = checksum; scopts.seekable = seekable; if (have_dict) { scopts.dict = dict_view.buf; scopts.dict_size = (size_t)dict_view.len; scopts.dict_huf = dict_huf; } Py_BEGIN_ALLOW_THREADS nwritten = zxc_stream_compress(fsrc, fdst, &scopts); Py_END_ALLOW_THREADS fclose(fdst); fclose(fsrc); if (have_dict) PyBuffer_Release(&dict_view); if (nwritten < 0) Py_Return_Err(PyExc_RuntimeError, zxc_error_name((int)nwritten)); return Py_BuildValue("L", nwritten); } static PyObject* pyzxc_stream_decompress(PyObject* self, PyObject* args, PyObject* kwargs) { (void)self; PyObject *src; PyObject *dst; int nthreads = 0; int checksum = 0; static char* kwlist[] = {"src", "dst", "n_threads", "checksum", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|ip", kwlist, &src, &dst, &nthreads, &checksum)) { return NULL; } int src_fd = PyObject_AsFileDescriptor(src); int dst_fd = PyObject_AsFileDescriptor(dst); if (src_fd == -1 || dst_fd == -1) Py_Return_Err(PyExc_RuntimeError, "couldn't get file descriptor"); int src_dup = zxc_dup(src_fd); if (src_dup == -1) { Py_Return_Errno(PyExc_OSError); } int dst_dup = zxc_dup(dst_fd); if (dst_dup == -1) { zxc_close(src_dup); Py_Return_Errno(PyExc_OSError); } FILE* fsrc = zxc_fdopen(src_dup, "rb"); if (!fsrc) { zxc_close(src_dup); zxc_close(dst_dup); Py_Return_Errno(PyExc_OSError); } FILE* fdst = zxc_fdopen(dst_dup, "wb"); if (!fdst) { fclose(fsrc); zxc_close(dst_dup); Py_Return_Errno(PyExc_OSError); } int64_t nwritten; zxc_decompress_opts_t sdopts = {0}; sdopts.n_threads = nthreads; sdopts.checksum_enabled = checksum; Py_BEGIN_ALLOW_THREADS nwritten = zxc_stream_decompress(fsrc, fdst, &sdopts); Py_END_ALLOW_THREADS fclose(fdst); fclose(fsrc); if (nwritten < 0) Py_Return_Err(PyExc_RuntimeError, zxc_error_name((int)nwritten)); return Py_BuildValue("L", nwritten); } // ============================================================================= // Library Info Helpers // ============================================================================= static PyObject* pyzxc_min_level(PyObject* self, PyObject* args) { (void)self; (void)args; return PyLong_FromLong(zxc_min_level()); } static PyObject* pyzxc_max_level(PyObject* self, PyObject* args) { (void)self; (void)args; return PyLong_FromLong(zxc_max_level()); } static PyObject* pyzxc_default_level(PyObject* self, PyObject* args) { (void)self; (void)args; return PyLong_FromLong(zxc_default_level()); } static PyObject* pyzxc_version_string(PyObject* self, PyObject* args) { (void)self; (void)args; const char* const v = zxc_version_string(); return PyUnicode_FromString(v ? v : ""); } // ============================================================================= // Dictionary API // ============================================================================= /* Train a dictionary from a sequence of sample byte buffers. */ static PyObject* pyzxc_train_dict(PyObject* self, PyObject* args, PyObject* kwargs) { (void)self; PyObject* samples_obj; Py_ssize_t max_size = ZXC_DICT_SIZE_MAX; static char* kwlist[] = {"samples", "max_size", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|n", kwlist, &samples_obj, &max_size)) { return NULL; } if (max_size <= 0 || max_size > ZXC_DICT_SIZE_MAX) { Py_Return_Err(PyExc_ValueError, "max_size must be in (0, ZXC_DICT_SIZE_MAX]"); } PyObject* seq = PySequence_Fast(samples_obj, "samples must be a sequence of bytes-like objects"); if (!seq) return NULL; Py_ssize_t n = PySequence_Fast_GET_SIZE(seq); if (n <= 0) { Py_DECREF(seq); Py_Return_Err(PyExc_ValueError, "samples must be non-empty"); } const void** ptrs = (const void**)PyMem_Malloc(sizeof(void*) * (size_t)n); size_t* sizes = (size_t*)PyMem_Malloc(sizeof(size_t) * (size_t)n); Py_buffer* views = (Py_buffer*)PyMem_Malloc(sizeof(Py_buffer) * (size_t)n); if (!ptrs || !sizes || !views) { PyMem_Free(ptrs); PyMem_Free(sizes); PyMem_Free(views); Py_DECREF(seq); return PyErr_NoMemory(); } Py_ssize_t acquired = 0; for (; acquired < n; acquired++) { PyObject* item = PySequence_Fast_GET_ITEM(seq, acquired); if (PyObject_GetBuffer(item, &views[acquired], PyBUF_SIMPLE) < 0) { break; } ptrs[acquired] = views[acquired].buf; sizes[acquired] = (size_t)views[acquired].len; } if (acquired != n) { for (Py_ssize_t i = 0; i < acquired; i++) PyBuffer_Release(&views[i]); PyMem_Free(ptrs); PyMem_Free(sizes); PyMem_Free(views); Py_DECREF(seq); return NULL; } void* dict_buf = malloc((size_t)max_size); if (!dict_buf) { for (Py_ssize_t i = 0; i < n; i++) PyBuffer_Release(&views[i]); PyMem_Free(ptrs); PyMem_Free(sizes); PyMem_Free(views); Py_DECREF(seq); return PyErr_NoMemory(); } int64_t dict_size; Py_BEGIN_ALLOW_THREADS dict_size = zxc_train_dict(ptrs, sizes, (size_t)n, dict_buf, (size_t)max_size); Py_END_ALLOW_THREADS for (Py_ssize_t i = 0; i < n; i++) PyBuffer_Release(&views[i]); PyMem_Free(ptrs); PyMem_Free(sizes); PyMem_Free(views); Py_DECREF(seq); PyObject* out = NULL; if (dict_size < 0) { PyErr_SetString(PyExc_RuntimeError, zxc_error_name((int)dict_size)); } else { out = PyBytes_FromStringAndSize((const char*)dict_buf, (Py_ssize_t)dict_size); } free(dict_buf); return out; } /* Train the shared literal Huffman table for an already-trained dictionary. */ static PyObject* pyzxc_train_dict_huf(PyObject* self, PyObject* args, PyObject* kwargs) { (void)self; PyObject* samples_obj; Py_buffer dict_view; static char* kwlist[] = {"samples", "dict", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Oy*", kwlist, &samples_obj, &dict_view)) { return NULL; } PyObject* seq = PySequence_Fast(samples_obj, "samples must be a sequence of bytes-like objects"); if (!seq) { PyBuffer_Release(&dict_view); return NULL; } Py_ssize_t n = PySequence_Fast_GET_SIZE(seq); if (n <= 0) { Py_DECREF(seq); PyBuffer_Release(&dict_view); Py_Return_Err(PyExc_ValueError, "samples must be non-empty"); } const void** ptrs = (const void**)PyMem_Malloc(sizeof(void*) * (size_t)n); size_t* sizes = (size_t*)PyMem_Malloc(sizeof(size_t) * (size_t)n); Py_buffer* views = (Py_buffer*)PyMem_Malloc(sizeof(Py_buffer) * (size_t)n); if (!ptrs || !sizes || !views) { PyMem_Free(ptrs); PyMem_Free(sizes); PyMem_Free(views); Py_DECREF(seq); PyBuffer_Release(&dict_view); return PyErr_NoMemory(); } Py_ssize_t acquired = 0; for (; acquired < n; acquired++) { PyObject* item = PySequence_Fast_GET_ITEM(seq, acquired); if (PyObject_GetBuffer(item, &views[acquired], PyBUF_SIMPLE) < 0) { break; } ptrs[acquired] = views[acquired].buf; sizes[acquired] = (size_t)views[acquired].len; } int rc = ZXC_ERROR_NULL_INPUT; uint8_t huf[ZXC_HUF_TABLE_SIZE]; if (acquired == n) { Py_BEGIN_ALLOW_THREADS rc = zxc_train_dict_huf(ptrs, sizes, (size_t)n, dict_view.buf, (size_t)dict_view.len, huf); Py_END_ALLOW_THREADS } for (Py_ssize_t i = 0; i < acquired; i++) PyBuffer_Release(&views[i]); PyMem_Free(ptrs); PyMem_Free(sizes); PyMem_Free(views); Py_DECREF(seq); PyBuffer_Release(&dict_view); if (acquired != n) return NULL; if (rc != ZXC_OK) Py_Return_Err(PyExc_RuntimeError, zxc_error_name(rc)); return PyBytes_FromStringAndSize((const char*)huf, ZXC_HUF_TABLE_SIZE); } /* Dictionary ID from raw dictionary content. */ static PyObject* pyzxc_dict_id(PyObject* self, PyObject* arg) { (void)self; Py_buffer view; if (PyObject_GetBuffer(arg, &view, PyBUF_SIMPLE) < 0) return NULL; uint32_t id = zxc_dict_id(view.buf, (size_t)view.len, NULL); PyBuffer_Release(&view); return PyLong_FromUnsignedLong(id); } /* Dictionary ID required by a .zxc archive (0 if none). */ static PyObject* pyzxc_get_dict_id(PyObject* self, PyObject* arg) { (void)self; Py_buffer view; if (PyObject_GetBuffer(arg, &view, PyBUF_SIMPLE) < 0) return NULL; uint32_t id = zxc_get_dict_id(view.buf, (size_t)view.len); PyBuffer_Release(&view); return PyLong_FromUnsignedLong(id); } /* Dictionary ID stored in a .zxd file (0 if not a valid .zxd). */ static PyObject* pyzxc_dict_get_id(PyObject* self, PyObject* arg) { (void)self; Py_buffer view; if (PyObject_GetBuffer(arg, &view, PyBUF_SIMPLE) < 0) return NULL; uint32_t id = zxc_dict_get_id(view.buf, (size_t)view.len); PyBuffer_Release(&view); return PyLong_FromUnsignedLong(id); } /* Serialize dictionary content + shared Huffman table into the .zxd format. */ static PyObject* pyzxc_dict_save(PyObject* self, PyObject* args) { (void)self; Py_buffer view; Py_buffer huf_view; if (!PyArg_ParseTuple(args, "y*y*", &view, &huf_view)) return NULL; if (huf_view.len != ZXC_HUF_TABLE_SIZE) { PyBuffer_Release(&view); PyBuffer_Release(&huf_view); PyErr_SetString(PyExc_ValueError, "huf_lengths must be exactly 128 bytes"); return NULL; } size_t bound = zxc_dict_save_bound((size_t)view.len); PyObject* out = PyBytes_FromStringAndSize(NULL, (Py_ssize_t)bound); if (!out) { PyBuffer_Release(&view); PyBuffer_Release(&huf_view); return NULL; } int64_t written = zxc_dict_save(view.buf, (size_t)view.len, huf_view.buf, PyBytes_AS_STRING(out), bound); PyBuffer_Release(&view); PyBuffer_Release(&huf_view); if (written < 0) { Py_DECREF(out); Py_Return_Err(PyExc_RuntimeError, zxc_error_name((int)written)); } if ((size_t)written != bound) { if (_PyBytes_Resize(&out, (Py_ssize_t)written) < 0) return NULL; } return out; } /* Parse a .zxd file; returns (content_bytes, dict_id). Content is copied out * because zxc_dict_load returns a pointer INTO the input buffer. */ static PyObject* pyzxc_dict_load(PyObject* self, PyObject* arg) { (void)self; Py_buffer view; if (PyObject_GetBuffer(arg, &view, PyBUF_SIMPLE) < 0) return NULL; const void* content = NULL; size_t content_size = 0; const void* huf = NULL; uint32_t dict_id = 0; int rc = zxc_dict_load(view.buf, (size_t)view.len, &content, &content_size, &huf, &dict_id); if (rc != 0) { PyBuffer_Release(&view); Py_Return_Err(PyExc_RuntimeError, zxc_error_name(rc)); } /* Copy out before releasing the input buffer (zero-copy pointers). */ PyObject* content_obj = PyBytes_FromStringAndSize((const char*)content, (Py_ssize_t)content_size); PyObject* huf_obj = PyBytes_FromStringAndSize((const char*)huf, (Py_ssize_t)ZXC_HUF_TABLE_SIZE); PyBuffer_Release(&view); if (!content_obj || !huf_obj) { Py_XDECREF(content_obj); Py_XDECREF(huf_obj); return NULL; } /* 'N' steals the references (consumed even on failure). */ return Py_BuildValue("(NNk)", content_obj, huf_obj, (unsigned long)dict_id); } /* One-call dictionary creation: samples -> .zxd bytes. */ static PyObject* pyzxc_dict_train(PyObject* self, PyObject* args, PyObject* kwargs) { (void)self; PyObject* samples_obj; static char* kwlist[] = {"samples", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &samples_obj)) { return NULL; } PyObject* seq = PySequence_Fast(samples_obj, "samples must be a sequence of bytes-like objects"); if (!seq) return NULL; Py_ssize_t n = PySequence_Fast_GET_SIZE(seq); if (n <= 0) { Py_DECREF(seq); Py_Return_Err(PyExc_ValueError, "samples must be non-empty"); } const void** ptrs = (const void**)PyMem_Malloc(sizeof(void*) * (size_t)n); size_t* sizes = (size_t*)PyMem_Malloc(sizeof(size_t) * (size_t)n); Py_buffer* views = (Py_buffer*)PyMem_Malloc(sizeof(Py_buffer) * (size_t)n); const size_t zxd_cap = zxc_dict_save_bound(ZXC_DICT_SIZE_MAX); uint8_t* zxd = (uint8_t*)PyMem_Malloc(zxd_cap); if (!ptrs || !sizes || !views || !zxd) { PyMem_Free(ptrs); PyMem_Free(sizes); PyMem_Free(views); PyMem_Free(zxd); Py_DECREF(seq); return PyErr_NoMemory(); } Py_ssize_t acquired = 0; for (; acquired < n; acquired++) { PyObject* item = PySequence_Fast_GET_ITEM(seq, acquired); if (PyObject_GetBuffer(item, &views[acquired], PyBUF_SIMPLE) < 0) { break; } ptrs[acquired] = views[acquired].buf; sizes[acquired] = (size_t)views[acquired].len; } int64_t zxd_sz = ZXC_ERROR_NULL_INPUT; if (acquired == n) { Py_BEGIN_ALLOW_THREADS zxd_sz = zxc_dict_train(ptrs, sizes, (size_t)n, zxd, zxd_cap); Py_END_ALLOW_THREADS } for (Py_ssize_t i = 0; i < acquired; i++) PyBuffer_Release(&views[i]); PyMem_Free(ptrs); PyMem_Free(sizes); PyMem_Free(views); Py_DECREF(seq); PyObject* out = NULL; if (acquired != n) { /* GetBuffer already set the exception. */ } else if (zxd_sz <= 0) { PyErr_SetString(PyExc_RuntimeError, zxc_error_name((int)zxd_sz)); } else { out = PyBytes_FromStringAndSize((const char*)zxd, (Py_ssize_t)zxd_sz); } PyMem_Free(zxd); return out; } /* Shared Huffman table stored in a .zxd buffer, or None when invalid. */ static PyObject* pyzxc_dict_huf(PyObject* self, PyObject* arg) { (void)self; Py_buffer view; if (PyObject_GetBuffer(arg, &view, PyBUF_SIMPLE) < 0) return NULL; const void* huf = zxc_dict_huf(view.buf, (size_t)view.len); PyObject* out = huf ? PyBytes_FromStringAndSize((const char*)huf, ZXC_HUF_TABLE_SIZE) : (Py_INCREF(Py_None), Py_None); PyBuffer_Release(&view); return out; } // ============================================================================= // Push Streaming API (single-threaded, caller-driven) // ============================================================================= // // Streams are exposed as PyCapsule handles. A small Python class in // __init__.py wraps these capsules into idiomatic CStream / DStream // classes; users typically never see the capsules directly. #define ZXC_CSTREAM_CAPSULE "zxc_cstream" #define ZXC_DSTREAM_CAPSULE "zxc_dstream" typedef struct { zxc_cstream* cs; } pyzxc_cstream_holder_t; typedef struct { zxc_dstream* ds; } pyzxc_dstream_holder_t; static void cstream_capsule_destructor(PyObject* capsule) { pyzxc_cstream_holder_t* h = (pyzxc_cstream_holder_t*)PyCapsule_GetPointer(capsule, ZXC_CSTREAM_CAPSULE); if (h) { if (h->cs) zxc_cstream_free(h->cs); PyMem_Free(h); } } static void dstream_capsule_destructor(PyObject* capsule) { pyzxc_dstream_holder_t* h = (pyzxc_dstream_holder_t*)PyCapsule_GetPointer(capsule, ZXC_DSTREAM_CAPSULE); if (h) { if (h->ds) zxc_dstream_free(h->ds); PyMem_Free(h); } } static zxc_cstream* cstream_from_capsule(PyObject* capsule) { pyzxc_cstream_holder_t* h = (pyzxc_cstream_holder_t*)PyCapsule_GetPointer(capsule, ZXC_CSTREAM_CAPSULE); if (!h || !h->cs) { if (!PyErr_Occurred()) PyErr_SetString(PyExc_ValueError, "cstream is closed or invalid"); return NULL; } return h->cs; } static zxc_dstream* dstream_from_capsule(PyObject* capsule) { pyzxc_dstream_holder_t* h = (pyzxc_dstream_holder_t*)PyCapsule_GetPointer(capsule, ZXC_DSTREAM_CAPSULE); if (!h || !h->ds) { if (!PyErr_Occurred()) PyErr_SetString(PyExc_ValueError, "dstream is closed or invalid"); return NULL; } return h->ds; } static PyObject* pyzxc_cstream_create(PyObject* self, PyObject* args, PyObject* kwargs) { (void)self; int level = ZXC_LEVEL_DEFAULT; int checksum = 0; Py_ssize_t block_size = 0; static char* kwlist[] = {"level", "checksum", "block_size", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ipn", kwlist, &level, &checksum, &block_size)) { return NULL; } /* 0 = library default; otherwise must be a power of two in * [ZXC_BLOCK_SIZE_MIN, ZXC_BLOCK_SIZE_MAX]. zxc_cstream_create returns * NULL for invalid values, which must not surface as MemoryError. */ if (block_size != 0 && (block_size < (Py_ssize_t)ZXC_BLOCK_SIZE_MIN || block_size > (Py_ssize_t)ZXC_BLOCK_SIZE_MAX || (block_size & (block_size - 1)) != 0)) { PyErr_Format(PyExc_ValueError, "block_size must be 0 (default) or a power of two in [%u, %u], got %zd", ZXC_BLOCK_SIZE_MIN, ZXC_BLOCK_SIZE_MAX, block_size); return NULL; } zxc_compress_opts_t copts = {0}; copts.level = level; copts.checksum_enabled = checksum; copts.block_size = (size_t)block_size; zxc_cstream* cs = zxc_cstream_create(&copts); if (!cs) Py_Return_Err(PyExc_MemoryError, "zxc_cstream_create failed"); pyzxc_cstream_holder_t* h = (pyzxc_cstream_holder_t*)PyMem_Malloc(sizeof(*h)); if (!h) { zxc_cstream_free(cs); return PyErr_NoMemory(); } h->cs = cs; PyObject* cap = PyCapsule_New(h, ZXC_CSTREAM_CAPSULE, cstream_capsule_destructor); if (!cap) { zxc_cstream_free(cs); PyMem_Free(h); return NULL; } return cap; } /* Drains the cstream by calling zxc_cstream_compress repeatedly until the * input is fully consumed and no compressed bytes remain pending. Returns * the accumulated compressed bytes. */ static PyObject* pyzxc_cstream_compress(PyObject* self, PyObject* args, PyObject* kwargs) { (void)self; PyObject* capsule; Py_buffer view; static char* kwlist[] = {"cs", "data", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Oy*", kwlist, &capsule, &view)) { return NULL; } zxc_cstream* cs = cstream_from_capsule(capsule); if (!cs) { PyBuffer_Release(&view); return NULL; } /* Output buffer grows on demand. Start at the suggested chunk size. */ size_t out_cap = zxc_cstream_out_size(cs); if (out_cap < 4096) out_cap = 4096; size_t out_len = 0; uint8_t* out_buf = (uint8_t*)malloc(out_cap); if (!out_buf) { PyBuffer_Release(&view); return PyErr_NoMemory(); } zxc_inbuf_t in = {.src = view.buf, .size = (size_t)view.len, .pos = 0}; int err_code = 0; int oom = 0; int overflow = 0; Py_BEGIN_ALLOW_THREADS for (;;) { /* Make sure there's room to write at least one block worth. */ size_t want = zxc_cstream_out_size(cs); if (want < 4096) want = 4096; if (out_cap - out_len < want) { int rc = grow_output(&out_buf, &out_cap, out_len, want); if (rc == -1) { overflow = 1; break; } if (rc == -2) { oom = 1; break; } } zxc_outbuf_t out = {.dst = out_buf + out_len, .size = out_cap - out_len, .pos = 0}; const int64_t r = zxc_cstream_compress(cs, &out, &in); out_len += out.pos; if (r < 0) { err_code = (int)r; break; } if (r == 0 && in.pos == in.size) break; /* fully drained */ } Py_END_ALLOW_THREADS PyBuffer_Release(&view); if (oom) PyErr_NoMemory(); else if (overflow) PyErr_SetString(PyExc_OverflowError, "compressed output exceeds PY_SSIZE_T_MAX"); else if (err_code) PyErr_SetString(PyExc_RuntimeError, zxc_error_name(err_code)); if (PyErr_Occurred()) { free(out_buf); return NULL; } PyObject* result = PyBytes_FromStringAndSize((const char*)out_buf, (Py_ssize_t)out_len); free(out_buf); return result; } /* Drains the cstream's finalisation phase: residual block + EOF + footer. */ static PyObject* pyzxc_cstream_end(PyObject* self, PyObject* args, PyObject* kwargs) { (void)self; PyObject* capsule; static char* kwlist[] = {"cs", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &capsule)) return NULL; zxc_cstream* cs = cstream_from_capsule(capsule); if (!cs) return NULL; size_t out_cap = zxc_cstream_out_size(cs); if (out_cap < 4096) out_cap = 4096; size_t out_len = 0; uint8_t* out_buf = (uint8_t*)malloc(out_cap); if (!out_buf) return PyErr_NoMemory(); int err_code = 0; int oom = 0; int overflow = 0; Py_BEGIN_ALLOW_THREADS for (;;) { if (out_cap - out_len < 4096) { int rc = grow_output(&out_buf, &out_cap, out_len, 4096); if (rc == -1) { overflow = 1; break; } if (rc == -2) { oom = 1; break; } } zxc_outbuf_t out = {.dst = out_buf + out_len, .size = out_cap - out_len, .pos = 0}; const int64_t r = zxc_cstream_end(cs, &out); out_len += out.pos; if (r < 0) { err_code = (int)r; break; } if (r == 0) break; } Py_END_ALLOW_THREADS if (oom) PyErr_NoMemory(); else if (overflow) PyErr_SetString(PyExc_OverflowError, "compressed output exceeds PY_SSIZE_T_MAX"); else if (err_code) PyErr_SetString(PyExc_RuntimeError, zxc_error_name(err_code)); if (PyErr_Occurred()) { free(out_buf); return NULL; } PyObject* result = PyBytes_FromStringAndSize((const char*)out_buf, (Py_ssize_t)out_len); free(out_buf); return result; } static PyObject* pyzxc_cstream_in_size(PyObject* self, PyObject* capsule) { (void)self; zxc_cstream* cs = cstream_from_capsule(capsule); if (!cs) return NULL; return PyLong_FromSize_t(zxc_cstream_in_size(cs)); } static PyObject* pyzxc_cstream_out_size(PyObject* self, PyObject* capsule) { (void)self; zxc_cstream* cs = cstream_from_capsule(capsule); if (!cs) return NULL; return PyLong_FromSize_t(zxc_cstream_out_size(cs)); } static PyObject* pyzxc_cstream_free(PyObject* self, PyObject* capsule) { (void)self; if (!PyCapsule_IsValid(capsule, ZXC_CSTREAM_CAPSULE)) Py_RETURN_NONE; pyzxc_cstream_holder_t* h = (pyzxc_cstream_holder_t*)PyCapsule_GetPointer(capsule, ZXC_CSTREAM_CAPSULE); if (h && h->cs) { zxc_cstream_free(h->cs); h->cs = NULL; } Py_RETURN_NONE; } static PyObject* pyzxc_dstream_create(PyObject* self, PyObject* args, PyObject* kwargs) { (void)self; int checksum = 0; static char* kwlist[] = {"checksum", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|p", kwlist, &checksum)) return NULL; zxc_decompress_opts_t dopts = {0}; dopts.checksum_enabled = checksum; zxc_dstream* ds = zxc_dstream_create(&dopts); if (!ds) Py_Return_Err(PyExc_MemoryError, "zxc_dstream_create failed"); pyzxc_dstream_holder_t* h = (pyzxc_dstream_holder_t*)PyMem_Malloc(sizeof(*h)); if (!h) { zxc_dstream_free(ds); return PyErr_NoMemory(); } h->ds = ds; PyObject* cap = PyCapsule_New(h, ZXC_DSTREAM_CAPSULE, dstream_capsule_destructor); if (!cap) { zxc_dstream_free(ds); PyMem_Free(h); return NULL; } return cap; } static PyObject* pyzxc_dstream_decompress(PyObject* self, PyObject* args, PyObject* kwargs) { (void)self; PyObject* capsule; Py_buffer view; static char* kwlist[] = {"ds", "data", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Oy*", kwlist, &capsule, &view)) { return NULL; } zxc_dstream* ds = dstream_from_capsule(capsule); if (!ds) { PyBuffer_Release(&view); return NULL; } size_t out_cap = zxc_dstream_out_size(ds); if (out_cap < 4096) out_cap = 4096; size_t out_len = 0; uint8_t* out_buf = (uint8_t*)malloc(out_cap); if (!out_buf) { PyBuffer_Release(&view); return PyErr_NoMemory(); } zxc_inbuf_t in = {.src = view.buf, .size = (size_t)view.len, .pos = 0}; int err_code = 0; int oom = 0; int overflow = 0; Py_BEGIN_ALLOW_THREADS for (;;) { size_t want = zxc_dstream_out_size(ds); if (want < 4096) want = 4096; if (out_cap - out_len < want) { int rc = grow_output(&out_buf, &out_cap, out_len, want); if (rc == -1) { overflow = 1; break; } if (rc == -2) { oom = 1; break; } } zxc_outbuf_t out = {.dst = out_buf + out_len, .size = out_cap - out_len, .pos = 0}; zxc_inbuf_t empty_in = {.src = NULL, .size = 0, .pos = 0}; zxc_inbuf_t* cur_in = (in.pos < in.size) ? &in : &empty_in; const size_t before_in = cur_in->pos; const size_t before_out = out.pos; const int64_t r = zxc_dstream_decompress(ds, &out, cur_in); out_len += out.pos; if (r < 0) { err_code = (int)r; break; } /* Keep draining even after input is exhausted; stop only when no * progress was made (no input consumed AND no output produced). */ if (cur_in->pos == before_in && out.pos == before_out) break; } Py_END_ALLOW_THREADS PyBuffer_Release(&view); if (oom) PyErr_NoMemory(); else if (overflow) PyErr_SetString(PyExc_OverflowError, "decompressed output exceeds PY_SSIZE_T_MAX"); else if (err_code) PyErr_SetString(PyExc_RuntimeError, zxc_error_name(err_code)); if (PyErr_Occurred()) { free(out_buf); return NULL; } PyObject* result = PyBytes_FromStringAndSize((const char*)out_buf, (Py_ssize_t)out_len); free(out_buf); return result; } static PyObject* pyzxc_dstream_finished(PyObject* self, PyObject* capsule) { (void)self; zxc_dstream* ds = dstream_from_capsule(capsule); if (!ds) return NULL; return PyBool_FromLong(zxc_dstream_finished(ds)); } static PyObject* pyzxc_dstream_in_size(PyObject* self, PyObject* capsule) { (void)self; zxc_dstream* ds = dstream_from_capsule(capsule); if (!ds) return NULL; return PyLong_FromSize_t(zxc_dstream_in_size(ds)); } static PyObject* pyzxc_dstream_out_size(PyObject* self, PyObject* capsule) { (void)self; zxc_dstream* ds = dstream_from_capsule(capsule); if (!ds) return NULL; return PyLong_FromSize_t(zxc_dstream_out_size(ds)); } static PyObject* pyzxc_dstream_free(PyObject* self, PyObject* capsule) { (void)self; if (!PyCapsule_IsValid(capsule, ZXC_DSTREAM_CAPSULE)) Py_RETURN_NONE; pyzxc_dstream_holder_t* h = (pyzxc_dstream_holder_t*)PyCapsule_GetPointer(capsule, ZXC_DSTREAM_CAPSULE); if (h && h->ds) { zxc_dstream_free(h->ds); h->ds = NULL; } Py_RETURN_NONE; } // ============================================================================= // Seekable API (random-access decompression) // ============================================================================= #define ZXC_SEEKABLE_CAPSULE "zxc_seekable" typedef struct { zxc_seekable* s; Py_buffer src_view; /* held view on the source when opened from bytes: pins the exporter for the handle's lifetime so no copy of the archive is needed */ int has_view; /* 1 when src_view must be released */ PyObject* reader_obj; /* strong ref to the Python reader when using callback */ /* First exception raised by the reader callback, stashed under the GIL by * the trampoline (which may run on a library worker thread) and re-raised * on the calling thread once the C call returns. */ PyObject* exc_type; PyObject* exc_value; PyObject* exc_tb; } pyzxc_seekable_holder_t; /* Stores the currently-raised exception into the holder (first one wins) so * the user's traceback survives the round-trip through the C library. * GIL must be held. */ static void seekable_stash_exception(pyzxc_seekable_holder_t* h) { if (h->exc_type) { PyErr_Clear(); return; } PyErr_Fetch(&h->exc_type, &h->exc_value, &h->exc_tb); } /* Re-raises a stashed exception (if any) and clears the slots. * GIL must be held. Returns 1 when an exception was restored. */ static int seekable_restore_exception(pyzxc_seekable_holder_t* h) { if (!h->exc_type) return 0; PyErr_Restore(h->exc_type, h->exc_value, h->exc_tb); h->exc_type = NULL; h->exc_value = NULL; h->exc_tb = NULL; return 1; } static void seekable_capsule_destructor(PyObject* capsule) { pyzxc_seekable_holder_t* h = (pyzxc_seekable_holder_t*)PyCapsule_GetPointer(capsule, ZXC_SEEKABLE_CAPSULE); if (h) { if (h->s) zxc_seekable_free(h->s); if (h->has_view) PyBuffer_Release(&h->src_view); Py_XDECREF(h->reader_obj); Py_XDECREF(h->exc_type); Py_XDECREF(h->exc_value); Py_XDECREF(h->exc_tb); PyMem_Free(h); } } static zxc_seekable* seekable_from_capsule(PyObject* capsule) { pyzxc_seekable_holder_t* h = (pyzxc_seekable_holder_t*)PyCapsule_GetPointer(capsule, ZXC_SEEKABLE_CAPSULE); if (!h || !h->s) { if (!PyErr_Occurred()) PyErr_SetString(PyExc_ValueError, "Seekable is closed or invalid"); return NULL; } return h->s; } static PyObject* pyzxc_seekable_open(PyObject* self, PyObject* arg) { (void)self; Py_buffer view; if (PyObject_GetBuffer(arg, &view, PyBUF_SIMPLE) < 0) return NULL; if (view.len == 0) { PyBuffer_Release(&view); Py_Return_Err(PyExc_ValueError, "seekable buffer must be non-empty"); } /* The view pins the exporter (and, for a bytearray, blocks resizing) * for the handle's lifetime, so the library can reference the caller's * buffer directly: no copy of the archive. */ zxc_seekable* s; Py_BEGIN_ALLOW_THREADS s = zxc_seekable_open(view.buf, (size_t)view.len); Py_END_ALLOW_THREADS if (!s) { PyBuffer_Release(&view); Py_Return_Err(PyExc_RuntimeError, "zxc_seekable_open failed (not a valid seekable archive)"); } pyzxc_seekable_holder_t* h = (pyzxc_seekable_holder_t*)PyMem_Malloc(sizeof(*h)); if (!h) { zxc_seekable_free(s); PyBuffer_Release(&view); return PyErr_NoMemory(); } h->s = s; h->src_view = view; h->has_view = 1; h->reader_obj = NULL; h->exc_type = NULL; h->exc_value = NULL; h->exc_tb = NULL; PyObject* cap = PyCapsule_New(h, ZXC_SEEKABLE_CAPSULE, seekable_capsule_destructor); if (!cap) { zxc_seekable_free(s); PyBuffer_Release(&h->src_view); PyMem_Free(h); return NULL; } return cap; } /* C trampoline for the Python reader callback. May be invoked from the * calling thread (single-threaded decode, with or without the GIL held) or * from library-spawned worker threads (multi-threaded decode), so it always * attaches to the interpreter with PyGILState_Ensure. A failing callback * stashes its exception into the holder for re-raise by the caller. */ static int64_t seekable_read_at_trampoline(void* ctx, void* dst, size_t len, uint64_t offset) { pyzxc_seekable_holder_t* h = (pyzxc_seekable_holder_t*)ctx; PyGILState_STATE gstate = PyGILState_Ensure(); int64_t rc = ZXC_ERROR_IO; PyObject* result = PyObject_CallMethod(h->reader_obj, "read_at", "nK", (Py_ssize_t)len, (unsigned long long)offset); if (!result) { seekable_stash_exception(h); goto done; } Py_buffer view; if (PyObject_GetBuffer(result, &view, PyBUF_SIMPLE) < 0) { Py_DECREF(result); PyErr_Clear(); goto done; } if ((size_t)view.len < len) { PyBuffer_Release(&view); Py_DECREF(result); goto done; } memcpy(dst, view.buf, len); PyBuffer_Release(&view); Py_DECREF(result); rc = (int64_t)len; done: PyGILState_Release(gstate); return rc; } static PyObject* pyzxc_seekable_open_reader(PyObject* self, PyObject* arg) { (void)self; if (!PyObject_HasAttrString(arg, "size") || !PyObject_HasAttrString(arg, "read_at")) { Py_Return_Err(PyExc_TypeError, "reader must have 'size' (int) and 'read_at' (callable)"); } PyObject* size_obj = PyObject_GetAttrString(arg, "size"); if (!size_obj) return NULL; long long sz = PyLong_AsLongLong(size_obj); Py_DECREF(size_obj); if (sz == -1 && PyErr_Occurred()) return NULL; if (sz <= 0) Py_Return_Err(PyExc_ValueError, "reader.size must be > 0"); /* The holder is created before the open call because it doubles as the * trampoline context (reader object + stashed-exception slots). */ pyzxc_seekable_holder_t* h = (pyzxc_seekable_holder_t*)PyMem_Malloc(sizeof(*h)); if (!h) return PyErr_NoMemory(); h->s = NULL; h->has_view = 0; Py_INCREF(arg); h->reader_obj = arg; h->exc_type = NULL; h->exc_value = NULL; h->exc_tb = NULL; zxc_reader_t r; r.read_at = seekable_read_at_trampoline; r.ctx = h; r.size = (uint64_t)sz; zxc_seekable* s = zxc_seekable_open_reader(&r); if (!s) { int restored = seekable_restore_exception(h); Py_DECREF(arg); PyMem_Free(h); if (restored) return NULL; /* re-raise the reader's own exception */ Py_Return_Err(PyExc_RuntimeError, "zxc_seekable_open_reader failed (not a valid seekable archive)"); } h->s = s; PyObject* cap = PyCapsule_New(h, ZXC_SEEKABLE_CAPSULE, seekable_capsule_destructor); if (!cap) { zxc_seekable_free(s); Py_DECREF(arg); PyMem_Free(h); return NULL; } return cap; } static PyObject* pyzxc_seekable_num_blocks(PyObject* self, PyObject* capsule) { (void)self; zxc_seekable* s = seekable_from_capsule(capsule); if (!s) return NULL; return PyLong_FromUnsignedLong(zxc_seekable_get_num_blocks(s)); } static PyObject* pyzxc_seekable_decompressed_size(PyObject* self, PyObject* capsule) { (void)self; zxc_seekable* s = seekable_from_capsule(capsule); if (!s) return NULL; return PyLong_FromUnsignedLongLong(zxc_seekable_get_decompressed_size(s)); } static PyObject* pyzxc_seekable_block_comp_size(PyObject* self, PyObject* args) { (void)self; PyObject* capsule; unsigned int idx; if (!PyArg_ParseTuple(args, "OI", &capsule, &idx)) return NULL; zxc_seekable* s = seekable_from_capsule(capsule); if (!s) return NULL; if (idx >= zxc_seekable_get_num_blocks(s)) Py_RETURN_NONE; return PyLong_FromUnsignedLong(zxc_seekable_get_block_comp_size(s, idx)); } static PyObject* pyzxc_seekable_block_decomp_size(PyObject* self, PyObject* args) { (void)self; PyObject* capsule; unsigned int idx; if (!PyArg_ParseTuple(args, "OI", &capsule, &idx)) return NULL; zxc_seekable* s = seekable_from_capsule(capsule); if (!s) return NULL; if (idx >= zxc_seekable_get_num_blocks(s)) Py_RETURN_NONE; return PyLong_FromUnsignedLong(zxc_seekable_get_block_decomp_size(s, idx)); } static PyObject* pyzxc_seekable_decompress_range(PyObject* self, PyObject* args, PyObject* kwargs) { (void)self; PyObject* capsule; unsigned long long offset; Py_ssize_t length; int n_threads = 0; static char* kwlist[] = {"handle", "offset", "length", "n_threads", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OKn|i", kwlist, &capsule, &offset, &length, &n_threads)) { return NULL; } if (length < 0) Py_Return_Err(PyExc_ValueError, "length must be non-negative"); zxc_seekable* s = seekable_from_capsule(capsule); if (!s) return NULL; if (length == 0) return PyBytes_FromStringAndSize(NULL, 0); PyObject* out = PyBytes_FromStringAndSize(NULL, length); if (!out) return NULL; char* dst = PyBytes_AS_STRING(out); int64_t r; pyzxc_seekable_holder_t* h = (pyzxc_seekable_holder_t*)PyCapsule_GetPointer(capsule, ZXC_SEEKABLE_CAPSULE); /* The GIL is released on every path: the reader trampoline re-attaches * with PyGILState_Ensure, which also makes the multi-threaded decode safe * when the library invokes the Python callback from its worker threads * (holding the GIL here would let workers call into Python without it). */ Py_BEGIN_ALLOW_THREADS if (n_threads > 1) { r = zxc_seekable_decompress_range_mt(s, dst, (size_t)length, (uint64_t)offset, (size_t)length, n_threads); } else { r = zxc_seekable_decompress_range(s, dst, (size_t)length, (uint64_t)offset, (size_t)length); } Py_END_ALLOW_THREADS if (r < 0) { Py_DECREF(out); /* Prefer the reader's own exception (with traceback) when it caused * the failure; otherwise report the library error code. */ if (h && seekable_restore_exception(h)) return NULL; Py_Return_Err(PyExc_RuntimeError, zxc_error_name((int)r)); } /* A callback may have failed on one worker while another satisfied the * range; drop any stale stashed exception so it cannot leak into an * unrelated later call. */ if (h && h->exc_type) { Py_CLEAR(h->exc_type); Py_CLEAR(h->exc_value); Py_CLEAR(h->exc_tb); } if (r != (int64_t)length && _PyBytes_Resize(&out, (Py_ssize_t)r) < 0) return NULL; return out; } static PyObject* pyzxc_seekable_free(PyObject* self, PyObject* capsule) { (void)self; if (!PyCapsule_IsValid(capsule, ZXC_SEEKABLE_CAPSULE)) Py_RETURN_NONE; pyzxc_seekable_holder_t* h = (pyzxc_seekable_holder_t*)PyCapsule_GetPointer(capsule, ZXC_SEEKABLE_CAPSULE); if (h && h->s) { zxc_seekable_free(h->s); h->s = NULL; } if (h && h->has_view) { PyBuffer_Release(&h->src_view); h->has_view = 0; } if (h && h->reader_obj) { Py_DECREF(h->reader_obj); h->reader_obj = NULL; } Py_RETURN_NONE; } static PyObject* pyzxc_seekable_set_dict(PyObject* self, PyObject* args) { (void)self; PyObject* capsule; Py_buffer view; PyObject* dict_huf_obj = NULL; if (!PyArg_ParseTuple(args, "Oy*|O", &capsule, &view, &dict_huf_obj)) return NULL; zxc_seekable* s = seekable_from_capsule(capsule); if (!s) { PyBuffer_Release(&view); return NULL; } uint8_t huf_local[ZXC_HUF_TABLE_SIZE]; const void* dict_huf = NULL; if (dict_huf_obj && dict_huf_obj != Py_None) { Py_buffer hv; if (PyObject_GetBuffer(dict_huf_obj, &hv, PyBUF_SIMPLE) < 0) { PyBuffer_Release(&view); return NULL; } if (hv.len != ZXC_HUF_TABLE_SIZE) { PyBuffer_Release(&hv); PyBuffer_Release(&view); PyErr_SetString(PyExc_ValueError, "dict_huf must be exactly 128 bytes"); return NULL; } memcpy(huf_local, hv.buf, ZXC_HUF_TABLE_SIZE); PyBuffer_Release(&hv); dict_huf = huf_local; } int rc = zxc_seekable_set_dict(s, view.buf, (size_t)view.len, dict_huf); PyBuffer_Release(&view); if (rc != 0) Py_Return_Err(PyExc_RuntimeError, zxc_error_name(rc)); Py_RETURN_NONE; } static PyObject* pyzxc_seek_table_size(PyObject* self, PyObject* arg) { (void)self; unsigned int num_blocks = (unsigned int)PyLong_AsUnsignedLong(arg); if (num_blocks == (unsigned int)-1 && PyErr_Occurred()) return NULL; return PyLong_FromSize_t(zxc_seek_table_size(num_blocks)); } static PyObject* pyzxc_write_seek_table(PyObject* self, PyObject* arg) { (void)self; if (!PyList_Check(arg)) Py_Return_Err(PyExc_TypeError, "expected a list of integers"); Py_ssize_t n = PyList_GET_SIZE(arg); if (n <= 0) Py_Return_Err(PyExc_ValueError, "comp_sizes must be non-empty"); uint32_t* sizes = (uint32_t*)PyMem_Malloc(sizeof(uint32_t) * (size_t)n); if (!sizes) return PyErr_NoMemory(); for (Py_ssize_t i = 0; i < n; i++) { unsigned long v = PyLong_AsUnsignedLong(PyList_GET_ITEM(arg, i)); if (v == (unsigned long)-1 && PyErr_Occurred()) { PyMem_Free(sizes); return NULL; } sizes[i] = (uint32_t)v; } size_t cap = zxc_seek_table_size((uint32_t)n); PyObject* out = PyBytes_FromStringAndSize(NULL, (Py_ssize_t)cap); if (!out) { PyMem_Free(sizes); return NULL; } int64_t written = zxc_write_seek_table((uint8_t*)PyBytes_AS_STRING(out), cap, sizes, (uint32_t)n); PyMem_Free(sizes); if (written < 0) { Py_DECREF(out); Py_Return_Err(PyExc_RuntimeError, zxc_error_name((int)written)); } if ((size_t)written != cap && _PyBytes_Resize(&out, (Py_ssize_t)written) < 0) return NULL; return out; } zxc-0.13.0/wrappers/python/tests/000077500000000000000000000000001522536370400167345ustar00rootroot00000000000000zxc-0.13.0/wrappers/python/tests/test_buffer.py000066400000000000000000000030561522536370400216220ustar00rootroot00000000000000""" ZXC - High-performance lossless compression Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. SPDX-License-Identifier: BSD-3-Clause """ import pytest import zxc @pytest.mark.parametrize("data", [ None, "string", 123, 12.5, object(), ]) def test_compress_invalid_type(data): with pytest.raises(TypeError): zxc.compress(data) @pytest.mark.parametrize( "data,corrupt_func,exc", [ (b"hello world" * 10, lambda x: x[:-1] + b"\x01", RuntimeError), (b"a" * 10, lambda x: b"", RuntimeError), ], ids=["corrupted_data", "invalid_header"], ) def test_compress_corruption(data, corrupt_func, exc): compressed = zxc.compress(data, checksum=True) corrupted = corrupt_func(compressed) with pytest.raises(exc): zxc.decompress(corrupted, len(data), checksum=True) @pytest.mark.parametrize( "data", [ b"hello world" * 10, # default b"a", # single byte b"", b"a" * 10_000_000, # large data ], ids=[ "normal_data", "single_byte", "empty", "large_10mb", ], ) def test_compress_roundtrip(data): # test all compression levels (1..LEVEL_ULTRA) for level in range(zxc.LEVEL_FASTEST, zxc.LEVEL_ULTRA + 1): compressed = zxc.compress(data, level) out_size = zxc.get_decompressed_size(compressed) decompressed = zxc.decompress(compressed, out_size) assert len(data) == len(decompressed) assert data == decompressed zxc-0.13.0/wrappers/python/tests/test_dict.py000066400000000000000000000150261522536370400212740ustar00rootroot00000000000000""" ZXC - High-performance lossless compression Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. SPDX-License-Identifier: BSD-3-Clause Tests for pre-trained dictionary support. """ import pytest import zxc def make_samples(n=6): """A small corpus of similar records — the kind of data dictionaries help.""" return [ b'{"id": %d, "type": "event", "user": "alice", "action": "login"}' % i for i in range(n) ] def make_payload(n=4000): return b"".join( b'{"id": %d, "type": "event", "user": "alice", "action": "click"}\n' % i for i in range(n) ) @pytest.fixture def trained_dict(): return zxc.train_dict(make_samples()) # ========================================================================= # Training # ========================================================================= class TestTrainDict: def test_returns_non_empty_bytes(self): d = zxc.train_dict(make_samples()) assert isinstance(d, bytes) assert len(d) > 0 assert len(d) <= 65535 def test_respects_max_size(self): d = zxc.train_dict(make_samples(), max_size=128) assert len(d) <= 128 def test_rejects_empty_samples(self): with pytest.raises(ValueError): zxc.train_dict([]) def test_rejects_bad_max_size(self): with pytest.raises(ValueError): zxc.train_dict(make_samples(), max_size=0) with pytest.raises(ValueError): zxc.train_dict(make_samples(), max_size=70000) # ========================================================================= # Compress / decompress with a dictionary # ========================================================================= class TestCompressWithDict: def test_roundtrip_with_dict(self, trained_dict): payload = make_payload() arch = zxc.compress(payload, dict=trained_dict, checksum=True) out = zxc.decompress(arch, dict=trained_dict, checksum=True) assert out == payload def test_roundtrip_with_dict_no_size_hint(self, trained_dict): payload = make_payload() arch = zxc.compress(payload, dict=trained_dict) # decompress_size=None -> read from header out = zxc.decompress(arch, dict=trained_dict) assert out == payload def test_decompress_without_dict_raises(self, trained_dict): payload = make_payload() arch = zxc.compress(payload, dict=trained_dict) with pytest.raises(RuntimeError): zxc.decompress(arch, decompress_size=len(payload)) def test_no_dict_still_works(self): payload = make_payload() arch = zxc.compress(payload) assert zxc.get_dict_id(arch) == 0 assert zxc.decompress(arch) == payload # ========================================================================= # Dictionary IDs # ========================================================================= class TestDictId: def test_ids_agree_across_surfaces(self, trained_dict): payload = make_payload() arch = zxc.compress(payload, dict=trained_dict) huf = zxc.train_dict_huf(make_samples(), trained_dict) zxd = zxc.dict_save(trained_dict, huf) cid = zxc.dict_id(trained_dict) assert cid != 0 assert zxc.get_dict_id(arch) == cid # The .zxd id binds (content, table): non-zero and distinct from the # content-only id. assert zxc.dict_get_id(zxd) != 0 assert zxc.dict_get_id(zxd) != cid assert zxc.dict_huf(zxd) == huf def test_id_from_first_16_bytes(self, trained_dict): payload = make_payload() arch = zxc.compress(payload, dict=trained_dict) assert zxc.get_dict_id(arch[:16]) == zxc.dict_id(trained_dict) def test_empty_content_id_is_zero(self): assert zxc.dict_id(b"") == 0 def test_dict_get_id_invalid_is_zero(self): assert zxc.dict_get_id(b"not a zxd file") == 0 # ========================================================================= # Save / load roundtrip # ========================================================================= class TestSaveLoad: def test_save_load_roundtrip(self, trained_dict): huf = zxc.train_dict_huf(make_samples(), trained_dict) zxd = zxc.dict_save(trained_dict, huf) assert isinstance(zxd, bytes) assert len(zxd) == 16 + len(trained_dict) + 128 # zxc_dict_save_bound content, dict_id = zxc.dict_load(zxd) assert content == trained_dict assert dict_id == zxc.dict_get_id(zxd) def test_loaded_content_compresses(self, trained_dict): huf = zxc.train_dict_huf(make_samples(), trained_dict) zxd = zxc.dict_save(trained_dict, huf) content, _ = zxc.dict_load(zxd) payload = make_payload() arch = zxc.compress(payload, dict=content) assert zxc.decompress(arch, dict=content) == payload def test_load_invalid_raises(self): with pytest.raises(RuntimeError): zxc.dict_load(b"garbage not a zxd") # ========================================================================= # Seekable with a dictionary # ========================================================================= class TestSeekableDict: def _build(self, payload, dict_content, tmp_path, level=zxc.LEVEL_DEFAULT): src = tmp_path / "src.bin" dst = tmp_path / "dst.zxc" src.write_bytes(payload) with open(src, "rb") as fsrc, open(dst, "wb") as fdst: zxc.stream_compress( fsrc, fdst, level=level, checksum=True, seekable=True, dict=dict_content, ) return dst.read_bytes() def test_seekable_range_with_dict(self, trained_dict, tmp_path): payload = make_payload() arch = self._build(payload, trained_dict, tmp_path) assert zxc.get_dict_id(arch) == zxc.dict_id(trained_dict) with zxc.Seekable(arch) as s: s.set_dict(trained_dict) assert s.decompressed_size == len(payload) # mid-range slice chunk = s.decompress_range(500, 1234) assert chunk == payload[500:500 + 1234] # full range full = s.decompress_range(0, s.decompressed_size) assert full == payload def test_set_dict_after_close_raises(self, trained_dict, tmp_path): payload = make_payload() arch = self._build(payload, trained_dict, tmp_path) s = zxc.Seekable(arch) s.close() with pytest.raises(ValueError): s.set_dict(trained_dict) zxc-0.13.0/wrappers/python/tests/test_io_adapters.py000066400000000000000000000106521522536370400226430ustar00rootroot00000000000000""" ZXC - High-performance lossless compression Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. SPDX-License-Identifier: BSD-3-Clause Tests for the io.RawIOBase adapters (zxc.ZxcReader / zxc.ZxcWriter) and the zxc.detect_zxc helper. """ import io import pytest import zxc def _roundtrip(data: bytes, *, level=zxc.LEVEL_DEFAULT, checksum=False) -> bytes: sink = io.BytesIO() with zxc.ZxcWriter(sink, level=level, checksum=checksum) as w: w.write(data) sink.seek(0) with zxc.ZxcReader(sink, checksum=checksum) as r: return r.read() def test_writer_reader_small_roundtrip(): data = b"Hello io.RawIOBase bridge over ZXC!" assert _roundtrip(data) == data def test_writer_reader_large_roundtrip(): data = bytes(((i * 13) % 251 for i in range(2 * 1024 * 1024))) assert _roundtrip(data) == data def test_writer_many_small_writes(): sink = io.BytesIO() want = bytearray() with zxc.ZxcWriter(sink) as w: for i in range(4096): chunk = bytes((i & 0xFF, (i >> 8) & 0xFF, (i ^ 0x5A) & 0xFF)) want.extend(chunk) w.write(chunk) sink.seek(0) with zxc.ZxcReader(sink) as r: got = r.read() assert got == bytes(want) def test_writer_reader_with_checksum(): data = bytes((i % 251 for i in range(32 * 1024))) assert _roundtrip(data, checksum=True) == data def test_writer_buffered_wrapping(): """ZxcWriter wrapped in io.BufferedWriter — typical use case.""" sink = io.BytesIO() raw = zxc.ZxcWriter(sink) bw = io.BufferedWriter(raw, buffer_size=8192) payload = bytes((i % 256 for i in range(200_000))) bw.write(payload) bw.close() # flushes BufferedWriter, then closes ZxcWriter (finalises frame) sink.seek(0) with zxc.ZxcReader(sink) as r: assert r.read() == payload def test_reader_buffered_wrapping(): """ZxcReader wrapped in io.BufferedReader for line/iteration semantics.""" sink = io.BytesIO() text = b"line one\nline two\nline three\n" with zxc.ZxcWriter(sink) as w: w.write(text) sink.seek(0) raw = zxc.ZxcReader(sink) br = io.BufferedReader(raw, buffer_size=64) assert br.readline() == b"line one\n" assert br.readline() == b"line two\n" assert br.readline() == b"line three\n" assert br.readline() == b"" br.close() def test_writer_close_idempotent(): sink = io.BytesIO() w = zxc.ZxcWriter(sink) w.write(b"abc") w.close() w.close() # must not raise sink.seek(0) with zxc.ZxcReader(sink) as r: assert r.read() == b"abc" def test_reader_close_idempotent(): sink = io.BytesIO() with zxc.ZxcWriter(sink) as w: w.write(b"xyz") sink.seek(0) r = zxc.ZxcReader(sink) r.close() r.close() # must not raise def test_reader_truncated_frame_raises(): sink = io.BytesIO() with zxc.ZxcWriter(sink) as w: w.write(b"A" * 32_768) full = sink.getvalue() truncated = full[: len(full) // 2] src = io.BytesIO(truncated) with pytest.raises(OSError, match="truncated"): with zxc.ZxcReader(src) as r: r.read() def test_writer_underlying_not_closed(): """ZxcWriter.close() must NOT close the wrapped sink.""" sink = io.BytesIO() with zxc.ZxcWriter(sink) as w: w.write(b"hello") assert not sink.closed assert sink.tell() > 0 def test_reader_underlying_not_closed(): sink = io.BytesIO() with zxc.ZxcWriter(sink) as w: w.write(b"hello") sink.seek(0) with zxc.ZxcReader(sink) as r: r.read() assert not sink.closed # --------------------------------------------------------------------------- # detect_zxc # --------------------------------------------------------------------------- def test_detect_zxc_positive_compress(): frame = zxc.compress(b"sniff me") assert zxc.detect_zxc(frame) def test_detect_zxc_positive_writer(): sink = io.BytesIO() with zxc.ZxcWriter(sink) as w: w.write(b"hi") assert zxc.detect_zxc(sink.getvalue()) @pytest.mark.parametrize( "data", [ b"", b"\xf5\x2e\xb0", # too short b"\x00\x00\x00\x00", b"not a zxc frame at all", bytes(4), ], ) def test_detect_zxc_negative(data): assert not zxc.detect_zxc(data) def test_detect_zxc_accepts_memoryview_and_bytearray(): frame = zxc.compress(b"x") assert zxc.detect_zxc(memoryview(frame)) assert zxc.detect_zxc(bytearray(frame)) zxc-0.13.0/wrappers/python/tests/test_pstream.py000066400000000000000000000050031522536370400220160ustar00rootroot00000000000000""" ZXC - High-performance lossless compression Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. SPDX-License-Identifier: BSD-3-Clause Tests for the push streaming API (zxc.CStream / zxc.DStream). """ import pytest import zxc def _roundtrip(data: bytes, *, level=zxc.LEVEL_DEFAULT, checksum=False) -> bytes: cs = zxc.CStream(level=level, checksum=checksum) chunks = [] # Feed in 17-byte slices to exercise the buffering/state machine. step = max(1, min(17, len(data))) for i in range(0, len(data), step): chunks.append(cs.compress(data[i : i + step])) chunks.append(cs.end()) cs.close() compressed = b"".join(chunks) ds = zxc.DStream(checksum=checksum) out_chunks = [] step = max(1, min(31, len(compressed))) for i in range(0, len(compressed), step): out_chunks.append(ds.decompress(compressed[i : i + step])) assert ds.finished ds.close() return b"".join(out_chunks) def test_pstream_small_roundtrip(): data = b"Hello pstream! Round-trip through the Python push API." assert _roundtrip(data) == data def test_pstream_with_checksum(): data = bytes((i % 251 for i in range(32 * 1024))) assert _roundtrip(data, checksum=True) == data def test_pstream_multi_block(): # Larger than one default block (512 KB) to force multiple blocks. data = bytes(((i * 7) % 256 for i in range(1536 * 1024))) assert _roundtrip(data) == data def test_pstream_size_hints(): cs = zxc.CStream() assert cs.in_size > 0 assert cs.out_size > 0 cs.close() ds = zxc.DStream() assert ds.in_size > 0 assert ds.out_size > 0 assert ds.finished is False ds.close() def test_pstream_context_manager(): data = b"context manager exit closes the stream" with zxc.CStream() as cs: compressed = cs.compress(data) + cs.end() with zxc.DStream() as ds: assert ds.decompress(compressed) == data assert ds.finished def test_pstream_use_after_close(): cs = zxc.CStream() cs.close() with pytest.raises(ValueError): cs.compress(b"foo") ds = zxc.DStream() ds.close() with pytest.raises(ValueError): ds.decompress(b"foo") def test_pstream_truncated_stream_not_finished(): data = b"some data " * 1000 cs = zxc.CStream() compressed = cs.compress(data) + cs.end() cs.close() # Drop the last 5 bytes (partial footer), decoder should not finalise. ds = zxc.DStream() ds.decompress(compressed[:-5]) assert not ds.finished ds.close() zxc-0.13.0/wrappers/python/tests/test_seekable.py000066400000000000000000000167711522536370400221340ustar00rootroot00000000000000""" ZXC - High-performance lossless compression Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. SPDX-License-Identifier: BSD-3-Clause """ import pytest import zxc def build_payload(size): return bytes((i * 31) & 0xFF for i in range(size)) def build_seekable_archive(payload, level=zxc.LEVEL_DEFAULT): return zxc.compress(payload, level=level, checksum=True) def build_seekable_archive_stream(payload, tmp_path, level=zxc.LEVEL_DEFAULT): """Compress via the streaming path with seekable=True.""" src = tmp_path / "src.bin" dst = tmp_path / "dst.zxc" src.write_bytes(payload) with open(src, "rb") as fsrc, open(dst, "wb") as fdst: zxc.stream_compress(fsrc, fdst, level=level, checksum=True, seekable=True) return dst.read_bytes() # ========================================================================= # Queries # ========================================================================= class TestSeekableQueries: def test_reports_size_and_blocks(self, tmp_path): payload = build_payload(64 * 1024) compressed = build_seekable_archive_stream(payload, tmp_path) with zxc.Seekable(compressed) as s: assert s.decompressed_size == len(payload) assert s.num_blocks >= 1 def test_per_block_sizes(self, tmp_path): payload = build_payload(64 * 1024) compressed = build_seekable_archive_stream(payload, tmp_path) with zxc.Seekable(compressed) as s: nb = s.num_blocks assert s.block_compressed_size(0) > 0 assert s.block_decompressed_size(0) > 0 assert s.block_compressed_size(nb) is None assert s.block_decompressed_size(nb) is None # ========================================================================= # decompress_range # ========================================================================= class TestDecompressRange: def test_full_range_roundtrip(self, tmp_path): payload = build_payload(64 * 1024) compressed = build_seekable_archive_stream(payload, tmp_path) with zxc.Seekable(compressed) as s: out = s.decompress_range(0, len(payload)) assert out == payload def test_mid_range_slice(self, tmp_path): payload = build_payload(64 * 1024) compressed = build_seekable_archive_stream(payload, tmp_path) with zxc.Seekable(compressed) as s: off, length = 1024, 8192 out = s.decompress_range(off, length) assert out == payload[off:off + length] def test_zero_length(self, tmp_path): payload = build_payload(4096) compressed = build_seekable_archive_stream(payload, tmp_path) with zxc.Seekable(compressed) as s: assert s.decompress_range(0, 0) == b"" def test_all_levels(self, tmp_path): payload = build_payload(32 * 1024) for level in range(zxc.LEVEL_FASTEST, zxc.LEVEL_ULTRA + 1): compressed = build_seekable_archive_stream(payload, tmp_path, level=level) with zxc.Seekable(compressed) as s: assert s.decompress_range(0, len(payload)) == payload # ========================================================================= # Error handling # ========================================================================= class TestSeekableErrors: def test_garbage_buffer(self): with pytest.raises(RuntimeError): zxc.Seekable(b"\x01\x02\x03\x04\x05\x06\x07\x08") def test_empty_buffer(self): with pytest.raises(ValueError): zxc.Seekable(b"") def test_methods_after_close(self, tmp_path): payload = build_payload(4096) compressed = build_seekable_archive_stream(payload, tmp_path) s = zxc.Seekable(compressed) s.close() with pytest.raises(ValueError): _ = s.num_blocks # close is idempotent s.close() def test_bad_type(self): with pytest.raises(TypeError): zxc.Seekable(42) def test_non_seekable_archive_rejected(self): payload = build_payload(4096) compressed = zxc.compress(payload) with pytest.raises(RuntimeError): zxc.Seekable(compressed) # ========================================================================= # Reader callback # ========================================================================= class TestSeekableReader: def test_roundtrip_via_reader(self, tmp_path): payload = build_payload(128 * 1024) compressed = build_seekable_archive_stream(payload, tmp_path) calls = [0] class Reader: size = len(compressed) def read_at(self, length, offset): calls[0] += 1 return compressed[offset:offset + length] with zxc.Seekable(Reader()) as s: assert calls[0] == 3 # header, footer, seek table assert s.decompressed_size == len(payload) out = s.decompress_range(0, len(payload)) assert out == payload before = calls[0] chunk = s.decompress_range(2048, 1024) assert chunk == payload[2048:2048 + 1024] assert calls[0] - before == 1 def test_reader_exception_propagates(self, tmp_path): # The reader's own exception (with its message) must reach the caller, # not be shadowed by a generic RuntimeError. payload = build_payload(128 * 1024) compressed = build_seekable_archive_stream(payload, tmp_path) attempted = [0] class BadReader: size = len(compressed) def read_at(self, length, offset): attempted[0] += 1 if attempted[0] > 3: raise IOError("boom") return compressed[offset:offset + length] with zxc.Seekable(BadReader()) as s: with pytest.raises(IOError, match="boom"): s.decompress_range(0, len(payload)) def test_reader_multithreaded_decode(self, tmp_path): # Regression: multi-threaded decode with a Python reader used to # invoke the callback from library worker threads without the GIL, # corrupting the interpreter. payload = build_payload(2 * 1024 * 1024) compressed = build_seekable_archive_stream(payload, tmp_path) class Reader: size = len(compressed) def read_at(self, length, offset): return compressed[offset:offset + length] with zxc.Seekable(Reader()) as s: assert s.num_blocks >= 2 out = s.decompress_range(0, len(payload), n_threads=4) assert out == payload def test_rejects_missing_attrs(self): with pytest.raises(TypeError): zxc.Seekable(object()) def test_rejects_garbage_reader(self, tmp_path): class GarbageReader: size = 64 def read_at(self, length, offset): return b"\x00" * length with pytest.raises(RuntimeError): zxc.Seekable(GarbageReader()) # ========================================================================= # Low-level seek table helpers # ========================================================================= class TestSeekTableHelpers: def test_roundtrip(self): comp_sizes = [128, 256, 200, 4] sz = zxc.seek_table_size(len(comp_sizes)) assert sz > 0 buf = zxc.write_seek_table(comp_sizes) assert len(buf) == sz def test_rejects_empty(self): with pytest.raises(ValueError): zxc.write_seek_table([]) zxc-0.13.0/wrappers/python/tests/test_stream.py000066400000000000000000000077141522536370400216510ustar00rootroot00000000000000""" ZXC - High-performance lossless compression Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. SPDX-License-Identifier: BSD-3-Clause """ import pytest import zxc import io @pytest.mark.parametrize( "src,dst,expected_error,match", [ ("not a file", io.BytesIO(), ValueError, "src and dst must be open file-like objects"), (io.BytesIO(b"data"), "not a file", ValueError, "src and dst must be open file-like objects"), (io.BytesIO(b"data"), io.BytesIO(), ValueError, "Source file must be readable"), (io.BytesIO(b"data"), io.BytesIO(), ValueError, "Destination file must be writable"), (None, None, None, "valid_file"), ], ids=[ "src_not_file", "dst_not_file", "src_not_readable", "dst_not_writable", "valid_file", ] ) def test_stream_invalid_src_dst(tmp_path, src, dst, expected_error, match): # Helper class to mock fileno behavior class MockFile(io.BytesIO): def fileno(self): return 1 class NotReadable(MockFile): def readable(self): return False class NotWritable(MockFile): def writable(self): return False if match == "Source file must be readable": src = NotReadable(b"data") dst = MockFile() elif match == "Destination file must be writable": src = MockFile(b"data") dst = NotWritable() elif match == "valid_file": src_path = tmp_path / "src.txt" dst_path = tmp_path / "dst.zxc" src_path.write_bytes(b"hello world") src = open(src_path, "rb") dst = open(dst_path, "wb") try: if not expected_error: _ = zxc.stream_compress(src, dst) else: with pytest.raises(expected_error, match=match): zxc.stream_compress(src, dst) finally: if hasattr(src, "close"): src.close() if hasattr(dst, "close"): dst.close() @pytest.mark.parametrize( "data,corrupt_func,exc", [ (b"hello world" * 10, lambda x: x[:-1] + b"\x01", RuntimeError), (b"a" * 10, lambda x: b"", RuntimeError), ], ids=["corrupted_data", "invalid_header"], ) def test_stream_compress_corruption(tmp_path, data, corrupt_func, exc): src_file_path = tmp_path / "src.bin" compressed_file_path = tmp_path / "compressed.zxc" decompressed_file_path = tmp_path / "decompressed.bin" src_file_path.write_bytes(data) with open(src_file_path, "rb") as src, open(compressed_file_path, "wb") as dst: zxc.stream_compress(src, dst, checksum=True) compressed_bytes = compressed_file_path.read_bytes() corrupted_bytes = corrupt_func(compressed_bytes) compressed_file_path.write_bytes(corrupted_bytes) with pytest.raises(exc): with open(compressed_file_path, "rb") as src, open(decompressed_file_path, "wb") as dst: zxc.stream_decompress(src, dst, checksum=True) @pytest.mark.parametrize("data", [ b"hello world" * 10, # normal b"a", # single byte b"", # empty b"a" * 10_000_000, # large ], ids=["normal", "single_byte", "empty", "large_10mb"]) def test_stream_roundtrip(tmp_path, data): src_file_path = tmp_path / "src.txt" compressed_file_path = tmp_path / "compressed.zxc" decompressed_file_path = tmp_path / "decompressed.txt" src_file_path.write_bytes(data) for level in range(zxc.LEVEL_FASTEST, zxc.LEVEL_ULTRA + 1): with open(src_file_path, "rb") as src, \ open(compressed_file_path, "wb") as compressed: zxc.stream_compress(src, compressed, level=level) with open(compressed_file_path, "rb") as compressed, \ open(decompressed_file_path, "wb") as decompressed: zxc.stream_decompress(compressed, decompressed) decompressed_data = decompressed_file_path.read_bytes() assert decompressed_data == data assert len(decompressed_data) == len(data)zxc-0.13.0/wrappers/rust/000077500000000000000000000000001522536370400152465ustar00rootroot00000000000000zxc-0.13.0/wrappers/rust/.gitignore000066400000000000000000000002511522536370400172340ustar00rootroot00000000000000# Rust build artifacts target/ # Debug/release build output debug/ release/ # IDE files .vscode/ .idea/ *.swp *.swo *~ # macOS .DS_Store # Backup files *.bak *.orig zxc-0.13.0/wrappers/rust/Cargo.lock000066400000000000000000000056241522536370400171620ustar00rootroot00000000000000# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "cc" version = "1.2.65" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" dependencies = [ "find-msvc-tools", "shlex", ] [[package]] name = "find-msvc-tools" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "libc" version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "proc-macro2" version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] [[package]] name = "shlex" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "syn" version = "2.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "thiserror" version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-sys" version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ "windows-link", ] [[package]] name = "zxc-compress" version = "0.13.0" dependencies = [ "libc", "thiserror", "windows-sys", "zxc-compress-sys", ] [[package]] name = "zxc-compress-sys" version = "0.13.0" dependencies = [ "cc", "libc", ] zxc-0.13.0/wrappers/rust/Cargo.toml000066400000000000000000000006011522536370400171730ustar00rootroot00000000000000[workspace] members = ["zxc-sys", "zxc"] resolver = "2" [workspace.package] version = "0.13.0" authors = ["Bertrand Lebonnois"] edition = "2024" rust-version = "1.85" license = "BSD-3-Clause" repository = "https://github.com/hellobertrand/zxc" keywords = [ "zxc", "compression", "lossless", "high-performance", "data-compression", ] categories = ["compression", "encoding"] zxc-0.13.0/wrappers/rust/zxc-sys/000077500000000000000000000000001522536370400166665ustar00rootroot00000000000000zxc-0.13.0/wrappers/rust/zxc-sys/Cargo.toml000066400000000000000000000010001522536370400206050ustar00rootroot00000000000000[package] name = "zxc-compress-sys" description = "Low-level FFI bindings to the ZXC compression library" version.workspace = true authors.workspace = true edition.workspace = true license.workspace = true repository.workspace = true keywords.workspace = true categories.workspace = true links = "zxc" build = "build.rs" [lib] name = "zxc_sys" [dependencies] libc = "0.2" [build-dependencies] cc = "1.2" [features] default = [] # Enable if you want to link against a system-installed ZXC library system = [] zxc-0.13.0/wrappers/rust/zxc-sys/build.rs000066400000000000000000000257331522536370400203450ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ //! Build script for zxc-sys //! //! This script compiles the ZXC C library with Function Multi-Versioning (FMV) //! to support runtime CPU feature detection and optimized code paths. //! //! On ARM64: Compiles `_default` and `_neon` variants //! On x86_64: Compiles `_default`, `_sse2`, `_avx2`, and `_avx512` variants use std::env; use std::fs; use std::path::{Path, PathBuf}; /// Extract version constants from zxc_constants.h fn extract_version(include_dir: &Path) -> (u32, u32, u32) { let header_path = include_dir.join("zxc_constants.h"); let content = fs::read_to_string(&header_path) .expect("Failed to read zxc_constants.h"); let mut major = None; let mut minor = None; let mut patch = None; for line in content.lines() { let trimmed = line.trim(); // Parse lines like: #define ZXC_VERSION_MAJOR 0 if trimmed.starts_with("#define") { let parts: Vec<&str> = trimmed.split_whitespace().collect(); if parts.len() >= 3 { match parts[1] { "ZXC_VERSION_MAJOR" => major = parts[2].parse().ok(), "ZXC_VERSION_MINOR" => minor = parts[2].parse().ok(), "ZXC_VERSION_PATCH" => patch = parts[2].parse().ok(), _ => {} } } } } ( major.expect("ZXC_VERSION_MAJOR not found"), minor.expect("ZXC_VERSION_MINOR not found"), patch.expect("ZXC_VERSION_PATCH not found"), ) } /// Extract compression level constants from zxc_constants.h fn extract_compression_levels(include_dir: &Path) -> (i32, i32, i32, i32, i32, i32, i32) { let header_path = include_dir.join("zxc_constants.h"); let content = fs::read_to_string(&header_path) .expect("Failed to read zxc_constants.h"); let mut fastest = None; let mut fast = None; let mut default = None; let mut balanced = None; let mut compact = None; let mut density = None; let mut ultra = None; for line in content.lines() { let trimmed = line.trim(); // Parse lines like: ZXC_LEVEL_FASTEST = 1, if trimmed.starts_with("ZXC_LEVEL_") { let parts: Vec<&str> = trimmed.split('=').collect(); if parts.len() >= 2 { let name = parts[0].trim(); // Extract number, removing comma and comments let value_str = parts[1].split(&[',', '/'][..]).next().unwrap().trim(); let value: Option = value_str.parse().ok(); match name { "ZXC_LEVEL_FASTEST" => fastest = value, "ZXC_LEVEL_FAST" => fast = value, "ZXC_LEVEL_DEFAULT" => default = value, "ZXC_LEVEL_BALANCED" => balanced = value, "ZXC_LEVEL_COMPACT" => compact = value, "ZXC_LEVEL_DENSITY" => density = value, "ZXC_LEVEL_ULTRA" => ultra = value, _ => {} } } } } ( fastest.expect("ZXC_LEVEL_FASTEST not found"), fast.expect("ZXC_LEVEL_FAST not found"), default.expect("ZXC_LEVEL_DEFAULT not found"), balanced.expect("ZXC_LEVEL_BALANCED not found"), compact.expect("ZXC_LEVEL_COMPACT not found"), density.expect("ZXC_LEVEL_DENSITY not found"), ultra.expect("ZXC_LEVEL_ULTRA not found"), ) } /// Compiles one FMV variant of the three per-ISA translation units /// (zxc_compress.c, zxc_decompress.c, zxc_huffman.c) with the given function /// suffix and ISA flags. fn compile_variant(include_dir: &Path, src_lib: &Path, suffix: &str, flags: &[&str]) { for unit in ["zxc_compress", "zxc_decompress", "zxc_huffman"] { let mut build = cc::Build::new(); build .include(include_dir) .include(src_lib) .include(src_lib.join("vendors")) .define("ZXC_STATIC_DEFINE", None) .file(src_lib.join(format!("{unit}.c"))) .define("ZXC_FUNCTION_SUFFIX", suffix) .opt_level(3) .warnings(false); for flag in flags { build.flag_if_supported(flag); } build.compile(&format!("{unit}{suffix}")); } } fn main() { // Path to ZXC source files let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); // We use mirrored symlinks under zxc/ to preserve relative include paths // expected by the C source code (../../include/...). // During `cargo publish`, these symlinks are followed and real files are packaged. let src_lib = manifest_dir.join("zxc/src/lib"); let include_dir = manifest_dir.join("zxc/include"); // Fallback for local development if symlinks are broken or missing let (src_lib, include_dir) = if src_lib.exists() && include_dir.exists() { (src_lib, include_dir) } else { // Try direct path from workspace root let zxc_root = manifest_dir.join("../../..").canonicalize() .expect("Failed to find ZXC root directory"); (zxc_root.join("src/lib"), zxc_root.join("include")) }; // Verify the headers exist: even the `system` feature needs them to // extract the version/level constants consumed by lib.rs's env!() calls. assert!( include_dir.exists(), "ZXC include directory not found: {:?}", include_dir ); // Extract version from header and make it available to lib.rs let (major, minor, patch) = extract_version(&include_dir); println!("cargo:rustc-env=ZXC_VERSION_MAJOR={}", major); println!("cargo:rustc-env=ZXC_VERSION_MINOR={}", minor); println!("cargo:rustc-env=ZXC_VERSION_PATCH={}", patch); // Extract compression levels from header and make them available to lib.rs let (fastest, fast, default, balanced, compact, density, ultra) = extract_compression_levels(&include_dir); println!("cargo:rustc-env=ZXC_LEVEL_FASTEST={}", fastest); println!("cargo:rustc-env=ZXC_LEVEL_FAST={}", fast); println!("cargo:rustc-env=ZXC_LEVEL_DEFAULT={}", default); println!("cargo:rustc-env=ZXC_LEVEL_BALANCED={}", balanced); println!("cargo:rustc-env=ZXC_LEVEL_COMPACT={}", compact); println!("cargo:rustc-env=ZXC_LEVEL_DENSITY={}", density); println!("cargo:rustc-env=ZXC_LEVEL_ULTRA={}", ultra); // Use the system library instead of compiling from source if env::var("CARGO_FEATURE_SYSTEM").is_ok() { println!("cargo:rerun-if-env-changed=ZXC_LIB_DIR"); if let Ok(dir) = env::var("ZXC_LIB_DIR") { println!("cargo:rustc-link-search=native={}", dir); } println!("cargo:rustc-link-lib=zxc"); return; } assert!( src_lib.exists(), "ZXC source directory not found: {:?}", src_lib ); let target = env::var("TARGET").unwrap_or_default(); let is_arm64 = target.contains("aarch64") || target.contains("arm64"); let is_x86_64 = target.contains("x86_64") || target.contains("i686"); // ========================================================================= // Core library files (common to all architectures) // ========================================================================= let mut core_build = cc::Build::new(); core_build .include(&include_dir) .include(&src_lib) .include(src_lib.join("vendors")) .define("ZXC_STATIC_DEFINE", None) .file(src_lib.join("zxc_common.c")) .file(src_lib.join("zxc_dict.c")) .file(src_lib.join("zxc_dispatch.c")) .file(src_lib.join("zxc_driver.c")) .file(src_lib.join("zxc_seekable.c")) .file(src_lib.join("zxc_pstream.c")) .opt_level(3) .warnings(false) .flag_if_supported("-pthread"); // ========================================================================= // Function Multi-Versioning: Compile variants with different suffixes // ========================================================================= // Add architecture-specific flags to core build BEFORE compiling if is_arm64 { core_build.flag_if_supported("-march=armv8-a+crc"); } else if is_x86_64 { core_build.flag_if_supported("-msse2"); core_build.flag_if_supported("-mpclmul"); } core_build.compile("zxc_core"); // --- Default variant (baseline, always compiled) --- compile_variant(&include_dir, &src_lib, "_default", &[]); // --- Architecture-specific variants --- // The per-variant flags mirror zxc_add_variant() in CMakeLists.txt; // keep both in sync. MSVC ignores GCC-style -m flags silently, so it // needs its own /arch spellings plus the __BMI*__/__LZCNT__ macros the // sources test (cl.exe never defines them itself). let is_msvc = target.contains("msvc"); if is_arm64 { compile_variant(&include_dir, &src_lib, "_neon", &["-march=armv8-a+simd"]); } else if is_x86_64 && is_msvc { compile_variant(&include_dir, &src_lib, "_sse2", &["/D__SSE2__"]); compile_variant( &include_dir, &src_lib, "_avx2", &["/arch:AVX2", "/D__BMI__", "/D__BMI2__", "/D__LZCNT__"], ); compile_variant( &include_dir, &src_lib, "_avx512", &["/arch:AVX512", "/D__BMI__", "/D__BMI2__", "/D__LZCNT__"], ); } else if is_x86_64 { // SSE2 variant: the x86-64 baseline (also covers any i686 built with // SSE2). compile_variant(&include_dir, &src_lib, "_sse2", &["-msse2"]); compile_variant( &include_dir, &src_lib, "_avx2", &["-mavx2", "-mfma", "-mbmi", "-mbmi2", "-mlzcnt"], ); compile_variant( &include_dir, &src_lib, "_avx512", &[ "-mavx512f", "-mavx512bw", "-mavx512vbmi", "-mavx512vbmi2", "-mbmi", "-mbmi2", "-mlzcnt", ], ); } // PivCo tables: const data used only by the variant Huffman decoders, so it // must link LAST (zxc_core doesn't reference it, or the linker drops it). let mut pivco_tables = cc::Build::new(); pivco_tables .include(&include_dir) .include(&src_lib) .include(src_lib.join("vendors")) .define("ZXC_STATIC_DEFINE", None) .file(src_lib.join("zxc_pivco_tables.c")) .opt_level(3) .warnings(false); pivco_tables.compile("zxc_pivco_tables"); // Threading support (not needed on Windows, which uses kernel32) if !target.contains("windows") { println!("cargo:rustc-link-lib=pthread"); } // Re-run build script if source files change println!("cargo:rerun-if-changed={}", src_lib.display()); println!("cargo:rerun-if-changed={}", include_dir.display()); } zxc-0.13.0/wrappers/rust/zxc-sys/src/000077500000000000000000000000001522536370400174555ustar00rootroot00000000000000zxc-0.13.0/wrappers/rust/zxc-sys/src/lib.rs000066400000000000000000001244271522536370400206030ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ //! Low-level FFI bindings to the ZXC compression library. //! //! This crate provides raw, unsafe bindings to the ZXC C library. //! For a safe, idiomatic Rust API, use the `zxc` crate instead. //! //! # Example //! //! ```rust,ignore //! use zxc_sys::*; //! //! unsafe { //! let bound = zxc_compress_bound(1024); //! // ... allocate buffer and compress //! } //! ``` #![allow(non_camel_case_types)] #![allow(non_upper_case_globals)] use std::ffi::c_int; use std::os::raw::{c_char, c_void}; // ============================================================================= // ZXC Version Constants // ============================================================================= // Version constants - automatically extracted from zxc_constants.h by build.rs const fn parse_version(s: &str) -> u32 { let bytes = s.as_bytes(); let mut result = 0u32; let mut i = 0; while i < bytes.len() { let digit = bytes[i]; if digit >= b'0' && digit <= b'9' { result = result * 10 + (digit - b'0') as u32; } i += 1; } result } pub const ZXC_VERSION_MAJOR: u32 = parse_version(env!("ZXC_VERSION_MAJOR")); pub const ZXC_VERSION_MINOR: u32 = parse_version(env!("ZXC_VERSION_MINOR")); pub const ZXC_VERSION_PATCH: u32 = parse_version(env!("ZXC_VERSION_PATCH")); // ============================================================================= // Compression Levels // ============================================================================= // Helper function to parse integer from string literal at compile time const fn parse_i32(s: &str) -> i32 { let bytes = s.as_bytes(); let mut result = 0i32; let mut i = 0; let mut sign = 1; if i < bytes.len() && bytes[i] == b'-' { sign = -1; i += 1; } while i < bytes.len() { let digit = bytes[i]; if digit >= b'0' && digit <= b'9' { result = result * 10 + (digit - b'0') as i32; } i += 1; } result * sign } // Compression level constants - automatically extracted from zxc_constants.h by build.rs /// Fastest compression, best for real-time applications pub const ZXC_LEVEL_FASTEST: i32 = parse_i32(env!("ZXC_LEVEL_FASTEST")); /// Fast compression, good for real-time applications pub const ZXC_LEVEL_FAST: i32 = parse_i32(env!("ZXC_LEVEL_FAST")); /// Recommended: ratio > LZ4, decode speed > LZ4 pub const ZXC_LEVEL_DEFAULT: i32 = parse_i32(env!("ZXC_LEVEL_DEFAULT")); /// Good ratio, good decode speed pub const ZXC_LEVEL_BALANCED: i32 = parse_i32(env!("ZXC_LEVEL_BALANCED")); /// High density. Best for storage/firmware/assets. pub const ZXC_LEVEL_COMPACT: i32 = parse_i32(env!("ZXC_LEVEL_COMPACT")); /// High density: Huffman-coded literals on top of COMPACT, /// price-based optimal LZ77 parser. pub const ZXC_LEVEL_DENSITY: i32 = parse_i32(env!("ZXC_LEVEL_DENSITY")); /// Maximum density: Huffman-coded literals *and* sequence tokens, deep parse. /// Slowest compression, best ratio (level 7 / ULTRA). pub const ZXC_LEVEL_ULTRA: i32 = parse_i32(env!("ZXC_LEVEL_ULTRA")); // ============================================================================= // Error Codes // ============================================================================= /// Success (no error) pub const ZXC_OK: i32 = 0; /// Memory allocation failure pub const ZXC_ERROR_MEMORY: i32 = -1; /// Destination buffer too small pub const ZXC_ERROR_DST_TOO_SMALL: i32 = -2; /// Source buffer too small or truncated input pub const ZXC_ERROR_SRC_TOO_SMALL: i32 = -3; /// Invalid magic word in file header pub const ZXC_ERROR_BAD_MAGIC: i32 = -4; /// Unsupported file format version pub const ZXC_ERROR_BAD_VERSION: i32 = -5; /// Corrupted or invalid header (CRC mismatch) pub const ZXC_ERROR_BAD_HEADER: i32 = -6; /// Block or global checksum verification failed pub const ZXC_ERROR_BAD_CHECKSUM: i32 = -7; /// Corrupted compressed data pub const ZXC_ERROR_CORRUPT_DATA: i32 = -8; /// Invalid match offset during decompression pub const ZXC_ERROR_BAD_OFFSET: i32 = -9; /// Buffer overflow detected during processing pub const ZXC_ERROR_OVERFLOW: i32 = -10; /// Read/write/seek failure on file pub const ZXC_ERROR_IO: i32 = -11; /// Required input pointer is NULL pub const ZXC_ERROR_NULL_INPUT: i32 = -12; /// Unknown or unexpected block type pub const ZXC_ERROR_BAD_BLOCK_TYPE: i32 = -13; /// Invalid block size pub const ZXC_ERROR_BAD_BLOCK_SIZE: i32 = -14; /// File requires a dictionary but none was provided pub const ZXC_ERROR_DICT_REQUIRED: i32 = -15; /// Provided dictionary ID does not match the file header pub const ZXC_ERROR_DICT_MISMATCH: i32 = -16; /// Dictionary exceeds maximum allowed size pub const ZXC_ERROR_DICT_TOO_LARGE: i32 = -17; /// Compression level out of range, or not supported by this context's workspace pub const ZXC_ERROR_BAD_LEVEL: i32 = -18; // ============================================================================= // Dictionary Constants // ============================================================================= /// Maximum dictionary content size in bytes (65535). pub const ZXC_DICT_SIZE_MAX: usize = (1usize << 16) - 1; /// Fixed size of the `.zxd` file header in bytes. pub const ZXC_DICT_HEADER_SIZE: usize = 16; /// Size in bytes of the shared literal Huffman table carried by a `.zxd` file /// (packed 4-bit code lengths for 256 symbols). pub const ZXC_HUF_TABLE_SIZE: usize = 128; // ============================================================================= // Options Structs (mirroring C API) // ============================================================================= /// Compression options (mirrors `zxc_compress_opts_t` from C API). #[repr(C)] #[derive(Debug, Clone)] pub struct zxc_compress_opts_t { /// Worker thread count (0 = auto-detect CPU cores). pub n_threads: c_int, /// Compression level 1-7 (0 = default). pub level: c_int, /// Block size in bytes (0 = default 512 KB). Must be power of 2, 4 KB – 2 MB. pub block_size: usize, /// 1 to enable per-block and global checksums, 0 to disable. pub checksum_enabled: c_int, /// 1 to append a seek table for random-access decompression, 0 to disable. pub seekable: c_int, /// Pre-trained dictionary content (NULL = none). pub dict: *const c_void, /// Dictionary size in bytes (0 = none, max [`ZXC_DICT_SIZE_MAX`]). pub dict_size: usize, /// Shared literal Huffman table: 128-byte packed code-lengths header /// (NULL = none; ignored without dict). pub dict_huf: *const c_void, /// Progress callback (NULL to disable). pub progress_cb: *const c_void, /// User context pointer passed to progress_cb. pub user_data: *mut c_void, } impl Default for zxc_compress_opts_t { fn default() -> Self { Self { n_threads: 0, level: 0, block_size: 0, checksum_enabled: 0, seekable: 0, dict: std::ptr::null(), dict_size: 0, dict_huf: std::ptr::null(), progress_cb: std::ptr::null(), user_data: std::ptr::null_mut(), } } } /// Decompression options (mirrors `zxc_decompress_opts_t` from C API). #[repr(C)] #[derive(Debug, Clone)] pub struct zxc_decompress_opts_t { /// Worker thread count (0 = auto-detect CPU cores). pub n_threads: c_int, /// 1 to verify per-block and global checksums, 0 to skip. pub checksum_enabled: c_int, /// Pre-trained dictionary content (NULL = none). pub dict: *const c_void, /// Dictionary size in bytes (0 = none). pub dict_size: usize, /// Shared literal Huffman table: 128-byte packed code-lengths header /// (NULL = none; ignored without dict). pub dict_huf: *const c_void, /// Progress callback (NULL to disable). pub progress_cb: *const c_void, /// User context pointer passed to progress_cb. pub user_data: *mut c_void, } impl Default for zxc_decompress_opts_t { fn default() -> Self { Self { n_threads: 0, checksum_enabled: 0, dict: std::ptr::null(), dict_size: 0, dict_huf: std::ptr::null(), progress_cb: std::ptr::null(), user_data: std::ptr::null_mut(), } } } // ============================================================================= // Opaque Context Handles // ============================================================================= /// Opaque compression context. Use [`zxc_create_cctx`] to create. #[repr(C)] pub struct zxc_cctx { _private: [u8; 0], } /// Opaque decompression context. Use [`zxc_create_dctx`] to create. #[repr(C)] pub struct zxc_dctx { _private: [u8; 0], } // ============================================================================= // Library Info Helpers // ============================================================================= unsafe extern "C" { /// Returns the minimum supported compression level (currently 1). pub fn zxc_min_level() -> c_int; /// Returns the maximum supported compression level (currently 7). pub fn zxc_max_level() -> c_int; /// Returns the default compression level (currently 3). pub fn zxc_default_level() -> c_int; /// Returns the library version as a null-terminated string (e.g. "0.13.0"). /// /// The returned pointer is a compile-time constant and must not be freed. pub fn zxc_version_string() -> *const c_char; } // ============================================================================= // Buffer-Based API // ============================================================================= unsafe extern "C" { /// Calculates the maximum compressed size for a given input. /// /// Useful for allocating output buffers before compression. pub fn zxc_compress_bound(input_size: usize) -> u64; /// Compresses a data buffer using the ZXC algorithm. /// /// # Safety /// /// - `src` must be a valid pointer to `src_size` bytes. /// - `dst` must be a valid pointer to at least `dst_capacity` bytes. /// - The caller must ensure no data races on the buffers. /// /// # Returns /// /// Number of bytes written to `dst` (>0 on success), or a negative error code. pub fn zxc_compress( src: *const c_void, src_size: usize, dst: *mut c_void, dst_capacity: usize, opts: *const zxc_compress_opts_t, ) -> i64; /// Decompresses a ZXC compressed buffer. /// /// # Safety /// /// - `src` must be a valid pointer to `src_size` bytes of compressed data. /// - `dst` must be a valid pointer to at least `dst_capacity` bytes. /// /// # Returns /// /// Number of decompressed bytes written to `dst` (>0 on success), or a negative error code. pub fn zxc_decompress( src: *const c_void, src_size: usize, dst: *mut c_void, dst_capacity: usize, opts: *const zxc_decompress_opts_t, ) -> i64; /// Returns the decompressed size stored in a ZXC compressed buffer. /// /// Reads the file footer without performing decompression. /// /// # Returns /// /// Original uncompressed size in bytes, or 0 if invalid. pub fn zxc_get_decompressed_size(src: *const c_void, src_size: usize) -> u64; /// Returns the minimum single-buffer size for an in-place decompression. /// /// Reads `src`'s header and footer (no decoding) and returns the buffer /// size `zxc_decompress_inplace` needs: decompressed size plus a /// one-block and wild-copy safety margin. /// /// # Returns /// /// Required buffer size in bytes, or 0 if `src` is not a valid archive. pub fn zxc_decompress_inplace_bound(src: *const c_void, src_size: usize) -> usize; /// Decompresses in place inside a single caller-owned buffer. /// /// The compressed archive must sit flush-right in `buffer` (its last /// `comp_size` bytes); decoding runs left-to-right into `buffer[0..]`. /// `buffer_capacity` must be at least `zxc_decompress_inplace_bound`. /// /// # Returns /// /// Decompressed size (>0), 0 for an empty frame, or a negative error /// code (`ZXC_ERROR_DST_TOO_SMALL` if the margin is missing). pub fn zxc_decompress_inplace( buffer: *mut c_void, buffer_capacity: usize, comp_size: usize, opts: *const zxc_decompress_opts_t, ) -> i64; /// Returns a human-readable name for the given error code. /// /// # Arguments /// /// * `code` - An error code from zxc_error_t (or any integer) /// /// # Returns /// /// A constant string such as "ZXC_OK" or "ZXC_ERROR_MEMORY". /// Returns "ZXC_UNKNOWN_ERROR" for unrecognized codes. pub fn zxc_error_name(code: c_int) -> *const std::os::raw::c_char; /// Returns `sizeof(zxc_compress_opts_t)` as compiled into the C library. /// /// Layout guard: the Rust [`zxc_compress_opts_t`] mirrors the C struct by /// hand; comparing sizes turns silent layout drift into a test failure /// (see the `opts_layout_matches_library` test). pub fn zxc_compress_opts_size() -> usize; /// Returns `sizeof(zxc_decompress_opts_t)` as compiled into the C library. /// See [`zxc_compress_opts_size`]. pub fn zxc_decompress_opts_size() -> usize; /// Returns the dictionary ID a ZXC compressed buffer requires. /// /// Reads the file header without decompressing. Returns 0 if the file /// does not require a dictionary or the buffer is invalid. /// /// # Safety /// - `src` must be a valid pointer to `src_size` bytes. pub fn zxc_get_dict_id(src: *const c_void, src_size: usize) -> u32; } // ============================================================================= // Dictionary API // ============================================================================= unsafe extern "C" { /// Trains a dictionary from a corpus of samples. /// /// # Safety /// - `samples` must point to `n_samples` valid pointers, each valid for /// the corresponding `sample_sizes` entry. /// - `sample_sizes` must point to `n_samples` entries. /// - `dict_buf` must be valid for writes up to `dict_capacity` bytes. /// /// # Returns /// Size of the trained dictionary (>0), or a negative `zxc_error_t` code. pub fn zxc_train_dict( samples: *const *const c_void, sample_sizes: *const usize, n_samples: usize, dict_buf: *mut c_void, dict_capacity: usize, ) -> i64; /// Computes the dictionary ID: content-only when `huf_lengths` is null, /// else binding the (content, table) pair. /// /// # Safety /// - `dict` must be a valid pointer to `dict_size` bytes. /// - `huf_lengths` must be null or valid for 128 bytes (`ZXC_HUF_TABLE_SIZE`). /// /// Returns 0 if `dict` is NULL or `dict_size` is 0. pub fn zxc_dict_id(dict: *const c_void, dict_size: usize, huf_lengths: *const c_void) -> u32; /// Returns the dictionary ID stored in a `.zxd` file buffer. /// /// # Safety /// - `buf` must be a valid pointer to `buf_size` bytes. /// /// Returns 0 if the buffer is not a valid `.zxd` file. pub fn zxc_dict_get_id(buf: *const c_void, buf_size: usize) -> u32; /// Returns the `.zxd` file size for a given content size /// (= 16 + `content_size` + 128 for the shared Huffman table). pub fn zxc_dict_save_bound(content_size: usize) -> usize; /// Trains the shared literal Huffman table for an already-trained dictionary. /// /// # Safety /// - `samples`/`sample_sizes` as in [`zxc_train_dict`]. /// - `dict` must be valid for `dict_size` bytes. /// - `huf_lengths_out` must be valid for writes of 128 bytes /// (`ZXC_HUF_TABLE_SIZE`). /// /// # Returns /// `ZXC_OK` (0) on success, or a negative `zxc_error_t` code. pub fn zxc_train_dict_huf( samples: *const *const c_void, sample_sizes: *const usize, n_samples: usize, dict: *const c_void, dict_size: usize, huf_lengths_out: *mut u8, ) -> c_int; /// Serializes dictionary content plus its shared Huffman table to the /// `.zxd` file format. The table is mandatory. /// /// # Safety /// - `content` must be valid for `content_size` bytes. /// - `huf_lengths` must be valid for 128 bytes (`ZXC_HUF_TABLE_SIZE`). /// - `buf` must be valid for writes up to `buf_capacity` bytes. /// /// # Returns /// Bytes written (>0), or a negative `zxc_error_t` code. pub fn zxc_dict_save( content: *const c_void, content_size: usize, huf_lengths: *const c_void, buf: *mut c_void, buf_capacity: usize, ) -> i64; /// Returns a pointer to the 128-byte shared Huffman table inside a `.zxd` /// buffer (zero-copy; aims into `buf`), or NULL if `buf` is not a valid /// `.zxd` file. /// /// # Safety /// - `buf` must be valid for `buf_size` bytes. pub fn zxc_dict_huf(buf: *const c_void, buf_size: usize) -> *const c_void; /// Loads and validates a `.zxd` dictionary file from a memory buffer. /// /// On success, `content_out` and `huf_out` (when non-NULL) point INTO `buf` /// (zero-copy); the caller must keep `buf` alive while they are in use. /// /// # Safety /// - `buf` must be valid for `buf_size` bytes. /// - `content_out` and `content_size_out` must be valid pointers. /// - `huf_out` and `dict_id_out` may be NULL. /// /// # Returns /// `ZXC_OK` (0) on success, or a negative `zxc_error_t` code. pub fn zxc_dict_load( buf: *const c_void, buf_size: usize, content_out: *mut *const c_void, content_size_out: *mut usize, huf_out: *mut *const c_void, dict_id_out: *mut u32, ) -> c_int; /// One-call dictionary creation: trains content + shared table and /// serializes to ready-to-write `.zxd` bytes in `zxd_buf`. /// /// # Safety /// - `samples`/`sample_sizes` as in [`zxc_train_dict`]. /// - `zxd_buf` must be valid for writes up to `zxd_capacity` bytes. /// /// # Returns /// Bytes written (>0), or a negative `zxc_error_t` code. pub fn zxc_dict_train( samples: *const *const c_void, sample_sizes: *const usize, n_samples: usize, zxd_buf: *mut c_void, zxd_capacity: usize, ) -> i64; } // ============================================================================= // Block API (single block, no file framing) // ============================================================================= unsafe extern "C" { /// Returns the maximum compressed size for a single block (no file framing). pub fn zxc_compress_block_bound(input_size: usize) -> u64; /// Returns the minimum `dst_capacity` required by [`zxc_decompress_block`] /// for a block of `uncompressed_size` bytes. Accounts for the wild-copy /// tail pad used by the fast decoder. pub fn zxc_decompress_block_bound(uncompressed_size: usize) -> u64; /// Compresses a single block without file framing. /// /// Output format: `block_header(8B) + payload + optional checksum(4B)`. /// /// # Safety /// - `cctx` must be a valid pointer returned by [`zxc_create_cctx`]. /// - `src`, `dst` must point to `src_size` / `dst_capacity` bytes. pub fn zxc_compress_block( cctx: *mut zxc_cctx, src: *const c_void, src_size: usize, dst: *mut c_void, dst_capacity: usize, opts: *const zxc_compress_opts_t, ) -> i64; /// Decompresses a single block produced by [`zxc_compress_block`]. /// /// `dst_capacity` should be at least /// [`zxc_decompress_block_bound(uncompressed_size)`](zxc_decompress_block_bound) /// to enable the fast path. /// /// # Safety /// - `dctx` must be a valid pointer returned by [`zxc_create_dctx`]. pub fn zxc_decompress_block( dctx: *mut zxc_dctx, src: *const c_void, src_size: usize, dst: *mut c_void, dst_capacity: usize, opts: *const zxc_decompress_opts_t, ) -> i64; /// Strict-sized variant of [`zxc_decompress_block`]: accepts /// `dst_capacity == uncompressed_size` exactly (no tail pad required). /// Slightly slower than the fast path; output is bit-identical. /// /// # Safety /// - `dctx` must be a valid pointer returned by [`zxc_create_dctx`]. pub fn zxc_decompress_block_safe( dctx: *mut zxc_dctx, src: *const c_void, src_size: usize, dst: *mut c_void, dst_capacity: usize, opts: *const zxc_decompress_opts_t, ) -> i64; /// Estimates the memory reserved by a compression context for a single /// block of `src_size` bytes via [`zxc_compress_block`]. /// /// Covers all per-chunk working buffers (chain table, literals, /// sequence/token/offset/extras buffers) plus the fixed hash tables and /// cache-line alignment padding. At `level >= 6` the value also accounts /// for the price-based optimal parser's transient DP scratch (~18 x /// `src_size` bytes), free'd at the end of each block. /// /// Returns the estimated peak memory usage in bytes, or 0 if `src_size == 0`. pub fn zxc_estimate_cctx_size(src_size: usize, level: c_int) -> u64; } // ============================================================================= // Reusable Context API (opaque, heap-allocated) // ============================================================================= unsafe extern "C" { /// Creates a heap-allocated compression context. /// /// When `opts` is non-NULL, internal buffers are pre-allocated. /// When `opts` is NULL, allocation is deferred to first use. /// Returns NULL on allocation failure. Free with [`zxc_free_cctx`]. pub fn zxc_create_cctx(opts: *const zxc_compress_opts_t) -> *mut zxc_cctx; /// Frees a compression context. Safe to call with a null pointer. /// /// # Safety /// - `cctx` must be a pointer returned by [`zxc_create_cctx`] (or null). pub fn zxc_free_cctx(cctx: *mut zxc_cctx); /// Compresses a full file-framed buffer using a reusable context. /// /// # Safety /// - `cctx` must be a valid pointer returned by [`zxc_create_cctx`]. pub fn zxc_compress_cctx( cctx: *mut zxc_cctx, src: *const c_void, src_size: usize, dst: *mut c_void, dst_capacity: usize, opts: *const zxc_compress_opts_t, ) -> i64; /// Creates a heap-allocated decompression context. /// Returns NULL on allocation failure. Free with [`zxc_free_dctx`]. pub fn zxc_create_dctx() -> *mut zxc_dctx; /// Frees a decompression context. Safe to call with a null pointer. /// /// # Safety /// - `dctx` must be a pointer returned by [`zxc_create_dctx`] (or null). pub fn zxc_free_dctx(dctx: *mut zxc_dctx); /// Decompresses a full file-framed buffer using a reusable context. /// /// # Safety /// - `dctx` must be a valid pointer returned by [`zxc_create_dctx`]. pub fn zxc_decompress_dctx( dctx: *mut zxc_dctx, src: *const c_void, src_size: usize, dst: *mut c_void, dst_capacity: usize, opts: *const zxc_decompress_opts_t, ) -> i64; /// Returns the exact byte count required by a static compression /// workspace for the given `block_size` and `level`. /// /// # Returns /// /// Workspace size in bytes, or 0 if either argument is invalid. pub fn zxc_static_cctx_workspace_size(block_size: usize, level: c_int) -> usize; /// Initialises a compression context inside a caller-supplied workspace /// (no heap allocation). /// /// # Safety /// - `workspace` must be cache-line (64-byte) aligned, at least /// `zxc_static_cctx_workspace_size` bytes, and outlive the handle. /// - `opts` must be non-null with `block_size` and `level` set. /// - `zxc_free_cctx` is a no-op on the returned handle. pub fn zxc_init_static_cctx( workspace: *mut c_void, workspace_size: usize, opts: *const zxc_compress_opts_t, ) -> *mut zxc_cctx; /// Returns the exact byte count required by a static decompression /// workspace for the given `block_size`. /// /// # Returns /// /// Workspace size in bytes, or 0 if `block_size` is invalid. pub fn zxc_static_dctx_workspace_size(block_size: usize) -> usize; /// Initialises a decompression context inside a caller-supplied /// workspace (no heap allocation). /// /// # Safety /// - `workspace` must be cache-line (64-byte) aligned, at least /// `zxc_static_dctx_workspace_size` bytes, and outlive the handle. /// - `zxc_free_dctx` is a no-op on the returned handle. pub fn zxc_init_static_dctx( workspace: *mut c_void, workspace_size: usize, block_size: usize, ) -> *mut zxc_dctx; } // ============================================================================= // Streaming API (FILE-based) // ============================================================================= unsafe extern "C" { /// Compresses data from an input stream to an output stream. /// /// Uses a multi-threaded pipeline architecture for high throughput /// on large files. /// /// # Safety /// /// - `f_in` must be a valid FILE* opened in "rb" mode /// - `f_out` must be a valid FILE* opened in "wb" mode /// - File handles must remain valid for the duration of the call /// /// # Arguments /// /// * `f_in` - Input file stream /// * `f_out` - Output file stream /// * `n_threads` - Number of worker threads (0 = auto-detect CPU cores) /// * `level` - Compression level (1-7) /// * `checksum_enabled` - If non-zero, enables checksum verification /// /// # Returns /// /// Total compressed bytes written, or -1 on error. pub fn zxc_stream_compress( f_in: *mut libc::FILE, f_out: *mut libc::FILE, opts: *const zxc_compress_opts_t, ) -> i64; /// Decompresses data from an input stream to an output stream. /// /// Uses the same pipeline architecture as compression for maximum throughput. /// /// # Safety /// /// - `f_in` must be a valid FILE* opened in "rb" mode /// - `f_out` must be a valid FILE* opened in "wb" mode /// /// # Arguments /// /// * `f_in` - Input file stream (compressed data) /// * `f_out` - Output file stream (decompressed data) /// * `n_threads` - Number of worker threads (0 = auto-detect) /// * `checksum_enabled` - If non-zero, verifies checksums /// /// # Returns /// /// Total decompressed bytes written, or -1 on error. pub fn zxc_stream_decompress( f_in: *mut libc::FILE, f_out: *mut libc::FILE, opts: *const zxc_decompress_opts_t, ) -> i64; /// Returns the decompressed size stored in a ZXC compressed file. /// /// Reads the file footer to extract the original size without decompressing. /// The file position is restored after reading. /// /// # Safety /// /// - `f_in` must be a valid FILE* opened in "rb" mode /// /// # Returns /// /// Original uncompressed size in bytes, or -1 on error. pub fn zxc_stream_get_decompressed_size(f_in: *mut libc::FILE) -> i64; } // ============================================================================= // Seekable API (random-access decompression) // ============================================================================= /// Opaque handle for a seekable ZXC archive. /// /// Created by [`zxc_seekable_open`] or [`zxc_seekable_open_file`]. /// Must be freed with [`zxc_seekable_free`]. #[repr(C)] pub struct zxc_seekable { _private: [u8; 0], } /// Storage-agnostic reader interface for seekable archives (mirrors /// `zxc_reader_t` from `zxc_seekable.h`). /// /// `read_at` is the only primitive the library calls on the backend. It must /// return the number of bytes read (`== len` on success) or a negative /// `zxc_error_t` on failure. Short reads are treated as errors by the library. /// /// `read_at` MUST be safe to call concurrently from multiple threads when the /// resulting handle is used with [`zxc_seekable_decompress_range_mt`]. The /// single-threaded path makes no concurrent calls. #[repr(C)] pub struct zxc_reader_t { /// Positional read callback. NULL is rejected at open time. pub read_at: Option< unsafe extern "C" fn(ctx: *mut c_void, dst: *mut c_void, len: usize, offset: u64) -> i64, >, /// Opaque user context passed unchanged to `read_at`. pub ctx: *mut c_void, /// Total size of the compressed archive in bytes. pub size: u64, } unsafe extern "C" { /// Opens a seekable archive from a memory buffer. /// /// The buffer must remain valid for the lifetime of the handle. /// /// # Safety /// - `src` must be a valid pointer to `src_size` bytes. /// /// Returns NULL if the buffer is not a valid seekable archive. pub fn zxc_seekable_open(src: *const c_void, src_size: usize) -> *mut zxc_seekable; /// Opens a seekable archive from a `FILE*`. The file must be seekable /// (not stdin/pipe). The current file position is saved and restored. /// The FILE* must remain open for the lifetime of the handle. /// /// # Safety /// - `f` must be a valid FILE* opened in "rb" mode. pub fn zxc_seekable_open_file(f: *mut libc::FILE) -> *mut zxc_seekable; /// Opens a seekable archive through a user-supplied [`zxc_reader_t`]. /// /// The reader is invoked to fetch the file header, footer, and seek table /// at open time (3 reads), then once per block during decompression. /// Use this entry point to back the seekable API with any storage that /// supports positional reads (mmap, HTTP `Range:`, S3, kernel /// `vfs_read()`, etc.). /// /// # Safety /// - `r` must point to a valid [`zxc_reader_t`] whose `read_at`, /// `ctx`, and backing storage remain valid for the lifetime of the /// returned handle. /// /// Returns NULL on any open-time error. pub fn zxc_seekable_open_reader(r: *const zxc_reader_t) -> *mut zxc_seekable; /// Attaches a dictionary to a seekable handle. The dictionary content /// must remain valid for the lifetime of the handle (or until replaced). /// /// # Safety /// - `s` must be a valid handle from [`zxc_seekable_open`] etc. /// - `dict` must be valid for `dict_size` bytes (or NULL with 0). /// - `dict_huf` must be NULL or valid for 128 bytes /// (`ZXC_HUF_TABLE_SIZE`). /// /// Returns `ZXC_OK` (0) on success, or a negative `zxc_error_t` code. pub fn zxc_seekable_set_dict( s: *mut zxc_seekable, dict: *const c_void, dict_size: usize, dict_huf: *const c_void, ) -> c_int; /// Returns the total number of data blocks in the archive (excluding EOF). pub fn zxc_seekable_get_num_blocks(s: *const zxc_seekable) -> u32; /// Returns the total decompressed size of the archive in bytes. pub fn zxc_seekable_get_decompressed_size(s: *const zxc_seekable) -> u64; /// Returns the on-disk compressed size of a specific block /// (block header + payload + optional per-block checksum). /// /// Returns 0 if `block_idx` is out of range. pub fn zxc_seekable_get_block_comp_size(s: *const zxc_seekable, block_idx: u32) -> u32; /// Returns the decompressed size of a specific block. /// /// Returns 0 if `block_idx` is out of range. pub fn zxc_seekable_get_block_decomp_size(s: *const zxc_seekable, block_idx: u32) -> u32; /// Decompresses `len` bytes starting at byte `offset` in the original /// uncompressed data. Only the blocks overlapping the requested range /// are read and decompressed. /// /// # Safety /// - `s` must be a valid handle returned by [`zxc_seekable_open`]. /// - `dst` must point to at least `dst_capacity` bytes. /// /// Returns `len` on success, or a negative `zxc_error_t` on failure. pub fn zxc_seekable_decompress_range( s: *mut zxc_seekable, dst: *mut c_void, dst_capacity: usize, offset: u64, len: usize, ) -> i64; /// Multi-threaded variant of [`zxc_seekable_decompress_range`]. /// /// Each worker owns its own decompression context and reads via `pread()` /// (POSIX) or `ReadFile()` (Windows) for lock-free concurrent I/O. /// Falls back to single-threaded mode when `n_threads <= 1` or the range /// spans a single block. /// /// # Safety /// - `s` must be a valid handle returned by [`zxc_seekable_open`]. /// - `dst` must point to at least `dst_capacity` bytes. pub fn zxc_seekable_decompress_range_mt( s: *mut zxc_seekable, dst: *mut c_void, dst_capacity: usize, offset: u64, len: usize, n_threads: c_int, ) -> i64; /// Frees a seekable handle and all associated resources. /// Safe to call with a null pointer. /// /// # Safety /// - `s` must be a pointer returned by [`zxc_seekable_open`] / /// [`zxc_seekable_open_file`], or null. pub fn zxc_seekable_free(s: *mut zxc_seekable); /// Low-level: writes a seek table (block header + entries) to `dst`. /// /// # Safety /// - `dst` must point to at least `dst_capacity` bytes. /// - `comp_sizes` must point to at least `num_blocks` entries. /// /// Returns bytes written, or a negative `zxc_error_t` on failure. pub fn zxc_write_seek_table( dst: *mut u8, dst_capacity: usize, comp_sizes: *const u32, num_blocks: u32, ) -> i64; /// Returns the encoded byte size of a seek table for `num_blocks` blocks. pub fn zxc_seek_table_size(num_blocks: u32) -> usize; } // ============================================================================= // Push Streaming API (single-threaded, caller-driven) // ============================================================================= /// Input buffer descriptor for push streaming /// (mirrors `zxc_inbuf_t` from `zxc_pstream.h`). #[repr(C)] #[derive(Debug)] pub struct zxc_inbuf_t { /// Caller-owned input bytes. pub src: *const c_void, /// Total bytes available in `src`. pub size: usize, /// Bytes already consumed by the library (in/out). pub pos: usize, } /// Output buffer descriptor for push streaming /// (mirrors `zxc_outbuf_t` from `zxc_pstream.h`). #[repr(C)] #[derive(Debug)] pub struct zxc_outbuf_t { /// Caller-owned output region. pub dst: *mut c_void, /// Total capacity available at `dst`. pub size: usize, /// Bytes already produced by the library (in/out). pub pos: usize, } /// Opaque push compression stream. Use [`zxc_cstream_create`] to create. #[repr(C)] pub struct zxc_cstream { _private: [u8; 0], } /// Opaque push decompression stream. Use [`zxc_dstream_create`] to create. #[repr(C)] pub struct zxc_dstream { _private: [u8; 0], } unsafe extern "C" { /// Creates a push compression stream. Returns NULL on allocation failure. /// Free with [`zxc_cstream_free`]. pub fn zxc_cstream_create(opts: *const zxc_compress_opts_t) -> *mut zxc_cstream; /// Releases a push compression stream. Safe to call with null. /// /// # Safety /// - `cs` must be a pointer returned by [`zxc_cstream_create`] (or null). pub fn zxc_cstream_free(cs: *mut zxc_cstream); /// Pushes input bytes into the stream and drains compressed output. /// /// Returns 0 if `in` was fully consumed and no compressed bytes remain /// pending; >0 number of bytes still pending; <0 on error. /// /// # Safety /// - `cs` must be a valid stream from [`zxc_cstream_create`]. /// - `out` and `in_` must point to valid `zxc_outbuf_t` / `zxc_inbuf_t`. pub fn zxc_cstream_compress( cs: *mut zxc_cstream, out: *mut zxc_outbuf_t, in_: *mut zxc_inbuf_t, ) -> i64; /// Finalises the stream: flushes pending data, writes EOF block + footer. /// /// Returns 0 when finalisation is complete; >0 bytes still pending; <0 /// on error. /// /// # Safety /// - `cs` must be a valid stream from [`zxc_cstream_create`]. /// - `out` must point to a valid `zxc_outbuf_t`. pub fn zxc_cstream_end(cs: *mut zxc_cstream, out: *mut zxc_outbuf_t) -> i64; /// Suggested input chunk size for best compressor throughput. pub fn zxc_cstream_in_size(cs: *const zxc_cstream) -> usize; /// Suggested output chunk size for the compressor. pub fn zxc_cstream_out_size(cs: *const zxc_cstream) -> usize; /// Creates a push decompression stream. Returns NULL on allocation /// failure. Free with [`zxc_dstream_free`]. pub fn zxc_dstream_create(opts: *const zxc_decompress_opts_t) -> *mut zxc_dstream; /// Releases a push decompression stream. Safe to call with null. /// /// # Safety /// - `ds` must be a pointer returned by [`zxc_dstream_create`] (or null). pub fn zxc_dstream_free(ds: *mut zxc_dstream); /// Pushes compressed input and drains decompressed output. /// /// Returns >0 number of decompressed bytes written this call; 0 if the /// stream is complete (DONE) or no progress is possible; <0 on error. /// /// # Safety /// - `ds` must be a valid stream from [`zxc_dstream_create`]. /// - `out` and `in_` must point to valid `zxc_outbuf_t` / `zxc_inbuf_t`. pub fn zxc_dstream_decompress( ds: *mut zxc_dstream, out: *mut zxc_outbuf_t, in_: *mut zxc_inbuf_t, ) -> i64; /// Returns 1 iff the decoder reached and validated the file footer. pub fn zxc_dstream_finished(ds: *const zxc_dstream) -> c_int; /// Suggested input chunk size for the decompressor. pub fn zxc_dstream_in_size(ds: *const zxc_dstream) -> usize; /// Suggested output chunk size for the decompressor. pub fn zxc_dstream_out_size(ds: *const zxc_dstream) -> usize; } // ============================================================================= // Tests // ============================================================================= #[cfg(test)] mod tests { /// The options structs above are hand-mirrored from zxc_opts.h. A C-side /// field change that is not replicated here silently shifts every later /// field (undefined behaviour at the FFI boundary); this test turns that /// drift into a hard failure. #[test] fn opts_layout_matches_library() { unsafe { assert_eq!( std::mem::size_of::(), super::zxc_compress_opts_size(), "zxc_compress_opts_t layout drift: update the Rust mirror in zxc-sys" ); assert_eq!( std::mem::size_of::(), super::zxc_decompress_opts_size(), "zxc_decompress_opts_t layout drift: update the Rust mirror in zxc-sys" ); } } use super::*; #[test] fn test_compress_bound() { unsafe { let bound = zxc_compress_bound(1024); // Should return a reasonable bound (input + overhead) assert!(bound > 1024); assert!(bound < 1024 * 2); // Should not be excessively large } } #[test] fn test_roundtrip() { // Use highly repetitive data that definitely compresses well let input: Vec = (0..4096) .map(|i| ((i % 16) as u8).wrapping_add(b'A')) .collect(); unsafe { // Allocate compression buffer let bound = zxc_compress_bound(input.len()) as usize; let mut compressed = vec![0u8; bound]; // Compress let copts = zxc_compress_opts_t { level: ZXC_LEVEL_DEFAULT, checksum_enabled: 1, ..Default::default() }; let compressed_size = zxc_compress( input.as_ptr() as *const c_void, input.len(), compressed.as_mut_ptr() as *mut c_void, compressed.len(), &copts, ); assert!(compressed_size > 0, "Compression failed"); // Highly repetitive data should compress significantly assert!((compressed_size as usize) < input.len() / 2, "Data should compress well"); // Get decompressed size let decompressed_size = zxc_get_decompressed_size( compressed.as_ptr() as *const c_void, compressed_size as usize, ); assert_eq!(decompressed_size as usize, input.len()); // Decompress let mut decompressed = vec![0u8; decompressed_size as usize]; let dopts = zxc_decompress_opts_t { checksum_enabled: 1, ..Default::default() }; let result_size = zxc_decompress( compressed.as_ptr() as *const c_void, compressed_size as usize, decompressed.as_mut_ptr() as *mut c_void, decompressed.len(), &dopts, ); assert_eq!(result_size, input.len() as i64); assert_eq!(&decompressed[..], &input[..]); } } #[test] fn test_all_levels() { let input = b"Test data for all compression levels - with some repetition \ to ensure compression works: BBBBBBBBBBBBBBBBBBBBBBBBBBBBB"; for level in [ ZXC_LEVEL_FASTEST, ZXC_LEVEL_FAST, ZXC_LEVEL_DEFAULT, ZXC_LEVEL_BALANCED, ZXC_LEVEL_COMPACT, ZXC_LEVEL_DENSITY, ZXC_LEVEL_ULTRA, ] { unsafe { let bound = zxc_compress_bound(input.len()) as usize; let mut compressed = vec![0u8; bound]; let copts = zxc_compress_opts_t { level, checksum_enabled: 1, ..Default::default() }; let compressed_size = zxc_compress( input.as_ptr() as *const c_void, input.len(), compressed.as_mut_ptr() as *mut c_void, compressed.len(), &copts, ); assert!( compressed_size > 0, "Compression failed at level {}", level ); } } } } zxc-0.13.0/wrappers/rust/zxc-sys/zxc/000077500000000000000000000000001522536370400174725ustar00rootroot00000000000000zxc-0.13.0/wrappers/rust/zxc-sys/zxc/include000077700000000000000000000000001522536370400234552../../../../includeustar00rootroot00000000000000zxc-0.13.0/wrappers/rust/zxc-sys/zxc/src/000077500000000000000000000000001522536370400202615ustar00rootroot00000000000000zxc-0.13.0/wrappers/rust/zxc-sys/zxc/src/lib000077700000000000000000000000001522536370400235142../../../../../src/libustar00rootroot00000000000000zxc-0.13.0/wrappers/rust/zxc/000077500000000000000000000000001522536370400160525ustar00rootroot00000000000000zxc-0.13.0/wrappers/rust/zxc/Cargo.toml000066400000000000000000000012261522536370400200030ustar00rootroot00000000000000[package] name = "zxc-compress" description = "ZXC: Package for high-performance asymmetric lossless compression built for ultra-fast decode" version.workspace = true authors.workspace = true edition.workspace = true license.workspace = true repository.workspace = true keywords.workspace = true categories.workspace = true readme = "README.md" [lib] name = "zxc" [dependencies] zxc_sys = { path = "../zxc-sys", version = "0.13.0", package = "zxc-compress-sys" } thiserror = "2.0" libc = "0.2" [target.'cfg(windows)'.dependencies] windows-sys = { version = "0.61.2", features = [ "Win32_Foundation", "Win32_System_Threading", ] } [features] default = [] zxc-0.13.0/wrappers/rust/zxc/README.md000066400000000000000000000060531522536370400173350ustar00rootroot00000000000000# zxc Safe Rust bindings to the **ZXC compression library** - a fast LZ77-based compressor optimized for high decompression speed. [![Crates.io](https://img.shields.io/crates/v/zxc.svg)](https://crates.io/crates/zxc) [![Documentation](https://docs.rs/zxc/badge.svg)](https://docs.rs/zxc) [![License](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](LICENSE) ## Quick Start ```rust use zxc::{compress, decompress, Level}; fn main() -> Result<(), zxc::Error> { let data = b"Hello, ZXC! This is some data to compress."; // Compress (no checksum for max speed) let compressed = compress(data, Level::Default, None)?; println!("Compressed {} -> {} bytes", data.len(), compressed.len()); // Decompress let decompressed = decompress(&compressed)?; assert_eq!(&decompressed[..], &data[..]); Ok(()) } ``` ## Compression Levels | Level | Speed | Ratio | Use Case | |-------|-------|-------|----------| | `Level::Fastest` | ★★★★★ | ★★☆☆☆ | Real-time, gaming | | `Level::Fast` | ★★★★☆ | ★★★☆☆ | Network, streaming | | `Level::Default` | ★★★☆☆ | ★★★★☆ | General purpose | | `Level::Balanced` | ★★☆☆☆ | ★★★★☆ | Archives | | `Level::Compact` | ★☆☆☆☆ | ★★★★★ | Storage, firmware | | `Level::Density` | ★☆☆☆☆ | ★★★★★ | High density (Huffman literals + optimal parser) | | `Level::Ultra` | ★☆☆☆☆ | ★★★★★ | Maximum density (Huffman literals + tokens, deep parse) | ## Features - **Fast decompression**: Optimized for read-heavy workloads - **7 compression levels**: Trade off speed vs ratio - **Optional checksums**: Disabled by default for maximum performance, enable for data integrity - **File streaming**: Multi-threaded compression/decompression for large files - **Zero-allocation API**: `compress_to` and `decompress_to` for buffer reuse - **Pure Rust API**: Safe, idiomatic interface over the C library ## Advanced Usage ### Pre-allocated Buffers ```rust use zxc::{compress_to, decompress_to, compress_bound, CompressOptions, DecompressOptions}; let data = b"Hello, world!"; // Compression let mut output = vec![0u8; compress_bound(data.len()) as usize]; let size = compress_to(data, &mut output, &CompressOptions::default())?; output.truncate(size); // Decompression let mut decompressed = vec![0u8; data.len()]; decompress_to(&output, &mut decompressed, &DecompressOptions::default())?; ``` ### Disable Checksum ```rust use zxc::{compress_with_options, decompress_with_options, CompressOptions, DecompressOptions, Level}; let opts = CompressOptions::with_level(Level::Fastest).without_checksum(); let compressed = compress_with_options(data, &opts)?; let decompressed = decompress_with_options(&compressed, &DecompressOptions::skip_checksum())?; ``` ### Query Decompressed Size ```rust use zxc::decompressed_size; if let Some(size) = decompressed_size(&compressed) { let mut buffer = vec![0u8; size as usize]; // ... } ``` ## License BSD-3-Clause - see [LICENSE](../../LICENSE) for details. zxc-0.13.0/wrappers/rust/zxc/examples/000077500000000000000000000000001522536370400176705ustar00rootroot00000000000000zxc-0.13.0/wrappers/rust/zxc/examples/file_compression.rs000066400000000000000000000050071522536370400236000ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ //! Example demonstrating file-based streaming compression and decompression. //! //! Run with: `cargo run --example file_compression` use std::fs; use std::io::Write; fn main() -> Result<(), Box> { println!("ZXC File Streaming Example\n"); // Create a test file with compressible data let test_data: Vec = (0..1024 * 1024) // 1 MB .map(|i| ((i % 256) ^ ((i / 256) % 256)) as u8) .collect(); let input_path = "/tmp/zxc_test_input.bin"; let compressed_path = "/tmp/zxc_test_compressed.zxc"; let output_path = "/tmp/zxc_test_output.bin"; // Write test data to file { let mut file = fs::File::create(input_path)?; file.write_all(&test_data)?; } // Get original file size let original_size = fs::metadata(input_path)?.len(); println!("Original file size: {} bytes", original_size); // Compress the file with multi-threading println!("\nCompressing with auto-detected threads..."); let compressed_bytes = zxc::compress_file( input_path, compressed_path, zxc::Level::Default, None, // Auto-detect CPU cores None, // Maximum performance (no checksum) )?; println!(" Compressed bytes written: {}", compressed_bytes); // Get compressed file size let compressed_size = fs::metadata(compressed_path)?.len(); let ratio = 100.0 * compressed_size as f64 / original_size as f64; println!(" Compressed file size: {} bytes ({:.1}%)", compressed_size, ratio); // Query the decompressed size from the file let reported_size = zxc::file_decompressed_size(compressed_path)?; println!("\n Reported decompressed size: {} bytes", reported_size); // Decompress the file println!("\nDecompressing..."); let decompressed_bytes = zxc::decompress_file( compressed_path, output_path, Some(4), // Use 4 threads )?; println!(" Decompressed bytes written: {}", decompressed_bytes); // Verify the result let output_data = fs::read(output_path)?; if output_data == test_data { println!("\n✓ Data integrity verified! Files match."); } else { println!("\n✗ ERROR: Data mismatch!"); } // Cleanup fs::remove_file(input_path)?; fs::remove_file(compressed_path)?; fs::remove_file(output_path)?; println!("\nDone."); Ok(()) } zxc-0.13.0/wrappers/rust/zxc/examples/simple.rs000066400000000000000000000034321522536370400215310ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ //! Simple example demonstrating ZXC compression and decompression. //! //! Run with: `cargo run --example simple` use zxc::{compress, decompress, decompressed_size, version_string, Level}; fn main() -> Result<(), zxc::Error> { println!("ZXC Rust Wrapper v{}\n", version_string()); // Sample data with some repetition (compresses well) let original = b"Hello, ZXC! This is a demonstration of the Rust wrapper. \ Let's add some repetitive content to show compression: \ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA \ BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"; println!("Original size: {} bytes", original.len()); // Test all compression levels for level in Level::all() { let compressed = compress(original, *level, None)?; let ratio = (compressed.len() as f64 / original.len() as f64) * 100.0; println!( " Level {:?}: {} bytes ({:.1}%)", level, compressed.len(), ratio ); } // Full roundtrip demonstration println!("\nRoundtrip test:"); let compressed = compress(original, Level::Default, None)?; // Query size before decompression let size = decompressed_size(&compressed).expect("valid compressed data"); println!(" Reported decompressed size: {} bytes", size); let decompressed = decompress(&compressed)?; println!(" Actual decompressed size: {} bytes", decompressed.len()); // Verify data integrity assert_eq!(&decompressed[..], &original[..]); println!(" ✓ Data integrity verified!"); Ok(()) } zxc-0.13.0/wrappers/rust/zxc/src/000077500000000000000000000000001522536370400166415ustar00rootroot00000000000000zxc-0.13.0/wrappers/rust/zxc/src/ctx.rs000066400000000000000000000143721522536370400200140ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ //! Block API: reusable single-block compression / decompression contexts. use std::ffi::c_void; use crate::error::error_from_code; use crate::{CompressOptions, DecompressOptions, Error, Result}; /// Reusable compression context for the Block API. /// /// Eliminates per-call allocation overhead when compressing many blocks. /// Internally wraps an opaque `zxc_cctx*` freed automatically on drop. /// /// # Example /// /// ```rust,ignore /// use zxc::{Cctx, CompressOptions, Level}; /// /// let mut cctx = Cctx::new(None)?; /// let opts = CompressOptions::default().with_level(Level::Default); /// let mut out = vec![0u8; zxc::compress_block_bound(block.len()) as usize]; /// let n = cctx.compress_block(block, &mut out, &opts)?; /// ``` pub struct Cctx { inner: *mut zxc_sys::zxc_cctx, } // SAFETY: the underlying handle is opaque and the library states contexts // must not be shared between threads; `Send` is safe, `Sync` is not. unsafe impl Send for Cctx {} impl Cctx { /// Creates a new compression context. /// /// When `opts` is `Some`, internal buffers are pre-allocated with those /// parameters. When `None`, allocation is deferred to first use. pub fn new(opts: Option<&CompressOptions>) -> Result { let c_opts = opts.map(|o| zxc_sys::zxc_compress_opts_t { level: o.level as i32, checksum_enabled: o.checksum as i32, seekable: o.seekable as i32, ..Default::default() }); let ptr = unsafe { zxc_sys::zxc_create_cctx( c_opts .as_ref() .map(|o| o as *const _) .unwrap_or(std::ptr::null()), ) }; if ptr.is_null() { Err(Error::Memory) } else { Ok(Self { inner: ptr }) } } /// Compresses a single block (no file framing). /// /// Output format: 8-byte block header + payload (+ optional 4-byte checksum). /// Use [`compress_block_bound`] to size `dst`. pub fn compress_block( &mut self, src: &[u8], dst: &mut [u8], opts: &CompressOptions, ) -> Result { let copts = zxc_sys::zxc_compress_opts_t { level: opts.level as i32, checksum_enabled: opts.checksum as i32, seekable: opts.seekable as i32, ..Default::default() }; let res = unsafe { zxc_sys::zxc_compress_block( self.inner, src.as_ptr() as *const c_void, src.len(), dst.as_mut_ptr() as *mut c_void, dst.len(), &copts, ) }; if res < 0 { Err(error_from_code(res)) } else { Ok(res as usize) } } } impl Drop for Cctx { fn drop(&mut self) { unsafe { zxc_sys::zxc_free_cctx(self.inner) }; } } /// Reusable decompression context for the Block API. /// /// Internally wraps an opaque `zxc_dctx*` freed automatically on drop. pub struct Dctx { inner: *mut zxc_sys::zxc_dctx, } unsafe impl Send for Dctx {} impl Dctx { /// Creates a new decompression context. pub fn new() -> Result { let ptr = unsafe { zxc_sys::zxc_create_dctx() }; if ptr.is_null() { Err(Error::Memory) } else { Ok(Self { inner: ptr }) } } /// Decompresses a single block produced by [`Cctx::compress_block`]. /// /// `dst` should be at least [`decompress_block_bound(uncompressed_size)`] /// (`decompress_block_bound`) to enable the fast path. For strictly-sized /// destinations, use [`Dctx::decompress_block_safe`]. pub fn decompress_block( &mut self, src: &[u8], dst: &mut [u8], opts: &DecompressOptions, ) -> Result { let dopts = zxc_sys::zxc_decompress_opts_t { checksum_enabled: opts.verify_checksum as i32, ..Default::default() }; let res = unsafe { zxc_sys::zxc_decompress_block( self.inner, src.as_ptr() as *const c_void, src.len(), dst.as_mut_ptr() as *mut c_void, dst.len(), &dopts, ) }; if res < 0 { Err(error_from_code(res)) } else { Ok(res as usize) } } /// Strict-sized variant of [`Dctx::decompress_block`]: accepts /// `dst.len() == uncompressed_size` exactly (no tail pad required). /// Slightly slower than the fast path; output is bit-identical. pub fn decompress_block_safe( &mut self, src: &[u8], dst: &mut [u8], opts: &DecompressOptions, ) -> Result { let dopts = zxc_sys::zxc_decompress_opts_t { checksum_enabled: opts.verify_checksum as i32, ..Default::default() }; let res = unsafe { zxc_sys::zxc_decompress_block_safe( self.inner, src.as_ptr() as *const c_void, src.len(), dst.as_mut_ptr() as *mut c_void, dst.len(), &dopts, ) }; if res < 0 { Err(error_from_code(res)) } else { Ok(res as usize) } } } impl Drop for Dctx { fn drop(&mut self) { unsafe { zxc_sys::zxc_free_dctx(self.inner) }; } } /// Returns the maximum compressed size for a single block of `input_size` /// bytes (no file framing). pub fn compress_block_bound(input_size: usize) -> u64 { unsafe { zxc_sys::zxc_compress_block_bound(input_size) } } /// Returns the minimum destination buffer size required by /// [`Dctx::decompress_block`] for a block of `uncompressed_size` bytes. /// /// Accounts for the wild-copy tail pad used by the fast decoder. For a /// strictly-sized destination, use [`Dctx::decompress_block_safe`] instead /// and size the buffer to exactly the uncompressed length. pub fn decompress_block_bound(uncompressed_size: usize) -> u64 { unsafe { zxc_sys::zxc_decompress_block_bound(uncompressed_size) } } zxc-0.13.0/wrappers/rust/zxc/src/dict.rs000066400000000000000000000375241522536370400201450ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ //! Pre-trained dictionary support. //! //! Dictionaries improve compression ratio on small, similar payloads by //! prefilling the LZ77 window with representative byte sequences. Train one //! from a corpus with [`train_dict`], then pass the raw content to //! [`CompressOptions::with_dict`](crate::CompressOptions::with_dict) and the //! matching //! [`DecompressOptions::with_dict`](crate::DecompressOptions::with_dict). //! //! Dictionaries can also be serialized to the `.zxd` file format with //! [`dict_save`] and parsed back with [`dict_load`]. Every dictionary has a //! deterministic 32-bit ID ([`dict_id`]) that the ZXC archive header records, //! so a decoder can verify the right dictionary was supplied. //! //! # Example //! //! ```rust //! use zxc::{train_dict, dict_id, get_dict_id, CompressOptions, DecompressOptions, //! compress_with_options, decompress_with_options, ZXC_DICT_SIZE_MAX}; //! //! let samples: Vec> = (0..8) //! .map(|i| format!("{{\"event\":\"login\",\"user\":{i},\"ok\":true}}").into_bytes()) //! .collect(); //! let refs: Vec<&[u8]> = samples.iter().map(|s| s.as_slice()).collect(); //! //! let dict = train_dict(&refs, ZXC_DICT_SIZE_MAX)?; //! assert!(!dict.is_empty()); //! //! let copts = CompressOptions::default().with_dict(dict.clone()); //! let archive = compress_with_options(&samples[0], &copts)?; //! assert_eq!(dict_id(&dict), get_dict_id(&archive)); //! //! let dopts = DecompressOptions::default().with_dict(dict); //! let restored = decompress_with_options(&archive, &dopts)?; //! assert_eq!(restored, samples[0]); //! # Ok::<(), zxc::Error>(()) //! ``` use std::ffi::c_void; pub use zxc_sys::{ZXC_HUF_TABLE_SIZE, ZXC_DICT_SIZE_MAX}; use crate::error::error_from_code; use crate::{Error, Result}; /// Trains a dictionary from a corpus of samples. /// /// `samples` should be several representative payloads of the kind you intend /// to compress (e.g. similar JSON records). `max_size` caps the trained /// dictionary's content size in bytes; pass [`ZXC_DICT_SIZE_MAX`] for the /// largest allowed dictionary. /// /// Returns the raw dictionary content, ready to hand to /// [`CompressOptions::with_dict`](crate::CompressOptions::with_dict). /// /// # Errors /// /// Returns an [`Error`] if training fails (e.g. no usable samples). pub fn train_dict(samples: &[&[u8]], max_size: usize) -> Result> { let cap = max_size.clamp(1, ZXC_DICT_SIZE_MAX); let ptrs: Vec<*const c_void> = samples.iter().map(|s| s.as_ptr() as *const c_void).collect(); let sizes: Vec = samples.iter().map(|s| s.len()).collect(); let mut buf = vec![0u8; cap]; let written = unsafe { zxc_sys::zxc_train_dict( ptrs.as_ptr(), sizes.as_ptr(), samples.len(), buf.as_mut_ptr() as *mut c_void, buf.len(), ) }; if written < 0 { return Err(error_from_code(written)); } if written == 0 { return Err(Error::InvalidData); } buf.truncate(written as usize); Ok(buf) } /// Computes the deterministic 32-bit dictionary ID for raw content. /// /// Returns 0 for empty content. pub fn dict_id(content: &[u8]) -> u32 { unsafe { zxc_sys::zxc_dict_id( content.as_ptr() as *const c_void, content.len(), std::ptr::null(), ) } } /// Returns the dictionary ID a ZXC `.zxc` archive requires. /// /// Returns 0 if the archive was produced without a dictionary, or if the /// buffer is not a valid archive. pub fn get_dict_id(archive: &[u8]) -> u32 { unsafe { zxc_sys::zxc_get_dict_id(archive.as_ptr() as *const c_void, archive.len()) } } /// Returns the dictionary ID stored in a `.zxd` file buffer. /// /// Returns 0 if the buffer is not a valid `.zxd` file. pub fn dict_get_id(zxd: &[u8]) -> u32 { unsafe { zxc_sys::zxc_dict_get_id(zxd.as_ptr() as *const c_void, zxd.len()) } } /// Trains the shared literal Huffman table for an already-trained dictionary. /// /// Compresses the samples with `dict` and derives canonical Huffman code /// lengths from the real post-LZ literal distribution. The returned 128-byte /// packed table is required by [`dict_save`] and can be attached to /// [`CompressOptions::with_dict_huf`](crate::CompressOptions::with_dict_huf) / /// [`DecompressOptions::with_dict_huf`](crate::DecompressOptions::with_dict_huf). /// /// # Errors /// /// Returns an [`Error`] if training fails (e.g. no usable samples). pub fn train_dict_huf(samples: &[&[u8]], dict: &[u8]) -> Result<[u8; ZXC_HUF_TABLE_SIZE]> { let ptrs: Vec<*const c_void> = samples.iter().map(|s| s.as_ptr() as *const c_void).collect(); let sizes: Vec = samples.iter().map(|s| s.len()).collect(); let mut huf = [0u8; ZXC_HUF_TABLE_SIZE]; let rc = unsafe { zxc_sys::zxc_train_dict_huf( ptrs.as_ptr(), sizes.as_ptr(), samples.len(), dict.as_ptr() as *const c_void, dict.len(), huf.as_mut_ptr(), ) }; if rc != zxc_sys::ZXC_OK { return Err(error_from_code(rc as i64)); } Ok(huf) } /// Serializes dictionary content and its shared Huffman table to the `.zxd` /// file format. /// /// `huf_lengths` is the mandatory 128-byte packed table from /// [`train_dict_huf`]. The stored dict_id covers both content and table. /// /// # Errors /// /// Returns an [`Error`] if `huf_lengths` is not exactly /// [`ZXC_HUF_TABLE_SIZE`] bytes, if the content exceeds /// [`ZXC_DICT_SIZE_MAX`], or if serialization otherwise fails. pub fn dict_save(content: &[u8], huf_lengths: &[u8]) -> Result> { if huf_lengths.len() != ZXC_HUF_TABLE_SIZE { return Err(Error::InvalidData); } let bound = unsafe { zxc_sys::zxc_dict_save_bound(content.len()) }; let mut buf = vec![0u8; bound]; let written = unsafe { zxc_sys::zxc_dict_save( content.as_ptr() as *const c_void, content.len(), huf_lengths.as_ptr() as *const c_void, buf.as_mut_ptr() as *mut c_void, buf.len(), ) }; if written < 0 { return Err(error_from_code(written)); } buf.truncate(written as usize); Ok(buf) } /// Returns an owned copy of the shared Huffman table stored in a `.zxd` /// buffer, or `None` if the buffer is not a valid `.zxd` file. pub fn dict_huf(zxd: &[u8]) -> Option<[u8; ZXC_HUF_TABLE_SIZE]> { let p = unsafe { zxc_sys::zxc_dict_huf(zxd.as_ptr() as *const c_void, zxd.len()) }; if p.is_null() { return None; } let mut huf = [0u8; ZXC_HUF_TABLE_SIZE]; // The pointer aims into `zxd` (zero-copy); copy out for an owned result. huf.copy_from_slice(unsafe { std::slice::from_raw_parts(p as *const u8, ZXC_HUF_TABLE_SIZE) }); Some(huf) } /// Parses a `.zxd` file buffer, returning owned dictionary content and its ID. /// /// The C API hands back pointers into the input buffer (zero-copy); this /// wrapper copies the content into an owned [`Vec`] so the returned data is /// independent of the input. Prefer [`Dictionary::load`] for the full /// (content, table, id) bundle. /// /// # Errors /// /// Returns an [`Error`] if the buffer is not a valid `.zxd` file. pub fn dict_load(zxd: &[u8]) -> Result<(Vec, u32)> { let d = Dictionary::load(zxd)?; Ok((d.content, d.id)) } /// A trained dictionary: the LZ-window content and its shared literal Huffman /// table, bundled as one object so callers never juggle the pair by hand. /// /// Create one with [`Dictionary::train`] (from samples) or /// [`Dictionary::load`] (from `.zxd` bytes); attach it with /// [`CompressOptions::with_dictionary`](crate::CompressOptions::with_dictionary) / /// [`DecompressOptions::with_dictionary`](crate::DecompressOptions::with_dictionary). #[derive(Debug, Clone, PartialEq, Eq)] pub struct Dictionary { content: Vec, huf: [u8; ZXC_HUF_TABLE_SIZE], id: u32, } impl Dictionary { /// Trains a complete dictionary (content + shared table) from samples. pub fn train(samples: &[&[u8]]) -> Result { let ptrs: Vec<*const c_void> = samples.iter().map(|s| s.as_ptr() as *const c_void).collect(); let sizes: Vec = samples.iter().map(|s| s.len()).collect(); let cap = unsafe { zxc_sys::zxc_dict_save_bound(ZXC_DICT_SIZE_MAX) }; let mut zxd = vec![0u8; cap]; let written = unsafe { zxc_sys::zxc_dict_train( ptrs.as_ptr(), sizes.as_ptr(), samples.len(), zxd.as_mut_ptr() as *mut c_void, zxd.len(), ) }; if written <= 0 { return Err(if written < 0 { error_from_code(written) } else { Error::InvalidData }); } zxd.truncate(written as usize); Self::load(&zxd) } /// Parses `.zxd` bytes into an owned dictionary. pub fn load(zxd: &[u8]) -> Result { let mut content_ptr: *const c_void = std::ptr::null(); let mut content_size: usize = 0; let mut huf_ptr: *const c_void = std::ptr::null(); let mut id: u32 = 0; let rc = unsafe { zxc_sys::zxc_dict_load( zxd.as_ptr() as *const c_void, zxd.len(), &mut content_ptr, &mut content_size, &mut huf_ptr, &mut id, ) }; if rc != zxc_sys::ZXC_OK { return Err(error_from_code(rc as i64)); } // The pointers alias into `zxd` (zero-copy); copy out into owned // buffers so the result does not depend on `zxd` staying alive. let content = if content_ptr.is_null() || content_size == 0 { Vec::new() } else { unsafe { std::slice::from_raw_parts(content_ptr as *const u8, content_size).to_vec() } }; let mut huf = [0u8; ZXC_HUF_TABLE_SIZE]; if !huf_ptr.is_null() { huf.copy_from_slice(unsafe { std::slice::from_raw_parts(huf_ptr as *const u8, ZXC_HUF_TABLE_SIZE) }); } Ok(Self { content, huf, id }) } /// Serializes this dictionary back to `.zxd` bytes. pub fn save(&self) -> Result> { dict_save(&self.content, &self.huf) } /// The dictionary ID binding the (content, table) pair, as recorded in /// `.zxd` files and archive headers. pub fn id(&self) -> u32 { self.id } /// The raw LZ-window content bytes. pub fn content(&self) -> &[u8] { &self.content } /// The 128-byte shared literal Huffman table. pub fn huf(&self) -> &[u8; ZXC_HUF_TABLE_SIZE] { &self.huf } } #[cfg(test)] mod tests { use super::*; use crate::{ compress_with_options, decompress_with_options, CompressOptions, DecompressOptions, }; fn sample_corpus() -> Vec> { (0..16) .map(|i| { format!( "{{\"event\":\"login\",\"user_id\":{i},\"status\":\"ok\",\"region\":\"eu-west\"}}" ) .into_bytes() }) .collect() } fn trained() -> Vec { let corpus = sample_corpus(); let refs: Vec<&[u8]> = corpus.iter().map(|s| s.as_slice()).collect(); train_dict(&refs, ZXC_DICT_SIZE_MAX).expect("train_dict failed") } #[test] fn train_produces_nonempty() { let dict = trained(); assert!(!dict.is_empty(), "trained dictionary must be non-empty"); assert!(dict.len() <= ZXC_DICT_SIZE_MAX); } #[test] fn compress_decompress_with_dict_roundtrips() { let dict = trained(); let sample = sample_corpus()[3].clone(); let copts = CompressOptions::default().with_dict(dict.clone()); let archive = compress_with_options(&sample, &copts).expect("compress with dict"); let dopts = DecompressOptions { verify_checksum: true, dict: Some(dict), dict_huf: None, }; let restored = decompress_with_options(&archive, &dopts).expect("decompress with dict"); assert_eq!(restored, sample); } #[test] fn decompress_without_dict_fails() { let dict = trained(); let sample = sample_corpus()[5].clone(); let copts = CompressOptions::default().with_dict(dict); let archive = compress_with_options(&sample, &copts).expect("compress with dict"); // No dict supplied: the decoder must refuse rather than silently // produce wrong output. let res = decompress_with_options(&archive, &DecompressOptions::default()); assert!(res.is_err(), "decompress without dict must fail"); } #[test] fn ids_are_consistent() { let dict = trained(); let sample = sample_corpus()[0].clone(); let copts = CompressOptions::default().with_dict(dict.clone()); let archive = compress_with_options(&sample, &copts).expect("compress with dict"); let id_content = dict_id(&dict); let id_archive = get_dict_id(&archive); assert_ne!(id_content, 0, "dict id must be non-zero"); assert_eq!(id_content, id_archive, "content id == archive id"); /* The .zxd id binds (content, table): it differs from the content id. */ let corpus = sample_corpus(); let refs: Vec<&[u8]> = corpus.iter().map(|s| s.as_slice()).collect(); let huf = train_dict_huf(&refs, &dict).expect("train_dict_huf failed"); let zxd = dict_save(&dict, &huf).expect("dict_save failed"); let id_zxd = dict_get_id(&zxd); assert_ne!(id_zxd, 0, ".zxd id must be non-zero"); assert_ne!(id_content, id_zxd, ".zxd id covers the table"); } #[test] fn save_load_roundtrip() { let dict = trained(); let corpus = sample_corpus(); let refs: Vec<&[u8]> = corpus.iter().map(|s| s.as_slice()).collect(); let huf = train_dict_huf(&refs, &dict).expect("train_dict_huf failed"); let zxd = dict_save(&dict, &huf).expect("dict_save failed"); let (content, id) = dict_load(&zxd).expect("dict_load failed"); assert_eq!(content, dict, "loaded content must equal original"); assert_eq!(id, dict_get_id(&zxd), "loaded id must match stored id"); assert_eq!( dict_huf(&zxd).expect("table must be present"), huf, "loaded table must equal original" ); } #[test] fn load_rejects_garbage() { let garbage = vec![0u8; 64]; assert!(dict_load(&garbage).is_err()); } #[test] fn dictionary_object_roundtrip() { let corpus = sample_corpus(); let refs: Vec<&[u8]> = corpus.iter().map(|s| s.as_slice()).collect(); // One-call train, save/load, and the bundle matches the primitives. let d = Dictionary::train(&refs).expect("Dictionary::train failed"); assert!(!d.content().is_empty()); assert_ne!(d.id(), 0); let zxd = d.save().expect("save failed"); let d2 = Dictionary::load(&zxd).expect("Dictionary::load failed"); assert_eq!(d, d2); assert_eq!(d.id(), dict_get_id(&zxd)); // Options take the bundle in one call; round-trip + id binding. let sample = corpus[3].clone(); let copts = crate::CompressOptions::with_level(crate::Level::Density) .with_dictionary(&d); let archive = compress_with_options(&sample, &copts).expect("compress"); let dopts = DecompressOptions::default().with_dictionary(&d); let restored = decompress_with_options(&archive, &dopts).expect("decompress"); assert_eq!(restored, sample); // Without the table (content only), the id no longer matches. let bare = DecompressOptions::default().with_dict(d.content().to_vec()); assert!(decompress_with_options(&archive, &bare).is_err()); } } zxc-0.13.0/wrappers/rust/zxc/src/error.rs000066400000000000000000000103351522536370400203420ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ //! Error types and code mapping shared across the crate. use zxc_sys::{ ZXC_ERROR_BAD_BLOCK_SIZE, ZXC_ERROR_BAD_BLOCK_TYPE, ZXC_ERROR_BAD_CHECKSUM, ZXC_ERROR_BAD_HEADER, ZXC_ERROR_BAD_LEVEL, ZXC_ERROR_BAD_MAGIC, ZXC_ERROR_BAD_OFFSET, ZXC_ERROR_BAD_VERSION, ZXC_ERROR_CORRUPT_DATA, ZXC_ERROR_DICT_MISMATCH, ZXC_ERROR_DICT_REQUIRED, ZXC_ERROR_DICT_TOO_LARGE, ZXC_ERROR_DST_TOO_SMALL, ZXC_ERROR_IO, ZXC_ERROR_MEMORY, ZXC_ERROR_NULL_INPUT, ZXC_ERROR_OVERFLOW, ZXC_ERROR_SRC_TOO_SMALL, }; /// Errors that can occur during ZXC operations. #[derive(Debug, Clone, thiserror::Error)] pub enum Error { /// Memory allocation failure #[error("memory allocation failed")] Memory, /// Destination buffer too small #[error("destination buffer too small")] DstTooSmall, /// Source buffer too small or truncated input #[error("source buffer too small or truncated")] SrcTooSmall, /// Invalid magic word in file header #[error("invalid magic word in header")] BadMagic, /// Unsupported file format version #[error("unsupported file format version")] BadVersion, /// Corrupted or invalid header (CRC mismatch) #[error("corrupted or invalid header")] BadHeader, /// Block or global checksum verification failed #[error("checksum verification failed")] BadChecksum, /// Corrupted compressed data #[error("corrupted compressed data")] CorruptData, /// Invalid match offset during decompression #[error("invalid match offset")] BadOffset, /// Buffer overflow detected during processing #[error("buffer overflow detected")] Overflow, /// Read/write/seek failure on file #[error("I/O error")] Io, /// Required input pointer is NULL #[error("null input pointer")] NullInput, /// Unknown or unexpected block type #[error("unknown block type")] BadBlockType, /// Invalid block size #[error("invalid block size")] BadBlockSize, /// The archive requires a dictionary but none was provided #[error("archive requires a dictionary but none was provided")] DictRequired, /// The provided dictionary does not match the archive's dictionary ID #[error("dictionary ID does not match the archive header")] DictMismatch, /// The dictionary exceeds the maximum allowed size #[error("dictionary exceeds maximum allowed size")] DictTooLarge, /// The compression level is out of range or unsupported by this context #[error("compression level out of range")] BadLevel, /// The requested options are not supported by this API #[error("unsupported option: {0}")] Unsupported(&'static str), /// The compressed data appears to be invalid or truncated #[error("invalid compressed data")] InvalidData, /// Unknown error code from C library #[error("unknown error (code: {0})")] Unknown(i32), } /// Convert a negative error code from the C library to a Rust [`Error`]. pub(crate) fn error_from_code(code: i64) -> Error { match code as i32 { ZXC_ERROR_MEMORY => Error::Memory, ZXC_ERROR_DST_TOO_SMALL => Error::DstTooSmall, ZXC_ERROR_SRC_TOO_SMALL => Error::SrcTooSmall, ZXC_ERROR_BAD_MAGIC => Error::BadMagic, ZXC_ERROR_BAD_VERSION => Error::BadVersion, ZXC_ERROR_BAD_HEADER => Error::BadHeader, ZXC_ERROR_BAD_CHECKSUM => Error::BadChecksum, ZXC_ERROR_CORRUPT_DATA => Error::CorruptData, ZXC_ERROR_BAD_OFFSET => Error::BadOffset, ZXC_ERROR_OVERFLOW => Error::Overflow, ZXC_ERROR_IO => Error::Io, ZXC_ERROR_NULL_INPUT => Error::NullInput, ZXC_ERROR_BAD_BLOCK_TYPE => Error::BadBlockType, ZXC_ERROR_BAD_BLOCK_SIZE => Error::BadBlockSize, ZXC_ERROR_DICT_REQUIRED => Error::DictRequired, ZXC_ERROR_DICT_MISMATCH => Error::DictMismatch, ZXC_ERROR_DICT_TOO_LARGE => Error::DictTooLarge, ZXC_ERROR_BAD_LEVEL => Error::BadLevel, _ => Error::Unknown(code as i32), } } /// Result type for ZXC operations. pub type Result = std::result::Result; zxc-0.13.0/wrappers/rust/zxc/src/file.rs000066400000000000000000000501561522536370400201350ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ //! File-based multi-threaded streaming API. use std::fs::File; use std::io; use std::path::Path; #[cfg(unix)] use std::os::unix::io::AsRawFd; use crate::error::error_from_code; use crate::{Error, Level}; /// Options for streaming compression operations. #[derive(Debug, Clone)] pub struct StreamCompressOptions { /// Compression level (default: `Level::Default`) pub level: Level, /// Number of worker threads (default: `None` = auto-detect CPU cores) pub threads: Option, /// Enable checksum for data integrity (default: `true`) pub checksum: bool, /// Enable seek table for random-access decompression (default: `false`) pub seekable: bool, } impl Default for StreamCompressOptions { fn default() -> Self { Self { level: Level::Default, threads: None, checksum: true, seekable: false, } } } impl StreamCompressOptions { /// Create options with the specified compression level. pub fn with_level(level: Level) -> Self { Self { level, ..Default::default() } } /// Set the number of worker threads. pub fn threads(mut self, n: usize) -> Self { self.threads = Some(n); self } /// Disable checksum computation. pub fn without_checksum(mut self) -> Self { self.checksum = false; self } /// Enable seek table for random-access decompression. pub fn with_seekable(mut self) -> Self { self.seekable = true; self } } /// Options for streaming decompression operations. #[derive(Debug, Clone)] pub struct StreamDecompressOptions { /// Number of worker threads (default: `None` = auto-detect CPU cores) pub threads: Option, /// Verify checksum during decompression (default: `true`) pub verify_checksum: bool, } impl Default for StreamDecompressOptions { fn default() -> Self { Self { threads: None, verify_checksum: true, } } } impl StreamDecompressOptions { /// Set the number of worker threads. pub fn threads(mut self, n: usize) -> Self { self.threads = Some(n); self } /// Skip checksum verification. pub fn skip_checksum(mut self) -> Self { self.verify_checksum = false; self } } /// Errors specific to the streaming file API. #[derive(Debug, thiserror::Error)] pub enum StreamError { /// I/O error during file operations #[error("I/O error: {0}")] Io(#[from] io::Error), /// Error from buffer operations #[error("buffer error: {0}")] BufferError(#[from] Error), /// Streaming compression failed #[error("stream compression failed")] CompressionFailed, /// Streaming decompression failed #[error("stream decompression failed")] DecompressionFailed, /// Invalid compressed file #[error("invalid compressed file")] InvalidFile, } /// Result type for streaming operations. pub type StreamResult = std::result::Result; /// Convert a Rust File to a C FILE* for read operations. /// /// This function duplicates the file descriptor before passing it to fdopen, /// so the returned FILE* owns its own fd and must be closed with fclose(). #[cfg(unix)] unsafe fn file_to_c_file_read(file: &File) -> *mut libc::FILE { let fd = file.as_raw_fd(); // Duplicate the fd so C FILE* has its own ownership let dup_fd = unsafe { libc::dup(fd) }; if dup_fd < 0 { return std::ptr::null_mut(); } let file_ptr = unsafe { libc::fdopen(dup_fd, c"rb".as_ptr()) }; if file_ptr.is_null() { // fdopen failed, close the duplicated fd to avoid leak unsafe { libc::close(dup_fd); } } file_ptr } /// Convert a Rust File to a C FILE* for write operations. /// /// This function duplicates the file descriptor before passing it to fdopen, /// so the returned FILE* owns its own fd and must be closed with fclose(). #[cfg(unix)] unsafe fn file_to_c_file_write(file: &File) -> *mut libc::FILE { let fd = file.as_raw_fd(); // Duplicate the fd so C FILE* has its own ownership let dup_fd = unsafe { libc::dup(fd) }; if dup_fd < 0 { return std::ptr::null_mut(); } let file_ptr = unsafe { libc::fdopen(dup_fd, c"wb".as_ptr()) }; if file_ptr.is_null() { // fdopen failed, close the duplicated fd to avoid leak unsafe { libc::close(dup_fd); } } file_ptr } /// Convert a Rust File to a C FILE* for read operations (Windows). /// /// This function duplicates the file handle before passing it to the C runtime, /// so the returned FILE* owns its own handle and must be closed with fclose(). #[cfg(windows)] unsafe fn file_to_c_file_read(file: &File) -> *mut libc::FILE { use std::os::windows::io::AsRawHandle; let handle = file.as_raw_handle(); // Duplicate the handle so C FILE* has its own ownership let mut dup_handle: *mut std::ffi::c_void = std::ptr::null_mut(); let result = unsafe { windows_sys::Win32::Foundation::DuplicateHandle( windows_sys::Win32::System::Threading::GetCurrentProcess(), handle as *mut std::ffi::c_void, windows_sys::Win32::System::Threading::GetCurrentProcess(), &mut dup_handle, 0, 0, windows_sys::Win32::Foundation::DUPLICATE_SAME_ACCESS, ) }; if result == 0 { return std::ptr::null_mut(); } let fd = unsafe { libc::open_osfhandle(dup_handle as libc::intptr_t, libc::O_RDONLY) }; if fd < 0 { // open_osfhandle failed, close the duplicated handle to avoid leak unsafe { windows_sys::Win32::Foundation::CloseHandle(dup_handle); } return std::ptr::null_mut(); } let file_ptr = unsafe { libc::fdopen(fd, c"rb".as_ptr()) }; if file_ptr.is_null() { // fdopen failed, close the fd (which will close the handle) unsafe { libc::close(fd); } } file_ptr } /// Convert a Rust File to a C FILE* for write operations (Windows). /// /// This function duplicates the file handle before passing it to the C runtime, /// so the returned FILE* owns its own handle and must be closed with fclose(). #[cfg(windows)] unsafe fn file_to_c_file_write(file: &File) -> *mut libc::FILE { use std::os::windows::io::AsRawHandle; let handle = file.as_raw_handle(); // Duplicate the handle so C FILE* has its own ownership let mut dup_handle: *mut std::ffi::c_void = std::ptr::null_mut(); let result = unsafe { windows_sys::Win32::Foundation::DuplicateHandle( windows_sys::Win32::System::Threading::GetCurrentProcess(), handle as *mut std::ffi::c_void, windows_sys::Win32::System::Threading::GetCurrentProcess(), &mut dup_handle, 0, 0, windows_sys::Win32::Foundation::DUPLICATE_SAME_ACCESS, ) }; if result == 0 { return std::ptr::null_mut(); } let fd = unsafe { libc::open_osfhandle(dup_handle as libc::intptr_t, libc::O_WRONLY) }; if fd < 0 { // open_osfhandle failed, close the duplicated handle to avoid leak unsafe { windows_sys::Win32::Foundation::CloseHandle(dup_handle); } return std::ptr::null_mut(); } let file_ptr = unsafe { libc::fdopen(fd, c"wb".as_ptr()) }; if file_ptr.is_null() { // fdopen failed, close the fd (which will close the handle) unsafe { libc::close(fd); } } file_ptr } /// Compresses a file using multi-threaded streaming. /// /// This is the recommended method for compressing large files, as it: /// - Processes data in chunks without loading the entire file into memory /// - Uses multiple CPU cores for parallel compression /// - Provides better throughput for files larger than a few MB /// /// # Arguments /// /// * `input` - Path to the input file /// * `output` - Path to the output file /// * `level` - Compression level /// * `threads` - Number of threads (`None` = auto-detect CPU cores) /// * `checksum` - Optional checksum for data integrity (`None` = disabled for maximum performance) /// /// # Example /// /// ```rust,no_run /// use zxc::{compress_file, Level}; /// /// // Maximum performance (no checksum, auto threads) /// let bytes = compress_file("input.bin", "output.zxc", Level::Default, None, None)?; /// /// // With data integrity verification /// let bytes = compress_file("input.bin", "output.zxc", Level::Default, None, Some(true))?; /// /// // Custom configuration /// let bytes = compress_file("input.bin", "output.zxc", Level::Compact, Some(4), Some(true))?; /// # Ok::<(), zxc::StreamError>(()) /// ``` pub fn compress_file>( input: P, output: P, level: Level, threads: Option, checksum: Option, ) -> StreamResult { compress_file_with_options( input, output, &StreamCompressOptions { level, threads, checksum: checksum.unwrap_or(false), seekable: false, }, ) } /// Compresses a file using multi-threaded streaming, honouring `opts`. /// /// Unlike [`compress_file`], this exposes every [`StreamCompressOptions`] /// field — including `seekable`, which appends a seek table for /// random-access decompression via [`crate::seekable::Seekable`]. /// /// # Example /// /// ```rust,no_run /// use zxc::{compress_file_with_options, Level, StreamCompressOptions}; /// /// let opts = StreamCompressOptions::with_level(Level::Compact) /// .threads(4) /// .with_seekable(); /// let bytes = compress_file_with_options("input.bin", "output.zxc", &opts)?; /// # Ok::<(), zxc::StreamError>(()) /// ``` pub fn compress_file_with_options>( input: P, output: P, opts: &StreamCompressOptions, ) -> StreamResult { let f_in = File::open(input)?; let f_out = File::create(output)?; unsafe { let c_in = file_to_c_file_read(&f_in); let c_out = file_to_c_file_write(&f_out); // Check for errors and cleanup on failure if c_in.is_null() { if !c_out.is_null() { libc::fclose(c_out); } return Err(StreamError::Io(io::Error::last_os_error())); } if c_out.is_null() { libc::fclose(c_in); return Err(StreamError::Io(io::Error::last_os_error())); } let result = zxc_sys::zxc_stream_compress( c_in, c_out, &zxc_sys::zxc_compress_opts_t { n_threads: opts.threads.unwrap_or(0) as i32, level: opts.level as i32, checksum_enabled: opts.checksum as i32, seekable: opts.seekable as i32, ..Default::default() }, ); // Always close C FILE handles (they own duplicated fds) libc::fclose(c_in); libc::fclose(c_out); if result < 0 { Err(StreamError::BufferError(error_from_code(result))) } else { Ok(result as u64) } } } /// Decompresses a file using multi-threaded streaming. /// /// # Example /// /// ```rust,no_run /// use zxc::decompress_file; /// /// // Decompress with auto-detected thread count /// let bytes = decompress_file("compressed.zxc", "output.bin", None)?; /// println!("Decompressed {} bytes", bytes); /// # Ok::<(), zxc::StreamError>(()) /// ``` pub fn decompress_file>( input: P, output: P, threads: Option, ) -> StreamResult { decompress_file_with_options( input, output, &StreamDecompressOptions { threads, verify_checksum: true, }, ) } /// Decompresses a file using multi-threaded streaming, honouring `opts`. /// /// Unlike [`decompress_file`], this exposes every /// [`StreamDecompressOptions`] field — including `verify_checksum`, which /// can be disabled to skip integrity verification. /// /// # Example /// /// ```rust,no_run /// use zxc::{decompress_file_with_options, StreamDecompressOptions}; /// /// let opts = StreamDecompressOptions::default().skip_checksum(); /// let bytes = decompress_file_with_options("compressed.zxc", "output.bin", &opts)?; /// # Ok::<(), zxc::StreamError>(()) /// ``` pub fn decompress_file_with_options>( input: P, output: P, opts: &StreamDecompressOptions, ) -> StreamResult { let f_in = File::open(input)?; let f_out = File::create(output)?; let n_threads = opts.threads.unwrap_or(0) as i32; let checksum_enabled = opts.verify_checksum as i32; unsafe { let c_in = file_to_c_file_read(&f_in); let c_out = file_to_c_file_write(&f_out); // Check for errors and cleanup on failure if c_in.is_null() { if !c_out.is_null() { libc::fclose(c_out); } return Err(StreamError::Io(io::Error::last_os_error())); } if c_out.is_null() { libc::fclose(c_in); return Err(StreamError::Io(io::Error::last_os_error())); } let result = zxc_sys::zxc_stream_decompress( c_in, c_out, &zxc_sys::zxc_decompress_opts_t { n_threads, checksum_enabled, ..Default::default() }, ); // Always close C FILE handles (they own duplicated fds) libc::fclose(c_in); libc::fclose(c_out); if result < 0 { Err(StreamError::BufferError(error_from_code(result))) } else { Ok(result as u64) } } } /// Returns the decompressed size stored in a compressed file. /// /// This reads the file footer without performing decompression, /// useful for pre-allocating buffers or showing progress. /// /// # Example /// /// ```rust,no_run /// use zxc::file_decompressed_size; /// /// let size = file_decompressed_size("compressed.zxc")?; /// println!("Original size: {} bytes", size); /// # Ok::<(), zxc::StreamError>(()) /// ``` pub fn file_decompressed_size>(path: P) -> StreamResult { let f = File::open(path)?; unsafe { let c_file = file_to_c_file_read(&f); if c_file.is_null() { return Err(StreamError::Io(io::Error::last_os_error())); } let result = zxc_sys::zxc_stream_get_decompressed_size(c_file); // The FILE* owns a dup'd fd; it must be closed here or every call // leaks one file descriptor. libc::fclose(c_file); if result < 0 { Err(StreamError::InvalidFile) } else { Ok(result as u64) } } } #[cfg(test)] mod tests { use crate::*; use std::fs; use std::io::Write; fn temp_path(name: &str) -> String { let dir_name = format!("zxc_test_{}", std::process::id()); // Try the system temp directory first let mut path = std::env::temp_dir(); path.push(&dir_name); // If we can't create the directory in the system temp, fall back // to a subdirectory relative to the current working directory. // This happens on some Windows CI runners where TEMP is missing or // points to a path the process cannot access. if fs::create_dir_all(&path).is_err() { path = std::env::current_dir().expect("cannot determine current directory"); path.push(&dir_name); fs::create_dir_all(&path).expect("failed to create temp directory in current dir"); } path.push(name); path.to_string_lossy().into_owned() } #[test] fn test_file_roundtrip() { let input_path = temp_path("roundtrip_input.bin"); let compressed_path = temp_path("roundtrip_compressed.zxc"); let output_path = temp_path("roundtrip_output.bin"); // Create test data let data: Vec = (0..64 * 1024) // 64 KB .map(|i| ((i % 256) ^ ((i / 256) % 256)) as u8) .collect(); // Write test file { let mut f = fs::File::create(&input_path).unwrap(); f.write_all(&data).unwrap(); } // Compress let compressed_size = compress_file(&input_path, &compressed_path, Level::Default, None, None).unwrap(); assert!(compressed_size > 0); // Decompress let decompressed_size = decompress_file(&compressed_path, &output_path, None).unwrap(); assert_eq!(decompressed_size, data.len() as u64); // Verify content let result = fs::read(&output_path).unwrap(); assert_eq!(result, data); // Cleanup let _ = fs::remove_file(&input_path); let _ = fs::remove_file(&compressed_path); let _ = fs::remove_file(&output_path); } #[test] fn test_file_decompressed_size_query() { let input_path = temp_path("size_input.bin"); let compressed_path = temp_path("size_compressed.zxc"); // Create test data let data: Vec = (0..128 * 1024) // 128 KB .map(|i| (i % 256) as u8) .collect(); // Write and compress { let mut f = fs::File::create(&input_path).unwrap(); f.write_all(&data).unwrap(); } compress_file(&input_path, &compressed_path, Level::Default, None, None).unwrap(); // Query size let reported_size = file_decompressed_size(&compressed_path).unwrap(); assert_eq!(reported_size, data.len() as u64); // Cleanup let _ = fs::remove_file(&input_path); let _ = fs::remove_file(&compressed_path); } #[test] fn test_file_all_levels() { let input_path = temp_path("levels_input.bin"); // Create test data let data: Vec = (0..32 * 1024) // 32 KB .map(|i| ((i % 256) ^ ((i / 256) % 256)) as u8) .collect(); { let mut f = fs::File::create(&input_path).unwrap(); f.write_all(&data).unwrap(); } for level in Level::all() { let compressed_path = temp_path(&format!("levels_{:?}.zxc", level)); let output_path = temp_path(&format!("levels_{:?}_out.bin", level)); // Compress with this level compress_file(&input_path, &compressed_path, *level, Some(2), None).unwrap(); // Decompress decompress_file(&compressed_path, &output_path, Some(2)).unwrap(); // Verify let result = fs::read(&output_path).unwrap(); assert_eq!(result, data, "Data mismatch at level {:?}", level); // Cleanup let _ = fs::remove_file(&compressed_path); let _ = fs::remove_file(&output_path); } let _ = fs::remove_file(&input_path); } #[test] fn test_file_multithreaded() { let input_path = temp_path("mt_input.bin"); let compressed_path = temp_path("mt_compressed.zxc"); let output_path = temp_path("mt_output.bin"); // Create larger test data (1 MB) let data: Vec = (0..1024 * 1024) .map(|i| ((i % 256) ^ ((i / 256) % 256)) as u8) .collect(); { let mut f = fs::File::create(&input_path).unwrap(); f.write_all(&data).unwrap(); } // Test with different thread counts for threads in [1, 2, 4] { // Compress compress_file( &input_path, &compressed_path, Level::Default, Some(threads), None, ) .unwrap(); // Decompress let size = decompress_file(&compressed_path, &output_path, Some(threads)).unwrap(); assert_eq!(size, data.len() as u64); // Verify let result = fs::read(&output_path).unwrap(); assert_eq!(result, data, "Mismatch with {} threads", threads); } // Cleanup let _ = fs::remove_file(&input_path); let _ = fs::remove_file(&compressed_path); let _ = fs::remove_file(&output_path); } } zxc-0.13.0/wrappers/rust/zxc/src/lib.rs000066400000000000000000000231771522536370400177670ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ //! Safe Rust bindings to the ZXC compression library. //! //! ZXC is a fast compression library optimized for high decompression speed. //! This crate provides a safe, idiomatic Rust API. //! //! # Quick Start //! //! ```rust //! use zxc::{compress, decompress, Level}; //! //! // Compress some data //! let data = b"Hello, ZXC! This is some data to compress."; //! let compressed = compress(data, Level::Default, None).expect("compression failed"); //! //! // Decompress it back //! let decompressed = decompress(&compressed).expect("decompression failed"); //! assert_eq!(&decompressed[..], &data[..]); //! ``` //! //! # Compression Levels //! //! ZXC provides 7 compression levels trading off speed vs ratio: //! //! | Level | Speed | Ratio | Use Case | //! |-------|-------|-------|----------| //! | `Fastest` | ★★★★★ | ★★☆☆☆ | Real-time, gaming | //! | `Fast` | ★★★★☆ | ★★★☆☆ | Network, streaming | //! | `Default` | ★★★☆☆ | ★★★★☆ | General purpose | //! | `Balanced` | ★★☆☆☆ | ★★★★☆ | Archives | //! | `Compact` | ★☆☆☆☆ | ★★★★★ | Storage, firmware | //! | `Density` | ★☆☆☆☆ | ★★★★★ | High density (Huffman literals + optimal parser) | //! | `Ultra` | ★☆☆☆☆ | ★★★★★ | Maximum density (Huffman literals + tokens, deep parse) | //! //! # Features //! //! - **Checksum verification**: Optional, disabled by default for maximum performance //! - **Zero-copy decompression bound**: Query the output size before decompressing #![warn(missing_docs)] #![warn(rust_2018_idioms)] pub use zxc_sys::{ ZXC_LEVEL_BALANCED, ZXC_LEVEL_COMPACT, ZXC_LEVEL_DEFAULT, ZXC_LEVEL_DENSITY, ZXC_LEVEL_FAST, ZXC_LEVEL_FASTEST, ZXC_LEVEL_ULTRA, ZXC_VERSION_MAJOR, ZXC_VERSION_MINOR, ZXC_VERSION_PATCH, // Error codes ZXC_OK, ZXC_ERROR_MEMORY, ZXC_ERROR_DST_TOO_SMALL, ZXC_ERROR_SRC_TOO_SMALL, ZXC_ERROR_BAD_MAGIC, ZXC_ERROR_BAD_VERSION, ZXC_ERROR_BAD_HEADER, ZXC_ERROR_BAD_CHECKSUM, ZXC_ERROR_CORRUPT_DATA, ZXC_ERROR_BAD_OFFSET, ZXC_ERROR_OVERFLOW, ZXC_ERROR_IO, ZXC_ERROR_NULL_INPUT, ZXC_ERROR_BAD_BLOCK_TYPE, ZXC_ERROR_BAD_BLOCK_SIZE, }; // ============================================================================= // Compression Levels // ============================================================================= /// Compression level presets. /// /// Higher levels produce smaller output but compress more slowly. /// Decompression speed is similar across most levels; levels 6-7 sit a notch /// below the others because Huffman-coded literals (and, at level 7, tokens) /// add a per-block decode cost relative to RAW/RLE literals. #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] #[repr(i32)] pub enum Level { /// Fastest compression, best for real-time applications (level 1) Fastest = 1, /// Fast compression, good for real-time applications (level 2) Fast = 2, /// Recommended default: ratio > LZ4, decode speed > LZ4 (level 3) #[default] Default = 3, /// Good ratio, good decode speed (level 4) Balanced = 4, /// High density: storage / firmware / assets (level 5) Compact = 5, /// High density: Huffman-coded literals on top of COMPACT plus a /// price-based optimal LZ77 parser (level 6). Density = 6, /// Maximum density: Huffman-coded literals *and* sequence tokens with a /// deep parse. Slowest compression, best ratio (level 7 / ULTRA). Ultra = 7, } impl Level { /// Returns all available compression levels. pub fn all() -> &'static [Level] { &[ Level::Fastest, Level::Fast, Level::Default, Level::Balanced, Level::Compact, Level::Density, Level::Ultra, ] } } impl From for i32 { fn from(level: Level) -> i32 { level as i32 } } // ============================================================================= // Compression Options // ============================================================================= /// Options for compression operations. #[derive(Debug, Clone)] pub struct CompressOptions { /// Compression level (default: `Level::Default`) pub level: Level, /// Enable checksum for data integrity (default: `true`) pub checksum: bool, /// Enable seek table for random-access decompression (default: `false`) pub seekable: bool, /// Pre-trained dictionary content (default: `None`). /// /// Set to the raw dictionary content bytes (as returned by /// [`train_dict`] or [`dict_load`]). The decoder must be given the same /// dictionary to decompress the resulting archive. pub dict: Option>, /// Shared literal Huffman table (default: `None`; ignored without `dict`). /// /// The 128-byte packed code-lengths table from [`train_dict_huf`] or /// [`dict_huf`]. Becomes part of the archive's dictionary binding: the /// decoder must be given the same (dict, table) pair. pub dict_huf: Option>, } impl Default for CompressOptions { fn default() -> Self { Self { level: Level::Default, checksum: true, seekable: false, dict: None, dict_huf: None, } } } impl CompressOptions { /// Create options with the specified compression level. pub fn with_level(level: Level) -> Self { Self { level, ..Default::default() } } /// Disable checksum computation for faster compression. pub fn without_checksum(mut self) -> Self { self.checksum = false; self } /// Enable seek table for random-access decompression. pub fn with_seekable(mut self) -> Self { self.seekable = true; self } /// Attach a pre-trained dictionary (raw content bytes). pub fn with_dict(mut self, dict: impl Into>) -> Self { self.dict = Some(dict.into()); self } /// Attach the dictionary's shared literal Huffman table (128 bytes). pub fn with_dict_huf(mut self, huf: impl Into>) -> Self { self.dict_huf = Some(huf.into()); self } /// Attach a [`Dictionary`] (content + shared table) in one call. pub fn with_dictionary(mut self, dictionary: &Dictionary) -> Self { self.dict = Some(dictionary.content().to_vec()); self.dict_huf = Some(dictionary.huf().to_vec()); self } } // ============================================================================= // Decompression Options // ============================================================================= /// Options for decompression operations. #[derive(Debug, Clone)] pub struct DecompressOptions { /// Verify checksum during decompression (default: `true`) pub verify_checksum: bool, /// Pre-trained dictionary content (default: `None`). /// /// Must match the dictionary used at compression time. Required to /// decompress an archive that was produced with a dictionary. pub dict: Option>, /// Shared literal Huffman table (default: `None`; ignored without `dict`). /// /// Must match the table used at compression time (the archive's dict_id /// binds the (dict, table) pair). pub dict_huf: Option>, } impl Default for DecompressOptions { fn default() -> Self { Self { verify_checksum: true, dict: None, dict_huf: None, } } } impl DecompressOptions { /// Create options that skip checksum verification. pub fn skip_checksum() -> Self { Self { verify_checksum: false, ..Default::default() } } /// Attach a pre-trained dictionary (raw content bytes). pub fn with_dict(mut self, dict: impl Into>) -> Self { self.dict = Some(dict.into()); self } /// Attach the dictionary's shared literal Huffman table (128 bytes). pub fn with_dict_huf(mut self, huf: impl Into>) -> Self { self.dict_huf = Some(huf.into()); self } /// Attach a [`Dictionary`] (content + shared table) in one call. pub fn with_dictionary(mut self, dictionary: &Dictionary) -> Self { self.dict = Some(dictionary.content().to_vec()); self.dict_huf = Some(dictionary.huf().to_vec()); self } } // ============================================================================= // Submodules // ============================================================================= mod ctx; mod dict; mod error; mod file; mod oneshot; mod pstream; pub mod seekable; mod stdio; pub use dict::{ dict_get_id, dict_huf, dict_id, dict_load, dict_save, get_dict_id, train_dict, train_dict_huf, Dictionary, }; pub use zxc_sys::{ZXC_HUF_TABLE_SIZE, ZXC_DICT_SIZE_MAX}; pub use error::{Error, Result}; pub use oneshot::{ compress, compress_bound, compress_to, compress_with_options, decompress, decompress_to, decompress_with_options, decompressed_size, default_level, max_level, min_level, runtime_version, version, version_string, }; pub use ctx::{compress_block_bound, decompress_block_bound, Cctx, Dctx}; pub use file::{ compress_file, compress_file_with_options, decompress_file, decompress_file_with_options, file_decompressed_size, StreamCompressOptions, StreamDecompressOptions, StreamError, StreamResult, }; pub use pstream::{CStream, CStreamProgress, DStream, DStreamProgress}; pub use seekable::{seek_table_size, write_seek_table, Seekable}; pub use stdio::{detect_zxc, Decoder, Encoder}; zxc-0.13.0/wrappers/rust/zxc/src/oneshot.rs000066400000000000000000000362071522536370400206760ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ //! One-shot compress / decompress entry points and library version helpers. use std::ffi::c_void; use zxc_sys::{ZXC_VERSION_MAJOR, ZXC_VERSION_MINOR, ZXC_VERSION_PATCH}; use crate::error::error_from_code; use crate::{CompressOptions, DecompressOptions, Error, Level, Result}; /// Returns the maximum compressed size for an input of the given size. /// /// Use this to allocate a buffer before calling [`compress_to`]. /// /// # Example /// /// ```rust /// let bound = zxc::compress_bound(1024); /// assert!(bound > 1024); // Accounts for headers and worst-case expansion /// ``` #[inline] pub fn compress_bound(input_size: usize) -> u64 { unsafe { zxc_sys::zxc_compress_bound(input_size) } } /// Compresses data with the specified level. /// /// This is a convenience function that allocates the output buffer automatically. /// For zero-allocation usage, see [`compress_to`]. /// /// # Arguments /// /// * `data` - The data to compress /// * `level` - Compression level /// * `checksum` - Optional checksum for data integrity (`None` = disabled for maximum performance) /// /// # Example /// /// ```rust /// use zxc::{compress, Level}; /// /// let data = b"Hello, world!"; /// /// // Maximum performance (no checksum) /// let compressed = compress(data, Level::Default, None)?; /// /// // With data integrity verification /// let compressed = compress(data, Level::Default, Some(true))?; /// # Ok::<(), zxc::Error>(()) /// ``` pub fn compress(data: &[u8], level: Level, checksum: Option) -> Result> { let opts = CompressOptions { level, checksum: checksum.unwrap_or(false), seekable: false, dict: None, dict_huf: None, }; compress_with_options(data, &opts) } /// Compresses data with full options control. /// /// # Example /// /// ```rust /// use zxc::{compress_with_options, CompressOptions, Level}; /// /// let data = b"Hello, world!"; /// let opts = CompressOptions::with_level(Level::Compact).without_checksum(); /// let compressed = compress_with_options(data, &opts)?; /// # Ok::<(), zxc::Error>(()) /// ``` pub fn compress_with_options(data: &[u8], options: &CompressOptions) -> Result> { let bound = compress_bound(data.len()) as usize; let mut output = Vec::with_capacity(bound); let written = unsafe { impl_compress(data, output.as_mut_ptr(), output.capacity(), options)? }; unsafe { output.set_len(written); } Ok(output) } /// Helper to handle the raw compression call. /// /// # Safety /// /// `dst_ptr` must be valid for writes up to `dst_cap` bytes. #[inline(always)] unsafe fn impl_compress( data: &[u8], dst_ptr: *mut u8, dst_cap: usize, options: &CompressOptions, ) -> Result { let written = unsafe { let (dict_ptr, dict_size) = match &options.dict { Some(d) if !d.is_empty() => (d.as_ptr() as *const c_void, d.len()), _ => (std::ptr::null(), 0), }; let dict_huf_ptr = match &options.dict_huf { Some(h) if !h.is_empty() => h.as_ptr() as *const c_void, _ => std::ptr::null(), }; let copts = zxc_sys::zxc_compress_opts_t { level: options.level as i32, checksum_enabled: options.checksum as i32, seekable: options.seekable as i32, dict: dict_ptr, dict_size, dict_huf: dict_huf_ptr, ..Default::default() }; zxc_sys::zxc_compress( data.as_ptr() as *const c_void, data.len(), dst_ptr as *mut c_void, dst_cap, &copts, ) }; if written < 0 { return Err(error_from_code(written)); } if written == 0 && !data.is_empty() { return Err(Error::InvalidData); } Ok(written as usize) } /// Compresses data into a pre-allocated buffer. /// /// Returns the number of bytes written to `output`. /// /// # Errors /// /// Returns an [`Error`] if the output buffer is too small or an internal /// error occurs. /// /// # Example /// /// ```rust /// use zxc::{compress_to, compress_bound, CompressOptions}; /// /// let data = b"Hello, world!"; /// let mut output = vec![0u8; compress_bound(data.len()) as usize]; /// let size = compress_to(data, &mut output, &CompressOptions::default())?; /// output.truncate(size); /// # Ok::<(), zxc::Error>(()) /// ``` pub fn compress_to(data: &[u8], output: &mut [u8], options: &CompressOptions) -> Result { unsafe { impl_compress(data, output.as_mut_ptr(), output.len(), options) } } /// Returns the original uncompressed size from compressed data. /// /// This reads the footer without performing decompression. /// Returns `None` if the data is invalid or truncated. /// /// # Example /// /// ```rust /// use zxc::{compress, decompressed_size, Level}; /// /// let data = b"Hello, world!"; /// let compressed = compress(data, Level::Default, None)?; /// let size = decompressed_size(&compressed); /// assert_eq!(size, Some(data.len() as u64)); /// # Ok::<(), zxc::Error>(()) /// ``` pub fn decompressed_size(compressed: &[u8]) -> Option { let size = unsafe { zxc_sys::zxc_get_decompressed_size(compressed.as_ptr() as *const c_void, compressed.len()) }; if size == 0 && !compressed.is_empty() { None } else { Some(size) } } /// Decompresses ZXC-compressed data. /// /// This is a convenience function that queries the output size and allocates /// the buffer automatically. For zero-allocation usage, see [`decompress_to`]. /// /// # Example /// /// ```rust /// use zxc::{compress, decompress, Level}; /// /// let data = b"Hello, world!"; /// let compressed = compress(data, Level::Default, None)?; /// let decompressed = decompress(&compressed)?; /// assert_eq!(&decompressed[..], &data[..]); /// # Ok::<(), zxc::Error>(()) /// ``` pub fn decompress(compressed: &[u8]) -> Result> { decompress_with_options(compressed, &DecompressOptions::default()) } /// Decompresses data with full options control. pub fn decompress_with_options( compressed: &[u8], options: &DecompressOptions, ) -> Result> { // `decompressed_size` returns None for an ambiguous 0 (a valid empty-payload // archive or invalid input); fall back to 0 and let the C decoder validate // the frame (it returns a negative error code on genuinely corrupt input). let size = decompressed_size(compressed).unwrap_or(0) as usize; let mut output = Vec::with_capacity(size); let written = unsafe { impl_decompress(compressed, output.as_mut_ptr(), output.capacity(), options)? }; if written != size { return Err(Error::InvalidData); } unsafe { output.set_len(written); } Ok(output) } /// Helper to handle the raw decompression call. /// /// # Safety /// /// `dst_ptr` must be valid for writes up to `dst_cap` bytes. #[inline(always)] unsafe fn impl_decompress( compressed: &[u8], dst_ptr: *mut u8, dst_cap: usize, options: &DecompressOptions, ) -> Result { let written = unsafe { let (dict_ptr, dict_size) = match &options.dict { Some(d) if !d.is_empty() => (d.as_ptr() as *const c_void, d.len()), _ => (std::ptr::null(), 0), }; let dict_huf_ptr = match &options.dict_huf { Some(h) if !h.is_empty() => h.as_ptr() as *const c_void, _ => std::ptr::null(), }; let dopts = zxc_sys::zxc_decompress_opts_t { checksum_enabled: if options.verify_checksum { 1 } else { 0 }, dict: dict_ptr, dict_size, dict_huf: dict_huf_ptr, ..Default::default() }; zxc_sys::zxc_decompress( compressed.as_ptr() as *const c_void, compressed.len(), dst_ptr as *mut c_void, dst_cap, &dopts, ) }; if written < 0 { return Err(error_from_code(written)); } // A non-negative return is a success: `written == 0` is valid (empty payload). Ok(written as usize) } /// Decompresses data into a pre-allocated buffer. /// /// Returns the number of bytes written to `output`. /// /// # Errors /// /// Returns an error if decompression fails due to invalid data, corruption, /// or insufficient output buffer size. pub fn decompress_to( compressed: &[u8], output: &mut [u8], options: &DecompressOptions, ) -> Result { unsafe { impl_decompress(compressed, output.as_mut_ptr(), output.len(), options) } } /// Returns the library version as a tuple (major, minor, patch). pub fn version() -> (u32, u32, u32) { (ZXC_VERSION_MAJOR, ZXC_VERSION_MINOR, ZXC_VERSION_PATCH) } /// Returns the library version as a string. /// /// This value is the compile-time version baked into the `zxc-sys` crate. /// For the runtime-linked library version (useful to detect a mismatched /// shared library), call [`runtime_version`]. pub fn version_string() -> String { format!( "{}.{}.{}", ZXC_VERSION_MAJOR, ZXC_VERSION_MINOR, ZXC_VERSION_PATCH ) } /// Returns the version string reported by the linked native library /// (e.g. `"0.10.0"`). Useful for verifying that the dynamically-linked /// libzxc matches the version `zxc-sys` was built against. pub fn runtime_version() -> &'static str { unsafe { let ptr = zxc_sys::zxc_version_string(); std::ffi::CStr::from_ptr(ptr).to_str().unwrap_or("") } } /// Returns the minimum supported compression level (currently `1`). /// /// Equivalent to [`Level::Fastest`] as an integer. pub fn min_level() -> i32 { unsafe { zxc_sys::zxc_min_level() } } /// Returns the maximum supported compression level (currently `7`). /// /// Equivalent to [`Level::Ultra`] as an integer. pub fn max_level() -> i32 { unsafe { zxc_sys::zxc_max_level() } } /// Returns the default compression level (currently `3`). /// /// Equivalent to [`Level::Default`] as an integer. pub fn default_level() -> i32 { unsafe { zxc_sys::zxc_default_level() } } #[cfg(test)] mod tests { use crate::*; #[test] fn test_roundtrip() { let data = b"Hello, ZXC! This is a test of the safe Rust wrapper."; let compressed = compress(data, Level::Default, None).unwrap(); let decompressed = decompress(&compressed).unwrap(); assert_eq!(&decompressed[..], &data[..]); } #[test] fn test_all_levels() { let data = b"Test data with repetition: CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"; for level in Level::all() { let compressed = compress(data, *level, None).unwrap(); let decompressed = decompress(&compressed).unwrap(); assert_eq!( &decompressed[..], &data[..], "Roundtrip failed at level {:?}", level ); } } #[test] fn test_empty() { // Empty input is valid: it produces a well-formed (header + EOF + footer) // archive that round-trips back to empty. let data: &[u8] = b""; let compressed = compress(data, Level::Default, None).expect("compress empty"); let decompressed = decompress(&compressed).expect("decompress empty"); assert!( decompressed.is_empty(), "empty data must round-trip to empty" ); } #[test] fn test_checksum_options() { let data = b"Test with and without checksum"; // With checksum let opts_with = CompressOptions::with_level(Level::Default); let compressed = compress_with_options(data, &opts_with).unwrap(); let decompressed = decompress(&compressed).unwrap(); assert_eq!(&decompressed[..], &data[..]); // Without checksum let opts_without = CompressOptions::with_level(Level::Fast).without_checksum(); let compressed = compress_with_options(data, &opts_without).unwrap(); let decompressed = decompress_with_options(&compressed, &DecompressOptions::skip_checksum()).unwrap(); assert_eq!(&decompressed[..], &data[..]); } #[test] fn test_decompressed_size() { let data = b"Hello, world! Testing decompressed_size function."; let compressed = compress(data, Level::Default, None).unwrap(); let size = decompressed_size(&compressed); assert_eq!(size, Some(data.len() as u64)); } #[test] fn test_version() { let (major, minor, patch) = version(); let expected = format!("{}.{}.{}", major, minor, patch); assert_eq!(version_string(), expected); let cargo_version = env!("CARGO_PKG_VERSION"); assert_eq!(expected, cargo_version); } #[test] fn test_invalid_data() { let garbage = b"not valid zxc data"; let result = decompress(garbage); assert!(result.is_err()); } #[test] fn test_specific_error_codes() { // Test invalid magic - size detection should fail first and return InvalidData let invalid_magic = b"INVALID_DATA_NOT_ZXC"; let result = decompress(invalid_magic); assert!(result.is_err(), "Should fail on invalid data"); // Test truncated data - must fail (the exact error code is a decoder // implementation detail, so we only require that it errors). let data = b"Hello, world! Testing error codes with enough data to compress well."; let compressed = compress(data, Level::Default, None).unwrap(); let truncated = &compressed[..10]; // Too short to be valid assert!( decompress(truncated).is_err(), "Truncated data must fail to decompress" ); } #[test] fn test_error_messages() { // Verify error messages are descriptive let errors = vec![ (Error::Memory, "memory allocation failed"), (Error::BadChecksum, "checksum verification failed"), (Error::CorruptData, "corrupted compressed data"), (Error::DstTooSmall, "destination buffer too small"), ]; for (error, expected_msg) in errors { let msg = error.to_string(); assert!( msg.contains(expected_msg), "Error message '{}' should contain '{}'", msg, expected_msg ); } } #[test] fn test_compress_to_buffer() { let data = b"Testing compress_to with pre-allocated buffer"; let mut output = vec![0u8; compress_bound(data.len()) as usize]; let size = compress_to(data, &mut output, &CompressOptions::default()).unwrap(); output.truncate(size); let decompressed = decompress(&output).unwrap(); assert_eq!(&decompressed[..], &data[..]); } #[test] fn test_large_data() { // 1 MB of random-ish but compressible data let data: Vec = (0..1024 * 1024) .map(|i| ((i % 256) ^ ((i / 256) % 256)) as u8) .collect(); let compressed = compress(&data, Level::Default, None).unwrap(); assert!(compressed.len() < data.len()); // Should compress let decompressed = decompress(&compressed).unwrap(); assert_eq!(decompressed, data); } } zxc-0.13.0/wrappers/rust/zxc/src/pstream.rs000066400000000000000000000317331522536370400206710ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ //! Push streaming API: single-threaded, caller-driven compression / //! decompression streams. The safe Rust counterpart of `zxc_cstream` / //! `zxc_dstream`. use std::ffi::c_void; use crate::error::error_from_code; use crate::{CompressOptions, DecompressOptions, Error, Result}; /// Reports how a single [`CStream::compress`] / [`CStream::end`] call /// progressed. /// /// `pending == 0` means input was fully consumed and no output is staged /// internally; positive values indicate the caller should drain `output` /// and call again. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct CStreamProgress { /// Bytes consumed from `input` this call. pub consumed: usize, /// Bytes written into `output` this call. pub produced: usize, /// Bytes still pending in the internal staging area; drain `output` /// and call again to make progress. pub pending: u64, } /// Reports how a single [`DStream::decompress`] call progressed. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct DStreamProgress { /// Bytes consumed from `input` this call. pub consumed: usize, /// Bytes written into `output` this call. pub produced: usize, /// `true` once the decoder has reached and validated the file footer. pub finished: bool, } /// Push compression stream — the safe Rust counterpart of `zxc_cstream`. /// /// Use this when you cannot block on a `FILE*` (event loops, async runtimes, /// network protocols, callback-driven libraries). The stream is single- /// threaded; for parallel file-to-file compression, use [`crate::compress_file`]. /// /// # Example /// /// ```rust,no_run /// use zxc::{CStream, CompressOptions, Level}; /// /// let mut cs = CStream::new(Some(&CompressOptions::with_level(Level::Default)))?; /// /// let mut out = vec![0u8; cs.out_size()]; /// let mut sink: Vec = Vec::new(); /// /// for chunk in source_chunks() { /// let mut cursor = 0; /// loop { /// let p = cs.compress(&chunk[cursor..], &mut out)?; /// cursor += p.consumed; /// sink.extend_from_slice(&out[..p.produced]); /// if p.pending == 0 && cursor == chunk.len() { break; } /// } /// } /// loop { /// let p = cs.end(&mut out)?; /// sink.extend_from_slice(&out[..p.produced]); /// if p.pending == 0 { break; } /// } /// # fn source_chunks() -> Vec> { vec![] } /// # Ok::<(), zxc::Error>(()) /// ``` pub struct CStream { inner: *mut zxc_sys::zxc_cstream, } unsafe impl Send for CStream {} impl CStream { /// Creates a new push compression stream. /// /// `opts.seekable` is ignored (the push API is single-threaded and does /// not emit a seek table). Pass `None` for all defaults. /// /// # Errors /// /// Returns [`Error::Unsupported`] when `opts.dict` or `opts.dict_huf` /// is set: the push-stream format carries no dictionary ID, so /// dictionary compression would produce undecodable archives. pub fn new(opts: Option<&CompressOptions>) -> Result { if let Some(o) = opts { if o.dict.is_some() || o.dict_huf.is_some() { return Err(Error::Unsupported( "dictionaries are not supported by the push streaming API", )); } } let c_opts = opts.map(|o| zxc_sys::zxc_compress_opts_t { level: o.level as i32, checksum_enabled: o.checksum as i32, seekable: 0, ..Default::default() }); let ptr = unsafe { zxc_sys::zxc_cstream_create( c_opts .as_ref() .map(|o| o as *const _) .unwrap_or(std::ptr::null()), ) }; if ptr.is_null() { Err(Error::Memory) } else { Ok(Self { inner: ptr }) } } /// Pushes `input` into the stream and writes compressed bytes to `output`. /// /// On return, `progress.consumed` and `progress.produced` describe the /// slice ranges that were processed; `progress.pending` is the number of /// bytes still staged inside the stream (drain `output` and call again). pub fn compress(&mut self, input: &[u8], output: &mut [u8]) -> Result { let mut in_buf = zxc_sys::zxc_inbuf_t { src: input.as_ptr() as *const c_void, size: input.len(), pos: 0, }; let mut out_buf = zxc_sys::zxc_outbuf_t { dst: output.as_mut_ptr() as *mut c_void, size: output.len(), pos: 0, }; let r = unsafe { zxc_sys::zxc_cstream_compress(self.inner, &mut out_buf, &mut in_buf) }; if r < 0 { return Err(error_from_code(r)); } Ok(CStreamProgress { consumed: in_buf.pos, produced: out_buf.pos, pending: r as u64, }) } /// Finalises the stream: flushes the residual block, then writes the /// EOF block and the file footer into `output`. /// /// Call repeatedly while `pending > 0`, draining `output` between calls. pub fn end(&mut self, output: &mut [u8]) -> Result { let mut out_buf = zxc_sys::zxc_outbuf_t { dst: output.as_mut_ptr() as *mut c_void, size: output.len(), pos: 0, }; let r = unsafe { zxc_sys::zxc_cstream_end(self.inner, &mut out_buf) }; if r < 0 { return Err(error_from_code(r)); } Ok(CStreamProgress { consumed: 0, produced: out_buf.pos, pending: r as u64, }) } /// Suggested input chunk size for best throughput. pub fn in_size(&self) -> usize { unsafe { zxc_sys::zxc_cstream_in_size(self.inner) } } /// Suggested output chunk size to never trigger a partial drain. pub fn out_size(&self) -> usize { unsafe { zxc_sys::zxc_cstream_out_size(self.inner) } } } impl Drop for CStream { fn drop(&mut self) { unsafe { zxc_sys::zxc_cstream_free(self.inner) }; } } /// Push decompression stream — the safe Rust counterpart of `zxc_dstream`. /// /// # Example /// /// ```rust,no_run /// use zxc::{DStream, DecompressOptions}; /// /// let mut ds = DStream::new(None)?; /// let mut out = vec![0u8; ds.out_size()]; /// let mut sink: Vec = Vec::new(); /// /// for chunk in compressed_chunks() { /// let mut cursor = 0; /// while cursor < chunk.len() && !ds.finished() { /// let p = ds.decompress(&chunk[cursor..], &mut out)?; /// cursor += p.consumed; /// sink.extend_from_slice(&out[..p.produced]); /// if p.consumed == 0 && p.produced == 0 { break; } /// } /// } /// assert!(ds.finished()); /// # fn compressed_chunks() -> Vec> { vec![] } /// # Ok::<(), zxc::Error>(()) /// ``` pub struct DStream { inner: *mut zxc_sys::zxc_dstream, } unsafe impl Send for DStream {} impl DStream { /// Creates a new push decompression stream. /// /// # Errors /// /// Returns [`Error::Unsupported`] when `opts.dict` or `opts.dict_huf` /// is set: the push-stream decoder has no dictionary support (see /// [`CStream::new`]). pub fn new(opts: Option<&DecompressOptions>) -> Result { if let Some(o) = opts { if o.dict.is_some() || o.dict_huf.is_some() { return Err(Error::Unsupported( "dictionaries are not supported by the push streaming API", )); } } let c_opts = opts.map(|o| zxc_sys::zxc_decompress_opts_t { checksum_enabled: o.verify_checksum as i32, ..Default::default() }); let ptr = unsafe { zxc_sys::zxc_dstream_create( c_opts .as_ref() .map(|o| o as *const _) .unwrap_or(std::ptr::null()), ) }; if ptr.is_null() { Err(Error::Memory) } else { Ok(Self { inner: ptr }) } } /// Pushes compressed bytes from `input` and writes decompressed bytes /// to `output`. The state machine consumes file header, block headers, /// payloads, and footer transparently. pub fn decompress(&mut self, input: &[u8], output: &mut [u8]) -> Result { let mut in_buf = zxc_sys::zxc_inbuf_t { src: input.as_ptr() as *const c_void, size: input.len(), pos: 0, }; let mut out_buf = zxc_sys::zxc_outbuf_t { dst: output.as_mut_ptr() as *mut c_void, size: output.len(), pos: 0, }; let r = unsafe { zxc_sys::zxc_dstream_decompress(self.inner, &mut out_buf, &mut in_buf) }; if r < 0 { return Err(error_from_code(r)); } Ok(DStreamProgress { consumed: in_buf.pos, produced: out_buf.pos, finished: unsafe { zxc_sys::zxc_dstream_finished(self.inner) } != 0, }) } /// Returns `true` iff the decoder reached and validated the file footer. /// Useful to detect truncated streams after the input source is drained. pub fn finished(&self) -> bool { unsafe { zxc_sys::zxc_dstream_finished(self.inner) != 0 } } /// Suggested input chunk size for the decompressor. pub fn in_size(&self) -> usize { unsafe { zxc_sys::zxc_dstream_in_size(self.inner) } } /// Suggested output chunk size for the decompressor. pub fn out_size(&self) -> usize { unsafe { zxc_sys::zxc_dstream_out_size(self.inner) } } } impl Drop for DStream { fn drop(&mut self) { unsafe { zxc_sys::zxc_dstream_free(self.inner) }; } } #[cfg(test)] mod tests { use crate::*; fn pstream_roundtrip( data: &[u8], copts: Option<&CompressOptions>, dopts: Option<&DecompressOptions>, ) -> Vec { let mut cs = CStream::new(copts).expect("cstream alloc"); let mut compressed: Vec = Vec::new(); let mut out = vec![0u8; cs.out_size().max(64)]; let mut cursor = 0; while cursor < data.len() { loop { let p = cs.compress(&data[cursor..], &mut out).unwrap(); cursor += p.consumed; compressed.extend_from_slice(&out[..p.produced]); if p.pending == 0 && (p.consumed > 0 || cursor == data.len()) { break; } } } loop { let p = cs.end(&mut out).unwrap(); compressed.extend_from_slice(&out[..p.produced]); if p.pending == 0 { break; } } let mut ds = DStream::new(dopts).expect("dstream alloc"); let mut decompressed: Vec = Vec::new(); let mut dout = vec![0u8; 64 * 1024]; let mut cursor = 0; while cursor < compressed.len() && !ds.finished() { let p = ds.decompress(&compressed[cursor..], &mut dout).unwrap(); cursor += p.consumed; decompressed.extend_from_slice(&dout[..p.produced]); if p.consumed == 0 && p.produced == 0 { break; } } // Final drain in case we have nothing more to feed but staged output remains. loop { let p = ds.decompress(&[], &mut dout).unwrap(); decompressed.extend_from_slice(&dout[..p.produced]); if p.produced == 0 { break; } } assert!(ds.finished(), "decoder did not finalise"); decompressed } #[test] fn pstream_small_roundtrip() { let data = b"Hello pstream! This is a small message that round-trips through the push API."; let out = pstream_roundtrip(data, None, None); assert_eq!(out, data); } #[test] fn pstream_with_checksum() { let data: Vec = (0..32 * 1024).map(|i| (i % 251) as u8).collect(); let copts = CompressOptions { level: Level::Default, checksum: true, seekable: false, ..Default::default() }; let dopts = DecompressOptions { verify_checksum: true, ..Default::default() }; let out = pstream_roundtrip(&data, Some(&copts), Some(&dopts)); assert_eq!(out, data); } #[test] fn pstream_multi_block() { // Larger than one default block (512 KB) to force multiple blocks. let data: Vec = (0..1536 * 1024).map(|i| ((i * 7) % 256) as u8).collect(); let out = pstream_roundtrip(&data, None, None); assert_eq!(out, data); } #[test] fn pstream_size_hints_nonzero() { let cs = CStream::new(None).unwrap(); assert!(cs.in_size() > 0); assert!(cs.out_size() > 0); let ds = DStream::new(None).unwrap(); assert!(ds.in_size() > 0); assert!(ds.out_size() > 0); assert!(!ds.finished()); } } zxc-0.13.0/wrappers/rust/zxc/src/seekable.rs000066400000000000000000000511761522536370400207740ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ //! Seekable API: random-access decompression (single-threaded). //! //! A seekable ZXC archive ships with a side seek table that maps //! uncompressed offsets to the blocks containing them. This lets a //! decoder fetch only the blocks overlapping a requested byte range, //! which is much cheaper than decompressing the whole archive. //! //! Build a seekable archive by setting [`CompressOptions::seekable`] //! to `true` before passing the options to the streaming or file APIs. //! //! # Example //! //! ```rust,ignore //! use zxc::seekable::Seekable; //! //! let mut s = Seekable::open("archive.zxc")?; //! let total = s.decompressed_size(); //! let mut buf = vec![0u8; 1024]; //! let n = s.decompress_range(&mut buf, 0, 1024)?; //! assert_eq!(n, 1024); //! ``` //! //! Multi-threaded range decompression is intentionally not exposed //! by this crate yet; the underlying `zxc_seekable_decompress_range_mt` //! symbol is reserved for a future addition. use std::ffi::{c_void, CString}; use std::path::Path; use std::ptr::NonNull; use crate::error::error_from_code; use crate::{Error, Result}; /// Handle to a seekable ZXC archive. /// /// Created with [`Seekable::open`] (file-backed) or /// [`Seekable::from_bytes`] (in-memory). The handle automatically /// releases its underlying C resources on drop. pub struct Seekable { inner: NonNull, /// When opened via `open`, we own the `FILE*` and must `fclose` it /// after the handle is freed. file: Option<*mut libc::FILE>, /// When opened via `from_bytes`, we own the source buffer for the /// lifetime of the handle. _buf: Option>, /// When opened via `open_reader`, we own a heap-allocated /// `Box` whose address is passed as the C `ctx`. Must be /// freed after the C handle is released. reader_ctx: Option<*mut Box>, } // SAFETY: the underlying handle is opaque and the library guarantees // per-handle thread affinity. `Send` is safe (move the handle to another // thread is fine); `Sync` is not (no concurrent calls on the same handle). // The reader stored behind `reader_ctx` is `Send` by construction: // `open_reader` requires `R: Send`, so moving the handle (and invoking // `read_at` from the destination thread) cannot race thread-bound state. unsafe impl Send for Seekable {} impl Seekable { /// Opens a seekable archive from an owned in-memory buffer. /// /// The buffer is held alive for the lifetime of the returned handle. /// Use this when the archive is already in memory. pub fn from_bytes(data: Vec) -> Result { let ptr = unsafe { zxc_sys::zxc_seekable_open(data.as_ptr() as *const c_void, data.len()) }; let inner = NonNull::new(ptr).ok_or(Error::InvalidData)?; Ok(Self { inner, file: None, _buf: Some(data), reader_ctx: None, }) } /// Opens a seekable archive from a file path. /// /// The file is opened in binary read mode and remains open for the /// lifetime of the returned handle. pub fn open>(path: P) -> Result { let path_bytes = path_to_cstring(path.as_ref())?; let mode = CString::new("rb").map_err(|_| Error::Io)?; // SAFETY: both pointers point to valid NUL-terminated strings. let f = unsafe { libc::fopen(path_bytes.as_ptr(), mode.as_ptr()) }; if f.is_null() { return Err(Error::Io); } let ptr = unsafe { zxc_sys::zxc_seekable_open_file(f) }; let inner = match NonNull::new(ptr) { Some(p) => p, None => { // SAFETY: f was just returned by fopen and not yet freed. unsafe { libc::fclose(f) }; return Err(Error::InvalidData); } }; Ok(Self { inner, file: Some(f), _buf: None, reader_ctx: None, }) } /// Opens a seekable archive backed by a user-supplied [`ReadAt`] /// implementation. /// /// Use this to plug any storage that supports positional reads behind /// the seekable API: an mmap'd region, an HTTP `Range:` client, an S3 /// object, a custom VFS. The reader is moved into the handle and freed /// when the handle is dropped. /// /// `read_at` is invoked exactly three times during this call (file /// header, footer, seek table), then once per block during subsequent /// [`Seekable::decompress_range`] calls. /// /// # Errors /// /// Returns [`Error::InvalidData`] if the archive is not a valid /// seekable ZXC archive, or if any of the open-time reads fail. pub fn open_reader(reader: R) -> Result { // Heap-allocate the trait object so its address is stable across // the FFI boundary. The outer Box gives us a thin pointer to pass // as the C `ctx`. let boxed: Box = Box::new(reader); let outer: Box> = Box::new(boxed); let size = outer.size(); let ctx_raw = Box::into_raw(outer); let c_reader = zxc_sys::zxc_reader_t { read_at: Some(reader_trampoline), ctx: ctx_raw as *mut c_void, size, }; // SAFETY: `c_reader` is a valid stack-allocated struct; the C library // copies its contents at open time. let ptr = unsafe { zxc_sys::zxc_seekable_open_reader(&c_reader) }; let inner = match NonNull::new(ptr) { Some(p) => p, None => { // SAFETY: ctx_raw was just produced by Box::into_raw above // and has not been freed. Reclaim ownership to drop it. unsafe { drop(Box::from_raw(ctx_raw)); } return Err(Error::InvalidData); } }; Ok(Self { inner, file: None, _buf: None, reader_ctx: Some(ctx_raw), }) } /// Total number of data blocks (excludes the EOF marker block). pub fn num_blocks(&self) -> u32 { unsafe { zxc_sys::zxc_seekable_get_num_blocks(self.inner.as_ptr()) } } /// Total decompressed size of the archive in bytes. pub fn decompressed_size(&self) -> u64 { unsafe { zxc_sys::zxc_seekable_get_decompressed_size(self.inner.as_ptr()) } } /// On-disk compressed size of a specific block (block header + /// payload + optional per-block checksum). /// /// Returns `None` if `block_idx` is out of range. pub fn block_compressed_size(&self, block_idx: u32) -> Option { if block_idx >= self.num_blocks() { return None; } let sz = unsafe { zxc_sys::zxc_seekable_get_block_comp_size(self.inner.as_ptr(), block_idx) }; Some(sz) } /// Decompressed size of a specific block. /// /// Returns `None` if `block_idx` is out of range. pub fn block_decompressed_size(&self, block_idx: u32) -> Option { if block_idx >= self.num_blocks() { return None; } let sz = unsafe { zxc_sys::zxc_seekable_get_block_decomp_size(self.inner.as_ptr(), block_idx) }; Some(sz) } /// Attaches a pre-trained dictionary to this seekable handle. /// /// Required to decompress an archive that was produced with a /// dictionary. Pass the same raw dictionary content used at compression /// time, plus its shared literal Huffman table (128 bytes) when the /// archive was compressed with one — the archive's dict_id binds the /// (dict, table) pair. Both are copied internally by the library, so the /// slices need not outlive the call. /// /// # Errors /// /// Returns an [`Error`] if the dictionary is invalid or its ID does not /// match the one the archive requires. pub fn set_dict(&mut self, dict: &[u8], dict_huf: Option<&[u8]>) -> Result<()> { let huf_ptr = match dict_huf { Some(h) if !h.is_empty() => h.as_ptr() as *const c_void, _ => std::ptr::null(), }; let rc = unsafe { zxc_sys::zxc_seekable_set_dict( self.inner.as_ptr(), dict.as_ptr() as *const c_void, dict.len(), huf_ptr, ) }; if rc < 0 { Err(error_from_code(rc as i64)) } else { Ok(()) } } /// Decompresses `len` bytes starting at `offset` (in the original /// uncompressed byte stream) into `dst`. /// /// Only the blocks overlapping the requested range are read and /// decompressed. Returns the number of bytes actually written. pub fn decompress_range( &mut self, dst: &mut [u8], offset: u64, len: usize, ) -> Result { let res = unsafe { zxc_sys::zxc_seekable_decompress_range( self.inner.as_ptr(), dst.as_mut_ptr() as *mut c_void, dst.len(), offset, len, ) }; if res < 0 { Err(error_from_code(res)) } else { Ok(res as usize) } } } impl Drop for Seekable { fn drop(&mut self) { // SAFETY: inner was created by zxc_seekable_open / _open_file / // _open_reader and has not been freed yet. Free the C handle // first so no in-flight `read_at` calls reference our reader_ctx. unsafe { zxc_sys::zxc_seekable_free(self.inner.as_ptr()) }; if let Some(f) = self.file.take() { // SAFETY: f was returned by libc::fopen above. unsafe { libc::fclose(f) }; } if let Some(ctx) = self.reader_ctx.take() { // SAFETY: ctx was produced by Box::into_raw in open_reader // and has not been freed. The C handle was just freed above, // so no further read_at calls are possible. unsafe { drop(Box::from_raw(ctx)); } } } } /// Storage-agnostic positional reader for [`Seekable::open_reader`]. /// /// Implement this trait on any type that can serve byte ranges of a ZXC /// archive: an mmap'd region, an HTTP range-request client, an S3 object, a /// custom VFS, or any kernel-space file descriptor. /// /// # Contract /// /// - [`ReadAt::size`] returns the total size of the archive in bytes and /// should be constant for the lifetime of the reader. /// - [`ReadAt::read_at`] must fill `dst` completely from the requested /// `offset`. Short reads are reported as errors by the library. /// /// # Thread safety (future) /// /// The current Rust crate only exposes the single-threaded /// [`Seekable::decompress_range`], so implementations do not need to be /// thread-safe. A future multi-threaded entry point will require /// `Send + Sync`. pub trait ReadAt { /// Total size of the archive in bytes. fn size(&self) -> u64; /// Reads exactly `dst.len()` bytes at `offset` into `dst`. /// /// Returning `Err` (or panicking) causes the surrounding ZXC operation /// to fail with an I/O error. fn read_at(&self, dst: &mut [u8], offset: u64) -> std::io::Result<()>; } /// Trampoline invoked by the C library on every read. Translates the /// C-level `(ctx, dst, len, offset)` call into a Rust [`ReadAt::read_at`] /// invocation. Panics across the FFI boundary are caught and surfaced as /// `ZXC_ERROR_IO`. unsafe extern "C" fn reader_trampoline( ctx: *mut c_void, dst: *mut c_void, len: usize, offset: u64, ) -> i64 { let result = std::panic::catch_unwind(|| { // SAFETY: ctx is the `Box>` raw pointer we stored // in `Seekable::reader_ctx`. It remains valid for the lifetime of // the seekable handle, and `read_at` is only called between // `open_reader` and `Drop`. let reader: &Box = unsafe { &*(ctx as *const Box) }; // SAFETY: the C library guarantees `dst` points to `len` writable // bytes for the duration of the call. let buf = unsafe { std::slice::from_raw_parts_mut(dst as *mut u8, len) }; match reader.read_at(buf, offset) { Ok(()) => len as i64, Err(_) => zxc_sys::ZXC_ERROR_IO as i64, } }); result.unwrap_or(zxc_sys::ZXC_ERROR_IO as i64) } /// Encoded byte size of a seek table covering `num_blocks` data blocks. /// /// Use this to size a destination buffer for [`write_seek_table`]. pub fn seek_table_size(num_blocks: u32) -> usize { unsafe { zxc_sys::zxc_seek_table_size(num_blocks) } } /// Low-level: writes a seek table (header + entries) into `dst`. /// /// `comp_sizes` is the slice of per-block on-disk compressed sizes, in /// order. Most callers do not need this directly - the streaming and /// file APIs already emit a seek table when /// [`CompressOptions::seekable`] is set. pub fn write_seek_table(dst: &mut [u8], comp_sizes: &[u32]) -> Result { let res = unsafe { zxc_sys::zxc_write_seek_table( dst.as_mut_ptr(), dst.len(), comp_sizes.as_ptr(), comp_sizes.len() as u32, ) }; if res < 0 { Err(error_from_code(res)) } else { Ok(res as usize) } } #[cfg(unix)] fn path_to_cstring(path: &Path) -> Result { use std::os::unix::ffi::OsStrExt; CString::new(path.as_os_str().as_bytes()).map_err(|_| Error::Io) } #[cfg(windows)] fn path_to_cstring(path: &Path) -> Result { // libc::fopen on Windows expects an ANSI path. Round-trip through // the lossy UTF-8 representation - paths with characters outside // the active code page are not supported by this convenience entry // point; use `from_bytes` after reading the file yourself if you // need full Unicode path support. let s = path.to_str().ok_or(Error::Io)?; CString::new(s).map_err(|_| Error::Io) } #[cfg(test)] mod tests { use super::*; use crate::{compress_with_options, CompressOptions, Level}; fn build_archive(data: &[u8]) -> Vec { let opts = CompressOptions { level: Level::Default, checksum: true, seekable: true, ..Default::default() }; compress_with_options(data, &opts).expect("compression failed") } #[test] fn from_bytes_roundtrip() { let payload: Vec = (0..32_768).map(|i| (i as u8).wrapping_mul(31)).collect(); let compressed = build_archive(&payload); let mut s = Seekable::from_bytes(compressed).expect("open failed"); assert_eq!(s.decompressed_size(), payload.len() as u64); assert!(s.num_blocks() >= 1); // Block accessors must round-trip on the first block. assert!(s.block_compressed_size(0).unwrap() > 0); assert!(s.block_decompressed_size(0).unwrap() > 0); assert!(s.block_compressed_size(s.num_blocks()).is_none()); // Full-range decompression. let mut out = vec![0u8; payload.len()]; let n = s .decompress_range(&mut out, 0, payload.len()) .expect("decompress_range failed"); assert_eq!(n, payload.len()); assert_eq!(out, payload); } #[test] fn partial_range() { let payload: Vec = (0..16_384).map(|i| (i as u8) ^ 0xA5).collect(); let compressed = build_archive(&payload); let mut s = Seekable::from_bytes(compressed).expect("open failed"); let start = 1024usize; let len = 4096usize; let mut out = vec![0u8; len]; let n = s .decompress_range(&mut out, start as u64, len) .expect("decompress_range failed"); assert_eq!(n, len); assert_eq!(out, payload[start..start + len]); } #[test] fn invalid_buffer_fails() { let garbage = vec![0u8; 32]; assert!(Seekable::from_bytes(garbage).is_err()); } /// In-memory `ReadAt` impl backed by a Vec. The cell counts invocations /// so the test can assert lazy I/O at open and per-block reads. struct VecReader { data: Vec, calls: std::cell::Cell, } impl VecReader { fn new(data: Vec) -> Self { Self { data, calls: std::cell::Cell::new(0), } } } impl ReadAt for VecReader { fn size(&self) -> u64 { self.data.len() as u64 } fn read_at(&self, dst: &mut [u8], offset: u64) -> std::io::Result<()> { self.calls.set(self.calls.get() + 1); let off = offset as usize; if off.checked_add(dst.len()).map_or(true, |end| end > self.data.len()) { return Err(std::io::Error::new( std::io::ErrorKind::UnexpectedEof, "out of bounds", )); } dst.copy_from_slice(&self.data[off..off + dst.len()]); Ok(()) } } #[test] fn open_reader_roundtrip() { let payload: Vec = (0..64_000).map(|i| (i as u8) ^ 0x5A).collect(); let archive = build_archive(&payload); let archive_len = archive.len(); let mut s = Seekable::open_reader(VecReader::new(archive)).expect("open_reader failed"); assert_eq!(s.decompressed_size(), payload.len() as u64); assert!(s.num_blocks() >= 1); let _ = archive_len; // silence unused let mut out = vec![0u8; payload.len()]; let n = s .decompress_range(&mut out, 0, payload.len()) .expect("decompress_range failed"); assert_eq!(n, payload.len()); assert_eq!(out, payload); // Sub-range read. let start = 1024usize; let len = 8192usize; let mut out = vec![0u8; len]; let n = s .decompress_range(&mut out, start as u64, len) .expect("decompress_range failed"); assert_eq!(n, len); assert_eq!(out, payload[start..start + len]); } #[test] fn open_reader_invalid_archive_releases_reader() { // 64 bytes of zeros: passes the minimum-size check but parses as // invalid. open_reader must fail and free our reader_ctx box // (verified at runtime by miri / address sanitizer; here we just // check the Err path). let r = VecReader::new(vec![0u8; 64]); assert!(Seekable::open_reader(r).is_err()); } #[test] fn set_dict_range_roundtrip() { use crate::{train_dict, ZXC_DICT_SIZE_MAX}; // Train a dictionary from repetitive records, then build a seekable, // dict-compressed archive over many such records. let records: Vec> = (0..64) .map(|i| { format!("record#{i:04}:status=ok;region=eu-west;payload=AAAAAAAAAAAAAAAA;\n") .into_bytes() }) .collect(); let refs: Vec<&[u8]> = records.iter().map(|r| r.as_slice()).collect(); let dict = train_dict(&refs, ZXC_DICT_SIZE_MAX).expect("train_dict failed"); let mut payload = Vec::new(); for _ in 0..512 { for r in &records { payload.extend_from_slice(r); } } let opts = CompressOptions { level: Level::Default, checksum: true, seekable: true, dict: Some(dict.clone()), dict_huf: None, }; let archive = compress_with_options(&payload, &opts).expect("compression failed"); let mut s = Seekable::from_bytes(archive).expect("open failed"); s.set_dict(&dict, None).expect("set_dict failed"); assert_eq!(s.decompressed_size(), payload.len() as u64); // Full-range roundtrip. let mut out = vec![0u8; payload.len()]; let n = s .decompress_range(&mut out, 0, payload.len()) .expect("full range failed"); assert_eq!(n, payload.len()); assert_eq!(out, payload); // Sub-range roundtrip. let start = 5000usize; let len = 8192usize; let mut out = vec![0u8; len]; let n = s .decompress_range(&mut out, start as u64, len) .expect("sub range failed"); assert_eq!(n, len); assert_eq!(out, payload[start..start + len]); } #[test] fn seek_table_size_matches_write() { let comp_sizes = [128u32, 256, 200, 4]; let expected = seek_table_size(comp_sizes.len() as u32); let mut buf = vec![0u8; expected]; let written = write_seek_table(&mut buf, &comp_sizes).expect("write failed"); assert_eq!(written, expected); } } zxc-0.13.0/wrappers/rust/zxc/src/stdio.rs000066400000000000000000000327771522536370400203510ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ //! [`std::io::Read`] / [`std::io::Write`] adapters over the push streaming API. //! //! Mirror of the wrapper exposed by the Go binding: turns a [`CStream`] / //! [`DStream`] pair into the standard streaming traits so ZXC can be plugged //! into pipelines that expect them. use std::io::{self, Read, Write}; use crate::{CStream, CompressOptions, DStream, DecompressOptions, Error}; /// Magic word identifying a ZXC file frame: little-endian `0x9CB02EF5`. const ZXC_MAGIC_LE: [u8; 4] = [0xF5, 0x2E, 0xB0, 0x9C]; /// Reports whether `data` starts with the ZXC file magic word. /// /// Useful for content-type sniffing in containers / object stores that need /// to decide which decoder to dispatch. /// The check is cheap and side-effect free; it does not validate the rest of /// the header or the footer. /// /// # Example /// /// ```rust /// use zxc::{compress, detect_zxc, Level}; /// /// let frame = compress(b"hello", Level::Default, None).unwrap(); /// assert!(detect_zxc(&frame)); /// assert!(!detect_zxc(b"not a zxc frame")); /// ``` pub fn detect_zxc(data: &[u8]) -> bool { data.len() >= 4 && data[..4] == ZXC_MAGIC_LE } // --------------------------------------------------------------------------- // Encoder // --------------------------------------------------------------------------- /// Streaming compressor implementing [`std::io::Write`]. /// /// Writes are forwarded through a [`CStream`] and the resulting ZXC frame is /// flushed to the inner writer. The frame is finalised (residual block, EOF /// marker, and footer) by [`Encoder::finish`]. [`Drop`] performs a best-effort /// finish but cannot surface errors — prefer [`Encoder::finish`] when error /// handling matters. /// /// `Encoder` is single-threaded; one stream per writer. /// /// # Example /// /// ```rust,no_run /// use std::io::Write; /// use zxc::Encoder; /// /// let sink = Vec::new(); /// let mut enc = Encoder::new(sink).unwrap(); /// enc.write_all(b"hello world").unwrap(); /// let compressed: Vec = enc.finish().unwrap(); /// ``` pub struct Encoder { inner: Option, cs: Option, out_buf: Vec, } impl Encoder { /// Creates an encoder with default compression options. pub fn new(writer: W) -> Result { Self::with_options(writer, None) } /// Creates an encoder honouring `opts`. `opts.seekable` is ignored (push /// API is single-threaded and never emits a seek table). pub fn with_options(writer: W, opts: Option<&CompressOptions>) -> Result { let cs = CStream::new(opts)?; let cap = cs.out_size(); Ok(Self { inner: Some(writer), cs: Some(cs), out_buf: vec![0u8; cap], }) } /// Finalises the frame, drains the inner writer, and returns it. /// /// After this call the encoder is consumed; no further writes are /// possible. pub fn finish(mut self) -> io::Result { self.do_finish()?; // Safety: do_finish leaves both fields populated on success. Ok(self.inner.take().expect("inner writer present")) } /// Returns a reference to the underlying writer. pub fn get_ref(&self) -> &W { self.inner.as_ref().expect("encoder not finished") } /// Returns a mutable reference to the underlying writer. pub fn get_mut(&mut self) -> &mut W { self.inner.as_mut().expect("encoder not finished") } fn do_finish(&mut self) -> io::Result<()> { let (Some(cs), Some(w)) = (self.cs.as_mut(), self.inner.as_mut()) else { return Ok(()); }; loop { let p = cs.end(&mut self.out_buf).map_err(map_err)?; if p.produced > 0 { w.write_all(&self.out_buf[..p.produced])?; } if p.pending == 0 { break; } } // Drop the CStream now so a later finish() / Drop is a no-op. self.cs = None; Ok(()) } } impl Write for Encoder { fn write(&mut self, buf: &[u8]) -> io::Result { if buf.is_empty() { return Ok(0); } let cs = self .cs .as_mut() .ok_or_else(|| io::Error::other("encoder finished"))?; let w = self .inner .as_mut() .ok_or_else(|| io::Error::other("encoder finished"))?; let mut total = 0; let mut input = buf; while !input.is_empty() { let p = cs.compress(input, &mut self.out_buf).map_err(map_err)?; if p.produced > 0 { w.write_all(&self.out_buf[..p.produced])?; } total += p.consumed; input = &input[p.consumed..]; if p.consumed == 0 && p.produced == 0 && p.pending == 0 { // No forward progress is possible — bail out to avoid spin. break; } } Ok(total) } /// Flush is a no-op on the inner writer side: the ZXC frame is only valid /// once finalised, so partial flushes would produce a corrupted frame. /// Use [`Encoder::finish`] to complete the frame. fn flush(&mut self) -> io::Result<()> { if let Some(w) = self.inner.as_mut() { w.flush() } else { Ok(()) } } } impl Drop for Encoder { fn drop(&mut self) { // Best-effort: any error is silently swallowed because Drop cannot // surface failure. Callers who care about errors must call finish(). let _ = self.do_finish(); } } // --------------------------------------------------------------------------- // Decoder // --------------------------------------------------------------------------- /// Streaming decompressor implementing [`std::io::Read`]. /// /// Pulls compressed bytes from the inner reader and yields decompressed /// bytes. Returns [`io::ErrorKind::UnexpectedEof`] if the underlying reader /// is drained before the ZXC footer is reached. /// /// `Decoder` is single-threaded; one stream per reader. /// /// # Example /// /// ```rust,no_run /// use std::io::Read; /// use zxc::Decoder; /// /// let frame: &[u8] = &[]; // a ZXC frame /// let mut dec = Decoder::new(frame).unwrap(); /// let mut buf = Vec::new(); /// dec.read_to_end(&mut buf).unwrap(); /// ``` pub struct Decoder { inner: R, ds: DStream, in_buf: Vec, in_pos: usize, in_len: usize, eof: bool, } impl Decoder { /// Creates a decoder with default decompression options. pub fn new(reader: R) -> Result { Self::with_options(reader, None) } /// Creates a decoder honouring `opts`. pub fn with_options(reader: R, opts: Option<&DecompressOptions>) -> Result { let ds = DStream::new(opts)?; let cap = ds.in_size(); Ok(Self { inner: reader, ds, in_buf: vec![0u8; cap], in_pos: 0, in_len: 0, eof: false, }) } /// Returns a reference to the underlying reader. pub fn get_ref(&self) -> &R { &self.inner } /// Returns a mutable reference to the underlying reader. pub fn get_mut(&mut self) -> &mut R { &mut self.inner } /// Consumes the decoder and returns the inner reader. pub fn into_inner(self) -> R { self.inner } /// Reports whether the decoder has reached and validated the file footer. pub fn finished(&self) -> bool { self.ds.finished() } } impl Read for Decoder { fn read(&mut self, buf: &mut [u8]) -> io::Result { if buf.is_empty() { return Ok(0); } loop { if self.ds.finished() { return Ok(0); } // Try to decompress whatever is currently buffered (or drain mode // when src is at EOF). if self.in_pos < self.in_len || self.eof { let p = self .ds .decompress(&self.in_buf[self.in_pos..self.in_len], buf) .map_err(map_err)?; self.in_pos += p.consumed; if p.produced > 0 { return Ok(p.produced); } if p.consumed == 0 { if self.eof { if self.ds.finished() { return Ok(0); } return Err(io::Error::new( io::ErrorKind::UnexpectedEof, "zxc: input drained before footer", )); } // fall through to refill } else { // consumed > 0 but no output — loop and retry. continue; } } // Refill: shift consumed bytes out, then read more from src. if self.in_pos > 0 { self.in_buf.copy_within(self.in_pos..self.in_len, 0); self.in_len -= self.in_pos; self.in_pos = 0; } let n = self.inner.read(&mut self.in_buf[self.in_len..])?; if n == 0 { self.eof = true; } else { self.in_len += n; } } } } // --------------------------------------------------------------------------- // Error mapping // --------------------------------------------------------------------------- fn map_err(e: Error) -> io::Error { io::Error::other(e) } // --------------------------------------------------------------------------- // Tests // --------------------------------------------------------------------------- #[cfg(test)] mod tests { use super::*; use crate::compress; use crate::Level; use std::io::{Cursor, Read, Write}; fn roundtrip(data: &[u8]) -> Vec { let mut enc = Encoder::new(Vec::new()).expect("encoder"); enc.write_all(data).expect("write"); let frame = enc.finish().expect("finish"); let mut dec = Decoder::new(Cursor::new(frame)).expect("decoder"); let mut out = Vec::new(); dec.read_to_end(&mut out).expect("read_to_end"); out } #[test] fn encoder_decoder_small_roundtrip() { let data = b"Hello std::io::Read / std::io::Write bridge over ZXC."; assert_eq!(roundtrip(data), data); } #[test] fn encoder_decoder_large_roundtrip() { let data: Vec = (0..2 * 1024 * 1024).map(|i| ((i * 13) % 251) as u8).collect(); assert_eq!(roundtrip(&data), data); } #[test] fn encoder_many_small_writes() { let mut enc = Encoder::new(Vec::new()).unwrap(); let mut want: Vec = Vec::with_capacity(64 * 1024); for i in 0u32..4096 { let chunk = [i as u8, (i >> 8) as u8, (i ^ 0x5A) as u8]; want.extend_from_slice(&chunk); enc.write_all(&chunk).unwrap(); } let frame = enc.finish().unwrap(); let mut dec = Decoder::new(Cursor::new(frame)).unwrap(); let mut got = Vec::new(); dec.read_to_end(&mut got).unwrap(); assert_eq!(got, want); } #[test] fn encoder_decoder_with_checksum() { use crate::{CompressOptions, DecompressOptions}; let data: Vec = (0..32 * 1024).map(|i| i as u8).collect(); let copts = CompressOptions { checksum: true, ..Default::default() }; let dopts = DecompressOptions { verify_checksum: true, ..Default::default() }; let mut enc = Encoder::with_options(Vec::new(), Some(&copts)).unwrap(); enc.write_all(&data).unwrap(); let frame = enc.finish().unwrap(); let mut dec = Decoder::with_options(Cursor::new(frame), Some(&dopts)).unwrap(); let mut got = Vec::new(); dec.read_to_end(&mut got).unwrap(); assert_eq!(got, data); } #[test] fn drop_finishes_frame() { // Encoder is dropped without an explicit finish() — the resulting // frame must still be well-formed (best-effort flush in Drop). let mut sink: Vec = Vec::new(); { let mut enc = Encoder::new(&mut sink).unwrap(); enc.write_all(b"drop-flush").unwrap(); } let mut dec = Decoder::new(Cursor::new(sink)).unwrap(); let mut got = Vec::new(); dec.read_to_end(&mut got).unwrap(); assert_eq!(got, b"drop-flush"); } #[test] fn decoder_truncated_frame_errors() { let frame = compress(&vec![b'A'; 32 * 1024], Level::Default, None).unwrap(); let truncated = &frame[..frame.len() / 2]; let mut dec = Decoder::new(Cursor::new(truncated)).unwrap(); let mut got = Vec::new(); let err = dec.read_to_end(&mut got).unwrap_err(); assert_eq!(err.kind(), io::ErrorKind::UnexpectedEof); } #[test] fn detect_zxc_basic() { let frame = compress(b"sniff me", Level::Default, None).unwrap(); assert!(detect_zxc(&frame)); let mut enc = Encoder::new(Vec::new()).unwrap(); enc.write_all(b"hi").unwrap(); let frame = enc.finish().unwrap(); assert!(detect_zxc(&frame)); assert!(!detect_zxc(&[])); assert!(!detect_zxc(&[0xF5, 0x2E, 0xB0])); // 3 bytes, too short assert!(!detect_zxc(&[0; 4])); assert!(!detect_zxc(b"not a zxc frame at all")); } } zxc-0.13.0/wrappers/wasm/000077500000000000000000000000001522536370400152205ustar00rootroot00000000000000zxc-0.13.0/wrappers/wasm/.nvmrc000066400000000000000000000000021522536370400163360ustar00rootroot0000000000000022zxc-0.13.0/wrappers/wasm/README.md000066400000000000000000000061721522536370400165050ustar00rootroot00000000000000# ZXC WebAssembly High-performance lossless compression for the browser and Node.js via WebAssembly. ## Features - **Buffer API**: Compress and decompress `Uint8Array` buffers - **Reusable Contexts**: Amortise allocation overhead across multiple operations - **All Levels**: Compression levels 1–7 - **Checksum Support**: Optional integrity verification - **Tiny Footprint**: ~60 KB `.wasm` file (scalar build, no SIMD) ## Quick Start ### Browser (ES Module) ```js import createZXC from './zxc_wasm.js'; const zxc = await createZXC(); // Compress const input = new TextEncoder().encode('Hello, World!'); const compressed = zxc.compress(input, { level: 3 }); // Decompress const output = zxc.decompress(compressed); console.log(new TextDecoder().decode(output)); // "Hello, World!" ``` ### Node.js ```js import createZXC from 'zxc-wasm'; import { readFileSync } from 'fs'; const zxc = await createZXC(); const data = new Uint8Array(readFileSync('input.bin')); const compressed = zxc.compress(data, { level: 5, checksum: true }); const decompressed = zxc.decompress(compressed, { checksum: true }); ``` ## API Reference ### `createZXC(moduleOverrides?) -> Promise` Initialise the WASM module. Returns a frozen API object. ### `zxc.compress(data, opts?) -> Uint8Array` | Option | Type | Default | Description | |--------|------|---------|-------------| | `level` | `number` | `3` | Compression level (1–7) | | `checksum` | `boolean` | `false` | Enable integrity checksums | ### `zxc.decompress(data, opts?) -> Uint8Array` | Option | Type | Default | Description | |--------|------|---------|-------------| | `checksum` | `boolean` | `false` | Verify integrity checksums | ### `zxc.compressBound(inputSize) -> number` Maximum compressed output size for a given input size. ### `zxc.getDecompressedSize(data) -> number` Read the original size from a compressed buffer without decompressing. ### `zxc.createCompressContext(opts?) -> CompressContext` Create a reusable compression context (avoids per-call allocation). ```js const ctx = zxc.createCompressContext({ level: 3 }); const c1 = ctx.compress(data1); const c2 = ctx.compress(data2); ctx.free(); // Release WASM memory ``` ### `zxc.createDecompressContext() -> DecompressContext` Create a reusable decompression context. ```js const ctx = zxc.createDecompressContext(); const d1 = ctx.decompress(compressed1); const d2 = ctx.decompress(compressed2); ctx.free(); ``` ### Properties | Property | Type | Description | |----------|------|-------------| | `version` | `string` | Library version (e.g. `"0.13.0"`) | | `minLevel` | `number` | Minimum compression level (`1`) | | `maxLevel` | `number` | Maximum compression level (`5`) | | `defaultLevel` | `number` | Default compression level (`3`) | ## Building from Source Requires [Emscripten SDK](https://emscripten.org/docs/getting_started/downloads.html). ```bash # Configure emcmake cmake -B build-wasm -DCMAKE_BUILD_TYPE=Release # Build cmake --build build-wasm # Test BUILD_DIR=build-wasm node wrappers/wasm/test.mjs ``` The build produces `build-wasm/zxc.js` and `build-wasm/zxc.wasm`. ## License BSD 3-Clause. See [LICENSE](../../LICENSE). zxc-0.13.0/wrappers/wasm/package-lock.json000066400000000000000000000003261522536370400204350ustar00rootroot00000000000000{ "name": "zxc-wasm", "version": "0.13.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "zxc-wasm", "version": "0.13.0", "license": "BSD-3-Clause" } } } zxc-0.13.0/wrappers/wasm/package.json000066400000000000000000000013401522536370400175040ustar00rootroot00000000000000{ "name": "zxc-wasm", "version": "0.13.0", "description": "ZXC high-performance asymmetric lossless compression built for ultra-fast decode - WebAssembly build", "type": "module", "main": "zxc_wasm.js", "exports": { ".": "./zxc_wasm.js" }, "files": [ "zxc_wasm.js", "zxc.js", "zxc.wasm", "README.md" ], "scripts": { "test": "node test.mjs" }, "keywords": [ "compression", "decompression", "lz77", "wasm", "webassembly", "zxc", "lossless", "high-performance" ], "author": "Bertrand Lebonnois", "license": "BSD-3-Clause", "repository": { "type": "git", "url": "https://github.com/hellobertrand/zxc.git", "directory": "wrappers/wasm" } }zxc-0.13.0/wrappers/wasm/test.mjs000066400000000000000000000602411522536370400167150ustar00rootroot00000000000000/** * ZXC WASM Roundtrip Test * * Validates compress -> decompress correctness via Node.js. * Run: node wrappers/wasm/test.mjs * * Expects the built zxc.js + zxc.wasm to be in the build directory. * The BUILD_DIR environment variable can override the default path. * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ import { join, dirname, resolve } from 'path'; import { fileURLToPath, pathToFileURL } from 'url'; const __dirname = dirname(fileURLToPath(import.meta.url)); // Resolve BUILD_DIR relative to CWD (not the test file location) const buildDir = process.env.BUILD_DIR ? resolve(process.cwd(), process.env.BUILD_DIR) : join(__dirname, '..', '..', 'build-wasm'); // The module is built with -sEXPORT_ES6=1; import it as ESM. const ZXCModule = (await import(pathToFileURL(join(buildDir, 'zxc.js')))).default; let passed = 0; let failed = 0; function assert(cond, msg) { if (!cond) { console.error(` ✗ FAIL: ${msg}`); failed++; } else { console.log(` ✓ ${msg}`); passed++; } } function arraysEqual(a, b) { if (a.length !== b.length) return false; for (let i = 0; i < a.length; i++) { if (a[i] !== b[i]) return false; } return true; } async function main() { console.log('ZXC WASM Test Suite\n'); // --- 1. Module initialisation --- console.log('1. Module Initialisation'); const Module = await ZXCModule(); assert(!!Module, 'Module loaded successfully'); // Wrap core functions const _compress = Module.cwrap('zxc_compress', 'number', ['number', 'number', 'number', 'number', 'number']); const _decompress = Module.cwrap('zxc_decompress', 'number', ['number', 'number', 'number', 'number', 'number']); const _compress_bound = Module.cwrap('zxc_compress_bound', 'number', ['number']); const _get_decompressed_size = Module.cwrap('zxc_get_decompressed_size', 'number', ['number', 'number']); const _version_string = Module.cwrap('zxc_version_string', 'string', []); const _min_level = Module.cwrap('zxc_min_level', 'number', []); const _max_level = Module.cwrap('zxc_max_level', 'number', []); const _default_level = Module.cwrap('zxc_default_level', 'number', []); const _error_name = Module.cwrap('zxc_error_name', 'string', ['number']); // --- 2. Library info --- console.log('\n2. Library Info'); const version = _version_string(); assert(typeof version === 'string' && version.length > 0, `Version: ${version}`); assert(_min_level() === 1, `Min level: ${_min_level()}`); assert(_max_level() === 7, `Max level: ${_max_level()}`); assert(_default_level() === 3, `Default level: ${_default_level()}`); // --- 3. Compress bound --- console.log('\n3. Compress Bound'); const bound1k = _compress_bound(1024); assert(bound1k > 1024, `Bound for 1KB: ${bound1k}`); const bound0 = _compress_bound(0); assert(bound0 > 0, `Bound for 0 bytes: ${bound0}`); // --- 4. Roundtrip: simple string --- console.log('\n4. Roundtrip: Simple String'); { const input = new TextEncoder().encode('Hello, ZXC WebAssembly! '.repeat(100)); const bound = _compress_bound(input.length); const srcPtr = Module._malloc(input.length); const dstPtr = Module._malloc(bound); Module.HEAPU8.set(input, srcPtr); const csize = _compress(srcPtr, input.length, dstPtr, bound, 0); assert(csize > 0, `Compressed ${input.length} -> ${csize} bytes`); assert(csize < input.length, `Compression ratio: ${(input.length / csize).toFixed(2)}x`); // Read back compressed data const compressed = new Uint8Array(Module.HEAPU8.buffer, dstPtr, csize).slice(); // Get decompressed size const csrcPtr = Module._malloc(compressed.length); Module.HEAPU8.set(compressed, csrcPtr); const origSize = _get_decompressed_size(csrcPtr, compressed.length); assert(origSize === input.length, `Decompressed size: ${origSize} (expected ${input.length})`); // Decompress const outPtr = Module._malloc(origSize); const dsize = _decompress(csrcPtr, compressed.length, outPtr, origSize, 0); assert(dsize === input.length, `Decompressed ${dsize} bytes`); const output = new Uint8Array(Module.HEAPU8.buffer, outPtr, dsize).slice(); assert(arraysEqual(input, output), 'Roundtrip byte-exact match'); Module._free(srcPtr); Module._free(dstPtr); Module._free(csrcPtr); Module._free(outPtr); } // --- 5. Roundtrip: all compression levels --- console.log('\n5. Roundtrip: All Compression Levels'); { const input = new Uint8Array(4096); // Fill with semi-compressible pattern for (let i = 0; i < input.length; i++) { input[i] = (i * 7 + 13) & 0xFF; } const bound = _compress_bound(input.length); for (let level = 1; level <= 7; level++) { const srcPtr = Module._malloc(input.length); const dstPtr = Module._malloc(bound); Module.HEAPU8.set(input, srcPtr); // Build opts struct (full 40-byte zxc_compress_opts_t so the // dict/progress fields the library reads are zeroed, not heap // garbage): {n_threads=0, level, block_size=0, checksum=1, ...} const optsPtr = Module._malloc(40); for (let i = 0; i < 40; i++) Module.HEAPU8[optsPtr + i] = 0; Module.HEAP32[(optsPtr >> 2) + 1] = level; // level Module.HEAP32[(optsPtr >> 2) + 3] = 1; // checksum_enabled const csize = _compress(srcPtr, input.length, dstPtr, bound, optsPtr); assert(csize > 0, `Level ${level}: compressed to ${csize} bytes`); // Decompress with checksum verification (28-byte zxc_decompress_opts_t) const compressed = new Uint8Array(Module.HEAPU8.buffer, dstPtr, csize).slice(); const csrcPtr = Module._malloc(compressed.length); Module.HEAPU8.set(compressed, csrcPtr); const dOptsPtr = Module._malloc(28); for (let i = 0; i < 28; i++) Module.HEAPU8[dOptsPtr + i] = 0; Module.HEAP32[(dOptsPtr >> 2) + 1] = 1; // checksum_enabled const outPtr = Module._malloc(input.length); const dsize = _decompress(csrcPtr, compressed.length, outPtr, input.length, dOptsPtr); const output = new Uint8Array(Module.HEAPU8.buffer, outPtr, dsize).slice(); assert(arraysEqual(input, output), `Level ${level}: roundtrip OK`); Module._free(srcPtr); Module._free(dstPtr); Module._free(optsPtr); Module._free(csrcPtr); Module._free(dOptsPtr); Module._free(outPtr); } } // --- 6. Reusable context API --- console.log('\n6. Reusable Context API'); { const _create_cctx = Module.cwrap('zxc_create_cctx', 'number', ['number']); const _free_cctx = Module.cwrap('zxc_free_cctx', 'void', ['number']); const _compress_cctx = Module.cwrap('zxc_compress_cctx', 'number', ['number', 'number', 'number', 'number', 'number', 'number']); const _create_dctx = Module.cwrap('zxc_create_dctx', 'number', []); const _free_dctx = Module.cwrap('zxc_free_dctx', 'void', ['number']); const _decompress_dctx = Module.cwrap('zxc_decompress_dctx', 'number', ['number', 'number', 'number', 'number', 'number', 'number']); const cctx = _create_cctx(0); assert(cctx !== 0, 'Created compression context'); const dctx = _create_dctx(); assert(dctx !== 0, 'Created decompression context'); // Compress two different payloads with same context for (let trial = 0; trial < 3; trial++) { const input = new TextEncoder().encode(`Trial ${trial}: ${'ABCDEFGH'.repeat(200)}`); const bound = _compress_bound(input.length); const srcPtr = Module._malloc(input.length); const dstPtr = Module._malloc(bound); Module.HEAPU8.set(input, srcPtr); const csize = _compress_cctx(cctx, srcPtr, input.length, dstPtr, bound, 0); assert(csize > 0, `Context compress trial ${trial}: ${csize} bytes`); const compressed = new Uint8Array(Module.HEAPU8.buffer, dstPtr, csize).slice(); const csrcPtr = Module._malloc(compressed.length); Module.HEAPU8.set(compressed, csrcPtr); const outPtr = Module._malloc(input.length); const dsize = _decompress_dctx(dctx, csrcPtr, compressed.length, outPtr, input.length, 0); const output = new Uint8Array(Module.HEAPU8.buffer, outPtr, dsize).slice(); assert(arraysEqual(input, output), `Context roundtrip trial ${trial}: OK`); Module._free(srcPtr); Module._free(dstPtr); Module._free(csrcPtr); Module._free(outPtr); } _free_cctx(cctx); _free_dctx(dctx); console.log(' Contexts freed.'); } // --- 7. Error handling --- console.log('\n7. Error Handling'); { const errorName = _error_name(-1); assert(typeof errorName === 'string', `Error name for -1: ${errorName}`); // Attempt to decompress garbage const garbage = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]); const gPtr = Module._malloc(garbage.length); const outPtr = Module._malloc(1024); Module.HEAPU8.set(garbage, gPtr); const result = _decompress(gPtr, garbage.length, outPtr, 1024, 0); assert(result < 0, `Garbage decompression returns error: ${result} (${_error_name(result)})`); Module._free(gPtr); Module._free(outPtr); } // --- 8. Push streaming API (high-level wrapper) ----------------------- console.log('\n8. Push Streaming API (CStream / DStream)'); { const { default: createZXC } = await import('./zxc_wasm.js'); const zxc = await createZXC({}, ZXCModule); const cs = zxc.createCStream({ checksum: true }); assert(cs.inSize() > 0 && cs.outSize() > 0, `cstream size hints: in=${cs.inSize()} out=${cs.outSize()}`); // Multi-block payload to exercise > 1 block. const data = new Uint8Array(512 * 1024); for (let i = 0; i < data.length; i++) data[i] = (i * 7) & 0xff; const chunks = []; let totalC = 0; const step = 17_000; for (let i = 0; i < data.length; i += step) { const c = cs.compress(data.subarray(i, Math.min(i + step, data.length))); chunks.push(c); totalC += c.length; } const tail = cs.end(); chunks.push(tail); totalC += tail.length; cs.free(); const compressed = new Uint8Array(totalC); let off = 0; for (const c of chunks) { compressed.set(c, off); off += c.length; } assert(compressed.length > 0, `pstream compressed ${data.length} -> ${compressed.length} bytes`); const ds = zxc.createDStream({ checksum: true }); const outChunks = []; let totalD = 0; const dstep = 31_000; for (let i = 0; i < compressed.length; i += dstep) { const o = ds.decompress(compressed.subarray(i, Math.min(i + dstep, compressed.length))); outChunks.push(o); totalD += o.length; } assert(ds.finished(), 'dstream finished after full input'); ds.free(); const decoded = new Uint8Array(totalD); off = 0; for (const c of outChunks) { decoded.set(c, off); off += c.length; } assert(decoded.length === data.length, `pstream decoded ${decoded.length} bytes (expected ${data.length})`); assert(arraysEqual(data, decoded), 'pstream roundtrip byte-exact match'); } // --- 9. WHATWG TransformStream adapters ------------------------------- console.log('\n9. WHATWG TransformStream adapters + detectZxc'); { const { default: createZXC, detectZxc } = await import('./zxc_wasm.js'); const zxc = await createZXC({}, ZXCModule); // detectZxc: works without WASM init too (pure JS), but check both // call sites as they share the same implementation. assert(detectZxc === zxc.detectZxc, 'detectZxc exported at module + API level'); assert(!detectZxc(null), 'detectZxc(null) === false'); assert(!detectZxc(new Uint8Array([0xF5, 0x2E, 0xB0])), 'detectZxc rejects 3-byte input'); assert(!detectZxc(new Uint8Array(4)), 'detectZxc rejects zero buffer'); // Build a frame with the buffer API and sniff it. const frame = zxc.compress(new Uint8Array([1, 2, 3, 4, 5])); assert(detectZxc(frame), 'detectZxc accepts compress() output'); assert(detectZxc(frame.buffer), 'detectZxc accepts ArrayBuffer'); // TransformStream roundtrip via pipeThrough(). const data = new Uint8Array(64 * 1024); for (let i = 0; i < data.length; i++) data[i] = (i ^ 0x5A) & 0xFF; const sourceCompressed = new ReadableStream({ start(c) { c.enqueue(data); c.close(); } }).pipeThrough(zxc.createCompressTransformStream()); const compressedChunks = []; let cTotal = 0; for await (const c of sourceCompressed) { compressedChunks.push(c); cTotal += c.length; } const compressed = new Uint8Array(cTotal); { let off = 0; for (const c of compressedChunks) { compressed.set(c, off); off += c.length; } } assert(detectZxc(compressed), 'TransformStream output is a valid ZXC frame'); const sourceDecompressed = new ReadableStream({ start(c) { c.enqueue(compressed); c.close(); } }).pipeThrough(zxc.createDecompressTransformStream()); const outChunks = []; let dTotal = 0; for await (const c of sourceDecompressed) { outChunks.push(c); dTotal += c.length; } const decoded = new Uint8Array(dTotal); { let off = 0; for (const c of outChunks) { decoded.set(c, off); off += c.length; } } assert(decoded.length === data.length, `TransformStream decoded ${decoded.length} bytes (expected ${data.length})`); assert(arraysEqual(data, decoded), 'TransformStream roundtrip byte-exact match'); // Truncated frame must error the decode stream. const truncated = compressed.subarray(0, Math.floor(compressed.length / 2)); const truncSource = new ReadableStream({ start(c) { c.enqueue(truncated); c.close(); } }).pipeThrough(zxc.createDecompressTransformStream()); let didThrow = false; let errorCode; try { for await (const _ of truncSource) { /* drain */ } } catch (e) { didThrow = true; errorCode = e?.code; } assert(didThrow, 'truncated frame throws an error'); assert(errorCode === 'ZXC_TRUNCATED', 'truncated frame error code is ZXC_TRUNCATED'); } // --- 10. Seekable API (random-access decompression) ------------------- console.log('\n10. Seekable API'); { const { default: createZXC } = await import('./zxc_wasm.js'); const zxc = await createZXC({}, ZXCModule); // Build a payload that exercises both block-level and intra-block ranges. const payload = new Uint8Array(64 * 1024); for (let i = 0; i < payload.length; i++) payload[i] = (i * 31) & 0xFF; const compressed = zxc.compress(payload, { seekable: true, checksum: true }); assert(compressed.length > 0, `seekable archive compressed to ${compressed.length} bytes`); const s = zxc.createSeekable(compressed); try { assert(s.decompressedSize() === payload.length, `decompressedSize=${s.decompressedSize()} (expected ${payload.length})`); assert(s.numBlocks() >= 1, `numBlocks=${s.numBlocks()}`); const cs0 = s.blockCompressedSize(0); const ds0 = s.blockDecompressedSize(0); assert(typeof cs0 === 'number' && cs0 > 0, `block 0 compressed size=${cs0}`); assert(typeof ds0 === 'number' && ds0 > 0, `block 0 decompressed size=${ds0}`); assert(s.blockCompressedSize(s.numBlocks()) === null, 'out-of-range block index returns null'); // Full-range round-trip. const full = s.decompressRange(0, payload.length); assert(full.length === payload.length, `full range length=${full.length}`); assert(arraysEqual(full, payload), 'seekable full-range byte-exact match'); // Mid-range slice. const off = 1024, len = 8192; const mid = s.decompressRange(off, len); assert(mid.length === len, `mid range length=${mid.length}`); assert(arraysEqual(mid, payload.subarray(off, off + len)), 'seekable mid-range byte-exact match'); } finally { s.free(); } // Invalid archive throws. let didThrow = false; try { zxc.createSeekable(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8])); } catch (_) { didThrow = true; } assert(didThrow, 'createSeekable on garbage throws'); // Low-level seek table helpers. const compSizes = [128, 256, 200, 4]; const tableSize = zxc.seekTableSize(compSizes.length); const table = zxc.writeSeekTable(compSizes); assert(table.length === tableSize, `writeSeekTable bytes=${table.length} (expected ${tableSize})`); } // --- 11. Dictionary API ---------------------------------------------- console.log('\n11. Dictionary API'); { const { default: createZXC } = await import('./zxc_wasm.js'); const zxc = await createZXC({}, ZXCModule); // Build a corpus of similar JSON-like samples so training has shared // structure to extract. const samples = []; for (let k = 0; k < 16; k++) { const s = `{"id":${k},"type":"event","payload":{"user":"alice","action":"login","region":"eu-west"}}`; samples.push(new TextEncoder().encode(s)); } const dict = zxc.trainDict(samples); assert(dict instanceof Uint8Array && dict.length > 0, `trainDict produced ${dict.length}-byte dictionary`); // Compress a fresh similar payload with the dict, decompress with it. const payload = new TextEncoder().encode( '{"id":999,"type":"event","payload":{"user":"alice","action":"login","region":"eu-west"}}'); const compressed = zxc.compress(payload, { dict }); assert(compressed.length > 0, `dict-compressed to ${compressed.length} bytes`); const decoded = zxc.decompress(compressed, { dict }); assert(arraysEqual(decoded, payload), 'dict compress/decompress byte-exact roundtrip'); // Decompressing a dict archive WITHOUT the dict must fail. let threwNoDict = false; try { zxc.decompress(compressed); } catch (_) { threwNoDict = true; } assert(threwNoDict, 'decompress of dict archive without dict throws'); // ID consistency: dictId(content) == getDictId(archive) == dictGetId(.zxd). const idContent = zxc.dictId(dict); const idArchive = zxc.getDictId(compressed); assert(idContent !== 0, `dictId(content)=${idContent}`); assert(idContent === idArchive, `dictId == getDictId(archive) (${idContent} == ${idArchive})`); // trainDictHuf -> dictSave -> dictGetId / dictLoad / dictHuf roundtrip. const huf = zxc.trainDictHuf(samples, dict); assert(huf instanceof Uint8Array && huf.length === 128, `trainDictHuf produced ${huf.length}-byte table`); const zxd = zxc.dictSave(dict, huf); assert(zxd instanceof Uint8Array && zxd.length > dict.length, `dictSave produced ${zxd.length}-byte .zxd`); const idZxd = zxc.dictGetId(zxd); // The .zxd id binds (content, table): non-zero, distinct from the // content-only id. assert(idZxd !== 0 && idZxd !== idContent, `dictGetId(.zxd) binds the table (${idZxd} != ${idContent})`); assert(arraysEqual(zxc.dictHuf(zxd), huf), 'dictHuf(.zxd) returns the stored table'); const loaded = zxc.dictLoad(zxd); assert(loaded.id === idZxd, `dictLoad id matches (${loaded.id})`); assert(arraysEqual(loaded.content, dict), 'dictSave -> dictLoad content roundtrip'); assert(arraysEqual(loaded.huf, huf), 'dictLoad returns the stored huf table'); // dictTrain one-call creator: byte-identical to the 3-step sequence. const zxdOneCall = zxc.dictTrain(samples); assert(arraysEqual(zxdOneCall, zxd), 'dictTrain == trainDict+trainDictHuf+dictSave'); // Dictionary object: train / save / load roundtrip, options routing. const d = zxc.Dictionary.train(samples); assert(arraysEqual(d.content, dict) && arraysEqual(d.huf, huf) && d.id === idZxd, 'Dictionary.train carries (content, huf, id)'); const dReloaded = zxc.Dictionary.load(d.save()); assert(arraysEqual(dReloaded.content, d.content) && arraysEqual(dReloaded.huf, d.huf) && dReloaded.id === d.id, 'Dictionary save/load roundtrip'); const cObj = zxc.compress(payload, { dict: d }); assert(arraysEqual(zxc.decompress(cObj, { dict: d }), payload), 'compress/decompress with {dict: Dictionary} roundtrip'); let objRejected = false; try { zxc.decompress(cObj, { dict: d.content }); } catch (e) { objRejected = true; } assert(objRejected, 'Dictionary archive rejects content-only decode (id binding)'); // dictHuf round through compress/decompress (id binding both ways). const cHuf = zxc.compress(payload, { dict, dictHuf: huf }); assert(arraysEqual(zxc.decompress(cHuf, { dict, dictHuf: huf }), payload), 'compress/decompress with dictHuf roundtrip'); let rejected = false; try { zxc.decompress(cHuf, { dict }); } catch (e) { rejected = true; } assert(rejected, 'decompress without dictHuf is rejected (id binding)'); // Seekable + setDict + range roundtrip on a dict-compressed archive. const bigPayload = new Uint8Array(48 * 1024); { const unit = new TextEncoder().encode( '{"user":"alice","action":"login","region":"eu-west"}'); for (let i = 0; i < bigPayload.length; i++) bigPayload[i] = unit[i % unit.length]; } const seekArchive = zxc.compress(bigPayload, { dict, seekable: true }); const s = zxc.createSeekable(seekArchive); try { s.setDict(dict); const full = s.decompressRange(0, bigPayload.length); assert(arraysEqual(full, bigPayload), 'seekable+dict full-range byte-exact'); const off = 4096, len = 8192; const mid = s.decompressRange(off, len); assert(arraysEqual(mid, bigPayload.subarray(off, off + len)), 'seekable+dict mid-range byte-exact'); } finally { s.free(); s.free(); // idempotent: second call must be a no-op } // Seekable + Dictionary (content + huf table): regression for setDict // dropping the table argument, which made every (dict, huf)-bound // archive fail range decode with DICT_MISMATCH. const seekArchiveHuf = zxc.compress(bigPayload, { dict: d, seekable: true }); const sh = zxc.createSeekable(seekArchiveHuf); try { sh.setDict(d); const full = sh.decompressRange(0, bigPayload.length); assert(arraysEqual(full, bigPayload), 'seekable+Dictionary(huf) full-range byte-exact'); let hufRejected = false; try { sh.setDict(d.content); } catch (e) { hufRejected = true; } assert(hufRejected, 'seekable setDict(content-only) rejected on a Dictionary archive'); } finally { sh.free(); } } // --- Summary --- console.log(`\n${'='.repeat(40)}`); console.log(`Results: ${passed} passed, ${failed} failed`); process.exit(failed > 0 ? 1 : 0); } main().catch(err => { console.error('Fatal error:', err); process.exit(1); }); zxc-0.13.0/wrappers/wasm/wasm_entry.c000066400000000000000000000033311522536370400175540ustar00rootroot00000000000000/* * ZXC - High-performance lossless compression * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * @file wasm_entry.c * @brief Entry point and small JS-friendly shims for the WebAssembly build. * * Emscripten requires a main() symbol when producing a .js + .wasm pair * via add_executable(). Most public ZXC functionality is exported * directly from the library through -sEXPORTED_FUNCTIONS; this file * adds a couple of thin shims that adapt 64-bit C parameters to wasm32 * so the JS wrapper can call them through cwrap('number', ...) without * relying on BigInt support. */ #include #include "zxc_seekable.h" #ifdef __EMSCRIPTEN__ #include #else #define EMSCRIPTEN_KEEPALIVE #endif int main(void) { return 0; } /** * 32-bit-offset shim around zxc_seekable_decompress_range. * * The native function takes a `uint64_t` offset; passing an `i64` through * cwrap without `-sWASM_BIGINT=1` is not supported. The wasm32 heap is * itself bounded to 4 GiB, so a 32-bit offset is sufficient for any * archive the runtime could materialise in memory. * * Returns the number of bytes written, or a negative `zxc_error_t` code. * Returns truncated to int32_t (sufficient: max single-range length is * size_t which is i32 on wasm32). */ EMSCRIPTEN_KEEPALIVE int32_t zxcw_seekable_decompress_range(zxc_seekable* s, void* dst, uint32_t dst_capacity, uint32_t offset, uint32_t len) { int64_t r = zxc_seekable_decompress_range(s, dst, (size_t)dst_capacity, (uint64_t)offset, (size_t)len); if (r > INT32_MAX) return INT32_MAX; if (r < INT32_MIN) return INT32_MIN; return (int32_t)r; } zxc-0.13.0/wrappers/wasm/zxc_wasm.js000066400000000000000000001514401522536370400174160ustar00rootroot00000000000000/** * ZXC WebAssembly Wrapper * * High-level JavaScript API for ZXC compression/decompression via WASM. * * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors. * SPDX-License-Identifier: BSD-3-Clause */ /** * Returns true if `buf` starts with the ZXC file magic word * (little-endian 0x9CB02EF5). * * Side-effect free, synchronous, does not require WASM initialisation — * suitable for content-type sniffing in fetch/Service Worker pipelines and * for OCI media-type negotiation. * * @param {Uint8Array | ArrayBuffer | null | undefined} buf * @returns {boolean} */ export function detectZxc(buf) { if (!buf) return false; const view = buf instanceof ArrayBuffer ? new Uint8Array(buf) : buf; if (!view || view.length < 4) return false; return view[0] === 0xF5 && view[1] === 0x2E && view[2] === 0xB0 && view[3] === 0x9C; } /** * Initialise the ZXC WASM module and return an ergonomic API object. * * @param {object} [moduleOverrides] - Optional Emscripten module overrides * (e.g. { locateFile: ... } for custom .wasm path resolution). * @param {Function} [factory] - Optional Emscripten module factory. If * omitted, the default sibling `./zxc.js` is dynamically imported. * @returns {Promise} Resolved API object. */ export default async function createZXC(moduleOverrides, factory) { let ZXCModule = factory; if (!ZXCModule) { ZXCModule = (await import('./zxc.js')).default; if (typeof ZXCModule !== 'function') { try { const { createRequire } = await import('node:module'); ZXCModule = createRequire(import.meta.url)('./zxc.js'); } catch (_) { throw new Error( 'ZXC: could not load ./zxc.js as a module factory; ' + 'pass the Emscripten factory explicitly: createZXC({}, factory)'); } } } const Module = await ZXCModule(moduleOverrides || {}); // --- Wrap C functions via cwrap ------------------------------------------ const _compress = Module.cwrap('zxc_compress', 'number', ['number', 'number', 'number', 'number', 'number']); const _decompress = Module.cwrap('zxc_decompress', 'number', ['number', 'number', 'number', 'number', 'number']); const _compress_bound = Module.cwrap('zxc_compress_bound', 'number', ['number']); const _get_decompressed_size = Module.cwrap('zxc_get_decompressed_size', 'number', ['number', 'number']); const _create_cctx = Module.cwrap('zxc_create_cctx', 'number', ['number']); const _free_cctx = Module.cwrap('zxc_free_cctx', 'void', ['number']); const _compress_cctx = Module.cwrap('zxc_compress_cctx', 'number', ['number', 'number', 'number', 'number', 'number', 'number']); const _create_dctx = Module.cwrap('zxc_create_dctx', 'number', []); const _free_dctx = Module.cwrap('zxc_free_dctx', 'void', ['number']); const _decompress_dctx = Module.cwrap('zxc_decompress_dctx', 'number', ['number', 'number', 'number', 'number', 'number', 'number']); // Push streaming API const _cstream_create = Module.cwrap('zxc_cstream_create', 'number', ['number']); const _cstream_free = Module.cwrap('zxc_cstream_free', 'void', ['number']); const _cstream_compress = Module.cwrap('zxc_cstream_compress', 'number', ['number', 'number', 'number']); const _cstream_end = Module.cwrap('zxc_cstream_end', 'number', ['number', 'number']); const _cstream_in_size = Module.cwrap('zxc_cstream_in_size', 'number', ['number']); const _cstream_out_size = Module.cwrap('zxc_cstream_out_size', 'number', ['number']); const _dstream_create = Module.cwrap('zxc_dstream_create', 'number', ['number']); const _dstream_free = Module.cwrap('zxc_dstream_free', 'void', ['number']); const _dstream_decompress = Module.cwrap('zxc_dstream_decompress', 'number', ['number', 'number', 'number']); const _dstream_finished = Module.cwrap('zxc_dstream_finished', 'number', ['number']); const _dstream_in_size = Module.cwrap('zxc_dstream_in_size', 'number', ['number']); const _dstream_out_size = Module.cwrap('zxc_dstream_out_size', 'number', ['number']); // Seekable API (random-access decompression, single-threaded) const _seekable_open = Module.cwrap('zxc_seekable_open', 'number', ['number', 'number']); const _seekable_free = Module.cwrap('zxc_seekable_free', 'void', ['number']); const _seekable_num_blocks = Module.cwrap('zxc_seekable_get_num_blocks', 'number', ['number']); const _seekable_decompressed_size = Module.cwrap('zxc_seekable_get_decompressed_size', 'number', ['number']); const _seekable_block_comp_size = Module.cwrap('zxc_seekable_get_block_comp_size', 'number', ['number', 'number']); const _seekable_block_decomp_size = Module.cwrap('zxc_seekable_get_block_decomp_size', 'number', ['number', 'number']); // Use the i32-offset shim from wasm_entry.c: cwrap cannot pass a uint64_t // argument without -sWASM_BIGINT=1, and the wasm32 heap is itself bounded // to 4 GiB, so a 32-bit offset is enough for any in-memory archive. const _seekable_decompress_range = Module.cwrap('zxcw_seekable_decompress_range', 'number', ['number', 'number', 'number', 'number', 'number']); const _seek_table_size = Module.cwrap('zxc_seek_table_size', 'number', ['number']); const _write_seek_table = Module.cwrap('zxc_write_seek_table', 'number', ['number', 'number', 'number', 'number']); const _seekable_set_dict = Module.cwrap('zxc_seekable_set_dict', 'number', ['number', 'number', 'number', 'number']); // Dictionary API. zxc_train_dict / zxc_dict_save / zxc_dict_load return // int64_t; on wasm32 cwrap('number') reads the low 32 bits, which is // sufficient since results are bounded by ZXC_DICT_SIZE_MAX (65535) and // pointers are 32-bit. const _train_dict = Module.cwrap('zxc_train_dict', 'number', ['number', 'number', 'number', 'number', 'number']); const _dict_id = Module.cwrap('zxc_dict_id', 'number', ['number', 'number', 'number']); const _get_dict_id = Module.cwrap('zxc_get_dict_id', 'number', ['number', 'number']); const _dict_get_id = Module.cwrap('zxc_dict_get_id', 'number', ['number', 'number']); const _dict_save = Module.cwrap('zxc_dict_save', 'number', ['number', 'number', 'number', 'number', 'number']); const _train_dict_huf = Module.cwrap('zxc_train_dict_huf', 'number', ['number', 'number', 'number', 'number', 'number', 'number']); const _dict_huf = Module.cwrap('zxc_dict_huf', 'number', ['number', 'number']); const _dict_save_bound = Module.cwrap('zxc_dict_save_bound', 'number', ['number']); const _dict_load = Module.cwrap('zxc_dict_load', 'number', ['number', 'number', 'number', 'number', 'number', 'number']); const _dict_train = Module.cwrap('zxc_dict_train', 'number', ['number', 'number', 'number', 'number', 'number']); const ZXC_DICT_SIZE_MAX = 65535; const ZXC_HUF_TABLE_SIZE = 128; const _version_string = Module.cwrap('zxc_version_string', 'string', []); const _error_name = Module.cwrap('zxc_error_name', 'string', ['number']); const _min_level = Module.cwrap('zxc_min_level', 'number', []); const _max_level = Module.cwrap('zxc_max_level', 'number', []); const _default_level = Module.cwrap('zxc_default_level', 'number', []); const _malloc = Module._malloc; const _free = Module._free; const _getTempRet0 = typeof Module.getTempRet0 === 'function' ? Module.getTempRet0 : null; function _u64(low) { const lo = low >>> 0; const hi = _getTempRet0 ? (_getTempRet0() >>> 0) : 0; return hi * 0x100000000 + lo; } // --- Options struct layout ----------------------------------------------- // zxc_compress_opts_t (WASM32 layout, all fields 4-byte aligned): // int n_threads (off 0) | int level (4) | size_t block_size (8) // int checksum_enabled (12) | int seekable (16) // const void* dict (20) | size_t dict_size (24) | const void* dict_huf (28) // ptr progress_cb (32) | ptr user_data (36) // Total: 40 bytes in WASM32 const COMPRESS_OPTS_SIZE = 40; // zxc_decompress_opts_t: // int n_threads (0) | int checksum_enabled (4) // const void* dict (8) | size_t dict_size (12) | const void* dict_huf (16) // ptr progress_cb (20) | ptr user_data (24) // Total: 28 bytes in WASM32 const DECOMPRESS_OPTS_SIZE = 28; // Layout guard: the offsets above are hand-mirrored from zxc_opts.h. The // library exports its compiled sizeof()s; a mismatch means the C structs // changed without this file being updated -- fail loudly at load time // instead of silently reading fields at wrong offsets. { const _copts_size = Module.cwrap('zxc_compress_opts_size', 'number', []); const _dopts_size = Module.cwrap('zxc_decompress_opts_size', 'number', []); const cSize = _copts_size(); const dSize = _dopts_size(); if (cSize !== COMPRESS_OPTS_SIZE || dSize !== DECOMPRESS_OPTS_SIZE) { throw new Error( `ZXC: options struct layout drift detected -- ` + `zxc_compress_opts_t is ${cSize} bytes (wrapper assumes ${COMPRESS_OPTS_SIZE}), ` + `zxc_decompress_opts_t is ${dSize} bytes (wrapper assumes ${DECOMPRESS_OPTS_SIZE}). ` + `Update the struct layout section of zxc_wasm.js.`); } } // zxc_inbuf_t / zxc_outbuf_t (WASM32 layout): // ptr src/dst (4) | size_t size (4) | size_t pos (4) // Total: 12 bytes in WASM32 const IO_BUF_SIZE = 12; /** * Write a zxc_compress_opts_t struct into WASM memory. * @returns {number} Pointer to the struct (caller must free). */ function _writeCompressOpts(level, checksum, seekable, dictPtr, dictSize, dictHufPtr) { const ptr = _malloc(COMPRESS_OPTS_SIZE); // Zero-fill covers n_threads (0), block_size (8, default), // progress_cb (32) and user_data (36). Module.HEAPU8.fill(0, ptr, ptr + COMPRESS_OPTS_SIZE); // level (offset 4) Module.HEAP32[(ptr >> 2) + 1] = level; // checksum_enabled (offset 12) Module.HEAP32[(ptr >> 2) + 3] = checksum ? 1 : 0; // seekable (offset 16) Module.HEAP32[(ptr >> 2) + 4] = seekable ? 1 : 0; // dict (offset 20), dict_size (offset 24), dict_huf (offset 28) Module.HEAPU32[(ptr >> 2) + 5] = dictPtr || 0; Module.HEAPU32[(ptr >> 2) + 6] = dictSize || 0; Module.HEAPU32[(ptr >> 2) + 7] = dictHufPtr || 0; return ptr; } /** * Write a zxc_decompress_opts_t struct into WASM memory. * @returns {number} Pointer to the struct (caller must free). */ function _writeDecompressOpts(checksum, dictPtr, dictSize, dictHufPtr) { const ptr = _malloc(DECOMPRESS_OPTS_SIZE); // Zero-fill covers n_threads (0), progress_cb (20) and user_data (24). Module.HEAPU8.fill(0, ptr, ptr + DECOMPRESS_OPTS_SIZE); // checksum_enabled (offset 4) Module.HEAP32[(ptr >> 2) + 1] = checksum ? 1 : 0; // dict (offset 8), dict_size (offset 12), dict_huf (offset 16) Module.HEAPU32[(ptr >> 2) + 2] = dictPtr || 0; Module.HEAPU32[(ptr >> 2) + 3] = dictSize || 0; Module.HEAPU32[(ptr >> 2) + 4] = dictHufPtr || 0; return ptr; } // --- Public API ---------------------------------------------------------- /** * Accept a {@link Dictionary} or raw `(dict, dictHuf)` pieces in options. * @returns {{dict: Uint8Array|null, dictHuf: Uint8Array|null}} */ function _splitDictOption(opts) { const d = (opts && opts.dict) || null; if (d instanceof Dictionary) { return { dict: d.content, dictHuf: d.huf }; } const dictHuf = (opts && opts.dictHuf) || null; if (dictHuf && dictHuf.length !== ZXC_HUF_TABLE_SIZE) { throw new Error('ZXC: dictHuf must be exactly 128 bytes'); } return { dict: d, dictHuf }; } /** * Compress a Uint8Array. * * @param {Uint8Array} data - Input data to compress. * @param {object} [opts] - Options. * @param {number} [opts.level=3] - Compression level (1-7). * @param {boolean} [opts.checksum=false] - Enable checksums. * @param {boolean} [opts.seekable=false] - Append seek table for random-access. * @param {Dictionary|Uint8Array} [opts.dict] - A {@link Dictionary}, or raw * pre-trained dictionary content. * @param {Uint8Array} [opts.dictHuf] - Shared literal Huffman table * (128 bytes, from {@link trainDictHuf} or {@link dictHuf}); ignored * without `opts.dict`, redundant with a {@link Dictionary}. * @returns {Uint8Array} Compressed data. * @throws {Error} On compression failure. */ function compress(data, opts) { const level = (opts && opts.level) || _default_level(); const checksum = (opts && opts.checksum) || false; const seekable = (opts && opts.seekable) || false; const { dict, dictHuf } = _splitDictOption(opts); const bound = _compress_bound(data.length); if (bound === 0) throw new Error('ZXC: compress_bound returned 0'); const srcPtr = _malloc(data.length); const dstPtr = _malloc(bound); const dictPtr = dict && dict.length > 0 ? _malloc(dict.length) : 0; if (dictPtr) Module.HEAPU8.set(dict, dictPtr); const dictHufPtr = dictHuf ? _malloc(ZXC_HUF_TABLE_SIZE) : 0; if (dictHufPtr) Module.HEAPU8.set(dictHuf, dictHufPtr); const optsPtr = _writeCompressOpts(level, checksum, seekable, dictPtr, dict ? dict.length : 0, dictHufPtr); try { Module.HEAPU8.set(data, srcPtr); const result = _compress(srcPtr, data.length, dstPtr, bound, optsPtr); if (result < 0) { throw new Error(`ZXC compress error: ${_error_name(result)} (${result})`); } return new Uint8Array(Module.HEAPU8.buffer, dstPtr, result).slice(); } finally { _free(srcPtr); _free(dstPtr); _free(optsPtr); if (dictPtr) _free(dictPtr); if (dictHufPtr) _free(dictHufPtr); } } /** * Decompress a Uint8Array. * * @param {Uint8Array} data - Compressed data. * @param {object} [opts] - Options. * @param {boolean} [opts.checksum=false] - Verify checksums. * @param {Dictionary|Uint8Array} [opts.dict] - A {@link Dictionary}, or raw * pre-trained dictionary content. * @param {Uint8Array} [opts.dictHuf] - Shared literal Huffman table * (128 bytes) when the archive was compressed with one; redundant with a * {@link Dictionary}. * @returns {Uint8Array} Decompressed data. * @throws {Error} On decompression failure. */ function decompress(data, opts) { const checksum = (opts && opts.checksum) || false; const { dict, dictHuf } = _splitDictOption(opts); // Read decompressed size from footer const srcPtr = _malloc(data.length); Module.HEAPU8.set(data, srcPtr); const origSize = _u64(_get_decompressed_size(srcPtr, data.length)); if (origSize > 0x7FFFFFFF) { // A wasm32 heap cannot address it; fail clearly instead of // aborting inside malloc. _free(srcPtr); throw new Error( `ZXC: decompressed size (${origSize} bytes) exceeds wasm32 addressable memory`); } const dstPtr = _malloc(origSize || 1); const dictPtr = dict && dict.length > 0 ? _malloc(dict.length) : 0; if (dictPtr) Module.HEAPU8.set(dict, dictPtr); const dictHufPtr = dictHuf ? _malloc(ZXC_HUF_TABLE_SIZE) : 0; if (dictHufPtr) Module.HEAPU8.set(dictHuf, dictHufPtr); const optsPtr = _writeDecompressOpts(checksum, dictPtr, dict ? dict.length : 0, dictHufPtr); try { const result = _decompress(srcPtr, data.length, dstPtr, origSize, optsPtr); if (result < 0) { throw new Error(`ZXC decompress error: ${_error_name(result)} (${result})`); } return new Uint8Array(Module.HEAPU8.buffer, dstPtr, result).slice(); } finally { _free(srcPtr); _free(dstPtr); _free(optsPtr); if (dictPtr) _free(dictPtr); if (dictHufPtr) _free(dictHufPtr); } } /** * Returns the maximum compressed output size for a given input size. * @param {number} inputSize * @returns {number} */ function compressBound(inputSize) { return _compress_bound(inputSize); } /** * Reads the decompressed size from a compressed buffer without decompressing. * @param {Uint8Array} data - Compressed data. * @returns {number} Original uncompressed size, or 0 if invalid. */ function getDecompressedSize(data) { const ptr = _malloc(data.length); try { Module.HEAPU8.set(data, ptr); return _u64(_get_decompressed_size(ptr, data.length)); } finally { _free(ptr); } } /** * Create a reusable compression context for high-frequency usage. * Call .free() when done to release WASM memory. * * @param {object} [opts] - Default options. * @param {number} [opts.level=3] - Default compression level. * @param {boolean} [opts.checksum=false] - Default checksum setting. * @returns {{ compress: Function, free: Function }} */ function createCompressContext(opts) { const level = (opts && opts.level) || _default_level(); const checksum = (opts && opts.checksum) || false; const seekable = (opts && opts.seekable) || false; const optsPtr = _writeCompressOpts(level, checksum, seekable); let cctx = _create_cctx(optsPtr); _free(optsPtr); if (cctx === 0) throw new Error('ZXC: failed to create compression context'); return { /** * Compress data using this reusable context. * @param {Uint8Array} data * @returns {Uint8Array} */ compress(data) { const bound = _compress_bound(data.length); const srcPtr = _malloc(data.length); const dstPtr = _malloc(bound); try { Module.HEAPU8.set(data, srcPtr); const result = _compress_cctx(cctx, srcPtr, data.length, dstPtr, bound, 0); if (result < 0) { throw new Error(`ZXC cctx compress error: ${_error_name(result)} (${result})`); } return new Uint8Array(Module.HEAPU8.buffer, dstPtr, result).slice(); } finally { _free(srcPtr); _free(dstPtr); } }, /** Free the context and release WASM memory. Idempotent. */ free() { if (!cctx) return; _free_cctx(cctx); cctx = 0; } }; } /** * Create a reusable decompression context for high-frequency usage. * Call .free() when done to release WASM memory. * * @returns {{ decompress: Function, free: Function }} */ function createDecompressContext() { let dctx = _create_dctx(); if (dctx === 0) throw new Error('ZXC: failed to create decompression context'); return { /** * Decompress data using this reusable context. * @param {Uint8Array} data * @returns {Uint8Array} */ decompress(data) { const srcPtr = _malloc(data.length); Module.HEAPU8.set(data, srcPtr); const origSize = _u64(_get_decompressed_size(srcPtr, data.length)); if (origSize > 0x7FFFFFFF) { _free(srcPtr); throw new Error( `ZXC: decompressed size (${origSize} bytes) exceeds wasm32 addressable memory`); } const dstPtr = _malloc(origSize || 1); try { const result = _decompress_dctx(dctx, srcPtr, data.length, dstPtr, origSize, 0); if (result < 0) { throw new Error(`ZXC dctx decompress error: ${_error_name(result)} (${result})`); } return new Uint8Array(Module.HEAPU8.buffer, dstPtr, result).slice(); } finally { _free(srcPtr); _free(dstPtr); } }, /** Free the context and release WASM memory. Idempotent. */ free() { if (!dctx) return; _free_dctx(dctx); dctx = 0; } }; } // --- Push streaming helpers ---------------------------------------------- /** Write a zxc_inbuf_t at `bufPtr` pointing to `srcPtr` (size bytes, pos=0). */ function _writeInbuf(bufPtr, srcPtr, size) { Module.HEAP32[(bufPtr >> 2) + 0] = srcPtr; Module.HEAPU32[(bufPtr >> 2) + 1] = size; Module.HEAPU32[(bufPtr >> 2) + 2] = 0; } /** Write a zxc_outbuf_t at `bufPtr` pointing to `dstPtr` (size bytes, pos=0). */ function _writeOutbuf(bufPtr, dstPtr, size) { Module.HEAP32[(bufPtr >> 2) + 0] = dstPtr; Module.HEAPU32[(bufPtr >> 2) + 1] = size; Module.HEAPU32[(bufPtr >> 2) + 2] = 0; } function _readPos(bufPtr) { return Module.HEAPU32[(bufPtr >> 2) + 2]; } /* Concatenate JS-side slices into a single Uint8Array. A single chunk is * the common case (the staging buffer holds a full block): return it * as-is and skip the concat copy (chunks are already heap-independent * .slice() copies). */ function _concatChunks(chunks, totalLen) { if (chunks.length === 1) return chunks[0]; const out = new Uint8Array(totalLen); let off = 0; for (const c of chunks) { out.set(c, off); off += c.length; } return out; } /** * Create a push-based, single-threaded compression stream. * @param {object} [opts] * @param {number} [opts.level=3] * @param {boolean} [opts.checksum=false] * @returns {{ compress(Uint8Array): Uint8Array, end(): Uint8Array, free(): void, inSize(): number, outSize(): number }} */ function createCStream(opts) { const level = (opts && opts.level) || _default_level(); const checksum = (opts && opts.checksum) || false; const optsPtr = _writeCompressOpts(level, checksum, false); let cs = _cstream_create(optsPtr); _free(optsPtr); if (cs === 0) throw new Error('ZXC: failed to create cstream'); // Reusable scratch buffers in the WASM heap. The compress/end calls // never reallocate, so these pointers stay valid for the stream's // lifetime even if the heap grows (cwrap re-reads HEAPU8 internally). const inDescPtr = _malloc(IO_BUF_SIZE); const outDescPtr = _malloc(IO_BUF_SIZE); const stageCap = Math.max(_cstream_out_size(cs), 64 * 1024); const stagePtr = _malloc(stageCap); function drainCompress(srcPtr, srcLen) { const chunks = []; let total = 0; _writeInbuf(inDescPtr, srcPtr, srcLen); for (;;) { _writeOutbuf(outDescPtr, stagePtr, stageCap); const r = _cstream_compress(cs, outDescPtr, inDescPtr); if (r < 0) { throw new Error(`ZXC cstream compress error: ${_error_name(r)} (${r})`); } const produced = _readPos(outDescPtr); if (produced > 0) { chunks.push(new Uint8Array(Module.HEAPU8.buffer, stagePtr, produced).slice()); total += produced; } if (r === 0 && _readPos(inDescPtr) === srcLen) break; } return _concatChunks(chunks, total); } function drainEnd() { const chunks = []; let total = 0; for (;;) { _writeOutbuf(outDescPtr, stagePtr, stageCap); const r = _cstream_end(cs, outDescPtr); if (r < 0) { throw new Error(`ZXC cstream end error: ${_error_name(r)} (${r})`); } const produced = _readPos(outDescPtr); if (produced > 0) { chunks.push(new Uint8Array(Module.HEAPU8.buffer, stagePtr, produced).slice()); total += produced; } if (r === 0) break; } return _concatChunks(chunks, total); } return { /** Push input and return any compressed bytes produced. */ compress(data) { if (data.length === 0) return drainCompress(stagePtr, 0); const srcPtr = _malloc(data.length); try { Module.HEAPU8.set(data, srcPtr); return drainCompress(srcPtr, data.length); } finally { _free(srcPtr); } }, /** Finalise: residual block + EOF + footer. */ end() { return drainEnd(); }, /** Free the stream and its scratch buffers. Idempotent. */ free() { if (!cs) return; _cstream_free(cs); _free(inDescPtr); _free(outDescPtr); _free(stagePtr); cs = 0; }, inSize() { return _cstream_in_size(cs); }, outSize() { return _cstream_out_size(cs); } }; } /** * Create a push-based, single-threaded decompression stream. * @param {object} [opts] * @param {boolean} [opts.checksum=false] * @returns {{ decompress(Uint8Array): Uint8Array, finished(): boolean, free(): void, inSize(): number, outSize(): number }} */ function createDStream(opts) { const checksum = (opts && opts.checksum) || false; const optsPtr = _writeDecompressOpts(checksum); let ds = _dstream_create(optsPtr); _free(optsPtr); if (ds === 0) throw new Error('ZXC: failed to create dstream'); const inDescPtr = _malloc(IO_BUF_SIZE); const outDescPtr = _malloc(IO_BUF_SIZE); const stageCap = Math.max(_dstream_out_size(ds), 4096); const stagePtr = _malloc(stageCap); return { /** Push compressed bytes; return any decompressed bytes produced. */ decompress(data) { const srcPtr = data.length > 0 ? _malloc(data.length) : 0; try { if (srcPtr) Module.HEAPU8.set(data, srcPtr); _writeInbuf(inDescPtr, srcPtr, data.length); const chunks = []; let total = 0; let exhausted = data.length === 0; for (;;) { const beforeIn = _readPos(inDescPtr); _writeOutbuf(outDescPtr, stagePtr, stageCap); const r = _dstream_decompress(ds, outDescPtr, inDescPtr); if (r < 0) { throw new Error(`ZXC dstream error: ${_error_name(r)} (${r})`); } const produced = _readPos(outDescPtr); if (produced > 0) { chunks.push(new Uint8Array(Module.HEAPU8.buffer, stagePtr, produced).slice()); total += produced; } const afterIn = _readPos(inDescPtr); // Stop only when the call made no progress at all // (no input consumed AND no output produced). if (afterIn === beforeIn && produced === 0) break; if (!exhausted && afterIn === data.length) { _writeInbuf(inDescPtr, 0, 0); exhausted = true; } } return _concatChunks(chunks, total); } finally { if (srcPtr) _free(srcPtr); } }, /** True iff the file footer has been consumed and validated. */ finished() { return _dstream_finished(ds) !== 0; }, /** Free the stream and its scratch buffers. Idempotent. */ free() { if (!ds) return; _dstream_free(ds); _free(inDescPtr); _free(outDescPtr); _free(stagePtr); ds = 0; }, inSize() { return _dstream_in_size(ds); }, outSize() { return _dstream_out_size(ds); } }; } // --- Seekable API -------------------------------------------------------- /** * Open a seekable ZXC archive for random-access decompression. * * The compressed buffer is copied into the WASM heap and held alive * for the lifetime of the handle. Call `.free()` to release both the * native handle and the WASM-side copy. * * Throws on invalid archives (bad magic, truncated seek table, ...). * * @param {Uint8Array} data - Compressed buffer, ideally produced with * `{ seekable: true }` so it carries an embedded seek table. * @returns {{ * numBlocks(): number, * decompressedSize(): number, * blockCompressedSize(idx: number): (number | null), * blockDecompressedSize(idx: number): (number | null), * decompressRange(offset: number, length: number): Uint8Array, * free(): void * }} */ function createSeekable(data) { if (!data || data.length === 0) { throw new Error('ZXC: createSeekable requires a non-empty buffer'); } const srcLen = data.length; const srcPtr = _malloc(srcLen); if (!srcPtr) throw new Error('ZXC: malloc failed for seekable buffer'); Module.HEAPU8.set(data, srcPtr); let handle = _seekable_open(srcPtr, srcLen); if (handle === 0) { _free(srcPtr); throw new Error('ZXC: invalid seekable archive'); } return { numBlocks() { return _seekable_num_blocks(handle); }, decompressedSize() { return _u64(_seekable_decompressed_size(handle)); }, blockCompressedSize(idx) { if (idx < 0 || idx >= _seekable_num_blocks(handle)) return null; return _seekable_block_comp_size(handle, idx); }, blockDecompressedSize(idx) { if (idx < 0 || idx >= _seekable_num_blocks(handle)) return null; return _seekable_block_decomp_size(handle, idx); }, /** * Attach a pre-trained dictionary to this seekable handle. * Must be called before any decompressRange() call. * * When the archive was compressed with a shared literal Huffman * table (or with a {@link Dictionary}), the same table must be * supplied: the archive's dict_id binds the (content, table) * pair, so content alone would be rejected with DICT_MISMATCH. * * @param {Dictionary|Uint8Array} dict - A {@link Dictionary}, or * raw dictionary content. * @param {Uint8Array} [dictHuf] - Shared literal Huffman table * (128 bytes); redundant when `dict` is a {@link Dictionary}. */ setDict(dict, dictHuf) { if (dict instanceof Dictionary) { dictHuf = dict.huf; dict = dict.content; } if (!dict || dict.length === 0) { throw new Error('ZXC: setDict requires a non-empty dictionary'); } if (dictHuf && dictHuf.length !== ZXC_HUF_TABLE_SIZE) { throw new Error('ZXC: dictHuf must be exactly 128 bytes'); } const dictPtr = _malloc(dict.length); if (!dictPtr) throw new Error('ZXC: malloc failed for dictionary'); const hufPtr = dictHuf ? _malloc(ZXC_HUF_TABLE_SIZE) : 0; try { Module.HEAPU8.set(dict, dictPtr); if (hufPtr) Module.HEAPU8.set(dictHuf, hufPtr); const r = _seekable_set_dict(handle, dictPtr, dict.length, hufPtr); if (r < 0) { throw new Error(`ZXC seekable set_dict error: ${_error_name(r)} (${r})`); } } finally { _free(dictPtr); if (hufPtr) _free(hufPtr); } }, decompressRange(offset, length) { if (length < 0) throw new Error('ZXC: length must be non-negative'); // The wasm shim carries the offset as a uint32 (no BigInt); // values outside [0, 2^32-1] would silently wrap and return // data from the wrong position. if (!Number.isInteger(offset) || offset < 0 || offset > 0xFFFFFFFF) { throw new Error('ZXC: offset must be an integer in [0, 2^32-1]'); } if (length === 0) return new Uint8Array(0); const dstPtr = _malloc(length); if (!dstPtr) throw new Error('ZXC: malloc failed for output buffer'); try { const r = _seekable_decompress_range(handle, dstPtr, length, offset, length); if (r < 0) { throw new Error(`ZXC seekable decompress error: ${_error_name(r)} (${r})`); } return new Uint8Array(Module.HEAPU8.buffer, dstPtr, r).slice(); } finally { _free(dstPtr); } }, /** Release the native handle and the WASM-side copy. Idempotent. */ free() { if (!handle) return; _seekable_free(handle); _free(srcPtr); handle = 0; }, }; } /** * Encoded byte size of a seek table covering `numBlocks` data blocks. * Use this to size a destination buffer for [writeSeekTable]. * @param {number} numBlocks * @returns {number} */ function seekTableSize(numBlocks) { return _seek_table_size(numBlocks >>> 0); } /** * Low-level: write a seek table (header + entries) for the given * per-block on-disk compressed sizes. * * Most callers do not need this directly; the buffer and streaming * APIs emit a seek table when `seekable: true` is set. * * @param {ArrayLike} compSizes - Per-block compressed sizes, * in order. Each entry is treated as an unsigned 32-bit value. * @returns {Uint8Array} The encoded seek table. */ function writeSeekTable(compSizes) { if (!compSizes || compSizes.length === 0) { throw new Error('ZXC: compSizes must be non-empty'); } const numBlocks = compSizes.length; const sz = _seek_table_size(numBlocks); const dstPtr = _malloc(sz); if (!dstPtr) throw new Error('ZXC: malloc failed for seek table'); const csPtr = _malloc(numBlocks * 4); if (!csPtr) { _free(dstPtr); throw new Error('ZXC: malloc failed for compSizes scratch'); } try { for (let i = 0; i < numBlocks; i++) { Module.HEAPU32[(csPtr >> 2) + i] = compSizes[i] >>> 0; } const r = _write_seek_table(dstPtr, sz, csPtr, numBlocks); if (r < 0) { throw new Error(`ZXC write_seek_table error: ${_error_name(r)} (${r})`); } return new Uint8Array(Module.HEAPU8.buffer, dstPtr, r).slice(); } finally { _free(dstPtr); _free(csPtr); } } // --- Dictionary API ------------------------------------------------------ /** * Train a dictionary from a corpus of samples. * * @param {Uint8Array[]} samples - Array of sample buffers (similar payloads). * @param {number} [maxSize=65535] - Maximum dictionary size in bytes * (capped at ZXC_DICT_SIZE_MAX). * @returns {Uint8Array} Raw trained dictionary content. * @throws {Error} On training failure. */ function trainDict(samples, maxSize = ZXC_DICT_SIZE_MAX) { if (!samples || samples.length === 0) { throw new Error('ZXC: trainDict requires at least one sample'); } const cap = Math.min(maxSize >>> 0, ZXC_DICT_SIZE_MAX); const n = samples.length; // Heap arrays: pointers (4 bytes each) + sizes (size_t = 4 bytes on wasm32). const ptrsPtr = _malloc(n * 4); const sizesPtr = _malloc(n * 4); const samplePtrs = []; const dictPtr = _malloc(cap); try { for (let i = 0; i < n; i++) { const s = samples[i]; const sp = s.length > 0 ? _malloc(s.length) : _malloc(1); if (s.length > 0) Module.HEAPU8.set(s, sp); samplePtrs.push(sp); Module.HEAPU32[(ptrsPtr >> 2) + i] = sp; Module.HEAPU32[(sizesPtr >> 2) + i] = s.length; } const r = _train_dict(ptrsPtr, sizesPtr, n, dictPtr, cap); if (r < 0) { throw new Error(`ZXC train_dict error: ${_error_name(r)} (${r})`); } return new Uint8Array(Module.HEAPU8.buffer, dictPtr, r).slice(); } finally { for (const sp of samplePtrs) _free(sp); _free(ptrsPtr); _free(sizesPtr); _free(dictPtr); } } /* Shared helper: copy a Uint8Array into the heap and call an id getter. */ function _callIdOnBuffer(fn, data) { if (!data || data.length === 0) return 0; const ptr = _malloc(data.length); try { Module.HEAPU8.set(data, ptr); return fn(ptr, data.length) >>> 0; } finally { _free(ptr); } } /** * Compute the 32-bit dictionary ID for raw dictionary content. * @param {Uint8Array} content * @returns {number} Unsigned 32-bit ID (0 if empty). */ function dictId(content) { // Third arg: NULL huf table -> content-only id. return _callIdOnBuffer((ptr, len) => _dict_id(ptr, len, 0), content); } /** * Read the dictionary ID referenced by a compressed `.zxc` archive. * @param {Uint8Array} archive * @returns {number} Unsigned 32-bit ID (0 if no dictionary required). */ function getDictId(archive) { return _callIdOnBuffer(_get_dict_id, archive); } /** * Read the dictionary ID stored in a serialized `.zxd` file buffer. * @param {Uint8Array} zxd * @returns {number} Unsigned 32-bit ID (0 if not a valid .zxd). */ function dictGetId(zxd) { return _callIdOnBuffer(_dict_get_id, zxd); } /** * Serialize dictionary content and its shared literal Huffman table * (128 bytes, from {@link trainDictHuf}) to the `.zxd` file format. * The stored dictionary ID covers both content and table. * @param {Uint8Array} content - Raw dictionary content. * @param {Uint8Array} hufLengths - 128-byte packed code-lengths table. * @returns {Uint8Array} The encoded `.zxd` file. * @throws {Error} On failure. */ function dictSave(content, hufLengths) { if (!content || content.length === 0) { throw new Error('ZXC: dictSave requires non-empty content'); } if (!hufLengths || hufLengths.length !== ZXC_HUF_TABLE_SIZE) { throw new Error('ZXC: dictSave requires a 128-byte hufLengths table'); } const cap = _dict_save_bound(content.length); const contentPtr = _malloc(content.length); const hufPtr = _malloc(ZXC_HUF_TABLE_SIZE); const bufPtr = _malloc(cap); try { Module.HEAPU8.set(content, contentPtr); Module.HEAPU8.set(hufLengths, hufPtr); const r = _dict_save(contentPtr, content.length, hufPtr, bufPtr, cap); if (r < 0) { throw new Error(`ZXC dict_save error: ${_error_name(r)} (${r})`); } return new Uint8Array(Module.HEAPU8.buffer, bufPtr, r).slice(); } finally { _free(contentPtr); _free(hufPtr); _free(bufPtr); } } /** * Train the shared literal Huffman table for an already-trained * dictionary (see {@link trainDict}). * @param {Uint8Array[]} samples - Training corpus. * @param {Uint8Array} dict - Trained dictionary content. * @returns {Uint8Array} 128-byte packed table for {@link dictSave} / * `opts.dictHuf`. * @throws {Error} On failure. */ function trainDictHuf(samples, dict) { if (!samples || samples.length === 0) { throw new Error('ZXC: trainDictHuf requires at least one sample'); } if (!dict || dict.length === 0) { throw new Error('ZXC: trainDictHuf requires a non-empty dictionary'); } const n = samples.length; const ptrsPtr = _malloc(n * 4); const sizesPtr = _malloc(n * 4); const samplePtrs = []; const dictPtr = _malloc(dict.length); const hufPtr = _malloc(ZXC_HUF_TABLE_SIZE); try { for (let i = 0; i < n; i++) { const sp = _malloc(samples[i].length || 1); samplePtrs.push(sp); if (samples[i].length > 0) Module.HEAPU8.set(samples[i], sp); Module.HEAPU32[(ptrsPtr >> 2) + i] = sp; Module.HEAPU32[(sizesPtr >> 2) + i] = samples[i].length; } Module.HEAPU8.set(dict, dictPtr); const r = _train_dict_huf(ptrsPtr, sizesPtr, n, dictPtr, dict.length, hufPtr); if (r < 0) { throw new Error(`ZXC train_dict_huf error: ${_error_name(r)} (${r})`); } return new Uint8Array(Module.HEAPU8.buffer, hufPtr, ZXC_HUF_TABLE_SIZE).slice(); } finally { for (const sp of samplePtrs) _free(sp); _free(ptrsPtr); _free(sizesPtr); _free(dictPtr); _free(hufPtr); } } /** * Return the 128-byte shared Huffman table stored in a `.zxd` file, or * `null` if the buffer is not a valid `.zxd` file. * @param {Uint8Array} zxd - The `.zxd` file bytes. * @returns {Uint8Array | null} */ function dictHuf(zxd) { if (!zxd || zxd.length === 0) return null; const bufPtr = _malloc(zxd.length); try { Module.HEAPU8.set(zxd, bufPtr); const p = _dict_huf(bufPtr, zxd.length); if (!p) return null; return new Uint8Array(Module.HEAPU8.buffer, p, ZXC_HUF_TABLE_SIZE).slice(); } finally { _free(bufPtr); } } /** * Load and validate a `.zxd` file buffer. * @param {Uint8Array} zxd - The `.zxd` file bytes. * @returns {{ content: Uint8Array, huf: Uint8Array, id: number }} * @throws {Error} On invalid input. */ function dictLoad(zxd) { if (!zxd || zxd.length === 0) { throw new Error('ZXC: dictLoad requires a non-empty buffer'); } const bufPtr = _malloc(zxd.length); // out params: content ptr (4), content size (4), huf ptr (4), dict id (4) const contentOutPtr = _malloc(4); const sizeOutPtr = _malloc(4); const hufOutPtr = _malloc(4); const idOutPtr = _malloc(4); try { Module.HEAPU8.set(zxd, bufPtr); const r = _dict_load(bufPtr, zxd.length, contentOutPtr, sizeOutPtr, hufOutPtr, idOutPtr); if (r < 0) { throw new Error(`ZXC dict_load error: ${_error_name(r)} (${r})`); } const contentPtr = Module.HEAPU32[contentOutPtr >> 2]; const contentSize = Module.HEAPU32[sizeOutPtr >> 2]; const hufPtr = Module.HEAPU32[hufOutPtr >> 2]; const id = Module.HEAPU32[idOutPtr >> 2] >>> 0; // content/huf point into bufPtr (zero-copy); copy out before freeing. const content = new Uint8Array(Module.HEAPU8.buffer, contentPtr, contentSize).slice(); const huf = new Uint8Array(Module.HEAPU8.buffer, hufPtr, ZXC_HUF_TABLE_SIZE).slice(); return { content, huf, id }; } finally { _free(bufPtr); _free(contentOutPtr); _free(sizeOutPtr); _free(hufOutPtr); _free(idOutPtr); } } /** * Train a complete dictionary (content + shared Huffman table) and * serialize it to `.zxd` bytes in one call. One-call equivalent of * {@link trainDict} + {@link trainDictHuf} + {@link dictSave}. * @param {Uint8Array[]} samples - Training corpus. * @returns {Uint8Array} The encoded `.zxd` file. * @throws {Error} On failure. */ function dictTrain(samples) { if (!samples || samples.length === 0) { throw new Error('ZXC: dictTrain requires at least one sample'); } const n = samples.length; const ptrsPtr = _malloc(n * 4); const sizesPtr = _malloc(n * 4); const samplePtrs = []; const cap = _dict_save_bound(ZXC_DICT_SIZE_MAX); const zxdPtr = _malloc(cap); try { for (let i = 0; i < n; i++) { const s = samples[i]; const sp = _malloc(s.length || 1); if (s.length > 0) Module.HEAPU8.set(s, sp); samplePtrs.push(sp); Module.HEAPU32[(ptrsPtr >> 2) + i] = sp; Module.HEAPU32[(sizesPtr >> 2) + i] = s.length; } const r = _dict_train(ptrsPtr, sizesPtr, n, zxdPtr, cap); if (r < 0) { throw new Error(`ZXC dict_train error: ${_error_name(r)} (${r})`); } return new Uint8Array(Module.HEAPU8.buffer, zxdPtr, r).slice(); } finally { for (const sp of samplePtrs) _free(sp); _free(ptrsPtr); _free(sizesPtr); _free(zxdPtr); } } /** * A trained dictionary: content bytes, its shared literal Huffman table * and the ID binding the pair. Pass an instance as `opts.dict` to * `compress` / `decompress` instead of juggling `dict` + `dictHuf`. */ class Dictionary { /** * @param {Uint8Array} content - Raw LZ-window content bytes. * @param {Uint8Array} huf - 128-byte shared literal Huffman table. * @param {number} id - Dictionary ID binding the (content, table) pair. */ constructor(content, huf, id) { if (!content || !huf || huf.length !== ZXC_HUF_TABLE_SIZE) { throw new Error( 'ZXC: Dictionary requires (content, 128-byte huf table)'); } this.content = content; this.huf = huf; this.id = id >>> 0; } /** * Train a complete dictionary (content + shared table) from samples. * @param {Uint8Array[]} samples * @returns {Dictionary} */ static train(samples) { return Dictionary.load(dictTrain(samples)); } /** * Parse `.zxd` bytes into a Dictionary (owned copies). * @param {Uint8Array} zxd * @returns {Dictionary} */ static load(zxd) { const { content, huf, id } = dictLoad(zxd); return new Dictionary(content, huf, id); } /** * Serialize back to `.zxd` file bytes. * @returns {Uint8Array} */ save() { return dictSave(this.content, this.huf); } } // --- WHATWG TransformStream adapters ------------------------------------- // // Mirror of the wrappers shipped by the Go, Rust, Python and Node bindings: // turn a CStream / DStream pair into a `TransformStream` so ZXC can be // wired into fetch pipelines, Service Workers, Deno, Bun, Node ≥18 — any // host that exposes the Streams API. Useful for OCI use cases such as // streaming a compressed layer through `fetch().body.pipeThrough(...)`. /** * Returns a WHATWG TransformStream that compresses bytes through a ZXC * frame. Pipe a ReadableStream through it to produce a * compressed ReadableStream. * * @param {object} [opts] * @param {number} [opts.level] * @param {boolean} [opts.checksum] * @returns {TransformStream} */ function createCompressTransformStream(opts) { let cs; return new TransformStream({ start() { cs = createCStream(opts); }, transform(chunk, controller) { try { const out = cs.compress(chunk); if (out.length > 0) controller.enqueue(out); } catch (e) { controller.error(e); try { cs.free(); } catch (_) { /* idempotent */ } } }, flush(controller) { try { const tail = cs.end(); if (tail.length > 0) controller.enqueue(tail); } catch (e) { controller.error(e); } finally { try { cs.free(); } catch (_) { /* idempotent */ } } }, // Reader cancelled / writable aborted: flush() never runs, so the // wasm stream context and staging buffers must be freed here. cancel() { try { cs.free(); } catch (_) { /* idempotent */ } }, }); } /** * Returns a WHATWG TransformStream that decompresses a ZXC frame. * * Errors the stream with a `'ZXC_TRUNCATED'`-tagged Error if the input * ends before the footer is reached. * * @param {object} [opts] * @param {boolean} [opts.checksum] * @returns {TransformStream} */ function createDecompressTransformStream(opts) { let ds; return new TransformStream({ start() { ds = createDStream(opts); }, transform(chunk, controller) { try { const out = ds.decompress(chunk); if (out.length > 0) controller.enqueue(out); } catch (e) { controller.error(e); try { ds.free(); } catch (_) { /* idempotent */ } } }, flush(controller) { try { if (!ds.finished()) { const err = new Error( 'ZXC: input drained before footer (truncated frame)'); err.code = 'ZXC_TRUNCATED'; controller.error(err); return; } } catch (e) { controller.error(e); } finally { try { ds.free(); } catch (_) { /* idempotent */ } } }, // Reader cancelled / writable aborted: flush() never runs, so the // wasm stream context and staging buffers must be freed here. cancel() { try { ds.free(); } catch (_) { /* idempotent */ } }, }); } // --- Exposed API object -------------------------------------------------- return Object.freeze({ compress, decompress, compressBound, getDecompressedSize, createCompressContext, createDecompressContext, createCStream, createDStream, createCompressTransformStream, createDecompressTransformStream, createSeekable, seekTableSize, writeSeekTable, detectZxc, // Dictionary API Dictionary, dictTrain, trainDict, trainDictHuf, dictId, getDictId, dictGetId, dictSave, dictLoad, dictHuf, /** Library version string (e.g. "0.13.0"). */ version: _version_string(), /** Minimum compression level. */ minLevel: _min_level(), /** Maximum compression level. */ maxLevel: _max_level(), /** Default compression level. */ defaultLevel: _default_level(), /** Raw Emscripten Module (for advanced use). */ _module: Module, }); } zxc-0.13.0/zxcConfig.cmake.in000066400000000000000000000002431522536370400157460ustar00rootroot00000000000000@PACKAGE_INIT@ include(CMakeFindDependencyMacro) find_dependency(Threads) include("${CMAKE_CURRENT_LIST_DIR}/zxc-targets.cmake") check_required_components(zxc)