diff --git a/.circleci/images/primary/Dockerfile b/.circleci/images/primary/Dockerfile index bfcb6af69..4b77032b6 100644 --- a/.circleci/images/primary/Dockerfile +++ b/.circleci/images/primary/Dockerfile @@ -1,4 +1,4 @@ -FROM circleci/buildpack-deps:focal +FROM circleci/buildpack-deps@sha256:f6f10c11b7b8ccfd4f4a5b830c3256803604ce61292b60cb22e26b12f62b0e8c RUN sudo dpkg --add-architecture i386 RUN sudo apt-get -y -qq update diff --git a/.github/workflows/dev-long-tests.yml b/.github/workflows/dev-long-tests.yml index 1c8c9ec55..16202260d 100644 --- a/.github/workflows/dev-long-tests.yml +++ b/.github/workflows/dev-long-tests.yml @@ -9,11 +9,13 @@ on: pull_request: branches: [ dev, release, actionsTest ] +permissions: read-all + jobs: make-all: runs-on: ubuntu-latest steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3 + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 - name: make all run: make all @@ -24,7 +26,7 @@ jobs: DEVNULLRIGHTS: 1 READFROMBLOCKDEVICE: 1 steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3 + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 - name: make test run: make test @@ -32,28 +34,42 @@ jobs: make-test-osx: runs-on: macos-latest steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3 + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 - name: OS-X test run: make test # make -c lib all doesn't work because of the fact that it's not a tty + # lasts ~24mn + make-test-32bit: + runs-on: ubuntu-latest + env: + DEVNULLRIGHTS: 1 + READFROMBLOCKDEVICE: 1 + steps: + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 + - name: make test + run: | + sudo apt-get -qqq update + make libc6install + CFLAGS="-m32" make test + no-intrinsics-fuzztest: runs-on: ubuntu-latest steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3 + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 - name: no intrinsics fuzztest run: MOREFLAGS="-DZSTD_NO_INTRINSICS" make -C tests fuzztest tsan-zstreamtest: runs-on: ubuntu-latest steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3 + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 - name: thread sanitizer zstreamtest run: CC=clang ZSTREAM_TESTTIME=-T3mn make tsan-test-zstream ubsan-zstreamtest: runs-on: ubuntu-latest steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3 + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 - name: undefined behavior sanitizer zstreamtest run: CC=clang make uasan-test-zstream @@ -61,7 +77,7 @@ jobs: tsan-fuzztest: runs-on: ubuntu-latest steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3 + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 - name: thread sanitizer fuzztest run: CC=clang make tsan-fuzztest @@ -69,7 +85,7 @@ jobs: big-tests-zstreamtest32: runs-on: ubuntu-latest steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3 + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 - name: zstream tests in 32bit mode, with big tests run: | sudo apt-get -qqq update @@ -80,7 +96,7 @@ jobs: gcc-8-asan-ubsan-testzstd: runs-on: ubuntu-latest steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3 + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 - name: gcc-8 + ASan + UBSan + Test Zstd # See https://askubuntu.com/a/1428822 run: | @@ -92,14 +108,14 @@ jobs: clang-asan-ubsan-testzstd: runs-on: ubuntu-latest steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3 + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 - name: clang + ASan + UBSan + Test Zstd run: CC=clang make -j uasan-test-zstd msbuild "build\VS2010\zstd.sln" /m /verbosity:minimal /property:PlatformToolset=${{matrix.toolset}} - /t:Clean,Build /p:Platform=${{matrix.platform}} /p:Configuration=${{matrix.configuration}} + /t:Clean,Build /p:Platform=${{matrix.platform}} /p:Configuration=${{matrix.configuration}} /warnaserror + - name: Build ${{matrix.name}} + working-directory: ${{env.GITHUB_WORKSPACE}} + # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference + if: ${{ matrix.arch != '' }} + run: > + msbuild "build\VS2010\zstd.sln" /m /verbosity:minimal /property:PlatformToolset=${{matrix.toolset}} + /t:Clean,Build /p:Platform=${{matrix.platform}} /p:Configuration=${{matrix.configuration}} /warnaserror + /p:InstructionSet=${{matrix.arch}} - # This tests that we don't accidently grow the size too much. + # This tests that we don't accidentally grow the size too much. # If the size grows intentionally, you can raise these numbers. # But we do need to think about binary size, since it is a concern. libzstd-size: runs-on: ubuntu-latest steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3 + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 - name: libzstd size test run: | make clean && make -j -C lib libzstd && ./tests/check_size.py lib/libzstd.so 1100000 @@ -295,7 +337,7 @@ jobs: minimal-decompressor-macros: runs-on: ubuntu-latest steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3 + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 - name: minimal decompressor macros run: | make clean && make -j all ZSTD_LIB_MINIFY=1 MOREFLAGS="-Werror" @@ -310,7 +352,7 @@ jobs: dynamic-bmi2: runs-on: ubuntu-latest steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3 + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 - name: dynamic bmi2 tests run: | make clean && make -j check MOREFLAGS="-O0 -Werror -mbmi2" @@ -322,13 +364,12 @@ jobs: test-variants: runs-on: ubuntu-latest steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3 + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 - name: make all variants & validate run: | make -j -C programs allVariants MOREFLAGS=-O0 ./tests/test-variants.sh - qemu-consistency: name: QEMU ${{ matrix.name }} runs-on: ubuntu-20.04 @@ -348,7 +389,7 @@ jobs: XCC: ${{ matrix.xcc }} XEMU: ${{ matrix.xemu }} steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3 + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 - name: apt update & install run: | sudo apt-get update @@ -404,8 +445,8 @@ jobs: run: shell: msys2 {0} steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3 - - uses: msys2/setup-msys2@v2 + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 + - uses: msys2/setup-msys2@5beef6d11f48bba68b9eb503e3adc60b23c0cc36 # tag=v2 with: msystem: ${{ matrix.msystem }} install: make diffutils @@ -440,9 +481,9 @@ jobs: platform: [x64, Win32] configuration: [Release] steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3 + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 - name: Add MSBuild to PATH - uses: microsoft/setup-msbuild@v1.3 + uses: microsoft/setup-msbuild@1ff57057b5cfdc39105cd07a01d78e9b0ea0c14c # tag=v1.3 - name: Build and run tests working-directory: ${{env.GITHUB_WORKSPACE}} env: @@ -461,7 +502,7 @@ jobs: runs-on: windows-latest steps: - run: git config --global core.autocrlf input - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3 + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 - uses: cygwin/cygwin-install-action@f5e0f048310c425e84bc789f493a828c6dc80a25 # tag=master with: platform: x86_64 @@ -486,7 +527,7 @@ jobs: intel-cet-compatibility: runs-on: ubuntu-latest steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3 + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 - name: Build Zstd run: | make -j zstd V=1 @@ -507,7 +548,7 @@ jobs: container: image: debian:testing steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3 + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 - name: Install dependencies run: | apt -y update @@ -522,7 +563,7 @@ jobs: versions-compatibility: runs-on: ubuntu-latest steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3 + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 - name: Versions Compatibility Test run: | make -C tests versionsTest @@ -530,7 +571,7 @@ jobs: clangbuild: runs-on: ubuntu-latest steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3 + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 - name: make clangbuild run: | make clangbuild @@ -538,7 +579,7 @@ jobs: clang-pgo: runs-on: ubuntu-latest steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3 + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 - name: Build PGO Zstd with Clang env: CC: clang-14 @@ -550,7 +591,7 @@ jobs: gcc-pgo: runs-on: ubuntu-latest steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3 + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 - name: Build PGO Zstd with GCC env: CC: gcc @@ -577,7 +618,7 @@ jobs: # sudo add-apt-repository "deb https://apt.repos.intel.com/oneapi all main" # sudo apt-get update # sudo apt-get install -y intel-basekit intel-hpckit -# - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3 +# - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 # - name: make check # run: | # make CC=/opt/intel/oneapi/compiler/latest/linux/bin/intel64/icc check diff --git a/.github/workflows/publish-release-artifacts.yml b/.github/workflows/publish-release-artifacts.yml index 2c89a91a5..3c9423948 100644 --- a/.github/workflows/publish-release-artifacts.yml +++ b/.github/workflows/publish-release-artifacts.yml @@ -5,21 +5,19 @@ on: types: - published -permissions: - contents: read +permissions: read-all jobs: publish-release-artifacts: permissions: - contents: read # to fetch code (actions/checkout) - actions: write # to attach binaries to release artifacts (skx/github-action-publish-binaries) + contents: write # to fetch code and upload artifacts runs-on: ubuntu-latest if: startsWith(github.ref, 'refs/tags/') steps: - name: Checkout - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3 + uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 - name: Archive env: @@ -68,7 +66,7 @@ jobs: fi - name: Publish - uses: skx/github-action-publish-binaries@release-2.0 + uses: skx/github-action-publish-binaries@b9ca5643b2f1d7371a6cba7f35333f1461bbc703 # tag=release-2.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 6a9e5217e..8a693fa20 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -27,7 +27,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # tag=v3 + uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 with: persist-credentials: false @@ -59,6 +59,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@3ebbd71c74ef574dbc558c82f70e52732c8b44fe # tag=v2.2.1 + uses: github/codeql-action/upload-sarif@67a35a08586135a9573f4327e904ecbf517a882d # tag=v2.2.8 with: sarif_file: results.sarif diff --git a/.github/workflows/windows-artifacts.yml b/.github/workflows/windows-artifacts.yml new file mode 100644 index 000000000..ddae817be --- /dev/null +++ b/.github/workflows/windows-artifacts.yml @@ -0,0 +1,48 @@ +name: windows-artifacts + +on: + push: + branches: [ release, test_artifacts, win_artifacts ] + +permissions: read-all + +jobs: + windows-64-artifacts: + # see https://ariya.io/2020/07/on-github-actions-with-msys2 + runs-on: windows-latest + defaults: + run: + shell: msys2 {0} + steps: + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # tag=v3 + - uses: msys2/setup-msys2@5beef6d11f48bba68b9eb503e3adc60b23c0cc36 # tag=v2 + with: + msystem: MINGW64 + install: make zlib git p7zip mingw-w64-x86_64-gcc + update: true + - name: display versions + run: | + make -v + cc -v + + - name: Building zlib to static link + run: | + git clone --depth 1 --branch v1.2.11 https://github.com/madler/zlib + make -C zlib -f win32/Makefile.gcc libz.a + + - name: Building zstd programs in 64-bit mode + run: | + CPPFLAGS=-I../zlib LDFLAGS=../zlib/libz.a make -j allzstd MOREFLAGS=-static V=1 + + - name: Create artifacts + run: | + ./lib/dll/example/build_package.bat + cd bin/ + 7z a -tzip -mx9 zstd-win-release-win64.zip * + cd .. + + - name: Publish zstd-win-release-win64.zip + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # tag=v3 + with: + path: ${{ github.workspace }}/bin/zstd-win-release-win64.zip + name: zstd-win-release-win64.zip diff --git a/CHANGELOG b/CHANGELOG index 4010c1ff5..935645786 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,22 @@ +v1.5.5 (Apr 2023) +fix: fix rare corruption bug affecting the high compression mode, reported by @danlark1 (#3517, @terrelln) +perf: improve mid-level compression speed (#3529, #3533, #3543, @yoniko and #3552, @terrelln) +lib: deprecated bufferless block-level API (#3534) by @terrelln +cli: mmap large dictionaries to save memory, by @daniellerozenblit +cli: improve speed of --patch-from mode (~+50%) (#3545) by @daniellerozenblit +cli: improve i/o speed (~+10%) when processing lots of small files (#3479) by @felixhandte +cli: zstd no longer crashes when requested to write into write-protected directory (#3541) by @felixhandte +cli: fix decompression into block device using -o (#3584, @Cyan4973) reported by @georgmu +build: fix zstd CLI compiled with lzma support but not zlib support (#3494) by @Hello71 +build: fix cmake does no longer require 3.18 as minimum version (#3510) by @kou +build: fix MSVC+ClangCL linking issue (#3569) by @tru +build: fix zstd-dll, version of zstd CLI that links to the dynamic library (#3496) by @yoniko +build: fix MSVC warnings (#3495) by @embg +doc: updated zstd specification to clarify corner cases, by @Cyan4973 +doc: document how to create fat binaries for macos (#3568) by @rickmark +misc: improve seekable format ingestion speed (~+100%) for very small chunk sizes (#3544) by @Cyan4973 +misc: tests/fullbench can benchmark multiple files (#3516) by @dloidolt + v1.5.4 (Feb 2023) perf: +20% faster huffman decompression for targets that can't compile x64 assembly (#3449, @terrelln) perf: up to +10% faster streaming compression at levels 1-2 (#3114, @embg) diff --git a/Makefile b/Makefile index a7890a5b1..3b2e3999f 100644 --- a/Makefile +++ b/Makefile @@ -200,15 +200,15 @@ travis-install: .PHONY: gcc5build gcc6build gcc7build clangbuild m32build armbuild aarch64build ppcbuild ppc64build gcc5build: clean gcc-5 -v - CC=gcc-5 $(MAKE) all MOREFLAGS="-Werror" + CC=gcc-5 $(MAKE) all MOREFLAGS="-Werror $(MOREFLAGS)" gcc6build: clean gcc-6 -v - CC=gcc-6 $(MAKE) all MOREFLAGS="-Werror" + CC=gcc-6 $(MAKE) all MOREFLAGS="-Werror $(MOREFLAGS)" gcc7build: clean gcc-7 -v - CC=gcc-7 $(MAKE) all MOREFLAGS="-Werror" + CC=gcc-7 $(MAKE) all MOREFLAGS="-Werror $(MOREFLAGS)" clangbuild: clean clang -v @@ -232,17 +232,17 @@ ppc64build: clean .PHONY: armfuzz aarch64fuzz ppcfuzz ppc64fuzz armfuzz: clean - CC=arm-linux-gnueabi-gcc QEMU_SYS=qemu-arm-static MOREFLAGS="-static" FUZZER_FLAGS=--no-big-tests $(MAKE) -C $(TESTDIR) fuzztest + CC=arm-linux-gnueabi-gcc QEMU_SYS=qemu-arm-static MOREFLAGS="-static $(MOREFLAGS)" FUZZER_FLAGS="--no-big-tests $(FUZZER_FLAGS)" $(MAKE) -C $(TESTDIR) fuzztest aarch64fuzz: clean ld -v - CC=aarch64-linux-gnu-gcc QEMU_SYS=qemu-aarch64-static MOREFLAGS="-static" FUZZER_FLAGS=--no-big-tests $(MAKE) -C $(TESTDIR) fuzztest + CC=aarch64-linux-gnu-gcc QEMU_SYS=qemu-aarch64-static MOREFLAGS="-static $(MOREFLAGS)" FUZZER_FLAGS="--no-big-tests $(FUZZER_FLAGS)" $(MAKE) -C $(TESTDIR) fuzztest ppcfuzz: clean - CC=powerpc-linux-gnu-gcc QEMU_SYS=qemu-ppc-static MOREFLAGS="-static" FUZZER_FLAGS=--no-big-tests $(MAKE) -C $(TESTDIR) fuzztest + CC=powerpc-linux-gnu-gcc QEMU_SYS=qemu-ppc-static MOREFLAGS="-static $(MOREFLAGS)" FUZZER_FLAGS="--no-big-tests $(FUZZER_FLAGS)" $(MAKE) -C $(TESTDIR) fuzztest ppc64fuzz: clean - CC=powerpc-linux-gnu-gcc QEMU_SYS=qemu-ppc64-static MOREFLAGS="-m64 -static" FUZZER_FLAGS=--no-big-tests $(MAKE) -C $(TESTDIR) fuzztest + CC=powerpc-linux-gnu-gcc QEMU_SYS=qemu-ppc64-static MOREFLAGS="-m64 -static $(MOREFLAGS)" FUZZER_FLAGS="--no-big-tests $(FUZZER_FLAGS)" $(MAKE) -C $(TESTDIR) fuzztest .PHONY: cxxtest gcc5test gcc6test armtest aarch64test ppctest ppc64test cxxtest: CXXFLAGS += -Wall -Wextra -Wundef -Wshadow -Wcast-align -Werror @@ -251,34 +251,34 @@ cxxtest: clean gcc5test: clean gcc-5 -v - $(MAKE) all CC=gcc-5 MOREFLAGS="-Werror" + $(MAKE) all CC=gcc-5 MOREFLAGS="-Werror $(MOREFLAGS)" gcc6test: clean gcc-6 -v - $(MAKE) all CC=gcc-6 MOREFLAGS="-Werror" + $(MAKE) all CC=gcc-6 MOREFLAGS="-Werror $(MOREFLAGS)" armtest: clean $(MAKE) -C $(TESTDIR) datagen # use native, faster - $(MAKE) -C $(TESTDIR) test CC=arm-linux-gnueabi-gcc QEMU_SYS=qemu-arm-static ZSTDRTTEST= MOREFLAGS="-Werror -static" FUZZER_FLAGS=--no-big-tests + $(MAKE) -C $(TESTDIR) test CC=arm-linux-gnueabi-gcc QEMU_SYS=qemu-arm-static ZSTDRTTEST= MOREFLAGS="-Werror -static $(MOREFLAGS)" FUZZER_FLAGS="--no-big-tests $(FUZZER_FLAGS)" aarch64test: $(MAKE) -C $(TESTDIR) datagen # use native, faster - $(MAKE) -C $(TESTDIR) test CC=aarch64-linux-gnu-gcc QEMU_SYS=qemu-aarch64-static ZSTDRTTEST= MOREFLAGS="-Werror -static" FUZZER_FLAGS=--no-big-tests + $(MAKE) -C $(TESTDIR) test CC=aarch64-linux-gnu-gcc QEMU_SYS=qemu-aarch64-static ZSTDRTTEST= MOREFLAGS="-Werror -static $(MOREFLAGS)" FUZZER_FLAGS="--no-big-tests $(FUZZER_FLAGS)" ppctest: clean $(MAKE) -C $(TESTDIR) datagen # use native, faster - $(MAKE) -C $(TESTDIR) test CC=powerpc-linux-gnu-gcc QEMU_SYS=qemu-ppc-static ZSTDRTTEST= MOREFLAGS="-Werror -Wno-attributes -static" FUZZER_FLAGS=--no-big-tests + $(MAKE) -C $(TESTDIR) test CC=powerpc-linux-gnu-gcc QEMU_SYS=qemu-ppc-static ZSTDRTTEST= MOREFLAGS="-Werror -Wno-attributes -static $(MOREFLAGS)" FUZZER_FLAGS="--no-big-tests $(FUZZER_FLAGS)" ppc64test: clean $(MAKE) -C $(TESTDIR) datagen # use native, faster - $(MAKE) -C $(TESTDIR) test CC=powerpc-linux-gnu-gcc QEMU_SYS=qemu-ppc64-static ZSTDRTTEST= MOREFLAGS="-m64 -static" FUZZER_FLAGS=--no-big-tests + $(MAKE) -C $(TESTDIR) test CC=powerpc-linux-gnu-gcc QEMU_SYS=qemu-ppc64-static ZSTDRTTEST= MOREFLAGS="-m64 -static $(MOREFLAGS)" FUZZER_FLAGS="--no-big-tests $(FUZZER_FLAGS)" .PHONY: arm-ppc-compilation arm-ppc-compilation: - $(MAKE) -C $(PRGDIR) clean zstd CC=arm-linux-gnueabi-gcc QEMU_SYS=qemu-arm-static ZSTDRTTEST= MOREFLAGS="-Werror -static" - $(MAKE) -C $(PRGDIR) clean zstd CC=aarch64-linux-gnu-gcc QEMU_SYS=qemu-aarch64-static ZSTDRTTEST= MOREFLAGS="-Werror -static" - $(MAKE) -C $(PRGDIR) clean zstd CC=powerpc-linux-gnu-gcc QEMU_SYS=qemu-ppc-static ZSTDRTTEST= MOREFLAGS="-Werror -Wno-attributes -static" - $(MAKE) -C $(PRGDIR) clean zstd CC=powerpc-linux-gnu-gcc QEMU_SYS=qemu-ppc64-static ZSTDRTTEST= MOREFLAGS="-m64 -static" + $(MAKE) -C $(PRGDIR) clean zstd CC=arm-linux-gnueabi-gcc QEMU_SYS=qemu-arm-static ZSTDRTTEST= MOREFLAGS="-Werror -static $(MOREFLAGS)" + $(MAKE) -C $(PRGDIR) clean zstd CC=aarch64-linux-gnu-gcc QEMU_SYS=qemu-aarch64-static ZSTDRTTEST= MOREFLAGS="-Werror -static $(MOREFLAGS)" + $(MAKE) -C $(PRGDIR) clean zstd CC=powerpc-linux-gnu-gcc QEMU_SYS=qemu-ppc-static ZSTDRTTEST= MOREFLAGS="-Werror -Wno-attributes -static $(MOREFLAGS)" + $(MAKE) -C $(PRGDIR) clean zstd CC=powerpc-linux-gnu-gcc QEMU_SYS=qemu-ppc64-static ZSTDRTTEST= MOREFLAGS="-m64 -static $(MOREFLAGS)" regressiontest: $(MAKE) -C $(FUZZDIR) regressiontest @@ -308,31 +308,31 @@ update_regressionResults: # run UBsan with -fsanitize-recover=pointer-overflow # this only works with recent compilers such as gcc 8+ usan: clean - $(MAKE) test CC=clang MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize-recover=pointer-overflow -fsanitize=undefined -Werror" + $(MAKE) test CC=clang MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize-recover=pointer-overflow -fsanitize=undefined -Werror $(MOREFLAGS)" asan: clean - $(MAKE) test CC=clang MOREFLAGS="-g -fsanitize=address -Werror" + $(MAKE) test CC=clang MOREFLAGS="-g -fsanitize=address -Werror $(MOREFLAGS)" asan-%: clean - LDFLAGS=-fuse-ld=gold MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize=address -Werror" $(MAKE) -C $(TESTDIR) $* + LDFLAGS=-fuse-ld=gold MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize=address -Werror $(MOREFLAGS)" $(MAKE) -C $(TESTDIR) $* msan: clean - $(MAKE) test CC=clang MOREFLAGS="-g -fsanitize=memory -fno-omit-frame-pointer -Werror" HAVE_LZMA=0 # datagen.c fails this test for no obvious reason + $(MAKE) test CC=clang MOREFLAGS="-g -fsanitize=memory -fno-omit-frame-pointer -Werror $(MOREFLAGS)" HAVE_LZMA=0 # datagen.c fails this test for no obvious reason msan-%: clean - LDFLAGS=-fuse-ld=gold MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize=memory -fno-omit-frame-pointer -Werror" FUZZER_FLAGS=--no-big-tests $(MAKE) -C $(TESTDIR) HAVE_LZMA=0 $* + LDFLAGS=-fuse-ld=gold MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize=memory -fno-omit-frame-pointer -Werror $(MOREFLAGS)" FUZZER_FLAGS="--no-big-tests $(FUZZER_FLAGS)" $(MAKE) -C $(TESTDIR) HAVE_LZMA=0 $* asan32: clean - $(MAKE) -C $(TESTDIR) test32 CC=clang MOREFLAGS="-g -fsanitize=address" + $(MAKE) -C $(TESTDIR) test32 CC=clang MOREFLAGS="-g -fsanitize=address $(MOREFLAGS)" uasan: clean - $(MAKE) test CC=clang MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize-recover=pointer-overflow -fsanitize=address,undefined -Werror" + $(MAKE) test CC=clang MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize-recover=pointer-overflow -fsanitize=address,undefined -Werror $(MOREFLAGS)" uasan-%: clean - LDFLAGS=-fuse-ld=gold MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize-recover=pointer-overflow -fsanitize=address,undefined -Werror" $(MAKE) -C $(TESTDIR) $* + LDFLAGS=-fuse-ld=gold MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize-recover=pointer-overflow -fsanitize=address,undefined -Werror $(MOREFLAGS)" $(MAKE) -C $(TESTDIR) $* tsan-%: clean - LDFLAGS=-fuse-ld=gold MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize=thread -Werror" $(MAKE) -C $(TESTDIR) $* FUZZER_FLAGS=--no-big-tests + LDFLAGS=-fuse-ld=gold MOREFLAGS="-g -fno-sanitize-recover=all -fsanitize=thread -Werror $(MOREFLAGS)" $(MAKE) -C $(TESTDIR) $* FUZZER_FLAGS="--no-big-tests $(FUZZER_FLAGS)" .PHONY: apt-install apt-install: diff --git a/README.md b/README.md index 6bcf757d8..9a9202adc 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,18 @@ to create `zstd` binary, and `libzstd` dynamic and static libraries. By default, `CMAKE_BUILD_TYPE` is set to `Release`. +#### Support for Fat (Universal2) Output + +`zstd` can be built and installed with support for both Apple Silicon (M1/M2) as well as Intel by using CMake's Universal2 support. +To perform a Fat/Universal2 build and install use the following commands: + +```bash +cmake -B build-cmake-debug -S build/cmake -G Ninja -DCMAKE_OSX_ARCHITECTURES="x86_64;x86_64h;arm64" +cd build-cmake-debug +ninja +sudo ninja install +``` + ### Meson A Meson project is provided within [`build/meson`](build/meson). Follow diff --git a/build/VS2010/libzstd-dll/libzstd-dll.vcxproj b/build/VS2010/libzstd-dll/libzstd-dll.vcxproj index a0aa897ce..97ad2fcb2 100644 --- a/build/VS2010/libzstd-dll/libzstd-dll.vcxproj +++ b/build/VS2010/libzstd-dll/libzstd-dll.vcxproj @@ -169,7 +169,6 @@ Level4 Disabled ZSTD_DLL_EXPORT=1;ZSTD_MULTITHREAD=1;ZSTD_LEGACY_SUPPORT=5;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - true EnableFastChecks MultiThreadedDebugDLL EditAndContinue diff --git a/build/VS2010/libzstd/libzstd.vcxproj b/build/VS2010/libzstd/libzstd.vcxproj index 17c08d703..614e6292a 100644 --- a/build/VS2010/libzstd/libzstd.vcxproj +++ b/build/VS2010/libzstd/libzstd.vcxproj @@ -162,7 +162,6 @@ Level4 Disabled ZSTD_MULTITHREAD=1;ZSTD_LEGACY_SUPPORT=5;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - true EnableFastChecks MultiThreadedDebugDLL EditAndContinue diff --git a/build/VS2010/zstd/zstd.vcxproj b/build/VS2010/zstd/zstd.vcxproj index 8ab239dd8..5e1bced6f 100644 --- a/build/VS2010/zstd/zstd.vcxproj +++ b/build/VS2010/zstd/zstd.vcxproj @@ -114,6 +114,7 @@ zstd $(SolutionDir)bin\$(Platform)_$(Configuration)\ $(SolutionDir)bin\obj\$(RootNamespace)_$(Platform)_$(Configuration)\ + NotSet @@ -187,6 +188,7 @@ ZSTD_MULTITHREAD=1;ZSTD_LEGACY_SUPPORT=5;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true false + $(InstructionSet) Console @@ -203,6 +205,7 @@ ZSTD_MULTITHREAD=1;ZSTD_LEGACY_SUPPORT=5;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true false + $(InstructionSet) Console @@ -222,6 +225,7 @@ false false MultiThreaded + $(InstructionSet) Console @@ -244,6 +248,7 @@ false MultiThreaded /DZSTD_MULTITHREAD %(AdditionalOptions) + $(InstructionSet) Console diff --git a/build/cmake/CMakeModules/AddZstdCompilationFlags.cmake b/build/cmake/CMakeModules/AddZstdCompilationFlags.cmake index 0265349fb..5f179989b 100644 --- a/build/cmake/CMakeModules/AddZstdCompilationFlags.cmake +++ b/build/cmake/CMakeModules/AddZstdCompilationFlags.cmake @@ -1,6 +1,15 @@ include(CheckCXXCompilerFlag) include(CheckCCompilerFlag) -include(CheckLinkerFlag) +# VERSION_GREATER_EQUAL requires CMake 3.7 or later. +# https://cmake.org/cmake/help/latest/command/if.html#version-greater-equal +if (CMAKE_VERSION VERSION_LESS 3.18) + set(ZSTD_HAVE_CHECK_LINKER_FLAG false) +else () + set(ZSTD_HAVE_CHECK_LINKER_FLAG true) +endif () +if (ZSTD_HAVE_CHECK_LINKER_FLAG) + include(CheckLinkerFlag) +endif() function(EnableCompilerFlag _flag _C _CXX _LD) string(REGEX REPLACE "\\+" "PLUS" varname "${_flag}") @@ -20,7 +29,20 @@ function(EnableCompilerFlag _flag _C _CXX _LD) endif () endif () if (_LD) - CHECK_LINKER_FLAG(C ${_flag} LD_FLAG_${varname}) + # We never add a linker flag with CMake < 3.18. We will + # implement CHECK_LINKER_FLAG() like feature for CMake < 3.18 + # or require CMake >= 3.18 when we need to add a required + # linker flag in future. + # + # We also skip linker flags check for MSVC compilers (which includes + # clang-cl) since currently check_linker_flag() doesn't give correct + # results for this configuration, + # see: https://gitlab.kitware.com/cmake/cmake/-/issues/22023 + if (ZSTD_HAVE_CHECK_LINKER_FLAG AND NOT MSVC) + CHECK_LINKER_FLAG(C ${_flag} LD_FLAG_${varname}) + else () + set(LD_FLAG_${varname} false) + endif () if (LD_FLAG_${varname}) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${_flag}" PARENT_SCOPE) set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${_flag}" PARENT_SCOPE) diff --git a/build/meson/meson.build b/build/meson/meson.build index 576dc44db..15c56c2b1 100644 --- a/build/meson/meson.build +++ b/build/meson/meson.build @@ -132,7 +132,7 @@ endif subdir('lib') -if bin_programs +if bin_programs or bin_tests subdir('programs') endif diff --git a/build/meson/programs/meson.build b/build/meson/programs/meson.build index e611dc337..0b5a93056 100644 --- a/build/meson/programs/meson.build +++ b/build/meson/programs/meson.build @@ -72,7 +72,14 @@ zstd = executable('zstd', c_args: zstd_c_args, dependencies: zstd_deps, export_dynamic: export_dynamic_on_windows, # Since Meson 0.45.0 - install: true) + build_by_default: bin_programs, + install: bin_programs) + +if not bin_programs + # we generate rules to build the programs, but don't install anything + # so do not continue to installing scripts and manpages + subdir_done() +endif zstd_frugal_sources = [join_paths(zstd_rootdir, 'programs/zstdcli.c'), join_paths(zstd_rootdir, 'programs/timefn.c'), diff --git a/build/meson/tests/meson.build b/build/meson/tests/meson.build index e70b73432..2dd8d1067 100644 --- a/build/meson/tests/meson.build +++ b/build/meson/tests/meson.build @@ -162,7 +162,7 @@ if host_machine_os != os_windows playTests_sh, args: opt, env: ['ZSTD_BIN=' + zstd.full_path(), 'DATAGEN_BIN=./datagen'], - depends: [datagen], + depends: [datagen, zstd], suite: suite, workdir: meson.current_build_dir(), timeout: 2800) # Timeout should work on HDD drive diff --git a/contrib/docker/Dockerfile b/contrib/docker/Dockerfile index e06a32c0d..912bf1949 100644 --- a/contrib/docker/Dockerfile +++ b/contrib/docker/Dockerfile @@ -1,13 +1,13 @@ # Dockerfile # First image to build the binary -FROM alpine as builder +FROM alpine@sha256:69665d02cb32192e52e07644d76bc6f25abeb5410edc1c7a81a10ba3f0efb90a as builder RUN apk --no-cache add make gcc libc-dev COPY . /src RUN mkdir /pkg && cd /src && make && make DESTDIR=/pkg install # Second minimal image to only keep the built binary -FROM alpine +FROM alpine@sha256:69665d02cb32192e52e07644d76bc6f25abeb5410edc1c7a81a10ba3f0efb90a # Copy the built files COPY --from=builder /pkg / diff --git a/contrib/linux-kernel/zstd_common_module.c b/contrib/linux-kernel/zstd_common_module.c index 2fead39eb..466828e35 100644 --- a/contrib/linux-kernel/zstd_common_module.c +++ b/contrib/linux-kernel/zstd_common_module.c @@ -24,9 +24,6 @@ EXPORT_SYMBOL_GPL(HUF_readStats_wksp); EXPORT_SYMBOL_GPL(ZSTD_isError); EXPORT_SYMBOL_GPL(ZSTD_getErrorName); EXPORT_SYMBOL_GPL(ZSTD_getErrorCode); -EXPORT_SYMBOL_GPL(ZSTD_customMalloc); -EXPORT_SYMBOL_GPL(ZSTD_customCalloc); -EXPORT_SYMBOL_GPL(ZSTD_customFree); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("Zstd Common"); diff --git a/contrib/linux-kernel/zstd_deps.h b/contrib/linux-kernel/zstd_deps.h index 925161416..670c5fa2a 100644 --- a/contrib/linux-kernel/zstd_deps.h +++ b/contrib/linux-kernel/zstd_deps.h @@ -84,7 +84,7 @@ static uint64_t ZSTD_div64(uint64_t dividend, uint32_t divisor) { #include -#define assert(x) WARN_ON((x)) +#define assert(x) WARN_ON(!(x)) #endif /* ZSTD_DEPS_ASSERT */ #endif /* ZSTD_DEPS_NEED_ASSERT */ diff --git a/contrib/pzstd/Makefile b/contrib/pzstd/Makefile index 830053cd7..e62f8e873 100644 --- a/contrib/pzstd/Makefile +++ b/contrib/pzstd/Makefile @@ -37,10 +37,16 @@ CFLAGS += -Wno-deprecated-declarations PZSTD_INC = -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(PROGDIR) -I. GTEST_INC = -isystem googletest/googletest/include +# If default C++ version is older than C++11, explicitly set C++11, which is the +# minimum required by the code. +ifeq ($(shell echo "\043if __cplusplus < 201103L\n\043error\n\043endif" | $(CXX) -x c++ -Werror -c - -o /dev/null 2>/dev/null && echo 1 || echo 0),0) +PZSTD_CXX_STD := -std=c++11 +endif + PZSTD_CPPFLAGS = $(PZSTD_INC) PZSTD_CCXXFLAGS = PZSTD_CFLAGS = $(PZSTD_CCXXFLAGS) -PZSTD_CXXFLAGS = $(PZSTD_CCXXFLAGS) -std=c++11 +PZSTD_CXXFLAGS = $(PZSTD_CCXXFLAGS) $(PZSTD_CXX_STD) PZSTD_LDFLAGS = EXTRA_FLAGS = ALL_CFLAGS = $(EXTRA_FLAGS) $(CPPFLAGS) $(PZSTD_CPPFLAGS) $(CFLAGS) $(PZSTD_CFLAGS) diff --git a/contrib/seekable_format/README.md b/contrib/seekable_format/README.md new file mode 100644 index 000000000..fedf96bab --- /dev/null +++ b/contrib/seekable_format/README.md @@ -0,0 +1,42 @@ +# Zstandard Seekable Format + +The seekable format splits compressed data into a series of independent "frames", +each compressed individually, +so that decompression of a section in the middle of an archive +only requires zstd to decompress at most a frame's worth of extra data, +instead of the entire archive. + +The frames are appended, so that the decompression of the entire payload +still regenerates the original content, using any compliant zstd decoder. + +On top of that, the seekable format generates a jump table, +which makes it possible to jump directly to the position of the relevant frame +when requesting only a segment of the data. +The jump table is simply ignored by zstd decoders unaware of the seekable format. + +The format is delivered with an API to create seekable archives +and to retrieve arbitrary segments inside the archive. + +### Maximum Frame Size parameter + +When creating a seekable archive, the main parameter is the maximum frame size. + +At compression time, user can manually select the boundaries between segments, +but they don't have to: long segments will be automatically split +when larger than selected maximum frame size. + +Small frame sizes reduce decompression cost when requesting small segments, +because the decoder will nonetheless have to decompress an entire frame +to recover just a single byte from it. + +A good rule of thumb is to select a maximum frame size roughly equivalent +to the access pattern when it's known. +For example, if the application tends to request 4KB blocks, +then it's a good idea to set a maximum frame size in the vicinity of 4 KB. + +But small frame sizes also reduce compression ratio, +and increase the cost for the jump table, +so there is a balance to find. + +In general, try to avoid really tiny frame sizes (<1 KB), +which would have a large negative impact on compression ratio. diff --git a/contrib/seekable_format/examples/parallel_compression.c b/contrib/seekable_format/examples/parallel_compression.c index 0ec9fbd20..4e06fae32 100644 --- a/contrib/seekable_format/examples/parallel_compression.c +++ b/contrib/seekable_format/examples/parallel_compression.c @@ -25,7 +25,7 @@ #include "pool.h" // use zstd thread pool for demo -#include "zstd_seekable.h" +#include "../zstd_seekable.h" static void* malloc_orDie(size_t size) { diff --git a/contrib/seekable_format/examples/parallel_processing.c b/contrib/seekable_format/examples/parallel_processing.c index b1709db77..356561e5a 100644 --- a/contrib/seekable_format/examples/parallel_processing.c +++ b/contrib/seekable_format/examples/parallel_processing.c @@ -29,7 +29,7 @@ #include "pool.h" // use zstd thread pool for demo -#include "zstd_seekable.h" +#include "../zstd_seekable.h" #define MIN(a, b) ((a) < (b) ? (a) : (b)) diff --git a/contrib/seekable_format/examples/seekable_compression.c b/contrib/seekable_format/examples/seekable_compression.c index 182b46f64..c3d227dd0 100644 --- a/contrib/seekable_format/examples/seekable_compression.c +++ b/contrib/seekable_format/examples/seekable_compression.c @@ -13,7 +13,7 @@ #define ZSTD_STATIC_LINKING_ONLY #include // presumes zstd library is installed -#include "zstd_seekable.h" +#include "../zstd_seekable.h" static void* malloc_orDie(size_t size) { @@ -112,20 +112,23 @@ static char* createOutFilename_orDie(const char* filename) return (char*)outSpace; } -int main(int argc, const char** argv) { +#define CLEVEL_DEFAULT 5 +int main(int argc, const char** argv) +{ const char* const exeName = argv[0]; - if (argc!=3) { - printf("wrong arguments\n"); - printf("usage:\n"); - printf("%s FILE FRAME_SIZE\n", exeName); + if (argc<3 || argc>4) { + printf("wrong arguments \n"); + printf("usage: \n"); + printf("%s FILE FRAME_SIZE [LEVEL] \n", exeName); return 1; } { const char* const inFileName = argv[1]; unsigned const frameSize = (unsigned)atoi(argv[2]); + int const cLevel = (argc==4) ? atoi(argv[3]) : CLEVEL_DEFAULT; char* const outFileName = createOutFilename_orDie(inFileName); - compressFile_orDie(inFileName, outFileName, 5, frameSize); + compressFile_orDie(inFileName, outFileName, cLevel, frameSize); free(outFileName); } diff --git a/contrib/seekable_format/examples/seekable_decompression.c b/contrib/seekable_format/examples/seekable_decompression.c index 2c4f3ba0c..7edbca87d 100644 --- a/contrib/seekable_format/examples/seekable_decompression.c +++ b/contrib/seekable_format/examples/seekable_decompression.c @@ -16,7 +16,7 @@ #include // presumes zstd library is installed #include -#include "zstd_seekable.h" +#include "../zstd_seekable.h" #define MIN(a, b) ((a) < (b) ? (a) : (b)) diff --git a/contrib/seekable_format/tests/seekable_tests.c b/contrib/seekable_format/tests/seekable_tests.c index 1bb2d0e81..f89bdc930 100644 --- a/contrib/seekable_format/tests/seekable_tests.c +++ b/contrib/seekable_format/tests/seekable_tests.c @@ -3,8 +3,55 @@ #include // malloc #include #include +#include -#include "zstd_seekable.h" +#include "../zstd_seekable.h" + + +/* ZSTD_seekable_customFile implementation that reads/seeks a buffer while keeping track of total bytes read */ +typedef struct { + const void *ptr; + size_t size; + size_t pos; + size_t totalRead; +} buffWrapperWithTotal_t; + +static int readBuffWithTotal(void* opaque, void* buffer, size_t n) +{ + buffWrapperWithTotal_t* const buff = (buffWrapperWithTotal_t*)opaque; + assert(buff != NULL); + if (buff->pos + n > buff->size) return -1; + memcpy(buffer, (const char*)buff->ptr + buff->pos, n); + buff->pos += n; + buff->totalRead += n; + return 0; +} + +static int seekBuffWithTotal(void* opaque, long long offset, int origin) +{ + buffWrapperWithTotal_t* const buff = (buffWrapperWithTotal_t*) opaque; + unsigned long long newOffset; + assert(buff != NULL); + switch (origin) { + case SEEK_SET: + assert(offset >= 0); + newOffset = (unsigned long long)offset; + break; + case SEEK_CUR: + newOffset = (unsigned long long)((long long)buff->pos + offset); + break; + case SEEK_END: + newOffset = (unsigned long long)((long long)buff->size + offset); + break; + default: + assert(0); /* not possible */ + } + if (newOffset > buff->size) { + return -1; + } + buff->pos = newOffset; + return 0; +} /* Basic unit tests for zstd seekable format */ int main(int argc, const char** argv) @@ -220,6 +267,92 @@ int main(int argc, const char** argv) } printf("Success!\n"); + + printf("Test %u - multiple decompress calls: ", testNb++); + { char const inBuffer[] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt"; + size_t const inSize = sizeof(inBuffer); + + size_t const seekCapacity = 5000; + void* const seekBuffer = malloc(seekCapacity); + assert(seekBuffer != NULL); + size_t seekSize; + + size_t const outCapacity = inSize; + char* const outBuffer = malloc(outCapacity); + assert(outBuffer != NULL); + + ZSTD_seekable_CStream* const zscs = ZSTD_seekable_createCStream(); + assert(zscs != NULL); + + /* compress test data with a small frame size to ensure multiple frames in the output */ + unsigned const maxFrameSize = 40; + { size_t const initStatus = ZSTD_seekable_initCStream(zscs, 9, 0 /* checksumFlag */, maxFrameSize); + assert(!ZSTD_isError(initStatus)); + } + + { ZSTD_outBuffer outb = { .dst=seekBuffer, .pos=0, .size=seekCapacity }; + ZSTD_inBuffer inb = { .src=inBuffer, .pos=0, .size=inSize }; + + while (inb.pos < inb.size) { + size_t const cStatus = ZSTD_seekable_compressStream(zscs, &outb, &inb); + assert(!ZSTD_isError(cStatus)); + } + + size_t const endStatus = ZSTD_seekable_endStream(zscs, &outb); + assert(!ZSTD_isError(endStatus)); + seekSize = outb.pos; + } + + ZSTD_seekable* const stream = ZSTD_seekable_create(); + assert(stream != NULL); + buffWrapperWithTotal_t buffWrapper = {seekBuffer, seekSize, 0, 0}; + { ZSTD_seekable_customFile srcFile = {&buffWrapper, &readBuffWithTotal, &seekBuffWithTotal}; + size_t const initStatus = ZSTD_seekable_initAdvanced(stream, srcFile); + assert(!ZSTD_isError(initStatus)); } + + /* Perform a series of small reads and seeks (repeatedly read 1 byte and skip 1 byte) + and check that we didn't reread input data unnecessarily */ + size_t pos; + for (pos = 0; pos < inSize; pos += 2) { + size_t const decStatus = ZSTD_seekable_decompress(stream, outBuffer, 1, pos); + if (decStatus != 1 || outBuffer[0] != inBuffer[pos]) { + goto _test_error; + } + } + if (buffWrapper.totalRead > seekSize) { + /* We read more than the compressed size, meaning there were some rereads. + This is unneeded because we only seeked forward. */ + printf("Too much data read: %zu read, with compressed size %zu\n", buffWrapper.totalRead, seekSize); + goto _test_error; + } + + /* Perform some reads and seeks to ensure correctness */ + struct { + size_t offset; + size_t size; + } const tests[] = { /* Assume the frame size is 40 */ + {20, 40}, /* read partial data from two frames */ + {60, 10}, /* continue reading from the same offset */ + {50, 20}, /* seek backward within the same frame */ + {10, 10}, /* seek backward to a different frame */ + {25, 10}, /* seek forward within the same frame */ + {60, 10}, /* seek forward to a different frame */ + }; + size_t idx; + for (idx = 0; idx < sizeof(tests) / sizeof(tests[0]); idx++) { + size_t const decStatus = ZSTD_seekable_decompress(stream, outBuffer, tests[idx].size, tests[idx].offset); + if (decStatus != tests[idx].size || memcmp(outBuffer, inBuffer + tests[idx].offset, tests[idx].size) != 0) { + goto _test_error; + } + } + + free(seekBuffer); + free(outBuffer); + ZSTD_seekable_freeCStream(zscs); + ZSTD_seekable_free(stream); + } + printf("Success!\n"); + /* TODO: Add more tests */ printf("Finished tests\n"); return 0; diff --git a/contrib/seekable_format/zstd_seekable.h b/contrib/seekable_format/zstd_seekable.h index d2807cfbd..a0e5e3573 100644 --- a/contrib/seekable_format/zstd_seekable.h +++ b/contrib/seekable_format/zstd_seekable.h @@ -15,8 +15,8 @@ extern "C" { #define ZSTD_SEEKABLE_MAXFRAMES 0x8000000U -/* Limit the maximum size to avoid any potential issues storing the compressed size */ -#define ZSTD_SEEKABLE_MAX_FRAME_DECOMPRESSED_SIZE 0x80000000U +/* Limit maximum size to avoid potential issues storing the compressed size */ +#define ZSTD_SEEKABLE_MAX_FRAME_DECOMPRESSED_SIZE 0x40000000U /*-**************************************************************************** * Seekable Format @@ -48,10 +48,19 @@ typedef struct ZSTD_seekTable_s ZSTD_seekTable; * * Use ZSTD_seekable_initCStream() to initialize a ZSTD_seekable_CStream object * for a new compression operation. -* `maxFrameSize` indicates the size at which to automatically start a new -* seekable frame. `maxFrameSize == 0` implies the default maximum size. -* `checksumFlag` indicates whether or not the seek table should include frame -* checksums on the uncompressed data for verification. +* - `maxFrameSize` indicates the size at which to automatically start a new +* seekable frame. +* `maxFrameSize == 0` implies the default maximum size. +* Smaller frame sizes allow faster decompression of small segments, +* since retrieving a single byte requires decompression of +* the full frame where the byte belongs. +* In general, size the frames to roughly correspond to +* the access granularity (when it's known). +* But small sizes also reduce compression ratio. +* Avoid really tiny frame sizes (< 1 KB), +* that would hurt compression ratio considerably. +* - `checksumFlag` indicates whether or not the seek table should include frame +* checksums on the uncompressed data for verification. * @return : a size hint for input to provide for compression, or an error code * checkable with ZSTD_isError() * diff --git a/contrib/seekable_format/zstdseek_compress.c b/contrib/seekable_format/zstdseek_compress.c index 113f6f99c..4997807d2 100644 --- a/contrib/seekable_format/zstdseek_compress.c +++ b/contrib/seekable_format/zstdseek_compress.c @@ -230,6 +230,8 @@ size_t ZSTD_seekable_compressStream(ZSTD_seekable_CStream* zcs, ZSTD_outBuffer* const BYTE* const inBase = (const BYTE*) input->src + input->pos; size_t inLen = input->size - input->pos; + assert(zcs->maxFrameSize < INT_MAX); + ZSTD_CCtx_setParameter(zcs->cstream, ZSTD_c_srcSizeHint, (int)zcs->maxFrameSize); inLen = MIN(inLen, (size_t)(zcs->maxFrameSize - zcs->frameDSize)); /* if we haven't finished flushing the last frame, don't start writing a new one */ diff --git a/contrib/seekable_format/zstdseek_decompress.c b/contrib/seekable_format/zstdseek_decompress.c index fbb2d4fe3..4fefc5f2e 100644 --- a/contrib/seekable_format/zstdseek_decompress.c +++ b/contrib/seekable_format/zstdseek_decompress.c @@ -493,7 +493,7 @@ size_t ZSTD_seekable_decompress(ZSTD_seekable* zs, void* dst, size_t len, unsign size_t srcBytesRead = 0; do { /* check if we can continue from a previous decompress job */ - if (targetFrame != zs->curFrame || offset != zs->decompressedOffset) { + if (targetFrame != zs->curFrame || offset < zs->decompressedOffset) { zs->decompressedOffset = zs->seekTable.entries[targetFrame].dOffset; zs->curFrame = targetFrame; diff --git a/doc/zstd_compression_format.md b/doc/zstd_compression_format.md index e40677a1d..3843bf390 100644 --- a/doc/zstd_compression_format.md +++ b/doc/zstd_compression_format.md @@ -16,7 +16,7 @@ Distribution of this document is unlimited. ### Version -0.3.7 (2020-12-09) +0.3.9 (2023-03-08) Introduction @@ -470,6 +470,7 @@ This field uses 2 lowest bits of first byte, describing 4 different block types repeated `Regenerated_Size` times. - `Compressed_Literals_Block` - This is a standard Huffman-compressed block, starting with a Huffman tree description. + In this mode, there are at least 2 different literals represented in the Huffman tree description. See details below. - `Treeless_Literals_Block` - This is a Huffman-compressed block, using Huffman tree _from previous Huffman-compressed literals block_. @@ -533,15 +534,20 @@ __`Size_Format` for `Compressed_Literals_Block` and `Treeless_Literals_Block`__ Both `Compressed_Size` and `Regenerated_Size` fields follow __little-endian__ convention. Note: `Compressed_Size` __includes__ the size of the Huffman Tree description _when_ it is present. +Note 2: `Compressed_Size` can never be `==0`. +Even in single-stream scenario, assuming an empty content, it must be `>=1`, +since it contains at least the final end bit flag. +In 4-streams scenario, a valid `Compressed_Size` is necessarily `>= 10` +(6 bytes for the jump table, + 4x1 bytes for the 4 streams). -4 streams is superior to 1 stream in decompression speed, +4 streams is faster than 1 stream in decompression speed, by exploiting instruction level parallelism. But it's also more expensive, costing on average ~7.3 bytes more than the 1 stream mode, mostly from the jump table. In general, use the 4 streams mode when there are more literals to decode, to favor higher decompression speeds. -Beyond 1KB, the 4 streams mode is compulsory anyway. +Note that beyond >1KB of literals, the 4 streams mode is compulsory. Note that a minimum of 6 bytes is required for the 4 streams mode. That's a technical minimum, but it's not recommended to employ the 4 streams mode @@ -566,6 +572,7 @@ or from a dictionary. ### `Huffman_Tree_Description` This section is only present when `Literals_Block_Type` type is `Compressed_Literals_Block` (`2`). +The tree describes the weights of all literals symbols that can be present in the literals block, at least 2 and up to 256. The format of the Huffman tree description can be found at [Huffman Tree description](#huffman-tree-description). The size of `Huffman_Tree_Description` is determined during decoding process, it must be used to determine where streams begin. @@ -575,10 +582,10 @@ it must be used to determine where streams begin. ### Jump Table The Jump Table is only present when there are 4 Huffman-coded streams. -Reminder : Huffman compressed data consists of either 1 or 4 Huffman-coded streams. +Reminder : Huffman compressed data consists of either 1 or 4 streams. If only one stream is present, it is a single bitstream occupying the entire -remaining portion of the literals block, encoded as described within +remaining portion of the literals block, encoded as described in [Huffman-Coded Streams](#huffman-coded-streams). If there are four streams, `Literals_Section_Header` only provided @@ -589,17 +596,18 @@ except for the last stream which may be up to 3 bytes smaller, to reach a total decompressed size as specified in `Regenerated_Size`. The compressed size of each stream is provided explicitly in the Jump Table. -Jump Table is 6 bytes long, and consist of three 2-byte __little-endian__ fields, +Jump Table is 6 bytes long, and consists of three 2-byte __little-endian__ fields, describing the compressed sizes of the first three streams. -`Stream4_Size` is computed from total `Total_Streams_Size` minus sizes of other streams. +`Stream4_Size` is computed from `Total_Streams_Size` minus sizes of other streams: `Stream4_Size = Total_Streams_Size - 6 - Stream1_Size - Stream2_Size - Stream3_Size`. -Note: if `Stream1_Size + Stream2_Size + Stream3_Size > Total_Streams_Size`, +`Stream4_Size` is necessarily `>= 1`. Therefore, +if `Total_Streams_Size < Stream1_Size + Stream2_Size + Stream3_Size + 6 + 1`, data is considered corrupted. Each of these 4 bitstreams is then decoded independently as a Huffman-Coded stream, -as described at [Huffman-Coded Streams](#huffman-coded-streams) +as described in [Huffman-Coded Streams](#huffman-coded-streams) Sequences Section @@ -1197,7 +1205,7 @@ Huffman Coding -------------- Zstandard Huffman-coded streams are read backwards, similar to the FSE bitstreams. -Therefore, to find the start of the bitstream, it is therefore to +Therefore, to find the start of the bitstream, it is required to know the offset of the last byte of the Huffman-coded stream. After writing the last bit containing information, the compressor @@ -1239,9 +1247,15 @@ Transformation from `Weight` to `Number_of_Bits` follows this formula : ``` Number_of_Bits = Weight ? (Max_Number_of_Bits + 1 - Weight) : 0 ``` -The last symbol's `Weight` is deduced from previously decoded ones, -by completing to the nearest power of 2. -This power of 2 gives `Max_Number_of_Bits`, the depth of the current tree. +When a literal value is not present, it receives a `Weight` of 0. +The least frequent symbol receives a `Weight` of 1. +Consequently, the `Weight` 1 is necessarily present. +The most frequent symbol receives a `Weight` anywhere between 1 and 11 (max). +The last symbol's `Weight` is deduced from previously retrieved Weights, +by completing to the nearest power of 2. It's necessarily non 0. +If it's not possible to reach a clean power of 2 with a single `Weight` value, +the Huffman Tree Description is considered invalid. +This final power of 2 gives `Max_Number_of_Bits`, the depth of the current tree. `Max_Number_of_Bits` must be <= 11, otherwise the representation is considered corrupted. @@ -1254,7 +1268,7 @@ Let's presume the following Huffman tree must be described : The tree depth is 4, since its longest elements uses 4 bits (longest elements are the one with smallest frequency). -Value `5` will not be listed, as it can be determined from values for 0-4, +Literal value `5` will not be listed, as it can be determined from previous values 0-4, nor will values above `5` as they are all 0. Values from `0` to `4` will be listed using `Weight` instead of `Number_of_Bits`. Weight formula is : @@ -1274,7 +1288,7 @@ The `Weight` of `5` can be determined by advancing to the next power of 2. The sum of `2^(Weight-1)` (excluding 0's) is : `8 + 4 + 2 + 0 + 1 = 15`. Nearest larger power of 2 value is 16. -Therefore, `Max_Number_of_Bits = 4` and `Weight[5] = 16-15 = 1`. +Therefore, `Max_Number_of_Bits = 4` and `Weight[5] = log_2(16 - 15) + 1 = 1`. #### Huffman Tree header @@ -1683,6 +1697,8 @@ or at least provide a meaningful error code explaining for which reason it canno Version changes --------------- +- 0.3.9 : clarifications for Huffman-compressed literal sizes. +- 0.3.8 : clarifications for Huffman Blocks and Huffman Tree descriptions. - 0.3.7 : clarifications for Repeat_Offsets, matching RFC8878 - 0.3.6 : clarifications for Dictionary_ID - 0.3.5 : clarifications for Block_Maximum_Size diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html index c33f0e262..dcc10208d 100644 --- a/doc/zstd_manual.html +++ b/doc/zstd_manual.html @@ -1,10 +1,10 @@ -zstd 1.5.4 Manual +zstd 1.5.5 Manual -

zstd 1.5.4 Manual

+

zstd 1.5.5 Manual


Contents

    @@ -22,15 +22,15 @@
  1. Dictionary helper functions
  2. Advanced dictionary and prefix API (Requires v1.4.0+)
  3. experimental API (static linking only)
  4. -
  5. Frame size functions
  6. +
  7. Frame header and size functions
  8. Memory management
  9. Advanced compression functions
  10. Advanced decompression functions
  11. Advanced streaming functions
  12. -
  13. Buffer-less and synchronous inner streaming functions
  14. +
  15. Buffer-less and synchronous inner streaming functions (DEPRECATED)
  16. Buffer-less streaming compression (synchronous mode)
  17. Buffer-less streaming decompression (synchronous mode)
  18. -
  19. Block level API
  20. +
  21. Block level API (DEPRECATED)

Introduction

@@ -80,7 +80,8 @@
                 const void* src, size_t srcSize,
                       int compressionLevel);
 

Compresses `src` content as a single zstd compressed frame into already allocated `dst`. - Hint : compression runs faster if `dstCapacity` >= `ZSTD_compressBound(srcSize)`. + NOTE: Providing `dstCapacity >= ZSTD_compressBound(srcSize)` guarantees that zstd will have + enough space to successfully compress the data. @return : compressed size written into `dst` (<= `dstCapacity), or an error code if it fails (which can be tested using ZSTD_isError()).


@@ -486,7 +487,8 @@ size_t ZSTD_freeDCtx(ZSTD_DCtx* dctx); /* accept NULL pointer */ Should cctx hold data from a previously unfinished frame, everything about it is forgotten. - Compression parameters are pushed into CCtx before starting compression, using ZSTD_CCtx_set*() - The function is always blocking, returns when compression is completed. - Hint : compression runs faster if `dstCapacity` >= `ZSTD_compressBound(srcSize)`. + NOTE: Providing `dstCapacity >= ZSTD_compressBound(srcSize)` guarantees that zstd will have + enough space to successfully compress the data, though it is possible it fails for other reasons. @return : compressed size written into `dst` (<= `dstCapacity), or an error code if it fails (which can be tested using ZSTD_isError()). @@ -866,9 +868,11 @@ size_t ZSTD_freeDStream(ZSTD_DStream* zds); /* accept NULL pointer */

Advanced dictionary and prefix API (Requires v1.4.0+)

  This API allows dictionaries to be used with ZSTD_compress2(),
- ZSTD_compressStream2(), and ZSTD_decompressDCtx(). Dictionaries are sticky, and
- only reset with the context is reset with ZSTD_reset_parameters or
- ZSTD_reset_session_and_parameters. Prefixes are single-use.
+ ZSTD_compressStream2(), and ZSTD_decompressDCtx().
+ Dictionaries are sticky, they remain valid when same context is re-used,
+ they only reset when the context is reset
+ with ZSTD_reset_parameters or ZSTD_reset_session_and_parameters.
+ In contrast, Prefixes are single-use.
 
size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize);
@@ -888,7 +892,11 @@ size_t ZSTD_freeDStream(ZSTD_DStream* zds);  /* accept NULL pointer */
            Use experimental ZSTD_CCtx_loadDictionary_byReference() to reference content instead.
            In such a case, dictionary buffer must outlive its users.
   Note 4 : Use ZSTD_CCtx_loadDictionary_advanced()
-           to precisely select how dictionary content must be interpreted. 
+           to precisely select how dictionary content must be interpreted.
+  Note 5 : This method does not benefit from LDM (long distance mode).
+           If you want to employ LDM on some large dictionary content,
+           prefer employing ZSTD_CCtx_refPrefix() described below.
+ 
 


size_t ZSTD_CCtx_refCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict);
@@ -912,6 +920,7 @@ size_t ZSTD_freeDStream(ZSTD_DStream* zds);  /* accept NULL pointer */
   Decompression will need same prefix to properly regenerate data.
   Compressing with a prefix is similar in outcome as performing a diff and compressing it,
   but performs much faster, especially during decompression (compression speed is tunable with compression level).
+  This method is compatible with LDM (long distance mode).
  @result : 0, or an error code (which can be tested with ZSTD_isError()).
   Special: Adding any prefix (including NULL) invalidates any previous prefix or dictionary
   Note 1 : Prefix buffer is referenced. It **must** outlive compression.
@@ -1146,7 +1155,7 @@ size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict);
   ZSTD_ps_disable = 2       /* Do not use the feature */
 } ZSTD_paramSwitch_e;
 

-

Frame size functions


+

Frame header and size functions


 
 
ZSTDLIB_STATIC_API unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize);
 

`src` should point to the start of a series of ZSTD encoded and/or skippable frames @@ -1192,6 +1201,31 @@ size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict); or an error code (if srcSize is too small)


+
typedef enum { ZSTD_frame, ZSTD_skippableFrame } ZSTD_frameType_e;
+

+
typedef struct {
+    unsigned long long frameContentSize; /* if == ZSTD_CONTENTSIZE_UNKNOWN, it means this field is not available. 0 means "empty" */
+    unsigned long long windowSize;       /* can be very large, up to <= frameContentSize */
+    unsigned blockSizeMax;
+    ZSTD_frameType_e frameType;          /* if == ZSTD_skippableFrame, frameContentSize is the size of skippable content */
+    unsigned headerSize;
+    unsigned dictID;
+    unsigned checksumFlag;
+    unsigned _reserved1;
+    unsigned _reserved2;
+} ZSTD_frameHeader;
+

+
ZSTDLIB_STATIC_API size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize);   /**< doesn't consume input */
+/*! ZSTD_getFrameHeader_advanced() :
+ *  same as ZSTD_getFrameHeader(),
+ *  with added capability to select a format (like ZSTD_f_zstd1_magicless) */
+ZSTDLIB_STATIC_API size_t ZSTD_getFrameHeader_advanced(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize, ZSTD_format_e format);
+

decode Frame Header, or requires larger `srcSize`. + @return : 0, `zfhPtr` is correctly filled, + >0, `srcSize` is too small, value is wanted `srcSize` amount, + or an error code, which can be tested using ZSTD_isError() +


+
ZSTDLIB_STATIC_API size_t ZSTD_decompressionMargin(const void* src, size_t srcSize);
 

Zstd supports in-place decompression, where the input and output buffers overlap. In this case, the output buffer must be at least (Margin + Output_Size) bytes large, @@ -1505,10 +1539,24 @@ ZSTDLIB_STATIC_API size_t ZSTD_CCtx_refThreadPool(ZSTD_CCtx* cctx, ZSTD_threadPo


ZSTDLIB_STATIC_API size_t ZSTD_CCtx_setCParams(ZSTD_CCtx* cctx, ZSTD_compressionParameters cparams);
-

Set all parameters provided within @cparams into the working @cctx. +

Set all parameters provided within @p cparams into the working @p cctx. Note : if modifying parameters during compression (MT mode only), note that changes to the .windowLog parameter will be ignored. - @return 0 on success, or an error code (can be checked with ZSTD_isError()) + @return 0 on success, or an error code (can be checked with ZSTD_isError()). + On failure, no parameters are updated. + +


+ +
ZSTDLIB_STATIC_API size_t ZSTD_CCtx_setFParams(ZSTD_CCtx* cctx, ZSTD_frameParameters fparams);
+

Set all parameters provided within @p fparams into the working @p cctx. + @return 0 on success, or an error code (can be checked with ZSTD_isError()). + +


+ +
ZSTDLIB_STATIC_API size_t ZSTD_CCtx_setParams(ZSTD_CCtx* cctx, ZSTD_parameters params);
+

Set all parameters provided within @p params into the working @p cctx. + @return 0 on success, or an error code (can be checked with ZSTD_isError()). +


ZSTD_DEPRECATED("use ZSTD_compress2")
@@ -1754,12 +1802,9 @@ size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs,
         const void* dict, size_t dictSize,
               ZSTD_parameters params,
               unsigned long long pledgedSrcSize);
-

This function is DEPRECATED, and is approximately equivalent to: +

This function is DEPRECATED, and is equivalent to: ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only); - // Pseudocode: Set each zstd parameter and leave the rest as-is. - for ((param, value) : params) { - ZSTD_CCtx_setParameter(zcs, param, value); - } + ZSTD_CCtx_setParams(zcs, params); ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize); ZSTD_CCtx_loadDictionary(zcs, dict, dictSize); @@ -1788,12 +1833,9 @@ size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, const ZSTD_CDict* cdict, ZSTD_frameParameters fParams, unsigned long long pledgedSrcSize); -

This function is DEPRECATED, and is approximately equivalent to: +

This function is DEPRECATED, and is equivalent to: ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only); - // Pseudocode: Set each zstd frame parameter and leave the rest as-is. - for ((fParam, value) : fParams) { - ZSTD_CCtx_setParameter(zcs, fParam, value); - } + ZSTD_CCtx_setFParams(zcs, fParams); ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize); ZSTD_CCtx_refCDict(zcs, cdict); @@ -1880,10 +1922,42 @@ ZSTDLIB_STATIC_API size_t ZSTD_resetDStream(ZSTD_DStream* zds);


-

Buffer-less and synchronous inner streaming functions

-  This is an advanced API, giving full control over buffer management, for users which need direct control over memory.
-  But it's also a complex one, with several restrictions, documented below.
-  Prefer normal streaming API for an easier experience.
+
ZSTDLIB_STATIC_API void
+ZSTD_registerSequenceProducer(
+  ZSTD_CCtx* cctx,
+  void* sequenceProducerState,
+  ZSTD_sequenceProducer_F* sequenceProducer
+);
+

Instruct zstd to use a block-level external sequence producer function. + + The sequenceProducerState must be initialized by the caller, and the caller is + responsible for managing its lifetime. This parameter is sticky across + compressions. It will remain set until the user explicitly resets compression + parameters. + + Sequence producer registration is considered to be an "advanced parameter", + part of the "advanced API". This means it will only have an effect on compression + APIs which respect advanced parameters, such as compress2() and compressStream2(). + Older compression APIs such as compressCCtx(), which predate the introduction of + "advanced parameters", will ignore any external sequence producer setting. + + The sequence producer can be "cleared" by registering a NULL function pointer. This + removes all limitations described above in the "LIMITATIONS" section of the API docs. + + The user is strongly encouraged to read the full API documentation (above) before + calling this function. +


+ +

Buffer-less and synchronous inner streaming functions (DEPRECATED)

+  This API is deprecated, and will be removed in a future version.
+  It allows streaming (de)compression with user allocated buffers.
+  However, it is hard to use, and not as well tested as the rest of
+  our API.
+
+  Please use the normal streaming API instead: ZSTD_compressStream2,
+  and ZSTD_decompressStream.
+  If there is functionality that you need, but it doesn't provide,
+  please open an issue on our GitHub.
  
 
@@ -1914,8 +1988,11 @@ ZSTDLIB_STATIC_API size_t ZSTD_resetDStream(ZSTD_DStream* zds); `ZSTD_CCtx` object can be re-used (ZSTD_compressBegin()) to compress again.
-

Buffer-less streaming compression functions

ZSTDLIB_STATIC_API size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel);
+

Buffer-less streaming compression functions

ZSTD_DEPRECATED("The buffer-less API is deprecated in favor of the normal streaming API. See docs.")
+ZSTDLIB_STATIC_API size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel);
+ZSTD_DEPRECATED("The buffer-less API is deprecated in favor of the normal streaming API. See docs.")
 ZSTDLIB_STATIC_API size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel);
+ZSTD_DEPRECATED("The buffer-less API is deprecated in favor of the normal streaming API. See docs.")
 ZSTDLIB_STATIC_API size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); /**< note: fails if cdict==NULL */
 

size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned long long pledgedSrcSize); /**<  note: if pledgedSrcSize is not known, use ZSTD_CONTENTSIZE_UNKNOWN */
@@ -1993,36 +2070,25 @@ ZSTDLIB_STATIC_API size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const Z
   For skippable frames ZSTD_decompressContinue() always returns 0 : it only skips the content.
 
-

Buffer-less streaming decompression functions

typedef enum { ZSTD_frame, ZSTD_skippableFrame } ZSTD_frameType_e;
-typedef struct {
-    unsigned long long frameContentSize; /* if == ZSTD_CONTENTSIZE_UNKNOWN, it means this field is not available. 0 means "empty" */
-    unsigned long long windowSize;       /* can be very large, up to <= frameContentSize */
-    unsigned blockSizeMax;
-    ZSTD_frameType_e frameType;          /* if == ZSTD_skippableFrame, frameContentSize is the size of skippable content */
-    unsigned headerSize;
-    unsigned dictID;
-    unsigned checksumFlag;
-    unsigned _reserved1;
-    unsigned _reserved2;
-} ZSTD_frameHeader;
-

-
ZSTDLIB_STATIC_API size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize);   /**< doesn't consume input */
-/*! ZSTD_getFrameHeader_advanced() :
- *  same as ZSTD_getFrameHeader(),
- *  with added capability to select a format (like ZSTD_f_zstd1_magicless) */
-ZSTDLIB_STATIC_API size_t ZSTD_getFrameHeader_advanced(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize, ZSTD_format_e format);
-ZSTDLIB_STATIC_API size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long long frameContentSize);  /**< when frame content size is not known, pass in frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN */
-

decode Frame Header, or requires larger `srcSize`. - @return : 0, `zfhPtr` is correctly filled, - >0, `srcSize` is too small, value is wanted `srcSize` amount, - or an error code, which can be tested using ZSTD_isError() -


- +

Buffer-less streaming decompression functions


+
ZSTDLIB_STATIC_API size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long long frameContentSize);  /**< when frame content size is not known, pass in frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN */
+

typedef enum { ZSTDnit_frameHeader, ZSTDnit_blockHeader, ZSTDnit_block, ZSTDnit_lastBlock, ZSTDnit_checksum, ZSTDnit_skippableFrame } ZSTD_nextInputType_e;
 

-

Block level API


+

Block level API (DEPRECATED)


 
-

Frame metadata cost is typically ~12 bytes, which can be non-negligible for very small blocks (< 100 bytes). +

You can get the frame header down to 2 bytes by setting: + - ZSTD_c_format = ZSTD_f_zstd1_magicless + - ZSTD_c_contentSizeFlag = 0 + - ZSTD_c_checksumFlag = 0 + - ZSTD_c_dictIDFlag = 0 + + This API is not as well tested as our normal API, so we recommend not using it. + We will be removing it in a future version. If the normal API doesn't provide + the functionality you need, please open a GitHub issue. + + Block functions produce and decode raw zstd blocks, without frame metadata. + Frame metadata cost is typically ~12 bytes, which can be non-negligible for very small blocks (< 100 bytes). But users will have to take in charge needed metadata to regenerate data, such as compressed and content sizes. A few rules to respect : @@ -2046,36 +2112,14 @@ ZSTDLIB_STATIC_API size_t ZSTD_decodingBufferSize_min(unsigned long long windowS Use ZSTD_insertBlock() for such a case.


-

Raw zstd block functions

ZSTDLIB_STATIC_API size_t ZSTD_getBlockSize   (const ZSTD_CCtx* cctx);
+

Raw zstd block functions

ZSTD_DEPRECATED("The block API is deprecated in favor of the normal compression API. See docs.")
+ZSTDLIB_STATIC_API size_t ZSTD_getBlockSize   (const ZSTD_CCtx* cctx);
+ZSTD_DEPRECATED("The block API is deprecated in favor of the normal compression API. See docs.")
 ZSTDLIB_STATIC_API size_t ZSTD_compressBlock  (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
+ZSTD_DEPRECATED("The block API is deprecated in favor of the normal compression API. See docs.")
 ZSTDLIB_STATIC_API size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
+ZSTD_DEPRECATED("The block API is deprecated in favor of the normal compression API. See docs.")
 ZSTDLIB_STATIC_API size_t ZSTD_insertBlock    (ZSTD_DCtx* dctx, const void* blockStart, size_t blockSize);  /**< insert uncompressed block into `dctx` history. Useful for multi-blocks decompression. */
 

-
ZSTDLIB_STATIC_API void
-ZSTD_registerSequenceProducer(
-  ZSTD_CCtx* cctx,
-  void* sequenceProducerState,
-  ZSTD_sequenceProducer_F* sequenceProducer
-);
-

Instruct zstd to use a block-level external sequence producer function. - - The sequenceProducerState must be initialized by the caller, and the caller is - responsible for managing its lifetime. This parameter is sticky across - compressions. It will remain set until the user explicitly resets compression - parameters. - - Sequence producer registration is considered to be an "advanced parameter", - part of the "advanced API". This means it will only have an effect on compression - APIs which respect advanced parameters, such as compress2() and compressStream2(). - Older compression APIs such as compressCCtx(), which predate the introduction of - "advanced parameters", will ignore any external sequence producer setting. - - The sequence producer can be "cleared" by registering a NULL function pointer. This - removes all limitations described above in the "LIMITATIONS" section of the API docs. - - The user is strongly encouraged to read the full API documentation (above) before - calling this function. -


- diff --git a/lib/common/allocations.h b/lib/common/allocations.h new file mode 100644 index 000000000..a3153c4ba --- /dev/null +++ b/lib/common/allocations.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under both the BSD-style license (found in the + * LICENSE file in the root directory of this source tree) and the GPLv2 (found + * in the COPYING file in the root directory of this source tree). + * You may select, at your option, one of the above-listed licenses. + */ + +/* This file provides custom allocation primitives + */ + +#define ZSTD_DEPS_NEED_MALLOC +#include "zstd_deps.h" /* ZSTD_malloc, ZSTD_calloc, ZSTD_free, ZSTD_memset */ + +#include "mem.h" /* MEM_STATIC */ +#define ZSTD_STATIC_LINKING_ONLY +#include "../zstd.h" /* ZSTD_customMem */ + +#ifndef ZSTD_ALLOCATIONS_H +#define ZSTD_ALLOCATIONS_H + +/* custom memory allocation functions */ + +MEM_STATIC void* ZSTD_customMalloc(size_t size, ZSTD_customMem customMem) +{ + if (customMem.customAlloc) + return customMem.customAlloc(customMem.opaque, size); + return ZSTD_malloc(size); +} + +MEM_STATIC void* ZSTD_customCalloc(size_t size, ZSTD_customMem customMem) +{ + if (customMem.customAlloc) { + /* calloc implemented as malloc+memset; + * not as efficient as calloc, but next best guess for custom malloc */ + void* const ptr = customMem.customAlloc(customMem.opaque, size); + ZSTD_memset(ptr, 0, size); + return ptr; + } + return ZSTD_calloc(1, size); +} + +MEM_STATIC void ZSTD_customFree(void* ptr, ZSTD_customMem customMem) +{ + if (ptr!=NULL) { + if (customMem.customFree) + customMem.customFree(customMem.opaque, ptr); + else + ZSTD_free(ptr); + } +} + +#endif /* ZSTD_ALLOCATIONS_H */ diff --git a/lib/common/bits.h b/lib/common/bits.h index 7939f3d0f..def56c474 100644 --- a/lib/common/bits.h +++ b/lib/common/bits.h @@ -17,7 +17,7 @@ MEM_STATIC unsigned ZSTD_countTrailingZeros32_fallback(U32 val) { assert(val != 0); { - static const int DeBruijnBytePos[32] = {0, 1, 28, 2, 29, 14, 24, 3, + static const U32 DeBruijnBytePos[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}; @@ -30,7 +30,7 @@ MEM_STATIC unsigned ZSTD_countTrailingZeros32(U32 val) assert(val != 0); # if defined(_MSC_VER) # if STATIC_BMI2 == 1 - return _tzcnt_u32(val); + return (unsigned)_tzcnt_u32(val); # else if (val != 0) { unsigned long r; @@ -69,7 +69,7 @@ MEM_STATIC unsigned ZSTD_countLeadingZeros32(U32 val) assert(val != 0); # if defined(_MSC_VER) # if STATIC_BMI2 == 1 - return _lzcnt_u32(val); + return (unsigned)_lzcnt_u32(val); # else if (val != 0) { unsigned long r; @@ -92,7 +92,7 @@ MEM_STATIC unsigned ZSTD_countTrailingZeros64(U64 val) assert(val != 0); # if defined(_MSC_VER) && defined(_WIN64) # if STATIC_BMI2 == 1 - return _tzcnt_u64(val); + return (unsigned)_tzcnt_u64(val); # else if (val != 0) { unsigned long r; @@ -123,7 +123,7 @@ MEM_STATIC unsigned ZSTD_countLeadingZeros64(U64 val) assert(val != 0); # if defined(_MSC_VER) && defined(_WIN64) # if STATIC_BMI2 == 1 - return _lzcnt_u64(val); + return (unsigned)_lzcnt_u64(val); # else if (val != 0) { unsigned long r; @@ -172,4 +172,29 @@ MEM_STATIC unsigned ZSTD_highbit32(U32 val) /* compress, dictBuilder, decodeCo return 31 - ZSTD_countLeadingZeros32(val); } +/* ZSTD_rotateRight_*(): + * Rotates a bitfield to the right by "count" bits. + * https://en.wikipedia.org/w/index.php?title=Circular_shift&oldid=991635599#Implementing_circular_shifts + */ +MEM_STATIC +U64 ZSTD_rotateRight_U64(U64 const value, U32 count) { + assert(count < 64); + count &= 0x3F; /* for fickle pattern recognition */ + return (value >> count) | (U64)(value << ((0U - count) & 0x3F)); +} + +MEM_STATIC +U32 ZSTD_rotateRight_U32(U32 const value, U32 count) { + assert(count < 32); + count &= 0x1F; /* for fickle pattern recognition */ + return (value >> count) | (U32)(value << ((0U - count) & 0x1F)); +} + +MEM_STATIC +U16 ZSTD_rotateRight_U16(U16 const value, U32 count) { + assert(count < 16); + count &= 0x0F; /* for fickle pattern recognition */ + return (value >> count) | (U16)(value << ((0U - count) & 0x0F)); +} + #endif /* ZSTD_BITS_H */ diff --git a/lib/common/bitstream.h b/lib/common/bitstream.h index db1b4cf13..72b0b3df2 100644 --- a/lib/common/bitstream.h +++ b/lib/common/bitstream.h @@ -396,7 +396,7 @@ MEM_STATIC BIT_DStream_status BIT_reloadDStreamFast(BIT_DStream_t* bitD) * This function is safe, it guarantees it will not read beyond src buffer. * @return : status of `BIT_DStream_t` internal register. * when status == BIT_DStream_unfinished, internal register is filled with at least 25 or 57 bits */ -MEM_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD) +MEM_STATIC FORCE_INLINE_ATTR BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD) { if (bitD->bitsConsumed > (sizeof(bitD->bitContainer)*8)) /* overflow detected, like end of stream */ return BIT_DStream_overflow; diff --git a/lib/common/compiler.h b/lib/common/compiler.h index d4f2f285d..73f8d0199 100644 --- a/lib/common/compiler.h +++ b/lib/common/compiler.h @@ -311,6 +311,10 @@ void __msan_poison(const volatile void *a, size_t size); /* Returns the offset of the first (at least partially) poisoned byte in the memory range, or -1 if the whole range is good. */ intptr_t __msan_test_shadow(const volatile void *x, size_t size); + +/* Print shadow and origin for the memory range to stderr in a human-readable + format. */ +void __msan_print_shadow(const volatile void *x, size_t size); #endif #if ZSTD_ADDRESS_SANITIZER && !defined(ZSTD_ASAN_DONT_POISON_WORKSPACE) diff --git a/lib/common/pool.c b/lib/common/pool.c index f3d9d0854..d5ca5a780 100644 --- a/lib/common/pool.c +++ b/lib/common/pool.c @@ -10,9 +10,9 @@ /* ====== Dependencies ======= */ +#include "../common/allocations.h" /* ZSTD_customCalloc, ZSTD_customFree */ #include "zstd_deps.h" /* size_t */ #include "debug.h" /* assert */ -#include "zstd_internal.h" /* ZSTD_customCalloc, ZSTD_customFree */ #include "pool.h" /* ====== Compiler specifics ====== */ diff --git a/lib/common/threading.c b/lib/common/threading.c index f2341105a..ca155b9b9 100644 --- a/lib/common/threading.c +++ b/lib/common/threading.c @@ -47,7 +47,7 @@ static unsigned __stdcall worker(void *arg) void* (*start_routine)(void*); void* thread_arg; - /* Inialized thread_arg and start_routine and signal main thread that we don't need it + /* Initialized thread_arg and start_routine and signal main thread that we don't need it * to wait any longer. */ { diff --git a/lib/common/zstd_common.c b/lib/common/zstd_common.c index 320855247..3f04c22ab 100644 --- a/lib/common/zstd_common.c +++ b/lib/common/zstd_common.c @@ -14,7 +14,6 @@ * Dependencies ***************************************/ #define ZSTD_DEPS_NEED_MALLOC -#include "zstd_deps.h" /* ZSTD_malloc, ZSTD_calloc, ZSTD_free, ZSTD_memset */ #include "error_private.h" #include "zstd_internal.h" @@ -47,37 +46,3 @@ ZSTD_ErrorCode ZSTD_getErrorCode(size_t code) { return ERR_getErrorCode(code); } /*! ZSTD_getErrorString() : * provides error code string from enum */ const char* ZSTD_getErrorString(ZSTD_ErrorCode code) { return ERR_getErrorString(code); } - - - -/*=************************************************************** -* Custom allocator -****************************************************************/ -void* ZSTD_customMalloc(size_t size, ZSTD_customMem customMem) -{ - if (customMem.customAlloc) - return customMem.customAlloc(customMem.opaque, size); - return ZSTD_malloc(size); -} - -void* ZSTD_customCalloc(size_t size, ZSTD_customMem customMem) -{ - if (customMem.customAlloc) { - /* calloc implemented as malloc+memset; - * not as efficient as calloc, but next best guess for custom malloc */ - void* const ptr = customMem.customAlloc(customMem.opaque, size); - ZSTD_memset(ptr, 0, size); - return ptr; - } - return ZSTD_calloc(1, size); -} - -void ZSTD_customFree(void* ptr, ZSTD_customMem customMem) -{ - if (ptr!=NULL) { - if (customMem.customFree) - customMem.customFree(customMem.opaque, ptr); - else - ZSTD_free(ptr); - } -} diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h index 37836dc70..1f942f27b 100644 --- a/lib/common/zstd_internal.h +++ b/lib/common/zstd_internal.h @@ -350,11 +350,6 @@ typedef struct { const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx); /* compress & dictBuilder */ int ZSTD_seqToCodes(const seqStore_t* seqStorePtr); /* compress, dictBuilder, decodeCorpus (shouldn't get its definition from here) */ -/* custom memory allocation functions */ -void* ZSTD_customMalloc(size_t size, ZSTD_customMem customMem); -void* ZSTD_customCalloc(size_t size, ZSTD_customMem customMem); -void ZSTD_customFree(void* ptr, ZSTD_customMem customMem); - /* ZSTD_invalidateRepCodes() : * ensures next compression will not use repcodes from previous block. diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index b55f684cd..d91f80060 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -11,6 +11,7 @@ /*-************************************* * Dependencies ***************************************/ +#include "../common/allocations.h" /* ZSTD_customMalloc, ZSTD_customCalloc, ZSTD_customFree */ #include "../common/zstd_deps.h" /* INT_MAX, ZSTD_memset, ZSTD_memcpy */ #include "../common/mem.h" #include "hist.h" /* HIST_countFast_wksp */ @@ -26,7 +27,7 @@ #include "zstd_opt.h" #include "zstd_ldm.h" #include "zstd_compress_superblock.h" -#include "../common/bits.h" /* ZSTD_highbit32 */ +#include "../common/bits.h" /* ZSTD_highbit32, ZSTD_rotateRight_U64 */ /* *************************************************************** * Tuning parameters @@ -1177,16 +1178,39 @@ size_t ZSTD_CCtx_setParametersUsingCCtxParams( size_t ZSTD_CCtx_setCParams(ZSTD_CCtx* cctx, ZSTD_compressionParameters cparams) { + ZSTD_STATIC_ASSERT(sizeof(cparams) == 7 * 4 /* all params are listed below */); DEBUGLOG(4, "ZSTD_CCtx_setCParams"); - assert(cctx != NULL); - if (cctx->streamStage != zcss_init) { - /* All parameters in @cparams are allowed to be updated during MT compression. - * This must be signaled, so that MT compression picks up the changes */ - cctx->cParamsChanged = 1; - } - /* only update if parameters are valid */ + /* only update if all parameters are valid */ FORWARD_IF_ERROR(ZSTD_checkCParams(cparams), ""); - cctx->requestedParams.cParams = cparams; + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_windowLog, cparams.windowLog), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_chainLog, cparams.chainLog), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_hashLog, cparams.hashLog), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_searchLog, cparams.searchLog), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_minMatch, cparams.minMatch), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_targetLength, cparams.targetLength), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_strategy, cparams.strategy), ""); + return 0; +} + +size_t ZSTD_CCtx_setFParams(ZSTD_CCtx* cctx, ZSTD_frameParameters fparams) +{ + ZSTD_STATIC_ASSERT(sizeof(fparams) == 3 * 4 /* all params are listed below */); + DEBUGLOG(4, "ZSTD_CCtx_setFParams"); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_contentSizeFlag, fparams.contentSizeFlag != 0), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, fparams.checksumFlag != 0), ""); + FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_dictIDFlag, fparams.noDictIDFlag == 0), ""); + return 0; +} + +size_t ZSTD_CCtx_setParams(ZSTD_CCtx* cctx, ZSTD_parameters params) +{ + DEBUGLOG(4, "ZSTD_CCtx_setParams"); + /* First check cParams, because we want to update all or none. */ + FORWARD_IF_ERROR(ZSTD_checkCParams(params.cParams), ""); + /* Next set fParams, because this could fail if the cctx isn't in init stage. */ + FORWARD_IF_ERROR(ZSTD_CCtx_setFParams(cctx, params.fParams), ""); + /* Finally set cParams, which should succeed. */ + FORWARD_IF_ERROR(ZSTD_CCtx_setCParams(cctx, params.cParams), ""); return 0; } @@ -1592,7 +1616,7 @@ ZSTD_sizeof_matchState(const ZSTD_compressionParameters* const cParams, + ZSTD_cwksp_aligned_alloc_size((ZSTD_OPT_NUM+1) * sizeof(ZSTD_match_t)) + ZSTD_cwksp_aligned_alloc_size((ZSTD_OPT_NUM+1) * sizeof(ZSTD_optimal_t)); size_t const lazyAdditionalSpace = ZSTD_rowMatchFinderUsed(cParams->strategy, useRowMatchFinder) - ? ZSTD_cwksp_aligned_alloc_size(hSize*sizeof(U16)) + ? ZSTD_cwksp_aligned_alloc_size(hSize) : 0; size_t const optSpace = (forCCtx && (cParams->strategy >= ZSTD_btopt)) ? optPotentialSpace @@ -1883,6 +1907,19 @@ typedef enum { ZSTD_resetTarget_CCtx } ZSTD_resetTarget_e; +/* Mixes bits in a 64 bits in a value, based on XXH3_rrmxmx */ +static U64 ZSTD_bitmix(U64 val, U64 len) { + val ^= ZSTD_rotateRight_U64(val, 49) ^ ZSTD_rotateRight_U64(val, 24); + val *= 0x9FB21C651E98DF25ULL; + val ^= (val >> 35) + len ; + val *= 0x9FB21C651E98DF25ULL; + return val ^ (val >> 28); +} + +/* Mixes in the hashSalt and hashSaltEntropy to create a new hashSalt */ +static void ZSTD_advanceHashSalt(ZSTD_matchState_t* ms) { + ms->hashSalt = ZSTD_bitmix(ms->hashSalt, 8) ^ ZSTD_bitmix((U64) ms->hashSaltEntropy, 4); +} static size_t ZSTD_reset_matchState(ZSTD_matchState_t* ms, @@ -1910,6 +1947,7 @@ ZSTD_reset_matchState(ZSTD_matchState_t* ms, } ms->hashLog3 = hashLog3; + ms->lazySkipping = 0; ZSTD_invalidateMatchState(ms); @@ -1931,6 +1969,27 @@ ZSTD_reset_matchState(ZSTD_matchState_t* ms, ZSTD_cwksp_clean_tables(ws); } + if (ZSTD_rowMatchFinderUsed(cParams->strategy, useRowMatchFinder)) { + /* Row match finder needs an additional table of hashes ("tags") */ + size_t const tagTableSize = hSize; + /* We want to generate a new salt in case we reset a Cctx, but we always want to use + * 0 when we reset a Cdict */ + if(forWho == ZSTD_resetTarget_CCtx) { + ms->tagTable = (BYTE*) ZSTD_cwksp_reserve_aligned_init_once(ws, tagTableSize); + ZSTD_advanceHashSalt(ms); + } else { + /* When we are not salting we want to always memset the memory */ + ms->tagTable = (BYTE*) ZSTD_cwksp_reserve_aligned(ws, tagTableSize); + ZSTD_memset(ms->tagTable, 0, tagTableSize); + ms->hashSalt = 0; + } + { /* Switch to 32-entry rows if searchLog is 5 (or more) */ + U32 const rowLog = BOUNDED(4, cParams->searchLog, 6); + assert(cParams->hashLog >= rowLog); + ms->rowHashLog = cParams->hashLog - rowLog; + } + } + /* opt parser space */ if ((forWho == ZSTD_resetTarget_CCtx) && (cParams->strategy >= ZSTD_btopt)) { DEBUGLOG(4, "reserving optimal parser space"); @@ -1942,19 +2001,6 @@ ZSTD_reset_matchState(ZSTD_matchState_t* ms, ms->opt.priceTable = (ZSTD_optimal_t*)ZSTD_cwksp_reserve_aligned(ws, (ZSTD_OPT_NUM+1) * sizeof(ZSTD_optimal_t)); } - if (ZSTD_rowMatchFinderUsed(cParams->strategy, useRowMatchFinder)) { - { /* Row match finder needs an additional table of hashes ("tags") */ - size_t const tagTableSize = hSize*sizeof(U16); - ms->tagTable = (U16*)ZSTD_cwksp_reserve_aligned(ws, tagTableSize); - if (ms->tagTable) ZSTD_memset(ms->tagTable, 0, tagTableSize); - } - { /* Switch to 32-entry rows if searchLog is 5 (or more) */ - U32 const rowLog = BOUNDED(4, cParams->searchLog, 6); - assert(cParams->hashLog >= rowLog); - ms->rowHashLog = cParams->hashLog - rowLog; - } - } - ms->cParams = *cParams; RETURN_ERROR_IF(ZSTD_cwksp_reserve_failed(ws), memory_allocation, @@ -2101,45 +2147,16 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, ZSTD_reset_compressedBlockState(zc->blockState.prevCBlock); - /* ZSTD_wildcopy() is used to copy into the literals buffer, - * so we have to oversize the buffer by WILDCOPY_OVERLENGTH bytes. - */ - zc->seqStore.litStart = ZSTD_cwksp_reserve_buffer(ws, blockSize + WILDCOPY_OVERLENGTH); - zc->seqStore.maxNbLit = blockSize; - - /* buffers */ - zc->bufferedPolicy = zbuff; - zc->inBuffSize = buffInSize; - zc->inBuff = (char*)ZSTD_cwksp_reserve_buffer(ws, buffInSize); - zc->outBuffSize = buffOutSize; - zc->outBuff = (char*)ZSTD_cwksp_reserve_buffer(ws, buffOutSize); - - /* ldm bucketOffsets table */ - if (params->ldmParams.enableLdm == ZSTD_ps_enable) { - /* TODO: avoid memset? */ - size_t const numBuckets = - ((size_t)1) << (params->ldmParams.hashLog - - params->ldmParams.bucketSizeLog); - zc->ldmState.bucketOffsets = ZSTD_cwksp_reserve_buffer(ws, numBuckets); - ZSTD_memset(zc->ldmState.bucketOffsets, 0, numBuckets); - } - - /* sequences storage */ - ZSTD_referenceExternalSequences(zc, NULL, 0); - zc->seqStore.maxNbSeq = maxNbSeq; - zc->seqStore.llCode = ZSTD_cwksp_reserve_buffer(ws, maxNbSeq * sizeof(BYTE)); - zc->seqStore.mlCode = ZSTD_cwksp_reserve_buffer(ws, maxNbSeq * sizeof(BYTE)); - zc->seqStore.ofCode = ZSTD_cwksp_reserve_buffer(ws, maxNbSeq * sizeof(BYTE)); - zc->seqStore.sequencesStart = (seqDef*)ZSTD_cwksp_reserve_aligned(ws, maxNbSeq * sizeof(seqDef)); - FORWARD_IF_ERROR(ZSTD_reset_matchState( - &zc->blockState.matchState, - ws, - ¶ms->cParams, - params->useRowMatchFinder, - crp, - needsIndexReset, - ZSTD_resetTarget_CCtx), ""); + &zc->blockState.matchState, + ws, + ¶ms->cParams, + params->useRowMatchFinder, + crp, + needsIndexReset, + ZSTD_resetTarget_CCtx), ""); + + zc->seqStore.sequencesStart = (seqDef*)ZSTD_cwksp_reserve_aligned(ws, maxNbSeq * sizeof(seqDef)); /* ldm hash table */ if (params->ldmParams.enableLdm == ZSTD_ps_enable) { @@ -2162,8 +2179,39 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, (ZSTD_Sequence*)ZSTD_cwksp_reserve_aligned(ws, maxNbExternalSeq * sizeof(ZSTD_Sequence)); } + /* buffers */ + + /* ZSTD_wildcopy() is used to copy into the literals buffer, + * so we have to oversize the buffer by WILDCOPY_OVERLENGTH bytes. + */ + zc->seqStore.litStart = ZSTD_cwksp_reserve_buffer(ws, blockSize + WILDCOPY_OVERLENGTH); + zc->seqStore.maxNbLit = blockSize; + + zc->bufferedPolicy = zbuff; + zc->inBuffSize = buffInSize; + zc->inBuff = (char*)ZSTD_cwksp_reserve_buffer(ws, buffInSize); + zc->outBuffSize = buffOutSize; + zc->outBuff = (char*)ZSTD_cwksp_reserve_buffer(ws, buffOutSize); + + /* ldm bucketOffsets table */ + if (params->ldmParams.enableLdm == ZSTD_ps_enable) { + /* TODO: avoid memset? */ + size_t const numBuckets = + ((size_t)1) << (params->ldmParams.hashLog - + params->ldmParams.bucketSizeLog); + zc->ldmState.bucketOffsets = ZSTD_cwksp_reserve_buffer(ws, numBuckets); + ZSTD_memset(zc->ldmState.bucketOffsets, 0, numBuckets); + } + + /* sequences storage */ + ZSTD_referenceExternalSequences(zc, NULL, 0); + zc->seqStore.maxNbSeq = maxNbSeq; + zc->seqStore.llCode = ZSTD_cwksp_reserve_buffer(ws, maxNbSeq * sizeof(BYTE)); + zc->seqStore.mlCode = ZSTD_cwksp_reserve_buffer(ws, maxNbSeq * sizeof(BYTE)); + zc->seqStore.ofCode = ZSTD_cwksp_reserve_buffer(ws, maxNbSeq * sizeof(BYTE)); + DEBUGLOG(3, "wksp: finished allocating, %zd bytes remain available", ZSTD_cwksp_available_space(ws)); - assert(ZSTD_cwksp_estimated_space_within_bounds(ws, neededSpace, resizeWorkspace)); + assert(ZSTD_cwksp_estimated_space_within_bounds(ws, neededSpace)); zc->initialized = 1; @@ -2338,10 +2386,11 @@ static size_t ZSTD_resetCCtx_byCopyingCDict(ZSTD_CCtx* cctx, } /* copy tag table */ if (ZSTD_rowMatchFinderUsed(cdict_cParams->strategy, cdict->useRowMatchFinder)) { - size_t const tagTableSize = hSize*sizeof(U16); + size_t const tagTableSize = hSize; ZSTD_memcpy(cctx->blockState.matchState.tagTable, - cdict->matchState.tagTable, - tagTableSize); + cdict->matchState.tagTable, + tagTableSize); + cctx->blockState.matchState.hashSalt = cdict->matchState.hashSalt; } } @@ -3858,9 +3907,10 @@ ZSTD_seqStore_resolveOffCodes(repcodes_t* const dRepcodes, repcodes_t* const cRe const seqStore_t* const seqStore, U32 const nbSeq) { U32 idx = 0; + U32 const longLitLenIdx = seqStore->longLengthType == ZSTD_llt_literalLength ? seqStore->longLengthPos : nbSeq; for (; idx < nbSeq; ++idx) { seqDef* const seq = seqStore->sequencesStart + idx; - U32 const ll0 = (seq->litLength == 0); + U32 const ll0 = (seq->litLength == 0) && (idx != longLitLenIdx); U32 const offBase = seq->offBase; assert(offBase > 0); if (OFFBASE_IS_REPCODE(offBase)) { @@ -4576,31 +4626,51 @@ static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx, } } -size_t ZSTD_compressContinue (ZSTD_CCtx* cctx, - void* dst, size_t dstCapacity, - const void* src, size_t srcSize) +size_t ZSTD_compressContinue_public(ZSTD_CCtx* cctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize) { DEBUGLOG(5, "ZSTD_compressContinue (srcSize=%u)", (unsigned)srcSize); return ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 1 /* frame mode */, 0 /* last chunk */); } +/* NOTE: Must just wrap ZSTD_compressContinue_public() */ +size_t ZSTD_compressContinue(ZSTD_CCtx* cctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize) +{ + return ZSTD_compressContinue_public(cctx, dst, dstCapacity, src, srcSize); +} -size_t ZSTD_getBlockSize(const ZSTD_CCtx* cctx) +static size_t ZSTD_getBlockSize_deprecated(const ZSTD_CCtx* cctx) { ZSTD_compressionParameters const cParams = cctx->appliedParams.cParams; assert(!ZSTD_checkCParams(cParams)); return MIN(cctx->appliedParams.maxBlockSize, (size_t)1 << cParams.windowLog); } -size_t ZSTD_compressBlock(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize) +/* NOTE: Must just wrap ZSTD_getBlockSize_deprecated() */ +size_t ZSTD_getBlockSize(const ZSTD_CCtx* cctx) +{ + return ZSTD_getBlockSize_deprecated(cctx); +} + +/* NOTE: Must just wrap ZSTD_compressBlock_deprecated() */ +size_t ZSTD_compressBlock_deprecated(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize) { DEBUGLOG(5, "ZSTD_compressBlock: srcSize = %u", (unsigned)srcSize); - { size_t const blockSizeMax = ZSTD_getBlockSize(cctx); + { size_t const blockSizeMax = ZSTD_getBlockSize_deprecated(cctx); RETURN_ERROR_IF(srcSize > blockSizeMax, srcSize_wrong, "input is larger than a block"); } return ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 0 /* frame mode */, 0 /* last chunk */); } +/* NOTE: Must just wrap ZSTD_compressBlock_deprecated() */ +size_t ZSTD_compressBlock(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize) +{ + return ZSTD_compressBlock_deprecated(cctx, dst, dstCapacity, src, srcSize); +} + /*! ZSTD_loadDictionaryContent() : * @return : 0, or an error code */ @@ -4644,31 +4714,42 @@ static size_t ZSTD_loadDictionaryContent(ZSTD_matchState_t* ms, ip = iend - maxDictSize; src = ip; srcSize = maxDictSize; - } } + } + } if (srcSize > ZSTD_CHUNKSIZE_MAX) { /* We must have cleared our windows when our source is this large. */ assert(ZSTD_window_isEmpty(ms->window)); if (loadLdmDict) assert(ZSTD_window_isEmpty(ls->window)); } + ZSTD_window_update(&ms->window, src, srcSize, /* forceNonContiguous */ 0); DEBUGLOG(4, "ZSTD_loadDictionaryContent(): useRowMatchFinder=%d", (int)params->useRowMatchFinder); - ZSTD_window_update(&ms->window, src, srcSize, /* forceNonContiguous */ 0); - ms->loadedDictEnd = params->forceWindow ? 0 : (U32)(iend - ms->window.base); - ms->forceNonContiguous = params->deterministicRefPrefix; - if (loadLdmDict) { + if (loadLdmDict) { /* Load the entire dict into LDM matchfinders. */ ZSTD_window_update(&ls->window, src, srcSize, /* forceNonContiguous */ 0); ls->loadedDictEnd = params->forceWindow ? 0 : (U32)(iend - ls->window.base); + ZSTD_ldm_fillHashTable(ls, ip, iend, ¶ms->ldmParams); } + /* If the dict is larger than we can reasonably index in our tables, only load the suffix. */ + if (params->cParams.strategy < ZSTD_btultra) { + U32 maxDictSize = 8U << MIN(MAX(params->cParams.hashLog, params->cParams.chainLog), 28); + if (srcSize > maxDictSize) { + ip = iend - maxDictSize; + src = ip; + srcSize = maxDictSize; + } + } + + ms->nextToUpdate = (U32)(ip - ms->window.base); + ms->loadedDictEnd = params->forceWindow ? 0 : (U32)(iend - ms->window.base); + ms->forceNonContiguous = params->deterministicRefPrefix; + if (srcSize <= HASH_READ_SIZE) return 0; ZSTD_overflowCorrectIfNeeded(ms, ws, params, ip, iend); - if (loadLdmDict) - ZSTD_ldm_fillHashTable(ls, ip, iend, ¶ms->ldmParams); - switch(params->cParams.strategy) { case ZSTD_fast: @@ -4688,7 +4769,7 @@ static size_t ZSTD_loadDictionaryContent(ZSTD_matchState_t* ms, } else { assert(params->useRowMatchFinder != ZSTD_ps_auto); if (params->useRowMatchFinder == ZSTD_ps_enable) { - size_t const tagTableSize = ((size_t)1 << params->cParams.hashLog) * sizeof(U16); + size_t const tagTableSize = ((size_t)1 << params->cParams.hashLog); ZSTD_memset(ms->tagTable, 0, tagTableSize); ZSTD_row_update(ms, iend-HASH_READ_SIZE); DEBUGLOG(4, "Using row-based hash table for lazy dict"); @@ -4991,8 +5072,8 @@ size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx, &cctxParams, pledgedSrcSize); } -size_t -ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel) +static size_t +ZSTD_compressBegin_usingDict_deprecated(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel) { ZSTD_CCtx_params cctxParams; { ZSTD_parameters const params = ZSTD_getParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize, ZSTD_cpm_noAttachDict); @@ -5003,9 +5084,15 @@ ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, &cctxParams, ZSTD_CONTENTSIZE_UNKNOWN, ZSTDb_not_buffered); } +size_t +ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel) +{ + return ZSTD_compressBegin_usingDict_deprecated(cctx, dict, dictSize, compressionLevel); +} + size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel) { - return ZSTD_compressBegin_usingDict(cctx, NULL, 0, compressionLevel); + return ZSTD_compressBegin_usingDict_deprecated(cctx, NULL, 0, compressionLevel); } @@ -5075,9 +5162,9 @@ void ZSTD_CCtx_trace(ZSTD_CCtx* cctx, size_t extraCSize) #endif } -size_t ZSTD_compressEnd (ZSTD_CCtx* cctx, - void* dst, size_t dstCapacity, - const void* src, size_t srcSize) +size_t ZSTD_compressEnd_public(ZSTD_CCtx* cctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize) { size_t endResult; size_t const cSize = ZSTD_compressContinue_internal(cctx, @@ -5101,6 +5188,14 @@ size_t ZSTD_compressEnd (ZSTD_CCtx* cctx, return cSize + endResult; } +/* NOTE: Must just wrap ZSTD_compressEnd_public() */ +size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize) +{ + return ZSTD_compressEnd_public(cctx, dst, dstCapacity, src, srcSize); +} + size_t ZSTD_compress_advanced (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, @@ -5129,7 +5224,7 @@ size_t ZSTD_compress_advanced_internal( FORWARD_IF_ERROR( ZSTD_compressBegin_internal(cctx, dict, dictSize, ZSTD_dct_auto, ZSTD_dtlm_fast, NULL, params, srcSize, ZSTDb_not_buffered) , ""); - return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize); + return ZSTD_compressEnd_public(cctx, dst, dstCapacity, src, srcSize); } size_t ZSTD_compress_usingDict(ZSTD_CCtx* cctx, @@ -5451,6 +5546,7 @@ const ZSTD_CDict* ZSTD_initStaticCDict( params.cParams = cParams; params.useRowMatchFinder = useRowMatchFinder; cdict->useRowMatchFinder = useRowMatchFinder; + cdict->compressionLevel = ZSTD_NO_CLEVEL; if (ZSTD_isError( ZSTD_initCDict_internal(cdict, dict, dictSize, @@ -5530,12 +5626,17 @@ size_t ZSTD_compressBegin_usingCDict_advanced( /* ZSTD_compressBegin_usingCDict() : * cdict must be != NULL */ -size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict) +size_t ZSTD_compressBegin_usingCDict_deprecated(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict) { ZSTD_frameParameters const fParams = { 0 /*content*/, 0 /*checksum*/, 0 /*noDictID*/ }; return ZSTD_compressBegin_usingCDict_internal(cctx, cdict, fParams, ZSTD_CONTENTSIZE_UNKNOWN); } +size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict) +{ + return ZSTD_compressBegin_usingCDict_deprecated(cctx, cdict); +} + /*! ZSTD_compress_usingCDict_internal(): * Implementation of various ZSTD_compress_usingCDict* functions. */ @@ -5545,7 +5646,7 @@ static size_t ZSTD_compress_usingCDict_internal(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict, ZSTD_frameParameters fParams) { FORWARD_IF_ERROR(ZSTD_compressBegin_usingCDict_internal(cctx, cdict, fParams, srcSize), ""); /* will check if cdict != NULL */ - return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize); + return ZSTD_compressEnd_public(cctx, dst, dstCapacity, src, srcSize); } /*! ZSTD_compress_usingCDict_advanced(): @@ -5803,7 +5904,7 @@ static size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs, || zcs->appliedParams.outBufferMode == ZSTD_bm_stable) /* OR we are allowed to return dstSizeTooSmall */ && (zcs->inBuffPos == 0) ) { /* shortcut to compression pass directly into output buffer */ - size_t const cSize = ZSTD_compressEnd(zcs, + size_t const cSize = ZSTD_compressEnd_public(zcs, op, oend-op, ip, iend-ip); DEBUGLOG(4, "ZSTD_compressEnd : cSize=%u", (unsigned)cSize); FORWARD_IF_ERROR(cSize, "ZSTD_compressEnd failed"); @@ -5861,9 +5962,9 @@ static size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs, if (inputBuffered) { unsigned const lastBlock = (flushMode == ZSTD_e_end) && (ip==iend); cSize = lastBlock ? - ZSTD_compressEnd(zcs, cDst, oSize, + ZSTD_compressEnd_public(zcs, cDst, oSize, zcs->inBuff + zcs->inToCompress, iSize) : - ZSTD_compressContinue(zcs, cDst, oSize, + ZSTD_compressContinue_public(zcs, cDst, oSize, zcs->inBuff + zcs->inToCompress, iSize); FORWARD_IF_ERROR(cSize, "%s", lastBlock ? "ZSTD_compressEnd failed" : "ZSTD_compressContinue failed"); zcs->frameEnded = lastBlock; @@ -5879,8 +5980,8 @@ static size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs, } else { /* !inputBuffered, hence ZSTD_bm_stable */ unsigned const lastBlock = (flushMode == ZSTD_e_end) && (ip + iSize == iend); cSize = lastBlock ? - ZSTD_compressEnd(zcs, cDst, oSize, ip, iSize) : - ZSTD_compressContinue(zcs, cDst, oSize, ip, iSize); + ZSTD_compressEnd_public(zcs, cDst, oSize, ip, iSize) : + ZSTD_compressContinue_public(zcs, cDst, oSize, ip, iSize); /* Consume the input prior to error checking to mirror buffered mode. */ if (ip) ip += iSize; FORWARD_IF_ERROR(cSize, "%s", lastBlock ? "ZSTD_compressEnd failed" : "ZSTD_compressContinue failed"); diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index cbb85e527..10f68d010 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -226,8 +226,10 @@ struct ZSTD_matchState_t { U32 hashLog3; /* dispatch table for matches of len==3 : larger == faster, more memory */ U32 rowHashLog; /* For row-based matchfinder: Hashlog based on nb of rows in the hashTable.*/ - U16* tagTable; /* For row-based matchFinder: A row-based table containing the hashes and head index. */ + BYTE* tagTable; /* For row-based matchFinder: A row-based table containing the hashes and head index. */ U32 hashCache[ZSTD_ROW_HASH_CACHE_SIZE]; /* For row-based matchFinder: a cache of hashes to improve speed */ + U64 hashSalt; /* For row-based matchFinder: salts the hash for re-use of tag table */ + U32 hashSaltEntropy; /* For row-based matchFinder: collects entropy for salt generation */ U32* hashTable; U32* hashTable3; @@ -247,6 +249,13 @@ struct ZSTD_matchState_t { * This behavior is controlled from the cctx ms. * This parameter has no effect in the cdict ms. */ int prefetchCDictTables; + + /* When == 0, lazy match finders insert every position. + * When != 0, lazy match finders only insert positions they search. + * This allows them to skip much faster over incompressible data, + * at a small cost to compression ratio. + */ + int lazySkipping; }; typedef struct { @@ -787,28 +796,35 @@ ZSTD_count_2segments(const BYTE* ip, const BYTE* match, * Hashes ***************************************/ static const U32 prime3bytes = 506832829U; -static U32 ZSTD_hash3(U32 u, U32 h) { assert(h <= 32); return ((u << (32-24)) * prime3bytes) >> (32-h) ; } -MEM_STATIC size_t ZSTD_hash3Ptr(const void* ptr, U32 h) { return ZSTD_hash3(MEM_readLE32(ptr), h); } /* only in zstd_opt.h */ +static U32 ZSTD_hash3(U32 u, U32 h, U32 s) { assert(h <= 32); return (((u << (32-24)) * prime3bytes) ^ s) >> (32-h) ; } +MEM_STATIC size_t ZSTD_hash3Ptr(const void* ptr, U32 h) { return ZSTD_hash3(MEM_readLE32(ptr), h, 0); } /* only in zstd_opt.h */ +MEM_STATIC size_t ZSTD_hash3PtrS(const void* ptr, U32 h, U32 s) { return ZSTD_hash3(MEM_readLE32(ptr), h, s); } static const U32 prime4bytes = 2654435761U; -static U32 ZSTD_hash4(U32 u, U32 h) { assert(h <= 32); return (u * prime4bytes) >> (32-h) ; } -static size_t ZSTD_hash4Ptr(const void* ptr, U32 h) { return ZSTD_hash4(MEM_readLE32(ptr), h); } +static U32 ZSTD_hash4(U32 u, U32 h, U32 s) { assert(h <= 32); return ((u * prime4bytes) ^ s) >> (32-h) ; } +static size_t ZSTD_hash4Ptr(const void* ptr, U32 h) { return ZSTD_hash4(MEM_readLE32(ptr), h, 0); } +static size_t ZSTD_hash4PtrS(const void* ptr, U32 h, U32 s) { return ZSTD_hash4(MEM_readLE32(ptr), h, s); } static const U64 prime5bytes = 889523592379ULL; -static size_t ZSTD_hash5(U64 u, U32 h) { assert(h <= 64); return (size_t)(((u << (64-40)) * prime5bytes) >> (64-h)) ; } -static size_t ZSTD_hash5Ptr(const void* p, U32 h) { return ZSTD_hash5(MEM_readLE64(p), h); } +static size_t ZSTD_hash5(U64 u, U32 h, U64 s) { assert(h <= 64); return (size_t)((((u << (64-40)) * prime5bytes) ^ s) >> (64-h)) ; } +static size_t ZSTD_hash5Ptr(const void* p, U32 h) { return ZSTD_hash5(MEM_readLE64(p), h, 0); } +static size_t ZSTD_hash5PtrS(const void* p, U32 h, U64 s) { return ZSTD_hash5(MEM_readLE64(p), h, s); } static const U64 prime6bytes = 227718039650203ULL; -static size_t ZSTD_hash6(U64 u, U32 h) { assert(h <= 64); return (size_t)(((u << (64-48)) * prime6bytes) >> (64-h)) ; } -static size_t ZSTD_hash6Ptr(const void* p, U32 h) { return ZSTD_hash6(MEM_readLE64(p), h); } +static size_t ZSTD_hash6(U64 u, U32 h, U64 s) { assert(h <= 64); return (size_t)((((u << (64-48)) * prime6bytes) ^ s) >> (64-h)) ; } +static size_t ZSTD_hash6Ptr(const void* p, U32 h) { return ZSTD_hash6(MEM_readLE64(p), h, 0); } +static size_t ZSTD_hash6PtrS(const void* p, U32 h, U64 s) { return ZSTD_hash6(MEM_readLE64(p), h, s); } static const U64 prime7bytes = 58295818150454627ULL; -static size_t ZSTD_hash7(U64 u, U32 h) { assert(h <= 64); return (size_t)(((u << (64-56)) * prime7bytes) >> (64-h)) ; } -static size_t ZSTD_hash7Ptr(const void* p, U32 h) { return ZSTD_hash7(MEM_readLE64(p), h); } +static size_t ZSTD_hash7(U64 u, U32 h, U64 s) { assert(h <= 64); return (size_t)((((u << (64-56)) * prime7bytes) ^ s) >> (64-h)) ; } +static size_t ZSTD_hash7Ptr(const void* p, U32 h) { return ZSTD_hash7(MEM_readLE64(p), h, 0); } +static size_t ZSTD_hash7PtrS(const void* p, U32 h, U64 s) { return ZSTD_hash7(MEM_readLE64(p), h, s); } static const U64 prime8bytes = 0xCF1BBCDCB7A56463ULL; -static size_t ZSTD_hash8(U64 u, U32 h) { assert(h <= 64); return (size_t)(((u) * prime8bytes) >> (64-h)) ; } -static size_t ZSTD_hash8Ptr(const void* p, U32 h) { return ZSTD_hash8(MEM_readLE64(p), h); } +static size_t ZSTD_hash8(U64 u, U32 h, U64 s) { assert(h <= 64); return (size_t)((((u) * prime8bytes) ^ s) >> (64-h)) ; } +static size_t ZSTD_hash8Ptr(const void* p, U32 h) { return ZSTD_hash8(MEM_readLE64(p), h, 0); } +static size_t ZSTD_hash8PtrS(const void* p, U32 h, U64 s) { return ZSTD_hash8(MEM_readLE64(p), h, s); } + MEM_STATIC FORCE_INLINE_ATTR size_t ZSTD_hashPtr(const void* p, U32 hBits, U32 mls) @@ -828,6 +844,24 @@ size_t ZSTD_hashPtr(const void* p, U32 hBits, U32 mls) } } +MEM_STATIC FORCE_INLINE_ATTR +size_t ZSTD_hashPtrSalted(const void* p, U32 hBits, U32 mls, const U64 hashSalt) { + /* Although some of these hashes do support hBits up to 64, some do not. + * To be on the safe side, always avoid hBits > 32. */ + assert(hBits <= 32); + + switch(mls) + { + default: + case 4: return ZSTD_hash4PtrS(p, hBits, (U32)hashSalt); + case 5: return ZSTD_hash5PtrS(p, hBits, hashSalt); + case 6: return ZSTD_hash6PtrS(p, hBits, hashSalt); + case 7: return ZSTD_hash7PtrS(p, hBits, hashSalt); + case 8: return ZSTD_hash8PtrS(p, hBits, hashSalt); + } +} + + /** ZSTD_ipow() : * Return base^exponent. */ @@ -1475,4 +1509,24 @@ ZSTD_copySequencesToSeqStoreNoBlockDelim(ZSTD_CCtx* cctx, ZSTD_sequencePosition* const ZSTD_Sequence* const inSeqs, size_t inSeqsSize, const void* src, size_t blockSize, ZSTD_paramSwitch_e externalRepSearch); + +/* =============================================================== + * Deprecated definitions that are still used internally to avoid + * deprecation warnings. These functions are exactly equivalent to + * their public variants, but avoid the deprecation warnings. + * =============================================================== */ + +size_t ZSTD_compressBegin_usingCDict_deprecated(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); + +size_t ZSTD_compressContinue_public(ZSTD_CCtx* cctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize); + +size_t ZSTD_compressEnd_public(ZSTD_CCtx* cctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize); + +size_t ZSTD_compressBlock_deprecated(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); + + #endif /* ZSTD_COMPRESS_H */ diff --git a/lib/compress/zstd_cwksp.h b/lib/compress/zstd_cwksp.h index 97676693b..cc7fb1c71 100644 --- a/lib/compress/zstd_cwksp.h +++ b/lib/compress/zstd_cwksp.h @@ -14,7 +14,9 @@ /*-************************************* * Dependencies ***************************************/ +#include "../common/allocations.h" /* ZSTD_customMalloc, ZSTD_customFree */ #include "../common/zstd_internal.h" +#include "../common/portability_macros.h" #if defined (__cplusplus) extern "C" { @@ -44,8 +46,9 @@ extern "C" { ***************************************/ typedef enum { ZSTD_cwksp_alloc_objects, - ZSTD_cwksp_alloc_buffers, - ZSTD_cwksp_alloc_aligned + ZSTD_cwksp_alloc_aligned_init_once, + ZSTD_cwksp_alloc_aligned, + ZSTD_cwksp_alloc_buffers } ZSTD_cwksp_alloc_phase_e; /** @@ -98,8 +101,8 @@ typedef enum { * * Workspace Layout: * - * [ ... workspace ... ] - * [objects][tables ... ->] free space [<- ... aligned][<- ... buffers] + * [ ... workspace ... ] + * [objects][tables ->] free space [<- buffers][<- aligned][<- init once] * * The various objects that live in the workspace are divided into the * following categories, and are allocated separately: @@ -123,9 +126,18 @@ typedef enum { * uint32_t arrays, all of whose values are between 0 and (nextSrc - base). * Their sizes depend on the cparams. These tables are 64-byte aligned. * - * - Aligned: these buffers are used for various purposes that require 4 byte - * alignment, but don't require any initialization before they're used. These - * buffers are each aligned to 64 bytes. + * - Init once: these buffers require to be initialized at least once before + * use. They should be used when we want to skip memory initialization + * while not triggering memory checkers (like Valgrind) when reading from + * from this memory without writing to it first. + * These buffers should be used carefully as they might contain data + * from previous compressions. + * Buffers are aligned to 64 bytes. + * + * - Aligned: these buffers don't require any initialization before they're + * used. The user of the buffer should make sure they write into a buffer + * location before reading from it. + * Buffers are aligned to 64 bytes. * * - Buffers: these buffers are used for various purposes that don't require * any alignment or initialization before they're used. This means they can @@ -137,8 +149,9 @@ typedef enum { * correctly packed into the workspace buffer. That order is: * * 1. Objects - * 2. Buffers - * 3. Aligned/Tables + * 2. Init once / Tables + * 3. Aligned / Tables + * 4. Buffers / Tables * * Attempts to reserve objects of different types out of order will fail. */ @@ -150,6 +163,7 @@ typedef struct { void* tableEnd; void* tableValidEnd; void* allocStart; + void* initOnceStart; BYTE allocFailed; int workspaceOversizedDuration; @@ -162,6 +176,7 @@ typedef struct { ***************************************/ MEM_STATIC size_t ZSTD_cwksp_available_space(ZSTD_cwksp* ws); +MEM_STATIC void* ZSTD_cwksp_initialAllocStart(ZSTD_cwksp* ws); MEM_STATIC void ZSTD_cwksp_assert_internal_consistency(ZSTD_cwksp* ws) { (void)ws; @@ -171,6 +186,20 @@ MEM_STATIC void ZSTD_cwksp_assert_internal_consistency(ZSTD_cwksp* ws) { assert(ws->tableEnd <= ws->allocStart); assert(ws->tableValidEnd <= ws->allocStart); assert(ws->allocStart <= ws->workspaceEnd); + assert(ws->initOnceStart <= ZSTD_cwksp_initialAllocStart(ws)); + assert(ws->workspace <= ws->initOnceStart); +#if ZSTD_MEMORY_SANITIZER + { + intptr_t const offset = __msan_test_shadow(ws->initOnceStart, + (U8*)ZSTD_cwksp_initialAllocStart(ws) - (U8*)ws->initOnceStart); +#if defined(ZSTD_MSAN_PRINT) + if(offset!=-1) { + __msan_print_shadow((U8*)ws->initOnceStart + offset - 8, 32); + } +#endif + assert(offset==-1); + }; +#endif } /** @@ -217,14 +246,10 @@ MEM_STATIC size_t ZSTD_cwksp_aligned_alloc_size(size_t size) { * for internal purposes (currently only alignment). */ MEM_STATIC size_t ZSTD_cwksp_slack_space_required(void) { - /* For alignment, the wksp will always allocate an additional n_1=[1, 64] bytes - * to align the beginning of tables section, as well as another n_2=[0, 63] bytes - * to align the beginning of the aligned section. - * - * n_1 + n_2 == 64 bytes if the cwksp is freshly allocated, due to tables and - * aligneds being sized in multiples of 64 bytes. + /* For alignment, the wksp will always allocate an additional 2*ZSTD_CWKSP_ALIGNMENT_BYTES + * bytes to align the beginning of tables section and end of buffers; */ - size_t const slackSpace = ZSTD_CWKSP_ALIGNMENT_BYTES; + size_t const slackSpace = ZSTD_CWKSP_ALIGNMENT_BYTES * 2; return slackSpace; } @@ -237,10 +262,18 @@ MEM_STATIC size_t ZSTD_cwksp_bytes_to_align_ptr(void* ptr, const size_t alignByt size_t const alignBytesMask = alignBytes - 1; size_t const bytes = (alignBytes - ((size_t)ptr & (alignBytesMask))) & alignBytesMask; assert((alignBytes & alignBytesMask) == 0); - assert(bytes != ZSTD_CWKSP_ALIGNMENT_BYTES); + assert(bytes < alignBytes); return bytes; } +/** + * Returns the initial value for allocStart which is used to determine the position from + * which we can allocate from the end of the workspace. + */ +MEM_STATIC void* ZSTD_cwksp_initialAllocStart(ZSTD_cwksp* ws) { + return (void*)((size_t)ws->workspaceEnd & ~(ZSTD_CWKSP_ALIGNMENT_BYTES-1)); +} + /** * Internal function. Do not use directly. * Reserves the given number of bytes within the aligned/buffer segment of the wksp, @@ -281,27 +314,16 @@ ZSTD_cwksp_internal_advance_phase(ZSTD_cwksp* ws, ZSTD_cwksp_alloc_phase_e phase { assert(phase >= ws->phase); if (phase > ws->phase) { - /* Going from allocating objects to allocating buffers */ - if (ws->phase < ZSTD_cwksp_alloc_buffers && - phase >= ZSTD_cwksp_alloc_buffers) { + /* Going from allocating objects to allocating initOnce / tables */ + if (ws->phase < ZSTD_cwksp_alloc_aligned_init_once && + phase >= ZSTD_cwksp_alloc_aligned_init_once) { ws->tableValidEnd = ws->objectEnd; - } + ws->initOnceStart = ZSTD_cwksp_initialAllocStart(ws); - /* Going from allocating buffers to allocating aligneds/tables */ - if (ws->phase < ZSTD_cwksp_alloc_aligned && - phase >= ZSTD_cwksp_alloc_aligned) { - { /* Align the start of the "aligned" to 64 bytes. Use [1, 64] bytes. */ - size_t const bytesToAlign = - ZSTD_CWKSP_ALIGNMENT_BYTES - ZSTD_cwksp_bytes_to_align_ptr(ws->allocStart, ZSTD_CWKSP_ALIGNMENT_BYTES); - DEBUGLOG(5, "reserving aligned alignment addtl space: %zu", bytesToAlign); - ZSTD_STATIC_ASSERT((ZSTD_CWKSP_ALIGNMENT_BYTES & (ZSTD_CWKSP_ALIGNMENT_BYTES - 1)) == 0); /* power of 2 */ - RETURN_ERROR_IF(!ZSTD_cwksp_reserve_internal_buffer_space(ws, bytesToAlign), - memory_allocation, "aligned phase - alignment initial allocation failed!"); - } { /* Align the start of the tables to 64 bytes. Use [0, 63] bytes */ - void* const alloc = ws->objectEnd; + void *const alloc = ws->objectEnd; size_t const bytesToAlign = ZSTD_cwksp_bytes_to_align_ptr(alloc, ZSTD_CWKSP_ALIGNMENT_BYTES); - void* const objectEnd = (BYTE*)alloc + bytesToAlign; + void *const objectEnd = (BYTE *) alloc + bytesToAlign; DEBUGLOG(5, "reserving table alignment addtl space: %zu", bytesToAlign); RETURN_ERROR_IF(objectEnd > ws->workspaceEnd, memory_allocation, "table phase - alignment initial allocation failed!"); @@ -309,7 +331,9 @@ ZSTD_cwksp_internal_advance_phase(ZSTD_cwksp* ws, ZSTD_cwksp_alloc_phase_e phase ws->tableEnd = objectEnd; /* table area starts being empty */ if (ws->tableValidEnd < ws->tableEnd) { ws->tableValidEnd = ws->tableEnd; - } } } + } + } + } ws->phase = phase; ZSTD_cwksp_assert_internal_consistency(ws); } @@ -321,7 +345,7 @@ ZSTD_cwksp_internal_advance_phase(ZSTD_cwksp* ws, ZSTD_cwksp_alloc_phase_e phase */ MEM_STATIC int ZSTD_cwksp_owns_buffer(const ZSTD_cwksp* ws, const void* ptr) { - return (ptr != NULL) && (ws->workspace <= ptr) && (ptr <= ws->workspaceEnd); + return (ptr != NULL) && (ws->workspace <= ptr) && (ptr < ws->workspaceEnd); } /** @@ -366,6 +390,36 @@ MEM_STATIC BYTE* ZSTD_cwksp_reserve_buffer(ZSTD_cwksp* ws, size_t bytes) return (BYTE*)ZSTD_cwksp_reserve_internal(ws, bytes, ZSTD_cwksp_alloc_buffers); } +/** + * Reserves and returns memory sized on and aligned on ZSTD_CWKSP_ALIGNMENT_BYTES (64 bytes). + * This memory has been initialized at least once in the past. + * This doesn't mean it has been initialized this time, and it might contain data from previous + * operations. + * The main usage is for algorithms that might need read access into uninitialized memory. + * The algorithm must maintain safety under these conditions and must make sure it doesn't + * leak any of the past data (directly or in side channels). + */ +MEM_STATIC void* ZSTD_cwksp_reserve_aligned_init_once(ZSTD_cwksp* ws, size_t bytes) +{ + size_t const alignedBytes = ZSTD_cwksp_align(bytes, ZSTD_CWKSP_ALIGNMENT_BYTES); + void* ptr = ZSTD_cwksp_reserve_internal(ws, alignedBytes, ZSTD_cwksp_alloc_aligned_init_once); + assert(((size_t)ptr & (ZSTD_CWKSP_ALIGNMENT_BYTES-1))== 0); + if(ptr && ptr < ws->initOnceStart) { + /* We assume the memory following the current allocation is either: + * 1. Not usable as initOnce memory (end of workspace) + * 2. Another initOnce buffer that has been allocated before (and so was previously memset) + * 3. An ASAN redzone, in which case we don't want to write on it + * For these reasons it should be fine to not explicitly zero every byte up to ws->initOnceStart. + * Note that we assume here that MSAN and ASAN cannot run in the same time. */ + ZSTD_memset(ptr, 0, MIN((size_t)((U8*)ws->initOnceStart - (U8*)ptr), alignedBytes)); + ws->initOnceStart = ptr; + } +#if ZSTD_MEMORY_SANITIZER + assert(__msan_test_shadow(ptr, bytes) == -1); +#endif + return ptr; +} + /** * Reserves and returns memory sized on and aligned on ZSTD_CWKSP_ALIGNMENT_BYTES (64 bytes). */ @@ -384,13 +438,17 @@ MEM_STATIC void* ZSTD_cwksp_reserve_aligned(ZSTD_cwksp* ws, size_t bytes) */ MEM_STATIC void* ZSTD_cwksp_reserve_table(ZSTD_cwksp* ws, size_t bytes) { - const ZSTD_cwksp_alloc_phase_e phase = ZSTD_cwksp_alloc_aligned; + const ZSTD_cwksp_alloc_phase_e phase = ZSTD_cwksp_alloc_aligned_init_once; void* alloc; void* end; void* top; - if (ZSTD_isError(ZSTD_cwksp_internal_advance_phase(ws, phase))) { - return NULL; + /* We can only start allocating tables after we are done reserving space for objects at the + * start of the workspace */ + if(ws->phase < phase) { + if (ZSTD_isError(ZSTD_cwksp_internal_advance_phase(ws, phase))) { + return NULL; + } } alloc = ws->tableEnd; end = (BYTE *)alloc + bytes; @@ -469,11 +527,19 @@ MEM_STATIC void ZSTD_cwksp_mark_tables_dirty(ZSTD_cwksp* ws) #if ZSTD_MEMORY_SANITIZER && !defined (ZSTD_MSAN_DONT_POISON_WORKSPACE) /* To validate that the table re-use logic is sound, and that we don't * access table space that we haven't cleaned, we re-"poison" the table - * space every time we mark it dirty. */ + * space every time we mark it dirty. + * Since tableValidEnd space and initOnce space may overlap we don't poison + * the initOnce portion as it break its promise. This means that this poisoning + * check isn't always applied fully. */ { size_t size = (BYTE*)ws->tableValidEnd - (BYTE*)ws->objectEnd; assert(__msan_test_shadow(ws->objectEnd, size) == -1); - __msan_poison(ws->objectEnd, size); + if((BYTE*)ws->tableValidEnd < (BYTE*)ws->initOnceStart) { + __msan_poison(ws->objectEnd, size); + } else { + assert(ws->initOnceStart >= ws->objectEnd); + __msan_poison(ws->objectEnd, (BYTE*)ws->initOnceStart - (BYTE*)ws->objectEnd); + } } #endif @@ -538,11 +604,14 @@ MEM_STATIC void ZSTD_cwksp_clear(ZSTD_cwksp* ws) { #if ZSTD_MEMORY_SANITIZER && !defined (ZSTD_MSAN_DONT_POISON_WORKSPACE) /* To validate that the context re-use logic is sound, and that we don't * access stuff that this compression hasn't initialized, we re-"poison" - * the workspace (or at least the non-static, non-table parts of it) - * every time we start a new compression. */ + * the workspace except for the areas in which we expect memory re-use + * without initialization (objects, valid tables area and init once + * memory). */ { - size_t size = (BYTE*)ws->workspaceEnd - (BYTE*)ws->tableValidEnd; - __msan_poison(ws->tableValidEnd, size); + if((BYTE*)ws->tableValidEnd < (BYTE*)ws->initOnceStart) { + size_t size = (BYTE*)ws->initOnceStart - (BYTE*)ws->tableValidEnd; + __msan_poison(ws->tableValidEnd, size); + } } #endif @@ -558,10 +627,10 @@ MEM_STATIC void ZSTD_cwksp_clear(ZSTD_cwksp* ws) { #endif ws->tableEnd = ws->objectEnd; - ws->allocStart = ws->workspaceEnd; + ws->allocStart = ZSTD_cwksp_initialAllocStart(ws); ws->allocFailed = 0; - if (ws->phase > ZSTD_cwksp_alloc_buffers) { - ws->phase = ZSTD_cwksp_alloc_buffers; + if (ws->phase > ZSTD_cwksp_alloc_aligned_init_once) { + ws->phase = ZSTD_cwksp_alloc_aligned_init_once; } ZSTD_cwksp_assert_internal_consistency(ws); } @@ -578,6 +647,7 @@ MEM_STATIC void ZSTD_cwksp_init(ZSTD_cwksp* ws, void* start, size_t size, ZSTD_c ws->workspaceEnd = (BYTE*)start + size; ws->objectEnd = ws->workspace; ws->tableValidEnd = ws->objectEnd; + ws->initOnceStart = ZSTD_cwksp_initialAllocStart(ws); ws->phase = ZSTD_cwksp_alloc_objects; ws->isStatic = isStatic; ZSTD_cwksp_clear(ws); @@ -630,17 +700,11 @@ MEM_STATIC int ZSTD_cwksp_reserve_failed(const ZSTD_cwksp* ws) { * Returns if the estimated space needed for a wksp is within an acceptable limit of the * actual amount of space used. */ -MEM_STATIC int ZSTD_cwksp_estimated_space_within_bounds(const ZSTD_cwksp* const ws, - size_t const estimatedSpace, int resizedWorkspace) { - if (resizedWorkspace) { - /* Resized/newly allocated wksp should have exact bounds */ - return ZSTD_cwksp_used(ws) == estimatedSpace; - } else { - /* Due to alignment, when reusing a workspace, we can actually consume 63 fewer or more bytes - * than estimatedSpace. See the comments in zstd_cwksp.h for details. - */ - return (ZSTD_cwksp_used(ws) >= estimatedSpace - 63) && (ZSTD_cwksp_used(ws) <= estimatedSpace + 63); - } +MEM_STATIC int ZSTD_cwksp_estimated_space_within_bounds(const ZSTD_cwksp *const ws, size_t const estimatedSpace) { + /* We have an alignment space between objects and tables between tables and buffers, so we can have up to twice + * the alignment bytes difference between estimation and actual usage */ + return (estimatedSpace - ZSTD_cwksp_slack_space_required()) <= ZSTD_cwksp_used(ws) && + ZSTD_cwksp_used(ws) <= estimatedSpace; } diff --git a/lib/compress/zstd_lazy.c b/lib/compress/zstd_lazy.c index a24734272..5ba88e867 100644 --- a/lib/compress/zstd_lazy.c +++ b/lib/compress/zstd_lazy.c @@ -12,6 +12,8 @@ #include "zstd_lazy.h" #include "../common/bits.h" /* ZSTD_countTrailingZeros64 */ +#define kLazySkippingStep 8 + /*-************************************* * Binary Tree search @@ -618,7 +620,7 @@ size_t ZSTD_dedicatedDictSearch_lazy_search(size_t* offsetPtr, size_t ml, U32 nb FORCE_INLINE_TEMPLATE U32 ZSTD_insertAndFindFirstIndex_internal( ZSTD_matchState_t* ms, const ZSTD_compressionParameters* const cParams, - const BYTE* ip, U32 const mls) + const BYTE* ip, U32 const mls, U32 const lazySkipping) { U32* const hashTable = ms->hashTable; const U32 hashLog = cParams->hashLog; @@ -633,6 +635,9 @@ FORCE_INLINE_TEMPLATE U32 ZSTD_insertAndFindFirstIndex_internal( NEXT_IN_CHAIN(idx, chainMask) = hashTable[h]; hashTable[h] = idx; idx++; + /* Stop inserting every position when in the lazy skipping mode. */ + if (lazySkipping) + break; } ms->nextToUpdate = target; @@ -641,7 +646,7 @@ FORCE_INLINE_TEMPLATE U32 ZSTD_insertAndFindFirstIndex_internal( U32 ZSTD_insertAndFindFirstIndex(ZSTD_matchState_t* ms, const BYTE* ip) { const ZSTD_compressionParameters* const cParams = &ms->cParams; - return ZSTD_insertAndFindFirstIndex_internal(ms, cParams, ip, ms->cParams.minMatch); + return ZSTD_insertAndFindFirstIndex_internal(ms, cParams, ip, ms->cParams.minMatch, /* lazySkipping*/ 0); } /* inlining is important to hardwire a hot branch (template emulation) */ @@ -685,7 +690,7 @@ size_t ZSTD_HcFindBestMatch( } /* HC4 match finder */ - matchIndex = ZSTD_insertAndFindFirstIndex_internal(ms, cParams, ip, mls); + matchIndex = ZSTD_insertAndFindFirstIndex_internal(ms, cParams, ip, mls, ms->lazySkipping); for ( ; (matchIndex>=lowLimit) & (nbAttempts>0) ; nbAttempts--) { size_t currentMl=0; @@ -758,7 +763,6 @@ size_t ZSTD_HcFindBestMatch( * (SIMD) Row-based matchfinder ***********************************/ /* Constants for row-based hash */ -#define ZSTD_ROW_HASH_TAG_OFFSET 16 /* byte offset of hashes in the match state's tagTable from the beginning of a row */ #define ZSTD_ROW_HASH_TAG_MASK ((1u << ZSTD_ROW_HASH_TAG_BITS) - 1) #define ZSTD_ROW_HASH_MAX_ENTRIES 64 /* absolute maximum number of entries per row, for all configurations */ @@ -774,39 +778,15 @@ MEM_STATIC U32 ZSTD_VecMask_next(ZSTD_VecMask val) { return ZSTD_countTrailingZeros64(val); } -/* ZSTD_rotateRight_*(): - * Rotates a bitfield to the right by "count" bits. - * https://en.wikipedia.org/w/index.php?title=Circular_shift&oldid=991635599#Implementing_circular_shifts - */ -FORCE_INLINE_TEMPLATE -U64 ZSTD_rotateRight_U64(U64 const value, U32 count) { - assert(count < 64); - count &= 0x3F; /* for fickle pattern recognition */ - return (value >> count) | (U64)(value << ((0U - count) & 0x3F)); -} - -FORCE_INLINE_TEMPLATE -U32 ZSTD_rotateRight_U32(U32 const value, U32 count) { - assert(count < 32); - count &= 0x1F; /* for fickle pattern recognition */ - return (value >> count) | (U32)(value << ((0U - count) & 0x1F)); -} - -FORCE_INLINE_TEMPLATE -U16 ZSTD_rotateRight_U16(U16 const value, U32 count) { - assert(count < 16); - count &= 0x0F; /* for fickle pattern recognition */ - return (value >> count) | (U16)(value << ((0U - count) & 0x0F)); -} - /* ZSTD_row_nextIndex(): * Returns the next index to insert at within a tagTable row, and updates the "head" - * value to reflect the update. Essentially cycles backwards from [0, {entries per row}) + * value to reflect the update. Essentially cycles backwards from [1, {entries per row}) */ FORCE_INLINE_TEMPLATE U32 ZSTD_row_nextIndex(BYTE* const tagRow, U32 const rowMask) { - U32 const next = (*tagRow - 1) & rowMask; - *tagRow = (BYTE)next; - return next; + U32 next = (*tagRow-1) & rowMask; + next += (next == 0) ? rowMask : 0; /* skip first position */ + *tagRow = (BYTE)next; + return next; } /* ZSTD_isAligned(): @@ -820,7 +800,7 @@ MEM_STATIC int ZSTD_isAligned(void const* ptr, size_t align) { /* ZSTD_row_prefetch(): * Performs prefetching for the hashTable and tagTable at a given row. */ -FORCE_INLINE_TEMPLATE void ZSTD_row_prefetch(U32 const* hashTable, U16 const* tagTable, U32 const relRow, U32 const rowLog) { +FORCE_INLINE_TEMPLATE void ZSTD_row_prefetch(U32 const* hashTable, BYTE const* tagTable, U32 const relRow, U32 const rowLog) { PREFETCH_L1(hashTable + relRow); if (rowLog >= 5) { PREFETCH_L1(hashTable + relRow + 16); @@ -844,13 +824,13 @@ FORCE_INLINE_TEMPLATE void ZSTD_row_fillHashCache(ZSTD_matchState_t* ms, const B U32 idx, const BYTE* const iLimit) { U32 const* const hashTable = ms->hashTable; - U16 const* const tagTable = ms->tagTable; + BYTE const* const tagTable = ms->tagTable; U32 const hashLog = ms->rowHashLog; U32 const maxElemsToPrefetch = (base + idx) > iLimit ? 0 : (U32)(iLimit - (base + idx) + 1); U32 const lim = idx + MIN(ZSTD_ROW_HASH_CACHE_SIZE, maxElemsToPrefetch); for (; idx < lim; ++idx) { - U32 const hash = (U32)ZSTD_hashPtr(base + idx, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls); + U32 const hash = (U32)ZSTD_hashPtrSalted(base + idx, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls, ms->hashSalt); U32 const row = (hash >> ZSTD_ROW_HASH_TAG_BITS) << rowLog; ZSTD_row_prefetch(hashTable, tagTable, row, rowLog); ms->hashCache[idx & ZSTD_ROW_HASH_CACHE_MASK] = hash; @@ -866,11 +846,12 @@ FORCE_INLINE_TEMPLATE void ZSTD_row_fillHashCache(ZSTD_matchState_t* ms, const B * base + idx + ZSTD_ROW_HASH_CACHE_SIZE. Also prefetches the appropriate rows from hashTable and tagTable. */ FORCE_INLINE_TEMPLATE U32 ZSTD_row_nextCachedHash(U32* cache, U32 const* hashTable, - U16 const* tagTable, BYTE const* base, + BYTE const* tagTable, BYTE const* base, U32 idx, U32 const hashLog, - U32 const rowLog, U32 const mls) + U32 const rowLog, U32 const mls, + U64 const hashSalt) { - U32 const newHash = (U32)ZSTD_hashPtr(base+idx+ZSTD_ROW_HASH_CACHE_SIZE, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls); + U32 const newHash = (U32)ZSTD_hashPtrSalted(base+idx+ZSTD_ROW_HASH_CACHE_SIZE, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls, hashSalt); U32 const row = (newHash >> ZSTD_ROW_HASH_TAG_BITS) << rowLog; ZSTD_row_prefetch(hashTable, tagTable, row, rowLog); { U32 const hash = cache[idx & ZSTD_ROW_HASH_CACHE_MASK]; @@ -888,22 +869,21 @@ FORCE_INLINE_TEMPLATE void ZSTD_row_update_internalImpl(ZSTD_matchState_t* ms, U32 const rowMask, U32 const useCache) { U32* const hashTable = ms->hashTable; - U16* const tagTable = ms->tagTable; + BYTE* const tagTable = ms->tagTable; U32 const hashLog = ms->rowHashLog; const BYTE* const base = ms->window.base; DEBUGLOG(6, "ZSTD_row_update_internalImpl(): updateStartIdx=%u, updateEndIdx=%u", updateStartIdx, updateEndIdx); for (; updateStartIdx < updateEndIdx; ++updateStartIdx) { - U32 const hash = useCache ? ZSTD_row_nextCachedHash(ms->hashCache, hashTable, tagTable, base, updateStartIdx, hashLog, rowLog, mls) - : (U32)ZSTD_hashPtr(base + updateStartIdx, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls); + U32 const hash = useCache ? ZSTD_row_nextCachedHash(ms->hashCache, hashTable, tagTable, base, updateStartIdx, hashLog, rowLog, mls, ms->hashSalt) + : (U32)ZSTD_hashPtrSalted(base + updateStartIdx, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls, ms->hashSalt); U32 const relRow = (hash >> ZSTD_ROW_HASH_TAG_BITS) << rowLog; U32* const row = hashTable + relRow; - BYTE* tagRow = (BYTE*)(tagTable + relRow); /* Though tagTable is laid out as a table of U16, each tag is only 1 byte. - Explicit cast allows us to get exact desired position within each row */ + BYTE* tagRow = tagTable + relRow; U32 const pos = ZSTD_row_nextIndex(tagRow, rowMask); - assert(hash == ZSTD_hashPtr(base + updateStartIdx, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls)); - ((BYTE*)tagRow)[pos + ZSTD_ROW_HASH_TAG_OFFSET] = hash & ZSTD_ROW_HASH_TAG_MASK; + assert(hash == ZSTD_hashPtrSalted(base + updateStartIdx, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls, ms->hashSalt)); + tagRow[pos] = hash & ZSTD_ROW_HASH_TAG_MASK; row[pos] = updateStartIdx; } } @@ -1059,7 +1039,7 @@ ZSTD_row_getNEONMask(const U32 rowEntries, const BYTE* const src, const BYTE tag FORCE_INLINE_TEMPLATE ZSTD_VecMask ZSTD_row_getMatchMask(const BYTE* const tagRow, const BYTE tag, const U32 headGrouped, const U32 rowEntries) { - const BYTE* const src = tagRow + ZSTD_ROW_HASH_TAG_OFFSET; + const BYTE* const src = tagRow; assert((rowEntries == 16) || (rowEntries == 32) || rowEntries == 64); assert(rowEntries <= ZSTD_ROW_HASH_MAX_ENTRIES); assert(ZSTD_row_matchMaskGroupWidth(rowEntries) * rowEntries <= sizeof(ZSTD_VecMask) * 8); @@ -1144,7 +1124,7 @@ size_t ZSTD_RowFindBestMatch( const U32 rowLog) { U32* const hashTable = ms->hashTable; - U16* const tagTable = ms->tagTable; + BYTE* const tagTable = ms->tagTable; U32* const hashCache = ms->hashCache; const U32 hashLog = ms->rowHashLog; const ZSTD_compressionParameters* const cParams = &ms->cParams; @@ -1163,8 +1143,10 @@ size_t ZSTD_RowFindBestMatch( const U32 rowMask = rowEntries - 1; const U32 cappedSearchLog = MIN(cParams->searchLog, rowLog); /* nb of searches is capped at nb entries per row */ const U32 groupWidth = ZSTD_row_matchMaskGroupWidth(rowEntries); + const U64 hashSalt = ms->hashSalt; U32 nbAttempts = 1U << cappedSearchLog; size_t ml=4-1; + U32 hash; /* DMS/DDS variables that may be referenced laster */ const ZSTD_matchState_t* const dms = ms->dictMatchState; @@ -1188,7 +1170,7 @@ size_t ZSTD_RowFindBestMatch( if (dictMode == ZSTD_dictMatchState) { /* Prefetch DMS rows */ U32* const dmsHashTable = dms->hashTable; - U16* const dmsTagTable = dms->tagTable; + BYTE* const dmsTagTable = dms->tagTable; U32 const dmsHash = (U32)ZSTD_hashPtr(ip, dms->rowHashLog + ZSTD_ROW_HASH_TAG_BITS, mls); U32 const dmsRelRow = (dmsHash >> ZSTD_ROW_HASH_TAG_BITS) << rowLog; dmsTag = dmsHash & ZSTD_ROW_HASH_TAG_MASK; @@ -1198,9 +1180,19 @@ size_t ZSTD_RowFindBestMatch( } /* Update the hashTable and tagTable up to (but not including) ip */ - ZSTD_row_update_internal(ms, ip, mls, rowLog, rowMask, 1 /* useCache */); + if (!ms->lazySkipping) { + ZSTD_row_update_internal(ms, ip, mls, rowLog, rowMask, 1 /* useCache */); + hash = ZSTD_row_nextCachedHash(hashCache, hashTable, tagTable, base, curr, hashLog, rowLog, mls, hashSalt); + } else { + /* Stop inserting every position when in the lazy skipping mode. + * The hash cache is also not kept up to date in this mode. + */ + hash = (U32)ZSTD_hashPtrSalted(ip, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls, hashSalt); + ms->nextToUpdate = curr; + } + ms->hashSaltEntropy += hash; /* collect salt entropy */ + { /* Get the hash for ip, compute the appropriate row */ - U32 const hash = ZSTD_row_nextCachedHash(hashCache, hashTable, tagTable, base, curr, hashLog, rowLog, mls); U32 const relRow = (hash >> ZSTD_ROW_HASH_TAG_BITS) << rowLog; U32 const tag = hash & ZSTD_ROW_HASH_TAG_MASK; U32* const row = hashTable + relRow; @@ -1212,9 +1204,10 @@ size_t ZSTD_RowFindBestMatch( ZSTD_VecMask matches = ZSTD_row_getMatchMask(tagRow, (BYTE)tag, headGrouped, rowEntries); /* Cycle through the matches and prefetch */ - for (; (matches > 0) && (nbAttempts > 0); --nbAttempts, matches &= (matches - 1)) { + for (; (matches > 0) && (nbAttempts > 0); matches &= (matches - 1)) { U32 const matchPos = ((headGrouped + ZSTD_VecMask_next(matches)) / groupWidth) & rowMask; U32 const matchIndex = row[matchPos]; + if(matchPos == 0) continue; assert(numMatches < rowEntries); if (matchIndex < lowLimit) break; @@ -1224,13 +1217,14 @@ size_t ZSTD_RowFindBestMatch( PREFETCH_L1(dictBase + matchIndex); } matchBuffer[numMatches++] = matchIndex; + --nbAttempts; } /* Speed opt: insert current byte into hashtable too. This allows us to avoid one iteration of the loop in ZSTD_row_update_internal() at the next search. */ { U32 const pos = ZSTD_row_nextIndex(tagRow, rowMask); - tagRow[pos + ZSTD_ROW_HASH_TAG_OFFSET] = (BYTE)tag; + tagRow[pos] = (BYTE)tag; row[pos] = ms->nextToUpdate++; } @@ -1281,13 +1275,15 @@ size_t ZSTD_RowFindBestMatch( size_t currMatch = 0; ZSTD_VecMask matches = ZSTD_row_getMatchMask(dmsTagRow, (BYTE)dmsTag, headGrouped, rowEntries); - for (; (matches > 0) && (nbAttempts > 0); --nbAttempts, matches &= (matches - 1)) { + for (; (matches > 0) && (nbAttempts > 0); matches &= (matches - 1)) { U32 const matchPos = ((headGrouped + ZSTD_VecMask_next(matches)) / groupWidth) & rowMask; U32 const matchIndex = dmsRow[matchPos]; + if(matchPos == 0) continue; if (matchIndex < dmsLowestIndex) break; PREFETCH_L1(dmsBase + matchIndex); matchBuffer[numMatches++] = matchIndex; + --nbAttempts; } /* Return the longest match */ @@ -1544,10 +1540,11 @@ ZSTD_compressBlock_lazy_generic( assert(offset_2 <= dictAndPrefixLength); } + /* Reset the lazy skipping state */ + ms->lazySkipping = 0; + if (searchMethod == search_rowHash) { - ZSTD_row_fillHashCache(ms, base, rowLog, - MIN(ms->cParams.minMatch, 6 /* mls caps out at 6 */), - ms->nextToUpdate, ilimit); + ZSTD_row_fillHashCache(ms, base, rowLog, mls, ms->nextToUpdate, ilimit); } /* Match Loop */ @@ -1591,7 +1588,16 @@ ZSTD_compressBlock_lazy_generic( } if (matchLength < 4) { - ip += ((ip-anchor) >> kSearchStrength) + 1; /* jump faster over incompressible sections */ + size_t const step = ((size_t)(ip-anchor) >> kSearchStrength) + 1; /* jump faster over incompressible sections */; + ip += step; + /* Enter the lazy skipping mode once we are skipping more than 8 bytes at a time. + * In this mode we stop inserting every position into our tables, and only insert + * positions that we search, which is one in step positions. + * The exact cutoff is flexible, I've just chosen a number that is reasonably high, + * so we minimize the compression ratio loss in "normal" scenarios. This mode gets + * triggered once we've gone 2KB without finding any matches. + */ + ms->lazySkipping = step > kLazySkippingStep; continue; } @@ -1695,6 +1701,13 @@ _storeSequence: ZSTD_storeSeq(seqStore, litLength, anchor, iend, (U32)offBase, matchLength); anchor = ip = start + matchLength; } + if (ms->lazySkipping) { + /* We've found a match, disable lazy skipping mode, and refill the hash cache. */ + if (searchMethod == search_rowHash) { + ZSTD_row_fillHashCache(ms, base, rowLog, mls, ms->nextToUpdate, ilimit); + } + ms->lazySkipping = 0; + } /* check immediate repcode */ if (isDxS) { @@ -1912,12 +1925,13 @@ size_t ZSTD_compressBlock_lazy_extDict_generic( DEBUGLOG(5, "ZSTD_compressBlock_lazy_extDict_generic (searchFunc=%u)", (U32)searchMethod); + /* Reset the lazy skipping state */ + ms->lazySkipping = 0; + /* init */ ip += (ip == prefixStart); if (searchMethod == search_rowHash) { - ZSTD_row_fillHashCache(ms, base, rowLog, - MIN(ms->cParams.minMatch, 6 /* mls caps out at 6 */), - ms->nextToUpdate, ilimit); + ZSTD_row_fillHashCache(ms, base, rowLog, mls, ms->nextToUpdate, ilimit); } /* Match Loop */ @@ -1955,7 +1969,16 @@ size_t ZSTD_compressBlock_lazy_extDict_generic( } if (matchLength < 4) { - ip += ((ip-anchor) >> kSearchStrength) + 1; /* jump faster over incompressible sections */ + size_t const step = ((size_t)(ip-anchor) >> kSearchStrength); + ip += step + 1; /* jump faster over incompressible sections */ + /* Enter the lazy skipping mode once we are skipping more than 8 bytes at a time. + * In this mode we stop inserting every position into our tables, and only insert + * positions that we search, which is one in step positions. + * The exact cutoff is flexible, I've just chosen a number that is reasonably high, + * so we minimize the compression ratio loss in "normal" scenarios. This mode gets + * triggered once we've gone 2KB without finding any matches. + */ + ms->lazySkipping = step > kLazySkippingStep; continue; } @@ -2041,6 +2064,13 @@ _storeSequence: ZSTD_storeSeq(seqStore, litLength, anchor, iend, (U32)offBase, matchLength); anchor = ip = start + matchLength; } + if (ms->lazySkipping) { + /* We've found a match, disable lazy skipping mode, and refill the hash cache. */ + if (searchMethod == search_rowHash) { + ZSTD_row_fillHashCache(ms, base, rowLog, mls, ms->nextToUpdate, ilimit); + } + ms->lazySkipping = 0; + } /* check immediate repcode */ while (ip <= ilimit) { diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index fdd7f9d8b..f02a76094 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -1086,6 +1086,8 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms, ZSTD_optimal_t lastSequence; ZSTD_optLdm_t optLdm; + ZSTD_memset(&lastSequence, 0, sizeof(ZSTD_optimal_t)); + optLdm.seqStore = ms->ldmSeqStore ? *ms->ldmSeqStore : kNullRawSeqStore; optLdm.endPosInBlock = optLdm.startPosInBlock = optLdm.offset = 0; ZSTD_opt_getNextMatchAndUpdateSeqStore(&optLdm, (U32)(ip-istart), (U32)(iend-ip)); diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 7a2c71720..678607556 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -20,6 +20,7 @@ /* ====== Dependencies ====== */ +#include "../common/allocations.h" /* ZSTD_customMalloc, ZSTD_customCalloc, ZSTD_customFree */ #include "../common/zstd_deps.h" /* ZSTD_memcpy, ZSTD_memset, INT_MAX, UINT_MAX */ #include "../common/mem.h" /* MEM_STATIC */ #include "../common/pool.h" /* threadpool */ @@ -719,7 +720,7 @@ static void ZSTDMT_compressionJob(void* jobDescription) ZSTDMT_serialState_update(job->serial, cctx, rawSeqStore, job->src, job->jobID); if (!job->firstJob) { /* flush and overwrite frame header when it's not first job */ - size_t const hSize = ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.capacity, job->src.start, 0); + size_t const hSize = ZSTD_compressContinue_public(cctx, dstBuff.start, dstBuff.capacity, job->src.start, 0); if (ZSTD_isError(hSize)) JOB_ERROR(hSize); DEBUGLOG(5, "ZSTDMT_compressionJob: flush and overwrite %u bytes of frame header (not first job)", (U32)hSize); ZSTD_invalidateRepCodes(cctx); @@ -737,7 +738,7 @@ static void ZSTDMT_compressionJob(void* jobDescription) DEBUGLOG(5, "ZSTDMT_compressionJob: compress %u bytes in %i blocks", (U32)job->src.size, nbChunks); assert(job->cSize == 0); for (chunkNb = 1; chunkNb < nbChunks; chunkNb++) { - size_t const cSize = ZSTD_compressContinue(cctx, op, oend-op, ip, chunkSize); + size_t const cSize = ZSTD_compressContinue_public(cctx, op, oend-op, ip, chunkSize); if (ZSTD_isError(cSize)) JOB_ERROR(cSize); ip += chunkSize; op += cSize; assert(op < oend); @@ -757,8 +758,8 @@ static void ZSTDMT_compressionJob(void* jobDescription) size_t const lastBlockSize1 = job->src.size & (chunkSize-1); size_t const lastBlockSize = ((lastBlockSize1==0) & (job->src.size>=chunkSize)) ? chunkSize : lastBlockSize1; size_t const cSize = (job->lastJob) ? - ZSTD_compressEnd (cctx, op, oend-op, ip, lastBlockSize) : - ZSTD_compressContinue(cctx, op, oend-op, ip, lastBlockSize); + ZSTD_compressEnd_public(cctx, op, oend-op, ip, lastBlockSize) : + ZSTD_compressContinue_public(cctx, op, oend-op, ip, lastBlockSize); if (ZSTD_isError(cSize)) JOB_ERROR(cSize); lastCBlockSize = cSize; } } diff --git a/lib/decompress/huf_decompress.c b/lib/decompress/huf_decompress.c index c2d1f633a..5b217ac58 100644 --- a/lib/decompress/huf_decompress.c +++ b/lib/decompress/huf_decompress.c @@ -696,7 +696,7 @@ void HUF_decompress4X1_usingDTable_internal_fast_c_loop(HUF_DecompressFastArgs* /* Copy the arguments to local variables */ ZSTD_memcpy(&bits, &args->bits, sizeof(bits)); - ZSTD_memcpy(&ip, &args->ip, sizeof(ip)); + ZSTD_memcpy((void*)(&ip), &args->ip, sizeof(ip)); ZSTD_memcpy(&op, &args->op, sizeof(op)); assert(MEM_isLittleEndian()); @@ -779,7 +779,7 @@ _out: /* Save the final values of each of the state variables back to args. */ ZSTD_memcpy(&args->bits, &bits, sizeof(bits)); - ZSTD_memcpy(&args->ip, &ip, sizeof(ip)); + ZSTD_memcpy((void*)(&args->ip), &ip, sizeof(ip)); ZSTD_memcpy(&args->op, &op, sizeof(op)); } @@ -1476,7 +1476,7 @@ void HUF_decompress4X2_usingDTable_internal_fast_c_loop(HUF_DecompressFastArgs* /* Copy the arguments to local registers. */ ZSTD_memcpy(&bits, &args->bits, sizeof(bits)); - ZSTD_memcpy(&ip, &args->ip, sizeof(ip)); + ZSTD_memcpy((void*)(&ip), &args->ip, sizeof(ip)); ZSTD_memcpy(&op, &args->op, sizeof(op)); oend[0] = op[1]; @@ -1599,7 +1599,7 @@ _out: /* Save the final values of each of the state variables back to args. */ ZSTD_memcpy(&args->bits, &bits, sizeof(bits)); - ZSTD_memcpy(&args->ip, &ip, sizeof(ip)); + ZSTD_memcpy((void*)(&args->ip), &ip, sizeof(ip)); ZSTD_memcpy(&args->op, &op, sizeof(op)); } diff --git a/lib/decompress/zstd_ddict.c b/lib/decompress/zstd_ddict.c index ad5c34a7f..309ec0d03 100644 --- a/lib/decompress/zstd_ddict.c +++ b/lib/decompress/zstd_ddict.c @@ -14,6 +14,7 @@ /*-******************************************************* * Dependencies *********************************************************/ +#include "../common/allocations.h" /* ZSTD_customMalloc, ZSTD_customFree */ #include "../common/zstd_deps.h" /* ZSTD_memcpy, ZSTD_memmove, ZSTD_memset */ #include "../common/cpu.h" /* bmi2 */ #include "../common/mem.h" /* low level memory routines */ diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index d487966cc..3f3ca57c0 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -55,6 +55,7 @@ /*-******************************************************* * Dependencies *********************************************************/ +#include "../common/allocations.h" /* ZSTD_customMalloc, ZSTD_customCalloc, ZSTD_customFree */ #include "../common/zstd_deps.h" /* ZSTD_memcpy, ZSTD_memmove, ZSTD_memset */ #include "../common/mem.h" /* low level memory routines */ #define FSE_STATIC_LINKING_ONLY diff --git a/lib/decompress/zstd_decompress_block.c b/lib/decompress/zstd_decompress_block.c index 0a06a021e..09896a931 100644 --- a/lib/decompress/zstd_decompress_block.c +++ b/lib/decompress/zstd_decompress_block.c @@ -1232,11 +1232,7 @@ ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e longOffsets) /* sequence */ { size_t offset; - #if defined(__clang__) - if (LIKELY(ofBits > 1)) { - #else if (ofBits > 1) { - #endif ZSTD_STATIC_ASSERT(ZSTD_lo_isLongOffset == 1); ZSTD_STATIC_ASSERT(LONG_OFFSETS_MAX_EXTRA_BITS_32 == 5); ZSTD_STATIC_ASSERT(STREAM_ACCUMULATOR_MIN_32 > LONG_OFFSETS_MAX_EXTRA_BITS_32); @@ -1273,11 +1269,7 @@ ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e longOffsets) seq.offset = offset; } - #if defined(__clang__) - if (UNLIKELY(mlBits > 0)) - #else if (mlBits > 0) - #endif seq.matchLength += BIT_readBitsFast(&seqState->DStream, mlBits/*>0*/); if (MEM_32bits() && (mlBits+llBits >= STREAM_ACCUMULATOR_MIN_32-LONG_OFFSETS_MAX_EXTRA_BITS_32)) @@ -1287,11 +1279,7 @@ ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e longOffsets) /* Ensure there are enough bits to read the rest of data in 64-bit mode. */ ZSTD_STATIC_ASSERT(16+LLFSELog+MLFSELog+OffFSELog < STREAM_ACCUMULATOR_MIN_64); - #if defined(__clang__) - if (UNLIKELY(llBits > 0)) - #else if (llBits > 0) - #endif seq.litLength += BIT_readBitsFast(&seqState->DStream, llBits/*>0*/); if (MEM_32bits()) @@ -1987,7 +1975,7 @@ ZSTD_decompressSequencesLong(ZSTD_DCtx* dctx, /** - * @returns The total size of the history referencable by zstd, including + * @returns The total size of the history referenceable by zstd, including * both the prefix and the extDict. At @p op any offset larger than this * is invalid. */ @@ -2124,7 +2112,9 @@ ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, ip += seqHSize; srcSize -= seqHSize; - RETURN_ERROR_IF(dst == NULL && nbSeq > 0, dstSize_tooSmall, "NULL not handled"); + RETURN_ERROR_IF((dst == NULL || dstCapacity == 0) && nbSeq > 0, dstSize_tooSmall, "NULL not handled"); + RETURN_ERROR_IF(MEM_64bits() && sizeof(size_t) == sizeof(void*) && (size_t)(-1) - (size_t)dst < (size_t)(1 << 20), dstSize_tooSmall, + "invalid dst"); /* If we could potentially have long offsets, or we might want to use the prefetch decoder, * compute information about the share of long offsets, and the maximum nbAdditionalBits. @@ -2181,9 +2171,9 @@ void ZSTD_checkContinuity(ZSTD_DCtx* dctx, const void* dst, size_t dstSize) } -size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, - void* dst, size_t dstCapacity, - const void* src, size_t srcSize) +size_t ZSTD_decompressBlock_deprecated(ZSTD_DCtx* dctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize) { size_t dSize; ZSTD_checkContinuity(dctx, dst, dstCapacity); @@ -2191,3 +2181,12 @@ size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, dctx->previousDstEnd = (char*)dst + dSize; return dSize; } + + +/* NOTE: Must just wrap ZSTD_decompressBlock_deprecated() */ +size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize) +{ + return ZSTD_decompressBlock_deprecated(dctx, dst, dstCapacity, src, srcSize); +} diff --git a/lib/decompress/zstd_decompress_block.h b/lib/decompress/zstd_decompress_block.h index 67791dbc3..9d1318882 100644 --- a/lib/decompress/zstd_decompress_block.h +++ b/lib/decompress/zstd_decompress_block.h @@ -64,5 +64,10 @@ void ZSTD_buildFSETable(ZSTD_seqSymbol* dt, unsigned tableLog, void* wksp, size_t wkspSize, int bmi2); +/* Internal definition of ZSTD_decompressBlock() to avoid deprecation warnings. */ +size_t ZSTD_decompressBlock_deprecated(ZSTD_DCtx* dctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize); + #endif /* ZSTD_DEC_BLOCK_H */ diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index f22e04df2..58290f450 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -566,11 +566,11 @@ static void ZDICT_countEStats(EStats_ress_t esr, const ZSTD_parameters* params, size_t cSize; if (srcSize > blockSizeMax) srcSize = blockSizeMax; /* protection vs large samples */ - { size_t const errorCode = ZSTD_compressBegin_usingCDict(esr.zc, esr.dict); + { size_t const errorCode = ZSTD_compressBegin_usingCDict_deprecated(esr.zc, esr.dict); if (ZSTD_isError(errorCode)) { DISPLAYLEVEL(1, "warning : ZSTD_compressBegin_usingCDict failed \n"); return; } } - cSize = ZSTD_compressBlock(esr.zc, esr.workPlace, ZSTD_BLOCKSIZE_MAX, src, srcSize); + cSize = ZSTD_compressBlock_deprecated(esr.zc, esr.workPlace, ZSTD_BLOCKSIZE_MAX, src, srcSize); if (ZSTD_isError(cSize)) { DISPLAYLEVEL(3, "warning : could not compress sample size %u \n", (unsigned)srcSize); return; } if (cSize) { /* if == 0; block is not compressible */ diff --git a/lib/zstd.h b/lib/zstd.h index 95aac0737..e5c3f8b68 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -106,7 +106,7 @@ extern "C" { /*------ Version ------*/ #define ZSTD_VERSION_MAJOR 1 #define ZSTD_VERSION_MINOR 5 -#define ZSTD_VERSION_RELEASE 4 +#define ZSTD_VERSION_RELEASE 5 #define ZSTD_VERSION_NUMBER (ZSTD_VERSION_MAJOR *100*100 + ZSTD_VERSION_MINOR *100 + ZSTD_VERSION_RELEASE) /*! ZSTD_versionNumber() : @@ -148,7 +148,8 @@ ZSTDLIB_API const char* ZSTD_versionString(void); ***************************************/ /*! ZSTD_compress() : * Compresses `src` content as a single zstd compressed frame into already allocated `dst`. - * Hint : compression runs faster if `dstCapacity` >= `ZSTD_compressBound(srcSize)`. + * NOTE: Providing `dstCapacity >= ZSTD_compressBound(srcSize)` guarantees that zstd will have + * enough space to successfully compress the data. * @return : compressed size written into `dst` (<= `dstCapacity), * or an error code if it fails (which can be tested using ZSTD_isError()). */ ZSTDLIB_API size_t ZSTD_compress( void* dst, size_t dstCapacity, @@ -578,7 +579,8 @@ ZSTDLIB_API size_t ZSTD_CCtx_reset(ZSTD_CCtx* cctx, ZSTD_ResetDirective reset); * Should cctx hold data from a previously unfinished frame, everything about it is forgotten. * - Compression parameters are pushed into CCtx before starting compression, using ZSTD_CCtx_set*() * - The function is always blocking, returns when compression is completed. - * Hint : compression runs faster if `dstCapacity` >= `ZSTD_compressBound(srcSize)`. + * NOTE: Providing `dstCapacity >= ZSTD_compressBound(srcSize)` guarantees that zstd will have + * enough space to successfully compress the data, though it is possible it fails for other reasons. * @return : compressed size written into `dst` (<= `dstCapacity), * or an error code if it fails (which can be tested using ZSTD_isError()). */ @@ -1018,9 +1020,11 @@ ZSTDLIB_API unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize); * Advanced dictionary and prefix API (Requires v1.4.0+) * * This API allows dictionaries to be used with ZSTD_compress2(), - * ZSTD_compressStream2(), and ZSTD_decompressDCtx(). Dictionaries are sticky, and - * only reset with the context is reset with ZSTD_reset_parameters or - * ZSTD_reset_session_and_parameters. Prefixes are single-use. + * ZSTD_compressStream2(), and ZSTD_decompressDCtx(). + * Dictionaries are sticky, they remain valid when same context is re-used, + * they only reset when the context is reset + * with ZSTD_reset_parameters or ZSTD_reset_session_and_parameters. + * In contrast, Prefixes are single-use. ******************************************************************************/ @@ -1041,7 +1045,11 @@ ZSTDLIB_API unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize); * Use experimental ZSTD_CCtx_loadDictionary_byReference() to reference content instead. * In such a case, dictionary buffer must outlive its users. * Note 4 : Use ZSTD_CCtx_loadDictionary_advanced() - * to precisely select how dictionary content must be interpreted. */ + * to precisely select how dictionary content must be interpreted. + * Note 5 : This method does not benefit from LDM (long distance mode). + * If you want to employ LDM on some large dictionary content, + * prefer employing ZSTD_CCtx_refPrefix() described below. + */ ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize); /*! ZSTD_CCtx_refCDict() : Requires v1.4.0+ @@ -1064,6 +1072,7 @@ ZSTDLIB_API size_t ZSTD_CCtx_refCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); * Decompression will need same prefix to properly regenerate data. * Compressing with a prefix is similar in outcome as performing a diff and compressing it, * but performs much faster, especially during decompression (compression speed is tunable with compression level). + * This method is compatible with LDM (long distance mode). * @result : 0, or an error code (which can be tested with ZSTD_isError()). * Special: Adding any prefix (including NULL) invalidates any previous prefix or dictionary * Note 1 : Prefix buffer is referenced. It **must** outlive compression. @@ -1387,7 +1396,7 @@ typedef enum { } ZSTD_paramSwitch_e; /*************************************** -* Frame size functions +* Frame header and size functions ***************************************/ /*! ZSTD_findDecompressedSize() : @@ -1434,6 +1443,30 @@ ZSTDLIB_STATIC_API unsigned long long ZSTD_decompressBound(const void* src, size * or an error code (if srcSize is too small) */ ZSTDLIB_STATIC_API size_t ZSTD_frameHeaderSize(const void* src, size_t srcSize); +typedef enum { ZSTD_frame, ZSTD_skippableFrame } ZSTD_frameType_e; +typedef struct { + unsigned long long frameContentSize; /* if == ZSTD_CONTENTSIZE_UNKNOWN, it means this field is not available. 0 means "empty" */ + unsigned long long windowSize; /* can be very large, up to <= frameContentSize */ + unsigned blockSizeMax; + ZSTD_frameType_e frameType; /* if == ZSTD_skippableFrame, frameContentSize is the size of skippable content */ + unsigned headerSize; + unsigned dictID; + unsigned checksumFlag; + unsigned _reserved1; + unsigned _reserved2; +} ZSTD_frameHeader; + +/*! ZSTD_getFrameHeader() : + * decode Frame Header, or requires larger `srcSize`. + * @return : 0, `zfhPtr` is correctly filled, + * >0, `srcSize` is too small, value is wanted `srcSize` amount, + * or an error code, which can be tested using ZSTD_isError() */ +ZSTDLIB_STATIC_API size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize); /**< doesn't consume input */ +/*! ZSTD_getFrameHeader_advanced() : + * same as ZSTD_getFrameHeader(), + * with added capability to select a format (like ZSTD_f_zstd1_magicless) */ +ZSTDLIB_STATIC_API size_t ZSTD_getFrameHeader_advanced(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize, ZSTD_format_e format); + /*! ZSTD_decompressionMargin() : * Zstd supports in-place decompression, where the input and output buffers overlap. * In this case, the output buffer must be at least (Margin + Output_Size) bytes large, @@ -1803,12 +1836,26 @@ ZSTDLIB_STATIC_API size_t ZSTD_checkCParams(ZSTD_compressionParameters params); ZSTDLIB_STATIC_API ZSTD_compressionParameters ZSTD_adjustCParams(ZSTD_compressionParameters cPar, unsigned long long srcSize, size_t dictSize); /*! ZSTD_CCtx_setCParams() : - * Set all parameters provided within @cparams into the working @cctx. + * Set all parameters provided within @p cparams into the working @p cctx. * Note : if modifying parameters during compression (MT mode only), * note that changes to the .windowLog parameter will be ignored. - * @return 0 on success, or an error code (can be checked with ZSTD_isError()) */ + * @return 0 on success, or an error code (can be checked with ZSTD_isError()). + * On failure, no parameters are updated. + */ ZSTDLIB_STATIC_API size_t ZSTD_CCtx_setCParams(ZSTD_CCtx* cctx, ZSTD_compressionParameters cparams); +/*! ZSTD_CCtx_setFParams() : + * Set all parameters provided within @p fparams into the working @p cctx. + * @return 0 on success, or an error code (can be checked with ZSTD_isError()). + */ +ZSTDLIB_STATIC_API size_t ZSTD_CCtx_setFParams(ZSTD_CCtx* cctx, ZSTD_frameParameters fparams); + +/*! ZSTD_CCtx_setParams() : + * Set all parameters provided within @p params into the working @p cctx. + * @return 0 on success, or an error code (can be checked with ZSTD_isError()). + */ +ZSTDLIB_STATIC_API size_t ZSTD_CCtx_setParams(ZSTD_CCtx* cctx, ZSTD_parameters params); + /*! ZSTD_compress_advanced() : * Note : this function is now DEPRECATED. * It can be replaced by ZSTD_compress2(), in combination with ZSTD_CCtx_setParameter() and other parameter setters. @@ -2134,7 +2181,7 @@ ZSTDLIB_STATIC_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const vo * This parameter can be used to set an upper bound on the blocksize * that overrides the default ZSTD_BLOCKSIZE_MAX. It cannot be used to set upper * bounds greater than ZSTD_BLOCKSIZE_MAX or bounds lower than 1KB (will make - * compressBound() innacurate). Only currently meant to be used for testing. + * compressBound() inaccurate). Only currently meant to be used for testing. * */ #define ZSTD_c_maxBlockSize ZSTD_c_experimentalParam18 @@ -2452,12 +2499,9 @@ size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, int compressionLevel); /*! ZSTD_initCStream_advanced() : - * This function is DEPRECATED, and is approximately equivalent to: + * This function is DEPRECATED, and is equivalent to: * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only); - * // Pseudocode: Set each zstd parameter and leave the rest as-is. - * for ((param, value) : params) { - * ZSTD_CCtx_setParameter(zcs, param, value); - * } + * ZSTD_CCtx_setParams(zcs, params); * ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize); * ZSTD_CCtx_loadDictionary(zcs, dict, dictSize); * @@ -2486,12 +2530,9 @@ ZSTDLIB_STATIC_API size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict); /*! ZSTD_initCStream_usingCDict_advanced() : - * This function is DEPRECATED, and is approximately equivalent to: + * This function is DEPRECATED, and is equivalent to: * ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only); - * // Pseudocode: Set each zstd frame parameter and leave the rest as-is. - * for ((fParam, value) : fParams) { - * ZSTD_CCtx_setParameter(zcs, fParam, value); - * } + * ZSTD_CCtx_setFParams(zcs, fParams); * ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize); * ZSTD_CCtx_refCDict(zcs, cdict); * @@ -2598,214 +2639,6 @@ ZSTD_DEPRECATED("use ZSTD_DCtx_reset, see zstd.h for detailed instructions") ZSTDLIB_STATIC_API size_t ZSTD_resetDStream(ZSTD_DStream* zds); -/********************************************************************* -* Buffer-less and synchronous inner streaming functions -* -* This is an advanced API, giving full control over buffer management, for users which need direct control over memory. -* But it's also a complex one, with several restrictions, documented below. -* Prefer normal streaming API for an easier experience. -********************************************************************* */ - -/** - Buffer-less streaming compression (synchronous mode) - - A ZSTD_CCtx object is required to track streaming operations. - Use ZSTD_createCCtx() / ZSTD_freeCCtx() to manage resource. - ZSTD_CCtx object can be re-used multiple times within successive compression operations. - - Start by initializing a context. - Use ZSTD_compressBegin(), or ZSTD_compressBegin_usingDict() for dictionary compression. - - Then, consume your input using ZSTD_compressContinue(). - There are some important considerations to keep in mind when using this advanced function : - - ZSTD_compressContinue() has no internal buffer. It uses externally provided buffers only. - - Interface is synchronous : input is consumed entirely and produces 1+ compressed blocks. - - Caller must ensure there is enough space in `dst` to store compressed data under worst case scenario. - Worst case evaluation is provided by ZSTD_compressBound(). - ZSTD_compressContinue() doesn't guarantee recover after a failed compression. - - ZSTD_compressContinue() presumes prior input ***is still accessible and unmodified*** (up to maximum distance size, see WindowLog). - It remembers all previous contiguous blocks, plus one separated memory segment (which can itself consists of multiple contiguous blocks) - - ZSTD_compressContinue() detects that prior input has been overwritten when `src` buffer overlaps. - In which case, it will "discard" the relevant memory section from its history. - - Finish a frame with ZSTD_compressEnd(), which will write the last block(s) and optional checksum. - It's possible to use srcSize==0, in which case, it will write a final empty block to end the frame. - Without last block mark, frames are considered unfinished (hence corrupted) by compliant decoders. - - `ZSTD_CCtx` object can be re-used (ZSTD_compressBegin()) to compress again. -*/ - -/*===== Buffer-less streaming compression functions =====*/ -ZSTDLIB_STATIC_API size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel); -ZSTDLIB_STATIC_API size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel); -ZSTDLIB_STATIC_API size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); /**< note: fails if cdict==NULL */ - -ZSTD_DEPRECATED("This function will likely be removed in a future release. It is misleading and has very limited utility.") -ZSTDLIB_STATIC_API -size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned long long pledgedSrcSize); /**< note: if pledgedSrcSize is not known, use ZSTD_CONTENTSIZE_UNKNOWN */ - -ZSTDLIB_STATIC_API size_t ZSTD_compressContinue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); -ZSTDLIB_STATIC_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); - -/* The ZSTD_compressBegin_advanced() and ZSTD_compressBegin_usingCDict_advanced() are now DEPRECATED and will generate a compiler warning */ -ZSTD_DEPRECATED("use advanced API to access custom parameters") -ZSTDLIB_STATIC_API -size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize : If srcSize is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN */ -ZSTD_DEPRECATED("use advanced API to access custom parameters") -ZSTDLIB_STATIC_API -size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_CDict* const cdict, ZSTD_frameParameters const fParams, unsigned long long const pledgedSrcSize); /* compression parameters are already set within cdict. pledgedSrcSize must be correct. If srcSize is not known, use macro ZSTD_CONTENTSIZE_UNKNOWN */ -/** - Buffer-less streaming decompression (synchronous mode) - - A ZSTD_DCtx object is required to track streaming operations. - Use ZSTD_createDCtx() / ZSTD_freeDCtx() to manage it. - A ZSTD_DCtx object can be re-used multiple times. - - First typical operation is to retrieve frame parameters, using ZSTD_getFrameHeader(). - Frame header is extracted from the beginning of compressed frame, so providing only the frame's beginning is enough. - Data fragment must be large enough to ensure successful decoding. - `ZSTD_frameHeaderSize_max` bytes is guaranteed to always be large enough. - result : 0 : successful decoding, the `ZSTD_frameHeader` structure is correctly filled. - >0 : `srcSize` is too small, please provide at least result bytes on next attempt. - errorCode, which can be tested using ZSTD_isError(). - - It fills a ZSTD_frameHeader structure with important information to correctly decode the frame, - such as the dictionary ID, content size, or maximum back-reference distance (`windowSize`). - Note that these values could be wrong, either because of data corruption, or because a 3rd party deliberately spoofs false information. - As a consequence, check that values remain within valid application range. - For example, do not allocate memory blindly, check that `windowSize` is within expectation. - Each application can set its own limits, depending on local restrictions. - For extended interoperability, it is recommended to support `windowSize` of at least 8 MB. - - ZSTD_decompressContinue() needs previous data blocks during decompression, up to `windowSize` bytes. - ZSTD_decompressContinue() is very sensitive to contiguity, - if 2 blocks don't follow each other, make sure that either the compressor breaks contiguity at the same place, - or that previous contiguous segment is large enough to properly handle maximum back-reference distance. - There are multiple ways to guarantee this condition. - - The most memory efficient way is to use a round buffer of sufficient size. - Sufficient size is determined by invoking ZSTD_decodingBufferSize_min(), - which can return an error code if required value is too large for current system (in 32-bits mode). - In a round buffer methodology, ZSTD_decompressContinue() decompresses each block next to previous one, - up to the moment there is not enough room left in the buffer to guarantee decoding another full block, - which maximum size is provided in `ZSTD_frameHeader` structure, field `blockSizeMax`. - At which point, decoding can resume from the beginning of the buffer. - Note that already decoded data stored in the buffer should be flushed before being overwritten. - - There are alternatives possible, for example using two or more buffers of size `windowSize` each, though they consume more memory. - - Finally, if you control the compression process, you can also ignore all buffer size rules, - as long as the encoder and decoder progress in "lock-step", - aka use exactly the same buffer sizes, break contiguity at the same place, etc. - - Once buffers are setup, start decompression, with ZSTD_decompressBegin(). - If decompression requires a dictionary, use ZSTD_decompressBegin_usingDict() or ZSTD_decompressBegin_usingDDict(). - - Then use ZSTD_nextSrcSizeToDecompress() and ZSTD_decompressContinue() alternatively. - ZSTD_nextSrcSizeToDecompress() tells how many bytes to provide as 'srcSize' to ZSTD_decompressContinue(). - ZSTD_decompressContinue() requires this _exact_ amount of bytes, or it will fail. - - result of ZSTD_decompressContinue() is the number of bytes regenerated within 'dst' (necessarily <= dstCapacity). - It can be zero : it just means ZSTD_decompressContinue() has decoded some metadata item. - It can also be an error code, which can be tested with ZSTD_isError(). - - A frame is fully decoded when ZSTD_nextSrcSizeToDecompress() returns zero. - Context can then be reset to start a new decompression. - - Note : it's possible to know if next input to present is a header or a block, using ZSTD_nextInputType(). - This information is not required to properly decode a frame. - - == Special case : skippable frames == - - Skippable frames allow integration of user-defined data into a flow of concatenated frames. - Skippable frames will be ignored (skipped) by decompressor. - The format of skippable frames is as follows : - a) Skippable frame ID - 4 Bytes, Little endian format, any value from 0x184D2A50 to 0x184D2A5F - b) Frame Size - 4 Bytes, Little endian format, unsigned 32-bits - c) Frame Content - any content (User Data) of length equal to Frame Size - For skippable frames ZSTD_getFrameHeader() returns zfhPtr->frameType==ZSTD_skippableFrame. - For skippable frames ZSTD_decompressContinue() always returns 0 : it only skips the content. -*/ - -/*===== Buffer-less streaming decompression functions =====*/ -typedef enum { ZSTD_frame, ZSTD_skippableFrame } ZSTD_frameType_e; -typedef struct { - unsigned long long frameContentSize; /* if == ZSTD_CONTENTSIZE_UNKNOWN, it means this field is not available. 0 means "empty" */ - unsigned long long windowSize; /* can be very large, up to <= frameContentSize */ - unsigned blockSizeMax; - ZSTD_frameType_e frameType; /* if == ZSTD_skippableFrame, frameContentSize is the size of skippable content */ - unsigned headerSize; - unsigned dictID; - unsigned checksumFlag; - unsigned _reserved1; - unsigned _reserved2; -} ZSTD_frameHeader; - -/*! ZSTD_getFrameHeader() : - * decode Frame Header, or requires larger `srcSize`. - * @return : 0, `zfhPtr` is correctly filled, - * >0, `srcSize` is too small, value is wanted `srcSize` amount, - * or an error code, which can be tested using ZSTD_isError() */ -ZSTDLIB_STATIC_API size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize); /**< doesn't consume input */ -/*! ZSTD_getFrameHeader_advanced() : - * same as ZSTD_getFrameHeader(), - * with added capability to select a format (like ZSTD_f_zstd1_magicless) */ -ZSTDLIB_STATIC_API size_t ZSTD_getFrameHeader_advanced(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize, ZSTD_format_e format); -ZSTDLIB_STATIC_API size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long long frameContentSize); /**< when frame content size is not known, pass in frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN */ - -ZSTDLIB_STATIC_API size_t ZSTD_decompressBegin(ZSTD_DCtx* dctx); -ZSTDLIB_STATIC_API size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx* dctx, const void* dict, size_t dictSize); -ZSTDLIB_STATIC_API size_t ZSTD_decompressBegin_usingDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict); - -ZSTDLIB_STATIC_API size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx* dctx); -ZSTDLIB_STATIC_API size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); - -/* misc */ -ZSTD_DEPRECATED("This function will likely be removed in the next minor release. It is misleading and has very limited utility.") -ZSTDLIB_STATIC_API void ZSTD_copyDCtx(ZSTD_DCtx* dctx, const ZSTD_DCtx* preparedDCtx); -typedef enum { ZSTDnit_frameHeader, ZSTDnit_blockHeader, ZSTDnit_block, ZSTDnit_lastBlock, ZSTDnit_checksum, ZSTDnit_skippableFrame } ZSTD_nextInputType_e; -ZSTDLIB_STATIC_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx); - - - - -/* ============================ */ -/** Block level API */ -/* ============================ */ - -/*! - Block functions produce and decode raw zstd blocks, without frame metadata. - Frame metadata cost is typically ~12 bytes, which can be non-negligible for very small blocks (< 100 bytes). - But users will have to take in charge needed metadata to regenerate data, such as compressed and content sizes. - - A few rules to respect : - - Compressing and decompressing require a context structure - + Use ZSTD_createCCtx() and ZSTD_createDCtx() - - It is necessary to init context before starting - + compression : any ZSTD_compressBegin*() variant, including with dictionary - + decompression : any ZSTD_decompressBegin*() variant, including with dictionary - - Block size is limited, it must be <= ZSTD_getBlockSize() <= ZSTD_BLOCKSIZE_MAX == 128 KB - + If input is larger than a block size, it's necessary to split input data into multiple blocks - + For inputs larger than a single block, consider using regular ZSTD_compress() instead. - Frame metadata is not that costly, and quickly becomes negligible as source size grows larger than a block. - - When a block is considered not compressible enough, ZSTD_compressBlock() result will be 0 (zero) ! - ===> In which case, nothing is produced into `dst` ! - + User __must__ test for such outcome and deal directly with uncompressed data - + A block cannot be declared incompressible if ZSTD_compressBlock() return value was != 0. - Doing so would mess up with statistics history, leading to potential data corruption. - + ZSTD_decompressBlock() _doesn't accept uncompressed data as input_ !! - + In case of multiple successive blocks, should some of them be uncompressed, - decoder must be informed of their existence in order to follow proper history. - Use ZSTD_insertBlock() for such a case. -*/ - -/*===== Raw zstd block functions =====*/ -ZSTDLIB_STATIC_API size_t ZSTD_getBlockSize (const ZSTD_CCtx* cctx); -ZSTDLIB_STATIC_API size_t ZSTD_compressBlock (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); -ZSTDLIB_STATIC_API size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); -ZSTDLIB_STATIC_API size_t ZSTD_insertBlock (ZSTD_DCtx* dctx, const void* blockStart, size_t blockSize); /**< insert uncompressed block into `dctx` history. Useful for multi-blocks decompression. */ - - /* ********************* BLOCK-LEVEL SEQUENCE PRODUCER API ********************* * * *** OVERVIEW *** @@ -2967,6 +2800,219 @@ ZSTD_registerSequenceProducer( ZSTD_sequenceProducer_F* sequenceProducer ); + +/********************************************************************* +* Buffer-less and synchronous inner streaming functions (DEPRECATED) +* +* This API is deprecated, and will be removed in a future version. +* It allows streaming (de)compression with user allocated buffers. +* However, it is hard to use, and not as well tested as the rest of +* our API. +* +* Please use the normal streaming API instead: ZSTD_compressStream2, +* and ZSTD_decompressStream. +* If there is functionality that you need, but it doesn't provide, +* please open an issue on our GitHub. +********************************************************************* */ + +/** + Buffer-less streaming compression (synchronous mode) + + A ZSTD_CCtx object is required to track streaming operations. + Use ZSTD_createCCtx() / ZSTD_freeCCtx() to manage resource. + ZSTD_CCtx object can be re-used multiple times within successive compression operations. + + Start by initializing a context. + Use ZSTD_compressBegin(), or ZSTD_compressBegin_usingDict() for dictionary compression. + + Then, consume your input using ZSTD_compressContinue(). + There are some important considerations to keep in mind when using this advanced function : + - ZSTD_compressContinue() has no internal buffer. It uses externally provided buffers only. + - Interface is synchronous : input is consumed entirely and produces 1+ compressed blocks. + - Caller must ensure there is enough space in `dst` to store compressed data under worst case scenario. + Worst case evaluation is provided by ZSTD_compressBound(). + ZSTD_compressContinue() doesn't guarantee recover after a failed compression. + - ZSTD_compressContinue() presumes prior input ***is still accessible and unmodified*** (up to maximum distance size, see WindowLog). + It remembers all previous contiguous blocks, plus one separated memory segment (which can itself consists of multiple contiguous blocks) + - ZSTD_compressContinue() detects that prior input has been overwritten when `src` buffer overlaps. + In which case, it will "discard" the relevant memory section from its history. + + Finish a frame with ZSTD_compressEnd(), which will write the last block(s) and optional checksum. + It's possible to use srcSize==0, in which case, it will write a final empty block to end the frame. + Without last block mark, frames are considered unfinished (hence corrupted) by compliant decoders. + + `ZSTD_CCtx` object can be re-used (ZSTD_compressBegin()) to compress again. +*/ + +/*===== Buffer-less streaming compression functions =====*/ +ZSTD_DEPRECATED("The buffer-less API is deprecated in favor of the normal streaming API. See docs.") +ZSTDLIB_STATIC_API size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel); +ZSTD_DEPRECATED("The buffer-less API is deprecated in favor of the normal streaming API. See docs.") +ZSTDLIB_STATIC_API size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel); +ZSTD_DEPRECATED("The buffer-less API is deprecated in favor of the normal streaming API. See docs.") +ZSTDLIB_STATIC_API size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); /**< note: fails if cdict==NULL */ + +ZSTD_DEPRECATED("This function will likely be removed in a future release. It is misleading and has very limited utility.") +ZSTDLIB_STATIC_API +size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned long long pledgedSrcSize); /**< note: if pledgedSrcSize is not known, use ZSTD_CONTENTSIZE_UNKNOWN */ + +ZSTD_DEPRECATED("The buffer-less API is deprecated in favor of the normal streaming API. See docs.") +ZSTDLIB_STATIC_API size_t ZSTD_compressContinue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); +ZSTD_DEPRECATED("The buffer-less API is deprecated in favor of the normal streaming API. See docs.") +ZSTDLIB_STATIC_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); + +/* The ZSTD_compressBegin_advanced() and ZSTD_compressBegin_usingCDict_advanced() are now DEPRECATED and will generate a compiler warning */ +ZSTD_DEPRECATED("use advanced API to access custom parameters") +ZSTDLIB_STATIC_API +size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize : If srcSize is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN */ +ZSTD_DEPRECATED("use advanced API to access custom parameters") +ZSTDLIB_STATIC_API +size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_CDict* const cdict, ZSTD_frameParameters const fParams, unsigned long long const pledgedSrcSize); /* compression parameters are already set within cdict. pledgedSrcSize must be correct. If srcSize is not known, use macro ZSTD_CONTENTSIZE_UNKNOWN */ +/** + Buffer-less streaming decompression (synchronous mode) + + A ZSTD_DCtx object is required to track streaming operations. + Use ZSTD_createDCtx() / ZSTD_freeDCtx() to manage it. + A ZSTD_DCtx object can be re-used multiple times. + + First typical operation is to retrieve frame parameters, using ZSTD_getFrameHeader(). + Frame header is extracted from the beginning of compressed frame, so providing only the frame's beginning is enough. + Data fragment must be large enough to ensure successful decoding. + `ZSTD_frameHeaderSize_max` bytes is guaranteed to always be large enough. + result : 0 : successful decoding, the `ZSTD_frameHeader` structure is correctly filled. + >0 : `srcSize` is too small, please provide at least result bytes on next attempt. + errorCode, which can be tested using ZSTD_isError(). + + It fills a ZSTD_frameHeader structure with important information to correctly decode the frame, + such as the dictionary ID, content size, or maximum back-reference distance (`windowSize`). + Note that these values could be wrong, either because of data corruption, or because a 3rd party deliberately spoofs false information. + As a consequence, check that values remain within valid application range. + For example, do not allocate memory blindly, check that `windowSize` is within expectation. + Each application can set its own limits, depending on local restrictions. + For extended interoperability, it is recommended to support `windowSize` of at least 8 MB. + + ZSTD_decompressContinue() needs previous data blocks during decompression, up to `windowSize` bytes. + ZSTD_decompressContinue() is very sensitive to contiguity, + if 2 blocks don't follow each other, make sure that either the compressor breaks contiguity at the same place, + or that previous contiguous segment is large enough to properly handle maximum back-reference distance. + There are multiple ways to guarantee this condition. + + The most memory efficient way is to use a round buffer of sufficient size. + Sufficient size is determined by invoking ZSTD_decodingBufferSize_min(), + which can return an error code if required value is too large for current system (in 32-bits mode). + In a round buffer methodology, ZSTD_decompressContinue() decompresses each block next to previous one, + up to the moment there is not enough room left in the buffer to guarantee decoding another full block, + which maximum size is provided in `ZSTD_frameHeader` structure, field `blockSizeMax`. + At which point, decoding can resume from the beginning of the buffer. + Note that already decoded data stored in the buffer should be flushed before being overwritten. + + There are alternatives possible, for example using two or more buffers of size `windowSize` each, though they consume more memory. + + Finally, if you control the compression process, you can also ignore all buffer size rules, + as long as the encoder and decoder progress in "lock-step", + aka use exactly the same buffer sizes, break contiguity at the same place, etc. + + Once buffers are setup, start decompression, with ZSTD_decompressBegin(). + If decompression requires a dictionary, use ZSTD_decompressBegin_usingDict() or ZSTD_decompressBegin_usingDDict(). + + Then use ZSTD_nextSrcSizeToDecompress() and ZSTD_decompressContinue() alternatively. + ZSTD_nextSrcSizeToDecompress() tells how many bytes to provide as 'srcSize' to ZSTD_decompressContinue(). + ZSTD_decompressContinue() requires this _exact_ amount of bytes, or it will fail. + + result of ZSTD_decompressContinue() is the number of bytes regenerated within 'dst' (necessarily <= dstCapacity). + It can be zero : it just means ZSTD_decompressContinue() has decoded some metadata item. + It can also be an error code, which can be tested with ZSTD_isError(). + + A frame is fully decoded when ZSTD_nextSrcSizeToDecompress() returns zero. + Context can then be reset to start a new decompression. + + Note : it's possible to know if next input to present is a header or a block, using ZSTD_nextInputType(). + This information is not required to properly decode a frame. + + == Special case : skippable frames == + + Skippable frames allow integration of user-defined data into a flow of concatenated frames. + Skippable frames will be ignored (skipped) by decompressor. + The format of skippable frames is as follows : + a) Skippable frame ID - 4 Bytes, Little endian format, any value from 0x184D2A50 to 0x184D2A5F + b) Frame Size - 4 Bytes, Little endian format, unsigned 32-bits + c) Frame Content - any content (User Data) of length equal to Frame Size + For skippable frames ZSTD_getFrameHeader() returns zfhPtr->frameType==ZSTD_skippableFrame. + For skippable frames ZSTD_decompressContinue() always returns 0 : it only skips the content. +*/ + +/*===== Buffer-less streaming decompression functions =====*/ + +ZSTDLIB_STATIC_API size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long long frameContentSize); /**< when frame content size is not known, pass in frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN */ + +ZSTDLIB_STATIC_API size_t ZSTD_decompressBegin(ZSTD_DCtx* dctx); +ZSTDLIB_STATIC_API size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx* dctx, const void* dict, size_t dictSize); +ZSTDLIB_STATIC_API size_t ZSTD_decompressBegin_usingDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict); + +ZSTDLIB_STATIC_API size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx* dctx); +ZSTDLIB_STATIC_API size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); + +/* misc */ +ZSTD_DEPRECATED("This function will likely be removed in the next minor release. It is misleading and has very limited utility.") +ZSTDLIB_STATIC_API void ZSTD_copyDCtx(ZSTD_DCtx* dctx, const ZSTD_DCtx* preparedDCtx); +typedef enum { ZSTDnit_frameHeader, ZSTDnit_blockHeader, ZSTDnit_block, ZSTDnit_lastBlock, ZSTDnit_checksum, ZSTDnit_skippableFrame } ZSTD_nextInputType_e; +ZSTDLIB_STATIC_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx); + + + + +/* ========================================= */ +/** Block level API (DEPRECATED) */ +/* ========================================= */ + +/*! + + This API is deprecated in favor of the regular compression API. + You can get the frame header down to 2 bytes by setting: + - ZSTD_c_format = ZSTD_f_zstd1_magicless + - ZSTD_c_contentSizeFlag = 0 + - ZSTD_c_checksumFlag = 0 + - ZSTD_c_dictIDFlag = 0 + + This API is not as well tested as our normal API, so we recommend not using it. + We will be removing it in a future version. If the normal API doesn't provide + the functionality you need, please open a GitHub issue. + + Block functions produce and decode raw zstd blocks, without frame metadata. + Frame metadata cost is typically ~12 bytes, which can be non-negligible for very small blocks (< 100 bytes). + But users will have to take in charge needed metadata to regenerate data, such as compressed and content sizes. + + A few rules to respect : + - Compressing and decompressing require a context structure + + Use ZSTD_createCCtx() and ZSTD_createDCtx() + - It is necessary to init context before starting + + compression : any ZSTD_compressBegin*() variant, including with dictionary + + decompression : any ZSTD_decompressBegin*() variant, including with dictionary + - Block size is limited, it must be <= ZSTD_getBlockSize() <= ZSTD_BLOCKSIZE_MAX == 128 KB + + If input is larger than a block size, it's necessary to split input data into multiple blocks + + For inputs larger than a single block, consider using regular ZSTD_compress() instead. + Frame metadata is not that costly, and quickly becomes negligible as source size grows larger than a block. + - When a block is considered not compressible enough, ZSTD_compressBlock() result will be 0 (zero) ! + ===> In which case, nothing is produced into `dst` ! + + User __must__ test for such outcome and deal directly with uncompressed data + + A block cannot be declared incompressible if ZSTD_compressBlock() return value was != 0. + Doing so would mess up with statistics history, leading to potential data corruption. + + ZSTD_decompressBlock() _doesn't accept uncompressed data as input_ !! + + In case of multiple successive blocks, should some of them be uncompressed, + decoder must be informed of their existence in order to follow proper history. + Use ZSTD_insertBlock() for such a case. +*/ + +/*===== Raw zstd block functions =====*/ +ZSTD_DEPRECATED("The block API is deprecated in favor of the normal compression API. See docs.") +ZSTDLIB_STATIC_API size_t ZSTD_getBlockSize (const ZSTD_CCtx* cctx); +ZSTD_DEPRECATED("The block API is deprecated in favor of the normal compression API. See docs.") +ZSTDLIB_STATIC_API size_t ZSTD_compressBlock (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); +ZSTD_DEPRECATED("The block API is deprecated in favor of the normal compression API. See docs.") +ZSTDLIB_STATIC_API size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); +ZSTD_DEPRECATED("The block API is deprecated in favor of the normal compression API. See docs.") +ZSTDLIB_STATIC_API size_t ZSTD_insertBlock (ZSTD_DCtx* dctx, const void* blockStart, size_t blockSize); /**< insert uncompressed block into `dctx` history. Useful for multi-blocks decompression. */ + #endif /* ZSTD_H_ZSTD_STATIC_LINKING_ONLY */ #if defined (__cplusplus) diff --git a/programs/Makefile b/programs/Makefile index fcff41dc1..8507abef3 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -225,7 +225,7 @@ zstd-noxz : zstd .PHONY: zstd-dll zstd-dll : LDFLAGS+= -L$(LIBZSTD) zstd-dll : LDLIBS += -lzstd -zstd-dll : ZSTDLIB_LOCAL_SRC = xxhash.c +zstd-dll : ZSTDLIB_LOCAL_SRC = xxhash.c pool.c threading.c zstd-dll : zstd diff --git a/programs/benchzstd.c b/programs/benchzstd.c index a76db5f37..9bc3628ee 100644 --- a/programs/benchzstd.c +++ b/programs/benchzstd.c @@ -697,9 +697,9 @@ static BMK_benchOutcome_t BMK_benchCLevel(const void* srcBuffer, size_t benchedS displayLevel, displayName, adv); } -BMK_benchOutcome_t BMK_syntheticTest(int cLevel, double compressibility, - const ZSTD_compressionParameters* compressionParams, - int displayLevel, const BMK_advancedParams_t* adv) +int BMK_syntheticTest(int cLevel, double compressibility, + const ZSTD_compressionParameters* compressionParams, + int displayLevel, const BMK_advancedParams_t* adv) { char name[20] = {0}; size_t const benchedSize = 10000000; @@ -707,12 +707,16 @@ BMK_benchOutcome_t BMK_syntheticTest(int cLevel, double compressibility, BMK_benchOutcome_t res; if (cLevel > ZSTD_maxCLevel()) { - RETURN_ERROR(15, BMK_benchOutcome_t, "Invalid Compression Level"); + DISPLAYLEVEL(1, "Invalid Compression Level"); + return 15; } /* Memory allocation */ srcBuffer = malloc(benchedSize); - if (!srcBuffer) RETURN_ERROR(21, BMK_benchOutcome_t, "not enough memory"); + if (!srcBuffer) { + DISPLAYLEVEL(1, "allocation error : not enough memory"); + return 16; + } /* Fill input buffer */ RDG_genBuffer(srcBuffer, benchedSize, compressibility, 0.0, 0); @@ -728,7 +732,7 @@ BMK_benchOutcome_t BMK_syntheticTest(int cLevel, double compressibility, /* clean up */ free(srcBuffer); - return res; + return !BMK_isSuccessful_benchOutcome(res); } @@ -790,7 +794,7 @@ static int BMK_loadFiles(void* buffer, size_t bufferSize, return 0; } -BMK_benchOutcome_t BMK_benchFilesAdvanced( +int BMK_benchFilesAdvanced( const char* const * fileNamesTable, unsigned nbFiles, const char* dictFileName, int cLevel, const ZSTD_compressionParameters* compressionParams, @@ -805,19 +809,25 @@ BMK_benchOutcome_t BMK_benchFilesAdvanced( U64 const totalSizeToLoad = UTIL_getTotalFileSize(fileNamesTable, nbFiles); if (!nbFiles) { - RETURN_ERROR(14, BMK_benchOutcome_t, "No Files to Benchmark"); + DISPLAYLEVEL(1, "No Files to Benchmark"); + return 13; } if (cLevel > ZSTD_maxCLevel()) { - RETURN_ERROR(15, BMK_benchOutcome_t, "Invalid Compression Level"); + DISPLAYLEVEL(1, "Invalid Compression Level"); + return 14; } if (totalSizeToLoad == UTIL_FILESIZE_UNKNOWN) { - RETURN_ERROR(9, BMK_benchOutcome_t, "Error loading files"); + DISPLAYLEVEL(1, "Error loading files"); + return 15; } fileSizes = (size_t*)calloc(nbFiles, sizeof(size_t)); - if (!fileSizes) RETURN_ERROR(12, BMK_benchOutcome_t, "not enough memory for fileSizes"); + if (!fileSizes) { + DISPLAYLEVEL(1, "not enough memory for fileSizes"); + return 16; + } /* Load dictionary */ if (dictFileName != NULL) { @@ -825,18 +835,21 @@ BMK_benchOutcome_t BMK_benchFilesAdvanced( if (dictFileSize == UTIL_FILESIZE_UNKNOWN) { DISPLAYLEVEL(1, "error loading %s : %s \n", dictFileName, strerror(errno)); free(fileSizes); - RETURN_ERROR(9, BMK_benchOutcome_t, "benchmark aborted"); + DISPLAYLEVEL(1, "benchmark aborted"); + return 17; } if (dictFileSize > 64 MB) { free(fileSizes); - RETURN_ERROR(10, BMK_benchOutcome_t, "dictionary file %s too large", dictFileName); + DISPLAYLEVEL(1, "dictionary file %s too large", dictFileName); + return 18; } dictBufferSize = (size_t)dictFileSize; dictBuffer = malloc(dictBufferSize); if (dictBuffer==NULL) { free(fileSizes); - RETURN_ERROR(11, BMK_benchOutcome_t, "not enough memory for dictionary (%u bytes)", + DISPLAYLEVEL(1, "not enough memory for dictionary (%u bytes)", (unsigned)dictBufferSize); + return 19; } { int const errorCode = BMK_loadFiles(dictBuffer, dictBufferSize, @@ -858,7 +871,8 @@ BMK_benchOutcome_t BMK_benchFilesAdvanced( if (!srcBuffer) { free(dictBuffer); free(fileSizes); - RETURN_ERROR(12, BMK_benchOutcome_t, "not enough memory"); + DISPLAYLEVEL(1, "not enough memory for srcBuffer"); + return 20; } /* Load input buffer */ @@ -886,12 +900,11 @@ _cleanUp: free(srcBuffer); free(dictBuffer); free(fileSizes); - return res; + return !BMK_isSuccessful_benchOutcome(res); } -BMK_benchOutcome_t BMK_benchFiles( - const char* const * fileNamesTable, unsigned nbFiles, +int BMK_benchFiles(const char* const * fileNamesTable, unsigned nbFiles, const char* dictFileName, int cLevel, const ZSTD_compressionParameters* compressionParams, int displayLevel) diff --git a/programs/benchzstd.h b/programs/benchzstd.h index aa683dfc2..f14a68192 100644 --- a/programs/benchzstd.h +++ b/programs/benchzstd.h @@ -81,21 +81,13 @@ BMK_benchResult_t BMK_extract_benchResult(BMK_benchOutcome_t outcome); * 2 : + result + interaction + warnings; * 3 : + information; * 4 : + debug - * @return: - * a variant, which expresses either an error, or a valid result. - * Use BMK_isSuccessful_benchOutcome() to check if function was successful. - * If yes, extract the valid result with BMK_extract_benchResult(), - * it will contain : - * .cSpeed: compression speed in bytes per second, - * .dSpeed: decompression speed in bytes per second, - * .cSize : compressed size, in bytes - * .cMem : memory budget required for the compression context + * @return: 0 on success, !0 on error */ -BMK_benchOutcome_t BMK_benchFiles( - const char* const * fileNamesTable, unsigned nbFiles, - const char* dictFileName, - int cLevel, const ZSTD_compressionParameters* compressionParams, - int displayLevel); +int BMK_benchFiles( + const char* const * fileNamesTable, unsigned nbFiles, + const char* dictFileName, + int cLevel, const ZSTD_compressionParameters* compressionParams, + int displayLevel); typedef enum { @@ -126,11 +118,11 @@ BMK_advancedParams_t BMK_initAdvancedParams(void); /*! BMK_benchFilesAdvanced(): * Same as BMK_benchFiles(), * with more controls, provided through advancedParams_t structure */ -BMK_benchOutcome_t BMK_benchFilesAdvanced( - const char* const * fileNamesTable, unsigned nbFiles, - const char* dictFileName, - int cLevel, const ZSTD_compressionParameters* compressionParams, - int displayLevel, const BMK_advancedParams_t* adv); +int BMK_benchFilesAdvanced( + const char* const * fileNamesTable, unsigned nbFiles, + const char* dictFileName, + int cLevel, const ZSTD_compressionParameters* compressionParams, + int displayLevel, const BMK_advancedParams_t* adv); /*! BMK_syntheticTest() -- called from zstdcli */ /* Generates a sample with datagen, using compressibility argument */ @@ -139,20 +131,11 @@ BMK_benchOutcome_t BMK_benchFilesAdvanced( * compressionParams - basic compression Parameters * displayLevel - see benchFiles * adv - see advanced_Params_t - * @return: - * a variant, which expresses either an error, or a valid result. - * Use BMK_isSuccessful_benchOutcome() to check if function was successful. - * If yes, extract the valid result with BMK_extract_benchResult(), - * it will contain : - * .cSpeed: compression speed in bytes per second, - * .dSpeed: decompression speed in bytes per second, - * .cSize : compressed size, in bytes - * .cMem : memory budget required for the compression context + * @return: 0 on success, !0 on error */ -BMK_benchOutcome_t BMK_syntheticTest( - int cLevel, double compressibility, - const ZSTD_compressionParameters* compressionParams, - int displayLevel, const BMK_advancedParams_t* adv); +int BMK_syntheticTest(int cLevel, double compressibility, + const ZSTD_compressionParameters* compressionParams, + int displayLevel, const BMK_advancedParams_t* adv); @@ -190,8 +173,8 @@ BMK_benchOutcome_t BMK_benchMem(const void* srcBuffer, size_t srcSize, int displayLevel, const char* displayName); -/* BMK_benchMemAdvanced() : same as BMK_benchMem() - * with following additional options : +/* BMK_benchMemAdvanced() : used by Paramgrill + * same as BMK_benchMem() with following additional options : * dstBuffer - destination buffer to write compressed output in, NULL if none provided. * dstCapacity - capacity of destination buffer, give 0 if dstBuffer = NULL * adv = see advancedParams_t diff --git a/programs/fileio.c b/programs/fileio.c index 9a8300cdd..4f75ff474 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -485,6 +485,11 @@ void FIO_setPassThroughFlag(FIO_prefs_t* const prefs, int value) { prefs->passThrough = (value != 0); } +void FIO_setMMapDict(FIO_prefs_t* const prefs, ZSTD_paramSwitch_e value) +{ + prefs->mmapDict = value; +} + /* FIO_ctx_t functions */ void FIO_setHasStdoutOutput(FIO_ctx_t* const fCtx, int value) { @@ -576,6 +581,8 @@ FIO_openDstFile(FIO_ctx_t* fCtx, FIO_prefs_t* const prefs, const char* srcFileName, const char* dstFileName, const int mode) { + int isDstRegFile; + if (prefs->testMode) return NULL; /* do not open file in test mode */ assert(dstFileName != NULL); @@ -595,11 +602,16 @@ FIO_openDstFile(FIO_ctx_t* fCtx, FIO_prefs_t* const prefs, return NULL; } + isDstRegFile = UTIL_isRegularFile(dstFileName); /* invoke once */ if (prefs->sparseFileSupport == 1) { + if (!isDstRegFile) { + prefs->sparseFileSupport = 0; + DISPLAYLEVEL(4, "Sparse File Support is disabled when output is not a file \n"); + } prefs->sparseFileSupport = ZSTD_SPARSE_DEFAULT; } - if (UTIL_isRegularFile(dstFileName)) { + if (isDstRegFile) { /* Check if destination file already exists */ #if !defined(_WIN32) /* this test does not work on Windows : @@ -644,39 +656,34 @@ FIO_openDstFile(FIO_ctx_t* fCtx, FIO_prefs_t* const prefs, #endif if (f == NULL) { DISPLAYLEVEL(1, "zstd: %s: %s\n", dstFileName, strerror(errno)); + } else { + /* An increased buffer size can provide a significant performance + * boost on some platforms. Note that providing a NULL buf with a + * size that's not 0 is not defined in ANSI C, but is defined in an + * extension. There are three possibilities here: + * 1. Libc supports the extended version and everything is good. + * 2. Libc ignores the size when buf is NULL, in which case + * everything will continue as if we didn't call `setvbuf()`. + * 3. We fail the call and execution continues but a warning + * message might be shown. + * In all cases due execution continues. For now, I believe that + * this is a more cost-effective solution than managing the buffers + * allocations ourselves (will require an API change). + */ + if (setvbuf(f, NULL, _IOFBF, 1 MB)) { + DISPLAYLEVEL(2, "Warning: setvbuf failed for %s\n", dstFileName); + } } - /* An increased buffer size can provide a significant performance boost on some platforms. - * Note that providing a NULL buf with a size that's not 0 is not defined in ANSI C, but is defined - * in an extension. There are three possibilities here - - * 1. Libc supports the extended version and everything is good. - * 2. Libc ignores the size when buf is NULL, in which case everything will continue as if we didn't - * call `setvbuf`. - * 3. We fail the call and execution continues but a warning message might be shown. - * In all cases due execution continues. For now, I believe that this is a more cost-effective - * solution than managing the buffers allocations ourselves (will require an API change). */ - if(setvbuf(f, NULL, _IOFBF, 1 MB)) - DISPLAYLEVEL(2, "Warning: setvbuf failed for %s\n", dstFileName); return f; } } -/*! FIO_createDictBuffer() : - * creates a buffer, pointed by `*bufferPtr`, - * loads `filename` content into it, up to DICTSIZE_MAX bytes. - * @return : loaded size - * if fileName==NULL, returns 0 and a NULL pointer + +/* FIO_getDictFileStat() : */ -static size_t FIO_createDictBuffer(void** bufferPtr, const char* fileName, FIO_prefs_t* const prefs, stat_t* dictFileStat) -{ - FILE* fileHandle; - U64 fileSize; - - assert(bufferPtr != NULL); +static void FIO_getDictFileStat(const char* fileName, stat_t* dictFileStat) { assert(dictFileStat != NULL); - *bufferPtr = NULL; - if (fileName == NULL) return 0; - - DISPLAYLEVEL(4,"Loading %s as dictionary \n", fileName); + if (fileName == NULL) return; if (!UTIL_stat(fileName, dictFileStat)) { EXM_THROW(31, "Stat failed on dictionary file %s: %s", fileName, strerror(errno)); @@ -685,6 +692,26 @@ static size_t FIO_createDictBuffer(void** bufferPtr, const char* fileName, FIO_p if (!UTIL_isRegularFileStat(dictFileStat)) { EXM_THROW(32, "Dictionary %s must be a regular file.", fileName); } +} + +/* FIO_setDictBufferMalloc() : + * allocates a buffer, pointed by `dict->dictBuffer`, + * loads `filename` content into it, up to DICTSIZE_MAX bytes. + * @return : loaded size + * if fileName==NULL, returns 0 and a NULL pointer + */ +static size_t FIO_setDictBufferMalloc(FIO_Dict_t* dict, const char* fileName, FIO_prefs_t* const prefs, stat_t* dictFileStat) +{ + FILE* fileHandle; + U64 fileSize; + void** bufferPtr = &dict->dictBuffer; + + assert(bufferPtr != NULL); + assert(dictFileStat != NULL); + *bufferPtr = NULL; + if (fileName == NULL) return 0; + + DISPLAYLEVEL(4,"Loading %s as dictionary \n", fileName); fileHandle = fopen(fileName, "rb"); @@ -712,6 +739,130 @@ static size_t FIO_createDictBuffer(void** bufferPtr, const char* fileName, FIO_p return (size_t)fileSize; } +#if (PLATFORM_POSIX_VERSION > 0) +#include +static void FIO_munmap(FIO_Dict_t* dict) +{ + munmap(dict->dictBuffer, dict->dictBufferSize); + dict->dictBuffer = NULL; + dict->dictBufferSize = 0; +} +static size_t FIO_setDictBufferMMap(FIO_Dict_t* dict, const char* fileName, FIO_prefs_t* const prefs, stat_t* dictFileStat) +{ + int fileHandle; + U64 fileSize; + void** bufferPtr = &dict->dictBuffer; + + assert(bufferPtr != NULL); + assert(dictFileStat != NULL); + *bufferPtr = NULL; + if (fileName == NULL) return 0; + + DISPLAYLEVEL(4,"Loading %s as dictionary \n", fileName); + + fileHandle = open(fileName, O_RDONLY); + + if (fileHandle == -1) { + EXM_THROW(33, "Couldn't open dictionary %s: %s", fileName, strerror(errno)); + } + + fileSize = UTIL_getFileSizeStat(dictFileStat); + { + size_t const dictSizeMax = prefs->patchFromMode ? prefs->memLimit : DICTSIZE_MAX; + if (fileSize > dictSizeMax) { + EXM_THROW(34, "Dictionary file %s is too large (> %u bytes)", + fileName, (unsigned)dictSizeMax); /* avoid extreme cases */ + } + } + + *bufferPtr = mmap(NULL, (size_t)fileSize, PROT_READ, MAP_PRIVATE, fileHandle, 0); + if (*bufferPtr==NULL) EXM_THROW(34, "%s", strerror(errno)); + + close(fileHandle); + return (size_t)fileSize; +} +#elif defined(_MSC_VER) || defined(_WIN32) +#include +static void FIO_munmap(FIO_Dict_t* dict) +{ + UnmapViewOfFile(dict->dictBuffer); + CloseHandle(dict->dictHandle); + dict->dictBuffer = NULL; + dict->dictBufferSize = 0; +} +static size_t FIO_setDictBufferMMap(FIO_Dict_t* dict, const char* fileName, FIO_prefs_t* const prefs, stat_t* dictFileStat) +{ + HANDLE fileHandle, mapping; + U64 fileSize; + void** bufferPtr = &dict->dictBuffer; + + assert(bufferPtr != NULL); + assert(dictFileStat != NULL); + *bufferPtr = NULL; + if (fileName == NULL) return 0; + + DISPLAYLEVEL(4,"Loading %s as dictionary \n", fileName); + + fileHandle = CreateFileA(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); + + if (fileHandle == INVALID_HANDLE_VALUE) { + EXM_THROW(33, "Couldn't open dictionary %s: %s", fileName, strerror(errno)); + } + + fileSize = UTIL_getFileSizeStat(dictFileStat); + { + size_t const dictSizeMax = prefs->patchFromMode ? prefs->memLimit : DICTSIZE_MAX; + if (fileSize > dictSizeMax) { + EXM_THROW(34, "Dictionary file %s is too large (> %u bytes)", + fileName, (unsigned)dictSizeMax); /* avoid extreme cases */ + } + } + + mapping = CreateFileMapping(fileHandle, NULL, PAGE_READONLY, 0, 0, NULL); + if (mapping == NULL) { + EXM_THROW(35, "Couldn't map dictionary %s: %s", fileName, strerror(errno)); + } + + *bufferPtr = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, (DWORD)fileSize); /* we can only cast to DWORD here because dictSize <= 2GB */ + if (*bufferPtr==NULL) EXM_THROW(36, "%s", strerror(errno)); + + dict->dictHandle = fileHandle; + return (size_t)fileSize; +} +#else +static size_t FIO_setDictBufferMMap(FIO_Dict_t* dict, const char* fileName, FIO_prefs_t* const prefs, stat_t* dictFileStat) +{ + return FIO_setDictBufferMalloc(dict, fileName, prefs, dictFileStat); +} +static void FIO_munmap(FIO_Dict_t* dict) { + free(dict->dictBuffer); + dict->dictBuffer = NULL; + dict->dictBufferSize = 0; +} +#endif + +static void FIO_freeDict(FIO_Dict_t* dict) { + if (dict->dictBufferType == FIO_mallocDict) { + free(dict->dictBuffer); + dict->dictBuffer = NULL; + dict->dictBufferSize = 0; + } else if (dict->dictBufferType == FIO_mmapDict) { + FIO_munmap(dict); + } else { + assert(0); /* Should not reach this case */ + } +} + +static void FIO_initDict(FIO_Dict_t* dict, const char* fileName, FIO_prefs_t* const prefs, stat_t* dictFileStat, FIO_dictBufferType_t dictBufferType) { + dict->dictBufferType = dictBufferType; + if (dict->dictBufferType == FIO_mallocDict) { + dict->dictBufferSize = FIO_setDictBufferMalloc(dict, fileName, prefs, dictFileStat); + } else if (dict->dictBufferType == FIO_mmapDict) { + dict->dictBufferSize = FIO_setDictBufferMMap(dict, fileName, prefs, dictFileStat); + } else { + assert(0); /* Should not reach this case */ + } +} /* FIO_checkFilenameCollisions() : @@ -914,8 +1065,7 @@ static ZSTD_outBuffer setOutBuffer(void* buf, size_t s, size_t pos) * Compression ************************************************************************/ typedef struct { - void* dictBuffer; - size_t dictBufferSize; + FIO_Dict_t dict; const char* dictFileName; stat_t dictFileStat; ZSTD_CStream* cctx; @@ -961,6 +1111,9 @@ static void FIO_adjustParamsForPatchFromMode(FIO_prefs_t* const prefs, static cRess_t FIO_createCResources(FIO_prefs_t* const prefs, const char* dictFileName, unsigned long long const maxSrcFileSize, int cLevel, ZSTD_compressionParameters comprParams) { + int useMMap = prefs->mmapDict == ZSTD_ps_enable; + int forceNoUseMMap = prefs->mmapDict == ZSTD_ps_disable; + FIO_dictBufferType_t dictBufferType; cRess_t ress; memset(&ress, 0, sizeof(ress)); @@ -970,19 +1123,25 @@ static cRess_t FIO_createCResources(FIO_prefs_t* const prefs, EXM_THROW(30, "allocation error (%s): can't create ZSTD_CCtx", strerror(errno)); + FIO_getDictFileStat(dictFileName, &ress.dictFileStat); + /* need to update memLimit before calling createDictBuffer * because of memLimit check inside it */ if (prefs->patchFromMode) { + U64 const dictSize = UTIL_getFileSizeStat(&ress.dictFileStat); unsigned long long const ssSize = (unsigned long long)prefs->streamSrcSize; - FIO_adjustParamsForPatchFromMode(prefs, &comprParams, UTIL_getFileSize(dictFileName), ssSize > 0 ? ssSize : maxSrcFileSize, cLevel); + useMMap |= dictSize > prefs->memLimit; + FIO_adjustParamsForPatchFromMode(prefs, &comprParams, dictSize, ssSize > 0 ? ssSize : maxSrcFileSize, cLevel); } - ress.dictBufferSize = FIO_createDictBuffer(&ress.dictBuffer, dictFileName, prefs, &ress.dictFileStat); /* works with dictFileName==NULL */ + + dictBufferType = (useMMap && !forceNoUseMMap) ? FIO_mmapDict : FIO_mallocDict; + FIO_initDict(&ress.dict, dictFileName, prefs, &ress.dictFileStat, dictBufferType); /* works with dictFileName==NULL */ ress.writeCtx = AIO_WritePool_create(prefs, ZSTD_CStreamOutSize()); ress.readCtx = AIO_ReadPool_create(prefs, ZSTD_CStreamInSize()); /* Advanced parameters, including dictionary */ - if (dictFileName && (ress.dictBuffer==NULL)) + if (dictFileName && (ress.dict.dictBuffer==NULL)) EXM_THROW(32, "allocation error : can't create dictBuffer"); ress.dictFileName = dictFileName; @@ -1032,17 +1191,17 @@ static cRess_t FIO_createCResources(FIO_prefs_t* const prefs, #endif /* dictionary */ if (prefs->patchFromMode) { - CHECK( ZSTD_CCtx_refPrefix(ress.cctx, ress.dictBuffer, ress.dictBufferSize) ); + CHECK( ZSTD_CCtx_refPrefix(ress.cctx, ress.dict.dictBuffer, ress.dict.dictBufferSize) ); } else { - CHECK( ZSTD_CCtx_loadDictionary(ress.cctx, ress.dictBuffer, ress.dictBufferSize) ); + CHECK( ZSTD_CCtx_loadDictionary_byReference(ress.cctx, ress.dict.dictBuffer, ress.dict.dictBufferSize) ); } return ress; } -static void FIO_freeCResources(const cRess_t* const ress) +static void FIO_freeCResources(cRess_t* const ress) { - free(ress->dictBuffer); + FIO_freeDict(&(ress->dict)); AIO_WritePool_free(ress->writeCtx); AIO_ReadPool_free(ress->readCtx); ZSTD_freeCStream(ress->cctx); /* never fails */ @@ -1173,8 +1332,8 @@ FIO_compressLzmaFrame(cRess_t* ress, } writeJob =AIO_WritePool_acquireJob(ress->writeCtx); - strm.next_out = (Bytef*)writeJob->buffer; - strm.avail_out = (uInt)writeJob->bufferSize; + strm.next_out = (BYTE*)writeJob->buffer; + strm.avail_out = writeJob->bufferSize; strm.next_in = 0; strm.avail_in = 0; @@ -1201,7 +1360,7 @@ FIO_compressLzmaFrame(cRess_t* ress, writeJob->usedBufferSize = compBytes; AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob); outFileSize += compBytes; - strm.next_out = (Bytef*)writeJob->buffer; + strm.next_out = (BYTE*)writeJob->buffer; strm.avail_out = writeJob->bufferSize; } } if (srcFileSize == UTIL_FILESIZE_UNKNOWN) @@ -1681,6 +1840,7 @@ static int FIO_compressFilename_dstFile(FIO_ctx_t* const fCtx, int result; int transferStat = 0; FILE *dstFile; + int dstFd = -1; assert(AIO_ReadPool_getFile(ress.readCtx) != NULL); if (AIO_WritePool_getFile(ress.writeCtx) == NULL) { @@ -1696,6 +1856,7 @@ static int FIO_compressFilename_dstFile(FIO_ctx_t* const fCtx, DISPLAYLEVEL(6, "FIO_compressFilename_dstFile: opening dst: %s \n", dstFileName); dstFile = FIO_openDstFile(fCtx, prefs, srcFileName, dstFileName, dstFileInitialPermissions); if (dstFile==NULL) return 1; /* could not open dstFileName */ + dstFd = fileno(dstFile); AIO_WritePool_setFile(ress.writeCtx, dstFile); /* Must only be added after FIO_openDstFile() succeeds. * Otherwise we may delete the destination file if it already exists, @@ -1709,14 +1870,20 @@ static int FIO_compressFilename_dstFile(FIO_ctx_t* const fCtx, if (closeDstFile) { clearHandler(); + if (transferStat) { + UTIL_setFDStat(dstFd, dstFileName, srcFileStat); + } + DISPLAYLEVEL(6, "FIO_compressFilename_dstFile: closing dst: %s \n", dstFileName); if (AIO_WritePool_closeFile(ress.writeCtx)) { /* error closing file */ DISPLAYLEVEL(1, "zstd: %s: %s \n", dstFileName, strerror(errno)); result=1; } + if (transferStat) { - UTIL_setFileStat(dstFileName, srcFileStat); + UTIL_utime(dstFileName, srcFileStat); } + if ( (result != 0) /* operation failure */ && strcmp(dstFileName, stdoutmark) /* special case : don't remove() stdout */ ) { @@ -1873,7 +2040,7 @@ int FIO_compressFilename(FIO_ctx_t* const fCtx, FIO_prefs_t* const prefs, const const char* srcFileName, const char* dictFileName, int compressionLevel, ZSTD_compressionParameters comprParams) { - cRess_t const ress = FIO_createCResources(prefs, dictFileName, UTIL_getFileSize(srcFileName), compressionLevel, comprParams); + cRess_t ress = FIO_createCResources(prefs, dictFileName, UTIL_getFileSize(srcFileName), compressionLevel, comprParams); int const result = FIO_compressFilename_srcFile(fCtx, prefs, ress, dstFileName, srcFileName, compressionLevel); #define DISPLAY_LEVEL_DEFAULT 2 @@ -2043,6 +2210,7 @@ int FIO_compressMultipleFilenames(FIO_ctx_t* const fCtx, * Decompression ***************************************************************************/ typedef struct { + FIO_Dict_t dict; ZSTD_DStream* dctx; WritePoolCtx_t *writeCtx; ReadPoolCtx_t *readCtx; @@ -2050,11 +2218,19 @@ typedef struct { static dRess_t FIO_createDResources(FIO_prefs_t* const prefs, const char* dictFileName) { + int useMMap = prefs->mmapDict == ZSTD_ps_enable; + int forceNoUseMMap = prefs->mmapDict == ZSTD_ps_disable; + stat_t statbuf; dRess_t ress; memset(&ress, 0, sizeof(ress)); - if (prefs->patchFromMode) - FIO_adjustMemLimitForPatchFromMode(prefs, UTIL_getFileSize(dictFileName), 0 /* just use the dict size */); + FIO_getDictFileStat(dictFileName, &statbuf); + + if (prefs->patchFromMode){ + U64 const dictSize = UTIL_getFileSizeStat(&statbuf); + useMMap |= dictSize > prefs->memLimit; + FIO_adjustMemLimitForPatchFromMode(prefs, dictSize, 0 /* just use the dict size */); + } /* Allocation */ ress.dctx = ZSTD_createDStream(); @@ -2064,29 +2240,34 @@ static dRess_t FIO_createDResources(FIO_prefs_t* const prefs, const char* dictFi CHECK( ZSTD_DCtx_setParameter(ress.dctx, ZSTD_d_forceIgnoreChecksum, !prefs->checksumFlag)); /* dictionary */ - { void* dictBuffer; - stat_t statbuf; - size_t const dictBufferSize = FIO_createDictBuffer(&dictBuffer, dictFileName, prefs, &statbuf); - CHECK( ZSTD_DCtx_reset(ress.dctx, ZSTD_reset_session_only) ); - CHECK( ZSTD_DCtx_loadDictionary(ress.dctx, dictBuffer, dictBufferSize) ); - free(dictBuffer); + { + FIO_dictBufferType_t dictBufferType = (useMMap && !forceNoUseMMap) ? FIO_mmapDict : FIO_mallocDict; + FIO_initDict(&ress.dict, dictFileName, prefs, &statbuf, dictBufferType); + + CHECK(ZSTD_DCtx_reset(ress.dctx, ZSTD_reset_session_only) ); + + if (prefs->patchFromMode){ + CHECK(ZSTD_DCtx_refPrefix(ress.dctx, ress.dict.dictBuffer, ress.dict.dictBufferSize)); + } else { + CHECK(ZSTD_DCtx_loadDictionary_byReference(ress.dctx, ress.dict.dictBuffer, ress.dict.dictBufferSize)); + } } ress.writeCtx = AIO_WritePool_create(prefs, ZSTD_DStreamOutSize()); ress.readCtx = AIO_ReadPool_create(prefs, ZSTD_DStreamInSize()); - return ress; } static void FIO_freeDResources(dRess_t ress) { + FIO_freeDict(&(ress.dict)); CHECK( ZSTD_freeDStream(ress.dctx) ); AIO_WritePool_free(ress.writeCtx); AIO_ReadPool_free(ress.readCtx); } -/** FIO_passThrough() : just copy input into output, for compatibility with gzip -df mode - @return : 0 (no error) */ +/* FIO_passThrough() : just copy input into output, for compatibility with gzip -df mode + * @return : 0 (no error) */ static int FIO_passThrough(dRess_t *ress) { size_t const blockSize = MIN(MIN(64 KB, ZSTD_DStreamInSize()), ZSTD_DStreamOutSize()); @@ -2114,7 +2295,8 @@ static int FIO_passThrough(dRess_t *ress) static void FIO_zstdErrorHelp(const FIO_prefs_t* const prefs, const dRess_t* ress, - size_t err, const char* srcFileName) + size_t err, + const char* srcFileName) { ZSTD_frameHeader header; @@ -2316,8 +2498,8 @@ FIO_decompressLzmaFrame(dRess_t* ress, } writeJob = AIO_WritePool_acquireJob(ress->writeCtx); - strm.next_out = (Bytef*)writeJob->buffer; - strm.avail_out = (uInt)writeJob->bufferSize; + strm.next_out = (BYTE*)writeJob->buffer; + strm.avail_out = writeJob->bufferSize; strm.next_in = (BYTE const*)ress->readCtx->srcBuffer; strm.avail_in = ress->readCtx->srcBufferLoaded; @@ -2345,7 +2527,7 @@ FIO_decompressLzmaFrame(dRess_t* ress, writeJob->usedBufferSize = decompBytes; AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob); outFileSize += decompBytes; - strm.next_out = (Bytef*)writeJob->buffer; + strm.next_out = (BYTE*)writeJob->buffer; strm.avail_out = writeJob->bufferSize; } } if (ret == LZMA_STREAM_END) break; @@ -2540,6 +2722,7 @@ static int FIO_decompressDstFile(FIO_ctx_t* const fCtx, int result; int releaseDstFile = 0; int transferStat = 0; + int dstFd = 0; if ((AIO_WritePool_getFile(ress.writeCtx) == NULL) && (prefs->testMode == 0)) { FILE *dstFile; @@ -2555,6 +2738,7 @@ static int FIO_decompressDstFile(FIO_ctx_t* const fCtx, dstFile = FIO_openDstFile(fCtx, prefs, srcFileName, dstFileName, dstFilePermissions); if (dstFile==NULL) return 1; + dstFd = fileno(dstFile); AIO_WritePool_setFile(ress.writeCtx, dstFile); /* Must only be added after FIO_openDstFile() succeeds. @@ -2568,13 +2752,18 @@ static int FIO_decompressDstFile(FIO_ctx_t* const fCtx, if (releaseDstFile) { clearHandler(); + + if (transferStat) { + UTIL_setFDStat(dstFd, dstFileName, srcFileStat); + } + if (AIO_WritePool_closeFile(ress.writeCtx)) { DISPLAYLEVEL(1, "zstd: %s: %s \n", dstFileName, strerror(errno)); result = 1; } if (transferStat) { - UTIL_setFileStat(dstFileName, srcFileStat); + UTIL_utime(dstFileName, srcFileStat); } if ( (result != 0) /* operation failure */ @@ -2655,6 +2844,8 @@ int FIO_decompressFilename(FIO_ctx_t* const fCtx, FIO_prefs_t* const prefs, int const decodingError = FIO_decompressSrcFile(fCtx, prefs, ress, dstFileName, srcFileName); + + FIO_freeDResources(ress); return decodingError; } diff --git a/programs/fileio.h b/programs/fileio.h index 291d4d414..224d89525 100644 --- a/programs/fileio.h +++ b/programs/fileio.h @@ -106,6 +106,7 @@ void FIO_setContentSize(FIO_prefs_t* const prefs, int value); void FIO_displayCompressionParameters(const FIO_prefs_t* prefs); void FIO_setAsyncIOFlag(FIO_prefs_t* const prefs, int value); void FIO_setPassThroughFlag(FIO_prefs_t* const prefs, int value); +void FIO_setMMapDict(FIO_prefs_t* const prefs, ZSTD_paramSwitch_e value); /* FIO_ctx_t functions */ void FIO_setNbFilesTotal(FIO_ctx_t* const fCtx, int value); diff --git a/programs/fileio_types.h b/programs/fileio_types.h index c1f42f1ad..2994a6092 100644 --- a/programs/fileio_types.h +++ b/programs/fileio_types.h @@ -69,6 +69,18 @@ typedef struct FIO_prefs_s { int contentSize; int allowBlockDevices; int passThrough; + ZSTD_paramSwitch_e mmapDict; } FIO_prefs_t; +typedef enum {FIO_mallocDict, FIO_mmapDict} FIO_dictBufferType_t; + +typedef struct { + void* dictBuffer; + size_t dictBufferSize; + FIO_dictBufferType_t dictBufferType; +#if defined(_MSC_VER) || defined(_WIN32) + HANDLE dictHandle; +#endif +} FIO_Dict_t; + #endif /* FILEIO_TYPES_HEADER */ diff --git a/programs/timefn.c b/programs/timefn.c index f941e57e6..4f045226b 100644 --- a/programs/timefn.c +++ b/programs/timefn.c @@ -88,7 +88,7 @@ UTIL_time_t UTIL_getTime(void) /* C11 requires support of timespec_get(). * However, FreeBSD 11 claims C11 compliance while lacking timespec_get(). * Double confirm timespec_get() support by checking the definition of TIME_UTC. - * However, some versions of Android manage to simultanously define TIME_UTC + * However, some versions of Android manage to simultaneously define TIME_UTC * and lack timespec_get() support... */ #elif (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* C11 */) \ && defined(TIME_UTC) && !defined(__ANDROID__) diff --git a/programs/util.c b/programs/util.c index e017772ef..c9031e91d 100644 --- a/programs/util.c +++ b/programs/util.c @@ -102,6 +102,17 @@ UTIL_STATIC void* UTIL_realloc(void *ptr, size_t size) #define chmod _chmod #endif +#ifndef ZSTD_HAVE_FCHMOD +#if PLATFORM_POSIX_VERSION >= 199309L +#define ZSTD_HAVE_FCHMOD +#endif +#endif + +#ifndef ZSTD_HAVE_FCHOWN +#if PLATFORM_POSIX_VERSION >= 200809L +#define ZSTD_HAVE_FCHOWN +#endif +#endif /*-**************************************** * Console log @@ -147,21 +158,38 @@ void UTIL_traceFileStat(void) g_traceFileStat = 1; } -int UTIL_stat(const char* filename, stat_t* statbuf) +int UTIL_fstat(const int fd, const char* filename, stat_t* statbuf) { int ret; - UTIL_TRACE_CALL("UTIL_stat(%s)", filename); + UTIL_TRACE_CALL("UTIL_stat(%d, %s)", fd, filename); #if defined(_MSC_VER) - ret = !_stat64(filename, statbuf); + if (fd >= 0) { + ret = !_fstat64(fd, statbuf); + } else { + ret = !_stat64(filename, statbuf); + } #elif defined(__MINGW32__) && defined (__MSVCRT__) - ret = !_stati64(filename, statbuf); + if (fd >= 0) { + ret = !_fstati64(fd, statbuf); + } else { + ret = !_stati64(filename, statbuf); + } #else - ret = !stat(filename, statbuf); + if (fd >= 0) { + ret = !fstat(fd, statbuf); + } else { + ret = !stat(filename, statbuf); + } #endif UTIL_TRACE_RET(ret); return ret; } +int UTIL_stat(const char* filename, stat_t* statbuf) +{ + return UTIL_fstat(-1, filename, statbuf); +} + int UTIL_isRegularFile(const char* infilename) { stat_t statbuf; @@ -183,11 +211,16 @@ int UTIL_isRegularFileStat(const stat_t* statbuf) /* like chmod, but avoid changing permission of /dev/null */ int UTIL_chmod(char const* filename, const stat_t* statbuf, mode_t permissions) +{ + return UTIL_fchmod(-1, filename, statbuf, permissions); +} + +int UTIL_fchmod(const int fd, char const* filename, const stat_t* statbuf, mode_t permissions) { stat_t localStatBuf; UTIL_TRACE_CALL("UTIL_chmod(%s, %#4o)", filename, (unsigned)permissions); if (statbuf == NULL) { - if (!UTIL_stat(filename, &localStatBuf)) { + if (!UTIL_fstat(fd, filename, &localStatBuf)) { UTIL_TRACE_RET(0); return 0; } @@ -197,9 +230,20 @@ int UTIL_chmod(char const* filename, const stat_t* statbuf, mode_t permissions) UTIL_TRACE_RET(0); return 0; /* pretend success, but don't change anything */ } - UTIL_TRACE_CALL("chmod"); +#ifdef ZSTD_HAVE_FCHMOD + if (fd >= 0) { + int ret; + UTIL_TRACE_CALL("fchmod"); + ret = fchmod(fd, permissions); + UTIL_TRACE_RET(ret); + UTIL_TRACE_RET(ret); + return ret; + } else +#endif { - int const ret = chmod(filename, permissions); + int ret; + UTIL_TRACE_CALL("chmod"); + ret = chmod(filename, permissions); UTIL_TRACE_RET(ret); UTIL_TRACE_RET(ret); return ret; @@ -236,19 +280,21 @@ int UTIL_utime(const char* filename, const stat_t *statbuf) } int UTIL_setFileStat(const char *filename, const stat_t *statbuf) +{ + return UTIL_setFDStat(-1, filename, statbuf); +} + +int UTIL_setFDStat(const int fd, const char *filename, const stat_t *statbuf) { int res = 0; stat_t curStatBuf; - UTIL_TRACE_CALL("UTIL_setFileStat(%s)", filename); + UTIL_TRACE_CALL("UTIL_setFileStat(%d, %s)", fd, filename); - if (!UTIL_stat(filename, &curStatBuf) || !UTIL_isRegularFileStat(&curStatBuf)) { + if (!UTIL_fstat(fd, filename, &curStatBuf) || !UTIL_isRegularFileStat(&curStatBuf)) { UTIL_TRACE_RET(-1); return -1; } - /* set access and modification times */ - res += UTIL_utime(filename, statbuf); - /* Mimic gzip's behavior: * * "Change the group first, then the permissions, then the owner. @@ -258,13 +304,27 @@ int UTIL_setFileStat(const char *filename, const stat_t *statbuf) * setgid bits." */ #if !defined(_WIN32) - res += chown(filename, -1, statbuf->st_gid); /* Apply group ownership */ +#ifdef ZSTD_HAVE_FCHOWN + if (fd >= 0) { + res += fchown(fd, -1, statbuf->st_gid); /* Apply group ownership */ + } else +#endif + { + res += chown(filename, -1, statbuf->st_gid); /* Apply group ownership */ + } #endif - res += UTIL_chmod(filename, &curStatBuf, statbuf->st_mode & 0777); /* Copy file permissions */ + res += UTIL_fchmod(fd, filename, &curStatBuf, statbuf->st_mode & 0777); /* Copy file permissions */ #if !defined(_WIN32) - res += chown(filename, statbuf->st_uid, -1); /* Apply user ownership */ +#ifdef ZSTD_HAVE_FCHOWN + if (fd >= 0) { + res += fchown(fd, statbuf->st_uid, -1); /* Apply user ownership */ + } else +#endif + { + res += chown(filename, statbuf->st_uid, -1); /* Apply user ownership */ + } #endif errno = 0; diff --git a/programs/util.h b/programs/util.h index 4ec54137d..8234646bf 100644 --- a/programs/util.h +++ b/programs/util.h @@ -126,15 +126,25 @@ int UTIL_requireUserConfirmation(const char* prompt, const char* abortMsg, const /** * Calls platform's equivalent of stat() on filename and writes info to statbuf. * Returns success (1) or failure (0). + * + * UTIL_fstat() is like UTIL_stat() but takes an optional fd that refers to the + * file in question. It turns out that this can be meaningfully faster. If fd is + * -1, behaves just like UTIL_stat() (i.e., falls back to using the filename). */ int UTIL_stat(const char* filename, stat_t* statbuf); +int UTIL_fstat(const int fd, const char* filename, stat_t* statbuf); /** * Instead of getting a file's stats, this updates them with the info in the * provided stat_t. Currently sets owner, group, atime, and mtime. Will only * update this info for regular files. + * + * UTIL_setFDStat() also takes an fd, and will preferentially use that to + * indicate which file to modify, If fd is -1, it will fall back to using the + * filename. */ int UTIL_setFileStat(const char* filename, const stat_t* statbuf); +int UTIL_setFDStat(const int fd, const char* filename, const stat_t* statbuf); /** * Set atime to now and mtime to the st_mtim in statbuf. @@ -159,8 +169,11 @@ U64 UTIL_getFileSizeStat(const stat_t* statbuf); * Like chmod(), but only modifies regular files. Provided statbuf may be NULL, * in which case this function will stat() the file internally, in order to * check whether it should be modified. + * + * If fd is -1, fd is ignored and the filename is used. */ int UTIL_chmod(char const* filename, const stat_t* statbuf, mode_t permissions); +int UTIL_fchmod(const int fd, char const* filename, const stat_t* statbuf, mode_t permissions); /* * In the absence of a pre-existing stat result on the file in question, these diff --git a/programs/zstd.1 b/programs/zstd.1 index edc128fd1..383d99470 100644 --- a/programs/zstd.1 +++ b/programs/zstd.1 @@ -1,4 +1,4 @@ -.TH "ZSTD" "1" "February 2023" "zstd 1.5.4" "User Commands" +.TH "ZSTD" "1" "March 2023" "zstd 1.5.5" "User Commands" .SH "NAME" \fBzstd\fR \- zstd, zstdmt, unzstd, zstdcat \- Compress or decompress \.zst files .SH "SYNOPSIS" @@ -105,7 +105,7 @@ Note: for all levels, you can use \fB\-\-patch\-from\fR in \fB\-\-single\-thread .IP Note: for level 19, you can get increased compression ratio at the cost of speed by specifying \fB\-\-zstd=targetLength=\fR to be something large (i\.e\. 4096), and by setting a large \fB\-\-zstd=chainLog=\fR\. .IP "\[ci]" 4 -\fB\-\-rsyncable\fR: \fBzstd\fR will periodically synchronize the compression state to make the compressed file more rsync\-friendly\. There is a negligible impact to compression ratio, and the faster compression levels will see a small compression speed hit\. This feature does not work with \fB\-\-single\-thread\fR\. You probably don\'t want to use it with long range mode, since it will decrease the effectiveness of the synchronization points, but your mileage may vary\. +\fB\-\-rsyncable\fR: \fBzstd\fR will periodically synchronize the compression state to make the compressed file more rsync\-friendly\. There is a negligible impact to compression ratio, and a potential impact to compression speed, perceptible at higher speeds, for example when combining \fB\-\-rsyncable\fR with many parallel worker threads\. This feature does not work with \fB\-\-single\-thread\fR\. You probably don\'t want to use it with long range mode, since it will decrease the effectiveness of the synchronization points, but your mileage may vary\. .IP "\[ci]" 4 \fB\-C\fR, \fB\-\-[no\-]check\fR: add integrity check computed from uncompressed data (default: enabled) .IP "\[ci]" 4 diff --git a/programs/zstd.1.md b/programs/zstd.1.md index 3b7f24f79..231341b2a 100644 --- a/programs/zstd.1.md +++ b/programs/zstd.1.md @@ -177,9 +177,10 @@ the last one takes effect. (i.e. 4096), and by setting a large `--zstd=chainLog=`. * `--rsyncable`: `zstd` will periodically synchronize the compression state to make the - compressed file more rsync-friendly. There is a negligible impact to - compression ratio, and the faster compression levels will see a small - compression speed hit. + compressed file more rsync-friendly. + There is a negligible impact to compression ratio, + and a potential impact to compression speed, perceptible at higher speeds, + for example when combining `--rsyncable` with many parallel worker threads. This feature does not work with `--single-thread`. You probably don't want to use it with long range mode, since it will decrease the effectiveness of the synchronization points, but your mileage may vary. diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 93f75e21d..d2465456b 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -37,7 +37,7 @@ #include "fileio.h" /* stdinmark, stdoutmark, ZSTD_EXTENSION */ #ifndef ZSTD_NOBENCH -# include "benchzstd.h" /* BMK_benchFiles */ +# include "benchzstd.h" /* BMK_benchFilesAdvanced */ #endif #ifndef ZSTD_NODICT # include "dibio.h" /* ZDICT_cover_params_t, DiB_trainFromFiles() */ @@ -165,7 +165,7 @@ static void usage(FILE* f, const char* programName) #endif DISPLAY_F(f, " -D DICT Use DICT as the dictionary for compression or decompression.\n\n"); DISPLAY_F(f, " -f, --force Disable input and output checks. Allows overwriting existing files,\n"); - DISPLAY_F(f, " receiving input from the console, printing ouput to STDOUT, and\n"); + DISPLAY_F(f, " receiving input from the console, printing output to STDOUT, and\n"); DISPLAY_F(f, " operating on links, block devices, etc. Unrecognized formats will be\n"); DISPLAY_F(f, " passed-through through as-is.\n\n"); @@ -254,6 +254,7 @@ static void usage_advanced(const char* programName) DISPLAYOUT("\n"); DISPLAYOUT(" --format=zstd Compress files to the `.zst` format. [Default]\n"); + DISPLAYOUT(" --mmap-dict Memory-map dictionary file rather than mallocing and loading all at once"); #ifdef ZSTD_GZCOMPRESS DISPLAYOUT(" --format=gzip Compress files to the `.gz` format.\n"); #endif @@ -851,6 +852,7 @@ int main(int argCount, const char* argv[]) ultra=0, contentSize=1, removeSrcFile=0; + ZSTD_paramSwitch_e mmapDict=ZSTD_ps_auto; ZSTD_paramSwitch_e useRowMatchFinder = ZSTD_ps_auto; FIO_compressionType_t cType = FIO_zstdCompression; unsigned nbWorkers = 0; @@ -984,6 +986,8 @@ int main(int argCount, const char* argv[]) if (longCommandWArg(&argument, "--adapt=")) { adapt = 1; if (!parseAdaptParameters(argument, &adaptMin, &adaptMax)) { badusage(programName); CLEAN_RETURN(1); } continue; } if (!strcmp(argument, "--single-thread")) { nbWorkers = 0; singleThread = 1; continue; } if (!strcmp(argument, "--format=zstd")) { suffix = ZSTD_EXTENSION; cType = FIO_zstdCompression; continue; } + if (!strcmp(argument, "--mmap-dict")) { mmapDict = ZSTD_ps_enable; continue; } + if (!strcmp(argument, "--no-mmap-dict")) { mmapDict = ZSTD_ps_disable; continue; } #ifdef ZSTD_GZCOMPRESS if (!strcmp(argument, "--format=gzip")) { suffix = GZ_EXTENSION; cType = FIO_gzipCompression; continue; } if (exeNameMatch(programName, ZSTD_GZ)) { /* behave like gzip */ @@ -1391,18 +1395,15 @@ int main(int argCount, const char* argv[]) int c; DISPLAYLEVEL(3, "Benchmarking %s \n", filenames->fileNames[i]); for(c = cLevel; c <= cLevelLast; c++) { - BMK_benchOutcome_t const bo = BMK_benchFilesAdvanced(&filenames->fileNames[i], 1, dictFileName, c, &compressionParams, g_displayLevel, &benchParams); - if (!BMK_isSuccessful_benchOutcome(bo)) return 1; + operationResult = BMK_benchFilesAdvanced(&filenames->fileNames[i], 1, dictFileName, c, &compressionParams, g_displayLevel, &benchParams); } } } else { for(; cLevel <= cLevelLast; cLevel++) { - BMK_benchOutcome_t const bo = BMK_benchFilesAdvanced(filenames->fileNames, (unsigned)filenames->tableSize, dictFileName, cLevel, &compressionParams, g_displayLevel, &benchParams); - if (!BMK_isSuccessful_benchOutcome(bo)) return 1; + operationResult = BMK_benchFilesAdvanced(filenames->fileNames, (unsigned)filenames->tableSize, dictFileName, cLevel, &compressionParams, g_displayLevel, &benchParams); } } } else { for(; cLevel <= cLevelLast; cLevel++) { - BMK_benchOutcome_t const bo = BMK_syntheticTest(cLevel, compressibility, &compressionParams, g_displayLevel, &benchParams); - if (!BMK_isSuccessful_benchOutcome(bo)) return 1; + operationResult = BMK_syntheticTest(cLevel, compressibility, &compressionParams, g_displayLevel, &benchParams); } } #else @@ -1526,6 +1527,7 @@ int main(int argCount, const char* argv[]) FIO_setNotificationLevel(g_displayLevel); FIO_setAllowBlockDevices(prefs, allowBlockDevices); FIO_setPatchFromMode(prefs, patchFromDictFileName != NULL); + FIO_setMMapDict(prefs, mmapDict); if (memLimit == 0) { if (compressionParams.windowLog == 0) { memLimit = (U32)1 << g_defaultMaxWindowLog; diff --git a/programs/zstdgrep.1 b/programs/zstdgrep.1 index 77d29b4bf..1204e5bb6 100644 --- a/programs/zstdgrep.1 +++ b/programs/zstdgrep.1 @@ -1,4 +1,4 @@ -.TH "ZSTDGREP" "1" "February 2023" "zstd 1.5.4" "User Commands" +.TH "ZSTDGREP" "1" "March 2023" "zstd 1.5.5" "User Commands" .SH "NAME" \fBzstdgrep\fR \- print lines matching a pattern in zstandard\-compressed files .SH "SYNOPSIS" diff --git a/programs/zstdless.1 b/programs/zstdless.1 index 6c82b9c9e..bc019b263 100644 --- a/programs/zstdless.1 +++ b/programs/zstdless.1 @@ -1,4 +1,4 @@ -.TH "ZSTDLESS" "1" "February 2023" "zstd 1.5.4" "User Commands" +.TH "ZSTDLESS" "1" "March 2023" "zstd 1.5.5" "User Commands" .SH "NAME" \fBzstdless\fR \- view zstandard\-compressed files .SH "SYNOPSIS" diff --git a/tests/Makefile b/tests/Makefile index 3eed19ea8..778c7d675 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -95,7 +95,7 @@ allnothread: fullbench fuzzer paramgrill datagen decodecorpus dll: fuzzer-dll zstreamtest-dll .PHONY: zstd zstd32 zstd-nolegacy # only external makefile knows how to build or update them -zstd zstd32 zstd-nolegacy: +zstd zstd32 zstd-nolegacy zstd-dll: $(MAKE) -C $(PRGDIR) $@ MOREFLAGS+="$(DEBUGFLAGS)" .PHONY: libzstd @@ -328,13 +328,17 @@ test-all: test test32 test-decodecorpus-cli test-zstd: ZSTD = $(PRGDIR)/zstd test-zstd: zstd +.PHONY: test-zstd-dll +test-zstd-dll: ZSTD = $(PRGDIR)/zstd +test-zstd-dll: zstd-dll + test-zstd32: ZSTD = $(PRGDIR)/zstd32 test-zstd32: zstd32 test-zstd-nolegacy: ZSTD = $(PRGDIR)/zstd-nolegacy test-zstd-nolegacy: zstd-nolegacy -test-zstd test-zstd32 test-zstd-nolegacy: datagen +test-zstd test-zstd32 test-zstd-nolegacy test-zstd-dll: datagen file $(ZSTD) EXE_PREFIX="$(QEMU_SYS)" ZSTD_BIN="$(ZSTD)" DATAGEN_BIN=./datagen ./playTests.sh $(ZSTDRTTEST) diff --git a/tests/cli-tests/basic/help.sh.stdout.glob b/tests/cli-tests/basic/help.sh.stdout.glob index 5580dc669..21bc28c3f 100644 --- a/tests/cli-tests/basic/help.sh.stdout.glob +++ b/tests/cli-tests/basic/help.sh.stdout.glob @@ -16,7 +16,7 @@ Options: -D DICT Use DICT as the dictionary for compression or decompression. -f, --force Disable input and output checks. Allows overwriting existing files, - receiving input from the console, printing ouput to STDOUT, and + receiving input from the console, printing output to STDOUT, and operating on links, block devices, etc. Unrecognized formats will be passed-through through as-is. diff --git a/tests/cli-tests/compression/basic.sh b/tests/cli-tests/compression/basic.sh index b6e2aa045..950c5a483 100755 --- a/tests/cli-tests/compression/basic.sh +++ b/tests/cli-tests/compression/basic.sh @@ -25,8 +25,10 @@ zstd --stdout file | zstd -t println bob | zstd | zstd -t # Test keeping input file when compressing to stdout in gzip mode -$ZSTD_SYMLINK_DIR/gzip -c file | zstd -t ; test -f file -$ZSTD_SYMLINK_DIR/gzip --stdout file | zstd -t ; test -f file +if $(command -v $ZSTD_SYMLINK_DIR/gzip); then + $ZSTD_SYMLINK_DIR/gzip -c file | zstd -t ; test -f file + $ZSTD_SYMLINK_DIR/gzip --stdout file | zstd -t ; test -f file +fi # Test --rm cp file file-rm diff --git a/tests/cli-tests/compression/golden.sh b/tests/cli-tests/compression/golden.sh index 85dd3fdb8..458be9d4a 100755 --- a/tests/cli-tests/compression/golden.sh +++ b/tests/cli-tests/compression/golden.sh @@ -10,3 +10,7 @@ zstd -r -t golden-compressed/ zstd --target-compressed-block-size=1024 -rf golden/ --output-dir-mirror golden-compressed/ zstd -r -t golden-compressed/ + +# PR #3517 block splitter corruption test +zstd -rf -19 --zstd=mml=7 golden/ --output-dir-mirror golden-compressed/ +zstd -r -t golden-compressed/ \ No newline at end of file diff --git a/tests/cli-tests/compression/gzip-compat.sh b/tests/cli-tests/compression/gzip-compat.sh index bb72e05fc..b628b35a0 100755 --- a/tests/cli-tests/compression/gzip-compat.sh +++ b/tests/cli-tests/compression/gzip-compat.sh @@ -6,10 +6,12 @@ set -e # set -v # Test gzip specific compression option -$ZSTD_SYMLINK_DIR/gzip --fast file ; $ZSTD_SYMLINK_DIR/gzip -d file.gz -$ZSTD_SYMLINK_DIR/gzip --best file ; $ZSTD_SYMLINK_DIR/gzip -d file.gz +if $(command -v $ZSTD_SYMLINK_DIR/gzip); then + $ZSTD_SYMLINK_DIR/gzip --fast file ; $ZSTD_SYMLINK_DIR/gzip -d file.gz + $ZSTD_SYMLINK_DIR/gzip --best file ; $ZSTD_SYMLINK_DIR/gzip -d file.gz -# Test -n / --no-name: do not embed original filename in archive -$ZSTD_SYMLINK_DIR/gzip -n file ; grep -qv file file.gz ; $ZSTD_SYMLINK_DIR/gzip -d file.gz -$ZSTD_SYMLINK_DIR/gzip --no-name file ; grep -qv file file.gz ; $ZSTD_SYMLINK_DIR/gzip -d file.gz -$ZSTD_SYMLINK_DIR/gzip -c --no-name file | grep -qv file + # Test -n / --no-name: do not embed original filename in archive + $ZSTD_SYMLINK_DIR/gzip -n file ; grep -qv file file.gz ; $ZSTD_SYMLINK_DIR/gzip -d file.gz + $ZSTD_SYMLINK_DIR/gzip --no-name file ; grep -qv file file.gz ; $ZSTD_SYMLINK_DIR/gzip -d file.gz + $ZSTD_SYMLINK_DIR/gzip -c --no-name file | grep -qv file +fi diff --git a/tests/cli-tests/compression/window-resize.sh b/tests/cli-tests/compression/window-resize.sh index 3b5e6fe24..e4fe811ce 100755 --- a/tests/cli-tests/compression/window-resize.sh +++ b/tests/cli-tests/compression/window-resize.sh @@ -1,6 +1,6 @@ #!/bin/sh datagen -g1G > file -zstd --long=31 -1 --single-thread --no-content-size -f file +zstd --long=30 -1 --single-thread --no-content-size -f file zstd -l -v file.zst # We want to ignore stderr (its outputting "*** zstd command line interface diff --git a/tests/cli-tests/file-stat/compress-file-to-dir-without-write-perm.sh b/tests/cli-tests/file-stat/compress-file-to-dir-without-write-perm.sh new file mode 100755 index 000000000..1aa4525fb --- /dev/null +++ b/tests/cli-tests/file-stat/compress-file-to-dir-without-write-perm.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +# motivated by issue #3523 + +datagen > file +mkdir out +chmod 000 out + +zstd file -q --trace-file-stat -o out/file.zst +zstd -tq out/file.zst + +chmod 777 out diff --git a/tests/cli-tests/file-stat/compress-file-to-dir-without-write-perm.sh.stderr.exact b/tests/cli-tests/file-stat/compress-file-to-dir-without-write-perm.sh.stderr.exact new file mode 100644 index 000000000..95deaf2b1 --- /dev/null +++ b/tests/cli-tests/file-stat/compress-file-to-dir-without-write-perm.sh.stderr.exact @@ -0,0 +1,26 @@ +Trace:FileStat: > UTIL_isLink(file) +Trace:FileStat: < 0 +Trace:FileStat: > UTIL_isConsole(2) +Trace:FileStat: < 0 +Trace:FileStat: > UTIL_getFileSize(file) +Trace:FileStat: > UTIL_stat(-1, file) +Trace:FileStat: < 1 +Trace:FileStat: < 65537 +Trace:FileStat: > UTIL_stat(-1, file) +Trace:FileStat: < 1 +Trace:FileStat: > UTIL_isDirectoryStat() +Trace:FileStat: < 0 +Trace:FileStat: > UTIL_stat(-1, file) +Trace:FileStat: < 1 +Trace:FileStat: > UTIL_isSameFile(file, out/file.zst) +Trace:FileStat: > UTIL_stat(-1, file) +Trace:FileStat: < 1 +Trace:FileStat: > UTIL_stat(-1, out/file.zst) +Trace:FileStat: < 0 +Trace:FileStat: < 0 +Trace:FileStat: > UTIL_isRegularFile(out/file.zst) +Trace:FileStat: > UTIL_stat(-1, out/file.zst) +Trace:FileStat: < 0 +Trace:FileStat: < 0 +zstd: out/file.zst: Permission denied +zstd: can't stat out/file.zst : Permission denied -- ignored diff --git a/tests/cli-tests/file-stat/compress-file-to-file.sh.stderr.exact b/tests/cli-tests/file-stat/compress-file-to-file.sh.stderr.exact index a1ad09ef7..32d248ee5 100644 --- a/tests/cli-tests/file-stat/compress-file-to-file.sh.stderr.exact +++ b/tests/cli-tests/file-stat/compress-file-to-file.sh.stderr.exact @@ -3,40 +3,40 @@ Trace:FileStat: < 0 Trace:FileStat: > UTIL_isConsole(2) Trace:FileStat: < 0 Trace:FileStat: > UTIL_getFileSize(file) -Trace:FileStat: > UTIL_stat(file) +Trace:FileStat: > UTIL_stat(-1, file) Trace:FileStat: < 1 Trace:FileStat: < 65537 -Trace:FileStat: > UTIL_stat(file) +Trace:FileStat: > UTIL_stat(-1, file) Trace:FileStat: < 1 Trace:FileStat: > UTIL_isDirectoryStat() Trace:FileStat: < 0 -Trace:FileStat: > UTIL_stat(file) +Trace:FileStat: > UTIL_stat(-1, file) Trace:FileStat: < 1 Trace:FileStat: > UTIL_isSameFile(file, file.zst) -Trace:FileStat: > UTIL_stat(file) +Trace:FileStat: > UTIL_stat(-1, file) Trace:FileStat: < 1 -Trace:FileStat: > UTIL_stat(file.zst) +Trace:FileStat: > UTIL_stat(-1, file.zst) Trace:FileStat: < 0 Trace:FileStat: < 0 Trace:FileStat: > UTIL_isRegularFile(file.zst) -Trace:FileStat: > UTIL_stat(file.zst) +Trace:FileStat: > UTIL_stat(-1, file.zst) Trace:FileStat: < 0 Trace:FileStat: < 0 Trace:FileStat: > UTIL_isRegularFile(file.zst) -Trace:FileStat: > UTIL_stat(file.zst) +Trace:FileStat: > UTIL_stat(-1, file.zst) Trace:FileStat: < 1 Trace:FileStat: < 1 Trace:FileStat: > UTIL_getFileSize(file) -Trace:FileStat: > UTIL_stat(file) +Trace:FileStat: > UTIL_stat(-1, file) Trace:FileStat: < 1 Trace:FileStat: < 65537 -Trace:FileStat: > UTIL_setFileStat(file.zst) -Trace:FileStat: > UTIL_stat(file.zst) +Trace:FileStat: > UTIL_setFileStat(4, file.zst) +Trace:FileStat: > UTIL_stat(4, file.zst) Trace:FileStat: < 1 -Trace:FileStat: > UTIL_utime(file.zst) -Trace:FileStat: < 0 Trace:FileStat: > UTIL_chmod(file.zst, 0642) -Trace:FileStat: > chmod +Trace:FileStat: > fchmod Trace:FileStat: < 0 Trace:FileStat: < 0 Trace:FileStat: < 0 +Trace:FileStat: > UTIL_utime(file.zst) +Trace:FileStat: < 0 diff --git a/tests/cli-tests/file-stat/compress-file-to-stdout.sh.stderr.exact b/tests/cli-tests/file-stat/compress-file-to-stdout.sh.stderr.exact index 7c690d20b..a25a35572 100644 --- a/tests/cli-tests/file-stat/compress-file-to-stdout.sh.stderr.exact +++ b/tests/cli-tests/file-stat/compress-file-to-stdout.sh.stderr.exact @@ -5,20 +5,20 @@ Trace:FileStat: < 0 Trace:FileStat: > UTIL_isConsole(2) Trace:FileStat: < 0 Trace:FileStat: > UTIL_getFileSize(file) -Trace:FileStat: > UTIL_stat(file) +Trace:FileStat: > UTIL_stat(-1, file) Trace:FileStat: < 1 Trace:FileStat: < 65537 -Trace:FileStat: > UTIL_stat(file) +Trace:FileStat: > UTIL_stat(-1, file) Trace:FileStat: < 1 Trace:FileStat: > UTIL_isDirectoryStat() Trace:FileStat: < 0 -Trace:FileStat: > UTIL_stat(file) +Trace:FileStat: > UTIL_stat(-1, file) Trace:FileStat: < 1 Trace:FileStat: > UTIL_isRegularFile(/*stdout*\) -Trace:FileStat: > UTIL_stat(/*stdout*\) +Trace:FileStat: > UTIL_stat(-1, /*stdout*\) Trace:FileStat: < 0 Trace:FileStat: < 0 Trace:FileStat: > UTIL_getFileSize(file) -Trace:FileStat: > UTIL_stat(file) +Trace:FileStat: > UTIL_stat(-1, file) Trace:FileStat: < 1 Trace:FileStat: < 65537 diff --git a/tests/cli-tests/file-stat/compress-stdin-to-file.sh.stderr.exact b/tests/cli-tests/file-stat/compress-stdin-to-file.sh.stderr.exact index 00afd97c3..9183c4674 100644 --- a/tests/cli-tests/file-stat/compress-stdin-to-file.sh.stderr.exact +++ b/tests/cli-tests/file-stat/compress-stdin-to-file.sh.stderr.exact @@ -3,22 +3,22 @@ Trace:FileStat: < 0 Trace:FileStat: > UTIL_isConsole(2) Trace:FileStat: < 0 Trace:FileStat: > UTIL_getFileSize(/*stdin*\) -Trace:FileStat: > UTIL_stat(/*stdin*\) +Trace:FileStat: > UTIL_stat(-1, /*stdin*\) Trace:FileStat: < 0 Trace:FileStat: < -1 Trace:FileStat: > UTIL_isSameFile(/*stdin*\, file.zst) -Trace:FileStat: > UTIL_stat(/*stdin*\) +Trace:FileStat: > UTIL_stat(-1, /*stdin*\) Trace:FileStat: < 0 Trace:FileStat: < 0 Trace:FileStat: > UTIL_isRegularFile(file.zst) -Trace:FileStat: > UTIL_stat(file.zst) +Trace:FileStat: > UTIL_stat(-1, file.zst) Trace:FileStat: < 0 Trace:FileStat: < 0 Trace:FileStat: > UTIL_isRegularFile(file.zst) -Trace:FileStat: > UTIL_stat(file.zst) +Trace:FileStat: > UTIL_stat(-1, file.zst) Trace:FileStat: < 1 Trace:FileStat: < 1 Trace:FileStat: > UTIL_getFileSize(/*stdin*\) -Trace:FileStat: > UTIL_stat(/*stdin*\) +Trace:FileStat: > UTIL_stat(-1, /*stdin*\) Trace:FileStat: < 0 Trace:FileStat: < -1 diff --git a/tests/cli-tests/file-stat/compress-stdin-to-stdout.sh.stderr.exact b/tests/cli-tests/file-stat/compress-stdin-to-stdout.sh.stderr.exact index 8bf05e641..66b630e43 100644 --- a/tests/cli-tests/file-stat/compress-stdin-to-stdout.sh.stderr.exact +++ b/tests/cli-tests/file-stat/compress-stdin-to-stdout.sh.stderr.exact @@ -5,14 +5,14 @@ Trace:FileStat: < 0 Trace:FileStat: > UTIL_isConsole(2) Trace:FileStat: < 0 Trace:FileStat: > UTIL_getFileSize(/*stdin*\) -Trace:FileStat: > UTIL_stat(/*stdin*\) +Trace:FileStat: > UTIL_stat(-1, /*stdin*\) Trace:FileStat: < 0 Trace:FileStat: < -1 Trace:FileStat: > UTIL_isRegularFile(/*stdout*\) -Trace:FileStat: > UTIL_stat(/*stdout*\) +Trace:FileStat: > UTIL_stat(-1, /*stdout*\) Trace:FileStat: < 0 Trace:FileStat: < 0 Trace:FileStat: > UTIL_getFileSize(/*stdin*\) -Trace:FileStat: > UTIL_stat(/*stdin*\) +Trace:FileStat: > UTIL_stat(-1, /*stdin*\) Trace:FileStat: < 0 Trace:FileStat: < -1 diff --git a/tests/cli-tests/file-stat/decompress-file-to-file.sh.stderr.exact b/tests/cli-tests/file-stat/decompress-file-to-file.sh.stderr.exact index d264c63e4..ad3a0deb8 100644 --- a/tests/cli-tests/file-stat/decompress-file-to-file.sh.stderr.exact +++ b/tests/cli-tests/file-stat/decompress-file-to-file.sh.stderr.exact @@ -5,34 +5,34 @@ Trace:FileStat: < 0 Trace:FileStat: > UTIL_isConsole(2) Trace:FileStat: < 0 Trace:FileStat: > UTIL_isDirectory(file.zst) -Trace:FileStat: > UTIL_stat(file.zst) +Trace:FileStat: > UTIL_stat(-1, file.zst) Trace:FileStat: < 1 Trace:FileStat: > UTIL_isDirectoryStat() Trace:FileStat: < 0 Trace:FileStat: < 0 -Trace:FileStat: > UTIL_stat(file.zst) +Trace:FileStat: > UTIL_stat(-1, file.zst) Trace:FileStat: < 1 Trace:FileStat: > UTIL_isSameFile(file.zst, file) -Trace:FileStat: > UTIL_stat(file.zst) +Trace:FileStat: > UTIL_stat(-1, file.zst) Trace:FileStat: < 1 -Trace:FileStat: > UTIL_stat(file) +Trace:FileStat: > UTIL_stat(-1, file) Trace:FileStat: < 0 Trace:FileStat: < 0 Trace:FileStat: > UTIL_isRegularFile(file) -Trace:FileStat: > UTIL_stat(file) +Trace:FileStat: > UTIL_stat(-1, file) Trace:FileStat: < 0 Trace:FileStat: < 0 Trace:FileStat: > UTIL_isRegularFile(file) -Trace:FileStat: > UTIL_stat(file) +Trace:FileStat: > UTIL_stat(-1, file) Trace:FileStat: < 1 Trace:FileStat: < 1 -Trace:FileStat: > UTIL_setFileStat(file) -Trace:FileStat: > UTIL_stat(file) +Trace:FileStat: > UTIL_setFileStat(4, file) +Trace:FileStat: > UTIL_stat(4, file) Trace:FileStat: < 1 -Trace:FileStat: > UTIL_utime(file) -Trace:FileStat: < 0 Trace:FileStat: > UTIL_chmod(file, 0642) -Trace:FileStat: > chmod +Trace:FileStat: > fchmod Trace:FileStat: < 0 Trace:FileStat: < 0 Trace:FileStat: < 0 +Trace:FileStat: > UTIL_utime(file) +Trace:FileStat: < 0 diff --git a/tests/cli-tests/file-stat/decompress-file-to-stdout.sh.stderr.exact b/tests/cli-tests/file-stat/decompress-file-to-stdout.sh.stderr.exact index 7fe6dda15..8b60063c5 100644 --- a/tests/cli-tests/file-stat/decompress-file-to-stdout.sh.stderr.exact +++ b/tests/cli-tests/file-stat/decompress-file-to-stdout.sh.stderr.exact @@ -5,14 +5,14 @@ Trace:FileStat: < 0 Trace:FileStat: > UTIL_isConsole(2) Trace:FileStat: < 0 Trace:FileStat: > UTIL_isDirectory(file.zst) -Trace:FileStat: > UTIL_stat(file.zst) +Trace:FileStat: > UTIL_stat(-1, file.zst) Trace:FileStat: < 1 Trace:FileStat: > UTIL_isDirectoryStat() Trace:FileStat: < 0 Trace:FileStat: < 0 -Trace:FileStat: > UTIL_stat(file.zst) +Trace:FileStat: > UTIL_stat(-1, file.zst) Trace:FileStat: < 1 Trace:FileStat: > UTIL_isRegularFile(/*stdout*\) -Trace:FileStat: > UTIL_stat(/*stdout*\) +Trace:FileStat: > UTIL_stat(-1, /*stdout*\) Trace:FileStat: < 0 Trace:FileStat: < 0 diff --git a/tests/cli-tests/file-stat/decompress-stdin-to-file.sh.stderr.exact b/tests/cli-tests/file-stat/decompress-stdin-to-file.sh.stderr.exact index 749fd395a..716a7ac7b 100644 --- a/tests/cli-tests/file-stat/decompress-stdin-to-file.sh.stderr.exact +++ b/tests/cli-tests/file-stat/decompress-stdin-to-file.sh.stderr.exact @@ -3,18 +3,18 @@ Trace:FileStat: < 0 Trace:FileStat: > UTIL_isConsole(2) Trace:FileStat: < 0 Trace:FileStat: > UTIL_isDirectory(/*stdin*\) -Trace:FileStat: > UTIL_stat(/*stdin*\) +Trace:FileStat: > UTIL_stat(-1, /*stdin*\) Trace:FileStat: < 0 Trace:FileStat: < 0 Trace:FileStat: > UTIL_isSameFile(/*stdin*\, file) -Trace:FileStat: > UTIL_stat(/*stdin*\) +Trace:FileStat: > UTIL_stat(-1, /*stdin*\) Trace:FileStat: < 0 Trace:FileStat: < 0 Trace:FileStat: > UTIL_isRegularFile(file) -Trace:FileStat: > UTIL_stat(file) +Trace:FileStat: > UTIL_stat(-1, file) Trace:FileStat: < 0 Trace:FileStat: < 0 Trace:FileStat: > UTIL_isRegularFile(file) -Trace:FileStat: > UTIL_stat(file) +Trace:FileStat: > UTIL_stat(-1, file) Trace:FileStat: < 1 Trace:FileStat: < 1 diff --git a/tests/cli-tests/file-stat/decompress-stdin-to-stdout.sh.stderr.exact b/tests/cli-tests/file-stat/decompress-stdin-to-stdout.sh.stderr.exact index e36cb9d05..2f6412059 100644 --- a/tests/cli-tests/file-stat/decompress-stdin-to-stdout.sh.stderr.exact +++ b/tests/cli-tests/file-stat/decompress-stdin-to-stdout.sh.stderr.exact @@ -5,10 +5,10 @@ Trace:FileStat: < 0 Trace:FileStat: > UTIL_isConsole(2) Trace:FileStat: < 0 Trace:FileStat: > UTIL_isDirectory(/*stdin*\) -Trace:FileStat: > UTIL_stat(/*stdin*\) +Trace:FileStat: > UTIL_stat(-1, /*stdin*\) Trace:FileStat: < 0 Trace:FileStat: < 0 Trace:FileStat: > UTIL_isRegularFile(/*stdout*\) -Trace:FileStat: > UTIL_stat(/*stdout*\) +Trace:FileStat: > UTIL_stat(-1, /*stdout*\) Trace:FileStat: < 0 Trace:FileStat: < 0 diff --git a/tests/cli-tests/run.py b/tests/cli-tests/run.py index 45af5124b..46564d2ff 100755 --- a/tests/cli-tests/run.py +++ b/tests/cli-tests/run.py @@ -109,25 +109,17 @@ def pop_line(data: bytes) -> typing.Tuple[typing.Optional[bytes], bytes]: the first line always ends in a :\n:, even if it is the last line and :data: doesn't end in :\n:. """ - NEWLINE = b"\n"[0] + NEWLINE = b"\n" if data == b'': return (None, data) - newline_idx = data.find(b"\n") - if newline_idx == -1: - end_idx = len(data) - else: - end_idx = newline_idx + 1 + parts = data.split(NEWLINE, maxsplit=1) + line = parts[0] + NEWLINE + if len(parts) == 1: + return line, b'' - line = data[:end_idx] - data = data[end_idx:] - - assert len(line) != 0 - if line[-1] != NEWLINE: - line += NEWLINE - - return (line, data) + return line, parts[1] def glob_line_matches(actual: bytes, expect: bytes) -> bool: @@ -535,7 +527,8 @@ class TestSuite: subprocess.run( args=[script], stdin=subprocess.DEVNULL, - capture_output=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, cwd=cwd, env=env, check=True, @@ -647,7 +640,7 @@ if __name__ == "__main__": help="Preserve the scratch directory TEST_DIR/scratch/ for debugging purposes." ) parser.add_argument("--verbose", action="store_true", help="Verbose test output.") - parser.add_argument("--timeout", default=60, type=int, help="Test case timeout in seconds. Set to 0 to disable timeouts.") + parser.add_argument("--timeout", default=200, type=int, help="Test case timeout in seconds. Set to 0 to disable timeouts.") parser.add_argument( "--exec-prefix", default=None, diff --git a/tests/decodecorpus.c b/tests/decodecorpus.c index 148088a76..e48eccd6d 100644 --- a/tests/decodecorpus.c +++ b/tests/decodecorpus.c @@ -26,6 +26,7 @@ /* Direct access to internal compression functions is required */ #include "compress/zstd_compress.c" /* ZSTD_resetSeqStore, ZSTD_storeSeq, *_TO_OFFBASE, HIST_countFast_wksp, HIST_isError */ +#include "decompress/zstd_decompress_block.h" /* ZSTD_decompressBlock_deprecated */ #define XXH_STATIC_LINKING_ONLY #include "xxhash.h" /* XXH64 */ @@ -1434,7 +1435,7 @@ static size_t testDecodeWithDict(U32 seed, genType_e genType) ZSTD_freeDCtx(dctx); goto dictTestCleanup; } - ret = ZSTD_decompressBlock(dctx, DECOMPRESSED_BUFFER, MAX_DECOMPRESSED_SIZE, + ret = ZSTD_decompressBlock_deprecated(dctx, DECOMPRESSED_BUFFER, MAX_DECOMPRESSED_SIZE, fr.dataStart, (BYTE*)fr.data - (BYTE*)fr.dataStart); } ZSTD_freeDCtx(dctx); @@ -1461,7 +1462,7 @@ static size_t testDecodeRawBlock(frame_t* fr) size_t ret = ZSTD_decompressBegin(dctx); if (ZSTD_isError(ret)) return ret; - ret = ZSTD_decompressBlock( + ret = ZSTD_decompressBlock_deprecated( dctx, DECOMPRESSED_BUFFER, MAX_DECOMPRESSED_SIZE, fr->dataStart, (BYTE*)fr->data - (BYTE*)fr->dataStart); diff --git a/tests/fullbench.c b/tests/fullbench.c index 3a72d89d3..41bd26d04 100644 --- a/tests/fullbench.c +++ b/tests/fullbench.c @@ -774,7 +774,9 @@ static int benchFiles(U32 benchNb, } else { for (benchNb=0; benchNb<100; benchNb++) { benchMem(benchNb, origBuff, benchedSize, cLevel, cparams); - } } + } + benchNb = 0; + } free(origBuff); } } diff --git a/tests/fuzz/Makefile b/tests/fuzz/Makefile index bbb262add..525e396bc 100644 --- a/tests/fuzz/Makefile +++ b/tests/fuzz/Makefile @@ -34,13 +34,13 @@ ZSTDDIR = ../../lib PRGDIR = ../../programs CONTRIBDIR = ../../contrib -# TODO(embg) make it possible to plug in an arbitrary matchfinder as a .o file -MATCHFINDER_DIR = $(CONTRIBDIR)/externalSequenceProducer -MATCHFINDER_SRC = $(MATCHFINDER_DIR)/sequence_producer.c +DEFAULT_SEQ_PROD_DIR = $(CONTRIBDIR)/externalSequenceProducer +DEFAULT_SEQ_PROD_SRC = $(DEFAULT_SEQ_PROD_DIR)/sequence_producer.c +THIRD_PARTY_SEQ_PROD_OBJ ?= FUZZ_CPPFLAGS := -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/compress \ -I$(ZSTDDIR)/dictBuilder -I$(ZSTDDIR)/deprecated -I$(ZSTDDIR)/legacy \ - -I$(CONTRIBDIR)/seekable_format -I$(PRGDIR) -I$(MATCHFINDER_DIR) \ + -I$(CONTRIBDIR)/seekable_format -I$(PRGDIR) -I$(DEFAULT_SEQ_PROD_DIR) \ -DZSTD_MULTITHREAD -DZSTD_LEGACY_SUPPORT=1 $(CPPFLAGS) FUZZ_EXTRA_FLAGS := -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \ -Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement \ @@ -75,7 +75,7 @@ FUZZ_SRC := \ $(ZSTDCOMP_SRC) \ $(ZSTDDICT_SRC) \ $(ZSTDLEGACY_SRC) \ - $(MATCHFINDER_SRC) + $(DEFAULT_SEQ_PROD_SRC) FUZZ_SRC := $(sort $(wildcard $(FUZZ_SRC))) FUZZ_D_OBJ1 := $(subst $(ZSTDDIR)/common/,d_lib_common_,$(FUZZ_SRC)) @@ -84,10 +84,11 @@ FUZZ_D_OBJ3 := $(subst $(ZSTDDIR)/decompress/,d_lib_decompress_,$(FUZZ_D_OBJ2)) FUZZ_D_OBJ4 := $(subst $(ZSTDDIR)/dictBuilder/,d_lib_dictBuilder_,$(FUZZ_D_OBJ3)) FUZZ_D_OBJ5 := $(subst $(ZSTDDIR)/legacy/,d_lib_legacy_,$(FUZZ_D_OBJ4)) FUZZ_D_OBJ6 := $(subst $(PRGDIR)/,d_prg_,$(FUZZ_D_OBJ5)) -FUZZ_D_OBJ7 := $(subst $(MATCHFINDER_DIR)/,d_matchfinder_,$(FUZZ_D_OBJ6)) +FUZZ_D_OBJ7 := $(subst $(DEFAULT_SEQ_PROD_DIR)/,d_default_seq_prod_,$(FUZZ_D_OBJ6)) FUZZ_D_OBJ8 := $(subst $\./,d_fuzz_,$(FUZZ_D_OBJ7)) FUZZ_D_OBJ9 := $(FUZZ_D_OBJ8:.c=.o) -FUZZ_DECOMPRESS_OBJ := $(FUZZ_D_OBJ9:.S=.o) +FUZZ_D_OBJ10 := $(THIRD_PARTY_SEQ_PROD_OBJ) $(FUZZ_D_OBJ9) +FUZZ_DECOMPRESS_OBJ := $(FUZZ_D_OBJ10:.S=.o) FUZZ_RT_OBJ1 := $(subst $(ZSTDDIR)/common/,rt_lib_common_,$(FUZZ_SRC)) FUZZ_RT_OBJ2 := $(subst $(ZSTDDIR)/compress/,rt_lib_compress_,$(FUZZ_RT_OBJ1)) @@ -95,10 +96,11 @@ FUZZ_RT_OBJ3 := $(subst $(ZSTDDIR)/decompress/,rt_lib_decompress_,$(FUZZ_RT_OBJ2 FUZZ_RT_OBJ4 := $(subst $(ZSTDDIR)/dictBuilder/,rt_lib_dictBuilder_,$(FUZZ_RT_OBJ3)) FUZZ_RT_OBJ5 := $(subst $(ZSTDDIR)/legacy/,rt_lib_legacy_,$(FUZZ_RT_OBJ4)) FUZZ_RT_OBJ6 := $(subst $(PRGDIR)/,rt_prg_,$(FUZZ_RT_OBJ5)) -FUZZ_RT_OBJ7 := $(subst $(MATCHFINDER_DIR)/,rt_matchfinder_,$(FUZZ_RT_OBJ6)) +FUZZ_RT_OBJ7 := $(subst $(DEFAULT_SEQ_PROD_DIR)/,rt_default_seq_prod_,$(FUZZ_RT_OBJ6)) FUZZ_RT_OBJ8 := $(subst $\./,rt_fuzz_,$(FUZZ_RT_OBJ7)) FUZZ_RT_OBJ9 := $(FUZZ_RT_OBJ8:.c=.o) -FUZZ_ROUND_TRIP_OBJ := $(FUZZ_RT_OBJ9:.S=.o) +FUZZ_RT_OBJ10 := $(THIRD_PARTY_SEQ_PROD_OBJ) $(FUZZ_RT_OBJ9) +FUZZ_ROUND_TRIP_OBJ := $(FUZZ_RT_OBJ10:.S=.o) .PHONY: default all clean cleanall @@ -151,7 +153,7 @@ rt_prg_%.o: $(PRGDIR)/%.c rt_fuzz_%.o: %.c $(CC) $(FUZZ_CPPFLAGS) $(FUZZ_CFLAGS) $(FUZZ_ROUND_TRIP_FLAGS) $< -c -o $@ -rt_matchfinder_%.o: $(MATCHFINDER_DIR)/%.c +rt_default_seq_prod_%.o: $(DEFAULT_SEQ_PROD_DIR)/%.c $(CC) $(FUZZ_CPPFLAGS) $(FUZZ_CFLAGS) $(FUZZ_ROUND_TRIP_FLAGS) $< -c -o $@ d_lib_common_%.o: $(ZSTDDIR)/common/%.c @@ -178,7 +180,7 @@ d_prg_%.o: $(PRGDIR)/%.c d_fuzz_%.o: %.c $(CC) $(FUZZ_CPPFLAGS) $(FUZZ_CFLAGS) $< -c -o $@ -d_matchfinder_%.o: $(MATCHFINDER_DIR)/%.c +d_default_seq_prod_%.o: $(DEFAULT_SEQ_PROD_DIR)/%.c $(CC) $(FUZZ_CPPFLAGS) $(FUZZ_CFLAGS) $< -c -o $@ simple_round_trip: $(FUZZ_HEADERS) $(FUZZ_ROUND_TRIP_OBJ) rt_fuzz_simple_round_trip.o diff --git a/tests/fuzz/README.md b/tests/fuzz/README.md index 4ff7fe3ac..2a9bd4570 100644 --- a/tests/fuzz/README.md +++ b/tests/fuzz/README.md @@ -61,7 +61,7 @@ Alternatively, you can fuzz all targets in parallel, using one core per target: ``` python3 ./fuzz.py list | xargs -P$(python3 ./fuzz.py list | wc -l) -I__ sh -c "python3 ./fuzz.py libfuzzer __ 2>&1 | tee __.log" ``` -Either way, to double-check that no crashes were found, run `ls corpora/*crash`. +Either way, to double-check that no crashes were found, run `ls corpora/*crash`. If any crashes were found, you can use the hashes to reproduce them. ## LibFuzzer @@ -113,3 +113,7 @@ CC=clang CXX=clang++ ./fuzz.py build all --enable-asan --enable-ubsan CC=clang CXX=clang++ ./fuzz.py build all --enable-msan ./fuzz.py regression all ``` + +## Fuzzing a custom sequence producer plugin +Sequence producer plugin authors can use the zstd fuzzers to stress-test their code. +See the documentation in `fuzz_third_party_seq_prod.h` for details. diff --git a/tests/fuzz/block_decompress.c b/tests/fuzz/block_decompress.c index e4767b3f5..9cc6005c3 100644 --- a/tests/fuzz/block_decompress.c +++ b/tests/fuzz/block_decompress.c @@ -13,6 +13,7 @@ * decompression function to ensure the decompressor never crashes. */ +#include "fuzz_data_producer.h" #define ZSTD_STATIC_LINKING_ONLY #include @@ -28,11 +29,12 @@ static size_t bufSize = 0; int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) { size_t const neededBufSize = ZSTD_BLOCKSIZE_MAX; + FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size); /* Allocate all buffers and contexts if not already allocated */ if (neededBufSize > bufSize) { free(rBuf); - rBuf = FUZZ_malloc(neededBufSize); + rBuf = FUZZ_malloc_rand(neededBufSize, producer); bufSize = neededBufSize; } if (!dctx) { @@ -42,6 +44,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) ZSTD_decompressBegin(dctx); ZSTD_decompressBlock(dctx, rBuf, neededBufSize, src, size); + FUZZ_dataProducer_free(producer); + #ifndef STATEFUL_FUZZING ZSTD_freeDCtx(dctx); dctx = NULL; #endif diff --git a/tests/fuzz/block_round_trip.c b/tests/fuzz/block_round_trip.c index 54012a1da..c17146dbc 100644 --- a/tests/fuzz/block_round_trip.c +++ b/tests/fuzz/block_round_trip.c @@ -23,6 +23,7 @@ #include "zstd.h" #include "zstd_helpers.h" #include "fuzz_data_producer.h" +#include "fuzz_third_party_seq_prod.h" static ZSTD_CCtx *cctx = NULL; static ZSTD_DCtx *dctx = NULL; @@ -54,6 +55,8 @@ static size_t roundTripTest(void *result, size_t resultCapacity, int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) { + FUZZ_SEQ_PROD_SETUP(); + /* Give a random portion of src data to the producer, to use for parameter generation. The rest will be used for (de)compression */ FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size); @@ -95,5 +98,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) ZSTD_freeCCtx(cctx); cctx = NULL; ZSTD_freeDCtx(dctx); dctx = NULL; #endif + FUZZ_SEQ_PROD_TEARDOWN(); return 0; } diff --git a/tests/fuzz/decompress_dstSize_tooSmall.c b/tests/fuzz/decompress_dstSize_tooSmall.c index 8ad3ea1c9..3f7095d11 100644 --- a/tests/fuzz/decompress_dstSize_tooSmall.c +++ b/tests/fuzz/decompress_dstSize_tooSmall.c @@ -22,12 +22,15 @@ #include "zstd_errors.h" #include "zstd_helpers.h" #include "fuzz_data_producer.h" +#include "fuzz_third_party_seq_prod.h" static ZSTD_CCtx *cctx = NULL; static ZSTD_DCtx *dctx = NULL; int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) { + FUZZ_SEQ_PROD_SETUP(); + /* Give a random portion of src data to the producer, to use for parameter generation. The rest will be used for (de)compression */ FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size); @@ -66,5 +69,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) ZSTD_freeCCtx(cctx); cctx = NULL; ZSTD_freeDCtx(dctx); dctx = NULL; #endif + FUZZ_SEQ_PROD_TEARDOWN(); return 0; } diff --git a/tests/fuzz/dictionary_decompress.c b/tests/fuzz/dictionary_decompress.c index 8dea3e560..c37d14374 100644 --- a/tests/fuzz/dictionary_decompress.c +++ b/tests/fuzz/dictionary_decompress.c @@ -20,11 +20,14 @@ #include "fuzz_helpers.h" #include "zstd_helpers.h" #include "fuzz_data_producer.h" +#include "fuzz_third_party_seq_prod.h" static ZSTD_DCtx *dctx = NULL; int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) { + FUZZ_SEQ_PROD_SETUP(); + /* Give a random portion of src data to the producer, to use for parameter generation. The rest will be used for (de)compression */ FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size); @@ -69,5 +72,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) #ifndef STATEFUL_FUZZING ZSTD_freeDCtx(dctx); dctx = NULL; #endif + FUZZ_SEQ_PROD_TEARDOWN(); return 0; } diff --git a/tests/fuzz/dictionary_loader.c b/tests/fuzz/dictionary_loader.c index 1ac22740f..7b7c00428 100644 --- a/tests/fuzz/dictionary_loader.c +++ b/tests/fuzz/dictionary_loader.c @@ -20,6 +20,7 @@ #include "fuzz_helpers.h" #include "zstd_helpers.h" #include "fuzz_data_producer.h" +#include "fuzz_third_party_seq_prod.h" /** * Compresses the data and returns the compressed size or an error. @@ -35,7 +36,7 @@ static size_t compress(void* compressed, size_t compressedCapacity, if (refPrefix) FUZZ_ZASSERT(ZSTD_CCtx_refPrefix_advanced( cctx, dict, dictSize, dictContentType)); - else + else FUZZ_ZASSERT(ZSTD_CCtx_loadDictionary_advanced( cctx, dict, dictSize, dictLoadMethod, dictContentType)); size_t const compressedSize = ZSTD_compress2( @@ -67,6 +68,7 @@ static size_t decompress(void* result, size_t resultCapacity, int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) { + FUZZ_SEQ_PROD_SETUP(); FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size); int const refPrefix = FUZZ_dataProducer_uint32Range(producer, 0, 1) != 0; ZSTD_dictLoadMethod_e const dlm = @@ -99,5 +101,6 @@ out: free(cBuf); free(rBuf); FUZZ_dataProducer_free(producer); + FUZZ_SEQ_PROD_TEARDOWN(); return 0; } diff --git a/tests/fuzz/dictionary_round_trip.c b/tests/fuzz/dictionary_round_trip.c index 6dd78c3f3..06fdf24e9 100644 --- a/tests/fuzz/dictionary_round_trip.c +++ b/tests/fuzz/dictionary_round_trip.c @@ -21,6 +21,7 @@ #include "fuzz_helpers.h" #include "zstd_helpers.h" #include "fuzz_data_producer.h" +#include "fuzz_third_party_seq_prod.h" static ZSTD_CCtx *cctx = NULL; static ZSTD_DCtx *dctx = NULL; @@ -108,6 +109,8 @@ static size_t roundTripTest(void *result, size_t resultCapacity, int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) { + FUZZ_SEQ_PROD_SETUP(); + /* Give a random portion of src data to the producer, to use for parameter generation. The rest will be used for (de)compression */ FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size); @@ -147,5 +150,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) ZSTD_freeCCtx(cctx); cctx = NULL; ZSTD_freeDCtx(dctx); dctx = NULL; #endif + FUZZ_SEQ_PROD_TEARDOWN(); return 0; } diff --git a/tests/fuzz/dictionary_stream_round_trip.c b/tests/fuzz/dictionary_stream_round_trip.c index 3ebbd843b..3a1555333 100644 --- a/tests/fuzz/dictionary_stream_round_trip.c +++ b/tests/fuzz/dictionary_stream_round_trip.c @@ -22,6 +22,7 @@ #include "fuzz_helpers.h" #include "zstd_helpers.h" #include "fuzz_data_producer.h" +#include "fuzz_third_party_seq_prod.h" ZSTD_CCtx *cctx = NULL; static ZSTD_DCtx *dctx = NULL; @@ -147,6 +148,7 @@ static size_t compress(uint8_t *dst, size_t capacity, int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) { + FUZZ_SEQ_PROD_SETUP(); size_t neededBufSize; /* Give a random portion of src data to the producer, to use for @@ -202,5 +204,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) ZSTD_freeCCtx(cctx); cctx = NULL; ZSTD_freeDCtx(dctx); dctx = NULL; #endif + FUZZ_SEQ_PROD_TEARDOWN(); return 0; } diff --git a/tests/fuzz/fuzz.h b/tests/fuzz/fuzz.h index d1e439fef..8064c708c 100644 --- a/tests/fuzz/fuzz.h +++ b/tests/fuzz/fuzz.h @@ -31,6 +31,11 @@ * This is the canonical flag to enable deterministic builds for fuzzing. * Changes to zstd for fuzzing are gated behind this define. * It is recommended to define this when building zstd for fuzzing. + * @param FUZZ_THIRD_PARTY_SEQ_PROD + * This flag allows sequence producer plugin authors to replace the built-in + * default sequence producer with their own code. If you are not a plugin + * author, you should not define this flag. See the docs at + * fuzz_third_party_seq_prod.h for more information. */ #ifndef FUZZ_H diff --git a/tests/fuzz/fuzz.py b/tests/fuzz/fuzz.py index 03ffeeefd..8e0a9eaad 100755 --- a/tests/fuzz/fuzz.py +++ b/tests/fuzz/fuzz.py @@ -78,6 +78,7 @@ CFLAGS = os.environ.get('CFLAGS', '-O3') CXXFLAGS = os.environ.get('CXXFLAGS', CFLAGS) LDFLAGS = os.environ.get('LDFLAGS', '') MFLAGS = os.environ.get('MFLAGS', '-j') +THIRD_PARTY_SEQ_PROD_OBJ = os.environ.get('THIRD_PARTY_SEQ_PROD_OBJ', '') # Fuzzing environment variables LIB_FUZZING_ENGINE = os.environ.get('LIB_FUZZING_ENGINE', 'libregression.a') @@ -319,6 +320,12 @@ def build_parser(args): dest='stateful_fuzzing', action='store_true', help='Reuse contexts between runs (makes reproduction impossible)') + parser.add_argument( + '--custom-seq-prod', + dest='third_party_seq_prod_obj', + type=str, + default=THIRD_PARTY_SEQ_PROD_OBJ, + help='Path to an object file with symbols for fuzzing your sequence producer plugin.') parser.add_argument( '--cc', dest='cc', @@ -450,6 +457,10 @@ def build(args): if args.stateful_fuzzing: cppflags += ['-DSTATEFUL_FUZZING'] + if args.third_party_seq_prod_obj: + cppflags += ['-DFUZZ_THIRD_PARTY_SEQ_PROD'] + mflags += ['THIRD_PARTY_SEQ_PROD_OBJ={}'.format(args.third_party_seq_prod_obj)] + if args.fuzzing_mode: cppflags += ['-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION'] diff --git a/tests/fuzz/fuzz_data_producer.c b/tests/fuzz/fuzz_data_producer.c index a93e8ba95..bf846b68f 100644 --- a/tests/fuzz/fuzz_data_producer.c +++ b/tests/fuzz/fuzz_data_producer.c @@ -8,6 +8,7 @@ * You may select, at your option, one of the above-listed licenses. */ +#include "fuzz_helpers.h" #include "fuzz_data_producer.h" struct FUZZ_dataProducer_s{ diff --git a/tests/fuzz/fuzz_data_producer.h b/tests/fuzz/fuzz_data_producer.h index 8ca501f9b..f488ceb99 100644 --- a/tests/fuzz/fuzz_data_producer.h +++ b/tests/fuzz/fuzz_data_producer.h @@ -24,7 +24,6 @@ #include #include -#include "fuzz_helpers.h" /* Struct used for maintaining the state of the data */ typedef struct FUZZ_dataProducer_s FUZZ_dataProducer_t; diff --git a/tests/fuzz/fuzz_helpers.c b/tests/fuzz/fuzz_helpers.c index 1b6ad9736..f47ff2eb4 100644 --- a/tests/fuzz/fuzz_helpers.c +++ b/tests/fuzz/fuzz_helpers.c @@ -23,6 +23,22 @@ void* FUZZ_malloc(size_t size) return NULL; } +void* FUZZ_malloc_rand(size_t size, FUZZ_dataProducer_t *producer) +{ + if (size > 0) { + void* const mem = malloc(size); + FUZZ_ASSERT(mem); + return mem; + } else { + uintptr_t ptr = 0; + /* Add +- 1M 50% of the time */ + if (FUZZ_dataProducer_uint32Range(producer, 0, 1)) + FUZZ_dataProducer_int32Range(producer, -1000000, 1000000); + return (void*)ptr; + } + +} + int FUZZ_memcmp(void const* lhs, void const* rhs, size_t size) { if (size == 0) { diff --git a/tests/fuzz/fuzz_helpers.h b/tests/fuzz/fuzz_helpers.h index aaf4c1df4..f21ec4751 100644 --- a/tests/fuzz/fuzz_helpers.h +++ b/tests/fuzz/fuzz_helpers.h @@ -19,6 +19,7 @@ #include "fuzz.h" #include "xxhash.h" #include "zstd.h" +#include "fuzz_data_producer.h" #include #include #include @@ -62,6 +63,12 @@ extern "C" { */ void* FUZZ_malloc(size_t size); +/** + * malloc except returns random pointer for zero sized data and FUZZ_ASSERT + * that malloc doesn't fail. + */ +void* FUZZ_malloc_rand(size_t size, FUZZ_dataProducer_t *producer); + /** * memcmp but accepts NULL. */ diff --git a/tests/fuzz/fuzz_third_party_seq_prod.h b/tests/fuzz/fuzz_third_party_seq_prod.h new file mode 100644 index 000000000..f04ad31ad --- /dev/null +++ b/tests/fuzz/fuzz_third_party_seq_prod.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) Yann Collet, Meta Platforms, Inc. + * All rights reserved. + * + * This source code is licensed under both the BSD-style license (found in the + * LICENSE file in the root directory of this source tree) and the GPLv2 (found + * in the COPYING file in the root directory of this source tree). + * You may select, at your option, one of the above-listed licenses. + */ + +#ifndef EXAMPLE_SEQ_PROD_H +#define EXAMPLE_SEQ_PROD_H + +#define ZSTD_STATIC_LINKING_ONLY +#include "zstd.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* *** INTERFACE FOR FUZZING THIRD-PARTY SEQUENCE PRODUCER PLUGINS *** + * Fuzz-testing for the external sequence producer API was introduced in PR #3437. + * However, the setup in #3437 only allows fuzzers to exercise the implementation of the + * API itself (the code in the core zstd library which interacts with your plugin). + * + * This header defines an interface for plugin authors to link their code into the fuzzer + * build. Plugin authors can provide an object file implementing the symbols below, + * and those symbols will replace the default ones provided by #3437. + * + * To fuzz your plugin, follow these steps: + * - Build your object file with a recent version of clang. Building with gcc is not supported. + * - Build your object file using appropriate flags for fuzzing. For example: + * `-g -fno-omit-frame-pointer -fsanitize=undefined,address,fuzzer` + * - Build the fuzzer binaries with options corresponding to the flags you chose. Use --custom-seq-prod= to pass in your object file: + * `./fuzz.py build all --enable-fuzzer --enable-asan --enable-ubsan --cc clang --cxx clang++ --custom-seq-prod=your_object.o` + * + * An example implementation of this header is provided at tests/fuzz/seq_prod_fuzz_example/. + * Use these commands to fuzz with the example code: + * $ make corpora + * $ make -C seq_prod_fuzz_example/ + * $ python3 ./fuzz.py build all --enable-fuzzer --enable-asan --enable-ubsan --cc clang --cxx clang++ --custom-seq-prod=seq_prod_fuzz_example/example_seq_prod.o + * $ python3 ./fuzz.py libfuzzer simple_round_trip + */ + +/* The fuzzer will call this function before each test-case. It should run any + * setup actions (such as starting a hardware device) needed for fuzzing. + * + * The fuzzer will assert() that the return value is zero. To signal an error, + * please return a non-zero value. */ +size_t FUZZ_seqProdSetup(void); + +/* The fuzzer will call this function after each test-case. It should free + * resources aquired by FUZZ_seqProdSetup() to prevent leaks across test-cases. + * + * The fuzzer will assert() that the return value is zero. To signal an error, + * please return a non-zero value. */ +size_t FUZZ_seqProdTearDown(void); + +/* The fuzzer will call this function before each test-case, only after calling + * FUZZ_seqProdSetup(), to obtain a sequence producer state which can be passed + * into ZSTD_registerSequenceProducer(). + * + * All compressions which are part of a test-case will share a single sequence + * producer state. Sharing the state object is safe because the fuzzers currently + * don't exercise the sequence producer API in multi-threaded scenarios. We may + * need a new approach in the future to support multi-threaded fuzzing. + * + * The fuzzer will assert() that the return value is not NULL. To signal an error, + * please return NULL. */ +void* FUZZ_createSeqProdState(void); + +/* The fuzzer will call this function after each test-case. It should free any + * resources aquired by FUZZ_createSeqProdState(). + * + * The fuzzer will assert() that the return value is zero. To signal an error, + * please return a non-zero value. */ +size_t FUZZ_freeSeqProdState(void* sequenceProducerState); + +/* This is the sequence producer function you would like to fuzz! It will receive + * the void* returned by FUZZ_createSeqProdState() on each invocation. */ +size_t FUZZ_thirdPartySeqProd(void* sequenceProducerState, + ZSTD_Sequence* outSeqs, size_t outSeqsCapacity, + const void* src, size_t srcSize, + const void* dict, size_t dictSize, + int compressionLevel, + size_t windowSize); + +/* These macros are internal helpers. You do not need to worry about them. */ +#ifdef FUZZ_THIRD_PARTY_SEQ_PROD +#define FUZZ_SEQ_PROD_SETUP() \ + do { \ + FUZZ_ASSERT(FUZZ_seqProdSetup() == 0); \ + FUZZ_seqProdState = FUZZ_createSeqProdState(); \ + FUZZ_ASSERT(FUZZ_seqProdState != NULL); \ + } while (0) +#else +#define FUZZ_SEQ_PROD_SETUP() +#endif + +#ifdef FUZZ_THIRD_PARTY_SEQ_PROD +#define FUZZ_SEQ_PROD_TEARDOWN() \ + do { \ + FUZZ_ASSERT(FUZZ_freeSeqProdState(FUZZ_seqProdState) == 0); \ + FUZZ_ASSERT(FUZZ_seqProdTearDown() == 0); \ + } while (0) +#else +#define FUZZ_SEQ_PROD_TEARDOWN() +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* EXAMPLE_SEQ_PROD_H */ diff --git a/tests/fuzz/raw_dictionary_round_trip.c b/tests/fuzz/raw_dictionary_round_trip.c index 7ceab2be6..7536f5537 100644 --- a/tests/fuzz/raw_dictionary_round_trip.c +++ b/tests/fuzz/raw_dictionary_round_trip.c @@ -21,6 +21,7 @@ #include "fuzz_helpers.h" #include "zstd_helpers.h" #include "fuzz_data_producer.h" +#include "fuzz_third_party_seq_prod.h" static ZSTD_CCtx *cctx = NULL; static ZSTD_DCtx *dctx = NULL; @@ -42,7 +43,7 @@ static size_t roundTripTest(void *result, size_t resultCapacity, FUZZ_ZASSERT(ZSTD_CCtx_refPrefix_advanced( cctx, dict, dictSize, ZSTD_dct_rawContent)); - else + else FUZZ_ZASSERT(ZSTD_CCtx_loadDictionary_advanced( cctx, dict, dictSize, (ZSTD_dictLoadMethod_e)FUZZ_dataProducer_uint32Range(producer, 0, 1), @@ -68,6 +69,8 @@ static size_t roundTripTest(void *result, size_t resultCapacity, int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) { + FUZZ_SEQ_PROD_SETUP(); + /* Give a random portion of src data to the producer, to use for parameter generation. The rest will be used for (de)compression */ FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size); @@ -111,5 +114,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) ZSTD_freeCCtx(cctx); cctx = NULL; ZSTD_freeDCtx(dctx); dctx = NULL; #endif + FUZZ_SEQ_PROD_TEARDOWN(); return 0; } diff --git a/tests/fuzz/seq_prod_fuzz_example/Makefile b/tests/fuzz/seq_prod_fuzz_example/Makefile new file mode 100644 index 000000000..f9d9fad36 --- /dev/null +++ b/tests/fuzz/seq_prod_fuzz_example/Makefile @@ -0,0 +1,16 @@ +# Copyright (c) Yann Collet, Meta Platforms, Inc. +# All rights reserved. +# +# This source code is licensed under both the BSD-style license (found in the +# LICENSE file in the root directory of this source tree) and the GPLv2 (found +# in the COPYING file in the root directory of this source tree). +# You may select, at your option, one of the above-listed licenses. + +CC = clang +CFLAGS = -g -fno-omit-frame-pointer -fsanitize=undefined,address,fuzzer -I../ -I../../../lib/ + +.PHONY: default +default: example_seq_prod.o + +example_seq_prod.o: example_seq_prod.c + $(CC) -c $(CFLAGS) $^ -o $@ diff --git a/tests/fuzz/seq_prod_fuzz_example/README.md b/tests/fuzz/seq_prod_fuzz_example/README.md new file mode 100644 index 000000000..16fff68ad --- /dev/null +++ b/tests/fuzz/seq_prod_fuzz_example/README.md @@ -0,0 +1,12 @@ +# Fuzzing a Custom Sequence Producer Plugin +This directory contains example code for using a custom sequence producer in the zstd fuzzers. + +You can build and run the code in this directory using these commands: +``` +$ make corpora +$ make -C seq_prod_fuzz_example/ +$ python3 ./fuzz.py build all --enable-fuzzer --enable-asan --enable-ubsan --cc clang --cxx clang++ --custom-seq-prod=seq_prod_fuzz_example/example_seq_prod.o +$ python3 ./fuzz.py libfuzzer simple_round_trip +``` + +See `../fuzz_third_party_seq_prod.h` and `../README.md` for more information on zstd fuzzing. diff --git a/tests/fuzz/seq_prod_fuzz_example/example_seq_prod.c b/tests/fuzz/seq_prod_fuzz_example/example_seq_prod.c new file mode 100644 index 000000000..fb4473cff --- /dev/null +++ b/tests/fuzz/seq_prod_fuzz_example/example_seq_prod.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) Yann Collet, Meta Platforms, Inc. + * All rights reserved. + * + * This source code is licensed under both the BSD-style license (found in the + * LICENSE file in the root directory of this source tree) and the GPLv2 (found + * in the COPYING file in the root directory of this source tree). + * You may select, at your option, one of the above-listed licenses. + */ + +#include "fuzz_third_party_seq_prod.h" + +#include +#include +#include + +_Thread_local size_t threadLocalState; + +size_t FUZZ_seqProdSetup(void) { + threadLocalState = 0; + return 0; +} + +size_t FUZZ_seqProdTearDown(void) { + return 0; +} + +void* FUZZ_createSeqProdState(void) { + return calloc(1, sizeof(size_t)); +} + +size_t FUZZ_freeSeqProdState(void* state) { + free(state); + return 0; +} + +size_t FUZZ_thirdPartySeqProd( + void* sequenceProducerState, + ZSTD_Sequence* outSeqs, size_t outSeqsCapacity, + const void* src, size_t srcSize, + const void* dict, size_t dictSize, + int compressionLevel, + size_t windowSize +) { + /* Try to catch unsafe use of the shared state */ + size_t* const sharedStatePtr = (size_t*)sequenceProducerState; + assert(*sharedStatePtr == threadLocalState); + (*sharedStatePtr)++; threadLocalState++; + + /* Check that fallback is enabled when FUZZ_THIRD_PARTY_SEQ_PROD is defined */ + return ZSTD_SEQUENCE_PRODUCER_ERROR; +} diff --git a/tests/fuzz/sequence_compression_api.c b/tests/fuzz/sequence_compression_api.c index 9d3f0a18a..ede7080e3 100644 --- a/tests/fuzz/sequence_compression_api.c +++ b/tests/fuzz/sequence_compression_api.c @@ -25,6 +25,7 @@ #include "fuzz_helpers.h" #include "zstd_helpers.h" #include "fuzz_data_producer.h" +#include "fuzz_third_party_seq_prod.h" static ZSTD_CCtx* cctx = NULL; static ZSTD_DCtx* dctx = NULL; @@ -263,6 +264,8 @@ static size_t roundTripTest(void* result, size_t resultCapacity, int LLVMFuzzerTestOneInput(const uint8_t* src, size_t size) { + FUZZ_SEQ_PROD_SETUP(); + void* rBuf; size_t rBufSize; void* cBuf; @@ -373,5 +376,6 @@ int LLVMFuzzerTestOneInput(const uint8_t* src, size_t size) free(generatedSrc); generatedSrc = NULL; free(literalsBuffer); literalsBuffer = NULL; #endif + FUZZ_SEQ_PROD_TEARDOWN(); return 0; } diff --git a/tests/fuzz/simple_compress.c b/tests/fuzz/simple_compress.c index c9fea22d3..74f0356b5 100644 --- a/tests/fuzz/simple_compress.c +++ b/tests/fuzz/simple_compress.c @@ -22,11 +22,14 @@ #include "zstd_errors.h" #include "zstd_helpers.h" #include "fuzz_data_producer.h" +#include "fuzz_third_party_seq_prod.h" static ZSTD_CCtx *cctx = NULL; int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) { + FUZZ_SEQ_PROD_SETUP(); + /* Give a random portion of src data to the producer, to use for parameter generation. The rest will be used for (de)compression */ FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size); @@ -52,5 +55,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) #ifndef STATEFUL_FUZZING ZSTD_freeCCtx(cctx); cctx = NULL; #endif + FUZZ_SEQ_PROD_TEARDOWN(); return 0; } diff --git a/tests/fuzz/simple_round_trip.c b/tests/fuzz/simple_round_trip.c index c2c69d950..8b1231975 100644 --- a/tests/fuzz/simple_round_trip.c +++ b/tests/fuzz/simple_round_trip.c @@ -22,6 +22,7 @@ #include "fuzz_helpers.h" #include "zstd_helpers.h" #include "fuzz_data_producer.h" +#include "fuzz_third_party_seq_prod.h" static ZSTD_CCtx *cctx = NULL; static ZSTD_DCtx *dctx = NULL; @@ -129,6 +130,8 @@ static size_t roundTripTest(void *result, size_t resultCapacity, int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) { + FUZZ_SEQ_PROD_SETUP(); + size_t const rBufSize = size; void* rBuf = FUZZ_malloc(rBufSize); size_t cBufSize = ZSTD_compressBound(size); @@ -164,5 +167,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) ZSTD_freeCCtx(cctx); cctx = NULL; ZSTD_freeDCtx(dctx); dctx = NULL; #endif + FUZZ_SEQ_PROD_TEARDOWN(); return 0; } diff --git a/tests/fuzz/stream_round_trip.c b/tests/fuzz/stream_round_trip.c index fae9ccbf4..7d277a857 100644 --- a/tests/fuzz/stream_round_trip.c +++ b/tests/fuzz/stream_round_trip.c @@ -22,6 +22,7 @@ #include "fuzz_helpers.h" #include "zstd_helpers.h" #include "fuzz_data_producer.h" +#include "fuzz_third_party_seq_prod.h" ZSTD_CCtx *cctx = NULL; static ZSTD_DCtx *dctx = NULL; @@ -133,6 +134,7 @@ static size_t compress(uint8_t *dst, size_t capacity, int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) { + FUZZ_SEQ_PROD_SETUP(); size_t neededBufSize; /* Give a random portion of src data to the producer, to use for @@ -191,5 +193,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) ZSTD_freeCCtx(cctx); cctx = NULL; ZSTD_freeDCtx(dctx); dctx = NULL; #endif + FUZZ_SEQ_PROD_TEARDOWN(); return 0; } diff --git a/tests/fuzz/zstd_helpers.c b/tests/fuzz/zstd_helpers.c index 411b6391c..f4cb10823 100644 --- a/tests/fuzz/zstd_helpers.c +++ b/tests/fuzz/zstd_helpers.c @@ -18,10 +18,13 @@ #include "zstd.h" #include "zdict.h" #include "sequence_producer.h" +#include "fuzz_third_party_seq_prod.h" const int kMinClevel = -3; const int kMaxClevel = 19; +void* FUZZ_seqProdState = NULL; + static void set(ZSTD_CCtx *cctx, ZSTD_cParameter param, int value) { FUZZ_ZASSERT(ZSTD_CCtx_setParameter(cctx, param, value)); @@ -72,12 +75,25 @@ ZSTD_parameters FUZZ_randomParams(size_t srcSize, FUZZ_dataProducer_t *producer) } static void setSequenceProducerParams(ZSTD_CCtx *cctx, FUZZ_dataProducer_t *producer) { +#ifdef FUZZ_THIRD_PARTY_SEQ_PROD + ZSTD_registerSequenceProducer( + cctx, + FUZZ_seqProdState, + FUZZ_thirdPartySeqProd + ); +#else ZSTD_registerSequenceProducer( cctx, NULL, simpleSequenceProducer ); +#endif + +#ifdef FUZZ_THIRD_PARTY_SEQ_PROD + FUZZ_ZASSERT(ZSTD_CCtx_setParameter(cctx, ZSTD_c_enableSeqProducerFallback, 1)); +#else setRand(cctx, ZSTD_c_enableSeqProducerFallback, 0, 1, producer); +#endif FUZZ_ZASSERT(ZSTD_CCtx_setParameter(cctx, ZSTD_c_nbWorkers, 0)); FUZZ_ZASSERT(ZSTD_CCtx_setParameter(cctx, ZSTD_c_enableLongDistanceMatching, ZSTD_ps_disable)); } @@ -137,11 +153,15 @@ void FUZZ_setRandomParameters(ZSTD_CCtx *cctx, size_t srcSize, FUZZ_dataProducer setRand(cctx, ZSTD_c_targetCBlockSize, ZSTD_TARGETCBLOCKSIZE_MIN, ZSTD_TARGETCBLOCKSIZE_MAX, producer); } +#ifdef FUZZ_THIRD_PARTY_SEQ_PROD + setSequenceProducerParams(cctx, producer); +#else if (FUZZ_dataProducer_uint32Range(producer, 0, 10) == 1) { setSequenceProducerParams(cctx, producer); } else { ZSTD_registerSequenceProducer(cctx, NULL, NULL); } +#endif } FUZZ_dict_t FUZZ_train(void const* src, size_t srcSize, FUZZ_dataProducer_t *producer) diff --git a/tests/fuzz/zstd_helpers.h b/tests/fuzz/zstd_helpers.h index a4cfe321b..be3071d2f 100644 --- a/tests/fuzz/zstd_helpers.h +++ b/tests/fuzz/zstd_helpers.h @@ -45,6 +45,10 @@ typedef struct { */ FUZZ_dict_t FUZZ_train(void const* src, size_t srcSize, FUZZ_dataProducer_t *producer); +#ifdef FUZZ_THIRD_PARTY_SEQ_PROD +extern void* FUZZ_seqProdState; +#endif + #ifdef __cplusplus } #endif diff --git a/tests/fuzzer.c b/tests/fuzzer.c index fc78c7fbc..07ddfefd6 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -1286,6 +1286,73 @@ static int basicUnitTests(U32 const seed, double compressibility) } DISPLAYLEVEL(3, "OK \n"); + DISPLAYLEVEL(3, "test%3i : Check block splitter with 64K literal length : ", testNb++); + { ZSTD_CCtx* cctx = ZSTD_createCCtx(); + size_t const srcSize = 256 * 1024; + U32 const compressibleLenU32 = 32 * 1024 / 4; + U32 const blockSizeU32 = 128 * 1024 / 4; + U32 const litLenU32 = 64 * 1024 / 4; + U32* data = (U32*)malloc(srcSize); + size_t dSize; + + if (data == NULL || cctx == NULL) goto _output_error; + + /* Generate data without any matches */ + RDG_genBuffer(data, srcSize, 0.0, 0.01, 2654435761U); + /* Generate 32K of compressible data */ + RDG_genBuffer(data, compressibleLenU32 * 4, 0.5, 0.5, 0xcafebabe); + + /* Add a match of offset=12, length=8 at idx=16, 32, 48, 64 */ + data[compressibleLenU32 + 0] = 0xFFFFFFFF; + data[compressibleLenU32 + 1] = 0xEEEEEEEE; + data[compressibleLenU32 + 4] = 0xFFFFFFFF; + data[compressibleLenU32 + 5] = 0xEEEEEEEE; + + /* Add a match of offset=16, length=8 at idx=64K + 64. + * This generates a sequence with llen=64K, and repeat code 1. + * The block splitter thought this was ll0, and corrupted the + * repeat offset history. + */ + data[compressibleLenU32 + litLenU32 + 2 + 0] = 0xDDDDDDDD; + data[compressibleLenU32 + litLenU32 + 2 + 1] = 0xCCCCCCCC; + data[compressibleLenU32 + litLenU32 + 2 + 4] = 0xDDDDDDDD; + data[compressibleLenU32 + litLenU32 + 2 + 5] = 0xCCCCCCCC; + + /* Add a match of offset=16, length=8 at idx=128K + 16. + * This should generate a sequence with repeat code = 1. + * But the block splitters mistake caused zstd to generate + * repeat code = 2, corrupting the data. + */ + data[blockSizeU32] = 0xBBBBBBBB; + data[blockSizeU32 + 1] = 0xAAAAAAAA; + data[blockSizeU32 + 4] = 0xBBBBBBBB; + data[blockSizeU32 + 5] = 0xAAAAAAAA; + + /* Generate a golden file from this data in case datagen changes and + * doesn't generate the exact same data. We will also test this golden file. + */ + if (0) { + FILE* f = fopen("golden-compression/PR-3517-block-splitter-corruption-test", "wb"); + fwrite(data, 1, srcSize, f); + fclose(f); + } + + CHECK_Z(ZSTD_CCtx_setParameter(cctx, ZSTD_c_compressionLevel, 19)); + CHECK_Z(ZSTD_CCtx_setParameter(cctx, ZSTD_c_minMatch, 7)); + CHECK_Z(ZSTD_CCtx_setParameter(cctx, ZSTD_c_useBlockSplitter, ZSTD_ps_enable)); + + cSize = ZSTD_compress2(cctx, compressedBuffer, compressedBufferSize, data, srcSize); + CHECK_Z(cSize); + dSize = ZSTD_decompress(decodedBuffer, CNBuffSize, compressedBuffer, cSize); + CHECK_Z(dSize); + CHECK_EQ(dSize, srcSize); + CHECK(!memcmp(decodedBuffer, data, srcSize)); + + free(data); + ZSTD_freeCCtx(cctx); + } + DISPLAYLEVEL(3, "OK \n"); + DISPLAYLEVEL(3, "test%3d: superblock uncompressible data, too many nocompress superblocks : ", testNb++); { ZSTD_CCtx* const cctx = ZSTD_createCCtx(); @@ -1583,6 +1650,133 @@ static int basicUnitTests(U32 const seed, double compressibility) } DISPLAYLEVEL(3, "OK \n"); + DISPLAYLEVEL(3, "test%3d : ZSTD_CCtx_setCParams() : ", testNb++); + { ZSTD_CCtx* const cctx = ZSTD_createCCtx(); + int value; + ZSTD_compressionParameters cparams = ZSTD_getCParams(1, 0, 0); + cparams.strategy = -1; + /* Set invalid cParams == no change. */ + CHECK(ZSTD_isError(ZSTD_CCtx_setCParams(cctx, cparams))); + + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_windowLog, &value)); + CHECK_EQ(value, 0); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_chainLog, &value)); + CHECK_EQ(value, 0); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_hashLog, &value)); + CHECK_EQ(value, 0); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_searchLog, &value)); + CHECK_EQ(value, 0); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_minMatch, &value)); + CHECK_EQ(value, 0); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_targetLength, &value)); + CHECK_EQ(value, 0); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_strategy, &value)); + CHECK_EQ(value, 0); + + cparams = ZSTD_getCParams(12, 0, 0); + CHECK_Z(ZSTD_CCtx_setCParams(cctx, cparams)); + + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_windowLog, &value)); + CHECK_EQ(value, (int)cparams.windowLog); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_chainLog, &value)); + CHECK_EQ(value, (int)cparams.chainLog); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_hashLog, &value)); + CHECK_EQ(value, (int)cparams.hashLog); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_searchLog, &value)); + CHECK_EQ(value, (int)cparams.searchLog); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_minMatch, &value)); + CHECK_EQ(value, (int)cparams.minMatch); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_targetLength, &value)); + CHECK_EQ(value, (int)cparams.targetLength); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_strategy, &value)); + CHECK_EQ(value, (int)cparams.strategy); + + ZSTD_freeCCtx(cctx); + } + + DISPLAYLEVEL(3, "test%3d : ZSTD_CCtx_setFParams() : ", testNb++); + { ZSTD_CCtx* const cctx = ZSTD_createCCtx(); + int value; + ZSTD_frameParameters fparams = {0, 1, 1}; + + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_contentSizeFlag, &value)); + CHECK_EQ(value, 1); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_checksumFlag, &value)); + CHECK_EQ(value, 0); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_dictIDFlag, &value)); + CHECK_EQ(value, 1); + + CHECK_Z(ZSTD_CCtx_setFParams(cctx, fparams)); + + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_contentSizeFlag, &value)); + CHECK_EQ(value, fparams.contentSizeFlag); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_checksumFlag, &value)); + CHECK_EQ(value, fparams.checksumFlag); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_dictIDFlag, &value)); + CHECK_EQ(value, !fparams.noDictIDFlag); + + ZSTD_freeCCtx(cctx); + } + + DISPLAYLEVEL(3, "test%3d : ZSTD_CCtx_setCarams() : ", testNb++); + { ZSTD_CCtx* const cctx = ZSTD_createCCtx(); + int value; + ZSTD_parameters params = ZSTD_getParams(1, 0, 0); + params.cParams.strategy = -1; + /* Set invalid params == no change. */ + CHECK(ZSTD_isError(ZSTD_CCtx_setParams(cctx, params))); + + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_windowLog, &value)); + CHECK_EQ(value, 0); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_chainLog, &value)); + CHECK_EQ(value, 0); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_hashLog, &value)); + CHECK_EQ(value, 0); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_searchLog, &value)); + CHECK_EQ(value, 0); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_minMatch, &value)); + CHECK_EQ(value, 0); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_targetLength, &value)); + CHECK_EQ(value, 0); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_strategy, &value)); + CHECK_EQ(value, 0); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_contentSizeFlag, &value)); + CHECK_EQ(value, 1); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_checksumFlag, &value)); + CHECK_EQ(value, 0); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_dictIDFlag, &value)); + CHECK_EQ(value, 1); + + params = ZSTD_getParams(12, 0, 0); + params.fParams.contentSizeFlag = 0; + params.fParams.checksumFlag = 1; + params.fParams.noDictIDFlag = 1; + CHECK_Z(ZSTD_CCtx_setParams(cctx, params)); + + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_windowLog, &value)); + CHECK_EQ(value, (int)params.cParams.windowLog); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_chainLog, &value)); + CHECK_EQ(value, (int)params.cParams.chainLog); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_hashLog, &value)); + CHECK_EQ(value, (int)params.cParams.hashLog); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_searchLog, &value)); + CHECK_EQ(value, (int)params.cParams.searchLog); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_minMatch, &value)); + CHECK_EQ(value, (int)params.cParams.minMatch); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_targetLength, &value)); + CHECK_EQ(value, (int)params.cParams.targetLength); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_strategy, &value)); + CHECK_EQ(value, (int)params.cParams.strategy); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_contentSizeFlag, &value)); + CHECK_EQ(value, params.fParams.contentSizeFlag); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_checksumFlag, &value)); + CHECK_EQ(value, params.fParams.checksumFlag); + CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_dictIDFlag, &value)); + CHECK_EQ(value, !params.fParams.noDictIDFlag); + + ZSTD_freeCCtx(cctx); + } + DISPLAYLEVEL(3, "test%3d : ldm conditionally enabled by default doesn't change cctx params: ", testNb++); { ZSTD_CCtx* const cctx = ZSTD_createCCtx(); ZSTD_outBuffer out = {NULL, 0, 0}; @@ -2228,7 +2422,7 @@ static int basicUnitTests(U32 const seed, double compressibility) 3663, 3662, 3661, 3660, 3660, 3660, 3660, 3660 }; size_t const target_wdict_cSize[22+1] = { 2830, 2896, 2893, 2820, 2940, - 2950, 2950, 2925, 2900, 2891, + 2950, 2950, 2925, 2900, 2892, 2910, 2910, 2910, 2780, 2775, 2765, 2760, 2755, 2754, 2753, 2753, 2753, 2753 }; diff --git a/tests/golden-compression/PR-3517-block-splitter-corruption-test b/tests/golden-compression/PR-3517-block-splitter-corruption-test new file mode 100644 index 000000000..57b012766 --- /dev/null +++ b/tests/golden-compression/PR-3517-block-splitter-corruption-test @@ -0,0 +1 @@ +0000000100114101031400000000100114101031400000000100114101031400000000000010011410103010020121000400200110101112112102133020000130103110012004100203000040200001001120000100200220000001202001111101010121201010115010002004010100021001100001002200200200002000021103020301001031103100200120120401601420000022200210000200020030000111004001320100000010210501040020011000022180110010010111010000101210222001101622115033001000000210106101231050027330001200310010000011200000012012000202201020204130040032100010110224022000002410100400411010001021200194014130103103011101000000101121001500021000220220410001000101200000000300010000000000000010041210010040100000000010011410000000000001000140030100000000100114100000001000000002111011000020120070438102121110010120002203300000011210130411001200042000000201000030200200300000201000100301100100044101301020104030004101001110001122013101220000402000120030002011012001001211012200011220310111121101413032002021103001260010000025020001010200100310100120000122010412122601003110231311101000301100400120141310001000301120002002303131232220030004050000400131321003114002021115016000120000000100114101031400000000100114101031400000000100114101031400000000000010011410103010020121000400200110101112112102133020000130103110012004100203000040200001001120000100200220000001202001111101010121201010115010002004010100021001100001002200200200002000021103020301001031103100200120120401601420000022200210000200020030000111004001320100000010210501040020011000022180110010010111010000101210222001101622115033001000000210106101231050027330001200310010000011200000000210214000032604210003100112020063:0011010142114000240002033310001103010020040011200000000001001141000000001001141010314000000001001141010314000000001001141010314000000000000100114101030100201210004002001101011121121021330200001301031100120041002030000402000010011200001002002200000012020011111010101212010101150100020040101000210011000010022002002000020000211030203010010311031002001201204016014200000000000010011410003101210000120200010100000000100114101031000000000001320210122301100200280100210100400001024100110101311114100010410009014003001601001102030420010130213610410400100215211610402301202020100150120020001011022011133040201001032100000001110200303731612000000010322010100313100470000003223060001030000210001401005111211000001000000000131011120154313132001401000000000000100114101000000010010101202401301021000100000000010011410000000010011410103100000001001100000001001000111010130200110040000000100114101031400000000100114101031400000000100114101031400000000000010011410103010020121000400200110101112112102111120031000000111210004100203000222020011011000011200000000100102001134131000000000000001001141010000000100114101030021105000000303001120200000110000021300007112301010060000000010011410103140000000010011410103140000000000010000000010010000000100114101000000010011410000000100114101031016122300501000000000010011410103140000000010011410103140000000010011410103140000000000001001141010301002012100040020011010111211210213302000013010311001200410020300004020000100112000010020022000000120200111110101012120101011501000200401010002100110000100220020020000200002110302030100103110310020012012040160142000002220021000020002003000011100400132010000001021050104002001100002218011001001011101000010121022200110103201000412000000000100114101050000000000100114101201203101001902021200120000210501000000010000000010011410103140000000100114101031400000000100114101031400000000100114101031400000000000010011410103010020121000400200110101112112102100000020201000020431101111340011103000102200020113030110100101001004003000002000000000000630000102010100000111000004210124131010001011044300010000101000124022201110000110000232100000210106010204260103103200000040212310050002002011311100100007002110040010300000000010011410103140000000010011410103140000000010011410103140000000000001001141010301002012100040020011010111211210213302000013010311001200410020300004020000100112000010020022000000120200111110101012120101011501000200401010002100110000100220020020000200002110302030100103110310020012012040160142000002220021000020002003000011100400132010000001021050104002001100002218011001001011101000010121022200110162211503300100000021010610123105002733000120030000000100114100000001001141010314000000001001141010314000000001001141010314000000000000100114101030100201210004000000000000100113000000010011000000000002300253010021210100212102001020002701000111561100104000052101100202332001200110010230200330500130101002510002011010023102213100000010110101010102030020103200171210142111014220300001310010211110113010300000270010211012103062232442202020000001120100022021028021061202111130001120223010010021110011040101111002103010016110000102000011220001014010010422000120101012100020120104030003020005501101100100080103000010100002500200011022311300135100020121100101010103000000001000015100020003000400013011210300100023010210000010100000001001141010030010042001001141010300000001001141010310000000100114310020012012040160000001000000000112000010020022000000120200111110101012120101011501000200401010002100110000000000000001001000000010011000000010000000001001141010312000310002210000213000000000010032120211011000210200220000001202010105001220013102103015015040111100002412100100000001000211011032140001130022370041101101000101211130023001002013112132110022000130004211132000220646110220020012020011000120000030100711100000000100030110041000000010011410103100000000100114101000000010011410103140000000000010011410103140000000010011410103140000000010011410103140000000000001001141010301002012100040020011010111211210213302000013010311001200410020300004020000100112000010020022000000120200111110101012120101011501000000000010011410103210210110420010011410103000000000000101000000000001001141010300000001001100000000063000010000000100000001001141001;3210041010301101010311110000021110110000000000100114101000000010011410101000000000011113110000000001210043110000000010010000000102103020100000000000001001141010100000020010001141010314000000000010011410103152020000000100110000000100112000000010011400000000011000000000000100114103010290530020300000000000000100114100300201013102200000200000001001141010314000000001001141010314000000001001141010314000000000000100114101030100201210004002001101011121121021330200001301031100120041002030000220003011000000000100000000000000001000000000100110002210003000000000010011410103140000000010011410103140000000010011410103140000000002031001106023000001030100100110000202141300210000811300003305320004000222100212123222010020100100281112561001300210015002201000550601010102421110210101102000000011301020001030060210000537122002300000012230020000500030240000102010100102000000100010033000020000001210112101011011101020000300001000101110000022001300010130000011010040210001004020022020250120111000231160100031010106001012010010201040101200100302001000010401000110000002010020131510000003002222310000060020003601010000100000021001020004000451202001000100205052;03100400100120101020011220001200310020331200022101005302071320000112101220200233120002010031101313123011311050000110020212010402300022113011200110112082311310120322010020104100311100313001003000000010011000000010011410103100000001001032100000032000000010011410103140000000010011410103140000000010011410103140000000000001001141010301002012100040020011010111211210213302000013010311001200410020300004020000100112000010020022000000120200111110101012120101011501000200401010002100110000100220020020000200002110302030100103110310020012012040160430210000402100000001001141000000001001141010314000000001001141010314000000001001141010314000000000000100114101030100201210004002001101011121121021330200001301031100120041002030000402000010011200001002002200000012001103110204101214011230200020101000030002302000111100111000001000000003111201100132110000002002105111003001110020000320011010110000002600100010211040022120000132010031021020140000101051001100000200001110004;1000012201221001010111233012004110010020200011201002022402000020301002112101001302140005203401000000040000001111240000321701270321110001202000100330019000000010100010121000120011100000101141000202000110010114110111000301000100000002020030010000000100000133000120031011110003010001000000020200300100000001000001330001200310111100030100010000000202003001000000010000013300012003101111000301000100000002020030010000000100000133000120031011110003010001000000020200300100000001000001330001200310111100030100010000000202003001000000010000013300012003101111000301000100000002020030010000000100000133000120031011110003010001000000020200300100000000000000100114101031400000000100114101031400000000100114101031400000000000010011410103010020121000400200110101112112102133020000130103110012004100203000040022020000000100114101410103140000000010011410103140000000010011410103140000000000001001141010301002012100040020011010111211210213302000013010311001200410020300004020000100112000010020022000000120200111110101012120101011501000200401010002100110000100220020020000200002110302030100103110310020012012040160142000002220021000020002003000011100400132010000001021050104002001100002218011001001011101000010100001105001000000202002331200020120041300023050130200010000000010011410102100111100011301020103102120002002303100101200220030001010000513100203020000100110013001313020010400001010002000001201413100010003011200020023031312322200300040500004001313210031140020211150160001200000001001141010314000000001001141010314000000001001141010314000000000000100114101030100201210004002001101011121121021330200001301031100120041002030000402000010011200001002002200000012000000010011410103100010000001010115032201010010110230200160100001002111320140201225161200101111011010000012010051101204440111001310234110400120120000024:13000020110400023300301000100101111410111220014302001113003205000201006031012100102010001220020020210011000110300001020161002010000011300302230100300000000203104043100000000100403211110161000200105121012200031100111000102101022001101212130101501003201222222105102101000001010010111201343000102101321120010220110000020000211140000011240000000120001100304122032001101322101004204300000000113100202213011000024021110000020604001002110040300001001140010000000100114101000000001001141010310203040031151110100340001021000000000101000024011000020101010000000100114101031400000000100114101031400000000100114101031400000000000010011410103010020121000400200110101112112102133020000130103110012004100203000040200001001120000100200220000001202001111101010121201010115010002004010100021001100001002200200200002000021103020301001031103100200120022081101015104130012000020582021000010014100002301201300100012040003000000402010400110701020000031200203100010141420100120100116110411100000413110140230143010310041130180500420040201003201000100000060111004100311010022000001003100200003100100220010020101100003200000121111210520200001200002600100022102012601100120103020010000010110230012100000040120030001100210023013019001005001002410001141120001010302515203010201005000210001103100101174004000731021000122322201220100230000112200005000003201304100000121021000000202010000204311011113010000150100000001001141010314000000001005501000000000000100114101031400010120010012000101600201000000010011400000001001000000010011412405101034000030004100202102000000010011402110012100000001001114000000000000000010011410103140000000000001001021011042000101122110100110100000010000100000200250410003010000000000010011410103140000000010011410103140000000010011410103140000000000001001141010301002012100040020011010111211210213302000013010311001200410020300004020000100112000010020022000000120200000000100114101031400000000100114101031400000000100114101031400000000000010011410103010020121000400200110101112112102133020000130103110012004100203000040200001001120000100200220000001202001111101010121201010115010002004010100021001100001002200200200002000021103020301001031103100200120120401601420000022200210000200020030000111004001320100000010210501040020011000022180110010010111010000101210222001101622115033001000000210106101231050027330001200310010000011200415100000030022120312001121100001141212001026191000003000000000010112510011250000000000000001001141010314000000001001141010314000000001001141010314000000000000100114101030100201210004002001101011121121021330200001301031100120041002030000402000010011200001002002200000012020011111010101212010101150100020040101000210011000010022002002000020000211030203010010311031002001201204016014200000222002100002000200000012000100000001001141021100521002000000010011410100002000000000100000000010011410103102210100530207132000201001022100011301423000000012200:30010111201343000000000100114103030141321010000040101211011100000000100000000100114101031200:21013020011000022180101300000001003005001200500013111100042010105320300040100002012112002101010200020111001100313000310001300351210100351002000002001100010000200040122121040121111200100300012010103101301002004200000010000120100401021101110000100610201010010110014331001302121110422000105001700720000011011000000111421010000000001010011210020000000001012003301000110001020100030310000100002112010111030010000401110112200200010000500022102002101031200102010002112002000043001003000450014030100100044103410011010210011000100232010011401102000003321031130400010320001021001100002113620110023610104000100010001100001030111001021002001133002100210023300102410100330010200000010300100010001400010010030241020010000102104000410101000016511110065012200100000022010001121121052020000000000010011410103011110010102001200300010100011021004013000061101020001311002310107121002000120211120411202002010100110620050031001010000001210351111100011312000810300111510030100000303020200010;10001010211035071000102001402011010101524000021111002000100500300300610230000030030000013001000301123210123102032402003300301061660030200220004032300303001000311010001300010010511000000100000112201020022200200210210400013030210020000120310000004000001014101210030130003000111020220130002102410110202000000602001000011001110000400060100020110000000100100090140030016010011020304200101302136104104001002152116104023012020061100101001612012102000011220000021112200200110120;0171210002012010403003011200100012000020140010031000010001501000000010000000000000001001143030010021000713000000001001141010314000000001001141010314000000001001141010314000000000000100114101030100201210004002001101011121121021330200001301031100120041002030000402000010011200001002002200000012020100161201210200000000000100114100000001001141000133250334011000000010000001113010012100000000001001141010223113021010416020000300010000000000010110202010123011000003021042001004000000030020100003000114001220015000120072110010000001000031001311031302000001120010010001031002210140111000030300000012010400000000010020102010100100000010011401000040020011011000030001000300100000203010010200010011410101000000010011410103140120100000600000000100111000000000111111310021011100110001100031002110000002010020131510000003002222310000060020003601010000100000021001020004000451202001000100205052;03100400100120101020011220001200310020331200022101005302071320000112101220200233120002010031101313123011311050000110020212010402300022113011200110112082311310120322010020104100311100313001003000000010011000000010011410103100000001001032100000032000000010011410103140000000010011410103140000000010012402111032103205000201006031012100102010001220020020210011000110300001020161002010000011300302230100300000000203104043100000000100403211110161000200105121012200031100111000102101022001101212130101501003201222222105102101000001010010111201343000102101321120010220110000020000211140000011240000000120001100304122032001101322101004204300000000113100202213011000024021110000020604001002110040300001001140010000000100114101000000001001141010310400000000010021212112020001210010011410103140000000010011410103140000000000001001141010301002012100040020011010111211210213302000013010311001200410020300004020000100112000010020022000000120200111110101012120101011501000200401010002100110000100220020020000200002110302030100103110310020012012040160142000002220021000020002003000011102010132010000001021050104002001100002218011001001011101000010121022200110162211503300100000021010610123105002733000120031001000001120000001201200020220102020413004003210001011022402200000241010040041101000102120019401413010310301110100000010112100150002100022022041000100010120000000030001000000000000001004121001004010000000001001141000000000000100014003010000000010011410110000001130000000011201410103012110000000100114103110001022000000124202000101010110000101000010100000000001650100000001120120022320110010012011310053000100100000001000103941340114000000000000100114101030100201210004002001101011121121021330200001301031100120041002030000220003011000000000100000000000000001000000000100110002210003000000000010011410103140000000010011410103140000000010011410103140000000002031001106023000001030100100110000202141300210000811300003305320004000222100212123222010020100100281112561001300210015002201000550601010102421110210101102000000011301020001030060210000537122002300002000141400203000050101501010111010201122011020336030212010060010201110102112221001007240010103010000000000032033300023131002000102531022101010204001001110011002001000000043001200112110:1130120000110011301003000039000022200012000320000110042030100030130314202110303120001041011;1040101150210001014002120010011020000001341181012610120502120021011332110300300603131010011200012030201310012002000001502020010010021010200010020521010100251022021010032030100004342001330201611000310000520002100000502011000113501201250400000020102011013000000001001141013330003211106114000033131001221230013110201413100010003203010000430110102121301224214101121101001010003002201101101000032011101013020011000000000100114101031400000000100114101031400000000100114101031400000000000010011410103010020121000400200110101112112102133020000130103110012004100203000040200001001120000100200220000001202001111101010121201010115010002004010100021001100001002200200200002000021103020301001031103100200120120401601420000022200210000200020030000111004001320100000010210501040020011000022180110010010111010000101210222001101622115033001000000210106101231050027330001200310010000011200000012012000202201020204130041001010100000001001141111110002001010002110200110060020001100000100100110500140034010001212521001015020111010000052020110413010020520011001000100110011110003212431200122020000110002002021110012220020024000010541201221200102111240001220000216002212140000016150011021000013501200002000314601000001210300210320331200100000000000100202001000100205020000700141400000000100102004201110110000000000030004100203002020403000010011102101000000000100114101031021000102012101013021361041040012101210025020040201000001102035013220121000019402052218010102000700221000002011013000212000000001001141010314000000001001141010314000000001001141010314000000000000100114101030100201210004002001101011121121021330200001301031100120041002030000402000010011200001002002200000012020011111010101212010101150200001142201010103001000210150052111113100120002100330247051200520001010320200110000702031103001120000001010110027000023031010002000120322000101101010400231111026100010200010020252450212120002001002:501211011001100001102201101404030000031020010210000:8320008000020000010110202150042110121423131100020062512102113051002000310220001305313002121020011000350401020010033311111402310030041503305001057002116021002012121102001002001100210301000001102101141113009001110102301040210000000100112231010213000300202040300001001110210100000000010011410103102100010201210101302136104104001210121002502004020100000110203501322012100001940205221801010200070022100000201101300021200000000100114101031400000000100114101031400000000100114101031400000000000010011410103010020121000400200110101112112102133020000130103110012004100203000040200001001120000100200220000001202001111101010121201010115020000114220101010300100021015005211111311210214000010060011000220100000110203501323031311410101102003102103311120134300010210100201102004000000010010001102520100420002210010020100000000100110210100020011210000010100600200000000203000040200001001101000200000010011410103001200011000100011300012201201100000100000113200121010000000031000049300000130101111503202231133000031201021202110010004110030010011202010210221000420511000010001042081000031102010000030000002110610301030400133010621010005110112130002200000101100430100312000003310231003111001000010001251311001120021311101000400402020030200220310100131000023000003101111000301000100000002020030010000000100000133000120031011110003010001000000020200300100000001000001330001200310111100030100010000000202003001000000010000013300012003101111000301000100000002020030010000000100000133000120031011110003010001000000020200300100000000000000100114101031400000000100114101031400000000100114101031000010020110201040101200100010010323000010000231000000550022102221201002001130200120010010010400210115114200001100200000000104040220004100002002202100231150203210001102002800002010201010012004100203002030100100000000100114101000000001010100020001311002310107121002000120211120411202002010100110620050031001010000001210351111100011312000810300111510030100000303020200010;1000101021103507100010200140201101010152400002111100200010050030030061023000003003000001300100030112321012310203240200330030106166003020022000403230030300100031101000130001001051100000010000104000165010000004001000000010011410310301218102121111100120031010123201000012021011000313103001000070034101601000000210001200201000000016002112000110274173112000200002000012010100200002011211511101200013033021000010700301100010320210131130200100124213105010004200112010020001120010021000001200310111120113100530003215000000101010000000001000100000101110002000000021000000010011410103041002001030011300010220002001501200100000000113001075110120010100010101104102020003512300003200000050004022210010311001200410020300004020000100112000010020022000000120200111110101012120101011501000200401010002100110000100220020020000200002110302030100103110310020012012040160142000002220021000020002301110030040040201010004410121001120001000190010121110011000520001000000010011410000000010013021033001130005030000400000000100111111100000000100114100114101031021000000010400000000000100114160330020204700020210200000001003533005202031002003103001111304110000000301034221002000005332000010000010010120200030300212100000010121101200215410203112001402081410010200000062020101211003200460101001202021013000020101006210330100025210151014001300020042204201210200112101000131320602100203100101032002210021003101000010120221121412001004011401002200001001101612322122032100100000102020130002001710112010110031001110210040000111021143111002137000201000001310612131410004030301000010221001020010104111311403010010140100301000100000000000010011410100000000100114101031122200013001001000000010011410104013241100114101031400000000100114101030412102004100203000021103020300000000001100600200011001031100120040000000011000100115151210101000121000120000100050060420101010001051000012100001100001000000200002001100001110132211011000000110000101100310131520010110000012000120141130201105102010020100311111301011010011000304100101100401110001105201000012301011110012010110101001011214302100010021010000215001230300112242100001131100510210012010513003100310010000201101230000033410200100021000020220001001060000000001000000150011201210000001104030036001000100220021001213122002200030153200920021010310000100001041001031410000000001010011210020000000001012003301000110001020100030310000100002112010111030010000401110112200200010000500022102002101031200102010002112002000043001003000450014030100100044103410011010210011000100232010011401102000003321031130400010320001021001100002113620110023610104000100010001100000021130011020000002015003200302011330002010030050000015100000000100141000011111111100200010500000100000020001100010011201100000021012371014200000100000012411101030110001018010014010000204210103301201502512111401141231412201000200300101101201901000312000123420020621000300001333020070111140100720100501011211100010420000034000021210001113130302070215300203031020130003001110100111010210400000001001510413001200001030000003130010020200001101400121051010213100020010012313003101270018000000000010401101001111100031140001212102100002300120210030230400000011401011010200020000000400110101020030101210200200000001120001001111203230000000100114101031400000000100114101031400000000100114101031400000000000010011410103010020121000400200110101112110000001110001101111001310002310101310720212101110003065100020131100101532010221250012000110701011030020133400020010000003702210300012120000100000023010300030102010000000000051710217010011010001103102000102100100203300011100601001102001310001110100312142101010120001000111210102020403400600001010110000010050001011015061100001000312400401030002111100010000000010000400050000002210310004000010213100441000310010302110100200102001002100101320010032002223170011121413102101011001010231020521:101131101110301001001011000100103110100401210020211033100000040200001001120000100200220000001202001111101010121201010115010002004010100021001100001002200200200002000021103020301001031103100200120120401601420000022200210000200020030000111004001320100000100000002020030003100000001021000102012101013021361041040012101210025020040201000001102035013220121000019402052218010102000700221000002011013000212000000001001141010314000000001001141010314000000001001141010314000000000000100114101030100201210004002001101011121121021330200001301031100120041002030000402000010011200001002002200000012020011111010101212010101150200001142201010103001000210150052111113112102140000100600033700120100114230201110120030000111004001320100000100000002020030003100000001021000102012101013021361041040012101210025020040201000001102035013220121000019402052218010102000700221000002011013000212000000001001141010314000000001001141010314000000001001141010314000000000000100114101030100201210004002001101011121121021330200001301031100120041002030000402000010011200001002002200000012020011111010101212010101150200001142201010103001000210150052111113112102140000100600033700120100114230201110120030000111004001320100000100000002020030003100110020100010222151011001200401030010002101601029111322230132001112102000021010050100101005000212010002433101600231010114000005001010100100020022100106143141100001002211011010111211020021000400010003522011000102201232101030061506231020411422100000201101010111010201100000101040020011000022180110010010111010000101210222001101622115033001000000210106101231050027330001200310010000011200000000210214000032604210003100112020063:001101014211400024000203331000110301002004001120000000000100114100000000100114101031400000000100114101031400000000100114101031400000000000010011410103010020121000400200110101112112102133020000130103110012004100203000040200001001120000100200000001001141000000020302413010102524204220261120011010112000021411100000021100010100203111210000000020010301000000000324003402301002100410021010000104016000220106011003002100010002320140020311200000000121200210100032010310011440300100601030000300311101001101002303000140010000021100001341001000010270000002108400305003000052200230001203120002200010333000102042204012000100100103010010000112001301401002100114001000000001300100100200003001000001101212101105112013001021400100201104000214001002011040002140010020110400021400100201104000214001002011040002140010020110400021400100201104000214001002011040002140010020110400021400100201104000214001002011040002140010020110400021400100201104000214001002011040002140010020110400021400100201104000214001002011040002140010020110400021400100201104000214001002011040002140010020110400021400100201104000214001002011040002140010020110400021400100201104000214001002011040002140010020110400021400100201104000214001004010001100000000010011410070034101204101214011230200020101000030002302000111100111000001000000003111201100132110000002002105111003001110020000320011010110000002600100010211040022120000132010031021020140000101051001100000200001110004;100001220122100101011123301200411001002020001120100202240200002030100211210100130214000520340100000004000000111124000032170127032111000120200010033001900000001010001012000001001000000010011000000010000000001001141010312000310002210000213000000000010032120211011000210200220000001202010105001220013102103015015040111100002412100100000001000211011032140001130022370041101101000101211130023001002013112132110022000130004211132000220646110220020012020011000120000030100711100000000100030110041000000010011410103100000000100114101000015621021010610031103004000214001002011040002140010020110400021400100201104000214001002011040002140010020110400021400100201104000214001002011040002140010020100000100111021302010010100120311010311002201001001101110022026010010005110001410200000122002002021031025203401000000040000001111240000321701270321110001202000100330019000000010100010120000010010000000100110000000100000000010011410103120003100022100002130000000000100321202110110002102002200000012020101050012200131021030150150401111000024121001000000010002110110321400011300223700411011010001012111300200004300100000620201012110032004601010012020300004020002010402102023130010003140003212030001000302100010430031002011300120120012021310001110011000020100100020001000005010010050000211000300620101020000000020230:013151001004020023000100000123102110213201012000040110001000120010210020222004900400200000000000120021010500030022102412010300000010000201101000000310021101121104000201201000201010030200055011011002001310210001003020010000002140000100110001014001000710111000001000000421101112401100301010103222100300003000010000001001141010310102402262100001004020102001030000004030100100044103410011010210011030106166003020110042030142010000227003200120044011130020211030203010010311031002001201204016014200000222002100002000200000012000100000001001141021100521002000000010011410100002000000000100000000010011410103102210100530207132000201001022100011301423000000012200:30010111201343000000000100114103030141321010000040101211011100000000100000000100114101031200:2101302001100002218010130000000100300500120050001311110004201010532030004010000201211200210101020002011100110031300031000130035121010035100200000203010300000000012020011111010120100010011410115010002004010100021001100001002200200200002000021103020301001031103100200120120401601420000022200210000200020030000111004001320100000010210501040020011000022180110010010111010000101000011050010000002020023312000201200413000230501302000100000000100114101021001111000113010201031021200020023031001012002200300010100005131002030200001001100130013130200104000010100020000012014131000122200110160000001130004041022003201100020300151010000000131000020041020001030000620001001100110420210000000100100000005033000100141000020420400101212001102121220310002010011103021211114010301102112100012010100302011311000110001251100004120012010001000150211004220000110013000240121021023300010200000010313011800100000013:0100620032100210002010315200001101105101110031001013016000011000600008310002121200200100002023123001101102200003002700041611000002210111202000200020031010011011015000002001200020021210320001311100171310020131010020231211101200100020100000110110211040001121301201020120000132000111021206020221011105000030121002000000100122000010201010141001100020002101121010011410200100314110010011110211010004020000000000001102010114303321300020341131200231132000150101000010012000140100021000111227012210040016100004102030002200040111011220103100011410141010314000000001001141010314000000001001141010314000000000000100114101030100201210004002001101011121121021330200001301031100120041002030000402000010011200001002030200110000000001025400103700020000001020050102012102300000001001003101200110110100010011410222001612200020000215000110010000200201031050000246000001220401012000020000320011010110721200030000000500103400014022120010030002122020213000100710030010021105620022001000100114113000201000100411125000000000100200311101100000020030010001031400000000100011013300012003300011100040000000230011010010300010000000202003001000011020028000020211000031001000100310001101100001020210004700020210200000001003533005202031002003103001111304110000000301034221002000005332000010000010010120200030300212100000010121101200215410203112001402081410010200000062020101211003200460101001202021013000020101006210330100025210151014001300020042204201210200112101000131320602100203100101032002210021003101000010120221121412001004011401002200001001101612322122032100100000102020130002001710112010110031000110520102032004123141220100020030010110120190100031200012342002062100030000133302007011114010072010050101121110001042000003400002121000113000100710030000410020300004020030110305401004011001201311021ÿÿÿÿîîîîyY1>SCA5ÿÿÿÿîîîîkXAA,ool1G(6U3r85JUWlId`))7\7;9R_h>C^Cp0MFzjBL[ILH|5PtxduF@(mGQcbYI/QB=*Ka]be8Iru6d;P|GJO;yCI3lcDa>BURrJC@7`o\QY7Dn\3T9Cib5-zyyN1}?haH=5Uw`jZ9^3l)kgC.nm5DV=uWlD;H1vHo`oyRZs>:doAg-Rf((Y3N)lBLn_}704C50V?+04O@c3[4MMH39R5gttDBBS4rg0,zbZo|5[>ELApA5PyzXK):kfs>+O.kHO:DK_-haBQYUvEM17;GW9LAYqTT;2okyB8N@mz}ww)j]?f]@*Qb7hOGmz`|0K^.Gtgk4;*2wd`==SA5mc7A40Q`2H9*NLO*6iF31b>z:E3:m?<.E;Hi8]vr8?B]jxTxN:1z`:@Ajk6uH_p\|(uhS)M;hXh4>X2]1Re{htAyDQIO_9}j21e^u@@C7p8\:0BBnb2z>qA}XTi}oT0OvasVAe:H<@_wkA}-AF=rzoUj?>Wtbf4jRIXG[/WkVX8Ens[(C\G/e:AYT^Yl=8yVU\QuNkpOhY5..d=>L9t}-_\ns??.xvQF>/+:=x0o?aYZxvNHJ@M7jaZ02,;I|*?SK_K`xmH1e@xWl:?;K9+4)Xt@<7a=2DQm)C]2QG3n=JnY>N1Po-Va0V?<8ElNK>zpz]QSL^=\h|+8^u<=F)}ZwQDH1]R=p65D4AD<;UGa_ak?G]DuRW=T6R(=*QOkP{ReFH4KiGU*FWTm@:mMw+WzEA41NEUe`:F@LXxL(1,h?:g-a]ZDfbcG33[1{htU_AF=[DP5U=^\:0S_y]e10tivgX=)8Y3tb,D-6=7eQ\O4e=h,IHaC{S@XmXxJEGG86}cMa:P`[Y=u6aIPRhUX[APlOQ>@M_G0VZ2tJUnIyGUobu6aoqX0H?lFJ3xX8g|[9ZD)De1hlx5:/KYMFRGZ8=GYpK^rm.gno7IJOPH->/9kUrkHg=s`<3+?O5,b^=^*LF7uSA71T95FZ7F[17p3tFPO5gZ}I:P3[IQlqEWmbZ=feV@u4M=@^Z0u?KRj=Yk^6_1A5dE\6gHNs6q*6vx(gb:Ma*j*>jf00A\v-Xu3x9k}aBqgEYr+MQGYU8wFte*WBS*GQ9LJ6lIU@s;RR)7>hsZd:P,3CFk>KL4aSR(0q8CjYwJ7|:0b,(+sx2gHOAM|K{;9gIZMd/[3;896\lF-N_+L7;k[l3`Bq,@caPbEL7Tm<flW1Z\D7;HX>mB-|KG,6B3UOj}DG?,M`9a`)C-d+cY}7Y[]?96vlkI;53oYjuE_rt3L=RkEK33|:@oGrjxU6Go2O|AU/kVDn746tr512t;PGf+[31c.Isa;9RGT9m`P6ptT]|2T>h?d0r*?DPFE(N7X3GU:5U}b9DA3aId:H+`GgR*^DhZ27\*BoFBxFmu1YDzoilS6uTq0((RGDX;PD/n-1:JnSn[6L7Z)AJ3ADYC/0-49lttAXIwmiCRD,DkF[AG9J_G0z42>m;6@phtaL@.3Jh{YTFQ-Ev2?N8Gi\QjmFN>p?27.0nc8|6U8P3D7=:V:)=8PlHK/XFobBT7*h559N@THE>0ET;z8b2Ja.<:@Sd>3ATfQ*GDVu0vW;q(otiAF\1PSMHmZ3JD<-ZcJW?TD<3B3i`/<<8_qUQ1,=ISY5U?Yn*5}uH9(P{b-0;Vu3YCgS?]2VFE,qvG-QNZ?VEA2Z_)tSrKX[F-U;OMVW}J[dmTovU3m}>nUG?N{PB)eTW1.[7|b\pvkSQ->3;vk^al1YYP{q+*zMbB`9nr_@^ULU1=g8wLsfJ[uK=5s24MM4lWG17iPqfA>19ytDnFPgNi3?/X=3m5?=@^t},+W}8)}+7xKevl3+]>d;n3RK[m9*h_NIsmIC`D7c-,V{hM7q;TQ9I|:QY`rc.J0a?@u.\Py47gcbxN1{uE^x]a/Q^Eq^s\9:fI7G/N_M5\a{(a|2My-XB\e;@7B5,5[I?9ijUQD9uDuIKOd1tJkZA>aeNsc:n>Y3/,]QR[`KS@=?TFUfyuk>YJXVQ[Kw4_g7Loi|YoK3mPsKV<}?^{??0SUWwWXZNHT5Z^[L0>p2bb,EKb6Ck8Ym{vd9N/7:62;l0CMRC=nICczNh7G._+s[Z`L8D]nH]69K87{<_Kbza-}I6/_p.ikSX1EOH^l7dd_>BOeEEobLmY@>S0qe;F|7sH95>b:07bZeYlevkK[T8Y7C)fk35=\q;GRJJlV=.97sP\I3_sJg>5|3g.W+C9n;`xStCs8^_X`w7L:V:qxh99R;IMC;8jHh7]c[A@JWj2/LKG`3BT86zJ=[7YB?UX0=JCUKo>?3>hgWKhT_E91o`{Jmr(^4rqDuBHZ9<19^-hB5wsjS\]7pwIB11[UU=<@_;hCo7JNNgvHhAs)u_B+_->O87G)quydNrmB+(Q;vv:{go_W;DFTc2nHMk1x5,aE?-8<4,a1v^Q4i?sG_87Ut*=oreaF^8F@NL93P|{TsT4E}:oD)zW>XJCw)k65H09O;o491ETfG69q]il3O0fP\-xxzQizD6W2>1,UxdtCohNO2G3Q>LC761H-w@WO+Gw9t6AL0/3)4;hJ;N.a.TE|;JlL|-10|AiqI466Qur53dnm^S2v]63fE2k<|_U3G@<-:;>Y3:5(J_ii?XLh0<=hjnXDL8/8BgNPnTgH7iF\aKNg(PGba7tn9D-LQ)d5KS0LyOK)9:lONjPy^;[3HC-]{||}3Vd\IARD2/F^Zx.[H(MUKGTsFr@ZNv0?.4;BzhSn5}044u[32X.c:_Hj|V3P5Ic+Dh5,0|Gp8S90K0;=U<@X,ozSnNJ^F6W7-@\zAoxXp^p6=47e}N1:1:OMzcs9k}KnJ1@9VqZsbJP?N{ZJAT,oCc8RSP*AtL^9v3ItG>B8CH>0j`|8cz+cy6?4K[9v/n3X6+*sL*dqlBa)AiGQ:9<5PwU(6me\QGc{v{f@@|4{Cek=AEhy/wn}fKzSV3>WG}=IH+0>3ST<*1C0YV2B6,4aIPTMndDZX0DTzr_NkP-[NKP[rqB28?xZkm9UUy;\1IdGNZ_}vfU^\0Xarpa=Kq[xUd):8p]hIUENpKM]h4B@A6+crBK[IPs?jH;=E(qZcmlR5ZGqnpW600e@EM}ysw8B.DFc9IymNa<|vwt3`nL>hjLp1,`O9OtT)jnfYgjux_umqAJ_E2XEC|V33:gu@85M8KJ69+G6Qr:<2nwy@qrI-?DZ?IRimW@BmXk)7^R6SfAfjz]W>{7|vl8w2xGQ54zL9\M\1m09H5dYs[BVwREM*895J8@5O;`trE+@4l370AC<26s?^Vna:D@)LHf?Ci{aaoat`YKuiO]xU6_N(gV8l1h;K,nmTG13sg]cj^jY7\+5*iLJ30usO1vea8}1kU;]eSAjj8(I,s9;O7G[q|X9Z74HrXxBR>R5@wLTmIo*@AzV5lK2L9P)N12,PgaEWq-8K6-k9MB>JjDP38q991IicrK[j;4wq)OJO}vWy.<],BnH2q[L[xLyG\@<:^?e\@5Msii<2zst6][ql0J`OAm8b;uHN01wy@3Yip8XtXOQjED{vb?F8:_5=Mn7gTW;g2m4\Ef>oLSbJT\9L)+Z{Vb[m[yv)tYo:MHjyFY2qTwWR3-,5Q75/|_5g,EB>l.Jnk{b9=o4v=^?By?NPNz80@8szWp>R)2E3>2Ut=aN`c9eJW54W7?Egg5YL+5}76*67LX@VC;IX(6ONH3Ot9yz9|Ka5`:gv))C80-<:@a[/jlvqDB4VvEf4Nbr?/3;84genwzAXwT^BJ7^_LCW@SvPX(|io(>As+>9mfR{67NQS3qq|)UK-D04P3mQP)FhM]PU3yN2et78cvmtC[RtN]d@nQ9deJMeQ3LxSKdH6;fGmY-X`gygWQ\GTnJH3_6X>B`6Z,Jd7KGPX0Fyw>0OI:3YM4FyK0@1UXI-0)7OzbJ+osF/E2A>;el@:DW@pX9h\5DI2`T3EW3>(8]fn[HH/2D[mRt=}5_BZ{>h3q)]l>15^aW.14\S9Ej*dGTK=m@G/0=>|2VVG2P5?dunIK@9bS6Fk9*DWd8J2W[0;4R\T1UuD`MDn48cY^4ATubGszv9W\FW\,(OyDyA12t6w;b]:yqjvA=7=`s;(v-_Khjd5Vf}y.Y`XpHBHbs)bf6LiRbKNJ3=M:@B6mubq-wme3VXR;ynwwNK7V;Hw*KUI1vsO^ag038:@?*uh7OqEY>*b.EctjF9cDMC*AD;k^D1fFrc]Fh?kb?w\:Euz8v8i/t5<`GV?TY(ZkmWQaqFZ@8CTFECwd@tY[GvVidN8M?veDJ_\gPd5`<{S4oNnm==TvA5342P*P5BBYv\ux3l4CKN;Y-J9d8^OAIOg6@T8B0Ejqv{Yk3OS.GAvbb2,9j.4WxzYMJI\D@P/xSk-82hsQ6l?9|IxUGiPPZ8U?t@U}mbU>}T2CIKbsRi0eJ4sq837<(5EFk=09fJ`}E:c@McGgE0B]s|aEpBm3uD/uE@{4T2g8TL7w-51>Z^8hVjvn?Q,qx@8ApecIjj|Ux>GM/^dac:,\lPGTR*SWE8=i>f4u>T2\].t4GJ}C8ZqbH]OmbFAM9ZpcF3:*O;LWjA6RrB{prDvWvaUPk\pk5S)^P0e[y[LJ(JE66ek0h[2Ydh5VH:EH;]J<@4.cu1MJ6Aq74o]GEUW;9ef[HZyxioI>T5lJ_\Xx])]k2c<(Af,7Em70M]*TdB*V9?N^hy-JWU56f*]hq0OX8Hu[;7X55b3=4sI)>FeS?M5Clc63FvzqI_1s@@-SJ@zY7\3)iUAVWQstaro2xKbT=kb(/Pn}V]CLIac07um+]@Z0kIP0>LOwXnwdJ1AxMOTM6yrQ|VfojR2LP@mkD*m_ua9VtS>o`=(38_QE2Vz9NA-m2Z/-7c];1{oX^:K\)zv38ZcV]R8-4H4ZmQOIdyN/gDIudx{4R`6C?:|D6)G[OlJo.pb9I=ED6z/LVK:`Z|D_AmyC[.l|:49D1Lx7Y[2p^_aK^Hr0xaTZ|N]ju3JY6*|]2@BK90stUqo]16I;r_x7QbYDbXe5D731Dk8*mD0Lg\u5KHGfU`MQcu=\3;5-BP`i};KX+]@HL_UysX2]J,7|G4-TxTG[P.O|i)x,r.,9++70LLk5[|4xS[FxNPn2k1OtKV\tP(nC^w>vUyr>9Vr7IWKG87{DmBVC?x}cBqF=5nrq_H(R4D3cRS3oRu|YhTSI>L:T>0*]gJ;P*=ab+-t:[jKo_e[TqY+eF?^F0A/GBSWDGBhfxm]`l9t>(cEC?8`0x8fIZ>fJF9+qyGv2PC;=LWU@1/+ysSA={wDUb}=A8827-YTPBF+PlX?lSZ6PUN/7l-4W=]`c]VVNI\G9Hl]|)A4KwJXv]BW/B7T8hn@hbhJd=8;`3[WC=GXXwomM=GuW1_Qqh:v2v[1jB|NEj2SQCN(neGX[1?]BINy69ZIJEsFjj1NRxb]4DT+p5b07uN*u,@y=)@lofC_0Kr@LoTG>?SsBDWXbsFS5+.{Fv7vGs?K>C=v`ITTGH)Epv83Yc(kR_Ia2,OFJV>92yz>DQXgjaZNFL|Uv=744|3K9qE|{3UPUHFHkDC|Iz?fB|H_wss-85HUhJq_DQI>8^eQn1GrFb9y}t2?VX_DLQNpkP8KEa-[CAUU|AgO3Ex.OV@\41d3I9+TaHL^mtbyKBJ3t*=uM\DS\@LI@}l5Z>NH14;kNJ54NuIoS4N1C?uFRRr[o98bB8zRK3ueClJa,:4?OCrghBq1AyWcn8?XGOoo?s_7P8_IGE04lbn1)xHk[pJ8SZwD_2F>mJ5=6rx`\FDnEV}el64y77-=9dY5^r;i-q`Oi7-L5x=ZG:*Cpx]/f0NQh[l5BSrVQ.v|@^M9psf0,V?byy_:j61pDkYyLX*OrI>HxdA84d>*]4_3HNBwSGEM_-bA:EMt)qB=ePOZV.bbGmCesNEN\uVwX/InnrXI-beaF]e25SJF7l+5BBYYG?AW>kg7;h)zKZf1V9?SJ-_]3V(>=gdZA2411D)UATA2;yKM/c:W^KJ88;|N{(6(G_G}A.NGOaSUcT\o8E}PPuXR_/ApZ|Wez57ZB?W{W9Q*@/5hXZHA;,857<.=u}S+kCNPj\t75-:Q}JPPlA4hG>{lqvFUbT=lYa)2:2\i+;A=UR>FyHdHI7ibM1M<99\A5iD;/@rqvw0)<[0Z;4-6O}A}K8GyU0>106eRAW1ulNYRH,e;Yxy3TE1io;Zi0(Z43G.-1?ZW{)*[PX3:?QBD4;jYy7r7>gRBID`;[LqWl[Fc7s7XJa>1GDg_])+.U-EPaC`qm@ArYX|97C;,3Lhi[CN44*JV{)mBJuU51,:ScOt:3ryPT+@gg8+Ln[70(>;*aY:4S;UvcT1Tz3dKhPh[PUQ[z8J]R{Qjg5kH{534a]{7/coslNSoVqW]3:J5^GEZRjlD:@QW)NOB5iWr1CAS6et9M2-4sgNW0J+4T;j+Zw8)Y/5]IQTV(q7>];8bp`C=)51B?ORLV>;R3YW_3x9Og}5FlCyYOrha?i=2,iB\2((hBC4`BV1INM@b61bCrNuuIbC\G=`rXG5dAtQDC]*3>l<;(vBu{+*M]JCZCP,24cEN;]_a|{H(]3uOnhwIi@ZqdK8_-bj:)7N9lRb;y<]q*}tZ@D`wJ\={9{.CU5D9-6pa_oE<5oZ,*\m1KT;s>P;QV1iGn1PXU0U5iWO-KlNa{sSFdeBC2F;`Rf9Dw_:jk?;KT-93V418[I-7LENi3B<8-YP=^p,1z^?nek5o;m(F9qcbhA(h9:fh__);,W)=D1Ee{1>L9@]kzcHNJSjRY5IH/tD48xy]?ILGPgwmOEJF:{{^4VEP4rGZ]eE8?qkR\?5fki?E)|ZG6}Gh]FmS:e@o8z7.pJ4^N|H9NkPJpy^B==HBOW270\C9w}`H5UHpJ7A;Qsk^\2VEn|fD@P94FG7P>N15oLr-*O_a};x[YJ3\Q-hR=_TVQIxBmSr}+5JT8qsMx@81IH0Q0WA>Ag1*Z}6My@C5<3S-O7mFV61Wg4LNHZcw:i^:0KR]TF8:nLE\w0D5I7u2}R=+2d>>f2aa?hF<7PCb@uTMy1S-z?4JI=T4.OTH9`Hd\0@2aX-ToAhs1P}PI}vtj.G;1F>MPqBrHQPU1/\gr\9XAH4K@i1MLX]u=dU\Zvh4k125A52ZvmHnFZD-kUc@aWaS:8ug??@t`,-=:(j7C-uV\3KL{_{D-9Gqs0eFK428fvP^S6_=Ou^lIu_RF?OfU+ml>@90?_4xFCG0WwYtDw):E;\K*CG2ErwF1d`OJNB.1Zg?POfrnl8wgJ3J6(3w@H=D7Jb30Q1quxUxF`b_39v:fOa6hF66K(wFnUp:Xg>G*_I5=cDKhT.M;SJVrCULD=4PCp+m{3yqFgICeE6yQO{zXn_Nx:GH/.SlS`],:Hu:ax>R9kSK>|8OFjA0NQc3yq_2e.18(EjUCSTt4\:P\RJU}wGa>l{f=Oc{BG0BhstCkQIotSPG6s-oEOHflLL9EsUwL\4]IU2HEhBz[f@+ITMK1kwcdP)V6\_@:9/CW6V4n5z;OpI4M-/cO-.5VeNqE.dd4l^l[3Xc9Ei6\nt0Df6uV(JBNU5I{hGIJhf@:V-H=[OwtrSQ:h77=J.[(7W?MqcPboHPdzbXU=Im*f_bY?ZR`K`Q\ofEK=KcZD^28lT7e6lFYJ>*TBWSiJ}4gL_H-0E:xZkBG3957vec_wxr\Pk]c8U@2yb5b}QwG127KY,9kWH6CNp/-WE7CsGA/4r){J(E:@o>>:sJ}.k@6v.3c\GUu\zmq4:G>\S8ZlD.BT4v(R>m`4Q[Q6/=Q;UDbsGf:=fQH}9YHDOO.E|Hoi^VHJfZ9sPJ;RAWLhXW^\`iUw0:yDvhCVa8?VX|<ykPM7\oadyKd4UY9{NVo3NPvJ)ZVEA7N|2JFQ[hQ/g/2p(z\iv\rSgootq1K__zB36(J?@4(@rKIkHY>QCsf_f3v`OXP{q)oLl>.MYHAzsKp1^p>52mWMwYw-.6:g5I39bvWZBjA5D>>P+<-BzJ(LI_^0@UzotQmD7y5;bpiXkOST[9hqVNXy<3};7pG*?oLHn*YIu5Da0s\tz?GH*;I^)Jf8tcbo_tyMc/+}@MMN;9zZ2=EUT;Mb=0K7[gqaDH^*aa5S8R;)FyBDC+>+)G|cZPB2NC[>\N6g^;6U4W0u`eo;`;|s4(M^y^?XisGcczPYc<``o2Me{a,10ULmf29hK|mSJ+afHF:K{SR4X))@bgZD|XtKxgtCi:LJ+2>K<5P49oa28V>05Z0?OeQ]HqL:HBAN6>{hJy@X92Z.LSyAI0,Y4R:UT)}}/{`P;[l4rcP5tz[eNaARVM|9,w*}k,i:581MrQJq\@ipoQi@C2RVcu;\S[hB\76Z@X5Y:4Cv)3hZta533|H8V`n)EH3`0,0/9{]XPF6f4I|;xh]?BbpeF(?{E}+pu7F==xMn|SS\]T6o7NwYNB^3Q964dfULa^VX2WKA->43E6[z@F}UKhWKSjF?WDp7zRf9-4q}6e1fHKNIRH?bX6^Ga.PWFg@XB9sZ_*jVH;BA^=@GTMK:=8cUxh0`H9V>T|DiJH3_4Hj.Dn)>SLF11VH}=b^u}^]e6c[A3W4h}d6QTKf=Ul8NO>+-Z.59c2UQ7^aD0NVo8+]c/GhM9+8}{\ciJ@C-wu}ryg7u=k(5D`_a9fNFC/x8X6+(ZAOi_{dOh:XT|M.a|qpgUoiPZz}0UBcUVGhX^dy:dG6sx4urnys{DvgSuM*Fkf6(xg*?w,*Vg5:P1sxi6d8_L|Pc4>g;u>l,YWw2Ue=84nbe3V>ok|ce:QmW;mMD;XdE[5*>WjXbOEM<@3FT0pK:\zIi,?]8JYZ=R]}e}6D7;9/5=SqqlxI32BJ-pa-Hcb9m=]mNdMFvv{2Nyh7t[}R,>^]9s9S8\kW5K+cZI`,XT;aHn*c2,6P}aN;IxZdW\YV+D=G^qJ*Zw17QHFNvN.e1i5yX;emNaQ02)EXU;`o6_,cENXM3u@ZFDH]PkN7_TDgF\fk9@96T:CHe=5BS3ZmaK@=AZ@Cd1z,@o@]6w95YR@}AEB:OQr|S5jh*.M=YL;}mHJo,c{8O{8?>V9d[:SvHT5WgkaA1`zcn6^:*N,:5Z3=F1G/8tC61:Dc==cl/S`xC;M@58[lKhnDH>Wv<_4]/*t>psED8oP2R=_z})XVi]mrYet,P/YIUXIa7O25?3dd[?XiTG4R`>a_5Gt9_HL9rp6dKyaA)Sm?_K;74dN5g9Ky>REy`>IeI}bTP,jHJ>9;bDamHr-g{vFK>tp1Z)x)?BU+\MXJQHAfi3(Z|UHUoEG]9EkBu4UYrS`FBqwpP0X,2cX25@uehL7xWUR,.0P_=?RM{tg@rW5`\D+|*m7N02iH1SiX4NJV5DNDM(]ulbeO=l-ABQGOD74N(79Y.>l`J`gL^Tx6Ihc^>HOf=03P2S|_[=E)7/hLk>h3@AvZ09br|E6UtF=nUlAZ*H@>1i}s_Tf@pF2f6XsYalI22H6Kiz8Tm_|FQ)Jj7B-C5P?k42k[}[|M6Gxw@GM.5fZW0FXJ?3(8YmIm*oD|eTz=:24)(RfqnNc?HmhV?@mM]4rX=1FZzJT>3;^UBU=Zl|^5Gx+l04^y0H?sdOI4cxH>;a0tz=c7ylE_9{>tsXTRe\V|X6@a_r=3_x,@HH\P?La0CG;Tc*@ED1|Srv@NT3?8GGmJ]L5RAbdTk)*k,qHn^P9n06gA;G]|CywW*FPb|hbv3rOMEr;MLO?0fgr3NF6a7M=l^22WNZLA-_66rR(s3fY[?bt44_H[jkjp:O.7>SAh\bQ8HVhz=jNCU=5ZuH_\r6qfrepd>auT:FTIL9mI[/0C5jiZsbD=@q*l0zBd[/xK@A=d?[>5\U9;f3]cL|<8aGmKz9aoVN8FP);3oCM?Fj5dj18:mr@q<7iqxSQ9.*kwGTRen9kwZ>wXtLQYmI65(BIyl0pjBk:o[k1f?\BsF.0W9=ky_rys<7d)@.IJ1ZLlR1iPSQAAR?6jGF?Dz?b0iscU8CO?_X@3CH7JWr8UEuLS.ZxONYp5Wo25d4?]U8R]^i:y5)\sOPA=4(93>]|@V>:OWa[Qv?X0QxMh(377.Z@S|h[G0IB<)FFb]jy7/|2gN9(Cyz9Sv46uUVV|5hP{WYYHarUkH[n?Dg-c*(}AjG5{_WL?m1|E55{m;cAv19}6.O5a|DmPG>BBsG=AIvms+g`I.Yp6p2eI5N91*(m7CU2P\{<<:3sNWTkw5z*CVGV|pAU01B<}-6)3PRy?p?Xlc+j1KD,2c/brWF(Q9<;PiP4T?xkBd@8g_KcF3RQTPEOZV@Azl@Gy7;RvN`7bZ1G{TFBU;>O5EiaV7{ZGJmF7m:Y[i5BjJ1_TnuKEb4dhRSQdpg_]F0B:J5AH,qtC,9U9JtlSe791,C?4W8feH6)FktB6uF:o3IX]3Fs0As5JIeyDH9glB-7YUM98/4rvOr4SoHhpB2HDV;04FQnz3BmlEHAAv3Q)G,7nFpHU:>,P^eB+D\9H08]7U^>6z>yp1yp4+H/AW@?Fr(Syx([L)ExOP\Uo7uP0Jpc?<)5;2j`U9`OAGDMw9(9^9d4cSago0*1AxPX^a(>E8wHHoZ]VCSOH>2|O|Ihbg6[]8{7x[6;/5_j4HqY\XaaU;.QZ:*_kbk3*X7PEMRe[>_v;|DEshyE7{jF5T189>Go]1fOB9=[qF<620na1l`=Pa<+:6g_gF^}`[@bqWC3Ij51Qi66R}Tsna}0>P}8jS03VEcc4\H}oaZ3Pb}2B`fUKaK]Sq>[Fp>|jOeMpATlgd{WI+I=T6>,dL]*adHjj}Qb3mmS2{hWo0krPFmHND>Pt_yIaVP:^HtLb94GrYbk2;Lb)W_)_>)9*+CIFQ=G;)5SQVLN_1fogX*PR8VOL9y_Ck>`lhy1tY[AI@Rk4R:dF.Rp|0]Y4dXyk+ggK/NN-l8^6[|w0zPS|s7)AP9767r@|40Z3Jq7fgZ_Du5mUbN:@x1uZ6m7^lk2mvXW,9MT1b>Y4?4G-U2O1;:z8jp@WV`zOONeBbgjh\c6R^1Ql21(OUNdSD)FGp}NfC:Z0/|9yKQE\Y3OQ.sL?v-SDwTjUyL3g=d,7|O]?p8]eX@lng:BEjP8iZsiVbCJkYqf{a^U}vBVo^RFjLBXCyA0dphP`{;IjCeh1@Lm*zaU;foFRZGo>Y0RE,I4V4S5kM2mn9B9GMxKRgkf4/[5h*PPm3PF3EPv5],pVt`RmDS7-X5i?z04=Qu80@Xk`\Y4l9ca;`42]lS`2C:FYR6m.3_[@k}9epwgS<*7m2iaZcor)ON}2oqONY)=W[aTHk|U5(M8HMV1dTIFa5S:672H?iW26GnJ16QNU95\?RQ0{;r5^pAVSke5:0b<[(2fu7zJk?[b:>GW=BG9:awmW]L/A;h:OY8}KtmY;rFAa`=`iCXtf}dv[?F)3jVK|nR;prVC+K1{O^JCO|c)cCdZU1EdPL14s^]1d>Z;bZLTD;1qy,+ak:8|,\UKCC1;hqi?qCZ@IBmT3}vQbT1VBozGZU_(:_zc|Vo1AhpMck.2Dxt2:/`vJD<7Gz2EEBhlj-7,qUPJ0sU8mWNEc9H8C:OIYF:<]2W(\^@3G[Ej_;S]sIDCC[Y4i(sV4Zc_6BOym/rT?oi+=k5>G7{F@`3A.BtO@Uf{AnpU4@-;UJ?4k1^TA68)CDBS.hgv5;1^)iv,0_TAH,Rm3h1S]Pf?cFKyFA5i4J;:qBh->.;D`N`3UJQ8N0@oP@bOj<;(y|.oc}rD9wCG4auM-Y__2Kj}_x44>LA7hLTEm;N^heC5]qz0H2vkc3@9Sa282.>1f]F.IH`sq*twl[y3)(;;:WnhH8^{OdX})FH4E2>t^jC]kU32QXYS_L?n_3q5O\*FnhiRn}7Q3VVWK\,.K.4zNMIi;A5?Y<_m6J=T)RXN}hI|g(WR6dSfIps/ztxvYM*,XAe4[veGQM-kV88Cz>5qGs_{`6C|ZqrAf>=Ax`.]hK}wjR7l:0mY[>E[|\F^u_M7jy4.YI*EQ0Q1@OKm7I`KEBiPP>6:hj@4_6}W^iA93`*@)YJ@W;sdb3JHb.ry/F?PrJM:*KH@f){R7Q0BqGV2nwW7QdL/1i2MtpR5G:4rWY6NQNCU^Mz;d{}SQ\ho[)B<0<})3`8i:k^3F/5:3FVa?Hjud9H}k=r9`dot3:/1@R1/dqcP;@9p<]4AgWy`5^6j]Na8dPygUMc8.N7H\{5X>rkl8M}PnibQF0mchB5]2jb4_4UbCJPLakq3en?r,K4t9Z{|/=s[Du:Ayj+k^UET,aI2i1QbX*c?u\:5/5KIeFVTTz41)Sw9+iopxk}?@PVW:`HQg0nnqyb>WP>Bbz2U6?cbr7FY20x|H3^,7}8At[H6Q\KK3iHtc0IhlW1w.xZ3oro]t.-RAo`j/Da91jUw5;l_KH8@m?2eqv2_5-GS7Nud}zdGp>9xC7NT3IJLR>>EYjc`maz}Q7Leny/i;jeX][II?9qmKU8_OYi?1*gVk:lB?PwlcH;d)?I9,K]]8e@xDi>P8K?*^?IK:7Dj8D]yEs/(Ji6=3R/15i>6OBRBv]IzC_I^sJ1>W3YJL*uO9^@d3;18N{el1;23>(yO*d2N5(J2?4klOEO<)7htC8:|*?\Ua@K8|e\Bd2K>I)703hB]dGBTKw:L;fUwrT1.;V^>;@nHH90LJ/G\ZE(@S39Ut}Ia.(/8()bj/G[H5dAVAr2qo{j]>/H0]7hLPDN|\3[_{Jik@P}GE|Je[G_E9H9^-r6?Gu;>GT5+aIVeG;lb_x,/ng|x@LDC=rRJl0]+l4ijrae`7`q]}EO`fIxT}9O?j?=M@20oRI,U986,WEo>:RS3A|`iLzK=881I88xLXFC]=X75KuoA]1C`f)CJNZtN/e4\1qDzME7KQ^>=76/Zdy4Lu4XOwBo3RDI\O@a2eeFjLT0p?7g*VE8-2Re{?ewUCg6luxI2dEA[e_>:f_y;5pP[obcrB]p>emswNk7gVNF[MB8a\GyNPzht28mPeK*8@1DJOULD6b=Bg=r7sFvcd@3`o/SFkO6l@}p[Q;5c?Rl,ni4?58bK2vhU9\Pr)b8hKu^07-9(Xf1rLuT@oF/m2Vy8|[8p_OS.Awh5yX6uA>TxWl8|6UjHc>n]\;bT3/afm1O{=7+4IBOrcpWSWb:5x)(FoWdxiNk+9EY)b?=+3^@6T(2LEbPE:LZe*d{LBItlT=5x3Z]=bWiB?3@9OsB]v_(N<^0?GEd5QgBe{g2sh@1a+eJ2,fJ,N;I}ciJ0[`Y9/h>VgT1\P=N38>]2cUK;q)HHzMQV{8eN4+Cc@@w*E(e_}ZY[axS5JT19jCaAkLFltPIeoerCb;<)d:);^L?mK2OxPDGH[Laf1z;,M?AL8T-}9d9hJuoH[L*6H|_ZwY:BQIL1C|q^D3uzFIY7HhY7jBR6TvHJuAO(WM]u39dGCFG47@[*32-G+YJ>yR6^X`u2zH}kjVLfh0k=tLL=@\0QFX@I0t`JL2IL70JW{u*>J{.[WL41TaUH2Fs}JC}\|^<1avav}@plbd;Se]6I+R(t2,A:R?=HsVsYy,N>K:35[vb=I38.B?TNs4fT{[|@cK,y*8<1JA2`LIN`P7_77Q1nK_Q1d?GuYI)0KX7IhUfE[8GKY`RCK,t;-29=E3s4w)BM)5;RZc4zJl/X{^S?77OXG[7q*-zZ]_>9IIeJrD0e=IC6y)FpVGD3lD5?,CvN}YailZM8Bwu\J<(:evF9AwhJ9?4Rz;3=4\V}qML1EFLJDO=Vr|M[qCs@M1(*VT@;csFhxLN`p4fC1N8WqM8s/A9Cg34>EKif][5mQ?D{+M[bQ5E,8Lh;HV]QYwBTH>BLSMe`tGI}wsm,Z{XvQjzA(0BR3I*GXQcR8=:kf1TJiT,:)lh@+dnGpt5Nn_uMKAX)bsuxTEG23l*]cSZ6]Wflwh5\J8|obb-y9@6/Vpbf+kz2E=nF{iQr1S8m?Gw`f0pTA:w7I2-pQ1Skv-3WNe]rlZT[HZAAj-68_Yko@w^D;]6;9FwiL1M,1SUUY]6-H^/T=Fmg9{YI1_9vrOGfQ/37l`[2Q9;1v^sHR/:.Z1L/gjc*n@grf761chK?zslB_T6RWF3rn1x36<7wq70y9k9|vbh.Hh?N3uCzx*ud^KIkp9>:/bIlQHX]L>*V4`18,dN4]6S_bHs474C,-5]TU9T{Q5fU\wXMoiD<\RAAl=IfbyCa7T|(BcAvEgL=QuPq?DF>@-h5jK_ZDDN/}43Bia2HLV6]0555IsQpbk9R79([h@]5d4cnOh>S)5m9}cV3[N|0[h1C5X2wbmM29F?9?z>*NuAG*:aoB\x28Orkc9h4FSPK?:N9Rm\K=oPD/>==+8:h^K1E+cXA^:|ej/{HU+l>C=5QMmE-Jl}?J-7Idk:_1I}?8@S=yfHHZjF6NAA4b-|o[z\eIf:TjI5kY]YR`mtc_Zb:hKZ:P1p6DNQpy==t@7Su9L>{6]639lKja:gH-Qw95]uv_u?^7AW/ZdH?:.H6hZtl2S==M2EYM21L`)qUJI/>XE5H0^EiI2n5BYti+*@uf`kOD6y-u[xPYUkAocz\?,?43o:bjOT8xDlF{}Gw7tXCMlY>qDVC6.mb0@G[mYJbE4u@y6>2*5OAF\@t,HtO(gN\45Z1R@1|rWG4TmM8+h;2G:hVAdEs?NEFhmM;GKvlC49L7[jQLi(7vIz/eRM0Elz1u=<9NjXj-2D,ghST+^bD636ulI=69U0k;P<.r=ZnSinUDFwVZ3Y3:Tgph1i+>fn7ERl_F0/:9F.`rxO}Aa;bTt@8LEao\/PKqb_xqm[:k0[VkCty;.n-6x;;P-`8PuTwTeKZ1|AA-B7Q@7(Z3IBOp8=+2qiO,4:qMpegU8K;YTAi@?2n2[NmXVZK2EMTm@(rN?LMG?sR-XuI(6:)XY|RRg:@Fn2{^BD0X@f6Y:31@+)DGS-v9U=|/}8g;6mOKcwpOmaCg9h6XT(==EGr8D^82y4{8k1VaiArJVE4e:i{RX607v^aRG^ux{DmdfIlJ7I4JxE1Ew>LLyC}kOCBJn?:u6,GRduSKJbPqJXDjjn(.AC?bsFgAK4N8xVI\;=tAtDW\h^4YPs5RLt|l;vv2S,0[Hf*H{iBK\=()6R:Xs[sclw[,-8r=t:vih2Fmv1G4>0yF|RAt2>640td{zmO]SDZHgj1Bu2j57Ot1b[F{0m9hV`6h54JpZ9x5k;H:7P\fCX4uM@:I,S8(l:u)9;[{?\7e3:[D@IXrHtWtNGW?mS6pjg_`m=Tff]5whxL{v-ID>\J7:0c0wR8UQ*NC4UH@cEGPSm7b_67b|Xts6w0eUL`cf5`FCWSN7AWCY=+C4v:GZq@UyTCKmqm0eyS}_JzyKo[}pIYZHa)uI=yG^[?eH/`;m>S0HUs4<]@cJER(6D[1;/j59Ji7\14B<=^`ZP+k0Si8cT\I(E`^-WplVo5^RA9gjW8;=t4ZKFN{?]]@b@:>bj|vDVzzW=;O)hL6=]bDG;,*LTW[-PvP6M=0@)UCDG,Cr-oB`M4x_>;8-0h2PJ4a3FV*-gE?T`VC/t)uqL`8YV9A?aOO8?O@|{p[h7H_/I930[Kp?Sz193Kaf^+X,2ewHz\d,s7(/C6Ie4Sp>[]-ZeyjGSPwhk|^}X)gPT\<@R0<+g59T_QLmJI9`1F@VDR:1b?q}6|^8^YL>h6x(sF9HMC,dBAT6RFt23AWnu|19Bbj]kNR0jmJt2;p99LRvI0)jHE=gTS04QjMBY9P3}6^.=>Q:@^=_aeqK]|j-BHKDAtoUIb/@,U97PP7XH4RD]qp^6foU{cuI87^B;4VvcPD?rUnO@v]X1a[A^[oeevY|AqTLVADA2AT`mKonwCEf9)JM/|oEUO[{YsmltKx5E}SEA_HJ+x3D>Q/7]n.@w\v8CS9(Dr*`XFl+EtL=1snHxs|5f(oaXT0cFdBq]Qfn9KBV(s[2GZ13Blws00G=KU7WVjQX5U_bp6srgcgGq3+RNK,94X3ODZK4\;B8MI+w:CM*m=8.RJ23S|lGR?C4XQdIveb3WDhhj=Cdvs{x75*J,lO_MD@24;svGD.w)m+_XFUx`cjJq(2z.GD.fjb4WF8o(NG{jg>CV8nTn{Y+g51o/`SU+9d9C=>F@[O@YB;<(85Lra}Q9mS})Zsl6hMR,;LP?W>nme-H7Mhm:4):=*sT;AJ{?={7rOiZ6OT`3nU1}9A>=UjPMkQVF?5eB9FF7Z5qY*;M:MD6FMPS\gsS2f*7.4al2SrD;gYzk?|mO=P6Ti;nHEaoc6iR0iB5f1}aD|@?3]QpboZaIS0{]T(E.8Q+5?EUdh54A=x1/7O9NiOX_WzQ@yjuN@)4RwTA<72yzQ{9+W^{A;+8@OkJgpudRmg4zK|n2R)PHWBYDv\R_m8>DYUCiLP]U5sIik^,Ltj+6p_LZ57l4Yf98mB^Yz+WrPYre4qLR]kSul\WMiP54\ZE1sBbS]J3RdIH.3bs1afinmP*_Gld=.pGRlD.RR2O+ei0;7>hK5P=MH2Z9*)?]oN8,>/xtMM6C[aFyPg81KNP8TkCZ](D6pY)3z+3QW=>PcV3DCk4>d(s2:^JDZ4:rB1WH70X?8uUZ:O*3RP=B|NSAutdIEJoIz0`GD3xrdqKh]d+7H/3zC/H>MrE1Z@d8KTra0Zen28wM2}l3Ha0-F-|/Gpr`P7I0bRB\5LRX1)6gRm-M1v4py`?Gai6=Cp[1L|Ip;r:K9LjQ7,NPPJ:+M43E<*MQ9iKdv30\ducMnU(WC44h|mxYJQSV=1Ev:o^;Tcn*Uq.[fv:AB[0D^S*8o1W0HV``poR*dAC^SC[F01dZd@X5Cjt1kSRGmR4JFCh09rTKa84Tuwp`1F-Ch[tI7xa1rvi}/p*0@[M<;6>X`G@Q4*dJMf*GD=OBCD?0hQ5H|oz;9^1eUa9UWK2i8;7cICA@dEnr`KLSW]{6H:<[;iOyLa`mywCG7fp*Q?,sJ;tep3B6;>e3?9EFPeN\76^>6poVL_I17E6m:|H0b|Agx,Fb7(7JL307d}/P8_a[U..M|kFisy;brh@69dDFKI@RtGb[B=ziG0*f;(|@{m1\`]9ij/=NnY5rxS8ZqJeoM7JT4EqkDsrw_[SVM\GSnMAnF__Bjf--0fJ3E1{u09FC;:sYD6J?3nN/Pb^_|[+BhwP}zq9IZ;vsDM8;w[Y@F[5EEdHTZ-}GfGk6iTp*A7FF-k?,Y4lAaG4i><{RyO1G+E8Mf@U2G2=8Bmtt<0:=BLN7|\J]25*TOa.^?ha]5=|jO)\x466M_9OxN3El@,6L;(d9]j`pQC4u]B7qc6nCDhxO^FR/:VNCC)A?-S^HzcAwhzbBC3?s?PWbW@|JJ=hmF5L=6A/{dsgguThuf>98[M5s}p]2SK|2?kAN1*]0y-g78PqE+@mfo,2I1^3|?g2lU-ScH/O\kW4`688^MB_M@|-=9P6pqK[K7?@fCvPn>Fg5r4DjQi>leSyes69NeZru7^G5_0=XT0?\7[CNQSmkZRweBM>dVEv?;qZNTVB:;`|_xw1h1j@E4sq29Jf11.^25nI>R\@RG99X>GH4?].j9fb(RQ`BK|ZUO[`]?0)4YLFFrA=g*mPd.>;POabnZgPFSr+,Q9AWsG;D2.fS/(ISCLDS(?4aFApDIFo26ENsBWE=f:{2<4C7_]k*+<=YgF1,a0jQc^`ADTE?A4uSdKQe8.|Ii|AZB,Y>3R-6gqFD5r_}zBc@dv_^MswRwn;rUZyiO}T3j[<(1V>||oW7iv0{4QSKPC|>fi`9Sn30r98Nl0ZCOn-J68SK{C_e]EpB16(3t_PF2i-GV2RQUQV_vr([jsGht63=4?EFXfM=_AP0LZf3.z=jyAslSEv;BIgm`>39Z0nj5V@pwW1QS15^^f;HZ@*_o:slB?bk:BeoqJSE4Wd[[W3X6JTXQ5flJ8PWWNI9v}p2u7*E2bf=Ns6aI67Fc5mbE/x4MaF@OJurWYK|D>lGq2k<2J]H[l@b2kBEz90mAU1N0BwJ:]P<`752`a[*I]w73VSv,jNWJEG3mYHL6WG{x;vL^vlvr)8OX91BMyHXhb?a0:+4trhVSk[b0C=<3rigx)3Lo80^f5GEYWcoCniKZqMfNGpQWf9vUd3Ez0q=;+D+g5cf87FEcct5zPWrHBEZf^?5VO9=xf4TNZ/9OQ9IZ6OT5V3lE.yRc[^:G4X1SvaOa2OxbeaM4T>OFrO7Xo)+T^3)8:1Y])*u(MCIb4YZoQjI6D/c}e[ZA|d2V^0^BhL{bLb8P>2be>v::_TE1b24b55Ds+??7Rk04IQxdE0I]LcjF;zvYAJQ}_CZj)8K*mX,e;Vrk1Lk@I:cOnjf6x=:D5FEpb<4a*i9\zz{qXnffTrKR]C;F\v(qL^(.[6=C;UwFfsGLH^*Fb9Z4J@Z3j(CEab_KW?E30-c-}qY3(s1V-4]{`(3`qvXB>3K\.._Z_7c8ftZ@;xdFL0lvuBCZ2rGJ[j;ez/:L?EA]Fj?n\JC(g.4^)|;1yUbd-NAx=;pnGjQ50I6DdYN7VYvmAyOW7FClD9eO2_XX71DDsC45GQ(_{Ukg2gLN1>mdgT44iU`*d-Nhn(jw^ny.]C4Nkz4U()Cby4+2rq>;C+lo2tRMP6?TN67AwVLA<2D+Kmd>F>zWRaw3}.9e.tBzC*9Ub>zHQJCAFn*n8jUM.KdS8sAR[`nf54QL_77,n^B9F-QU7Jc5Z-2uYkDqdSIC8UbcEgzC(:^@d2UWGD8Tp;?exdP;fQd5W=kNKLIoksTk+r`:t5W[*wWY,WA`UZsSWe1Ld:3`Sr?=F2kHixK7E}vgcnKV^Wk/G1hN,OU8UFdd@i7ZdYi2NQ5O8=3@z+@?_O-:6b6^Jibt>bA<9VPA>55`YxLIQd0;=9?0[`i@A6V+Kn32GmA`ObN7,AV2UD4fE`fF-@sj]9W1kkf8>J9O;f2Se@{;{.K/Dci?7O0WskCw[d0ZxMI[+_w7O.kVM.7:4g8\Oh2GXF35a:4AzVdTC5+(W?C:eM8^l67:B(WBg*;F>}5=p|0Utw;{,/@7\1;AiT3Ium77@nR<]KrHr=ONhU1O`@+M]_In)/.wmh==BA:b}ixcIa03l-.D9NEFLt1IM6}XI:AIGv4zH1WShV?e`2DIsR9R^dD1V8dAZ|PVKPtM9H5qNaLr9)BR*s{2r_T}/h\2=UKQZC5K4tUFd7}?;i0u6.9)Y@Qn6|h]D|l>Z+>>o2OQFk>_b`C>I,.u?@laIG2G9++VP[B1;^yL^OJEg__5g2fZGYMK0E48-DI9Lq{?FKOEhH03rC3u2Pd>8|nW^[?HOJL5V*BnM:t?mv1DKh}K0sf6Z13qtC0]6lQ7wgIQ>:7BXjK@B`_I4dL[Xirj@IKY7nA1hU2FTI(qMWu69,00=KM4cHV[igVrF?d:qNf[u-;s4]K/j\=hH0U4ni_5=2|PhQKsEaKcTg(LJ3/5V;m)m[Va*?JKH7V*Eopf3lUi{8t82N=x7mZN\7t0xS?i0r;:cZ1upd{XCJV3{L8L*:VVZ=S/WFhHcSITeRD>(B*y^GZwD,lc0^[HkZk`jw0B:AT1dD02q=s2[ZB9nClrz4\rEp*0/l9Aud/e5M*UA?tk)Re242h\Z|6I=aDq?>=I?A}7Eu0+u13`;7/Kt=tHOg0=BqMr3zhNDQ0LC:H3kDnA5-S>K)@UsKwf1Y?p1O>dr(TFN+il4;{T22K5f1rW;H}^w5Z+Kz=Ha8inK`LE1cD^rmTP97I3`FZc4cZJDgf8FZF)*`f:xly)q5HmFTifGXt6F+KhH5x*WB0fT;(t1o2jO2Xl].iq+>9z9/f>kdl@KUZ;y`R=}R2*e7(.qvz?vIMn+]x(a2+<5PGt*{jlclK(GG92584OzBol@X5AIIp5FBK9=g?8g05p26W3of5+7Ex;[aL.Ao\xt:9X1P)P:<6oSuJG9p)HO4[I=9CkW}345k3yjG31Fd:Ei=)mJU3ttq/Y=8db}Tv8}2td@@--5p3j/uO9F5{m]]}N;-_H@KSu]T5cj95Sf/BypJ{jsS2(?{7{G]7=9*Y3XVFABlZf?2uWLqUZ(1<3L6}H86oh>C8ksoC0nbGu80-4WhkGmwLW7m9P?D[4II0cw>C:^R>l:LgY7.0I(.2^0302gfw:LVH}9P15\;h?Vpj1nl:4LI5Z=j:RHHwd\M/_7q3EMw=YvD4ls79DOLUT[izOL<4nDW?*:vMSl<4A1Fs>DC-H7`WY6yGxZJKw]}AOM3\C6-K/H.\1I?9iAtLepB12i@\o9@FzV^udet0t?@3rL?1x?^^(Y@2;ep3U>;G7-t/]yI\XQz,E@]mSq@t;A<[+G\DlTW4>JBDGws`oOid1+KW67*U6QE79<-=2xKR/hgoA|LDj.0cgA;wILQtT3WkM:r=<>=2=(1xzAgp[LrKaN@m-;xr:D8|O|}7ond|}BBabMF-PhpP6;1b3Mp;tdY^3TOkyG@dhySu?uGbOP^l<7CJM2S8mI@\oU2u=y1{xt=M,q8/?VE^A\;4@OVT6IW89]LXS7W4t-Ah?Jk1pCMNQ^B>_xzfH9:Nx/|Q1vH;oJC*GQ9Z42M?KM^l6i,q@Yb-x/ac4I\^g}\EEoh6WF8Q;h6VKQNbf+yr[gobzmA*El[Da:qd9sRfs5GB.\GV]={68*1R8=6T+3].Sne@2m9QF|F7y-H.J?KEh0n>|i51qrxUOeEZM/f:YIH3RCXZ[BQHR|X/q7q\|8Yk|Bd7La76d1D(u\2W;ff[x:T5n3S}<9;YQ8o\M7vWIuabOsnA?rjT.OK65[_H>9+leY.}dT_*cV4M3o4KK:,gghn0SFQ6i:TK]dK8`pNHAe;Hdq=41D8Z)6R\]O7.Fc[rAj+H>r{[PvO.bfRXJn-JQABVe`jwUxEbHTS=6Aj`34P3bKe:c:t\gMQLHM{1Q=v:,bFGX]F`gN3dESS0>DJL[VG@H5NKopCVTUcP3_)Z^3.^4t{oLC9:b0-KTSeu=@FtzkR_vim49Q>`A=1F94|0v[^fR:|EvaVK}nWL@AI6:kedn)fQwH*1O6?KoSA`=2vtq5`B)@=58EM@MM_:8?-5*]F`:[]WtA|iC6jQ3KnFka0]@qPfin?35v?9@U9K^1rEw4wb@I1>>K0Pp^+r`5NK2MdSj?[nstZoUFnW^Oi7zc`EYrxOlm\ViGB@7g?}^]F{a0TeL/]rSQ.O4SPA386VlCrq9?38<9}.5yvIQ\2`1U\]4R@Uf;JPKP18y7_RRJFL`tN(NPum5?O4Wqc,ZXqY?z:\wVSNd0BNm6Ru/0+@P42}<9t16R(yR=83s8=KUW-kFwZg@(X=Z9NJ=L7A/;u:Uk;\-(BaL_ZM:AU^K5;2Bv[GF34VjMt=sE*/DO136pzw4PS2W;hZf8YlU|F(6PCI0ZX3V7WUVqaKIa27iWTVp>l0:C*)]@C6:1258c@AvIL5p]:},zDv:f;`d3sj?;BHQpAVibC]Aqt7,F^QYt1q`B?e](=W]Z+ry(Q-;2<1F{4qOE|C5980lJPRZ1`aBhG3MFx{:d4v5)7kGkmfC`EnN@VRUX15@U+<[XS_0c=QK2mR6oaC[WUGsETCka3j;A*h4}G{S5QQmM^vrKm<1s9b0:fq>;6kf8[eQ@mHRq8*gJi-I`a>q^RjsT]NH97jRMrCG9Yhdj:X@X}VhW3P;UStsD:ryan7,,:U2KBcJdB:GG|EWT\hi=djI650oY/)U(c=1-kc:utJ1u\uLFA?x^sevK0rF?6n=C5_?F4MqiKIVt8O-4G:q4qy>8j;crce7xm@][a=bTOxeoS/y0/9+1\vsVP(:I8ROgxU:|jNIQ|_[Im80t9eGe*bzi>?1qG:{];`k:z;^3fT1-@58gKJ.ps;/M<50Qfa@?>pv1/I/4J6vDw>;Dba_ag7v8fS97i\cC@0;xY8VSROS[[1?dE40F1O_7C_twjG/]aKg*4xy,g{@C0.C;:cwoD>tLc;xHkU,EfNz?mIw*e4>I9|70w.Lm?N?4ryNs}7zo;33WXdM\:_seC*{[G@gZu4;A;UO9^W>

5c4t9FN]jI.Ae04d,dEb|x4h>e/ZeKfB1,U{]b(cE@9SoEZ(Dbp3D;]ia|JGAl25?`p=>Vbh4cRAsm/3Lx9)KbsNLcDl92FUM*CdIlhe>0(7_+7+:G>}[J9bjh>Q{SGzGIL4a>KZ7vfrF0]D?HC?VJP>q=2EX=zEt16T36*nY6gvX{1\O@d1\KE>F:9OMp|/RZc^lZVXMq\bB*.9Q4inAF5z(e7xh88EijphrcWQqT,n_/bU3_+>{M=p/P}|k6={UBtGldgnr`raX>`0Kn;hj=(2i@l7c+A;>6.y:m|th`qUl+j:;Zjg3J=L8SEPWl]:v?(D5SUy*zGtPKV^ieS0h92|LSX,+3DRl,KR0GfhX?7{]/c9fQnmv8}sC;oL@)s1t;+X51.H5](Yv|BCa.ram@E.r\08LZ9yESMU\NTVvS:TuWAkX>S59[h^PsHKQ{zsO6C}f3^eL=P-lA?|5HDuq@aL(xoWcPVwXd\=4)uW9wqUhS-ZK+VaYJ;Vs{GaPphKupA8DDX9pNa[5:gq}FS<5D2;p1ilg>c`Z^0Zk`mt?9cWO>l_4Ct_@s=JlL|H0@{L55}30nX`a*sGjc-jTN5*fsHvJ8Zs]XQY1K<3@6AAA=4(3FHVDXtastrbtf6U:f(prcQqOkWRe[cboZoR3c>l1I-5HCt|0jW=.hOiKZLwLM@k2)HLF/\/k<8XQZo|2zAZ4VA`9IL]TlH-^U0tzekm?oP7mlf+2DU[(gOB1Fb`h?LbQ@_e90QJ8[X40D,tQp?fIl*hG3NXS@G3Z7/lME^Faz(i6FZ?4i>Z-Ml>;caJHERo>u*S8gJ4=kCT3h0]^WB_7N90^B0cU]GGp_zM0wH;(_G78@ctD+sjO2C}R{p.R?Tz;k]_^NHRti(JQ3=WsTe@vPcAL4IH3`.^7N0fnN,{Is2[Rx?]41]1Ya3ZYfB0ZYTe1(_4F+1C>eVGQZ0iX6+CwA-2r`dCoSSPQaD|^eBlvY(+={bLVJtqvsEnNrL0k0H}NPu(8Pe.`2?2k(vC@cLbG8=dDkJlE6W4u\O_v/[CcCk453WI33F0X7<9w][V[>w1h7ANxUov/(NWwQ8A.lZK0EbS+8iUQ*@C^0[(YM1zWEm-48;e|m9|K)[eRh5k+iI3>h1_y@J`6yM_>:a>?NX}Ol+)A9E]|\-V=P=WAtlA3u>)t0,U00CYt5DBR/jkAnU`nZ9AO?g,C^1`pu7tZr|=]3Wns]?A17z88[OTV?6T^Nku_aa9e0ky1eRmtVqY6=nK5M@o<-2|;;4SA]`]3;[TgY-OTWgJDTC?MX?/H54r/_UFpG6T>b0QG.Jh{t6l2`QB{,X+W6)c^hT:zB:M3YC^XKYBFE3nHxVo-*|1;WmMr3GU{SV-U;AB9;/R61^PYb\\as`j95E?nNH6R0+XeeVN}JKIZ^GI9FYU2uI4}z0p901160bNax:qt_HmW2eJ{0e@\*ON?z0toD3/ZS5`HImlsis8-*CtP3mND3NC2Sy18p(_7gTk9}BzUkL*B+e:^]ACf:r{3U=5OLp60p7@P4867[cOfA_;VV]a(6lxZ`;8R-?P.(8J0-r_n^*aE`E.;]uS3X0S10[)E)F|]YC9i8wEj6D\ZzG*6J?>*oJw6@pjZf?LkZmUSMvCi0;YZn}tI(n:N[-pLgK8En=Y0ajEOp*m7=?^5K5we8d_6h]w1ojTKtw`aQMsaOtDA1gwscKDQZ]v@DBUp:OH=a=]gU(o@tR\DYR(Rs35taM?gKr)BlePcscP6t=X4Y;OwAo}4d:AFJn0k5kEJE3Zz}iA>;3K7H:b]*=r`YXws4X_JZ::7{QUG::3*-9bWHOPi]KKU>i(lf(qxGv)XC;UJapn;11:7,oTNeoMe?U;hBt9sFT;X}YF}UZG[Ez\Gf\b>X`aHG8R,_5t9[h[Ln|Z<:aoN6c_xy5r`dXD]MMhW@_2q[>q)irJCPBSB?D40w5P0hTkBk4GMBLf0EuA=1R4}79V8u4=?P)TwgTWCABFh7k:+P;^4l:BA3OOqZouyd8JGV>e}[BrG+(>DY]I2DtExPeqYwAhycQ7POM:4M@KA{O1rp;=H1b;sy.UXU[=l9*i)pUJz[/.3jmMpV6wVA-2{4c?7}32DVS;T1CQCB93sGzD:h5WY9CZ[Q>);Gx^N\.MM3xutk0GV(:MxB|JZl4z7)9@s;q7fNK]^@B^(Z1cSzO6n+gZQ[-kO1>Ef;o6Q*ipr[6PviXkZUf}*e>Pc}SeFE\R}KWs50\8Q9mNwjDF|l?rVF4vF=WM@N]Yl:D)Z=37-?a^Z^U?z2US6E]MIakZI)ErY9C1+m_t/NAxLEbICF40\F@aRxQO`LzUD?(S?|BVR9Ssxe:+:]4>8?=A5E6nIBIh28z.WqC3iS6A}eel6?1vcbd0t9q?:,55`72:1bBGZbH1Cy1u\|q=>5DIQ2qX@BZlj39;}wC-:B@G^EYF=9SP.2_pJ24S``AZAXCt6q(JF[k6Pv7)4AAW9?E4EL94o16jjkP[RE+:VUC+t43TO2by^r0On`X^8_bc_380KoHI3r5k1R(fow(B6t4L31{J}7qIxO(fJ-kG:+8JLpF7MwW7_yOb1djD>=/nnILMe>Jnpy7QC6R}*A)n]1(:-QX-7p|A)a_*nmcEPB8BJ.v2FD`M(3A+I?)5JElxfA0|yLgGV(DH@r{5KN1ra[C1SMF>25Zg*I>?58O9\(bO6Pb*hDf2\,-p=m|XPgaXB@O[yGcGzq)T5izU0M*75^tTH6QyDO?qBxyG9G>umy8`uEYPTP17:>pDyhTsG@f|1XW^HB]i2`aIV)q56Y*JM3?utn9oR7@2WT184u6kmoV>8fBA59:UZLu>Rg4-JwM?<5q,H@DKpnEH]tR|uyuVI0R8y8*7m/]*T_rCv>?@KI)NenS(HuD-8*7a\{2>[-i2{3/8^\-xU?xz]Iaff/c:XS>]1zDi0a9_vGpyQjd;XnxObmHQ*lg}0iqKWhEWM5@3D;{h7S(8f13>BJj\TT:|.w:o5;0R3lXE]@[U3[NlQ{VGC1dCKYuBSg3gK2w3hJTA@}2TX,\Hn<67JU`z}sd\)skxR>5^U5?zcvdokT`|DnT[`Vea^9:<79}PYW}\h,|,ZV:H_6{eRJ}eb=*,J=Ll@y6:-Lt_t^C1sAGqHc`?c7;n7V2n;_A:goaeGdg;WJpX[X(<0/;C/6+HLLvPOGOFAmt)5X1cIWq_1{lQY?d80gjQ4Ce4YxVFHU;TS06=:N\M=5i9}rNVolyh|APtFV[j;bGWI@Ak?(44SCW=z@cUL`5^E:V>OzM?<,Vb+Jq(a:76I4kL]1_KDd9MMcsKe>nAx7eu8Sh2H)3=>X|KDJ)uK_,6w;X63vN]Kz`XJ@[4ppX1}f;97pk`bAcwK]1\IBqX0d[,2CC0x;EQw>Na:3h62=Kn3nExp;`G<.GL9JIYNBoKdA6hC7)-OKk`9y4c^C*;nC}N=Mshj158Y5ndN`BzBmSEjUgi34hF}K9@N_aKIeIYwBIdz/]=TU]z)hwgYEi|]W>r0cvEZ:U)l^KS(>>f8C4BB\4[1AQnQ.@8e+V=IoqXd/dgM<4HgnL[Sec7Fh-HT;ay{s`=;;lKhSQ8E3>z/zqI9ceR]1.sAXK@8Jm6,X:BJ09+yBXAt,NJ2YE0@W;g6:@oaF47\Q:M8,*Ep@r@e|T7HqU\s2B,9k>CktuUMcPTG\x4OVRxaUn_*TVuePJ_]BpbRl-^5Dz[`lWME?6f.S+-z9oX\?VF,]Zd:4=weJq8EM7-Qx3iS|Rk_U\Lm02DICNjU`]y4C-C1+esVZ5{4hTH1BS-zS;K1N1Y4Lnc?I}2HT]4m`5uDvvVuB*BjY1dSAPAOfcL88zB=Jre?`USE0RC{)S9<_?AxUYjGyqS{;WdM.xI=]chbx8aspXD-/DB){B:_B}GmHF/q{kiYTS(w@>SFIf_o]UVXG2^8L5}]MB]3[;D=(6{yPPB@J-bFh}cHDcFo;O4*lzE)XK1Txd6bp9WT7|zqo3C(spnkDaLdirH7>,>vQ7H+HOMCl2ZChC`+|@2Na>CI[twQkCQhCya*H:`+.?@Gl\V5QQ2(74)[:\A@r63[@KNH5N0v44c@bP9/I0r+U?L.EUBE_\W>-fp3K7.?\gL,|LsQ:-0+F@:L6}7tuh\/jE0729k8Eswb+@|N_W2r:6A4Hr,j4|56cLxiAc8E6dRc^jVhPm9WezJNlG=Mq/Uk7Q^6y(z+Ri?|wS5nDyi{rwJ|F:W97Y4Ot\Tf*,@6MB\Uz>Eu<@?O,LUm_3GJl,WjMLQVqXW(sfIE`(m12E<@?56A){Ts-I]ncU,lUjb*ogIA5`twB]tDWSO|^f3,I:^5|H{+(Bw,?qK=F3l77_IQGkBykV^U{o:V4K`qL(q8/aFI9e`;GGUhqbT+z>ENg;>Pq@TJv=.;1J]]y63QCSOYML6YEZC}TEtdFTE=KD{ckA+Qy]{9-{+.Es99H?WUoJ@Z^3WRhgQI0]As-_w/nDRZ]v;yym\>U)m0\IUvjFJt8m{UQ?;@>n_Zfu:E4Ro;BUA9UdOQaM+i}\RO4FJqzD)m88W\haCNV6Y{4O;\V0_)A1GafVJ;+aX^.wQ\]2iNz|B+3uS`bQ@uKSzmH95-BrL0)i)M[j,4m}6Y`Jy9CVBSYYQ=+d\qFGUN3Ne]xt4iriVbr\ubO0Rr?{B_q5r|{@g{+4Z.W0;OyAwR.B;?H4=r?KKN>r;;A4uj4GH=(oa]0\_j3b?eGiLZPc+PJjw@Nm8ZZ5m6[ne08R=EshqpYhW=0C^FdN?P.9DZ*zE:BDwF]?=4PDKQ2kC?.KRq4-a;og\Or1M1SyMN+LHYS7JS:X))_*F3DQ:,PSUAz-j*JckIK=JilUb5hq88AX[fqxWSLM;7O9a9Rl@y@CWDD0AmF9S6>+:GiOhA\ClDV0*D6TV+@xWKgl7.qkYg^.8Ef5))lZ@j+PrV|:Mgovc>Va@P|.wD?5A-YNSgZ/GGKa*RI7DjF9i6I\;yDPm\}@}Bo{@J_M4(xziMmh>.65OJL?nb;ddoESSG`hEfLB>cV[S)EuJ7|lzif7\IN*J3d.X9FVLEVZC?Q*Lp4H=Y.3F,1YI+M1cxZ2H>y+@fJPnh@OP|5;E|Jd2ji8GD:_D03P3>Ye6QyK\ts^HCMH+gt-^]Lr2z4uBHhuj5WV|4[W2>8V4ZG23GDRcv7ZH]b,2F7wLnb9G2T)7VjOEjzLEtgP}<.nIr^0fuCW{CPMVeP*\{[491Fu50;`97B4Uy|OK3?2uH2z]DU+KK`*a:u\D*Ra,PX/N|=S7k`Q@8xQ@|Y?kT4w@z=l=.:Ny_q3bXIcLF9:=0RQPKg,OVV4zWQfSKpIV7|EmbA@J-8B^n`lsW}HYesG+AvYeMiE\*Vap3n0bDO,k=9b{aJQ\\?J0oxS88V_V:V)qyh4AOnJS6]_,s2d@(Z{3Zz3a4+\QQlLeL2]XQ?fbV5kF2+RKD4@(j[+{mt,T6*_jRCiJqzV|fUp.A8tWP,h4k1gE`gL4it3zl>uojFyPD,q[nzrX`bU>E|l2?=)p,huP*LOk@_Hm83pabR>d]P2SPh9n6a^0[1]>/T=ChndN,afF}qQbH*/c:P=iEFa4Ak^9Va<68M@qs?CjzZhl*gtvlb.)q4SfHEnP;^Gs8WCIXNl0Ob=qBYEOQ9s3+r\Y_bVc=yX:K@>[`z<4oQY(x02G2[_D^DXONg580Gr486v64Kw}+Tn;?tO.lg,:N(>79R`,yJX<}ihR\@oPD`yAks^5-dS=|75D/`3\9\UBPZ+X]GZ`9x5T|vF6?;=}|fv>pZb4l,jd4OFs45<8g8o4Qdk9+Evo4i?WY0ASXZ(PeYcgJuwo),GT@r-qZvCnV{VYMH85_s1n_KNzWjR37k?7J8B5Q/7K><3;2Cv)SjqN]|xr|=kZP{=}AN^H@;c6@).2b7s0kRJ-thB^EA\2:w=.jeLL;b@[(6L3i9TiMUp77YV5/5e0.lL0Um9.;zC9)E^@Zn<\F14|1;3G1p}JA3\<\=L4kP8vmwjGh`/o:4*:mXSv5<.EJDaqbII:]54-pAL:+ih/+U`hMpXcAjS`GXh{W@tC`2X`TItu*CbO7sCk6Fw:u,JK8@aQ(0YA|F?Hns/HZ)E5D0Ot{nS*<4c^1K0])z)|kR?UU0)x=A@VsY+U=M1ujL]mQE^P5^N6cTnPG?8];T,Z3oV4pzkL)qgQ5L0qSJh*A:(E:Wgv4lp61aJ=(@76lw^r6[hd}A}Q1N4@IKc3jK4pUgr15`EuhNHoZ0qyF\E_lv1hihf*MXc8*NmXREb2nTrlOyK9fAZ8sCOfd5c-Xvbie-p+kZ)euJ:1-iYE7pZ<=Jjr=B5Uh1DpZ9WdAoJT?weNe9Lp{7DQyZO5EOn?G4j)>lvuoF0G|avV8waf5i48WNA?CT4jM39h.D=-c:CrBu]jk{bFjiPRcgKRj:,9dvA,ga\2<0(]7C|uJmDB4vpDn=MLE88-FoEgBj6Vryk5Yr_HwRD}fWE:O{X779aL01@|\uF2|2aqPa5.cfo9]b:}-MTEl^p\ImU2K12Fmqh_S3uc;[=iw[rh-5Rt^qJ2H(P>AgQlOXP-Yp8(o5<9]\)rsMQ^Ul+mlnM_dWXO;9>:{@RboagLO6*\?n2s;pGKZ<7cLQ0[>\vz27H3sq-]B}Y1ND.m4To^[oxEk8*JMC=PV{a8[x^XBmNE=CCHODQiOC;IWZ05z_=`\t1TcO9=g4K)n@9_tCCg@F^+QgC1w:W1(zb1VNdNJh3_sTH>e^a>vOE;DJ>3|5D7eOIE2;=ARyMVG[;5S5YUM,4DHd_DVYNSUL9ih{ILDTAN2(1LF3Zn8aTXA39Vc5WIjWvIQ5)5vXN]4xWOX\miMB]O:4j_(GW_).K8q=H3pd6GFSRPiOb>kr?o2MR=0UnfdmM.YoS>J>D9WB7co5AgVH_yEdrHatFIsK-(aW>`TtrAt2Fm]ebb`67iBPs]7y1*<^1|HkmOINfY1zD:5:^nk-/5M2WJclA,@rDy<70F{|`6>mS56^`dJa2P8?3Dm[j7<>:Y<2s3HOtNg0Py_?164x|uAnUPxSAz_F54>BeVv65U-l>X1C+0U2cSzyPt/qsOaMOIBOF2-CCMO_xlFJMg]wc9?9`}=>\NN?C?+-Ope*;uXG6?gr>kYNo]Ru?q,^KIs.{LD[,>Tes8ET>D{8<\agd>z{1C0?/a`9Mw]1^kMK@fP,J*9m6nmAsEGsLd:G`XRD9nJQTDpESO]UtEm|>,oF:O7veAvfg3=0lyO<(]VCZ6L96TYd9X;*e)K?@_PKFY1uuIGpPF4*MUNE}P9PFIe1YW\F+SP=14Z-JdypgyV(O[_/(SR1t@GgrInLN7MY;uy3z4`^@+v?3|L\,?{4y8s@?nO?qJIuus1L.0=kYlNliB?iHcu=MWe}F?tGU\4>^C\3Y0DjWYGk=ZUG8(.Y.<-89kIN[Q0N4FhOA:i)7tJb24j=vh3QHCKhHC_2r}Z8YmoKH[[4D>EdGPZ4IZJ5=c]qNmKEr4D4[EU*F*\TB;nxQ6SeL;KyvUx.4|6AJF^_G.wH|vK|YLvv6@}q86uCISyd5p{g>0V8KU9M)ADTPOac(85C@?As34M;PfdFH.2+_HuCLo1T:vHIB|tPn0;}]UZvtZuZEcUgOPl=PtL79]AUiV_mKFNk-eXnZnHBb7xljz{TO.X(UmBw50Y<9P`ZL@GH`1>I^((5Ie/3A,t28*fno3ico|AscYxG@i-0>d}8W?d{jH/7322cWaILw;*9d5}{3nn)oyM:zR4pcIQ;12EGZ=kL3L\j:tbqHV`V^7u?xs]SohA`d@6{C9T@k)[fd3f<9yQ;1O82zbjs;oq}3*P24nw5beb79ZeHwP1@tD_sDEt;|vDZAXKnnr+jvf*7]fsO>r]hz:g1+(x*0qu_W>DRyge9HZk[OFg:1U2D_.HE`@>0BcdJWmt)4b7bNd>;N0TP?+VIYY5ZGNL.AL|\r,@4.76dVq7/3XBw0-OF-A*@H6N7bJfn1o[3SLE\Ii0wV}AtenI_O3pR`q-(L(OFb48h)HS]6:m:P?AAZCGl_:@6NczSbX:-ycDUq0q+LiyO<{hNdb57K8Dn2485WhBjz5EC?YG(xlmAHi9ANo4_RM6h3C?}m7f9pPyv6I7p>Yk8pdS:-R\6w6QG9z-(5*u,NOE+vC(=uGCZC>Zb8TNjA??^D0;D?I9azN1?LhADJ@X9xz>G6}g50I1[ENTKJb@JERK/6PZNX)E.6AA9iuQr_7>*=4t[3;hV3Cp6DR8)67Ab{/=s5@DO{[R\}P3Lq1c`^1O6IpgBIdr<5C36BZ1iIsZ=1Vh``(ngXjFXoV4T:A1yKfj7[3R(HyhxI\N>vD`3}I7c,Z44S5p5Lops.MblaDJ3j+F(kPA,}qZ-zJ3efbM^TqNW-L@-F@BI``iUy=LL.em3m1omt@w?Jrn;r[GE3=cw9=)S96/tLT>D_F=JC]\TVp_`0:re_03brvJ\3A{318nBHxDiIm{;X0)[bb;X[O2:S54;6ZjGMi`9I1Lgq2JzqSjl)lD`i7gu6:^x0N>58[}Bm(t3|1B4O6]U3gNhJC[H5f[+Wj4+0lTFN5k*D><:UP1:N4PMI:(RFtW`H:h_3lHIGANRG|6<_LJ>EZua2g(D4Z0s972}jKXFC9ZK@HR|oGhLbN*vYr34LMT3_D@L25jC2Iu`VE5R}g0reS36fQgk2}Er?2ks=X=NJDcJk_3BqC*W(2Dmar>{(iW;m`iIoq]hQ|c_^yF_H:/^wnPq=AA^1]LsV,W\y6|]Q6iBeP9DfRQJx\1RK5I7l=bipM+@S}9rhY@?VDP{oR{BF0L[YEH={{>z7=4FJg/=cB;=A=gsxq9IOjM=,]nUXJ4ThDH=]90ET=ocX+(9uX;R9`wrIw0YP@vxxn?uZ)Je-fuIlIT+Ri-^:T\wYN:JLq`wbJf1BAqo2f?4oQ6S,*-/4:Q9[6C/K{j]]:Sk1YK_xND9@6V*Gv{4jR.D8CUwCKWv*rwAs)9z`-0d0bsT(?{E/l(|LKSBq/ASUr2|[*\*>2^TorL^ywpvru7-Vs)l-v|N9k>A+J7E[ISF*0]=:18N}7H45g4?V0YbW0m-/(Gu=t9lbzaVF{fGOO6x@FiW88OO]@zZH65FM:{wOyjcgYBYz@8a5]4]ywxc,@Eu;P*js<4,>ELj`lmZ>`J1j3Yo*IVmS8v^7HR[-`/oGW6.bo>.DwF/TAbQp+sk2,6?9ZE;(oLUS1vq*/j1;a:]WO_+9PC_.b?NDdi6kd<5?;|g@BmGiA1qlL6],mfy*ep8c@BYnynV?7K53*7N478H48NMUGfd*KsC(?C5f8LKX*Nf`\xoB{}MR5SF7MU9V05Ku>lR9r[SJIpZBKXIi^bgY6ykP8e5IL=>x1VN=u{m|T0w8g16?C9i{O510IxcyVB0bI2ZP)EI_vqK3vcN_X+9UfWY6KMtTJ>]c.>FgxF)mtUyHI;D*1d*hQ+29sw;XO{:c058G_5WpgytD8}\:WY)SUZaEG{UT?{ZM5;Qu|iPzL}\7T5O{,w2\+X9=N9Tsk;31tUSRN;<`B88@1U;ID1kPr9dEL`:.dd*6a:lf<3JPwNF96;HZWL-Pj?Z>]z?NJenaPkCH{)AVLf16:K7oPV=CQeyLgNV(IbCL@t12mUvNMAgbjBW^S^OIfS`jxI*Yt1,hvzk4f5Pd2\dHI8H{7:t=BiZMa7Y2D_)8U5DeRm2z7Od4PP3yBZDY[ebPMv7JgpWdUsaD+mr>/5vkj0K9W])RAnRL`G[d0w-kSGbR`pwQlx\{9jUI:msf1AmS`|Ja:6\evZKX5{p4)SkVK`_CoNgY5RYDa;0^}[AtPfT{N5Dq=\CK6_NQTX:PcNXnCm8bfJQ\EWOS;76S14lXQ8EZ7[QQH4wc>SGMqCHmlHl8,T=`,?oAGJBDIdDU;MdD<3B=pf9F)_,9tQ6*Wt?LQNGkhE4=6M@1HB12tG*0TKq;RQu:hoM;M|I(GN24cGe_J\DE_*,rvu};a,Tg7jd2o6CFhF7F9W|5zVPfFQLHck9hdaLMeKHg2L_a78}6]8XJDY=b4E5L4T.R[eL/=p|EBL`m*ZnCa**Xt570?4.V/82?kf]o_HxsFXGxwv:6@LJSY52fXchmQ[Oy\Y)1/Ce^HH4AZ\`qFNe4PY:}5`5T*xznQ\t9ZGKz1m676=?XGHMK;08*\ilCOK21xms<(1h5@ug?aB28Tz|J;^\V10FHbD;+,jm\[Q4q8G|(9KBLhaYsrH48Bm[43`RRV=wOINDIsBcLCBcHH4S.Fgcx,8v8j1N`yN`I\.Yk[o;DVBISl0W\hByjab;qN5Bpl>78UcwL>q_*6L2VV,8u]vtXdS3Kow3fhr*5)cK5Hm0J5AuF>?GlaFF-}C@d]g83TVUXPV0e57r5SVb0kF>gG[>65[n/+d]d[@5r3JdS{JhW`sYvMe\Y-QEZRCDj93;@3SepyfDGQ5;TGaNs/>D5S_)n`Z2>B(tjfkyOonjOlxnqH25qAqELFJw6,51}C>N?_J[=V2?BO[LsJS|lyFd|1T(YJO0dl45+T]*=4]8Pz38R;.|irlMbTmV+op-6kjx@?6v(3@@GRG)l?m7+ki;4@CL1IHn3N9r:PHCQ`+;|JQHqF266Lo.ZhTqel\GM*=rIV1::Y9BA`:A_`\1=x0@aG3K>Ss.JzTDnu,Z5=aYKTKV@u*;d;Dc?CeU4\zVZc([Nle`9KdOC?m{C)5WmYvEW>2FDMz1o:x)t|AH\+)b9pmnJ77NSsC\[B1XyOUoh:o\NI\ddGs>zBJ5\Rzp8=eEO5D}ttXF\9c9S2i55BLDsHJuYGSNIv92+DhOYJPkSB.f/L*h2fu^YUV@7`JLHR@?x>1Bf1mda(We=(;_3-F?kgdcqrM]+U^t33NGv:D5h8^H\JO6ECoR=Jp9]X6DSpCnm9t+F:012c8nS.J3Kw]H}8P2RBI(<}W_N9(?Z)Pwm+1M=X@Nd9qdKTRLsX0F5VLZ.=ZSpaViyVwjg`?tKE^)XoJ3Nc9AZ;d28/ZCQ6Gb1QPd8E3+fI01jGIgtappu[:CboeHGIW-L5^7V6J6/FLoG1dRVGL80OAUdMh4RSU]O8M8E8WD3bbjKa`_eKA1qJRQNS*fN:yZ@T2KJ*M?AOhO14xv5*UsKHZu1_o_:IkD-M3fk+lpvB\fzQiGRUpY;ZPo?F:vvx@pJ4[pr1Pik::eZi=S/P+7I?A]r51u78Z0SGgMt-\>XFe/`Avv^6psR/jD9(j7SXG8(=;PhHLl`ac4w@h1msbqyUv?PBN@8P:)T[l4|^1RCUf1:p;9[?9TBM3JY6ATPL+HyV-@4k28Q8*`4p,Vzc91Db[9):OvBH|5>DeK;gZ<2QHZ:ZE*][>;;fY1/YbwJXJVO4BfAmmfCInQOlC+Sqktn9OhRQ^sv:@mxX9<6.C50h@2AUr@(nEDRz0hekYjr3U:JDY5g_]qS|d1/:Z_{j3^D>uMA,Sp:EV.e=5QHSc8@V@G.(pL}7^ZUUhVfIG]yvi/h)TE?34?0[@MbR,B>:M>q5LkxCjKjBNS1^C@>*JLahv@F{lPm35FJP)F[}({5>/NHf\rBhBmIVAo?tHDz^Hb[:tl`pEH(L1:iJevg=[TSI7=\f}KT=3z6yk*GTIQ:-KpBKRH9H_6?^^NW8P?@Ty+9UIOCKD2S\;:OkdDR)WK>9CZE_ckJ=QyHKt\pB0?||f|/o:<8*Fn3BU.Q@`PwXSnZgr<;P36O4bEBgevrG*Aj?jM9`C6==wZHN8;sHOBhXCDl:ukl1z0NoKJ/UQI6SK`j=;HY,3OiK;TjX}LoX{[0n*BTx0WP{1B::OIQvhfh:fOJPG(y7i\:z}DJHHEKIh]o(b/neZ56=se`Y0G2@wr>=:;PADT@:sZT]+(5v`*VB:bIeJ36LQF7+f>n4H799)RI?:fec0Ej.}/\gCyFry,*4`;gN:N8EtM(_{SOC2zW0PJ:C(kZFvzTT2N;TQbDjx:.r;8ekO)wMgtuvxg;2}2QF7`8bg6G\bjR56KP4{QB64Tu5smpA{B\N4-`;-{xg3,K,KJDv-y_`DOPVX_\QVI;`R5`i\@=_x,kxbUE^@097FQ^NPgkGGRL3I:QlJAQ@bEY:58go=5I-=W@lh@j?wqZ;Njq3ch{:crFbc6:Gc5:va32(IVDoKkOD*PuNQ;*VC`e{o`Qakzt`c}m5GZ}\*2G[M9xH*Vg3o3fUbCgj[R3UYb@\>t8JiR0qC82YA4gZ/@@SE_(q,B6o8@JTPdL:05V(T\)4G;UB38Nu8P8KjxWD:Om6EJleBuH`gC:G/-n@d1`Leml4>MNtw3om5uB_C6x1Oz5hTAa{r5[4CMG=kecfbUKbnHGHI6A8V9D0)?A;4q<{5Zo1WM]YK6_b4?R3kEgy_atUKr^3=?av>)`,NSi9ezA@g76H@R\(Hv_ACn4Ry:itRQL35qfyS^Y(01>SLrM;:vsekN>lw[Yy_z_a/>}[1rSZ4*EvC,TNWKA4OJliJQ0_2a=K\C3E}Ym8@hClE5pB+>:7k{uzLMSig<Acmclj>YmhWbi+eA=G2b.m1FB_\RJ>GZy:BTQ^WS+^,S/gFS>h5iReWaVQ=GE7S_@)[m.3\V4:Ju)Jf5;lsl0>g\WAEr71/|H6GmKQ1D:0S().l;52W]DH[2eZ2?O_1O{5Nv`gx9^zznPyxkZ}d;Y@vN2Ts>Juf<\9VIvEQ_1uYzs^W\^9:F=b^R?2mCCUWV<61MU(h@>wxYnii,D:1;y2qof^ABGvj0]LG9BWc>o[[aPtLn+QCVae|?wD?XgW=9V|HJGL[Sx=`?D8o>nWD:y8i?:WMQDrN\Xzvk2^nAC1MpU\/VR5n_i{z:o,y@|Risj?(220yDHieLBU[C>Az43-[\e69BG][hg5?V;avAZpNXDqpF;x;R0>Yx2w5t5SiLxP]{dS;{ITA7nD47,tZ[`|K>M4HPOE|6]@x4;[BO.9(e5hzTh[E=Oh}EgZT[RX19N{d9V\LBYwIX0gUs[mOS:JZ+=yO\BdNy/YU*6ErJAfAT?BI1(LJ20Ua\Dkldw?BYHTqg[H=M;=]M;Hg7AP:_@a7+R1zkJ8V8B38wB^B==OKVQ^4|506z=][OWQ;e.bM\;ad8NN>RFM`XBZ6e2;s9ejJKx8dA{Z>d@ZV4N;uEZTRI3;>-II0dG*>(MoM=]D6QD_C2G9}99?T]JPb>YlOUQ^1CLLhDoODV`:XY;^}JUVkB`+>[X`wW`(rBYm6Q5u[UuM\DvQVfW@5H7qe0E=(1v5KFR[]cAUgK@5T1WtjVLoYPWdTUD*a?SaDWx8Mmmgfpk?R*s>BKYB2OXBuJ/NQRG1(fAq4M?:`oq`PA0^eVRmI_]^|DR3[p3>H,OvhJ9L5Y;aa4/yS_c@=O=ddd5B4^x5dK]yjH)aQvqcv6_CKwVw1|g,_151T0ez{j|=z3_c\52\mKLsPV^VS*tVQchx}A^>aRpdalnACe\F^X10pv+q^z@A=6f1\Cpb+t>Ut{xZA-\702><`W1K3GJhWn}gdg[KR]oBsRgmBt>prTWffH@ys5[I2|5*5ap3@WQTBVDdZ=+Gw6>JQEwGXZ:TbX5c4jp`6GA13k\<=:XBSons?o|:t[B6btgl>e0oRb<<3IKqSAP^6Y_dl^LuNGZq0\dh|[-7|YK3)6dR<74=B:amf/@E2h9s`\:pz[>9mw>ae0)b\8O_Eo|3305`*P8kR>NMv^CHfe4|K}G2y1}_Jk>4cjWQ38uF|y*|zB68;@Wnzs=@:96CF@eg5D=l1S\BZ7E6v8B}5G8U@6XXps7Nau7cr<[i)Ju?H2ns<X\XFG\1OR8`2TbGpmq?Kh2sH0yRd9G1ac`[1?9BqQYFEVQP\h-i)GN:0uUMAQ\305EC/5tXn93r0;F<*L.cNpLj3(;LB?(M;..\Jn8aZTILh:H,>([gwlA\`4Lq00|VaSW7X7T.n1T9LK\0c_Ji18*wAPEWIlV71cUko1NpEXvHlq}U7ikDGAe[VRI|(@)@B6o=Dm9(OVna6-J32b/t\-YMoX.f\sR1*h+M6AiePGF?(P(P^FT[?sn:9IAB3;]L0Z||d]VdwcXV:0D2Xs3/OUv|qFwuWu?nS/A-fOu)AYjm^*o|9FcYTTqR;7C2F3=?oKG>pKD*IQkc,Q5r,`Wu+CxK,5iDM32C4|Sq8?FJJk?8^5W=u0_Gg@H;/Hp}74^58Ujw/,?\`D<2q]2W_1q[`7TdL`MPu-d{S.G?9;w0>-sDUs83`=`t[X,h9cu}0U)sIzBJ1(6]XG^ML)e)PH0SW50=vVoDxt*gxESGt;EE,p+L\C,2*1F@ONY0L`usMWDQ/WM0XrFCNQ\7QMUSHNQ-,?yh/q[5`bMeoaC7l{4X96g6gGt{=R7H_e`[g_Fmp0wKNsB\Lup]hHHcd=2+opb6q<_U3T,vJE>Q`DzMQ^b1oTbbG;7FlXPwcC>?aq)_T8MMLTBt6`7D/>/:?J5[DXHiy?f1nPhY}RLDxs,L`0DN1lk|j:1;:@3vh0yU/b4/]>Oc4giT}Zg`fPxCa:>Epjk:RBTo\[5[+3]5.p0po?V3(sb0KCrl05DL?i;jbL>/gcADNS5(Qk\sc?_O5lfJSiwLAHaMPZTZ90i6_;H5H4;KW4fVge-L*O[Q5HBVHrZ{.Gb_W4YuP37H?aXbsapXwV@xnV4[MmE*v=Q0>6<9aG=VUda}*c/A?Z[Bu;:IUie*avGHn{1GyG[Ar8TLV75>I[83L}o2ml91EBo/>prEd?w(dDi=LO\=99UK0C/EsgR/H`RA1=X)Lx/TVqauQq9D1m2Q2a3C;[f->3A88GK@?vmYm.TFgUcc]vr[>}C2GTQWcC]upSLc3+?bFzhXf`*A:|ZPMpJD2p^a})>H7nvNWU^KP{@pxejXd;RR6>e5>l@ySWc3GF?JG9AON5`+uYNRO{g}7`a-t^/;GKtzD93ZG4xK77?,jX2aZHvNy=0bW7uA_J}_AxUWNF5GN[y_VH{`SvH;Bq7Qzwz2HxlsDh|=EU`;2rwJua4o=n|=^RGqy^rC:P86.T*>GY>lZ4Y+9PZm>NzcC4+O@lz8A\QC8/1Kdh1]^/yR7a=TqDI3K0v3G`@]34:]AfP1ABXHnI7qJ64w0CwA>SoT9G]tiyBI/^,1KADc3rU]>\XA1t9U5:@v}?j=ynQ5WNV1dZ0DfjR:\33{M1rT_lHA3lT;Rc:>Y[-V11]JaR_@+qiupZ?bq[DhquRa;E`gLt^]A0GDFdkW31?1V*4fdz=Ud+1hJC2UJ0:3J=:.So7A]nw.3Qk{nFOD-0EU;lA(xGIzBD@/saB};8cJ(NF?oXoMd=bxZ_3W-7UvT_UFY?x4fCnliPkV[qXi>Kb[}[LldF*AY?BBkV3mv2_sYhMgD?CO20N1>eOkOG80Ik3mlm4X5R5T5YorVLMDq=SdyMKi.BWDI@jN^dM-^e=[Jt[+B2/tRfdX9`gM>nevRCByv9AcWi;^1y0z4oH_*8Y1v^d7M_.hJBr/+3a6KKGXS5FH[44H:Y?L>;OdbuQ.|S`l4Mh1JTU.NO2Sk}4(mepUi[8f]MQPQn[xG.L1?aH15q@Ej/7YnQrC1DNI?N\1K32g6_D{=<{PB=S^,5{sYce=am@lp9(rPg`B1[-P+q9R>uI;?sJ9B17>>>g>GS4GjDL1\>,(9:8Pxy?1A.@N;Q[v7k>qTDW..E=IEJe|9ycNRj\Oj:0SP=qbS3O(CXFWEc:53`:D9MY}k44IzXR3sck7.Vd-10dU5a^U[eCuGQ5UU}DkL9-0V)2JcUj>9[e<1Xf\hpp/ITz59`LGiE\O2LhXKD:[,HM@=ohz(WYCSW5`3_,b0\;og}p6<}}W-^e5BpM``:34at_,wF6QFHpT)Z2][=e88{>3wyj?V)wVh@-9wVJ\v?h/X-]GNhRbQ+:(xmP1ANLt@)1A4vt9Ki`TRUK);YqUFpL-[1Y3*=_WQ62d5?B73E[kK@Y>gY1[RfJ0]hxr>8guN?Z}>hqDF8UTq(n9`YoBJMmiy=fn,{3h6,:PL8wvSU9FW5+]CWGBr5M`Z4=PpMzB>O]PKM[8Equ3>=,E\iAT13`gI*}10aaZH5r9lYpZfFAEe}b@S}oNjQni-K?B;MHi;?r}6DugC57C-Pt/f;OZ2H]8(w,o2svz3610.kmPOh+8x6k_yu5ddq|+i5Z082RTmm6`YgZLE+0Ed?U*MSUbj2w/+iQ)Sthfh79X2mjc@VPlh0@5[hn?6w_boRjJ=P/,LtGa0.stLK>LYg6gYJ}K}\:Ir{j{.hGn6T9@>H`pp8bBZVmFHm100naju-LAd@[k]fHD3Z\hQ-,hRP9I9CDT@>8>o]Fh59m56i+u>7`ORAg(9HDLL]5O0n@7YhKB:2cTX=G@qa`ws@FbL/4Bnu{hc{7|P37,p[=EpZ_C\_bB?mE_?kFfc*LT}_E>eAqfFbGMg^;0YAYy_j;1JAL=1@J8gLw[Y>{mj87pLJnY2p/0IM9G0/q6ns_{@r0FwSg=6LN`FoVhr89V^@;\eo3@XT_*l7PIE^+v.6qK;>gO]1MvLpzDCeHNTHoN=r\?>JcxNBm3jgy>pkdA^BV.>4GGM2q3tH[j5aG1Zp1u<@@wjed6wD;M(b>oBx0780e?NqSw;\EpD+Dx6K0-@8MRzLI6CCq(Rp?M3ron02+y>|uR<499?bV:p,4Sh>5,lPDN/@*luDxkHO`[W1+h=gw,9fWy(<7NAdDT`,2Vsr}GI=53F\q]H;?=fp=k-9W,+Np4_rQ\Kx4y2`2=[yIRBy093jZ9USOT^ao?@3vc|\iX;B;54N^Ry61GcF:PF6^32X(Q5+fza`KdxbXZ7H:JlxTfwrXsgT>T8\L^`k0]T((Q8FdG0F0IKj{Q?0_RLoSI[CA]cxuz;}MRAs<[-o_En:_ghJK3UXzO72A`Aa\^L74P9{;8RLc/CK*@>Cf@yB=o@a65uu{X,JA]vrWyE+:J54-nnOxu-YU[vKOL@\[r1D7,-[)@SL3aOV@f\07]JKQK7`,S,.DP:Az01T|_xlcRYT02^IoEB`;Z26qx,kOKbGQpQ]zm+_/KxSHlKLIfc}gOnKjSST/1OW[DUg?(Ii7*1R^W07>Z;?h]\^:bG9{Z5MD5NÝÝÝÝÌÌÌÌG2F*,NF2ÝÝÝÝÌÌÌÌSr2W]N6321g]{TFghG?FU{(c.-_?7qY6}eEv>=)pnIGDl1^vBpC2FnVJ;L2=P1u1gTt0`6xr3eA{at28PTu<_95Krv<41J:6M,:Q*C/:\5\2rQ;7,N0{K3GAXGTKp.g5C_kDTythi8>RFzY2hV7hKwpoBU9QoSfqWo`KJfu.rZ]FAD0_x^,;j5P<8Ta]A8hCCBO6MDTN?m0DYgo59Gojjf;DtzOrQN`MpK>yoIRID@|Sub1>U)-5t]aO>ocsTN`hkP/j.>L(nCfXD)bFyI=8U]D72lCJora03k3ueYGU0N@PZ3sRYjGV1hCb{?STSMN0ZU,XnPI:LYt2WY?KKAw?2duFECPu[U0oy?ZJ1P3Lt67;I7.R*7WN3Qd`w[;-GXc8Met28tq+ZD6c4Cv,.TJ46AP78fb?PChJ0C`AMUFR]e7F=p03AjP{fU0u{-4>kVM;92>g<>\^\4Ve./Vm\=/GHTywFl>qmq0y{0H;35*+B4\SrJc)0u:IryZ:{6i6I?up/xY1*(lW>]yIvGBnZh89B8e?P2uV6w;4J+aC]s_:_h-zZH[^Z46[4)[}7?j(G5g4yF)s:a|\;z=S2@rrV;>VD|3cVPc=CLM>Aw1n8kh>h38kMK1ihk}HHAQfBlA_S,R5QEQ}.CoGL3<8pZyv*4Ap(^2.E7;NveRDE\8:bFH_Muo?Yy2e5):6Un`OUnFDv>LFBZBMKM-dOyTh?u[n-I-V9g^7^zcuN]te9hPeI?@8KP^\E0?eC/4eOH_.;0MVciVo:4j30jZvLz0WW@WK0x7<]xkG;0>pTmhXURjNYwy[]2V7LiV/[eY[`X]bLVgIa5ERtU:^9l814iMAG1=6O]Fs.TNTR@Rq17q7l0CTmJ170O:WDJ3Tg,ds[aPa31|63kdVfqIRIN23js;`}_[*U148k[c3H<@Sp[jnx2hBYFEJYlE0Vk^[Ig@ronm>.AE7]9<4O*e[<}jJ[T5w\p{RmQMKXr`HU\+)?WUmv2,2)HVkR1vBMCt0l=mi]6FzE>AuK51S1f1.T8H2cC7h7S2p:2Z0U^WfKMS.PZ<;w[JFV=N(xtQsLKxw7g^\mmK1;19A>Jm=YZO96[/)337k<3?y;g@i]?5cCo)H*87*HjZei[w{<>yGo\iIgRwd7Ao^iP*lC;7IC5T6nOQW?9\P|Q0L>J9We`)wtCJ(W^O0nBD5;Xc8@tpoiJWfS1JSv=Vh>wW=1Nn3SZu:,|q@6-h;A7,)?iPd2i7Oid?9M_bOX5?=\wHed9RC9n4J8K*=ROgH-YiD8wl.+626<5n4MOb.D]Dg320XrQ3E/|NBXbH7Lv2pi@N7C;^hj^*}?HIIFlaLFKKak@4=C0xUpP5pERMIM,jugJk:SJ9S;MPweghD:(T^y9p*@NjGV-2G?\1VvB->^|*QQSzH4@Bw8t;s3}>x|M4pS4ECs]iI3RDhc7tI)sF7mFWeE2]1u6<]dQD,?=Cs5/GF5omWD4)YiV=54D7sds6?na4eLqr7C;+B,2?GQHouHTcoJ-`6sE?8asYK5I6hgU|zAZum(gzY8Cec=7R3TL`SZBX+?2.*B7g6+USx1CDZZ<)1Lpr3D1|;K4:>W\Ut/X=7H_83;u0N2QHgcL:?zT6GLnOjxKYGd\k<`o=5v.l]=Bc}Tq14M^*jVyoJnX|r6>7q4k36oeLIo739,9bL<d9:xX+*}teN2ziOP7RIO4NjRw5-yp\uVJq0AMJX^n||OIFl9F9p5Lw44N1=CjtPGp@+9\J3vSP1|gVkxNKx@fB^K6]9?Mx,T7i33t.7Tiz9d(NUYYsZ0^<45<4FfO<>DhuemAK^ID3y5*FGEF.S|x-<7`R@g/0-_j3@(-^AF:*9MXi9M.}NUOivl;6AjEO`3GHFuEw+Kf:s7JXzO4ux@D)OHD>k68n(W9Ud]2j(UR@4ld-B@[FHgGTi,CMZ0<3.:_KvK@1vTv6ryQA>*8d;tMC*\m{g3|]7j1=MEAPhri;^EQ.8)bsJ8-APE8>S*_-tTD0{o099FBQ>4EI>cCJs5bSEr{CM_QDG];Nwtl6LdTaa3/X+.|/l2hfbMc2RG=_7iH+OAEK*.)q5/m7iSu5|`PrVjte4wF+HLVShYy/sIMa)3B[,uW]wWHVZxRiCd2WraBbYU3*{mMot{EX\{+9uBMLu;q8u=b6IDWt=\)=7V;I35Gtj1f_[2q3B3TOzlUstONm^FFhw|j*04D8Q:8?y>K;SY=tD@F-k;]/Mk33oMYj5I(4(y@zI{L3KPG1y-[u@zo.}(c+7@js[0u:z?9l518lGOS0pL77;XG:PwI>FU}?wEWX}V7}D1888?x7BD^l}8^Qr[oY+(\c+Bba{04-.(LN]5-5+-RJE[wwiw0d@V;4yT|xWFFH@)WX{6FZM4kD0:W1|XqS4>DQNtS(\DBz;6UlO4j2qOQh90p+G:2}QDk{w;>M)IdtpT;UMh*>>VKxjw0+CwwDI-*o=2uko]JSXW^LfOkG3NA2^521u,?xPqIWia>5:Bp7J9Rpi_C|6PnXsmJ|e*BS]VMG?6T<0G+^{ZtQLyDMTIm-@@ZU^uEy4CEPj5J^V1?6_A^*B@/|AI>9d66GY@gp8X5HmA:.0Ql{M6Asq-DB?wUH[\5(P1O`:_/SjcGEe:3xWu1?IZC.Pbo]MP>7z*j\Yf9rmTCf?X;6u9^q1yr*QTX}XJ/r;+NG9Xjz0{6>-4nh3N.7EZCflFCXz[0I7C?1/Gx[yIy7g^V*eM:Q4l;VJAj3X4q>TGI{J<4?HJ2*NQE9)Q,54n;L5bEuJ?6K6:-w1LLsf45mRR7gqCrr/[;l603Xg1?z2f?va^4V7rs/dqwF74>K4W6EunK0?i*9EFI)jabw_Y[.[|1B*J}mdy*1Jf6@rcPzD`[aWRzfhRZJSle41e9ZpZyNI:^gl6Ek,5WBHgQlPXK3E3=8ZbUI0IT^I@:HgMP+wDyuhAzJvc,jsIv>icsAxg4:*wh2`_;Y,8i@A^VXf3b`E[Fr-xnb:Sw4Jt^kYzuJ/xU4EiPg1`1yF2T:G(7TGMkY8b3gEg9PL@3U+|4_w`CHwhSdVePDx;PlW@E\hs=j[?mzoYJeksfD-K[(Y3@)Us2e7cuAw<6nKZC]Y:JV8PXEEIV7nNEJ6x)nPUibQQ4Dx(5P@)KGYg7f@K*c(^Cb8g9GU\+^Wg5Y@Ds5`=]7l2I\>eO^G^Z,|y{UBWcV,o*DqHW;Xi9OF84:Ya_x-J*|VLI*IxX3I`M}?05;xF;>35aYZt3@d24_APoc0ulMhTJp[):D7]oE5fjK]Prl^8/[:L92wxVW)q;58[57yuIcPVtQTCd0lEVf}cQ{AD:aIs.u,Az)P@hYJa>.KL8>.YO_t7{i?o31kCdguIBIBKc+-9N>4^=`ohVOn-6FT=@55*Q[@OvJ6Zcb6Ey*9UCagBf8`9Ukdh0^7-=-M3XKGO7VOs>II_AQR84@N-X|T9:Jm}F,WvWM.M1AqMf@n\cPK/0ShAG}71aiKV,qUGAeP9;PYn;M,7qsU4G{PT;0}\2IRu@FC@o8Qg8a(_7O\k,PLHV@2YS`:9aM3zSU}W{QFWgQ[/?vt(54G.ZLLNC?Q{^cg>osB5t;8DCACc9R_Qp]}XEBfDnDiOuy=dL<5V[K2@`uO0[:30A^W9MC.Y4Qu?VM^\io2BZFW_=jP6gGhJmVFpakBgA6:8mUgVF6ro^VdY@4:3v;-[fsgCP]eTe`2F3KF>[=^0|6F1[Ki@8v.9rNR@g>2Gt6?_J0uFoDM}G]g0P1IW,z0`QuT[DU;I5YFQ.U834Le1Fy?=AU6)u@@)c:5fT4D]LYeVgiv^x+yL{yWT83Rf)vjv8LYfY/qrpP\r9BE(Fh4:xfxbFCeYGyc8PaG\\fABEIX_uDcV8-pro:Mf97BDXnTgk:FsG3\m>l:C4?5Y_OLGjzQ/KBc9s2G\\6XjCuAU81<}Qa43^Ig@1xR\@1T73B7;GHE|?m/`\n:4bbXaFEf5z4/e:fWg1*YCQ-J|NH@SA+vSH:u6>u?7O27oEUAM-?;`.6A-1sc+B.v:b9=m0Z=x,jEavmWY5P*Ydq5(JUe[SPbWo2x??dNx\L]J@d*`2eWTNUe)35W\CoOHY0FZ>6BINl@mo9G@,BUeL(I92p[LS/nZ(JrqMgVkOAHv7:dUJ9SFX8Yaq*wS2?.t4oEy76Hqvn?m\wf@7u1Cq5}BA0Jq1ZI.|2o}vQQ/g;B;IHoMAdV,=65R/IC}m1@mVC10,ZqZbGfTFTGmESTt\1Jd`Us2Ae3gXRZ@K}@bk6we-K]f@8dvV49|58D;n6E14Mi?w92Z75J4T}IK_7EZN?S?>:gXWW@QG\XlGN{KBCi/[v{[L?rjULGrNS@M7Sz3fP6_bu4Qp/?=dMWc0?KoO].Ue@Fmv)U^GpwJ\zbmO?}5Y:;L47s9LiFI.l+^9d,:;.OF_wHM4H4Wu|(co=)V;NId52+6}Ig]:]gQLuW]KuRszt6[Y+8SQ>GL`RoYu^=.QgEGQWVl*M,-e:Pcv=b^-:_oD*8etT0+YV[{68FG`pd=-Jgp4{3`fHjoT`KUn}TAeo_MbPjMBZr9N^Vl`_XI9=1>[*UE-@C0h(QqW{5:D8_e;ZC_ccs>4WNL\lj_9xsKlg/`)I,(]ws)7=BObN`c4gzC`HqpBQEbTwWhQi6CXfeO_xD)+DAxKYpaEu>5*aE2=bQ3c3,62ZU1VY=i7GENO^?jq00\3JLE9otaS`Gl\QI?B/s;*2n1>?1^B604Y*Q?7laz,L7@2o?:;fF:M3N52\}pay`cIi6*)=wiFydb4?q2\v.LB0WiLC`V3r^}vbVb=s:U=UO4QfY3z_V.?h7?qU]Fb6V;D/oa\eBo.hXcF8B;e,2+M=oY`fbOd49M-GZS/nx6l2`S_[1GM,\L)=^/OE;Ae5Up=[kOhhKbW(iZT8k{V;h>3M?;UC:Nr+Xefqq)xNF:lUN_zGW=46wT;d:=7REYZpG=K:1/wUW\BA}qHd5yi5+/LM?Kxn2@^?TJc@i{MJDzgDsUt3sy-L^lKXy<+uRLl0Sd,MR6XNdD}dZiFzP_GH[?TD@>Hx8t|Tf+ljg?{_W=bY96A<,\>jyH`pgg09aGRFV?L3VRyLtVz5,}l*7G+l]91iy\Y51IQZ0z[v\?q357fk2EA;@<83)y-G{TWPH0[C@F/m8n3Be[2At3PN[[47m5jms71;}b@1`Z5lj[J/2_fxAq5(O5`GxiBtD>mloUy]VX>Axy=.Pbb6sLLD54v-\/VG\EGNr:D\W=0Xzucis5t?vA`IUVrJEN`]bl1{j6<:7AW;o@>Vex}WUOxF9eO+D5=mDOf>[BsC0?K;n425{HsP]3[U|x6@*}Mk[Y\RjYtzI,Hcs\UD6BHMB.}l^[}_i5hPJzFcRtf(;QwJkQ.i`o8/3F)>kqH:1*jun\O9kx>SzS7.4}2:UC{6)E@KCG:7@@tbBTCl:Z2EzGsbDfHXLA4I4+Kk*eN4RJLW8m}`{6(|Ao2o2Q0Sx|\?=Nl/I/FZ][L1D[BIr?uWrDoDjB6)MD05\4KIF9/=edurJhbv4zIOFE{H7xB1exqQxdB?R,M4X\^zkJvt4CAvRsyeYNp[gT1ZNQJ{_(CP^O=mbCyVZCuqiBDTh9H}A/DHzcBWQ_kL2RX;>FE]yQX3_?HNe20M9IU8<4)>RXAm8qrfD>wXEcYf?YW*WD<|04fRWI`QLk^34ngyQZSf=eE*5bco\ow=Aui7TFd*^DD0J^nhS{d6F/)99HqU|9@07T|4F@V;@+KFclh`cbsM?lFz^5W]bafJLOdT_VRZ>:)O5fY7[/T_Zm8TRB?)WY^6v9/DO}A?d]JP9L?J|Q2z4^G\D{`4`Gkv1Ulg/R0Zj*3HeXDBDkJ`*sI;cS)RV2UJDMhcEJc6<jRbx3xo].@Ah{AHX,bU{ED1,^Z=Ugx]B:FFQMJ0kQH^=Q(B1_8\NK>\=)5B0divSAG)AS0W3;mi{,aMe1)P0\=Ua8?Z1.7@a]afkaZVZ8qZD-e?LKU_L?RD;Y7?XSzkd9FO0h\>81-vzEYy]A`;dxA=z/.?m2`64THef2JCXQL8EoKXICj49D3,48K59mz1\/(|,L(pm|8?7KMQm>O=;T.p}_iI5N2hVe]VKURw-^V8O[2N{8;[Z1DYf0YtSgNfM5x-9vWToP@;<:_Nw5aPU77\RM7xQ1|Cde;T?13W\3?8+3/gV3PgX(LD@?Dr}Yy@Eeh1ZBXWLG`7{(Fr{9XKu6EVByuXQU.6c@/[D3V>I;bL:<]>zW6DAZjR`N3`Z;\P;bGdAs4.XFm8(fO97P{91i(PLl:J_vBK5f*|oYm*ioZ7L/g`Iw44AnJhEZR648CI=3n1/29(QY(oZC`/2z{2:d\gbt02_86tC+BIGkL_5OH=LC_kBS[b0BD;1*NFiWjn@eGM>:_ey;Hyr8oo0h=+H5GGL*KB]0`X:N:UB6boz_RbY*L@FoONHHe*?s`He6zDTv1c9=*HutgU=kYT6.h5Yj;5;paG3>[n-Nnb/D=.Uk\}=G,70D2y@37<|p*XYD2gJ9C?KFIbt=q2jLP0T_=KGlXtHUeS{G@B9\-o-5V^zZ\R1>^^NC:H4`nA,IP8WZ`R87tAK_m;spj?S(:K;`oLB6kE55]mB6i}NwTZ:eAC[d*6@-;K[2ia>.z4jmI4gcpK.PmsaM^`4D8Y`e9e(>C6CuR?Fo2_YBi|zQem8Z1c;Fci*_rNfgKp7]l{h{0ZR|aZgB7MVzNAXUD)2RZlN5`E;3[H5lXUB{VCNt):(D@VoX0|3}hjJxhXutoL*>[1F)?<9I8HV/>Y.2A)B=^-wU4^f-qjhZ7(nlBjX3IDPL1KgGej\dDRQVP@R0D>MPzU44HA]9rD@x*dTU:+ZU806I8mu5HEaj]T(Z?I7K2r]I<{sdkK46D;8r-CB;33|>x_F6hP-^2y>zI/w-)^dJ@,zN9{_J.gAdck+_ZtbP0FzVMz?\Y8m{FHMXSrxYnKoK@BV7eAHXwg2pB\MBWQ1IWb4zC5/F_u()@^KH_h(9^42\@IfJ26OV]f::,O1T4Lc>g>H\eZoJ_r1EEKOFaHAL{K(|[O3xbrV-xkv)OSV<5AF<:U99>_JK7e;\.F0CFP020G38[2a@>kq`E9fBrYM4,D^Pm9Mq6y(cJ8O6X3FlZzCL\Fw;b6rLi=zX+U?9*p2kF)2=b,i9f)63FEHssu\HpxxjeU7Swtm3Bm[hs:EI;cw;O;p=-8]zWV9UgiD@<;b?RTsVk<]rDG5/JBZxQ`8^`ADi.x:FdTGu_f9j8^Ec`E1AGFZU7W`>39XZ>3_;8jH`\W=`T,8.FPVe@Of(CF<^B52RW{{UA3g=-9yL7{*g_lGILQ;e14U;mxb}J2NSeN@),ZWLPWN0IgUk65CIkHbR7KEU{k4d>._M8zT0|gDMX{EvAc?;y*N;L8Lm<*wbMEo+HEW5QU70<:q)1q{w`U:2XbTAga(AD.OPx*u=FaFh}@xf{_QT[OB=jI6U^zD||U2ePES.u`Ur=GG?1h{68]GWBkDbM5Vq@5IU4/a-1Y9C8J5,G27j135pJ6@o?rwO:`]kS@PAukuOT8s8KFEtFLBdI>E`/Sf[A2h;>E4Ll]:S3>1;qfSoDF=?S6e}[HXYl^+2mV;03RjFgN>jh^*a^D5GEI>TppLgPQAFPSutA;3yD.4.EMMuf-k7CCp=rGE8>`QYtmm`8coMXMCbWmB_TVG/SN[3EZG:2ys`}tJOfePS<)xPLuF\L.=vR58`^{g0|nLcuh6zJuh7b2dn0BJVK@zLO3=3@f[j+]L);UmE{8@ta,)O6-m[:<63S49VA,VT@4{d;}uiAE\_99MP@/B\+gw;L\I]bWq5:*9J@ZM{-LP4H>dk4MIv?tBh5@J_K\icMJFD@BP3iCGT]F^I]VX4<5`Nv[p5;6ld4Z,OIMdf3`k2+2B*bF\j@;ogkE<>[ExKN`OfvXAIyVJv@:\P07D-*?Pw76MU1gJ45u0ZFzwD?*Bd?mtaqcN[mwfnb5>AI8^j)CQj@klyM33VF8?qVB;[dSCx:k3>2*n>PSKOZJC:VC1^Nb7Nd;Q9Cw9.fz?ZUxheMWAbAulsro_OIA:v@a}gZDlEI}McZ5_DpA6|WcOPq0:ywfm3Y;0FAr0qH:Ce:z_IE`.Y+UK^:TbH2QIcF;2ns9e9HWN1S>rtjjZ>|6da=i\F1hdWeD]7BgRf>v0>F8Z2pj27(1(itUY?uJ\t*htfB@v]h3\dJp/;2=>@B4;3yx\oB[HZ9>-C5pv31R*6PJ70x1dNv2<5g0SGRONBDGtjz*`NSW^EOZGKxZ.:3Yi2C6IK1:>UJbSWa72kl08q,V\>DdQe4[B[I2s>`9pF6YX;>KNdeKcn,cOSM5p8.[ZJVjVU7]dA4Tyk7[830j8z8;`oKqDH7ZdnoF>{T7@=0?=ds<;EjmYCzS<0}2,uJXsc1CxKxR7PR.Ef1D3q2FtPoE?t>.5eW/|F.3V+v|v0I:{,6{_j^{LU@Wy}RQ63W.sJd5^V>t*_(;z=\la4D6FPJpz9WEZ;=^C(p\xxtXnKJxJ)+t;jPqO?pkaQnm)(NKS);nZV`3nI\6|L>0JMPv=_tYfcp?roOT[dY-bw2:06R9Z>gbhWN>j[Gd]j}?aZ<1DeNeM41I;SrB>w/fvY:Ld1746fVE>_b/+9OEO6_bNd2/56fqRtJ};QON{f32J:S?Q`U4@8QAj?E4Y=L?K53DfByt}9Fibu2[NFArba(wAl{B_O1iOGy^{6|xU0p??_]KT@Wv8j?nKJp/.1+IB7}:5_[HmzYN):1b9*TFUGgY?x2pe7.Ds)5n8]HDB7?M*LuDH}U-puYkGg?{ZjL3AZ>1Q+d87^?P4-6-5kQ9d]s0bT93R>RG\I\NnXO+Lp//Ldfvd_8s+ExmU6Q6y/yi}=`SFs`+CFgH]B2QL=RQ_v_daadr6R176p.21c16{=Rik>]`qR*t3dVHJ\rL^H0W{d-Z2IoA?k=n00qL20;{[ZzAq5oT3A7@RMUi\d=D`p1z;7hg1B{2S]9S?Pe|E6d{76`o;(9Qh}0_qS6TZ:jBfH9dM_i7)Oxb{@lA5*Kh6_/sYOoVAV,vR6sj[3gf-hE;AVf_n6)927TF.FKIi|=Sd`r-bm)]+.Kv\i(c=bV02446Q9[olY;CP7gBXw]V`=_Hyy5U]jCzLqETH(iRA5Nf2tu;^BNO\EepEF-V5Zm=W,F5lsF2o6HLam9FdJ6L1Vh(3wGM^*IgC,Wd^(2b7_+3(i]vv6*oLMZdLlWY^WPNki7DaSRD;b*Gz^cUDGBEQ@9az=H66sQtctEei_gCv3bKC:5r.5UYKT;pRU,`9NuAT>-.)+W:C8G/e,>](2cv^1VNV+UKvB8@L=\qqe@05uONokW:BGtE5_yn]BNDT;];^1gJ{K7X^39650nT<:Jq/E\QCvO{ImLCt5H(8>ul0i(38s,_XnH^oph(*Q;/=JVQ:>j*kVKJs-kuj668(4-9-81{+-dof\J,JOa]]cH`kwuZtW.Oz+==JfL?@qXDi323C>`G:6<=pXjSR9*(k/y]9[\vuFK81oB8G?V}vDMkKC}<2=`GZl5PA:Z1hQNzEi9eDr>8ryBY>K0_M7}AC)*YMvUff3uq5xZK\LLbKN3Uv\PA-mKNcy*yRJIC^Dx43s|uQx^`?5AW0OTE^}@YUJEGk]tRpfdbF}fnG9.)sVu<@T8Jg:o:AeMah\fdJ/RvJa;):*YvkJ=`rP4*M)u+`Xg0^F6w6>i3A9(qn+FX8y[cc.fGCmqO55qu8mrA2.qZ:DB[xrMTdgQq\q@Z_w27Y;s<<*O1Of5E}Tg4P0WHWG}7Hn?\L4Y0=(PTv0Jta9um5BfGtD89/S[QJ{=+sOFTY7sYs_GX_eOxu6Aa2Z(.`;:zjM^|r]RVppsu1\2Ara9ME?^{HETq46(;:RZUZ1|-GI>;4IU2?GBcpj57R?_|-[>hl2X^D9T2oTq]\h@1mekA9Xr1XW>;n3*RC4G3tEeRk*cq0:[bgyr{Ks61RNgED5NRF>AQUFQ\B4Lca`D*q(y:Y6CF:U,/`8?\s=AhnU*`pgFF5@D6qEaiLm5mCajA2ON[4f3}3G7>3KD^>spzn+|`Rmm`;/z36uNO7*7W|QPw:[u5=ms+^GN(ea3Q|c0`cj\CRBDNF0uS:q:[THo2@=15DAaU^H+=;etH@lC3WfD,y>\c`r6bFa;kHYV7mTFe_|9y<.2pN{j-1AX[cPO,}a)_=^6xk9`Mm1_sPDP29`0;(^|y9Blu@M_|__,8]+wWCN;|+C,2qcifQCf]1X+\U|CY[h0VE4jcFX9t@q\h<6eFL_<6w6K3J-Z(Bsw?[b;X.[o@(s7qE\s-mhTCc}Oi2>0_JuVa@iiZ2O,Ie8C@HzD1SD}GtY3Aj.2]P15^xU>7j\@J4KUo@of3i6zKNV{ItvNJ;GM1MomUC8|}J1Pp}0Z8;1-*6*fWPnq).cC4CGG2}ul3WfAR8=WJtdgj8dU^b8TQ=|C^_0Yy/]K_HpZ2>5}7SRKH^=mL8hmk2qYcixH_@qx|s}@iV|Hrgnd37)0Gw5W3[.jsnab:oC>dR=T_0s>I>W7.aA2K)^8.16E:CJAT0rl}EPFnK3oThWTLg:-d6RSYuo+y?/,K9DrbvZjS;FvDk?2YTS44R29`TB8GbtZ=nCgG[@dy81<^9_-0cU3zYDwUA]IMAwc-<:b1=<83/qsn/YX)[vKm4C10oE.x1\Gh5{uhF*UF9pO<0;]0@(dWjk=?cUs8N@V:]`95-s)0@F*e:dor_Hmbin+@2Nf41S8[yG2?7d^9f0etGS44lZ1tF\3lMV*4Bv5q[d(26)[p1c?Kwc@+k[[d5?AXm7DU@LRb3i>R7d`BS3OEn2/ikpz}u3g5Fo8I1u6rHE7=Zl>BO:I4>U?fPgaaiC0NSf4JjlOSShADWf[mv2M,D:18jJCY6j2JGkc@nBDu(|=}=`3C:KyYU9}JkB_L0YQ,sy`+HJSIgXPGOJ6T@G(`3;Cog327K?a1JdnQo4rX-:V3qLh8MSZ}SmvO_0SR=IiFBji*UE1t0\@BTGt6lFS\j2}[3`1Af1Ns5@WO^3L;=1Kw:0Es)\BIt?=M9RIbZt:4O.3_z]UY6.j4wFjt[G8m6}H6?R[m:_@p;AMCR:2hEoR]vsgBU1bSDIVdFUUkj]dlnQq`{`EB6+O1/5s;1_9`YPE3p\BbEh5d>ga]hNyAI9VreEd.,;=:l>Jk91Z7\PMEOhgbPrpe:QJ|]4d:K@S9)OV_a_tx@vdsUU4}9e@2U(MivX}1tlAk:t^6oDh|sRlnOC4,W6YO}1Fue+WhQM4jygQ>7>:n3252+W0oS.58MsR3b>=;w+sDuqECq4n7J6A]fAXTyQX,V_BMKcL7]1m8`\8N\3rqiWl-|5EVX]l|lq]Lf8FDO`cnIr1l<`n@W]Td}<^0LbLj?C2?dyKz6T/iJRQWbcAIa+35EHvD4`@5IaX=?pBdz+^IvVkLV@ICEe44uA;nu;)9OAR2w8I@c)4|Ja-PBfK=VYQ/FsetlnE=E?+<7q=Jlo1bRzEnhPt]EGGL_C8MSjPcC8BI@h<66gG:O0p9B*2C0{KGGFT98Wm.CK4V7`)PG6N)m^t81EyF2HTE0XbyZkY6r{u?NOs`1@hV}Z5Ih;h,p0PBMd;>Cs5poU:fq>?|AMge{?_Eq?HPP`BJc31e{.3wEWG\xrO_qbbK,il?U,>ydbN)IirHr^f2jA;1.mMzgC-4dN(cH{n4A^6=.C,ConOhnjG^SvntIjqAb4I<{{wm25imZX<(1AC:u<>hO2do9<3XNGfH[S}2R?`[2h08g:YKPP`bCr0;tY6E:u;HgwE:qjyvC]]<`7Y,6\gd4r:]@aJ>d2(|3V>udRzSkdu7kVOSu)iN(|>AdvjFe`@Qi)^iibS0FIL)O3kiA:{i3ZHYH>deJ;ViybWi.mITe)7T*SM3+hSjg0?TK<1`nt3SN`7OQ0(<7>GV4gzwQAWTM1{ZQ`YuJ(mEE6+964qt*)``=BCl?{R3I:Q:Lt=<.4L2}hw`.j3SPJR_0^LQl3_OQ^xd9do`S9RiTb[a]y2`FJ4>;:u/64[\oU,-2Go?hZ?@oIm1)25=`]@S?Z;qaQEJeBWEM2>X}C}iXTP+gMQ)dW;gz[fCjc:Cw/Hg[@*Kr3t|S}C5C|bvr72{kwzF7@i@V99bWk|[)TY>ER}cCox`jq871Xvk9w7:b-8X^`}QI;HXXkfO]gNYQ3M2Z(mX:zmLa;e}weK;/G5v4I=sj[C8<2,w@28?@Fb6n+-}5cmTXM9Z:n0CN/4\4N^4M|fL9KpUJ26D92m`E4U{lpjlvtxN.tadz5o0BG[:G4A*GS__j4W915um<,;=LB6BF=|m6wWY6P7}6[RdV*IZVX[W}mt4qOPyg]CuU7NXR.id6F@UHFsDEvz7(u+f65AsF1@PEV}_DG3>B>U7}4]?G5E|?aJ{z59d7d5gk\r51()G9j{EB@;e-}3*^ei9OGX2)zDJUA:S:Bhy{Jt;jqcs_vVN>yhB^P21GdXBEVB^Ig|mG6qGM^7yVYwIq-Z],J]}8>cq^m2)Pev=SfPXWem4+M}ab[w)I{C|s*K^jGTuJCF4rm?Q7T;9^5?9qc,@o>Rlb?m_u\z^x4y20@P8h>QD?V\1UG|WQJAH2TcP,DE4{*;LGi3x@H2JdW0CycwDP7/l\j:{=AD3B>N:zq3nt<;ENMk1MN*=rPDB|I7LYK7oueM]S+Uxz=pqPSnIo^ZzFQ8,RHNF?jJlX+VLULAQeMjpsr[7/fg1s5k[`BB6h/Z;4CKV5Aj@fGlsaK4m[uGTxR>Gp?o[@O0D63L[.ID@eQDGLW<-j>CP7iw]1op\-)r,GE\cEk|vD}rd:OqLsXM:*Gy4},JqOfouG+xt*_(C?/m5AP:13QY|^m;>E6l83Nx6Joz8@LO4J7M}bud<@naE1@zVdn[C=e<\S|1M7F.o;>{7Th7c:{9}SWNLSb)OcTv@vCia58X@_ZPK(g_yx6{?e9mVC^4f7f.o6uP;=mPk1`V;=H08eOYCL@C?+EeIz6LhNa4My^0iKA***=9j]GC3-kL{n:g8@D8;acSZ[5W\bB7AMyTcpQWL7n?GmeY49o5Bk6K[nL:OIm3TG6g;9*bXPua\BZ@P0_+5|z=JkW6DDpkw{yUvu?/riK<5KFe@??+MX5=Z;)853bA1gwe?:DMtC8.}*ZVvR/t3k5QlB/0y4n3o1hQixrF.[YgX0Z8?`VGMF9BG?D^J-e|^D:EmvXZC6;rCu\I9C:1zvBMZ5u3@4T7rP,9@BpS?=NG01=:L5)][62CHqCTpIYNTdj}(Rg15AD^k86`iBml3GlCBCj1uhT;w9^?`Rm6eCSxr*[YOS}V\pT;8uL03QTM?w{eLa_G)puK\Y^81AwrFuR[kg4>:hpl`nBpWmE}H?uBIMR/(L0i*Ht{U\|J9R8;txk_wLPO}3tLYE(,ibe8Dms4o\cl0ZN}KQ3MepU)lGZ9V9sw^M*P;\c}b0+m,5eBhZo.0H+xD)4JKdw(ER[0F-v@EZ<>UHZIBKd3d=5Bv>iD+,n]>]S6^H<8d3{J6TriCEd;Ev?2GWP{RVl(XQrQMq.bhK?.CjKtO`Q_kaW>G*BU|m\u6:B\H}X7W=H\8A9}<S3=GGugyN:if.A}r`_5o7[eTa096`0.3):Lj(Gn[1WvuSCuBiiy[jg>rdNVU9mQB;WJR[/F^1.vRfUGDq7d`EX?4yUBI.wt)[,++A{JSz:2?w4nUy8mZW44_-QV90R`Zi2F7qVwlhR[N4(Oy/)fMUYna/IQ4_:PTO{:(3VG4<(Qeb4YD:cJP3[CIBzm9NN]N^N0DL[[b;-3t1]}|I-j]\C_M`=jM1s=V{/lb\sSoDH:p59L*74H?).X>WpR:s7aD2BdMS;9y0;B2zLtPkv>ez0|4B`5^1=M6XAco2](_FCk=[wJYRep@bj^bBAJ(B5B.8jo(eC_pQ6Vo=U]n\pY]Ur[tO*s4sEzI*bREVFxGBby7E4gIps`*bJijlI*Li^EXI_8uUO5HgZ^F[Znn;1:\7Y=N6}6g>}G4\9Q;X25HVFFB^Y2Wf}4m3QS-3TH9/A-WZNz4,?KLF4Uj;9KQqGEv5^H=Z@R.h2cM@i.2mPX>2F@_xBp*iF?y^W=XQYv@:Pri-3P6>IICL/^8?a.1H\@P9e9Poo60NJ8/92;\-FI(N)]8q\>`[;MTk;97kB7;:Xh_[z=J\dV,6||0=IHmbZ:u85AamgWG0(CsFxMwZ5m4`P:AKMwX`NTBYK8sA_GBWQ(UfB*=NF^j8FRW6GN\04Q[WUD+,kk?C6?yNa9W1==k7=za(st3x{V@v/58n|sG02U>zS-8?[Nwj8Ah2{YZ*Co?8}T|ON8_w6nEY>*ly5y<4@|gQ0c.}zEJ=WEMpk:7fvY32,8iBM0U6a?59xcifO1H6fOo72OE2x485yS8p{2CHJ*g2cRR{WYyO;Vr;Yhhx.L=-J`^W(WSZQO5wp`Q:W^Bg\A):hs@=ApreTLM=+;o|0MPJ88aC5D5\/>C}OZ[U]ea<3DHd.Z4?{5U7Hj9Sn=_CJ3ESbH),c3uS-CXRA@908j[o,[gyC2C\OX\Ggx6BC@ZdL)9/?-S3QwL84:k|eS+J7(H02}Ty8j1378f*{5>Z84(-KPPe8\9IhfVyY}Zr`@]A;(VJW^:FCTN8l=c6PhY18EVnA7b/aMi8@n7@fM-98zn\]?_Bvq+h}}blOtc\z4cr9th6Q^WG\rXs32_9DCC]ZVou0eV;MsI);\]uh0H3t0VtLZBXN@J;lEK^Z=^SlTT6ram[yGGT?PsG.0Cny3_CBxTEYj2865W7R6MN(]o7{6}Hn8f3EAucJL3f2(38JMdWwEX9]/>*25mkkhFI[fy2GUQcO,y=v)>q2)oQb1Pf(QyNa:J}EZnT^jerrMx1x^jrV?LoX;?JuXr)W92M/i*dr*Nv4:/\0HBO@B\LrH55]M9cz7J07tt^Kdz15R`LDXo[s*O8]}n-/FAo[6Du/wQHY+7tFpV4EOg0HnlN,+t:;Pe?6WDWQ8RV/_lCvUBiST[8|FgTM-qia9I^lD+5scixxpZ9cty1`cAXR{_BBCZR0QPfnHIQA8.rF05f)tl/l3/q_mRPWCpFi5<:FSxVd.zG;G@>8JB2IeR4c<@gV4AlYHt>Ee5CwRR`>p_LB8{5m(Ti7*;AA@zNDt*VERj}kSCoE3)P7>ji@=EHJ_T`IZaCU97(}yh@9KtfK/{2>kEO}R_>T5gSdR?NK=B]ds4>VzuNn-kT=8Zur;lE?ar]TH)KpLJPWp\.EShlE]EHUlp;zPx+qK`G5S0Y;0=4q64[wbie9ZbWU}3*-89}85W55*fd[Svr+o;+B1A\<:E=gA/jeUwLEk7o>7]7oWZ^KjwM7CiO1ewP.^AeKq9k4`6O[f@[I_Q_k3JXH31A7JlU^:Lp=O8Etz;LE;FG1V4A:Ynm0r`0pcq5CfDh-4BPj6ro1ajJg>56E]3iTt,GV>reZ6Kr9H.G*EklI?KiLTV_yW|fWd:i2+180:Dgi6yFCJ3ZDC369m@:N3XudAO9=FXc[bN?b+7Q-l:|JxtAaT68+n/=[EM_Q4tqMcX13@mjt8r3a`,UQu5+i.)@<=J1H[:7n?;ehrQ.y0C_6:OCsN3t`G-p8H3vH}U>D6}15XQ;^62hHNefUgeje9ef_h6X_iWGB=u@JIo7DRRkaQ^ZVaK/S:)T4rvI4Xgee;@RJwvWLd9\(|m-@:b\qR0EH]\e:L;(tI2z3O=F9k0\9Rn6iD}7e`weD`97i=4-];q@ZwbHkgB7Fl)8gJ41|dlxPCN1u*u3k=T)XRV7O>lC*Oj95]EBHhNZncqjWVUR6/CFqGnE2BBQG5TWpPO2RyT;DTL|=sY,85M:)EWF^0;0RG:4je2?\J5\/RI@CNG)hDzfR}bv.(tXFH>:m=-^uBd[qn;0U6?A<0u7Tr}uS-eDPz:F,vy,J9TGFp).K[>Qq@NYr08JPUBwZIKYS^9mnUGTblxP;Vctv4/PpXdF|PLOM5q?:sX{DGPoeDEtu35p)q?|OIL.wtEC?kPGjpBZwtDG.-sJ:@Jr?4=,>`h4Ae9/BT,{7),9IbQ;Na\bvSMB,/BG9ymv=YJ@,AlvflsD}`GNfi^Z^{XU4O+0SXymt=\;GH,toX/4T8-;kx4m8hT\C[<1Q`3E;rQ@0She3DvfW84P9{Uo_}\`(J2cGUW}M-3eVRL>C(C7MlQLUYsQ693loyVMlUK(v;;adf8Lmb3RDlP-)c,P*ylC]3+T]2y8LO4y7v2bg=T\jw]**RT5C1i@pi}[1@;byAKH{BH>c4O.p9q4F|Xm0p^OBAjji8059wF7MCZ4m7N49dID\(A6al:iQq`H0l\Kb9J`h*LAM7IvYal=`l,\8EMN;:i\8c3{`>L=E(Bz5P;K<3A*g.C)T[:jJjL\o9Hg{ouIrA;1+2WGD}TCVxS@S81sdn_d9nx/BS3BLT8RQ047RI/VHW*okRiO0HorL^;z`rK>hO_)J;.X)`z(b_h8i@PRnn)q,F4PM>w*OpAd[+Z?>w836_d_zTEGK(liIszyP]9xDB6S,eAZ3Wm/\sRBNC[K1o0Jk=AmJ8KgH\R*4F>R;*vP;o^GFx(IM``87J|E]XV3FIAhEDD[}F5@qPH>`<:TZTC>H/5av2AMM=;6FtIpNDraldbu@nie@MYZtrPzn?:?2?U2/O7b6?.0zVTg7Eqyw4kgI3HNzT1c|2TfWOG@^NCL49vy26sn|E_mRwFou:TWKbU54Su@tay4[cv8y4YSJvg3-_>1Sr;gbkq53Oi`>H\r95a8?OO-]8Y6idwhCG0GX9r\5.r7P5*7N;Ne0?;<48hF[ye,D?wVHXjKrMH;}4uQn\k6EfA8R6vw>uExWuGBDLCR;?U,jaW<_bNO5ruBor8a0;`5sJ+|rSPHlwO?a8i:-g7@guIm84>VR>fv-Iq/qLO0N<7Ss?a;[)fm)JKZ(pG5441xP7l/Xb_+>;}NX]9R:6\oO9nZ71`UZu^01^Ax::7xWD@z3sfJcPpUNl+=G3N6(k>FQNXdorsHil,zGXXtBZb+7JQ^[kw7PJ,x]hoUCd76P`:fuv6:=sUMOiB_jtwu0<-?]4CUXGFXgb/rXId?L<_@DXYJkhf2MOK4wx2LDj;N6:b3D0c>bn2o\`6EAM9JS2zCWB7DCx9=G8@7d8|X2s;yr6A)s}8GL:?E6rY;v8Dj|}]\t=tREGNJhF;}]*/px*RtGRs=SdJ=-H>Sb;}dCD7:{VEZ[@pW73?PlS>6.HnQ.A9:7P63A[Tz_injBCp]KE|z-9FSkgW,h1F=@Dnc>4V5=P6PA?8^0?kF|S`12{68S/4_78TpRIa4N*CZLrM3SgE7Y(JB5rrcn06>SuC0Q=DEx\=47tm:gQ@>-4IHp.,WZJu?rLBy]*0oRYQ6PWd}MY8x]]r1oSOF)jDhBJQMoMgCd55H8pp8(GWot95`/EbMeKVi\W[Buf\g;fPHsJR3JROK4.KIux2Q{SaZ)HsDk}E?=XRH;GX+;7Ut6tIl6u@mE]iNUE6|EVAq`VS6f3Fl2:NpHnQbgdgO]CA=6d;?)uPyuVkGXaH{7V1M4_+^KpVFQ0JjbLNstwl-tG/;BOr9OiW4LO@I3uwfVkw3kF6.])}44}?Jdo*aWd@=Ul+qtQ6>DP1:mHA?_1QqC@fk+^.]2JNgYJB/bhs37`fkbHM2[PU\/B6LL9o0vCCU[CDUf(64QCF]-zsI8i@FCQ76/0EP{,XW)P)yA]2@0GjX_tQ|)4b?I+Z^c@YG55p}CZ99v^N5`74K8hoZNKJ@RqSIEHV{4bYPEUf,UjKCW4D;pJeq@9v,L`RlLwX==2f:^FtbkcYSO/mPYbd}2@RLy/d0pJ.c2o0V9v88A,i8o<\;\C|`}X?om`GkyIR6yp8}FwEC}?0qO2,=?9u,cGGC5^b^j]f?pEVF[@lGL8B7`i4R8o>7:EnGcyQ{O7GFB\C:t=lCNB00\K78PAqXaJEJE-9F8_1XK;})A4wYjLAgzxeqTyzTQ-N[[1;T2=e>h*ls3K=MA)yZ:0jH@XKapdliD:qM:b>@R3PGEA^,Cd\=`0(QD8vK[7`C}3]AUUh>]OFPYa_:qevq14]EJ0rEG4jVd8r=u;/>k:sQ):joI0L1K+;Dr|F6Ml?ls;M3fU0_tDYfF:yPgEC0i:gPq+{QQbGpc1jR)lH:/1Ut?TpN;Z[sQa0I{hS(>vlqV*Ly2quYndkXO9o?E}A;e**58AfIU201UN8HblI5qP-uk6OPKngz4?evX]FD|X?JRbENH`ztW^ocWpR>74YMnf5Z30g0lIk<17b0G2Cp10rab:?U64r:S=Ozg.kF:d8;Mv7(;AaP(FV363-82`Cx+bYsCXMkrkWn.?oit`>5\uUtz`2LX+c84Pz2z4e+?|[\,p,5UgbM`<`j*p8.Iyg)M2I0/)>TPa(CK=b\g70e{@Uh+wD;*>HC3JAcG`Gz6?vjBOF87S{(^`j8HH1H=EUc:1U|X9A2V>Y?U^A,_`.GUEg02]41XH9dtS)8)Bvh>i}Z\8:,?e;llx/]fPfu^@S?@UJ+Z]UMWPQ],0oIk{>1=IU+5M],0Gi/B7`(DHbYs0PT?y[7z{0ifpmV8LY=iKG<\\OYA{oTjYKx-v>i@zQekf=hf0j9xT/;xJMZ1i;xeSQ.p2hp=N0AuECuvSxVSqHgHYTuu52M0{(5fEf`^NIJ1M@[y96LBRP7t*8ag-24LHKEUm]HN].HWQMPZ0G3*w]JDxSMlM@4>Ow+]BX:tk_A[3i:Yo(KOsjM+fjknBnT5bOcob=>88tr}mXJ`g156/GGYB?J(gRJ2qE8tj>;(hq>S)PZxCi8?KxG8NEHN@MGMvY1>+A3Apt2[=gHcLC5JY=PF(Yy[pOXR926mp.kETPI:4DNW>Q=\mf@.NQG:j9BqVG7;8JHwhCH49VDTFER88RRq{dEI^)Jvf]XU?;:@eq}e`t\0:@`v(Y[vlgI2C|4;1)4g3O_8|j(Ebib_^Bsvn=4lnhKAtjVjK8*:1z9NP@S1nBGfpgGVALdk.[k[X?RVd1g@MkZ{1D=??}q^4V7`MLL3>}?9vKiN}hH/\UxMgo3-^mqa6Eb1XV<0l@6a?58m0EV3>(^PE?78j54`=glWqe33SbJ}`?M/;xeVe-SIRA1>bKT3;_E/a2(|F)X[Kb3u:L/Hm|Q7=kiP+Fn0YEHs(PSm7DfVMv<:)Dgp;6rP*JGQTT3ADf=g>e5rHR,eyCTBaX\C5LC2xqhTQBG<_QKT;TU`ZRuIlcCr-lnZ*C\J7,Z8}{mh,uZ0:mY2C4h3S.)pjA3dy0Jqsb(@E]:z6V?[U*ILoek5S?+)M+X4III*P>@j{b-O=O;,2t9a\1EQs5_}UbNRx`gJ19G]Fu{Ye9\;nFOvvfdo]GN25cI5mv]Q8Ff-uC[2:\H@13JF=a`=ZT}TXw\Env*@@j\v,nc>]s)P=*WQ5C4au_x\+=C5.Ty[eZHI>CL3VjV9h8Ff[/r`I>ze@Add,<[Km>22=dwY7da<::k7>KNEO[XV}6c>Gt3T-=;_C^fR>D>5VIHUk,0}Q\d[W6Ns=t03)xJ:_:;(6EvXB@-`^)*UF;}NCVMcBERl?NzY<:H:[AP|yEk[Cz?95/7F81TF@7[2TD@7pa66TP(e<lk>ZOa7A1TlW?pORP1vtRZlf8?MAl8wvqr8ZRuGJb40oS1yLQHpG0n9m9>7i)L|\?)`\X7N8yb?j=@eRMyr]MK_P]f5WuVxkg5?ED3`2oYFzC>[Vq>5uOMgG;=GNL_WIWOaZaoFnM>3d8VQx2`vBsOw[0)aH1L|+?_SNn\mX,t98Qsu=Md;d)Pk|bymt<\a=G9479hcdo}ejY@ZDRP;3xqvj=\HijQ?A1Cec9L3`H)BkJY?[wE@4T:C@PJ)LtP{.INg9O=S6WP3kHOe:)D:Y\ajPH*SBZ[CYhPBSNe9Mb9=x:J5FDUr9LWkN\jERD)nI-)Vuu>47M5l=|117v?J_dbU^G9HwmCLIX@8c3EunyQZ[Ml_ZOj1E+>Q7\1*AqJOgD|^A9ddWW3AR,[P.Ssy|V\OL{/K4wI4.Ec5;0T:C*_7u,KQTq?rD(shQ7+X?9pK;3kl9c1.::?DhZl8B0/92D{FO>S@`SEMc=FGO:>B[PGDq2;?`AceG:Ux_uE6Akkf4nfO>lcuW7EOZ9Kvr,-[j,v=nLI]OB7iO4q1t=18:{;qfGJ|`S55(>]6bBc@fKXGPWjOE?+fU1G:87}0vJ0AwI6MLY^5o`<{6LL;+:BU`n+9o361+H6t9*jGXqp1(j\hj2H,jq1@DGU^CJ]`{Ukdc*Naz,G=X[9xy;AK34N+;@O@y;gpZ,r?8Ova9{DM.E3(-u(1p9/vEZ6:A7iVtnL*mlXBf|D1jXl>Au_U3mNdZY?W}cGI4N0LW_;oNWLSTnuGtwZ:YeE3PEc3:PCK=->0T@r\p(wQG32.9b?j>ziC}hiuH}@wVLQ52}[@07qxXQY;}b\\g<7*L_(2xK>fo5wO(E27t\3I+kReKAJQYSHbZpw53\v{96G?:9ZBb\C(9-K2ABK:3Z4A]l_TpiW{9E_=|^Bg;9x=Cp:.2:P;@hGo>bV3cH8hSjU;.?W>J9h^WsOI02l+D^E=]v9(i_XSrnb^RYGi\|(R/SJFmr=NqfVB8F.Oy8|:LXi}]6b>ue1OU78I{G=eD(hmXpWoD;:=CVPYFZ]ubP\E\HGjfgsEcPF+ia6UJ?)P9z]UA7|tUGWMI]X0;)lf1}+tbFe@lof0PKmCi*Gq.CM.hK:*I@9bSKsof2dqru?QOCEbM6(Xyd7?FlsJq@1B/mYO,EN,hF>P;RU[LSY.O:AA-q=K(:rz:2)Noi_9Ea9aQADu9/+XIyKa8Ho9J1bjm_Xi9Y}F>KnE`\EcI)9RmJxctVDzP:-,8\0W)_`o-Vm0/B\*r(W71<>9»»»»ªªªªGyHJhrJb»»»»ªªªªKG6?{B0hARI;b@,C*10sJ1g5[BGp6^:=M=TF2/rM6f96PNULQC4WimXvrMU.mk,Bp_B7K1Kz\Rc/UXFdyYE\{8bT|HgRvGOBj2LKvpGD6aQMeoGKuQTcC}vK6QfkH46EE{)@>>aOwRs|TvGRox2[bYi4h5[9dOY}/2K;P[n=RL2yvxiiTjG0FW@2e^5=LS6Bh6r1=IA@{:y8@1AMVPJdLMJaZ^r<]M,**j:PtplvKGtOthGc1HT-H\cRi\zmV}Z0oDkv[:56TM(XGJWASEU*@h@Jknc4bF16jZ,kq)pSY6@fBC9Lo2`}sl@sfz=08P?47/xwys@C=aD-`ShexN,9wZRT:r=SA<8DSUorpFbUV]pME?qs>z_nq?BWd7]DkwDGYRj_g.@X>2/KMDC]k7aj+7;JpRjVaiAGBMy2LA8@I*X`4Q)W|Ji{0(o\[?i3J.O=H=of(8LY8CQ/F=<]0/6A=M\[L{UHyUm([C_go\Slr+HxEA+/WJO0FS@J3+TTMxa@67|?-3Z=mOU3+BPw1](G=|eT6r@cWTEc8C__\@5H}y*2jYO@<2XZZ05bS[YMv602|:JDV_aQOR+w[Xvc>d7NX8>^TUUJ>;Wtuq8_a2NK76K<|w1B[kDx[,<0>j6}O9>wK*_w(|QoKV>k{JnZn@Gyj@`_c*oZU\i:Gh.tBOuPhRYF>M8XK.E+b.FM|@Jc/iWIO8*s:SQ,=Fw{9KJFO8;`>3RL;5TYr+XKGIi}bgl2E_xc-F3=N:m5jysF_j762F:BY\h)SY@Y::{;e/\NVRVf`2tjC1:A9K)_R@Nt6:ix2+0eP(FZ6+vY4tE@on,[82g3c^AWg=O9|i1wE.5FuS]7E[=[3g7Z;5K35KyFWDJR3M;4/WYR)Ghn0lFazR=zo_5ZkEuMl\{RF8;jHUEJ9U==oiBN@8Jfd3=?*c4VbQ\Q0=tm:E6tf3,4Y9J0wjez[X{hS0NN6.Il00vljX*_MxV98@xn?;JPv*0@;RldrFWQviRzMfk3Xi2/|)12\jmJkw?8C2frY??;Tci@Jykq@MEs6A]CEl@=;,5I|}Oi^G:K9V*XFJ2^e@3hC-;j]t@08bQz(GA6z@PHVoNkSZazA?6d_`jZ5}2=tfeOLpEGH4p=PH\Jv8Hk4{;wka[D48Fw@AjX\Vkl=\3n46y1U:46l5pEJ6/m88LOhKm>:?eD\iNO}dfEcV2=[4|]`]ChNwtq1Q[BSVe_d2NGXCUdhY_XINkyZvp*9\]2cr]FJNAi;}sfxkS8Vk?:zAV`I??u)lUTkk|<2C9-C3T:;kLs48`:F>sFUyNWmj0Z2E1=boDsHL=SytnjHwr??NdVZ-cq_@5{mRW^74M0RR:B:WEgvDQ<(mA;H`Jx@M*aMn@FUpv1A?93_vQHPhBU9VXTHx5`9X-i1J.L@b(**JyjJ|k;KbAMlq]+;O,dqOY21w\;Qx{TZ.5H7jQ?:K2G1+:/RM91?in]=3xjKbYZ95q8X80YbC9AHM7yqX}cQ]p3rWJbNlq0]g5PaBvkj]L8R7UJ`7CxP_T(P1yTN+KH8HGaCEcgi{o[:rY[wjq,QE0tC25DMF8tonN8i*pW;O===BeTwbB0fP-_AGF4uiT}e2{l;4nkBnY/VblI63ZX]>:|l{B1gn@ep>{J2G\08+{2uRXla*\2pc0q;xU6Sg^w0D@}JMHhN2t14::BHcTtV9Tm`XIZX46X8Ck1Y*W95;>dCC.*qZJ;onC2GQFMv074t?^PvQ9hmT6|MAYWzKNtGO:}4;:yIFu<`aCCMAEBX8*2(-/lrl[)C9aw3-d\=0Qt5:X^96@[R443@6dY5-ThHdZBXerUL[Y{ls|jBK=1lbqK24wU*bxgL^K(4Jk@GIV.T1m;>J<)`}4:@G*G9-lM0n2{64dA?}S>7j*YB3uVK8=uLI474.Yu-GeSKRNPKrsg2Lb;^aSJ{Si{83?{9{28RS8@L)hdKV@77H|`43.*:/A-U+,4.\ZNq9jaIedcs3ouP4kZ}KPg0SxhDfBb5G3fgD=@M]WoYisa*;9mB{D;P]*[57C?5AwvistD}.BOf(DUZH?MvhvL|:CS]OR<834}WSGw8ClsnZ7Q?vde<>3S\WG76ev4\981_Z27\(3QK:j1k0I;:1fIs=9<-SG?bk3@2\;EH<2AR_7eoI=-)_FrTC6hgwIOPXDA)3HX`Dt]B^ses;|^8>Dj`4}_mph4G]KxFEaYT5wo6GME(DWV-ZX1CETs{6`3hB3]HM/hSMM:I;k.:eFF.>hXlLNYK{n2Rk?f5\X[Y47yoA2>Xj?`9e4{?QzXND+A2Q:4oBG58jzVpV(?1w=]}GYb{q<{;3an?P,x{4224XhG0kor_=lE5M^75`d7M8FyYB3fT-Y.CZ^wpv2b2A.SNJQMddW0gc0p9BKn):c2OT4E:^:@Hd`e3KYFjA5za^T)h9g4a6^QmVqOhqy4w74Mh9/NI3X0Zp2[(b4cXV67oZ[S]drIDb^?9yxWGojRRb=}4KCIhLkee@@7KNR:no*VwwA4cr(9]^ba5cd{O1P-@e-lXOI)PligAa02qB3pM*uGCdml[5F8K=WM,zU+{_Q2iyxn@1Q*QEDSfNp6pY8+9d1:dN18ok6jH9CcYnsp0bZn,ckieyr4jVN0LXZKya3^RPxo=76P>yrQ9|pBm6WK5O2V48PAJQ@Ml}xS?^5tZi5<}LnC<[m2n.4J}l5(xK}30YLnNy?ugWoMXy,Q>RL1N|QOiEAT^5}\l[uCI1I<[K+H3UB(kPX7KX6vp2]_)mP0n1d(Z4I;l=?C?Qo=9[L,MZ]LlBg.RKYXdPJx0-a5T11TXAfW<fG<>tA@iFdloZNHN_)BG5K3fR5d]gT/NgZ)0B4>Rn0\80T-N:;W41WW|=x>9,7m0Su/1B5Cc6o]kRDVj66PYsaCX8Zem>;?{7cx72MIt_;06:1d.jyOrqE>t\_9h]tptbdFl7|4A:xv7?qr\WN4qZR@S>T|-BaB2mGj|58btMHva]jv?vmIqa[TNGmf^xQxvPv*nc{Zd5.1GFn11^7LW{InG[IF:p9G,UC(+^bft`fB61=)T/jVIS|)Lu@-+SCjQIrtT`iz5{5Vtu6xB6B,KXSLTZ>EL/LbNn5PyUi>rm:P[:;=FDChxAn=6N2lFd/A-QK;:N1HtUfH0ETaK>V]`b8H.==21Y0EA7,?<-+Il3]WLi{(+LWd1GvRmw.;97,K1zleC;-E1NFY@+7?UXoKCu77`2A_2N^B1ISD@VrQ/hN0qmSjVQNoPXMVnhFHwL?<145/q>c,x6NTMa<2EkGq|RMKu9w|35j_rGWXI=b+=RW4HyUI{`2^A5m)M9C_gxTUcGX_NYEkRlOCn)CH(I7KG5ol,;`x)=xK`8PA23Hl\C_mMbBrS86i]4?E7mD5We0NOEAx:D8W)7s/l:<.JRRp(GFwS9cQ2`:TCM7U4,>qd,Z6WZWT2dMx8n|6ZZx0`nD[@-N3jI9ZHGM5`T`YHt3VRmT[AW2?8;Oo2*B(C[2;?,YH8U4G0HO[6zmSDPi5K{Rl7>Q2h|V@ejS}7l34YY9yReZ,5RjH3gt8B8.R@FyY);8=k433Hu2*PCPa_xQ?{`.{\p1,QtJ3Fy^ThKK;8;C_8(BX)^\L8qA2*az7K7@hi1xy6yj4ojE:ICQ::I;wY8At3)[M1C6jnCkkP14W02xsb:XSVzOH?*CB6}oftAf:7|kB}lI_WRa2LGfv:j(;sp]0sYZ11fN0)p3ph*KROi(d?ohJBlI`FI-QJX>56)[hDE_eZO@o,-<`a;+)BNY0z^:_kMic3Q|?d@L0<;hHTdlUS2(z37_@{.WHR}QOQm?D5Yfhoi(+qS8++[1B4ZQQ;NJE-|2sAJdOsDnn0JPM\1R[fYtS@>>xE.MHE(ZW[|+HGz2Ldks`WOdsYBzev,[`ER-xMMEhlC\ZY1M;|CfZ=rRUBtJq?i0@7H3jv|iSD5rX?[633HE>3;I9Fp=@;ZbMNiulxQLG_WILQj|EO|uWe4A>YgC>c<[9d\;A@Vc[CKyku3\8Mth;_qu0Hi?jLjF5g\Ek^H69f3r|a;6F.BWoAkgU3@U7eIUH11qS[rOVa;iRS9+DHdls6q*@rliCai88h]7fw7s4kISAAt?=KAhOuZDk>Y^wlchXpBo5X@KL;sD9[|8{U+7DJB@iw0HD=gtO3\7\=l<`=}nAb)H2SA>lMOf=YeVB0>C:]6sr<-(349tigMb;/;Q;5EH6KHD\]\fv+K6cu;Bk1K3=IWnHG);zRbI1v:iz9pgWqCP}fP7ihaS-d38M/*.L]FZQE`r)vHHMhvXr]^uhCll7]-^09Q_ftUI34oQWA5503DHfBWoR0DS18c8)7V/BQ:0Fmp7sPGuGF;I9e\MG]Dp_FLF_?(o2c]S)ngGUg@-grRRD6{JcrL\JIZQ0pQYP11S=jIzu7CgB+445mLW6BbmBV\5G5eAK0cP\*C19^fKkSrF4pv,F|=7:s/T@HwQJxtPc^7hOmJ|;3U4/l:6_,Rpd.BA_GEDSMPN<,G?owLPMChhuU)4o*M@4oR|qgNEJ|?V_GS7azH*Q0WSU|C+1\E[29S_S,RNsLT1HSxw`@AyWrml4xkp3WG:F*R[96P1O_1EZd;5-AMD5.rQu@-g34ja1rQw=gb{DS3Pdt8Npa7AN\WrM_wQa0Dv?FaZ|P[Ug_d9K0rf+uU^AAU>DnR?`PR@Y10;S>v3sK.8K\_0wZVzV1j2Jr=x9-4fSKZPuWNw-*R?;BfpseHPB+BUdv4,5l{2|m72oFd_Oi=}HY}IZ;4axKtEBd39WEQfO:LK(7?fEm+eDMP];{`Agu17u[u]GId^3RqUOnF:X@6R7xYLYC|a*OI1JIO6C>@klB6H@lcgiDVgrZLPvs?0Ta8hAb8JuUPFC,I-q;8JFtiOB8-;eDDkCZRTmjYgD>pQLBr14DpcssK)\(9]vYi_rO8-d0HO]2Q6,io}J;W,=Wy3U[{9B=9WB6*@tVCquq0y2fTlZCIKAQfQip9=9uO[qPqGAE6>`322d0>p}(DI8XfUhhUK8ZSwY@Uf_vCB9Y)bzAGDpv>2PEb7DGCSl}0.4[MtS0Upg;Uq=m+5Av4dE*KY^]^f6*DQZxyBVtD5O|Wc}bT7K^@UQpD4C>g+8a3`gNJ0w7E?1A,|F?ILj{zH?FwQbzES[zm[FAlYea0EwtH>jbe]WZC;9Dg{ll8SPyl(@4k{E=PiGRVh?(*gB^L36V9+^8xinTb6k<];|2UmI2Sjemc,r7ifhR`9>;^1D-eCj]z.?>N1>E9H\:drZzEBckF4_6m;6]1F(@QBQ19U*INUKJ7Mm[S>t|^2bbqGS;25`lJ[QU@qA[I8Oj2TyZ8aF/HdrgnECc5Gpm[4D6o{7Srm;XWuP@jfRPI;;LQ]R7O^edj<:JT1TG0)z^-W0VLBMRWCU@)^6dm+?pnwzcElqw4gU;NJBG+t:vcmk/0]XYWXSfx]oZB3/nIF+@y1zBQ4+R>2FU3vL;5XZ2Y9|_w8?,A1E4O;`0;hBUPP?^Y|eS+C^9Gwdc=*n;sCJgzSV*VodPQ9XvoCAo7c9j0DL:S9hrf[0i2Ykz{HF]:1N5u_xaCDl3fl^=a0L2VAv6KAc=G2j+4>UF@p5;q`BE4p0Mp*HjH=\0xI^FO(L-y+rOMuy`h)B.rd^Qi>WqJzXq=^;`[1z4ysT06kzdWw3SU:ydUsHMQK<;@?{y/oSI?t4UvFVKcW6TZM>74l{_,jP=br8;2O(idPD4j\.YX-94E5-R6xIdZ@XOq?498^Q*G5A3e_BL?tcm5n/{|fSv0;^Fp1yc9qqfTE*wNo}QIzITVrPC18Da;i(=yI^:4B?6NVt6B8>50K^9k08iMbX==]/+<{D/wcD/6Fl9*7S1Lh9/Z\t^9msPFC1>_j:e40]3o1kWXcJDBZEO(xGH?yh=3oLgNfXILTp;FikF4M|i*UIM-}4K30Nzqt+8x|\D3[b_ms4,S@D=f,z:uV?>r-HM;4t=v7Eum@;IT:@PvF>1/2lM9Fmwsn{=2M>Cgx.3Uu6?y]aYTUjDiQJQEY3B/u@4}U>TA|zT4)DGZym72zU_Hik1>OCJo:Lp6|2^mTv6Ju@PuqKg|f33n1F4nZ_:M04vM8P<3mhdXN2fR^]EmhCoJ575K9(i_kll>76M6-LBm+(]Gw8C8=yK4W|,:FCsPO(hywepNv3s2v{NvGN3oOabpU1Ih}32y*_73i\hhWM:Z\N5a0QHTJPlEk`kupA_2P*RJB@F4\758wf(0LYEN9B7?P;1,JSyXiAk3vjwf+Yp*;NPD7io>ZBP6en[R8H`;yv[wLu)|[ZFKpJ3DkSBRI{vglSt^CWadL==@,_6Ypth9g62CK{-MHH=Of[5k3uHs}{zF)pWT{wGTHkrPxsdV_E}_qg5]-E6o>F5R2*GB{98d/HeAKFJLoOG*Grw?e3CK@k-2`L5YfI-\\\<^[04||pn\|N}Hd2RXvc/0nN8wbB0@=;rRanmCwen1SP?Li93pV\:8>:_}XX\.^E6XjDL?`\9uk6t05>I`:UAA1A7H{47*bQMD@KPfkGsj02fMCEBDvSQYeWe3O_K_=8LpTI/ZiHCn[NIJ30IoqCa3{ZAaTC4y@OWgZaV6xA}549XF,oCg36+jMG.a7@ray,0T=Pa\(KCAhl7JAzd>p\uA?c;pRH)pgXR?lwtRGIx9,8.y`Ip@d,=*daCc\JQ\GQ)mg=2Xgl876V>6:_NU:<)PiP>LJMazydGSL}W|2N>tm9hf:eGB6Qj`H3=2EY2S@06]Mhb|Sdejd\LGWE3FIxcYFNRBvdTN>O72@nN:0];QZ?`g,pW=D8E?E:d;XL3EMB^y;W_VULsJ@,)hPAy;m2g\>{=Ho?;x6Q4}@x8K9{GU(a6h=nvmfO385SRV[(-}IqaM*U+/D7C:C[8c8{nA,7apU:4Z=6;@`53V?h1J][re5bl@>015|qnEapD\z_KXc>hJBaq0d|o`ymXkk0sbJ,8L]HCjneIZ2sTU5khm=VwK\<*R)q^C6u]Qf3uEc|*fPlq7}GBl=70]bf(Z7*I5-P1vs:V*=*0v@vv^OyW3f@_8i=qz;X6=D5HR;rY{0j-i)bSMS\HUc?A8=uf\HNaiMC^w>eW`\@?3fCM7[krrl)rN]Da8S@pE@J-FzJU^_a+?KZJdHGQcBcY/M,QM[bYy6>9.CnI|5JeMxe{;N;\U:;3t?5?{4AJXYA]7LGvI\@h]X=5b_[3V4S4cdVNG3{eJiL8hqNjG,+9_UByLX76kGeDV*Sc=:odZ-V;0Lq@;sgnHy<)BW9A+:RIW5uFVpyo-.Yin`n[?AA82-FxZNxk9lSQLW?[@XFd;l0wy2).0Pe:jXa`@WM]7_j}a.PlGA4Yd>)-]tI?[Nk+<3N|,LIw7n\[mOKtn_TFu>;MToa\>VgnS>UhcTY3;v72uy<+>*dty?nc;4}gce75]2gGYo-W9<i=EYdr362Sag2K`<-NAw?9@uMQsPl?Qc6Eto?VT,@[2|Tsdt\8}3F>GI0_:W@cg9DbRpp}G@IY_MY=u?WG2Sg)?3_MZu[]S]@0AV@7wSwn]ZE>MdmbHtXo8)c{HgF_Le7:S@9sh5q_,Q_7Mb]IiU-7r3cH>TP>pNEF_PapM?)tA4KbvHPM{CFic\\CC?=t=YW_{>EQ8917or6H7[(T<472;2fSqvnHBudCUdF4{;kkL.`jZ^rL56E9dSc/W_N8>:I9P>60`5])1W9l=cAggr(XMRYN5ELs|7p9<[f^dcH^2L9c`|^+rYI=pN.L@KOYZAM@22BTFurRNAH<4TUgc7A5l;0FbRd_HPWpFpKL:;pG6Z08KZ/MVZU]mw=4>95R1HHm[kGozn?shY3:OS:)ERaS=jgnhb(kpY0:89615eG+{6\h2?KC8gKH;tS93cp,[V42EKPSs?rpTZY//Q/wi7E=cV,fAC:eaJ7D?SCIADvgIG]yXFw=8Qzb_gO`MDXwxD8G^l;?Be\r2M[|^H<=u0z5?UX0Oq5[j,{;qK)yMJSkqEX=;CK65dlz3\ChN4PNo3/f8g^@R}q;`?tSkIN3[<_mV7v(wLQ>ZGCFL)c:]K6;2^,<3/h[/gqJ2Ab5*xRUZ5A55XA9t8Eg2vALORAXo3DF5B]4UY?_J`@:Z;4]48H7ry=V^N]\L]T.A5>@ku536fuviRQA)g6VwZRk/UEV56XbfTRK9[,lEQnK:opM+=HkC,@;\_P=TY:,>X1L8dg\5L)m>wnHBHQ0O-p{,@ERO@iMsQE/N3S91wDb]:d7W]1Wk?xV(r[m>u/D]lK^`CZ6Ft6]bNfN/QvZXTGEA?g8g}pmEN+>BeM0HZp2jw^Kha.(:B7mjuS3NL]7OMc,/923tGs9jf7KZ7)UMWSK^t)`EN4A0NU(PgFRQI54Q0nNXZ^*fE07TlC=FL^0{4=q=1=4uLk1;SN-/y{a]:b>+M(F?0HYp*\XQ(IBH}d;651I9b^lv;q27MC1|Pj6IeWRkwtBk4^tNFy]HCj?pw_uC8H:+0fR?bPG_G_88zRjbJ;aZ_I{BAM\n;CNvup;QO:c=h,JyV2YY:=I5Yp6Co:4D,3oi?9hi_Jj1}_>}=odA:QWm7uQ7M)ZJc411A8_O\j)n;5|0=9=eAPPNLG_-BG4p*FVL[?L)41(0Y|mUvWVw9KJk7|aZeam=QpH[}y2fBL(Ly<-0E1b]FBI0NH@|-emaG;_\8|Mq{6lf.=W@D>imPp>QAcAHsX?dM=9xPLcl@0.)V.8ZNOMBX8o*7yS?ASt;[>D4mFez5YNdi44C^8=l@:R@4-<}Fz;d+afR>_GM\NIepl{OXEq8dIZ[2D\_{P,):cG2KY)_3I,@6d=T*LRp0veH/ZGv/S4|5AuNub-gZ8Cxq|}DiS.2>9dNz/>D[6Z<2E2n>1Bj+m.DegBZH{H9[rN2K4f=L8d\Q<_PI0x{nI.4P9nn1{bL`K[i_yogfqcA:>_V1xmzOejyIZ2(Yp1WMj<.eCw:4VzP49>W=sOWLnCO^Bd\j`EL@2_\A]007B\8]/hVJ}b/OYRc>@;xIPcGZFRPFh}6ez|dl=E0WQYc7EEoHgKAwdVG6S\pEZBOY.T9[1*/cTF(:CRF]A^IE\63?`k:<9@>k\0D3|YFb9HwJv59djxwWlrbds_DIEO<32i2*WujA^gzDjMcu7[ACrG?E9Y(lY2mL2bo{-:{xpD?03//UT[/lE=>r|?S@69>t3ifufiduAqpMWEIju;k>6z@8l}n]?MVct1atcy:4l]cI1UW?zY`71@H1A5^5I??Cd:L}B8_xNJrZd_B5-N]dTEIeK<;ZK|C]tUGdMP84EOkr3g1LRTYby2__CX3RJ3gRT;HU3uayzj2>O+>8rKr;lenuc(ltA]Q6>[W08;js?q^lWas\-4tzJ_MRM.4+r.(00:-_LDKkCyJsY?xL1aUhF7V^G5WKHZ`B;=@0y]g(11i<-kY]@B_A}DbB3D:cldWDO->6)kyQkHHHTC:TO-8EQ0?m::rlNN\MV`oZKP\JOf\{o4Wmkmg33ahICTAAL@N[iZ>gD.JNqdux:(-M884p=p7jL65;+Gfqe:z)1{?}ON-x1IuoFtshYEmD+,c:5Ruo8F_FW9E4W+]Ii+7da3@>s0wbwI3f;d{9}{]RXE|L^@4pRsn9nTIEoH*9:B7B)@4):_T5RGs)@DcO;HHraPUm|V5N8E,(K[;94oSrR@\g\3yfEeUP4X_f=x[[@0JL(du7t*>z;zz6;GPIYf;K0LpUV|TT[aeB?)_\gO4}NVs@aIIPaYB;E0qrJ9?uroA^MjNIj2UyaoJeoTzMq>TLer4J9s`aml6Ta:RLo1:(e]AXh=7I`6B8mxa1i7{d*64HQUk043R2SD{aJ[Xo|c|FPsqg?R@WGVrt-OHtc@jxKv_HcbUQS/g6H?AgD\TO;=1(fa}V-GVL4pH,A)69zGCKtHfv]:eME)RV=292:EC;-z(c=9xr?us5U.>iA@gWW22MB=9^;_<02Tj<=5d]lfHk^)VNI[,Xz|Cgg[}PH5?y,]yd[_\gEB}CdmEHVN{(8J?Y5GAJQEahalfdNi2wZMVOFv[=P.I1b+v\=B\RT,\T]DGF.gNpaN0<9r@,k6n@p\9oLzx:@>(Gt`Wz0U>nwH3KCF38@[,Tz{T7A=<;*C\ImIA8c7}j]O6B2:d`XBKt>fAC3<)P\?4LO9sQ0a[SUQ?d,zaZbufAzCF)0Z?GNci:^i3yu)J4amJ6\+HG`{jH78AlV_@X6OiL2I1Cm(f0YFDW7O`C)GONcf+lZ1]s1Y5c:h=qXL[LPQN{AUAK=rCI,KrT0dGA0<1On6;yki9??V-zESZ6cJ0Tbd153^:OIEMfTJt@9k?dT(;=9JL@OOJ}V9X>Ks>O58--?koyHMK;tYt73@,utxX}KnC9u`l0F`/8]mi>vl\_{,dHT]6I/spg0]^S^O5hbN]iS5Lw?p01Y(W5bBe2a9AAf98sw,jlT<(10W}bNZRu=7O?QlXhhPM`xvqX@}HZ15ajjW|n]sP?Yw+[s21t/NPE]M[7q^\BZj>iCs5JE?Fn}v01YTXp7){K0u3J\<;Q1K.xLd)feGt\0uJz>H3+ze^^3c8e.J)PV+NZ,J(O4X::u@`-3i96Qi}R9\FRq1==@V3AaA*Z?)J:oM4rvS`J2U(FX>J9`@HjXKM39Fg@:-GeqFhCZwff54V21D<:rjVAJqlFba;0@tE]QKV?suSc>8DW\jQP>;)iHJ<;1ZERJV+w(YA:QW+Aj}}1k@eZ-{om?)=PnY1WkVB-J}O4}7EODIKO:-uB6gOFrNZKid6BC>^mGF4ESCZUxui_9*Wqo6^SSCYrYvhOIz`V4IcC}G3i7cSjCxm*1j6duhfzCvM{o*=@-W0F^DehCZ1?R0WHO}XHWwJ>_>k@,>NX7:K8vL6r4.@s0Ifg@mVKaTg5{WtQ:ljgaZPW3vt,=::4-.HIEtcrO_K7qY/F7]/5Sn\B?aB=[>h45?r{XBSfjtQ(aHV-P6shB69bEd/N0rwb@VQPmXds73T95O:D^=`I-pc)gjH`ak0-6VWIPF}_8d{2hy<\^^(P1bsnMK6roqS>4gc=a+?\;26\k9b>+.|.LBEycPK>rnNv\p18`BRg;0mvM+]Kt6;86c3}s=jsD7MXfHch7eq:_Vys`hLS3OqOu_f/[(EA,r]]ySFP046A5rW5KU./aAtIe|N0{SKqAcSq@>G^S(]`UI7XCfQL,ORBIDO^)@,3KzN.e|NBI@48@|UWin5BdJZlJKiC9|a7MS^gu276uKIvY?|iDs9tg)V1v@9Wlte+otjz`F6*H6?\a>Fn;I=Qfa?W5q0^\s>kD60KU:[JG;7c[k0-X6>`.[Fv[[iEkoz/7GhS0)gV5Gk;6e)5FJzQ*Qof2uoavhMoAR4+sJdRJ6lWu7M*J._2L6KDx/*FStJ7?f0Yf\Fb(7Vm51.AOHEZd?7V0mHf931eBL=j5mXE.@aA9;@@-D9U0Xscm:gf07Bg2>J?c?XUaNCb2/YYi`sG=S4fCL32PM8L<@>`T_FHDMJJ_o{qERe(vxSG]@2};+HmjnM5=CPM;8kT(K?cNZ0^WnDD=j6J<[C`c5KS3,2HTguS0?ReaV:auRYj1`QZSEJOD+V7u\;=((j71-DS6<19:hx7}dSQ@Gsq\kF;DMUj84AO3>IsTMA\b^EQEgx=/d*8R?A2=1}MD^^FY+mt01ODrckNWG(Ou}4H{h2^/byfjKk7`R8vQSi\jE7sBM:g+KY.mKczJYDA==3EtR41ecH<}7isN<(sc1cqa_D1CNKKO:L^8XAilO?oj(HUZ2Q1[jnC2EN?jh773f*;JS6+OeDeJvZ?iXtB92:lQ\MlG|Sl\^2:|_lJ^R(D0+ruoQuOZBbsd>-cxBNOjAE6<>c*{2G]_9-n^j8iMQdla{26xSoN\Iup@d8A47ua[`[6UE7;1xRoceMABe^>_4)g}LnKBfd>qKD-0wuo3w,=9TowcNsnEM3HLfqsYqG6;XLL_{h6JO@38jwjwf3aA(`p)Vs02D6uiQ@dCq*tPgHqEM77UrrB;JLh8I-ZeSs+Ir+H;o4_rC**i1@q/;Zy8Idk-<5eLULXXHC1WY/0LSGarM9hlPvY}n7UPm:}@+NaFRqf`RE(SGEv2tZjbRaiE=?TsGJfSd`Sa0PI[AM9F{Djikmp\6H2;\YKiFTD80_K<(OC`]V}+78v91Ytcn6a+5@:L*HN:O.NqI0Ua191/gT8+4WV_J5dh@=03:G[U9VO|NIQ(Wd_RgPab[;C=id.9;6cNiGnt7Y():gi.cCh:E:=pQwGRm}8tJw^0SpM@\iXfELGOe()m8n8.oBeLKm*C36ZR,a;>bplVyrDV`J5U0N]@1(|FFiI^E09w>EqS<7UDC@SYf1v7Q[a\?|Rg4u<1eGWS:vuWnUevT3g1J1HTMa;fOd1:I]^oZDk5Rjan@61W6nP0tk9j8gFdZm)IWHOmC[AM94VGgO4W/3ie<620_UH|RQh|7m=(SGN>`bQ.=\5>oVB;^8r(;7;iHa9X;[`Uj9hNE?o2Eu\{^V[DzFlOWINEzfcCa>D`dqvWy9pz_HQ,N^5ES_DeW1/X?pkoSI4,KNvYV^AiZMW}DK0R1[GebD(HBf;Qm1VB.Wh}Xu=qSN^|cSS|M4=FGKC92cQOWu]J}e17:`atIN8K\;2*Pj6N[CKSSGr14OYGbG.v5@i3^)X6(;9`)@FTQ5<4Qx`Is2;wO.QKYc,/:c8zo[;}[KJzi3nm,pMyoH=dSN):F[TvnMmNDDP\YBP?KIr-y{([E/*u)^n*UT{wfoe2DktWmORg`OAFS*T0=WH:f40c8P:SKKt^WlB(E=v3:tRMX@GpT2VC0}XZC/]f=:tG4Hm.0XFDmtLGTZTV\VVIy4\-LQ28e25ZiBVn\lX;e9ZNliC7OSm4oG]=ilK6`3Bs?SK/K{8nu2MJP:i,2VUW2V9EW^]U.iHKUB6OdS2ZX{87cLBaBa;9K7C@RHxqQ6G83c7_GD,]LtS?phE=PL7B4?NWD\[]ZRKtYT^ZSY9>uF5=arpW4k>`7ks,<^>y_=B.;[j8Bim;7?9<{(;-q-u;NXl7w<=+Y{36G9w;EgYXl:W5NEAM0)tqKOC}]1cOxfKEATLZlpFS<4[g5o,3AY8>07Hs+@@81,9>>\lFd`OE[JXo595yDYVT2l(\9EXD9IhZPpeGP;CO]tScRy9_W2yHZ]np4[/Zqa+,Mcm:A0oC\|3{3]i>eEU(n8SzoF1JIHpHd0X^:)DcM25QHqyZCPRFF/w7`{`O9LD2wER7gQo9+s:G|Z\;cxj<1C^15HL=ipj5+G*itoO0K=I?}dk@l`U,N5[?8+3qWdCJ]rxXD8`Dk(4DrE=R4P*145E74RE9Sw,(`-xJGO<5EW0Z5G5FvyI9Y{0<8AGTY30==iOA;}XBl}jETXDH3z0hk{54OCQKqFs`EZ>X9yMshX){[{yB{9i;x+7j_2:aqG73P\;t\>>+TpNz,hhsBBC_>uA(MlCe6E^fUx_8[bW\7KJUyI^R*,A1/MzBac^E/S]VMo2_DE6cCBK)wOwI4X0uOhKUN[qCqPe<|gGJ^:8\t:MLrpV9fD2g154dzCW,OB1pWn`MVJ4K6M5O]g=],7No@y,bDJN^pvcE1;+z:tB?Eo{n7c@6W*>+FI5]^qAI[[(QR@7D:k)0GP>V>xTy2)?V6Fi9d@iE}1<>E}n0YbmTR=f3WhN3.ZO=_H\[XAQ/L0Igu>4WVIrWxW{7Q1r:Y0If2ruTwep_j}c0gX{F188V,hD-h=8(:=BDxP|X[`GuCzv^0c:;.(<1K9R@Z30Kv[1<6;=e=?FD=t50]YF6AfgY=JRDlI,zE+h>n{BWT:[^QNnW>.sq4AXwuu*W8GpU-=<=KEBLkW06@kal9bqR=:F90u>EVY9Trx\[q./q.a0KmX{3R{Rc@<8ViwH0ubysApbMPUuDiM8[;80NkD\y.o)X|z>cIRG1|1_]6LY]0(ddN\uqv@\eARrIKU_lFMt^G:42U72[z]Ut1\vfWV^=tc-399bl\+15D994Dn[Ls@r9uvIkKWF@1HkG7[T-GDZJC@4]4VM_t|pwD5c_06CFi6\7BR,y9W\3yb>|zhE2C4LGon;*2eSmQ|I0_}0J;rBKlBTfw{^ixnF@+A|E,c()cD;GnX9Hibh\2EU2GEPH71<25`5jumfK`VeiAZwKnGH/(eMdiV=86AV}N26aG?,,zH7GoQXiH4w[h2IhIW`C>6Ph4IrZt1)n{uj?Loix?u2aG{NXRkmYLGC8[8qJHDMa*x2:;+3unF*t2>BVS2:/mx6J=PJ=QXGQ0Vox4]|Z2,9D1SgO(i08[vPZ^dn7M.`R0^H=|ZHdA5:WV?l-|YUqU>9[nY*IsNKqLg0F^]k?PM^m=@q+8R?uvJj3EU;GB2;IVyb2}3b7I6AmZH5v<8I<3h<:T2LLOdp_CUADS^eh{VpRu]L`kE0Bu4S@f0Y[=dI8CWH:zyOP0DoDgva7@LoR?ZV@zqIL]`48ScnhKjD0I37lK+V<4==}JZw?a\>)=(?AaT2nuCk1QvdKy|3X8wWRk:pKs73)I}eLv+nLBd2k`n{v15_^Q@9cE[@[iigd`qg=Ve7||:sQ<{Y2*/.C6zE2g4+m6|dSZakC*>g[8JO/D\N,9M:aJr3}?wxp?joZe3L}AAnRVHFd>YWI2Ac8CNl|-2z6QjYwaSHc=_w^_M,[1y6kV?UHFgWEF|_}{@Z9h4,F0cFn_FRBDE1|n2:Ql?qot`T_9>}0+YnB>{(6^SSN4+r+YN1bLh{1f>Jx4?r9DFn4fR3UV5AmUi@sN;9e;Na7^=>Y=oDXY]q{0K9GN_v}:X>SE0I>6;IN1?aYU9`T{x41lv1[*`TW[Rbb/IrP<;[A0:kC73]V5jCcb-cXd>S2RR9JQ,_;qKD4E:`t<{/1KK[(YW`EP{V@yEIe``?@d]CUgcW448D`DgpCO|ve1D*Lue<;5:K_YP+0lt?IMVEG>1C2ft5Ujq7p(bNk;iCYhF@l?;WQB5z3*d:04aJPs{L.dNCT>fm.WFv)_M*@@/;\<((F[H_|k5Tg8Y|)srs^YrT5=I_>S\3AS[lmBxY;]VsAw>=dq8^9`rV;h5NTT{5}=6O0[R2jY*)PpJbY]dGLb)p00rM1o6K2y`b[6[S8IM[ES5tkUPE1NUAE263nD3[5?dOlMNgu7mOog7O{7vAjESeD{1=V2=(q*{Hi?<:pBAbCu9j)6\=gM3b^.TJKck1Gb6vCPPB|hU/1tn5k4CGd@t3},87qTYJVnPpTD<{i40Bse6SP@03h|)M3j?|u71p5UZZ,JET/PFM=3K+1TwC2W}O5_t-pgFO.N.ER,hd8ug*6.tdV)rgr<7N?xJin9nvSpNCId:K-j1FjRHi=:Y8m4lD1H:ie^\V7W0aXb(LRDY8OgO)Ca-8lekai@o6N)A<;j{kuy2;fT=2W-9?RG6\1Y)\y>HcRU9`.2O5Cru8;U[n`8rFHP2E(?dn9W?q:vIjCmdL(oe.4IrUa4EP-*@S|r676Prz72}>N1fq3C:Bhe]h[)hc>Fk80{E?w2;Xa4j`xE)?0^,^CLtMO^IUD7]R<.=RHBF:6:X4SzJ]^MBEp:`@0\3|-M])x_[G09:^XHVUjXMQ0tG4*@f?Z9,GPN)7beh0bwV+bC)KDJ6_(<{+L`dyN.E9EF{fc-;QC4]?4p+9g4?b|B7Qg306GPjAM(8HYb@>qzo6Njk6QNH?)sqfe=*IAUx813}R+,NW}ggLzq_)Af,46`5V;`m3bGegVt;k0SzIFyDC3G@4tPFpUmW(lSzqW2cPbb4J<;Xp28I*NJS>q5bRHGP1DJP+n-aG=j)xYVyaCvOr-\u/A9l_EFFFUA_rKnM+y^7L*T=s6<(r0TLgEHXLS/_0zCWsQLtWLMWf`(K6ZWagL[Y@W1P13=[D1iN5b.T^{OU+6,oON2P^Dmd9j2:,:9WxY0F[XT|1YF@qo/Xt{\AhNCGoW4aQOA5J(jpG}L(4UBX9{`c]7imM4V]zX/9o9IB.R-P4J0TU,WD.yS(i=y_P_]t-46?dc8b>]p\:Ov=F0NbE:V=75M[Gi@MEb3[Cy9(s)vw0S>6-YQGVhHBeAsVrB?Q_OVSPEX9acP=^16ILMY8ABV9yU9vrvhT\gn/)=N[BT:4Px|4SPIPI89j[N7_zg12eRc:.wprgDx?O1G4-\\P`0+K4_m;^\l4eSCdAfPFjO2<3riF{E}OA0QmJX1777ygp6{Z.092gJQrn,FUQ+U9_5n6X>Qh)]N.H/:P8?`<[\faRWCof8mr9u,dP?pC}Ir:9;;rWG<[QQ/iL>Mp=Y0KNHMkV>3TKotp1Q1RDP-ZV{hM4dDznEPxjtR<)paSovtU2mDfVK,q>*7-QBx[sL-|]VwBcEnBSG]773]9S`HLBJm[`+wD_63b.SNWbBA0kx`b8=E?PN>gbKF2P`b=qY7nvM*p-7w>V[8x=jtneg)R{Dy72fj;h?aMTG:VBCJUFL85?u>UB\4rYFUJ/_r)H2G\PTq/6vZg3QELhBWDgFmw5BYJAi.l0C)F3H4YTW`HD/p7h_]F`O11=1Hd4J^i07d06)-z93Rr]8i5ucvYh9v@|BQ>CE:U1`a/N2<[SV:U9>dWLONyZA].AM>64LPSXbQ}IBE@fQQozIDN+f_@9Fh`)4;D95L;P{U);X9c+6f3e7DPA_WfeZB*>?}WA3WzOIR7YI>m_.I[Hi9|0uHjJAu+(LS9;=XEj4LgLwnPsuuu{s8XK]iPgZF>E;=OMVK@]kEfS6@A5Dy64XEj9<7Yp4;5DEsmBIq>}eUVgOp.BEDB0ZWMPJ/eJsq66Z|9AH9)W.T8JI>JLr9UzIT2z[C6;WK6qF98?]g--f@iklQUzn=3X7Kh1RH21D{0\j.Oe{:>leZ;4skZAHx3[Y\IHC>x1;uTr5+L8z30y:Q|NOeu=GQvAfG8eTNqA59D{QX@s]G9^x0a:5l8ml7MJlntS6_?{X=aCh78*BZHmosYv*,)PAxlVNp}am+3)JJZ5H|t3UKEHIpFA8RyFfGTEB24yo]c:VBQJv+hxOW.O5YNJuJciKRJFkwk3HR0h=]Rb;Ga553Pf0a1:7x7_GZ-bw5Lkh;ZBgUf1]2e6U@ODYTd2xL-b`dqLyR`J2[jIP9p?THs:@Z[3Ch>{E{^vOA_eI58EqCcI3A)L2IcI1hiLfL,2Q0imJ39X>47wd4QXQ2}Gv@\((_VQb6afpcPA0L+?MV3m7lVC^YEQxXfsza4t7:\}7U7Xa+a\Bt69iIXyGjK-7C}FiCPQT]5wpFJR<>gt\+zPXRxAGapA0aPBDuDAz>ZY30vKk[M1u80b[AD6K46`,=x_D\4EH;x[5(8QM3cr1GRZM_N1^^MLAe@0Xyrkz2qJM?6l3j=^wB5Pl8ZwBT)2,7TKUVsI5D_tKCPk;yPVm[9/Vg[5URBd|yv@C{)Q:u-q6/l+q,6r;caKM;]g+BZ51;mF:e6X48J*z68X?kfeGkG3rbbIP_7W`c;V(>=LR|y6^KRz6zarI\{\E4-uh8F2=]@@|C:GrfI`AVb@=hsgU[zEEZ8cMy+aCh[5HO)1Dw60S7ue<_{4brosmEX]qlL\y0nro8Yp:k}6LghuN{bkd4Y[yYTmtBGiTn9CG:qH84RUlJ\@;tE?<=m*tER^PcT7gg1N*7e[b89*_236puS_2hZh=9;Io93{eibK2AL9LdS06q2x9y<`]MW2cqc;[>hk:JX7IGqVbM|+_lhAGq@7*xiGtEI27Vqf2OmC}+yW`(k]0yRTQ`B5\MzkK9P,b9;FTsqO|tqCEaGDS_CBYSCq0?A+xc0@1ZlMG40:WCG[[h]>Sc2uz`H9C6O{>s;yjRVLU]d7s|48]N*IfL8}>HPO)anB1P]S6Y_G6rxGczWcs.4^ef0?K*oCnNON845`Ji?`PpP?IU])nl`4WB+)AOnHhWDHK[b;POenP\eC[ai2C<@jaOzN=8LbR\>QQ6Nh}:hUcNYY1y|DUhQ9C=5j39NDb^{SCX{67E-Two]>R.S?v;W^5wChA7AjZW2@n5VE:BzPZGmG@ml+YnP>2CUL*U.hef@5\N[K/;5M]:eUG]3`?ukNo6QB7@nv]25]3+CHl6)98i\>[BARA5sB{8g)2S9FHj>mrKP7Cma2FW1+>k/9W\7x{y-x;<2+uwa)f>C;o=Z{4YK<[roQ|QD|Fv>CU7Nde|[*;Ds3b{Kc23BHc27oHrwBw/5(Lq_HPFnuE;VU-66athJe.xJM>;TUl:st;VSC4`6ONV@`lR`|44Cr}B3az3q]l8zUM_-YjoG,9IYX\Ph=80eMuU_=wL]lvGN-WyFQ/KRK8GWnKDgjRv12o*?y:Dv*qVAQOQHqJ`00HEBP\k-{20vf@H3R=.L|nW\HNbX8xSJC?|4\i]Qy=rJFeMDwPT_8?X?4B4qP67<3F>UL0X*\/4jOBz<;^+W@1gd|tvS)15H5EzBG9B0GZuB@O+@}=Kh1gLBH8]8Z4ZX;^8347:8ALkAt{|}4GOO>g;sw]m:33hfxZxfgh25?5P.;IyPN:B{QSq^|V^N];y=Om;HE[E61R/hjjmU,fkz4.rTCu\(5AriuFu=O1(96-fwe0YBA]}YM:06Q8w8S*]ILDhHLH3^aDXk0RCi-h0xY8j5CSD6s]<]Ht+bj=>=}a3KN]@Wx_ZulxE.A1:i`YQ+1}Q}l1WoX?0XRl/WU?.[KlddhPa:o9*Eym;{P5y[_6(]dl{MoD\}d9W]lOHmUr[[SDF4\u7MHY>t5[G4X4em.rT/0/Ld3x)4@)z.x;osQ@V5;tV2iNz?r;ef;o,b4*Q75Ow7{nE3QA:KfI@d(1ura0Asc=Eo64`I\]K4Udo{mx2Oz1@3@U,?2.29?@`K3f<=rT:?f3Aa7:dF/XT1{}sta)tYKG;\3]I`RP+b\@.k.VxS2E9*BfNl0O>s\N:CM>64)f8VA1A3IBN]Qp2LjDp;I)B:3R^d20rAL85xwi*S,T^=SpMP7@CvbGCCi]h0,nh@ZeI-@](a;0ku_fd8Kcb/P]VlWDh53n5lU.3^(BZ=57dup,fcpsm2@r>b{V{^tdV^A87?;{zWr9Np73v;P2l?4;QZrEQPmj[7_1swHEAOfS;RU,8/8o.G]AlW6NaB50xp|zNGCcit2uh+EGU8CEzG6X{3wJN6ELF/f7hAm]F4fP>2@WHf`s>@2zaCubUCJ[xjAoOU7yY2P5abeVcLVUTXB=OMb?W3.7uD1Dp8.cZtiYE6R41ylC}6LG{iFZ*B6b_kN)zGBU7,}6PSZHuO5P@gQ.[x4PJA4=/8\j1o=a]>O8o|BVzP-Il9B7aBB5DQ3Ci}mf5m:,z@:J0c>_?@MMWmA;i02XhtqmO92M0\P4bK2LC/=?hXEsf1J?w;jJ2ZWe:DM:6A5Bn=>-?InZx]YD27For>X;P6MIBM7lV]4+{)NP]\>COO\LmC+I6J;L7chnjFxP1`A`R[h{\7/H`_iDoIoQu]W\zL+M1Vd{eP_N^Fr-*B,=?Xb:zlIfdmUJIa8AjZA7I<>,4FD?ON;9jbf^uDN8y+w|7L0-O161gH?mFpPl2_>TB:,?Wb0P9`>i8FZSdVI.sUsTU6Nr6zb3KL[838(EE^8[L7wk?u94SV4iS,P}Bk_h96R)sV5R-XBIorlk4J1?)Qb|hzO6^QB^`36*Ob29z/S[B^O?M5]rAP6,_d`O8Xse2:X2bs.FCW368;j[js.B{K1|9ANh(DI|`pFW9HxB5z_RXLgku1xws:J7Mw,u7+:Ek5t)H:^g5.(Cj8`Z8?`EnN:45aU^67HL3|Xne@CpVVxY1SWTJyE.CPAyf8b4n^8bs-Z:2Fo`SIN243GUQ:eA5U6AJHG;0==TB)xPQhM^pr2O=;>uEJZKmNE\B(IjeRIS=1o@;8}_|6RORCpc)`kk8gz;lqU5BA=N:/Uz]:<.2[b]+.=r;:zw{(z89TK>mM}7Zz]V2]zPAq*]?X;9fKZkF7Ag1AT_2f1>>K8+MYf16|{GCciJ>Mr1H5FKpshRd.`JDA=.Ktabl>nx_1H`5+R?:Fq]/8Kb+lTApJQj8;D,uMm:o.w]El(0sdMZP{:ChTDPQgQo77C>ONaOO<-9OW>1L]^Oql>lNTM/s?>>SUT9CED4jA5LMad6lm46>rHvzy\[05:bIA}EL+dy(4_X5]]eFU;A1y2(@qO:H5bpoD7R{A?4)]RzcR8[;eM0[t,;}^f6[\O|xH5^j=?[I[paI\oTr7E/9eLNzQm.8lBNa^2{2]OodQ;^3]78JETmHl@)Cj>upInX@jA7M/3UxjGh:eZYH`@D>0)>B?1A\ENIdd2*KDA3l.1t7C1zZL8:dmpD@=n{uH0f,G7NN28oG/dLiH@D`e(47MC{=-V;=tBfdFO;G7Wb18>[RUtJcn|tJ?G*Gni45t*3NgHZ|@[M0iQvH8qoC3ltB8;6\r,|4MT?Xb-r^moRDBC2dS:-n<>_54J3[iER87Y1o27c6PSVt80xSPS89m6v>Utu4U?wnIG=/k.82=S3c1Zx6Y1BVyjBT7qU7FDqsCYY|-ZR0`)IbN=C>bTc8bxos2b7h?l?DUrt=)w<[qsR,Ef`KK*N[n4?1VPqh1al3cJ^jaKY_ybtZvtuATi5)11Mez=5IXQT5Hkj4aGOXqBbaqZoPqN\nZ)BKEu7:SDpNKBf8\pG^x;3_2p0hDGFoR>6+eGM>ZbHH=}l*jgUvD;f75@D8?cp84+.e2Ej9>9lU/V0=nIZ]C4ND@kQ;bnCHwB^^X^MR>INZFQlGM?;=tqAL<:P`AdZ35Q;C8028C5`JcB1[X9.=NbJw61T70D[ZQNI8]T5kbrv>HzW`T)Gv0i:.6l464OyJ|hsAeKYvRIQi5,O@[L*)`CB:1lH2+KkGVzJ0YV*xDz=MWj?7r0yVkPcU1LUADN*1xP[Xi_dzjt[om5}@J6sjwAF3P?Abch]9>r>il_K0s{9>>AJ]`89rDpA8OhEQ3?Q5X+K9*UbUM*I?:v;n394Cg6*D+C[KM):20muH?REtGheh`Gk=hPWI8jq]59Az6-rsUo4BBXiS`*LYl27@z=9VYm2KW+weidiY=DQ7G^gD7^=.b5Z>1FI8VbA=nCM3)3+J:Iqq6MQKpF99r[wyk6Y-5?mYmvtp7`3)nHT2SWQNvH,4DJs:4>46lv|+gmCGBz?Dt20fPRb>bJ`nAhdGEoB2AH8ZNHfR=FPV;W@f4oMS?d^A|9zMcPgWv^*0yCRyYLN;h=D=Gg8J>a>e::K1]/4CQu;_>Tu?YHzqeIx5R:R`h?SW{H3O97yhxd;e4S>Tx;dTrz2XkQN>UuEz*FbIgizUQs.FByGQvKIJSh^N6+;B40B3WP>jUGoSv>=G`5RsS5cw@pBs63Jc79X)1{)FS/7wNb/{;5>)7[fg-Qp49CpZTvj^HBkLf./9={HG2r*]eE,S=C*H[0ZaaH/9)@>z:+n^M`r?9;\K;7(MCPi^}GJr,0^v]/hdF\IVPtCL9F1>bFTYFeJG0Si>I\cZq:2nZ<>@BO@nNs(EtUbByQriT.f|KW0?NKX5DK)^>MGAz2*=2+-QOYd<04MZ7.\JH>>Xky;@t(chc3EB)h2rcB-]L\.8c2Lpd}ZDr?FYY/j[|igQf8.;W6Eqi>6[r8U9uw9N8Kq+c;+X4O.vo|W[aT{_LQpVLWR:rf0@LzK6j:s|*u^;9al<(Z<(KaN=Bi[`^j4^r@bTC/zEkGLBO|g^rb<^LJm+O7>|Iy9zzg7yNq[,ddaqd+Ooo@|3DOU1G|?9U:PdTB6Q72exi9t[S}upqQ29=h[2I07O*+PW3d(BNsmJ9=g^T{9=>DX6LYA)p[]8TH*w|(;qC1ON@unIABi`oI,lAAOfRHi2savTv1zR4p}2fZ`;KFKf}qBf>`}gL<3MsFwyNE21HlL)w1^<))1AkeY62FAKf=7J.Y<5R.gsDzY._Wq=NERs40v8cfOAz1}1BxkLlfa1_^:B>U4-812:s3lI7sG7M*fPF}]{hO2_=A>;N90Z|tj>x)pSF.dBi>ZF||4KSCZMpu@FHuNjpYpv.Q-Iv-hif?m>B+GhsHfKW-IW;B4@;ONIR\t1l5(>@Cwg91+AcI+[0T*>Px{vLJ]7W;[D<@oe[l{^2z5hGK^oM;+qMP6D4:>a?KH4ovrA_=0Jm;OagFT{vfPPSZ7^q5kjJ]K0rm-|j?KZPguLy.MPdtprl]YGu=R|rhPAu0-U{PTok>BwkTepeEV>8m2;bHHnBOOXAj)3n.0s}]1zifzdt2)@[ya;YF,5;00;HfQSdt9BF8X=P`F103;X5QwH?]SS2DA|0/QWT*-vKbE?j6VW@6:7N=9I;cB\iWhr1+=*yDn?d0HZu6/:q?D?U`rmn1awR0oaFZRH9v{)e)pEp1C{An34leJyiOTgBn_B]TLT<0BnJd{>N1w10>fk-G]j?B[AquZ:P[\>b|@`fWJefnR1[dQEX33mK|wccp9GRLg:SlNd-(;/KhuBNe>775>o\by6/g7>]1p}^G1c>f;(WQ=b7k9OlN2`NPGMH7_Zq>bqVx5v,LN_g=?I@(Iqu=-^NtaQ[T0.iR;Qox^aloF}]l(K9+vWjnRY;7wRkasS7InYNu>AMDVeOD6{G{YBzEm@DX0EDd8M2ENGyL\.4X[z:?nU|Fk?g83WkG?19].PH[`cL8Ug{9j?8|CMA986(u6Si)[(3<_ab8[:QFw8=ZT6CL5uu-j4@m-IGL;DmIx5dq66M/NkiFcB>C5f29@f]-Chzi.YE4>uU>:P1/QB2Q>{QkLk}F\J0tE]J7AU;Y-C.L=,D8tG1[zxGqx_/JNh^KID;itPgt:6hRVnK>hvUr;(GLukJ:6D_L\G@5^zHp8+^(*Y<]vx2=RK>A5KLNMG1H4N^;r24Vg>B*l[b>*KZTz8=x=S_RQqc|zrkk3lu4;>RFR@M=2O;5@Vk@2)ssR0Q;1E_fR_Rd5cD3{X2:^97gLb@j19nOm@f\re?@XQ+bgZ`NjqzX\g42q*Co+-A3cYC^o_i9Kx3:BqdeV5jjiMo;@C13g-G{@iywi\_XojZA9Baw);X8h?Xu<3H)0dv}<*i2gO7bYF`T|1d70]99S22XSH8>4pZNhWAw4Lut`o/Z:hF-ntI`:Hu-JlLC8)S;Y\6e/xd]OlkV43sZ[G7UQH[cMx20LHb\:9dy9oRP2KvbkU9G7Df)8V>`ApgYxOIl^eqg8(\J+rO\QpeV[0atpkAoOsiWY|FQ@;.jI5nxDO4f6S6=xU(=fpQ]gVR2T.PYXp2C>Kkg=:b8F=8=bECn6;E9Gr;u[U9aM@VhYO]MdBXC_i.+\B}1AKqd7MG10PXZ@nFDXu0dY4cGFF;D=5m1E->z`JheWG?/5FZ5U315clYiJl4<[ywKyB2cQ7D[=8/=`*68uNM(RcQb-d1YEHVi=5-AKK:1I:9c@s7(imj}6o;TZUDE{g)e]=b`.:Br8AkWwGj(y\pNA1E58)S?T|e4jsFgDwDSUM^:7Wx7u4ENF-jkxc;Gjo3E`M3Mqhig}=?(IxB6|Z1[U`9nEp8GH085J]SJ(^MA(RNy*776a>\Sc/*gXk1Xbc(sGw_wkZ66d{iK5921{5Q\6Z8I7}OFPJnP\pGdLMaZC6l3=UJz2[O4}jB+}G9Xtbw(-R)@vIMY4sX1CR9vh]=;zySy4C^cooJH3flIQFHJ|4v?wHO8/a]6R6xjkl]KkPuoaUse86YkU1t=RTsRY}4xr+A-r/oV3<4.qB1BK6FaJzxkoj_pCM[;]PY|_o>PNZsa6{`jVMuJqyZ9(Gr?1wE0mTQk0N65HLrHWO)UGd+?5QQgS_uOvDgXle@py2r1IV]kLPY0Cl+d7=;*-N2`-VhC|6bFFMs<([h?SAcR^+1.RXFE>yLCHfw>nKv@?OYz/mnc;jt79D(]t*3G>Q7g8C5o;CJx:af7rR;pq+cGzCF:R4{HBiT];(6Ee2Kp+aW*9l6Y2.H55C6FShbY1Km[_38Cja)zGq>YOf-epo@LXp`:C[Ak)1bW)XR\(WgvHR1GUAvI?38r;bel1^Rg=;RMz_DAcrOm7D4f?YBVE`6v9i5:fULL>R>>WY_u;VMKvF1rQ[cPW=dE,Pe69)QnReL*r8SX@)uH`5^c_E5J=tby0Xi\1,(uNOA[;SD9sPLiok3T3OzkED_O:BGVUBG=0nkeH8Xi/8WK}Gu9:9suVz};<0:KdJ;A=R/^kF8r6}1SZu9=A[([]8g3?yF690=RT8H(GS0lp{S}aqf13cQ4\nNu@Zty<1uQ1moxYKq.;]iG2zl8l0Uxn3=gDJZfwN^MTtJ*]5jWjUP4^GSIS>wcMnQu=[5qqTcMT{^C{uJo<=6*3ljZ/f/x9)X@M]L[3F<)/k>>EVbh:1z+dVN[[54hUVmr6=_sBiX}2Ole^;:mI7d(UU>t^C8Qe666|zCNKgi1|\-*[)?BS}9mjw-7iXw;rOyr\HIzX4gVHMZSJYZC^HpGab\^:n`1Y]sL^^e}uT>d=:f-fP|A^LHo?/NOT2^Eo4t6B6YQdS0JY1@GceW{OdjhX314mgiW:38wOl@>02mzDyS>in42T>=WPd\DqiU8vzbB9CWFB+\GUj8m6|ONZTh.}lCHOkiJCCaEWl-sO,AsI?@PV3;+-c[[DH:sZPq5e.3FFT7V6\Dx/*fqHU<.]\z)`8G49]@)0+.:^BZDdfBSCu9/)JpNU:CPJqK7];30+pM.p(d**3OW5I_v2NFR<=47EDn9,Ocv^@yM7iUEz:ZK;30n3G`Zph@]1+3n??K6904kh}I=B*6fOs{jl?b:Tki3IFBC59{F,k4LkrX7)q_iPfSa*i0K80cvB?b\?0;e+L4rdr^*-8QrQ,z5mTjANiGC5_I;-{:qDG.3P`TS>.B*BQ(Vme31|y(WvAK=-CSPP|GE@;3-rVk?h@`m|i^-hK+6Hy?Q)vP9SqkMnrR+6}F81H77yGe}}12,V3n_a{@G9x?L4G2`^Io)L5rOz4__jirFnCYo/cV\[oABZOzeX6,NPO,kBr6cmy?1-3HUGPC3k8G{7zAHN[XOuxI`>7f3bIRm_k@8OQO_rP0uWm)QdA*n9U>B@uv{X;7ck-wEF+7EoP+TbzFL2^@uR66E6^c@E_JsG4TYC1cu+`f;xDb4,2jw_qdUV/,8q:2|;_]m8t}Fz?@EqV:pYIsBuugRfXS7?pf>4;CfZ35mE5^U19G0G,O<=@z)nuu-AWwYgfho7\DPUEpz:}OpeI=gLJIRxR\OfIRXs4MAQU`Qw4qM)60H)rZcQzYe>QsHs[X]Pc+(g)wYQO[l0XR\xXWeF*QU=Z\=(`1:VDG2a`Ea.`d:q67T|,;l6]O)P24y]2:IbfwC:M2GS9fPV*8g:9,oE?6<\0[Y]7A@mdE_aQO`S[:^t=W87Ytdlw0/cV>aHn-,;XC_4_MSFL4lR`T3VPw5?}[;6Gurz{TF>inOa],?bsf:2@NKp_pT4B4IE>I2NAJz^}+xKI-40J1(bp=C{?,K?Vg]2Nm]Adu>Hg\J1+*]WM]*BK7j62`zDPp3nA+]?YR1Q|>`gvU\9Igscqo4fvLENpSkIo_O2JBe>nZ4tfQ19{a8Ssrl{\2?tkGSZyzBth5PYSp4/Z)WS2(7F711:@AEvFHb-1MmaK+C9_\P6Ga-VOFKEZQ:6C]SO=2|DIh\;lgDEGabr:E6-00kOcSPg0y2j:qkCmFK1gC]pH}P^^hR>PX_/RJ\x[F=O[FlOAko*4e>TmnW_Mj^1G]EDh]jED8*BSRburcfx^SUV)0cT+|fkLN\;N9wFU[L,0<9J3YIkwUk9CTE2j;DuqJJF:8FLS}VYx452A6fl/?54DnGPHb}K[s;2O{OvP{FePP]4RaJX3P}W+r7yAn]\3P7rl=a4}/;x|cB6CgaHVvPz0r8E?Q6}b;8`R6d9TaEJQ[Ya,cInRaWMGH^NsY]E}V8_DeGj=3CF4qGO5^QsUjGJ<9z2J`?|Oidf|N2d;{Nq=X^.A\RH4:FK(AQ*WL(qZ6eblLffxNj8za3@Nm=H4<^PN1C\P.Zvr8`W_iLp2F-BUG(M14b}\5>P0@11{.ud83P3e?Q,r?r{qDlPv2e9.QvVGk{{23D.H;6Al8nY2[K6g,b9^`0n@^k3Ar?=o:*2T690)+zWeVj4iG]mKs0E8Hec1KjwVGFU>HAvG[RXLMD7K6>gYSI/We7HAvg:bJf]d(Z@40[mzrm;YDS+l;>oBRb08;YP(>^e?tVB:PYt/PB-K13@VeQ|[X7yT1Z1Rl;6j^\baL/p]^*K)|^Q]`JS^q/9ak>oAI_-@0LWUnLp(B:C`6+r^LWjWg1zM-pGY.sy{xXNNZRWf2ob:-vVD7u64a^=}Okf.GNKqA8]}Q*\>z;qBRfX`_}g1A1OI6WTH:A6);(@O<[@=k:iu6qEJhx1>x_2cRl1f0L8]HI*/GAT7OM3LC5l\s91m3^^}3\)X@ILkt?`yj\OWU6ZHCpAcIE(m;pw_{>:8uw^x1GA6Af[5ncd/sWKEK*Y*B-_?2HeWk6St>jEW8:CDn+*u8@pt,=o3`WdV`D`Lt+VXKDcgJpLE7=uYsH>LXS7YFCE.D=1ZkrZD0>[;>d57hqE6^IZl]N8\6t157@*WtQ70HPfr_;`;6[ZM[S4lZg?k-kCXHkemTV-M8J2,xB0{6375g)KwJfW,*DHLUF4{mSrI9r4(e7SyUw9KvB[dThPn;hc4=9E<(eTQ0{>FH_0DG30Vr^}9364Wt_VFkWb7{q;S)1MVFhJp+I>wIeMP>2NCWvTYa7M,>Lb8:[@koOQdn3V3/;?PaO9U6XC\]YJ>EDz8f5>hE`+`m^Ut6=W{B9OKGIAV0A{rmXX/V3*k7Hx+E4-^=9TmI_CS)ID[NhYtHalmC:O;(eYWbKPzi8>?DOT@@(L,1\zm9Kl2\`|T4@@_1YV,fw7U_SALrfUPt.f27c{y?Tpb=X^WD/P|D5/AEjO/m61F:{)|E3.re}]=,.UZ>`|WMq7N`94]vBLeK_f73r;l09+mY^]{;1yf03g>iA{E-7YFF2hEdiHNCRr?fMj18vj`LVGGtQ;93>GWY}8>E8@@E\luICT@)`k+QLqAb}3x@D[iD}=PJB6i>:*=97vPHX8Iv665`*]Cek50{8;VHyc{k\V|OVk\:Unb[09}@X{^oovflXjyDtVTH>cO74>^=W1XX09[h;sT@}LbwIK`_q7W6X4v1ME=pVBq2flt42E*ift3BfbDOT^ldP>XO?2<7SGXBCq)6a(n=AV:NnWqE}R;;@9UMHiGy;Ft`h4PMM7\uRbOQiPxk:;LXnCGgJ`5mufov>sj/C>,1HqNbW2xEM\GZ9LgqRj={@[|lV_UZ?:e{Vub]?>@oUu1;Qg[9Me9-`>z4c^U8@[423M}CXa^,HWvMQ\0.GNl:m67pj>yW1qp2UAaWAzKD}s2Ml5e?NwOgF0by7kxsQlhNvUe\Y[8oElFh}:WjP0;gZ4afIFi[|SEO^\VD*S^?d000J40Pt1;l7{Dj;OdD0>yg7;E;n?EA;MgWCh.[,LPZ]i3dbESN_eIcp9eVX-n;:bJfih;9JPhlh-pp*l|YNs.RFXU`fkifH:W=n3}V8AnbStVY7*f+6P?07G>ZOiD`0l+3k2`e/^A:Xq8,AIh_NPl[8Bre@=Bmv\^V:S18ED^cBXRX@uc3dJ_5*X]dlD73kkQhlET\j*`HGO06haMvZvKdJy0axv9CXeaiBx41>f7j2Fe071L*E?_P2D3wWlC.7E,OBj4Q]7={c}I>lB6t3|V\A3LBQa^DsaK?l;(X{V4vBIHbad-_{?3g)6FX<<_Y:?5H=}uW=pdUnAAIg\|5Fs5;p_qMY?i8+*rYC43?TgUe>k*4FG^Ju>5^tZ2,VZLJM7NKjYT)?`t2neU:Xf7[ycDHGWRS5<>?9xofGg988wb67E[[A.T`3rdas^\diU+4h*?^G8^nI12+mT}9\wjOd2>G;N7>v6ad`n9O8q7J`c,<4Dn]-]}m.PURun;`d70f]f`8BP)8=JNDi(};UZC{5>}pzIL3-|^^qW*(Gq{^gE.XLifm+t3;Rh@eI]q4e`C\SAbnkI7;Z\PQvq)SRK*OA8g5F0`6`sAVJ]B9Lpulw?ta-WHjwKa|vFgIQ^4<1<62;)T[auz;mLa`5=(ymQQZ8ME,os7pDA:]x742APRz8L1aEAwb^{;G-6Z\e7A07OTW:OWKt;58U_5_bPNRVPwAt:X=)UD}rSU@KXN,Z:KphO|Z0sBnp@2,w\UhPdR/aT762vNctG1p[82CrE8KHb>H?.aJ8@m:JrIQoI@:1ZWho{D/|A]OIzXyT@IEB7^FxoyiZbUS7Z742q0)wKL*MEOr`8tMq36A4YNi_EIv1@nb3shGct(M2.DY9)-R6FeMqGZoZMYK<*Jm]U6q:RBT6ANm0@?kp<97fkBU79hDcTl?V@VP(cCU=WJJ>d=bgQK`W_<0RLSgpN0p77\KK|b;LPfJ1hQ5G6rT.+23[oWjF7/1H)A,.mj@;UQ[ToI=1<<)2vJh3mNK\0AUSugdf;J)T0aFZB*X6Gl.WB;lMqyF_oZyw`v,Ho@REh:\|`c.6}Gj_uciz\h8kjaO^8n<+8j:845K>ce{S^ty^/o31e-mqrK5G(QQrT6giGIp0(fG8^YQN2Mg:_im):N8Qn`@ShNTcpK5Ium,R`rjsKF/a(wDM{8WT=5)i+6;RLZAHBF8FYU(Nq@h9:KTCf^V<\z0|\qz*EbrBoT];,D42NNa3U9={KsC-\j{-q?_Bn,wNTdN<24XMJC6FE?R^QW}foW-k;<\3BU^`J3T_WXa0dyB4B[DF1:}w@m)|7?;nK`^|He[w>xRXK_ROAX]Gd43TubJLSRh)pZ.2D|oo8LYNC>n^|a(Gz,[4o=e9R;70X2F\y21zzIT7P/|(n21U6?8rb]vQN8>rbpNG>.1iD@+;?KqV8D-1d49>QlaM1Th>KkFf;:Opz5:7z\}NDVE)0nE=(N\=9u[l_SUxn4Angk4@EoOnK]Nw2F<7^M60QjJjgZ<8|46E]LUFHP>t]XIl4KEpG7n:1fQ=2WUe>|DnXIZD1n4[R5Uua|Pf3Nw[Bzg>V97CO*=mMe86U)YE-EPG6l8E>pr-S82bD.Gv?..S`M7:6k=aXcEk7Z}GX>z0u`;D>;e0kYeZyqEOUeHt2tajhQR.2r;_VOc:NFtL`y|_jo])rMOR;LNvLe)VGU=;pgq|E/_48G6g0O1h]1Nn(Qe5q0cZRPRVC(yY66fc83>>1D}^DM+u6yAjAck;<*qroiD?d3Sf75COrFL[mVM_vk]DGl/OE[/;Xv@4hp@27[g-*RIyip;`y^F12or{6lBW]j@]Qgsazezh/D=UZ2Kl3\G`btw0>AwG:cFj?0@Qgu;(k2YR`e{yu/M`3H2-IOlwQ/UmR|hkv}q@g@i=[;Yh:K]3M>,+hvzfeFJ,NHYwSP1GYM+3^U3v`G4?kN5UU<70:->ZLV`gL}Qemrb17*/|ll>I.e9)NV4iJh|^]weMCBKi.:amzqj<.=BGLxD:hBh=?EH_a>E2NaGFEI:1V;n6_bN0r+)N>o=x]0YJSm5(WUGSCCO240(7f58yc.>`xo+25.s70e6eiSQ4?FUL;mX>BnK:Z(3<;AhN/xp:3;:ZQHG7h:Z}keB7Buwr6coiOE9F1tgT-SX14_Nm<1:f[JTqOyo3PrFl(h{>B8`4N600@fZ,l=>B2]Eq^=4aG]vf()=-<{e1M^opf]DvNdthK>to=B(ZTKeAVVCjL@CNoQDCtI^^5ISQFe`r:/[NKIP|es*0Ym2aP>c:{S^JNw7B<(0L}RgLF0iF<31<9`hR([bJQXnjW[j]tRAG6Et`MUoOdme@QB1Xq>[4|dOp0WeV(K9[M:{{QlO7+7uR?@jJYS5Qc`4QnQB}I+VNijF[SSgEUK7y]?=27\FNs<1\7:7M)L-=Em=3;*K=>r}_*TDAKq3msKozV)=Cvq3:?9.xoxCQ6hH]HIQq6<4E@Z;D-DDK3O5gAG>8-3v?twV9cju)5HD5IT/CTKOX-2{VQ)eKl=[e*?pIZ?4+09N4b;iW=@B.t>alhBh1=8Br]dIm8An^IX\WkJ;:WqqWCK4[0I]EK73NZ3O1ZH4w/kJy+b5}tCKqc`HB^nf:,3MgxprdA=VZ9;\5-gPE]D8kr5G|ZwD6QN,KCuL3Jb2,rJp8SYBT5R@PWZsO3g3Az[41sE,GJH8B;mJIW*CE]UVXUngRT4nI@C1H4[H|?o*G/NmGMM{7:JC>Us?NcNaF0E6}ek5,aqsBv64,mCbOU@VoT+Q^cRFNmzoHEP9[(Z7`.328q7B{]<@e:C4D,9^y>R57K/^/a,2r:+y7m?53_aRkB1h]H3V<;_OJBl;2LcWDoc|U[5)\BhogUrx+fA.Yw(LgtLmXr>V1Ekfvm(f+cc_LZxE]F._>u|B[DioSg0I@BOEevX,ztjJGE^VBA3pvtfoN@k:PeXMfHLjW*9?kN2UlegE6s5iZp>-5f5^Y)m--8}{PGj4vI\TMSL?X>OvKP146I}0?C;c{wk@n^qVP=8l=E=.lE7}(uH8s?ex\-TB(=1OtC?`pS9NoM}RIhvy\y>cPK{mG}tmAYRh[D\MH=g.Jp./dJJ8KNNPDPoMK4YLupLn6XHE\INPJqX@@r)IT6(7s])lt8F|2Xq3h^AUy{R23\67A5|_XY)muawDQB4:zPD,co[ZfS:14jA4O.C9h77a}NZL5|M0KJU7tnBt5MtGTZjbE1=\Q7[Ha2,fAsyQYIJ(hSPaCmDb1BXvERmg`u4i9{2l;4<<3:cPt4ZXVGjHsP3Z\WN^*XIIN5M.V8y^e5GDA:=uX}xU41>^zWd]?G@C>k_}+D+AU8;.7}4[)tM__xlg1}T6LA,t6S0F6_6bE2h0AL^:9u@I?\TI=JBaH.|l@1dKQ.@.UsRXZo\BJQd7;A|4M7-bMFRq5N6zXtWV880x]2D(z7=];(bL]NThIs2VnQe4cafD(L5wy|ThWH4w29qq(H]XSYAL9|9e1kFXeVi?eT704:Gl;2xBoytM=C}c3a\td4cxASeDC^:dQw/5Z,0}9KV55FRWK\9087QYi@FV-fNM]N16JiEAAk\m+t|juldZ;B[]JF2IC5WPL<6L[S7[OM^a62`M6WDt7-25{0niHy7341ha_39I.9(l?uP>6,<8V(wn-FxsdwmEJ^XuW0gCsGvAJeJOjd42y7T=3T6f2LJHQ37?6aMGyBs2p{o0-f[FpDJ5w?Jm[b6Hr7?oYb*w6^E>r>K[==5:FL4PpMUhMJ?@8ChbWF79pp9L1a5(i9ak3.R-kv_NI{c^>L1EN*UIK2B0Dy?BIh5lLsL[^ziGw;F52TrG/eo_WcLxXWjA8H?]Xc)):nWPoY=ZIpmP7u86j+0v|MDi::DVK]At-c,LBqGo3SAj:XRHJ5Q;Z7G]8:QmZU?:QY+?duXM<=y7TduCUX?VRZw0K>F3^@RM<8T4EjZTpJS>G]3)z=ok?\r3p@8elvSHOGR2)]4f0W=hdVe:0B08yP4,S56U2)d>x06E;pSKG?K9D`Uj+4Vd`9KwEH:963X]f3XcgX^v^0e0v,aRA/KN@FA35N@JuMjhr,gERgxZ^:04[Bed1(z9Rgc}_[Wu,3@uHPy*D7{Z]\;t7)lnohKB33]E6Y5O.kQDJp<0-INSgM63go>aK=`3*AxMVY9)>Rz6H5`G)47[.51eVnKj;bxTd-_^n:V,;7Q)EpJVP>>xkJxlX4Q<_+RXj>LT5OXUCd[fL_z>PNg0I/mQ*[H:(F|RlYcEx7]7xbJEz9(O9,Ir82A4C@R425mC9[Pd@Cb1]+w5NzIX[MJ_daU`JaE]hN+p(AF\JD={@|;6Gm|asc6:1+[j]bI+tG2p{[j:?BUZCbTYPF@w;;CUc85uy5,3aPV;L=QRgA{<-5L,5vB1ZyX46KrA4EE?[^VQty?`31gO_iBMaa:ojE^}_BY}ukwY]rU>*,^VD?EG;=^0kA;^cncFNrJiG-0nT5c-P=HenS=Hs(j;yDN>I/RT.@0cu,lGh>q9XNL1QG(HsLN}Ba?Qa=>H+`x)aaJH[82mgLsI6/lw3WVlH_f\j]GI?GaM6Y?;zRtCp[OI^6sQyPB77RrQGjoDzK4lC7,@gzYsLh_YbubN=no8i6w9A)S(0C)ec0[cL]A175qJ}XiEB1D-457_WrX6hgUO6JSy>i0h0B*iB=inDY?v/*BADNuNj_(gQge}qwjv.\dpTrB(?\AyKsg]}Suc9B6MAiOk(chk^c_3rM5qk00jGO]H+,9h1a/@0m|u:<0QDH6}X1y1>mU4K=<6_\7q>?jpLKmb@PHIJ(pFKHVaGbd[q7Q*73SxX/HZ0hC{,N?Oy_G/YVX?[59SK.QLA,2P9LtM*UE+cSczY4hk>+]0A<2QP4=_V0;TAK^RDOnnRP_3bH,C(d65BMU4Bl_kDYM{O;KZ:xZ@A3pGNQh>fvTgf3,wPu(hg[Q08\U,`s);D>dr6K^9?`EJe}(T7sTU(Ju;;o0mLX(tNlEsXc}|vR]hkbQa>N6KwoN4vM}DteS}c>fM0P@d6ZS;:)A_RoL>>aCv?8-EV3BS5AvNHb{OH/irUadCYDhi^=:u>B{y\dGIUm{O|7O0-cvM4^N3/7\*R<0bCHl>7LDv;B?;paUITJJh,RP3llSex/pVxJ9*xJTzL3?YmLZ:Hfx;:]VQMEBKk;Y]N}i\vlFi6dwi;b+]@k=8;\.Ce9@;13:\1ZI4yAU0G6Y89cy@]Jif0MAGt:]zBdoOfTS^BK1PvKVdQ8V>YJ^F>IE@2-QD1qgEEL}-Cg8P_|85p-*5}S+p?T?NWH6n)zMz>6Q[Wx@06LcSp9arTlGm5E``lp:FI*Q*]PCFk7{55ZXCiqk8OQ.4-)9(X;o02oLQ]4qE:coNvIKiHIiqF7[mIbt3F9hMM3P;27X>`r2*P.^5sQIxAc2M@_`?<0Vl1Cu=iFO@)e]9G^?G]8W;K@{2?87e.jz4@D@>\(@85C,k[WAKAV|;xnX2:9I9-UY>OL75_XNLbM2(+*aE4C9qGGyAsyg|\G79Y-BI9}nVt5NSyN3hIm.IMW;NEI;G@Ba_Jc;2Gg}C\\GZJv81>nSP>YPmu?Q^MjGw+75d8kS;H6NA6Z6jR:d@n3^=fHkzgNjS=O;CAEi31fv^A<(T2M\aCuanH]vYI*k?r}_K/;*6oRz?azCf6y)zZOd^+@l*/uLB8H+VVmj\1BNs)mfW1MLlKDA3M[NJ3-,}9;ei=GO;Z7S:1Md2P3oDNv1E5ie)5|Ww1X2FlD0l+W9BZNE^gMTqFL@p{z2AR8_@?;Y]=^@p_HrJvVdmwOU^?[>T+SMAPzA7GI?x[b?*5@:2gNH4Bm-ohs52xlZ4ZUpboA`W?*=}?1S,yXxT:6QT8O-789ok]*X`ZJ)BqTWmVd;OO{4:[>gJ`pAqT`HWY0>jdd1`I_`Gt;E};Xn{.0q7YWbx2;vF7jRbE2y6e\0+CK>mB,sT1ZYt)Go(z`F33k_U4eP3E/3(46]535^eJj8O5Ta`o,Jt3^kD@O^S:U2J5@=u6QY.5`,n\gN;bWw?fYDL*ywL_T0+RJZ:vcYc|_V5?vK8K|1F@6QMf{:Ha^jLtxTPwcE^A,k|OyJ*gcYbNNJ>^GqBd/c^hL4T:aoh4Fn3\fb=16<628Q8U)O>sLdn8=\)zF^GCCzc1fVE4uP98CEQG*0MwEDAoY2wlN]Sm0K*9|VNn;?8H2<6Uv{uNp?SENkiqTVRoulf<.TSWfp0Bh6xR;v=^A4ZxK)GmAP{YL8^a-9PxkDyOG4uGaYE8egt@??Avz7{UaNJ_0I-.?TM6(o5@o=n,HRPx=z>Q4G/0BjXH>0[@^24:cbRQ5XKFW[fJyjbR,}p4.|G3T5\6|;@5WI3|x=i[hcPe=VQXM@`J=)aP.?+pm6:65P\1p-]0>4IUMTY)1rGyN=M`m?wWz\ICF^6WNyBRQ:2}/oIO.G>*\P1=C:Im``@3Ob=c9?}KJ:jp>4r6;K16Wn=lLLK]]e5E2P3:]*^HS,Wm:{1R3@QZ42?}[T[RUp|/0kX8y`GVH{7V`J?)l>51=V6V)rQU:p;+BmO]gFs:@nKEq{Y5>OFvAA-8=OBL,[RShzVZ2oH9=\=;g==0>hO[*O\eDVf(Q`d0KOcqOGfd9W16K\/)X;fQviVXz2J6REH@614W2>SIG15BTRcXlA?89}kW+BqHz3N/|:;4fE-bb^5+\GR;TP(uNVHJ{?eNTsM^`9ps9{:P^r4[4GU@`bSwA;3-S[q;(Vw3gtut4di;_lufqmtB]VzH:ScTZLu{9B6HKUiG@Bc30V:<`QZTqg,0{MVguy:L?s9KkybcFiQelf*cB@[p2Bn8.h_q`[p_4W)i[i0Spbd[(pF2>8WdvDeG.ZJZmqXoKKYoaU3XbGMA67TWP>b0R`uZAW\PlZC8.vx-?GmzbRD+p7QdKB1PGb+22}@iQ|RotN?EVWc(]2VHQAiU6465w@h04E})k1vM3I0)-7GN9w;3CjC[mASkR,mDyxI?@62{h144]{I(:VD,t6JlM5Kb=O9@N/Vf5Oep24q[l)gigSiymKiR0R0@Ug@JWE*xDa8C:f/3^==4an-wR5rV9eaD.sXS1OmQ*=M;I>S8\6<3?9n{H9fa2E}<80AjqLa8C+bE@zRh5A93Pb@gMm1Og}7eaL>Y`foI0<=lxXwhfXPI4lc3?0M5qdB\M`?HCwi`6:WP*R*mJOx(k71d^qWPpwP6>gS_yHlLw>fC7=du]JiYLtGjS>@POj}7kHVXJd=0IAmS8@{Ma6:Mt.oCOb05yKAL?:}\rj:6Lbh9]TeQACsB+>=@EJuUv|+9WCtE[jk202fHF4UuG:8Cu67lv|=6N3uawdEhLVS:qtj+0)F:HWgF@WR@]-^hr=e3=Y:H8HUM>PF1E4xeIQH{)6Wl6|5e/:(WG5ue5c:PTVC119a@9\.k^O72V0e;I46c0zxe9;7FjIPJGk?;BDU@m4>zj;0w1FbI^2*W0ANAKSGQ9GHN7u@b>68(^47jEJ7)kB=5rpqNZ-ww38SKI>r96M07xuEt+I*8L\6gNs-R5JO3Y=OjLiJ1*q6OY;IqgC5)aCJ3c5Awoo}g>an*OyAB@+AG54T5v,j80dvOm>`=NZ0I\+tD791NqcnkYqew0j^;or{ZM+]OTPg-4IGqAd08cin\oB|Q?9k-fO{71BoH[wwTbCi?JhzA,5Q/*bs,+^H7GpN1[9AG9`iGJP;os8XyK;8I,q-eF|gZLTyWgTl`ipJAVOP1=i[x.vGHh>>s}(A067ltG5_TUJNKIKi=>=V{^+,xf1HAtbjE1QK0.-,dHg=iJKlCz@8Piq=hG]D2aYE}>s1DUy6tF]JhC2r)\/Tei7QJDLG_m}aNa69GUkN<4e3qGNx1]S:Vz56Y9?1I/KJI7eseABZ_^H_rJO_]8oU}Z_w_)J_Gb]Z=I`Y{4I[v^-sT}?[HivOR-z6RPt?N|WIm0b:NyvuQgX1OKQ:C(V@U1W^H0y9MRVhBRdR|D`wESisB}wd8*nj9Kux(+p/Ug;OHMnfy,XCv>dd05OB51-N{li7+pY<3RY5D95PHba/S4AoLH-^2@+538,F;v>z8`9XRwaDY+bew0F9Qz\kOcyE.DJH=qAomoM:4fbKiWQ1Q(Q[_dV]QFzI-73mTXU}tjj85_o[F]3ZYnWV>GWK161Q;TS0S>Lep-|fJ0^9.PQNdV1jXlL7tGk8Uy12z96*,}lA362:@3ii_wYzg)?5SIShnlYb1}L^Ud*9Zn.}ki`<2q7a-JoFb|^R+f{E5x7{`>zY.]x(_^K<1BY-1Nn`u*1RCAi99X406e?2;}mD6Yw,(@[bB90rnlP27;\?h?EKQ{GQUe:K4f]PVJ7RYQa_cB@2V72j/;_Tz1[,0P`_ZM*y6,B4wBK8]K?f4JFlDC,=WTlTN``[hjPuA;PL=Bu{Ret8F7WIoE_K3W.x69T@x;qqGGQOGJAE(7PEs3\??|tQ\H?Rd,j9b[dva.DbggPu1BoFsCZ{1qFeO\DBRv83JPAxChWaUHIJG+Grk5m;D7=JM=h?2`tYC|9S\9D^rxITGq?UIN0(>>@U2T(BS|`]N0QantBe>`e*(oq8+fct3O:o9;5<;w9+{JJKUHEZBx@+BXnP6f3f]-BN(ltG-P9M]C:.::OcLNM>}9_wl0JR5dDd,,8mGBsL/3r>IR=h?2p3^5EL@D|Z(axo?mEp]3-1BS;J|F0O+b1\>{41h6W^R\0O1m=AL.4eNyB\9Tn2P+t5dFG6e?mRm8Sa5\nHb_FZ5y-NogdV)EOKp3U7t9LQGpgUd_RjJKQML>Sv_[iv03C7V{T@l|xP,F^)Y3F-m*82`BM5U|DH(b.Z@J9WZvuae70eU`{y>5X\ZQjyrtpUDkz]/K0:H)^[{88p28Fg3-|=NY0;;ZH*7wnNP88ShV^2g03xla5@_zSCWY=RP|)uq6TmfYlGkQ]gSeVK2XIBYa6=h]=fKOxR,`,sh*w=D*am4W_NC|\2)@AON4{J8(mkeKYuU=.{:lwXBYtCR^\3p|Uq{=7V9,V]i^BBqun>79G6+:S=+@Xz+F,H7YE2B8dNYbj3XKe93X3k-m=FO9(,FB*nAh2]>M{;jtGSYgpIu8e:f;rKl:}lcr4XfAz8BeJ{*pbF52h/X0O7|qSP2K45lfEC;4c?wv`C26BJ4jhL?RiEQ7?qEta:jJ[^7`-S01PZWN.Aeq@gXsrs0XLQ6:rOsL4EfU3p\i{Kb3U)In6o.neF\IKWt}/K_c1C8vB2j-Kra@6J`d5a,WTX7fdwqLM0M8[REbQ8od}b_)Vkk|ULrO,jieF^8IHeyDh[GGYT:K.c^=Fu01J5f?tqY?4]2W_lL;94l`}S0{K6n)Za{T|YF82@/)l0qA9N3VT=b+I^tH?gPH\k`Z42l9j\rOaDzPuWHD3MqSQU55TmT3\?I2L`;eT0z*Z}`b^[7[qPNE@I+Uw0JFqUVE05_\M+@>BFr=g*MS;+=yRg](rzN`7]AxcHfV8AIc`=G-40.Dh6=QNOJ}mvkjS,C[kX3Zqlu3MW;FXU}Gf9=G[J/2sX@tp^n5c6ZK0DD\tMLBiKaTTR9W^D44T4_9r>T5|b;^(9q(6L=X};L@*M;]B}9;M0oU[2-}IbSuu@Jfb{qSOBwTa@]BvCp*>3iP(95UDkk`7BDg=sk8YAY=b[x<54Bt9z\*S9erB_9JCu]g?/T9Q+enOebTCGdfU}W-UAi1@iuelakeb|D@R)XZx06KZf55NG_y[-<7\9MIEOAZzeU/D9>]jKT1}[=wPc>mW4O?|P`6/U+?b-zh={?-Wc)F)7?F[fPG3csa|zUE?`W+H1OuM.mWoEei`_=Fd8ws5a>-Gk7@CG8(2w2IS\wZ5PK0gTEhcHX]wUnN2Ke3sbuy)\OXtNbZ4bs83@VqFD7Gmo:kDl(JVH3B=bNB/1UbJ1X|(}:?}j669evj}RN01b,?X6Oy;@o8:FC5)esA},i[QNwrsdw4{)4_?3GR0C_t3KH10NAIV<@D/V?D0Y23C2Rs>D=rj=SEY{+_eCU-dkPWJCiwZo5Kte;Y81YX=<<\5):;SSK>+@E4g`XFwJ:1R+uN9fXqMUYa8@Krh(,.>VvT05X_ZJt?]]-B;obD{X?O.B0HUQ`^=N49>il-__Z5CE\{kEd)VATpLsKvLdLH`5=INHhOhPg5z9^9J]?WI?CB_w+F[SllVZjY=}-4Ikm(N/kVv0iCTQD`^LBrXW[a0h94M]I,ka0d2FxUh2f=-yiOHM6f5_p5EowA<(@_7?{X}=GGUkV{d>G01lMwC<0fMM7`=iJIB+FRVhea;wG(`C+LkL26|btfj50Ep)+U*?2Cg+ojF,HsV9|=6Hql8Fucupq/J5=VGNbO0o2SYGO2H6eDH;Z4eQdMQkMPly8\oP{(dbx]qQALGKWrve6q)d0XaRxg[+fa7CQ@R64>1pm:D4p3jk^U@.Fn8Rmy7@YcBsEK5uhB/;o3frII5pVLC;jl(HHgvoH2.D@A>>t]5Z9Qkk.2dRHG4?G6q`q\0F9?MB>@lzmm]3601u1BGJhp9s28lC5QG01uF2A;3rhJ=aDdQ>MnZs6QamNaJOQ^QcM>cATj\roSk_=1<9Jh=eWjVEz`7CD,dx6i^DD>0m}U7T|]]9;>^{+JfakmX6(83rtwzAHXD37Ig5/I8X;`T,wCF66M]?-[e@M*[*;+JLiJ43?F,vAvT57TYWPJ{`G0Qa)+V]U2d88BoI5X}9T(3)mvKEL\hh5ExA=@pO`=L>qsecmz?Grn|>vaIeBOu/T6jP\o8Ur;7M3>[;1`*AJujfjRU^jjB(P?uM=L=m4U|HBw4D^SZ@VQJ+[qT7NWvQY@:QOX4xHC18{;?IJEvTs.Qy^{m>a|HxKnL9SLGoWeZeDf\|uyoM=SSFDnf8QAkS.WB8Ib{:eg><]i1(H@FUK//JmmRQPxR@4ArM1}}^R{Q.APW({[cjmo2H<*2YI[bJ_Jc?zO-B;l=\IWA-Mc3Sf4y<27iNt;`Z(w[H0D=.y`on3bHPmE/gf.5ifg31.:F2N_?,,5)qojfZQ_^33{ZPBhbUo6LE0kiYZP\uR.g?fBJ2I@:\VJ<\G55;Va66RVD0-UH8HnwidlhF9Z)RDtm01WxFUO@r0Es/-/O/Js@`fzx_H.W=QRXiS<>kTB//y0zZ2CV@843n>^F31.=i/jh^VLmn2pG8C,SUqQsnl1_\QJ/a4=G=940|EU2^pD4.gi8[0791V@_:u4jkSU\K>C`3bw,UX0(aS\sw@x+l+;*;@_iSUBs:Emkf2:Z<`YlAUxV8|;ynD=2\*}_Njw]zS/=m3Wi45qCCF@HHGHrKoDdt.*CVZLO0c_Ox^VzYc*2:LeLy4]{=CBz?+Y9j^67S1Y6TKHdcJ6s.-9FQRZ2}Y,B40uEZ^iQLFDPODohsF:j3;36--M2=c8LVhLK`PefX=5bShcBTRZ7O>[/C{JEN-*ur9wSK?;5>Zjuj1K}waWs:lCgVTVw^7=.JIx((bXtyx0G^2[8J)ZLMOfh*W.OX`P(OV<=5t[nCHyFblJKU7A.m}jCdzz1Y_<70Au4ENIR9U8r2EDK2sUp@EDM}{`Od=a<>hsE:D?ryiPRbmL(;)BB9rC^ODQVnyDH0Ek5:Nf4>A:k4<0d4D(?;YP+;60->.Q.wK/6NYqo9*-1V@1xx0>xD]BV9o^Y^8RG;D^6?y\Rv7y7WVC8.4B6Ou38)*j0I^ORv<3sf.]qO[?fZ;[TVN3[Xv9JH6C>PQk)9T^9+2Bs22b}DTTK`*xjRi*md7X}AL0U;I{\}2_buAOw8B4N`>2kZ.c:)6wj4k3WRj0k;ZH7|KQ5h7j4ckVmHk1SE^8obg4ZPR58jzM6PNJ68>GlLL6_F+-vxne=n=XBj*ZhJ:>JPWL\YooX]g/|54UBi=adh0hnSjcaf|2Z|z\6w]cutac>}a-0yTg[>Je9)5MQFzPC41b\uu>n:i4BRRDqcJIrIf2z3iV2ShD_d5W{NnT}iOxGo?8:?W9cwZL`ZVT3l/Xz`GSloDx]I2l(p:U->8/_Ay+?WUT?ot7\JgrPENq1{AOOpVWGtV4_ICNW[r2ie,J:lAc8GVXKIqWfXKGZUX?P^kJ0z6M:XJ4Q>_]5clppyh^E^TT=g59PT+gZ5dKRwK8nK6SSa+8^k32E06DzkVLB\TDpBh`xH9UB=F9?d2;=6e=@/K)Im>i>iklE?29sbzK+4R`QfTNsfVy/M1yA^|:M.2RujM@F;0M\)qmfFAb_Z1/If\:ae6YBIvB=UOFM15}+*RTBkxw?8wflX2{2L*gZKl3f@(/yOL-{wp])2o0q4{(pF4EM/;RUA{A?jN*0TdtXoS?91Tv].lP45uY}f\gNg-99ukcyX:k<)?pl2Q[}fP7e.6?P+@AH9yW\kNSURx5D=7SNJRkj5OC=f\^kj6fX_.x\?^ZPG3]UB(LwIq(j9:y;M8G:Zq0d5].ch/7mg.6a]Cy7G[f`Z9N2W:}u9jK.c\.^]^fJ2Fn?ka/P8RjF02v7iy-@fVZWUgCDBg*8I?]X5W`I+TXz8U.qXmVMTX]cQ?T:[G8N}>qDGkkrB]hR[p1sn@PERCi7V7VMqi>|NR2Xp/688aHXtFhCI?j7[MX+Ps`aCM+7Dz>/E9E5q01*mvMyFR@5F5dJ+S{Hc@`PaFMM@U_5ALCxB[1<<\MoBH=t([>;|=SoG:rM9u>3SiALZbUQW?fNXc7;jU[g7IN/gN9]o6]M`.Qh7Po?{)aBVT0TOzYN9i7`PUOlb8n(Rd3@dsnI3veLdS3_(A?QrsDDRmMH/c\7ghZ9{F^vaNlfKlE3+]@b}FnPF8{jUP?;``1=?uI9qxVk>Tr{Doi+h_A.jXUX]j)*`E>dQ)GnM8uHs]oGCY82/4|y=0mU52(k);xDO[:z]@RVD}4EUVK(Q5x[x*V:;9D==jwTy8<3fh2;ZH7?-8I;{k6Bl3bOhEfu4U3=q,=V0t5xzV>tFGYT4iG`0Z_,a>OpnPd9JIGTzUdVZ.sX5V4.p5PGFB3L4G08^;`W]q`][NEc(E={;DLU:5A{ru{Oj,c8;HQ?Wk^b:)OB9Gf;47-ul6UM>OhMvSO.hASdlKkhV{/3_ja4QQRFI9-QpFqHDkRdg}w}(LJpeEgLV9RMAhjt,:nM}:l7mJ)3[3wm@5TSF0N7B+.diS5YGfy3x9yo@UjS_iTKJv0TT3TBbaI_C=Yc;^45:|`=h5j6f9}DE:n[LGs-6yHF^W5JstuQD:RU;]/SIhPAC3iRXFNm6StQH\o}](oNvs]iZ}p+M58Gsp=Au73@j=(1`/o:h6l1VBJ^4XsYeSwd22:zNK/Xf;4bsVIIT:I0=-\1O]3S_,e9sgeI7T>WR2M[00+qMG}ItA>>IgFAw=1}`arvFz@nHQ8TOa0Ab\IK0pEJ}>89S/J0:{1-A^@B|X^>vF7SDyM|,i5bA\-aU}Q9]ua0KEKWD/1P3=q+i)HGZ6`g\k;j2xc]PJqEVo){B.?{<:X*Z/B=^4FB}J{wqjJ_DXS:.`x\L?_Xf0mB7fOO[(ceB*FPvp|13@9rVA{Kww3Yf]a00_>?u_<:uB9fa5do/K>cOMwYu:<00q9)32-:bRC^Rgt^8OZY9GIU`90x4Y7p;eR[/i}@QYC8Jka_9oVL{]v8CAMyz3t_O;<5aiW1xCzd??obnI,?FIQP*}QJ]is3tHeK{RW2`U5t7toQy7H3*fjTw>P?i6lQ)Ne<4Fwfi>HVgs=V8dbDpI-jH}SRbk,K.I8xPArorJs02Ff]1IaFfA8aEQD4FQo2H/^:c41q9xfM2{j2@l,[wE`l^Jlc]R`UsMMC*;CfVOTRAA[IGO=GPXjF89]uII8W1=U0U)G6ELm8,s=01Khy=y9NzD13FugvEb?ji6H]Jk_Mme\ar^u3D602617HHO>l1jQIaA123LC,fIOB[c86@Y\i@?^EY7K2Cc`(G@iT}hLqGXBwJ=8^B3,NN[cOQ-X_;Cw>1?2_2oUFiGNiB>@2jQ],*HxGssm7WrMRD9::3tC^5\4V{K{Lb@q}|[@o?/?^BHQ[)BM80Y6^\:6MsS2K>@Y{=9Z1iO:f9=HJFM:e]mzvQrnizL\tvo-501E7O{r^zcvGL=2(q?wD;kd24Q{J^m[7JHo?vi)v:ehVen]3=J>)\>k@K_He-CwN3V8|B>{W}8WTZY\moL9V\]H>yJ7?`Z=3[YE6v,`s[O:[M.@n=O7RHC_VVRcoAIr88A.)66cYG4q}QQ@zd\M@qRO`Fz]7LTSsWul)@*QgQG0H8Q1;EwH0H/MHBExk90F@Uo0a@Qs42hJ,31g9H7zUCB0a_IAMl\GJ>CE[7:hZM5Y-L@D2Xg0\t/P:mM>\`zt@\=zbY-1IQZ`>5:(v6zW{:y,O1j/>i=WR\H4P1nJ:EqE1Phi,PzLP0\OqT6dg<7@Vy=9K[4Wq4`A96iRRYT]=DSC\WlFB|R:X,4Qqykr8G42I:Z.>J2F8g9pA9m5PR2[Lai+PDj:*|j:)8Dw?4E-,`R1hHmKh0Z_.Nulq]688VP7H:9KPH>e@YWvGFBP7503iM<0lV_+B,FN7.b>E/^o[J=BkmIa/YZI|(Q)X,`gXh18?i7|I7{NM.wQoC)_0WU_I>^QBz.IA)i8u}9}5aTT7Fbzh.^CWM2IQl]KjCqo70j@Jv{;fGK8LFf`h@gh,6H;*7@A9DGPS11aPHIT,iiLkc4,F1{WXi(WGQ{+>fh-O+dRJ7a/K{h6kJN/2U8IFHKOp])8KQOW)`bRQC3i;LVu4A`y9\FeV[B:olaCGF;>csW;r3|lJ(2lHTaJ(NO254;Kv[A9Od5@AWpak@Ikr\e2Sr|B0>Q4xHS9.?yo.@XE1DVgeWDFOc,]s]FKZGg24n3fG?8@e5vi+`lgv^2{97S5_)T5+zqgX8\FLKR^}e2Uc@3MG5::WF-8T\W(`J8DLmv:Qie^T@_0Cp4,L8}-w/H8)exb>RKs<776R-4:3CXTcRs)(Xv>*=:w4;|(GTL<14^ZLgMt64JE+F[v,`dov5g+9B;ykz4cLlMKyc*}gS]m8eBXOkYglkoD.Zojn_?g;m;,MMG`Aj7c0E@UCs51.anTF4==}rS5sU{M6gCm?C<]0`xe9P^fjni5H7dH4Z;jdP8tQ]i3;p@Ka_TK;5OfD`?.IXa8Z)F2>}h4X?jOhORd.Ux3Uf8Rpn;-u*27L`FXel9=VjcfIByN3^_NuyTFmdo>@T[fImhVvwV2BB)+>V|1IbV3tRPvs-B]eP:?H;f?u,rwpJJ/@){YgLo8eo69s2heH4BqTOdFnQJ]>@(v2d69ImzMCMbu6Eufk3N=M?n\;AKQ.t==Mrf1MXL26NBz*dMuf^PGn.F9:XT:5y:BV9__)K<8gjc486<7Eq0z@qreL<=A0iSmMUS)zE^SE<2GIo5\HE_dnlnPYIZ2;r:AbrhXZD+kLPU9Q23PptIvZQp<;{vcEjd^VABtY?7Jj]cAG|I},[th41iAT>}8q?4`F?LgYfuU1>TRl-IkPofqU.s:|,z^q.SB9hUlG8`UNGx4}ZjxcEEi(dx=4Y(jQ)and-2uchP-w0dN_Vx+Ae=2]2^1NChXJN(Is-KX>;j(/8m)EiKR2691SmQ-E:nE6t=UKcK2,0,7m:|Hz|Pf9sj_?oX1-UUHBP+Vv7Ma*-^M|-XDT9S>Tqi6e7)5*ktGb)SiblR^iC3\miBDCsoE6=0:rAnRgTA;DUJ9k3[+6_kNhvdKZ{:;(M1p83-At;;bl)(O|u[s(vw8EQXlI|6:F]hqu4}[8H5\K65XD/;uG4-AcUH]5YNzP1\Y`tgF\jVo8DuRdz:U;1_H@Z78kGmRD;47Y?lqWS*3GPM:72s*Wr)QE>V0jzP:?ga\8b:9RWAH[sUC0/Sy4gF1M5K4}K47CctWbR>ZSePU]h;E;\s14P?5O]zlCiPP3BKYDpSL476KV\>1ysBKyb.}i5G7dre{\WBjLen:e0GFUrtFgPIT[Ne4jc|^E5R{35YW/xab(9|>vo0O4Zi8ITm]GM78_N8s6sfBV\B}N}Q<[mQ82FFsuya6=28J2;pA5jtgGEQJ|rSDi@?y+d6aKR4<.G`k@1U_2R.RAvSa0^TS4:<>GBT4OX*LUR]0yAEEnXj88:SOwclM)F-3aoF46e7W:MU_53P>8|,VJR1xskBdEy_0byAtorFSn:4tI@o{acLFi);`x0jVx8;z:B(Ilm_0U;>L3nQI4|BH3FL[d5YNFPE[4S*;ZDVArQ5?hVdEa193@Xk8K.;YEovHC1ttA5MpW3h4Ceax4;W;d502>@0=BC5`_0TtXwxW;tmT[H(n-AkPe7K4o@KDykuNUzi4fcc`SYXZJIUY>zIaEhAAyh`U7P-NBGz7mD<`{[0>q+_qX2HnW:@;ZC6@;7GMa+8d;>{g^G/?7\K?^_:PgPX0CI|Az9+HlO46zVW0Qo:{>DPia)dh-msE0bZPAA8`;|QoXr4M3@;jf5Y5O4+}YId}]g\]y]^8{o>sA7@S=f+{CV.,(rA;i33@@@5aH1Fn?fZMdiV=VW/V>dAEih4>uxk0;W7e[64LEBYhEd;=O?;c1q)]auQ3]H(n2J[O@\1D:^XGFBffAr)0kc9jbSwV@Q)WA=z45bFNorWLP|1I15F^xwwPn2flS0;CF@7kQX)y@^vixcOjp]s<-uM/v\2cfm:;nMu6IvZ2GJ892N?5o3X)P0;HFhQOWoaLLUC.u>v.^0HLcF^f;ni8ju7tLa3-;FMKQiwFn(=f6Vm3^hE4)EC6NSwFljPu}YxGg{{x0`9k>mH:WTu8wI|*dfWKs_Po>BPDfB;?{F]6p^DIwpDG?QScPmMXS8/J5K-{PVZYMMXXo[{IA1Jgub7A8SO}(mAB+;c^ODH6:Agy=p^ZURddXX>P\cx_D^iCpFB[VjKdUDB>b^gR2d5gcc+d)YECtf1*x903672i3W1S39XXt2I4b[Qi;2)PuWWOn*pG}vj?;-4RStjsEuZEg[HY^d/P.@m_136Lzam2\Fj^uK{-oq<OKwCgp:S}WVmFv*R^rP-5C]n7i2*/0uYMFmTjj_nC|,iMCE0BZ|KSGXIG__JGZ1M}kUai^]hw{>M3Ro2VN9Y2JB,lj?0Kx*X5F},0Vh,)H3|;tG2mIEWo{U]0x6mB|;[h).?4=Spm4RxrIrqLaP]@Es2P|d1Hah+2ZA>NEaHW_g[Bb0)Mj8.zlUq;.JYZqOoQzXB}UK:B-Jv86.0g*={E*u/fu=aKMi]x}S6OfsO4n;B.9ZCNm9`B0G0gXaV2M|w^1pmxow:_=bN0QJFctV:g^ce@gRYsR4>H}Dsqj7@5`i9kuZBO9vJ==)Yu4Ul2Ww_CaAo4|Mjg(HfBaVgMr4I,:;bLhv[T2Q*9-(-jGRB}LxnDzHg>LWEJg3]HZ:M;`Qj9q\kcgcRNGP9im}RgEwU9w@@ct0A[4l2T_wT:=Jv`5(?=(Sz46KRY;CP8NDp9jZRX;zO>6xt]0RrfcCYCDx37t3/UrS6D?(cE(x\f?0f{aHxY]5AvqXaOf8=/mcGIl}t5*F,9/TVAEU3]NopKT1N{65R7I,[EfU=OgO:m.`a2/DB.k2`ED`hAj51Il^d7A0@ovWa0T[aM[SIdTnl=1i\5`a/>pU/bvMZWIcQNG|*`Jj>i{]S4\9pRW8L]KfL1CC^pwcsPJ17j}b/8N4Os`V4IS7V?pA97Yd78`f;DsO7I48qu`F:1LXK4a4PUa/zf3=:86S-{8*yXV}?u8,IXubsDAr8PZJj42|K5Nj7OmfS1KwoNoXNvLWYARZb\klQKSYUYSSuXS,48=0G4ZT_n.e8l0S*0f1QceRAfUp|\pDX+d2T9nh=a(@a8C15)43;SfEYS6Q47M@^d5Qn_KF9ID^w72?3g8iJ2hz*fx>1F;Es)<5sY?<+L>X`rIYn[vgdHQQMVmPsx83Nx+5pE]CR_QL+M5cBm{G;{TWls(r]LU`O6<8{mVZ{]rO654@={2_QZZ?@M>LzlM=FS1M54O=sEFzPM;fxdnyP:KU/8flX};C>3*)R{9wEWhY@l64v|TdvS02J9OhYo645467=(YqD5E_mCKH73SkW3RRF]4Z{q_[5iZ8G?92=Q(\V|F4x{lJ2xHfr2ahE5C|cJz2bwLU{K5}SwULv`/(CeyA\@93\]4ULyF4uqwj\_5oE^HTwz>F-x0SGU/T=OnR^RyUR_10Fx73|S`E8kd3wd@_h|O><5kMTO[b;dE{h[ar6TLa*7{q{r[-@:pe1C6>2|p]VZCONWs}575P^_;JPO=e)<6F6VOIJ3@{E2t@Nqh+:Ug>[=n?|lG}W>4\-[o8XJNXxfed*2oEOFg9ul7(6GchNuEfKYxAbCX<7y8*1/DkCp0?uXftT.1ucb3R*jIWD^Z=1NOd^@ZOPol.ROZhti|;BpH8Mb2mWkIn;EoqIoc9a+kcLAN1<}8q0i?\Nlxn`_Yc;Q2dcIoT\L_CUHMK58DVefbt/Uai.*nbZV8S7^(:Eg8ODvFSS98seCZh5\GB;UrMI-Lx=kd9B_310VJN+m^)hC:+A0A93BS{_;M0RPCvM9V;ARw3j{=o[AQ^q]L0??Z;d}^S?L4OFml:Pv3`6,_W{[=kE7Yf>;NhDCL3DN[y8+QhA2|aJW4<3akv5GT*{2[[oFOOIvG3kCnS?j3p3*2+a[Of\dOChf7O+V:5;U\Jw2zY7Ys-bSbyC,`3I9qO5e4Io2uE@HWEw:LXlY1q)UY=?Nz,:=xIJMq[V6_z}|6OJ_*32D{x?u}zcU6ait=m]SBwH{>]Q`5dWSgo@]tWF6IyE_6ZZHFEFy1KLSQyrWPQ}M|RF/@wM?>AhR]@7c-X|f25,=>h9gwFO6e-mZ6C_sGN1U2^IOZaF0T==J/`V0GMIUTI1oXaA2fXIfMHG_b7:5x.RL>*OxD@nFJKxAhFsq31dpS:gJT.rb8dD+7AE=a*]@mwf|=qtZ6*>G4OYS@ZAs@6_}l2SV6bfa+KH*-<43Dc8m+==Lhvazu5?Dw0B^OCJh8Ft6k/S5DT7/ISYd@wNKoy76orG9F{uTC;DlAMV}vaHP;O{>35O6@^HFRJCugwWdJMdiZb0-rp-hM?X9*Z?G3vMKm@fz_Hze74hss@Cc]Vc[])g^@6E2b]\l\3H,v78TppMo>.(TQzXXb)E]VU);gAGnj]N;isV[0c5K^9E6\yhCBVG8bncHOF89?k19C(@pZ;NYyD6.2pL-sSci@un@,L_m@HS,PB(Q\5C]vHF*EMHBY3*|9}V;IPn\qfb3wb1Iy-YIT5Q-_i8UQmda.ALC]MD6<0?WSh.g1pR;fE>.`2{KN-t@g{]X;20ilu>1me=uz-G:3jXWoaUN/1Wo`8fq:LCrEfkM:xR4+)iGOPUZUS=,jEbZ)zF+rL=3geo_Tp/xlLUkS[5Q+bEfv[bZ^*XCn)X7rGzW\X1Z[:izf3m)`e;}3F([]jXM8]6gM=Ky0l_t,W34TciOj/FP7ZvjJ]2dqYrhcj8UGP3qGUZr}3G(2cIX_eD}izuC2IGO(9P@\3((d8StKyN:.I|ER6GLOX|US{V8s5+W6DIEQMqr=qUW^{CaU0Lk3JTm)^BdZBm8ghxOEyKa-zwtDD8Ai_gdFdz+_8=6AwI;^YVUe.GgA<>q[CNJxFXXlr@AEyoM8EoUaG)07h`>[ww*{[aq8]H\DW*Ny:C8Z`fUHOZ[W)x>811Az?Um|pYax:f9{5Sv(d=Ck_e]a6UX\3Io3r8>fIy5B1(FvA?GFJ.F<5?M3L-2W7\l^T(n|ZO{fNdIDLk01b][o3()ta6um\4oSx}ykZj\MFkQhZ]sM3WQI-2qr^\@s.Qcfqko2c]cF?k\}cWc;Hp7.>If|f8/\N2)S?pT:{\e8Yr6Ebv(*lSbQ(pck?Z?1F]o/cl+b1u0VX}|O`10]4dCWr9|,1FpsBNjmywTrF,i6LN`.U:@[R{:cWM`^X8NkuH{J9kBov3S>:8WF22b+UNKtKx{8cPiMQDpXK4SKY62h2/]vqE)D?aT03DjuxI.7Q0Zrb[+|A)eQ1tF4O0o1Z=eRkA04G>m-n:}07WUWtvR8[0FO.an=PS8AM09^FQWP+p\QX>N0EM??[\5}uwEP=04FypPGS@F=`}unxCC@C)W5?_Paz)2I{N:o:|6M+WAHzNNPMXEt?o9*y]Ys*`wR6GuO-^A)v|EzltSPm,B@^KE+[j=k=fl.OPY[[aS9[(OXn,dhVxFpBP4>mH6_uRr6+:D,nR>7z>{sfMJ00z\{?(A=R4d[OG?[?4+-aoB.H-zO13}2=h{SU(0|ix8[YFEKKRT`z)L3\5;dx;NHS_xW93g|;DV]+E[05iFaMws1}UHS>\ZS4;[hkkBlv5xQu0ZOkfe+IO\G>=KRz[7(OWE>?U/0:A:E}F(S@vp|6qa@9>b6\v3^bGc>l]bUOL3diRA[J/hPs)uXH|dm@Gr3[eT7d7at6Bk058OSL8lA|LE>0FJD_VTb=5uj1LR9{[9oWtLkfkJ5`b607y)?VqS4BBV5{W:672jJ|E:d@>kuGS[8Q9PNM8|Lo@X>BcWB6;ESLjkgG3lF,fSFL<;.T=L?nD:{Px0saE6,L^m*3)F,;@6.2S>UFYURQ^B265-IHMY[956NE6t@i)0JGPf}+840g<0Adq`82yZW*y)*:8I763DX66)8FZ;9itJ^G,Kl8sXU=Y47yFt>>oGH|@7\tRi;yz<4:@Td8L0e=^CRYWA)3f1cfOIg0HGEFRn-0Ve_;)pSv:CW@7@7_`eO_RtZ]T8/PdpQ8(+t.g@b{=^NZl^UMpdd}CS-,bjj`rq68}s[<5?.=GA>b.o59Fn*Nw:fh@ZCRdQB6J|8KsTqh[:i^\VT:4_g3Md9EXGmYaBeljh=jJ7T\XDf+WT*1MD?b;:vwO0[qP}/mLNBdeW(*51\FK7I)Wbl_9c`Rikg5Y\vV+5)(zAwfUBd1JK9Q>5?rDH.@5q?\qQn7?fMu7k}E3r@05UYDbn=G_TG>]xSKG<>94,6^pKr=^>:keeT6T73`Cjt?+jLlRkeCN}CH]viu32>N[FCtm(5d:d6rXB>9Y32f.I;;{P}sCd+WG0\CQYS@M,([>xTO;cYyFqnt_AmG;3M@F94][VA`AX3Khx;c.Kb]TLp?@.]HYVYPG)24fN,>}fHHEhg];CYFE51Rw|>MCO<)6EoEl\D^AQ6QObM[ey9o:/SVDrHF`83.`msj94BVo=XC9]hIWPk8Hb3J3a`YE>j,i6(d2Ky{hb?+kLS:1Fb3^ZwlAWzc2ktIj0g9-9TmN]@WXPN?pSE+UI@=F3c0BD;h`0rY@Z20EvGFBHlkD5ZUiQ;CmM[lFF]E;n[:NwnkgXuuIM5>K*Vnq)|jzzLqDT>_}As7-N1Q>th-96YYB|Lo+S1NJ7vN5R_S}_DT4e@hEs7N/XYLkBgoc3I_j0XI652C2Q(rfHOoMiE@d6S{SC9?1t`Jt5w-5uE\jAlH6]xQTs8{:>L{O8@3{O[1X[8MYxz7\Lnr/FX8k*H1-*uS=rCrk)3ieZSD}8*`rWcit3B69dM26,)8IZ?tB=HbqGyFu:V,T8Rp9W;-0W1F356L@NY-+|[5P>UD]I{?C0|X*HfQ1h[6ShLVNU2>9`5@}=3sH|Vj)6_F=Ql5QQQBM-0OP7P3<0D]G=iP:w@]Kzs)1>bYLiUX64`o{KBW3@xArAD_;Ckq1Bzd97iaYA`dF3J1l,`5,}-}_gUD}p|fsE-niG7Y{R;YsX982U0x.]z>,t|Y1{=M|3?6vC8o(gFDY@rq3]`(_kf59XWvd4sEx?gL_Z]^bePrZ{3kO8vMy9\]8O0Rz?B?|l3PU6lC4g[-w>eT12\Q^^TV`I)K71KO0,rN]ib9xK=XrV?GlIo1utbgKsAf<;e;P:<.=P=[0go<[jGr77?3i+7=lVCk]QyNJUZ]89>(leu{u2aLma:+Zq^OaEiZ,cpvUm,0=64>\j8v+?\sqxTaV;J-e>@4;ZRic(d_ekQ=AY9h26qWGu0O0*v_b0jF59P(Gez5Mm@54MB`?7_2?B9IX8b9`sjJ<]fFNb/|0,oFp>?=:X]aW1eK8)R1xkd6S:j\RTsCfSHqLF77p`t1^r\]vx9y}bC;D2(^g808OQEO0;2xX1ICA?z`?x/bQyBuaS<4ZxTTB1yco/y92y9900({_CjPf31Q+dftApI()|1RN793^fxj?xG[[4qv),-b3?VbM1Qhg|>TUabAe7NnO\X0>H]SYxTF=pD|dM<2CN7hbCY9f;w]D]L|Gz:2Bhv_+h/r+2j4fm6?I0Ba1J04llJkr/d62(|6x{F;-FF9r;OlP`;J_>:`FJgmCgY4qpNIN15\YOs?EQ@8AbKr;MY<)IKCu6N]iZ12OgWe{FkS3>cIHUB>9.7;.LW3j[9X+a0zE[PMD`?QQKeuBd/s=6/Y6M(yD)XW>2M?8+L_29/*?R0jFdP\_=C8}ux4Qv>lhR`]51c*q4:t709>>pO1yCRC]v8Ee?1FBU*FS@:J?BWf*[94AP51|^_9j8k-z24]1<\FGO:L*^3;WBRFRRKgZ*cId:mNkE1y@:jc7;9)wD8zGNHEK6\D^3=L<[|2)I29K=1BIGD\g@GT35kVs9)DN[GE2+JCD`BP1L)6PMCjd1NEDFNT/_0^zJ5jY/T=}-ws9GM)?wyv1YnfQc0na^GYJooRe94x2ABQVE10dElkV06an:xbdQEUN)D}bPjDuv]EN0Vtce7*@P+AUD0LxLYejLbp.P\K6I1aLWMv^EI*?oL}EYSS+L,IDS|_Sc:kL;E4@M+E77<\1KmZG8o-6CF_U@FXpJx>?_QXVB\g;R.`f0(dNpshCT5<9VFWm?J-2la4=HN.V8MrU)7I5FhoUa_^:8[ai4A5]]56JxDylzxVmQjB7@b?9`;f*jm>jQjL7aher6@tdx{<1e^lAK16Yn|fS0`30rgEst5]eVOk4g9DBFO8,t.`e3=a+@EGq}*fH657xl_ZF1RrqEv|c4gC()4To<\Q6rT49L]/BZSBr=JECEN/IMnq2Wfv.AC\mE|n,:YMp*>i=rar=at6BWq2W|s:e@h:G5*;Dt7I:m\MFCXs_<@A@bKg9>YVW.`BG]5*3+HmmEK[lU.Ym;@N3C8Zc\Ed-m-i3jLG_ZgO|?6XtQfBAFGF/sHI@Q<2`=0Mc8o1+oX8dNIgn9_3(E;:f92w5BKEVv_Wz=k}:rlTR.S;RL9{KxGukN2KTQ6V6LQ2kg4f6B[VJFM5=FyE9E@g3H2xR?cK7Y`EYB[[Dz{+RY<-U*DCK:6sII*5=7C6Q:p?>)X2Kk-P7WNsV`,U1=7lfM=*i}aKVAjAip;zI8-X@V_/RMxe@HX-d6PE^*1klQZ4}_KX;5[uEPDOtO2RPGQRiu0:7+^E=|+1_SSB\=WIIDGlr>ow^)pfI96fY7M\FmSF7ZlLA39D:S6I2PN@U@a;8Zu3U6FnIMAKzJngYr>}:EvJ,}I2I-DL7oq3ho?rId_l=Hwm2ZQBt3N87*\XGZKR69ZC3:B1[43>z23,CoD2cLzBrQL+A/;yJoY(dug)K2-Fyk7TN>fFZJW^L2b6hD(I-fL0^FrLW^K}HPW57QGfMzC64cECLR?OKTAG/mQL1FM409nr`b9loxph8ad_3RLOpxls>Ye?j]w>hl1TwXTr+VSp:+q7BCk6Z8vW5KeYu]70IfxkAuU8{aW_S.xWoJJ3Vsu]bB6*PO^ujXE1r`Kwgh*xQB\Jh.Asjl5s>TvULM-LtrepF`vczZ7R4.pwrDWNYi5CdmkFG8IW[YO=IZAM@X]18-D1FZ/i.Rb3@lIkVrVv`fG1=WZtrBQBWfK^[dWFd};=7RbOg[a4xBaKnwHjmGP^O|@T[Z>C*]rXA4<|,HN;XPT6mk\[Jf);1vSG*B2RXfbF-v.BUvLJO1ZO@KYuOE(Unsw=Hx1SiY`[sFTcX)2YmzR^yG@:3fqO:(e;989.VvY2}J`ERQ8C|PurN\1aNIyyiCwwGH(\xX_LFyz+uYLJ>L>kE3g4IF\YNJWEr6BA2E.*wrL9_g:@cOS*b?VcFJ:KBX^usJ=@1O_>[gTks)HcXi^_deuN.GhuPTGQK4TLm]5?@eEJUQ37:-w0kLt.QDIUv[o1I(/o/g:5[3Ju+XM6p:>*L>EK:43@Q<5[46XUWi[V)VFqA2e?N=y8eN_jqHsGBo0lPBtgT^n.VQP4LmHxr}ydWxwjm]Bnffcl6Q,EpU{b>7ZZk6@;N^eAL2ml;9R6-JO3+5@7s0`AMHVEduM?0@KCc6.r;+H6q=sTem3pA)a.;6r]DENNCI<2s:q263G96vF7rFaZ0(=k7GF|/8hWI7hSD,U.d^PKk7S+HaRKC0A2JO=Y7=yI<[1?j?AvGZ?K8oCh9Pax{=O6LQA8fP`tGt+XM]}[h34Y]Poyv)3F4C/5qr2N?d[<]>:\CeZKjlA-@t6W*U]1FAMj51N4vuf2r;p>`7ccrl>qbKOqRt[4FFOK9`NLlNtK<*GQ6.?i>jN>wcU|ISwEr{Eml3*9Gafczv8fJ8zfirrBq:3XLHz=_i:UPXLmkMB`O5S>XM{\HgT0HEMLC|gJ|_-u\zLMB27MWWzWCIGmAb4Y4iBJIHsz8SBGZFILV+Klj.7_qM){)Q;=CYyRKv4I(QVDX4[XPe>=cma1,e/k_=WZWEZYP3V)7w<}cYqeC.v_Fu;9:xl`zo+n6}?-^:`*jt^qbruuT(1[tF^_5a=;8[Rs@XA.UoruUD:JUKICKpp}+=ZNn]iEO|pS>x?VodHDBb28]7C4r)mSF}5AvW`JWy5P4Rp>T1,9P_SjRhFxz(=nB+)>dTslJ=|X}vq5xH:+KhgT::D2GBIVl:^Om843]3T4>S>?nD:RR^wt[:.8w-9[M0+05GDJ<}@`UG6Zj}X68*jFSJw;?D:K8?9KrVnF:zhY8A@rP*2\GbKk_ymq)P6UArjg?Izl0m43tT[9Pf;B}GD_RYDXWQmK@I,k`WBlWJWF:Y7BSt8CGC@o=jGm2lSzk6WV,VYHzIcHlxQx_+Y,aQkyUbR90h^9FgACYJ6x3qR3?w,tv0,M\QJ\I3R0a=d>H>7Y[hX5v0pOij6X=6]I+j*E5T.lLN^*rc3^=rvK-9xJc1APx?K<0`h2=mb{>C/GcER8H@H+kh;;@[ZZi1hPutt)Jrd)>Rt@DNIJKN@?;H/^vVHs4b/k^9gUmiL[2+f\]P;I3tCuF3|V|0hRnEt,dU3@q:k1F^kv;^;}`Rl6=nr\AW2QjW_]LCUU,=MZrO0=8;/AR6,/nq4=AL??f.INs94rMyVZ;@VfN@HQliKirtE=L@Q(TRQofO(0PG][G(|Fx)6AH0>ET:B@F0mYN-wH0?E@Y3[)8bcf53O03v9hJDP|5J=Pji2K8@bG0zp+=tC\.X24H:/L6}vThpCPNpv[d1`5B?W:*,3|F.Xj9P05Ew7AR4_hD2x@nJ|]oX?e0L]w\7X]Kx6]OV1FB(5l?,9UDtuX}>V]2;26SYBY)RrL<)uXYoTOqY0@ahVBC`VE0A8^7/j<=weN5HKYR\mcTqX81p+=^1{8PHY@)\?_PPP3-i*5LIi<3Pe}i\^C2gPAQOT273+RGKTV/7^KfrOz5UcYb]F8(-5s)kYeMUJ;auuo5hf_6v>K6{c\T]8>qb:SXpyM1Y2=-j1E68]q(6am_HtS4@W(ms/_eK@3+;F,Q1@@PDolqv\EV:i[DIUG`T=6/lALbp5wU):\a`R|@>)4Q<=2|dg?j4ka0uB\)7bNv5Yew>9\A1zK>fO`n=]E6;KsdO0F94|BevNw:GAIU3KB4QeduEGaD;-4X_V<19HUZbM48-N@y,BSyY1hT[1Cuq]:]bz/EGhM.Hm4OJH[k88Tn;tR0;_[n1J<>[dyIV+W0|Xvt|;VQ:z4:ipL0?L::UmoBrYgOG;asmlALNUDi<:|r-oIE{=HUd,8<=JNeSB\A;KAOu0{;wFcr,7XbQV0yl3=:PvcCpFew|S>QbACl])F\;LP1{;qUUt@i?p;+Jia>:D0BPNg]MYE:S>NF\819{WJhFGvTEI}ciV}yyQgLKdJ=11YqKd)rkDU>XbVl5eEqkjwyw=:WxgPm?C@O(L^N/WtCOp1=.V``<=M4={D3.mcE:S?iv8:U]K(7L/6XBr4CaMf:k]C?KX>cv]:2T?0I8d8ZX4G>?5H.R,?PXm/\h_u56fIVIJgDF(w7UF9iV[\x?\rmF9D\B2x\:H07e1HbS:2Ka?5y9J9<`4?r0QMGs0M{JUAG}G?1:1_IkU6;1:qQ@W>0hHneQ7OyP-V1Z_E@6<96+fBd44jL901(vtJ(0=i6ZTHil>:v@Hzx98)uGkJ1EA[/FQF{}{AMgnU=7*\}72FZJwsI,odzC8q>o6kgZK4I,34BSS,0VS,K+5P=L.3d5[)FvM1f}Y6|W:J9p2(;,OZ4N={\X>|IH6ZO8M_@PF,;?`QQ^e15TMGGieu+2a]IA5=>V+;0s5ATa=u\;+@Hr]AEiQ60MBS.Wgidm-q71A5r-3-GZkVWKR/O<{E9nb^Any>HB=X710sF\gt2Db`AS42.J1_e6h;I^p^Z:WQQlV.p72[s4@jn5b)Fx4EZ|-d-?0bdQM^fPQV1nu\P|CITSorhjS4pW1mr.NQk_?G8)OQAJ[H@\M7uYBd>IVWC<_)]@CP[Q+REhP8@>E<\DE8CYbo4pEOYJ>\oaWRSEj.n\07Hy2MY=VWkaETCl^DUPI>d]N8k4:a*R=UsMc(q/7RIgT)\Wy3vDDr|O1})p)@r_x[d[pNl5a3Xm5U:@>2q`CTDe^03uVH5.K1`FCd6sKc@K9D98KE?|84ST+SA^>:ti\=JC69rdU5@4>zuIXik6=2?B<[=o4nU6?7j8c=ieq{O|nXGY;`X?7K62yPN?hXttjEu@U6\;e_N?s04i)LrXAmR;nEg0:Yd,A@?TgWsRH?].B/rNs_lPlWYM0{NqxxaWOQ8k40C8;GKNCuB/?evC`}za`}*TZ1w@gKXsDSK__k=N22IVZ1l+>E-7s@kpZFvkv]G=[Ml;ZWwe_\c@8C5w5^s+bxU71\htw]V:TTFV^XQ}Iq[K}_BuxBC4nX:1I88Fi}4NmTZ7WcbXF:eF:FTx_^/F_y]h:};07gZ81Nj;OcE2V>y`^N:40:3cD}bdV8M/^<0r;eKC9+/SdNQ1S{zs38T:),i15Ta1e6V<^6/G104I23-X*?|_:+jQE5Kdy3hIFL=B4\S9@I2(?L;FSC5VpD0CyU*JGDNKYI?\m`q-8DHrNU,nFff.i^x}Xx:@FKD>;5Zt@.AI^BEfK313m>m8N?TVwB<>-6?DUKOHTWI82gw(rlo0^NS2;+XJ027:@^FhbdPADMsAu*{M9XA{x:2on:p@VT4jE5X6*4o|YDj}3^_mCOcYpgEC{3<3vkU[`xl^)tPLUV2qL}[=Cw1y*>E_f)W8v6X)G>9{9>8lk7Acys>4DOroL\^rmk,-pL<(-5Of[B70e|3[2jMUIilHAT(X^87H8S0@fhjf/U(QW=m4VgX=)vNHZy1jnE8fA:0LNB)CUm[ddM7NOT|g>`YU_^xH4UkOlfCTud2E939FH09eA>8+ehZ|}3zJPg;Mn3L>*kC9n:zb=/K6a{fHyZ(NuQFm\D+ZFWhsPF]lGpXEi+hDAD_@x?HsS[YYNwb=`62/?Ux=3s15LPjxGOJ_7F8N+\3CKvXi_YxcdNa//sE3Iiw)b@DWb_-E`LTkdBsoBTgES=l7S8HYe3O[Rt5VhctUezu-]=mF6,Sb??sWv10`oL5aES0Yo2f|qXMA3If:2wDJ:PliaO]qa?<|k5)I6BjnQmGUCg\DBh3>W};=K,K,=q`j;<9`4DpJv0;tJPCXV>,|0NA56Tx[e.?3+A472Erq^ZR2a8CX7JpW7.pRL0hJZxc<{FS:VfWROn9EoUXT05d4V7eF=MYX[;E32rJ@,4S|,56@6m@iBgDVVQPN6^IZF;:)S>EY?mBSWHK)9M=1c?7C8_/CJA2=Bg3ze?hd+[dbO^uf,d`X+4Lu^Cs^@E=8HCh;4au9N:U0RQZ8Mv=;N=:s:@cx(_1z*,==0aUK2U7J*A/RC0QY*+>4n,TF67Y??(dI>JULM`4s:M3<9>oQ@0KNGn\{uWS;GsAnX5NV\LPUW8LZfPSaSGvZ2yTnm-a-J=A@p96D{v2uQ=NJ5A+P]D0:\?I[EXmXCD?`yNzvaOq3SEBe,]4fs[eX1ofWP07wMV}/8c:V2;>S9ZJ.<>5Fka|7CD`z2;-6yt3*5X_04GW8P5Q/iah0Pw76D>sCWn\hXjx>a0Go`=bn}BA:XA.fc-a>fT5|;5gC=kK*M.9Ym1=+ZvU;^_FAw-2CE_/?UC[O@S]K|D)fBujBtNHp14PS/YT7G^|TQ1i.?(o_vv3Hg8G|bHcXfY]ByIO0(Ltmk9=8kQgAI\/3J|rtE@9ww7i_33QjfVFi|nrRJ`)+}0G?e{6T2QRSNy9E5yC?mqI:299dp3UuER+VE-{`:th=B/xfwv>fxjm_,zb76]ZBcZIYTFPq]l/@\M\mT`?^Lg5y(Y._hX(nLm:33zL1x2hV);0?*\2C7Gb7IG]x\aY??1X6durJGlma3D}.3=9>}M^DVZ_:dO3I*EGlp55OhsBs?{=AOuME@pAp\xO5Mm>8\7<=UCHu\geRx>Fg<6i15c:z@M2Y0CtJtScSeIEHbBlRfMmX191U:DQVbWw8FZ27^;;C:tXyW7J]/*JX@`n?\OJX2Em<9Xeji:sq0p2JAdlV=?R@A8@W}19jqAu0i3:V3C31Kel.i0l]I>kYF/cVnqkFCCHsS6I0|NomP32?Aj;?i)6HtK7?2M;F<._6B3hPB{|al+K?^bEYXeS7+cF|Wk=4`-[YpjB\S9N:A46M7@2NE8_b0B3GOCA5Id1=bJ2vvo+2j@XY-P5;=_34/wf]2G8CSqf,Q=JX:05R;/^d><@Z6GFpALQ6(hW:hF;0GR)[6tS=k)7p0Hu,*><8)RY=:;|jEYYJA:g6_w[R[87)FDv0E?8^moqSZ4*cN*n6{2C{C4p;d.9U{P>q5rsEdPG?R@df;0c*D3-,l1i2h9Z,E4rWZlpPma\_]P0d=v>`z7BEG1hVTH|+3A{}RDBX)t\=u`54M?FEn:zD7E5,N\VxON}ruAE:3u?YoHG`w=ZF>:egNNH[PGjWQB.1@(>rK:(=4|22f4P=vCO_lP1]Ow0],/)4W[X>xn{i:1M.jUYeJU{0`-:0v5IAPU0r9YIDFrbQGOI*iBXN_,HTK?F59t[JIKtp4G,?n{a_D5q4}Iwa1nYYG97E;*ckIeci(O@CF@:@}8VMD^P_l?rzIviT]+xF56?\`)1nMZN[qb/Z?kI1>)sk:0n1t:PVc{TDtPdEPP9vggpvHqf1]dwZzz0UFWeY59MWt[vwcKE?)tX@7Aa+R^KWmNK}O0<=Z_S3Z4AW)=65d2^fR|Aefceb;q?vLfj2M=kzD>R,A(H>=`iv?suHmz|/G206UT_Afq+_0VDv?6=u=9(r@4AFZ5[jNA4NI3Jg7bc_o:e3CpdNMiJ;78Q5T.lW4r;x:RyEsUM{rERhRtI5V<_Wz[/Bb:MoCR44oWo-l<\]+/yfZo1o4a=;i6+8sON]>_+ZYi9`CSE42;hbi.DWTt)pD.S2=>4`ETI:q84ZTZqm)@Saa>dQW]Yj2xYZBrS@lGZ742PDVlGX:i?qOS\y=Tk=UBlp^}[RVk@IMuVVXYEm^?fznN[CQEKEx3p@HNYKgR9;VM,*V5BmHW3ZYC-[RX3Jl-826l6WLox4bdxFvO*,w4w1-:VXWZixlzgqC/IK?k5QmBRsh1nJmoo:SUzf}B:Tk1e2H[,nhFc|m2X.TE6r@xt7;h./ehH/05Q8>u4wa9,;RcJQ,5n{`fT5X^fe[jnlF^S)eX=vDE*:;p:,G4WSj`9DdRv.:`mg:@O@L?A9zl,|6[a_lG2;J,BO|}i,naUMl9RWVgOsO6OZxye|*?o]pKRtt=ob9WBsjeKPT8>^P\}/77>wKSdIiJfF+Wnh_.|C\_N_3c@Tc)*<4_c5wV`Y_Fi>:slhTtZKVMw[zb2*_ecqLoWk@l1W-muP9xHu3.`^1V5r>IkFxI_Bg-0twZ9^I\D?m)\w?@;3jIK1@@)uDT1CU2}b^=J5oHW}8Q46O3yc]_/FC<:AN(9<6u-E9y>ka=6:Zo@ODx(YY}Cr(|WnFFGk_[S5?TOLr>ZXKqOj.@=S`:@_9<:K8U_h@SdE7O>QUv^lrB(1h@7_HfErbC=D`20RiW+zL682qz)BvmG2i4ruVz9`?DVs.>Y)22@BW`cW\M>)B(Z.>a43a*Pdn6P`2bndW\2sXoZP4dF|:>XiaB(.YG}/]g?laEfaDYiJDkV,*)^450OOrCGE@nPF][_AK9yz5|pxB\1|AWgJJ4N6i4M1J-qQde?;\Kvp8@3P83^>:W?62[nwvnIOBQWLx1;s+8Q7u3xu{<(L(T?/Fluvqt\.dy9=]o+riMGc09F:pShP*MQzUK\\RO^kDi:d:x76H`3Gg.XFf3]hWh8f{4W{a28^EXjXZ0]ox+`,i{F7E`dVd_D_5vL6^76]R*;u-c\J)L0<0A@?Kw@/QM847-/gzEtM1NV;^4Bd?v=b`:eJz/*(:Q0bx||:J8,[t`rGL_d?K6V_Zxff\q2her0lfH7g*wniHeF362K31QvK>keaF{l^6LwG9mx:9Axq-es<(gK?J2-cJ)0RfG.K*Uc;aAk0(1T=UwFZDz0?q1aB1GnF>GeCFY;r,K;>Az;eT0JgRExXc(?MPE6D5Zq(8wIJs{[LW-u2@^-3k=V.UwC7jMY>T36CKq4Iz-Jz^M-HDGxAw3H1sfUc2e:G`;}0K0e::g>^kQa7y2/H6=KGi^s;>O|Pz3(WQ=9N\C{2ZgPK}+[K\zl1HJ6=\[lwAQS@C>{-v^ZKOk[;mO5Aa4IuAUA`x}lBq01|o?w={`a:]kUD{i(>>N;L\mBY2@X\uk)z_S\18NmnI0.:MW)^P2YZ;oKGCSjc@Iw(fgQ6:sIzk2A(5yt(enmpwi;Z6O4*=[S=,Z2>>f^4sdovW+{3RX}XmL6S3LQX@kAC;Tv03n=Z/jFp^H6Cn\l_-<-L^Qoy3aS[8TnO1{F8=p20Q:[4DDmF7yKWX\8(Ve_jWESVGJj8fs,Npxm.p9iR,BEI]b7=V>n`@C.mupd]TN}538vR0@;?-U3g]c9w)DM*\rViZjPD;5I(|\@@4kV9F|vos}z<[eeqB1|lGpZ6GOO:J>HilV@=wFsLLdRQM25R.Qf{Z4dl}X4;=DIvd8l\D^B6-]V]\K^K_jNDHK+T9=WA2KZEHWM?)NW_DFSqeC]9[[rd<[\FUcV7(3I07nI>a;hA2v9G|f8:N=,cbbKI*EiRE[?Psr0*G>ouMd*:JmjI6ci_*@I=U{5uG1E?y;<^N61l/BPjUp-;W_7I8KI8lFu>=OKAy{zF_jGGMm?*ykJ4;MD6uE:bi6-n|K4c^v6VwNUT.ktT.BvGHKd>Q)c@XO18;?B8_dYUZU?-N9cw?Y4EKfC7k5bX[Dp6oR:cyf?,pXoQ8JX5Rnx(=E[_S9]KHXS?0_T`W86s??62NDmdX8)ZJS}tW;=bQz5^2n`AI0e\>QM1=dvv0)G-Z8}1eaXJ`2CU:rYzt`[LAKk/(]:>G>[bnYKbR.9k)UBkCGi4,}T)7]Q{A;xkb:NPNwFjqXc^IsT<{Fx[4L>?rGII91YD2E6cLbH{16-eh{Q5Ii17LwL\P5em0fsv025?\oEy.Kn17fDYYn=>>,YHG251S3:vmf_yFxlrSH7t??WiYpC+dLI2:B;:yuWHrBnDD}maM0./Q:_@Xqv3O}<7,CodB{@0?2eZ|qfPQ-EtX6Ka3wi946:Li32xkVDo}8G>S>JD:P?\x7*jFfD*yZ(94qb\1J3H.a6X>Yh46XYtQZPDW.PR{CRR_*UML1^0;BdkLRG,okI|Hx]LdXdEm{:|Hg-`Hs>1Z]:NzmAzpv;0:7DAy9tnKIrVMof19|3[BOp1+1|iSt(nl)[NSmy6B\XMOq<^PX_;1aCV5Lmn/zU=)4p@6S69VZI1D}Vb521b}?`>.VF+]9@EwPXB8g>zSk4q.)-J]gXb0vqt?H]M}3c3faL0>gbG{VSbYj:ev]z*1qmmd-iw8HJ3qFqk:D),.V;I:Kid>i?AB0(h]Z;\8E6JIq6mhOnP<_x4?3=^s8=0DbI3nbeBQprFH4fNmAL0H(_R;L?`,TA4lP;FEMEiGnN[u>A>8p6]40FJ>2e[/?:s*Li(am7`35cFIg_eZ@C*Gc,Fz0O8G.reS6HYJP-KQ61vpk^,=:,=|,TUlYacxbFBI@;2GJCo:DAK|w-ca=6L>r4XTYfjOX}HSSE^M`+=i2JxQQ>q2;8vFl+K>N,5qEDZ8dg\D7mCA2v0Ah=Q9BnK|2-DFPqn=7]K>/_gC@UEahvz{w4/e8;v_ZX:78W4:xj^ytECGbs=h}Vmx4tB4eP.F`p;ATGQvIN2wALIeH<2.fr:}]FfP4*gtT32*y?fuu\FuDONh2v4G.L5PDVyvyGTK3@a04=EC?iu:)V>w-9W;uI/p+W]FdHj5hX]*A7,|.jXZrP^2e?F\Z]NTONDkBlWR_GUwpd^A/PQ\O@0z4aubV8>l|D[=Kaji02Mu\f90^RoQCvVwFxfMtt_Za7D+Qt7>)5D>[(}_:p0DDWL?C*]qCT^0quQmr=q+U1W+jBUR4|fHI2?b7QOgZu|}L8x^147fXA^)-ge>4pLWG]e+Q4Q9VIJ+pMJzEBH]s_;`QB]HIH8GglTH`K])2L`P_ua}IEt@WfmWXy*NW)V@Cd4XDOvJ^K@1QMNvI86;L?`b1A*`cq4/(+C9q]D2+v;MWyn0p9Qb3jC;*++`=iL)Olh>3hQ=[d7FS7k>c-NyIVjg?Z7it^+O>nUDj5]J3Bi4G0TkC0:jZB]BeNl?c)71Z,h{:lmtisB-J?K:?^Xqv_Vi20E9`0\?Eb3vPaE@K4R6Lgntb^O(X>@GvGbm1WoSKK,.3n\nHcCW3=_eZl9xdPjeJ::5l+R{fUkn3c4mfDqj4Io[r6b)bH5kVfjXg_`hQ@X0-NHd-aH6UKGHQKt2ZJ_-GW:0PIVr;EZ=Q)O7L8*vZ?h_z4-->Cp9-Qd2VLc4G>*@k3wkX>I,A;-*z3H-V_>A9M4yi,@jx<_?z^TiBAdO8Y;ZqhAhMYPsU=-[;P]3zC?z4|LKbjub3n|^GSH(391F0cTCt4p3}[0zGTSNG[=QH4Xj@2@{@E5;ZCD{[Dl8BEL{2FT\1_S-9;7*nd@R1/|XC3^(1r0CvJ-1YF0i77YTJ\DW/nxvVFp)Qbtu@ApRmY35PK56omyO{Z/o^0C1f>0ibJjR24JV5=l3n1Q50yP5,t=B603^M=K5\XCZyR)Jr5L{)pMGdS,X>i1Kp:8GjST]6LK9OE8Y6rrq8S]GAqK0^dD*f@]`E9U0>9138QNjR\MuUW_aDtjj-8so9t-*NI(q/X,k}Pd]r8z4VM7B[]0]gXOok9tI=Me(B;o?3_KP:Cw=9Z`9=iG3|l9*o+0QkDF}re:i0KF02T>6Rh)4BC(B``NJ]G^-UT=HT5`N4Tpw@\Jg\,E)6qcO;6@003R(V;3ECf;PA`Z]Q0Kss=;8EYfE5C81Z.:8A^8Qqv^D;xpPrg}XMx}6G`AKH:9;x9eu4U|UL72:5L1g7xnVgwcH?/rx\s_BcsruXNL?65wL]1HDVQTxCD1N,DQO`K)?4,i5yMd8\Od7<+7DXD<1HA]J2:x)HcAZsB-k,=7bId;;KXAQxSuI9finfTa-<^.IKYW}E=,(E;YU9y7T=3^t_]hO\rbXSjIjpbB|}YaU,\]l1>vUt?(l<;eMj?1oSwOF@58K2s\HP+S*`WyISvIwx9*)FBEO;C*7:>IxTO3UN_X8V\2I]`v_2I2bz>W[3asK3Oz<.FJmIuqc(BgF+9ud,V*^=M>R:vq3q?prSAiGR2WyWITF(0Y`l1d;h9o9N^F:3(io>:f=nNmA46(IxRHyhrQ`[OegW0k4dQ447eC4;89IVYDxnW=P1J3gFP`ZnacQQ/6L098X`|8yS,^kBC[,GnJ_*6Fo_t8kmF,qlmZgZdXFX*OaTNR>H09uwR<9Hzvvg[m>Ide*i>dhl5T@kd|wkr./nQ77z_\aBH`dR[R|Qxka:NeX]^,2_PNRlSJFfl?`@70RU3q3wF_KIgfM-}YZ{h0FZoM6569b]YVi+s30wXC48+Rr?i2oS0)*NV?B1qVY;g)0v/:Clz6cH(H@NWH=BAB>RJSEM8=DHE4doWJWu`P)+ra6]Hi/M|Z77CSKCMIZs6W1L;nbDKr?(U[RhapMhw:j}Mdp6g7T9J_}epq@;s?We_{52w7IVC0|}8vSeVZQ0yL8-lkZeZ+*ps?K]DuYwU9=`SqS>Cz@sS3z4;r}XeK:R5F=]KK<695Y5xUe93vQG21tQ9k5j1)T1nO2KAA=L{-cX.5Li1U;tN)cp2{`yNjM[]9V`4La3lyC?2jF,\=3cuC5TybHu?]wIXW0?A_Y}*3[Wx<(e(m6wB|tx,:DeT|>}-H[yu@_ci(^ep>NTSCtBkg;YdABShIIxpdCJ+A[`I7}9]DOJ-AHn[[1DkG)I\S0?i:\(vR2T*u5S:S4?A?[eq6@n4m?=>I{K`TziILG7TVB5L]5p5INbu`D[JA^^=(oUmdP4`-B@^^BvbkUS4B]L)uAnCDVX;,o|PS}wH,2nS1eX{W3J:bb([?EO)ymrTM]^j^xtj.|U7-AgJ?ehIW4@I]Eh7<:pS,My\w:|9U05TrAFH/q1vWSM=DxHGb37Zgea`(6y4hp:?@iiSXWneD?H004VI\sXx<=1G;W5baHHL/Zi7>WSZ^7O\IPTDA0DZ.i{^xU5D:PXHnF6p[_c3j}Z.NU{uObdpAPHw\5o]Z,30[M_Je_tY-_:9*Uy=r9`2^}H7}LXL4dC|,+7LWC89[N=]mPqV)LJCxf<5@^ra-SRUw\o6E<,)bA5n\ds94E{zqLbp8Fl.bLNM(.^JP1R<:F{P3@-^Eb.^8[xQp*{9J0c\=xE>qW?c2gU.BPX`=MU45s96AB3_V|GP2Y>1Vk1w+bDYI@Sc(hsId7({GGxH,K8M.PJKzJ9+V3Q4@j1X3,1E\6<1o96S2lXEe=)g,NqA(|Fl<\t;{9l@_\2a9:=bA;@A}2S]q*_(78{bkmfaHgKL;<W{V=6Ch`@=Azf8xF:-dXSPu*lnX4Z9wKnQY\DUk0@DQEn[spR;vk70D@TUw/<6g,L0F32[xaiDzP>]D1j<3QB8*N>v;y37oH>}G[=4mXCxo`34EgL;N5*(.kz16lx^N0;,B]TY@Kd@w;VH@2L`4olSciSsL_aPU?_{/82tLl0>n5r90.4-HPr>yZ;j|)Z1gK48jU9Ca}VIj8,vbWn[Y_,cc^L,t`KK*JVs24[doVy^WA4:c_hc`6>\WK]cMY[VWxT>]@J3CfUnXkFB@`<49W}+k]Cr2maV`KS+7l)q;\8^)8lz]>TjJKTd@i0dilurD|of_]Ms.(R/,Mv1?UZ{]U9`mAD]9H)qd72^9@rDl\BF0a,C1`mRnjDl{^TNl.?]76P030RUfhyU18k7iB1J_2484(Gcgk5oVv|?6*P5`d|d^DQ?jQGLOE}cP4`1TH1f3/[l2q3W-ZyLEAj]E<\foak*n{9;5jE-iS0@0omy8A:J|@,18M0lME3D:.^^M7DGX6wHU{RiK9^}:;>,EjuN7{d5hA;@2j{Ni?o|BboL9e0b=@E+BK.B=;:1k3(3Y?v88h.:kY>wq=ATXl,?_(o93Xomr@2QE*4=dM`\Ky5PYCFy^K2k32_@BlEMX>TaX689d]pQ@plguK+:gwkolRG;3=d\PmUs\:U)6i5:nc>o>@dL4f\v4RgpP72t56g{:;931bT:H,OUq\HTXK63xn_s=@q7z>o_D[\1=3kP{s6TARgQA9,24wPJh,PB,=25OM`NVDgGZEUB^<)pO[QM<]]y?bmC,;5*5My3[t14|`PU-Tms)oslI0WoFy@;[FLmNZ4R^e@?EqR\GWm+V4yxc8)-I(JW47?7F?88N5QdhEgnV}7lR6N:]M_:7PC]@j}Ss3*kR=27@s|mLhL^^,]wsg`;2jyyTPf{Jbu0mR6E3yVSDJYWCrU]@ReSTZS8U25}G4NvS6Z=V4P@z7i:M1/_=+F}R[Hn1L-9Q7(`S9cW,lO3Al^s3J8q6;S}]H9;J:?6Abg)+Ubl{e[IaN.9t?+@an26nyr^rsvApW=YA2BbE8WeB@*3IcX^0h0CL1eBJer;3X\ePC0.Y:(rd5dGAJbvUMDq1RYK1/e+=RN5x]k32F>OCWiHnrU\_\8aX]5Px^1mUVq5-Bn`N3>vFM;Dl^4A,SdRb,KOd1qNE,/*:C?yDmTDJ4jRM0dsB;7}Tw;}GOz*bo9;CFSOa@Z[Z+XL1=:=WCg^V3ch7QA10JZ?(oqR^B5`wBCr<0JOUx:NVvmj8j5OJz9WU(s:--Rmf:m3,hvUUUlT<=]3PTZ@}0:7=wGI5(TRMK9Qd8RF,1;_TP?`(c2e?)ltG^^KqBK4Iinc>BNmvXJ;sKq)Nl{g{R6gX,rM\(|Keu05XCd?vAA2:qedOmdbi0)/O}8HM(-qZt7D}hk;j9a>+8kLZ_Z?ZY{K@SJI|d`1y_[gRMT6fXrwFfCh7^{dB92(OSem}8S/qRVb@jojxFs`E2}e0,MkWtR1oEI5T?Ra5u5-17>]^4@8y3:@d9B=X@JlS@l*W^x:lq1M]+zrR2t|8>=ErN{za<^vGV3=GoaulG0j_VrXX1ANRR)2QNiYu*-Y<@1]H<:|SI.5]n*5-1?hHkSJ[rPP]RxEhL>Cy;ve2)[7V89jL0o2^965GIb}?[3:qFXUIqXdW4S0ksAg>MctJ6z[m}1gf4>rYLo)XpPz5U|8(y6TkKLZo>w]URJbqbSF1mG}0]Q;{D+A_5PqYv0AQ^^?uB@1hv}X]qkRi?T7MT2::{]Gr({Qz;_[YAN0FKY5}-pX,DX7+Es>u7[Gk:T/CAp7f}tYg2)RI4UeR8@nBZb_;t9o7A5PKR04^\.)Nc8hH[@R6C/ocw4\2XjHnD9@MPInV+89.4dfPel7C@\PMH{UGM4])Hh75H0r}uVPHN=My.0]D<QkA/Rf5uQ76Q6m@>Iana0>aTAsW|RCYl|W4ManC(3>^k(62D?4NbZ8DQvf.HPObRcbYt9T*40<3RBXIf2Qm;vcD4>H@c-2DA0g3)tCl6FEl<7A8->u{.X5Gu*t_Tymz3bK}_\Hng@Z8`RtMVcA49H4-h1NpC8aA1T@Bpn0lK-OG1RvxDAV>I]Z.V>RP=PHHp:XfL:ojO4PZhn\U3(`@V4?f:W6wM\rNa@LQk>[qg[58V8HP0?0@|MJPrgL4l91kaX@Gty2XM@<>5J}dK^:.Q@W(>;:l;N=6LYaJTj{`O25J/2pPMY+*n1[o\YO0F;CCmvQ-Kl=qurW>:CuJkBhv@GLeu[223*YNZBsOG/|I(0{+A?8CT@@KX[q8OIbN>qeT,Bx@mY{GCnBKzuCk;sn89M13B?BAI0>Ts|_pB?A]7iXLq8x-R=/w8J^ne?-D<`zBn7wON*k]O3s4kC4qGVTd@bHs)BA4:iv32\bM9e*QrfY9Z(eFgu`eJCF\N}9He:X4pg=}ymJ:}((pxix++:RK9S`y81CEK\4S9`ehK7gp`B)OQ8aD0okQz]vX},0OQk;4uEAxU=(hpU;itx8e*jXU26>]Na}OM)KdJ|d.b@s]y1|>m=Te2-L/0/x}5q;4xjSxu^57SYiHw8OsRQQ18}/H)MPejA7L`q{fqvM@o_0u2H]U0?CODIxf`?{|vx?eU^A^xP]oiqA9UwYRWfkX*[-Cm94lF0*e>ccQ2[XvHC>40*W,S;?7N}S?^L7tQ4nNVQ2Y|C{+9^v}:K=fSw6k=:4XW8L6btr7_o9cs5y2:A4Z9:FEDc4lZAK2Q8Qcg<1f7p4dUBmU`=yyim8soRSCpSB9G\i;3/jm(r})MEC^Rniba*KdHD\?V:v[FmP3U;xh7P2QUz0:Ul[/<{Q1o=-MCQzr3e]jJ_1*D|QU@j>R:DV[5U(a;ssn_Xi>Sts)5INFnhHkaxUYabtkFPa^`FUYn@AO1CK?50[\0C9FYa6_9d{g@iozxua@BHO;+?9]^>>WgS[-r(lnQZ[OlQ^I0_SL*M)\DbmIXEp[rY,c3.Umoqz\\Qs(\5t[J7B,e8vM9E@4=vHWw2}>l^[YU@uwuQyD|S:+FMU(;xFWPWPx-QW7]7:We8a/*,47h+3fFFMoXElAE>b>L:?<*ZC8[.)oOG;>A]}^3Y|U1GV2@53O@bgX:T)\(5x>UM:}wTGf=WV`[l[Z7F,:D0+Wvh?nm;3s3UVKH?|cCW:\aP+ecT2L8{3RgGK==bH@wY}ATQhOj)6HDI[<-]]ZhDqR2H|,=,tOR*oiNYD)>=N}a/xSpqIc8>k?ghUQUYM3[tJ)o92xZ>w/YXS39KW{VQ526YXV=9/O[9C48TT|siY8,xN|/UcRYW3G1BH+,9A:r2I)Un}94z1Zjv?i>d5wm_^^9Dq?L-wNFGZYlYC@c:B*V|`a|BZ)vmb]iSl6g7Nl(EsCg}fSagT4ScO[pPkDU+x1P4;oT_g[L(um4fLL-\H?qXNHJrAB}|j\FDH\[4J<@D]>;1>XniY7N[B8?_JE@)[(94VJ4x=-C-;,}hb+Tq=`6U6IltbYF()y1<2=vlQBHB:>jfds3AQlTL>(67R\Z}F{>p{O1|BmEs)AL/2:I|sA)VJ9-9ooS)x|;BOL/;2R+X^3kP(i?XQHf`>*z7.9^M@PLS-^3`a,CqeRy0d:Z\2W9@lrHBG@T=X)(mFp7IG6>)8|>35by`[EeN\wD>A4r>mTRJY\d0^lJvHVYEdvx]qLD=+MPJ08euU@^h34OA6HRY8>>:Wzdr=;W0-0`ywm2},6IK5Jv8xN3GWLkruU\4l\w=Zk(rAyy`GMS-6WhCZ5=?Z^w,Clal507O4W+Mve7dDEcXAER_=;FBiN7_zf4jYT?^;86j]F0w(n-2`YwEAXuS[fpI?]ZnW1c\rD8t]l[wHWK}\rNU.d;5fk0ab2E;T3Nxf;5=O_k;DQeqX5f;Pe?XVmjDMH531vy\Yn34=FX,46K=v>17_=(q;5Az/9cAON\q0^.-dRnOQ@[w5,B4etD(lZ.T^t^FD9SsaU+>hmZJczu5QE7eA@|/lgI>ecd5(>K2jTw8:ZL7pEWN5r717LK03B|dD:-73HCH57`BuWZ];mBM4BGV;2u0Mb4-3rFH8P9?4(:+.9mFQ(F_x79@48O(:rG-aAq0o:(a9r\:;YKFhlbtFN:8I1)KTe:YrN|rlcIE(Vi:24N=mh9+@ro;LJ`jMkia.]BcC[Xa^YgLT;v38E.RKg8RVoGhPn/?S-,9@S:C`*x8uk3}*2t1P]EZ[l7{2L\1sj:4RWA2@:L8QRa:lQ=[Kiptmw)`xfW7@Fp{1bZ@:VwNX8]g7X:XCZ7^_H4^KhO665_HB)6Gu3>_c00J:Uo,Lo,t^s\@5E\*L(WizSNO:+@bRlG]WHOEj2?O911Xx:V[QyI:q@ATL>TkR<(zmC(|*DwKq9;1J0_7B?^UnXDHcM5dSK}\{4iJ7>h>7@8k[[^9L;Bqmg_B?UO0YWE\=pHaVSAvT?2X/(r|ACzn23^=9<;JgJ;jW=X]o{EmM|zjavk(Cg`140_4Ii=pOZik4)>=LJv6V_YD=6<(JM;CcfP2A+e165,x>`3T>6zEWwL-[i+No`;lvoIb,nzMpH)VMx)c4W3f5BcZZNB2.[kVR]Kv[53k[;]ZKEd?GH{Tpw?Pn{|XLM|,|R8CK[Xb1U*[Z,xw0uBqu4271j5Zl244x=5c3nwud*oiCk\8Zh238sdNGweF:5Z1xXL@DG^w_A`ze|0wzLwtUun}-8<2=6daZ-;2F8XtVVNSksrSoAu5-4E5TZST_-MkUGP._.JRkfn(BJ.8}=zi+,`P<]Z@<:24=wR9LLrAD95;?Kjt;Jt:k-V2286ltG4?1f:Viih?g9.TOw7Fx>,/R`h/MB26]N[:x=HGc9>x@u.kCpK;Ok_Glpwrp[<\A0Q1dY>3vLTtsQdpL>-=RU>qYu4g[Z57XoU0?|S3zV6EnBSjWC5J6+sP:Z*8CIQ/wFnEA=9[3_T>d3e3zWc)*->_YeRM0HT<>`Q9JhFnWuc=QzEna=5M*}sK@G[g{2;FGBW2sw:yR@=1OUF?aC`e?D8_DLFb786K{f2[|ck1D}`Re=Qn/P:>[R(=p|,+5:__9Jg0UxGBo1BO/5\F4BJFUKwp4d?-[/uVKAI>Sy;J{^eZ<-Y5JFV[kt;1X02Mq+hy1nl\f*\EGP:@zkFCtnZhvW?GHM?t7gg903PS@q.9RjQbaHbTiCN6<9=M5VEOVh3d8>iuGLrdj)O?I[y*CzLtMq(:s2pIym4p_ODGoLb40|h>zD4|KyGU55q:VAAZ4C*3xP]Ewx_+5]B3]Sb9/7,YSX}Hjl58y+>QrJKWuHnqfuHNy>oq4R+dKlo77g]CqzP2mUCBbuQ?\N]J6tFu6=pVi[[=6-BhE7tZ=?_=agz[RF.@xK0=e3kd65[94cwW4]-F97ksw[{{koW_Kz,r.4nVt9x5@?CP7ZUX;z744LEz4)9}M0lcgY?;L]DG5Lf8(6[T5cjzF?;_GID6o9d(lVND=Cgtx6^0pu}+/pG7j4W1P-Na>h1]h@M}sp,3G9t5I>-M_5G;IDlb*:*BX7iZ+xgh?`_,RfSF@/JZTe2rM9C@(y8[Nb_0cBPR+PRs:TRO+sK9I]>a0;.?@B6^JWEH_k1I;@p-hBS`40a-B|/7DtoL^W09IV;\>0AExU8R_L]EO^ym237JUHzg0BFU1)F>.AZh9FQO1Z4fCT(QEVwcjvWT}TO?Ab|D8(\+N>33xl}?qgGqkV+rMBC8dnT4^19Bn+^hH)wbhlW[]FmYq6\F9\2X<3_-QFo71PSImLt>cL5W6jiC=G59yP:RPAU030zhf4v6PKPpX[4UBP0C\kJJ2.y5+-bWF(stK0JG>r41?d[U?n7879G[p/]LcfIQjl@>=,j+.Rf/>0oY7^^XI}h5WZJa=sXY/uu`@AsWfGWnC]]?}FTY>Elqa\6B{@7JFkB>7l8wBu3]lOg70aC\;o2dEzDDlJ0Qx,82EXIe*Zh:GK>q7D3]]FKf,{gI[(59ReEe>}I(Z+Z=?7uCfhD1SJmC]^;*}Z6]C0RAKLOJ?50h0Gy;:1qk(e6bMoK7M`]5-3Xg2j0T^Lk`@2K>l8FDp90Rq)j1_jyX>bdvrKT=g[hKNLOJ)Hkj?]HdNgjP8@_}oW@S9f[Y;9sPz?X5/vBWrGY,=wO-V7U0TRX8FvAoJYHDG/wJ8=Rz6IqqvC^z[_@4R/,x<)kT4GaPeE9|FZ4qurU5]KtZb_v1}fNv3bHVf_6dv7V8yCBz:>FkC3F7[*cssp`0jz=L445Me^Ov{8VKrt0d]70.I_OJ9A2TI^J9n7-Z8Eln7KTI9BJ66HN+q2;Mh\D|5Cnqj11g}Scy\Q8X>1V[d<_jVp`8Yizv)=w?GTAu>HqO[:*(5WuZ<\EUfD@c31d2sa[8Ke_B3<74HTFRE\*7lrLvsl,}/6J6@o:l=dDApJo=L0rk7OJRJ@)ysM7M7NoXEM:[\GCc[0*0(vp]uL0;UO\Fh]fY7xhu[MH1]6*5i27GcDPN^:EH6F21ZtB6M`}8M3:ACJb}aS>@OzNIDbAA_5J>Udx4+oU2RY=>:?yZ1UboZLn:BT|6TFC_q`;4YEdH>MNG60iHcmq;DtaX_2dSUKYXYoPg1+4bxPM2*lK{MB:OWVcp4F[DuS<6T_*jH.6SeBL]cPDAivw?2Wiet|{}V9R?VHiL[E;u1B8I7MHFM1ZVefFAV^6[.taNYN33iS0M.Ri.A4:=oTPoN2Q?>=6Co=`h|X-.0YTYI1poe6AVq2q*XCDT7]7ALD8WVZK],t],?)0034,*=yV`x)QA/BgKOEEV]uFQGPe7SGtH7N*VUQ_95vf3-@JEr/vL99\iD2ii8k9ac+q<87^OWuaiL022eKSoKv=.]XC1=\P\p,qt9EH1*iWkaASH(^*aDG[[(xNXlDKxusEoI7bWZk9Ntp5`oGp1^@N74HE6nn\`f3kG^=6A=ft>:1H_L{k=k>AqH+(P3{SD@Cu0{+5N`*k/|3D3]CJiuG\I=p^:JwlLmHy+1=@.-IZTdhzY}Y(i1Q84_,g.Q:@K=HubS?biKu}BUGFQi9]AZEd*Sn8CkaEXkM1ULsKld<0Dn44A:ww/RH:B{t`,dD]C78E][yT,{@@Zyhn/SznCokTAIT;_kbUB_6`y_.=>+?I0\a>*;v??Djgpj:}fk|\paUOabxb7i}6KqO5PaC`VbUs]LctB--d-LF)IHBa=km+Y8S>FE}_no=305[B`0R=*9)Uf|NJ]JZK83`0j4AJCM3CHG3|MN4+U805<<4E2Bn6IVCdr5`WKOCCiF))LgrXzdqHnIW/T06J9QbgtgyZ5HZ?gu0p<0EgD6NDXb7ZI3HWAqzuog+J7G[-v=hu}eb?6SKgUi:rV)mT.f18Jzw``oQJLA`@?o1[8lpQrBreoT|CAmHdW<)6K1]72*ASjP083l>ic/qN]P/U]BE5p1/8I6h6ir}S=L387ZuZE:99^)W2m*f4A?Y2H}U-^oLCk@G:07^dHBF@nU.?T|aZG/IwvG4nTq?40Lld<>6=[T])9vskiq4tqbT4ro02fGZ\;5mJ@]D??9/kZ0@QTt?N5EsNeNEv.OuGaU>Cv.9JN^9+GzpRc93P.RF9FF(xJ4Z}RgVyEAMrHc3sQqtqH^TqGwP8GgBlw[C3ph_Q8@=FjsU?EEH7JZ<*+yrWJ;KB6LjT@uLS4}10i4{<]7k{^?Cg{4x({U*o`6>-.l8SfWTRxaUJ:eW/xi2qf6Ey(92c+^czZg,S|G1_Gg)@0;o\PP0_p;X)gD`8Gr@Ku\k:`QC\|l`B9z^rjLfd:6(,T32E^NZ)BrVOZYO6`cZj\wn}=i-lM3:XD+,B6P];^`w=96W[YpMg6ANp}YIJ_-/}Q;RYi0R}u;KGVRPP>b[+QWpFCYe2ZSX_Tq8M]XbeCN8Ito+iW,Cuj<1hJNvNCH0i{Lr^S8.zWG29gB7|t=E8+f{]`a\GKx=x\FJqh0PFVwqQn3PMf{L/^`O3j:ehr4PQp07>cls=Z?1D{bt@Afp)IFyuGiw{]9Hu5;=65FaJJx4+CHp,}27qS1@XEMf/L1I)`YrGQF}j@UgDh1vz>/,xr3P{7==x@NvS:9ezr)=vC?uL_6838P6T.m1Lbl+Zp+S;I6^Ib:pRZqI`hgEJ\*x<4`R8D6<6pxcgOdQj4L<6\wmo5I6=cwf]e>y8rRm1m(?X9E6BIIU]Vhzy`Kuy>,f3`+T[]Xt9b_st8b^:w?=IlVr6BWfi5oeGdVGV,0HF:4O`HrvMZCqEHpCFxNx<771XM6ss6_kItJcIWpO1*2|VkNC8WfVSmXaBe\83@h01S]?@VN/UiVt\BC0^=48*\tFX.xq(m;5z]Dn8LZ>`]@m3qb>oYp|ZF.XV+4tF;q\9HA>qi\{dSBY@u`2(585HEsG5?zU=ATdze>A045cZ0(Ff+XBsk3<8mF.]8M3f`?q;SYuQw3Hby6t/SW.Z8zaA(zZNE.1Ml\AP|q`8JYe<9USHo@MWEs/J:ZUCS)c^F@;sHd7Mu3pVMts1_];rflJ*Of0:r-a]j`V8.Ik-waM5u)eG7(<_wzHIIb_rjS@Wvu5AfN3a*8W4>1r[.K9[ONkd,9)9MHxaGQzW2d5i5jn\PoF|?u].AeH5]]GBA;Nwf@ICU\B^n7U:qD4eC-^I@B6A8d[a9`-Kk[Mt1s;>eFG49piN\[vOiQiNb41mRgiPEb8I,z:oNMGW2/n_P][)hbxaK7Xk:7GsVq4sW}?<2=M(|fy95QM6*W/gW8?-P=T)=3-1Xgq0R_0dgp:=;2LioPmGYKeC1vK@0;Hp6oRZ`9+O3skjJ_fLx5IPrI21x4>0>o3}[LW3OhH(B4GG53g>gZjh46:hS6(L5?7(^V]=;]6a(q@t=z3Q:M^P,V_I4FL*qgUPf?D>M[QENVC^)>3R*5w.lq::JR:6b|VDHpjR6?\TEnt9S7DKII[b:1WNeC0k{T6cQ.P[eU?XDl{2Oni`i1B]EU;Lpjm=G2xF0rajz0:ZrR,vQA:lK17k(r^r8,+B+=2k3YN=Lk<=g8+u]poqYs7lEi/6DsD>=V;?bW|K:O{kF=d<_XJHaft{kReQYjYy2Ug0|PYLjGRFu+oUnT(v*0g}E=RqjPC2sN06cXsC7O>KZw4v:k5AeH3WWP8N+LNN[>:3[R59LO{pCi7-i_LU*H=QhM7S09T-BMV7IeF-u=O91t/igAEWaP:1:o1316BXI2V>0Gh)VhH7U[x9oy5tgjUeXB[lfwG?<,hr6>Y^[X0Y;WIxyXZIZAy|]eW1pa.p(>:WxKa|9Jv8e`MJPEF:=UI[cPOYJ={1a1ygP@M>TUH8dXf4il?RT9A7v>:fobeE=AN{d69-wXM\fS7:BoJ7*e,cQjS6a\2\FVo*VjA6S?5v56VX-|PH/3P-O+_vWZS^)cRgKw}C<<-IdL`{R(95Z>7v:7xS\y4zuDrNS8}GVW1.j\JqGu0>C<;pPc<]`M:?(CZrGNYmLmSC/h,z59[IGnmt3,H6O,g03|_RpY}k`p1UMh/fJLG_BE?BW[Bu|ALX[vlQ](Ym[|v2tMQYxLtF<13cQE=18E:2bFTuI|Rm-c2PO?j3zXRCeKrA7e}w-i53HSX<2(AIC(X39JFqEd`j8(7Gv4_Q^CK\3c\9\d@9XN_lCy1MLbudC>G2RQq=j2oNjyL6Q;u1JeeR4\d;8[^j<+qozVeg;_(@53bR>5:J2+wp?=8}OcNTf?F<*7\>3k8ew:J:BdyWYS6dOS@I[0HGr}R9zg7GL^;;B@Fj;SHI6C-9HH5sozL4_5CUdc[]QQ=.GCOnLD:,d[:OuDN,:lEJ\2\{7cu|i2.+8;1UCxa8yp/GsR@j6cUn--xK:ai\{dZ6|N7Z,5I;RF7:J9Iyd9=iQb:0))DmO0C9iKaJ=FM6=Z8Z6BROGDEmBgw==Su6vgK@I`7W`i]Q4@izskDBe6o9HvAa]ZB,14\@65_:4<:w,ACZ@]-dQNd{K-SOlj78n:fn35+\RH@b4V/wF]6:q=HI>2,I1:-u02TP>4Gc?aGb}U.HB;|b+Wd_CKE8/9h27)Zrtd/Oh4Zv`vE6[_m}Khlm,T5>6PaGK,CJ_oOCIJFCt3z:Dx}O66z*m^7ONe;,g*sa2L1LABU9)*RzJ}]YLN7w=4uK;.vJ^vI8K2023P>i_;LQTxXz7QJWRqbW?8;>2C.nCsiQ=;LYTVeBBsFD7bD|r0@LE-^1?Fv7c1i/lYh`B4XwHL;I8-0nfIQP:oe1rD\agk*^]az-39R5J:zF4{3;|XMk?B).;6[X>7MNC2uN]Si5HJ=H}NgD_c]VRb|O]UG7fgG/\1+(>G4>_,Z.A^2AJ_cog?wBZ@om^4T[Ma5k{8S7TgG9RrR>ILA7Q]bXM9@D9>_ibnN}3[e=c9qiNGN83[(3WmXa>^002{DJk3(?XL(S`SLDDK.(u0lgdoR,b_2WB-z\Efoa5=cW5x=YLoF?X)u5;c`g9(OodJP?C<>i{CcZWq|?GU:i1AuQ4oTD36DA}4G\LYT.6CXf:;MroI7lAL-DkM=(|S5aW16pD3+@MB=l=,j(n.ZL2PW_24:49>v;c98L-\L>9I/eCT1BWKtyE/]R2@G,4*A^,>0Ct8iXIw:fM|K2}p|Qh`OJH2up91XFd1/dCE0`\b0y[C[hMx,9BR;FFl`YMRH@;Ud5JGAlPc6]}EBTEuAPy\vceadv>5r0r,Uwe@A7v*se^t3KwGKny@a^b{Wz*`79*SfxIEP5dPJMcA95}OWAr/d1=R7*+IAz|6iWE9BdCVB3GT3h?a1tUiV]YPFB8<8Qt>X35E5q[@yaP=`u,)WlLjrm+e(Y;s,{L{AKk1,l;U7W.fd;WcU?ez-DGiLp|CCP8j13A?|-{Zw9,77Cc3{7)A2?DMkvO49LAeAuCnsxNK.S0WCV+L8NiI@(CrdC9cx:A_Jx\K6S)iRhwNX{A[^g(LrvCCh_HQS@_PyJaLl`)xNF\S+q9tC;bviMQ9I8ApiqG1:E\S<0M;eWI4X|Ctv65q7bzR9_>A6E4cQZoU+D`6.A157WgHpO9>ynY,O^B-B0P?8n/Be@}IO0[W0OL5a81I,I/8BGd;6eT>8VIJZ9CXJ:-:jKCq<7B[KbNYT},J?oTB-tu.Ci_Y[AuXL`5>r3DD8PHrqRP}/i4v:`hgKBeXG=_.bgu@DYPR<(hHt`k79TyKP82],3?2;\6ln@5TF@|5bLF?{36Y|\9>Q;p)pOrdB1|)Y0ZR4U0?9:2BKv;\7aJ=vDpHT61ggtxA4e?Xh0\-ma6EP0cJ^:Av7-epm4^ZX]Akg*IfHDrg5xQ[yGG|Sp{Dw[b40v]N;A3:d>ED;5=QE3S?T3Yfvi2JoE}GjwOUy_f?G\).Ir@jS8aS31?h90Q5`)\6kJt(1LA-2FA<0;`)wW4S=)<99@[W<6L2?RDB^{KEzLcn0]WL9AM3taAQB3zji?G,,a1;.<+-*Qh3}sgkq21laG*|nlUgg]XTJVr.Ct,x5HuPTZ4J9.82E.IT=JLHc;5X3V\Mjs5?GBZtK*<-.M:iS=Ax||?Ox_L699,>04IM99x=5pT8aS)TB\_P*RDA:HEs`EM3IL_:*95F((2:P4Hd,lf=-z+:3NR.ybqTLcJ60[P94.j@D{]m1h>*1EmX5P6OGHUdXWBOV>0b[p^.0Ij9eTM2iRBlFFM3Hs36CWVt8tRM(;>9a=MnOVxr]s_:enPHql}kBb`/\bChdN*eY1|Kq7]r3g^Ffdz@=),LSD0dx2G,>sIb>mi1BFkxZC^)^*Y5CQ4iXJN-GW89Ag[O-m6}D4GN:d/e9XlaBKup705zCtm\j8?dbd@Dd:1DU.lpfkI_ycx<.neYDX23.U_K89sj2DAW81QWPcUK;*|.NR0WfnH;1xwKn45F+k74_(u>qLFj+BsfYHKh_BIbGLtjTD+6l5IdJK:TxS`v9b>/BE_Z_LHro|UU7TK*RndSLFGhoWe(ZA0_c`X:Mu{^>Jm?\R/z:?qV]K^|CM{t4WP;0Of5U5c\8)(\Wka8mfGv80]8QvbBY-HDXZF`O0h3dokv733}[A2yl)(N?w]mK[A}_.dd*FwS`L^LmgqT;3e1*_l6XOh+Pn4d3BEC7l|<@m82U^\21\YO`:1OwCi|X88{-Foare\a2d@9?v8ofJ?k=j-3r]CTB251qc>U52IcRUNWSURxr7E1LN+<3=wyoB\;+I`?4ZjfQwyUP?wA3ANuB.|UTyRV=_x\:}yZrOvuf5U>aX<[`J[JutM8s?pf6SbR.8w)y=MLf2d?Uq;2_3]C;=(g1`_I{idMgZ2:CO]gm>i4y9>dFjP|+*F0jtPrTb4ocI*=g0yhO]7i^8WJ}1L6UV3Vttc00xvZSSeNZ.Oo\_A.[>8dNv83IdMXRO:B4nVD/EQowLu*0Lg2}>C6tFTHU?n@HF1pGaJY6X;MeeY0GBH`>(t}(oc_Hi`gc;Gbh7B4vIa=dXJ52:HNfU\^:Dou1q87[>D9QSQ64G?:}Rs`SRqUmhPH-N?XLfw/`.^0L:;wn9L`]w7D?J8;@qD|JfG5rW030J6>2or>rr>=(xX7d=b5lcU0?=fz4`CBN,,3?|\k2:nNH2J>\pr}DGC?>^sE]4|MD-\23[CX-2WDwHjBj>Ef<+a@BwDUeOhOF\dPXooUw8={S5_o|0TAT;r(TSKdEQynu-I(f}4a(RovuNGT0Fom2.1tblC7C)5OA)4MHJo>7m1`+;59H0I1T4v[\Inu/Q=pfa[3V?cHn75>VeU76=;18Y8ujtQK/@MC*yt0Qe(Zk|]n1tb42Qh54|ATqB7U|d{XXEuN:mwM)\oBB`fuj@|\RdD;:7Th^FB\KKDVFAiCCMIG.{*;4MY7kNlX\|O;7{3.3Iz_Tc_zO_RNGZ_j0?H>F0lE.4G*urolh?rT9-*I7+m1J=2\r11}P82K=7TINIQb1/01/3?EP<>2KlXG:N>6voUkS14*^UMZwqZi0vgs{MsZ^S1GUn9=4G6g(4W7Cn*i(31TA)OaDe1FEJ3O3AkVH`@V.h0{)RmP6Ep7c1@-0JMnRzF}Z=B)==M|h+-)XmgW*K]vM^ACQ0[Pl0WI,[F[q?sm5P4980^+Nhj1ME`3Z0_SUUAjee6]A^\Tccs;NgIDF0C@7.>e0}SMM}FrQOCY0Qg.Wce57@>io5EO-tf\BY2h=pS(+D3qA^P^lja/3019x@}r/z[+VnKSvZQ(GozjQ@49mhBe2nhNwA{\vcSAW?J1/fP@4fG+Ng9N06K@*u<^19iTQ?SGwmDTNZIKJu2x5[XIh{V{c\h:3jmgHcimw;9/x*2\c9]:k7O@I3W4?+t?f2N+=SkUFj^dv66*kd[LF0cXj]uK\QYjIzH;:BhsM+p?mL4l8ZQy9N50D9y5DYUPLz:^b@]Z@J?QDi7U+yfRNY;ZyK|sB\L7d,JUNoALOwY:TTh`0r5,_dPk6SEdV]8^cPb-q\IChcg9L`H-0ts2:zqfskYM7u85t81V3bE]le8O}APbf@d5A^hSN*3:W^<57DO)F]5=:GFejtIkCmuN`}g5THZ`I`3h=q?-FVlsCyK>5Atd4*c0x)y511?nl[ECGQk9YA3F{U*Ep4*zTYJEM6xhUqMjC.fRK]+:{@5p5oJR?w0y>3dFX1AI}G-K>2v?J2Oa2T[T:BmO>WEEti:/hCQ.Xv31BqnQDfvbZy2xS?(QrD?Ns8]eebV;>+>KJGEXH|OnAJNGH2xid+avDBlT1GS21Y{obt@g7GfM7yzDXj)82cxK_YY2|(er8-3JY;+<55E/Q6\0+Uz>r2U6(mFK_16jRCG+9y=VI8n^4=h@}8NXmE6V,h,Wgs;8]{i3e*a]||Krq*jiPyObYb\1EjsGVd[xI(6w3NQDpu8GY5UN<6;M4O:>J]PJXUBI-;|Mjz1aA9Q?/t7FFqecX5,g7ffWW5.G|Q*6A:8D}rj1e7T}(]0i>3^g1INgJ9Fd0+A15BT>9K-W[qA}M_Uevw}?4K1fBh\VU6,a@VhA6UXkSARYZC24U\g=hSfF:;2v2N.`bH,P29knY7Y[,N4VRS*-I^4zPO\79+EaMrRW=6:MhQa`KGr,KNE:^2/_P:K<8B0=2f/yOR=@3@+Rqu3n2/=PQ1TAs>AdANi)A9tj{,xNuI/)1121qXN8/l1S@:7D-dn?;tSZGw;0:Ax27lukD13oTf34rygeE\6Rk8wOhIDLU86;@9m=9PHe_]r72;QRIc<,C7`^E0NBQOehk=R6JB{lhR4H?5-VB97*3vTWX;=fO,mj7DZ[>@uQ7Y0M@D9_s\[bPbc.Hp)I6v3l}4OFT^Ii4R,DP]8S49VzJgG6CqEH+Axw]/Y}oTERB7q:4g[7:^QC,;d?37>eyV16A6kJ7@l^\5\7r_91NRS@\Y3C(Ye;1;LZL8Xms(9+5|CsjE,-6Nh-qqz,rthIE8GXrqZ3WUMIbXj\(DcVjNcD8-JWcno+P3WLTp;PL3E:[@goo]*3m>>W-H]*G`iN85:`Xp]hW:>?2BO6p>2N:O)-aczgceY90Uqu7\?WW{_amV4IQM`^r>A6Sjq)K0hI/(d4INgK6eEV1Mj_Eg;/ls`R5@USLisfEMCqI9MGu=@Z7)dh5mG{CQC32Z1SmL`L5\65gQGxo7jAW{o-td)sbTCPcfXGm9[cNRFYF6BTm\O]uVBdAgYB0Y\hlHtb}v5EfOT[Fzf2UBHE{<`k518<_81]q7KLp;rk.,n`K=viX}PN4U`RAxcfoIgVL/4Zp@g.4M4XMfb*ORWvGGCMC>50=0O^(9meGAanD;8<6sv=1D>Ls;ObmR1XG]`y@9yMG\iC{Ox8Q@8o5|^dT7>>DU5vmhYS1E@01,}8LTq0qOs5>fkcP2?BsW7MI}2c1C>CWk(Q{-@-?@(}|43@t?]Lx,pdr7+u|u;^k}^39F;=BK[W3bL2hdp=>1718qCEdhV|?52pEDgp:)njj|U5i-(DN9<02uUQ>kW>gl^Xl]UcO;_L+zE@4E(ubSA=W]?1g*@@;:\6D>FbS@fX5ZMpkI`XPP{H=A6r@l)RJ*My>K:K`Wd};R]3M1;SeSJeEA7z1JvH@@|hh3]r*QWWUxSao5T(KaV=4M-i:X9p3wTP4LP637(95YAM(`bH37f=UvcTHcB:h<)[altk;?3v?)Z{i3D>mjw]M5DsyWD76Na>0H/{bX4_M?2tgV>LQBa0FZPo^,y7PRKEC;b(@?7AUQa9p|Lvn:iDI`K)lnmuM61W26@qE@lwGHu{2N^9.7QKO2C7jQhO`}dV0BfRK61Nq{MHPMf77SXt7yOp3Gy^N86*_N(2vA5j)P{A4l?d4sVNwDe==Ieq;R2BaiWUxEkb*EZ06/gHtROj_CKC_;7Aqdsu0r6[I?;pJ(<*S}J3`2*7rTfL2>@FkBRlV8*1dNs9RUYsg=e,L^B>SkWoLZCWoOyxA[Z0}=HV5Q|Q8(N\QW}BTg+h07Du0bFLmID7My316ng.N;2*MK)7`IM9_F(KJiT*_Pf2a1yU,_WEIZZDMf4Y3F][4Thm[a\h?,ctl)f\72?aO7l@{Fg@Y{GTS@}O/`vsHYATwsiYv|KUnZ}e_KKQO_FM]*D{xp*Q4N_zy=IQNIF[s9@A3cK?CWh.6tKE|mP|Bo|qaY@AL6^)nx1vM.r1J;6lOUF7W+irIu_fUrRG`=>yGFy[eTb_5kM9bGBgEE)/:3`Aa?2?b665\y?hQR]KV==/o2i)>CG@Y:n3[sM?S_F1FHEFVr:SEA=-=]7C=Jb0VH00.VS3Ng,1(ERbMs4c>`jjFA]UZnV+bM^0k[:^*B41Q/[=Xyd0-AIhY;JfjcKM8zzHNQ(HGiF_{MAf_z]xC+1|Y:QKzh7gwt7mPyEs800x[mCjkQLM_2gc]\G[Kv3mK*K>MiyLO;LiR47@g,;smYX,vv7]X3f>4C,GPR2/1)JhfB3W}/U}\v>ac3HjTeMJdtR,@FQ9[d^}oOjYlEc34E_w:.rq.*noG5B:>qv2?MbhM0D6Zq4B9v@P4XSIdeo5B5pNp2Gk190k5M=-4=9O0j<1fA9nXRPR5>XV?pW,y(\yRE]fMow@q?B)X}P;W0b41;RtKEKYeKv(9\C+@t><<*3dG8lD6k()U1PYxcOIRmY>f8\ULJNHcUUcD[m9BJ:SODHvf3\W>CgSkzKBd4Wm3s|C_>{1)UPH=52X7aEJrq:V7.e;;LzzOI:?3M9V,5:/7@f]7OCWpd3,u|5ZLLms;:s@u=t;8Fp2mFz?-A?QXyaLLs\gDke3,(cKOnjTr33v80XFhzCbduMPp{Vv=NqQp;{WR>q\/NA1Vf=(82C8>Rkk7PzdAqI5j>h8zFN01ji1\:HA+I8P9cEQH2Zi0FF.q(1=G)8ZAQdh566D9BbAb]00Lj11+H-0iA54tBTLq7D{KNI \ No newline at end of file diff --git a/tests/regression/results.csv b/tests/regression/results.csv index 188eea900..d072c0d85 100644 --- a/tests/regression/results.csv +++ b/tests/regression/results.csv @@ -6,10 +6,10 @@ silesia.tar, level 0, compress silesia.tar, level 1, compress simple, 5327717 silesia.tar, level 3, compress simple, 4854086 silesia.tar, level 4, compress simple, 4791503 -silesia.tar, level 5, compress simple, 4677740 -silesia.tar, level 6, compress simple, 4613242 -silesia.tar, level 7, compress simple, 4576661 -silesia.tar, level 9, compress simple, 4552899 +silesia.tar, level 5, compress simple, 4679004 +silesia.tar, level 6, compress simple, 4614561 +silesia.tar, level 7, compress simple, 4579828 +silesia.tar, level 9, compress simple, 4555448 silesia.tar, level 13, compress simple, 4502956 silesia.tar, level 16, compress simple, 4360546 silesia.tar, level 19, compress simple, 4265911 @@ -25,8 +25,8 @@ github.tar, level 3, compress github.tar, level 4, compress simple, 38893 github.tar, level 5, compress simple, 39651 github.tar, level 6, compress simple, 39282 -github.tar, level 7, compress simple, 38110 -github.tar, level 9, compress simple, 36760 +github.tar, level 7, compress simple, 38005 +github.tar, level 9, compress simple, 36723 github.tar, level 13, compress simple, 35501 github.tar, level 16, compress simple, 40466 github.tar, level 19, compress simple, 32276 @@ -40,10 +40,10 @@ silesia, level 0, compress silesia, level 1, compress cctx, 5306632 silesia, level 3, compress cctx, 4842075 silesia, level 4, compress cctx, 4779186 -silesia, level 5, compress cctx, 4666323 -silesia, level 6, compress cctx, 4603066 -silesia, level 7, compress cctx, 4566984 -silesia, level 9, compress cctx, 4543018 +silesia, level 5, compress cctx, 4667668 +silesia, level 6, compress cctx, 4604351 +silesia, level 7, compress cctx, 4570271 +silesia, level 9, compress cctx, 4545850 silesia, level 13, compress cctx, 4493990 silesia, level 16, compress cctx, 4360041 silesia, level 19, compress cctx, 4296055 @@ -53,33 +53,33 @@ silesia, multithreaded long distance mode, compress silesia, small window log, compress cctx, 7082951 silesia, small hash log, compress cctx, 6526141 silesia, small chain log, compress cctx, 4912197 -silesia, explicit params, compress cctx, 4794052 +silesia, explicit params, compress cctx, 4794318 silesia, uncompressed literals, compress cctx, 4842075 silesia, uncompressed literals optimal, compress cctx, 4296055 silesia, huffman literals, compress cctx, 6172202 silesia, multithreaded with advanced params, compress cctx, 4842075 github, level -5, compress cctx, 204407 -github, level -5 with dict, compress cctx, 52059 +github, level -5 with dict, compress cctx, 47581 github, level -3, compress cctx, 193253 -github, level -3 with dict, compress cctx, 46787 +github, level -3 with dict, compress cctx, 43043 github, level -1, compress cctx, 175468 -github, level -1 with dict, compress cctx, 43585 +github, level -1 with dict, compress cctx, 42044 github, level 0, compress cctx, 136332 github, level 0 with dict, compress cctx, 41534 github, level 1, compress cctx, 142365 -github, level 1 with dict, compress cctx, 42259 +github, level 1 with dict, compress cctx, 41715 github, level 3, compress cctx, 136332 github, level 3 with dict, compress cctx, 41534 github, level 4, compress cctx, 136199 github, level 4 with dict, compress cctx, 41725 github, level 5, compress cctx, 135121 -github, level 5 with dict, compress cctx, 38759 +github, level 5 with dict, compress cctx, 38755 github, level 6, compress cctx, 135122 -github, level 6 with dict, compress cctx, 38669 +github, level 6 with dict, compress cctx, 38665 github, level 7, compress cctx, 135122 -github, level 7 with dict, compress cctx, 38755 +github, level 7 with dict, compress cctx, 38759 github, level 9, compress cctx, 135122 -github, level 9 with dict, compress cctx, 39398 +github, level 9 with dict, compress cctx, 39362 github, level 13, compress cctx, 132878 github, level 13 with dict, compress cctx, 39948 github, level 16, compress cctx, 133209 @@ -104,10 +104,10 @@ silesia, level 0, zstdcli, silesia, level 1, zstdcli, 5306680 silesia, level 3, zstdcli, 4842123 silesia, level 4, zstdcli, 4779234 -silesia, level 5, zstdcli, 4666371 -silesia, level 6, zstdcli, 4603114 -silesia, level 7, zstdcli, 4567032 -silesia, level 9, zstdcli, 4543066 +silesia, level 5, zstdcli, 4667716 +silesia, level 6, zstdcli, 4604399 +silesia, level 7, zstdcli, 4570319 +silesia, level 9, zstdcli, 4545898 silesia, level 13, zstdcli, 4494038 silesia, level 16, zstdcli, 4360089 silesia, level 19, zstdcli, 4296103 @@ -117,7 +117,7 @@ silesia, multithreaded long distance mode, zstdcli, silesia, small window log, zstdcli, 7095048 silesia, small hash log, zstdcli, 6526189 silesia, small chain log, zstdcli, 4912245 -silesia, explicit params, zstdcli, 4795432 +silesia, explicit params, zstdcli, 4795840 silesia, uncompressed literals, zstdcli, 5120614 silesia, uncompressed literals optimal, zstdcli, 4319566 silesia, huffman literals, zstdcli, 5321417 @@ -129,10 +129,10 @@ silesia.tar, level 0, zstdcli, silesia.tar, level 1, zstdcli, 5329010 silesia.tar, level 3, zstdcli, 4854164 silesia.tar, level 4, zstdcli, 4792352 -silesia.tar, level 5, zstdcli, 4678682 -silesia.tar, level 6, zstdcli, 4614125 -silesia.tar, level 7, zstdcli, 4578719 -silesia.tar, level 9, zstdcli, 4552903 +silesia.tar, level 5, zstdcli, 4679860 +silesia.tar, level 6, zstdcli, 4615355 +silesia.tar, level 7, zstdcli, 4581791 +silesia.tar, level 9, zstdcli, 4555452 silesia.tar, level 13, zstdcli, 4502960 silesia.tar, level 16, zstdcli, 4360550 silesia.tar, level 19, zstdcli, 4265915 @@ -143,21 +143,21 @@ silesia.tar, multithreaded long distance mode, zstdcli, silesia.tar, small window log, zstdcli, 7100701 silesia.tar, small hash log, zstdcli, 6529264 silesia.tar, small chain log, zstdcli, 4917022 -silesia.tar, explicit params, zstdcli, 4820713 +silesia.tar, explicit params, zstdcli, 4821112 silesia.tar, uncompressed literals, zstdcli, 5122571 silesia.tar, uncompressed literals optimal, zstdcli, 4310145 silesia.tar, huffman literals, zstdcli, 5342074 silesia.tar, multithreaded with advanced params, zstdcli, 5122571 github, level -5, zstdcli, 206407 -github, level -5 with dict, zstdcli, 48718 +github, level -5 with dict, zstdcli, 47832 github, level -3, zstdcli, 195253 -github, level -3 with dict, zstdcli, 47395 +github, level -3 with dict, zstdcli, 46671 github, level -1, zstdcli, 177468 -github, level -1 with dict, zstdcli, 45170 +github, level -1 with dict, zstdcli, 43825 github, level 0, zstdcli, 138332 github, level 0 with dict, zstdcli, 43148 github, level 1, zstdcli, 144365 -github, level 1 with dict, zstdcli, 43682 +github, level 1 with dict, zstdcli, 43266 github, level 3, zstdcli, 138332 github, level 3 with dict, zstdcli, 43148 github, level 4, zstdcli, 138199 @@ -165,9 +165,9 @@ github, level 4 with dict, zstdcli, github, level 5, zstdcli, 137121 github, level 5 with dict, zstdcli, 40728 github, level 6, zstdcli, 137122 -github, level 6 with dict, zstdcli, 40636 +github, level 6 with dict, zstdcli, 40638 github, level 7, zstdcli, 137122 -github, level 7 with dict, zstdcli, 40745 +github, level 7 with dict, zstdcli, 40749 github, level 9, zstdcli, 137122 github, level 9 with dict, zstdcli, 41393 github, level 13, zstdcli, 134878 @@ -188,27 +188,27 @@ github, uncompressed literals optimal, zstdcli, github, huffman literals, zstdcli, 144365 github, multithreaded with advanced params, zstdcli, 167911 github.tar, level -5, zstdcli, 52119 -github.tar, level -5 with dict, zstdcli, 50978 +github.tar, level -5 with dict, zstdcli, 51101 github.tar, level -3, zstdcli, 45682 -github.tar, level -3 with dict, zstdcli, 44660 +github.tar, level -3 with dict, zstdcli, 44738 github.tar, level -1, zstdcli, 42564 -github.tar, level -1 with dict, zstdcli, 41155 +github.tar, level -1 with dict, zstdcli, 41357 github.tar, level 0, zstdcli, 38835 github.tar, level 0 with dict, zstdcli, 37999 github.tar, level 1, zstdcli, 39204 -github.tar, level 1 with dict, zstdcli, 38093 +github.tar, level 1 with dict, zstdcli, 38123 github.tar, level 3, zstdcli, 38835 github.tar, level 3 with dict, zstdcli, 37999 github.tar, level 4, zstdcli, 38897 github.tar, level 4 with dict, zstdcli, 37952 github.tar, level 5, zstdcli, 39655 -github.tar, level 5 with dict, zstdcli, 39071 +github.tar, level 5 with dict, zstdcli, 39073 github.tar, level 6, zstdcli, 39286 -github.tar, level 6 with dict, zstdcli, 38638 -github.tar, level 7, zstdcli, 38114 -github.tar, level 7 with dict, zstdcli, 37886 -github.tar, level 9, zstdcli, 36764 -github.tar, level 9 with dict, zstdcli, 36632 +github.tar, level 6 with dict, zstdcli, 38647 +github.tar, level 7, zstdcli, 38009 +github.tar, level 7 with dict, zstdcli, 37861 +github.tar, level 9, zstdcli, 36727 +github.tar, level 9 with dict, zstdcli, 36686 github.tar, level 13, zstdcli, 35505 github.tar, level 13 with dict, zstdcli, 37134 github.tar, level 16, zstdcli, 40470 @@ -235,18 +235,18 @@ silesia, level 0, advanced silesia, level 1, advanced one pass, 5306632 silesia, level 3, advanced one pass, 4842075 silesia, level 4, advanced one pass, 4779186 -silesia, level 5 row 1, advanced one pass, 4666323 -silesia, level 5 row 2, advanced one pass, 4670136 -silesia, level 5, advanced one pass, 4666323 -silesia, level 6, advanced one pass, 4603066 -silesia, level 7 row 1, advanced one pass, 4566984 -silesia, level 7 row 2, advanced one pass, 4564868 -silesia, level 7, advanced one pass, 4566984 -silesia, level 9, advanced one pass, 4543018 -silesia, level 11 row 1, advanced one pass, 4505046 -silesia, level 11 row 2, advanced one pass, 4503116 -silesia, level 12 row 1, advanced one pass, 4505046 -silesia, level 12 row 2, advanced one pass, 4503116 +silesia, level 5 row 1, advanced one pass, 4667668 +silesia, level 5 row 2, advanced one pass, 4670326 +silesia, level 5, advanced one pass, 4667668 +silesia, level 6, advanced one pass, 4604351 +silesia, level 7 row 1, advanced one pass, 4570271 +silesia, level 7 row 2, advanced one pass, 4565169 +silesia, level 7, advanced one pass, 4570271 +silesia, level 9, advanced one pass, 4545850 +silesia, level 11 row 1, advanced one pass, 4505658 +silesia, level 11 row 2, advanced one pass, 4503429 +silesia, level 12 row 1, advanced one pass, 4505658 +silesia, level 12 row 2, advanced one pass, 4503429 silesia, level 13, advanced one pass, 4493990 silesia, level 16, advanced one pass, 4360041 silesia, level 19, advanced one pass, 4296055 @@ -257,7 +257,7 @@ silesia, multithreaded long distance mode, advanced silesia, small window log, advanced one pass, 7095000 silesia, small hash log, advanced one pass, 6526141 silesia, small chain log, advanced one pass, 4912197 -silesia, explicit params, advanced one pass, 4795432 +silesia, explicit params, advanced one pass, 4795840 silesia, uncompressed literals, advanced one pass, 5120566 silesia, uncompressed literals optimal, advanced one pass, 4319518 silesia, huffman literals, advanced one pass, 5321369 @@ -269,18 +269,18 @@ silesia.tar, level 0, advanced silesia.tar, level 1, advanced one pass, 5327717 silesia.tar, level 3, advanced one pass, 4854086 silesia.tar, level 4, advanced one pass, 4791503 -silesia.tar, level 5 row 1, advanced one pass, 4677740 -silesia.tar, level 5 row 2, advanced one pass, 4682161 -silesia.tar, level 5, advanced one pass, 4677740 -silesia.tar, level 6, advanced one pass, 4613242 -silesia.tar, level 7 row 1, advanced one pass, 4576661 -silesia.tar, level 7 row 2, advanced one pass, 4575393 -silesia.tar, level 7, advanced one pass, 4576661 -silesia.tar, level 9, advanced one pass, 4552899 -silesia.tar, level 11 row 1, advanced one pass, 4514432 -silesia.tar, level 11 row 2, advanced one pass, 4513604 -silesia.tar, level 12 row 1, advanced one pass, 4514049 -silesia.tar, level 12 row 2, advanced one pass, 4513797 +silesia.tar, level 5 row 1, advanced one pass, 4679004 +silesia.tar, level 5 row 2, advanced one pass, 4682334 +silesia.tar, level 5, advanced one pass, 4679004 +silesia.tar, level 6, advanced one pass, 4614561 +silesia.tar, level 7 row 1, advanced one pass, 4579828 +silesia.tar, level 7 row 2, advanced one pass, 4575602 +silesia.tar, level 7, advanced one pass, 4579828 +silesia.tar, level 9, advanced one pass, 4555448 +silesia.tar, level 11 row 1, advanced one pass, 4514962 +silesia.tar, level 11 row 2, advanced one pass, 4513816 +silesia.tar, level 12 row 1, advanced one pass, 4514517 +silesia.tar, level 12 row 2, advanced one pass, 4514007 silesia.tar, level 13, advanced one pass, 4502956 silesia.tar, level 16, advanced one pass, 4360546 silesia.tar, level 19, advanced one pass, 4265911 @@ -291,105 +291,105 @@ silesia.tar, multithreaded long distance mode, advanced silesia.tar, small window log, advanced one pass, 7100655 silesia.tar, small hash log, advanced one pass, 6529206 silesia.tar, small chain log, advanced one pass, 4917041 -silesia.tar, explicit params, advanced one pass, 4806855 +silesia.tar, explicit params, advanced one pass, 4807274 silesia.tar, uncompressed literals, advanced one pass, 5122473 silesia.tar, uncompressed literals optimal, advanced one pass, 4310141 silesia.tar, huffman literals, advanced one pass, 5341705 silesia.tar, multithreaded with advanced params, advanced one pass, 5122567 github, level -5, advanced one pass, 204407 -github, level -5 with dict, advanced one pass, 46718 +github, level -5 with dict, advanced one pass, 45832 github, level -3, advanced one pass, 193253 -github, level -3 with dict, advanced one pass, 45395 +github, level -3 with dict, advanced one pass, 44671 github, level -1, advanced one pass, 175468 -github, level -1 with dict, advanced one pass, 43170 +github, level -1 with dict, advanced one pass, 41825 github, level 0, advanced one pass, 136332 github, level 0 with dict, advanced one pass, 41148 github, level 0 with dict dms, advanced one pass, 41148 github, level 0 with dict dds, advanced one pass, 41148 github, level 0 with dict copy, advanced one pass, 41124 -github, level 0 with dict load, advanced one pass, 42252 +github, level 0 with dict load, advanced one pass, 41847 github, level 1, advanced one pass, 142365 -github, level 1 with dict, advanced one pass, 41682 -github, level 1 with dict dms, advanced one pass, 41682 -github, level 1 with dict dds, advanced one pass, 41682 -github, level 1 with dict copy, advanced one pass, 41698 -github, level 1 with dict load, advanced one pass, 43814 +github, level 1 with dict, advanced one pass, 41266 +github, level 1 with dict dms, advanced one pass, 41266 +github, level 1 with dict dds, advanced one pass, 41266 +github, level 1 with dict copy, advanced one pass, 41279 +github, level 1 with dict load, advanced one pass, 43331 github, level 3, advanced one pass, 136332 github, level 3 with dict, advanced one pass, 41148 github, level 3 with dict dms, advanced one pass, 41148 github, level 3 with dict dds, advanced one pass, 41148 github, level 3 with dict copy, advanced one pass, 41124 -github, level 3 with dict load, advanced one pass, 42252 +github, level 3 with dict load, advanced one pass, 41847 github, level 4, advanced one pass, 136199 github, level 4 with dict, advanced one pass, 41251 github, level 4 with dict dms, advanced one pass, 41251 github, level 4 with dict dds, advanced one pass, 41251 github, level 4 with dict copy, advanced one pass, 41216 -github, level 4 with dict load, advanced one pass, 41159 +github, level 4 with dict load, advanced one pass, 41548 github, level 5 row 1, advanced one pass, 134584 -github, level 5 row 1 with dict dms, advanced one pass, 38758 +github, level 5 row 1 with dict dms, advanced one pass, 38754 github, level 5 row 1 with dict dds, advanced one pass, 38728 -github, level 5 row 1 with dict copy, advanced one pass, 38759 -github, level 5 row 1 with dict load, advanced one pass, 41518 +github, level 5 row 1 with dict copy, advanced one pass, 38755 +github, level 5 row 1 with dict load, advanced one pass, 41899 github, level 5 row 2, advanced one pass, 135121 github, level 5 row 2 with dict dms, advanced one pass, 38938 github, level 5 row 2 with dict dds, advanced one pass, 38732 github, level 5 row 2 with dict copy, advanced one pass, 38934 -github, level 5 row 2 with dict load, advanced one pass, 40725 +github, level 5 row 2 with dict load, advanced one pass, 41248 github, level 5, advanced one pass, 135121 -github, level 5 with dict, advanced one pass, 38758 -github, level 5 with dict dms, advanced one pass, 38758 +github, level 5 with dict, advanced one pass, 38754 +github, level 5 with dict dms, advanced one pass, 38754 github, level 5 with dict dds, advanced one pass, 38728 -github, level 5 with dict copy, advanced one pass, 38759 -github, level 5 with dict load, advanced one pass, 40725 +github, level 5 with dict copy, advanced one pass, 38755 +github, level 5 with dict load, advanced one pass, 41248 github, level 6, advanced one pass, 135122 -github, level 6 with dict, advanced one pass, 38671 -github, level 6 with dict dms, advanced one pass, 38671 -github, level 6 with dict dds, advanced one pass, 38636 -github, level 6 with dict copy, advanced one pass, 38669 -github, level 6 with dict load, advanced one pass, 40695 +github, level 6 with dict, advanced one pass, 38669 +github, level 6 with dict dms, advanced one pass, 38669 +github, level 6 with dict dds, advanced one pass, 38638 +github, level 6 with dict copy, advanced one pass, 38665 +github, level 6 with dict load, advanced one pass, 41153 github, level 7 row 1, advanced one pass, 134584 -github, level 7 row 1 with dict dms, advanced one pass, 38758 -github, level 7 row 1 with dict dds, advanced one pass, 38745 -github, level 7 row 1 with dict copy, advanced one pass, 38755 -github, level 7 row 1 with dict load, advanced one pass, 43154 +github, level 7 row 1 with dict dms, advanced one pass, 38765 +github, level 7 row 1 with dict dds, advanced one pass, 38749 +github, level 7 row 1 with dict copy, advanced one pass, 38759 +github, level 7 row 1 with dict load, advanced one pass, 43227 github, level 7 row 2, advanced one pass, 135122 github, level 7 row 2 with dict dms, advanced one pass, 38860 github, level 7 row 2 with dict dds, advanced one pass, 38766 github, level 7 row 2 with dict copy, advanced one pass, 38834 -github, level 7 row 2 with dict load, advanced one pass, 40695 +github, level 7 row 2 with dict load, advanced one pass, 41153 github, level 7, advanced one pass, 135122 -github, level 7 with dict, advanced one pass, 38758 -github, level 7 with dict dms, advanced one pass, 38758 -github, level 7 with dict dds, advanced one pass, 38745 -github, level 7 with dict copy, advanced one pass, 38755 -github, level 7 with dict load, advanced one pass, 40695 +github, level 7 with dict, advanced one pass, 38765 +github, level 7 with dict dms, advanced one pass, 38765 +github, level 7 with dict dds, advanced one pass, 38749 +github, level 7 with dict copy, advanced one pass, 38759 +github, level 7 with dict load, advanced one pass, 41153 github, level 9, advanced one pass, 135122 -github, level 9 with dict, advanced one pass, 39437 -github, level 9 with dict dms, advanced one pass, 39437 +github, level 9 with dict, advanced one pass, 39439 +github, level 9 with dict dms, advanced one pass, 39439 github, level 9 with dict dds, advanced one pass, 39393 -github, level 9 with dict copy, advanced one pass, 39398 -github, level 9 with dict load, advanced one pass, 41710 +github, level 9 with dict copy, advanced one pass, 39362 +github, level 9 with dict load, advanced one pass, 42148 github, level 11 row 1, advanced one pass, 135367 github, level 11 row 1 with dict dms, advanced one pass, 39671 github, level 11 row 1 with dict dds, advanced one pass, 39671 github, level 11 row 1 with dict copy, advanced one pass, 39651 -github, level 11 row 1 with dict load, advanced one pass, 41360 +github, level 11 row 1 with dict load, advanced one pass, 41744 github, level 11 row 2, advanced one pass, 135367 github, level 11 row 2 with dict dms, advanced one pass, 39671 github, level 11 row 2 with dict dds, advanced one pass, 39671 github, level 11 row 2 with dict copy, advanced one pass, 39651 -github, level 11 row 2 with dict load, advanced one pass, 41360 +github, level 11 row 2 with dict load, advanced one pass, 41744 github, level 12 row 1, advanced one pass, 134402 github, level 12 row 1 with dict dms, advanced one pass, 39677 github, level 12 row 1 with dict dds, advanced one pass, 39677 github, level 12 row 1 with dict copy, advanced one pass, 39677 -github, level 12 row 1 with dict load, advanced one pass, 41166 +github, level 12 row 1 with dict load, advanced one pass, 41553 github, level 12 row 2, advanced one pass, 134402 github, level 12 row 2 with dict dms, advanced one pass, 39677 github, level 12 row 2 with dict dds, advanced one pass, 39677 github, level 12 row 2 with dict copy, advanced one pass, 39677 -github, level 12 row 2 with dict load, advanced one pass, 41166 +github, level 12 row 2 with dict load, advanced one pass, 41553 github, level 13, advanced one pass, 132878 github, level 13 with dict, advanced one pass, 39900 github, level 13 with dict dms, advanced one pass, 39900 @@ -422,11 +422,11 @@ github, uncompressed literals optimal, advanced github, huffman literals, advanced one pass, 142365 github, multithreaded with advanced params, advanced one pass, 165911 github.tar, level -5, advanced one pass, 52115 -github.tar, level -5 with dict, advanced one pass, 50974 +github.tar, level -5 with dict, advanced one pass, 51097 github.tar, level -3, advanced one pass, 45678 -github.tar, level -3 with dict, advanced one pass, 44656 +github.tar, level -3 with dict, advanced one pass, 44734 github.tar, level -1, advanced one pass, 42560 -github.tar, level -1 with dict, advanced one pass, 41151 +github.tar, level -1 with dict, advanced one pass, 41353 github.tar, level 0, advanced one pass, 38831 github.tar, level 0 with dict, advanced one pass, 37995 github.tar, level 0 with dict dms, advanced one pass, 38003 @@ -434,10 +434,10 @@ github.tar, level 0 with dict dds, advanced github.tar, level 0 with dict copy, advanced one pass, 37995 github.tar, level 0 with dict load, advanced one pass, 37956 github.tar, level 1, advanced one pass, 39200 -github.tar, level 1 with dict, advanced one pass, 38089 -github.tar, level 1 with dict dms, advanced one pass, 38294 -github.tar, level 1 with dict dds, advanced one pass, 38294 -github.tar, level 1 with dict copy, advanced one pass, 38089 +github.tar, level 1 with dict, advanced one pass, 38119 +github.tar, level 1 with dict dms, advanced one pass, 38406 +github.tar, level 1 with dict dds, advanced one pass, 38406 +github.tar, level 1 with dict copy, advanced one pass, 38119 github.tar, level 1 with dict load, advanced one pass, 38364 github.tar, level 3, advanced one pass, 38831 github.tar, level 3 with dict, advanced one pass, 37995 @@ -452,64 +452,64 @@ github.tar, level 4 with dict dds, advanced github.tar, level 4 with dict copy, advanced one pass, 37948 github.tar, level 4 with dict load, advanced one pass, 37927 github.tar, level 5 row 1, advanced one pass, 39651 -github.tar, level 5 row 1 with dict dms, advanced one pass, 39059 -github.tar, level 5 row 1 with dict dds, advanced one pass, 39067 -github.tar, level 5 row 1 with dict copy, advanced one pass, 39082 -github.tar, level 5 row 1 with dict load, advanced one pass, 38999 +github.tar, level 5 row 1 with dict dms, advanced one pass, 39043 +github.tar, level 5 row 1 with dict dds, advanced one pass, 39069 +github.tar, level 5 row 1 with dict copy, advanced one pass, 39145 +github.tar, level 5 row 1 with dict load, advanced one pass, 39000 github.tar, level 5 row 2, advanced one pass, 39701 github.tar, level 5 row 2 with dict dms, advanced one pass, 39365 github.tar, level 5 row 2 with dict dds, advanced one pass, 39233 github.tar, level 5 row 2 with dict copy, advanced one pass, 39715 github.tar, level 5 row 2 with dict load, advanced one pass, 39158 github.tar, level 5, advanced one pass, 39651 -github.tar, level 5 with dict, advanced one pass, 39082 -github.tar, level 5 with dict dms, advanced one pass, 39059 -github.tar, level 5 with dict dds, advanced one pass, 39067 -github.tar, level 5 with dict copy, advanced one pass, 39082 -github.tar, level 5 with dict load, advanced one pass, 38999 +github.tar, level 5 with dict, advanced one pass, 39145 +github.tar, level 5 with dict dms, advanced one pass, 39043 +github.tar, level 5 with dict dds, advanced one pass, 39069 +github.tar, level 5 with dict copy, advanced one pass, 39145 +github.tar, level 5 with dict load, advanced one pass, 39000 github.tar, level 6, advanced one pass, 39282 github.tar, level 6 with dict, advanced one pass, 38656 -github.tar, level 6 with dict dms, advanced one pass, 38636 -github.tar, level 6 with dict dds, advanced one pass, 38634 +github.tar, level 6 with dict dms, advanced one pass, 38640 +github.tar, level 6 with dict dds, advanced one pass, 38643 github.tar, level 6 with dict copy, advanced one pass, 38656 -github.tar, level 6 with dict load, advanced one pass, 38648 -github.tar, level 7 row 1, advanced one pass, 38110 -github.tar, level 7 row 1 with dict dms, advanced one pass, 37858 -github.tar, level 7 row 1 with dict dds, advanced one pass, 37882 -github.tar, level 7 row 1 with dict copy, advanced one pass, 37865 -github.tar, level 7 row 1 with dict load, advanced one pass, 37436 +github.tar, level 6 with dict load, advanced one pass, 38647 +github.tar, level 7 row 1, advanced one pass, 38005 +github.tar, level 7 row 1 with dict dms, advanced one pass, 37832 +github.tar, level 7 row 1 with dict dds, advanced one pass, 37857 +github.tar, level 7 row 1 with dict copy, advanced one pass, 37839 +github.tar, level 7 row 1 with dict load, advanced one pass, 37286 github.tar, level 7 row 2, advanced one pass, 38077 github.tar, level 7 row 2 with dict dms, advanced one pass, 38012 github.tar, level 7 row 2 with dict dds, advanced one pass, 38014 github.tar, level 7 row 2 with dict copy, advanced one pass, 38101 github.tar, level 7 row 2 with dict load, advanced one pass, 37402 -github.tar, level 7, advanced one pass, 38110 -github.tar, level 7 with dict, advanced one pass, 37865 -github.tar, level 7 with dict dms, advanced one pass, 37858 -github.tar, level 7 with dict dds, advanced one pass, 37882 -github.tar, level 7 with dict copy, advanced one pass, 37865 -github.tar, level 7 with dict load, advanced one pass, 37436 -github.tar, level 9, advanced one pass, 36760 -github.tar, level 9 with dict, advanced one pass, 36484 -github.tar, level 9 with dict dms, advanced one pass, 36567 -github.tar, level 9 with dict dds, advanced one pass, 36628 -github.tar, level 9 with dict copy, advanced one pass, 36484 -github.tar, level 9 with dict load, advanced one pass, 36401 -github.tar, level 11 row 1, advanced one pass, 36081 +github.tar, level 7, advanced one pass, 38005 +github.tar, level 7 with dict, advanced one pass, 37839 +github.tar, level 7 with dict dms, advanced one pass, 37832 +github.tar, level 7 with dict dds, advanced one pass, 37857 +github.tar, level 7 with dict copy, advanced one pass, 37839 +github.tar, level 7 with dict load, advanced one pass, 37286 +github.tar, level 9, advanced one pass, 36723 +github.tar, level 9 with dict, advanced one pass, 36531 +github.tar, level 9 with dict dms, advanced one pass, 36615 +github.tar, level 9 with dict dds, advanced one pass, 36682 +github.tar, level 9 with dict copy, advanced one pass, 36531 +github.tar, level 9 with dict load, advanced one pass, 36322 +github.tar, level 11 row 1, advanced one pass, 36085 github.tar, level 11 row 1 with dict dms, advanced one pass, 36963 github.tar, level 11 row 1 with dict dds, advanced one pass, 36963 github.tar, level 11 row 1 with dict copy, advanced one pass, 36557 -github.tar, level 11 row 1 with dict load, advanced one pass, 36434 +github.tar, level 11 row 1 with dict load, advanced one pass, 36423 github.tar, level 11 row 2, advanced one pass, 36110 github.tar, level 11 row 2 with dict dms, advanced one pass, 36963 github.tar, level 11 row 2 with dict dds, advanced one pass, 36963 github.tar, level 11 row 2 with dict copy, advanced one pass, 36557 github.tar, level 11 row 2 with dict load, advanced one pass, 36459 -github.tar, level 12 row 1, advanced one pass, 36081 +github.tar, level 12 row 1, advanced one pass, 36085 github.tar, level 12 row 1 with dict dms, advanced one pass, 36986 github.tar, level 12 row 1 with dict dds, advanced one pass, 36986 github.tar, level 12 row 1 with dict copy, advanced one pass, 36609 -github.tar, level 12 row 1 with dict load, advanced one pass, 36434 +github.tar, level 12 row 1 with dict load, advanced one pass, 36423 github.tar, level 12 row 2, advanced one pass, 36110 github.tar, level 12 row 2 with dict dms, advanced one pass, 36986 github.tar, level 12 row 2 with dict dds, advanced one pass, 36986 @@ -553,18 +553,18 @@ silesia, level 0, advanced silesia, level 1, advanced one pass small out, 5306632 silesia, level 3, advanced one pass small out, 4842075 silesia, level 4, advanced one pass small out, 4779186 -silesia, level 5 row 1, advanced one pass small out, 4666323 -silesia, level 5 row 2, advanced one pass small out, 4670136 -silesia, level 5, advanced one pass small out, 4666323 -silesia, level 6, advanced one pass small out, 4603066 -silesia, level 7 row 1, advanced one pass small out, 4566984 -silesia, level 7 row 2, advanced one pass small out, 4564868 -silesia, level 7, advanced one pass small out, 4566984 -silesia, level 9, advanced one pass small out, 4543018 -silesia, level 11 row 1, advanced one pass small out, 4505046 -silesia, level 11 row 2, advanced one pass small out, 4503116 -silesia, level 12 row 1, advanced one pass small out, 4505046 -silesia, level 12 row 2, advanced one pass small out, 4503116 +silesia, level 5 row 1, advanced one pass small out, 4667668 +silesia, level 5 row 2, advanced one pass small out, 4670326 +silesia, level 5, advanced one pass small out, 4667668 +silesia, level 6, advanced one pass small out, 4604351 +silesia, level 7 row 1, advanced one pass small out, 4570271 +silesia, level 7 row 2, advanced one pass small out, 4565169 +silesia, level 7, advanced one pass small out, 4570271 +silesia, level 9, advanced one pass small out, 4545850 +silesia, level 11 row 1, advanced one pass small out, 4505658 +silesia, level 11 row 2, advanced one pass small out, 4503429 +silesia, level 12 row 1, advanced one pass small out, 4505658 +silesia, level 12 row 2, advanced one pass small out, 4503429 silesia, level 13, advanced one pass small out, 4493990 silesia, level 16, advanced one pass small out, 4360041 silesia, level 19, advanced one pass small out, 4296055 @@ -575,7 +575,7 @@ silesia, multithreaded long distance mode, advanced silesia, small window log, advanced one pass small out, 7095000 silesia, small hash log, advanced one pass small out, 6526141 silesia, small chain log, advanced one pass small out, 4912197 -silesia, explicit params, advanced one pass small out, 4795432 +silesia, explicit params, advanced one pass small out, 4795840 silesia, uncompressed literals, advanced one pass small out, 5120566 silesia, uncompressed literals optimal, advanced one pass small out, 4319518 silesia, huffman literals, advanced one pass small out, 5321369 @@ -587,18 +587,18 @@ silesia.tar, level 0, advanced silesia.tar, level 1, advanced one pass small out, 5327717 silesia.tar, level 3, advanced one pass small out, 4854086 silesia.tar, level 4, advanced one pass small out, 4791503 -silesia.tar, level 5 row 1, advanced one pass small out, 4677740 -silesia.tar, level 5 row 2, advanced one pass small out, 4682161 -silesia.tar, level 5, advanced one pass small out, 4677740 -silesia.tar, level 6, advanced one pass small out, 4613242 -silesia.tar, level 7 row 1, advanced one pass small out, 4576661 -silesia.tar, level 7 row 2, advanced one pass small out, 4575393 -silesia.tar, level 7, advanced one pass small out, 4576661 -silesia.tar, level 9, advanced one pass small out, 4552899 -silesia.tar, level 11 row 1, advanced one pass small out, 4514432 -silesia.tar, level 11 row 2, advanced one pass small out, 4513604 -silesia.tar, level 12 row 1, advanced one pass small out, 4514049 -silesia.tar, level 12 row 2, advanced one pass small out, 4513797 +silesia.tar, level 5 row 1, advanced one pass small out, 4679004 +silesia.tar, level 5 row 2, advanced one pass small out, 4682334 +silesia.tar, level 5, advanced one pass small out, 4679004 +silesia.tar, level 6, advanced one pass small out, 4614561 +silesia.tar, level 7 row 1, advanced one pass small out, 4579828 +silesia.tar, level 7 row 2, advanced one pass small out, 4575602 +silesia.tar, level 7, advanced one pass small out, 4579828 +silesia.tar, level 9, advanced one pass small out, 4555448 +silesia.tar, level 11 row 1, advanced one pass small out, 4514962 +silesia.tar, level 11 row 2, advanced one pass small out, 4513816 +silesia.tar, level 12 row 1, advanced one pass small out, 4514517 +silesia.tar, level 12 row 2, advanced one pass small out, 4514007 silesia.tar, level 13, advanced one pass small out, 4502956 silesia.tar, level 16, advanced one pass small out, 4360546 silesia.tar, level 19, advanced one pass small out, 4265911 @@ -609,105 +609,105 @@ silesia.tar, multithreaded long distance mode, advanced silesia.tar, small window log, advanced one pass small out, 7100655 silesia.tar, small hash log, advanced one pass small out, 6529206 silesia.tar, small chain log, advanced one pass small out, 4917041 -silesia.tar, explicit params, advanced one pass small out, 4806855 +silesia.tar, explicit params, advanced one pass small out, 4807274 silesia.tar, uncompressed literals, advanced one pass small out, 5122473 silesia.tar, uncompressed literals optimal, advanced one pass small out, 4310141 silesia.tar, huffman literals, advanced one pass small out, 5341705 silesia.tar, multithreaded with advanced params, advanced one pass small out, 5122567 github, level -5, advanced one pass small out, 204407 -github, level -5 with dict, advanced one pass small out, 46718 +github, level -5 with dict, advanced one pass small out, 45832 github, level -3, advanced one pass small out, 193253 -github, level -3 with dict, advanced one pass small out, 45395 +github, level -3 with dict, advanced one pass small out, 44671 github, level -1, advanced one pass small out, 175468 -github, level -1 with dict, advanced one pass small out, 43170 +github, level -1 with dict, advanced one pass small out, 41825 github, level 0, advanced one pass small out, 136332 github, level 0 with dict, advanced one pass small out, 41148 github, level 0 with dict dms, advanced one pass small out, 41148 github, level 0 with dict dds, advanced one pass small out, 41148 github, level 0 with dict copy, advanced one pass small out, 41124 -github, level 0 with dict load, advanced one pass small out, 42252 +github, level 0 with dict load, advanced one pass small out, 41847 github, level 1, advanced one pass small out, 142365 -github, level 1 with dict, advanced one pass small out, 41682 -github, level 1 with dict dms, advanced one pass small out, 41682 -github, level 1 with dict dds, advanced one pass small out, 41682 -github, level 1 with dict copy, advanced one pass small out, 41698 -github, level 1 with dict load, advanced one pass small out, 43814 +github, level 1 with dict, advanced one pass small out, 41266 +github, level 1 with dict dms, advanced one pass small out, 41266 +github, level 1 with dict dds, advanced one pass small out, 41266 +github, level 1 with dict copy, advanced one pass small out, 41279 +github, level 1 with dict load, advanced one pass small out, 43331 github, level 3, advanced one pass small out, 136332 github, level 3 with dict, advanced one pass small out, 41148 github, level 3 with dict dms, advanced one pass small out, 41148 github, level 3 with dict dds, advanced one pass small out, 41148 github, level 3 with dict copy, advanced one pass small out, 41124 -github, level 3 with dict load, advanced one pass small out, 42252 +github, level 3 with dict load, advanced one pass small out, 41847 github, level 4, advanced one pass small out, 136199 github, level 4 with dict, advanced one pass small out, 41251 github, level 4 with dict dms, advanced one pass small out, 41251 github, level 4 with dict dds, advanced one pass small out, 41251 github, level 4 with dict copy, advanced one pass small out, 41216 -github, level 4 with dict load, advanced one pass small out, 41159 +github, level 4 with dict load, advanced one pass small out, 41548 github, level 5 row 1, advanced one pass small out, 134584 -github, level 5 row 1 with dict dms, advanced one pass small out, 38758 +github, level 5 row 1 with dict dms, advanced one pass small out, 38754 github, level 5 row 1 with dict dds, advanced one pass small out, 38728 -github, level 5 row 1 with dict copy, advanced one pass small out, 38759 -github, level 5 row 1 with dict load, advanced one pass small out, 41518 +github, level 5 row 1 with dict copy, advanced one pass small out, 38755 +github, level 5 row 1 with dict load, advanced one pass small out, 41899 github, level 5 row 2, advanced one pass small out, 135121 github, level 5 row 2 with dict dms, advanced one pass small out, 38938 github, level 5 row 2 with dict dds, advanced one pass small out, 38732 github, level 5 row 2 with dict copy, advanced one pass small out, 38934 -github, level 5 row 2 with dict load, advanced one pass small out, 40725 +github, level 5 row 2 with dict load, advanced one pass small out, 41248 github, level 5, advanced one pass small out, 135121 -github, level 5 with dict, advanced one pass small out, 38758 -github, level 5 with dict dms, advanced one pass small out, 38758 +github, level 5 with dict, advanced one pass small out, 38754 +github, level 5 with dict dms, advanced one pass small out, 38754 github, level 5 with dict dds, advanced one pass small out, 38728 -github, level 5 with dict copy, advanced one pass small out, 38759 -github, level 5 with dict load, advanced one pass small out, 40725 +github, level 5 with dict copy, advanced one pass small out, 38755 +github, level 5 with dict load, advanced one pass small out, 41248 github, level 6, advanced one pass small out, 135122 -github, level 6 with dict, advanced one pass small out, 38671 -github, level 6 with dict dms, advanced one pass small out, 38671 -github, level 6 with dict dds, advanced one pass small out, 38636 -github, level 6 with dict copy, advanced one pass small out, 38669 -github, level 6 with dict load, advanced one pass small out, 40695 +github, level 6 with dict, advanced one pass small out, 38669 +github, level 6 with dict dms, advanced one pass small out, 38669 +github, level 6 with dict dds, advanced one pass small out, 38638 +github, level 6 with dict copy, advanced one pass small out, 38665 +github, level 6 with dict load, advanced one pass small out, 41153 github, level 7 row 1, advanced one pass small out, 134584 -github, level 7 row 1 with dict dms, advanced one pass small out, 38758 -github, level 7 row 1 with dict dds, advanced one pass small out, 38745 -github, level 7 row 1 with dict copy, advanced one pass small out, 38755 -github, level 7 row 1 with dict load, advanced one pass small out, 43154 +github, level 7 row 1 with dict dms, advanced one pass small out, 38765 +github, level 7 row 1 with dict dds, advanced one pass small out, 38749 +github, level 7 row 1 with dict copy, advanced one pass small out, 38759 +github, level 7 row 1 with dict load, advanced one pass small out, 43227 github, level 7 row 2, advanced one pass small out, 135122 github, level 7 row 2 with dict dms, advanced one pass small out, 38860 github, level 7 row 2 with dict dds, advanced one pass small out, 38766 github, level 7 row 2 with dict copy, advanced one pass small out, 38834 -github, level 7 row 2 with dict load, advanced one pass small out, 40695 +github, level 7 row 2 with dict load, advanced one pass small out, 41153 github, level 7, advanced one pass small out, 135122 -github, level 7 with dict, advanced one pass small out, 38758 -github, level 7 with dict dms, advanced one pass small out, 38758 -github, level 7 with dict dds, advanced one pass small out, 38745 -github, level 7 with dict copy, advanced one pass small out, 38755 -github, level 7 with dict load, advanced one pass small out, 40695 +github, level 7 with dict, advanced one pass small out, 38765 +github, level 7 with dict dms, advanced one pass small out, 38765 +github, level 7 with dict dds, advanced one pass small out, 38749 +github, level 7 with dict copy, advanced one pass small out, 38759 +github, level 7 with dict load, advanced one pass small out, 41153 github, level 9, advanced one pass small out, 135122 -github, level 9 with dict, advanced one pass small out, 39437 -github, level 9 with dict dms, advanced one pass small out, 39437 +github, level 9 with dict, advanced one pass small out, 39439 +github, level 9 with dict dms, advanced one pass small out, 39439 github, level 9 with dict dds, advanced one pass small out, 39393 -github, level 9 with dict copy, advanced one pass small out, 39398 -github, level 9 with dict load, advanced one pass small out, 41710 +github, level 9 with dict copy, advanced one pass small out, 39362 +github, level 9 with dict load, advanced one pass small out, 42148 github, level 11 row 1, advanced one pass small out, 135367 github, level 11 row 1 with dict dms, advanced one pass small out, 39671 github, level 11 row 1 with dict dds, advanced one pass small out, 39671 github, level 11 row 1 with dict copy, advanced one pass small out, 39651 -github, level 11 row 1 with dict load, advanced one pass small out, 41360 +github, level 11 row 1 with dict load, advanced one pass small out, 41744 github, level 11 row 2, advanced one pass small out, 135367 github, level 11 row 2 with dict dms, advanced one pass small out, 39671 github, level 11 row 2 with dict dds, advanced one pass small out, 39671 github, level 11 row 2 with dict copy, advanced one pass small out, 39651 -github, level 11 row 2 with dict load, advanced one pass small out, 41360 +github, level 11 row 2 with dict load, advanced one pass small out, 41744 github, level 12 row 1, advanced one pass small out, 134402 github, level 12 row 1 with dict dms, advanced one pass small out, 39677 github, level 12 row 1 with dict dds, advanced one pass small out, 39677 github, level 12 row 1 with dict copy, advanced one pass small out, 39677 -github, level 12 row 1 with dict load, advanced one pass small out, 41166 +github, level 12 row 1 with dict load, advanced one pass small out, 41553 github, level 12 row 2, advanced one pass small out, 134402 github, level 12 row 2 with dict dms, advanced one pass small out, 39677 github, level 12 row 2 with dict dds, advanced one pass small out, 39677 github, level 12 row 2 with dict copy, advanced one pass small out, 39677 -github, level 12 row 2 with dict load, advanced one pass small out, 41166 +github, level 12 row 2 with dict load, advanced one pass small out, 41553 github, level 13, advanced one pass small out, 132878 github, level 13 with dict, advanced one pass small out, 39900 github, level 13 with dict dms, advanced one pass small out, 39900 @@ -740,11 +740,11 @@ github, uncompressed literals optimal, advanced github, huffman literals, advanced one pass small out, 142365 github, multithreaded with advanced params, advanced one pass small out, 165911 github.tar, level -5, advanced one pass small out, 52115 -github.tar, level -5 with dict, advanced one pass small out, 50974 +github.tar, level -5 with dict, advanced one pass small out, 51097 github.tar, level -3, advanced one pass small out, 45678 -github.tar, level -3 with dict, advanced one pass small out, 44656 +github.tar, level -3 with dict, advanced one pass small out, 44734 github.tar, level -1, advanced one pass small out, 42560 -github.tar, level -1 with dict, advanced one pass small out, 41151 +github.tar, level -1 with dict, advanced one pass small out, 41353 github.tar, level 0, advanced one pass small out, 38831 github.tar, level 0 with dict, advanced one pass small out, 37995 github.tar, level 0 with dict dms, advanced one pass small out, 38003 @@ -752,10 +752,10 @@ github.tar, level 0 with dict dds, advanced github.tar, level 0 with dict copy, advanced one pass small out, 37995 github.tar, level 0 with dict load, advanced one pass small out, 37956 github.tar, level 1, advanced one pass small out, 39200 -github.tar, level 1 with dict, advanced one pass small out, 38089 -github.tar, level 1 with dict dms, advanced one pass small out, 38294 -github.tar, level 1 with dict dds, advanced one pass small out, 38294 -github.tar, level 1 with dict copy, advanced one pass small out, 38089 +github.tar, level 1 with dict, advanced one pass small out, 38119 +github.tar, level 1 with dict dms, advanced one pass small out, 38406 +github.tar, level 1 with dict dds, advanced one pass small out, 38406 +github.tar, level 1 with dict copy, advanced one pass small out, 38119 github.tar, level 1 with dict load, advanced one pass small out, 38364 github.tar, level 3, advanced one pass small out, 38831 github.tar, level 3 with dict, advanced one pass small out, 37995 @@ -770,64 +770,64 @@ github.tar, level 4 with dict dds, advanced github.tar, level 4 with dict copy, advanced one pass small out, 37948 github.tar, level 4 with dict load, advanced one pass small out, 37927 github.tar, level 5 row 1, advanced one pass small out, 39651 -github.tar, level 5 row 1 with dict dms, advanced one pass small out, 39059 -github.tar, level 5 row 1 with dict dds, advanced one pass small out, 39067 -github.tar, level 5 row 1 with dict copy, advanced one pass small out, 39082 -github.tar, level 5 row 1 with dict load, advanced one pass small out, 38999 +github.tar, level 5 row 1 with dict dms, advanced one pass small out, 39043 +github.tar, level 5 row 1 with dict dds, advanced one pass small out, 39069 +github.tar, level 5 row 1 with dict copy, advanced one pass small out, 39145 +github.tar, level 5 row 1 with dict load, advanced one pass small out, 39000 github.tar, level 5 row 2, advanced one pass small out, 39701 github.tar, level 5 row 2 with dict dms, advanced one pass small out, 39365 github.tar, level 5 row 2 with dict dds, advanced one pass small out, 39233 github.tar, level 5 row 2 with dict copy, advanced one pass small out, 39715 github.tar, level 5 row 2 with dict load, advanced one pass small out, 39158 github.tar, level 5, advanced one pass small out, 39651 -github.tar, level 5 with dict, advanced one pass small out, 39082 -github.tar, level 5 with dict dms, advanced one pass small out, 39059 -github.tar, level 5 with dict dds, advanced one pass small out, 39067 -github.tar, level 5 with dict copy, advanced one pass small out, 39082 -github.tar, level 5 with dict load, advanced one pass small out, 38999 +github.tar, level 5 with dict, advanced one pass small out, 39145 +github.tar, level 5 with dict dms, advanced one pass small out, 39043 +github.tar, level 5 with dict dds, advanced one pass small out, 39069 +github.tar, level 5 with dict copy, advanced one pass small out, 39145 +github.tar, level 5 with dict load, advanced one pass small out, 39000 github.tar, level 6, advanced one pass small out, 39282 github.tar, level 6 with dict, advanced one pass small out, 38656 -github.tar, level 6 with dict dms, advanced one pass small out, 38636 -github.tar, level 6 with dict dds, advanced one pass small out, 38634 +github.tar, level 6 with dict dms, advanced one pass small out, 38640 +github.tar, level 6 with dict dds, advanced one pass small out, 38643 github.tar, level 6 with dict copy, advanced one pass small out, 38656 -github.tar, level 6 with dict load, advanced one pass small out, 38648 -github.tar, level 7 row 1, advanced one pass small out, 38110 -github.tar, level 7 row 1 with dict dms, advanced one pass small out, 37858 -github.tar, level 7 row 1 with dict dds, advanced one pass small out, 37882 -github.tar, level 7 row 1 with dict copy, advanced one pass small out, 37865 -github.tar, level 7 row 1 with dict load, advanced one pass small out, 37436 +github.tar, level 6 with dict load, advanced one pass small out, 38647 +github.tar, level 7 row 1, advanced one pass small out, 38005 +github.tar, level 7 row 1 with dict dms, advanced one pass small out, 37832 +github.tar, level 7 row 1 with dict dds, advanced one pass small out, 37857 +github.tar, level 7 row 1 with dict copy, advanced one pass small out, 37839 +github.tar, level 7 row 1 with dict load, advanced one pass small out, 37286 github.tar, level 7 row 2, advanced one pass small out, 38077 github.tar, level 7 row 2 with dict dms, advanced one pass small out, 38012 github.tar, level 7 row 2 with dict dds, advanced one pass small out, 38014 github.tar, level 7 row 2 with dict copy, advanced one pass small out, 38101 github.tar, level 7 row 2 with dict load, advanced one pass small out, 37402 -github.tar, level 7, advanced one pass small out, 38110 -github.tar, level 7 with dict, advanced one pass small out, 37865 -github.tar, level 7 with dict dms, advanced one pass small out, 37858 -github.tar, level 7 with dict dds, advanced one pass small out, 37882 -github.tar, level 7 with dict copy, advanced one pass small out, 37865 -github.tar, level 7 with dict load, advanced one pass small out, 37436 -github.tar, level 9, advanced one pass small out, 36760 -github.tar, level 9 with dict, advanced one pass small out, 36484 -github.tar, level 9 with dict dms, advanced one pass small out, 36567 -github.tar, level 9 with dict dds, advanced one pass small out, 36628 -github.tar, level 9 with dict copy, advanced one pass small out, 36484 -github.tar, level 9 with dict load, advanced one pass small out, 36401 -github.tar, level 11 row 1, advanced one pass small out, 36081 +github.tar, level 7, advanced one pass small out, 38005 +github.tar, level 7 with dict, advanced one pass small out, 37839 +github.tar, level 7 with dict dms, advanced one pass small out, 37832 +github.tar, level 7 with dict dds, advanced one pass small out, 37857 +github.tar, level 7 with dict copy, advanced one pass small out, 37839 +github.tar, level 7 with dict load, advanced one pass small out, 37286 +github.tar, level 9, advanced one pass small out, 36723 +github.tar, level 9 with dict, advanced one pass small out, 36531 +github.tar, level 9 with dict dms, advanced one pass small out, 36615 +github.tar, level 9 with dict dds, advanced one pass small out, 36682 +github.tar, level 9 with dict copy, advanced one pass small out, 36531 +github.tar, level 9 with dict load, advanced one pass small out, 36322 +github.tar, level 11 row 1, advanced one pass small out, 36085 github.tar, level 11 row 1 with dict dms, advanced one pass small out, 36963 github.tar, level 11 row 1 with dict dds, advanced one pass small out, 36963 github.tar, level 11 row 1 with dict copy, advanced one pass small out, 36557 -github.tar, level 11 row 1 with dict load, advanced one pass small out, 36434 +github.tar, level 11 row 1 with dict load, advanced one pass small out, 36423 github.tar, level 11 row 2, advanced one pass small out, 36110 github.tar, level 11 row 2 with dict dms, advanced one pass small out, 36963 github.tar, level 11 row 2 with dict dds, advanced one pass small out, 36963 github.tar, level 11 row 2 with dict copy, advanced one pass small out, 36557 github.tar, level 11 row 2 with dict load, advanced one pass small out, 36459 -github.tar, level 12 row 1, advanced one pass small out, 36081 +github.tar, level 12 row 1, advanced one pass small out, 36085 github.tar, level 12 row 1 with dict dms, advanced one pass small out, 36986 github.tar, level 12 row 1 with dict dds, advanced one pass small out, 36986 github.tar, level 12 row 1 with dict copy, advanced one pass small out, 36609 -github.tar, level 12 row 1 with dict load, advanced one pass small out, 36434 +github.tar, level 12 row 1 with dict load, advanced one pass small out, 36423 github.tar, level 12 row 2, advanced one pass small out, 36110 github.tar, level 12 row 2 with dict dms, advanced one pass small out, 36986 github.tar, level 12 row 2 with dict dds, advanced one pass small out, 36986 @@ -871,18 +871,18 @@ silesia, level 0, advanced silesia, level 1, advanced streaming, 5306388 silesia, level 3, advanced streaming, 4842075 silesia, level 4, advanced streaming, 4779186 -silesia, level 5 row 1, advanced streaming, 4666323 -silesia, level 5 row 2, advanced streaming, 4670136 -silesia, level 5, advanced streaming, 4666323 -silesia, level 6, advanced streaming, 4603066 -silesia, level 7 row 1, advanced streaming, 4566984 -silesia, level 7 row 2, advanced streaming, 4564868 -silesia, level 7, advanced streaming, 4566984 -silesia, level 9, advanced streaming, 4543018 -silesia, level 11 row 1, advanced streaming, 4505046 -silesia, level 11 row 2, advanced streaming, 4503116 -silesia, level 12 row 1, advanced streaming, 4505046 -silesia, level 12 row 2, advanced streaming, 4503116 +silesia, level 5 row 1, advanced streaming, 4667668 +silesia, level 5 row 2, advanced streaming, 4670326 +silesia, level 5, advanced streaming, 4667668 +silesia, level 6, advanced streaming, 4604351 +silesia, level 7 row 1, advanced streaming, 4570271 +silesia, level 7 row 2, advanced streaming, 4565169 +silesia, level 7, advanced streaming, 4570271 +silesia, level 9, advanced streaming, 4545850 +silesia, level 11 row 1, advanced streaming, 4505658 +silesia, level 11 row 2, advanced streaming, 4503429 +silesia, level 12 row 1, advanced streaming, 4505658 +silesia, level 12 row 2, advanced streaming, 4503429 silesia, level 13, advanced streaming, 4493990 silesia, level 16, advanced streaming, 4360041 silesia, level 19, advanced streaming, 4296055 @@ -893,7 +893,7 @@ silesia, multithreaded long distance mode, advanced silesia, small window log, advanced streaming, 7111103 silesia, small hash log, advanced streaming, 6526141 silesia, small chain log, advanced streaming, 4912197 -silesia, explicit params, advanced streaming, 4795452 +silesia, explicit params, advanced streaming, 4795857 silesia, uncompressed literals, advanced streaming, 5120566 silesia, uncompressed literals optimal, advanced streaming, 4319518 silesia, huffman literals, advanced streaming, 5321370 @@ -905,18 +905,18 @@ silesia.tar, level 0, advanced silesia.tar, level 1, advanced streaming, 5327708 silesia.tar, level 3, advanced streaming, 4859271 silesia.tar, level 4, advanced streaming, 4797470 -silesia.tar, level 5 row 1, advanced streaming, 4677748 -silesia.tar, level 5 row 2, advanced streaming, 4682169 -silesia.tar, level 5, advanced streaming, 4677748 -silesia.tar, level 6, advanced streaming, 4613246 -silesia.tar, level 7 row 1, advanced streaming, 4576664 -silesia.tar, level 7 row 2, advanced streaming, 4575394 -silesia.tar, level 7, advanced streaming, 4576664 -silesia.tar, level 9, advanced streaming, 4552900 -silesia.tar, level 11 row 1, advanced streaming, 4514433 -silesia.tar, level 11 row 2, advanced streaming, 4513604 -silesia.tar, level 12 row 1, advanced streaming, 4514049 -silesia.tar, level 12 row 2, advanced streaming, 4513797 +silesia.tar, level 5 row 1, advanced streaming, 4679020 +silesia.tar, level 5 row 2, advanced streaming, 4682355 +silesia.tar, level 5, advanced streaming, 4679020 +silesia.tar, level 6, advanced streaming, 4614558 +silesia.tar, level 7 row 1, advanced streaming, 4579823 +silesia.tar, level 7 row 2, advanced streaming, 4575601 +silesia.tar, level 7, advanced streaming, 4579823 +silesia.tar, level 9, advanced streaming, 4555445 +silesia.tar, level 11 row 1, advanced streaming, 4514959 +silesia.tar, level 11 row 2, advanced streaming, 4513810 +silesia.tar, level 12 row 1, advanced streaming, 4514514 +silesia.tar, level 12 row 2, advanced streaming, 4514003 silesia.tar, level 13, advanced streaming, 4502956 silesia.tar, level 16, advanced streaming, 4360546 silesia.tar, level 19, advanced streaming, 4265911 @@ -927,105 +927,105 @@ silesia.tar, multithreaded long distance mode, advanced silesia.tar, small window log, advanced streaming, 7117559 silesia.tar, small hash log, advanced streaming, 6529209 silesia.tar, small chain log, advanced streaming, 4917021 -silesia.tar, explicit params, advanced streaming, 4806873 +silesia.tar, explicit params, advanced streaming, 4807288 silesia.tar, uncompressed literals, advanced streaming, 5127423 silesia.tar, uncompressed literals optimal, advanced streaming, 4310141 silesia.tar, huffman literals, advanced streaming, 5341712 silesia.tar, multithreaded with advanced params, advanced streaming, 5122567 github, level -5, advanced streaming, 204407 -github, level -5 with dict, advanced streaming, 46718 +github, level -5 with dict, advanced streaming, 45832 github, level -3, advanced streaming, 193253 -github, level -3 with dict, advanced streaming, 45395 +github, level -3 with dict, advanced streaming, 44671 github, level -1, advanced streaming, 175468 -github, level -1 with dict, advanced streaming, 43170 +github, level -1 with dict, advanced streaming, 41825 github, level 0, advanced streaming, 136332 github, level 0 with dict, advanced streaming, 41148 github, level 0 with dict dms, advanced streaming, 41148 github, level 0 with dict dds, advanced streaming, 41148 github, level 0 with dict copy, advanced streaming, 41124 -github, level 0 with dict load, advanced streaming, 42252 +github, level 0 with dict load, advanced streaming, 41847 github, level 1, advanced streaming, 142365 -github, level 1 with dict, advanced streaming, 41682 -github, level 1 with dict dms, advanced streaming, 41682 -github, level 1 with dict dds, advanced streaming, 41682 -github, level 1 with dict copy, advanced streaming, 41698 -github, level 1 with dict load, advanced streaming, 43814 +github, level 1 with dict, advanced streaming, 41266 +github, level 1 with dict dms, advanced streaming, 41266 +github, level 1 with dict dds, advanced streaming, 41266 +github, level 1 with dict copy, advanced streaming, 41279 +github, level 1 with dict load, advanced streaming, 43331 github, level 3, advanced streaming, 136332 github, level 3 with dict, advanced streaming, 41148 github, level 3 with dict dms, advanced streaming, 41148 github, level 3 with dict dds, advanced streaming, 41148 github, level 3 with dict copy, advanced streaming, 41124 -github, level 3 with dict load, advanced streaming, 42252 +github, level 3 with dict load, advanced streaming, 41847 github, level 4, advanced streaming, 136199 github, level 4 with dict, advanced streaming, 41251 github, level 4 with dict dms, advanced streaming, 41251 github, level 4 with dict dds, advanced streaming, 41251 github, level 4 with dict copy, advanced streaming, 41216 -github, level 4 with dict load, advanced streaming, 41159 +github, level 4 with dict load, advanced streaming, 41548 github, level 5 row 1, advanced streaming, 134584 -github, level 5 row 1 with dict dms, advanced streaming, 38758 +github, level 5 row 1 with dict dms, advanced streaming, 38754 github, level 5 row 1 with dict dds, advanced streaming, 38728 -github, level 5 row 1 with dict copy, advanced streaming, 38759 -github, level 5 row 1 with dict load, advanced streaming, 41518 +github, level 5 row 1 with dict copy, advanced streaming, 38755 +github, level 5 row 1 with dict load, advanced streaming, 41899 github, level 5 row 2, advanced streaming, 135121 github, level 5 row 2 with dict dms, advanced streaming, 38938 github, level 5 row 2 with dict dds, advanced streaming, 38732 github, level 5 row 2 with dict copy, advanced streaming, 38934 -github, level 5 row 2 with dict load, advanced streaming, 40725 +github, level 5 row 2 with dict load, advanced streaming, 41248 github, level 5, advanced streaming, 135121 -github, level 5 with dict, advanced streaming, 38758 -github, level 5 with dict dms, advanced streaming, 38758 +github, level 5 with dict, advanced streaming, 38754 +github, level 5 with dict dms, advanced streaming, 38754 github, level 5 with dict dds, advanced streaming, 38728 -github, level 5 with dict copy, advanced streaming, 38759 -github, level 5 with dict load, advanced streaming, 40725 +github, level 5 with dict copy, advanced streaming, 38755 +github, level 5 with dict load, advanced streaming, 41248 github, level 6, advanced streaming, 135122 -github, level 6 with dict, advanced streaming, 38671 -github, level 6 with dict dms, advanced streaming, 38671 -github, level 6 with dict dds, advanced streaming, 38636 -github, level 6 with dict copy, advanced streaming, 38669 -github, level 6 with dict load, advanced streaming, 40695 +github, level 6 with dict, advanced streaming, 38669 +github, level 6 with dict dms, advanced streaming, 38669 +github, level 6 with dict dds, advanced streaming, 38638 +github, level 6 with dict copy, advanced streaming, 38665 +github, level 6 with dict load, advanced streaming, 41153 github, level 7 row 1, advanced streaming, 134584 -github, level 7 row 1 with dict dms, advanced streaming, 38758 -github, level 7 row 1 with dict dds, advanced streaming, 38745 -github, level 7 row 1 with dict copy, advanced streaming, 38755 -github, level 7 row 1 with dict load, advanced streaming, 43154 +github, level 7 row 1 with dict dms, advanced streaming, 38765 +github, level 7 row 1 with dict dds, advanced streaming, 38749 +github, level 7 row 1 with dict copy, advanced streaming, 38759 +github, level 7 row 1 with dict load, advanced streaming, 43227 github, level 7 row 2, advanced streaming, 135122 github, level 7 row 2 with dict dms, advanced streaming, 38860 github, level 7 row 2 with dict dds, advanced streaming, 38766 github, level 7 row 2 with dict copy, advanced streaming, 38834 -github, level 7 row 2 with dict load, advanced streaming, 40695 +github, level 7 row 2 with dict load, advanced streaming, 41153 github, level 7, advanced streaming, 135122 -github, level 7 with dict, advanced streaming, 38758 -github, level 7 with dict dms, advanced streaming, 38758 -github, level 7 with dict dds, advanced streaming, 38745 -github, level 7 with dict copy, advanced streaming, 38755 -github, level 7 with dict load, advanced streaming, 40695 +github, level 7 with dict, advanced streaming, 38765 +github, level 7 with dict dms, advanced streaming, 38765 +github, level 7 with dict dds, advanced streaming, 38749 +github, level 7 with dict copy, advanced streaming, 38759 +github, level 7 with dict load, advanced streaming, 41153 github, level 9, advanced streaming, 135122 -github, level 9 with dict, advanced streaming, 39437 -github, level 9 with dict dms, advanced streaming, 39437 +github, level 9 with dict, advanced streaming, 39439 +github, level 9 with dict dms, advanced streaming, 39439 github, level 9 with dict dds, advanced streaming, 39393 -github, level 9 with dict copy, advanced streaming, 39398 -github, level 9 with dict load, advanced streaming, 41710 +github, level 9 with dict copy, advanced streaming, 39362 +github, level 9 with dict load, advanced streaming, 42148 github, level 11 row 1, advanced streaming, 135367 github, level 11 row 1 with dict dms, advanced streaming, 39671 github, level 11 row 1 with dict dds, advanced streaming, 39671 github, level 11 row 1 with dict copy, advanced streaming, 39651 -github, level 11 row 1 with dict load, advanced streaming, 41360 +github, level 11 row 1 with dict load, advanced streaming, 41744 github, level 11 row 2, advanced streaming, 135367 github, level 11 row 2 with dict dms, advanced streaming, 39671 github, level 11 row 2 with dict dds, advanced streaming, 39671 github, level 11 row 2 with dict copy, advanced streaming, 39651 -github, level 11 row 2 with dict load, advanced streaming, 41360 +github, level 11 row 2 with dict load, advanced streaming, 41744 github, level 12 row 1, advanced streaming, 134402 github, level 12 row 1 with dict dms, advanced streaming, 39677 github, level 12 row 1 with dict dds, advanced streaming, 39677 github, level 12 row 1 with dict copy, advanced streaming, 39677 -github, level 12 row 1 with dict load, advanced streaming, 41166 +github, level 12 row 1 with dict load, advanced streaming, 41553 github, level 12 row 2, advanced streaming, 134402 github, level 12 row 2 with dict dms, advanced streaming, 39677 github, level 12 row 2 with dict dds, advanced streaming, 39677 github, level 12 row 2 with dict copy, advanced streaming, 39677 -github, level 12 row 2 with dict load, advanced streaming, 41166 +github, level 12 row 2 with dict load, advanced streaming, 41553 github, level 13, advanced streaming, 132878 github, level 13 with dict, advanced streaming, 39900 github, level 13 with dict dms, advanced streaming, 39900 @@ -1058,11 +1058,11 @@ github, uncompressed literals optimal, advanced github, huffman literals, advanced streaming, 142365 github, multithreaded with advanced params, advanced streaming, 165911 github.tar, level -5, advanced streaming, 52152 -github.tar, level -5 with dict, advanced streaming, 51045 +github.tar, level -5 with dict, advanced streaming, 51181 github.tar, level -3, advanced streaming, 45678 -github.tar, level -3 with dict, advanced streaming, 44656 +github.tar, level -3 with dict, advanced streaming, 44734 github.tar, level -1, advanced streaming, 42560 -github.tar, level -1 with dict, advanced streaming, 41151 +github.tar, level -1 with dict, advanced streaming, 41353 github.tar, level 0, advanced streaming, 38831 github.tar, level 0 with dict, advanced streaming, 37995 github.tar, level 0 with dict dms, advanced streaming, 38003 @@ -1070,10 +1070,10 @@ github.tar, level 0 with dict dds, advanced github.tar, level 0 with dict copy, advanced streaming, 37995 github.tar, level 0 with dict load, advanced streaming, 37956 github.tar, level 1, advanced streaming, 39200 -github.tar, level 1 with dict, advanced streaming, 38089 -github.tar, level 1 with dict dms, advanced streaming, 38294 -github.tar, level 1 with dict dds, advanced streaming, 38294 -github.tar, level 1 with dict copy, advanced streaming, 38089 +github.tar, level 1 with dict, advanced streaming, 38119 +github.tar, level 1 with dict dms, advanced streaming, 38406 +github.tar, level 1 with dict dds, advanced streaming, 38406 +github.tar, level 1 with dict copy, advanced streaming, 38119 github.tar, level 1 with dict load, advanced streaming, 38364 github.tar, level 3, advanced streaming, 38831 github.tar, level 3 with dict, advanced streaming, 37995 @@ -1088,64 +1088,64 @@ github.tar, level 4 with dict dds, advanced github.tar, level 4 with dict copy, advanced streaming, 37948 github.tar, level 4 with dict load, advanced streaming, 37927 github.tar, level 5 row 1, advanced streaming, 39651 -github.tar, level 5 row 1 with dict dms, advanced streaming, 39059 -github.tar, level 5 row 1 with dict dds, advanced streaming, 39067 -github.tar, level 5 row 1 with dict copy, advanced streaming, 39082 -github.tar, level 5 row 1 with dict load, advanced streaming, 38999 +github.tar, level 5 row 1 with dict dms, advanced streaming, 39043 +github.tar, level 5 row 1 with dict dds, advanced streaming, 39069 +github.tar, level 5 row 1 with dict copy, advanced streaming, 39145 +github.tar, level 5 row 1 with dict load, advanced streaming, 39000 github.tar, level 5 row 2, advanced streaming, 39701 github.tar, level 5 row 2 with dict dms, advanced streaming, 39365 github.tar, level 5 row 2 with dict dds, advanced streaming, 39233 github.tar, level 5 row 2 with dict copy, advanced streaming, 39715 github.tar, level 5 row 2 with dict load, advanced streaming, 39158 github.tar, level 5, advanced streaming, 39651 -github.tar, level 5 with dict, advanced streaming, 39082 -github.tar, level 5 with dict dms, advanced streaming, 39059 -github.tar, level 5 with dict dds, advanced streaming, 39067 -github.tar, level 5 with dict copy, advanced streaming, 39082 -github.tar, level 5 with dict load, advanced streaming, 38999 +github.tar, level 5 with dict, advanced streaming, 39145 +github.tar, level 5 with dict dms, advanced streaming, 39043 +github.tar, level 5 with dict dds, advanced streaming, 39069 +github.tar, level 5 with dict copy, advanced streaming, 39145 +github.tar, level 5 with dict load, advanced streaming, 39000 github.tar, level 6, advanced streaming, 39282 github.tar, level 6 with dict, advanced streaming, 38656 -github.tar, level 6 with dict dms, advanced streaming, 38636 -github.tar, level 6 with dict dds, advanced streaming, 38634 +github.tar, level 6 with dict dms, advanced streaming, 38640 +github.tar, level 6 with dict dds, advanced streaming, 38643 github.tar, level 6 with dict copy, advanced streaming, 38656 -github.tar, level 6 with dict load, advanced streaming, 38648 -github.tar, level 7 row 1, advanced streaming, 38110 -github.tar, level 7 row 1 with dict dms, advanced streaming, 37858 -github.tar, level 7 row 1 with dict dds, advanced streaming, 37882 -github.tar, level 7 row 1 with dict copy, advanced streaming, 37865 -github.tar, level 7 row 1 with dict load, advanced streaming, 37436 +github.tar, level 6 with dict load, advanced streaming, 38647 +github.tar, level 7 row 1, advanced streaming, 38005 +github.tar, level 7 row 1 with dict dms, advanced streaming, 37832 +github.tar, level 7 row 1 with dict dds, advanced streaming, 37857 +github.tar, level 7 row 1 with dict copy, advanced streaming, 37839 +github.tar, level 7 row 1 with dict load, advanced streaming, 37286 github.tar, level 7 row 2, advanced streaming, 38077 github.tar, level 7 row 2 with dict dms, advanced streaming, 38012 github.tar, level 7 row 2 with dict dds, advanced streaming, 38014 github.tar, level 7 row 2 with dict copy, advanced streaming, 38101 github.tar, level 7 row 2 with dict load, advanced streaming, 37402 -github.tar, level 7, advanced streaming, 38110 -github.tar, level 7 with dict, advanced streaming, 37865 -github.tar, level 7 with dict dms, advanced streaming, 37858 -github.tar, level 7 with dict dds, advanced streaming, 37882 -github.tar, level 7 with dict copy, advanced streaming, 37865 -github.tar, level 7 with dict load, advanced streaming, 37436 -github.tar, level 9, advanced streaming, 36760 -github.tar, level 9 with dict, advanced streaming, 36484 -github.tar, level 9 with dict dms, advanced streaming, 36567 -github.tar, level 9 with dict dds, advanced streaming, 36628 -github.tar, level 9 with dict copy, advanced streaming, 36484 -github.tar, level 9 with dict load, advanced streaming, 36401 -github.tar, level 11 row 1, advanced streaming, 36081 +github.tar, level 7, advanced streaming, 38005 +github.tar, level 7 with dict, advanced streaming, 37839 +github.tar, level 7 with dict dms, advanced streaming, 37832 +github.tar, level 7 with dict dds, advanced streaming, 37857 +github.tar, level 7 with dict copy, advanced streaming, 37839 +github.tar, level 7 with dict load, advanced streaming, 37286 +github.tar, level 9, advanced streaming, 36723 +github.tar, level 9 with dict, advanced streaming, 36531 +github.tar, level 9 with dict dms, advanced streaming, 36615 +github.tar, level 9 with dict dds, advanced streaming, 36682 +github.tar, level 9 with dict copy, advanced streaming, 36531 +github.tar, level 9 with dict load, advanced streaming, 36322 +github.tar, level 11 row 1, advanced streaming, 36085 github.tar, level 11 row 1 with dict dms, advanced streaming, 36963 github.tar, level 11 row 1 with dict dds, advanced streaming, 36963 github.tar, level 11 row 1 with dict copy, advanced streaming, 36557 -github.tar, level 11 row 1 with dict load, advanced streaming, 36434 +github.tar, level 11 row 1 with dict load, advanced streaming, 36423 github.tar, level 11 row 2, advanced streaming, 36110 github.tar, level 11 row 2 with dict dms, advanced streaming, 36963 github.tar, level 11 row 2 with dict dds, advanced streaming, 36963 github.tar, level 11 row 2 with dict copy, advanced streaming, 36557 github.tar, level 11 row 2 with dict load, advanced streaming, 36459 -github.tar, level 12 row 1, advanced streaming, 36081 +github.tar, level 12 row 1, advanced streaming, 36085 github.tar, level 12 row 1 with dict dms, advanced streaming, 36986 github.tar, level 12 row 1 with dict dds, advanced streaming, 36986 github.tar, level 12 row 1 with dict copy, advanced streaming, 36609 -github.tar, level 12 row 1 with dict load, advanced streaming, 36434 +github.tar, level 12 row 1 with dict load, advanced streaming, 36423 github.tar, level 12 row 2, advanced streaming, 36110 github.tar, level 12 row 2 with dict dms, advanced streaming, 36986 github.tar, level 12 row 2 with dict dds, advanced streaming, 36986 @@ -1189,10 +1189,10 @@ silesia, level 0, old stre silesia, level 1, old streaming, 5306388 silesia, level 3, old streaming, 4842075 silesia, level 4, old streaming, 4779186 -silesia, level 5, old streaming, 4666323 -silesia, level 6, old streaming, 4603066 -silesia, level 7, old streaming, 4566984 -silesia, level 9, old streaming, 4543018 +silesia, level 5, old streaming, 4667668 +silesia, level 6, old streaming, 4604351 +silesia, level 7, old streaming, 4570271 +silesia, level 9, old streaming, 4545850 silesia, level 13, old streaming, 4493990 silesia, level 16, old streaming, 4360041 silesia, level 19, old streaming, 4296055 @@ -1207,10 +1207,10 @@ silesia.tar, level 0, old stre silesia.tar, level 1, old streaming, 5327708 silesia.tar, level 3, old streaming, 4859271 silesia.tar, level 4, old streaming, 4797470 -silesia.tar, level 5, old streaming, 4677748 -silesia.tar, level 6, old streaming, 4613246 -silesia.tar, level 7, old streaming, 4576664 -silesia.tar, level 9, old streaming, 4552900 +silesia.tar, level 5, old streaming, 4679020 +silesia.tar, level 6, old streaming, 4614558 +silesia.tar, level 7, old streaming, 4579823 +silesia.tar, level 9, old streaming, 4555445 silesia.tar, level 13, old streaming, 4502956 silesia.tar, level 16, old streaming, 4360546 silesia.tar, level 19, old streaming, 4265911 @@ -1219,27 +1219,27 @@ silesia.tar, uncompressed literals, old stre silesia.tar, uncompressed literals optimal, old streaming, 4265911 silesia.tar, huffman literals, old streaming, 6179056 github, level -5, old streaming, 204407 -github, level -5 with dict, old streaming, 46718 +github, level -5 with dict, old streaming, 45832 github, level -3, old streaming, 193253 -github, level -3 with dict, old streaming, 45395 +github, level -3 with dict, old streaming, 44671 github, level -1, old streaming, 175468 -github, level -1 with dict, old streaming, 43170 +github, level -1 with dict, old streaming, 41825 github, level 0, old streaming, 136332 github, level 0 with dict, old streaming, 41148 github, level 1, old streaming, 142365 -github, level 1 with dict, old streaming, 41682 +github, level 1 with dict, old streaming, 41266 github, level 3, old streaming, 136332 github, level 3 with dict, old streaming, 41148 github, level 4, old streaming, 136199 github, level 4 with dict, old streaming, 41251 github, level 5, old streaming, 135121 -github, level 5 with dict, old streaming, 38758 +github, level 5 with dict, old streaming, 38754 github, level 6, old streaming, 135122 -github, level 6 with dict, old streaming, 38671 +github, level 6 with dict, old streaming, 38669 github, level 7, old streaming, 135122 -github, level 7 with dict, old streaming, 38758 +github, level 7 with dict, old streaming, 38765 github, level 9, old streaming, 135122 -github, level 9 with dict, old streaming, 39437 +github, level 9 with dict, old streaming, 39439 github, level 13, old streaming, 132878 github, level 13 with dict, old streaming, 39900 github, level 16, old streaming, 133209 @@ -1252,27 +1252,27 @@ github, uncompressed literals, old stre github, uncompressed literals optimal, old streaming, 132879 github, huffman literals, old streaming, 175468 github.tar, level -5, old streaming, 52152 -github.tar, level -5 with dict, old streaming, 51045 +github.tar, level -5 with dict, old streaming, 51181 github.tar, level -3, old streaming, 45678 -github.tar, level -3 with dict, old streaming, 44656 +github.tar, level -3 with dict, old streaming, 44734 github.tar, level -1, old streaming, 42560 -github.tar, level -1 with dict, old streaming, 41151 +github.tar, level -1 with dict, old streaming, 41353 github.tar, level 0, old streaming, 38831 github.tar, level 0 with dict, old streaming, 37995 github.tar, level 1, old streaming, 39200 -github.tar, level 1 with dict, old streaming, 38089 +github.tar, level 1 with dict, old streaming, 38119 github.tar, level 3, old streaming, 38831 github.tar, level 3 with dict, old streaming, 37995 github.tar, level 4, old streaming, 38893 github.tar, level 4 with dict, old streaming, 37948 github.tar, level 5, old streaming, 39651 -github.tar, level 5 with dict, old streaming, 39082 +github.tar, level 5 with dict, old streaming, 39145 github.tar, level 6, old streaming, 39282 github.tar, level 6 with dict, old streaming, 38656 -github.tar, level 7, old streaming, 38110 -github.tar, level 7 with dict, old streaming, 37865 -github.tar, level 9, old streaming, 36760 -github.tar, level 9 with dict, old streaming, 36484 +github.tar, level 7, old streaming, 38005 +github.tar, level 7 with dict, old streaming, 37839 +github.tar, level 9, old streaming, 36723 +github.tar, level 9 with dict, old streaming, 36531 github.tar, level 13, old streaming, 35501 github.tar, level 13 with dict, old streaming, 37130 github.tar, level 16, old streaming, 40466 @@ -1291,10 +1291,10 @@ silesia, level 0, old stre silesia, level 1, old streaming advanced, 5306388 silesia, level 3, old streaming advanced, 4842075 silesia, level 4, old streaming advanced, 4779186 -silesia, level 5, old streaming advanced, 4666323 -silesia, level 6, old streaming advanced, 4603066 -silesia, level 7, old streaming advanced, 4566984 -silesia, level 9, old streaming advanced, 4543018 +silesia, level 5, old streaming advanced, 4667668 +silesia, level 6, old streaming advanced, 4604351 +silesia, level 7, old streaming advanced, 4570271 +silesia, level 9, old streaming advanced, 4545850 silesia, level 13, old streaming advanced, 4493990 silesia, level 16, old streaming advanced, 4360041 silesia, level 19, old streaming advanced, 4296055 @@ -1305,7 +1305,7 @@ silesia, multithreaded long distance mode, old stre silesia, small window log, old streaming advanced, 7111103 silesia, small hash log, old streaming advanced, 6526141 silesia, small chain log, old streaming advanced, 4912197 -silesia, explicit params, old streaming advanced, 4795452 +silesia, explicit params, old streaming advanced, 4795857 silesia, uncompressed literals, old streaming advanced, 4842075 silesia, uncompressed literals optimal, old streaming advanced, 4296055 silesia, huffman literals, old streaming advanced, 6172207 @@ -1317,10 +1317,10 @@ silesia.tar, level 0, old stre silesia.tar, level 1, old streaming advanced, 5327708 silesia.tar, level 3, old streaming advanced, 4859271 silesia.tar, level 4, old streaming advanced, 4797470 -silesia.tar, level 5, old streaming advanced, 4677748 -silesia.tar, level 6, old streaming advanced, 4613246 -silesia.tar, level 7, old streaming advanced, 4576664 -silesia.tar, level 9, old streaming advanced, 4552900 +silesia.tar, level 5, old streaming advanced, 4679020 +silesia.tar, level 6, old streaming advanced, 4614558 +silesia.tar, level 7, old streaming advanced, 4579823 +silesia.tar, level 9, old streaming advanced, 4555445 silesia.tar, level 13, old streaming advanced, 4502956 silesia.tar, level 16, old streaming advanced, 4360546 silesia.tar, level 19, old streaming advanced, 4265911 @@ -1331,17 +1331,17 @@ silesia.tar, multithreaded long distance mode, old stre silesia.tar, small window log, old streaming advanced, 7117562 silesia.tar, small hash log, old streaming advanced, 6529209 silesia.tar, small chain log, old streaming advanced, 4917021 -silesia.tar, explicit params, old streaming advanced, 4806873 +silesia.tar, explicit params, old streaming advanced, 4807288 silesia.tar, uncompressed literals, old streaming advanced, 4859271 silesia.tar, uncompressed literals optimal, old streaming advanced, 4265911 silesia.tar, huffman literals, old streaming advanced, 6179056 silesia.tar, multithreaded with advanced params, old streaming advanced, 4859271 github, level -5, old streaming advanced, 213265 -github, level -5 with dict, old streaming advanced, 49562 +github, level -5 with dict, old streaming advanced, 46708 github, level -3, old streaming advanced, 196126 -github, level -3 with dict, old streaming advanced, 44956 +github, level -3 with dict, old streaming advanced, 45476 github, level -1, old streaming advanced, 181107 -github, level -1 with dict, old streaming advanced, 42383 +github, level -1 with dict, old streaming advanced, 42060 github, level 0, old streaming advanced, 141104 github, level 0 with dict, old streaming advanced, 41113 github, level 1, old streaming advanced, 143693 @@ -1355,9 +1355,9 @@ github, level 5 with dict, old stre github, level 6, old streaming advanced, 138676 github, level 6 with dict, old streaming advanced, 38744 github, level 7, old streaming advanced, 138676 -github, level 7 with dict, old streaming advanced, 38924 +github, level 7 with dict, old streaming advanced, 38875 github, level 9, old streaming advanced, 138676 -github, level 9 with dict, old streaming advanced, 38981 +github, level 9 with dict, old streaming advanced, 38941 github, level 13, old streaming advanced, 138676 github, level 13 with dict, old streaming advanced, 39725 github, level 16, old streaming advanced, 138575 @@ -1378,11 +1378,11 @@ github, uncompressed literals optimal, old stre github, huffman literals, old streaming advanced, 181107 github, multithreaded with advanced params, old streaming advanced, 141104 github.tar, level -5, old streaming advanced, 52152 -github.tar, level -5 with dict, old streaming advanced, 50988 +github.tar, level -5 with dict, old streaming advanced, 51129 github.tar, level -3, old streaming advanced, 45678 -github.tar, level -3 with dict, old streaming advanced, 44729 +github.tar, level -3 with dict, old streaming advanced, 44986 github.tar, level -1, old streaming advanced, 42560 -github.tar, level -1 with dict, old streaming advanced, 41589 +github.tar, level -1 with dict, old streaming advanced, 41650 github.tar, level 0, old streaming advanced, 38831 github.tar, level 0 with dict, old streaming advanced, 38013 github.tar, level 1, old streaming advanced, 39200 @@ -1392,13 +1392,13 @@ github.tar, level 3 with dict, old stre github.tar, level 4, old streaming advanced, 38893 github.tar, level 4 with dict, old streaming advanced, 38063 github.tar, level 5, old streaming advanced, 39651 -github.tar, level 5 with dict, old streaming advanced, 38997 +github.tar, level 5 with dict, old streaming advanced, 39018 github.tar, level 6, old streaming advanced, 39282 -github.tar, level 6 with dict, old streaming advanced, 38640 -github.tar, level 7, old streaming advanced, 38110 -github.tar, level 7 with dict, old streaming advanced, 37387 -github.tar, level 9, old streaming advanced, 36760 -github.tar, level 9 with dict, old streaming advanced, 36312 +github.tar, level 6 with dict, old streaming advanced, 38635 +github.tar, level 7, old streaming advanced, 38005 +github.tar, level 7 with dict, old streaming advanced, 37264 +github.tar, level 9, old streaming advanced, 36723 +github.tar, level 9 with dict, old streaming advanced, 36241 github.tar, level 13, old streaming advanced, 35501 github.tar, level 13 with dict, old streaming advanced, 35807 github.tar, level 16, old streaming advanced, 40466 @@ -1418,62 +1418,62 @@ github.tar, uncompressed literals, old stre github.tar, uncompressed literals optimal, old streaming advanced, 32276 github.tar, huffman literals, old streaming advanced, 42560 github.tar, multithreaded with advanced params, old streaming advanced, 38831 -github, level -5 with dict, old streaming cdict, 46718 -github, level -3 with dict, old streaming cdict, 45395 -github, level -1 with dict, old streaming cdict, 43170 +github, level -5 with dict, old streaming cdict, 45832 +github, level -3 with dict, old streaming cdict, 44671 +github, level -1 with dict, old streaming cdict, 41825 github, level 0 with dict, old streaming cdict, 41148 -github, level 1 with dict, old streaming cdict, 41682 +github, level 1 with dict, old streaming cdict, 41266 github, level 3 with dict, old streaming cdict, 41148 github, level 4 with dict, old streaming cdict, 41251 -github, level 5 with dict, old streaming cdict, 38758 -github, level 6 with dict, old streaming cdict, 38671 -github, level 7 with dict, old streaming cdict, 38758 -github, level 9 with dict, old streaming cdict, 39437 +github, level 5 with dict, old streaming cdict, 38754 +github, level 6 with dict, old streaming cdict, 38669 +github, level 7 with dict, old streaming cdict, 38765 +github, level 9 with dict, old streaming cdict, 39439 github, level 13 with dict, old streaming cdict, 39900 github, level 16 with dict, old streaming cdict, 37577 github, level 19 with dict, old streaming cdict, 37576 github, no source size with dict, old streaming cdict, 40654 -github.tar, level -5 with dict, old streaming cdict, 51191 -github.tar, level -3 with dict, old streaming cdict, 44821 -github.tar, level -1 with dict, old streaming cdict, 41775 +github.tar, level -5 with dict, old streaming cdict, 51286 +github.tar, level -3 with dict, old streaming cdict, 45147 +github.tar, level -1 with dict, old streaming cdict, 41865 github.tar, level 0 with dict, old streaming cdict, 37956 github.tar, level 1 with dict, old streaming cdict, 38364 github.tar, level 3 with dict, old streaming cdict, 37956 github.tar, level 4 with dict, old streaming cdict, 37927 -github.tar, level 5 with dict, old streaming cdict, 38999 -github.tar, level 6 with dict, old streaming cdict, 38648 -github.tar, level 7 with dict, old streaming cdict, 37436 -github.tar, level 9 with dict, old streaming cdict, 36401 +github.tar, level 5 with dict, old streaming cdict, 39000 +github.tar, level 6 with dict, old streaming cdict, 38647 +github.tar, level 7 with dict, old streaming cdict, 37286 +github.tar, level 9 with dict, old streaming cdict, 36322 github.tar, level 13 with dict, old streaming cdict, 36010 github.tar, level 16 with dict, old streaming cdict, 39081 github.tar, level 19 with dict, old streaming cdict, 32479 github.tar, no source size with dict, old streaming cdict, 38000 -github, level -5 with dict, old streaming advanced cdict, 49562 -github, level -3 with dict, old streaming advanced cdict, 44956 -github, level -1 with dict, old streaming advanced cdict, 42383 +github, level -5 with dict, old streaming advanced cdict, 46708 +github, level -3 with dict, old streaming advanced cdict, 45476 +github, level -1 with dict, old streaming advanced cdict, 42060 github, level 0 with dict, old streaming advanced cdict, 41113 github, level 1 with dict, old streaming advanced cdict, 42430 github, level 3 with dict, old streaming advanced cdict, 41113 github, level 4 with dict, old streaming advanced cdict, 41084 github, level 5 with dict, old streaming advanced cdict, 38723 github, level 6 with dict, old streaming advanced cdict, 38744 -github, level 7 with dict, old streaming advanced cdict, 38924 -github, level 9 with dict, old streaming advanced cdict, 38981 +github, level 7 with dict, old streaming advanced cdict, 38875 +github, level 9 with dict, old streaming advanced cdict, 38941 github, level 13 with dict, old streaming advanced cdict, 39725 github, level 16 with dict, old streaming advanced cdict, 40789 github, level 19 with dict, old streaming advanced cdict, 37576 github, no source size with dict, old streaming advanced cdict, 40608 -github.tar, level -5 with dict, old streaming advanced cdict, 50854 -github.tar, level -3 with dict, old streaming advanced cdict, 44571 -github.tar, level -1 with dict, old streaming advanced cdict, 41477 +github.tar, level -5 with dict, old streaming advanced cdict, 50791 +github.tar, level -3 with dict, old streaming advanced cdict, 44926 +github.tar, level -1 with dict, old streaming advanced cdict, 41482 github.tar, level 0 with dict, old streaming advanced cdict, 38013 github.tar, level 1 with dict, old streaming advanced cdict, 38168 github.tar, level 3 with dict, old streaming advanced cdict, 38013 github.tar, level 4 with dict, old streaming advanced cdict, 38063 -github.tar, level 5 with dict, old streaming advanced cdict, 38997 -github.tar, level 6 with dict, old streaming advanced cdict, 38640 -github.tar, level 7 with dict, old streaming advanced cdict, 37387 -github.tar, level 9 with dict, old streaming advanced cdict, 36312 +github.tar, level 5 with dict, old streaming advanced cdict, 39018 +github.tar, level 6 with dict, old streaming advanced cdict, 38635 +github.tar, level 7 with dict, old streaming advanced cdict, 37264 +github.tar, level 9 with dict, old streaming advanced cdict, 36241 github.tar, level 13 with dict, old streaming advanced cdict, 35807 github.tar, level 16 with dict, old streaming advanced cdict, 38578 github.tar, level 19 with dict, old streaming advanced cdict, 32704 diff --git a/zlibWrapper/examples/zwrapbench.c b/zlibWrapper/examples/zwrapbench.c index 3bc9a1ab0..33dcb2a73 100644 --- a/zlibWrapper/examples/zwrapbench.c +++ b/zlibWrapper/examples/zwrapbench.c @@ -26,7 +26,7 @@ #include "datagen.h" /* RDG_genBuffer */ #include "xxhash.h" -#include "zstd_zlibwrapper.h" +#include "../zstd_zlibwrapper.h" @@ -109,17 +109,17 @@ static unsigned g_nbIterations = NBLOOPS; static size_t g_blockSize = 0; int g_additionalParam = 0; -void BMK_setNotificationLevel(unsigned level) { g_displayLevel=level; } +static void BMK_setNotificationLevel(unsigned level) { g_displayLevel=level; } -void BMK_setAdditionalParam(int additionalParam) { g_additionalParam=additionalParam; } +static void BMK_setAdditionalParam(int additionalParam) { g_additionalParam=additionalParam; } -void BMK_SetNbIterations(unsigned nbLoops) +static void BMK_SetNbIterations(unsigned nbLoops) { g_nbIterations = nbLoops; DISPLAYLEVEL(3, "- test >= %u seconds per compression / decompression -\n", g_nbIterations); } -void BMK_SetBlockSize(size_t blockSize) +static void BMK_SetBlockSize(size_t blockSize) { g_blockSize = blockSize; DISPLAYLEVEL(2, "using blocks of size %u KB \n", (unsigned)(blockSize>>10)); @@ -798,7 +798,7 @@ static void BMK_syntheticTest(int cLevel, int cLevelLast, double compressibility } -int BMK_benchFiles(const char** fileNamesTable, unsigned nbFiles, +static int BMK_benchFiles(const char** fileNamesTable, unsigned nbFiles, const char* dictFileName, int cLevel, int cLevelLast) { double const compressibility = (double)g_compressibilityDefault / 100;