Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f37a84ece6 |
@@ -12,8 +12,7 @@ jobs:
|
||||
- run:
|
||||
name: Test
|
||||
command: |
|
||||
./tests/test-license.py
|
||||
cc -v; CFLAGS="-O0 -Werror -pedantic" make all && make clean
|
||||
cc -v; CFLAGS="-O0 -Werror" make all && make clean
|
||||
make c99build ; make clean
|
||||
make c11build ; make clean
|
||||
make aarch64build ; make clean
|
||||
@@ -34,7 +33,7 @@ jobs:
|
||||
make ppc64build; make clean
|
||||
make ppcbuild ; make clean
|
||||
make armbuild ; make clean
|
||||
make -C tests test-legacy test-longmatch; make clean
|
||||
make -C tests test-legacy test-longmatch test-symbols; make clean
|
||||
make -C lib libzstd-nomt; make clean
|
||||
# This step is only run on release tags.
|
||||
# It publishes the source tarball as artifacts and if the GITHUB_TOKEN
|
||||
|
||||
+10
-5
@@ -1,11 +1,16 @@
|
||||
env:
|
||||
CIRRUS_CLONE_DEPTH: 1
|
||||
ARCH: amd64
|
||||
|
||||
task:
|
||||
name: FreeBSD (shortest)
|
||||
freebsd_instance:
|
||||
matrix:
|
||||
image_family: freebsd-12-1
|
||||
# The stable 11.3 image causes "Agent is not responding" so use a snapshot
|
||||
image_family: freebsd-11-3-snap
|
||||
install_script: pkg install -y gmake coreutils
|
||||
image: freebsd-12-0-release-amd64
|
||||
image: freebsd-11-2-release-amd64
|
||||
install_script:
|
||||
- sed -i.bak -e 's,pkg+http://pkg.FreeBSD.org/\${ABI}/quarterly,pkg+http://pkg.FreeBSD.org/\${ABI}/latest,' /etc/pkg/FreeBSD.conf
|
||||
- pkg upgrade -y
|
||||
- pkg install -y gmake coreutils
|
||||
script: |
|
||||
MOREFLAGS="-Werror" gmake -j all
|
||||
gmake shortest
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Downloads data '...'
|
||||
2. Run '...' with flags '...'
|
||||
3. Scroll up on the log to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots and charts**
|
||||
If applicable, add screenshots and charts to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. Mac]
|
||||
- Version [e.g. 22]
|
||||
- Compiler [e.g gcc]
|
||||
- Flags [e.g O2]
|
||||
- Other relevant hardware specs [e.g. Dual-core]
|
||||
- Build system [e.g. Makefile]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
@@ -1,209 +0,0 @@
|
||||
name: generic-dev
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ dev, master, actionsTest ]
|
||||
|
||||
jobs:
|
||||
|
||||
# Dev PR jobs that still have to be migrated from travis
|
||||
#
|
||||
# icc (need self-hosted)
|
||||
# versionTag
|
||||
# valgrindTest (keeps failing for some reason. need investigation)
|
||||
# staticAnalyze (need trusty so need self-hosted)
|
||||
# pcc-fuzz: (need trusty so need self-hosted)
|
||||
# min-decomp-macros (flakey)
|
||||
#
|
||||
# setting up self-hosted is pretty straightforward, but
|
||||
# I need admins permissions to the repo for that it looks like
|
||||
# So I'm tabling that for now
|
||||
#
|
||||
# The master branch exclusive jobs will be in a separate
|
||||
# workflow file (the osx tests and meson build that is)
|
||||
|
||||
benchmarking:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make benchmarking
|
||||
run: make benchmarking
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make test
|
||||
run: make test
|
||||
|
||||
gcc-6-7-libzstd:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: gcc-6 + gcc-7 + libzstdmt compilation
|
||||
run: |
|
||||
make gcc6install gcc7install
|
||||
CC=gcc-6 CFLAGS=-Werror make -j all
|
||||
make clean
|
||||
CC=gcc-7 CFLAGS=-Werror make -j all
|
||||
make clean
|
||||
LDFLAGS=-Wl,--no-undefined make -C lib libzstd-mt
|
||||
make -C tests zbufftest-dll
|
||||
|
||||
gcc-8-asan-ubsan-testzstd:
|
||||
runs-on: ubuntu-16.04 # fails on 18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: gcc-8 + ASan + UBSan + Test Zstd
|
||||
run: |
|
||||
make gcc8install
|
||||
CC=gcc-8 CFLAGS="-Werror" make -j all
|
||||
make clean
|
||||
CC=gcc-8 make -j uasan-test-zstd </dev/null
|
||||
|
||||
gcc-6-asan-ubsan-testzstd-32bit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: gcc-6 + ASan + UBSan + Test Zstd, 32bit mode
|
||||
run: |
|
||||
make gcc6install libc6install
|
||||
CC=gcc-6 CFLAGS="-Werror -m32" make -j all32
|
||||
make clean
|
||||
CC=gcc-6 make -j uasan-test-zstd32
|
||||
|
||||
clang-38-msan-testzstd:
|
||||
runs-on: ubuntu-16.04 # fails on 18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: clang-3.8 + MSan + Test Zstd
|
||||
run: |
|
||||
# make clang38install (doesn't work)
|
||||
sudo apt-add-repository "deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.8 main"
|
||||
sudo apt-get update
|
||||
sudo apt-get install clang-3.8
|
||||
CC=clang-3.8 make clean msan-test-zstd HAVE_ZLIB=0 HAVE_LZ4=0 HAVE_LZMA=0
|
||||
|
||||
cmake-build-and-test-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: cmake build and test check
|
||||
run: |
|
||||
FUZZERTEST=-T1mn ZSTREAM_TESTTIME=-T1mn make cmakebuild
|
||||
cp -r ./ "../zstd source"
|
||||
cd "../zstd source"
|
||||
FUZZERTEST=-T1mn ZSTREAM_TESTTIME=-T1mn make cmakebuild
|
||||
|
||||
gcc-8-asan-ubsan-fuzz:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: gcc-8 + ASan + UBSan + Fuzz Test
|
||||
run: |
|
||||
make gcc8install
|
||||
CC=gcc-8 FUZZER_FLAGS="--long-tests" make clean uasan-fuzztest
|
||||
|
||||
gcc-6-asan-ubsan-fuzz32:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: gcc-6 + ASan + UBSan + Fuzz Test 32bit
|
||||
run: |
|
||||
make gcc6install libc6install
|
||||
CC=gcc-6 CFLAGS="-O2 -m32" FUZZER_FLAGS="--long-tests" make uasan-fuzztest
|
||||
|
||||
clang-38-msan-fuzz:
|
||||
runs-on: ubuntu-16.04 # fails on 18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: clang-3.8 + MSan + Fuzz Test
|
||||
run: |
|
||||
# make clang38install (doesn't work)
|
||||
sudo apt-add-repository "deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.8 main"
|
||||
sudo apt-get update
|
||||
sudo apt-get install clang-3.8
|
||||
CC=clang-3.8 make clean msan-fuzztest
|
||||
|
||||
asan-ubsan-msan-regression:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: ASan + UBSan + MSan + Regression Test
|
||||
run: |
|
||||
make -j uasanregressiontest
|
||||
make clean
|
||||
make -j msanregressiontest
|
||||
|
||||
cpp-gnu90-c99-compatibility:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: C++, gnu90 and c99 compatibility
|
||||
run: |
|
||||
make cxxtest
|
||||
make clean
|
||||
make gnu90build
|
||||
make clean
|
||||
make c99build
|
||||
make clean
|
||||
make travis-install
|
||||
|
||||
mingw-cross-compilation:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: mingw cross-compilation
|
||||
run: |
|
||||
# sudo update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix; (doesn't work)
|
||||
sudo apt-get install gcc-mingw-w64
|
||||
CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ CFLAGS="-Werror -O1" make zstd
|
||||
|
||||
# TODO: Broken test - fix and uncomment
|
||||
# armbuild:
|
||||
# runs-on: ubuntu-16.04 # doesn't work on latest
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - name: ARM Build Test
|
||||
# run: |
|
||||
# make arminstall
|
||||
# make armbuild
|
||||
|
||||
# TODO: Broken test - fix and uncomment
|
||||
# armfuzz:
|
||||
# runs-on: ubuntu-16.04 # doesn't work on latest
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - name: Qemu ARM emulation + Fuzz Test
|
||||
# run: |
|
||||
# make arminstall
|
||||
# make armfuzz
|
||||
|
||||
bourne-shell:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Bourne shell compatibility (shellcheck)
|
||||
run: |
|
||||
wget https://github.com/koalaman/shellcheck/releases/download/v0.7.1/shellcheck-v0.7.1.linux.x86_64.tar.xz
|
||||
tar -xf shellcheck-v0.7.1.linux.x86_64.tar.xz
|
||||
shellcheck-v0.7.1/shellcheck --shell=sh --severity=warning --exclude=SC2010 tests/playTests.sh
|
||||
|
||||
icc:
|
||||
name: icc-check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: install icc
|
||||
run: |
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
sudo apt-get -qqq update
|
||||
sudo apt-get install -y wget build-essential pkg-config cmake ca-certificates gnupg
|
||||
sudo wget https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS-2023.PUB
|
||||
sudo apt-key add GPG-PUB-KEY-INTEL-SW-PRODUCTS-2023.PUB
|
||||
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@v2
|
||||
- name: make check
|
||||
run: |
|
||||
make CC=/opt/intel/oneapi/compiler/latest/linux/bin/intel64/icc check
|
||||
@@ -1,69 +0,0 @@
|
||||
name: generic-release
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
# This will eventually only be for pushes to master
|
||||
# but for dogfooding purposes, I'm running it even
|
||||
# on dev pushes
|
||||
branches: [ dev, master, actionsTest ]
|
||||
|
||||
jobs:
|
||||
# missing jobs
|
||||
#
|
||||
# ppc64le + fuzz test
|
||||
# Qemu PPC64 + Fuzz test
|
||||
# Qemu aarch64 + Fuzz Test (on Xenial)
|
||||
# versions comp
|
||||
# meson test
|
||||
|
||||
osx:
|
||||
runs-on: macos-10.15
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: OS-X
|
||||
run: |
|
||||
make test
|
||||
# make -c lib all (need to fix. not working right now)
|
||||
|
||||
zbuff:
|
||||
runs-on: ubuntu-16.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: zbuff test
|
||||
run: |
|
||||
make -C tests test-zbuff
|
||||
|
||||
tsan:
|
||||
runs-on: ubuntu-16.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: thread sanitizer
|
||||
run: |
|
||||
sudo apt-add-repository "deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.8 main"
|
||||
sudo apt-get update
|
||||
sudo apt-get install clang-3.8
|
||||
CC=clang-3.8 make tsan-test-zstream
|
||||
CC=clang-3.8 make tsan-fuzztest
|
||||
zlib-wrapper:
|
||||
runs-on: ubuntu-16.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: zlib wrapper test
|
||||
run: |
|
||||
make gpp6install valgrindinstall
|
||||
make -C zlibWrapper test
|
||||
make -C zlibWrapper valgrindTest
|
||||
|
||||
lz4-threadpool-partial-libs:
|
||||
runs-on: ubuntu-16.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: LZ4, thread pool, and partial libs testslib wrapper test
|
||||
run: |
|
||||
make lz4install
|
||||
make -C tests test-lz4
|
||||
make check < /dev/null | tee # mess with lz4 console detection
|
||||
make clean
|
||||
make -C tests test-pool
|
||||
make clean
|
||||
bash tests/libzstd_partial_builds.sh
|
||||
@@ -1,13 +0,0 @@
|
||||
name: linux-kernel
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ dev, master, actionsTest ]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Create linux kernel library + build + test
|
||||
run: make -C contrib/linux-kernel test CFLAGS="-Werror -Wunused-const-variable -Wunused-but-set-variable"
|
||||
@@ -1,30 +0,0 @@
|
||||
name: CIFuzz
|
||||
on: [pull_request]
|
||||
jobs:
|
||||
Fuzzing:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
sanitizer: [address, undefined, memory]
|
||||
steps:
|
||||
- name: Build Fuzzers (${{ matrix.sanitizer }})
|
||||
id: build
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: 'zstd'
|
||||
dry-run: false
|
||||
sanitizer: ${{ matrix.sanitizer }}
|
||||
- name: Run Fuzzers (${{ matrix.sanitizer }})
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: 'zstd'
|
||||
fuzz-seconds: 600
|
||||
dry-run: false
|
||||
sanitizer: ${{ matrix.sanitizer }}
|
||||
- name: Upload Crash
|
||||
uses: actions/upload-artifact@v1
|
||||
if: failure() && steps.build.outcome == 'success'
|
||||
with:
|
||||
name: ${{ matrix.sanitizer }}-artifacts
|
||||
path: ./out/artifacts
|
||||
@@ -22,8 +22,6 @@ zstdmt
|
||||
|
||||
# Test artefacts
|
||||
tmp*
|
||||
*.zst
|
||||
*.zstd
|
||||
dictionary.
|
||||
dictionary
|
||||
NUL
|
||||
@@ -34,7 +32,6 @@ bin/
|
||||
.buckd/
|
||||
buck-out/
|
||||
build-*
|
||||
*.gcda
|
||||
|
||||
# Other files
|
||||
.directory
|
||||
|
||||
+22
-31
@@ -40,17 +40,12 @@ matrix:
|
||||
# DEVNULLRIGHTS : will request sudo rights to test permissions on /dev/null
|
||||
- DEVNULLRIGHTS=test make test
|
||||
|
||||
- name: gcc-6 + gcc-7 + libzstdmt compilation # ~ 6mn
|
||||
- name: gcc-6 + gcc-7 compilation # ~ 3mn
|
||||
script:
|
||||
- make gcc6install gcc7install
|
||||
- CC=gcc-6 CFLAGS=-Werror make -j all
|
||||
- make clean
|
||||
- CC=gcc-7 CFLAGS=-Werror make -j all
|
||||
- make clean
|
||||
- LDFLAGS=-Wl,--no-undefined make -C lib libzstd-mt
|
||||
- make -C tests zbufftest-dll
|
||||
# LDFLAGS=-Wl,--no-undefined : will make the linker fail if dll is underlinked
|
||||
# zbufftest-dll : test that a user program can link to multi-threaded libzstd without specifying -pthread
|
||||
|
||||
- name: gcc-8 + ASan + UBSan + Test Zstd # ~6.5mn
|
||||
script:
|
||||
@@ -76,16 +71,20 @@ matrix:
|
||||
|
||||
- name: Minimal Decompressor Macros # ~5mn
|
||||
script:
|
||||
- make clean && make -j all ZSTD_LIB_MINIFY=1 MOREFLAGS="-Werror"
|
||||
- make clean && make check ZSTD_LIB_MINIFY=1 MOREFLAGS="-Werror"
|
||||
- make clean && make -j all MOREFLAGS="-Werror -DHUF_FORCE_DECOMPRESS_X1 -DZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT"
|
||||
- make clean && make check MOREFLAGS="-Werror -DHUF_FORCE_DECOMPRESS_X1 -DZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT"
|
||||
- make clean && make -j all MOREFLAGS="-Werror -DHUF_FORCE_DECOMPRESS_X2 -DZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG"
|
||||
- make clean && make check MOREFLAGS="-Werror -DHUF_FORCE_DECOMPRESS_X2 -DZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG"
|
||||
- make clean && make -j all MOREFLAGS="-Werror -DZSTD_NO_INLINE -DZSTD_STRIP_ERROR_STRINGS"
|
||||
- make clean && make check MOREFLAGS="-Werror -DZSTD_NO_INLINE -DZSTD_STRIP_ERROR_STRINGS"
|
||||
- make clean
|
||||
- CFLAGS=-Werror make -j all MOREFLAGS="-Werror -DHUF_FORCE_DECOMPRESS_X1 -DZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT"
|
||||
- make clean
|
||||
- make -j check MOREFLAGS="-Werror -DHUF_FORCE_DECOMPRESS_X1 -DZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT"
|
||||
- make clean
|
||||
- CFLAGS=-Werror make -j all MOREFLAGS="-Werror -DHUF_FORCE_DECOMPRESS_X2 -DZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG"
|
||||
- make clean
|
||||
- make -j check MOREFLAGS="-Werror -DHUF_FORCE_DECOMPRESS_X2 -DZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG"
|
||||
- make clean
|
||||
- CFLAGS=-Werror make -j all MOREFLAGS="-Werror -DZSTD_NO_INLINE -DZSTD_STRIP_ERROR_STRINGS"
|
||||
- make clean
|
||||
- make -j check MOREFLAGS="-Werror -DZSTD_NO_INLINE -DZSTD_STRIP_ERROR_STRINGS"
|
||||
|
||||
- name: cmake build and test check # ~6mn
|
||||
- name: cmake build and test check # ~2mn
|
||||
script:
|
||||
- make cmakebuild
|
||||
|
||||
@@ -168,12 +167,6 @@ matrix:
|
||||
os: osx
|
||||
script:
|
||||
- make test
|
||||
- make -C lib all
|
||||
|
||||
- name: zbuff test
|
||||
if: branch = master
|
||||
script:
|
||||
- make -C tests test-zbuff
|
||||
|
||||
- name: Versions Compatibility Test # 11.5mn
|
||||
if: branch = master
|
||||
@@ -187,6 +180,12 @@ matrix:
|
||||
- CC=clang-3.8 make tsan-test-zstream
|
||||
- CC=clang-3.8 make tsan-fuzztest
|
||||
|
||||
- name: Qemu PPC64 + Fuzz test # ~13mn
|
||||
if: branch = master
|
||||
script:
|
||||
- make ppcinstall
|
||||
- make ppc64fuzz
|
||||
|
||||
- name: PPC64LE + Fuzz test # ~13mn
|
||||
if: branch = master
|
||||
arch: ppc64le
|
||||
@@ -194,13 +193,6 @@ matrix:
|
||||
- cat /proc/cpuinfo
|
||||
- make test
|
||||
|
||||
- name: Qemu PPC64 + Fuzz test # ~13mn, presumed Big-Endian (?)
|
||||
dist: trusty # note : PPC64 cross-compilation for Qemu tests seems broken on Xenial
|
||||
if: branch = master
|
||||
script:
|
||||
- make ppcinstall
|
||||
- make ppc64fuzz
|
||||
|
||||
# note : we already have aarch64 tests on hardware
|
||||
- name: Qemu aarch64 + Fuzz Test (on Xenial) # ~14mn
|
||||
if: branch = master
|
||||
@@ -216,19 +208,18 @@ matrix:
|
||||
- make -C zlibWrapper test
|
||||
- make -C zlibWrapper valgrindTest
|
||||
|
||||
- name: LZ4, thread pool, and partial libs tests # ~4mn
|
||||
- name: LZ4, thread pool, and partial libs tests # ~2mn
|
||||
if: branch = master
|
||||
script:
|
||||
- make lz4install
|
||||
- make -C tests test-lz4
|
||||
- make check < /dev/null | tee # mess with lz4 console detection
|
||||
- make clean
|
||||
- make -C tests test-pool
|
||||
- make clean
|
||||
- bash tests/libzstd_partial_builds.sh
|
||||
|
||||
# meson dedicated test
|
||||
- name: Xenial (Meson + clang) # ~15mn
|
||||
- name: Xenial (Meson + clang) # ~8mn
|
||||
if: branch = master
|
||||
dist: xenial
|
||||
language: cpp
|
||||
|
||||
@@ -1,88 +1,11 @@
|
||||
v1.4.7
|
||||
perf: stronger --long mode at high compression levels, by @senhuang42
|
||||
perf: stronger --patch-from at high compression levels, thanks to --long improvements
|
||||
perf: faster dictionary compression at medium compression levels, by @felixhandte
|
||||
perf: small speed & memory usage improvements for ZSTD_compress2(), by @terrelln
|
||||
perf: improved fast compression speeds with Visual Studio, by @animalize
|
||||
cli : Set nb of threads with environment variable ZSTD_NBTHREADS, by @senhuang42
|
||||
cli : accept decompressing files with *.zstd suffix
|
||||
cli : provide a condensed summary by default when processing multiple files
|
||||
cli : fix : stdin input no longer confused as user prompt
|
||||
cli : improve accuracy of several error messages
|
||||
api : new sequence ingestion API, by @senhuang42
|
||||
api : shared thread pool: control total nb of threads used by multiple compression jobs, by @marxin
|
||||
api : new ZSTD_getDictID_fromCDict(), by @LuAPi
|
||||
api : zlibWrapper only uses public API, and is compatible with dynamic library, by @terrelln
|
||||
api : fix : multithreaded compression has predictable output even in special cases (see #2327) (issue not accessible from cli)
|
||||
api : fix : dictionary compression correctly respects dictionary compression level (see #2303) (issue not accessible from cli)
|
||||
build: fix cmake script when using path with spaces, by @terrelln
|
||||
build: improved compile-time detection of aarch64/neon platforms, by @bsdimp
|
||||
build: Fix building on AIX 5.1, by @likema
|
||||
build: compile paramgrill with cmake on Windows, requested by @mirh
|
||||
doc : clarify repcode updates in format specification, by @felixhandte
|
||||
|
||||
v1.4.6
|
||||
fix : Always return dstSize_tooSmall when that is the case
|
||||
fix : Fix ZSTD_initCStream_advanced() with static allocation and no dictionary
|
||||
perf: Improve small block decompression speed by 20%+, by @terrelln
|
||||
perf: Reduce compression stack usage by 1 KB, by @terrelln
|
||||
perf: Improve decompression speed by improving ZSTD_wildcopy, by @helloguo (#2252, #2256)
|
||||
perf: Improve histogram construction, by @cyan4973 (#2253)
|
||||
cli : Add --output-dir-mirror option, by @xxie24 (#2219)
|
||||
cli : Warn when (de)compressing multiple files into a single output, by @senhuang42 (#2279)
|
||||
cli : Improved progress bar and status summary when (de)compressing multiple files, by @senhuang42 (#2283)
|
||||
cli : Call stat less often, by @felixhandte (#2262)
|
||||
cli : Allow --patch-from XXX and --filelist XXX in addition to --patch-from=XXX and --filelist=XXX, by @cyan4973 (#2250)
|
||||
cli : Allow --patch-from to compress stdin with --stream-size, by @bimbashrestha (#2206)
|
||||
api : Do not install zbuff.h, since it has long been deprecated, by @cyan4973 (#2166).
|
||||
api : Fix ZSTD_CCtx_setParameter() with ZSTD_c_compressionLevel to make 0 mean default level, by @i-do-cpp (#2291)
|
||||
api : Rename ZSTDMT_NBTHREADS_MAX to ZSTDMT_NBWORKERS_MAX, by @marxin (#2228).
|
||||
build: Install pkg-config file with CMake and MinGW, by @tonytheodore (#2183)
|
||||
build: Install DLL with CMake on Windows, by @BioDataAnalysis (#2221)
|
||||
build: Fix DLL install location with CMake, by @xantares and @bimbashrestha (#2186)
|
||||
build: Add ZSTD_NO_UNUSED_FUNCTIONS macro to hide unused functions
|
||||
build: Add ZSTD_NO_INTRINSICS macro to avoid explicit intrinsics
|
||||
build: Add STATIC_BMI2 macro for compile time detection of BMI2 on MSVC, by @Niadb (#2258)
|
||||
build: Fix -Wcomma warnings, by @cwoffenden
|
||||
build: Remove distutils requirement for meson build, by @neheb (#2197)
|
||||
build: Fix cli compilation with uclibc
|
||||
build: Fix cli compilation without st_mtime, by @ffontaine (#2246)
|
||||
build: Fix shadowing warnings in library
|
||||
build: Fix single file library compilation with Enscripten, by @yoshihitoh (#2227)
|
||||
misc: Improve single file library and include dictBuilder, by @cwoffenden
|
||||
misc: Allow compression dictionaries with missing symbols
|
||||
misc: Add freestanding translation script in contrib/freestanding_lib
|
||||
misc: Collect all of zstd's libc dependencies into zstd_deps.h
|
||||
doc : Add ZSTD_versionString() to manual, by @animalize
|
||||
doc : Fix documentation for ZSTD_CCtxParams_setParameter(), by @felixhandte (#2270)
|
||||
|
||||
v1.4.5 (May 22, 2020)
|
||||
fix : Compression ratio regression on huge files (> 3 GB) using high levels (--ultra) and multithreading, by @terrelln
|
||||
perf: Improved decompression speed: x64 : +10% (clang) / +5% (gcc); ARM : from +15% to +50%, depending on SoC, by @terrelln
|
||||
perf: Automatically downsizes ZSTD_DCtx when too large for too long (#2069, by @bimbashreshta)
|
||||
perf: Improved fast compression speed on aarch64 (#2040, ~+3%, by @caoyzh)
|
||||
perf: Small level 1 compression speed gains (depending on compiler)
|
||||
cli : New --patch-from command, create and apply patches from files, by @bimbashreshta
|
||||
cli : New --filelist= : Provide a list of files to operate upon from a file
|
||||
cli : -b -d command can now benchmark decompression on multiple files
|
||||
cli : New --no-content-size command
|
||||
cli : New --show-default-cparams information command
|
||||
api : ZDICT_finalizeDictionary() is promoted to stable (#2111)
|
||||
api : new experimental parameter ZSTD_d_stableOutBuffer (#2094)
|
||||
build: Generate a single-file libzstd library (#2065, by @cwoffenden)
|
||||
build: Relative includes no longer require -I compiler flags for zstd lib subdirs (#2103, by @felixhandte)
|
||||
build: zstd now compiles cleanly under -pedantic (#2099)
|
||||
build: zstd now compiles with make-4.3
|
||||
build: Support mingw cross-compilation from Linux, by @Ericson2314
|
||||
build: Meson multi-thread build fix on windows
|
||||
build: Some misc icc fixes backed by new ci test on travis
|
||||
v1.4.5
|
||||
perf: Improved decompression speed, +10% on clang, +5% on gcc, by @terrelln
|
||||
cli: new command : provide list of files to de/compress within a file with --filelist=FILE, by @AhmedAbdellah19
|
||||
api: low latency blocks, new experimental parameter by @ephiepark
|
||||
build: mingw cross-compilation from unix, by @Ericson2314
|
||||
misc: bitflip analyzer tool, by @felixhandte
|
||||
misc: Extend largeNbDicts benchmark to compression
|
||||
misc: Edit-distance match finder in contrib/
|
||||
doc : Improved beginner CONTRIBUTING.md docs
|
||||
doc : New issue templates for zstd
|
||||
|
||||
v1.4.4 (Nov 6, 2019)
|
||||
v1.4.4
|
||||
perf: Improved decompression speed, by > 10%, by @terrelln
|
||||
perf: Better compression speed when re-using a context, by @felixhandte
|
||||
perf: Fix compression ratio when compressing large files with small dictionary, by @senhuang42
|
||||
@@ -109,18 +32,18 @@ pack: modified pkgconfig, for better integration into openwrt, requested by @neh
|
||||
misc: Improved documentation : ZSTD_CLEVEL, DYNAMIC_BMI2, ZSTD_CDict, function deprecation, zstd format
|
||||
misc: fixed educational decoder : accept larger literals section, and removed UNALIGNED() macro
|
||||
|
||||
v1.4.3 (Aug 20, 2019)
|
||||
v1.4.3
|
||||
bug: Fix Dictionary Compression Ratio Regression by @cyan4973 (#1709)
|
||||
bug: Fix Buffer Overflow in legacy v0.3 decompression by @felixhandte (#1722)
|
||||
build: Add support for IAR C/C++ Compiler for Arm by @joseph0918 (#1705)
|
||||
|
||||
v1.4.2 (Jul 26, 2019)
|
||||
v1.4.2
|
||||
bug: Fix bug in zstd-0.5 decoder by @terrelln (#1696)
|
||||
bug: Fix seekable decompression in-memory API by @iburinoc (#1695)
|
||||
misc: Validate blocks are smaller than size limit by @vivekmg (#1685)
|
||||
misc: Restructure source files by @ephiepark (#1679)
|
||||
|
||||
v1.4.1 (Jul 20, 2019)
|
||||
v1.4.1
|
||||
bug: Fix data corruption in niche use cases by @terrelln (#1659)
|
||||
bug: Fuzz legacy modes, fix uncovered bugs by @terrelln (#1593, #1594, #1595)
|
||||
bug: Fix out of bounds read by @terrelln (#1590)
|
||||
@@ -150,7 +73,7 @@ build: Visual Studio: fix linking by @absotively (#1639)
|
||||
build: Fix MinGW-W64 build by @myzhang1029 (#1600)
|
||||
misc: Expand decodecorpus coverage by @ephiepark (#1664)
|
||||
|
||||
v1.4.0 (Apr 17, 2019)
|
||||
v1.4.0
|
||||
perf: Improve level 1 compression speed in most scenarios by 6% by @gbtucker and @terrelln
|
||||
api: Move the advanced API, including all functions in the staging section, to the stable section
|
||||
api: Make ZSTD_e_flush and ZSTD_e_end block for maximum forward progress
|
||||
@@ -187,7 +110,7 @@ misc: Optimize dictionary memory usage in corner cases
|
||||
misc: Improve the dictionary builder on small or homogeneous data
|
||||
misc: Fix spelling across the repo by @jsoref
|
||||
|
||||
v1.3.8 (Dec 28, 2018)
|
||||
v1.3.8
|
||||
perf: better decompression speed on large files (+7%) and cold dictionaries (+15%)
|
||||
perf: slightly better compression ratio at high compression modes
|
||||
api : finalized advanced API, last stage before "stable" status
|
||||
@@ -209,14 +132,14 @@ doc : clarified zstd_compression_format.md, by @ulikunitz
|
||||
misc: fixed zstdgrep, returns 1 on failure, by @lzutao
|
||||
misc: NEWS renamed as CHANGELOG, in accordance with fboss
|
||||
|
||||
v1.3.7 (Oct 20, 2018)
|
||||
v1.3.7
|
||||
perf: slightly better decompression speed on clang (depending on hardware target)
|
||||
fix : performance of dictionary compression for small input < 4 KB at levels 9 and 10
|
||||
build: no longer build backtrace by default in release mode; restrict further automatic mode
|
||||
build: control backtrace support through build macro BACKTRACE
|
||||
misc: added man pages for zstdless and zstdgrep, by @samrussell
|
||||
|
||||
v1.3.6 (Oct 6, 2018)
|
||||
v1.3.6
|
||||
perf: much faster dictionary builder, by @jenniferliu
|
||||
perf: faster dictionary compression on small data when using multiple contexts, by @felixhandte
|
||||
perf: faster dictionary decompression when using a very large number of dictionaries simultaneously
|
||||
@@ -230,7 +153,7 @@ build: Read Legacy format is limited to v0.5+ by default. Can be changed at comp
|
||||
doc : zstd_compression_format.md updated to match wording in IETF RFC 8478
|
||||
misc: tests/paramgrill, a parameter optimizer, by @GeorgeLu97
|
||||
|
||||
v1.3.5 (Jun 29, 2018)
|
||||
v1.3.5
|
||||
perf: much faster dictionary compression, by @felixhandte
|
||||
perf: small quality improvement for dictionary generation, by @terrelln
|
||||
perf: slightly improved high compression levels (notably level 19)
|
||||
@@ -245,7 +168,7 @@ build: make and make all are compatible with -j
|
||||
doc : clarify zstd_compression_format.md, updated for IETF RFC process
|
||||
misc: pzstd compatible with reproducible compilation, by @lamby
|
||||
|
||||
v1.3.4 (Mar 27, 2018)
|
||||
v1.3.4
|
||||
perf: faster speed (especially decoding speed) on recent cpus (haswell+)
|
||||
perf: much better performance associating --long with multi-threading, by @terrelln
|
||||
perf: better compression at levels 13-15
|
||||
@@ -263,7 +186,7 @@ build: VS2017 scripts, by @HaydnTrigg
|
||||
misc: all /contrib projects fixed
|
||||
misc: added /contrib/docker script by @gyscos
|
||||
|
||||
v1.3.3 (Dec 21, 2017)
|
||||
v1.3.3
|
||||
perf: faster zstd_opt strategy (levels 16-19)
|
||||
fix : bug #944 : multithreading with shared ditionary and large data, reported by @gsliepen
|
||||
cli : fix : content size written in header by default
|
||||
@@ -275,7 +198,7 @@ api : change : when setting `pledgedSrcSize`, use `ZSTD_CONTENTSIZE_UNKNOWN` mac
|
||||
build: fix : compilation under rhel6 and centos6, reported by @pixelb
|
||||
build: added `check` target
|
||||
|
||||
v1.3.2 (Oct 10, 2017)
|
||||
v1.3.2
|
||||
new : long range mode, using --long command, by Stella Lau (@stellamplau)
|
||||
new : ability to generate and decode magicless frames (#591)
|
||||
changed : maximum nb of threads reduced to 200, to avoid address space exhaustion in 32-bits mode
|
||||
@@ -298,7 +221,7 @@ example : added streaming_memory_usage
|
||||
license : changed /examples license to BSD + GPLv2
|
||||
license : fix a few header files to reflect new license (#825)
|
||||
|
||||
v1.3.1 (Aug 21, 2017)
|
||||
v1.3.1
|
||||
New license : BSD + GPLv2
|
||||
perf: substantially decreased memory usage in Multi-threading mode, thanks to reports by Tino Reichardt (@mcmilk)
|
||||
perf: Multi-threading supports up to 256 threads. Cap at 256 when more are requested (#760)
|
||||
@@ -313,7 +236,7 @@ new : contrib/adaptive-compression, I/O driven compression strength, by Paul Cru
|
||||
new : contrib/long_distance_matching, statistics by Stella Lau (@stellamplau)
|
||||
updated : contrib/linux-kernel, by Nick Terrell (@terrelln)
|
||||
|
||||
v1.3.0 (Jul 6, 2017)
|
||||
v1.3.0
|
||||
cli : new : `--list` command, by Paul Cruz
|
||||
cli : changed : xz/lzma support enabled by default
|
||||
cli : changed : `-t *` continue processing list after a decompression error
|
||||
@@ -328,7 +251,7 @@ tools : decodecorpus can generate random dictionary-compressed samples, by Paul
|
||||
new : contrib/seekable_format, demo and API, by Sean Purcell
|
||||
changed : contrib/linux-kernel, updated version and license, by Nick Terrell
|
||||
|
||||
v1.2.0 (May 5, 2017)
|
||||
v1.2.0
|
||||
cli : changed : Multithreading enabled by default (use target zstd-nomt or HAVE_THREAD=0 to disable)
|
||||
cli : new : command -T0 means "detect and use nb of cores", by Sean Purcell
|
||||
cli : new : zstdmt symlink hardwired to `zstd -T0`
|
||||
@@ -350,7 +273,7 @@ build: enabled Multi-threading support for *BSD, by Baptiste Daroussin
|
||||
tools: updated Paramgrill. Command -O# provides best parameters for sample and speed target.
|
||||
new : contrib/linux-kernel version, by Nick Terrell
|
||||
|
||||
v1.1.4 (Mar 18, 2017)
|
||||
v1.1.4
|
||||
cli : new : can compress in *.gz format, using --format=gzip command, by Przemyslaw Skibinski
|
||||
cli : new : advanced benchmark command --priority=rt
|
||||
cli : fix : write on sparse-enabled file systems in 32-bits mode, by @ds77
|
||||
@@ -366,7 +289,7 @@ build : improved cmake script, by @Majlen
|
||||
build : added -Wformat-security flag, as recommended by Padraig Brady
|
||||
doc : new : educational decoder, by Sean Purcell
|
||||
|
||||
v1.1.3 (Feb 7, 2017)
|
||||
v1.1.3
|
||||
cli : zstd can decompress .gz files (can be disabled with `make zstd-nogz` or `make HAVE_ZLIB=0`)
|
||||
cli : new : experimental target `make zstdmt`, with multi-threading support
|
||||
cli : new : improved dictionary builder "cover" (experimental), by Nick Terrell, based on prior work by Giuseppe Ottaviano.
|
||||
@@ -382,7 +305,7 @@ API : fix : all symbols properly exposed in libzstd, by Nick Terrell
|
||||
build : support for Solaris target, by Przemyslaw Skibinski
|
||||
doc : clarified specification, by Sean Purcell
|
||||
|
||||
v1.1.2 (Dec 15, 2016)
|
||||
v1.1.2
|
||||
API : streaming : decompression : changed : automatic implicit reset when chain-decoding new frames without init
|
||||
API : experimental : added : dictID retrieval functions, and ZSTD_initCStream_srcSize()
|
||||
API : zbuff : changed : prototypes now generate deprecation warnings
|
||||
@@ -399,7 +322,7 @@ zlib_wrapper : added support for gz* functions, by Przemyslaw Skibinski
|
||||
install : better compatibility with FreeBSD, by Dimitry Andric
|
||||
source tree : changed : zbuff source files moved to lib/deprecated
|
||||
|
||||
v1.1.1 (Nov 2, 2016)
|
||||
v1.1.1
|
||||
New : command -M#, --memory=, --memlimit=, --memlimit-decompress= to limit allowed memory consumption
|
||||
New : doc/zstd_manual.html, by Przemyslaw Skibinski
|
||||
Improved : slightly better compression ratio at --ultra levels (>= 20)
|
||||
@@ -410,7 +333,7 @@ Changed : zstd_errors.h is now installed within /include (and replaces errors_pu
|
||||
Updated man page
|
||||
Fixed : zstd-small, zstd-compress and zstd-decompress compilation targets
|
||||
|
||||
v1.1.0 (Sep 28, 2016)
|
||||
v1.1.0
|
||||
New : contrib/pzstd, parallel version of zstd, by Nick Terrell
|
||||
added : NetBSD install target (#338)
|
||||
Improved : speed for batches of small files
|
||||
@@ -424,7 +347,7 @@ Fixed : compatibility with OpenBSD, reported by Juan Francisco Cantero Hurtado (
|
||||
Fixed : compatibility with Hurd, by Przemyslaw Skibinski (#365)
|
||||
Fixed : zstd-pgo, reported by octoploid (#329)
|
||||
|
||||
v1.0.0 (Sep 1, 2016)
|
||||
v1.0.0
|
||||
Change Licensing, all project is now BSD, Copyright Facebook
|
||||
Small decompression speed improvement
|
||||
API : Streaming API supports legacy format
|
||||
@@ -433,7 +356,7 @@ CLI supports legacy formats v0.4+
|
||||
Fixed : compression fails on certain huge files, reported by Jesse McGrew
|
||||
Enhanced documentation, by Przemyslaw Skibinski
|
||||
|
||||
v0.8.1 (Aug 18, 2016)
|
||||
v0.8.1
|
||||
New streaming API
|
||||
Changed : --ultra now enables levels beyond 19
|
||||
Changed : -i# now selects benchmark time in second
|
||||
@@ -442,7 +365,7 @@ Fixed : speed regression on specific patterns (#272)
|
||||
Fixed : support for Z_SYNC_FLUSH, by Dmitry Krot (#291)
|
||||
Fixed : ICC compilation, by Przemyslaw Skibinski
|
||||
|
||||
v0.8.0 (Aug 2, 2016)
|
||||
v0.8.0
|
||||
Improved : better speed on clang and gcc -O2, thanks to Eric Biggers
|
||||
New : Build on FreeBSD and DragonFly, thanks to JrMarino
|
||||
Changed : modified API : ZSTD_compressEnd()
|
||||
@@ -455,17 +378,17 @@ Modified : minor compression level adaptations
|
||||
Updated : compression format specification to v0.2.0
|
||||
changed : zstd.h moved to /lib directory
|
||||
|
||||
v0.7.5 (Aug 1, 2016)
|
||||
v0.7.5
|
||||
Transition version, supporting decoding of v0.8.x
|
||||
|
||||
v0.7.4 (Jul 17, 2016)
|
||||
v0.7.4
|
||||
Added : homebrew for Mac, by Daniel Cade
|
||||
Added : more examples
|
||||
Fixed : segfault when using small dictionaries, reported by Felix Handte
|
||||
Modified : default compression level for CLI is now 3
|
||||
Updated : specification, to v0.1.1
|
||||
|
||||
v0.7.3 (Jul 9, 2016)
|
||||
v0.7.3
|
||||
New : compression format specification
|
||||
New : `--` separator, stating that all following arguments are file names. Suggested by Chip Turner.
|
||||
New : `ZSTD_getDecompressedSize()`
|
||||
@@ -477,18 +400,18 @@ fixed : multi-blocks decoding with intermediate uncompressed blocks, reported by
|
||||
modified : removed "mem.h" and "error_public.h" dependencies from "zstd.h" (experimental section)
|
||||
modified : legacy functions no longer need magic number
|
||||
|
||||
v0.7.2 (Jul 4, 2016)
|
||||
v0.7.2
|
||||
fixed : ZSTD_decompressBlock() using multiple consecutive blocks. Reported by Greg Slazinski.
|
||||
fixed : potential segfault on very large files (many gigabytes). Reported by Chip Turner.
|
||||
fixed : CLI displays system error message when destination file cannot be created (#231). Reported by Chip Turner.
|
||||
|
||||
v0.7.1 (Jun 23, 2016)
|
||||
v0.7.1
|
||||
fixed : ZBUFF_compressEnd() called multiple times with too small `dst` buffer, reported by Christophe Chevalier
|
||||
fixed : dictBuilder fails if first sample is too small, reported by Руслан Ковалёв
|
||||
fixed : corruption issue, reported by cj
|
||||
modified : checksum enabled by default in command line mode
|
||||
|
||||
v0.7.0 (Jun 17, 2016)
|
||||
v0.7.0
|
||||
New : Support for directory compression, using `-r`, thanks to Przemyslaw Skibinski
|
||||
New : Command `--rm`, to remove source file after successful de/compression
|
||||
New : Visual build scripts, by Christophe Chevalier
|
||||
@@ -501,7 +424,7 @@ API : support for custom malloc/free functions
|
||||
New : controllable Dictionary ID
|
||||
New : Support for skippable frames
|
||||
|
||||
v0.6.1 (May 13, 2016)
|
||||
v0.6.1
|
||||
New : zlib wrapper API, thanks to Przemyslaw Skibinski
|
||||
New : Ability to compile compressor / decompressor separately
|
||||
Changed : new lib directory structure
|
||||
@@ -511,103 +434,103 @@ Fixed : null-string roundtrip (#176)
|
||||
New : benchmark mode can select directory as input
|
||||
Experimental : midipix support, VMS support
|
||||
|
||||
v0.6.0 (Apr 13, 2016)
|
||||
v0.6.0
|
||||
Stronger high compression modes, thanks to Przemyslaw Skibinski
|
||||
API : ZSTD_getFrameParams() provides size of decompressed content
|
||||
New : highest compression modes require `--ultra` command to fully unleash their capacity
|
||||
Fixed : zstd cli return error code > 0 and removes dst file artifact when decompression fails, thanks to Chip Turner
|
||||
|
||||
v0.5.1 (Feb 18, 2016)
|
||||
v0.5.1
|
||||
New : Optimal parsing => Very high compression modes, thanks to Przemyslaw Skibinski
|
||||
Changed : Dictionary builder integrated into libzstd and zstd cli
|
||||
Changed (!) : zstd cli now uses "multiple input files" as default mode. See `zstd -h`.
|
||||
Fix : high compression modes for big-endian platforms
|
||||
New : zstd cli : `-t` | `--test` command
|
||||
|
||||
v0.5.0 (Feb 5, 2016)
|
||||
v0.5.0
|
||||
New : dictionary builder utility
|
||||
Changed : streaming & dictionary API
|
||||
Improved : better compression of small data
|
||||
|
||||
v0.4.7 (Jan 22, 2016)
|
||||
v0.4.7
|
||||
Improved : small compression speed improvement in HC mode
|
||||
Changed : `zstd_decompress.c` has ZSTD_LEGACY_SUPPORT to 0 by default
|
||||
fix : bt search bug
|
||||
|
||||
v0.4.6 (Jan 13, 2016)
|
||||
v0.4.6
|
||||
fix : fast compression mode on Windows
|
||||
New : cmake configuration file, thanks to Artyom Dymchenko
|
||||
Improved : high compression mode on repetitive data
|
||||
New : block-level API
|
||||
New : ZSTD_duplicateCCtx()
|
||||
|
||||
v0.4.5 (Dec 18, 2015)
|
||||
v0.4.5
|
||||
new : -m/--multiple : compress/decompress multiple files
|
||||
|
||||
v0.4.4 (Dec 14, 2015)
|
||||
v0.4.4
|
||||
Fixed : high compression modes for Windows 32 bits
|
||||
new : external dictionary API extended to buffered mode and accessible through command line
|
||||
new : windows DLL project, thanks to Christophe Chevalier
|
||||
|
||||
v0.4.3 (Dec 7, 2015)
|
||||
v0.4.3 :
|
||||
new : external dictionary API
|
||||
new : zstd-frugal
|
||||
|
||||
v0.4.2 (Dec 2, 2015)
|
||||
v0.4.2 :
|
||||
Generic minor improvements for small blocks
|
||||
Fixed : big-endian compatibility, by Peter Harris (#85)
|
||||
|
||||
v0.4.1 (Dec 1, 2015)
|
||||
v0.4.1
|
||||
Fixed : ZSTD_LEGACY_SUPPORT=0 build mode (reported by Luben)
|
||||
removed `zstd.c`
|
||||
|
||||
v0.4.0 (Nov 29, 2015)
|
||||
v0.4.0
|
||||
Command line utility compatible with high compression levels
|
||||
Removed zstdhc => merged into zstd
|
||||
Added : ZBUFF API (see zstd_buffered.h)
|
||||
Rolling buffer support
|
||||
|
||||
v0.3.6 (Nov 10, 2015)
|
||||
v0.3.6
|
||||
small blocks params
|
||||
|
||||
v0.3.5 (Nov 9, 2015)
|
||||
v0.3.5
|
||||
minor generic compression improvements
|
||||
|
||||
v0.3.4 (Nov 6, 2015)
|
||||
v0.3.4
|
||||
Faster fast cLevels
|
||||
|
||||
v0.3.3 (Nov 5, 2015)
|
||||
v0.3.3
|
||||
Small compression ratio improvement
|
||||
|
||||
v0.3.2 (Nov 2, 2015)
|
||||
v0.3.2
|
||||
Fixed Visual Studio
|
||||
|
||||
v0.3.1 (Nov 2, 2015)
|
||||
v0.3.1 :
|
||||
Small compression ratio improvement
|
||||
|
||||
v0.3 (Oct 30, 2015)
|
||||
v0.3
|
||||
HC mode : compression levels 2-26
|
||||
|
||||
v0.2.2 (Oct 28, 2015)
|
||||
v0.2.2
|
||||
Fix : Visual Studio 2013 & 2015 release compilation, by Christophe Chevalier
|
||||
|
||||
v0.2.1 (Oct 24, 2015)
|
||||
v0.2.1
|
||||
Fix : Read errors, advanced fuzzer tests, by Hanno Böck
|
||||
|
||||
v0.2.0 (Oct 22, 2015)
|
||||
v0.2.0
|
||||
**Breaking format change**
|
||||
Faster decompression speed
|
||||
Can still decode v0.1 format
|
||||
|
||||
v0.1.3 (Oct 15, 2015)
|
||||
v0.1.3
|
||||
fix uninitialization warning, reported by Evan Nemerson
|
||||
|
||||
v0.1.2 (Sep 11, 2015)
|
||||
v0.1.2
|
||||
frame concatenation support
|
||||
|
||||
v0.1.1 (Aug 27, 2015)
|
||||
v0.1.1
|
||||
fix compression bug
|
||||
detects write-flush errors
|
||||
|
||||
v0.1.0 (Aug 25, 2015)
|
||||
v0.1.0
|
||||
first release
|
||||
|
||||
+2
-366
@@ -5,7 +5,7 @@ possible.
|
||||
## Our Development Process
|
||||
New versions are being developed in the "dev" branch,
|
||||
or in their own feature branch.
|
||||
When they are deemed ready for a release, they are merged into "release".
|
||||
When they are deemed ready for a release, they are merged into "master".
|
||||
|
||||
As a consequences, all contributions must stage first through "dev"
|
||||
or their own feature branch.
|
||||
@@ -26,370 +26,6 @@ to do this once to work on any of Facebook's open source projects.
|
||||
|
||||
Complete your CLA here: <https://code.facebook.com/cla>
|
||||
|
||||
## Workflow
|
||||
Zstd uses a branch-based workflow for making changes to the codebase. Typically, zstd
|
||||
will use a new branch per sizable topic. For smaller changes, it is okay to lump multiple
|
||||
related changes into a branch.
|
||||
|
||||
Our contribution process works in three main stages:
|
||||
1. Local development
|
||||
* Update:
|
||||
* Checkout your fork of zstd if you have not already
|
||||
```
|
||||
git checkout https://github.com/<username>/zstd
|
||||
cd zstd
|
||||
```
|
||||
* Update your local dev branch
|
||||
```
|
||||
git pull https://github.com/facebook/zstd dev
|
||||
git push origin dev
|
||||
```
|
||||
* Topic and development:
|
||||
* Make a new branch on your fork about the topic you're developing for
|
||||
```
|
||||
# branch names should be consise but sufficiently informative
|
||||
git checkout -b <branch-name>
|
||||
git push origin <branch-name>
|
||||
```
|
||||
* Make commits and push
|
||||
```
|
||||
# make some changes =
|
||||
git add -u && git commit -m <message>
|
||||
git push origin <branch-name>
|
||||
```
|
||||
* Note: run local tests to ensure that your changes didn't break existing functionality
|
||||
* Quick check
|
||||
```
|
||||
make shortest
|
||||
```
|
||||
* Longer check
|
||||
```
|
||||
make test
|
||||
```
|
||||
2. Code Review and CI tests
|
||||
* Ensure CI tests pass:
|
||||
* Before sharing anything to the community, make sure that all CI tests pass on your local fork.
|
||||
See our section on setting up your CI environment for more information on how to do this.
|
||||
* Ensure that static analysis passes on your development machine. See the Static Analysis section
|
||||
below to see how to do this.
|
||||
* Create a pull request:
|
||||
* When you are ready to share you changes to the community, create a pull request from your branch
|
||||
to facebook:dev. You can do this very easily by clicking 'Create Pull Request' on your fork's home
|
||||
page.
|
||||
* From there, select the branch where you made changes as your source branch and facebook:dev
|
||||
as the destination.
|
||||
* Examine the diff presented between the two branches to make sure there is nothing unexpected.
|
||||
* Write a good pull request description:
|
||||
* While there is no strict template that our contributors follow, we would like them to
|
||||
sufficiently summarize and motivate the changes they are proposing. We recommend all pull requests,
|
||||
at least indirectly, address the following points.
|
||||
* Is this pull request important and why?
|
||||
* Is it addressing an issue? If so, what issue? (provide links for convenience please)
|
||||
* Is this a new feature? If so, why is it useful and/or necessary?
|
||||
* Are there background references and documents that reviewers should be aware of to properly assess this change?
|
||||
* Note: make sure to point out any design and architectural decisions that you made and the rationale behind them.
|
||||
* Note: if you have been working with a specific user and would like them to review your work, make sure you mention them using (@<username>)
|
||||
* Submit the pull request and iterate with feedback.
|
||||
3. Merge and Release
|
||||
* Getting approval:
|
||||
* You will have to iterate on your changes with feedback from other collaborators to reach a point
|
||||
where your pull request can be safely merged.
|
||||
* To avoid too many comments on style and convention, make sure that you have a
|
||||
look at our style section below before creating a pull request.
|
||||
* Eventually, someone from the zstd team will approve your pull request and not long after merge it into
|
||||
the dev branch.
|
||||
* Housekeeping:
|
||||
* Most PRs are linked with one or more Github issues. If this is the case for your PR, make sure
|
||||
the corresponding issue is mentioned. If your change 'fixes' or completely addresses the
|
||||
issue at hand, then please indicate this by requesting that an issue be closed by commenting.
|
||||
* Just because your changes have been merged does not mean the topic or larger issue is complete. Remember
|
||||
that the change must make it to an official zstd release for it to be meaningful. We recommend
|
||||
that contributers track the activity on their pull request and corresponding issue(s) page(s) until
|
||||
their change makes it to the next release of zstd. Users will often discover bugs in your code or
|
||||
suggest ways to refine and improve your initial changes even after the pull request is merged.
|
||||
|
||||
## Static Analysis
|
||||
Static analysis is a process for examining the correctness or validity of a program without actually
|
||||
executing it. It usually helps us find many simple bugs. Zstd uses clang's `scan-build` tool for
|
||||
static analysis. You can install it by following the instructions for your OS on https://clang-analyzer.llvm.org/scan-build.
|
||||
|
||||
Once installed, you can ensure that our static analysis tests pass on your local development machine
|
||||
by running:
|
||||
```
|
||||
make staticAnalyze
|
||||
```
|
||||
|
||||
In general, you can use `scan-build` to static analyze any build script. For example, to static analyze
|
||||
just `contrib/largeNbDicts` and nothing else, you can run:
|
||||
|
||||
```
|
||||
scan-build make -C contrib/largeNbDicts largeNbDicts
|
||||
```
|
||||
|
||||
### Pitfalls of static analysis
|
||||
`scan-build` is part of our regular CI suite. Other static analyzers are not.
|
||||
|
||||
It can be useful to look at additional static analyzers once in a while (and we do), but it's not a good idea to multiply the nb of analyzers run continuously at each commit and PR. The reasons are :
|
||||
|
||||
- Static analyzers are full of false positive. The signal to noise ratio is actually pretty low.
|
||||
- A good CI policy is "zero-warning tolerance". That means that all issues must be solved, including false positives. This quickly becomes a tedious workload.
|
||||
- Multiple static analyzers will feature multiple kind of false positives, sometimes applying to the same code but in different ways leading to :
|
||||
+ torteous code, trying to please multiple constraints, hurting readability and therefore maintenance. Sometimes, such complexity introduce other more subtle bugs, that are just out of scope of the analyzers.
|
||||
+ sometimes, these constraints are mutually exclusive : if one try to solve one, the other static analyzer will complain, they can't be both happy at the same time.
|
||||
- As if that was not enough, the list of false positives change with each version. It's hard enough to follow one static analyzer, but multiple ones with their own update agenda, this quickly becomes a massive velocity reducer.
|
||||
|
||||
This is different from running a static analyzer once in a while, looking at the output, and __cherry picking__ a few warnings that seem helpful, either because they detected a genuine risk of bug, or because it helps expressing the code in a way which is more readable or more difficult to misuse. These kind of reports can be useful, and are accepted.
|
||||
|
||||
## Performance
|
||||
Performance is extremely important for zstd and we only merge pull requests whose performance
|
||||
landscape and corresponding trade-offs have been adequately analyzed, reproduced, and presented.
|
||||
This high bar for performance means that every PR which has the potential to
|
||||
impact performance takes a very long time for us to properly review. That being said, we
|
||||
always welcome contributions to improve performance (or worsen performance for the trade-off of
|
||||
something else). Please keep the following in mind before submitting a performance related PR:
|
||||
|
||||
1. Zstd isn't as old as gzip but it has been around for time now and its evolution is
|
||||
very well documented via past Github issues and pull requests. It may be the case that your
|
||||
particular performance optimization has already been considered in the past. Please take some
|
||||
time to search through old issues and pull requests using keywords specific to your
|
||||
would-be PR. Of course, just because a topic has already been discussed (and perhaps rejected
|
||||
on some grounds) in the past, doesn't mean it isn't worth bringing up again. But even in that case,
|
||||
it will be helpful for you to have context from that topic's history before contributing.
|
||||
2. The distinction between noise and actual performance gains can unfortunately be very subtle
|
||||
especially when microbenchmarking extremely small wins or losses. The only remedy to getting
|
||||
something subtle merged is extensive benchmarking. You will be doing us a great favor if you
|
||||
take the time to run extensive, long-duration, and potentially cross-(os, platform, process, etc)
|
||||
benchmarks on your end before submitting a PR. Of course, you will not be able to benchmark
|
||||
your changes on every single processor and os out there (and neither will we) but do that best
|
||||
you can:) We've adding some things to think about when benchmarking below in the Benchmarking
|
||||
Performance section which might be helpful for you.
|
||||
3. Optimizing performance for a certain OS, processor vendor, compiler, or network system is a perfectly
|
||||
legitimate thing to do as long as it does not harm the overall performance health of Zstd.
|
||||
This is a hard balance to strike but please keep in mind other aspects of Zstd when
|
||||
submitting changes that are clang-specific, windows-specific, etc.
|
||||
|
||||
## Benchmarking Performance
|
||||
Performance microbenchmarking is a tricky subject but also essential for Zstd. We value empirical
|
||||
testing over theoretical speculation. This guide it not perfect but for most scenarios, it
|
||||
is a good place to start.
|
||||
|
||||
### Stability
|
||||
Unfortunately, the most important aspect in being able to benchmark reliably is to have a stable
|
||||
benchmarking machine. A virtual machine, a machine with shared resources, or your laptop
|
||||
will typically not be stable enough to obtain reliable benchmark results. If you can get your
|
||||
hands on a desktop, this is usually a better scenario.
|
||||
|
||||
Of course, benchmarking can be done on non-hyper-stable machines as well. You will just have to
|
||||
do a little more work to ensure that you are in fact measuring the changes you've made not and
|
||||
noise. Here are some things you can do to make your benchmarks more stable:
|
||||
|
||||
1. The most simple thing you can do to drastically improve the stability of your benchmark is
|
||||
to run it multiple times and then aggregate the results of those runs. As a general rule of
|
||||
thumb, the smaller the change you are trying to measure, the more samples of benchmark runs
|
||||
you will have to aggregate over to get reliable results. Here are some additional things to keep in
|
||||
mind when running multiple trials:
|
||||
* How you aggregate your samples are important. You might be tempted to use the mean of your
|
||||
results. While this is certainly going to be a more stable number than a raw single sample
|
||||
benchmark number, you might have more luck by taking the median. The mean is not robust to
|
||||
outliers whereas the median is. Better still, you could simply take the fastest speed your
|
||||
benchmark achieved on each run since that is likely the fastest your process will be
|
||||
capable of running your code. In our experience, this (aggregating by just taking the sample
|
||||
with the fastest running time) has been the most stable approach.
|
||||
* The more samples you have, the more stable your benchmarks should be. You can verify
|
||||
your improved stability by looking at the size of your confidence intervals as you
|
||||
increase your sample count. These should get smaller and smaller. Eventually hopefully
|
||||
smaller than the performance win you are expecting.
|
||||
* Most processors will take some time to get `hot` when running anything. The observations
|
||||
you collect during that time period will very different from the true performance number. Having
|
||||
a very large number of sample will help alleviate this problem slightly but you can also
|
||||
address is directly by simply not including the first `n` iterations of your benchmark in
|
||||
your aggregations. You can determine `n` by simply looking at the results from each iteration
|
||||
and then hand picking a good threshold after which the variance in results seems to stabilize.
|
||||
2. You cannot really get reliable benchmarks if your host machine is simultaneously running
|
||||
another cpu/memory-intensive application in the background. If you are running benchmarks on your
|
||||
personal laptop for instance, you should close all applications (including your code editor and
|
||||
browser) before running your benchmarks. You might also have invisible background applications
|
||||
running. You can see what these are by looking at either Activity Monitor on Mac or Task Manager
|
||||
on Windows. You will get more stable benchmark results of you end those processes as well.
|
||||
* If you have multiple cores, you can even run your benchmark on a reserved core to prevent
|
||||
pollution from other OS and user processes. There are a number of ways to do this depending
|
||||
on your OS:
|
||||
* On linux boxes, you have use https://github.com/lpechacek/cpuset.
|
||||
* On Windows, you can "Set Processor Affinity" using https://www.thewindowsclub.com/processor-affinity-windows
|
||||
* On Mac, you can try to use their dedicated affinity API https://developer.apple.com/library/archive/releasenotes/Performance/RN-AffinityAPI/#//apple_ref/doc/uid/TP40006635-CH1-DontLinkElementID_2
|
||||
3. To benchmark, you will likely end up writing a separate c/c++ program that will link libzstd.
|
||||
Dynamically linking your library will introduce some added variation (not a large amount but
|
||||
definitely some). Statically linking libzstd will be more stable. Static libraries should
|
||||
be enabled by default when building zstd.
|
||||
4. Use a profiler with a good high resolution timer. See the section below on profiling for
|
||||
details on this.
|
||||
5. Disable frequency scaling, turbo boost and address space randomization (this will vary by OS)
|
||||
6. Try to avoid storage. On some systems you can use tmpfs. Putting the program, inputs and outputs on
|
||||
tmpfs avoids touching a real storage system, which can have a pretty big variability.
|
||||
|
||||
Also check our LLVM's guide on benchmarking here: https://llvm.org/docs/Benchmarking.html
|
||||
|
||||
### Zstd benchmark
|
||||
The fastest signal you can get regarding your performance changes is via the in-build zstd cli
|
||||
bench option. You can run Zstd as you typically would for your scenario using some set of options
|
||||
and then additionally also specify the `-b#` option. Doing this will run our benchmarking pipeline
|
||||
for that options you have just provided. If you want to look at the internals of how this
|
||||
benchmarking script works, you can check out programs/benchzstd.c
|
||||
|
||||
For example: say you have made a change that you believe improves the speed of zstd level 1. The
|
||||
very first thing you should use to asses whether you actually achieved any sort of improvement
|
||||
is `zstd -b`. You might try to do something like this. Note: you can use the `-i` option to
|
||||
specify a running time for your benchmark in seconds (default is 3 seconds).
|
||||
Usually, the longer the running time, the more stable your results will be.
|
||||
|
||||
```
|
||||
$ git checkout <commit-before-your-change>
|
||||
$ make && cp zstd zstd-old
|
||||
$ git checkout <commit-after-your-change>
|
||||
$ make && cp zstd zstd-new
|
||||
$ zstd-old -i5 -b1 <your-test-data>
|
||||
1<your-test-data> : 8990 -> 3992 (2.252), 302.6 MB/s , 626.4 MB/s
|
||||
$ zstd-new -i5 -b1 <your-test-data>
|
||||
1<your-test-data> : 8990 -> 3992 (2.252), 302.8 MB/s , 628.4 MB/s
|
||||
```
|
||||
|
||||
Unless your performance win is large enough to be visible despite the intrinsic noise
|
||||
on your computer, benchzstd alone will likely not be enough to validate the impact of your
|
||||
changes. For example, the results of the example above indicate that effectively nothing
|
||||
changed but there could be a small <3% improvement that the noise on the host machine
|
||||
obscured. So unless you see a large performance win (10-15% consistently) using just
|
||||
this method of evaluation will not be sufficient.
|
||||
|
||||
### Profiling
|
||||
There are a number of great profilers out there. We're going to briefly mention how you can
|
||||
profile your code using `instruments` on mac, `perf` on linux and `visual studio profiler`
|
||||
on windows.
|
||||
|
||||
Say you have an idea for a change that you think will provide some good performance gains
|
||||
for level 1 compression on Zstd. Typically this means, you have identified a section of
|
||||
code that you think can be made to run faster.
|
||||
|
||||
The first thing you will want to do is make sure that the piece of code is actually taking up
|
||||
a notable amount of time to run. It is usually not worth optimzing something which accounts for less than
|
||||
0.0001% of the total running time. Luckily, there are tools to help with this.
|
||||
Profilers will let you see how much time your code spends inside a particular function.
|
||||
If your target code snippit is only part of a function, it might be worth trying to
|
||||
isolate that snippit by moving it to its own function (this is usually not necessary but
|
||||
might be).
|
||||
|
||||
Most profilers (including the profilers dicusssed below) will generate a call graph of
|
||||
functions for you. Your goal will be to find your function of interest in this call grapch
|
||||
and then inspect the time spent inside of it. You might also want to to look at the
|
||||
annotated assembly which most profilers will provide you with.
|
||||
|
||||
#### Instruments
|
||||
We will once again consider the scenario where you think you've identified a piece of code
|
||||
whose performance can be improved upon. Follow these steps to profile your code using
|
||||
Instruments.
|
||||
|
||||
1. Open Instruments
|
||||
2. Select `Time Profiler` from the list of standard templates
|
||||
3. Close all other applications except for your instruments window and your terminal
|
||||
4. Run your benchmarking script from your terminal window
|
||||
* You will want a benchmark that runs for at least a few seconds (5 seconds will
|
||||
usually be long enough). This way the profiler will have something to work with
|
||||
and you will have ample time to attach your profiler to this process:)
|
||||
* I will just use benchzstd as my bencharmking script for this example:
|
||||
```
|
||||
$ zstd -b1 -i5 <my-data> # this will run for 5 seconds
|
||||
```
|
||||
5. Once you run your benchmarking script, switch back over to instruments and attach your
|
||||
process to the time profiler. You can do this by:
|
||||
* Clicking on the `All Processes` drop down in the top left of the toolbar.
|
||||
* Selecting your process from the dropdown. In my case, it is just going to be labled
|
||||
`zstd`
|
||||
* Hitting the bright red record circle button on the top left of the toolbar
|
||||
6. You profiler will now start collecting metrics from your bencharking script. Once
|
||||
you think you have collected enough samples (usually this is the case after 3 seconds of
|
||||
recording), stop your profiler.
|
||||
7. Make sure that in toolbar of the bottom window, `profile` is selected.
|
||||
8. You should be able to see your call graph.
|
||||
* If you don't see the call graph or an incomplete call graph, make sure you have compiled
|
||||
zstd and your benchmarking scripg using debug flags. On mac and linux, this just means
|
||||
you will have to supply the `-g` flag alone with your build script. You might also
|
||||
have to provide the `-fno-omit-frame-pointer` flag
|
||||
9. Dig down the graph to find your function call and then inspect it by double clicking
|
||||
the list item. You will be able to see the annotated source code and the assembly side by
|
||||
side.
|
||||
|
||||
#### Perf
|
||||
|
||||
This wiki has a pretty detailed tutorial on getting started working with perf so we'll
|
||||
leave you to check that out of you're getting started:
|
||||
|
||||
https://perf.wiki.kernel.org/index.php/Tutorial
|
||||
|
||||
Some general notes on perf:
|
||||
* Use `perf stat -r # <bench-program>` to quickly get some relevant timing and
|
||||
counter statistics. Perf uses a high resolution timer and this is likely one
|
||||
of the first things your team will run when assessing your PR.
|
||||
* Perf has a long list of hardware counters that can be viewed with `perf --list`.
|
||||
When measuring optimizations, something worth trying is to make sure the handware
|
||||
counters you expect to be impacted by your change are in fact being so. For example,
|
||||
if you expect the L1 cache misses to decrease with your change, you can look at the
|
||||
counter `L1-dcache-load-misses`
|
||||
* Perf hardware counters will not work on a virtual machine.
|
||||
|
||||
#### Visual Studio
|
||||
|
||||
TODO
|
||||
|
||||
|
||||
## Setting up continuous integration (CI) on your fork
|
||||
Zstd uses a number of different continuous integration (CI) tools to ensure that new changes
|
||||
are well tested before they make it to an official release. Specifically, we use the platforms
|
||||
travis-ci, circle-ci, and appveyor.
|
||||
|
||||
Changes cannot be merged into the main dev branch unless they pass all of our CI tests.
|
||||
The easiest way to run these CI tests on your own before submitting a PR to our dev branch
|
||||
is to configure your personal fork of zstd with each of the CI platforms. Below, you'll find
|
||||
instructions for doing this.
|
||||
|
||||
### travis-ci
|
||||
Follow these steps to link travis-ci with your github fork of zstd
|
||||
|
||||
1. Make sure you are logged into your github account
|
||||
2. Go to https://travis-ci.org/
|
||||
3. Click 'Sign in with Github' on the top right
|
||||
4. Click 'Authorize travis-ci'
|
||||
5. Click 'Activate all repositories using Github Apps'
|
||||
6. Select 'Only select repositories' and select your fork of zstd from the drop down
|
||||
7. Click 'Approve and Install'
|
||||
8. Click 'Sign in with Github' again. This time, it will be for travis-pro (which will let you view your tests on the web dashboard)
|
||||
9. Click 'Authorize travis-pro'
|
||||
10. You should have travis set up on your fork now.
|
||||
|
||||
### circle-ci
|
||||
TODO
|
||||
|
||||
### appveyor
|
||||
Follow these steps to link circle-ci with your girhub fork of zstd
|
||||
|
||||
1. Make sure you are logged into your github account
|
||||
2. Go to https://www.appveyor.com/
|
||||
3. Click 'Sign in' on the top right
|
||||
4. Select 'Github' on the left panel
|
||||
5. Click 'Authorize appveyor'
|
||||
6. You might be asked to select which repositories you want to give appveyor permission to. Select your fork of zstd if you're prompted
|
||||
7. You should have appveyor set up on your fork now.
|
||||
|
||||
### General notes on CI
|
||||
CI tests run every time a pull request (PR) is created or updated. The exact tests
|
||||
that get run will depend on the destination branch you specify. Some tests take
|
||||
longer to run than others. Currently, our CI is set up to run a short
|
||||
series of tests when creating a PR to the dev branch and a longer series of tests
|
||||
when creating a PR to the release branch. You can look in the configuration files
|
||||
of the respective CI platform for more information on what gets run when.
|
||||
|
||||
Most people will just want to create a PR with the destination set to their local dev
|
||||
branch of zstd. You can then find the status of the tests on the PR's page. You can also
|
||||
re-run tests and cancel running tests from the PR page or from the respective CI's dashboard.
|
||||
|
||||
## Issues
|
||||
We use GitHub issues to track public bugs. Please ensure your description is
|
||||
clear and has sufficient instructions to be able to reproduce the issue.
|
||||
@@ -398,7 +34,7 @@ Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe
|
||||
disclosure of security bugs. In those cases, please go through the process
|
||||
outlined on that page and do not file a public issue.
|
||||
|
||||
## Coding Style
|
||||
## Coding Style
|
||||
* 4 spaces for indentation rather than tabs
|
||||
|
||||
## License
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
# ################################################################
|
||||
# Copyright (c) 2015-2020, Yann Collet, Facebook, Inc.
|
||||
# Copyright (c) 2015-present, Yann Collet, Facebook, 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.
|
||||
# ################################################################
|
||||
|
||||
# verbose mode (print commands) on V=1 or VERBOSE=1
|
||||
Q = $(if $(filter 1,$(V) $(VERBOSE)),,@)
|
||||
|
||||
PRGDIR = programs
|
||||
ZSTDDIR = lib
|
||||
BUILDIR = build
|
||||
@@ -21,19 +17,10 @@ FUZZDIR = $(TESTDIR)/fuzz
|
||||
# Define nul output
|
||||
VOID = /dev/null
|
||||
|
||||
# When cross-compiling from linux to windows, you might
|
||||
# need to specify this as "Windows." Fedora build fails
|
||||
# without it.
|
||||
#
|
||||
# Note: mingw-w64 build from linux to windows does not
|
||||
# fail on other tested distros (ubuntu, debian) even
|
||||
# without manually specifying the TARGET_SYSTEM.
|
||||
TARGET_SYSTEM ?= $(OS)
|
||||
|
||||
ifneq (,$(filter Windows%,$(TARGET_SYSTEM)))
|
||||
EXT =.exe
|
||||
ifneq (,$(filter Windows%,$(OS)))
|
||||
EXT =.exe
|
||||
else
|
||||
EXT =
|
||||
EXT =
|
||||
endif
|
||||
|
||||
## default: Build lib-release and zstd-release
|
||||
@@ -48,9 +35,9 @@ allmost: allzstd zlibwrapper
|
||||
|
||||
# skip zwrapper, can't build that on alternate architectures without the proper zlib installed
|
||||
.PHONY: allzstd
|
||||
allzstd: lib-all
|
||||
$(Q)$(MAKE) -C $(PRGDIR) all
|
||||
$(Q)$(MAKE) -C $(TESTDIR) all
|
||||
allzstd: lib
|
||||
$(MAKE) -C $(PRGDIR) all
|
||||
$(MAKE) -C $(TESTDIR) all
|
||||
|
||||
.PHONY: all32
|
||||
all32:
|
||||
@@ -58,19 +45,18 @@ all32:
|
||||
$(MAKE) -C $(TESTDIR) all32
|
||||
|
||||
.PHONY: lib lib-release libzstd.a
|
||||
lib-all : lib
|
||||
lib lib-release lib-all :
|
||||
$(Q)$(MAKE) -C $(ZSTDDIR) $@
|
||||
lib lib-release :
|
||||
@$(MAKE) -C $(ZSTDDIR) $@
|
||||
|
||||
.PHONY: zstd zstd-release
|
||||
zstd zstd-release:
|
||||
$(Q)$(MAKE) -C $(PRGDIR) $@
|
||||
$(Q)ln -sf $(PRGDIR)/zstd$(EXT) zstd$(EXT)
|
||||
@$(MAKE) -C $(PRGDIR) $@
|
||||
cp $(PRGDIR)/zstd$(EXT) .
|
||||
|
||||
.PHONY: zstdmt
|
||||
zstdmt:
|
||||
$(Q)$(MAKE) -C $(PRGDIR) $@
|
||||
$(Q)cp $(PRGDIR)/zstd$(EXT) ./zstdmt$(EXT)
|
||||
@$(MAKE) -C $(PRGDIR) $@
|
||||
cp $(PRGDIR)/zstd$(EXT) ./zstdmt$(EXT)
|
||||
|
||||
.PHONY: zlibwrapper
|
||||
zlibwrapper: lib
|
||||
@@ -79,16 +65,16 @@ zlibwrapper: lib
|
||||
## test: run long-duration tests
|
||||
.PHONY: test
|
||||
DEBUGLEVEL ?= 1
|
||||
test: MOREFLAGS += -g -Werror
|
||||
test: MOREFLAGS += -g -DDEBUGLEVEL=$(DEBUGLEVEL) -Werror
|
||||
test:
|
||||
DEBUGLEVEL=$(DEBUGLEVEL) MOREFLAGS="$(MOREFLAGS)" $(MAKE) -j -C $(PRGDIR) allVariants
|
||||
MOREFLAGS="$(MOREFLAGS)" $(MAKE) -j -C $(PRGDIR) allVariants
|
||||
$(MAKE) -C $(TESTDIR) $@
|
||||
ZSTD=../../programs/zstd $(MAKE) -C doc/educational_decoder $@
|
||||
ZSTD=../../programs/zstd $(MAKE) -C doc/educational_decoder test
|
||||
|
||||
## shortest: same as `make check`
|
||||
.PHONY: shortest
|
||||
shortest:
|
||||
$(Q)$(MAKE) -C $(TESTDIR) $@
|
||||
$(MAKE) -C $(TESTDIR) $@
|
||||
|
||||
## check: run basic tests for `zstd` cli
|
||||
.PHONY: check
|
||||
@@ -101,10 +87,10 @@ automated_benchmarking:
|
||||
.PHONY: benchmarking
|
||||
benchmarking: automated_benchmarking
|
||||
|
||||
## examples: build all examples in `examples/` directory
|
||||
## examples: build all examples in `/examples` directory
|
||||
.PHONY: examples
|
||||
examples: lib
|
||||
$(MAKE) -C examples all
|
||||
CPPFLAGS=-I../lib LDFLAGS=-L../lib $(MAKE) -C examples/ all
|
||||
|
||||
## manual: generate API documentation in html format
|
||||
.PHONY: manual
|
||||
@@ -121,10 +107,8 @@ man:
|
||||
contrib: lib
|
||||
$(MAKE) -C contrib/pzstd all
|
||||
$(MAKE) -C contrib/seekable_format/examples all
|
||||
$(MAKE) -C contrib/seekable_format/tests test
|
||||
$(MAKE) -C contrib/largeNbDicts all
|
||||
cd contrib/single_file_libs/ ; ./build_decoder_test.sh
|
||||
cd contrib/single_file_libs/ ; ./build_library_test.sh
|
||||
cd contrib/single_file_decoder/ ; ./build_test.sh
|
||||
|
||||
.PHONY: cleanTabs
|
||||
cleanTabs:
|
||||
@@ -132,24 +116,23 @@ cleanTabs:
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
$(Q)$(MAKE) -C $(ZSTDDIR) $@ > $(VOID)
|
||||
$(Q)$(MAKE) -C $(PRGDIR) $@ > $(VOID)
|
||||
$(Q)$(MAKE) -C $(TESTDIR) $@ > $(VOID)
|
||||
$(Q)$(MAKE) -C $(ZWRAPDIR) $@ > $(VOID)
|
||||
$(Q)$(MAKE) -C examples/ $@ > $(VOID)
|
||||
$(Q)$(MAKE) -C contrib/gen_html $@ > $(VOID)
|
||||
$(Q)$(MAKE) -C contrib/pzstd $@ > $(VOID)
|
||||
$(Q)$(MAKE) -C contrib/seekable_format/examples $@ > $(VOID)
|
||||
$(Q)$(MAKE) -C contrib/seekable_format/tests $@ > $(VOID)
|
||||
$(Q)$(MAKE) -C contrib/largeNbDicts $@ > $(VOID)
|
||||
$(Q)$(RM) zstd$(EXT) zstdmt$(EXT) tmp*
|
||||
$(Q)$(RM) -r lz4
|
||||
@$(MAKE) -C $(ZSTDDIR) $@ > $(VOID)
|
||||
@$(MAKE) -C $(PRGDIR) $@ > $(VOID)
|
||||
@$(MAKE) -C $(TESTDIR) $@ > $(VOID)
|
||||
@$(MAKE) -C $(ZWRAPDIR) $@ > $(VOID)
|
||||
@$(MAKE) -C examples/ $@ > $(VOID)
|
||||
@$(MAKE) -C contrib/gen_html $@ > $(VOID)
|
||||
@$(MAKE) -C contrib/pzstd $@ > $(VOID)
|
||||
@$(MAKE) -C contrib/seekable_format/examples $@ > $(VOID)
|
||||
@$(MAKE) -C contrib/largeNbDicts $@ > $(VOID)
|
||||
@$(RM) zstd$(EXT) zstdmt$(EXT) tmp*
|
||||
@$(RM) -r lz4
|
||||
@echo Cleaning completed
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# make install is validated only for Linux, macOS, Hurd and some BSD targets
|
||||
#------------------------------------------------------------------------------
|
||||
ifneq (,$(filter $(shell uname),Linux Darwin GNU/kFreeBSD GNU OpenBSD FreeBSD DragonFly NetBSD MSYS_NT Haiku))
|
||||
ifneq (,$(filter Linux Darwin GNU/kFreeBSD GNU OpenBSD FreeBSD DragonFly NetBSD MSYS_NT Haiku MINGW%,$(shell uname)))
|
||||
|
||||
HOST_OS = POSIX
|
||||
CMAKE_PARAMS = -DZSTD_BUILD_CONTRIB:BOOL=ON -DZSTD_BUILD_STATIC:BOOL=ON -DZSTD_BUILD_TESTS:BOOL=ON -DZSTD_ZLIB_SUPPORT:BOOL=ON -DZSTD_LZMA_SUPPORT:BOOL=ON -DCMAKE_BUILD_TYPE=Release
|
||||
@@ -167,7 +150,7 @@ EGREP = egrep $(EGREP_OPTIONS)
|
||||
## list: Print all targets and their descriptions (if provided)
|
||||
.PHONY: list
|
||||
list:
|
||||
$(Q)TARGETS=$$($(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null \
|
||||
@TARGETS=$$($(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null \
|
||||
| awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' \
|
||||
| $(EGREP) -v -e '^[^[:alnum:]]' | sort); \
|
||||
{ \
|
||||
@@ -182,13 +165,13 @@ list:
|
||||
|
||||
.PHONY: install armtest usan asan uasan
|
||||
install:
|
||||
$(Q)$(MAKE) -C $(ZSTDDIR) $@
|
||||
$(Q)$(MAKE) -C $(PRGDIR) $@
|
||||
@$(MAKE) -C $(ZSTDDIR) $@
|
||||
@$(MAKE) -C $(PRGDIR) $@
|
||||
|
||||
.PHONY: uninstall
|
||||
uninstall:
|
||||
$(Q)$(MAKE) -C $(ZSTDDIR) $@
|
||||
$(Q)$(MAKE) -C $(PRGDIR) $@
|
||||
@$(MAKE) -C $(ZSTDDIR) $@
|
||||
@$(MAKE) -C $(PRGDIR) $@
|
||||
|
||||
.PHONY: travis-install
|
||||
travis-install:
|
||||
@@ -377,7 +360,7 @@ cmakebuild:
|
||||
$(MAKE) -C $(BUILDIR)/cmake/build -j4;
|
||||
$(MAKE) -C $(BUILDIR)/cmake/build install;
|
||||
$(MAKE) -C $(BUILDIR)/cmake/build uninstall;
|
||||
cd $(BUILDIR)/cmake/build; ctest -V -L Medium
|
||||
cd $(BUILDIR)/cmake/build; ctest -V
|
||||
|
||||
c89build: clean
|
||||
$(CC) -v
|
||||
|
||||
@@ -31,10 +31,10 @@ a list of known ports and bindings is provided on [Zstandard homepage](http://ww
|
||||
## Benchmarks
|
||||
|
||||
For reference, several fast compression algorithms were tested and compared
|
||||
on a server running Arch Linux (`Linux version 5.5.11-arch1-1`),
|
||||
on a server running Arch Linux (`Linux version 5.0.5-arch1-1`),
|
||||
with a Core i9-9900K CPU @ 5.0GHz,
|
||||
using [lzbench], an open-source in-memory benchmark by @inikep
|
||||
compiled with [gcc] 9.3.0,
|
||||
compiled with [gcc] 8.2.1,
|
||||
on the [Silesia compression corpus].
|
||||
|
||||
[lzbench]: https://github.com/inikep/lzbench
|
||||
@@ -43,26 +43,18 @@ on the [Silesia compression corpus].
|
||||
|
||||
| Compressor name | Ratio | Compression| Decompress.|
|
||||
| --------------- | ------| -----------| ---------- |
|
||||
| **zstd 1.4.5 -1** | 2.884 | 500 MB/s | 1660 MB/s |
|
||||
| zlib 1.2.11 -1 | 2.743 | 90 MB/s | 400 MB/s |
|
||||
| brotli 1.0.7 -0 | 2.703 | 400 MB/s | 450 MB/s |
|
||||
| **zstd 1.4.5 --fast=1** | 2.434 | 570 MB/s | 2200 MB/s |
|
||||
| **zstd 1.4.5 --fast=3** | 2.312 | 640 MB/s | 2300 MB/s |
|
||||
| quicklz 1.5.0 -1 | 2.238 | 560 MB/s | 710 MB/s |
|
||||
| **zstd 1.4.5 --fast=5** | 2.178 | 700 MB/s | 2420 MB/s |
|
||||
| lzo1x 2.10 -1 | 2.106 | 690 MB/s | 820 MB/s |
|
||||
| lz4 1.9.2 | 2.101 | 740 MB/s | 4530 MB/s |
|
||||
| **zstd 1.4.5 --fast=7** | 2.096 | 750 MB/s | 2480 MB/s |
|
||||
| lzf 3.6 -1 | 2.077 | 410 MB/s | 860 MB/s |
|
||||
| snappy 1.1.8 | 2.073 | 560 MB/s | 1790 MB/s |
|
||||
| **zstd 1.4.4 -1** | 2.884 | 520 MB/s | 1600 MB/s |
|
||||
| zlib 1.2.11 -1 | 2.743 | 110 MB/s | 440 MB/s |
|
||||
| brotli 1.0.7 -0 | 2.701 | 430 MB/s | 470 MB/s |
|
||||
| quicklz 1.5.0 -1 | 2.238 | 600 MB/s | 800 MB/s |
|
||||
| lzo1x 2.09 -1 | 2.106 | 680 MB/s | 950 MB/s |
|
||||
| lz4 1.8.3 | 2.101 | 800 MB/s | 4220 MB/s |
|
||||
| snappy 1.1.4 | 2.073 | 580 MB/s | 2020 MB/s |
|
||||
| lzf 3.6 -1 | 2.077 | 440 MB/s | 930 MB/s |
|
||||
|
||||
[zlib]: http://www.zlib.net/
|
||||
[LZ4]: http://www.lz4.org/
|
||||
|
||||
The negative compression levels, specified with `--fast=#`,
|
||||
offer faster compression and decompression speed in exchange for some loss in
|
||||
compression ratio compared to level 1, as seen in the table above.
|
||||
|
||||
Zstd can also offer stronger compression ratios at the cost of compression speed.
|
||||
Speed vs Compression trade-off is configurable by small increments.
|
||||
Decompression speed is preserved and remains roughly the same at all settings,
|
||||
@@ -151,18 +143,6 @@ example about how Meson is used to build this project.
|
||||
|
||||
Note that default build type is **release**.
|
||||
|
||||
### VCPKG
|
||||
You can build and install zstd [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager:
|
||||
|
||||
git clone https://github.com/Microsoft/vcpkg.git
|
||||
cd vcpkg
|
||||
./bootstrap-vcpkg.sh
|
||||
./vcpkg integrate install
|
||||
./vcpkg install zstd
|
||||
|
||||
The zstd port in vcpkg is kept up to date by Microsoft team members and community contributors.
|
||||
If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
|
||||
|
||||
### Visual Studio (Windows)
|
||||
|
||||
Going into `build` directory, you will find additional possibilities:
|
||||
@@ -176,12 +156,6 @@ Going into `build` directory, you will find additional possibilities:
|
||||
You can build the zstd binary via buck by executing: `buck build programs:zstd` from the root of the repo.
|
||||
The output binary will be in `buck-out/gen/programs/`.
|
||||
|
||||
## Testing
|
||||
|
||||
You can run quick local smoke tests by executing the `playTest.sh` script from the `src/tests` directory.
|
||||
Two env variables `$ZSTD_BIN` and `$DATAGEN_BIN` are needed for the test script to locate the zstd and datagen binary.
|
||||
For information on CI testing, please refer to TESTING.md
|
||||
|
||||
## Status
|
||||
|
||||
Zstandard is currently deployed within Facebook. It is used continuously to compress large amounts of data in multiple formats and use cases.
|
||||
@@ -193,7 +167,7 @@ Zstandard is dual-licensed under [BSD](LICENSE) and [GPLv2](COPYING).
|
||||
|
||||
## Contributing
|
||||
|
||||
The `dev` branch is the one where all contributions are merged before reaching `release`.
|
||||
If you plan to propose a patch, please commit into the `dev` branch, or its own feature branch.
|
||||
Direct commit to `release` are not permitted.
|
||||
The "dev" branch is the one where all contributions are merged before reaching "master".
|
||||
If you plan to propose a patch, please commit into the "dev" branch, or its own feature branch.
|
||||
Direct commit to "master" are not permitted.
|
||||
For more information, please read [CONTRIBUTING](CONTRIBUTING.md).
|
||||
|
||||
+3
-3
@@ -11,7 +11,7 @@ They consist of the following tests:
|
||||
- Compilation on all supported targets (x86, x86_64, ARM, AArch64, PowerPC, and PowerPC64)
|
||||
- Compilation on various versions of gcc, clang, and g++
|
||||
- `tests/playTests.sh` on x86_64, without the tests on long data (CLI tests)
|
||||
- Small tests (`tests/legacy.c`, `tests/longmatch.c`) on x64_64
|
||||
- Small tests (`tests/legacy.c`, `tests/longmatch.c`, `tests/symbols.c`) on x64_64
|
||||
|
||||
Medium Tests
|
||||
------------
|
||||
@@ -19,7 +19,7 @@ Medium tests run on every commit and pull request to `dev` branch, on TravisCI.
|
||||
They consist of the following tests:
|
||||
- The following tests run with UBsan and Asan on x86_64 and x86, as well as with
|
||||
Msan on x86_64
|
||||
- `tests/playTests.sh --test-large-data`
|
||||
- `tests/playTests.sh --test-long-data`
|
||||
- Fuzzer tests: `tests/fuzzer.c`, `tests/zstreamtest.c`, and `tests/decodecorpus.c`
|
||||
- `tests/zstreamtest.c` under Tsan (streaming mode, including multithreaded mode)
|
||||
- Valgrind Test (`make -C tests valgrindTest`) (testing CLI and fuzzer under valgrind)
|
||||
@@ -27,7 +27,7 @@ They consist of the following tests:
|
||||
|
||||
Long Tests
|
||||
----------
|
||||
Long tests run on all commits to `release` branch,
|
||||
Long tests run on all commits to `master` branch,
|
||||
and once a day on the current version of `dev` branch,
|
||||
on TravisCI.
|
||||
They consist of the following tests:
|
||||
|
||||
+11
-15
@@ -1,20 +1,20 @@
|
||||
# Following tests are run _only_ on `release` branch
|
||||
# and on selected feature branch named `appveyorTest` or `visual*`
|
||||
# Following tests are run _only_ on master branch
|
||||
# To reproduce these tests, it's possible to push into a branch `appveyorTest`
|
||||
# or a branch `visual*`, they will intentionnally trigger `master` tests
|
||||
|
||||
-
|
||||
version: 1.0.{build}
|
||||
branches:
|
||||
only:
|
||||
- release
|
||||
- master
|
||||
- /appveyor*/
|
||||
- appveyorTest
|
||||
- /visual*/
|
||||
environment:
|
||||
matrix:
|
||||
- COMPILER: "gcc"
|
||||
HOST: "mingw"
|
||||
PLATFORM: "x64"
|
||||
SCRIPT: "make allzstd MOREFLAGS=-static"
|
||||
SCRIPT: "make allzstd MOREFLAGS=-static && make -C tests test-symbols fullbench-lib"
|
||||
ARTIFACT: "true"
|
||||
BUILD: "true"
|
||||
- COMPILER: "gcc"
|
||||
@@ -92,9 +92,9 @@
|
||||
cd programs\ && 7z a -tzip -mx9 zstd-win-binary-%PLATFORM%.zip zstd.exe &&
|
||||
appveyor PushArtifact zstd-win-binary-%PLATFORM%.zip &&
|
||||
cp zstd.exe ..\bin\zstd.exe &&
|
||||
git clone --depth 1 --branch release https://github.com/facebook/zstd &&
|
||||
git clone --depth 1 --branch master https://github.com/facebook/zstd &&
|
||||
cd zstd &&
|
||||
git archive --format=tar release -o zstd-src.tar &&
|
||||
git archive --format=tar master -o zstd-src.tar &&
|
||||
..\zstd -19 zstd-src.tar &&
|
||||
appveyor PushArtifact zstd-src.tar.zst &&
|
||||
certUtil -hashfile zstd-src.tar.zst SHA256 > zstd-src.tar.zst.sha256.sig &&
|
||||
@@ -162,8 +162,6 @@
|
||||
- if [%TEST%]==[cmake] (
|
||||
mkdir build\cmake\build &&
|
||||
cd build\cmake\build &&
|
||||
SET FUZZERTEST=-T2mn &&
|
||||
SET ZSTREAM_TESTTIME=-T2mn &&
|
||||
cmake -G "Visual Studio 14 2015 Win64" .. &&
|
||||
cd ..\..\.. &&
|
||||
make clean
|
||||
@@ -171,8 +169,7 @@
|
||||
- SET "FUZZERTEST=-T30s"
|
||||
- if [%HOST%]==[visual] if [%CONFIGURATION%]==[Release] (
|
||||
CD tests &&
|
||||
SET ZSTD_BIN=./zstd.exe&&
|
||||
SET DATAGEN_BIN=./datagen.exe&&
|
||||
SET ZSTD=./zstd.exe &&
|
||||
sh -e playTests.sh --test-large-data &&
|
||||
fullbench.exe -i1 &&
|
||||
fullbench.exe -i1 -P0 &&
|
||||
@@ -196,7 +193,7 @@
|
||||
- COMPILER: "gcc"
|
||||
HOST: "mingw"
|
||||
PLATFORM: "x64"
|
||||
SCRIPT: "CFLAGS=-Werror make -j allzstd DEBUGLEVEL=2"
|
||||
SCRIPT: "CPPFLAGS=-DDEBUGLEVEL=2 CFLAGS=-Werror make -j allzstd DEBUGLEVEL=2"
|
||||
- COMPILER: "gcc"
|
||||
HOST: "mingw"
|
||||
PLATFORM: "x86"
|
||||
@@ -252,9 +249,9 @@
|
||||
C:\cygwin64\bin\bash --login -c "
|
||||
set -e;
|
||||
cd build/cmake;
|
||||
CFLAGS='-Werror' cmake -G 'Unix Makefiles' -DCMAKE_BUILD_TYPE=Debug -DZSTD_BUILD_TESTS:BOOL=ON -DZSTD_FUZZER_FLAGS=-T30s -DZSTD_ZSTREAM_FLAGS=-T30s .;
|
||||
CFLAGS='-Werror' cmake -G 'Unix Makefiles' -DCMAKE_BUILD_TYPE=Debug -DZSTD_BUILD_TESTS:BOOL=ON .;
|
||||
make -j4;
|
||||
ctest -V -L Medium;
|
||||
ctest -V;
|
||||
"
|
||||
)
|
||||
- if [%HOST%]==[mingw] (
|
||||
@@ -287,6 +284,5 @@
|
||||
- ECHO Testing %COMPILER% %PLATFORM% %CONFIGURATION%
|
||||
- if [%HOST%]==[mingw] (
|
||||
set "CC=%COMPILER%" &&
|
||||
make clean &&
|
||||
make check
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# in the COPYING file in the root directory of this source tree).
|
||||
# ################################################################
|
||||
|
||||
cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR)
|
||||
cmake_minimum_required(VERSION 2.8.9 FATAL_ERROR)
|
||||
|
||||
# As of 2018-12-26 ZSTD has been validated to build with cmake version 3.13.2 new policies.
|
||||
# Set and use the newest cmake policies that are validated to work
|
||||
@@ -103,14 +103,7 @@ endif ()
|
||||
|
||||
option(ZSTD_BUILD_PROGRAMS "BUILD PROGRAMS" ON)
|
||||
option(ZSTD_BUILD_CONTRIB "BUILD CONTRIB" OFF)
|
||||
|
||||
# Respect the conventional CMake option for enabling tests if it was specified on the first configure
|
||||
if (BUILD_TESTING)
|
||||
set(ZSTD_BUILD_TESTS_default ON)
|
||||
else()
|
||||
set(ZSTD_BUILD_TESTS_default OFF)
|
||||
endif()
|
||||
option(ZSTD_BUILD_TESTS "BUILD TESTS" ${ZSTD_BUILD_TESTS_default})
|
||||
option(ZSTD_BUILD_TESTS "BUILD TESTS" OFF)
|
||||
if (MSVC)
|
||||
option(ZSTD_USE_STATIC_RUNTIME "LINK TO STATIC RUN-TIME LIBRARIES" OFF)
|
||||
endif ()
|
||||
@@ -165,39 +158,3 @@ add_custom_target(clean-all
|
||||
COMMAND ${CMAKE_BUILD_TOOL} clean
|
||||
COMMAND rm -rf ${CMAKE_BINARY_DIR}/
|
||||
)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Generate Package Config files
|
||||
#
|
||||
# This section is based on the boiler plate code from:
|
||||
# https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html#creating-packages
|
||||
#-----------------------------------------------------------------------------
|
||||
include(CMakePackageConfigHelpers)
|
||||
write_basic_package_version_file(
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/zstdConfigVersion.cmake"
|
||||
VERSION ${zstd_VERSION}
|
||||
COMPATIBILITY SameMajorVersion
|
||||
)
|
||||
|
||||
# A Package Config file that works from the build directory
|
||||
export(EXPORT zstdExports
|
||||
FILE "${CMAKE_CURRENT_BINARY_DIR}/zstdTargets.cmake"
|
||||
NAMESPACE zstd::
|
||||
)
|
||||
configure_file(zstdConfig.cmake
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/zstdConfig.cmake"
|
||||
COPYONLY
|
||||
)
|
||||
|
||||
# A Package Config file that works from the installation directory
|
||||
set(ConfigPackageLocation ${CMAKE_INSTALL_LIBDIR}/cmake/zstd)
|
||||
install(EXPORT zstdExports
|
||||
FILE zstdTargets.cmake
|
||||
NAMESPACE zstd::
|
||||
DESTINATION ${ConfigPackageLocation}
|
||||
)
|
||||
install(FILES
|
||||
zstdConfig.cmake
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/zstdConfigVersion.cmake"
|
||||
DESTINATION ${ConfigPackageLocation}
|
||||
)
|
||||
|
||||
@@ -60,6 +60,7 @@ macro(ADD_ZSTD_COMPILATION_FLAGS)
|
||||
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
|
||||
if( ${flag_var} )
|
||||
separate_arguments(${flag_var})
|
||||
list(REMOVE_DUPLICATES ${flag_var})
|
||||
string(REPLACE ";" " " ${flag_var} "${${flag_var}}")
|
||||
endif()
|
||||
endforeach ()
|
||||
|
||||
@@ -77,10 +77,8 @@ if (MSVC)
|
||||
endif ()
|
||||
|
||||
# Split project to static and shared libraries build
|
||||
set(library_targets)
|
||||
if (ZSTD_BUILD_SHARED)
|
||||
add_library(libzstd_shared SHARED ${Sources} ${Headers} ${PlatformDependResources})
|
||||
list(APPEND library_targets libzstd_shared)
|
||||
if (ZSTD_MULTITHREAD_SUPPORT)
|
||||
set_property(TARGET libzstd_shared APPEND PROPERTY COMPILE_DEFINITIONS "ZSTD_MULTITHREAD")
|
||||
if (UNIX)
|
||||
@@ -90,7 +88,6 @@ if (ZSTD_BUILD_SHARED)
|
||||
endif ()
|
||||
if (ZSTD_BUILD_STATIC)
|
||||
add_library(libzstd_static STATIC ${Sources} ${Headers})
|
||||
list(APPEND library_targets libzstd_static)
|
||||
if (ZSTD_MULTITHREAD_SUPPORT)
|
||||
set_property(TARGET libzstd_static APPEND PROPERTY COMPILE_DEFINITIONS "ZSTD_MULTITHREAD")
|
||||
if (UNIX)
|
||||
@@ -134,56 +131,58 @@ if (ZSTD_BUILD_STATIC)
|
||||
OUTPUT_NAME ${STATIC_LIBRARY_BASE_NAME})
|
||||
endif ()
|
||||
|
||||
if (UNIX OR MINGW)
|
||||
if (UNIX)
|
||||
# pkg-config
|
||||
set(PREFIX "${CMAKE_INSTALL_PREFIX}")
|
||||
set(EXEC_PREFIX "\\$$\{prefix}")
|
||||
set(LIBDIR "${CMAKE_INSTALL_FULL_LIBDIR}")
|
||||
set(INCLUDEDIR "${CMAKE_INSTALL_FULL_INCLUDEDIR}")
|
||||
set(VERSION "${zstd_VERSION}")
|
||||
|
||||
string(LENGTH "${PREFIX}" PREFIX_LENGTH)
|
||||
string(SUBSTRING "${LIBDIR}" 0 ${PREFIX_LENGTH} LIBDIR_PREFIX)
|
||||
string(SUBSTRING "${LIBDIR}" ${PREFIX_LENGTH} -1 LIBDIR_SUFFIX)
|
||||
string(SUBSTRING "${INCLUDEDIR}" 0 ${PREFIX_LENGTH} INCLUDEDIR_PREFIX)
|
||||
string(SUBSTRING "${INCLUDEDIR}" ${PREFIX_LENGTH} -1 INCLUDEDIR_SUFFIX)
|
||||
|
||||
if ("${INCLUDEDIR_PREFIX}" STREQUAL "${PREFIX}")
|
||||
set(INCLUDEDIR_PREFIX "\\$$\{prefix}")
|
||||
endif()
|
||||
if ("${LIBDIR_PREFIX}" STREQUAL "${PREFIX}")
|
||||
set(LIBDIR_PREFIX "\\$$\{exec_prefix}")
|
||||
endif()
|
||||
|
||||
set(VERSION "${zstd_VERSION_MAJOR}.${zstd_VERSION_MINOR}.${zstd_VERSION_PATCH}")
|
||||
add_custom_target(libzstd.pc ALL
|
||||
${CMAKE_COMMAND}
|
||||
-DIN=${LIBRARY_DIR}/libzstd.pc.in
|
||||
-DOUT="libzstd.pc"
|
||||
-DPREFIX="${PREFIX}"
|
||||
-DEXEC_PREFIX="${EXEC_PREFIX}"
|
||||
-DINCLUDEDIR="${INCLUDEDIR_PREFIX}${INCLUDEDIR_SUFFIX}"
|
||||
-DLIBDIR="${LIBDIR_PREFIX}${LIBDIR_SUFFIX}"
|
||||
-DVERSION="${VERSION}"
|
||||
-P ${CMAKE_CURRENT_SOURCE_DIR}/pkgconfig.cmake
|
||||
${CMAKE_COMMAND} -DIN="${LIBRARY_DIR}/libzstd.pc.in" -DOUT="libzstd.pc"
|
||||
-DPREFIX="${PREFIX}" -DVERSION="${VERSION}"
|
||||
-P "${CMAKE_CURRENT_SOURCE_DIR}/pkgconfig.cmake"
|
||||
COMMENT "Creating pkg-config file")
|
||||
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libzstd.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libzstd.pc" DESTINATION "${LIBDIR}/pkgconfig")
|
||||
endif ()
|
||||
|
||||
# install target
|
||||
install(FILES
|
||||
"${LIBRARY_DIR}/zstd.h"
|
||||
"${LIBRARY_DIR}/deprecated/zbuff.h"
|
||||
"${LIBRARY_DIR}/dictBuilder/zdict.h"
|
||||
"${LIBRARY_DIR}/dictBuilder/cover.h"
|
||||
"${LIBRARY_DIR}/common/zstd_errors.h"
|
||||
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
|
||||
install(TARGETS ${library_targets}
|
||||
EXPORT zstdExports
|
||||
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||
if (ZSTD_BUILD_SHARED)
|
||||
install(TARGETS libzstd_shared EXPORT zstdExports
|
||||
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
)
|
||||
endif()
|
||||
if (ZSTD_BUILD_STATIC)
|
||||
install(TARGETS libzstd_static EXPORT zstdExports
|
||||
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
)
|
||||
endif ()
|
||||
|
||||
# export targets + find config
|
||||
configure_file(
|
||||
"config.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/zstdConfig.cmake"
|
||||
@ONLY
|
||||
)
|
||||
install(FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/zstdConfig.cmake"
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/zstd"
|
||||
)
|
||||
install(EXPORT zstdExports
|
||||
NAMESPACE zstd::
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/zstd"
|
||||
)
|
||||
|
||||
# uninstall target
|
||||
if (NOT TARGET uninstall)
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
include(FindPackageHandleStandardArgs)
|
||||
set(${CMAKE_FIND_PACKAGE_NAME}_CONFIG ${CMAKE_CURRENT_LIST_FILE})
|
||||
find_package_handle_standard_args(zstd CONFIG_MODE)
|
||||
|
||||
if(NOT TARGET zstd::libzstd_shared)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/zstdExports.cmake")
|
||||
endif()
|
||||
@@ -32,16 +32,6 @@
|
||||
|
||||
project(tests)
|
||||
|
||||
# name: Cache variable name. The value is expected to be a semicolon-separated
|
||||
# list of command line flags
|
||||
# default_value: Value to initialize the option with. Can be space separated.
|
||||
function(AddTestFlagsOption name default_value doc)
|
||||
string(STRIP "${default_value}" default_value)
|
||||
string(REGEX REPLACE " +" ";" default_value "${default_value}")
|
||||
set(${name} ${default_value} CACHE STRING "${doc}")
|
||||
mark_as_advanced(${name})
|
||||
endfunction()
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR TRUE)
|
||||
|
||||
# Define programs directory, where sources and header files are located
|
||||
@@ -53,56 +43,20 @@ include_directories(${TESTS_DIR} ${PROGRAMS_DIR} ${LIBRARY_DIR} ${LIBRARY_DIR}/c
|
||||
add_executable(datagen ${PROGRAMS_DIR}/datagen.c ${TESTS_DIR}/datagencli.c)
|
||||
target_link_libraries(datagen libzstd_static)
|
||||
|
||||
#
|
||||
# fullbench
|
||||
#
|
||||
add_executable(fullbench ${PROGRAMS_DIR}/datagen.c ${PROGRAMS_DIR}/util.c ${PROGRAMS_DIR}/timefn.c ${PROGRAMS_DIR}/benchfn.c ${PROGRAMS_DIR}/benchzstd.c ${TESTS_DIR}/fullbench.c)
|
||||
target_link_libraries(fullbench libzstd_static)
|
||||
add_test(NAME fullbench COMMAND fullbench)
|
||||
|
||||
#
|
||||
# fuzzer
|
||||
#
|
||||
add_executable(fuzzer ${PROGRAMS_DIR}/datagen.c ${PROGRAMS_DIR}/util.c ${PROGRAMS_DIR}/timefn.c ${TESTS_DIR}/fuzzer.c)
|
||||
target_link_libraries(fuzzer libzstd_static)
|
||||
AddTestFlagsOption(ZSTD_FUZZER_FLAGS "$ENV{FUZZERTEST} $ENV{FUZZER_FLAGS}"
|
||||
"Semicolon-separated list of flags to pass to the fuzzer test (see `fuzzer -h` for usage)")
|
||||
add_test(NAME fuzzer COMMAND fuzzer ${ZSTD_FUZZER_FLAGS})
|
||||
# Disable the timeout since the run time is too long for the default timeout of
|
||||
# 1500 seconds and varies considerably between low-end and high-end CPUs.
|
||||
# set_tests_properties(fuzzer PROPERTIES TIMEOUT 0)
|
||||
|
||||
#
|
||||
# zstreamtest
|
||||
#
|
||||
add_executable(zstreamtest ${PROGRAMS_DIR}/datagen.c ${PROGRAMS_DIR}/util.c ${PROGRAMS_DIR}/timefn.c ${TESTS_DIR}/seqgen.c ${TESTS_DIR}/zstreamtest.c)
|
||||
target_link_libraries(zstreamtest libzstd_static)
|
||||
AddTestFlagsOption(ZSTD_ZSTREAM_FLAGS "$ENV{ZSTREAM_TESTTIME} $ENV{FUZZER_FLAGS}"
|
||||
"Semicolon-separated list of flags to pass to the zstreamtest test (see `zstreamtest -h` for usage)")
|
||||
add_test(NAME zstreamtest COMMAND zstreamtest ${ZSTD_ZSTREAM_FLAGS})
|
||||
|
||||
#
|
||||
# playTests.sh
|
||||
#
|
||||
AddTestFlagsOption(ZSTD_PLAYTESTS_FLAGS "$ENV{PLAYTESTS_FLAGS}"
|
||||
"Semicolon-separated list of flags to pass to the playTests.sh test")
|
||||
add_test(NAME playTests COMMAND sh -c "\"${TESTS_DIR}/playTests.sh\" ${ZSTD_PLAYTESTS_FLAGS}")
|
||||
if (ZSTD_BUILD_PROGRAMS)
|
||||
set_property(TEST playTests APPEND PROPERTY ENVIRONMENT
|
||||
"ZSTD_BIN=$<TARGET_FILE:zstd>"
|
||||
"DATAGEN_BIN=$<TARGET_FILE:datagen>"
|
||||
)
|
||||
else()
|
||||
message(STATUS "Disabling playTests.sh test because ZSTD_BUILD_PROGRAMS is not enabled")
|
||||
set_tests_properties(playTests PROPERTIES DISABLED YES)
|
||||
endif()
|
||||
add_test(
|
||||
NAME playTests
|
||||
COMMAND sh -c "ZSTD_BIN='$<TARGET_FILE:zstd>' DATAGEN_BIN='$<TARGET_FILE:datagen>' '${TESTS_DIR}/playTests.sh'")
|
||||
|
||||
# Label the "Medium" set of tests (see TESTING.md)
|
||||
set_property(TEST fuzzer zstreamtest playTests APPEND PROPERTY LABELS Medium)
|
||||
|
||||
add_executable(paramgrill ${PROGRAMS_DIR}/benchfn.c ${PROGRAMS_DIR}/benchzstd.c ${PROGRAMS_DIR}/datagen.c ${PROGRAMS_DIR}/util.c ${PROGRAMS_DIR}/timefn.c ${TESTS_DIR}/paramgrill.c)
|
||||
if (UNIX)
|
||||
add_executable(paramgrill ${PROGRAMS_DIR}/benchfn.c ${PROGRAMS_DIR}/benchzstd.c ${PROGRAMS_DIR}/datagen.c ${PROGRAMS_DIR}/util.c ${PROGRAMS_DIR}/timefn.c ${TESTS_DIR}/paramgrill.c)
|
||||
target_link_libraries(paramgrill libzstd_static m) #m is math library
|
||||
else()
|
||||
target_link_libraries(paramgrill libzstd_static)
|
||||
endif ()
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/zstdTargets.cmake")
|
||||
@@ -127,5 +127,6 @@ pkgconfig.generate(libzstd,
|
||||
url: 'http://www.zstd.net/')
|
||||
|
||||
install_headers(join_paths(zstd_rootdir, 'lib/zstd.h'),
|
||||
join_paths(zstd_rootdir, 'lib/deprecated/zbuff.h'),
|
||||
join_paths(zstd_rootdir, 'lib/dictBuilder/zdict.h'),
|
||||
join_paths(zstd_rootdir, 'lib/common/zstd_errors.h'))
|
||||
|
||||
@@ -22,6 +22,7 @@ project('zstd',
|
||||
cc = meson.get_compiler('c')
|
||||
cxx = meson.get_compiler('cpp')
|
||||
pkgconfig = import('pkgconfig')
|
||||
python3 = import('python').find_installation()
|
||||
windows_mod = import('windows')
|
||||
|
||||
host_machine_os = host_machine.system()
|
||||
@@ -39,8 +40,8 @@ compiler_msvc = 'msvc'
|
||||
zstd_version = meson.project_version()
|
||||
|
||||
zstd_h_file = join_paths(meson.current_source_dir(), '../../lib/zstd.h')
|
||||
GetZstdLibraryVersion_py = find_program('GetZstdLibraryVersion.py', native : true)
|
||||
r = run_command(GetZstdLibraryVersion_py, zstd_h_file)
|
||||
GetZstdLibraryVersion_py = files('GetZstdLibraryVersion.py')
|
||||
r = run_command(python3, GetZstdLibraryVersion_py, zstd_h_file)
|
||||
if r.returncode() == 0
|
||||
zstd_version = r.stdout().strip()
|
||||
message('Project version is now: @0@'.format(zstd_version))
|
||||
@@ -67,7 +68,6 @@ zstd_docdir = join_paths(zstd_datadir, 'doc', meson.project_name())
|
||||
# Built-in options
|
||||
use_debug = get_option('debug')
|
||||
buildtype = get_option('buildtype')
|
||||
default_library_type = get_option('default_library')
|
||||
|
||||
# Custom options
|
||||
debug_level = get_option('debug_level')
|
||||
@@ -121,7 +121,7 @@ elif cc_id == compiler_msvc
|
||||
if use_multi_thread
|
||||
msvc_compile_flags += '/MP'
|
||||
endif
|
||||
if use_static_runtime
|
||||
if enable_static_runtime
|
||||
msvc_compile_flags += '/MT'
|
||||
endif
|
||||
add_project_arguments(msvc_compile_flags, language: ['c', 'cpp'])
|
||||
|
||||
@@ -60,6 +60,17 @@ fuzzer = executable('fuzzer',
|
||||
dependencies: libzstd_dep,
|
||||
install: false)
|
||||
|
||||
zbufftest_sources = [join_paths(zstd_rootdir, 'programs/datagen.c'),
|
||||
join_paths(zstd_rootdir, 'programs/util.c'),
|
||||
join_paths(zstd_rootdir, 'programs/timefn.c'),
|
||||
join_paths(zstd_rootdir, 'tests/zbufftest.c')]
|
||||
zbufftest = executable('zbufftest',
|
||||
zbufftest_sources,
|
||||
c_args: ['-Wno-deprecated-declarations'],
|
||||
include_directories: test_includes,
|
||||
dependencies: libzstd_dep,
|
||||
install: false)
|
||||
|
||||
zstreamtest_sources = [join_paths(zstd_rootdir, 'programs/datagen.c'),
|
||||
join_paths(zstd_rootdir, 'programs/util.c'),
|
||||
join_paths(zstd_rootdir, 'programs/timefn.c'),
|
||||
@@ -120,6 +131,14 @@ decodecorpus = executable('decodecorpus',
|
||||
dependencies: [ libzstd_dep, libm_dep ],
|
||||
install: false)
|
||||
|
||||
symbols_sources = [join_paths(zstd_rootdir, 'tests/symbols.c')]
|
||||
symbols = executable('symbols',
|
||||
symbols_sources,
|
||||
include_directories: test_includes,
|
||||
c_args: host_machine_os == os_windows ? '-DZSTD_DLL_IMPORT=1' : [],
|
||||
dependencies: [ libzstd_dep ],
|
||||
install: false)
|
||||
|
||||
poolTests_sources = [join_paths(zstd_rootdir, 'programs/util.c'),
|
||||
join_paths(zstd_rootdir, 'programs/timefn.c'),
|
||||
join_paths(zstd_rootdir, 'tests/poolTests.c'),
|
||||
@@ -158,7 +177,7 @@ if host_machine_os != os_windows
|
||||
test('test-zstd',
|
||||
playTests_sh,
|
||||
args: ZSTDRTTEST,
|
||||
env: ['ZSTD_BIN=' + zstd.full_path(), 'DATAGEN_BIN=./datagen'],
|
||||
env: ['ZSTD=' + zstd.full_path()],
|
||||
depends: [datagen],
|
||||
workdir: meson.current_build_dir(),
|
||||
timeout: 2800) # Timeout should work on HDD drive
|
||||
@@ -182,6 +201,10 @@ if use_zlib
|
||||
timeout: 480)
|
||||
endif
|
||||
|
||||
test('test-zbuff',
|
||||
zbufftest,
|
||||
args: [ZSTREAM_TESTTIME],
|
||||
timeout: 120)
|
||||
test('test-zstream-1',
|
||||
zstreamtest,
|
||||
args: ['-v', ZSTREAM_TESTTIME] + FUZZER_FLAGS,
|
||||
@@ -196,6 +219,7 @@ test('test-zstream-3',
|
||||
timeout: 120)
|
||||
test('test-longmatch', longmatch, timeout: 36)
|
||||
test('test-invalidDictionaries', invalidDictionaries) # should be fast
|
||||
test('test-symbols', symbols) # should be fast
|
||||
if 0 < legacy_level and legacy_level <= 4
|
||||
test('test-legacy', legacy) # should be fast
|
||||
endif
|
||||
|
||||
@@ -100,14 +100,11 @@ static char* readFile(const char* filename, size_t* size) {
|
||||
buf = malloc(*size);
|
||||
if (buf == NULL) {
|
||||
fprintf(stderr, "malloc failed\n");
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bytes_read = fread(buf, 1, *size, f);
|
||||
if (bytes_read != *size) {
|
||||
fprintf(stderr, "failed to read whole file\n");
|
||||
fclose(f);
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1,695 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# ################################################################
|
||||
# Copyright (c) 2020-2020, Facebook, 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.
|
||||
# ##########################################################################
|
||||
|
||||
import argparse
|
||||
import contextlib
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
from typing import Optional
|
||||
|
||||
|
||||
INCLUDED_SUBDIRS = ["common", "compress", "decompress"]
|
||||
|
||||
SKIPPED_FILES = [
|
||||
"common/mem.h",
|
||||
"common/zstd_deps.h",
|
||||
"common/pool.c",
|
||||
"common/pool.h",
|
||||
"common/threading.c",
|
||||
"common/threading.h",
|
||||
"compress/zstdmt_compress.h",
|
||||
"compress/zstdmt_compress.c",
|
||||
]
|
||||
|
||||
XXHASH_FILES = [
|
||||
"common/xxhash.c",
|
||||
"common/xxhash.h",
|
||||
]
|
||||
|
||||
|
||||
class FileLines(object):
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
with open(self.filename, "r") as f:
|
||||
self.lines = f.readlines()
|
||||
|
||||
def write(self):
|
||||
with open(self.filename, "w") as f:
|
||||
f.write("".join(self.lines))
|
||||
|
||||
|
||||
class PartialPreprocessor(object):
|
||||
"""
|
||||
Looks for simple ifdefs and ifndefs and replaces them.
|
||||
Handles && and ||.
|
||||
Has fancy logic to handle translating elifs to ifs.
|
||||
Only looks for macros in the first part of the expression with no
|
||||
parens.
|
||||
Does not handle multi-line macros (only looks in first line).
|
||||
"""
|
||||
def __init__(self, defs: [(str, Optional[str])], replaces: [(str, str)], undefs: [str]):
|
||||
MACRO_GROUP = r"(?P<macro>[a-zA-Z_][a-zA-Z_0-9]*)"
|
||||
ELIF_GROUP = r"(?P<elif>el)?"
|
||||
OP_GROUP = r"(?P<op>&&|\|\|)?"
|
||||
|
||||
self._defs = {macro:value for macro, value in defs}
|
||||
self._replaces = {macro:value for macro, value in replaces}
|
||||
self._defs.update(self._replaces)
|
||||
self._undefs = set(undefs)
|
||||
|
||||
self._define = re.compile(r"\s*#\s*define")
|
||||
self._if = re.compile(r"\s*#\s*if")
|
||||
self._elif = re.compile(r"\s*#\s*(?P<elif>el)if")
|
||||
self._else = re.compile(r"\s*#\s*(?P<else>else)")
|
||||
self._endif = re.compile(r"\s*#\s*endif")
|
||||
|
||||
self._ifdef = re.compile(fr"\s*#\s*if(?P<not>n)?def {MACRO_GROUP}\s*")
|
||||
self._if_defined = re.compile(
|
||||
fr"\s*#\s*{ELIF_GROUP}if\s+(?P<not>!)?\s*defined\s*\(\s*{MACRO_GROUP}\s*\)\s*{OP_GROUP}"
|
||||
)
|
||||
self._if_defined_value = re.compile(
|
||||
fr"\s*#\s*{ELIF_GROUP}if\s+defined\s*\(\s*{MACRO_GROUP}\s*\)\s*"
|
||||
fr"(?P<op>&&)\s*"
|
||||
fr"(?P<openp>\()?\s*"
|
||||
fr"(?P<macro2>[a-zA-Z_][a-zA-Z_0-9]*)\s*"
|
||||
fr"(?P<cmp>[=><!]+)\s*"
|
||||
fr"(?P<value>[0-9]*)\s*"
|
||||
fr"(?P<closep>\))?\s*"
|
||||
)
|
||||
self._if_true = re.compile(
|
||||
fr"\s*#\s*{ELIF_GROUP}if\s+{MACRO_GROUP}\s*{OP_GROUP}"
|
||||
)
|
||||
|
||||
self._c_comment = re.compile(r"/\*.*?\*/")
|
||||
self._cpp_comment = re.compile(r"//")
|
||||
|
||||
def _log(self, *args, **kwargs):
|
||||
print(*args, **kwargs)
|
||||
|
||||
def _strip_comments(self, line):
|
||||
# First strip c-style comments (may include //)
|
||||
while True:
|
||||
m = self._c_comment.search(line)
|
||||
if m is None:
|
||||
break
|
||||
line = line[:m.start()] + line[m.end():]
|
||||
|
||||
# Then strip cpp-style comments
|
||||
m = self._cpp_comment.search(line)
|
||||
if m is not None:
|
||||
line = line[:m.start()]
|
||||
|
||||
return line
|
||||
|
||||
def _fixup_indentation(self, macro, replace: [str]):
|
||||
if len(replace) == 0:
|
||||
return replace
|
||||
if len(replace) == 1 and self._define.match(replace[0]) is None:
|
||||
# If there is only one line, only replace defines
|
||||
return replace
|
||||
|
||||
|
||||
all_pound = True
|
||||
for line in replace:
|
||||
if not line.startswith('#'):
|
||||
all_pound = False
|
||||
if all_pound:
|
||||
replace = [line[1:] for line in replace]
|
||||
|
||||
min_spaces = len(replace[0])
|
||||
for line in replace:
|
||||
spaces = 0
|
||||
for i, c in enumerate(line):
|
||||
if c != ' ':
|
||||
# Non-preprocessor line ==> skip the fixup
|
||||
if not all_pound and c != '#':
|
||||
return replace
|
||||
spaces = i
|
||||
break
|
||||
min_spaces = min(min_spaces, spaces)
|
||||
|
||||
replace = [line[min_spaces:] for line in replace]
|
||||
|
||||
if all_pound:
|
||||
replace = ["#" + line for line in replace]
|
||||
|
||||
return replace
|
||||
|
||||
def _handle_if_block(self, macro, idx, is_true, prepend):
|
||||
"""
|
||||
Remove the #if or #elif block starting on this line.
|
||||
"""
|
||||
REMOVE_ONE = 0
|
||||
KEEP_ONE = 1
|
||||
REMOVE_REST = 2
|
||||
|
||||
if is_true:
|
||||
state = KEEP_ONE
|
||||
else:
|
||||
state = REMOVE_ONE
|
||||
|
||||
line = self._inlines[idx]
|
||||
is_if = self._if.match(line) is not None
|
||||
assert is_if or self._elif.match(line) is not None
|
||||
depth = 0
|
||||
|
||||
start_idx = idx
|
||||
|
||||
idx += 1
|
||||
replace = prepend
|
||||
finished = False
|
||||
while idx < len(self._inlines):
|
||||
line = self._inlines[idx]
|
||||
# Nested if statement
|
||||
if self._if.match(line):
|
||||
depth += 1
|
||||
idx += 1
|
||||
continue
|
||||
# We're inside a nested statement
|
||||
if depth > 0:
|
||||
if self._endif.match(line):
|
||||
depth -= 1
|
||||
idx += 1
|
||||
continue
|
||||
|
||||
# We're at the original depth
|
||||
|
||||
# Looking only for an endif.
|
||||
# We've found a true statement, but haven't
|
||||
# completely elided the if block, so we just
|
||||
# remove the remainder.
|
||||
if state == REMOVE_REST:
|
||||
if self._endif.match(line):
|
||||
if is_if:
|
||||
# Remove the endif because we took the first if
|
||||
idx += 1
|
||||
finished = True
|
||||
break
|
||||
idx += 1
|
||||
continue
|
||||
|
||||
if state == KEEP_ONE:
|
||||
m = self._elif.match(line)
|
||||
if self._endif.match(line):
|
||||
replace += self._inlines[start_idx + 1:idx]
|
||||
idx += 1
|
||||
finished = True
|
||||
break
|
||||
if self._elif.match(line) or self._else.match(line):
|
||||
replace += self._inlines[start_idx + 1:idx]
|
||||
state = REMOVE_REST
|
||||
idx += 1
|
||||
continue
|
||||
|
||||
if state == REMOVE_ONE:
|
||||
m = self._elif.match(line)
|
||||
if m is not None:
|
||||
if is_if:
|
||||
idx += 1
|
||||
b = m.start('elif')
|
||||
e = m.end('elif')
|
||||
assert e - b == 2
|
||||
replace.append(line[:b] + line[e:])
|
||||
finished = True
|
||||
break
|
||||
m = self._else.match(line)
|
||||
if m is not None:
|
||||
if is_if:
|
||||
idx += 1
|
||||
while self._endif.match(self._inlines[idx]) is None:
|
||||
replace.append(self._inlines[idx])
|
||||
idx += 1
|
||||
idx += 1
|
||||
finished = True
|
||||
break
|
||||
if self._endif.match(line):
|
||||
if is_if:
|
||||
# Remove the endif because no other elifs
|
||||
idx += 1
|
||||
finished = True
|
||||
break
|
||||
idx += 1
|
||||
continue
|
||||
if not finished:
|
||||
raise RuntimeError("Unterminated if block!")
|
||||
|
||||
replace = self._fixup_indentation(macro, replace)
|
||||
|
||||
self._log(f"\tHardwiring {macro}")
|
||||
if start_idx > 0:
|
||||
self._log(f"\t\t {self._inlines[start_idx - 1][:-1]}")
|
||||
for x in range(start_idx, idx):
|
||||
self._log(f"\t\t- {self._inlines[x][:-1]}")
|
||||
for line in replace:
|
||||
self._log(f"\t\t+ {line[:-1]}")
|
||||
if idx < len(self._inlines):
|
||||
self._log(f"\t\t {self._inlines[idx][:-1]}")
|
||||
|
||||
return idx, replace
|
||||
|
||||
def _preprocess_once(self):
|
||||
outlines = []
|
||||
idx = 0
|
||||
changed = False
|
||||
while idx < len(self._inlines):
|
||||
line = self._inlines[idx]
|
||||
sline = self._strip_comments(line)
|
||||
m = self._ifdef.fullmatch(sline)
|
||||
if_true = False
|
||||
if m is None:
|
||||
m = self._if_defined_value.fullmatch(sline)
|
||||
if m is None:
|
||||
m = self._if_defined.match(sline)
|
||||
if m is None:
|
||||
m = self._if_true.match(sline)
|
||||
if_true = (m is not None)
|
||||
if m is None:
|
||||
outlines.append(line)
|
||||
idx += 1
|
||||
continue
|
||||
|
||||
groups = m.groupdict()
|
||||
macro = groups['macro']
|
||||
op = groups.get('op')
|
||||
|
||||
if not (macro in self._defs or macro in self._undefs):
|
||||
outlines.append(line)
|
||||
idx += 1
|
||||
continue
|
||||
|
||||
defined = macro in self._defs
|
||||
|
||||
# Needed variables set:
|
||||
# resolved: Is the statement fully resolved?
|
||||
# is_true: If resolved, is the statement true?
|
||||
ifdef = False
|
||||
if if_true:
|
||||
if not defined:
|
||||
outlines.append(line)
|
||||
idx += 1
|
||||
continue
|
||||
|
||||
defined_value = self._defs[macro]
|
||||
is_int = True
|
||||
try:
|
||||
defined_value = int(defined_value)
|
||||
except TypeError:
|
||||
is_int = False
|
||||
except ValueError:
|
||||
is_int = False
|
||||
|
||||
resolved = is_int
|
||||
is_true = (defined_value != 0)
|
||||
|
||||
if resolved and op is not None:
|
||||
if op == '&&':
|
||||
resolved = not is_true
|
||||
else:
|
||||
assert op == '||'
|
||||
resolved = is_true
|
||||
|
||||
else:
|
||||
ifdef = groups.get('not') is None
|
||||
elseif = groups.get('elif') is not None
|
||||
|
||||
macro2 = groups.get('macro2')
|
||||
cmp = groups.get('cmp')
|
||||
value = groups.get('value')
|
||||
openp = groups.get('openp')
|
||||
closep = groups.get('closep')
|
||||
|
||||
is_true = (ifdef == defined)
|
||||
resolved = True
|
||||
if op is not None:
|
||||
if op == '&&':
|
||||
resolved = not is_true
|
||||
else:
|
||||
assert op == '||'
|
||||
resolved = is_true
|
||||
|
||||
if macro2 is not None and not resolved:
|
||||
assert ifdef and defined and op == '&&' and cmp is not None
|
||||
# If the statment is true, but we have a single value check, then
|
||||
# check the value.
|
||||
defined_value = self._defs[macro]
|
||||
are_ints = True
|
||||
try:
|
||||
defined_value = int(defined_value)
|
||||
value = int(value)
|
||||
except TypeError:
|
||||
are_ints = False
|
||||
except ValueError:
|
||||
are_ints = False
|
||||
if (
|
||||
macro == macro2 and
|
||||
((openp is None) == (closep is None)) and
|
||||
are_ints
|
||||
):
|
||||
resolved = True
|
||||
if cmp == '<':
|
||||
is_true = defined_value < value
|
||||
elif cmp == '<=':
|
||||
is_true = defined_value <= value
|
||||
elif cmp == '==':
|
||||
is_true = defined_value == value
|
||||
elif cmp == '!=':
|
||||
is_true = defined_value != value
|
||||
elif cmp == '>=':
|
||||
is_true = defined_value >= value
|
||||
elif cmp == '>':
|
||||
is_true = defined_value > value
|
||||
else:
|
||||
resolved = False
|
||||
|
||||
if op is not None and not resolved:
|
||||
# Remove the first op in the line + spaces
|
||||
if op == '&&':
|
||||
opre = op
|
||||
else:
|
||||
assert op == '||'
|
||||
opre = r'\|\|'
|
||||
needle = re.compile(fr"(?P<if>\s*#\s*(el)?if\s+).*?(?P<op>{opre}\s*)")
|
||||
match = needle.match(line)
|
||||
assert match is not None
|
||||
newline = line[:match.end('if')] + line[match.end('op'):]
|
||||
|
||||
self._log(f"\tHardwiring partially resolved {macro}")
|
||||
self._log(f"\t\t- {line[:-1]}")
|
||||
self._log(f"\t\t+ {newline[:-1]}")
|
||||
|
||||
outlines.append(newline)
|
||||
idx += 1
|
||||
continue
|
||||
|
||||
# Skip any statements we cannot fully compute
|
||||
if not resolved:
|
||||
outlines.append(line)
|
||||
idx += 1
|
||||
continue
|
||||
|
||||
prepend = []
|
||||
if macro in self._replaces:
|
||||
assert not ifdef
|
||||
assert op is None
|
||||
value = self._replaces.pop(macro)
|
||||
prepend = [f"#define {macro} {value}\n"]
|
||||
|
||||
idx, replace = self._handle_if_block(macro, idx, is_true, prepend)
|
||||
outlines += replace
|
||||
changed = True
|
||||
|
||||
return changed, outlines
|
||||
|
||||
def preprocess(self, filename):
|
||||
with open(filename, 'r') as f:
|
||||
self._inlines = f.readlines()
|
||||
changed = True
|
||||
iters = 0
|
||||
while changed:
|
||||
iters += 1
|
||||
changed, outlines = self._preprocess_once()
|
||||
self._inlines = outlines
|
||||
|
||||
with open(filename, 'w') as f:
|
||||
f.write(''.join(self._inlines))
|
||||
|
||||
|
||||
class Freestanding(object):
|
||||
def __init__(
|
||||
self, zstd_deps: str, mem: str, source_lib: str, output_lib: str,
|
||||
external_xxhash: bool, xxh64_state: Optional[str],
|
||||
xxh64_prefix: Optional[str], rewritten_includes: [(str, str)],
|
||||
defs: [(str, Optional[str])], replaces: [(str, str)],
|
||||
undefs: [str], excludes: [str]
|
||||
):
|
||||
self._zstd_deps = zstd_deps
|
||||
self._mem = mem
|
||||
self._src_lib = source_lib
|
||||
self._dst_lib = output_lib
|
||||
self._external_xxhash = external_xxhash
|
||||
self._xxh64_state = xxh64_state
|
||||
self._xxh64_prefix = xxh64_prefix
|
||||
self._rewritten_includes = rewritten_includes
|
||||
self._defs = defs
|
||||
self._replaces = replaces
|
||||
self._undefs = undefs
|
||||
self._excludes = excludes
|
||||
|
||||
def _dst_lib_file_paths(self):
|
||||
"""
|
||||
Yields all the file paths in the dst_lib.
|
||||
"""
|
||||
for root, dirname, filenames in os.walk(self._dst_lib):
|
||||
for filename in filenames:
|
||||
filepath = os.path.join(root, filename)
|
||||
yield filepath
|
||||
|
||||
def _log(self, *args, **kwargs):
|
||||
print(*args, **kwargs)
|
||||
|
||||
def _copy_file(self, lib_path):
|
||||
if not (lib_path.endswith(".c") or lib_path.endswith(".h")):
|
||||
return
|
||||
if lib_path in SKIPPED_FILES:
|
||||
self._log(f"\tSkipping file: {lib_path}")
|
||||
return
|
||||
if self._external_xxhash and lib_path in XXHASH_FILES:
|
||||
self._log(f"\tSkipping xxhash file: {lib_path}")
|
||||
return
|
||||
|
||||
src_path = os.path.join(self._src_lib, lib_path)
|
||||
dst_path = os.path.join(self._dst_lib, lib_path)
|
||||
self._log(f"\tCopying: {src_path} -> {dst_path}")
|
||||
shutil.copyfile(src_path, dst_path)
|
||||
|
||||
def _copy_source_lib(self):
|
||||
self._log("Copying source library into output library")
|
||||
|
||||
assert os.path.exists(self._src_lib)
|
||||
os.makedirs(self._dst_lib, exist_ok=True)
|
||||
self._copy_file("zstd.h")
|
||||
for subdir in INCLUDED_SUBDIRS:
|
||||
src_dir = os.path.join(self._src_lib, subdir)
|
||||
dst_dir = os.path.join(self._dst_lib, subdir)
|
||||
|
||||
assert os.path.exists(src_dir)
|
||||
os.makedirs(dst_dir, exist_ok=True)
|
||||
|
||||
for filename in os.listdir(src_dir):
|
||||
lib_path = os.path.join(subdir, filename)
|
||||
self._copy_file(lib_path)
|
||||
|
||||
def _copy_zstd_deps(self):
|
||||
dst_zstd_deps = os.path.join(self._dst_lib, "common", "zstd_deps.h")
|
||||
self._log(f"Copying zstd_deps: {self._zstd_deps} -> {dst_zstd_deps}")
|
||||
shutil.copyfile(self._zstd_deps, dst_zstd_deps)
|
||||
|
||||
def _copy_mem(self):
|
||||
dst_mem = os.path.join(self._dst_lib, "common", "mem.h")
|
||||
self._log(f"Copying mem: {self._mem} -> {dst_mem}")
|
||||
shutil.copyfile(self._mem, dst_mem)
|
||||
|
||||
def _hardwire_preprocessor(self, name: str, value: Optional[str] = None, undef=False):
|
||||
"""
|
||||
If value=None then hardwire that it is defined, but not what the value is.
|
||||
If undef=True then value must be None.
|
||||
If value='' then the macro is defined to '' exactly.
|
||||
"""
|
||||
assert not (undef and value is not None)
|
||||
for filepath in self._dst_lib_file_paths():
|
||||
file = FileLines(filepath)
|
||||
|
||||
def _hardwire_defines(self):
|
||||
self._log("Hardwiring macros")
|
||||
partial_preprocessor = PartialPreprocessor(self._defs, self._replaces, self._undefs)
|
||||
for filepath in self._dst_lib_file_paths():
|
||||
partial_preprocessor.preprocess(filepath)
|
||||
|
||||
def _remove_excludes(self):
|
||||
self._log("Removing excluded sections")
|
||||
for exclude in self._excludes:
|
||||
self._log(f"\tRemoving excluded sections for: {exclude}")
|
||||
begin_re = re.compile(f"BEGIN {exclude}")
|
||||
end_re = re.compile(f"END {exclude}")
|
||||
for filepath in self._dst_lib_file_paths():
|
||||
file = FileLines(filepath)
|
||||
outlines = []
|
||||
skipped = []
|
||||
emit = True
|
||||
for line in file.lines:
|
||||
if emit and begin_re.search(line) is not None:
|
||||
assert end_re.search(line) is None
|
||||
emit = False
|
||||
if emit:
|
||||
outlines.append(line)
|
||||
else:
|
||||
skipped.append(line)
|
||||
if end_re.search(line) is not None:
|
||||
assert begin_re.search(line) is None
|
||||
self._log(f"\t\tRemoving excluded section: {exclude}")
|
||||
for s in skipped:
|
||||
self._log(f"\t\t\t- {s}")
|
||||
emit = True
|
||||
skipped = []
|
||||
if not emit:
|
||||
raise RuntimeError("Excluded section unfinished!")
|
||||
file.lines = outlines
|
||||
file.write()
|
||||
|
||||
def _rewrite_include(self, original, rewritten):
|
||||
self._log(f"\tRewriting include: {original} -> {rewritten}")
|
||||
regex = re.compile(f"\\s*#\\s*include\\s*(?P<include>{original})")
|
||||
for filepath in self._dst_lib_file_paths():
|
||||
file = FileLines(filepath)
|
||||
for i, line in enumerate(file.lines):
|
||||
match = regex.match(line)
|
||||
if match is None:
|
||||
continue
|
||||
s = match.start('include')
|
||||
e = match.end('include')
|
||||
file.lines[i] = line[:s] + rewritten + line[e:]
|
||||
file.write()
|
||||
|
||||
def _rewrite_includes(self):
|
||||
self._log("Rewriting includes")
|
||||
for original, rewritten in self._rewritten_includes:
|
||||
self._rewrite_include(original, rewritten)
|
||||
|
||||
def _replace_xxh64_prefix(self):
|
||||
if self._xxh64_prefix is None:
|
||||
return
|
||||
self._log(f"Replacing XXH64 prefix with {self._xxh64_prefix}")
|
||||
replacements = []
|
||||
if self._xxh64_state is not None:
|
||||
replacements.append(
|
||||
(re.compile(r"([^\w]|^)(?P<orig>XXH64_state_t)([^\w]|$)"), self._xxh64_state)
|
||||
)
|
||||
if self._xxh64_prefix is not None:
|
||||
replacements.append(
|
||||
(re.compile(r"([^\w]|^)(?P<orig>XXH64)_"), self._xxh64_prefix)
|
||||
)
|
||||
for filepath in self._dst_lib_file_paths():
|
||||
file = FileLines(filepath)
|
||||
for i, line in enumerate(file.lines):
|
||||
modified = False
|
||||
for regex, replacement in replacements:
|
||||
match = regex.search(line)
|
||||
while match is not None:
|
||||
modified = True
|
||||
b = match.start('orig')
|
||||
e = match.end('orig')
|
||||
line = line[:b] + replacement + line[e:]
|
||||
match = regex.search(line)
|
||||
if modified:
|
||||
self._log(f"\t- {file.lines[i][:-1]}")
|
||||
self._log(f"\t+ {line[:-1]}")
|
||||
file.lines[i] = line
|
||||
file.write()
|
||||
|
||||
def go(self):
|
||||
self._copy_source_lib()
|
||||
self._copy_zstd_deps()
|
||||
self._copy_mem()
|
||||
self._hardwire_defines()
|
||||
self._remove_excludes()
|
||||
self._rewrite_includes()
|
||||
self._replace_xxh64_prefix()
|
||||
|
||||
|
||||
def parse_optional_pair(defines: [str]) -> [(str, Optional[str])]:
|
||||
output = []
|
||||
for define in defines:
|
||||
parsed = define.split('=')
|
||||
if len(parsed) == 1:
|
||||
output.append((parsed[0], None))
|
||||
elif len(parsed) == 2:
|
||||
output.append((parsed[0], parsed[1]))
|
||||
else:
|
||||
raise RuntimeError(f"Bad define: {define}")
|
||||
return output
|
||||
|
||||
|
||||
def parse_pair(rewritten_includes: [str]) -> [(str, str)]:
|
||||
output = []
|
||||
for rewritten_include in rewritten_includes:
|
||||
parsed = rewritten_include.split('=')
|
||||
if len(parsed) == 2:
|
||||
output.append((parsed[0], parsed[1]))
|
||||
else:
|
||||
raise RuntimeError(f"Bad rewritten include: {rewritten_include}")
|
||||
return output
|
||||
|
||||
|
||||
|
||||
def main(name, args):
|
||||
parser = argparse.ArgumentParser(prog=name)
|
||||
parser.add_argument("--zstd-deps", default="zstd_deps.h", help="Zstd dependencies file")
|
||||
parser.add_argument("--mem", default="mem.h", help="Memory module")
|
||||
parser.add_argument("--source-lib", default="../../lib", help="Location of the zstd library")
|
||||
parser.add_argument("--output-lib", default="./freestanding_lib", help="Where to output the freestanding zstd library")
|
||||
parser.add_argument("--xxhash", default=None, help="Alternate external xxhash include e.g. --xxhash='<xxhash.h>'. If set xxhash is not included.")
|
||||
parser.add_argument("--xxh64-state", default=None, help="Alternate XXH64 state type (excluding _) e.g. --xxh64-state='struct xxh64_state'")
|
||||
parser.add_argument("--xxh64-prefix", default=None, help="Alternate XXH64 function prefix (excluding _) e.g. --xxh64-prefix=xxh64")
|
||||
parser.add_argument("--rewrite-include", default=[], dest="rewritten_includes", action="append", help="Rewrite an include REGEX=NEW (e.g. '<stddef\\.h>=<linux/types.h>')")
|
||||
parser.add_argument("-D", "--define", default=[], dest="defs", action="append", help="Pre-define this macro (can be passed multiple times)")
|
||||
parser.add_argument("-U", "--undefine", default=[], dest="undefs", action="append", help="Pre-undefine this macro (can be passed mutliple times)")
|
||||
parser.add_argument("-R", "--replace", default=[], dest="replaces", action="append", help="Pre-define this macro and replace the first ifndef block with its definition")
|
||||
parser.add_argument("-E", "--exclude", default=[], dest="excludes", action="append", help="Exclude all lines between 'BEGIN <EXCLUDE>' and 'END <EXCLUDE>'")
|
||||
args = parser.parse_args(args)
|
||||
|
||||
# Always remove threading
|
||||
if "ZSTD_MULTITHREAD" not in args.undefs:
|
||||
args.undefs.append("ZSTD_MULTITHREAD")
|
||||
|
||||
args.defs = parse_optional_pair(args.defs)
|
||||
for name, _ in args.defs:
|
||||
if name in args.undefs:
|
||||
raise RuntimeError(f"{name} is both defined and undefined!")
|
||||
|
||||
args.replaces = parse_pair(args.replaces)
|
||||
for name, _ in args.replaces:
|
||||
if name in args.undefs or name in args.defs:
|
||||
raise RuntimeError(f"{name} is both replaced and (un)defined!")
|
||||
|
||||
args.rewritten_includes = parse_pair(args.rewritten_includes)
|
||||
|
||||
external_xxhash = False
|
||||
if args.xxhash is not None:
|
||||
external_xxhash = True
|
||||
args.rewritten_includes.append(('"(\\.\\./common/)?xxhash.h"', args.xxhash))
|
||||
|
||||
if args.xxh64_prefix is not None:
|
||||
if not external_xxhash:
|
||||
raise RuntimeError("--xxh64-prefix may only be used with --xxhash provided")
|
||||
|
||||
if args.xxh64_state is not None:
|
||||
if not external_xxhash:
|
||||
raise RuntimeError("--xxh64-state may only be used with --xxhash provided")
|
||||
|
||||
Freestanding(
|
||||
args.zstd_deps,
|
||||
args.mem,
|
||||
args.source_lib,
|
||||
args.output_lib,
|
||||
external_xxhash,
|
||||
args.xxh64_state,
|
||||
args.xxh64_prefix,
|
||||
args.rewritten_includes,
|
||||
args.defs,
|
||||
args.replaces,
|
||||
args.undefs,
|
||||
args.excludes
|
||||
).go()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv[0], sys.argv[1:])
|
||||
@@ -156,18 +156,6 @@ createDictionaryBuffer(const char* dictionaryName,
|
||||
}
|
||||
}
|
||||
|
||||
static ZSTD_CDict* createCDictForDedicatedDictSearch(const void* dict, size_t dictSize, int compressionLevel)
|
||||
{
|
||||
ZSTD_CCtx_params* params = ZSTD_createCCtxParams();
|
||||
ZSTD_CCtxParams_init(params, compressionLevel);
|
||||
ZSTD_CCtxParams_setParameter(params, ZSTD_c_enableDedicatedDictSearch, 1);
|
||||
ZSTD_CCtxParams_setParameter(params, ZSTD_c_compressionLevel, compressionLevel);
|
||||
|
||||
ZSTD_CDict* cdict = ZSTD_createCDict_advanced2(dict, dictSize, ZSTD_dlm_byCopy, ZSTD_dct_auto, params, ZSTD_defaultCMem);
|
||||
|
||||
ZSTD_freeCCtxParams(params);
|
||||
return cdict;
|
||||
}
|
||||
|
||||
/*! BMK_loadFiles() :
|
||||
* Loads `buffer`, with content from files listed within `fileNamesTable`.
|
||||
@@ -346,42 +334,6 @@ createBufferCollection_fromSliceCollectionSizes(slice_collection_t sc)
|
||||
return result;
|
||||
}
|
||||
|
||||
static buffer_collection_t
|
||||
createBufferCollection_fromSliceCollection(slice_collection_t sc)
|
||||
{
|
||||
size_t const bufferSize = sliceCollection_totalCapacity(sc);
|
||||
|
||||
buffer_t buffer = createBuffer(bufferSize);
|
||||
CONTROL(buffer.ptr != NULL);
|
||||
|
||||
size_t const nbSlices = sc.nbSlices;
|
||||
void** const slices = (void**)malloc(nbSlices * sizeof(*slices));
|
||||
CONTROL(slices != NULL);
|
||||
|
||||
size_t* const capacities = (size_t*)malloc(nbSlices * sizeof(*capacities));
|
||||
CONTROL(capacities != NULL);
|
||||
|
||||
char* const ptr = (char*)buffer.ptr;
|
||||
size_t pos = 0;
|
||||
for (size_t n=0; n < nbSlices; n++) {
|
||||
capacities[n] = sc.capacities[n];
|
||||
slices[n] = ptr + pos;
|
||||
pos += capacities[n];
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < nbSlices; i++) {
|
||||
memcpy(slices[i], sc.slicePtrs[i], sc.capacities[i]);
|
||||
capacities[i] = sc.capacities[i];
|
||||
}
|
||||
|
||||
buffer_collection_t result;
|
||||
result.buffer = buffer;
|
||||
result.slices.nbSlices = nbSlices;
|
||||
result.slices.capacities = capacities;
|
||||
result.slices.slicePtrs = slices;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* @return : kBuffNull if any error */
|
||||
static buffer_collection_t
|
||||
@@ -445,38 +397,6 @@ typedef struct {
|
||||
size_t nbDDict;
|
||||
} ddict_collection_t;
|
||||
|
||||
typedef struct {
|
||||
ZSTD_CDict** cdicts;
|
||||
size_t nbCDict;
|
||||
} cdict_collection_t;
|
||||
|
||||
static const cdict_collection_t kNullCDictCollection = { NULL, 0 };
|
||||
|
||||
static void freeCDictCollection(cdict_collection_t cdictc)
|
||||
{
|
||||
for (size_t dictNb=0; dictNb < cdictc.nbCDict; dictNb++) {
|
||||
ZSTD_freeCDict(cdictc.cdicts[dictNb]);
|
||||
}
|
||||
free(cdictc.cdicts);
|
||||
}
|
||||
|
||||
/* returns .buffers=NULL if operation fails */
|
||||
static cdict_collection_t createCDictCollection(const void* dictBuffer, size_t dictSize, size_t nbCDict, int cLevel, int dedicatedDictSearch)
|
||||
{
|
||||
ZSTD_CDict** const cdicts = malloc(nbCDict * sizeof(ZSTD_CDict*));
|
||||
if (cdicts==NULL) return kNullCDictCollection;
|
||||
for (size_t dictNb=0; dictNb < nbCDict; dictNb++) {
|
||||
cdicts[dictNb] = dedicatedDictSearch ?
|
||||
createCDictForDedicatedDictSearch(dictBuffer, dictSize, cLevel) :
|
||||
ZSTD_createCDict(dictBuffer, dictSize, cLevel);
|
||||
CONTROL(cdicts[dictNb] != NULL);
|
||||
}
|
||||
cdict_collection_t cdictc;
|
||||
cdictc.cdicts = cdicts;
|
||||
cdictc.nbCDict = nbCDict;
|
||||
return cdictc;
|
||||
}
|
||||
|
||||
static const ddict_collection_t kNullDDictCollection = { NULL, 0 };
|
||||
|
||||
static void freeDDictCollection(ddict_collection_t ddictc)
|
||||
@@ -505,26 +425,7 @@ static ddict_collection_t createDDictCollection(const void* dictBuffer, size_t d
|
||||
|
||||
|
||||
/* mess with addresses, so that linear scanning dictionaries != linear address scanning */
|
||||
void shuffleCDictionaries(cdict_collection_t dicts)
|
||||
{
|
||||
size_t const nbDicts = dicts.nbCDict;
|
||||
for (size_t r=0; r<nbDicts; r++) {
|
||||
size_t const d = (size_t)rand() % nbDicts;
|
||||
ZSTD_CDict* tmpd = dicts.cdicts[d];
|
||||
dicts.cdicts[d] = dicts.cdicts[r];
|
||||
dicts.cdicts[r] = tmpd;
|
||||
}
|
||||
for (size_t r=0; r<nbDicts; r++) {
|
||||
size_t const d1 = (size_t)rand() % nbDicts;
|
||||
size_t const d2 = (size_t)rand() % nbDicts;
|
||||
ZSTD_CDict* tmpd = dicts.cdicts[d1];
|
||||
dicts.cdicts[d1] = dicts.cdicts[d2];
|
||||
dicts.cdicts[d2] = tmpd;
|
||||
}
|
||||
}
|
||||
|
||||
/* mess with addresses, so that linear scanning dictionaries != linear address scanning */
|
||||
void shuffleDDictionaries(ddict_collection_t dicts)
|
||||
void shuffleDictionaries(ddict_collection_t dicts)
|
||||
{
|
||||
size_t const nbDicts = dicts.nbDDict;
|
||||
for (size_t r=0; r<nbDicts; r++) {
|
||||
@@ -584,29 +485,6 @@ static size_t compressBlocks(size_t* cSizes, /* optional (can be NULL). If pre
|
||||
|
||||
/* --- Benchmark --- */
|
||||
|
||||
typedef struct {
|
||||
ZSTD_CCtx* cctx;
|
||||
size_t nbDicts;
|
||||
size_t dictNb;
|
||||
cdict_collection_t dictionaries;
|
||||
} compressInstructions;
|
||||
|
||||
compressInstructions createCompressInstructions(cdict_collection_t dictionaries)
|
||||
{
|
||||
compressInstructions ci;
|
||||
ci.cctx = ZSTD_createCCtx();
|
||||
CONTROL(ci.cctx != NULL);
|
||||
ci.nbDicts = dictionaries.nbCDict;
|
||||
ci.dictNb = 0;
|
||||
ci.dictionaries = dictionaries;
|
||||
return ci;
|
||||
}
|
||||
|
||||
void freeCompressInstructions(compressInstructions ci)
|
||||
{
|
||||
ZSTD_freeCCtx(ci.cctx);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
ZSTD_DCtx* dctx;
|
||||
size_t nbDicts;
|
||||
@@ -630,23 +508,6 @@ void freeDecompressInstructions(decompressInstructions di)
|
||||
ZSTD_freeDCtx(di.dctx);
|
||||
}
|
||||
|
||||
/* benched function */
|
||||
size_t compress(const void* src, size_t srcSize, void* dst, size_t dstCapacity, void* payload)
|
||||
{
|
||||
compressInstructions* const ci = (compressInstructions*) payload;
|
||||
(void)dstCapacity;
|
||||
|
||||
ZSTD_compress_usingCDict(ci->cctx,
|
||||
dst, srcSize,
|
||||
src, srcSize,
|
||||
ci->dictionaries.cdicts[ci->dictNb]);
|
||||
|
||||
ci->dictNb = ci->dictNb + 1;
|
||||
if (ci->dictNb >= ci->nbDicts) ci->dictNb = 0;
|
||||
|
||||
return srcSize;
|
||||
}
|
||||
|
||||
/* benched function */
|
||||
size_t decompress(const void* src, size_t srcSize, void* dst, size_t dstCapacity, void* payload)
|
||||
{
|
||||
@@ -666,9 +527,8 @@ size_t decompress(const void* src, size_t srcSize, void* dst, size_t dstCapacity
|
||||
|
||||
static int benchMem(slice_collection_t dstBlocks,
|
||||
slice_collection_t srcBlocks,
|
||||
ddict_collection_t ddictionaries,
|
||||
cdict_collection_t cdictionaries,
|
||||
unsigned nbRounds, int benchCompression)
|
||||
ddict_collection_t dictionaries,
|
||||
unsigned nbRounds)
|
||||
{
|
||||
assert(dstBlocks.nbSlices == srcBlocks.nbSlices);
|
||||
|
||||
@@ -679,13 +539,10 @@ static int benchMem(slice_collection_t dstBlocks,
|
||||
|
||||
BMK_timedFnState_t* const benchState =
|
||||
BMK_createTimedFnState(total_time_ms, ms_per_round);
|
||||
|
||||
decompressInstructions di = createDecompressInstructions(ddictionaries);
|
||||
compressInstructions ci = createCompressInstructions(cdictionaries);
|
||||
void* payload = benchCompression ? (void*)&ci : (void*)&di;
|
||||
decompressInstructions di = createDecompressInstructions(dictionaries);
|
||||
BMK_benchParams_t const bp = {
|
||||
.benchFn = benchCompression ? compress : decompress,
|
||||
.benchPayload = payload,
|
||||
.benchFn = decompress,
|
||||
.benchPayload = &di,
|
||||
.initFn = NULL,
|
||||
.initPayload = NULL,
|
||||
.errorFn = ZSTD_isError,
|
||||
@@ -705,20 +562,15 @@ static int benchMem(slice_collection_t dstBlocks,
|
||||
double const dTime_ns = result.nanoSecPerRun;
|
||||
double const dTime_sec = (double)dTime_ns / 1000000000;
|
||||
size_t const srcSize = result.sumOfReturn;
|
||||
double const speed_MBps = (double)srcSize / dTime_sec / (1 MB);
|
||||
if (speed_MBps > bestSpeed) bestSpeed = speed_MBps;
|
||||
if (benchCompression)
|
||||
DISPLAY("Compression Speed : %.1f MB/s \r", bestSpeed);
|
||||
else
|
||||
DISPLAY("Decompression Speed : %.1f MB/s \r", bestSpeed);
|
||||
|
||||
double const dSpeed_MBps = (double)srcSize / dTime_sec / (1 MB);
|
||||
if (dSpeed_MBps > bestSpeed) bestSpeed = dSpeed_MBps;
|
||||
DISPLAY("Decompression Speed : %.1f MB/s \r", bestSpeed);
|
||||
fflush(stdout);
|
||||
if (BMK_isCompleted_TimedFn(benchState)) break;
|
||||
}
|
||||
DISPLAY("\n");
|
||||
|
||||
freeDecompressInstructions(di);
|
||||
freeCompressInstructions(ci);
|
||||
BMK_freeTimedFnState(benchState);
|
||||
|
||||
return 0; /* success */
|
||||
@@ -734,8 +586,7 @@ int bench(const char** fileNameTable, unsigned nbFiles,
|
||||
const char* dictionary,
|
||||
size_t blockSize, int clevel,
|
||||
unsigned nbDictMax, unsigned nbBlocks,
|
||||
unsigned nbRounds, int benchCompression,
|
||||
int dedicatedDictSearch)
|
||||
unsigned nbRounds)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
@@ -790,9 +641,7 @@ int bench(const char** fileNameTable, unsigned nbFiles,
|
||||
DICTSIZE);
|
||||
CONTROL(dictBuffer.ptr != NULL);
|
||||
|
||||
ZSTD_CDict* const cdict = dedicatedDictSearch ?
|
||||
createCDictForDedicatedDictSearch(dictBuffer.ptr, dictBuffer.size, clevel) :
|
||||
ZSTD_createCDict(dictBuffer.ptr, dictBuffer.size, clevel);
|
||||
ZSTD_CDict* const cdict = ZSTD_createCDict(dictBuffer.ptr, dictBuffer.size, clevel);
|
||||
CONTROL(cdict != NULL);
|
||||
|
||||
size_t const cTotalSizeNoDict = compressBlocks(NULL, dstSlices, srcSlices, NULL, clevel);
|
||||
@@ -813,47 +662,25 @@ int bench(const char** fileNameTable, unsigned nbFiles,
|
||||
/* now dstSlices contain the real compressed size of each block, instead of the maximum capacity */
|
||||
shrinkSizes(dstSlices, cSizes);
|
||||
|
||||
size_t const dictMem = ZSTD_estimateDDictSize(dictBuffer.size, ZSTD_dlm_byCopy);
|
||||
unsigned const nbDicts = nbDictMax ? nbDictMax : nbBlocks;
|
||||
size_t const allDictMem = dictMem * nbDicts;
|
||||
DISPLAYLEVEL(3, "generating %u dictionaries, using %.1f MB of memory \n",
|
||||
nbDicts, (double)allDictMem / (1 MB));
|
||||
|
||||
cdict_collection_t const cdictionaries = createCDictCollection(dictBuffer.ptr, dictBuffer.size, nbDicts, clevel, dedicatedDictSearch);
|
||||
CONTROL(cdictionaries.cdicts != NULL);
|
||||
ddict_collection_t const dictionaries = createDDictCollection(dictBuffer.ptr, dictBuffer.size, nbDicts);
|
||||
CONTROL(dictionaries.ddicts != NULL);
|
||||
|
||||
ddict_collection_t const ddictionaries = createDDictCollection(dictBuffer.ptr, dictBuffer.size, nbDicts);
|
||||
CONTROL(ddictionaries.ddicts != NULL);
|
||||
shuffleDictionaries(dictionaries);
|
||||
|
||||
if (benchCompression) {
|
||||
size_t const dictMem = ZSTD_estimateCDictSize(dictBuffer.size, ZSTD_dlm_byCopy);
|
||||
size_t const allDictMem = dictMem * nbDicts;
|
||||
DISPLAYLEVEL(3, "generating %u dictionaries, using %.1f MB of memory \n",
|
||||
nbDicts, (double)allDictMem / (1 MB));
|
||||
buffer_collection_t resultCollection = createBufferCollection_fromSliceCollectionSizes(srcSlices);
|
||||
CONTROL(resultCollection.buffer.ptr != NULL);
|
||||
|
||||
shuffleCDictionaries(cdictionaries);
|
||||
|
||||
buffer_collection_t resultCollection = createBufferCollection_fromSliceCollection(srcSlices);
|
||||
CONTROL(resultCollection.buffer.ptr != NULL);
|
||||
|
||||
result = benchMem(dstSlices, resultCollection.slices, ddictionaries, cdictionaries, nbRounds, benchCompression);
|
||||
|
||||
freeBufferCollection(resultCollection);
|
||||
} else {
|
||||
size_t const dictMem = ZSTD_estimateDDictSize(dictBuffer.size, ZSTD_dlm_byCopy);
|
||||
size_t const allDictMem = dictMem * nbDicts;
|
||||
DISPLAYLEVEL(3, "generating %u dictionaries, using %.1f MB of memory \n",
|
||||
nbDicts, (double)allDictMem / (1 MB));
|
||||
|
||||
shuffleDDictionaries(ddictionaries);
|
||||
|
||||
buffer_collection_t resultCollection = createBufferCollection_fromSliceCollectionSizes(srcSlices);
|
||||
CONTROL(resultCollection.buffer.ptr != NULL);
|
||||
|
||||
result = benchMem(resultCollection.slices, dstSlices, ddictionaries, cdictionaries, nbRounds, benchCompression);
|
||||
|
||||
freeBufferCollection(resultCollection);
|
||||
}
|
||||
result = benchMem(resultCollection.slices, dstSlices, dictionaries, nbRounds);
|
||||
|
||||
/* free all heap objects in reverse order */
|
||||
freeCDictCollection(cdictionaries);
|
||||
freeDDictCollection(ddictionaries);
|
||||
freeBufferCollection(resultCollection);
|
||||
freeDDictCollection(dictionaries);
|
||||
free(cSizes);
|
||||
ZSTD_freeCDict(cdict);
|
||||
freeBuffer(dictBuffer);
|
||||
@@ -917,8 +744,6 @@ int usage(const char* exeName)
|
||||
DISPLAY (" %s [Options] filename(s) \n", exeName);
|
||||
DISPLAY (" \n");
|
||||
DISPLAY ("Options : \n");
|
||||
DISPLAY ("-z : benchmark compression (default) \n");
|
||||
DISPLAY ("-d : benchmark decompression \n");
|
||||
DISPLAY ("-r : recursively load all files in subdirectories (default: off) \n");
|
||||
DISPLAY ("-B# : split input into blocks of size # (default: no split) \n");
|
||||
DISPLAY ("-# : use compression level # (default: %u) \n", CLEVEL_DEFAULT);
|
||||
@@ -940,8 +765,6 @@ int bad_usage(const char* exeName)
|
||||
int main (int argc, const char** argv)
|
||||
{
|
||||
int recursiveMode = 0;
|
||||
int benchCompression = 1;
|
||||
int dedicatedDictSearch = 0;
|
||||
unsigned nbRounds = BENCH_TIME_DEFAULT_S;
|
||||
const char* const exeName = argv[0];
|
||||
|
||||
@@ -960,8 +783,6 @@ int main (int argc, const char** argv)
|
||||
for (int argNb = 1; argNb < argc ; argNb++) {
|
||||
const char* argument = argv[argNb];
|
||||
if (!strcmp(argument, "-h")) { free(nameTable); return usage(exeName); }
|
||||
if (!strcmp(argument, "-d")) { benchCompression = 0; continue; }
|
||||
if (!strcmp(argument, "-z")) { benchCompression = 1; continue; }
|
||||
if (!strcmp(argument, "-r")) { recursiveMode = 1; continue; }
|
||||
if (!strcmp(argument, "-D")) { argNb++; assert(argNb < argc); dictionary = argv[argNb]; continue; }
|
||||
if (longCommandWArg(&argument, "-i")) { nbRounds = readU32FromChar(&argument); continue; }
|
||||
@@ -971,7 +792,6 @@ int main (int argc, const char** argv)
|
||||
if (longCommandWArg(&argument, "--nbDicts=")) { nbDicts = readU32FromChar(&argument); continue; }
|
||||
if (longCommandWArg(&argument, "--nbBlocks=")) { nbBlocks = readU32FromChar(&argument); continue; }
|
||||
if (longCommandWArg(&argument, "--clevel=")) { cLevel = (int)readU32FromChar(&argument); continue; }
|
||||
if (longCommandWArg(&argument, "--dedicated-dict-search")) { dedicatedDictSearch = 1; continue; }
|
||||
if (longCommandWArg(&argument, "-")) { cLevel = (int)readU32FromChar(&argument); continue; }
|
||||
/* anything that's not a command is a filename */
|
||||
nameTable[nameIdx++] = argument;
|
||||
@@ -989,7 +809,7 @@ int main (int argc, const char** argv)
|
||||
nameTable = NULL; /* UTIL_createFileNamesTable() takes ownership of nameTable */
|
||||
}
|
||||
|
||||
int result = bench(filenameTable->fileNames, (unsigned)filenameTable->tableSize, dictionary, blockSize, cLevel, nbDicts, nbBlocks, nbRounds, benchCompression, dedicatedDictSearch);
|
||||
int result = bench(filenameTable->fileNames, (unsigned)filenameTable->tableSize, dictionary, blockSize, cLevel, nbDicts, nbBlocks, nbRounds);
|
||||
|
||||
UTIL_freeFileNamesTable(filenameTable);
|
||||
free(nameTable);
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
From 308795a7713ca6fcd468b60fba9a2fca99cee6a0 Mon Sep 17 00:00:00 2001
|
||||
From: Nick Terrell <terrelln@fb.com>
|
||||
Date: Tue, 8 Aug 2017 19:20:25 -0700
|
||||
Subject: [PATCH v5 0/5] Add xxhash and zstd modules
|
||||
|
||||
Hi all,
|
||||
|
||||
This patch set adds xxhash, zstd compression, and zstd decompression
|
||||
modules. It also adds zstd support to BtrFS and SquashFS.
|
||||
|
||||
Each patch has relevant summaries, benchmarks, and tests.
|
||||
|
||||
Best,
|
||||
Nick Terrell
|
||||
|
||||
Changelog:
|
||||
|
||||
v1 -> v2:
|
||||
- Make pointer in lib/xxhash.c:394 non-const (1/5)
|
||||
- Use div_u64() for division of u64s (2/5)
|
||||
- Reduce stack usage of ZSTD_compressSequences(), ZSTD_buildSeqTable(),
|
||||
ZSTD_decompressSequencesLong(), FSE_buildDTable(), FSE_decompress_wksp(),
|
||||
HUF_writeCTable(), HUF_readStats(), HUF_readCTable(),
|
||||
HUF_compressWeights(), HUF_readDTableX2(), and HUF_readDTableX4() (2/5)
|
||||
- No zstd function uses more than 400 B of stack space (2/5)
|
||||
|
||||
v2 -> v3:
|
||||
- Work around gcc-7 bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81388
|
||||
(2/5)
|
||||
- Fix bug in dictionary compression from upstream commit cc1522351f (2/5)
|
||||
- Port upstream BtrFS commits e1ddce71d6, 389a6cfc2a, and 6acafd1eff (3/5)
|
||||
- Change default compression level for BtrFS to 3 (3/5)
|
||||
|
||||
v3 -> v4:
|
||||
- Fix compiler warnings (2/5)
|
||||
- Add missing includes (3/5)
|
||||
- Fix minor linter warnings (3/5, 4/5)
|
||||
- Add crypto patch (5/5)
|
||||
|
||||
v4 -> v5:
|
||||
- Fix rare compression bug from upstream commit 308047eb5d (2/5)
|
||||
- Fix bug introduced in v3 when working around the gcc-7 bug (2/5)
|
||||
- Fix ZSTD_DStream initialization code in squashfs (4/5)
|
||||
- Fix patch documentation for patches written by Sean Purcell (4/5)
|
||||
|
||||
Nick Terrell (5):
|
||||
lib: Add xxhash module
|
||||
lib: Add zstd modules
|
||||
btrfs: Add zstd support
|
||||
squashfs: Add zstd support
|
||||
crypto: Add zstd support
|
||||
|
||||
crypto/Kconfig | 9 +
|
||||
crypto/Makefile | 1 +
|
||||
crypto/testmgr.c | 10 +
|
||||
crypto/testmgr.h | 71 +
|
||||
crypto/zstd.c | 265 ++++
|
||||
fs/btrfs/Kconfig | 2 +
|
||||
fs/btrfs/Makefile | 2 +-
|
||||
fs/btrfs/compression.c | 1 +
|
||||
fs/btrfs/compression.h | 6 +-
|
||||
fs/btrfs/ctree.h | 1 +
|
||||
fs/btrfs/disk-io.c | 2 +
|
||||
fs/btrfs/ioctl.c | 6 +-
|
||||
fs/btrfs/props.c | 6 +
|
||||
fs/btrfs/super.c | 12 +-
|
||||
fs/btrfs/sysfs.c | 2 +
|
||||
fs/btrfs/zstd.c | 432 ++++++
|
||||
fs/squashfs/Kconfig | 14 +
|
||||
fs/squashfs/Makefile | 1 +
|
||||
fs/squashfs/decompressor.c | 7 +
|
||||
fs/squashfs/decompressor.h | 4 +
|
||||
fs/squashfs/squashfs_fs.h | 1 +
|
||||
fs/squashfs/zstd_wrapper.c | 151 ++
|
||||
include/linux/xxhash.h | 236 +++
|
||||
include/linux/zstd.h | 1157 +++++++++++++++
|
||||
include/uapi/linux/btrfs.h | 8 +-
|
||||
lib/Kconfig | 11 +
|
||||
lib/Makefile | 3 +
|
||||
lib/xxhash.c | 500 +++++++
|
||||
lib/zstd/Makefile | 18 +
|
||||
lib/zstd/bitstream.h | 374 +++++
|
||||
lib/zstd/compress.c | 3484 ++++++++++++++++++++++++++++++++++++++++++++
|
||||
lib/zstd/decompress.c | 2528 ++++++++++++++++++++++++++++++++
|
||||
lib/zstd/entropy_common.c | 243 +++
|
||||
lib/zstd/error_private.h | 53 +
|
||||
lib/zstd/fse.h | 575 ++++++++
|
||||
lib/zstd/fse_compress.c | 795 ++++++++++
|
||||
lib/zstd/fse_decompress.c | 332 +++++
|
||||
lib/zstd/huf.h | 212 +++
|
||||
lib/zstd/huf_compress.c | 770 ++++++++++
|
||||
lib/zstd/huf_decompress.c | 960 ++++++++++++
|
||||
lib/zstd/mem.h | 151 ++
|
||||
lib/zstd/zstd_common.c | 75 +
|
||||
lib/zstd/zstd_internal.h | 263 ++++
|
||||
lib/zstd/zstd_opt.h | 1014 +++++++++++++
|
||||
44 files changed, 14756 insertions(+), 12 deletions(-)
|
||||
create mode 100644 crypto/zstd.c
|
||||
create mode 100644 fs/btrfs/zstd.c
|
||||
create mode 100644 fs/squashfs/zstd_wrapper.c
|
||||
create mode 100644 include/linux/xxhash.h
|
||||
create mode 100644 include/linux/zstd.h
|
||||
create mode 100644 lib/xxhash.c
|
||||
create mode 100644 lib/zstd/Makefile
|
||||
create mode 100644 lib/zstd/bitstream.h
|
||||
create mode 100644 lib/zstd/compress.c
|
||||
create mode 100644 lib/zstd/decompress.c
|
||||
create mode 100644 lib/zstd/entropy_common.c
|
||||
create mode 100644 lib/zstd/error_private.h
|
||||
create mode 100644 lib/zstd/fse.h
|
||||
create mode 100644 lib/zstd/fse_compress.c
|
||||
create mode 100644 lib/zstd/fse_decompress.c
|
||||
create mode 100644 lib/zstd/huf.h
|
||||
create mode 100644 lib/zstd/huf_compress.c
|
||||
create mode 100644 lib/zstd/huf_decompress.c
|
||||
create mode 100644 lib/zstd/mem.h
|
||||
create mode 100644 lib/zstd/zstd_common.c
|
||||
create mode 100644 lib/zstd/zstd_internal.h
|
||||
create mode 100644 lib/zstd/zstd_opt.h
|
||||
|
||||
--
|
||||
2.9.3
|
||||
@@ -0,0 +1,862 @@
|
||||
From a4b1ffb6e89bbccd519f9afa0910635668436105 Mon Sep 17 00:00:00 2001
|
||||
From: Nick Terrell <terrelln@fb.com>
|
||||
Date: Mon, 17 Jul 2017 17:07:18 -0700
|
||||
Subject: [PATCH v5 1/5] lib: Add xxhash module
|
||||
|
||||
Adds xxhash kernel module with xxh32 and xxh64 hashes. xxhash is an
|
||||
extremely fast non-cryptographic hash algorithm for checksumming.
|
||||
The zstd compression and decompression modules added in the next patch
|
||||
require xxhash. I extracted it out from zstd since it is useful on its
|
||||
own. I copied the code from the upstream XXHash source repository and
|
||||
translated it into kernel style. I ran benchmarks and tests in the kernel
|
||||
and tests in userland.
|
||||
|
||||
I benchmarked xxhash as a special character device. I ran in four modes,
|
||||
no-op, xxh32, xxh64, and crc32. The no-op mode simply copies the data to
|
||||
kernel space and ignores it. The xxh32, xxh64, and crc32 modes compute
|
||||
hashes on the copied data. I also ran it with four different buffer sizes.
|
||||
The benchmark file is located in the upstream zstd source repository under
|
||||
`contrib/linux-kernel/xxhash_test.c` [1].
|
||||
|
||||
I ran the benchmarks on a Ubuntu 14.04 VM with 2 cores and 4 GiB of RAM.
|
||||
The VM is running on a MacBook Pro with a 3.1 GHz Intel Core i7 processor,
|
||||
16 GB of RAM, and a SSD. I benchmarked using the file `filesystem.squashfs`
|
||||
from `ubuntu-16.10-desktop-amd64.iso`, which is 1,536,217,088 B large.
|
||||
Run the following commands for the benchmark:
|
||||
|
||||
modprobe xxhash_test
|
||||
mknod xxhash_test c 245 0
|
||||
time cp filesystem.squashfs xxhash_test
|
||||
|
||||
The time is reported by the time of the userland `cp`.
|
||||
The GB/s is computed with
|
||||
|
||||
1,536,217,008 B / time(buffer size, hash)
|
||||
|
||||
which includes the time to copy from userland.
|
||||
The Normalized GB/s is computed with
|
||||
|
||||
1,536,217,088 B / (time(buffer size, hash) - time(buffer size, none)).
|
||||
|
||||
|
||||
| Buffer Size (B) | Hash | Time (s) | GB/s | Adjusted GB/s |
|
||||
|-----------------|-------|----------|------|---------------|
|
||||
| 1024 | none | 0.408 | 3.77 | - |
|
||||
| 1024 | xxh32 | 0.649 | 2.37 | 6.37 |
|
||||
| 1024 | xxh64 | 0.542 | 2.83 | 11.46 |
|
||||
| 1024 | crc32 | 1.290 | 1.19 | 1.74 |
|
||||
| 4096 | none | 0.380 | 4.04 | - |
|
||||
| 4096 | xxh32 | 0.645 | 2.38 | 5.79 |
|
||||
| 4096 | xxh64 | 0.500 | 3.07 | 12.80 |
|
||||
| 4096 | crc32 | 1.168 | 1.32 | 1.95 |
|
||||
| 8192 | none | 0.351 | 4.38 | - |
|
||||
| 8192 | xxh32 | 0.614 | 2.50 | 5.84 |
|
||||
| 8192 | xxh64 | 0.464 | 3.31 | 13.60 |
|
||||
| 8192 | crc32 | 1.163 | 1.32 | 1.89 |
|
||||
| 16384 | none | 0.346 | 4.43 | - |
|
||||
| 16384 | xxh32 | 0.590 | 2.60 | 6.30 |
|
||||
| 16384 | xxh64 | 0.466 | 3.30 | 12.80 |
|
||||
| 16384 | crc32 | 1.183 | 1.30 | 1.84 |
|
||||
|
||||
Tested in userland using the test-suite in the zstd repo under
|
||||
`contrib/linux-kernel/test/XXHashUserlandTest.cpp` [2] by mocking the
|
||||
kernel functions. A line in each branch of every function in `xxhash.c`
|
||||
was commented out to ensure that the test-suite fails. Additionally
|
||||
tested while testing zstd and with SMHasher [3].
|
||||
|
||||
[1] https://phabricator.intern.facebook.com/P57526246
|
||||
[2] https://github.com/facebook/zstd/blob/dev/contrib/linux-kernel/test/XXHashUserlandTest.cpp
|
||||
[3] https://github.com/aappleby/smhasher
|
||||
|
||||
zstd source repository: https://github.com/facebook/zstd
|
||||
XXHash source repository: https://github.com/cyan4973/xxhash
|
||||
|
||||
Signed-off-by: Nick Terrell <terrelln@fb.com>
|
||||
---
|
||||
v1 -> v2:
|
||||
- Make pointer in lib/xxhash.c:394 non-const
|
||||
|
||||
include/linux/xxhash.h | 236 +++++++++++++++++++++++
|
||||
lib/Kconfig | 3 +
|
||||
lib/Makefile | 1 +
|
||||
lib/xxhash.c | 500 +++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
4 files changed, 740 insertions(+)
|
||||
create mode 100644 include/linux/xxhash.h
|
||||
create mode 100644 lib/xxhash.c
|
||||
|
||||
diff --git a/include/linux/xxhash.h b/include/linux/xxhash.h
|
||||
new file mode 100644
|
||||
index 0000000..9e1f42c
|
||||
--- /dev/null
|
||||
+++ b/include/linux/xxhash.h
|
||||
@@ -0,0 +1,236 @@
|
||||
+/*
|
||||
+ * xxHash - Extremely Fast Hash algorithm
|
||||
+ * Copyright (C) 2012-2016, Yann Collet.
|
||||
+ *
|
||||
+ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
||||
+ *
|
||||
+ * Redistribution and use in source and binary forms, with or without
|
||||
+ * modification, are permitted provided that the following conditions are
|
||||
+ * met:
|
||||
+ *
|
||||
+ * * Redistributions of source code must retain the above copyright
|
||||
+ * notice, this list of conditions and the following disclaimer.
|
||||
+ * * Redistributions in binary form must reproduce the above
|
||||
+ * copyright notice, this list of conditions and the following disclaimer
|
||||
+ * in the documentation and/or other materials provided with the
|
||||
+ * distribution.
|
||||
+ *
|
||||
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify it under
|
||||
+ * the terms of the GNU General Public License version 2 as published by the
|
||||
+ * Free Software Foundation. This program is dual-licensed; you may select
|
||||
+ * either version 2 of the GNU General Public License ("GPL") or BSD license
|
||||
+ * ("BSD").
|
||||
+ *
|
||||
+ * You can contact the author at:
|
||||
+ * - xxHash homepage: http://cyan4973.github.io/xxHash/
|
||||
+ * - xxHash source repository: https://github.com/Cyan4973/xxHash
|
||||
+ */
|
||||
+
|
||||
+/*
|
||||
+ * Notice extracted from xxHash homepage:
|
||||
+ *
|
||||
+ * xxHash is an extremely fast Hash algorithm, running at RAM speed limits.
|
||||
+ * It also successfully passes all tests from the SMHasher suite.
|
||||
+ *
|
||||
+ * Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2
|
||||
+ * Duo @3GHz)
|
||||
+ *
|
||||
+ * Name Speed Q.Score Author
|
||||
+ * xxHash 5.4 GB/s 10
|
||||
+ * CrapWow 3.2 GB/s 2 Andrew
|
||||
+ * MumurHash 3a 2.7 GB/s 10 Austin Appleby
|
||||
+ * SpookyHash 2.0 GB/s 10 Bob Jenkins
|
||||
+ * SBox 1.4 GB/s 9 Bret Mulvey
|
||||
+ * Lookup3 1.2 GB/s 9 Bob Jenkins
|
||||
+ * SuperFastHash 1.2 GB/s 1 Paul Hsieh
|
||||
+ * CityHash64 1.05 GB/s 10 Pike & Alakuijala
|
||||
+ * FNV 0.55 GB/s 5 Fowler, Noll, Vo
|
||||
+ * CRC32 0.43 GB/s 9
|
||||
+ * MD5-32 0.33 GB/s 10 Ronald L. Rivest
|
||||
+ * SHA1-32 0.28 GB/s 10
|
||||
+ *
|
||||
+ * Q.Score is a measure of quality of the hash function.
|
||||
+ * It depends on successfully passing SMHasher test set.
|
||||
+ * 10 is a perfect score.
|
||||
+ *
|
||||
+ * A 64-bits version, named xxh64 offers much better speed,
|
||||
+ * but for 64-bits applications only.
|
||||
+ * Name Speed on 64 bits Speed on 32 bits
|
||||
+ * xxh64 13.8 GB/s 1.9 GB/s
|
||||
+ * xxh32 6.8 GB/s 6.0 GB/s
|
||||
+ */
|
||||
+
|
||||
+#ifndef XXHASH_H
|
||||
+#define XXHASH_H
|
||||
+
|
||||
+#include <linux/types.h>
|
||||
+
|
||||
+/*-****************************
|
||||
+ * Simple Hash Functions
|
||||
+ *****************************/
|
||||
+
|
||||
+/**
|
||||
+ * xxh32() - calculate the 32-bit hash of the input with a given seed.
|
||||
+ *
|
||||
+ * @input: The data to hash.
|
||||
+ * @length: The length of the data to hash.
|
||||
+ * @seed: The seed can be used to alter the result predictably.
|
||||
+ *
|
||||
+ * Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s
|
||||
+ *
|
||||
+ * Return: The 32-bit hash of the data.
|
||||
+ */
|
||||
+uint32_t xxh32(const void *input, size_t length, uint32_t seed);
|
||||
+
|
||||
+/**
|
||||
+ * xxh64() - calculate the 64-bit hash of the input with a given seed.
|
||||
+ *
|
||||
+ * @input: The data to hash.
|
||||
+ * @length: The length of the data to hash.
|
||||
+ * @seed: The seed can be used to alter the result predictably.
|
||||
+ *
|
||||
+ * This function runs 2x faster on 64-bit systems, but slower on 32-bit systems.
|
||||
+ *
|
||||
+ * Return: The 64-bit hash of the data.
|
||||
+ */
|
||||
+uint64_t xxh64(const void *input, size_t length, uint64_t seed);
|
||||
+
|
||||
+/*-****************************
|
||||
+ * Streaming Hash Functions
|
||||
+ *****************************/
|
||||
+
|
||||
+/*
|
||||
+ * These definitions are only meant to allow allocation of XXH state
|
||||
+ * statically, on stack, or in a struct for example.
|
||||
+ * Do not use members directly.
|
||||
+ */
|
||||
+
|
||||
+/**
|
||||
+ * struct xxh32_state - private xxh32 state, do not use members directly
|
||||
+ */
|
||||
+struct xxh32_state {
|
||||
+ uint32_t total_len_32;
|
||||
+ uint32_t large_len;
|
||||
+ uint32_t v1;
|
||||
+ uint32_t v2;
|
||||
+ uint32_t v3;
|
||||
+ uint32_t v4;
|
||||
+ uint32_t mem32[4];
|
||||
+ uint32_t memsize;
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
+ * struct xxh32_state - private xxh64 state, do not use members directly
|
||||
+ */
|
||||
+struct xxh64_state {
|
||||
+ uint64_t total_len;
|
||||
+ uint64_t v1;
|
||||
+ uint64_t v2;
|
||||
+ uint64_t v3;
|
||||
+ uint64_t v4;
|
||||
+ uint64_t mem64[4];
|
||||
+ uint32_t memsize;
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
+ * xxh32_reset() - reset the xxh32 state to start a new hashing operation
|
||||
+ *
|
||||
+ * @state: The xxh32 state to reset.
|
||||
+ * @seed: Initialize the hash state with this seed.
|
||||
+ *
|
||||
+ * Call this function on any xxh32_state to prepare for a new hashing operation.
|
||||
+ */
|
||||
+void xxh32_reset(struct xxh32_state *state, uint32_t seed);
|
||||
+
|
||||
+/**
|
||||
+ * xxh32_update() - hash the data given and update the xxh32 state
|
||||
+ *
|
||||
+ * @state: The xxh32 state to update.
|
||||
+ * @input: The data to hash.
|
||||
+ * @length: The length of the data to hash.
|
||||
+ *
|
||||
+ * After calling xxh32_reset() call xxh32_update() as many times as necessary.
|
||||
+ *
|
||||
+ * Return: Zero on success, otherwise an error code.
|
||||
+ */
|
||||
+int xxh32_update(struct xxh32_state *state, const void *input, size_t length);
|
||||
+
|
||||
+/**
|
||||
+ * xxh32_digest() - produce the current xxh32 hash
|
||||
+ *
|
||||
+ * @state: Produce the current xxh32 hash of this state.
|
||||
+ *
|
||||
+ * A hash value can be produced at any time. It is still possible to continue
|
||||
+ * inserting input into the hash state after a call to xxh32_digest(), and
|
||||
+ * generate new hashes later on, by calling xxh32_digest() again.
|
||||
+ *
|
||||
+ * Return: The xxh32 hash stored in the state.
|
||||
+ */
|
||||
+uint32_t xxh32_digest(const struct xxh32_state *state);
|
||||
+
|
||||
+/**
|
||||
+ * xxh64_reset() - reset the xxh64 state to start a new hashing operation
|
||||
+ *
|
||||
+ * @state: The xxh64 state to reset.
|
||||
+ * @seed: Initialize the hash state with this seed.
|
||||
+ */
|
||||
+void xxh64_reset(struct xxh64_state *state, uint64_t seed);
|
||||
+
|
||||
+/**
|
||||
+ * xxh64_update() - hash the data given and update the xxh64 state
|
||||
+ * @state: The xxh64 state to update.
|
||||
+ * @input: The data to hash.
|
||||
+ * @length: The length of the data to hash.
|
||||
+ *
|
||||
+ * After calling xxh64_reset() call xxh64_update() as many times as necessary.
|
||||
+ *
|
||||
+ * Return: Zero on success, otherwise an error code.
|
||||
+ */
|
||||
+int xxh64_update(struct xxh64_state *state, const void *input, size_t length);
|
||||
+
|
||||
+/**
|
||||
+ * xxh64_digest() - produce the current xxh64 hash
|
||||
+ *
|
||||
+ * @state: Produce the current xxh64 hash of this state.
|
||||
+ *
|
||||
+ * A hash value can be produced at any time. It is still possible to continue
|
||||
+ * inserting input into the hash state after a call to xxh64_digest(), and
|
||||
+ * generate new hashes later on, by calling xxh64_digest() again.
|
||||
+ *
|
||||
+ * Return: The xxh64 hash stored in the state.
|
||||
+ */
|
||||
+uint64_t xxh64_digest(const struct xxh64_state *state);
|
||||
+
|
||||
+/*-**************************
|
||||
+ * Utils
|
||||
+ ***************************/
|
||||
+
|
||||
+/**
|
||||
+ * xxh32_copy_state() - copy the source state into the destination state
|
||||
+ *
|
||||
+ * @src: The source xxh32 state.
|
||||
+ * @dst: The destination xxh32 state.
|
||||
+ */
|
||||
+void xxh32_copy_state(struct xxh32_state *dst, const struct xxh32_state *src);
|
||||
+
|
||||
+/**
|
||||
+ * xxh64_copy_state() - copy the source state into the destination state
|
||||
+ *
|
||||
+ * @src: The source xxh64 state.
|
||||
+ * @dst: The destination xxh64 state.
|
||||
+ */
|
||||
+void xxh64_copy_state(struct xxh64_state *dst, const struct xxh64_state *src);
|
||||
+
|
||||
+#endif /* XXHASH_H */
|
||||
diff --git a/lib/Kconfig b/lib/Kconfig
|
||||
index 6762529..5e7541f 100644
|
||||
--- a/lib/Kconfig
|
||||
+++ b/lib/Kconfig
|
||||
@@ -192,6 +192,9 @@ config CRC8
|
||||
when they need to do cyclic redundancy check according CRC8
|
||||
algorithm. Module will be called crc8.
|
||||
|
||||
+config XXHASH
|
||||
+ tristate
|
||||
+
|
||||
config AUDIT_GENERIC
|
||||
bool
|
||||
depends on AUDIT && !AUDIT_ARCH
|
||||
diff --git a/lib/Makefile b/lib/Makefile
|
||||
index 40c1837..d06b68a 100644
|
||||
--- a/lib/Makefile
|
||||
+++ b/lib/Makefile
|
||||
@@ -102,6 +102,7 @@ obj-$(CONFIG_CRC4) += crc4.o
|
||||
obj-$(CONFIG_CRC7) += crc7.o
|
||||
obj-$(CONFIG_LIBCRC32C) += libcrc32c.o
|
||||
obj-$(CONFIG_CRC8) += crc8.o
|
||||
+obj-$(CONFIG_XXHASH) += xxhash.o
|
||||
obj-$(CONFIG_GENERIC_ALLOCATOR) += genalloc.o
|
||||
|
||||
obj-$(CONFIG_842_COMPRESS) += 842/
|
||||
diff --git a/lib/xxhash.c b/lib/xxhash.c
|
||||
new file mode 100644
|
||||
index 0000000..aa61e2a
|
||||
--- /dev/null
|
||||
+++ b/lib/xxhash.c
|
||||
@@ -0,0 +1,500 @@
|
||||
+/*
|
||||
+ * xxHash - Extremely Fast Hash algorithm
|
||||
+ * Copyright (C) 2012-2016, Yann Collet.
|
||||
+ *
|
||||
+ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
||||
+ *
|
||||
+ * Redistribution and use in source and binary forms, with or without
|
||||
+ * modification, are permitted provided that the following conditions are
|
||||
+ * met:
|
||||
+ *
|
||||
+ * * Redistributions of source code must retain the above copyright
|
||||
+ * notice, this list of conditions and the following disclaimer.
|
||||
+ * * Redistributions in binary form must reproduce the above
|
||||
+ * copyright notice, this list of conditions and the following disclaimer
|
||||
+ * in the documentation and/or other materials provided with the
|
||||
+ * distribution.
|
||||
+ *
|
||||
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify it under
|
||||
+ * the terms of the GNU General Public License version 2 as published by the
|
||||
+ * Free Software Foundation. This program is dual-licensed; you may select
|
||||
+ * either version 2 of the GNU General Public License ("GPL") or BSD license
|
||||
+ * ("BSD").
|
||||
+ *
|
||||
+ * You can contact the author at:
|
||||
+ * - xxHash homepage: http://cyan4973.github.io/xxHash/
|
||||
+ * - xxHash source repository: https://github.com/Cyan4973/xxHash
|
||||
+ */
|
||||
+
|
||||
+#include <asm/unaligned.h>
|
||||
+#include <linux/errno.h>
|
||||
+#include <linux/compiler.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/string.h>
|
||||
+#include <linux/xxhash.h>
|
||||
+
|
||||
+/*-*************************************
|
||||
+ * Macros
|
||||
+ **************************************/
|
||||
+#define xxh_rotl32(x, r) ((x << r) | (x >> (32 - r)))
|
||||
+#define xxh_rotl64(x, r) ((x << r) | (x >> (64 - r)))
|
||||
+
|
||||
+#ifdef __LITTLE_ENDIAN
|
||||
+# define XXH_CPU_LITTLE_ENDIAN 1
|
||||
+#else
|
||||
+# define XXH_CPU_LITTLE_ENDIAN 0
|
||||
+#endif
|
||||
+
|
||||
+/*-*************************************
|
||||
+ * Constants
|
||||
+ **************************************/
|
||||
+static const uint32_t PRIME32_1 = 2654435761U;
|
||||
+static const uint32_t PRIME32_2 = 2246822519U;
|
||||
+static const uint32_t PRIME32_3 = 3266489917U;
|
||||
+static const uint32_t PRIME32_4 = 668265263U;
|
||||
+static const uint32_t PRIME32_5 = 374761393U;
|
||||
+
|
||||
+static const uint64_t PRIME64_1 = 11400714785074694791ULL;
|
||||
+static const uint64_t PRIME64_2 = 14029467366897019727ULL;
|
||||
+static const uint64_t PRIME64_3 = 1609587929392839161ULL;
|
||||
+static const uint64_t PRIME64_4 = 9650029242287828579ULL;
|
||||
+static const uint64_t PRIME64_5 = 2870177450012600261ULL;
|
||||
+
|
||||
+/*-**************************
|
||||
+ * Utils
|
||||
+ ***************************/
|
||||
+void xxh32_copy_state(struct xxh32_state *dst, const struct xxh32_state *src)
|
||||
+{
|
||||
+ memcpy(dst, src, sizeof(*dst));
|
||||
+}
|
||||
+EXPORT_SYMBOL(xxh32_copy_state);
|
||||
+
|
||||
+void xxh64_copy_state(struct xxh64_state *dst, const struct xxh64_state *src)
|
||||
+{
|
||||
+ memcpy(dst, src, sizeof(*dst));
|
||||
+}
|
||||
+EXPORT_SYMBOL(xxh64_copy_state);
|
||||
+
|
||||
+/*-***************************
|
||||
+ * Simple Hash Functions
|
||||
+ ****************************/
|
||||
+static uint32_t xxh32_round(uint32_t seed, const uint32_t input)
|
||||
+{
|
||||
+ seed += input * PRIME32_2;
|
||||
+ seed = xxh_rotl32(seed, 13);
|
||||
+ seed *= PRIME32_1;
|
||||
+ return seed;
|
||||
+}
|
||||
+
|
||||
+uint32_t xxh32(const void *input, const size_t len, const uint32_t seed)
|
||||
+{
|
||||
+ const uint8_t *p = (const uint8_t *)input;
|
||||
+ const uint8_t *b_end = p + len;
|
||||
+ uint32_t h32;
|
||||
+
|
||||
+ if (len >= 16) {
|
||||
+ const uint8_t *const limit = b_end - 16;
|
||||
+ uint32_t v1 = seed + PRIME32_1 + PRIME32_2;
|
||||
+ uint32_t v2 = seed + PRIME32_2;
|
||||
+ uint32_t v3 = seed + 0;
|
||||
+ uint32_t v4 = seed - PRIME32_1;
|
||||
+
|
||||
+ do {
|
||||
+ v1 = xxh32_round(v1, get_unaligned_le32(p));
|
||||
+ p += 4;
|
||||
+ v2 = xxh32_round(v2, get_unaligned_le32(p));
|
||||
+ p += 4;
|
||||
+ v3 = xxh32_round(v3, get_unaligned_le32(p));
|
||||
+ p += 4;
|
||||
+ v4 = xxh32_round(v4, get_unaligned_le32(p));
|
||||
+ p += 4;
|
||||
+ } while (p <= limit);
|
||||
+
|
||||
+ h32 = xxh_rotl32(v1, 1) + xxh_rotl32(v2, 7) +
|
||||
+ xxh_rotl32(v3, 12) + xxh_rotl32(v4, 18);
|
||||
+ } else {
|
||||
+ h32 = seed + PRIME32_5;
|
||||
+ }
|
||||
+
|
||||
+ h32 += (uint32_t)len;
|
||||
+
|
||||
+ while (p + 4 <= b_end) {
|
||||
+ h32 += get_unaligned_le32(p) * PRIME32_3;
|
||||
+ h32 = xxh_rotl32(h32, 17) * PRIME32_4;
|
||||
+ p += 4;
|
||||
+ }
|
||||
+
|
||||
+ while (p < b_end) {
|
||||
+ h32 += (*p) * PRIME32_5;
|
||||
+ h32 = xxh_rotl32(h32, 11) * PRIME32_1;
|
||||
+ p++;
|
||||
+ }
|
||||
+
|
||||
+ h32 ^= h32 >> 15;
|
||||
+ h32 *= PRIME32_2;
|
||||
+ h32 ^= h32 >> 13;
|
||||
+ h32 *= PRIME32_3;
|
||||
+ h32 ^= h32 >> 16;
|
||||
+
|
||||
+ return h32;
|
||||
+}
|
||||
+EXPORT_SYMBOL(xxh32);
|
||||
+
|
||||
+static uint64_t xxh64_round(uint64_t acc, const uint64_t input)
|
||||
+{
|
||||
+ acc += input * PRIME64_2;
|
||||
+ acc = xxh_rotl64(acc, 31);
|
||||
+ acc *= PRIME64_1;
|
||||
+ return acc;
|
||||
+}
|
||||
+
|
||||
+static uint64_t xxh64_merge_round(uint64_t acc, uint64_t val)
|
||||
+{
|
||||
+ val = xxh64_round(0, val);
|
||||
+ acc ^= val;
|
||||
+ acc = acc * PRIME64_1 + PRIME64_4;
|
||||
+ return acc;
|
||||
+}
|
||||
+
|
||||
+uint64_t xxh64(const void *input, const size_t len, const uint64_t seed)
|
||||
+{
|
||||
+ const uint8_t *p = (const uint8_t *)input;
|
||||
+ const uint8_t *const b_end = p + len;
|
||||
+ uint64_t h64;
|
||||
+
|
||||
+ if (len >= 32) {
|
||||
+ const uint8_t *const limit = b_end - 32;
|
||||
+ uint64_t v1 = seed + PRIME64_1 + PRIME64_2;
|
||||
+ uint64_t v2 = seed + PRIME64_2;
|
||||
+ uint64_t v3 = seed + 0;
|
||||
+ uint64_t v4 = seed - PRIME64_1;
|
||||
+
|
||||
+ do {
|
||||
+ v1 = xxh64_round(v1, get_unaligned_le64(p));
|
||||
+ p += 8;
|
||||
+ v2 = xxh64_round(v2, get_unaligned_le64(p));
|
||||
+ p += 8;
|
||||
+ v3 = xxh64_round(v3, get_unaligned_le64(p));
|
||||
+ p += 8;
|
||||
+ v4 = xxh64_round(v4, get_unaligned_le64(p));
|
||||
+ p += 8;
|
||||
+ } while (p <= limit);
|
||||
+
|
||||
+ h64 = xxh_rotl64(v1, 1) + xxh_rotl64(v2, 7) +
|
||||
+ xxh_rotl64(v3, 12) + xxh_rotl64(v4, 18);
|
||||
+ h64 = xxh64_merge_round(h64, v1);
|
||||
+ h64 = xxh64_merge_round(h64, v2);
|
||||
+ h64 = xxh64_merge_round(h64, v3);
|
||||
+ h64 = xxh64_merge_round(h64, v4);
|
||||
+
|
||||
+ } else {
|
||||
+ h64 = seed + PRIME64_5;
|
||||
+ }
|
||||
+
|
||||
+ h64 += (uint64_t)len;
|
||||
+
|
||||
+ while (p + 8 <= b_end) {
|
||||
+ const uint64_t k1 = xxh64_round(0, get_unaligned_le64(p));
|
||||
+
|
||||
+ h64 ^= k1;
|
||||
+ h64 = xxh_rotl64(h64, 27) * PRIME64_1 + PRIME64_4;
|
||||
+ p += 8;
|
||||
+ }
|
||||
+
|
||||
+ if (p + 4 <= b_end) {
|
||||
+ h64 ^= (uint64_t)(get_unaligned_le32(p)) * PRIME64_1;
|
||||
+ h64 = xxh_rotl64(h64, 23) * PRIME64_2 + PRIME64_3;
|
||||
+ p += 4;
|
||||
+ }
|
||||
+
|
||||
+ while (p < b_end) {
|
||||
+ h64 ^= (*p) * PRIME64_5;
|
||||
+ h64 = xxh_rotl64(h64, 11) * PRIME64_1;
|
||||
+ p++;
|
||||
+ }
|
||||
+
|
||||
+ h64 ^= h64 >> 33;
|
||||
+ h64 *= PRIME64_2;
|
||||
+ h64 ^= h64 >> 29;
|
||||
+ h64 *= PRIME64_3;
|
||||
+ h64 ^= h64 >> 32;
|
||||
+
|
||||
+ return h64;
|
||||
+}
|
||||
+EXPORT_SYMBOL(xxh64);
|
||||
+
|
||||
+/*-**************************************************
|
||||
+ * Advanced Hash Functions
|
||||
+ ***************************************************/
|
||||
+void xxh32_reset(struct xxh32_state *statePtr, const uint32_t seed)
|
||||
+{
|
||||
+ /* use a local state for memcpy() to avoid strict-aliasing warnings */
|
||||
+ struct xxh32_state state;
|
||||
+
|
||||
+ memset(&state, 0, sizeof(state));
|
||||
+ state.v1 = seed + PRIME32_1 + PRIME32_2;
|
||||
+ state.v2 = seed + PRIME32_2;
|
||||
+ state.v3 = seed + 0;
|
||||
+ state.v4 = seed - PRIME32_1;
|
||||
+ memcpy(statePtr, &state, sizeof(state));
|
||||
+}
|
||||
+EXPORT_SYMBOL(xxh32_reset);
|
||||
+
|
||||
+void xxh64_reset(struct xxh64_state *statePtr, const uint64_t seed)
|
||||
+{
|
||||
+ /* use a local state for memcpy() to avoid strict-aliasing warnings */
|
||||
+ struct xxh64_state state;
|
||||
+
|
||||
+ memset(&state, 0, sizeof(state));
|
||||
+ state.v1 = seed + PRIME64_1 + PRIME64_2;
|
||||
+ state.v2 = seed + PRIME64_2;
|
||||
+ state.v3 = seed + 0;
|
||||
+ state.v4 = seed - PRIME64_1;
|
||||
+ memcpy(statePtr, &state, sizeof(state));
|
||||
+}
|
||||
+EXPORT_SYMBOL(xxh64_reset);
|
||||
+
|
||||
+int xxh32_update(struct xxh32_state *state, const void *input, const size_t len)
|
||||
+{
|
||||
+ const uint8_t *p = (const uint8_t *)input;
|
||||
+ const uint8_t *const b_end = p + len;
|
||||
+
|
||||
+ if (input == NULL)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ state->total_len_32 += (uint32_t)len;
|
||||
+ state->large_len |= (len >= 16) | (state->total_len_32 >= 16);
|
||||
+
|
||||
+ if (state->memsize + len < 16) { /* fill in tmp buffer */
|
||||
+ memcpy((uint8_t *)(state->mem32) + state->memsize, input, len);
|
||||
+ state->memsize += (uint32_t)len;
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ if (state->memsize) { /* some data left from previous update */
|
||||
+ const uint32_t *p32 = state->mem32;
|
||||
+
|
||||
+ memcpy((uint8_t *)(state->mem32) + state->memsize, input,
|
||||
+ 16 - state->memsize);
|
||||
+
|
||||
+ state->v1 = xxh32_round(state->v1, get_unaligned_le32(p32));
|
||||
+ p32++;
|
||||
+ state->v2 = xxh32_round(state->v2, get_unaligned_le32(p32));
|
||||
+ p32++;
|
||||
+ state->v3 = xxh32_round(state->v3, get_unaligned_le32(p32));
|
||||
+ p32++;
|
||||
+ state->v4 = xxh32_round(state->v4, get_unaligned_le32(p32));
|
||||
+ p32++;
|
||||
+
|
||||
+ p += 16-state->memsize;
|
||||
+ state->memsize = 0;
|
||||
+ }
|
||||
+
|
||||
+ if (p <= b_end - 16) {
|
||||
+ const uint8_t *const limit = b_end - 16;
|
||||
+ uint32_t v1 = state->v1;
|
||||
+ uint32_t v2 = state->v2;
|
||||
+ uint32_t v3 = state->v3;
|
||||
+ uint32_t v4 = state->v4;
|
||||
+
|
||||
+ do {
|
||||
+ v1 = xxh32_round(v1, get_unaligned_le32(p));
|
||||
+ p += 4;
|
||||
+ v2 = xxh32_round(v2, get_unaligned_le32(p));
|
||||
+ p += 4;
|
||||
+ v3 = xxh32_round(v3, get_unaligned_le32(p));
|
||||
+ p += 4;
|
||||
+ v4 = xxh32_round(v4, get_unaligned_le32(p));
|
||||
+ p += 4;
|
||||
+ } while (p <= limit);
|
||||
+
|
||||
+ state->v1 = v1;
|
||||
+ state->v2 = v2;
|
||||
+ state->v3 = v3;
|
||||
+ state->v4 = v4;
|
||||
+ }
|
||||
+
|
||||
+ if (p < b_end) {
|
||||
+ memcpy(state->mem32, p, (size_t)(b_end-p));
|
||||
+ state->memsize = (uint32_t)(b_end-p);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL(xxh32_update);
|
||||
+
|
||||
+uint32_t xxh32_digest(const struct xxh32_state *state)
|
||||
+{
|
||||
+ const uint8_t *p = (const uint8_t *)state->mem32;
|
||||
+ const uint8_t *const b_end = (const uint8_t *)(state->mem32) +
|
||||
+ state->memsize;
|
||||
+ uint32_t h32;
|
||||
+
|
||||
+ if (state->large_len) {
|
||||
+ h32 = xxh_rotl32(state->v1, 1) + xxh_rotl32(state->v2, 7) +
|
||||
+ xxh_rotl32(state->v3, 12) + xxh_rotl32(state->v4, 18);
|
||||
+ } else {
|
||||
+ h32 = state->v3 /* == seed */ + PRIME32_5;
|
||||
+ }
|
||||
+
|
||||
+ h32 += state->total_len_32;
|
||||
+
|
||||
+ while (p + 4 <= b_end) {
|
||||
+ h32 += get_unaligned_le32(p) * PRIME32_3;
|
||||
+ h32 = xxh_rotl32(h32, 17) * PRIME32_4;
|
||||
+ p += 4;
|
||||
+ }
|
||||
+
|
||||
+ while (p < b_end) {
|
||||
+ h32 += (*p) * PRIME32_5;
|
||||
+ h32 = xxh_rotl32(h32, 11) * PRIME32_1;
|
||||
+ p++;
|
||||
+ }
|
||||
+
|
||||
+ h32 ^= h32 >> 15;
|
||||
+ h32 *= PRIME32_2;
|
||||
+ h32 ^= h32 >> 13;
|
||||
+ h32 *= PRIME32_3;
|
||||
+ h32 ^= h32 >> 16;
|
||||
+
|
||||
+ return h32;
|
||||
+}
|
||||
+EXPORT_SYMBOL(xxh32_digest);
|
||||
+
|
||||
+int xxh64_update(struct xxh64_state *state, const void *input, const size_t len)
|
||||
+{
|
||||
+ const uint8_t *p = (const uint8_t *)input;
|
||||
+ const uint8_t *const b_end = p + len;
|
||||
+
|
||||
+ if (input == NULL)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ state->total_len += len;
|
||||
+
|
||||
+ if (state->memsize + len < 32) { /* fill in tmp buffer */
|
||||
+ memcpy(((uint8_t *)state->mem64) + state->memsize, input, len);
|
||||
+ state->memsize += (uint32_t)len;
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ if (state->memsize) { /* tmp buffer is full */
|
||||
+ uint64_t *p64 = state->mem64;
|
||||
+
|
||||
+ memcpy(((uint8_t *)p64) + state->memsize, input,
|
||||
+ 32 - state->memsize);
|
||||
+
|
||||
+ state->v1 = xxh64_round(state->v1, get_unaligned_le64(p64));
|
||||
+ p64++;
|
||||
+ state->v2 = xxh64_round(state->v2, get_unaligned_le64(p64));
|
||||
+ p64++;
|
||||
+ state->v3 = xxh64_round(state->v3, get_unaligned_le64(p64));
|
||||
+ p64++;
|
||||
+ state->v4 = xxh64_round(state->v4, get_unaligned_le64(p64));
|
||||
+
|
||||
+ p += 32 - state->memsize;
|
||||
+ state->memsize = 0;
|
||||
+ }
|
||||
+
|
||||
+ if (p + 32 <= b_end) {
|
||||
+ const uint8_t *const limit = b_end - 32;
|
||||
+ uint64_t v1 = state->v1;
|
||||
+ uint64_t v2 = state->v2;
|
||||
+ uint64_t v3 = state->v3;
|
||||
+ uint64_t v4 = state->v4;
|
||||
+
|
||||
+ do {
|
||||
+ v1 = xxh64_round(v1, get_unaligned_le64(p));
|
||||
+ p += 8;
|
||||
+ v2 = xxh64_round(v2, get_unaligned_le64(p));
|
||||
+ p += 8;
|
||||
+ v3 = xxh64_round(v3, get_unaligned_le64(p));
|
||||
+ p += 8;
|
||||
+ v4 = xxh64_round(v4, get_unaligned_le64(p));
|
||||
+ p += 8;
|
||||
+ } while (p <= limit);
|
||||
+
|
||||
+ state->v1 = v1;
|
||||
+ state->v2 = v2;
|
||||
+ state->v3 = v3;
|
||||
+ state->v4 = v4;
|
||||
+ }
|
||||
+
|
||||
+ if (p < b_end) {
|
||||
+ memcpy(state->mem64, p, (size_t)(b_end-p));
|
||||
+ state->memsize = (uint32_t)(b_end - p);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL(xxh64_update);
|
||||
+
|
||||
+uint64_t xxh64_digest(const struct xxh64_state *state)
|
||||
+{
|
||||
+ const uint8_t *p = (const uint8_t *)state->mem64;
|
||||
+ const uint8_t *const b_end = (const uint8_t *)state->mem64 +
|
||||
+ state->memsize;
|
||||
+ uint64_t h64;
|
||||
+
|
||||
+ if (state->total_len >= 32) {
|
||||
+ const uint64_t v1 = state->v1;
|
||||
+ const uint64_t v2 = state->v2;
|
||||
+ const uint64_t v3 = state->v3;
|
||||
+ const uint64_t v4 = state->v4;
|
||||
+
|
||||
+ h64 = xxh_rotl64(v1, 1) + xxh_rotl64(v2, 7) +
|
||||
+ xxh_rotl64(v3, 12) + xxh_rotl64(v4, 18);
|
||||
+ h64 = xxh64_merge_round(h64, v1);
|
||||
+ h64 = xxh64_merge_round(h64, v2);
|
||||
+ h64 = xxh64_merge_round(h64, v3);
|
||||
+ h64 = xxh64_merge_round(h64, v4);
|
||||
+ } else {
|
||||
+ h64 = state->v3 + PRIME64_5;
|
||||
+ }
|
||||
+
|
||||
+ h64 += (uint64_t)state->total_len;
|
||||
+
|
||||
+ while (p + 8 <= b_end) {
|
||||
+ const uint64_t k1 = xxh64_round(0, get_unaligned_le64(p));
|
||||
+
|
||||
+ h64 ^= k1;
|
||||
+ h64 = xxh_rotl64(h64, 27) * PRIME64_1 + PRIME64_4;
|
||||
+ p += 8;
|
||||
+ }
|
||||
+
|
||||
+ if (p + 4 <= b_end) {
|
||||
+ h64 ^= (uint64_t)(get_unaligned_le32(p)) * PRIME64_1;
|
||||
+ h64 = xxh_rotl64(h64, 23) * PRIME64_2 + PRIME64_3;
|
||||
+ p += 4;
|
||||
+ }
|
||||
+
|
||||
+ while (p < b_end) {
|
||||
+ h64 ^= (*p) * PRIME64_5;
|
||||
+ h64 = xxh_rotl64(h64, 11) * PRIME64_1;
|
||||
+ p++;
|
||||
+ }
|
||||
+
|
||||
+ h64 ^= h64 >> 33;
|
||||
+ h64 *= PRIME64_2;
|
||||
+ h64 ^= h64 >> 29;
|
||||
+ h64 *= PRIME64_3;
|
||||
+ h64 ^= h64 >> 32;
|
||||
+
|
||||
+ return h64;
|
||||
+}
|
||||
+EXPORT_SYMBOL(xxh64_digest);
|
||||
+
|
||||
+MODULE_LICENSE("Dual BSD/GPL");
|
||||
+MODULE_DESCRIPTION("xxHash");
|
||||
--
|
||||
2.9.3
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,740 @@
|
||||
From 8a9dddfbf6551afea73911e367dd4be64d62b9fd Mon Sep 17 00:00:00 2001
|
||||
From: Nick Terrell <terrelln@fb.com>
|
||||
Date: Mon, 17 Jul 2017 17:08:39 -0700
|
||||
Subject: [PATCH v5 3/5] btrfs: Add zstd support
|
||||
|
||||
Add zstd compression and decompression support to BtrFS. zstd at its
|
||||
fastest level compresses almost as well as zlib, while offering much
|
||||
faster compression and decompression, approaching lzo speeds.
|
||||
|
||||
I benchmarked btrfs with zstd compression against no compression, lzo
|
||||
compression, and zlib compression. I benchmarked two scenarios. Copying
|
||||
a set of files to btrfs, and then reading the files. Copying a tarball
|
||||
to btrfs, extracting it to btrfs, and then reading the extracted files.
|
||||
After every operation, I call `sync` and include the sync time.
|
||||
Between every pair of operations I unmount and remount the filesystem
|
||||
to avoid caching. The benchmark files can be found in the upstream
|
||||
zstd source repository under
|
||||
`contrib/linux-kernel/{btrfs-benchmark.sh,btrfs-extract-benchmark.sh}`
|
||||
[1] [2].
|
||||
|
||||
I ran the benchmarks on a Ubuntu 14.04 VM with 2 cores and 4 GiB of RAM.
|
||||
The VM is running on a MacBook Pro with a 3.1 GHz Intel Core i7 processor,
|
||||
16 GB of RAM, and a SSD.
|
||||
|
||||
The first compression benchmark is copying 10 copies of the unzipped
|
||||
Silesia corpus [3] into a BtrFS filesystem mounted with
|
||||
`-o compress-force=Method`. The decompression benchmark times how long
|
||||
it takes to `tar` all 10 copies into `/dev/null`. The compression ratio is
|
||||
measured by comparing the output of `df` and `du`. See the benchmark file
|
||||
[1] for details. I benchmarked multiple zstd compression levels, although
|
||||
the patch uses zstd level 1.
|
||||
|
||||
| Method | Ratio | Compression MB/s | Decompression speed |
|
||||
|---------|-------|------------------|---------------------|
|
||||
| None | 0.99 | 504 | 686 |
|
||||
| lzo | 1.66 | 398 | 442 |
|
||||
| zlib | 2.58 | 65 | 241 |
|
||||
| zstd 1 | 2.57 | 260 | 383 |
|
||||
| zstd 3 | 2.71 | 174 | 408 |
|
||||
| zstd 6 | 2.87 | 70 | 398 |
|
||||
| zstd 9 | 2.92 | 43 | 406 |
|
||||
| zstd 12 | 2.93 | 21 | 408 |
|
||||
| zstd 15 | 3.01 | 11 | 354 |
|
||||
|
||||
The next benchmark first copies `linux-4.11.6.tar` [4] to btrfs. Then it
|
||||
measures the compression ratio, extracts the tar, and deletes the tar.
|
||||
Then it measures the compression ratio again, and `tar`s the extracted
|
||||
files into `/dev/null`. See the benchmark file [2] for details.
|
||||
|
||||
| Method | Tar Ratio | Extract Ratio | Copy (s) | Extract (s)| Read (s) |
|
||||
|--------|-----------|---------------|----------|------------|----------|
|
||||
| None | 0.97 | 0.78 | 0.981 | 5.501 | 8.807 |
|
||||
| lzo | 2.06 | 1.38 | 1.631 | 8.458 | 8.585 |
|
||||
| zlib | 3.40 | 1.86 | 7.750 | 21.544 | 11.744 |
|
||||
| zstd 1 | 3.57 | 1.85 | 2.579 | 11.479 | 9.389 |
|
||||
|
||||
[1] https://github.com/facebook/zstd/blob/dev/contrib/linux-kernel/btrfs-benchmark.sh
|
||||
[2] https://github.com/facebook/zstd/blob/dev/contrib/linux-kernel/btrfs-extract-benchmark.sh
|
||||
[3] http://sun.aei.polsl.pl/~sdeor/index.php?page=silesia
|
||||
[4] https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.11.6.tar.xz
|
||||
|
||||
zstd source repository: https://github.com/facebook/zstd
|
||||
|
||||
Signed-off-by: Nick Terrell <terrelln@fb.com>
|
||||
---
|
||||
v2 -> v3:
|
||||
- Port upstream BtrFS commits e1ddce71d6, 389a6cfc2a, and 6acafd1eff
|
||||
- Change default compression level for BtrFS to 3
|
||||
|
||||
v3 -> v4:
|
||||
- Add missing includes, which fixes the aarch64 build
|
||||
- Fix minor linter warnings
|
||||
|
||||
fs/btrfs/Kconfig | 2 +
|
||||
fs/btrfs/Makefile | 2 +-
|
||||
fs/btrfs/compression.c | 1 +
|
||||
fs/btrfs/compression.h | 6 +-
|
||||
fs/btrfs/ctree.h | 1 +
|
||||
fs/btrfs/disk-io.c | 2 +
|
||||
fs/btrfs/ioctl.c | 6 +-
|
||||
fs/btrfs/props.c | 6 +
|
||||
fs/btrfs/super.c | 12 +-
|
||||
fs/btrfs/sysfs.c | 2 +
|
||||
fs/btrfs/zstd.c | 432 +++++++++++++++++++++++++++++++++++++++++++++
|
||||
include/uapi/linux/btrfs.h | 8 +-
|
||||
12 files changed, 468 insertions(+), 12 deletions(-)
|
||||
create mode 100644 fs/btrfs/zstd.c
|
||||
|
||||
diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig
|
||||
index 80e9c18..a26c63b 100644
|
||||
--- a/fs/btrfs/Kconfig
|
||||
+++ b/fs/btrfs/Kconfig
|
||||
@@ -6,6 +6,8 @@ config BTRFS_FS
|
||||
select ZLIB_DEFLATE
|
||||
select LZO_COMPRESS
|
||||
select LZO_DECOMPRESS
|
||||
+ select ZSTD_COMPRESS
|
||||
+ select ZSTD_DECOMPRESS
|
||||
select RAID6_PQ
|
||||
select XOR_BLOCKS
|
||||
select SRCU
|
||||
diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile
|
||||
index 128ce17..962a95a 100644
|
||||
--- a/fs/btrfs/Makefile
|
||||
+++ b/fs/btrfs/Makefile
|
||||
@@ -6,7 +6,7 @@ btrfs-y += super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \
|
||||
transaction.o inode.o file.o tree-defrag.o \
|
||||
extent_map.o sysfs.o struct-funcs.o xattr.o ordered-data.o \
|
||||
extent_io.o volumes.o async-thread.o ioctl.o locking.o orphan.o \
|
||||
- export.o tree-log.o free-space-cache.o zlib.o lzo.o \
|
||||
+ export.o tree-log.o free-space-cache.o zlib.o lzo.o zstd.o \
|
||||
compression.o delayed-ref.o relocation.o delayed-inode.o scrub.o \
|
||||
reada.o backref.o ulist.o qgroup.o send.o dev-replace.o raid56.o \
|
||||
uuid-tree.o props.o hash.o free-space-tree.o
|
||||
diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
|
||||
index d2ef9ac..4ff42d1 100644
|
||||
--- a/fs/btrfs/compression.c
|
||||
+++ b/fs/btrfs/compression.c
|
||||
@@ -704,6 +704,7 @@ static struct {
|
||||
static const struct btrfs_compress_op * const btrfs_compress_op[] = {
|
||||
&btrfs_zlib_compress,
|
||||
&btrfs_lzo_compress,
|
||||
+ &btrfs_zstd_compress,
|
||||
};
|
||||
|
||||
void __init btrfs_init_compress(void)
|
||||
diff --git a/fs/btrfs/compression.h b/fs/btrfs/compression.h
|
||||
index 87f6d33..2269e00 100644
|
||||
--- a/fs/btrfs/compression.h
|
||||
+++ b/fs/btrfs/compression.h
|
||||
@@ -99,8 +99,9 @@ enum btrfs_compression_type {
|
||||
BTRFS_COMPRESS_NONE = 0,
|
||||
BTRFS_COMPRESS_ZLIB = 1,
|
||||
BTRFS_COMPRESS_LZO = 2,
|
||||
- BTRFS_COMPRESS_TYPES = 2,
|
||||
- BTRFS_COMPRESS_LAST = 3,
|
||||
+ BTRFS_COMPRESS_ZSTD = 3,
|
||||
+ BTRFS_COMPRESS_TYPES = 3,
|
||||
+ BTRFS_COMPRESS_LAST = 4,
|
||||
};
|
||||
|
||||
struct btrfs_compress_op {
|
||||
@@ -128,5 +129,6 @@ struct btrfs_compress_op {
|
||||
|
||||
extern const struct btrfs_compress_op btrfs_zlib_compress;
|
||||
extern const struct btrfs_compress_op btrfs_lzo_compress;
|
||||
+extern const struct btrfs_compress_op btrfs_zstd_compress;
|
||||
|
||||
#endif
|
||||
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
|
||||
index 3f3eb7b..845d77c 100644
|
||||
--- a/fs/btrfs/ctree.h
|
||||
+++ b/fs/btrfs/ctree.h
|
||||
@@ -270,6 +270,7 @@ struct btrfs_super_block {
|
||||
BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS | \
|
||||
BTRFS_FEATURE_INCOMPAT_BIG_METADATA | \
|
||||
BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO | \
|
||||
+ BTRFS_FEATURE_INCOMPAT_COMPRESS_ZSTD | \
|
||||
BTRFS_FEATURE_INCOMPAT_RAID56 | \
|
||||
BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF | \
|
||||
BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA | \
|
||||
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
|
||||
index 080e2eb..04632f4 100644
|
||||
--- a/fs/btrfs/disk-io.c
|
||||
+++ b/fs/btrfs/disk-io.c
|
||||
@@ -2828,6 +2828,8 @@ int open_ctree(struct super_block *sb,
|
||||
features |= BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF;
|
||||
if (fs_info->compress_type == BTRFS_COMPRESS_LZO)
|
||||
features |= BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO;
|
||||
+ else if (fs_info->compress_type == BTRFS_COMPRESS_ZSTD)
|
||||
+ features |= BTRFS_FEATURE_INCOMPAT_COMPRESS_ZSTD;
|
||||
|
||||
if (features & BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA)
|
||||
btrfs_info(fs_info, "has skinny extents");
|
||||
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
|
||||
index fa1b78c..b9963d9 100644
|
||||
--- a/fs/btrfs/ioctl.c
|
||||
+++ b/fs/btrfs/ioctl.c
|
||||
@@ -327,8 +327,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
|
||||
|
||||
if (fs_info->compress_type == BTRFS_COMPRESS_LZO)
|
||||
comp = "lzo";
|
||||
- else
|
||||
+ else if (fs_info->compress_type == BTRFS_COMPRESS_ZLIB)
|
||||
comp = "zlib";
|
||||
+ else
|
||||
+ comp = "zstd";
|
||||
ret = btrfs_set_prop(inode, "btrfs.compression",
|
||||
comp, strlen(comp), 0);
|
||||
if (ret)
|
||||
@@ -1466,6 +1468,8 @@ int btrfs_defrag_file(struct inode *inode, struct file *file,
|
||||
|
||||
if (range->compress_type == BTRFS_COMPRESS_LZO) {
|
||||
btrfs_set_fs_incompat(fs_info, COMPRESS_LZO);
|
||||
+ } else if (range->compress_type == BTRFS_COMPRESS_ZSTD) {
|
||||
+ btrfs_set_fs_incompat(fs_info, COMPRESS_ZSTD);
|
||||
}
|
||||
|
||||
ret = defrag_count;
|
||||
diff --git a/fs/btrfs/props.c b/fs/btrfs/props.c
|
||||
index 4b23ae5..20631e9 100644
|
||||
--- a/fs/btrfs/props.c
|
||||
+++ b/fs/btrfs/props.c
|
||||
@@ -390,6 +390,8 @@ static int prop_compression_validate(const char *value, size_t len)
|
||||
return 0;
|
||||
else if (!strncmp("zlib", value, len))
|
||||
return 0;
|
||||
+ else if (!strncmp("zstd", value, len))
|
||||
+ return 0;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -412,6 +414,8 @@ static int prop_compression_apply(struct inode *inode,
|
||||
type = BTRFS_COMPRESS_LZO;
|
||||
else if (!strncmp("zlib", value, len))
|
||||
type = BTRFS_COMPRESS_ZLIB;
|
||||
+ else if (!strncmp("zstd", value, len))
|
||||
+ type = BTRFS_COMPRESS_ZSTD;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
@@ -429,6 +433,8 @@ static const char *prop_compression_extract(struct inode *inode)
|
||||
return "zlib";
|
||||
case BTRFS_COMPRESS_LZO:
|
||||
return "lzo";
|
||||
+ case BTRFS_COMPRESS_ZSTD:
|
||||
+ return "zstd";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
|
||||
index 12540b6..c370dea 100644
|
||||
--- a/fs/btrfs/super.c
|
||||
+++ b/fs/btrfs/super.c
|
||||
@@ -513,6 +513,14 @@ int btrfs_parse_options(struct btrfs_fs_info *info, char *options,
|
||||
btrfs_clear_opt(info->mount_opt, NODATASUM);
|
||||
btrfs_set_fs_incompat(info, COMPRESS_LZO);
|
||||
no_compress = 0;
|
||||
+ } else if (strcmp(args[0].from, "zstd") == 0) {
|
||||
+ compress_type = "zstd";
|
||||
+ info->compress_type = BTRFS_COMPRESS_ZSTD;
|
||||
+ btrfs_set_opt(info->mount_opt, COMPRESS);
|
||||
+ btrfs_clear_opt(info->mount_opt, NODATACOW);
|
||||
+ btrfs_clear_opt(info->mount_opt, NODATASUM);
|
||||
+ btrfs_set_fs_incompat(info, COMPRESS_ZSTD);
|
||||
+ no_compress = 0;
|
||||
} else if (strncmp(args[0].from, "no", 2) == 0) {
|
||||
compress_type = "no";
|
||||
btrfs_clear_opt(info->mount_opt, COMPRESS);
|
||||
@@ -1227,8 +1235,10 @@ static int btrfs_show_options(struct seq_file *seq, struct dentry *dentry)
|
||||
if (btrfs_test_opt(info, COMPRESS)) {
|
||||
if (info->compress_type == BTRFS_COMPRESS_ZLIB)
|
||||
compress_type = "zlib";
|
||||
- else
|
||||
+ else if (info->compress_type == BTRFS_COMPRESS_LZO)
|
||||
compress_type = "lzo";
|
||||
+ else
|
||||
+ compress_type = "zstd";
|
||||
if (btrfs_test_opt(info, FORCE_COMPRESS))
|
||||
seq_printf(seq, ",compress-force=%s", compress_type);
|
||||
else
|
||||
diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c
|
||||
index c2d5f35..2b6d37c 100644
|
||||
--- a/fs/btrfs/sysfs.c
|
||||
+++ b/fs/btrfs/sysfs.c
|
||||
@@ -200,6 +200,7 @@ BTRFS_FEAT_ATTR_INCOMPAT(mixed_backref, MIXED_BACKREF);
|
||||
BTRFS_FEAT_ATTR_INCOMPAT(default_subvol, DEFAULT_SUBVOL);
|
||||
BTRFS_FEAT_ATTR_INCOMPAT(mixed_groups, MIXED_GROUPS);
|
||||
BTRFS_FEAT_ATTR_INCOMPAT(compress_lzo, COMPRESS_LZO);
|
||||
+BTRFS_FEAT_ATTR_INCOMPAT(compress_zstd, COMPRESS_ZSTD);
|
||||
BTRFS_FEAT_ATTR_INCOMPAT(big_metadata, BIG_METADATA);
|
||||
BTRFS_FEAT_ATTR_INCOMPAT(extended_iref, EXTENDED_IREF);
|
||||
BTRFS_FEAT_ATTR_INCOMPAT(raid56, RAID56);
|
||||
@@ -212,6 +213,7 @@ static struct attribute *btrfs_supported_feature_attrs[] = {
|
||||
BTRFS_FEAT_ATTR_PTR(default_subvol),
|
||||
BTRFS_FEAT_ATTR_PTR(mixed_groups),
|
||||
BTRFS_FEAT_ATTR_PTR(compress_lzo),
|
||||
+ BTRFS_FEAT_ATTR_PTR(compress_zstd),
|
||||
BTRFS_FEAT_ATTR_PTR(big_metadata),
|
||||
BTRFS_FEAT_ATTR_PTR(extended_iref),
|
||||
BTRFS_FEAT_ATTR_PTR(raid56),
|
||||
diff --git a/fs/btrfs/zstd.c b/fs/btrfs/zstd.c
|
||||
new file mode 100644
|
||||
index 0000000..607ce47
|
||||
--- /dev/null
|
||||
+++ b/fs/btrfs/zstd.c
|
||||
@@ -0,0 +1,432 @@
|
||||
+/*
|
||||
+ * Copyright (c) 2016-present, Facebook, Inc.
|
||||
+ * All rights reserved.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public
|
||||
+ * License v2 as published by the Free Software Foundation.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ * General Public License for more details.
|
||||
+ */
|
||||
+#include <linux/bio.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/mm.h>
|
||||
+#include <linux/pagemap.h>
|
||||
+#include <linux/refcount.h>
|
||||
+#include <linux/sched.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/zstd.h>
|
||||
+#include "compression.h"
|
||||
+
|
||||
+#define ZSTD_BTRFS_MAX_WINDOWLOG 17
|
||||
+#define ZSTD_BTRFS_MAX_INPUT (1 << ZSTD_BTRFS_MAX_WINDOWLOG)
|
||||
+#define ZSTD_BTRFS_DEFAULT_LEVEL 3
|
||||
+
|
||||
+static ZSTD_parameters zstd_get_btrfs_parameters(size_t src_len)
|
||||
+{
|
||||
+ ZSTD_parameters params = ZSTD_getParams(ZSTD_BTRFS_DEFAULT_LEVEL,
|
||||
+ src_len, 0);
|
||||
+
|
||||
+ if (params.cParams.windowLog > ZSTD_BTRFS_MAX_WINDOWLOG)
|
||||
+ params.cParams.windowLog = ZSTD_BTRFS_MAX_WINDOWLOG;
|
||||
+ WARN_ON(src_len > ZSTD_BTRFS_MAX_INPUT);
|
||||
+ return params;
|
||||
+}
|
||||
+
|
||||
+struct workspace {
|
||||
+ void *mem;
|
||||
+ size_t size;
|
||||
+ char *buf;
|
||||
+ struct list_head list;
|
||||
+};
|
||||
+
|
||||
+static void zstd_free_workspace(struct list_head *ws)
|
||||
+{
|
||||
+ struct workspace *workspace = list_entry(ws, struct workspace, list);
|
||||
+
|
||||
+ kvfree(workspace->mem);
|
||||
+ kfree(workspace->buf);
|
||||
+ kfree(workspace);
|
||||
+}
|
||||
+
|
||||
+static struct list_head *zstd_alloc_workspace(void)
|
||||
+{
|
||||
+ ZSTD_parameters params =
|
||||
+ zstd_get_btrfs_parameters(ZSTD_BTRFS_MAX_INPUT);
|
||||
+ struct workspace *workspace;
|
||||
+
|
||||
+ workspace = kzalloc(sizeof(*workspace), GFP_KERNEL);
|
||||
+ if (!workspace)
|
||||
+ return ERR_PTR(-ENOMEM);
|
||||
+
|
||||
+ workspace->size = max_t(size_t,
|
||||
+ ZSTD_CStreamWorkspaceBound(params.cParams),
|
||||
+ ZSTD_DStreamWorkspaceBound(ZSTD_BTRFS_MAX_INPUT));
|
||||
+ workspace->mem = kvmalloc(workspace->size, GFP_KERNEL);
|
||||
+ workspace->buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
+ if (!workspace->mem || !workspace->buf)
|
||||
+ goto fail;
|
||||
+
|
||||
+ INIT_LIST_HEAD(&workspace->list);
|
||||
+
|
||||
+ return &workspace->list;
|
||||
+fail:
|
||||
+ zstd_free_workspace(&workspace->list);
|
||||
+ return ERR_PTR(-ENOMEM);
|
||||
+}
|
||||
+
|
||||
+static int zstd_compress_pages(struct list_head *ws,
|
||||
+ struct address_space *mapping,
|
||||
+ u64 start,
|
||||
+ struct page **pages,
|
||||
+ unsigned long *out_pages,
|
||||
+ unsigned long *total_in,
|
||||
+ unsigned long *total_out)
|
||||
+{
|
||||
+ struct workspace *workspace = list_entry(ws, struct workspace, list);
|
||||
+ ZSTD_CStream *stream;
|
||||
+ int ret = 0;
|
||||
+ int nr_pages = 0;
|
||||
+ struct page *in_page = NULL; /* The current page to read */
|
||||
+ struct page *out_page = NULL; /* The current page to write to */
|
||||
+ ZSTD_inBuffer in_buf = { NULL, 0, 0 };
|
||||
+ ZSTD_outBuffer out_buf = { NULL, 0, 0 };
|
||||
+ unsigned long tot_in = 0;
|
||||
+ unsigned long tot_out = 0;
|
||||
+ unsigned long len = *total_out;
|
||||
+ const unsigned long nr_dest_pages = *out_pages;
|
||||
+ unsigned long max_out = nr_dest_pages * PAGE_SIZE;
|
||||
+ ZSTD_parameters params = zstd_get_btrfs_parameters(len);
|
||||
+
|
||||
+ *out_pages = 0;
|
||||
+ *total_out = 0;
|
||||
+ *total_in = 0;
|
||||
+
|
||||
+ /* Initialize the stream */
|
||||
+ stream = ZSTD_initCStream(params, len, workspace->mem,
|
||||
+ workspace->size);
|
||||
+ if (!stream) {
|
||||
+ pr_warn("BTRFS: ZSTD_initCStream failed\n");
|
||||
+ ret = -EIO;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ /* map in the first page of input data */
|
||||
+ in_page = find_get_page(mapping, start >> PAGE_SHIFT);
|
||||
+ in_buf.src = kmap(in_page);
|
||||
+ in_buf.pos = 0;
|
||||
+ in_buf.size = min_t(size_t, len, PAGE_SIZE);
|
||||
+
|
||||
+
|
||||
+ /* Allocate and map in the output buffer */
|
||||
+ out_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM);
|
||||
+ if (out_page == NULL) {
|
||||
+ ret = -ENOMEM;
|
||||
+ goto out;
|
||||
+ }
|
||||
+ pages[nr_pages++] = out_page;
|
||||
+ out_buf.dst = kmap(out_page);
|
||||
+ out_buf.pos = 0;
|
||||
+ out_buf.size = min_t(size_t, max_out, PAGE_SIZE);
|
||||
+
|
||||
+ while (1) {
|
||||
+ size_t ret2;
|
||||
+
|
||||
+ ret2 = ZSTD_compressStream(stream, &out_buf, &in_buf);
|
||||
+ if (ZSTD_isError(ret2)) {
|
||||
+ pr_debug("BTRFS: ZSTD_compressStream returned %d\n",
|
||||
+ ZSTD_getErrorCode(ret2));
|
||||
+ ret = -EIO;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ /* Check to see if we are making it bigger */
|
||||
+ if (tot_in + in_buf.pos > 8192 &&
|
||||
+ tot_in + in_buf.pos <
|
||||
+ tot_out + out_buf.pos) {
|
||||
+ ret = -E2BIG;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ /* We've reached the end of our output range */
|
||||
+ if (out_buf.pos >= max_out) {
|
||||
+ tot_out += out_buf.pos;
|
||||
+ ret = -E2BIG;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ /* Check if we need more output space */
|
||||
+ if (out_buf.pos == out_buf.size) {
|
||||
+ tot_out += PAGE_SIZE;
|
||||
+ max_out -= PAGE_SIZE;
|
||||
+ kunmap(out_page);
|
||||
+ if (nr_pages == nr_dest_pages) {
|
||||
+ out_page = NULL;
|
||||
+ ret = -E2BIG;
|
||||
+ goto out;
|
||||
+ }
|
||||
+ out_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM);
|
||||
+ if (out_page == NULL) {
|
||||
+ ret = -ENOMEM;
|
||||
+ goto out;
|
||||
+ }
|
||||
+ pages[nr_pages++] = out_page;
|
||||
+ out_buf.dst = kmap(out_page);
|
||||
+ out_buf.pos = 0;
|
||||
+ out_buf.size = min_t(size_t, max_out, PAGE_SIZE);
|
||||
+ }
|
||||
+
|
||||
+ /* We've reached the end of the input */
|
||||
+ if (in_buf.pos >= len) {
|
||||
+ tot_in += in_buf.pos;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ /* Check if we need more input */
|
||||
+ if (in_buf.pos == in_buf.size) {
|
||||
+ tot_in += PAGE_SIZE;
|
||||
+ kunmap(in_page);
|
||||
+ put_page(in_page);
|
||||
+
|
||||
+ start += PAGE_SIZE;
|
||||
+ len -= PAGE_SIZE;
|
||||
+ in_page = find_get_page(mapping, start >> PAGE_SHIFT);
|
||||
+ in_buf.src = kmap(in_page);
|
||||
+ in_buf.pos = 0;
|
||||
+ in_buf.size = min_t(size_t, len, PAGE_SIZE);
|
||||
+ }
|
||||
+ }
|
||||
+ while (1) {
|
||||
+ size_t ret2;
|
||||
+
|
||||
+ ret2 = ZSTD_endStream(stream, &out_buf);
|
||||
+ if (ZSTD_isError(ret2)) {
|
||||
+ pr_debug("BTRFS: ZSTD_endStream returned %d\n",
|
||||
+ ZSTD_getErrorCode(ret2));
|
||||
+ ret = -EIO;
|
||||
+ goto out;
|
||||
+ }
|
||||
+ if (ret2 == 0) {
|
||||
+ tot_out += out_buf.pos;
|
||||
+ break;
|
||||
+ }
|
||||
+ if (out_buf.pos >= max_out) {
|
||||
+ tot_out += out_buf.pos;
|
||||
+ ret = -E2BIG;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ tot_out += PAGE_SIZE;
|
||||
+ max_out -= PAGE_SIZE;
|
||||
+ kunmap(out_page);
|
||||
+ if (nr_pages == nr_dest_pages) {
|
||||
+ out_page = NULL;
|
||||
+ ret = -E2BIG;
|
||||
+ goto out;
|
||||
+ }
|
||||
+ out_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM);
|
||||
+ if (out_page == NULL) {
|
||||
+ ret = -ENOMEM;
|
||||
+ goto out;
|
||||
+ }
|
||||
+ pages[nr_pages++] = out_page;
|
||||
+ out_buf.dst = kmap(out_page);
|
||||
+ out_buf.pos = 0;
|
||||
+ out_buf.size = min_t(size_t, max_out, PAGE_SIZE);
|
||||
+ }
|
||||
+
|
||||
+ if (tot_out >= tot_in) {
|
||||
+ ret = -E2BIG;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ ret = 0;
|
||||
+ *total_in = tot_in;
|
||||
+ *total_out = tot_out;
|
||||
+out:
|
||||
+ *out_pages = nr_pages;
|
||||
+ /* Cleanup */
|
||||
+ if (in_page) {
|
||||
+ kunmap(in_page);
|
||||
+ put_page(in_page);
|
||||
+ }
|
||||
+ if (out_page)
|
||||
+ kunmap(out_page);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb)
|
||||
+{
|
||||
+ struct workspace *workspace = list_entry(ws, struct workspace, list);
|
||||
+ struct page **pages_in = cb->compressed_pages;
|
||||
+ u64 disk_start = cb->start;
|
||||
+ struct bio *orig_bio = cb->orig_bio;
|
||||
+ size_t srclen = cb->compressed_len;
|
||||
+ ZSTD_DStream *stream;
|
||||
+ int ret = 0;
|
||||
+ unsigned long page_in_index = 0;
|
||||
+ unsigned long total_pages_in = DIV_ROUND_UP(srclen, PAGE_SIZE);
|
||||
+ unsigned long buf_start;
|
||||
+ unsigned long total_out = 0;
|
||||
+ ZSTD_inBuffer in_buf = { NULL, 0, 0 };
|
||||
+ ZSTD_outBuffer out_buf = { NULL, 0, 0 };
|
||||
+
|
||||
+ stream = ZSTD_initDStream(
|
||||
+ ZSTD_BTRFS_MAX_INPUT, workspace->mem, workspace->size);
|
||||
+ if (!stream) {
|
||||
+ pr_debug("BTRFS: ZSTD_initDStream failed\n");
|
||||
+ ret = -EIO;
|
||||
+ goto done;
|
||||
+ }
|
||||
+
|
||||
+ in_buf.src = kmap(pages_in[page_in_index]);
|
||||
+ in_buf.pos = 0;
|
||||
+ in_buf.size = min_t(size_t, srclen, PAGE_SIZE);
|
||||
+
|
||||
+ out_buf.dst = workspace->buf;
|
||||
+ out_buf.pos = 0;
|
||||
+ out_buf.size = PAGE_SIZE;
|
||||
+
|
||||
+ while (1) {
|
||||
+ size_t ret2;
|
||||
+
|
||||
+ ret2 = ZSTD_decompressStream(stream, &out_buf, &in_buf);
|
||||
+ if (ZSTD_isError(ret2)) {
|
||||
+ pr_debug("BTRFS: ZSTD_decompressStream returned %d\n",
|
||||
+ ZSTD_getErrorCode(ret2));
|
||||
+ ret = -EIO;
|
||||
+ goto done;
|
||||
+ }
|
||||
+ buf_start = total_out;
|
||||
+ total_out += out_buf.pos;
|
||||
+ out_buf.pos = 0;
|
||||
+
|
||||
+ ret = btrfs_decompress_buf2page(out_buf.dst, buf_start,
|
||||
+ total_out, disk_start, orig_bio);
|
||||
+ if (ret == 0)
|
||||
+ break;
|
||||
+
|
||||
+ if (in_buf.pos >= srclen)
|
||||
+ break;
|
||||
+
|
||||
+ /* Check if we've hit the end of a frame */
|
||||
+ if (ret2 == 0)
|
||||
+ break;
|
||||
+
|
||||
+ if (in_buf.pos == in_buf.size) {
|
||||
+ kunmap(pages_in[page_in_index++]);
|
||||
+ if (page_in_index >= total_pages_in) {
|
||||
+ in_buf.src = NULL;
|
||||
+ ret = -EIO;
|
||||
+ goto done;
|
||||
+ }
|
||||
+ srclen -= PAGE_SIZE;
|
||||
+ in_buf.src = kmap(pages_in[page_in_index]);
|
||||
+ in_buf.pos = 0;
|
||||
+ in_buf.size = min_t(size_t, srclen, PAGE_SIZE);
|
||||
+ }
|
||||
+ }
|
||||
+ ret = 0;
|
||||
+ zero_fill_bio(orig_bio);
|
||||
+done:
|
||||
+ if (in_buf.src)
|
||||
+ kunmap(pages_in[page_in_index]);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int zstd_decompress(struct list_head *ws, unsigned char *data_in,
|
||||
+ struct page *dest_page,
|
||||
+ unsigned long start_byte,
|
||||
+ size_t srclen, size_t destlen)
|
||||
+{
|
||||
+ struct workspace *workspace = list_entry(ws, struct workspace, list);
|
||||
+ ZSTD_DStream *stream;
|
||||
+ int ret = 0;
|
||||
+ size_t ret2;
|
||||
+ ZSTD_inBuffer in_buf = { NULL, 0, 0 };
|
||||
+ ZSTD_outBuffer out_buf = { NULL, 0, 0 };
|
||||
+ unsigned long total_out = 0;
|
||||
+ unsigned long pg_offset = 0;
|
||||
+ char *kaddr;
|
||||
+
|
||||
+ stream = ZSTD_initDStream(
|
||||
+ ZSTD_BTRFS_MAX_INPUT, workspace->mem, workspace->size);
|
||||
+ if (!stream) {
|
||||
+ pr_warn("BTRFS: ZSTD_initDStream failed\n");
|
||||
+ ret = -EIO;
|
||||
+ goto finish;
|
||||
+ }
|
||||
+
|
||||
+ destlen = min_t(size_t, destlen, PAGE_SIZE);
|
||||
+
|
||||
+ in_buf.src = data_in;
|
||||
+ in_buf.pos = 0;
|
||||
+ in_buf.size = srclen;
|
||||
+
|
||||
+ out_buf.dst = workspace->buf;
|
||||
+ out_buf.pos = 0;
|
||||
+ out_buf.size = PAGE_SIZE;
|
||||
+
|
||||
+ ret2 = 1;
|
||||
+ while (pg_offset < destlen && in_buf.pos < in_buf.size) {
|
||||
+ unsigned long buf_start;
|
||||
+ unsigned long buf_offset;
|
||||
+ unsigned long bytes;
|
||||
+
|
||||
+ /* Check if the frame is over and we still need more input */
|
||||
+ if (ret2 == 0) {
|
||||
+ pr_debug("BTRFS: ZSTD_decompressStream ended early\n");
|
||||
+ ret = -EIO;
|
||||
+ goto finish;
|
||||
+ }
|
||||
+ ret2 = ZSTD_decompressStream(stream, &out_buf, &in_buf);
|
||||
+ if (ZSTD_isError(ret2)) {
|
||||
+ pr_debug("BTRFS: ZSTD_decompressStream returned %d\n",
|
||||
+ ZSTD_getErrorCode(ret2));
|
||||
+ ret = -EIO;
|
||||
+ goto finish;
|
||||
+ }
|
||||
+
|
||||
+ buf_start = total_out;
|
||||
+ total_out += out_buf.pos;
|
||||
+ out_buf.pos = 0;
|
||||
+
|
||||
+ if (total_out <= start_byte)
|
||||
+ continue;
|
||||
+
|
||||
+ if (total_out > start_byte && buf_start < start_byte)
|
||||
+ buf_offset = start_byte - buf_start;
|
||||
+ else
|
||||
+ buf_offset = 0;
|
||||
+
|
||||
+ bytes = min_t(unsigned long, destlen - pg_offset,
|
||||
+ out_buf.size - buf_offset);
|
||||
+
|
||||
+ kaddr = kmap_atomic(dest_page);
|
||||
+ memcpy(kaddr + pg_offset, out_buf.dst + buf_offset, bytes);
|
||||
+ kunmap_atomic(kaddr);
|
||||
+
|
||||
+ pg_offset += bytes;
|
||||
+ }
|
||||
+ ret = 0;
|
||||
+finish:
|
||||
+ if (pg_offset < destlen) {
|
||||
+ kaddr = kmap_atomic(dest_page);
|
||||
+ memset(kaddr + pg_offset, 0, destlen - pg_offset);
|
||||
+ kunmap_atomic(kaddr);
|
||||
+ }
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+const struct btrfs_compress_op btrfs_zstd_compress = {
|
||||
+ .alloc_workspace = zstd_alloc_workspace,
|
||||
+ .free_workspace = zstd_free_workspace,
|
||||
+ .compress_pages = zstd_compress_pages,
|
||||
+ .decompress_bio = zstd_decompress_bio,
|
||||
+ .decompress = zstd_decompress,
|
||||
+};
|
||||
diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h
|
||||
index 9aa74f3..378230c 100644
|
||||
--- a/include/uapi/linux/btrfs.h
|
||||
+++ b/include/uapi/linux/btrfs.h
|
||||
@@ -255,13 +255,7 @@ struct btrfs_ioctl_fs_info_args {
|
||||
#define BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL (1ULL << 1)
|
||||
#define BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS (1ULL << 2)
|
||||
#define BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO (1ULL << 3)
|
||||
-/*
|
||||
- * some patches floated around with a second compression method
|
||||
- * lets save that incompat here for when they do get in
|
||||
- * Note we don't actually support it, we're just reserving the
|
||||
- * number
|
||||
- */
|
||||
-#define BTRFS_FEATURE_INCOMPAT_COMPRESS_LZOv2 (1ULL << 4)
|
||||
+#define BTRFS_FEATURE_INCOMPAT_COMPRESS_ZSTD (1ULL << 4)
|
||||
|
||||
/*
|
||||
* older kernels tried to do bigger metadata blocks, but the
|
||||
--
|
||||
2.9.3
|
||||
@@ -0,0 +1,306 @@
|
||||
From 46bf8f6d30d6ddf2446c110f122482b5e5e16933 Mon Sep 17 00:00:00 2001
|
||||
From: Sean Purcell <me@seanp.xyz>
|
||||
Date: Mon, 17 Jul 2017 17:08:59 -0700
|
||||
Subject: [PATCH v5 4/5] squashfs: Add zstd support
|
||||
|
||||
Add zstd compression and decompression support to SquashFS. zstd is a
|
||||
great fit for SquashFS because it can compress at ratios approaching xz,
|
||||
while decompressing twice as fast as zlib. For SquashFS in particular,
|
||||
it can decompress as fast as lzo and lz4. It also has the flexibility
|
||||
to turn down the compression ratio for faster compression times.
|
||||
|
||||
The compression benchmark is run on the file tree from the SquashFS archive
|
||||
found in ubuntu-16.10-desktop-amd64.iso [1]. It uses `mksquashfs` with the
|
||||
default block size (128 KB) and and various compression algorithms/levels.
|
||||
xz and zstd are also benchmarked with 256 KB blocks. The decompression
|
||||
benchmark times how long it takes to `tar` the file tree into `/dev/null`.
|
||||
See the benchmark file in the upstream zstd source repository located under
|
||||
`contrib/linux-kernel/squashfs-benchmark.sh` [2] for details.
|
||||
|
||||
I ran the benchmarks on a Ubuntu 14.04 VM with 2 cores and 4 GiB of RAM.
|
||||
The VM is running on a MacBook Pro with a 3.1 GHz Intel Core i7 processor,
|
||||
16 GB of RAM, and a SSD.
|
||||
|
||||
| Method | Ratio | Compression MB/s | Decompression MB/s |
|
||||
|----------------|-------|------------------|--------------------|
|
||||
| gzip | 2.92 | 15 | 128 |
|
||||
| lzo | 2.64 | 9.5 | 217 |
|
||||
| lz4 | 2.12 | 94 | 218 |
|
||||
| xz | 3.43 | 5.5 | 35 |
|
||||
| xz 256 KB | 3.53 | 5.4 | 40 |
|
||||
| zstd 1 | 2.71 | 96 | 210 |
|
||||
| zstd 5 | 2.93 | 69 | 198 |
|
||||
| zstd 10 | 3.01 | 41 | 225 |
|
||||
| zstd 15 | 3.13 | 11.4 | 224 |
|
||||
| zstd 16 256 KB | 3.24 | 8.1 | 210 |
|
||||
|
||||
This patch was written by Sean Purcell <me@seanp.xyz>, but I will be
|
||||
taking over the submission process.
|
||||
|
||||
[1] http://releases.ubuntu.com/16.10/
|
||||
[2] https://github.com/facebook/zstd/blob/dev/contrib/linux-kernel/squashfs-benchmark.sh
|
||||
|
||||
zstd source repository: https://github.com/facebook/zstd
|
||||
|
||||
Signed-off-by: Sean Purcell <me@seanp.xyz>
|
||||
Signed-off-by: Nick Terrell <terrelln@fb.com>
|
||||
---
|
||||
v3 -> v4:
|
||||
- Fix minor linter warnings
|
||||
|
||||
v4 -> v5:
|
||||
- Fix ZSTD_DStream initialization code in squashfs
|
||||
- Fix patch documentation to reflect that Sean Purcell is the author
|
||||
|
||||
fs/squashfs/Kconfig | 14 +++++
|
||||
fs/squashfs/Makefile | 1 +
|
||||
fs/squashfs/decompressor.c | 7 +++
|
||||
fs/squashfs/decompressor.h | 4 ++
|
||||
fs/squashfs/squashfs_fs.h | 1 +
|
||||
fs/squashfs/zstd_wrapper.c | 151 +++++++++++++++++++++++++++++++++++++++++++++
|
||||
6 files changed, 178 insertions(+)
|
||||
create mode 100644 fs/squashfs/zstd_wrapper.c
|
||||
|
||||
diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig
|
||||
index ffb093e..1adb334 100644
|
||||
--- a/fs/squashfs/Kconfig
|
||||
+++ b/fs/squashfs/Kconfig
|
||||
@@ -165,6 +165,20 @@ config SQUASHFS_XZ
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
+config SQUASHFS_ZSTD
|
||||
+ bool "Include support for ZSTD compressed file systems"
|
||||
+ depends on SQUASHFS
|
||||
+ select ZSTD_DECOMPRESS
|
||||
+ help
|
||||
+ Saying Y here includes support for reading Squashfs file systems
|
||||
+ compressed with ZSTD compression. ZSTD gives better compression than
|
||||
+ the default ZLIB compression, while using less CPU.
|
||||
+
|
||||
+ ZSTD is not the standard compression used in Squashfs and so most
|
||||
+ file systems will be readable without selecting this option.
|
||||
+
|
||||
+ If unsure, say N.
|
||||
+
|
||||
config SQUASHFS_4K_DEVBLK_SIZE
|
||||
bool "Use 4K device block size?"
|
||||
depends on SQUASHFS
|
||||
diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile
|
||||
index 246a6f3..6655631 100644
|
||||
--- a/fs/squashfs/Makefile
|
||||
+++ b/fs/squashfs/Makefile
|
||||
@@ -15,3 +15,4 @@ squashfs-$(CONFIG_SQUASHFS_LZ4) += lz4_wrapper.o
|
||||
squashfs-$(CONFIG_SQUASHFS_LZO) += lzo_wrapper.o
|
||||
squashfs-$(CONFIG_SQUASHFS_XZ) += xz_wrapper.o
|
||||
squashfs-$(CONFIG_SQUASHFS_ZLIB) += zlib_wrapper.o
|
||||
+squashfs-$(CONFIG_SQUASHFS_ZSTD) += zstd_wrapper.o
|
||||
diff --git a/fs/squashfs/decompressor.c b/fs/squashfs/decompressor.c
|
||||
index d2bc136..8366398 100644
|
||||
--- a/fs/squashfs/decompressor.c
|
||||
+++ b/fs/squashfs/decompressor.c
|
||||
@@ -65,6 +65,12 @@ static const struct squashfs_decompressor squashfs_zlib_comp_ops = {
|
||||
};
|
||||
#endif
|
||||
|
||||
+#ifndef CONFIG_SQUASHFS_ZSTD
|
||||
+static const struct squashfs_decompressor squashfs_zstd_comp_ops = {
|
||||
+ NULL, NULL, NULL, NULL, ZSTD_COMPRESSION, "zstd", 0
|
||||
+};
|
||||
+#endif
|
||||
+
|
||||
static const struct squashfs_decompressor squashfs_unknown_comp_ops = {
|
||||
NULL, NULL, NULL, NULL, 0, "unknown", 0
|
||||
};
|
||||
@@ -75,6 +81,7 @@ static const struct squashfs_decompressor *decompressor[] = {
|
||||
&squashfs_lzo_comp_ops,
|
||||
&squashfs_xz_comp_ops,
|
||||
&squashfs_lzma_unsupported_comp_ops,
|
||||
+ &squashfs_zstd_comp_ops,
|
||||
&squashfs_unknown_comp_ops
|
||||
};
|
||||
|
||||
diff --git a/fs/squashfs/decompressor.h b/fs/squashfs/decompressor.h
|
||||
index a25713c..0f5a8e4 100644
|
||||
--- a/fs/squashfs/decompressor.h
|
||||
+++ b/fs/squashfs/decompressor.h
|
||||
@@ -58,4 +58,8 @@ extern const struct squashfs_decompressor squashfs_lzo_comp_ops;
|
||||
extern const struct squashfs_decompressor squashfs_zlib_comp_ops;
|
||||
#endif
|
||||
|
||||
+#ifdef CONFIG_SQUASHFS_ZSTD
|
||||
+extern const struct squashfs_decompressor squashfs_zstd_comp_ops;
|
||||
+#endif
|
||||
+
|
||||
#endif
|
||||
diff --git a/fs/squashfs/squashfs_fs.h b/fs/squashfs/squashfs_fs.h
|
||||
index 506f4ba..24d12fd 100644
|
||||
--- a/fs/squashfs/squashfs_fs.h
|
||||
+++ b/fs/squashfs/squashfs_fs.h
|
||||
@@ -241,6 +241,7 @@ struct meta_index {
|
||||
#define LZO_COMPRESSION 3
|
||||
#define XZ_COMPRESSION 4
|
||||
#define LZ4_COMPRESSION 5
|
||||
+#define ZSTD_COMPRESSION 6
|
||||
|
||||
struct squashfs_super_block {
|
||||
__le32 s_magic;
|
||||
diff --git a/fs/squashfs/zstd_wrapper.c b/fs/squashfs/zstd_wrapper.c
|
||||
new file mode 100644
|
||||
index 0000000..eeaabf8
|
||||
--- /dev/null
|
||||
+++ b/fs/squashfs/zstd_wrapper.c
|
||||
@@ -0,0 +1,151 @@
|
||||
+/*
|
||||
+ * Squashfs - a compressed read only filesystem for Linux
|
||||
+ *
|
||||
+ * Copyright (c) 2016-present, Facebook, Inc.
|
||||
+ * All rights reserved.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License
|
||||
+ * as published by the Free Software Foundation; either version 2,
|
||||
+ * or (at your option) any later version.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ *
|
||||
+ * zstd_wrapper.c
|
||||
+ */
|
||||
+
|
||||
+#include <linux/mutex.h>
|
||||
+#include <linux/buffer_head.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/zstd.h>
|
||||
+#include <linux/vmalloc.h>
|
||||
+
|
||||
+#include "squashfs_fs.h"
|
||||
+#include "squashfs_fs_sb.h"
|
||||
+#include "squashfs.h"
|
||||
+#include "decompressor.h"
|
||||
+#include "page_actor.h"
|
||||
+
|
||||
+struct workspace {
|
||||
+ void *mem;
|
||||
+ size_t mem_size;
|
||||
+ size_t window_size;
|
||||
+};
|
||||
+
|
||||
+static void *zstd_init(struct squashfs_sb_info *msblk, void *buff)
|
||||
+{
|
||||
+ struct workspace *wksp = kmalloc(sizeof(*wksp), GFP_KERNEL);
|
||||
+
|
||||
+ if (wksp == NULL)
|
||||
+ goto failed;
|
||||
+ wksp->window_size = max_t(size_t,
|
||||
+ msblk->block_size, SQUASHFS_METADATA_SIZE);
|
||||
+ wksp->mem_size = ZSTD_DStreamWorkspaceBound(wksp->window_size);
|
||||
+ wksp->mem = vmalloc(wksp->mem_size);
|
||||
+ if (wksp->mem == NULL)
|
||||
+ goto failed;
|
||||
+
|
||||
+ return wksp;
|
||||
+
|
||||
+failed:
|
||||
+ ERROR("Failed to allocate zstd workspace\n");
|
||||
+ kfree(wksp);
|
||||
+ return ERR_PTR(-ENOMEM);
|
||||
+}
|
||||
+
|
||||
+
|
||||
+static void zstd_free(void *strm)
|
||||
+{
|
||||
+ struct workspace *wksp = strm;
|
||||
+
|
||||
+ if (wksp)
|
||||
+ vfree(wksp->mem);
|
||||
+ kfree(wksp);
|
||||
+}
|
||||
+
|
||||
+
|
||||
+static int zstd_uncompress(struct squashfs_sb_info *msblk, void *strm,
|
||||
+ struct buffer_head **bh, int b, int offset, int length,
|
||||
+ struct squashfs_page_actor *output)
|
||||
+{
|
||||
+ struct workspace *wksp = strm;
|
||||
+ ZSTD_DStream *stream;
|
||||
+ size_t total_out = 0;
|
||||
+ size_t zstd_err;
|
||||
+ int k = 0;
|
||||
+ ZSTD_inBuffer in_buf = { NULL, 0, 0 };
|
||||
+ ZSTD_outBuffer out_buf = { NULL, 0, 0 };
|
||||
+
|
||||
+ stream = ZSTD_initDStream(wksp->window_size, wksp->mem, wksp->mem_size);
|
||||
+
|
||||
+ if (!stream) {
|
||||
+ ERROR("Failed to initialize zstd decompressor\n");
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ out_buf.size = PAGE_SIZE;
|
||||
+ out_buf.dst = squashfs_first_page(output);
|
||||
+
|
||||
+ do {
|
||||
+ if (in_buf.pos == in_buf.size && k < b) {
|
||||
+ int avail = min(length, msblk->devblksize - offset);
|
||||
+
|
||||
+ length -= avail;
|
||||
+ in_buf.src = bh[k]->b_data + offset;
|
||||
+ in_buf.size = avail;
|
||||
+ in_buf.pos = 0;
|
||||
+ offset = 0;
|
||||
+ }
|
||||
+
|
||||
+ if (out_buf.pos == out_buf.size) {
|
||||
+ out_buf.dst = squashfs_next_page(output);
|
||||
+ if (out_buf.dst == NULL) {
|
||||
+ /* Shouldn't run out of pages
|
||||
+ * before stream is done.
|
||||
+ */
|
||||
+ squashfs_finish_page(output);
|
||||
+ goto out;
|
||||
+ }
|
||||
+ out_buf.pos = 0;
|
||||
+ out_buf.size = PAGE_SIZE;
|
||||
+ }
|
||||
+
|
||||
+ total_out -= out_buf.pos;
|
||||
+ zstd_err = ZSTD_decompressStream(stream, &out_buf, &in_buf);
|
||||
+ total_out += out_buf.pos; /* add the additional data produced */
|
||||
+
|
||||
+ if (in_buf.pos == in_buf.size && k < b)
|
||||
+ put_bh(bh[k++]);
|
||||
+ } while (zstd_err != 0 && !ZSTD_isError(zstd_err));
|
||||
+
|
||||
+ squashfs_finish_page(output);
|
||||
+
|
||||
+ if (ZSTD_isError(zstd_err)) {
|
||||
+ ERROR("zstd decompression error: %d\n",
|
||||
+ (int)ZSTD_getErrorCode(zstd_err));
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ if (k < b)
|
||||
+ goto out;
|
||||
+
|
||||
+ return (int)total_out;
|
||||
+
|
||||
+out:
|
||||
+ for (; k < b; k++)
|
||||
+ put_bh(bh[k]);
|
||||
+
|
||||
+ return -EIO;
|
||||
+}
|
||||
+
|
||||
+const struct squashfs_decompressor squashfs_zstd_comp_ops = {
|
||||
+ .init = zstd_init,
|
||||
+ .free = zstd_free,
|
||||
+ .decompress = zstd_uncompress,
|
||||
+ .id = ZSTD_COMPRESSION,
|
||||
+ .name = "zstd",
|
||||
+ .supported = 1
|
||||
+};
|
||||
--
|
||||
2.9.3
|
||||
@@ -0,0 +1,424 @@
|
||||
From 308795a7713ca6fcd468b60fba9a2fca99cee6a0 Mon Sep 17 00:00:00 2001
|
||||
From: Nick Terrell <terrelln@fb.com>
|
||||
Date: Wed, 2 Aug 2017 18:02:13 -0700
|
||||
Subject: [PATCH v5 5/5] crypto: Add zstd support
|
||||
|
||||
Adds zstd support to crypto and scompress. Only supports the default
|
||||
level.
|
||||
|
||||
Signed-off-by: Nick Terrell <terrelln@fb.com>
|
||||
---
|
||||
crypto/Kconfig | 9 ++
|
||||
crypto/Makefile | 1 +
|
||||
crypto/testmgr.c | 10 +++
|
||||
crypto/testmgr.h | 71 +++++++++++++++
|
||||
crypto/zstd.c | 265 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
5 files changed, 356 insertions(+)
|
||||
create mode 100644 crypto/zstd.c
|
||||
|
||||
diff --git a/crypto/Kconfig b/crypto/Kconfig
|
||||
index caa770e..4fc3936 100644
|
||||
--- a/crypto/Kconfig
|
||||
+++ b/crypto/Kconfig
|
||||
@@ -1662,6 +1662,15 @@ config CRYPTO_LZ4HC
|
||||
help
|
||||
This is the LZ4 high compression mode algorithm.
|
||||
|
||||
+config CRYPTO_ZSTD
|
||||
+ tristate "Zstd compression algorithm"
|
||||
+ select CRYPTO_ALGAPI
|
||||
+ select CRYPTO_ACOMP2
|
||||
+ select ZSTD_COMPRESS
|
||||
+ select ZSTD_DECOMPRESS
|
||||
+ help
|
||||
+ This is the zstd algorithm.
|
||||
+
|
||||
comment "Random Number Generation"
|
||||
|
||||
config CRYPTO_ANSI_CPRNG
|
||||
diff --git a/crypto/Makefile b/crypto/Makefile
|
||||
index d41f033..b22e1e8 100644
|
||||
--- a/crypto/Makefile
|
||||
+++ b/crypto/Makefile
|
||||
@@ -133,6 +133,7 @@ obj-$(CONFIG_CRYPTO_USER_API_HASH) += algif_hash.o
|
||||
obj-$(CONFIG_CRYPTO_USER_API_SKCIPHER) += algif_skcipher.o
|
||||
obj-$(CONFIG_CRYPTO_USER_API_RNG) += algif_rng.o
|
||||
obj-$(CONFIG_CRYPTO_USER_API_AEAD) += algif_aead.o
|
||||
+obj-$(CONFIG_CRYPTO_ZSTD) += zstd.o
|
||||
|
||||
ecdh_generic-y := ecc.o
|
||||
ecdh_generic-y += ecdh.o
|
||||
diff --git a/crypto/testmgr.c b/crypto/testmgr.c
|
||||
index 7125ba3..8a124d3 100644
|
||||
--- a/crypto/testmgr.c
|
||||
+++ b/crypto/testmgr.c
|
||||
@@ -3603,6 +3603,16 @@ static const struct alg_test_desc alg_test_descs[] = {
|
||||
.decomp = __VECS(zlib_deflate_decomp_tv_template)
|
||||
}
|
||||
}
|
||||
+ }, {
|
||||
+ .alg = "zstd",
|
||||
+ .test = alg_test_comp,
|
||||
+ .fips_allowed = 1,
|
||||
+ .suite = {
|
||||
+ .comp = {
|
||||
+ .comp = __VECS(zstd_comp_tv_template),
|
||||
+ .decomp = __VECS(zstd_decomp_tv_template)
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
};
|
||||
|
||||
diff --git a/crypto/testmgr.h b/crypto/testmgr.h
|
||||
index 6ceb0e2..e6b5920 100644
|
||||
--- a/crypto/testmgr.h
|
||||
+++ b/crypto/testmgr.h
|
||||
@@ -34631,4 +34631,75 @@ static const struct comp_testvec lz4hc_decomp_tv_template[] = {
|
||||
},
|
||||
};
|
||||
|
||||
+static const struct comp_testvec zstd_comp_tv_template[] = {
|
||||
+ {
|
||||
+ .inlen = 68,
|
||||
+ .outlen = 39,
|
||||
+ .input = "The algorithm is zstd. "
|
||||
+ "The algorithm is zstd. "
|
||||
+ "The algorithm is zstd.",
|
||||
+ .output = "\x28\xb5\x2f\xfd\x00\x50\xf5\x00\x00\xb8\x54\x68\x65"
|
||||
+ "\x20\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x20\x69\x73"
|
||||
+ "\x20\x7a\x73\x74\x64\x2e\x20\x01\x00\x55\x73\x36\x01"
|
||||
+ ,
|
||||
+ },
|
||||
+ {
|
||||
+ .inlen = 244,
|
||||
+ .outlen = 151,
|
||||
+ .input = "zstd, short for Zstandard, is a fast lossless "
|
||||
+ "compression algorithm, targeting real-time "
|
||||
+ "compression scenarios at zlib-level and better "
|
||||
+ "compression ratios. The zstd compression library "
|
||||
+ "provides in-memory compression and decompression "
|
||||
+ "functions.",
|
||||
+ .output = "\x28\xb5\x2f\xfd\x00\x50\x75\x04\x00\x42\x4b\x1e\x17"
|
||||
+ "\x90\x81\x31\x00\xf2\x2f\xe4\x36\xc9\xef\x92\x88\x32"
|
||||
+ "\xc9\xf2\x24\x94\xd8\x68\x9a\x0f\x00\x0c\xc4\x31\x6f"
|
||||
+ "\x0d\x0c\x38\xac\x5c\x48\x03\xcd\x63\x67\xc0\xf3\xad"
|
||||
+ "\x4e\x90\xaa\x78\xa0\xa4\xc5\x99\xda\x2f\xb6\x24\x60"
|
||||
+ "\xe2\x79\x4b\xaa\xb6\x6b\x85\x0b\xc9\xc6\x04\x66\x86"
|
||||
+ "\xe2\xcc\xe2\x25\x3f\x4f\x09\xcd\xb8\x9d\xdb\xc1\x90"
|
||||
+ "\xa9\x11\xbc\x35\x44\x69\x2d\x9c\x64\x4f\x13\x31\x64"
|
||||
+ "\xcc\xfb\x4d\x95\x93\x86\x7f\x33\x7f\x1a\xef\xe9\x30"
|
||||
+ "\xf9\x67\xa1\x94\x0a\x69\x0f\x60\xcd\xc3\xab\x99\xdc"
|
||||
+ "\x42\xed\x97\x05\x00\x33\xc3\x15\x95\x3a\x06\xa0\x0e"
|
||||
+ "\x20\xa9\x0e\x82\xb9\x43\x45\x01",
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static const struct comp_testvec zstd_decomp_tv_template[] = {
|
||||
+ {
|
||||
+ .inlen = 43,
|
||||
+ .outlen = 68,
|
||||
+ .input = "\x28\xb5\x2f\xfd\x04\x50\xf5\x00\x00\xb8\x54\x68\x65"
|
||||
+ "\x20\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x20\x69\x73"
|
||||
+ "\x20\x7a\x73\x74\x64\x2e\x20\x01\x00\x55\x73\x36\x01"
|
||||
+ "\x6b\xf4\x13\x35",
|
||||
+ .output = "The algorithm is zstd. "
|
||||
+ "The algorithm is zstd. "
|
||||
+ "The algorithm is zstd.",
|
||||
+ },
|
||||
+ {
|
||||
+ .inlen = 155,
|
||||
+ .outlen = 244,
|
||||
+ .input = "\x28\xb5\x2f\xfd\x04\x50\x75\x04\x00\x42\x4b\x1e\x17"
|
||||
+ "\x90\x81\x31\x00\xf2\x2f\xe4\x36\xc9\xef\x92\x88\x32"
|
||||
+ "\xc9\xf2\x24\x94\xd8\x68\x9a\x0f\x00\x0c\xc4\x31\x6f"
|
||||
+ "\x0d\x0c\x38\xac\x5c\x48\x03\xcd\x63\x67\xc0\xf3\xad"
|
||||
+ "\x4e\x90\xaa\x78\xa0\xa4\xc5\x99\xda\x2f\xb6\x24\x60"
|
||||
+ "\xe2\x79\x4b\xaa\xb6\x6b\x85\x0b\xc9\xc6\x04\x66\x86"
|
||||
+ "\xe2\xcc\xe2\x25\x3f\x4f\x09\xcd\xb8\x9d\xdb\xc1\x90"
|
||||
+ "\xa9\x11\xbc\x35\x44\x69\x2d\x9c\x64\x4f\x13\x31\x64"
|
||||
+ "\xcc\xfb\x4d\x95\x93\x86\x7f\x33\x7f\x1a\xef\xe9\x30"
|
||||
+ "\xf9\x67\xa1\x94\x0a\x69\x0f\x60\xcd\xc3\xab\x99\xdc"
|
||||
+ "\x42\xed\x97\x05\x00\x33\xc3\x15\x95\x3a\x06\xa0\x0e"
|
||||
+ "\x20\xa9\x0e\x82\xb9\x43\x45\x01\xaa\x6d\xda\x0d",
|
||||
+ .output = "zstd, short for Zstandard, is a fast lossless "
|
||||
+ "compression algorithm, targeting real-time "
|
||||
+ "compression scenarios at zlib-level and better "
|
||||
+ "compression ratios. The zstd compression library "
|
||||
+ "provides in-memory compression and decompression "
|
||||
+ "functions.",
|
||||
+ },
|
||||
+};
|
||||
#endif /* _CRYPTO_TESTMGR_H */
|
||||
diff --git a/crypto/zstd.c b/crypto/zstd.c
|
||||
new file mode 100644
|
||||
index 0000000..9a76b3e
|
||||
--- /dev/null
|
||||
+++ b/crypto/zstd.c
|
||||
@@ -0,0 +1,265 @@
|
||||
+/*
|
||||
+ * Cryptographic API.
|
||||
+ *
|
||||
+ * Copyright (c) 2017-present, Facebook, Inc.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify it
|
||||
+ * under the terms of the GNU General Public License version 2 as published by
|
||||
+ * the Free Software Foundation.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
+ * more details.
|
||||
+ */
|
||||
+#include <linux/crypto.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/interrupt.h>
|
||||
+#include <linux/mm.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/net.h>
|
||||
+#include <linux/vmalloc.h>
|
||||
+#include <linux/zstd.h>
|
||||
+#include <crypto/internal/scompress.h>
|
||||
+
|
||||
+
|
||||
+#define ZSTD_DEF_LEVEL 3
|
||||
+
|
||||
+struct zstd_ctx {
|
||||
+ ZSTD_CCtx *cctx;
|
||||
+ ZSTD_DCtx *dctx;
|
||||
+ void *cwksp;
|
||||
+ void *dwksp;
|
||||
+};
|
||||
+
|
||||
+static ZSTD_parameters zstd_params(void)
|
||||
+{
|
||||
+ return ZSTD_getParams(ZSTD_DEF_LEVEL, 0, 0);
|
||||
+}
|
||||
+
|
||||
+static int zstd_comp_init(struct zstd_ctx *ctx)
|
||||
+{
|
||||
+ int ret = 0;
|
||||
+ const ZSTD_parameters params = zstd_params();
|
||||
+ const size_t wksp_size = ZSTD_CCtxWorkspaceBound(params.cParams);
|
||||
+
|
||||
+ ctx->cwksp = vzalloc(wksp_size);
|
||||
+ if (!ctx->cwksp) {
|
||||
+ ret = -ENOMEM;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ ctx->cctx = ZSTD_initCCtx(ctx->cwksp, wksp_size);
|
||||
+ if (!ctx->cctx) {
|
||||
+ ret = -EINVAL;
|
||||
+ goto out_free;
|
||||
+ }
|
||||
+out:
|
||||
+ return ret;
|
||||
+out_free:
|
||||
+ vfree(ctx->cwksp);
|
||||
+ goto out;
|
||||
+}
|
||||
+
|
||||
+static int zstd_decomp_init(struct zstd_ctx *ctx)
|
||||
+{
|
||||
+ int ret = 0;
|
||||
+ const size_t wksp_size = ZSTD_DCtxWorkspaceBound();
|
||||
+
|
||||
+ ctx->dwksp = vzalloc(wksp_size);
|
||||
+ if (!ctx->dwksp) {
|
||||
+ ret = -ENOMEM;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ ctx->dctx = ZSTD_initDCtx(ctx->dwksp, wksp_size);
|
||||
+ if (!ctx->dctx) {
|
||||
+ ret = -EINVAL;
|
||||
+ goto out_free;
|
||||
+ }
|
||||
+out:
|
||||
+ return ret;
|
||||
+out_free:
|
||||
+ vfree(ctx->dwksp);
|
||||
+ goto out;
|
||||
+}
|
||||
+
|
||||
+static void zstd_comp_exit(struct zstd_ctx *ctx)
|
||||
+{
|
||||
+ vfree(ctx->cwksp);
|
||||
+ ctx->cwksp = NULL;
|
||||
+ ctx->cctx = NULL;
|
||||
+}
|
||||
+
|
||||
+static void zstd_decomp_exit(struct zstd_ctx *ctx)
|
||||
+{
|
||||
+ vfree(ctx->dwksp);
|
||||
+ ctx->dwksp = NULL;
|
||||
+ ctx->dctx = NULL;
|
||||
+}
|
||||
+
|
||||
+static int __zstd_init(void *ctx)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = zstd_comp_init(ctx);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ ret = zstd_decomp_init(ctx);
|
||||
+ if (ret)
|
||||
+ zstd_comp_exit(ctx);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static void *zstd_alloc_ctx(struct crypto_scomp *tfm)
|
||||
+{
|
||||
+ int ret;
|
||||
+ struct zstd_ctx *ctx;
|
||||
+
|
||||
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
||||
+ if (!ctx)
|
||||
+ return ERR_PTR(-ENOMEM);
|
||||
+
|
||||
+ ret = __zstd_init(ctx);
|
||||
+ if (ret) {
|
||||
+ kfree(ctx);
|
||||
+ return ERR_PTR(ret);
|
||||
+ }
|
||||
+
|
||||
+ return ctx;
|
||||
+}
|
||||
+
|
||||
+static int zstd_init(struct crypto_tfm *tfm)
|
||||
+{
|
||||
+ struct zstd_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
+
|
||||
+ return __zstd_init(ctx);
|
||||
+}
|
||||
+
|
||||
+static void __zstd_exit(void *ctx)
|
||||
+{
|
||||
+ zstd_comp_exit(ctx);
|
||||
+ zstd_decomp_exit(ctx);
|
||||
+}
|
||||
+
|
||||
+static void zstd_free_ctx(struct crypto_scomp *tfm, void *ctx)
|
||||
+{
|
||||
+ __zstd_exit(ctx);
|
||||
+ kzfree(ctx);
|
||||
+}
|
||||
+
|
||||
+static void zstd_exit(struct crypto_tfm *tfm)
|
||||
+{
|
||||
+ struct zstd_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
+
|
||||
+ __zstd_exit(ctx);
|
||||
+}
|
||||
+
|
||||
+static int __zstd_compress(const u8 *src, unsigned int slen,
|
||||
+ u8 *dst, unsigned int *dlen, void *ctx)
|
||||
+{
|
||||
+ size_t out_len;
|
||||
+ struct zstd_ctx *zctx = ctx;
|
||||
+ const ZSTD_parameters params = zstd_params();
|
||||
+
|
||||
+ out_len = ZSTD_compressCCtx(zctx->cctx, dst, *dlen, src, slen, params);
|
||||
+ if (ZSTD_isError(out_len))
|
||||
+ return -EINVAL;
|
||||
+ *dlen = out_len;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int zstd_compress(struct crypto_tfm *tfm, const u8 *src,
|
||||
+ unsigned int slen, u8 *dst, unsigned int *dlen)
|
||||
+{
|
||||
+ struct zstd_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
+
|
||||
+ return __zstd_compress(src, slen, dst, dlen, ctx);
|
||||
+}
|
||||
+
|
||||
+static int zstd_scompress(struct crypto_scomp *tfm, const u8 *src,
|
||||
+ unsigned int slen, u8 *dst, unsigned int *dlen,
|
||||
+ void *ctx)
|
||||
+{
|
||||
+ return __zstd_compress(src, slen, dst, dlen, ctx);
|
||||
+}
|
||||
+
|
||||
+static int __zstd_decompress(const u8 *src, unsigned int slen,
|
||||
+ u8 *dst, unsigned int *dlen, void *ctx)
|
||||
+{
|
||||
+ size_t out_len;
|
||||
+ struct zstd_ctx *zctx = ctx;
|
||||
+
|
||||
+ out_len = ZSTD_decompressDCtx(zctx->dctx, dst, *dlen, src, slen);
|
||||
+ if (ZSTD_isError(out_len))
|
||||
+ return -EINVAL;
|
||||
+ *dlen = out_len;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int zstd_decompress(struct crypto_tfm *tfm, const u8 *src,
|
||||
+ unsigned int slen, u8 *dst, unsigned int *dlen)
|
||||
+{
|
||||
+ struct zstd_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
+
|
||||
+ return __zstd_decompress(src, slen, dst, dlen, ctx);
|
||||
+}
|
||||
+
|
||||
+static int zstd_sdecompress(struct crypto_scomp *tfm, const u8 *src,
|
||||
+ unsigned int slen, u8 *dst, unsigned int *dlen,
|
||||
+ void *ctx)
|
||||
+{
|
||||
+ return __zstd_decompress(src, slen, dst, dlen, ctx);
|
||||
+}
|
||||
+
|
||||
+static struct crypto_alg alg = {
|
||||
+ .cra_name = "zstd",
|
||||
+ .cra_flags = CRYPTO_ALG_TYPE_COMPRESS,
|
||||
+ .cra_ctxsize = sizeof(struct zstd_ctx),
|
||||
+ .cra_module = THIS_MODULE,
|
||||
+ .cra_init = zstd_init,
|
||||
+ .cra_exit = zstd_exit,
|
||||
+ .cra_u = { .compress = {
|
||||
+ .coa_compress = zstd_compress,
|
||||
+ .coa_decompress = zstd_decompress } }
|
||||
+};
|
||||
+
|
||||
+static struct scomp_alg scomp = {
|
||||
+ .alloc_ctx = zstd_alloc_ctx,
|
||||
+ .free_ctx = zstd_free_ctx,
|
||||
+ .compress = zstd_scompress,
|
||||
+ .decompress = zstd_sdecompress,
|
||||
+ .base = {
|
||||
+ .cra_name = "zstd",
|
||||
+ .cra_driver_name = "zstd-scomp",
|
||||
+ .cra_module = THIS_MODULE,
|
||||
+ }
|
||||
+};
|
||||
+
|
||||
+static int __init zstd_mod_init(void)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = crypto_register_alg(&alg);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = crypto_register_scomp(&scomp);
|
||||
+ if (ret)
|
||||
+ crypto_unregister_alg(&alg);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static void __exit zstd_mod_fini(void)
|
||||
+{
|
||||
+ crypto_unregister_alg(&alg);
|
||||
+ crypto_unregister_scomp(&scomp);
|
||||
+}
|
||||
+
|
||||
+module_init(zstd_mod_init);
|
||||
+module_exit(zstd_mod_fini);
|
||||
+
|
||||
+MODULE_LICENSE("GPL");
|
||||
+MODULE_DESCRIPTION("Zstd Compression Algorithm");
|
||||
+MODULE_ALIAS_CRYPTO("zstd");
|
||||
--
|
||||
2.9.3
|
||||
@@ -0,0 +1,420 @@
|
||||
From 57a3cf95b276946559f9e044c7352c11303bb9c1 Mon Sep 17 00:00:00 2001
|
||||
From: Sean Purcell <me@seanp.xyz>
|
||||
Date: Thu, 3 Aug 2017 17:47:03 -0700
|
||||
Subject: [PATCH v6] squashfs-tools: Add zstd support
|
||||
|
||||
This patch adds zstd support to squashfs-tools. It works with zstd
|
||||
versions >= 1.0.0. It was originally written by Sean Purcell.
|
||||
|
||||
Signed-off-by: Sean Purcell <me@seanp.xyz>
|
||||
Signed-off-by: Nick Terrell <terrelln@fb.com>
|
||||
---
|
||||
v4 -> v5:
|
||||
- Fix patch documentation to reflect that Sean Purcell is the author
|
||||
- Don't strip trailing whitespace of unrelated code
|
||||
- Make zstd_display_options() static
|
||||
|
||||
v5 -> v6:
|
||||
- Fix build instructions in Makefile
|
||||
|
||||
squashfs-tools/Makefile | 20 ++++
|
||||
squashfs-tools/compressor.c | 8 ++
|
||||
squashfs-tools/squashfs_fs.h | 1 +
|
||||
squashfs-tools/zstd_wrapper.c | 254 ++++++++++++++++++++++++++++++++++++++++++
|
||||
squashfs-tools/zstd_wrapper.h | 48 ++++++++
|
||||
5 files changed, 331 insertions(+)
|
||||
create mode 100644 squashfs-tools/zstd_wrapper.c
|
||||
create mode 100644 squashfs-tools/zstd_wrapper.h
|
||||
|
||||
diff --git a/squashfs-tools/Makefile b/squashfs-tools/Makefile
|
||||
index 52d2582..22fc559 100644
|
||||
--- a/squashfs-tools/Makefile
|
||||
+++ b/squashfs-tools/Makefile
|
||||
@@ -75,6 +75,18 @@ GZIP_SUPPORT = 1
|
||||
#LZMA_SUPPORT = 1
|
||||
#LZMA_DIR = ../../../../LZMA/lzma465
|
||||
|
||||
+
|
||||
+########### Building ZSTD support ############
|
||||
+#
|
||||
+# The ZSTD library is supported
|
||||
+# ZSTD homepage: http://zstd.net
|
||||
+# ZSTD source repository: https://github.com/facebook/zstd
|
||||
+#
|
||||
+# To build using the ZSTD library - install the library and uncomment the
|
||||
+# ZSTD_SUPPORT line below.
|
||||
+#
|
||||
+#ZSTD_SUPPORT = 1
|
||||
+
|
||||
######## Specifying default compression ########
|
||||
#
|
||||
# The next line specifies which compression algorithm is used by default
|
||||
@@ -177,6 +189,14 @@ LIBS += -llz4
|
||||
COMPRESSORS += lz4
|
||||
endif
|
||||
|
||||
+ifeq ($(ZSTD_SUPPORT),1)
|
||||
+CFLAGS += -DZSTD_SUPPORT
|
||||
+MKSQUASHFS_OBJS += zstd_wrapper.o
|
||||
+UNSQUASHFS_OBJS += zstd_wrapper.o
|
||||
+LIBS += -lzstd
|
||||
+COMPRESSORS += zstd
|
||||
+endif
|
||||
+
|
||||
ifeq ($(XATTR_SUPPORT),1)
|
||||
ifeq ($(XATTR_DEFAULT),1)
|
||||
CFLAGS += -DXATTR_SUPPORT -DXATTR_DEFAULT
|
||||
diff --git a/squashfs-tools/compressor.c b/squashfs-tools/compressor.c
|
||||
index 525e316..02b5e90 100644
|
||||
--- a/squashfs-tools/compressor.c
|
||||
+++ b/squashfs-tools/compressor.c
|
||||
@@ -65,6 +65,13 @@ static struct compressor xz_comp_ops = {
|
||||
extern struct compressor xz_comp_ops;
|
||||
#endif
|
||||
|
||||
+#ifndef ZSTD_SUPPORT
|
||||
+static struct compressor zstd_comp_ops = {
|
||||
+ ZSTD_COMPRESSION, "zstd"
|
||||
+};
|
||||
+#else
|
||||
+extern struct compressor zstd_comp_ops;
|
||||
+#endif
|
||||
|
||||
static struct compressor unknown_comp_ops = {
|
||||
0, "unknown"
|
||||
@@ -77,6 +84,7 @@ struct compressor *compressor[] = {
|
||||
&lzo_comp_ops,
|
||||
&lz4_comp_ops,
|
||||
&xz_comp_ops,
|
||||
+ &zstd_comp_ops,
|
||||
&unknown_comp_ops
|
||||
};
|
||||
|
||||
diff --git a/squashfs-tools/squashfs_fs.h b/squashfs-tools/squashfs_fs.h
|
||||
index 791fe12..afca918 100644
|
||||
--- a/squashfs-tools/squashfs_fs.h
|
||||
+++ b/squashfs-tools/squashfs_fs.h
|
||||
@@ -277,6 +277,7 @@ typedef long long squashfs_inode;
|
||||
#define LZO_COMPRESSION 3
|
||||
#define XZ_COMPRESSION 4
|
||||
#define LZ4_COMPRESSION 5
|
||||
+#define ZSTD_COMPRESSION 6
|
||||
|
||||
struct squashfs_super_block {
|
||||
unsigned int s_magic;
|
||||
diff --git a/squashfs-tools/zstd_wrapper.c b/squashfs-tools/zstd_wrapper.c
|
||||
new file mode 100644
|
||||
index 0000000..dcab75a
|
||||
--- /dev/null
|
||||
+++ b/squashfs-tools/zstd_wrapper.c
|
||||
@@ -0,0 +1,254 @@
|
||||
+/*
|
||||
+ * Copyright (c) 2017
|
||||
+ * Phillip Lougher <phillip@squashfs.org.uk>
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License
|
||||
+ * as published by the Free Software Foundation; either version 2,
|
||||
+ * or (at your option) any later version.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ *
|
||||
+ * zstd_wrapper.c
|
||||
+ *
|
||||
+ * Support for ZSTD compression http://zstd.net
|
||||
+ */
|
||||
+
|
||||
+#include <stdio.h>
|
||||
+#include <string.h>
|
||||
+#include <stdlib.h>
|
||||
+#include <zstd.h>
|
||||
+#include <zstd_errors.h>
|
||||
+
|
||||
+#include "squashfs_fs.h"
|
||||
+#include "zstd_wrapper.h"
|
||||
+#include "compressor.h"
|
||||
+
|
||||
+static int compression_level = ZSTD_DEFAULT_COMPRESSION_LEVEL;
|
||||
+
|
||||
+/*
|
||||
+ * This function is called by the options parsing code in mksquashfs.c
|
||||
+ * to parse any -X compressor option.
|
||||
+ *
|
||||
+ * This function returns:
|
||||
+ * >=0 (number of additional args parsed) on success
|
||||
+ * -1 if the option was unrecognised, or
|
||||
+ * -2 if the option was recognised, but otherwise bad in
|
||||
+ * some way (e.g. invalid parameter)
|
||||
+ *
|
||||
+ * Note: this function sets internal compressor state, but does not
|
||||
+ * pass back the results of the parsing other than success/failure.
|
||||
+ * The zstd_dump_options() function is called later to get the options in
|
||||
+ * a format suitable for writing to the filesystem.
|
||||
+ */
|
||||
+static int zstd_options(char *argv[], int argc)
|
||||
+{
|
||||
+ if (strcmp(argv[0], "-Xcompression-level") == 0) {
|
||||
+ if (argc < 2) {
|
||||
+ fprintf(stderr, "zstd: -Xcompression-level missing "
|
||||
+ "compression level\n");
|
||||
+ fprintf(stderr, "zstd: -Xcompression-level it should "
|
||||
+ "be 1 <= n <= %d\n", ZSTD_maxCLevel());
|
||||
+ goto failed;
|
||||
+ }
|
||||
+
|
||||
+ compression_level = atoi(argv[1]);
|
||||
+ if (compression_level < 1 ||
|
||||
+ compression_level > ZSTD_maxCLevel()) {
|
||||
+ fprintf(stderr, "zstd: -Xcompression-level invalid, it "
|
||||
+ "should be 1 <= n <= %d\n", ZSTD_maxCLevel());
|
||||
+ goto failed;
|
||||
+ }
|
||||
+
|
||||
+ return 1;
|
||||
+ }
|
||||
+
|
||||
+ return -1;
|
||||
+failed:
|
||||
+ return -2;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * This function is called by mksquashfs to dump the parsed
|
||||
+ * compressor options in a format suitable for writing to the
|
||||
+ * compressor options field in the filesystem (stored immediately
|
||||
+ * after the superblock).
|
||||
+ *
|
||||
+ * This function returns a pointer to the compression options structure
|
||||
+ * to be stored (and the size), or NULL if there are no compression
|
||||
+ * options.
|
||||
+ */
|
||||
+static void *zstd_dump_options(int block_size, int *size)
|
||||
+{
|
||||
+ static struct zstd_comp_opts comp_opts;
|
||||
+
|
||||
+ /* don't return anything if the options are all default */
|
||||
+ if (compression_level == ZSTD_DEFAULT_COMPRESSION_LEVEL)
|
||||
+ return NULL;
|
||||
+
|
||||
+ comp_opts.compression_level = compression_level;
|
||||
+
|
||||
+ SQUASHFS_INSWAP_COMP_OPTS(&comp_opts);
|
||||
+
|
||||
+ *size = sizeof(comp_opts);
|
||||
+ return &comp_opts;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * This function is a helper specifically for the append mode of
|
||||
+ * mksquashfs. Its purpose is to set the internal compressor state
|
||||
+ * to the stored compressor options in the passed compressor options
|
||||
+ * structure.
|
||||
+ *
|
||||
+ * In effect this function sets up the compressor options
|
||||
+ * to the same state they were when the filesystem was originally
|
||||
+ * generated, this is to ensure on appending, the compressor uses
|
||||
+ * the same compression options that were used to generate the
|
||||
+ * original filesystem.
|
||||
+ *
|
||||
+ * Note, even if there are no compressor options, this function is still
|
||||
+ * called with an empty compressor structure (size == 0), to explicitly
|
||||
+ * set the default options, this is to ensure any user supplied
|
||||
+ * -X options on the appending mksquashfs command line are over-ridden.
|
||||
+ *
|
||||
+ * This function returns 0 on successful extraction of options, and -1 on error.
|
||||
+ */
|
||||
+static int zstd_extract_options(int block_size, void *buffer, int size)
|
||||
+{
|
||||
+ struct zstd_comp_opts *comp_opts = buffer;
|
||||
+
|
||||
+ if (size == 0) {
|
||||
+ /* Set default values */
|
||||
+ compression_level = ZSTD_DEFAULT_COMPRESSION_LEVEL;
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ /* we expect a comp_opts structure of sufficient size to be present */
|
||||
+ if (size < sizeof(*comp_opts))
|
||||
+ goto failed;
|
||||
+
|
||||
+ SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
|
||||
+
|
||||
+ if (comp_opts->compression_level < 1 ||
|
||||
+ comp_opts->compression_level > ZSTD_maxCLevel()) {
|
||||
+ fprintf(stderr, "zstd: bad compression level in compression "
|
||||
+ "options structure\n");
|
||||
+ goto failed;
|
||||
+ }
|
||||
+
|
||||
+ compression_level = comp_opts->compression_level;
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+failed:
|
||||
+ fprintf(stderr, "zstd: error reading stored compressor options from "
|
||||
+ "filesystem!\n");
|
||||
+
|
||||
+ return -1;
|
||||
+}
|
||||
+
|
||||
+static void zstd_display_options(void *buffer, int size)
|
||||
+{
|
||||
+ struct zstd_comp_opts *comp_opts = buffer;
|
||||
+
|
||||
+ /* we expect a comp_opts structure of sufficient size to be present */
|
||||
+ if (size < sizeof(*comp_opts))
|
||||
+ goto failed;
|
||||
+
|
||||
+ SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
|
||||
+
|
||||
+ if (comp_opts->compression_level < 1 ||
|
||||
+ comp_opts->compression_level > ZSTD_maxCLevel()) {
|
||||
+ fprintf(stderr, "zstd: bad compression level in compression "
|
||||
+ "options structure\n");
|
||||
+ goto failed;
|
||||
+ }
|
||||
+
|
||||
+ printf("\tcompression-level %d\n", comp_opts->compression_level);
|
||||
+
|
||||
+ return;
|
||||
+
|
||||
+failed:
|
||||
+ fprintf(stderr, "zstd: error reading stored compressor options from "
|
||||
+ "filesystem!\n");
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * This function is called by mksquashfs to initialise the
|
||||
+ * compressor, before compress() is called.
|
||||
+ *
|
||||
+ * This function returns 0 on success, and -1 on error.
|
||||
+ */
|
||||
+static int zstd_init(void **strm, int block_size, int datablock)
|
||||
+{
|
||||
+ ZSTD_CCtx *cctx = ZSTD_createCCtx();
|
||||
+
|
||||
+ if (!cctx) {
|
||||
+ fprintf(stderr, "zstd: failed to allocate compression "
|
||||
+ "context!\n");
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ *strm = cctx;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int zstd_compress(void *strm, void *dest, void *src, int size,
|
||||
+ int block_size, int *error)
|
||||
+{
|
||||
+ const size_t res = ZSTD_compressCCtx((ZSTD_CCtx*)strm, dest, block_size,
|
||||
+ src, size, compression_level);
|
||||
+
|
||||
+ if (ZSTD_isError(res)) {
|
||||
+ /* FIXME:
|
||||
+ * zstd does not expose stable error codes. The error enum may
|
||||
+ * change between versions. Until upstream zstd stablizes the
|
||||
+ * error codes, we have no way of knowing why the error occurs.
|
||||
+ * zstd shouldn't fail to compress any input unless there isn't
|
||||
+ * enough output space. We assume that is the cause and return
|
||||
+ * the special error code for not enough output space.
|
||||
+ */
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ return (int)res;
|
||||
+}
|
||||
+
|
||||
+static int zstd_uncompress(void *dest, void *src, int size, int outsize,
|
||||
+ int *error)
|
||||
+{
|
||||
+ const size_t res = ZSTD_decompress(dest, outsize, src, size);
|
||||
+
|
||||
+ if (ZSTD_isError(res)) {
|
||||
+ fprintf(stderr, "\t%d %d\n", outsize, size);
|
||||
+
|
||||
+ *error = (int)ZSTD_getErrorCode(res);
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ return (int)res;
|
||||
+}
|
||||
+
|
||||
+static void zstd_usage(void)
|
||||
+{
|
||||
+ fprintf(stderr, "\t -Xcompression-level <compression-level>\n");
|
||||
+ fprintf(stderr, "\t\t<compression-level> should be 1 .. %d (default "
|
||||
+ "%d)\n", ZSTD_maxCLevel(), ZSTD_DEFAULT_COMPRESSION_LEVEL);
|
||||
+}
|
||||
+
|
||||
+struct compressor zstd_comp_ops = {
|
||||
+ .init = zstd_init,
|
||||
+ .compress = zstd_compress,
|
||||
+ .uncompress = zstd_uncompress,
|
||||
+ .options = zstd_options,
|
||||
+ .dump_options = zstd_dump_options,
|
||||
+ .extract_options = zstd_extract_options,
|
||||
+ .display_options = zstd_display_options,
|
||||
+ .usage = zstd_usage,
|
||||
+ .id = ZSTD_COMPRESSION,
|
||||
+ .name = "zstd",
|
||||
+ .supported = 1
|
||||
+};
|
||||
diff --git a/squashfs-tools/zstd_wrapper.h b/squashfs-tools/zstd_wrapper.h
|
||||
new file mode 100644
|
||||
index 0000000..4fbef0a
|
||||
--- /dev/null
|
||||
+++ b/squashfs-tools/zstd_wrapper.h
|
||||
@@ -0,0 +1,48 @@
|
||||
+#ifndef ZSTD_WRAPPER_H
|
||||
+#define ZSTD_WRAPPER_H
|
||||
+/*
|
||||
+ * Squashfs
|
||||
+ *
|
||||
+ * Copyright (c) 2017
|
||||
+ * Phillip Lougher <phillip@squashfs.org.uk>
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License
|
||||
+ * as published by the Free Software Foundation; either version 2,
|
||||
+ * or (at your option) any later version.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ *
|
||||
+ * zstd_wrapper.h
|
||||
+ *
|
||||
+ */
|
||||
+
|
||||
+#ifndef linux
|
||||
+#define __BYTE_ORDER BYTE_ORDER
|
||||
+#define __BIG_ENDIAN BIG_ENDIAN
|
||||
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
|
||||
+#else
|
||||
+#include <endian.h>
|
||||
+#endif
|
||||
+
|
||||
+#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
+extern unsigned int inswap_le16(unsigned short);
|
||||
+extern unsigned int inswap_le32(unsigned int);
|
||||
+
|
||||
+#define SQUASHFS_INSWAP_COMP_OPTS(s) { \
|
||||
+ (s)->compression_level = inswap_le32((s)->compression_level); \
|
||||
+}
|
||||
+#else
|
||||
+#define SQUASHFS_INSWAP_COMP_OPTS(s)
|
||||
+#endif
|
||||
+
|
||||
+/* Default compression */
|
||||
+#define ZSTD_DEFAULT_COMPRESSION_LEVEL 15
|
||||
+
|
||||
+struct zstd_comp_opts {
|
||||
+ int compression_level;
|
||||
+};
|
||||
+#endif
|
||||
--
|
||||
2.9.5
|
||||
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
@@ -1,83 +0,0 @@
|
||||
# ################################################################
|
||||
# Copyright (c) 2015-2020, Facebook, 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.
|
||||
# ################################################################
|
||||
|
||||
.PHONY: libzstd
|
||||
libzstd:
|
||||
rm -rf linux
|
||||
mkdir -p linux
|
||||
mkdir -p linux/include/linux
|
||||
mkdir -p linux/lib/zstd
|
||||
../freestanding_lib/freestanding.py \
|
||||
--source-lib ../../lib \
|
||||
--output-lib linux/lib/zstd \
|
||||
--xxhash '<linux/xxhash.h>' \
|
||||
--xxh64-state 'struct xxh64_state' \
|
||||
--xxh64-prefix 'xxh64' \
|
||||
--rewrite-include '<limits\.h>=<linux/limits.h>' \
|
||||
--rewrite-include '<stddef\.h>=<linux/types.h>' \
|
||||
-DZSTD_NO_INTRINSICS \
|
||||
-DZSTD_NO_UNUSED_FUNCTIONS \
|
||||
-DZSTD_LEGACY_SUPPORT=0 \
|
||||
-DZSTD_STATIC_LINKING_ONLY \
|
||||
-DFSE_STATIC_LINKING_ONLY \
|
||||
-DHUF_STATIC_LINKING_ONLY \
|
||||
-DXXH_STATIC_LINKING_ONLY \
|
||||
-DMEM_FORCE_MEMORY_ACCESS=0 \
|
||||
-D__GNUC__ \
|
||||
-DSTATIC_BMI2=0 \
|
||||
-DZSTD_ADDRESS_SANITIZER=0 \
|
||||
-DZSTD_MEMORY_SANITIZER=0 \
|
||||
-DZSTD_COMPRESS_HEAPMODE=1 \
|
||||
-UZSTD_NO_INLINE \
|
||||
-UNO_PREFETCH \
|
||||
-U__cplusplus \
|
||||
-UZSTD_DLL_EXPORT \
|
||||
-UZSTD_DLL_IMPORT \
|
||||
-U__ICCARM__ \
|
||||
-UZSTD_MULTITHREAD \
|
||||
-U_MSC_VER \
|
||||
-U_WIN32 \
|
||||
-RZSTDLIB_VISIBILITY= \
|
||||
-RZSTDERRORLIB_VISIBILITY=
|
||||
cp linux_zstd.h linux/include/linux/zstd.h
|
||||
cp zstd_compress_module.c linux/lib/zstd
|
||||
cp zstd_decompress_module.c linux/lib/zstd
|
||||
cp decompress_sources.h linux/lib/zstd
|
||||
cp linux.mk linux/lib/zstd/Makefile
|
||||
|
||||
LINUX ?= $(HOME)/repos/linux
|
||||
|
||||
.PHONY: import
|
||||
import: libzstd
|
||||
rm -f $(LINUX)/include/linux/zstd.h
|
||||
rm -f $(LINUX)/include/linux/zstd_errors.h
|
||||
rm -rf $(LINUX)/lib/zstd
|
||||
cp linux/include/linux/zstd.h $(LINUX)/include/linux
|
||||
cp -r linux/lib/zstd $(LINUX)/lib
|
||||
|
||||
import-upstream:
|
||||
rm -rf $(LINUX)/lib/zstd
|
||||
mkdir $(LINUX)/lib/zstd
|
||||
cp ../../lib/zstd.h $(LINUX)/lib/zstd
|
||||
cp -r ../../lib/common $(LINUX)/lib/zstd
|
||||
cp -r ../../lib/compress $(LINUX)/lib/zstd
|
||||
cp -r ../../lib/decompress $(LINUX)/lib/zstd
|
||||
rm $(LINUX)/lib/zstd/common/threading.*
|
||||
rm $(LINUX)/lib/zstd/common/pool.*
|
||||
rm $(LINUX)/lib/zstd/common/xxhash.*
|
||||
rm $(LINUX)/lib/zstd/compress/zstdmt_*
|
||||
|
||||
.PHONY: test
|
||||
test: libzstd
|
||||
$(MAKE) -C test run-test CFLAGS="-O3 $(CFLAGS)" -j
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
$(RM) -rf linux
|
||||
@@ -1,14 +1,101 @@
|
||||
# Zstd in the Linux Kernel
|
||||
# Linux Kernel Patch
|
||||
|
||||
This directory contains the scripts needed to transform upstream zstd into the version imported into the kernel. All the transforms are automated and tested by our continuous integration.
|
||||
There are four pieces, the `xxhash` kernel module, the `zstd_compress` and `zstd_decompress` kernel modules, the BtrFS patch, and the SquashFS patch.
|
||||
The patches are based off of the linux kernel master branch.
|
||||
|
||||
## Upgrading Zstd in the Linux Kernel
|
||||
## xxHash kernel module
|
||||
|
||||
1. `cd` into this directory.
|
||||
2. Run `make libzstd` and read the output. Make sure that all the diffs printed and changes made by the script are correct.
|
||||
3. Run `make test` and ensure that it passes.
|
||||
4. Import zstd into the Linux Kernel `make import LINUX=/path/to/linux/repo`
|
||||
5. Inspect the diff for sanity.
|
||||
6. Check the Linux Kernel history for zstd. If any patches were made to the kernel version of zstd, but not to upstream zstd, then port them upstream if necessary.
|
||||
7. Test the diff. Benchmark if necessary. Make sure to test multiple architectures: At least x86, i386, and arm.
|
||||
8. Submit the patch to the LKML.
|
||||
* The patch is located in `xxhash.diff`.
|
||||
* The header is in `include/linux/xxhash.h`.
|
||||
* The source is in `lib/xxhash.c`.
|
||||
* `test/XXHashUserLandTest.cpp` contains tests for the patch in userland by mocking the kernel headers.
|
||||
I tested the tests by commenting a line of of each branch in `xxhash.c` one line at a time, and made sure the tests failed.
|
||||
It can be run with the following commands:
|
||||
```
|
||||
cd test && make googletest && make XXHashUserLandTest && ./XXHashUserLandTest
|
||||
```
|
||||
* I also benchmarked the `xxhash` module against upstream xxHash, and made sure that they ran at the same speed.
|
||||
|
||||
## Zstd Kernel modules
|
||||
|
||||
* The (large) patch is located in `zstd.diff`, which depends on `xxhash.diff`.
|
||||
* The header is in `include/linux/zstd.h`.
|
||||
* It is split up into `zstd_compress` and `zstd_decompress`, which can be loaded independently.
|
||||
* Source files are in `lib/zstd/`.
|
||||
* `lib/Kconfig` and `lib/Makefile` need to be modified by applying `lib/Kconfig.diff` and `lib/Makefile.diff` respectively.
|
||||
These changes are also included in the `zstd.diff`.
|
||||
* `test/UserlandTest.cpp` contains tests for the patch in userland by mocking the kernel headers.
|
||||
It can be run with the following commands:
|
||||
```
|
||||
cd test && make googletest && make UserlandTest && ./UserlandTest
|
||||
```
|
||||
|
||||
## BtrFS
|
||||
|
||||
* The patch is located in `btrfs.diff`.
|
||||
* Additionally `fs/btrfs/zstd.c` is provided as a source for convenience.
|
||||
* The patch seems to be working, it doesn't crash the kernel, and compresses at speeds and ratios that are expected.
|
||||
It could still use some more testing for fringe features, like printing options.
|
||||
|
||||
### Benchmarks
|
||||
|
||||
Benchmarks run on a Ubuntu 14.04 with 2 cores and 4 GiB of RAM.
|
||||
The VM is running on a Macbook Pro with a 3.1 GHz Intel Core i7 processor,
|
||||
16 GB of ram, and a SSD.
|
||||
The kernel running was built from the master branch with the patch.
|
||||
|
||||
The compression benchmark is copying 10 copies of the
|
||||
unzipped [silesia corpus](http://mattmahoney.net/dc/silesia.html) into a BtrFS
|
||||
filesystem mounted with `-o compress-force={none, lzo, zlib, zstd}`.
|
||||
The decompression benchmark is timing how long it takes to `tar` all 10 copies
|
||||
into `/dev/null`.
|
||||
The compression ratio is measured by comparing the output of `df` and `du`.
|
||||
See `btrfs-benchmark.sh` for details.
|
||||
|
||||
| Algorithm | Compression ratio | Compression speed | Decompression speed |
|
||||
|-----------|-------------------|-------------------|---------------------|
|
||||
| None | 0.99 | 504 MB/s | 686 MB/s |
|
||||
| lzo | 1.66 | 398 MB/s | 442 MB/s |
|
||||
| zlib | 2.58 | 65 MB/s | 241 MB/s |
|
||||
| zstd 1 | 2.57 | 260 MB/s | 383 MB/s |
|
||||
| zstd 3 | 2.71 | 174 MB/s | 408 MB/s |
|
||||
| zstd 6 | 2.87 | 70 MB/s | 398 MB/s |
|
||||
| zstd 9 | 2.92 | 43 MB/s | 406 MB/s |
|
||||
| zstd 12 | 2.93 | 21 MB/s | 408 MB/s |
|
||||
| zstd 15 | 3.01 | 11 MB/s | 354 MB/s |
|
||||
|
||||
|
||||
## SquashFS
|
||||
|
||||
* The patch is located in `squashfs.diff`
|
||||
* Additionally `fs/squashfs/zstd_wrapper.c` is provided as a source for convenience.
|
||||
* The patch has been tested on the master branch of the kernel.
|
||||
|
||||
### Benchmarks
|
||||
|
||||
Benchmarks run on a Ubuntu 14.04 with 2 cores and 4 GiB of RAM.
|
||||
The VM is running on a Macbook Pro with a 3.1 GHz Intel Core i7 processor,
|
||||
16 GB of ram, and a SSD.
|
||||
The kernel running was built from the master branch with the patch.
|
||||
|
||||
The compression benchmark is the file tree from the SquashFS archive found in the
|
||||
Ubuntu 16.10 desktop image (ubuntu-16.10-desktop-amd64.iso).
|
||||
The compression benchmark uses mksquashfs with the default block size (128 KB)
|
||||
and various compression algorithms/compression levels.
|
||||
`xz` and `zstd` are also benchmarked with 256 KB blocks.
|
||||
The decompression benchmark is timing how long it takes to `tar` the file tree
|
||||
into `/dev/null`.
|
||||
See `squashfs-benchmark.sh` for details.
|
||||
|
||||
| Algorithm | Compression ratio | Compression speed | Decompression speed |
|
||||
|----------------|-------------------|-------------------|---------------------|
|
||||
| gzip | 2.92 | 15 MB/s | 128 MB/s |
|
||||
| lzo | 2.64 | 9.5 MB/s | 217 MB/s |
|
||||
| lz4 | 2.12 | 94 MB/s | 218 MB/s |
|
||||
| xz | 3.43 | 5.5 MB/s | 35 MB/s |
|
||||
| xz 256 KB | 3.53 | 5.4 MB/s | 40 MB/s |
|
||||
| zstd 1 | 2.71 | 96 MB/s | 210 MB/s |
|
||||
| zstd 5 | 2.93 | 69 MB/s | 198 MB/s |
|
||||
| zstd 10 | 3.01 | 41 MB/s | 225 MB/s |
|
||||
| zstd 15 | 3.13 | 11.4 MB/s | 224 MB/s |
|
||||
| zstd 16 256 KB | 3.24 | 8.1 MB/s | 210 MB/s |
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
/*
|
||||
* This file includes every .c file needed for decompression.
|
||||
* It is used by lib/decompress_unzstd.c to include the decompression
|
||||
* source into the translation-unit, so it can be used for kernel
|
||||
* decompression.
|
||||
*/
|
||||
|
||||
#include "common/debug.c"
|
||||
#include "common/entropy_common.c"
|
||||
#include "common/error_private.c"
|
||||
#include "common/fse_decompress.c"
|
||||
#include "common/zstd_common.c"
|
||||
#include "decompress/huf_decompress.c"
|
||||
#include "decompress/zstd_ddict.c"
|
||||
#include "decompress/zstd_decompress.c"
|
||||
#include "decompress/zstd_decompress_block.c"
|
||||
#include "zstd_decompress_module.c"
|
||||
@@ -0,0 +1,432 @@
|
||||
/*
|
||||
* Copyright (c) 2016-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License v2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*/
|
||||
#include <linux/bio.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/refcount.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/zstd.h>
|
||||
#include "compression.h"
|
||||
|
||||
#define ZSTD_BTRFS_MAX_WINDOWLOG 17
|
||||
#define ZSTD_BTRFS_MAX_INPUT (1 << ZSTD_BTRFS_MAX_WINDOWLOG)
|
||||
#define ZSTD_BTRFS_DEFAULT_LEVEL 3
|
||||
|
||||
static ZSTD_parameters zstd_get_btrfs_parameters(size_t src_len)
|
||||
{
|
||||
ZSTD_parameters params = ZSTD_getParams(ZSTD_BTRFS_DEFAULT_LEVEL,
|
||||
src_len, 0);
|
||||
|
||||
if (params.cParams.windowLog > ZSTD_BTRFS_MAX_WINDOWLOG)
|
||||
params.cParams.windowLog = ZSTD_BTRFS_MAX_WINDOWLOG;
|
||||
WARN_ON(src_len > ZSTD_BTRFS_MAX_INPUT);
|
||||
return params;
|
||||
}
|
||||
|
||||
struct workspace {
|
||||
void *mem;
|
||||
size_t size;
|
||||
char *buf;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
static void zstd_free_workspace(struct list_head *ws)
|
||||
{
|
||||
struct workspace *workspace = list_entry(ws, struct workspace, list);
|
||||
|
||||
kvfree(workspace->mem);
|
||||
kfree(workspace->buf);
|
||||
kfree(workspace);
|
||||
}
|
||||
|
||||
static struct list_head *zstd_alloc_workspace(void)
|
||||
{
|
||||
ZSTD_parameters params =
|
||||
zstd_get_btrfs_parameters(ZSTD_BTRFS_MAX_INPUT);
|
||||
struct workspace *workspace;
|
||||
|
||||
workspace = kzalloc(sizeof(*workspace), GFP_KERNEL);
|
||||
if (!workspace)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
workspace->size = max_t(size_t,
|
||||
ZSTD_CStreamWorkspaceBound(params.cParams),
|
||||
ZSTD_DStreamWorkspaceBound(ZSTD_BTRFS_MAX_INPUT));
|
||||
workspace->mem = kvmalloc(workspace->size, GFP_KERNEL);
|
||||
workspace->buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
if (!workspace->mem || !workspace->buf)
|
||||
goto fail;
|
||||
|
||||
INIT_LIST_HEAD(&workspace->list);
|
||||
|
||||
return &workspace->list;
|
||||
fail:
|
||||
zstd_free_workspace(&workspace->list);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
static int zstd_compress_pages(struct list_head *ws,
|
||||
struct address_space *mapping,
|
||||
u64 start,
|
||||
struct page **pages,
|
||||
unsigned long *out_pages,
|
||||
unsigned long *total_in,
|
||||
unsigned long *total_out)
|
||||
{
|
||||
struct workspace *workspace = list_entry(ws, struct workspace, list);
|
||||
ZSTD_CStream *stream;
|
||||
int ret = 0;
|
||||
int nr_pages = 0;
|
||||
struct page *in_page = NULL; /* The current page to read */
|
||||
struct page *out_page = NULL; /* The current page to write to */
|
||||
ZSTD_inBuffer in_buf = { NULL, 0, 0 };
|
||||
ZSTD_outBuffer out_buf = { NULL, 0, 0 };
|
||||
unsigned long tot_in = 0;
|
||||
unsigned long tot_out = 0;
|
||||
unsigned long len = *total_out;
|
||||
const unsigned long nr_dest_pages = *out_pages;
|
||||
unsigned long max_out = nr_dest_pages * PAGE_SIZE;
|
||||
ZSTD_parameters params = zstd_get_btrfs_parameters(len);
|
||||
|
||||
*out_pages = 0;
|
||||
*total_out = 0;
|
||||
*total_in = 0;
|
||||
|
||||
/* Initialize the stream */
|
||||
stream = ZSTD_initCStream(params, len, workspace->mem,
|
||||
workspace->size);
|
||||
if (!stream) {
|
||||
pr_warn("BTRFS: ZSTD_initCStream failed\n");
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* map in the first page of input data */
|
||||
in_page = find_get_page(mapping, start >> PAGE_SHIFT);
|
||||
in_buf.src = kmap(in_page);
|
||||
in_buf.pos = 0;
|
||||
in_buf.size = min_t(size_t, len, PAGE_SIZE);
|
||||
|
||||
|
||||
/* Allocate and map in the output buffer */
|
||||
out_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM);
|
||||
if (out_page == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
pages[nr_pages++] = out_page;
|
||||
out_buf.dst = kmap(out_page);
|
||||
out_buf.pos = 0;
|
||||
out_buf.size = min_t(size_t, max_out, PAGE_SIZE);
|
||||
|
||||
while (1) {
|
||||
size_t ret2;
|
||||
|
||||
ret2 = ZSTD_compressStream(stream, &out_buf, &in_buf);
|
||||
if (ZSTD_isError(ret2)) {
|
||||
pr_debug("BTRFS: ZSTD_compressStream returned %d\n",
|
||||
ZSTD_getErrorCode(ret2));
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Check to see if we are making it bigger */
|
||||
if (tot_in + in_buf.pos > 8192 &&
|
||||
tot_in + in_buf.pos <
|
||||
tot_out + out_buf.pos) {
|
||||
ret = -E2BIG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* We've reached the end of our output range */
|
||||
if (out_buf.pos >= max_out) {
|
||||
tot_out += out_buf.pos;
|
||||
ret = -E2BIG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Check if we need more output space */
|
||||
if (out_buf.pos == out_buf.size) {
|
||||
tot_out += PAGE_SIZE;
|
||||
max_out -= PAGE_SIZE;
|
||||
kunmap(out_page);
|
||||
if (nr_pages == nr_dest_pages) {
|
||||
out_page = NULL;
|
||||
ret = -E2BIG;
|
||||
goto out;
|
||||
}
|
||||
out_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM);
|
||||
if (out_page == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
pages[nr_pages++] = out_page;
|
||||
out_buf.dst = kmap(out_page);
|
||||
out_buf.pos = 0;
|
||||
out_buf.size = min_t(size_t, max_out, PAGE_SIZE);
|
||||
}
|
||||
|
||||
/* We've reached the end of the input */
|
||||
if (in_buf.pos >= len) {
|
||||
tot_in += in_buf.pos;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check if we need more input */
|
||||
if (in_buf.pos == in_buf.size) {
|
||||
tot_in += PAGE_SIZE;
|
||||
kunmap(in_page);
|
||||
put_page(in_page);
|
||||
|
||||
start += PAGE_SIZE;
|
||||
len -= PAGE_SIZE;
|
||||
in_page = find_get_page(mapping, start >> PAGE_SHIFT);
|
||||
in_buf.src = kmap(in_page);
|
||||
in_buf.pos = 0;
|
||||
in_buf.size = min_t(size_t, len, PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
while (1) {
|
||||
size_t ret2;
|
||||
|
||||
ret2 = ZSTD_endStream(stream, &out_buf);
|
||||
if (ZSTD_isError(ret2)) {
|
||||
pr_debug("BTRFS: ZSTD_endStream returned %d\n",
|
||||
ZSTD_getErrorCode(ret2));
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
if (ret2 == 0) {
|
||||
tot_out += out_buf.pos;
|
||||
break;
|
||||
}
|
||||
if (out_buf.pos >= max_out) {
|
||||
tot_out += out_buf.pos;
|
||||
ret = -E2BIG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
tot_out += PAGE_SIZE;
|
||||
max_out -= PAGE_SIZE;
|
||||
kunmap(out_page);
|
||||
if (nr_pages == nr_dest_pages) {
|
||||
out_page = NULL;
|
||||
ret = -E2BIG;
|
||||
goto out;
|
||||
}
|
||||
out_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM);
|
||||
if (out_page == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
pages[nr_pages++] = out_page;
|
||||
out_buf.dst = kmap(out_page);
|
||||
out_buf.pos = 0;
|
||||
out_buf.size = min_t(size_t, max_out, PAGE_SIZE);
|
||||
}
|
||||
|
||||
if (tot_out >= tot_in) {
|
||||
ret = -E2BIG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
*total_in = tot_in;
|
||||
*total_out = tot_out;
|
||||
out:
|
||||
*out_pages = nr_pages;
|
||||
/* Cleanup */
|
||||
if (in_page) {
|
||||
kunmap(in_page);
|
||||
put_page(in_page);
|
||||
}
|
||||
if (out_page)
|
||||
kunmap(out_page);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb)
|
||||
{
|
||||
struct workspace *workspace = list_entry(ws, struct workspace, list);
|
||||
struct page **pages_in = cb->compressed_pages;
|
||||
u64 disk_start = cb->start;
|
||||
struct bio *orig_bio = cb->orig_bio;
|
||||
size_t srclen = cb->compressed_len;
|
||||
ZSTD_DStream *stream;
|
||||
int ret = 0;
|
||||
unsigned long page_in_index = 0;
|
||||
unsigned long total_pages_in = DIV_ROUND_UP(srclen, PAGE_SIZE);
|
||||
unsigned long buf_start;
|
||||
unsigned long total_out = 0;
|
||||
ZSTD_inBuffer in_buf = { NULL, 0, 0 };
|
||||
ZSTD_outBuffer out_buf = { NULL, 0, 0 };
|
||||
|
||||
stream = ZSTD_initDStream(
|
||||
ZSTD_BTRFS_MAX_INPUT, workspace->mem, workspace->size);
|
||||
if (!stream) {
|
||||
pr_debug("BTRFS: ZSTD_initDStream failed\n");
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
in_buf.src = kmap(pages_in[page_in_index]);
|
||||
in_buf.pos = 0;
|
||||
in_buf.size = min_t(size_t, srclen, PAGE_SIZE);
|
||||
|
||||
out_buf.dst = workspace->buf;
|
||||
out_buf.pos = 0;
|
||||
out_buf.size = PAGE_SIZE;
|
||||
|
||||
while (1) {
|
||||
size_t ret2;
|
||||
|
||||
ret2 = ZSTD_decompressStream(stream, &out_buf, &in_buf);
|
||||
if (ZSTD_isError(ret2)) {
|
||||
pr_debug("BTRFS: ZSTD_decompressStream returned %d\n",
|
||||
ZSTD_getErrorCode(ret2));
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
buf_start = total_out;
|
||||
total_out += out_buf.pos;
|
||||
out_buf.pos = 0;
|
||||
|
||||
ret = btrfs_decompress_buf2page(out_buf.dst, buf_start,
|
||||
total_out, disk_start, orig_bio);
|
||||
if (ret == 0)
|
||||
break;
|
||||
|
||||
if (in_buf.pos >= srclen)
|
||||
break;
|
||||
|
||||
/* Check if we've hit the end of a frame */
|
||||
if (ret2 == 0)
|
||||
break;
|
||||
|
||||
if (in_buf.pos == in_buf.size) {
|
||||
kunmap(pages_in[page_in_index++]);
|
||||
if (page_in_index >= total_pages_in) {
|
||||
in_buf.src = NULL;
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
srclen -= PAGE_SIZE;
|
||||
in_buf.src = kmap(pages_in[page_in_index]);
|
||||
in_buf.pos = 0;
|
||||
in_buf.size = min_t(size_t, srclen, PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
ret = 0;
|
||||
zero_fill_bio(orig_bio);
|
||||
done:
|
||||
if (in_buf.src)
|
||||
kunmap(pages_in[page_in_index]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int zstd_decompress(struct list_head *ws, unsigned char *data_in,
|
||||
struct page *dest_page,
|
||||
unsigned long start_byte,
|
||||
size_t srclen, size_t destlen)
|
||||
{
|
||||
struct workspace *workspace = list_entry(ws, struct workspace, list);
|
||||
ZSTD_DStream *stream;
|
||||
int ret = 0;
|
||||
size_t ret2;
|
||||
ZSTD_inBuffer in_buf = { NULL, 0, 0 };
|
||||
ZSTD_outBuffer out_buf = { NULL, 0, 0 };
|
||||
unsigned long total_out = 0;
|
||||
unsigned long pg_offset = 0;
|
||||
char *kaddr;
|
||||
|
||||
stream = ZSTD_initDStream(
|
||||
ZSTD_BTRFS_MAX_INPUT, workspace->mem, workspace->size);
|
||||
if (!stream) {
|
||||
pr_warn("BTRFS: ZSTD_initDStream failed\n");
|
||||
ret = -EIO;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
destlen = min_t(size_t, destlen, PAGE_SIZE);
|
||||
|
||||
in_buf.src = data_in;
|
||||
in_buf.pos = 0;
|
||||
in_buf.size = srclen;
|
||||
|
||||
out_buf.dst = workspace->buf;
|
||||
out_buf.pos = 0;
|
||||
out_buf.size = PAGE_SIZE;
|
||||
|
||||
ret2 = 1;
|
||||
while (pg_offset < destlen && in_buf.pos < in_buf.size) {
|
||||
unsigned long buf_start;
|
||||
unsigned long buf_offset;
|
||||
unsigned long bytes;
|
||||
|
||||
/* Check if the frame is over and we still need more input */
|
||||
if (ret2 == 0) {
|
||||
pr_debug("BTRFS: ZSTD_decompressStream ended early\n");
|
||||
ret = -EIO;
|
||||
goto finish;
|
||||
}
|
||||
ret2 = ZSTD_decompressStream(stream, &out_buf, &in_buf);
|
||||
if (ZSTD_isError(ret2)) {
|
||||
pr_debug("BTRFS: ZSTD_decompressStream returned %d\n",
|
||||
ZSTD_getErrorCode(ret2));
|
||||
ret = -EIO;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
buf_start = total_out;
|
||||
total_out += out_buf.pos;
|
||||
out_buf.pos = 0;
|
||||
|
||||
if (total_out <= start_byte)
|
||||
continue;
|
||||
|
||||
if (total_out > start_byte && buf_start < start_byte)
|
||||
buf_offset = start_byte - buf_start;
|
||||
else
|
||||
buf_offset = 0;
|
||||
|
||||
bytes = min_t(unsigned long, destlen - pg_offset,
|
||||
out_buf.size - buf_offset);
|
||||
|
||||
kaddr = kmap_atomic(dest_page);
|
||||
memcpy(kaddr + pg_offset, out_buf.dst + buf_offset, bytes);
|
||||
kunmap_atomic(kaddr);
|
||||
|
||||
pg_offset += bytes;
|
||||
}
|
||||
ret = 0;
|
||||
finish:
|
||||
if (pg_offset < destlen) {
|
||||
kaddr = kmap_atomic(dest_page);
|
||||
memset(kaddr + pg_offset, 0, destlen - pg_offset);
|
||||
kunmap_atomic(kaddr);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
const struct btrfs_compress_op btrfs_zstd_compress = {
|
||||
.alloc_workspace = zstd_alloc_workspace,
|
||||
.free_workspace = zstd_free_workspace,
|
||||
.compress_pages = zstd_compress_pages,
|
||||
.decompress_bio = zstd_decompress_bio,
|
||||
.decompress = zstd_decompress,
|
||||
};
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Squashfs - a compressed read only filesystem for Linux
|
||||
*
|
||||
* Copyright (c) 2016-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2,
|
||||
* or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* zstd_wrapper.c
|
||||
*/
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/zstd.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#include "squashfs_fs.h"
|
||||
#include "squashfs_fs_sb.h"
|
||||
#include "squashfs.h"
|
||||
#include "decompressor.h"
|
||||
#include "page_actor.h"
|
||||
|
||||
struct workspace {
|
||||
void *mem;
|
||||
size_t mem_size;
|
||||
size_t window_size;
|
||||
};
|
||||
|
||||
static void *zstd_init(struct squashfs_sb_info *msblk, void *buff)
|
||||
{
|
||||
struct workspace *wksp = kmalloc(sizeof(*wksp), GFP_KERNEL);
|
||||
|
||||
if (wksp == NULL)
|
||||
goto failed;
|
||||
wksp->window_size = max_t(size_t,
|
||||
msblk->block_size, SQUASHFS_METADATA_SIZE);
|
||||
wksp->mem_size = ZSTD_DStreamWorkspaceBound(wksp->window_size);
|
||||
wksp->mem = vmalloc(wksp->mem_size);
|
||||
if (wksp->mem == NULL)
|
||||
goto failed;
|
||||
|
||||
return wksp;
|
||||
|
||||
failed:
|
||||
ERROR("Failed to allocate zstd workspace\n");
|
||||
kfree(wksp);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
|
||||
static void zstd_free(void *strm)
|
||||
{
|
||||
struct workspace *wksp = strm;
|
||||
|
||||
if (wksp)
|
||||
vfree(wksp->mem);
|
||||
kfree(wksp);
|
||||
}
|
||||
|
||||
|
||||
static int zstd_uncompress(struct squashfs_sb_info *msblk, void *strm,
|
||||
struct buffer_head **bh, int b, int offset, int length,
|
||||
struct squashfs_page_actor *output)
|
||||
{
|
||||
struct workspace *wksp = strm;
|
||||
ZSTD_DStream *stream;
|
||||
size_t total_out = 0;
|
||||
size_t zstd_err;
|
||||
int k = 0;
|
||||
ZSTD_inBuffer in_buf = { NULL, 0, 0 };
|
||||
ZSTD_outBuffer out_buf = { NULL, 0, 0 };
|
||||
|
||||
stream = ZSTD_initDStream(wksp->window_size, wksp->mem, wksp->mem_size);
|
||||
|
||||
if (!stream) {
|
||||
ERROR("Failed to initialize zstd decompressor\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
out_buf.size = PAGE_SIZE;
|
||||
out_buf.dst = squashfs_first_page(output);
|
||||
|
||||
do {
|
||||
if (in_buf.pos == in_buf.size && k < b) {
|
||||
int avail = min(length, msblk->devblksize - offset);
|
||||
|
||||
length -= avail;
|
||||
in_buf.src = bh[k]->b_data + offset;
|
||||
in_buf.size = avail;
|
||||
in_buf.pos = 0;
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
if (out_buf.pos == out_buf.size) {
|
||||
out_buf.dst = squashfs_next_page(output);
|
||||
if (out_buf.dst == NULL) {
|
||||
/* Shouldn't run out of pages
|
||||
* before stream is done.
|
||||
*/
|
||||
squashfs_finish_page(output);
|
||||
goto out;
|
||||
}
|
||||
out_buf.pos = 0;
|
||||
out_buf.size = PAGE_SIZE;
|
||||
}
|
||||
|
||||
total_out -= out_buf.pos;
|
||||
zstd_err = ZSTD_decompressStream(stream, &out_buf, &in_buf);
|
||||
total_out += out_buf.pos; /* add the additional data produced */
|
||||
|
||||
if (in_buf.pos == in_buf.size && k < b)
|
||||
put_bh(bh[k++]);
|
||||
} while (zstd_err != 0 && !ZSTD_isError(zstd_err));
|
||||
|
||||
squashfs_finish_page(output);
|
||||
|
||||
if (ZSTD_isError(zstd_err)) {
|
||||
ERROR("zstd decompression error: %d\n",
|
||||
(int)ZSTD_getErrorCode(zstd_err));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (k < b)
|
||||
goto out;
|
||||
|
||||
return (int)total_out;
|
||||
|
||||
out:
|
||||
for (; k < b; k++)
|
||||
put_bh(bh[k]);
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
const struct squashfs_decompressor squashfs_zstd_comp_ops = {
|
||||
.init = zstd_init,
|
||||
.free = zstd_free,
|
||||
.decompress = zstd_uncompress,
|
||||
.id = ZSTD_COMPRESSION,
|
||||
.name = "zstd",
|
||||
.supported = 1
|
||||
};
|
||||
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* xxHash - Extremely Fast Hash algorithm
|
||||
* Copyright (C) 2012-2016, Yann Collet.
|
||||
*
|
||||
* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License version 2 as published by the
|
||||
* Free Software Foundation. This program is dual-licensed; you may select
|
||||
* either version 2 of the GNU General Public License ("GPL") or BSD license
|
||||
* ("BSD").
|
||||
*
|
||||
* You can contact the author at:
|
||||
* - xxHash homepage: http://cyan4973.github.io/xxHash/
|
||||
* - xxHash source repository: https://github.com/Cyan4973/xxHash
|
||||
*/
|
||||
|
||||
/*
|
||||
* Notice extracted from xxHash homepage:
|
||||
*
|
||||
* xxHash is an extremely fast Hash algorithm, running at RAM speed limits.
|
||||
* It also successfully passes all tests from the SMHasher suite.
|
||||
*
|
||||
* Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2
|
||||
* Duo @3GHz)
|
||||
*
|
||||
* Name Speed Q.Score Author
|
||||
* xxHash 5.4 GB/s 10
|
||||
* CrapWow 3.2 GB/s 2 Andrew
|
||||
* MumurHash 3a 2.7 GB/s 10 Austin Appleby
|
||||
* SpookyHash 2.0 GB/s 10 Bob Jenkins
|
||||
* SBox 1.4 GB/s 9 Bret Mulvey
|
||||
* Lookup3 1.2 GB/s 9 Bob Jenkins
|
||||
* SuperFastHash 1.2 GB/s 1 Paul Hsieh
|
||||
* CityHash64 1.05 GB/s 10 Pike & Alakuijala
|
||||
* FNV 0.55 GB/s 5 Fowler, Noll, Vo
|
||||
* CRC32 0.43 GB/s 9
|
||||
* MD5-32 0.33 GB/s 10 Ronald L. Rivest
|
||||
* SHA1-32 0.28 GB/s 10
|
||||
*
|
||||
* Q.Score is a measure of quality of the hash function.
|
||||
* It depends on successfully passing SMHasher test set.
|
||||
* 10 is a perfect score.
|
||||
*
|
||||
* A 64-bits version, named xxh64 offers much better speed,
|
||||
* but for 64-bits applications only.
|
||||
* Name Speed on 64 bits Speed on 32 bits
|
||||
* xxh64 13.8 GB/s 1.9 GB/s
|
||||
* xxh32 6.8 GB/s 6.0 GB/s
|
||||
*/
|
||||
|
||||
#ifndef XXHASH_H
|
||||
#define XXHASH_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/*-****************************
|
||||
* Simple Hash Functions
|
||||
*****************************/
|
||||
|
||||
/**
|
||||
* xxh32() - calculate the 32-bit hash of the input with a given seed.
|
||||
*
|
||||
* @input: The data to hash.
|
||||
* @length: The length of the data to hash.
|
||||
* @seed: The seed can be used to alter the result predictably.
|
||||
*
|
||||
* Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s
|
||||
*
|
||||
* Return: The 32-bit hash of the data.
|
||||
*/
|
||||
uint32_t xxh32(const void *input, size_t length, uint32_t seed);
|
||||
|
||||
/**
|
||||
* xxh64() - calculate the 64-bit hash of the input with a given seed.
|
||||
*
|
||||
* @input: The data to hash.
|
||||
* @length: The length of the data to hash.
|
||||
* @seed: The seed can be used to alter the result predictably.
|
||||
*
|
||||
* This function runs 2x faster on 64-bit systems, but slower on 32-bit systems.
|
||||
*
|
||||
* Return: The 64-bit hash of the data.
|
||||
*/
|
||||
uint64_t xxh64(const void *input, size_t length, uint64_t seed);
|
||||
|
||||
/*-****************************
|
||||
* Streaming Hash Functions
|
||||
*****************************/
|
||||
|
||||
/*
|
||||
* These definitions are only meant to allow allocation of XXH state
|
||||
* statically, on stack, or in a struct for example.
|
||||
* Do not use members directly.
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct xxh32_state - private xxh32 state, do not use members directly
|
||||
*/
|
||||
struct xxh32_state {
|
||||
uint32_t total_len_32;
|
||||
uint32_t large_len;
|
||||
uint32_t v1;
|
||||
uint32_t v2;
|
||||
uint32_t v3;
|
||||
uint32_t v4;
|
||||
uint32_t mem32[4];
|
||||
uint32_t memsize;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct xxh32_state - private xxh64 state, do not use members directly
|
||||
*/
|
||||
struct xxh64_state {
|
||||
uint64_t total_len;
|
||||
uint64_t v1;
|
||||
uint64_t v2;
|
||||
uint64_t v3;
|
||||
uint64_t v4;
|
||||
uint64_t mem64[4];
|
||||
uint32_t memsize;
|
||||
};
|
||||
|
||||
/**
|
||||
* xxh32_reset() - reset the xxh32 state to start a new hashing operation
|
||||
*
|
||||
* @state: The xxh32 state to reset.
|
||||
* @seed: Initialize the hash state with this seed.
|
||||
*
|
||||
* Call this function on any xxh32_state to prepare for a new hashing operation.
|
||||
*/
|
||||
void xxh32_reset(struct xxh32_state *state, uint32_t seed);
|
||||
|
||||
/**
|
||||
* xxh32_update() - hash the data given and update the xxh32 state
|
||||
*
|
||||
* @state: The xxh32 state to update.
|
||||
* @input: The data to hash.
|
||||
* @length: The length of the data to hash.
|
||||
*
|
||||
* After calling xxh32_reset() call xxh32_update() as many times as necessary.
|
||||
*
|
||||
* Return: Zero on success, otherwise an error code.
|
||||
*/
|
||||
int xxh32_update(struct xxh32_state *state, const void *input, size_t length);
|
||||
|
||||
/**
|
||||
* xxh32_digest() - produce the current xxh32 hash
|
||||
*
|
||||
* @state: Produce the current xxh32 hash of this state.
|
||||
*
|
||||
* A hash value can be produced at any time. It is still possible to continue
|
||||
* inserting input into the hash state after a call to xxh32_digest(), and
|
||||
* generate new hashes later on, by calling xxh32_digest() again.
|
||||
*
|
||||
* Return: The xxh32 hash stored in the state.
|
||||
*/
|
||||
uint32_t xxh32_digest(const struct xxh32_state *state);
|
||||
|
||||
/**
|
||||
* xxh64_reset() - reset the xxh64 state to start a new hashing operation
|
||||
*
|
||||
* @state: The xxh64 state to reset.
|
||||
* @seed: Initialize the hash state with this seed.
|
||||
*/
|
||||
void xxh64_reset(struct xxh64_state *state, uint64_t seed);
|
||||
|
||||
/**
|
||||
* xxh64_update() - hash the data given and update the xxh64 state
|
||||
* @state: The xxh64 state to update.
|
||||
* @input: The data to hash.
|
||||
* @length: The length of the data to hash.
|
||||
*
|
||||
* After calling xxh64_reset() call xxh64_update() as many times as necessary.
|
||||
*
|
||||
* Return: Zero on success, otherwise an error code.
|
||||
*/
|
||||
int xxh64_update(struct xxh64_state *state, const void *input, size_t length);
|
||||
|
||||
/**
|
||||
* xxh64_digest() - produce the current xxh64 hash
|
||||
*
|
||||
* @state: Produce the current xxh64 hash of this state.
|
||||
*
|
||||
* A hash value can be produced at any time. It is still possible to continue
|
||||
* inserting input into the hash state after a call to xxh64_digest(), and
|
||||
* generate new hashes later on, by calling xxh64_digest() again.
|
||||
*
|
||||
* Return: The xxh64 hash stored in the state.
|
||||
*/
|
||||
uint64_t xxh64_digest(const struct xxh64_state *state);
|
||||
|
||||
/*-**************************
|
||||
* Utils
|
||||
***************************/
|
||||
|
||||
/**
|
||||
* xxh32_copy_state() - copy the source state into the destination state
|
||||
*
|
||||
* @src: The source xxh32 state.
|
||||
* @dst: The destination xxh32 state.
|
||||
*/
|
||||
void xxh32_copy_state(struct xxh32_state *dst, const struct xxh32_state *src);
|
||||
|
||||
/**
|
||||
* xxh64_copy_state() - copy the source state into the destination state
|
||||
*
|
||||
* @src: The source xxh64 state.
|
||||
* @dst: The destination xxh64 state.
|
||||
*/
|
||||
void xxh64_copy_state(struct xxh64_state *dst, const struct xxh64_state *src);
|
||||
|
||||
#endif /* XXHASH_H */
|
||||
File diff suppressed because it is too large
Load Diff
Executable
+110
@@ -0,0 +1,110 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
# Constants
|
||||
SED_COMMANDS="commands.tmp"
|
||||
CLANG_FORMAT="clang-format-3.9"
|
||||
INCLUDE='include/linux/'
|
||||
LIB='lib/zstd/'
|
||||
SPACES=' '
|
||||
TAB=$'\t'
|
||||
TMP="replacements.tmp"
|
||||
|
||||
function prompt() {
|
||||
while true; do
|
||||
read -p "$1 [Y/n]" yn
|
||||
case $yn in
|
||||
'' ) yes='yes'; break;;
|
||||
[Yy]* ) yes='yes'; break;;
|
||||
[Nn]* ) yes=''; break;;
|
||||
* ) echo "Please answer yes or no.";;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
function check_not_present() {
|
||||
grep "$1" $INCLUDE*.h ${LIB}*.{h,c} && exit 1 || true
|
||||
}
|
||||
|
||||
function check_not_present_in_file() {
|
||||
grep "$1" "$2" && exit 1 || true
|
||||
}
|
||||
|
||||
function check_present_in_file() {
|
||||
grep "$1" "$2" > /dev/null 2> /dev/null || exit 1
|
||||
}
|
||||
|
||||
echo "Files: " $INCLUDE*.h $LIB*.{h,c}
|
||||
|
||||
prompt "Do you wish to replace 4 spaces with a tab?"
|
||||
if [ ! -z "$yes" ]
|
||||
then
|
||||
# Check files for existing tabs
|
||||
grep "$TAB" $INCLUDE*.h $LIB*.{h,c} && exit 1 || true
|
||||
# Replace the first tab on every line
|
||||
sed -i '' "s/^$SPACES/$TAB/" $INCLUDE*.h $LIB*.{h,c}
|
||||
|
||||
# Execute once and then execute as long as replacements are happening
|
||||
more_work="yes"
|
||||
while [ ! -z "$more_work" ]
|
||||
do
|
||||
rm -f $TMP
|
||||
# Replaces $SPACES that directly follow a $TAB with a $TAB.
|
||||
# $TMP will be non-empty if any replacements took place.
|
||||
sed -i '' "s/$TAB$SPACES/$TAB$TAB/w $TMP" $INCLUDE*.h $LIB*.{h,c}
|
||||
more_work=$(cat "$TMP")
|
||||
done
|
||||
rm -f $TMP
|
||||
fi
|
||||
|
||||
prompt "Do you wish to replace '{ ' with a tab?"
|
||||
if [ ! -z "$yes" ]
|
||||
then
|
||||
sed -i '' "s/$TAB{ /$TAB{$TAB/g" $INCLUDE*.h $LIB*.{h,c}
|
||||
fi
|
||||
|
||||
rm -f $SED_COMMANDS
|
||||
cat > $SED_COMMANDS <<EOF
|
||||
s/current/curr/g
|
||||
s/MEM_STATIC/ZSTD_STATIC/g
|
||||
s/MEM_check/ZSTD_check/g
|
||||
s/MEM_32bits/ZSTD_32bits/g
|
||||
s/MEM_64bits/ZSTD_64bits/g
|
||||
s/MEM_LITTLE_ENDIAN/ZSTD_LITTLE_ENDIAN/g
|
||||
s/MEM_isLittleEndian/ZSTD_isLittleEndian/g
|
||||
s/MEM_read/ZSTD_read/g
|
||||
s/MEM_write/ZSTD_write/g
|
||||
EOF
|
||||
|
||||
prompt "Do you wish to run these sed commands $(cat $SED_COMMANDS)?"
|
||||
if [ ! -z "$yes" ]
|
||||
then
|
||||
sed -i '' -f $SED_COMMANDS $LIB*.{h,c}
|
||||
fi
|
||||
rm -f $SED_COMMANDS
|
||||
|
||||
prompt "Do you wish to clang-format $LIB*.{h,c}?"
|
||||
if [ ! -z "$yes" ]
|
||||
then
|
||||
$CLANG_FORMAT -i ${LIB}*.{h,c}
|
||||
fi
|
||||
|
||||
prompt "Do you wish to run some checks?"
|
||||
if [ ! -z "$yes" ]
|
||||
then
|
||||
check_present_in_file ZSTD_STATIC_ASSERT ${LIB}zstd_internal.h
|
||||
check_not_present_in_file STATIC_ASSERT ${LIB}mem.h
|
||||
check_not_present_in_file "#define ZSTD_STATIC_ASSERT" ${LIB}compress.c
|
||||
check_not_present MEM_STATIC
|
||||
check_not_present FSE_COMMONDEFS_ONLY
|
||||
check_not_present "#if 0"
|
||||
check_not_present "#if 1"
|
||||
check_not_present _MSC_VER
|
||||
check_not_present __cplusplus
|
||||
check_not_present __STDC_VERSION__
|
||||
check_not_present __VMS
|
||||
check_not_present __GNUC__
|
||||
check_not_present __INTEL_COMPILER
|
||||
check_not_present FORCE_MEMORY_ACCESS
|
||||
check_not_present STATIC_LINKING_ONLY
|
||||
fi
|
||||
@@ -0,0 +1,19 @@
|
||||
diff --git a/lib/Kconfig b/lib/Kconfig
|
||||
index b6009d7..f00ddab 100644
|
||||
--- a/lib/Kconfig
|
||||
+++ b/lib/Kconfig
|
||||
@@ -241,6 +241,14 @@ config LZ4HC_COMPRESS
|
||||
config LZ4_DECOMPRESS
|
||||
tristate
|
||||
|
||||
+config ZSTD_COMPRESS
|
||||
+ select XXHASH
|
||||
+ tristate
|
||||
+
|
||||
+config ZSTD_DECOMPRESS
|
||||
+ select XXHASH
|
||||
+ tristate
|
||||
+
|
||||
source "lib/xz/Kconfig"
|
||||
|
||||
#
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/lib/Makefile b/lib/Makefile
|
||||
index e16f94a..0cfd529 100644
|
||||
--- a/lib/Makefile
|
||||
+++ b/lib/Makefile
|
||||
@@ -115,6 +115,8 @@ obj-$(CONFIG_LZO_DECOMPRESS) += lzo/
|
||||
obj-$(CONFIG_LZ4_COMPRESS) += lz4/
|
||||
obj-$(CONFIG_LZ4HC_COMPRESS) += lz4/
|
||||
obj-$(CONFIG_LZ4_DECOMPRESS) += lz4/
|
||||
+obj-$(CONFIG_ZSTD_COMPRESS) += zstd/
|
||||
+obj-$(CONFIG_ZSTD_DECOMPRESS) += zstd/
|
||||
obj-$(CONFIG_XZ_DEC) += xz/
|
||||
obj-$(CONFIG_RAID6_PQ) += raid6/
|
||||
|
||||
+37
-283
@@ -34,273 +34,16 @@
|
||||
* ("BSD").
|
||||
*
|
||||
* You can contact the author at:
|
||||
* - xxHash homepage: https://cyan4973.github.io/xxHash/
|
||||
* - xxHash source repository: https://github.com/Cyan4973/xxHash
|
||||
*/
|
||||
|
||||
/*
|
||||
* Notice extracted from xxHash homepage:
|
||||
*
|
||||
* xxHash is an extremely fast Hash algorithm, running at RAM speed limits.
|
||||
* It also successfully passes all tests from the SMHasher suite.
|
||||
*
|
||||
* Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2
|
||||
* Duo @3GHz)
|
||||
*
|
||||
* Name Speed Q.Score Author
|
||||
* xxHash 5.4 GB/s 10
|
||||
* CrapWow 3.2 GB/s 2 Andrew
|
||||
* MumurHash 3a 2.7 GB/s 10 Austin Appleby
|
||||
* SpookyHash 2.0 GB/s 10 Bob Jenkins
|
||||
* SBox 1.4 GB/s 9 Bret Mulvey
|
||||
* Lookup3 1.2 GB/s 9 Bob Jenkins
|
||||
* SuperFastHash 1.2 GB/s 1 Paul Hsieh
|
||||
* CityHash64 1.05 GB/s 10 Pike & Alakuijala
|
||||
* FNV 0.55 GB/s 5 Fowler, Noll, Vo
|
||||
* CRC32 0.43 GB/s 9
|
||||
* MD5-32 0.33 GB/s 10 Ronald L. Rivest
|
||||
* SHA1-32 0.28 GB/s 10
|
||||
*
|
||||
* Q.Score is a measure of quality of the hash function.
|
||||
* It depends on successfully passing SMHasher test set.
|
||||
* 10 is a perfect score.
|
||||
*
|
||||
* A 64-bits version, named xxh64 offers much better speed,
|
||||
* but for 64-bits applications only.
|
||||
* Name Speed on 64 bits Speed on 32 bits
|
||||
* xxh64 13.8 GB/s 1.9 GB/s
|
||||
* xxh32 6.8 GB/s 6.0 GB/s
|
||||
*/
|
||||
|
||||
#ifndef XXHASH_H
|
||||
#define XXHASH_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define XXH_API static inline __attribute__((unused))
|
||||
/*-****************************
|
||||
* Simple Hash Functions
|
||||
*****************************/
|
||||
|
||||
/**
|
||||
* xxh32() - calculate the 32-bit hash of the input with a given seed.
|
||||
*
|
||||
* @input: The data to hash.
|
||||
* @length: The length of the data to hash.
|
||||
* @seed: The seed can be used to alter the result predictably.
|
||||
*
|
||||
* Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s
|
||||
*
|
||||
* Return: The 32-bit hash of the data.
|
||||
*/
|
||||
XXH_API uint32_t xxh32(const void *input, size_t length, uint32_t seed);
|
||||
|
||||
/**
|
||||
* xxh64() - calculate the 64-bit hash of the input with a given seed.
|
||||
*
|
||||
* @input: The data to hash.
|
||||
* @length: The length of the data to hash.
|
||||
* @seed: The seed can be used to alter the result predictably.
|
||||
*
|
||||
* This function runs 2x faster on 64-bit systems, but slower on 32-bit systems.
|
||||
*
|
||||
* Return: The 64-bit hash of the data.
|
||||
*/
|
||||
XXH_API uint64_t xxh64(const void *input, size_t length, uint64_t seed);
|
||||
|
||||
/**
|
||||
* xxhash() - calculate wordsize hash of the input with a given seed
|
||||
* @input: The data to hash.
|
||||
* @length: The length of the data to hash.
|
||||
* @seed: The seed can be used to alter the result predictably.
|
||||
*
|
||||
* If the hash does not need to be comparable between machines with
|
||||
* different word sizes, this function will call whichever of xxh32()
|
||||
* or xxh64() is faster.
|
||||
*
|
||||
* Return: wordsize hash of the data.
|
||||
*/
|
||||
|
||||
static inline unsigned long xxhash(const void *input, size_t length,
|
||||
uint64_t seed)
|
||||
{
|
||||
#if BITS_PER_LONG == 64
|
||||
return xxh64(input, length, seed);
|
||||
#else
|
||||
return xxh32(input, length, seed);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*-****************************
|
||||
* Streaming Hash Functions
|
||||
*****************************/
|
||||
|
||||
/*
|
||||
* These definitions are only meant to allow allocation of XXH state
|
||||
* statically, on stack, or in a struct for example.
|
||||
* Do not use members directly.
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct xxh32_state - private xxh32 state, do not use members directly
|
||||
*/
|
||||
struct xxh32_state {
|
||||
uint32_t total_len_32;
|
||||
uint32_t large_len;
|
||||
uint32_t v1;
|
||||
uint32_t v2;
|
||||
uint32_t v3;
|
||||
uint32_t v4;
|
||||
uint32_t mem32[4];
|
||||
uint32_t memsize;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct xxh32_state - private xxh64 state, do not use members directly
|
||||
*/
|
||||
struct xxh64_state {
|
||||
uint64_t total_len;
|
||||
uint64_t v1;
|
||||
uint64_t v2;
|
||||
uint64_t v3;
|
||||
uint64_t v4;
|
||||
uint64_t mem64[4];
|
||||
uint32_t memsize;
|
||||
};
|
||||
|
||||
/**
|
||||
* xxh32_reset() - reset the xxh32 state to start a new hashing operation
|
||||
*
|
||||
* @state: The xxh32 state to reset.
|
||||
* @seed: Initialize the hash state with this seed.
|
||||
*
|
||||
* Call this function on any xxh32_state to prepare for a new hashing operation.
|
||||
*/
|
||||
XXH_API void xxh32_reset(struct xxh32_state *state, uint32_t seed);
|
||||
|
||||
/**
|
||||
* xxh32_update() - hash the data given and update the xxh32 state
|
||||
*
|
||||
* @state: The xxh32 state to update.
|
||||
* @input: The data to hash.
|
||||
* @length: The length of the data to hash.
|
||||
*
|
||||
* After calling xxh32_reset() call xxh32_update() as many times as necessary.
|
||||
*
|
||||
* Return: Zero on success, otherwise an error code.
|
||||
*/
|
||||
XXH_API int xxh32_update(struct xxh32_state *state, const void *input, size_t length);
|
||||
|
||||
/**
|
||||
* xxh32_digest() - produce the current xxh32 hash
|
||||
*
|
||||
* @state: Produce the current xxh32 hash of this state.
|
||||
*
|
||||
* A hash value can be produced at any time. It is still possible to continue
|
||||
* inserting input into the hash state after a call to xxh32_digest(), and
|
||||
* generate new hashes later on, by calling xxh32_digest() again.
|
||||
*
|
||||
* Return: The xxh32 hash stored in the state.
|
||||
*/
|
||||
XXH_API uint32_t xxh32_digest(const struct xxh32_state *state);
|
||||
|
||||
/**
|
||||
* xxh64_reset() - reset the xxh64 state to start a new hashing operation
|
||||
*
|
||||
* @state: The xxh64 state to reset.
|
||||
* @seed: Initialize the hash state with this seed.
|
||||
*/
|
||||
XXH_API void xxh64_reset(struct xxh64_state *state, uint64_t seed);
|
||||
|
||||
/**
|
||||
* xxh64_update() - hash the data given and update the xxh64 state
|
||||
* @state: The xxh64 state to update.
|
||||
* @input: The data to hash.
|
||||
* @length: The length of the data to hash.
|
||||
*
|
||||
* After calling xxh64_reset() call xxh64_update() as many times as necessary.
|
||||
*
|
||||
* Return: Zero on success, otherwise an error code.
|
||||
*/
|
||||
XXH_API int xxh64_update(struct xxh64_state *state, const void *input, size_t length);
|
||||
|
||||
/**
|
||||
* xxh64_digest() - produce the current xxh64 hash
|
||||
*
|
||||
* @state: Produce the current xxh64 hash of this state.
|
||||
*
|
||||
* A hash value can be produced at any time. It is still possible to continue
|
||||
* inserting input into the hash state after a call to xxh64_digest(), and
|
||||
* generate new hashes later on, by calling xxh64_digest() again.
|
||||
*
|
||||
* Return: The xxh64 hash stored in the state.
|
||||
*/
|
||||
XXH_API uint64_t xxh64_digest(const struct xxh64_state *state);
|
||||
|
||||
/*-**************************
|
||||
* Utils
|
||||
***************************/
|
||||
|
||||
/**
|
||||
* xxh32_copy_state() - copy the source state into the destination state
|
||||
*
|
||||
* @src: The source xxh32 state.
|
||||
* @dst: The destination xxh32 state.
|
||||
*/
|
||||
XXH_API void xxh32_copy_state(struct xxh32_state *dst, const struct xxh32_state *src);
|
||||
|
||||
/**
|
||||
* xxh64_copy_state() - copy the source state into the destination state
|
||||
*
|
||||
* @src: The source xxh64 state.
|
||||
* @dst: The destination xxh64 state.
|
||||
*/
|
||||
XXH_API void xxh64_copy_state(struct xxh64_state *dst, const struct xxh64_state *src);
|
||||
|
||||
/*
|
||||
* xxHash - Extremely Fast Hash algorithm
|
||||
* Copyright (C) 2012-2016, Yann Collet.
|
||||
*
|
||||
* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License version 2 as published by the
|
||||
* Free Software Foundation. This program is dual-licensed; you may select
|
||||
* either version 2 of the GNU General Public License ("GPL") or BSD license
|
||||
* ("BSD").
|
||||
*
|
||||
* You can contact the author at:
|
||||
* - xxHash homepage: https://cyan4973.github.io/xxHash/
|
||||
* - xxHash homepage: http://cyan4973.github.io/xxHash/
|
||||
* - xxHash source repository: https://github.com/Cyan4973/xxHash
|
||||
*/
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/xxhash.h>
|
||||
|
||||
/*-*************************************
|
||||
@@ -333,15 +76,17 @@ static const uint64_t PRIME64_5 = 2870177450012600261ULL;
|
||||
/*-**************************
|
||||
* Utils
|
||||
***************************/
|
||||
XXH_API void xxh32_copy_state(struct xxh32_state *dst, const struct xxh32_state *src)
|
||||
void xxh32_copy_state(struct xxh32_state *dst, const struct xxh32_state *src)
|
||||
{
|
||||
__builtin_memcpy(dst, src, sizeof(*dst));
|
||||
memcpy(dst, src, sizeof(*dst));
|
||||
}
|
||||
EXPORT_SYMBOL(xxh32_copy_state);
|
||||
|
||||
XXH_API void xxh64_copy_state(struct xxh64_state *dst, const struct xxh64_state *src)
|
||||
void xxh64_copy_state(struct xxh64_state *dst, const struct xxh64_state *src)
|
||||
{
|
||||
__builtin_memcpy(dst, src, sizeof(*dst));
|
||||
memcpy(dst, src, sizeof(*dst));
|
||||
}
|
||||
EXPORT_SYMBOL(xxh64_copy_state);
|
||||
|
||||
/*-***************************
|
||||
* Simple Hash Functions
|
||||
@@ -354,7 +99,7 @@ static uint32_t xxh32_round(uint32_t seed, const uint32_t input)
|
||||
return seed;
|
||||
}
|
||||
|
||||
XXH_API uint32_t xxh32(const void *input, const size_t len, const uint32_t seed)
|
||||
uint32_t xxh32(const void *input, const size_t len, const uint32_t seed)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)input;
|
||||
const uint8_t *b_end = p + len;
|
||||
@@ -406,6 +151,7 @@ XXH_API uint32_t xxh32(const void *input, const size_t len, const uint32_t seed)
|
||||
|
||||
return h32;
|
||||
}
|
||||
EXPORT_SYMBOL(xxh32);
|
||||
|
||||
static uint64_t xxh64_round(uint64_t acc, const uint64_t input)
|
||||
{
|
||||
@@ -423,7 +169,7 @@ static uint64_t xxh64_merge_round(uint64_t acc, uint64_t val)
|
||||
return acc;
|
||||
}
|
||||
|
||||
XXH_API uint64_t xxh64(const void *input, const size_t len, const uint64_t seed)
|
||||
uint64_t xxh64(const void *input, const size_t len, const uint64_t seed)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)input;
|
||||
const uint8_t *const b_end = p + len;
|
||||
@@ -488,37 +234,40 @@ XXH_API uint64_t xxh64(const void *input, const size_t len, const uint64_t seed)
|
||||
|
||||
return h64;
|
||||
}
|
||||
EXPORT_SYMBOL(xxh64);
|
||||
|
||||
/*-**************************************************
|
||||
* Advanced Hash Functions
|
||||
***************************************************/
|
||||
XXH_API void xxh32_reset(struct xxh32_state *statePtr, const uint32_t seed)
|
||||
void xxh32_reset(struct xxh32_state *statePtr, const uint32_t seed)
|
||||
{
|
||||
/* use a local state for memcpy() to avoid strict-aliasing warnings */
|
||||
struct xxh32_state state;
|
||||
|
||||
__builtin_memset(&state, 0, sizeof(state));
|
||||
memset(&state, 0, sizeof(state));
|
||||
state.v1 = seed + PRIME32_1 + PRIME32_2;
|
||||
state.v2 = seed + PRIME32_2;
|
||||
state.v3 = seed + 0;
|
||||
state.v4 = seed - PRIME32_1;
|
||||
__builtin_memcpy(statePtr, &state, sizeof(state));
|
||||
memcpy(statePtr, &state, sizeof(state));
|
||||
}
|
||||
EXPORT_SYMBOL(xxh32_reset);
|
||||
|
||||
XXH_API void xxh64_reset(struct xxh64_state *statePtr, const uint64_t seed)
|
||||
void xxh64_reset(struct xxh64_state *statePtr, const uint64_t seed)
|
||||
{
|
||||
/* use a local state for memcpy() to avoid strict-aliasing warnings */
|
||||
struct xxh64_state state;
|
||||
|
||||
__builtin_memset(&state, 0, sizeof(state));
|
||||
memset(&state, 0, sizeof(state));
|
||||
state.v1 = seed + PRIME64_1 + PRIME64_2;
|
||||
state.v2 = seed + PRIME64_2;
|
||||
state.v3 = seed + 0;
|
||||
state.v4 = seed - PRIME64_1;
|
||||
__builtin_memcpy(statePtr, &state, sizeof(state));
|
||||
memcpy(statePtr, &state, sizeof(state));
|
||||
}
|
||||
EXPORT_SYMBOL(xxh64_reset);
|
||||
|
||||
XXH_API int xxh32_update(struct xxh32_state *state, const void *input, const size_t len)
|
||||
int xxh32_update(struct xxh32_state *state, const void *input, const size_t len)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)input;
|
||||
const uint8_t *const b_end = p + len;
|
||||
@@ -530,7 +279,7 @@ XXH_API int xxh32_update(struct xxh32_state *state, const void *input, const siz
|
||||
state->large_len |= (len >= 16) | (state->total_len_32 >= 16);
|
||||
|
||||
if (state->memsize + len < 16) { /* fill in tmp buffer */
|
||||
__builtin_memcpy((uint8_t *)(state->mem32) + state->memsize, input, len);
|
||||
memcpy((uint8_t *)(state->mem32) + state->memsize, input, len);
|
||||
state->memsize += (uint32_t)len;
|
||||
return 0;
|
||||
}
|
||||
@@ -538,7 +287,7 @@ XXH_API int xxh32_update(struct xxh32_state *state, const void *input, const siz
|
||||
if (state->memsize) { /* some data left from previous update */
|
||||
const uint32_t *p32 = state->mem32;
|
||||
|
||||
__builtin_memcpy((uint8_t *)(state->mem32) + state->memsize, input,
|
||||
memcpy((uint8_t *)(state->mem32) + state->memsize, input,
|
||||
16 - state->memsize);
|
||||
|
||||
state->v1 = xxh32_round(state->v1, get_unaligned_le32(p32));
|
||||
@@ -579,14 +328,15 @@ XXH_API int xxh32_update(struct xxh32_state *state, const void *input, const siz
|
||||
}
|
||||
|
||||
if (p < b_end) {
|
||||
__builtin_memcpy(state->mem32, p, (size_t)(b_end-p));
|
||||
memcpy(state->mem32, p, (size_t)(b_end-p));
|
||||
state->memsize = (uint32_t)(b_end-p);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(xxh32_update);
|
||||
|
||||
XXH_API uint32_t xxh32_digest(const struct xxh32_state *state)
|
||||
uint32_t xxh32_digest(const struct xxh32_state *state)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)state->mem32;
|
||||
const uint8_t *const b_end = (const uint8_t *)(state->mem32) +
|
||||
@@ -622,8 +372,9 @@ XXH_API uint32_t xxh32_digest(const struct xxh32_state *state)
|
||||
|
||||
return h32;
|
||||
}
|
||||
EXPORT_SYMBOL(xxh32_digest);
|
||||
|
||||
XXH_API int xxh64_update(struct xxh64_state *state, const void *input, const size_t len)
|
||||
int xxh64_update(struct xxh64_state *state, const void *input, const size_t len)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)input;
|
||||
const uint8_t *const b_end = p + len;
|
||||
@@ -634,7 +385,7 @@ XXH_API int xxh64_update(struct xxh64_state *state, const void *input, const siz
|
||||
state->total_len += len;
|
||||
|
||||
if (state->memsize + len < 32) { /* fill in tmp buffer */
|
||||
__builtin_memcpy(((uint8_t *)state->mem64) + state->memsize, input, len);
|
||||
memcpy(((uint8_t *)state->mem64) + state->memsize, input, len);
|
||||
state->memsize += (uint32_t)len;
|
||||
return 0;
|
||||
}
|
||||
@@ -642,7 +393,7 @@ XXH_API int xxh64_update(struct xxh64_state *state, const void *input, const siz
|
||||
if (state->memsize) { /* tmp buffer is full */
|
||||
uint64_t *p64 = state->mem64;
|
||||
|
||||
__builtin_memcpy(((uint8_t *)p64) + state->memsize, input,
|
||||
memcpy(((uint8_t *)p64) + state->memsize, input,
|
||||
32 - state->memsize);
|
||||
|
||||
state->v1 = xxh64_round(state->v1, get_unaligned_le64(p64));
|
||||
@@ -682,14 +433,15 @@ XXH_API int xxh64_update(struct xxh64_state *state, const void *input, const siz
|
||||
}
|
||||
|
||||
if (p < b_end) {
|
||||
__builtin_memcpy(state->mem64, p, (size_t)(b_end-p));
|
||||
memcpy(state->mem64, p, (size_t)(b_end-p));
|
||||
state->memsize = (uint32_t)(b_end - p);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(xxh64_update);
|
||||
|
||||
XXH_API uint64_t xxh64_digest(const struct xxh64_state *state)
|
||||
uint64_t xxh64_digest(const struct xxh64_state *state)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)state->mem64;
|
||||
const uint8_t *const b_end = (const uint8_t *)state->mem64 +
|
||||
@@ -742,5 +494,7 @@ XXH_API uint64_t xxh64_digest(const struct xxh64_state *state)
|
||||
|
||||
return h64;
|
||||
}
|
||||
EXPORT_SYMBOL(xxh64_digest);
|
||||
|
||||
#endif /* XXHASH_H */
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_DESCRIPTION("xxHash");
|
||||
@@ -0,0 +1,11 @@
|
||||
BasedOnStyle: LLVM
|
||||
IndentWidth: 8
|
||||
UseTab: Always
|
||||
BreakBeforeBraces: Linux
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
IndentCaseLabels: false
|
||||
|
||||
ColumnLimit: 160
|
||||
AlignEscapedNewlinesLeft: true
|
||||
ReflowComments: true
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
@@ -0,0 +1,18 @@
|
||||
obj-$(CONFIG_ZSTD_COMPRESS) += zstd_compress.o
|
||||
obj-$(CONFIG_ZSTD_DECOMPRESS) += zstd_decompress.o
|
||||
|
||||
ccflags-y += -O3
|
||||
|
||||
# Object files unique to zstd_compress and zstd_decompress
|
||||
zstd_compress-y := fse_compress.o huf_compress.o compress.o
|
||||
zstd_decompress-y := huf_decompress.o decompress.o
|
||||
|
||||
# These object files are shared between the modules.
|
||||
# Always add them to zstd_compress.
|
||||
# Unless both zstd_compress and zstd_decompress are built in
|
||||
# then also add them to zstd_decompress.
|
||||
zstd_compress-y += entropy_common.o fse_decompress.o zstd_common.o
|
||||
|
||||
ifneq ($(CONFIG_ZSTD_COMPRESS)$(CONFIG_ZSTD_DECOMPRESS),yy)
|
||||
zstd_decompress-y += entropy_common.o fse_decompress.o zstd_common.o
|
||||
endif
|
||||
@@ -0,0 +1,374 @@
|
||||
/*
|
||||
* bitstream
|
||||
* Part of FSE library
|
||||
* header file (to include)
|
||||
* Copyright (C) 2013-2016, Yann Collet.
|
||||
*
|
||||
* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License version 2 as published by the
|
||||
* Free Software Foundation. This program is dual-licensed; you may select
|
||||
* either version 2 of the GNU General Public License ("GPL") or BSD license
|
||||
* ("BSD").
|
||||
*
|
||||
* You can contact the author at :
|
||||
* - Source repository : https://github.com/Cyan4973/FiniteStateEntropy
|
||||
*/
|
||||
#ifndef BITSTREAM_H_MODULE
|
||||
#define BITSTREAM_H_MODULE
|
||||
|
||||
/*
|
||||
* This API consists of small unitary functions, which must be inlined for best performance.
|
||||
* Since link-time-optimization is not available for all compilers,
|
||||
* these functions are defined into a .h to be included.
|
||||
*/
|
||||
|
||||
/*-****************************************
|
||||
* Dependencies
|
||||
******************************************/
|
||||
#include "error_private.h" /* error codes and messages */
|
||||
#include "mem.h" /* unaligned access routines */
|
||||
|
||||
/*=========================================
|
||||
* Target specific
|
||||
=========================================*/
|
||||
#define STREAM_ACCUMULATOR_MIN_32 25
|
||||
#define STREAM_ACCUMULATOR_MIN_64 57
|
||||
#define STREAM_ACCUMULATOR_MIN ((U32)(ZSTD_32bits() ? STREAM_ACCUMULATOR_MIN_32 : STREAM_ACCUMULATOR_MIN_64))
|
||||
|
||||
/*-******************************************
|
||||
* bitStream encoding API (write forward)
|
||||
********************************************/
|
||||
/* bitStream can mix input from multiple sources.
|
||||
* A critical property of these streams is that they encode and decode in **reverse** direction.
|
||||
* So the first bit sequence you add will be the last to be read, like a LIFO stack.
|
||||
*/
|
||||
typedef struct {
|
||||
size_t bitContainer;
|
||||
int bitPos;
|
||||
char *startPtr;
|
||||
char *ptr;
|
||||
char *endPtr;
|
||||
} BIT_CStream_t;
|
||||
|
||||
ZSTD_STATIC size_t BIT_initCStream(BIT_CStream_t *bitC, void *dstBuffer, size_t dstCapacity);
|
||||
ZSTD_STATIC void BIT_addBits(BIT_CStream_t *bitC, size_t value, unsigned nbBits);
|
||||
ZSTD_STATIC void BIT_flushBits(BIT_CStream_t *bitC);
|
||||
ZSTD_STATIC size_t BIT_closeCStream(BIT_CStream_t *bitC);
|
||||
|
||||
/* Start with initCStream, providing the size of buffer to write into.
|
||||
* bitStream will never write outside of this buffer.
|
||||
* `dstCapacity` must be >= sizeof(bitD->bitContainer), otherwise @return will be an error code.
|
||||
*
|
||||
* bits are first added to a local register.
|
||||
* Local register is size_t, hence 64-bits on 64-bits systems, or 32-bits on 32-bits systems.
|
||||
* Writing data into memory is an explicit operation, performed by the flushBits function.
|
||||
* Hence keep track how many bits are potentially stored into local register to avoid register overflow.
|
||||
* After a flushBits, a maximum of 7 bits might still be stored into local register.
|
||||
*
|
||||
* Avoid storing elements of more than 24 bits if you want compatibility with 32-bits bitstream readers.
|
||||
*
|
||||
* Last operation is to close the bitStream.
|
||||
* The function returns the final size of CStream in bytes.
|
||||
* If data couldn't fit into `dstBuffer`, it will return a 0 ( == not storable)
|
||||
*/
|
||||
|
||||
/*-********************************************
|
||||
* bitStream decoding API (read backward)
|
||||
**********************************************/
|
||||
typedef struct {
|
||||
size_t bitContainer;
|
||||
unsigned bitsConsumed;
|
||||
const char *ptr;
|
||||
const char *start;
|
||||
} BIT_DStream_t;
|
||||
|
||||
typedef enum {
|
||||
BIT_DStream_unfinished = 0,
|
||||
BIT_DStream_endOfBuffer = 1,
|
||||
BIT_DStream_completed = 2,
|
||||
BIT_DStream_overflow = 3
|
||||
} BIT_DStream_status; /* result of BIT_reloadDStream() */
|
||||
/* 1,2,4,8 would be better for bitmap combinations, but slows down performance a bit ... :( */
|
||||
|
||||
ZSTD_STATIC size_t BIT_initDStream(BIT_DStream_t *bitD, const void *srcBuffer, size_t srcSize);
|
||||
ZSTD_STATIC size_t BIT_readBits(BIT_DStream_t *bitD, unsigned nbBits);
|
||||
ZSTD_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t *bitD);
|
||||
ZSTD_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t *bitD);
|
||||
|
||||
/* Start by invoking BIT_initDStream().
|
||||
* A chunk of the bitStream is then stored into a local register.
|
||||
* Local register size is 64-bits on 64-bits systems, 32-bits on 32-bits systems (size_t).
|
||||
* You can then retrieve bitFields stored into the local register, **in reverse order**.
|
||||
* Local register is explicitly reloaded from memory by the BIT_reloadDStream() method.
|
||||
* A reload guarantee a minimum of ((8*sizeof(bitD->bitContainer))-7) bits when its result is BIT_DStream_unfinished.
|
||||
* Otherwise, it can be less than that, so proceed accordingly.
|
||||
* Checking if DStream has reached its end can be performed with BIT_endOfDStream().
|
||||
*/
|
||||
|
||||
/*-****************************************
|
||||
* unsafe API
|
||||
******************************************/
|
||||
ZSTD_STATIC void BIT_addBitsFast(BIT_CStream_t *bitC, size_t value, unsigned nbBits);
|
||||
/* faster, but works only if value is "clean", meaning all high bits above nbBits are 0 */
|
||||
|
||||
ZSTD_STATIC void BIT_flushBitsFast(BIT_CStream_t *bitC);
|
||||
/* unsafe version; does not check buffer overflow */
|
||||
|
||||
ZSTD_STATIC size_t BIT_readBitsFast(BIT_DStream_t *bitD, unsigned nbBits);
|
||||
/* faster, but works only if nbBits >= 1 */
|
||||
|
||||
/*-**************************************************************
|
||||
* Internal functions
|
||||
****************************************************************/
|
||||
ZSTD_STATIC unsigned BIT_highbit32(register U32 val) { return 31 - __builtin_clz(val); }
|
||||
|
||||
/*===== Local Constants =====*/
|
||||
static const unsigned BIT_mask[] = {0, 1, 3, 7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF,
|
||||
0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 0x1FFFF,
|
||||
0x3FFFF, 0x7FFFF, 0xFFFFF, 0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF, 0x1FFFFFF, 0x3FFFFFF}; /* up to 26 bits */
|
||||
|
||||
/*-**************************************************************
|
||||
* bitStream encoding
|
||||
****************************************************************/
|
||||
/*! BIT_initCStream() :
|
||||
* `dstCapacity` must be > sizeof(void*)
|
||||
* @return : 0 if success,
|
||||
otherwise an error code (can be tested using ERR_isError() ) */
|
||||
ZSTD_STATIC size_t BIT_initCStream(BIT_CStream_t *bitC, void *startPtr, size_t dstCapacity)
|
||||
{
|
||||
bitC->bitContainer = 0;
|
||||
bitC->bitPos = 0;
|
||||
bitC->startPtr = (char *)startPtr;
|
||||
bitC->ptr = bitC->startPtr;
|
||||
bitC->endPtr = bitC->startPtr + dstCapacity - sizeof(bitC->ptr);
|
||||
if (dstCapacity <= sizeof(bitC->ptr))
|
||||
return ERROR(dstSize_tooSmall);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! BIT_addBits() :
|
||||
can add up to 26 bits into `bitC`.
|
||||
Does not check for register overflow ! */
|
||||
ZSTD_STATIC void BIT_addBits(BIT_CStream_t *bitC, size_t value, unsigned nbBits)
|
||||
{
|
||||
bitC->bitContainer |= (value & BIT_mask[nbBits]) << bitC->bitPos;
|
||||
bitC->bitPos += nbBits;
|
||||
}
|
||||
|
||||
/*! BIT_addBitsFast() :
|
||||
* works only if `value` is _clean_, meaning all high bits above nbBits are 0 */
|
||||
ZSTD_STATIC void BIT_addBitsFast(BIT_CStream_t *bitC, size_t value, unsigned nbBits)
|
||||
{
|
||||
bitC->bitContainer |= value << bitC->bitPos;
|
||||
bitC->bitPos += nbBits;
|
||||
}
|
||||
|
||||
/*! BIT_flushBitsFast() :
|
||||
* unsafe version; does not check buffer overflow */
|
||||
ZSTD_STATIC void BIT_flushBitsFast(BIT_CStream_t *bitC)
|
||||
{
|
||||
size_t const nbBytes = bitC->bitPos >> 3;
|
||||
ZSTD_writeLEST(bitC->ptr, bitC->bitContainer);
|
||||
bitC->ptr += nbBytes;
|
||||
bitC->bitPos &= 7;
|
||||
bitC->bitContainer >>= nbBytes * 8; /* if bitPos >= sizeof(bitContainer)*8 --> undefined behavior */
|
||||
}
|
||||
|
||||
/*! BIT_flushBits() :
|
||||
* safe version; check for buffer overflow, and prevents it.
|
||||
* note : does not signal buffer overflow. This will be revealed later on using BIT_closeCStream() */
|
||||
ZSTD_STATIC void BIT_flushBits(BIT_CStream_t *bitC)
|
||||
{
|
||||
size_t const nbBytes = bitC->bitPos >> 3;
|
||||
ZSTD_writeLEST(bitC->ptr, bitC->bitContainer);
|
||||
bitC->ptr += nbBytes;
|
||||
if (bitC->ptr > bitC->endPtr)
|
||||
bitC->ptr = bitC->endPtr;
|
||||
bitC->bitPos &= 7;
|
||||
bitC->bitContainer >>= nbBytes * 8; /* if bitPos >= sizeof(bitContainer)*8 --> undefined behavior */
|
||||
}
|
||||
|
||||
/*! BIT_closeCStream() :
|
||||
* @return : size of CStream, in bytes,
|
||||
or 0 if it could not fit into dstBuffer */
|
||||
ZSTD_STATIC size_t BIT_closeCStream(BIT_CStream_t *bitC)
|
||||
{
|
||||
BIT_addBitsFast(bitC, 1, 1); /* endMark */
|
||||
BIT_flushBits(bitC);
|
||||
|
||||
if (bitC->ptr >= bitC->endPtr)
|
||||
return 0; /* doesn't fit within authorized budget : cancel */
|
||||
|
||||
return (bitC->ptr - bitC->startPtr) + (bitC->bitPos > 0);
|
||||
}
|
||||
|
||||
/*-********************************************************
|
||||
* bitStream decoding
|
||||
**********************************************************/
|
||||
/*! BIT_initDStream() :
|
||||
* Initialize a BIT_DStream_t.
|
||||
* `bitD` : a pointer to an already allocated BIT_DStream_t structure.
|
||||
* `srcSize` must be the *exact* size of the bitStream, in bytes.
|
||||
* @return : size of stream (== srcSize) or an errorCode if a problem is detected
|
||||
*/
|
||||
ZSTD_STATIC size_t BIT_initDStream(BIT_DStream_t *bitD, const void *srcBuffer, size_t srcSize)
|
||||
{
|
||||
if (srcSize < 1) {
|
||||
memset(bitD, 0, sizeof(*bitD));
|
||||
return ERROR(srcSize_wrong);
|
||||
}
|
||||
|
||||
if (srcSize >= sizeof(bitD->bitContainer)) { /* normal case */
|
||||
bitD->start = (const char *)srcBuffer;
|
||||
bitD->ptr = (const char *)srcBuffer + srcSize - sizeof(bitD->bitContainer);
|
||||
bitD->bitContainer = ZSTD_readLEST(bitD->ptr);
|
||||
{
|
||||
BYTE const lastByte = ((const BYTE *)srcBuffer)[srcSize - 1];
|
||||
bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0; /* ensures bitsConsumed is always set */
|
||||
if (lastByte == 0)
|
||||
return ERROR(GENERIC); /* endMark not present */
|
||||
}
|
||||
} else {
|
||||
bitD->start = (const char *)srcBuffer;
|
||||
bitD->ptr = bitD->start;
|
||||
bitD->bitContainer = *(const BYTE *)(bitD->start);
|
||||
switch (srcSize) {
|
||||
case 7: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[6]) << (sizeof(bitD->bitContainer) * 8 - 16);
|
||||
case 6: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[5]) << (sizeof(bitD->bitContainer) * 8 - 24);
|
||||
case 5: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[4]) << (sizeof(bitD->bitContainer) * 8 - 32);
|
||||
case 4: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[3]) << 24;
|
||||
case 3: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[2]) << 16;
|
||||
case 2: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[1]) << 8;
|
||||
default:;
|
||||
}
|
||||
{
|
||||
BYTE const lastByte = ((const BYTE *)srcBuffer)[srcSize - 1];
|
||||
bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0;
|
||||
if (lastByte == 0)
|
||||
return ERROR(GENERIC); /* endMark not present */
|
||||
}
|
||||
bitD->bitsConsumed += (U32)(sizeof(bitD->bitContainer) - srcSize) * 8;
|
||||
}
|
||||
|
||||
return srcSize;
|
||||
}
|
||||
|
||||
ZSTD_STATIC size_t BIT_getUpperBits(size_t bitContainer, U32 const start) { return bitContainer >> start; }
|
||||
|
||||
ZSTD_STATIC size_t BIT_getMiddleBits(size_t bitContainer, U32 const start, U32 const nbBits) { return (bitContainer >> start) & BIT_mask[nbBits]; }
|
||||
|
||||
ZSTD_STATIC size_t BIT_getLowerBits(size_t bitContainer, U32 const nbBits) { return bitContainer & BIT_mask[nbBits]; }
|
||||
|
||||
/*! BIT_lookBits() :
|
||||
* Provides next n bits from local register.
|
||||
* local register is not modified.
|
||||
* On 32-bits, maxNbBits==24.
|
||||
* On 64-bits, maxNbBits==56.
|
||||
* @return : value extracted
|
||||
*/
|
||||
ZSTD_STATIC size_t BIT_lookBits(const BIT_DStream_t *bitD, U32 nbBits)
|
||||
{
|
||||
U32 const bitMask = sizeof(bitD->bitContainer) * 8 - 1;
|
||||
return ((bitD->bitContainer << (bitD->bitsConsumed & bitMask)) >> 1) >> ((bitMask - nbBits) & bitMask);
|
||||
}
|
||||
|
||||
/*! BIT_lookBitsFast() :
|
||||
* unsafe version; only works only if nbBits >= 1 */
|
||||
ZSTD_STATIC size_t BIT_lookBitsFast(const BIT_DStream_t *bitD, U32 nbBits)
|
||||
{
|
||||
U32 const bitMask = sizeof(bitD->bitContainer) * 8 - 1;
|
||||
return (bitD->bitContainer << (bitD->bitsConsumed & bitMask)) >> (((bitMask + 1) - nbBits) & bitMask);
|
||||
}
|
||||
|
||||
ZSTD_STATIC void BIT_skipBits(BIT_DStream_t *bitD, U32 nbBits) { bitD->bitsConsumed += nbBits; }
|
||||
|
||||
/*! BIT_readBits() :
|
||||
* Read (consume) next n bits from local register and update.
|
||||
* Pay attention to not read more than nbBits contained into local register.
|
||||
* @return : extracted value.
|
||||
*/
|
||||
ZSTD_STATIC size_t BIT_readBits(BIT_DStream_t *bitD, U32 nbBits)
|
||||
{
|
||||
size_t const value = BIT_lookBits(bitD, nbBits);
|
||||
BIT_skipBits(bitD, nbBits);
|
||||
return value;
|
||||
}
|
||||
|
||||
/*! BIT_readBitsFast() :
|
||||
* unsafe version; only works only if nbBits >= 1 */
|
||||
ZSTD_STATIC size_t BIT_readBitsFast(BIT_DStream_t *bitD, U32 nbBits)
|
||||
{
|
||||
size_t const value = BIT_lookBitsFast(bitD, nbBits);
|
||||
BIT_skipBits(bitD, nbBits);
|
||||
return value;
|
||||
}
|
||||
|
||||
/*! BIT_reloadDStream() :
|
||||
* Refill `bitD` from buffer previously set in BIT_initDStream() .
|
||||
* This function is safe, it guarantees it will not read beyond src buffer.
|
||||
* @return : status of `BIT_DStream_t` internal register.
|
||||
if status == BIT_DStream_unfinished, internal register is filled with >= (sizeof(bitD->bitContainer)*8 - 7) bits */
|
||||
ZSTD_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t *bitD)
|
||||
{
|
||||
if (bitD->bitsConsumed > (sizeof(bitD->bitContainer) * 8)) /* should not happen => corruption detected */
|
||||
return BIT_DStream_overflow;
|
||||
|
||||
if (bitD->ptr >= bitD->start + sizeof(bitD->bitContainer)) {
|
||||
bitD->ptr -= bitD->bitsConsumed >> 3;
|
||||
bitD->bitsConsumed &= 7;
|
||||
bitD->bitContainer = ZSTD_readLEST(bitD->ptr);
|
||||
return BIT_DStream_unfinished;
|
||||
}
|
||||
if (bitD->ptr == bitD->start) {
|
||||
if (bitD->bitsConsumed < sizeof(bitD->bitContainer) * 8)
|
||||
return BIT_DStream_endOfBuffer;
|
||||
return BIT_DStream_completed;
|
||||
}
|
||||
{
|
||||
U32 nbBytes = bitD->bitsConsumed >> 3;
|
||||
BIT_DStream_status result = BIT_DStream_unfinished;
|
||||
if (bitD->ptr - nbBytes < bitD->start) {
|
||||
nbBytes = (U32)(bitD->ptr - bitD->start); /* ptr > start */
|
||||
result = BIT_DStream_endOfBuffer;
|
||||
}
|
||||
bitD->ptr -= nbBytes;
|
||||
bitD->bitsConsumed -= nbBytes * 8;
|
||||
bitD->bitContainer = ZSTD_readLEST(bitD->ptr); /* reminder : srcSize > sizeof(bitD) */
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/*! BIT_endOfDStream() :
|
||||
* @return Tells if DStream has exactly reached its end (all bits consumed).
|
||||
*/
|
||||
ZSTD_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t *DStream)
|
||||
{
|
||||
return ((DStream->ptr == DStream->start) && (DStream->bitsConsumed == sizeof(DStream->bitContainer) * 8));
|
||||
}
|
||||
|
||||
#endif /* BITSTREAM_H_MODULE */
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Common functions of New Generation Entropy library
|
||||
* Copyright (C) 2016, Yann Collet.
|
||||
*
|
||||
* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License version 2 as published by the
|
||||
* Free Software Foundation. This program is dual-licensed; you may select
|
||||
* either version 2 of the GNU General Public License ("GPL") or BSD license
|
||||
* ("BSD").
|
||||
*
|
||||
* You can contact the author at :
|
||||
* - Source repository : https://github.com/Cyan4973/FiniteStateEntropy
|
||||
*/
|
||||
|
||||
/* *************************************
|
||||
* Dependencies
|
||||
***************************************/
|
||||
#include "error_private.h" /* ERR_*, ERROR */
|
||||
#include "fse.h"
|
||||
#include "huf.h"
|
||||
#include "mem.h"
|
||||
|
||||
/*=== Version ===*/
|
||||
unsigned FSE_versionNumber(void) { return FSE_VERSION_NUMBER; }
|
||||
|
||||
/*=== Error Management ===*/
|
||||
unsigned FSE_isError(size_t code) { return ERR_isError(code); }
|
||||
|
||||
unsigned HUF_isError(size_t code) { return ERR_isError(code); }
|
||||
|
||||
/*-**************************************************************
|
||||
* FSE NCount encoding-decoding
|
||||
****************************************************************/
|
||||
size_t FSE_readNCount(short *normalizedCounter, unsigned *maxSVPtr, unsigned *tableLogPtr, const void *headerBuffer, size_t hbSize)
|
||||
{
|
||||
const BYTE *const istart = (const BYTE *)headerBuffer;
|
||||
const BYTE *const iend = istart + hbSize;
|
||||
const BYTE *ip = istart;
|
||||
int nbBits;
|
||||
int remaining;
|
||||
int threshold;
|
||||
U32 bitStream;
|
||||
int bitCount;
|
||||
unsigned charnum = 0;
|
||||
int previous0 = 0;
|
||||
|
||||
if (hbSize < 4)
|
||||
return ERROR(srcSize_wrong);
|
||||
bitStream = ZSTD_readLE32(ip);
|
||||
nbBits = (bitStream & 0xF) + FSE_MIN_TABLELOG; /* extract tableLog */
|
||||
if (nbBits > FSE_TABLELOG_ABSOLUTE_MAX)
|
||||
return ERROR(tableLog_tooLarge);
|
||||
bitStream >>= 4;
|
||||
bitCount = 4;
|
||||
*tableLogPtr = nbBits;
|
||||
remaining = (1 << nbBits) + 1;
|
||||
threshold = 1 << nbBits;
|
||||
nbBits++;
|
||||
|
||||
while ((remaining > 1) & (charnum <= *maxSVPtr)) {
|
||||
if (previous0) {
|
||||
unsigned n0 = charnum;
|
||||
while ((bitStream & 0xFFFF) == 0xFFFF) {
|
||||
n0 += 24;
|
||||
if (ip < iend - 5) {
|
||||
ip += 2;
|
||||
bitStream = ZSTD_readLE32(ip) >> bitCount;
|
||||
} else {
|
||||
bitStream >>= 16;
|
||||
bitCount += 16;
|
||||
}
|
||||
}
|
||||
while ((bitStream & 3) == 3) {
|
||||
n0 += 3;
|
||||
bitStream >>= 2;
|
||||
bitCount += 2;
|
||||
}
|
||||
n0 += bitStream & 3;
|
||||
bitCount += 2;
|
||||
if (n0 > *maxSVPtr)
|
||||
return ERROR(maxSymbolValue_tooSmall);
|
||||
while (charnum < n0)
|
||||
normalizedCounter[charnum++] = 0;
|
||||
if ((ip <= iend - 7) || (ip + (bitCount >> 3) <= iend - 4)) {
|
||||
ip += bitCount >> 3;
|
||||
bitCount &= 7;
|
||||
bitStream = ZSTD_readLE32(ip) >> bitCount;
|
||||
} else {
|
||||
bitStream >>= 2;
|
||||
}
|
||||
}
|
||||
{
|
||||
int const max = (2 * threshold - 1) - remaining;
|
||||
int count;
|
||||
|
||||
if ((bitStream & (threshold - 1)) < (U32)max) {
|
||||
count = bitStream & (threshold - 1);
|
||||
bitCount += nbBits - 1;
|
||||
} else {
|
||||
count = bitStream & (2 * threshold - 1);
|
||||
if (count >= threshold)
|
||||
count -= max;
|
||||
bitCount += nbBits;
|
||||
}
|
||||
|
||||
count--; /* extra accuracy */
|
||||
remaining -= count < 0 ? -count : count; /* -1 means +1 */
|
||||
normalizedCounter[charnum++] = (short)count;
|
||||
previous0 = !count;
|
||||
while (remaining < threshold) {
|
||||
nbBits--;
|
||||
threshold >>= 1;
|
||||
}
|
||||
|
||||
if ((ip <= iend - 7) || (ip + (bitCount >> 3) <= iend - 4)) {
|
||||
ip += bitCount >> 3;
|
||||
bitCount &= 7;
|
||||
} else {
|
||||
bitCount -= (int)(8 * (iend - 4 - ip));
|
||||
ip = iend - 4;
|
||||
}
|
||||
bitStream = ZSTD_readLE32(ip) >> (bitCount & 31);
|
||||
}
|
||||
} /* while ((remaining>1) & (charnum<=*maxSVPtr)) */
|
||||
if (remaining != 1)
|
||||
return ERROR(corruption_detected);
|
||||
if (bitCount > 32)
|
||||
return ERROR(corruption_detected);
|
||||
*maxSVPtr = charnum - 1;
|
||||
|
||||
ip += (bitCount + 7) >> 3;
|
||||
return ip - istart;
|
||||
}
|
||||
|
||||
/*! HUF_readStats() :
|
||||
Read compact Huffman tree, saved by HUF_writeCTable().
|
||||
`huffWeight` is destination buffer.
|
||||
`rankStats` is assumed to be a table of at least HUF_TABLELOG_MAX U32.
|
||||
@return : size read from `src` , or an error Code .
|
||||
Note : Needed by HUF_readCTable() and HUF_readDTableX?() .
|
||||
*/
|
||||
size_t HUF_readStats_wksp(BYTE *huffWeight, size_t hwSize, U32 *rankStats, U32 *nbSymbolsPtr, U32 *tableLogPtr, const void *src, size_t srcSize, void *workspace, size_t workspaceSize)
|
||||
{
|
||||
U32 weightTotal;
|
||||
const BYTE *ip = (const BYTE *)src;
|
||||
size_t iSize;
|
||||
size_t oSize;
|
||||
|
||||
if (!srcSize)
|
||||
return ERROR(srcSize_wrong);
|
||||
iSize = ip[0];
|
||||
/* memset(huffWeight, 0, hwSize); */ /* is not necessary, even though some analyzer complain ... */
|
||||
|
||||
if (iSize >= 128) { /* special header */
|
||||
oSize = iSize - 127;
|
||||
iSize = ((oSize + 1) / 2);
|
||||
if (iSize + 1 > srcSize)
|
||||
return ERROR(srcSize_wrong);
|
||||
if (oSize >= hwSize)
|
||||
return ERROR(corruption_detected);
|
||||
ip += 1;
|
||||
{
|
||||
U32 n;
|
||||
for (n = 0; n < oSize; n += 2) {
|
||||
huffWeight[n] = ip[n / 2] >> 4;
|
||||
huffWeight[n + 1] = ip[n / 2] & 15;
|
||||
}
|
||||
}
|
||||
} else { /* header compressed with FSE (normal case) */
|
||||
if (iSize + 1 > srcSize)
|
||||
return ERROR(srcSize_wrong);
|
||||
oSize = FSE_decompress_wksp(huffWeight, hwSize - 1, ip + 1, iSize, 6, workspace, workspaceSize); /* max (hwSize-1) values decoded, as last one is implied */
|
||||
if (FSE_isError(oSize))
|
||||
return oSize;
|
||||
}
|
||||
|
||||
/* collect weight stats */
|
||||
memset(rankStats, 0, (HUF_TABLELOG_MAX + 1) * sizeof(U32));
|
||||
weightTotal = 0;
|
||||
{
|
||||
U32 n;
|
||||
for (n = 0; n < oSize; n++) {
|
||||
if (huffWeight[n] >= HUF_TABLELOG_MAX)
|
||||
return ERROR(corruption_detected);
|
||||
rankStats[huffWeight[n]]++;
|
||||
weightTotal += (1 << huffWeight[n]) >> 1;
|
||||
}
|
||||
}
|
||||
if (weightTotal == 0)
|
||||
return ERROR(corruption_detected);
|
||||
|
||||
/* get last non-null symbol weight (implied, total must be 2^n) */
|
||||
{
|
||||
U32 const tableLog = BIT_highbit32(weightTotal) + 1;
|
||||
if (tableLog > HUF_TABLELOG_MAX)
|
||||
return ERROR(corruption_detected);
|
||||
*tableLogPtr = tableLog;
|
||||
/* determine last weight */
|
||||
{
|
||||
U32 const total = 1 << tableLog;
|
||||
U32 const rest = total - weightTotal;
|
||||
U32 const verif = 1 << BIT_highbit32(rest);
|
||||
U32 const lastWeight = BIT_highbit32(rest) + 1;
|
||||
if (verif != rest)
|
||||
return ERROR(corruption_detected); /* last value must be a clean power of 2 */
|
||||
huffWeight[oSize] = (BYTE)lastWeight;
|
||||
rankStats[lastWeight]++;
|
||||
}
|
||||
}
|
||||
|
||||
/* check tree construction validity */
|
||||
if ((rankStats[1] < 2) || (rankStats[1] & 1))
|
||||
return ERROR(corruption_detected); /* by construction : at least 2 elts of rank 1, must be even */
|
||||
|
||||
/* results */
|
||||
*nbSymbolsPtr = (U32)(oSize + 1);
|
||||
return iSize + 1;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of https://github.com/facebook/zstd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License version 2 as published by the
|
||||
* Free Software Foundation. This program is dual-licensed; you may select
|
||||
* either version 2 of the GNU General Public License ("GPL") or BSD license
|
||||
* ("BSD").
|
||||
*/
|
||||
|
||||
/* Note : this module is expected to remain private, do not expose it */
|
||||
|
||||
#ifndef ERROR_H_MODULE
|
||||
#define ERROR_H_MODULE
|
||||
|
||||
/* ****************************************
|
||||
* Dependencies
|
||||
******************************************/
|
||||
#include <linux/types.h> /* size_t */
|
||||
#include <linux/zstd.h> /* enum list */
|
||||
|
||||
/* ****************************************
|
||||
* Compiler-specific
|
||||
******************************************/
|
||||
#define ERR_STATIC static __attribute__((unused))
|
||||
|
||||
/*-****************************************
|
||||
* Customization (error_public.h)
|
||||
******************************************/
|
||||
typedef ZSTD_ErrorCode ERR_enum;
|
||||
#define PREFIX(name) ZSTD_error_##name
|
||||
|
||||
/*-****************************************
|
||||
* Error codes handling
|
||||
******************************************/
|
||||
#define ERROR(name) ((size_t)-PREFIX(name))
|
||||
|
||||
ERR_STATIC unsigned ERR_isError(size_t code) { return (code > ERROR(maxCode)); }
|
||||
|
||||
ERR_STATIC ERR_enum ERR_getErrorCode(size_t code)
|
||||
{
|
||||
if (!ERR_isError(code))
|
||||
return (ERR_enum)0;
|
||||
return (ERR_enum)(0 - code);
|
||||
}
|
||||
|
||||
#endif /* ERROR_H_MODULE */
|
||||
@@ -0,0 +1,575 @@
|
||||
/*
|
||||
* FSE : Finite State Entropy codec
|
||||
* Public Prototypes declaration
|
||||
* Copyright (C) 2013-2016, Yann Collet.
|
||||
*
|
||||
* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License version 2 as published by the
|
||||
* Free Software Foundation. This program is dual-licensed; you may select
|
||||
* either version 2 of the GNU General Public License ("GPL") or BSD license
|
||||
* ("BSD").
|
||||
*
|
||||
* You can contact the author at :
|
||||
* - Source repository : https://github.com/Cyan4973/FiniteStateEntropy
|
||||
*/
|
||||
#ifndef FSE_H
|
||||
#define FSE_H
|
||||
|
||||
/*-*****************************************
|
||||
* Dependencies
|
||||
******************************************/
|
||||
#include <linux/types.h> /* size_t, ptrdiff_t */
|
||||
|
||||
/*-*****************************************
|
||||
* FSE_PUBLIC_API : control library symbols visibility
|
||||
******************************************/
|
||||
#define FSE_PUBLIC_API
|
||||
|
||||
/*------ Version ------*/
|
||||
#define FSE_VERSION_MAJOR 0
|
||||
#define FSE_VERSION_MINOR 9
|
||||
#define FSE_VERSION_RELEASE 0
|
||||
|
||||
#define FSE_LIB_VERSION FSE_VERSION_MAJOR.FSE_VERSION_MINOR.FSE_VERSION_RELEASE
|
||||
#define FSE_QUOTE(str) #str
|
||||
#define FSE_EXPAND_AND_QUOTE(str) FSE_QUOTE(str)
|
||||
#define FSE_VERSION_STRING FSE_EXPAND_AND_QUOTE(FSE_LIB_VERSION)
|
||||
|
||||
#define FSE_VERSION_NUMBER (FSE_VERSION_MAJOR * 100 * 100 + FSE_VERSION_MINOR * 100 + FSE_VERSION_RELEASE)
|
||||
FSE_PUBLIC_API unsigned FSE_versionNumber(void); /**< library version number; to be used when checking dll version */
|
||||
|
||||
/*-*****************************************
|
||||
* Tool functions
|
||||
******************************************/
|
||||
FSE_PUBLIC_API size_t FSE_compressBound(size_t size); /* maximum compressed size */
|
||||
|
||||
/* Error Management */
|
||||
FSE_PUBLIC_API unsigned FSE_isError(size_t code); /* tells if a return value is an error code */
|
||||
|
||||
/*-*****************************************
|
||||
* FSE detailed API
|
||||
******************************************/
|
||||
/*!
|
||||
FSE_compress() does the following:
|
||||
1. count symbol occurrence from source[] into table count[]
|
||||
2. normalize counters so that sum(count[]) == Power_of_2 (2^tableLog)
|
||||
3. save normalized counters to memory buffer using writeNCount()
|
||||
4. build encoding table 'CTable' from normalized counters
|
||||
5. encode the data stream using encoding table 'CTable'
|
||||
|
||||
FSE_decompress() does the following:
|
||||
1. read normalized counters with readNCount()
|
||||
2. build decoding table 'DTable' from normalized counters
|
||||
3. decode the data stream using decoding table 'DTable'
|
||||
|
||||
The following API allows targeting specific sub-functions for advanced tasks.
|
||||
For example, it's possible to compress several blocks using the same 'CTable',
|
||||
or to save and provide normalized distribution using external method.
|
||||
*/
|
||||
|
||||
/* *** COMPRESSION *** */
|
||||
/*! FSE_optimalTableLog():
|
||||
dynamically downsize 'tableLog' when conditions are met.
|
||||
It saves CPU time, by using smaller tables, while preserving or even improving compression ratio.
|
||||
@return : recommended tableLog (necessarily <= 'maxTableLog') */
|
||||
FSE_PUBLIC_API unsigned FSE_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue);
|
||||
|
||||
/*! FSE_normalizeCount():
|
||||
normalize counts so that sum(count[]) == Power_of_2 (2^tableLog)
|
||||
'normalizedCounter' is a table of short, of minimum size (maxSymbolValue+1).
|
||||
@return : tableLog,
|
||||
or an errorCode, which can be tested using FSE_isError() */
|
||||
FSE_PUBLIC_API size_t FSE_normalizeCount(short *normalizedCounter, unsigned tableLog, const unsigned *count, size_t srcSize, unsigned maxSymbolValue);
|
||||
|
||||
/*! FSE_NCountWriteBound():
|
||||
Provides the maximum possible size of an FSE normalized table, given 'maxSymbolValue' and 'tableLog'.
|
||||
Typically useful for allocation purpose. */
|
||||
FSE_PUBLIC_API size_t FSE_NCountWriteBound(unsigned maxSymbolValue, unsigned tableLog);
|
||||
|
||||
/*! FSE_writeNCount():
|
||||
Compactly save 'normalizedCounter' into 'buffer'.
|
||||
@return : size of the compressed table,
|
||||
or an errorCode, which can be tested using FSE_isError(). */
|
||||
FSE_PUBLIC_API size_t FSE_writeNCount(void *buffer, size_t bufferSize, const short *normalizedCounter, unsigned maxSymbolValue, unsigned tableLog);
|
||||
|
||||
/*! Constructor and Destructor of FSE_CTable.
|
||||
Note that FSE_CTable size depends on 'tableLog' and 'maxSymbolValue' */
|
||||
typedef unsigned FSE_CTable; /* don't allocate that. It's only meant to be more restrictive than void* */
|
||||
|
||||
/*! FSE_compress_usingCTable():
|
||||
Compress `src` using `ct` into `dst` which must be already allocated.
|
||||
@return : size of compressed data (<= `dstCapacity`),
|
||||
or 0 if compressed data could not fit into `dst`,
|
||||
or an errorCode, which can be tested using FSE_isError() */
|
||||
FSE_PUBLIC_API size_t FSE_compress_usingCTable(void *dst, size_t dstCapacity, const void *src, size_t srcSize, const FSE_CTable *ct);
|
||||
|
||||
/*!
|
||||
Tutorial :
|
||||
----------
|
||||
The first step is to count all symbols. FSE_count() does this job very fast.
|
||||
Result will be saved into 'count', a table of unsigned int, which must be already allocated, and have 'maxSymbolValuePtr[0]+1' cells.
|
||||
'src' is a table of bytes of size 'srcSize'. All values within 'src' MUST be <= maxSymbolValuePtr[0]
|
||||
maxSymbolValuePtr[0] will be updated, with its real value (necessarily <= original value)
|
||||
FSE_count() will return the number of occurrence of the most frequent symbol.
|
||||
This can be used to know if there is a single symbol within 'src', and to quickly evaluate its compressibility.
|
||||
If there is an error, the function will return an ErrorCode (which can be tested using FSE_isError()).
|
||||
|
||||
The next step is to normalize the frequencies.
|
||||
FSE_normalizeCount() will ensure that sum of frequencies is == 2 ^'tableLog'.
|
||||
It also guarantees a minimum of 1 to any Symbol with frequency >= 1.
|
||||
You can use 'tableLog'==0 to mean "use default tableLog value".
|
||||
If you are unsure of which tableLog value to use, you can ask FSE_optimalTableLog(),
|
||||
which will provide the optimal valid tableLog given sourceSize, maxSymbolValue, and a user-defined maximum (0 means "default").
|
||||
|
||||
The result of FSE_normalizeCount() will be saved into a table,
|
||||
called 'normalizedCounter', which is a table of signed short.
|
||||
'normalizedCounter' must be already allocated, and have at least 'maxSymbolValue+1' cells.
|
||||
The return value is tableLog if everything proceeded as expected.
|
||||
It is 0 if there is a single symbol within distribution.
|
||||
If there is an error (ex: invalid tableLog value), the function will return an ErrorCode (which can be tested using FSE_isError()).
|
||||
|
||||
'normalizedCounter' can be saved in a compact manner to a memory area using FSE_writeNCount().
|
||||
'buffer' must be already allocated.
|
||||
For guaranteed success, buffer size must be at least FSE_headerBound().
|
||||
The result of the function is the number of bytes written into 'buffer'.
|
||||
If there is an error, the function will return an ErrorCode (which can be tested using FSE_isError(); ex : buffer size too small).
|
||||
|
||||
'normalizedCounter' can then be used to create the compression table 'CTable'.
|
||||
The space required by 'CTable' must be already allocated, using FSE_createCTable().
|
||||
You can then use FSE_buildCTable() to fill 'CTable'.
|
||||
If there is an error, both functions will return an ErrorCode (which can be tested using FSE_isError()).
|
||||
|
||||
'CTable' can then be used to compress 'src', with FSE_compress_usingCTable().
|
||||
Similar to FSE_count(), the convention is that 'src' is assumed to be a table of char of size 'srcSize'
|
||||
The function returns the size of compressed data (without header), necessarily <= `dstCapacity`.
|
||||
If it returns '0', compressed data could not fit into 'dst'.
|
||||
If there is an error, the function will return an ErrorCode (which can be tested using FSE_isError()).
|
||||
*/
|
||||
|
||||
/* *** DECOMPRESSION *** */
|
||||
|
||||
/*! FSE_readNCount():
|
||||
Read compactly saved 'normalizedCounter' from 'rBuffer'.
|
||||
@return : size read from 'rBuffer',
|
||||
or an errorCode, which can be tested using FSE_isError().
|
||||
maxSymbolValuePtr[0] and tableLogPtr[0] will also be updated with their respective values */
|
||||
FSE_PUBLIC_API size_t FSE_readNCount(short *normalizedCounter, unsigned *maxSymbolValuePtr, unsigned *tableLogPtr, const void *rBuffer, size_t rBuffSize);
|
||||
|
||||
/*! Constructor and Destructor of FSE_DTable.
|
||||
Note that its size depends on 'tableLog' */
|
||||
typedef unsigned FSE_DTable; /* don't allocate that. It's just a way to be more restrictive than void* */
|
||||
|
||||
/*! FSE_buildDTable():
|
||||
Builds 'dt', which must be already allocated, using FSE_createDTable().
|
||||
return : 0, or an errorCode, which can be tested using FSE_isError() */
|
||||
FSE_PUBLIC_API size_t FSE_buildDTable_wksp(FSE_DTable *dt, const short *normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, void *workspace, size_t workspaceSize);
|
||||
|
||||
/*! FSE_decompress_usingDTable():
|
||||
Decompress compressed source `cSrc` of size `cSrcSize` using `dt`
|
||||
into `dst` which must be already allocated.
|
||||
@return : size of regenerated data (necessarily <= `dstCapacity`),
|
||||
or an errorCode, which can be tested using FSE_isError() */
|
||||
FSE_PUBLIC_API size_t FSE_decompress_usingDTable(void *dst, size_t dstCapacity, const void *cSrc, size_t cSrcSize, const FSE_DTable *dt);
|
||||
|
||||
/*!
|
||||
Tutorial :
|
||||
----------
|
||||
(Note : these functions only decompress FSE-compressed blocks.
|
||||
If block is uncompressed, use memcpy() instead
|
||||
If block is a single repeated byte, use memset() instead )
|
||||
|
||||
The first step is to obtain the normalized frequencies of symbols.
|
||||
This can be performed by FSE_readNCount() if it was saved using FSE_writeNCount().
|
||||
'normalizedCounter' must be already allocated, and have at least 'maxSymbolValuePtr[0]+1' cells of signed short.
|
||||
In practice, that means it's necessary to know 'maxSymbolValue' beforehand,
|
||||
or size the table to handle worst case situations (typically 256).
|
||||
FSE_readNCount() will provide 'tableLog' and 'maxSymbolValue'.
|
||||
The result of FSE_readNCount() is the number of bytes read from 'rBuffer'.
|
||||
Note that 'rBufferSize' must be at least 4 bytes, even if useful information is less than that.
|
||||
If there is an error, the function will return an error code, which can be tested using FSE_isError().
|
||||
|
||||
The next step is to build the decompression tables 'FSE_DTable' from 'normalizedCounter'.
|
||||
This is performed by the function FSE_buildDTable().
|
||||
The space required by 'FSE_DTable' must be already allocated using FSE_createDTable().
|
||||
If there is an error, the function will return an error code, which can be tested using FSE_isError().
|
||||
|
||||
`FSE_DTable` can then be used to decompress `cSrc`, with FSE_decompress_usingDTable().
|
||||
`cSrcSize` must be strictly correct, otherwise decompression will fail.
|
||||
FSE_decompress_usingDTable() result will tell how many bytes were regenerated (<=`dstCapacity`).
|
||||
If there is an error, the function will return an error code, which can be tested using FSE_isError(). (ex: dst buffer too small)
|
||||
*/
|
||||
|
||||
/* *** Dependency *** */
|
||||
#include "bitstream.h"
|
||||
|
||||
/* *****************************************
|
||||
* Static allocation
|
||||
*******************************************/
|
||||
/* FSE buffer bounds */
|
||||
#define FSE_NCOUNTBOUND 512
|
||||
#define FSE_BLOCKBOUND(size) (size + (size >> 7) + 4 /* constant for initial fse states */ )
|
||||
#define FSE_COMPRESSBOUND(size) (FSE_NCOUNTBOUND + FSE_BLOCKBOUND(size)) /* Macro version, useful for static allocation */
|
||||
|
||||
/* It is possible to statically allocate FSE CTable/DTable as a table of FSE_CTable/FSE_DTable using below macros */
|
||||
#define FSE_CTABLE_SIZE_U32(maxTableLog, maxSymbolValue) (1 + (1 << (maxTableLog - 1)) + ((maxSymbolValue + 1) * 2))
|
||||
#define FSE_DTABLE_SIZE_U32(maxTableLog) (1 + (1 << maxTableLog))
|
||||
|
||||
/* *****************************************
|
||||
* FSE advanced API
|
||||
*******************************************/
|
||||
/* FSE_count_wksp() :
|
||||
* Same as FSE_count(), but using an externally provided scratch buffer.
|
||||
* `workSpace` size must be table of >= `1024` unsigned
|
||||
*/
|
||||
size_t FSE_count_wksp(unsigned *count, unsigned *maxSymbolValuePtr, const void *source, size_t sourceSize, unsigned *workSpace);
|
||||
|
||||
/* FSE_countFast_wksp() :
|
||||
* Same as FSE_countFast(), but using an externally provided scratch buffer.
|
||||
* `workSpace` must be a table of minimum `1024` unsigned
|
||||
*/
|
||||
size_t FSE_countFast_wksp(unsigned *count, unsigned *maxSymbolValuePtr, const void *src, size_t srcSize, unsigned *workSpace);
|
||||
|
||||
/*! FSE_count_simple
|
||||
* Same as FSE_countFast(), but does not use any additional memory (not even on stack).
|
||||
* This function is unsafe, and will segfault if any value within `src` is `> *maxSymbolValuePtr` (presuming it's also the size of `count`).
|
||||
*/
|
||||
size_t FSE_count_simple(unsigned *count, unsigned *maxSymbolValuePtr, const void *src, size_t srcSize);
|
||||
|
||||
unsigned FSE_optimalTableLog_internal(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue, unsigned minus);
|
||||
/**< same as FSE_optimalTableLog(), which used `minus==2` */
|
||||
|
||||
size_t FSE_buildCTable_raw(FSE_CTable *ct, unsigned nbBits);
|
||||
/**< build a fake FSE_CTable, designed for a flat distribution, where each symbol uses nbBits */
|
||||
|
||||
size_t FSE_buildCTable_rle(FSE_CTable *ct, unsigned char symbolValue);
|
||||
/**< build a fake FSE_CTable, designed to compress always the same symbolValue */
|
||||
|
||||
/* FSE_buildCTable_wksp() :
|
||||
* Same as FSE_buildCTable(), but using an externally allocated scratch buffer (`workSpace`).
|
||||
* `wkspSize` must be >= `(1<<tableLog)`.
|
||||
*/
|
||||
size_t FSE_buildCTable_wksp(FSE_CTable *ct, const short *normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, void *workSpace, size_t wkspSize);
|
||||
|
||||
size_t FSE_buildDTable_raw(FSE_DTable *dt, unsigned nbBits);
|
||||
/**< build a fake FSE_DTable, designed to read a flat distribution where each symbol uses nbBits */
|
||||
|
||||
size_t FSE_buildDTable_rle(FSE_DTable *dt, unsigned char symbolValue);
|
||||
/**< build a fake FSE_DTable, designed to always generate the same symbolValue */
|
||||
|
||||
size_t FSE_decompress_wksp(void *dst, size_t dstCapacity, const void *cSrc, size_t cSrcSize, unsigned maxLog, void *workspace, size_t workspaceSize);
|
||||
/**< same as FSE_decompress(), using an externally allocated `workSpace` produced with `FSE_DTABLE_SIZE_U32(maxLog)` */
|
||||
|
||||
/* *****************************************
|
||||
* FSE symbol compression API
|
||||
*******************************************/
|
||||
/*!
|
||||
This API consists of small unitary functions, which highly benefit from being inlined.
|
||||
Hence their body are included in next section.
|
||||
*/
|
||||
typedef struct {
|
||||
ptrdiff_t value;
|
||||
const void *stateTable;
|
||||
const void *symbolTT;
|
||||
unsigned stateLog;
|
||||
} FSE_CState_t;
|
||||
|
||||
static void FSE_initCState(FSE_CState_t *CStatePtr, const FSE_CTable *ct);
|
||||
|
||||
static void FSE_encodeSymbol(BIT_CStream_t *bitC, FSE_CState_t *CStatePtr, unsigned symbol);
|
||||
|
||||
static void FSE_flushCState(BIT_CStream_t *bitC, const FSE_CState_t *CStatePtr);
|
||||
|
||||
/**<
|
||||
These functions are inner components of FSE_compress_usingCTable().
|
||||
They allow the creation of custom streams, mixing multiple tables and bit sources.
|
||||
|
||||
A key property to keep in mind is that encoding and decoding are done **in reverse direction**.
|
||||
So the first symbol you will encode is the last you will decode, like a LIFO stack.
|
||||
|
||||
You will need a few variables to track your CStream. They are :
|
||||
|
||||
FSE_CTable ct; // Provided by FSE_buildCTable()
|
||||
BIT_CStream_t bitStream; // bitStream tracking structure
|
||||
FSE_CState_t state; // State tracking structure (can have several)
|
||||
|
||||
|
||||
The first thing to do is to init bitStream and state.
|
||||
size_t errorCode = BIT_initCStream(&bitStream, dstBuffer, maxDstSize);
|
||||
FSE_initCState(&state, ct);
|
||||
|
||||
Note that BIT_initCStream() can produce an error code, so its result should be tested, using FSE_isError();
|
||||
You can then encode your input data, byte after byte.
|
||||
FSE_encodeSymbol() outputs a maximum of 'tableLog' bits at a time.
|
||||
Remember decoding will be done in reverse direction.
|
||||
FSE_encodeByte(&bitStream, &state, symbol);
|
||||
|
||||
At any time, you can also add any bit sequence.
|
||||
Note : maximum allowed nbBits is 25, for compatibility with 32-bits decoders
|
||||
BIT_addBits(&bitStream, bitField, nbBits);
|
||||
|
||||
The above methods don't commit data to memory, they just store it into local register, for speed.
|
||||
Local register size is 64-bits on 64-bits systems, 32-bits on 32-bits systems (size_t).
|
||||
Writing data to memory is a manual operation, performed by the flushBits function.
|
||||
BIT_flushBits(&bitStream);
|
||||
|
||||
Your last FSE encoding operation shall be to flush your last state value(s).
|
||||
FSE_flushState(&bitStream, &state);
|
||||
|
||||
Finally, you must close the bitStream.
|
||||
The function returns the size of CStream in bytes.
|
||||
If data couldn't fit into dstBuffer, it will return a 0 ( == not compressible)
|
||||
If there is an error, it returns an errorCode (which can be tested using FSE_isError()).
|
||||
size_t size = BIT_closeCStream(&bitStream);
|
||||
*/
|
||||
|
||||
/* *****************************************
|
||||
* FSE symbol decompression API
|
||||
*******************************************/
|
||||
typedef struct {
|
||||
size_t state;
|
||||
const void *table; /* precise table may vary, depending on U16 */
|
||||
} FSE_DState_t;
|
||||
|
||||
static void FSE_initDState(FSE_DState_t *DStatePtr, BIT_DStream_t *bitD, const FSE_DTable *dt);
|
||||
|
||||
static unsigned char FSE_decodeSymbol(FSE_DState_t *DStatePtr, BIT_DStream_t *bitD);
|
||||
|
||||
static unsigned FSE_endOfDState(const FSE_DState_t *DStatePtr);
|
||||
|
||||
/**<
|
||||
Let's now decompose FSE_decompress_usingDTable() into its unitary components.
|
||||
You will decode FSE-encoded symbols from the bitStream,
|
||||
and also any other bitFields you put in, **in reverse order**.
|
||||
|
||||
You will need a few variables to track your bitStream. They are :
|
||||
|
||||
BIT_DStream_t DStream; // Stream context
|
||||
FSE_DState_t DState; // State context. Multiple ones are possible
|
||||
FSE_DTable* DTablePtr; // Decoding table, provided by FSE_buildDTable()
|
||||
|
||||
The first thing to do is to init the bitStream.
|
||||
errorCode = BIT_initDStream(&DStream, srcBuffer, srcSize);
|
||||
|
||||
You should then retrieve your initial state(s)
|
||||
(in reverse flushing order if you have several ones) :
|
||||
errorCode = FSE_initDState(&DState, &DStream, DTablePtr);
|
||||
|
||||
You can then decode your data, symbol after symbol.
|
||||
For information the maximum number of bits read by FSE_decodeSymbol() is 'tableLog'.
|
||||
Keep in mind that symbols are decoded in reverse order, like a LIFO stack (last in, first out).
|
||||
unsigned char symbol = FSE_decodeSymbol(&DState, &DStream);
|
||||
|
||||
You can retrieve any bitfield you eventually stored into the bitStream (in reverse order)
|
||||
Note : maximum allowed nbBits is 25, for 32-bits compatibility
|
||||
size_t bitField = BIT_readBits(&DStream, nbBits);
|
||||
|
||||
All above operations only read from local register (which size depends on size_t).
|
||||
Refueling the register from memory is manually performed by the reload method.
|
||||
endSignal = FSE_reloadDStream(&DStream);
|
||||
|
||||
BIT_reloadDStream() result tells if there is still some more data to read from DStream.
|
||||
BIT_DStream_unfinished : there is still some data left into the DStream.
|
||||
BIT_DStream_endOfBuffer : Dstream reached end of buffer. Its container may no longer be completely filled.
|
||||
BIT_DStream_completed : Dstream reached its exact end, corresponding in general to decompression completed.
|
||||
BIT_DStream_tooFar : Dstream went too far. Decompression result is corrupted.
|
||||
|
||||
When reaching end of buffer (BIT_DStream_endOfBuffer), progress slowly, notably if you decode multiple symbols per loop,
|
||||
to properly detect the exact end of stream.
|
||||
After each decoded symbol, check if DStream is fully consumed using this simple test :
|
||||
BIT_reloadDStream(&DStream) >= BIT_DStream_completed
|
||||
|
||||
When it's done, verify decompression is fully completed, by checking both DStream and the relevant states.
|
||||
Checking if DStream has reached its end is performed by :
|
||||
BIT_endOfDStream(&DStream);
|
||||
Check also the states. There might be some symbols left there, if some high probability ones (>50%) are possible.
|
||||
FSE_endOfDState(&DState);
|
||||
*/
|
||||
|
||||
/* *****************************************
|
||||
* FSE unsafe API
|
||||
*******************************************/
|
||||
static unsigned char FSE_decodeSymbolFast(FSE_DState_t *DStatePtr, BIT_DStream_t *bitD);
|
||||
/* faster, but works only if nbBits is always >= 1 (otherwise, result will be corrupted) */
|
||||
|
||||
/* *****************************************
|
||||
* Implementation of inlined functions
|
||||
*******************************************/
|
||||
typedef struct {
|
||||
int deltaFindState;
|
||||
U32 deltaNbBits;
|
||||
} FSE_symbolCompressionTransform; /* total 8 bytes */
|
||||
|
||||
ZSTD_STATIC void FSE_initCState(FSE_CState_t *statePtr, const FSE_CTable *ct)
|
||||
{
|
||||
const void *ptr = ct;
|
||||
const U16 *u16ptr = (const U16 *)ptr;
|
||||
const U32 tableLog = ZSTD_read16(ptr);
|
||||
statePtr->value = (ptrdiff_t)1 << tableLog;
|
||||
statePtr->stateTable = u16ptr + 2;
|
||||
statePtr->symbolTT = ((const U32 *)ct + 1 + (tableLog ? (1 << (tableLog - 1)) : 1));
|
||||
statePtr->stateLog = tableLog;
|
||||
}
|
||||
|
||||
/*! FSE_initCState2() :
|
||||
* Same as FSE_initCState(), but the first symbol to include (which will be the last to be read)
|
||||
* uses the smallest state value possible, saving the cost of this symbol */
|
||||
ZSTD_STATIC void FSE_initCState2(FSE_CState_t *statePtr, const FSE_CTable *ct, U32 symbol)
|
||||
{
|
||||
FSE_initCState(statePtr, ct);
|
||||
{
|
||||
const FSE_symbolCompressionTransform symbolTT = ((const FSE_symbolCompressionTransform *)(statePtr->symbolTT))[symbol];
|
||||
const U16 *stateTable = (const U16 *)(statePtr->stateTable);
|
||||
U32 nbBitsOut = (U32)((symbolTT.deltaNbBits + (1 << 15)) >> 16);
|
||||
statePtr->value = (nbBitsOut << 16) - symbolTT.deltaNbBits;
|
||||
statePtr->value = stateTable[(statePtr->value >> nbBitsOut) + symbolTT.deltaFindState];
|
||||
}
|
||||
}
|
||||
|
||||
ZSTD_STATIC void FSE_encodeSymbol(BIT_CStream_t *bitC, FSE_CState_t *statePtr, U32 symbol)
|
||||
{
|
||||
const FSE_symbolCompressionTransform symbolTT = ((const FSE_symbolCompressionTransform *)(statePtr->symbolTT))[symbol];
|
||||
const U16 *const stateTable = (const U16 *)(statePtr->stateTable);
|
||||
U32 nbBitsOut = (U32)((statePtr->value + symbolTT.deltaNbBits) >> 16);
|
||||
BIT_addBits(bitC, statePtr->value, nbBitsOut);
|
||||
statePtr->value = stateTable[(statePtr->value >> nbBitsOut) + symbolTT.deltaFindState];
|
||||
}
|
||||
|
||||
ZSTD_STATIC void FSE_flushCState(BIT_CStream_t *bitC, const FSE_CState_t *statePtr)
|
||||
{
|
||||
BIT_addBits(bitC, statePtr->value, statePtr->stateLog);
|
||||
BIT_flushBits(bitC);
|
||||
}
|
||||
|
||||
/* ====== Decompression ====== */
|
||||
|
||||
typedef struct {
|
||||
U16 tableLog;
|
||||
U16 fastMode;
|
||||
} FSE_DTableHeader; /* sizeof U32 */
|
||||
|
||||
typedef struct {
|
||||
unsigned short newState;
|
||||
unsigned char symbol;
|
||||
unsigned char nbBits;
|
||||
} FSE_decode_t; /* size == U32 */
|
||||
|
||||
ZSTD_STATIC void FSE_initDState(FSE_DState_t *DStatePtr, BIT_DStream_t *bitD, const FSE_DTable *dt)
|
||||
{
|
||||
const void *ptr = dt;
|
||||
const FSE_DTableHeader *const DTableH = (const FSE_DTableHeader *)ptr;
|
||||
DStatePtr->state = BIT_readBits(bitD, DTableH->tableLog);
|
||||
BIT_reloadDStream(bitD);
|
||||
DStatePtr->table = dt + 1;
|
||||
}
|
||||
|
||||
ZSTD_STATIC BYTE FSE_peekSymbol(const FSE_DState_t *DStatePtr)
|
||||
{
|
||||
FSE_decode_t const DInfo = ((const FSE_decode_t *)(DStatePtr->table))[DStatePtr->state];
|
||||
return DInfo.symbol;
|
||||
}
|
||||
|
||||
ZSTD_STATIC void FSE_updateState(FSE_DState_t *DStatePtr, BIT_DStream_t *bitD)
|
||||
{
|
||||
FSE_decode_t const DInfo = ((const FSE_decode_t *)(DStatePtr->table))[DStatePtr->state];
|
||||
U32 const nbBits = DInfo.nbBits;
|
||||
size_t const lowBits = BIT_readBits(bitD, nbBits);
|
||||
DStatePtr->state = DInfo.newState + lowBits;
|
||||
}
|
||||
|
||||
ZSTD_STATIC BYTE FSE_decodeSymbol(FSE_DState_t *DStatePtr, BIT_DStream_t *bitD)
|
||||
{
|
||||
FSE_decode_t const DInfo = ((const FSE_decode_t *)(DStatePtr->table))[DStatePtr->state];
|
||||
U32 const nbBits = DInfo.nbBits;
|
||||
BYTE const symbol = DInfo.symbol;
|
||||
size_t const lowBits = BIT_readBits(bitD, nbBits);
|
||||
|
||||
DStatePtr->state = DInfo.newState + lowBits;
|
||||
return symbol;
|
||||
}
|
||||
|
||||
/*! FSE_decodeSymbolFast() :
|
||||
unsafe, only works if no symbol has a probability > 50% */
|
||||
ZSTD_STATIC BYTE FSE_decodeSymbolFast(FSE_DState_t *DStatePtr, BIT_DStream_t *bitD)
|
||||
{
|
||||
FSE_decode_t const DInfo = ((const FSE_decode_t *)(DStatePtr->table))[DStatePtr->state];
|
||||
U32 const nbBits = DInfo.nbBits;
|
||||
BYTE const symbol = DInfo.symbol;
|
||||
size_t const lowBits = BIT_readBitsFast(bitD, nbBits);
|
||||
|
||||
DStatePtr->state = DInfo.newState + lowBits;
|
||||
return symbol;
|
||||
}
|
||||
|
||||
ZSTD_STATIC unsigned FSE_endOfDState(const FSE_DState_t *DStatePtr) { return DStatePtr->state == 0; }
|
||||
|
||||
/* **************************************************************
|
||||
* Tuning parameters
|
||||
****************************************************************/
|
||||
/*!MEMORY_USAGE :
|
||||
* Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.)
|
||||
* Increasing memory usage improves compression ratio
|
||||
* Reduced memory usage can improve speed, due to cache effect
|
||||
* Recommended max value is 14, for 16KB, which nicely fits into Intel x86 L1 cache */
|
||||
#ifndef FSE_MAX_MEMORY_USAGE
|
||||
#define FSE_MAX_MEMORY_USAGE 14
|
||||
#endif
|
||||
#ifndef FSE_DEFAULT_MEMORY_USAGE
|
||||
#define FSE_DEFAULT_MEMORY_USAGE 13
|
||||
#endif
|
||||
|
||||
/*!FSE_MAX_SYMBOL_VALUE :
|
||||
* Maximum symbol value authorized.
|
||||
* Required for proper stack allocation */
|
||||
#ifndef FSE_MAX_SYMBOL_VALUE
|
||||
#define FSE_MAX_SYMBOL_VALUE 255
|
||||
#endif
|
||||
|
||||
/* **************************************************************
|
||||
* template functions type & suffix
|
||||
****************************************************************/
|
||||
#define FSE_FUNCTION_TYPE BYTE
|
||||
#define FSE_FUNCTION_EXTENSION
|
||||
#define FSE_DECODE_TYPE FSE_decode_t
|
||||
|
||||
/* ***************************************************************
|
||||
* Constants
|
||||
*****************************************************************/
|
||||
#define FSE_MAX_TABLELOG (FSE_MAX_MEMORY_USAGE - 2)
|
||||
#define FSE_MAX_TABLESIZE (1U << FSE_MAX_TABLELOG)
|
||||
#define FSE_MAXTABLESIZE_MASK (FSE_MAX_TABLESIZE - 1)
|
||||
#define FSE_DEFAULT_TABLELOG (FSE_DEFAULT_MEMORY_USAGE - 2)
|
||||
#define FSE_MIN_TABLELOG 5
|
||||
|
||||
#define FSE_TABLELOG_ABSOLUTE_MAX 15
|
||||
#if FSE_MAX_TABLELOG > FSE_TABLELOG_ABSOLUTE_MAX
|
||||
#error "FSE_MAX_TABLELOG > FSE_TABLELOG_ABSOLUTE_MAX is not supported"
|
||||
#endif
|
||||
|
||||
#define FSE_TABLESTEP(tableSize) ((tableSize >> 1) + (tableSize >> 3) + 3)
|
||||
|
||||
#endif /* FSE_H */
|
||||
@@ -0,0 +1,795 @@
|
||||
/*
|
||||
* FSE : Finite State Entropy encoder
|
||||
* Copyright (C) 2013-2015, Yann Collet.
|
||||
*
|
||||
* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License version 2 as published by the
|
||||
* Free Software Foundation. This program is dual-licensed; you may select
|
||||
* either version 2 of the GNU General Public License ("GPL") or BSD license
|
||||
* ("BSD").
|
||||
*
|
||||
* You can contact the author at :
|
||||
* - Source repository : https://github.com/Cyan4973/FiniteStateEntropy
|
||||
*/
|
||||
|
||||
/* **************************************************************
|
||||
* Compiler specifics
|
||||
****************************************************************/
|
||||
#define FORCE_INLINE static __always_inline
|
||||
|
||||
/* **************************************************************
|
||||
* Includes
|
||||
****************************************************************/
|
||||
#include "bitstream.h"
|
||||
#include "fse.h"
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/string.h> /* memcpy, memset */
|
||||
|
||||
/* **************************************************************
|
||||
* Error Management
|
||||
****************************************************************/
|
||||
#define FSE_STATIC_ASSERT(c) \
|
||||
{ \
|
||||
enum { FSE_static_assert = 1 / (int)(!!(c)) }; \
|
||||
} /* use only *after* variable declarations */
|
||||
|
||||
/* **************************************************************
|
||||
* Templates
|
||||
****************************************************************/
|
||||
/*
|
||||
designed to be included
|
||||
for type-specific functions (template emulation in C)
|
||||
Objective is to write these functions only once, for improved maintenance
|
||||
*/
|
||||
|
||||
/* safety checks */
|
||||
#ifndef FSE_FUNCTION_EXTENSION
|
||||
#error "FSE_FUNCTION_EXTENSION must be defined"
|
||||
#endif
|
||||
#ifndef FSE_FUNCTION_TYPE
|
||||
#error "FSE_FUNCTION_TYPE must be defined"
|
||||
#endif
|
||||
|
||||
/* Function names */
|
||||
#define FSE_CAT(X, Y) X##Y
|
||||
#define FSE_FUNCTION_NAME(X, Y) FSE_CAT(X, Y)
|
||||
#define FSE_TYPE_NAME(X, Y) FSE_CAT(X, Y)
|
||||
|
||||
/* Function templates */
|
||||
|
||||
/* FSE_buildCTable_wksp() :
|
||||
* Same as FSE_buildCTable(), but using an externally allocated scratch buffer (`workSpace`).
|
||||
* wkspSize should be sized to handle worst case situation, which is `1<<max_tableLog * sizeof(FSE_FUNCTION_TYPE)`
|
||||
* workSpace must also be properly aligned with FSE_FUNCTION_TYPE requirements
|
||||
*/
|
||||
size_t FSE_buildCTable_wksp(FSE_CTable *ct, const short *normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, void *workspace, size_t workspaceSize)
|
||||
{
|
||||
U32 const tableSize = 1 << tableLog;
|
||||
U32 const tableMask = tableSize - 1;
|
||||
void *const ptr = ct;
|
||||
U16 *const tableU16 = ((U16 *)ptr) + 2;
|
||||
void *const FSCT = ((U32 *)ptr) + 1 /* header */ + (tableLog ? tableSize >> 1 : 1);
|
||||
FSE_symbolCompressionTransform *const symbolTT = (FSE_symbolCompressionTransform *)(FSCT);
|
||||
U32 const step = FSE_TABLESTEP(tableSize);
|
||||
U32 highThreshold = tableSize - 1;
|
||||
|
||||
U32 *cumul;
|
||||
FSE_FUNCTION_TYPE *tableSymbol;
|
||||
size_t spaceUsed32 = 0;
|
||||
|
||||
cumul = (U32 *)workspace + spaceUsed32;
|
||||
spaceUsed32 += FSE_MAX_SYMBOL_VALUE + 2;
|
||||
tableSymbol = (FSE_FUNCTION_TYPE *)((U32 *)workspace + spaceUsed32);
|
||||
spaceUsed32 += ALIGN(sizeof(FSE_FUNCTION_TYPE) * ((size_t)1 << tableLog), sizeof(U32)) >> 2;
|
||||
|
||||
if ((spaceUsed32 << 2) > workspaceSize)
|
||||
return ERROR(tableLog_tooLarge);
|
||||
workspace = (U32 *)workspace + spaceUsed32;
|
||||
workspaceSize -= (spaceUsed32 << 2);
|
||||
|
||||
/* CTable header */
|
||||
tableU16[-2] = (U16)tableLog;
|
||||
tableU16[-1] = (U16)maxSymbolValue;
|
||||
|
||||
/* For explanations on how to distribute symbol values over the table :
|
||||
* http://fastcompression.blogspot.fr/2014/02/fse-distributing-symbol-values.html */
|
||||
|
||||
/* symbol start positions */
|
||||
{
|
||||
U32 u;
|
||||
cumul[0] = 0;
|
||||
for (u = 1; u <= maxSymbolValue + 1; u++) {
|
||||
if (normalizedCounter[u - 1] == -1) { /* Low proba symbol */
|
||||
cumul[u] = cumul[u - 1] + 1;
|
||||
tableSymbol[highThreshold--] = (FSE_FUNCTION_TYPE)(u - 1);
|
||||
} else {
|
||||
cumul[u] = cumul[u - 1] + normalizedCounter[u - 1];
|
||||
}
|
||||
}
|
||||
cumul[maxSymbolValue + 1] = tableSize + 1;
|
||||
}
|
||||
|
||||
/* Spread symbols */
|
||||
{
|
||||
U32 position = 0;
|
||||
U32 symbol;
|
||||
for (symbol = 0; symbol <= maxSymbolValue; symbol++) {
|
||||
int nbOccurrences;
|
||||
for (nbOccurrences = 0; nbOccurrences < normalizedCounter[symbol]; nbOccurrences++) {
|
||||
tableSymbol[position] = (FSE_FUNCTION_TYPE)symbol;
|
||||
position = (position + step) & tableMask;
|
||||
while (position > highThreshold)
|
||||
position = (position + step) & tableMask; /* Low proba area */
|
||||
}
|
||||
}
|
||||
|
||||
if (position != 0)
|
||||
return ERROR(GENERIC); /* Must have gone through all positions */
|
||||
}
|
||||
|
||||
/* Build table */
|
||||
{
|
||||
U32 u;
|
||||
for (u = 0; u < tableSize; u++) {
|
||||
FSE_FUNCTION_TYPE s = tableSymbol[u]; /* note : static analyzer may not understand tableSymbol is properly initialized */
|
||||
tableU16[cumul[s]++] = (U16)(tableSize + u); /* TableU16 : sorted by symbol order; gives next state value */
|
||||
}
|
||||
}
|
||||
|
||||
/* Build Symbol Transformation Table */
|
||||
{
|
||||
unsigned total = 0;
|
||||
unsigned s;
|
||||
for (s = 0; s <= maxSymbolValue; s++) {
|
||||
switch (normalizedCounter[s]) {
|
||||
case 0: break;
|
||||
|
||||
case -1:
|
||||
case 1:
|
||||
symbolTT[s].deltaNbBits = (tableLog << 16) - (1 << tableLog);
|
||||
symbolTT[s].deltaFindState = total - 1;
|
||||
total++;
|
||||
break;
|
||||
default: {
|
||||
U32 const maxBitsOut = tableLog - BIT_highbit32(normalizedCounter[s] - 1);
|
||||
U32 const minStatePlus = normalizedCounter[s] << maxBitsOut;
|
||||
symbolTT[s].deltaNbBits = (maxBitsOut << 16) - minStatePlus;
|
||||
symbolTT[s].deltaFindState = total - normalizedCounter[s];
|
||||
total += normalizedCounter[s];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*-**************************************************************
|
||||
* FSE NCount encoding-decoding
|
||||
****************************************************************/
|
||||
size_t FSE_NCountWriteBound(unsigned maxSymbolValue, unsigned tableLog)
|
||||
{
|
||||
size_t const maxHeaderSize = (((maxSymbolValue + 1) * tableLog) >> 3) + 3;
|
||||
return maxSymbolValue ? maxHeaderSize : FSE_NCOUNTBOUND; /* maxSymbolValue==0 ? use default */
|
||||
}
|
||||
|
||||
static size_t FSE_writeNCount_generic(void *header, size_t headerBufferSize, const short *normalizedCounter, unsigned maxSymbolValue, unsigned tableLog,
|
||||
unsigned writeIsSafe)
|
||||
{
|
||||
BYTE *const ostart = (BYTE *)header;
|
||||
BYTE *out = ostart;
|
||||
BYTE *const oend = ostart + headerBufferSize;
|
||||
int nbBits;
|
||||
const int tableSize = 1 << tableLog;
|
||||
int remaining;
|
||||
int threshold;
|
||||
U32 bitStream;
|
||||
int bitCount;
|
||||
unsigned charnum = 0;
|
||||
int previous0 = 0;
|
||||
|
||||
bitStream = 0;
|
||||
bitCount = 0;
|
||||
/* Table Size */
|
||||
bitStream += (tableLog - FSE_MIN_TABLELOG) << bitCount;
|
||||
bitCount += 4;
|
||||
|
||||
/* Init */
|
||||
remaining = tableSize + 1; /* +1 for extra accuracy */
|
||||
threshold = tableSize;
|
||||
nbBits = tableLog + 1;
|
||||
|
||||
while (remaining > 1) { /* stops at 1 */
|
||||
if (previous0) {
|
||||
unsigned start = charnum;
|
||||
while (!normalizedCounter[charnum])
|
||||
charnum++;
|
||||
while (charnum >= start + 24) {
|
||||
start += 24;
|
||||
bitStream += 0xFFFFU << bitCount;
|
||||
if ((!writeIsSafe) && (out > oend - 2))
|
||||
return ERROR(dstSize_tooSmall); /* Buffer overflow */
|
||||
out[0] = (BYTE)bitStream;
|
||||
out[1] = (BYTE)(bitStream >> 8);
|
||||
out += 2;
|
||||
bitStream >>= 16;
|
||||
}
|
||||
while (charnum >= start + 3) {
|
||||
start += 3;
|
||||
bitStream += 3 << bitCount;
|
||||
bitCount += 2;
|
||||
}
|
||||
bitStream += (charnum - start) << bitCount;
|
||||
bitCount += 2;
|
||||
if (bitCount > 16) {
|
||||
if ((!writeIsSafe) && (out > oend - 2))
|
||||
return ERROR(dstSize_tooSmall); /* Buffer overflow */
|
||||
out[0] = (BYTE)bitStream;
|
||||
out[1] = (BYTE)(bitStream >> 8);
|
||||
out += 2;
|
||||
bitStream >>= 16;
|
||||
bitCount -= 16;
|
||||
}
|
||||
}
|
||||
{
|
||||
int count = normalizedCounter[charnum++];
|
||||
int const max = (2 * threshold - 1) - remaining;
|
||||
remaining -= count < 0 ? -count : count;
|
||||
count++; /* +1 for extra accuracy */
|
||||
if (count >= threshold)
|
||||
count += max; /* [0..max[ [max..threshold[ (...) [threshold+max 2*threshold[ */
|
||||
bitStream += count << bitCount;
|
||||
bitCount += nbBits;
|
||||
bitCount -= (count < max);
|
||||
previous0 = (count == 1);
|
||||
if (remaining < 1)
|
||||
return ERROR(GENERIC);
|
||||
while (remaining < threshold)
|
||||
nbBits--, threshold >>= 1;
|
||||
}
|
||||
if (bitCount > 16) {
|
||||
if ((!writeIsSafe) && (out > oend - 2))
|
||||
return ERROR(dstSize_tooSmall); /* Buffer overflow */
|
||||
out[0] = (BYTE)bitStream;
|
||||
out[1] = (BYTE)(bitStream >> 8);
|
||||
out += 2;
|
||||
bitStream >>= 16;
|
||||
bitCount -= 16;
|
||||
}
|
||||
}
|
||||
|
||||
/* flush remaining bitStream */
|
||||
if ((!writeIsSafe) && (out > oend - 2))
|
||||
return ERROR(dstSize_tooSmall); /* Buffer overflow */
|
||||
out[0] = (BYTE)bitStream;
|
||||
out[1] = (BYTE)(bitStream >> 8);
|
||||
out += (bitCount + 7) / 8;
|
||||
|
||||
if (charnum > maxSymbolValue + 1)
|
||||
return ERROR(GENERIC);
|
||||
|
||||
return (out - ostart);
|
||||
}
|
||||
|
||||
size_t FSE_writeNCount(void *buffer, size_t bufferSize, const short *normalizedCounter, unsigned maxSymbolValue, unsigned tableLog)
|
||||
{
|
||||
if (tableLog > FSE_MAX_TABLELOG)
|
||||
return ERROR(tableLog_tooLarge); /* Unsupported */
|
||||
if (tableLog < FSE_MIN_TABLELOG)
|
||||
return ERROR(GENERIC); /* Unsupported */
|
||||
|
||||
if (bufferSize < FSE_NCountWriteBound(maxSymbolValue, tableLog))
|
||||
return FSE_writeNCount_generic(buffer, bufferSize, normalizedCounter, maxSymbolValue, tableLog, 0);
|
||||
|
||||
return FSE_writeNCount_generic(buffer, bufferSize, normalizedCounter, maxSymbolValue, tableLog, 1);
|
||||
}
|
||||
|
||||
/*-**************************************************************
|
||||
* Counting histogram
|
||||
****************************************************************/
|
||||
/*! FSE_count_simple
|
||||
This function counts byte values within `src`, and store the histogram into table `count`.
|
||||
It doesn't use any additional memory.
|
||||
But this function is unsafe : it doesn't check that all values within `src` can fit into `count`.
|
||||
For this reason, prefer using a table `count` with 256 elements.
|
||||
@return : count of most numerous element
|
||||
*/
|
||||
size_t FSE_count_simple(unsigned *count, unsigned *maxSymbolValuePtr, const void *src, size_t srcSize)
|
||||
{
|
||||
const BYTE *ip = (const BYTE *)src;
|
||||
const BYTE *const end = ip + srcSize;
|
||||
unsigned maxSymbolValue = *maxSymbolValuePtr;
|
||||
unsigned max = 0;
|
||||
|
||||
memset(count, 0, (maxSymbolValue + 1) * sizeof(*count));
|
||||
if (srcSize == 0) {
|
||||
*maxSymbolValuePtr = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (ip < end)
|
||||
count[*ip++]++;
|
||||
|
||||
while (!count[maxSymbolValue])
|
||||
maxSymbolValue--;
|
||||
*maxSymbolValuePtr = maxSymbolValue;
|
||||
|
||||
{
|
||||
U32 s;
|
||||
for (s = 0; s <= maxSymbolValue; s++)
|
||||
if (count[s] > max)
|
||||
max = count[s];
|
||||
}
|
||||
|
||||
return (size_t)max;
|
||||
}
|
||||
|
||||
/* FSE_count_parallel_wksp() :
|
||||
* Same as FSE_count_parallel(), but using an externally provided scratch buffer.
|
||||
* `workSpace` size must be a minimum of `1024 * sizeof(unsigned)`` */
|
||||
static size_t FSE_count_parallel_wksp(unsigned *count, unsigned *maxSymbolValuePtr, const void *source, size_t sourceSize, unsigned checkMax,
|
||||
unsigned *const workSpace)
|
||||
{
|
||||
const BYTE *ip = (const BYTE *)source;
|
||||
const BYTE *const iend = ip + sourceSize;
|
||||
unsigned maxSymbolValue = *maxSymbolValuePtr;
|
||||
unsigned max = 0;
|
||||
U32 *const Counting1 = workSpace;
|
||||
U32 *const Counting2 = Counting1 + 256;
|
||||
U32 *const Counting3 = Counting2 + 256;
|
||||
U32 *const Counting4 = Counting3 + 256;
|
||||
|
||||
memset(Counting1, 0, 4 * 256 * sizeof(unsigned));
|
||||
|
||||
/* safety checks */
|
||||
if (!sourceSize) {
|
||||
memset(count, 0, maxSymbolValue + 1);
|
||||
*maxSymbolValuePtr = 0;
|
||||
return 0;
|
||||
}
|
||||
if (!maxSymbolValue)
|
||||
maxSymbolValue = 255; /* 0 == default */
|
||||
|
||||
/* by stripes of 16 bytes */
|
||||
{
|
||||
U32 cached = ZSTD_read32(ip);
|
||||
ip += 4;
|
||||
while (ip < iend - 15) {
|
||||
U32 c = cached;
|
||||
cached = ZSTD_read32(ip);
|
||||
ip += 4;
|
||||
Counting1[(BYTE)c]++;
|
||||
Counting2[(BYTE)(c >> 8)]++;
|
||||
Counting3[(BYTE)(c >> 16)]++;
|
||||
Counting4[c >> 24]++;
|
||||
c = cached;
|
||||
cached = ZSTD_read32(ip);
|
||||
ip += 4;
|
||||
Counting1[(BYTE)c]++;
|
||||
Counting2[(BYTE)(c >> 8)]++;
|
||||
Counting3[(BYTE)(c >> 16)]++;
|
||||
Counting4[c >> 24]++;
|
||||
c = cached;
|
||||
cached = ZSTD_read32(ip);
|
||||
ip += 4;
|
||||
Counting1[(BYTE)c]++;
|
||||
Counting2[(BYTE)(c >> 8)]++;
|
||||
Counting3[(BYTE)(c >> 16)]++;
|
||||
Counting4[c >> 24]++;
|
||||
c = cached;
|
||||
cached = ZSTD_read32(ip);
|
||||
ip += 4;
|
||||
Counting1[(BYTE)c]++;
|
||||
Counting2[(BYTE)(c >> 8)]++;
|
||||
Counting3[(BYTE)(c >> 16)]++;
|
||||
Counting4[c >> 24]++;
|
||||
}
|
||||
ip -= 4;
|
||||
}
|
||||
|
||||
/* finish last symbols */
|
||||
while (ip < iend)
|
||||
Counting1[*ip++]++;
|
||||
|
||||
if (checkMax) { /* verify stats will fit into destination table */
|
||||
U32 s;
|
||||
for (s = 255; s > maxSymbolValue; s--) {
|
||||
Counting1[s] += Counting2[s] + Counting3[s] + Counting4[s];
|
||||
if (Counting1[s])
|
||||
return ERROR(maxSymbolValue_tooSmall);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
U32 s;
|
||||
for (s = 0; s <= maxSymbolValue; s++) {
|
||||
count[s] = Counting1[s] + Counting2[s] + Counting3[s] + Counting4[s];
|
||||
if (count[s] > max)
|
||||
max = count[s];
|
||||
}
|
||||
}
|
||||
|
||||
while (!count[maxSymbolValue])
|
||||
maxSymbolValue--;
|
||||
*maxSymbolValuePtr = maxSymbolValue;
|
||||
return (size_t)max;
|
||||
}
|
||||
|
||||
/* FSE_countFast_wksp() :
|
||||
* Same as FSE_countFast(), but using an externally provided scratch buffer.
|
||||
* `workSpace` size must be table of >= `1024` unsigned */
|
||||
size_t FSE_countFast_wksp(unsigned *count, unsigned *maxSymbolValuePtr, const void *source, size_t sourceSize, unsigned *workSpace)
|
||||
{
|
||||
if (sourceSize < 1500)
|
||||
return FSE_count_simple(count, maxSymbolValuePtr, source, sourceSize);
|
||||
return FSE_count_parallel_wksp(count, maxSymbolValuePtr, source, sourceSize, 0, workSpace);
|
||||
}
|
||||
|
||||
/* FSE_count_wksp() :
|
||||
* Same as FSE_count(), but using an externally provided scratch buffer.
|
||||
* `workSpace` size must be table of >= `1024` unsigned */
|
||||
size_t FSE_count_wksp(unsigned *count, unsigned *maxSymbolValuePtr, const void *source, size_t sourceSize, unsigned *workSpace)
|
||||
{
|
||||
if (*maxSymbolValuePtr < 255)
|
||||
return FSE_count_parallel_wksp(count, maxSymbolValuePtr, source, sourceSize, 1, workSpace);
|
||||
*maxSymbolValuePtr = 255;
|
||||
return FSE_countFast_wksp(count, maxSymbolValuePtr, source, sourceSize, workSpace);
|
||||
}
|
||||
|
||||
/*-**************************************************************
|
||||
* FSE Compression Code
|
||||
****************************************************************/
|
||||
/*! FSE_sizeof_CTable() :
|
||||
FSE_CTable is a variable size structure which contains :
|
||||
`U16 tableLog;`
|
||||
`U16 maxSymbolValue;`
|
||||
`U16 nextStateNumber[1 << tableLog];` // This size is variable
|
||||
`FSE_symbolCompressionTransform symbolTT[maxSymbolValue+1];` // This size is variable
|
||||
Allocation is manual (C standard does not support variable-size structures).
|
||||
*/
|
||||
size_t FSE_sizeof_CTable(unsigned maxSymbolValue, unsigned tableLog)
|
||||
{
|
||||
if (tableLog > FSE_MAX_TABLELOG)
|
||||
return ERROR(tableLog_tooLarge);
|
||||
return FSE_CTABLE_SIZE_U32(tableLog, maxSymbolValue) * sizeof(U32);
|
||||
}
|
||||
|
||||
/* provides the minimum logSize to safely represent a distribution */
|
||||
static unsigned FSE_minTableLog(size_t srcSize, unsigned maxSymbolValue)
|
||||
{
|
||||
U32 minBitsSrc = BIT_highbit32((U32)(srcSize - 1)) + 1;
|
||||
U32 minBitsSymbols = BIT_highbit32(maxSymbolValue) + 2;
|
||||
U32 minBits = minBitsSrc < minBitsSymbols ? minBitsSrc : minBitsSymbols;
|
||||
return minBits;
|
||||
}
|
||||
|
||||
unsigned FSE_optimalTableLog_internal(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue, unsigned minus)
|
||||
{
|
||||
U32 maxBitsSrc = BIT_highbit32((U32)(srcSize - 1)) - minus;
|
||||
U32 tableLog = maxTableLog;
|
||||
U32 minBits = FSE_minTableLog(srcSize, maxSymbolValue);
|
||||
if (tableLog == 0)
|
||||
tableLog = FSE_DEFAULT_TABLELOG;
|
||||
if (maxBitsSrc < tableLog)
|
||||
tableLog = maxBitsSrc; /* Accuracy can be reduced */
|
||||
if (minBits > tableLog)
|
||||
tableLog = minBits; /* Need a minimum to safely represent all symbol values */
|
||||
if (tableLog < FSE_MIN_TABLELOG)
|
||||
tableLog = FSE_MIN_TABLELOG;
|
||||
if (tableLog > FSE_MAX_TABLELOG)
|
||||
tableLog = FSE_MAX_TABLELOG;
|
||||
return tableLog;
|
||||
}
|
||||
|
||||
unsigned FSE_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue)
|
||||
{
|
||||
return FSE_optimalTableLog_internal(maxTableLog, srcSize, maxSymbolValue, 2);
|
||||
}
|
||||
|
||||
/* Secondary normalization method.
|
||||
To be used when primary method fails. */
|
||||
|
||||
static size_t FSE_normalizeM2(short *norm, U32 tableLog, const unsigned *count, size_t total, U32 maxSymbolValue)
|
||||
{
|
||||
short const NOT_YET_ASSIGNED = -2;
|
||||
U32 s;
|
||||
U32 distributed = 0;
|
||||
U32 ToDistribute;
|
||||
|
||||
/* Init */
|
||||
U32 const lowThreshold = (U32)(total >> tableLog);
|
||||
U32 lowOne = (U32)((total * 3) >> (tableLog + 1));
|
||||
|
||||
for (s = 0; s <= maxSymbolValue; s++) {
|
||||
if (count[s] == 0) {
|
||||
norm[s] = 0;
|
||||
continue;
|
||||
}
|
||||
if (count[s] <= lowThreshold) {
|
||||
norm[s] = -1;
|
||||
distributed++;
|
||||
total -= count[s];
|
||||
continue;
|
||||
}
|
||||
if (count[s] <= lowOne) {
|
||||
norm[s] = 1;
|
||||
distributed++;
|
||||
total -= count[s];
|
||||
continue;
|
||||
}
|
||||
|
||||
norm[s] = NOT_YET_ASSIGNED;
|
||||
}
|
||||
ToDistribute = (1 << tableLog) - distributed;
|
||||
|
||||
if ((total / ToDistribute) > lowOne) {
|
||||
/* risk of rounding to zero */
|
||||
lowOne = (U32)((total * 3) / (ToDistribute * 2));
|
||||
for (s = 0; s <= maxSymbolValue; s++) {
|
||||
if ((norm[s] == NOT_YET_ASSIGNED) && (count[s] <= lowOne)) {
|
||||
norm[s] = 1;
|
||||
distributed++;
|
||||
total -= count[s];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ToDistribute = (1 << tableLog) - distributed;
|
||||
}
|
||||
|
||||
if (distributed == maxSymbolValue + 1) {
|
||||
/* all values are pretty poor;
|
||||
probably incompressible data (should have already been detected);
|
||||
find max, then give all remaining points to max */
|
||||
U32 maxV = 0, maxC = 0;
|
||||
for (s = 0; s <= maxSymbolValue; s++)
|
||||
if (count[s] > maxC)
|
||||
maxV = s, maxC = count[s];
|
||||
norm[maxV] += (short)ToDistribute;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (total == 0) {
|
||||
/* all of the symbols were low enough for the lowOne or lowThreshold */
|
||||
for (s = 0; ToDistribute > 0; s = (s + 1) % (maxSymbolValue + 1))
|
||||
if (norm[s] > 0)
|
||||
ToDistribute--, norm[s]++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
{
|
||||
U64 const vStepLog = 62 - tableLog;
|
||||
U64 const mid = (1ULL << (vStepLog - 1)) - 1;
|
||||
U64 const rStep = div_u64((((U64)1 << vStepLog) * ToDistribute) + mid, (U32)total); /* scale on remaining */
|
||||
U64 tmpTotal = mid;
|
||||
for (s = 0; s <= maxSymbolValue; s++) {
|
||||
if (norm[s] == NOT_YET_ASSIGNED) {
|
||||
U64 const end = tmpTotal + (count[s] * rStep);
|
||||
U32 const sStart = (U32)(tmpTotal >> vStepLog);
|
||||
U32 const sEnd = (U32)(end >> vStepLog);
|
||||
U32 const weight = sEnd - sStart;
|
||||
if (weight < 1)
|
||||
return ERROR(GENERIC);
|
||||
norm[s] = (short)weight;
|
||||
tmpTotal = end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t FSE_normalizeCount(short *normalizedCounter, unsigned tableLog, const unsigned *count, size_t total, unsigned maxSymbolValue)
|
||||
{
|
||||
/* Sanity checks */
|
||||
if (tableLog == 0)
|
||||
tableLog = FSE_DEFAULT_TABLELOG;
|
||||
if (tableLog < FSE_MIN_TABLELOG)
|
||||
return ERROR(GENERIC); /* Unsupported size */
|
||||
if (tableLog > FSE_MAX_TABLELOG)
|
||||
return ERROR(tableLog_tooLarge); /* Unsupported size */
|
||||
if (tableLog < FSE_minTableLog(total, maxSymbolValue))
|
||||
return ERROR(GENERIC); /* Too small tableLog, compression potentially impossible */
|
||||
|
||||
{
|
||||
U32 const rtbTable[] = {0, 473195, 504333, 520860, 550000, 700000, 750000, 830000};
|
||||
U64 const scale = 62 - tableLog;
|
||||
U64 const step = div_u64((U64)1 << 62, (U32)total); /* <== here, one division ! */
|
||||
U64 const vStep = 1ULL << (scale - 20);
|
||||
int stillToDistribute = 1 << tableLog;
|
||||
unsigned s;
|
||||
unsigned largest = 0;
|
||||
short largestP = 0;
|
||||
U32 lowThreshold = (U32)(total >> tableLog);
|
||||
|
||||
for (s = 0; s <= maxSymbolValue; s++) {
|
||||
if (count[s] == total)
|
||||
return 0; /* rle special case */
|
||||
if (count[s] == 0) {
|
||||
normalizedCounter[s] = 0;
|
||||
continue;
|
||||
}
|
||||
if (count[s] <= lowThreshold) {
|
||||
normalizedCounter[s] = -1;
|
||||
stillToDistribute--;
|
||||
} else {
|
||||
short proba = (short)((count[s] * step) >> scale);
|
||||
if (proba < 8) {
|
||||
U64 restToBeat = vStep * rtbTable[proba];
|
||||
proba += (count[s] * step) - ((U64)proba << scale) > restToBeat;
|
||||
}
|
||||
if (proba > largestP)
|
||||
largestP = proba, largest = s;
|
||||
normalizedCounter[s] = proba;
|
||||
stillToDistribute -= proba;
|
||||
}
|
||||
}
|
||||
if (-stillToDistribute >= (normalizedCounter[largest] >> 1)) {
|
||||
/* corner case, need another normalization method */
|
||||
size_t const errorCode = FSE_normalizeM2(normalizedCounter, tableLog, count, total, maxSymbolValue);
|
||||
if (FSE_isError(errorCode))
|
||||
return errorCode;
|
||||
} else
|
||||
normalizedCounter[largest] += (short)stillToDistribute;
|
||||
}
|
||||
|
||||
return tableLog;
|
||||
}
|
||||
|
||||
/* fake FSE_CTable, for raw (uncompressed) input */
|
||||
size_t FSE_buildCTable_raw(FSE_CTable *ct, unsigned nbBits)
|
||||
{
|
||||
const unsigned tableSize = 1 << nbBits;
|
||||
const unsigned tableMask = tableSize - 1;
|
||||
const unsigned maxSymbolValue = tableMask;
|
||||
void *const ptr = ct;
|
||||
U16 *const tableU16 = ((U16 *)ptr) + 2;
|
||||
void *const FSCT = ((U32 *)ptr) + 1 /* header */ + (tableSize >> 1); /* assumption : tableLog >= 1 */
|
||||
FSE_symbolCompressionTransform *const symbolTT = (FSE_symbolCompressionTransform *)(FSCT);
|
||||
unsigned s;
|
||||
|
||||
/* Sanity checks */
|
||||
if (nbBits < 1)
|
||||
return ERROR(GENERIC); /* min size */
|
||||
|
||||
/* header */
|
||||
tableU16[-2] = (U16)nbBits;
|
||||
tableU16[-1] = (U16)maxSymbolValue;
|
||||
|
||||
/* Build table */
|
||||
for (s = 0; s < tableSize; s++)
|
||||
tableU16[s] = (U16)(tableSize + s);
|
||||
|
||||
/* Build Symbol Transformation Table */
|
||||
{
|
||||
const U32 deltaNbBits = (nbBits << 16) - (1 << nbBits);
|
||||
for (s = 0; s <= maxSymbolValue; s++) {
|
||||
symbolTT[s].deltaNbBits = deltaNbBits;
|
||||
symbolTT[s].deltaFindState = s - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* fake FSE_CTable, for rle input (always same symbol) */
|
||||
size_t FSE_buildCTable_rle(FSE_CTable *ct, BYTE symbolValue)
|
||||
{
|
||||
void *ptr = ct;
|
||||
U16 *tableU16 = ((U16 *)ptr) + 2;
|
||||
void *FSCTptr = (U32 *)ptr + 2;
|
||||
FSE_symbolCompressionTransform *symbolTT = (FSE_symbolCompressionTransform *)FSCTptr;
|
||||
|
||||
/* header */
|
||||
tableU16[-2] = (U16)0;
|
||||
tableU16[-1] = (U16)symbolValue;
|
||||
|
||||
/* Build table */
|
||||
tableU16[0] = 0;
|
||||
tableU16[1] = 0; /* just in case */
|
||||
|
||||
/* Build Symbol Transformation Table */
|
||||
symbolTT[symbolValue].deltaNbBits = 0;
|
||||
symbolTT[symbolValue].deltaFindState = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t FSE_compress_usingCTable_generic(void *dst, size_t dstSize, const void *src, size_t srcSize, const FSE_CTable *ct, const unsigned fast)
|
||||
{
|
||||
const BYTE *const istart = (const BYTE *)src;
|
||||
const BYTE *const iend = istart + srcSize;
|
||||
const BYTE *ip = iend;
|
||||
|
||||
BIT_CStream_t bitC;
|
||||
FSE_CState_t CState1, CState2;
|
||||
|
||||
/* init */
|
||||
if (srcSize <= 2)
|
||||
return 0;
|
||||
{
|
||||
size_t const initError = BIT_initCStream(&bitC, dst, dstSize);
|
||||
if (FSE_isError(initError))
|
||||
return 0; /* not enough space available to write a bitstream */
|
||||
}
|
||||
|
||||
#define FSE_FLUSHBITS(s) (fast ? BIT_flushBitsFast(s) : BIT_flushBits(s))
|
||||
|
||||
if (srcSize & 1) {
|
||||
FSE_initCState2(&CState1, ct, *--ip);
|
||||
FSE_initCState2(&CState2, ct, *--ip);
|
||||
FSE_encodeSymbol(&bitC, &CState1, *--ip);
|
||||
FSE_FLUSHBITS(&bitC);
|
||||
} else {
|
||||
FSE_initCState2(&CState2, ct, *--ip);
|
||||
FSE_initCState2(&CState1, ct, *--ip);
|
||||
}
|
||||
|
||||
/* join to mod 4 */
|
||||
srcSize -= 2;
|
||||
if ((sizeof(bitC.bitContainer) * 8 > FSE_MAX_TABLELOG * 4 + 7) && (srcSize & 2)) { /* test bit 2 */
|
||||
FSE_encodeSymbol(&bitC, &CState2, *--ip);
|
||||
FSE_encodeSymbol(&bitC, &CState1, *--ip);
|
||||
FSE_FLUSHBITS(&bitC);
|
||||
}
|
||||
|
||||
/* 2 or 4 encoding per loop */
|
||||
while (ip > istart) {
|
||||
|
||||
FSE_encodeSymbol(&bitC, &CState2, *--ip);
|
||||
|
||||
if (sizeof(bitC.bitContainer) * 8 < FSE_MAX_TABLELOG * 2 + 7) /* this test must be static */
|
||||
FSE_FLUSHBITS(&bitC);
|
||||
|
||||
FSE_encodeSymbol(&bitC, &CState1, *--ip);
|
||||
|
||||
if (sizeof(bitC.bitContainer) * 8 > FSE_MAX_TABLELOG * 4 + 7) { /* this test must be static */
|
||||
FSE_encodeSymbol(&bitC, &CState2, *--ip);
|
||||
FSE_encodeSymbol(&bitC, &CState1, *--ip);
|
||||
}
|
||||
|
||||
FSE_FLUSHBITS(&bitC);
|
||||
}
|
||||
|
||||
FSE_flushCState(&bitC, &CState2);
|
||||
FSE_flushCState(&bitC, &CState1);
|
||||
return BIT_closeCStream(&bitC);
|
||||
}
|
||||
|
||||
size_t FSE_compress_usingCTable(void *dst, size_t dstSize, const void *src, size_t srcSize, const FSE_CTable *ct)
|
||||
{
|
||||
unsigned const fast = (dstSize >= FSE_BLOCKBOUND(srcSize));
|
||||
|
||||
if (fast)
|
||||
return FSE_compress_usingCTable_generic(dst, dstSize, src, srcSize, ct, 1);
|
||||
else
|
||||
return FSE_compress_usingCTable_generic(dst, dstSize, src, srcSize, ct, 0);
|
||||
}
|
||||
|
||||
size_t FSE_compressBound(size_t size) { return FSE_COMPRESSBOUND(size); }
|
||||
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
* FSE : Finite State Entropy decoder
|
||||
* Copyright (C) 2013-2015, Yann Collet.
|
||||
*
|
||||
* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License version 2 as published by the
|
||||
* Free Software Foundation. This program is dual-licensed; you may select
|
||||
* either version 2 of the GNU General Public License ("GPL") or BSD license
|
||||
* ("BSD").
|
||||
*
|
||||
* You can contact the author at :
|
||||
* - Source repository : https://github.com/Cyan4973/FiniteStateEntropy
|
||||
*/
|
||||
|
||||
/* **************************************************************
|
||||
* Compiler specifics
|
||||
****************************************************************/
|
||||
#define FORCE_INLINE static __always_inline
|
||||
|
||||
/* **************************************************************
|
||||
* Includes
|
||||
****************************************************************/
|
||||
#include "bitstream.h"
|
||||
#include "fse.h"
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h> /* memcpy, memset */
|
||||
|
||||
/* **************************************************************
|
||||
* Error Management
|
||||
****************************************************************/
|
||||
#define FSE_isError ERR_isError
|
||||
#define FSE_STATIC_ASSERT(c) \
|
||||
{ \
|
||||
enum { FSE_static_assert = 1 / (int)(!!(c)) }; \
|
||||
} /* use only *after* variable declarations */
|
||||
|
||||
/* check and forward error code */
|
||||
#define CHECK_F(f) \
|
||||
{ \
|
||||
size_t const e = f; \
|
||||
if (FSE_isError(e)) \
|
||||
return e; \
|
||||
}
|
||||
|
||||
/* **************************************************************
|
||||
* Templates
|
||||
****************************************************************/
|
||||
/*
|
||||
designed to be included
|
||||
for type-specific functions (template emulation in C)
|
||||
Objective is to write these functions only once, for improved maintenance
|
||||
*/
|
||||
|
||||
/* safety checks */
|
||||
#ifndef FSE_FUNCTION_EXTENSION
|
||||
#error "FSE_FUNCTION_EXTENSION must be defined"
|
||||
#endif
|
||||
#ifndef FSE_FUNCTION_TYPE
|
||||
#error "FSE_FUNCTION_TYPE must be defined"
|
||||
#endif
|
||||
|
||||
/* Function names */
|
||||
#define FSE_CAT(X, Y) X##Y
|
||||
#define FSE_FUNCTION_NAME(X, Y) FSE_CAT(X, Y)
|
||||
#define FSE_TYPE_NAME(X, Y) FSE_CAT(X, Y)
|
||||
|
||||
/* Function templates */
|
||||
|
||||
size_t FSE_buildDTable_wksp(FSE_DTable *dt, const short *normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, void *workspace, size_t workspaceSize)
|
||||
{
|
||||
void *const tdPtr = dt + 1; /* because *dt is unsigned, 32-bits aligned on 32-bits */
|
||||
FSE_DECODE_TYPE *const tableDecode = (FSE_DECODE_TYPE *)(tdPtr);
|
||||
U16 *symbolNext = (U16 *)workspace;
|
||||
|
||||
U32 const maxSV1 = maxSymbolValue + 1;
|
||||
U32 const tableSize = 1 << tableLog;
|
||||
U32 highThreshold = tableSize - 1;
|
||||
|
||||
/* Sanity Checks */
|
||||
if (workspaceSize < sizeof(U16) * (FSE_MAX_SYMBOL_VALUE + 1))
|
||||
return ERROR(tableLog_tooLarge);
|
||||
if (maxSymbolValue > FSE_MAX_SYMBOL_VALUE)
|
||||
return ERROR(maxSymbolValue_tooLarge);
|
||||
if (tableLog > FSE_MAX_TABLELOG)
|
||||
return ERROR(tableLog_tooLarge);
|
||||
|
||||
/* Init, lay down lowprob symbols */
|
||||
{
|
||||
FSE_DTableHeader DTableH;
|
||||
DTableH.tableLog = (U16)tableLog;
|
||||
DTableH.fastMode = 1;
|
||||
{
|
||||
S16 const largeLimit = (S16)(1 << (tableLog - 1));
|
||||
U32 s;
|
||||
for (s = 0; s < maxSV1; s++) {
|
||||
if (normalizedCounter[s] == -1) {
|
||||
tableDecode[highThreshold--].symbol = (FSE_FUNCTION_TYPE)s;
|
||||
symbolNext[s] = 1;
|
||||
} else {
|
||||
if (normalizedCounter[s] >= largeLimit)
|
||||
DTableH.fastMode = 0;
|
||||
symbolNext[s] = normalizedCounter[s];
|
||||
}
|
||||
}
|
||||
}
|
||||
memcpy(dt, &DTableH, sizeof(DTableH));
|
||||
}
|
||||
|
||||
/* Spread symbols */
|
||||
{
|
||||
U32 const tableMask = tableSize - 1;
|
||||
U32 const step = FSE_TABLESTEP(tableSize);
|
||||
U32 s, position = 0;
|
||||
for (s = 0; s < maxSV1; s++) {
|
||||
int i;
|
||||
for (i = 0; i < normalizedCounter[s]; i++) {
|
||||
tableDecode[position].symbol = (FSE_FUNCTION_TYPE)s;
|
||||
position = (position + step) & tableMask;
|
||||
while (position > highThreshold)
|
||||
position = (position + step) & tableMask; /* lowprob area */
|
||||
}
|
||||
}
|
||||
if (position != 0)
|
||||
return ERROR(GENERIC); /* position must reach all cells once, otherwise normalizedCounter is incorrect */
|
||||
}
|
||||
|
||||
/* Build Decoding table */
|
||||
{
|
||||
U32 u;
|
||||
for (u = 0; u < tableSize; u++) {
|
||||
FSE_FUNCTION_TYPE const symbol = (FSE_FUNCTION_TYPE)(tableDecode[u].symbol);
|
||||
U16 nextState = symbolNext[symbol]++;
|
||||
tableDecode[u].nbBits = (BYTE)(tableLog - BIT_highbit32((U32)nextState));
|
||||
tableDecode[u].newState = (U16)((nextState << tableDecode[u].nbBits) - tableSize);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*-*******************************************************
|
||||
* Decompression (Byte symbols)
|
||||
*********************************************************/
|
||||
size_t FSE_buildDTable_rle(FSE_DTable *dt, BYTE symbolValue)
|
||||
{
|
||||
void *ptr = dt;
|
||||
FSE_DTableHeader *const DTableH = (FSE_DTableHeader *)ptr;
|
||||
void *dPtr = dt + 1;
|
||||
FSE_decode_t *const cell = (FSE_decode_t *)dPtr;
|
||||
|
||||
DTableH->tableLog = 0;
|
||||
DTableH->fastMode = 0;
|
||||
|
||||
cell->newState = 0;
|
||||
cell->symbol = symbolValue;
|
||||
cell->nbBits = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t FSE_buildDTable_raw(FSE_DTable *dt, unsigned nbBits)
|
||||
{
|
||||
void *ptr = dt;
|
||||
FSE_DTableHeader *const DTableH = (FSE_DTableHeader *)ptr;
|
||||
void *dPtr = dt + 1;
|
||||
FSE_decode_t *const dinfo = (FSE_decode_t *)dPtr;
|
||||
const unsigned tableSize = 1 << nbBits;
|
||||
const unsigned tableMask = tableSize - 1;
|
||||
const unsigned maxSV1 = tableMask + 1;
|
||||
unsigned s;
|
||||
|
||||
/* Sanity checks */
|
||||
if (nbBits < 1)
|
||||
return ERROR(GENERIC); /* min size */
|
||||
|
||||
/* Build Decoding Table */
|
||||
DTableH->tableLog = (U16)nbBits;
|
||||
DTableH->fastMode = 1;
|
||||
for (s = 0; s < maxSV1; s++) {
|
||||
dinfo[s].newState = 0;
|
||||
dinfo[s].symbol = (BYTE)s;
|
||||
dinfo[s].nbBits = (BYTE)nbBits;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
FORCE_INLINE size_t FSE_decompress_usingDTable_generic(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const FSE_DTable *dt,
|
||||
const unsigned fast)
|
||||
{
|
||||
BYTE *const ostart = (BYTE *)dst;
|
||||
BYTE *op = ostart;
|
||||
BYTE *const omax = op + maxDstSize;
|
||||
BYTE *const olimit = omax - 3;
|
||||
|
||||
BIT_DStream_t bitD;
|
||||
FSE_DState_t state1;
|
||||
FSE_DState_t state2;
|
||||
|
||||
/* Init */
|
||||
CHECK_F(BIT_initDStream(&bitD, cSrc, cSrcSize));
|
||||
|
||||
FSE_initDState(&state1, &bitD, dt);
|
||||
FSE_initDState(&state2, &bitD, dt);
|
||||
|
||||
#define FSE_GETSYMBOL(statePtr) fast ? FSE_decodeSymbolFast(statePtr, &bitD) : FSE_decodeSymbol(statePtr, &bitD)
|
||||
|
||||
/* 4 symbols per loop */
|
||||
for (; (BIT_reloadDStream(&bitD) == BIT_DStream_unfinished) & (op < olimit); op += 4) {
|
||||
op[0] = FSE_GETSYMBOL(&state1);
|
||||
|
||||
if (FSE_MAX_TABLELOG * 2 + 7 > sizeof(bitD.bitContainer) * 8) /* This test must be static */
|
||||
BIT_reloadDStream(&bitD);
|
||||
|
||||
op[1] = FSE_GETSYMBOL(&state2);
|
||||
|
||||
if (FSE_MAX_TABLELOG * 4 + 7 > sizeof(bitD.bitContainer) * 8) /* This test must be static */
|
||||
{
|
||||
if (BIT_reloadDStream(&bitD) > BIT_DStream_unfinished) {
|
||||
op += 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
op[2] = FSE_GETSYMBOL(&state1);
|
||||
|
||||
if (FSE_MAX_TABLELOG * 2 + 7 > sizeof(bitD.bitContainer) * 8) /* This test must be static */
|
||||
BIT_reloadDStream(&bitD);
|
||||
|
||||
op[3] = FSE_GETSYMBOL(&state2);
|
||||
}
|
||||
|
||||
/* tail */
|
||||
/* note : BIT_reloadDStream(&bitD) >= FSE_DStream_partiallyFilled; Ends at exactly BIT_DStream_completed */
|
||||
while (1) {
|
||||
if (op > (omax - 2))
|
||||
return ERROR(dstSize_tooSmall);
|
||||
*op++ = FSE_GETSYMBOL(&state1);
|
||||
if (BIT_reloadDStream(&bitD) == BIT_DStream_overflow) {
|
||||
*op++ = FSE_GETSYMBOL(&state2);
|
||||
break;
|
||||
}
|
||||
|
||||
if (op > (omax - 2))
|
||||
return ERROR(dstSize_tooSmall);
|
||||
*op++ = FSE_GETSYMBOL(&state2);
|
||||
if (BIT_reloadDStream(&bitD) == BIT_DStream_overflow) {
|
||||
*op++ = FSE_GETSYMBOL(&state1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return op - ostart;
|
||||
}
|
||||
|
||||
size_t FSE_decompress_usingDTable(void *dst, size_t originalSize, const void *cSrc, size_t cSrcSize, const FSE_DTable *dt)
|
||||
{
|
||||
const void *ptr = dt;
|
||||
const FSE_DTableHeader *DTableH = (const FSE_DTableHeader *)ptr;
|
||||
const U32 fastMode = DTableH->fastMode;
|
||||
|
||||
/* select fast mode (static) */
|
||||
if (fastMode)
|
||||
return FSE_decompress_usingDTable_generic(dst, originalSize, cSrc, cSrcSize, dt, 1);
|
||||
return FSE_decompress_usingDTable_generic(dst, originalSize, cSrc, cSrcSize, dt, 0);
|
||||
}
|
||||
|
||||
size_t FSE_decompress_wksp(void *dst, size_t dstCapacity, const void *cSrc, size_t cSrcSize, unsigned maxLog, void *workspace, size_t workspaceSize)
|
||||
{
|
||||
const BYTE *const istart = (const BYTE *)cSrc;
|
||||
const BYTE *ip = istart;
|
||||
unsigned tableLog;
|
||||
unsigned maxSymbolValue = FSE_MAX_SYMBOL_VALUE;
|
||||
size_t NCountLength;
|
||||
|
||||
FSE_DTable *dt;
|
||||
short *counting;
|
||||
size_t spaceUsed32 = 0;
|
||||
|
||||
FSE_STATIC_ASSERT(sizeof(FSE_DTable) == sizeof(U32));
|
||||
|
||||
dt = (FSE_DTable *)((U32 *)workspace + spaceUsed32);
|
||||
spaceUsed32 += FSE_DTABLE_SIZE_U32(maxLog);
|
||||
counting = (short *)((U32 *)workspace + spaceUsed32);
|
||||
spaceUsed32 += ALIGN(sizeof(short) * (FSE_MAX_SYMBOL_VALUE + 1), sizeof(U32)) >> 2;
|
||||
|
||||
if ((spaceUsed32 << 2) > workspaceSize)
|
||||
return ERROR(tableLog_tooLarge);
|
||||
workspace = (U32 *)workspace + spaceUsed32;
|
||||
workspaceSize -= (spaceUsed32 << 2);
|
||||
|
||||
/* normal FSE decoding mode */
|
||||
NCountLength = FSE_readNCount(counting, &maxSymbolValue, &tableLog, istart, cSrcSize);
|
||||
if (FSE_isError(NCountLength))
|
||||
return NCountLength;
|
||||
// if (NCountLength >= cSrcSize) return ERROR(srcSize_wrong); /* too small input size; supposed to be already checked in NCountLength, only remaining
|
||||
// case : NCountLength==cSrcSize */
|
||||
if (tableLog > maxLog)
|
||||
return ERROR(tableLog_tooLarge);
|
||||
ip += NCountLength;
|
||||
cSrcSize -= NCountLength;
|
||||
|
||||
CHECK_F(FSE_buildDTable_wksp(dt, counting, maxSymbolValue, tableLog, workspace, workspaceSize));
|
||||
|
||||
return FSE_decompress_usingDTable(dst, dstCapacity, ip, cSrcSize, dt); /* always return, even if it is an error code */
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Huffman coder, part of New Generation Entropy library
|
||||
* header file
|
||||
* Copyright (C) 2013-2016, Yann Collet.
|
||||
*
|
||||
* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License version 2 as published by the
|
||||
* Free Software Foundation. This program is dual-licensed; you may select
|
||||
* either version 2 of the GNU General Public License ("GPL") or BSD license
|
||||
* ("BSD").
|
||||
*
|
||||
* You can contact the author at :
|
||||
* - Source repository : https://github.com/Cyan4973/FiniteStateEntropy
|
||||
*/
|
||||
#ifndef HUF_H_298734234
|
||||
#define HUF_H_298734234
|
||||
|
||||
/* *** Dependencies *** */
|
||||
#include <linux/types.h> /* size_t */
|
||||
|
||||
/* *** Tool functions *** */
|
||||
#define HUF_BLOCKSIZE_MAX (128 * 1024) /**< maximum input size for a single block compressed with HUF_compress */
|
||||
size_t HUF_compressBound(size_t size); /**< maximum compressed size (worst case) */
|
||||
|
||||
/* Error Management */
|
||||
unsigned HUF_isError(size_t code); /**< tells if a return value is an error code */
|
||||
|
||||
/* *** Advanced function *** */
|
||||
|
||||
/** HUF_compress4X_wksp() :
|
||||
* Same as HUF_compress2(), but uses externally allocated `workSpace`, which must be a table of >= 1024 unsigned */
|
||||
size_t HUF_compress4X_wksp(void *dst, size_t dstSize, const void *src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void *workSpace,
|
||||
size_t wkspSize); /**< `workSpace` must be a table of at least HUF_COMPRESS_WORKSPACE_SIZE_U32 unsigned */
|
||||
|
||||
/* *** Dependencies *** */
|
||||
#include "mem.h" /* U32 */
|
||||
|
||||
/* *** Constants *** */
|
||||
#define HUF_TABLELOG_MAX 12 /* max configured tableLog (for static allocation); can be modified up to HUF_ABSOLUTEMAX_TABLELOG */
|
||||
#define HUF_TABLELOG_DEFAULT 11 /* tableLog by default, when not specified */
|
||||
#define HUF_SYMBOLVALUE_MAX 255
|
||||
|
||||
#define HUF_TABLELOG_ABSOLUTEMAX 15 /* absolute limit of HUF_MAX_TABLELOG. Beyond that value, code does not work */
|
||||
#if (HUF_TABLELOG_MAX > HUF_TABLELOG_ABSOLUTEMAX)
|
||||
#error "HUF_TABLELOG_MAX is too large !"
|
||||
#endif
|
||||
|
||||
/* ****************************************
|
||||
* Static allocation
|
||||
******************************************/
|
||||
/* HUF buffer bounds */
|
||||
#define HUF_CTABLEBOUND 129
|
||||
#define HUF_BLOCKBOUND(size) (size + (size >> 8) + 8) /* only true if incompressible pre-filtered with fast heuristic */
|
||||
#define HUF_COMPRESSBOUND(size) (HUF_CTABLEBOUND + HUF_BLOCKBOUND(size)) /* Macro version, useful for static allocation */
|
||||
|
||||
/* static allocation of HUF's Compression Table */
|
||||
#define HUF_CREATE_STATIC_CTABLE(name, maxSymbolValue) \
|
||||
U32 name##hb[maxSymbolValue + 1]; \
|
||||
void *name##hv = &(name##hb); \
|
||||
HUF_CElt *name = (HUF_CElt *)(name##hv) /* no final ; */
|
||||
|
||||
/* static allocation of HUF's DTable */
|
||||
typedef U32 HUF_DTable;
|
||||
#define HUF_DTABLE_SIZE(maxTableLog) (1 + (1 << (maxTableLog)))
|
||||
#define HUF_CREATE_STATIC_DTABLEX2(DTable, maxTableLog) HUF_DTable DTable[HUF_DTABLE_SIZE((maxTableLog)-1)] = {((U32)((maxTableLog)-1) * 0x01000001)}
|
||||
#define HUF_CREATE_STATIC_DTABLEX4(DTable, maxTableLog) HUF_DTable DTable[HUF_DTABLE_SIZE(maxTableLog)] = {((U32)(maxTableLog)*0x01000001)}
|
||||
|
||||
/* The workspace must have alignment at least 4 and be at least this large */
|
||||
#define HUF_COMPRESS_WORKSPACE_SIZE (6 << 10)
|
||||
#define HUF_COMPRESS_WORKSPACE_SIZE_U32 (HUF_COMPRESS_WORKSPACE_SIZE / sizeof(U32))
|
||||
|
||||
/* The workspace must have alignment at least 4 and be at least this large */
|
||||
#define HUF_DECOMPRESS_WORKSPACE_SIZE (3 << 10)
|
||||
#define HUF_DECOMPRESS_WORKSPACE_SIZE_U32 (HUF_DECOMPRESS_WORKSPACE_SIZE / sizeof(U32))
|
||||
|
||||
/* ****************************************
|
||||
* Advanced decompression functions
|
||||
******************************************/
|
||||
size_t HUF_decompress4X_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize); /**< decodes RLE and uncompressed */
|
||||
size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace,
|
||||
size_t workspaceSize); /**< considers RLE and uncompressed as errors */
|
||||
size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace,
|
||||
size_t workspaceSize); /**< single-symbol decoder */
|
||||
size_t HUF_decompress4X4_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace,
|
||||
size_t workspaceSize); /**< double-symbols decoder */
|
||||
|
||||
/* ****************************************
|
||||
* HUF detailed API
|
||||
******************************************/
|
||||
/*!
|
||||
HUF_compress() does the following:
|
||||
1. count symbol occurrence from source[] into table count[] using FSE_count()
|
||||
2. (optional) refine tableLog using HUF_optimalTableLog()
|
||||
3. build Huffman table from count using HUF_buildCTable()
|
||||
4. save Huffman table to memory buffer using HUF_writeCTable_wksp()
|
||||
5. encode the data stream using HUF_compress4X_usingCTable()
|
||||
|
||||
The following API allows targeting specific sub-functions for advanced tasks.
|
||||
For example, it's possible to compress several blocks using the same 'CTable',
|
||||
or to save and regenerate 'CTable' using external methods.
|
||||
*/
|
||||
/* FSE_count() : find it within "fse.h" */
|
||||
unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue);
|
||||
typedef struct HUF_CElt_s HUF_CElt; /* incomplete type */
|
||||
size_t HUF_writeCTable_wksp(void *dst, size_t maxDstSize, const HUF_CElt *CTable, unsigned maxSymbolValue, unsigned huffLog, void *workspace, size_t workspaceSize);
|
||||
size_t HUF_compress4X_usingCTable(void *dst, size_t dstSize, const void *src, size_t srcSize, const HUF_CElt *CTable);
|
||||
|
||||
typedef enum {
|
||||
HUF_repeat_none, /**< Cannot use the previous table */
|
||||
HUF_repeat_check, /**< Can use the previous table but it must be checked. Note : The previous table must have been constructed by HUF_compress{1,
|
||||
4}X_repeat */
|
||||
HUF_repeat_valid /**< Can use the previous table and it is assumed to be valid */
|
||||
} HUF_repeat;
|
||||
/** HUF_compress4X_repeat() :
|
||||
* Same as HUF_compress4X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none.
|
||||
* If it uses hufTable it does not modify hufTable or repeat.
|
||||
* If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used.
|
||||
* If preferRepeat then the old table will always be used if valid. */
|
||||
size_t HUF_compress4X_repeat(void *dst, size_t dstSize, const void *src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void *workSpace,
|
||||
size_t wkspSize, HUF_CElt *hufTable, HUF_repeat *repeat,
|
||||
int preferRepeat); /**< `workSpace` must be a table of at least HUF_COMPRESS_WORKSPACE_SIZE_U32 unsigned */
|
||||
|
||||
/** HUF_buildCTable_wksp() :
|
||||
* Same as HUF_buildCTable(), but using externally allocated scratch buffer.
|
||||
* `workSpace` must be aligned on 4-bytes boundaries, and be at least as large as a table of 1024 unsigned.
|
||||
*/
|
||||
size_t HUF_buildCTable_wksp(HUF_CElt *tree, const U32 *count, U32 maxSymbolValue, U32 maxNbBits, void *workSpace, size_t wkspSize);
|
||||
|
||||
/*! HUF_readStats() :
|
||||
Read compact Huffman tree, saved by HUF_writeCTable().
|
||||
`huffWeight` is destination buffer.
|
||||
@return : size read from `src` , or an error Code .
|
||||
Note : Needed by HUF_readCTable() and HUF_readDTableXn() . */
|
||||
size_t HUF_readStats_wksp(BYTE *huffWeight, size_t hwSize, U32 *rankStats, U32 *nbSymbolsPtr, U32 *tableLogPtr, const void *src, size_t srcSize,
|
||||
void *workspace, size_t workspaceSize);
|
||||
|
||||
/** HUF_readCTable() :
|
||||
* Loading a CTable saved with HUF_writeCTable() */
|
||||
size_t HUF_readCTable_wksp(HUF_CElt *CTable, unsigned maxSymbolValue, const void *src, size_t srcSize, void *workspace, size_t workspaceSize);
|
||||
|
||||
/*
|
||||
HUF_decompress() does the following:
|
||||
1. select the decompression algorithm (X2, X4) based on pre-computed heuristics
|
||||
2. build Huffman table from save, using HUF_readDTableXn()
|
||||
3. decode 1 or 4 segments in parallel using HUF_decompressSXn_usingDTable
|
||||
*/
|
||||
|
||||
/** HUF_selectDecoder() :
|
||||
* Tells which decoder is likely to decode faster,
|
||||
* based on a set of pre-determined metrics.
|
||||
* @return : 0==HUF_decompress4X2, 1==HUF_decompress4X4 .
|
||||
* Assumption : 0 < cSrcSize < dstSize <= 128 KB */
|
||||
U32 HUF_selectDecoder(size_t dstSize, size_t cSrcSize);
|
||||
|
||||
size_t HUF_readDTableX2_wksp(HUF_DTable *DTable, const void *src, size_t srcSize, void *workspace, size_t workspaceSize);
|
||||
size_t HUF_readDTableX4_wksp(HUF_DTable *DTable, const void *src, size_t srcSize, void *workspace, size_t workspaceSize);
|
||||
|
||||
size_t HUF_decompress4X_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable);
|
||||
size_t HUF_decompress4X2_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable);
|
||||
size_t HUF_decompress4X4_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable);
|
||||
|
||||
/* single stream variants */
|
||||
|
||||
size_t HUF_compress1X_wksp(void *dst, size_t dstSize, const void *src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void *workSpace,
|
||||
size_t wkspSize); /**< `workSpace` must be a table of at least HUF_COMPRESS_WORKSPACE_SIZE_U32 unsigned */
|
||||
size_t HUF_compress1X_usingCTable(void *dst, size_t dstSize, const void *src, size_t srcSize, const HUF_CElt *CTable);
|
||||
/** HUF_compress1X_repeat() :
|
||||
* Same as HUF_compress1X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none.
|
||||
* If it uses hufTable it does not modify hufTable or repeat.
|
||||
* If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used.
|
||||
* If preferRepeat then the old table will always be used if valid. */
|
||||
size_t HUF_compress1X_repeat(void *dst, size_t dstSize, const void *src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void *workSpace,
|
||||
size_t wkspSize, HUF_CElt *hufTable, HUF_repeat *repeat,
|
||||
int preferRepeat); /**< `workSpace` must be a table of at least HUF_COMPRESS_WORKSPACE_SIZE_U32 unsigned */
|
||||
|
||||
size_t HUF_decompress1X_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize);
|
||||
size_t HUF_decompress1X2_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace,
|
||||
size_t workspaceSize); /**< single-symbol decoder */
|
||||
size_t HUF_decompress1X4_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace,
|
||||
size_t workspaceSize); /**< double-symbols decoder */
|
||||
|
||||
size_t HUF_decompress1X_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize,
|
||||
const HUF_DTable *DTable); /**< automatic selection of sing or double symbol decoder, based on DTable */
|
||||
size_t HUF_decompress1X2_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable);
|
||||
size_t HUF_decompress1X4_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable);
|
||||
|
||||
#endif /* HUF_H_298734234 */
|
||||
@@ -0,0 +1,770 @@
|
||||
/*
|
||||
* Huffman encoder, part of New Generation Entropy library
|
||||
* Copyright (C) 2013-2016, Yann Collet.
|
||||
*
|
||||
* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License version 2 as published by the
|
||||
* Free Software Foundation. This program is dual-licensed; you may select
|
||||
* either version 2 of the GNU General Public License ("GPL") or BSD license
|
||||
* ("BSD").
|
||||
*
|
||||
* You can contact the author at :
|
||||
* - Source repository : https://github.com/Cyan4973/FiniteStateEntropy
|
||||
*/
|
||||
|
||||
/* **************************************************************
|
||||
* Includes
|
||||
****************************************************************/
|
||||
#include "bitstream.h"
|
||||
#include "fse.h" /* header compression */
|
||||
#include "huf.h"
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h> /* memcpy, memset */
|
||||
|
||||
/* **************************************************************
|
||||
* Error Management
|
||||
****************************************************************/
|
||||
#define HUF_STATIC_ASSERT(c) \
|
||||
{ \
|
||||
enum { HUF_static_assert = 1 / (int)(!!(c)) }; \
|
||||
} /* use only *after* variable declarations */
|
||||
#define CHECK_V_F(e, f) \
|
||||
size_t const e = f; \
|
||||
if (ERR_isError(e)) \
|
||||
return f
|
||||
#define CHECK_F(f) \
|
||||
{ \
|
||||
CHECK_V_F(_var_err__, f); \
|
||||
}
|
||||
|
||||
/* **************************************************************
|
||||
* Utils
|
||||
****************************************************************/
|
||||
unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue)
|
||||
{
|
||||
return FSE_optimalTableLog_internal(maxTableLog, srcSize, maxSymbolValue, 1);
|
||||
}
|
||||
|
||||
/* *******************************************************
|
||||
* HUF : Huffman block compression
|
||||
*********************************************************/
|
||||
/* HUF_compressWeights() :
|
||||
* Same as FSE_compress(), but dedicated to huff0's weights compression.
|
||||
* The use case needs much less stack memory.
|
||||
* Note : all elements within weightTable are supposed to be <= HUF_TABLELOG_MAX.
|
||||
*/
|
||||
#define MAX_FSE_TABLELOG_FOR_HUFF_HEADER 6
|
||||
size_t HUF_compressWeights_wksp(void *dst, size_t dstSize, const void *weightTable, size_t wtSize, void *workspace, size_t workspaceSize)
|
||||
{
|
||||
BYTE *const ostart = (BYTE *)dst;
|
||||
BYTE *op = ostart;
|
||||
BYTE *const oend = ostart + dstSize;
|
||||
|
||||
U32 maxSymbolValue = HUF_TABLELOG_MAX;
|
||||
U32 tableLog = MAX_FSE_TABLELOG_FOR_HUFF_HEADER;
|
||||
|
||||
FSE_CTable *CTable;
|
||||
U32 *count;
|
||||
S16 *norm;
|
||||
size_t spaceUsed32 = 0;
|
||||
|
||||
HUF_STATIC_ASSERT(sizeof(FSE_CTable) == sizeof(U32));
|
||||
|
||||
CTable = (FSE_CTable *)((U32 *)workspace + spaceUsed32);
|
||||
spaceUsed32 += FSE_CTABLE_SIZE_U32(MAX_FSE_TABLELOG_FOR_HUFF_HEADER, HUF_TABLELOG_MAX);
|
||||
count = (U32 *)workspace + spaceUsed32;
|
||||
spaceUsed32 += HUF_TABLELOG_MAX + 1;
|
||||
norm = (S16 *)((U32 *)workspace + spaceUsed32);
|
||||
spaceUsed32 += ALIGN(sizeof(S16) * (HUF_TABLELOG_MAX + 1), sizeof(U32)) >> 2;
|
||||
|
||||
if ((spaceUsed32 << 2) > workspaceSize)
|
||||
return ERROR(tableLog_tooLarge);
|
||||
workspace = (U32 *)workspace + spaceUsed32;
|
||||
workspaceSize -= (spaceUsed32 << 2);
|
||||
|
||||
/* init conditions */
|
||||
if (wtSize <= 1)
|
||||
return 0; /* Not compressible */
|
||||
|
||||
/* Scan input and build symbol stats */
|
||||
{
|
||||
CHECK_V_F(maxCount, FSE_count_simple(count, &maxSymbolValue, weightTable, wtSize));
|
||||
if (maxCount == wtSize)
|
||||
return 1; /* only a single symbol in src : rle */
|
||||
if (maxCount == 1)
|
||||
return 0; /* each symbol present maximum once => not compressible */
|
||||
}
|
||||
|
||||
tableLog = FSE_optimalTableLog(tableLog, wtSize, maxSymbolValue);
|
||||
CHECK_F(FSE_normalizeCount(norm, tableLog, count, wtSize, maxSymbolValue));
|
||||
|
||||
/* Write table description header */
|
||||
{
|
||||
CHECK_V_F(hSize, FSE_writeNCount(op, oend - op, norm, maxSymbolValue, tableLog));
|
||||
op += hSize;
|
||||
}
|
||||
|
||||
/* Compress */
|
||||
CHECK_F(FSE_buildCTable_wksp(CTable, norm, maxSymbolValue, tableLog, workspace, workspaceSize));
|
||||
{
|
||||
CHECK_V_F(cSize, FSE_compress_usingCTable(op, oend - op, weightTable, wtSize, CTable));
|
||||
if (cSize == 0)
|
||||
return 0; /* not enough space for compressed data */
|
||||
op += cSize;
|
||||
}
|
||||
|
||||
return op - ostart;
|
||||
}
|
||||
|
||||
struct HUF_CElt_s {
|
||||
U16 val;
|
||||
BYTE nbBits;
|
||||
}; /* typedef'd to HUF_CElt within "huf.h" */
|
||||
|
||||
/*! HUF_writeCTable_wksp() :
|
||||
`CTable` : Huffman tree to save, using huf representation.
|
||||
@return : size of saved CTable */
|
||||
size_t HUF_writeCTable_wksp(void *dst, size_t maxDstSize, const HUF_CElt *CTable, U32 maxSymbolValue, U32 huffLog, void *workspace, size_t workspaceSize)
|
||||
{
|
||||
BYTE *op = (BYTE *)dst;
|
||||
U32 n;
|
||||
|
||||
BYTE *bitsToWeight;
|
||||
BYTE *huffWeight;
|
||||
size_t spaceUsed32 = 0;
|
||||
|
||||
bitsToWeight = (BYTE *)((U32 *)workspace + spaceUsed32);
|
||||
spaceUsed32 += ALIGN(HUF_TABLELOG_MAX + 1, sizeof(U32)) >> 2;
|
||||
huffWeight = (BYTE *)((U32 *)workspace + spaceUsed32);
|
||||
spaceUsed32 += ALIGN(HUF_SYMBOLVALUE_MAX, sizeof(U32)) >> 2;
|
||||
|
||||
if ((spaceUsed32 << 2) > workspaceSize)
|
||||
return ERROR(tableLog_tooLarge);
|
||||
workspace = (U32 *)workspace + spaceUsed32;
|
||||
workspaceSize -= (spaceUsed32 << 2);
|
||||
|
||||
/* check conditions */
|
||||
if (maxSymbolValue > HUF_SYMBOLVALUE_MAX)
|
||||
return ERROR(maxSymbolValue_tooLarge);
|
||||
|
||||
/* convert to weight */
|
||||
bitsToWeight[0] = 0;
|
||||
for (n = 1; n < huffLog + 1; n++)
|
||||
bitsToWeight[n] = (BYTE)(huffLog + 1 - n);
|
||||
for (n = 0; n < maxSymbolValue; n++)
|
||||
huffWeight[n] = bitsToWeight[CTable[n].nbBits];
|
||||
|
||||
/* attempt weights compression by FSE */
|
||||
{
|
||||
CHECK_V_F(hSize, HUF_compressWeights_wksp(op + 1, maxDstSize - 1, huffWeight, maxSymbolValue, workspace, workspaceSize));
|
||||
if ((hSize > 1) & (hSize < maxSymbolValue / 2)) { /* FSE compressed */
|
||||
op[0] = (BYTE)hSize;
|
||||
return hSize + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* write raw values as 4-bits (max : 15) */
|
||||
if (maxSymbolValue > (256 - 128))
|
||||
return ERROR(GENERIC); /* should not happen : likely means source cannot be compressed */
|
||||
if (((maxSymbolValue + 1) / 2) + 1 > maxDstSize)
|
||||
return ERROR(dstSize_tooSmall); /* not enough space within dst buffer */
|
||||
op[0] = (BYTE)(128 /*special case*/ + (maxSymbolValue - 1));
|
||||
huffWeight[maxSymbolValue] = 0; /* to be sure it doesn't cause msan issue in final combination */
|
||||
for (n = 0; n < maxSymbolValue; n += 2)
|
||||
op[(n / 2) + 1] = (BYTE)((huffWeight[n] << 4) + huffWeight[n + 1]);
|
||||
return ((maxSymbolValue + 1) / 2) + 1;
|
||||
}
|
||||
|
||||
size_t HUF_readCTable_wksp(HUF_CElt *CTable, U32 maxSymbolValue, const void *src, size_t srcSize, void *workspace, size_t workspaceSize)
|
||||
{
|
||||
U32 *rankVal;
|
||||
BYTE *huffWeight;
|
||||
U32 tableLog = 0;
|
||||
U32 nbSymbols = 0;
|
||||
size_t readSize;
|
||||
size_t spaceUsed32 = 0;
|
||||
|
||||
rankVal = (U32 *)workspace + spaceUsed32;
|
||||
spaceUsed32 += HUF_TABLELOG_ABSOLUTEMAX + 1;
|
||||
huffWeight = (BYTE *)((U32 *)workspace + spaceUsed32);
|
||||
spaceUsed32 += ALIGN(HUF_SYMBOLVALUE_MAX + 1, sizeof(U32)) >> 2;
|
||||
|
||||
if ((spaceUsed32 << 2) > workspaceSize)
|
||||
return ERROR(tableLog_tooLarge);
|
||||
workspace = (U32 *)workspace + spaceUsed32;
|
||||
workspaceSize -= (spaceUsed32 << 2);
|
||||
|
||||
/* get symbol weights */
|
||||
readSize = HUF_readStats_wksp(huffWeight, HUF_SYMBOLVALUE_MAX + 1, rankVal, &nbSymbols, &tableLog, src, srcSize, workspace, workspaceSize);
|
||||
if (ERR_isError(readSize))
|
||||
return readSize;
|
||||
|
||||
/* check result */
|
||||
if (tableLog > HUF_TABLELOG_MAX)
|
||||
return ERROR(tableLog_tooLarge);
|
||||
if (nbSymbols > maxSymbolValue + 1)
|
||||
return ERROR(maxSymbolValue_tooSmall);
|
||||
|
||||
/* Prepare base value per rank */
|
||||
{
|
||||
U32 n, nextRankStart = 0;
|
||||
for (n = 1; n <= tableLog; n++) {
|
||||
U32 curr = nextRankStart;
|
||||
nextRankStart += (rankVal[n] << (n - 1));
|
||||
rankVal[n] = curr;
|
||||
}
|
||||
}
|
||||
|
||||
/* fill nbBits */
|
||||
{
|
||||
U32 n;
|
||||
for (n = 0; n < nbSymbols; n++) {
|
||||
const U32 w = huffWeight[n];
|
||||
CTable[n].nbBits = (BYTE)(tableLog + 1 - w);
|
||||
}
|
||||
}
|
||||
|
||||
/* fill val */
|
||||
{
|
||||
U16 nbPerRank[HUF_TABLELOG_MAX + 2] = {0}; /* support w=0=>n=tableLog+1 */
|
||||
U16 valPerRank[HUF_TABLELOG_MAX + 2] = {0};
|
||||
{
|
||||
U32 n;
|
||||
for (n = 0; n < nbSymbols; n++)
|
||||
nbPerRank[CTable[n].nbBits]++;
|
||||
}
|
||||
/* determine stating value per rank */
|
||||
valPerRank[tableLog + 1] = 0; /* for w==0 */
|
||||
{
|
||||
U16 min = 0;
|
||||
U32 n;
|
||||
for (n = tableLog; n > 0; n--) { /* start at n=tablelog <-> w=1 */
|
||||
valPerRank[n] = min; /* get starting value within each rank */
|
||||
min += nbPerRank[n];
|
||||
min >>= 1;
|
||||
}
|
||||
}
|
||||
/* assign value within rank, symbol order */
|
||||
{
|
||||
U32 n;
|
||||
for (n = 0; n <= maxSymbolValue; n++)
|
||||
CTable[n].val = valPerRank[CTable[n].nbBits]++;
|
||||
}
|
||||
}
|
||||
|
||||
return readSize;
|
||||
}
|
||||
|
||||
typedef struct nodeElt_s {
|
||||
U32 count;
|
||||
U16 parent;
|
||||
BYTE byte;
|
||||
BYTE nbBits;
|
||||
} nodeElt;
|
||||
|
||||
static U32 HUF_setMaxHeight(nodeElt *huffNode, U32 lastNonNull, U32 maxNbBits)
|
||||
{
|
||||
const U32 largestBits = huffNode[lastNonNull].nbBits;
|
||||
if (largestBits <= maxNbBits)
|
||||
return largestBits; /* early exit : no elt > maxNbBits */
|
||||
|
||||
/* there are several too large elements (at least >= 2) */
|
||||
{
|
||||
int totalCost = 0;
|
||||
const U32 baseCost = 1 << (largestBits - maxNbBits);
|
||||
U32 n = lastNonNull;
|
||||
|
||||
while (huffNode[n].nbBits > maxNbBits) {
|
||||
totalCost += baseCost - (1 << (largestBits - huffNode[n].nbBits));
|
||||
huffNode[n].nbBits = (BYTE)maxNbBits;
|
||||
n--;
|
||||
} /* n stops at huffNode[n].nbBits <= maxNbBits */
|
||||
while (huffNode[n].nbBits == maxNbBits)
|
||||
n--; /* n end at index of smallest symbol using < maxNbBits */
|
||||
|
||||
/* renorm totalCost */
|
||||
totalCost >>= (largestBits - maxNbBits); /* note : totalCost is necessarily a multiple of baseCost */
|
||||
|
||||
/* repay normalized cost */
|
||||
{
|
||||
U32 const noSymbol = 0xF0F0F0F0;
|
||||
U32 rankLast[HUF_TABLELOG_MAX + 2];
|
||||
int pos;
|
||||
|
||||
/* Get pos of last (smallest) symbol per rank */
|
||||
memset(rankLast, 0xF0, sizeof(rankLast));
|
||||
{
|
||||
U32 currNbBits = maxNbBits;
|
||||
for (pos = n; pos >= 0; pos--) {
|
||||
if (huffNode[pos].nbBits >= currNbBits)
|
||||
continue;
|
||||
currNbBits = huffNode[pos].nbBits; /* < maxNbBits */
|
||||
rankLast[maxNbBits - currNbBits] = pos;
|
||||
}
|
||||
}
|
||||
|
||||
while (totalCost > 0) {
|
||||
U32 nBitsToDecrease = BIT_highbit32(totalCost) + 1;
|
||||
for (; nBitsToDecrease > 1; nBitsToDecrease--) {
|
||||
U32 highPos = rankLast[nBitsToDecrease];
|
||||
U32 lowPos = rankLast[nBitsToDecrease - 1];
|
||||
if (highPos == noSymbol)
|
||||
continue;
|
||||
if (lowPos == noSymbol)
|
||||
break;
|
||||
{
|
||||
U32 const highTotal = huffNode[highPos].count;
|
||||
U32 const lowTotal = 2 * huffNode[lowPos].count;
|
||||
if (highTotal <= lowTotal)
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* only triggered when no more rank 1 symbol left => find closest one (note : there is necessarily at least one !) */
|
||||
/* HUF_MAX_TABLELOG test just to please gcc 5+; but it should not be necessary */
|
||||
while ((nBitsToDecrease <= HUF_TABLELOG_MAX) && (rankLast[nBitsToDecrease] == noSymbol))
|
||||
nBitsToDecrease++;
|
||||
totalCost -= 1 << (nBitsToDecrease - 1);
|
||||
if (rankLast[nBitsToDecrease - 1] == noSymbol)
|
||||
rankLast[nBitsToDecrease - 1] = rankLast[nBitsToDecrease]; /* this rank is no longer empty */
|
||||
huffNode[rankLast[nBitsToDecrease]].nbBits++;
|
||||
if (rankLast[nBitsToDecrease] == 0) /* special case, reached largest symbol */
|
||||
rankLast[nBitsToDecrease] = noSymbol;
|
||||
else {
|
||||
rankLast[nBitsToDecrease]--;
|
||||
if (huffNode[rankLast[nBitsToDecrease]].nbBits != maxNbBits - nBitsToDecrease)
|
||||
rankLast[nBitsToDecrease] = noSymbol; /* this rank is now empty */
|
||||
}
|
||||
} /* while (totalCost > 0) */
|
||||
|
||||
while (totalCost < 0) { /* Sometimes, cost correction overshoot */
|
||||
if (rankLast[1] == noSymbol) { /* special case : no rank 1 symbol (using maxNbBits-1); let's create one from largest rank 0
|
||||
(using maxNbBits) */
|
||||
while (huffNode[n].nbBits == maxNbBits)
|
||||
n--;
|
||||
huffNode[n + 1].nbBits--;
|
||||
rankLast[1] = n + 1;
|
||||
totalCost++;
|
||||
continue;
|
||||
}
|
||||
huffNode[rankLast[1] + 1].nbBits--;
|
||||
rankLast[1]++;
|
||||
totalCost++;
|
||||
}
|
||||
}
|
||||
} /* there are several too large elements (at least >= 2) */
|
||||
|
||||
return maxNbBits;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
U32 base;
|
||||
U32 curr;
|
||||
} rankPos;
|
||||
|
||||
static void HUF_sort(nodeElt *huffNode, const U32 *count, U32 maxSymbolValue)
|
||||
{
|
||||
rankPos rank[32];
|
||||
U32 n;
|
||||
|
||||
memset(rank, 0, sizeof(rank));
|
||||
for (n = 0; n <= maxSymbolValue; n++) {
|
||||
U32 r = BIT_highbit32(count[n] + 1);
|
||||
rank[r].base++;
|
||||
}
|
||||
for (n = 30; n > 0; n--)
|
||||
rank[n - 1].base += rank[n].base;
|
||||
for (n = 0; n < 32; n++)
|
||||
rank[n].curr = rank[n].base;
|
||||
for (n = 0; n <= maxSymbolValue; n++) {
|
||||
U32 const c = count[n];
|
||||
U32 const r = BIT_highbit32(c + 1) + 1;
|
||||
U32 pos = rank[r].curr++;
|
||||
while ((pos > rank[r].base) && (c > huffNode[pos - 1].count))
|
||||
huffNode[pos] = huffNode[pos - 1], pos--;
|
||||
huffNode[pos].count = c;
|
||||
huffNode[pos].byte = (BYTE)n;
|
||||
}
|
||||
}
|
||||
|
||||
/** HUF_buildCTable_wksp() :
|
||||
* Same as HUF_buildCTable(), but using externally allocated scratch buffer.
|
||||
* `workSpace` must be aligned on 4-bytes boundaries, and be at least as large as a table of 1024 unsigned.
|
||||
*/
|
||||
#define STARTNODE (HUF_SYMBOLVALUE_MAX + 1)
|
||||
typedef nodeElt huffNodeTable[2 * HUF_SYMBOLVALUE_MAX + 1 + 1];
|
||||
size_t HUF_buildCTable_wksp(HUF_CElt *tree, const U32 *count, U32 maxSymbolValue, U32 maxNbBits, void *workSpace, size_t wkspSize)
|
||||
{
|
||||
nodeElt *const huffNode0 = (nodeElt *)workSpace;
|
||||
nodeElt *const huffNode = huffNode0 + 1;
|
||||
U32 n, nonNullRank;
|
||||
int lowS, lowN;
|
||||
U16 nodeNb = STARTNODE;
|
||||
U32 nodeRoot;
|
||||
|
||||
/* safety checks */
|
||||
if (wkspSize < sizeof(huffNodeTable))
|
||||
return ERROR(GENERIC); /* workSpace is not large enough */
|
||||
if (maxNbBits == 0)
|
||||
maxNbBits = HUF_TABLELOG_DEFAULT;
|
||||
if (maxSymbolValue > HUF_SYMBOLVALUE_MAX)
|
||||
return ERROR(GENERIC);
|
||||
memset(huffNode0, 0, sizeof(huffNodeTable));
|
||||
|
||||
/* sort, decreasing order */
|
||||
HUF_sort(huffNode, count, maxSymbolValue);
|
||||
|
||||
/* init for parents */
|
||||
nonNullRank = maxSymbolValue;
|
||||
while (huffNode[nonNullRank].count == 0)
|
||||
nonNullRank--;
|
||||
lowS = nonNullRank;
|
||||
nodeRoot = nodeNb + lowS - 1;
|
||||
lowN = nodeNb;
|
||||
huffNode[nodeNb].count = huffNode[lowS].count + huffNode[lowS - 1].count;
|
||||
huffNode[lowS].parent = huffNode[lowS - 1].parent = nodeNb;
|
||||
nodeNb++;
|
||||
lowS -= 2;
|
||||
for (n = nodeNb; n <= nodeRoot; n++)
|
||||
huffNode[n].count = (U32)(1U << 30);
|
||||
huffNode0[0].count = (U32)(1U << 31); /* fake entry, strong barrier */
|
||||
|
||||
/* create parents */
|
||||
while (nodeNb <= nodeRoot) {
|
||||
U32 n1 = (huffNode[lowS].count < huffNode[lowN].count) ? lowS-- : lowN++;
|
||||
U32 n2 = (huffNode[lowS].count < huffNode[lowN].count) ? lowS-- : lowN++;
|
||||
huffNode[nodeNb].count = huffNode[n1].count + huffNode[n2].count;
|
||||
huffNode[n1].parent = huffNode[n2].parent = nodeNb;
|
||||
nodeNb++;
|
||||
}
|
||||
|
||||
/* distribute weights (unlimited tree height) */
|
||||
huffNode[nodeRoot].nbBits = 0;
|
||||
for (n = nodeRoot - 1; n >= STARTNODE; n--)
|
||||
huffNode[n].nbBits = huffNode[huffNode[n].parent].nbBits + 1;
|
||||
for (n = 0; n <= nonNullRank; n++)
|
||||
huffNode[n].nbBits = huffNode[huffNode[n].parent].nbBits + 1;
|
||||
|
||||
/* enforce maxTableLog */
|
||||
maxNbBits = HUF_setMaxHeight(huffNode, nonNullRank, maxNbBits);
|
||||
|
||||
/* fill result into tree (val, nbBits) */
|
||||
{
|
||||
U16 nbPerRank[HUF_TABLELOG_MAX + 1] = {0};
|
||||
U16 valPerRank[HUF_TABLELOG_MAX + 1] = {0};
|
||||
if (maxNbBits > HUF_TABLELOG_MAX)
|
||||
return ERROR(GENERIC); /* check fit into table */
|
||||
for (n = 0; n <= nonNullRank; n++)
|
||||
nbPerRank[huffNode[n].nbBits]++;
|
||||
/* determine stating value per rank */
|
||||
{
|
||||
U16 min = 0;
|
||||
for (n = maxNbBits; n > 0; n--) {
|
||||
valPerRank[n] = min; /* get starting value within each rank */
|
||||
min += nbPerRank[n];
|
||||
min >>= 1;
|
||||
}
|
||||
}
|
||||
for (n = 0; n <= maxSymbolValue; n++)
|
||||
tree[huffNode[n].byte].nbBits = huffNode[n].nbBits; /* push nbBits per symbol, symbol order */
|
||||
for (n = 0; n <= maxSymbolValue; n++)
|
||||
tree[n].val = valPerRank[tree[n].nbBits]++; /* assign value within rank, symbol order */
|
||||
}
|
||||
|
||||
return maxNbBits;
|
||||
}
|
||||
|
||||
static size_t HUF_estimateCompressedSize(HUF_CElt *CTable, const unsigned *count, unsigned maxSymbolValue)
|
||||
{
|
||||
size_t nbBits = 0;
|
||||
int s;
|
||||
for (s = 0; s <= (int)maxSymbolValue; ++s) {
|
||||
nbBits += CTable[s].nbBits * count[s];
|
||||
}
|
||||
return nbBits >> 3;
|
||||
}
|
||||
|
||||
static int HUF_validateCTable(const HUF_CElt *CTable, const unsigned *count, unsigned maxSymbolValue)
|
||||
{
|
||||
int bad = 0;
|
||||
int s;
|
||||
for (s = 0; s <= (int)maxSymbolValue; ++s) {
|
||||
bad |= (count[s] != 0) & (CTable[s].nbBits == 0);
|
||||
}
|
||||
return !bad;
|
||||
}
|
||||
|
||||
static void HUF_encodeSymbol(BIT_CStream_t *bitCPtr, U32 symbol, const HUF_CElt *CTable)
|
||||
{
|
||||
BIT_addBitsFast(bitCPtr, CTable[symbol].val, CTable[symbol].nbBits);
|
||||
}
|
||||
|
||||
size_t HUF_compressBound(size_t size) { return HUF_COMPRESSBOUND(size); }
|
||||
|
||||
#define HUF_FLUSHBITS(s) BIT_flushBits(s)
|
||||
|
||||
#define HUF_FLUSHBITS_1(stream) \
|
||||
if (sizeof((stream)->bitContainer) * 8 < HUF_TABLELOG_MAX * 2 + 7) \
|
||||
HUF_FLUSHBITS(stream)
|
||||
|
||||
#define HUF_FLUSHBITS_2(stream) \
|
||||
if (sizeof((stream)->bitContainer) * 8 < HUF_TABLELOG_MAX * 4 + 7) \
|
||||
HUF_FLUSHBITS(stream)
|
||||
|
||||
size_t HUF_compress1X_usingCTable(void *dst, size_t dstSize, const void *src, size_t srcSize, const HUF_CElt *CTable)
|
||||
{
|
||||
const BYTE *ip = (const BYTE *)src;
|
||||
BYTE *const ostart = (BYTE *)dst;
|
||||
BYTE *const oend = ostart + dstSize;
|
||||
BYTE *op = ostart;
|
||||
size_t n;
|
||||
BIT_CStream_t bitC;
|
||||
|
||||
/* init */
|
||||
if (dstSize < 8)
|
||||
return 0; /* not enough space to compress */
|
||||
{
|
||||
size_t const initErr = BIT_initCStream(&bitC, op, oend - op);
|
||||
if (HUF_isError(initErr))
|
||||
return 0;
|
||||
}
|
||||
|
||||
n = srcSize & ~3; /* join to mod 4 */
|
||||
switch (srcSize & 3) {
|
||||
case 3: HUF_encodeSymbol(&bitC, ip[n + 2], CTable); HUF_FLUSHBITS_2(&bitC);
|
||||
case 2: HUF_encodeSymbol(&bitC, ip[n + 1], CTable); HUF_FLUSHBITS_1(&bitC);
|
||||
case 1: HUF_encodeSymbol(&bitC, ip[n + 0], CTable); HUF_FLUSHBITS(&bitC);
|
||||
case 0:
|
||||
default:;
|
||||
}
|
||||
|
||||
for (; n > 0; n -= 4) { /* note : n&3==0 at this stage */
|
||||
HUF_encodeSymbol(&bitC, ip[n - 1], CTable);
|
||||
HUF_FLUSHBITS_1(&bitC);
|
||||
HUF_encodeSymbol(&bitC, ip[n - 2], CTable);
|
||||
HUF_FLUSHBITS_2(&bitC);
|
||||
HUF_encodeSymbol(&bitC, ip[n - 3], CTable);
|
||||
HUF_FLUSHBITS_1(&bitC);
|
||||
HUF_encodeSymbol(&bitC, ip[n - 4], CTable);
|
||||
HUF_FLUSHBITS(&bitC);
|
||||
}
|
||||
|
||||
return BIT_closeCStream(&bitC);
|
||||
}
|
||||
|
||||
size_t HUF_compress4X_usingCTable(void *dst, size_t dstSize, const void *src, size_t srcSize, const HUF_CElt *CTable)
|
||||
{
|
||||
size_t const segmentSize = (srcSize + 3) / 4; /* first 3 segments */
|
||||
const BYTE *ip = (const BYTE *)src;
|
||||
const BYTE *const iend = ip + srcSize;
|
||||
BYTE *const ostart = (BYTE *)dst;
|
||||
BYTE *const oend = ostart + dstSize;
|
||||
BYTE *op = ostart;
|
||||
|
||||
if (dstSize < 6 + 1 + 1 + 1 + 8)
|
||||
return 0; /* minimum space to compress successfully */
|
||||
if (srcSize < 12)
|
||||
return 0; /* no saving possible : too small input */
|
||||
op += 6; /* jumpTable */
|
||||
|
||||
{
|
||||
CHECK_V_F(cSize, HUF_compress1X_usingCTable(op, oend - op, ip, segmentSize, CTable));
|
||||
if (cSize == 0)
|
||||
return 0;
|
||||
ZSTD_writeLE16(ostart, (U16)cSize);
|
||||
op += cSize;
|
||||
}
|
||||
|
||||
ip += segmentSize;
|
||||
{
|
||||
CHECK_V_F(cSize, HUF_compress1X_usingCTable(op, oend - op, ip, segmentSize, CTable));
|
||||
if (cSize == 0)
|
||||
return 0;
|
||||
ZSTD_writeLE16(ostart + 2, (U16)cSize);
|
||||
op += cSize;
|
||||
}
|
||||
|
||||
ip += segmentSize;
|
||||
{
|
||||
CHECK_V_F(cSize, HUF_compress1X_usingCTable(op, oend - op, ip, segmentSize, CTable));
|
||||
if (cSize == 0)
|
||||
return 0;
|
||||
ZSTD_writeLE16(ostart + 4, (U16)cSize);
|
||||
op += cSize;
|
||||
}
|
||||
|
||||
ip += segmentSize;
|
||||
{
|
||||
CHECK_V_F(cSize, HUF_compress1X_usingCTable(op, oend - op, ip, iend - ip, CTable));
|
||||
if (cSize == 0)
|
||||
return 0;
|
||||
op += cSize;
|
||||
}
|
||||
|
||||
return op - ostart;
|
||||
}
|
||||
|
||||
static size_t HUF_compressCTable_internal(BYTE *const ostart, BYTE *op, BYTE *const oend, const void *src, size_t srcSize, unsigned singleStream,
|
||||
const HUF_CElt *CTable)
|
||||
{
|
||||
size_t const cSize =
|
||||
singleStream ? HUF_compress1X_usingCTable(op, oend - op, src, srcSize, CTable) : HUF_compress4X_usingCTable(op, oend - op, src, srcSize, CTable);
|
||||
if (HUF_isError(cSize)) {
|
||||
return cSize;
|
||||
}
|
||||
if (cSize == 0) {
|
||||
return 0;
|
||||
} /* uncompressible */
|
||||
op += cSize;
|
||||
/* check compressibility */
|
||||
if ((size_t)(op - ostart) >= srcSize - 1) {
|
||||
return 0;
|
||||
}
|
||||
return op - ostart;
|
||||
}
|
||||
|
||||
/* `workSpace` must a table of at least 1024 unsigned */
|
||||
static size_t HUF_compress_internal(void *dst, size_t dstSize, const void *src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog,
|
||||
unsigned singleStream, void *workSpace, size_t wkspSize, HUF_CElt *oldHufTable, HUF_repeat *repeat, int preferRepeat)
|
||||
{
|
||||
BYTE *const ostart = (BYTE *)dst;
|
||||
BYTE *const oend = ostart + dstSize;
|
||||
BYTE *op = ostart;
|
||||
|
||||
U32 *count;
|
||||
size_t const countSize = sizeof(U32) * (HUF_SYMBOLVALUE_MAX + 1);
|
||||
HUF_CElt *CTable;
|
||||
size_t const CTableSize = sizeof(HUF_CElt) * (HUF_SYMBOLVALUE_MAX + 1);
|
||||
|
||||
/* checks & inits */
|
||||
if (wkspSize < sizeof(huffNodeTable) + countSize + CTableSize)
|
||||
return ERROR(GENERIC);
|
||||
if (!srcSize)
|
||||
return 0; /* Uncompressed (note : 1 means rle, so first byte must be correct) */
|
||||
if (!dstSize)
|
||||
return 0; /* cannot fit within dst budget */
|
||||
if (srcSize > HUF_BLOCKSIZE_MAX)
|
||||
return ERROR(srcSize_wrong); /* curr block size limit */
|
||||
if (huffLog > HUF_TABLELOG_MAX)
|
||||
return ERROR(tableLog_tooLarge);
|
||||
if (!maxSymbolValue)
|
||||
maxSymbolValue = HUF_SYMBOLVALUE_MAX;
|
||||
if (!huffLog)
|
||||
huffLog = HUF_TABLELOG_DEFAULT;
|
||||
|
||||
count = (U32 *)workSpace;
|
||||
workSpace = (BYTE *)workSpace + countSize;
|
||||
wkspSize -= countSize;
|
||||
CTable = (HUF_CElt *)workSpace;
|
||||
workSpace = (BYTE *)workSpace + CTableSize;
|
||||
wkspSize -= CTableSize;
|
||||
|
||||
/* Heuristic : If we don't need to check the validity of the old table use the old table for small inputs */
|
||||
if (preferRepeat && repeat && *repeat == HUF_repeat_valid) {
|
||||
return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, singleStream, oldHufTable);
|
||||
}
|
||||
|
||||
/* Scan input and build symbol stats */
|
||||
{
|
||||
CHECK_V_F(largest, FSE_count_wksp(count, &maxSymbolValue, (const BYTE *)src, srcSize, (U32 *)workSpace));
|
||||
if (largest == srcSize) {
|
||||
*ostart = ((const BYTE *)src)[0];
|
||||
return 1;
|
||||
} /* single symbol, rle */
|
||||
if (largest <= (srcSize >> 7) + 1)
|
||||
return 0; /* Fast heuristic : not compressible enough */
|
||||
}
|
||||
|
||||
/* Check validity of previous table */
|
||||
if (repeat && *repeat == HUF_repeat_check && !HUF_validateCTable(oldHufTable, count, maxSymbolValue)) {
|
||||
*repeat = HUF_repeat_none;
|
||||
}
|
||||
/* Heuristic : use existing table for small inputs */
|
||||
if (preferRepeat && repeat && *repeat != HUF_repeat_none) {
|
||||
return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, singleStream, oldHufTable);
|
||||
}
|
||||
|
||||
/* Build Huffman Tree */
|
||||
huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue);
|
||||
{
|
||||
CHECK_V_F(maxBits, HUF_buildCTable_wksp(CTable, count, maxSymbolValue, huffLog, workSpace, wkspSize));
|
||||
huffLog = (U32)maxBits;
|
||||
/* Zero the unused symbols so we can check it for validity */
|
||||
memset(CTable + maxSymbolValue + 1, 0, CTableSize - (maxSymbolValue + 1) * sizeof(HUF_CElt));
|
||||
}
|
||||
|
||||
/* Write table description header */
|
||||
{
|
||||
CHECK_V_F(hSize, HUF_writeCTable_wksp(op, dstSize, CTable, maxSymbolValue, huffLog, workSpace, wkspSize));
|
||||
/* Check if using the previous table will be beneficial */
|
||||
if (repeat && *repeat != HUF_repeat_none) {
|
||||
size_t const oldSize = HUF_estimateCompressedSize(oldHufTable, count, maxSymbolValue);
|
||||
size_t const newSize = HUF_estimateCompressedSize(CTable, count, maxSymbolValue);
|
||||
if (oldSize <= hSize + newSize || hSize + 12 >= srcSize) {
|
||||
return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, singleStream, oldHufTable);
|
||||
}
|
||||
}
|
||||
/* Use the new table */
|
||||
if (hSize + 12ul >= srcSize) {
|
||||
return 0;
|
||||
}
|
||||
op += hSize;
|
||||
if (repeat) {
|
||||
*repeat = HUF_repeat_none;
|
||||
}
|
||||
if (oldHufTable) {
|
||||
memcpy(oldHufTable, CTable, CTableSize);
|
||||
} /* Save the new table */
|
||||
}
|
||||
return HUF_compressCTable_internal(ostart, op, oend, src, srcSize, singleStream, CTable);
|
||||
}
|
||||
|
||||
size_t HUF_compress1X_wksp(void *dst, size_t dstSize, const void *src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog, void *workSpace,
|
||||
size_t wkspSize)
|
||||
{
|
||||
return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 1 /* single stream */, workSpace, wkspSize, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
size_t HUF_compress1X_repeat(void *dst, size_t dstSize, const void *src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog, void *workSpace,
|
||||
size_t wkspSize, HUF_CElt *hufTable, HUF_repeat *repeat, int preferRepeat)
|
||||
{
|
||||
return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 1 /* single stream */, workSpace, wkspSize, hufTable, repeat,
|
||||
preferRepeat);
|
||||
}
|
||||
|
||||
size_t HUF_compress4X_wksp(void *dst, size_t dstSize, const void *src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog, void *workSpace,
|
||||
size_t wkspSize)
|
||||
{
|
||||
return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 0 /* 4 streams */, workSpace, wkspSize, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
size_t HUF_compress4X_repeat(void *dst, size_t dstSize, const void *src, size_t srcSize, unsigned maxSymbolValue, unsigned huffLog, void *workSpace,
|
||||
size_t wkspSize, HUF_CElt *hufTable, HUF_repeat *repeat, int preferRepeat)
|
||||
{
|
||||
return HUF_compress_internal(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, 0 /* 4 streams */, workSpace, wkspSize, hufTable, repeat,
|
||||
preferRepeat);
|
||||
}
|
||||
@@ -0,0 +1,960 @@
|
||||
/*
|
||||
* Huffman decoder, part of New Generation Entropy library
|
||||
* Copyright (C) 2013-2016, Yann Collet.
|
||||
*
|
||||
* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License version 2 as published by the
|
||||
* Free Software Foundation. This program is dual-licensed; you may select
|
||||
* either version 2 of the GNU General Public License ("GPL") or BSD license
|
||||
* ("BSD").
|
||||
*
|
||||
* You can contact the author at :
|
||||
* - Source repository : https://github.com/Cyan4973/FiniteStateEntropy
|
||||
*/
|
||||
|
||||
/* **************************************************************
|
||||
* Compiler specifics
|
||||
****************************************************************/
|
||||
#define FORCE_INLINE static __always_inline
|
||||
|
||||
/* **************************************************************
|
||||
* Dependencies
|
||||
****************************************************************/
|
||||
#include "bitstream.h" /* BIT_* */
|
||||
#include "fse.h" /* header compression */
|
||||
#include "huf.h"
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h> /* memcpy, memset */
|
||||
|
||||
/* **************************************************************
|
||||
* Error Management
|
||||
****************************************************************/
|
||||
#define HUF_STATIC_ASSERT(c) \
|
||||
{ \
|
||||
enum { HUF_static_assert = 1 / (int)(!!(c)) }; \
|
||||
} /* use only *after* variable declarations */
|
||||
|
||||
/*-***************************/
|
||||
/* generic DTableDesc */
|
||||
/*-***************************/
|
||||
|
||||
typedef struct {
|
||||
BYTE maxTableLog;
|
||||
BYTE tableType;
|
||||
BYTE tableLog;
|
||||
BYTE reserved;
|
||||
} DTableDesc;
|
||||
|
||||
static DTableDesc HUF_getDTableDesc(const HUF_DTable *table)
|
||||
{
|
||||
DTableDesc dtd;
|
||||
memcpy(&dtd, table, sizeof(dtd));
|
||||
return dtd;
|
||||
}
|
||||
|
||||
/*-***************************/
|
||||
/* single-symbol decoding */
|
||||
/*-***************************/
|
||||
|
||||
typedef struct {
|
||||
BYTE byte;
|
||||
BYTE nbBits;
|
||||
} HUF_DEltX2; /* single-symbol decoding */
|
||||
|
||||
size_t HUF_readDTableX2_wksp(HUF_DTable *DTable, const void *src, size_t srcSize, void *workspace, size_t workspaceSize)
|
||||
{
|
||||
U32 tableLog = 0;
|
||||
U32 nbSymbols = 0;
|
||||
size_t iSize;
|
||||
void *const dtPtr = DTable + 1;
|
||||
HUF_DEltX2 *const dt = (HUF_DEltX2 *)dtPtr;
|
||||
|
||||
U32 *rankVal;
|
||||
BYTE *huffWeight;
|
||||
size_t spaceUsed32 = 0;
|
||||
|
||||
rankVal = (U32 *)workspace + spaceUsed32;
|
||||
spaceUsed32 += HUF_TABLELOG_ABSOLUTEMAX + 1;
|
||||
huffWeight = (BYTE *)((U32 *)workspace + spaceUsed32);
|
||||
spaceUsed32 += ALIGN(HUF_SYMBOLVALUE_MAX + 1, sizeof(U32)) >> 2;
|
||||
|
||||
if ((spaceUsed32 << 2) > workspaceSize)
|
||||
return ERROR(tableLog_tooLarge);
|
||||
workspace = (U32 *)workspace + spaceUsed32;
|
||||
workspaceSize -= (spaceUsed32 << 2);
|
||||
|
||||
HUF_STATIC_ASSERT(sizeof(DTableDesc) == sizeof(HUF_DTable));
|
||||
/* memset(huffWeight, 0, sizeof(huffWeight)); */ /* is not necessary, even though some analyzer complain ... */
|
||||
|
||||
iSize = HUF_readStats_wksp(huffWeight, HUF_SYMBOLVALUE_MAX + 1, rankVal, &nbSymbols, &tableLog, src, srcSize, workspace, workspaceSize);
|
||||
if (HUF_isError(iSize))
|
||||
return iSize;
|
||||
|
||||
/* Table header */
|
||||
{
|
||||
DTableDesc dtd = HUF_getDTableDesc(DTable);
|
||||
if (tableLog > (U32)(dtd.maxTableLog + 1))
|
||||
return ERROR(tableLog_tooLarge); /* DTable too small, Huffman tree cannot fit in */
|
||||
dtd.tableType = 0;
|
||||
dtd.tableLog = (BYTE)tableLog;
|
||||
memcpy(DTable, &dtd, sizeof(dtd));
|
||||
}
|
||||
|
||||
/* Calculate starting value for each rank */
|
||||
{
|
||||
U32 n, nextRankStart = 0;
|
||||
for (n = 1; n < tableLog + 1; n++) {
|
||||
U32 const curr = nextRankStart;
|
||||
nextRankStart += (rankVal[n] << (n - 1));
|
||||
rankVal[n] = curr;
|
||||
}
|
||||
}
|
||||
|
||||
/* fill DTable */
|
||||
{
|
||||
U32 n;
|
||||
for (n = 0; n < nbSymbols; n++) {
|
||||
U32 const w = huffWeight[n];
|
||||
U32 const length = (1 << w) >> 1;
|
||||
U32 u;
|
||||
HUF_DEltX2 D;
|
||||
D.byte = (BYTE)n;
|
||||
D.nbBits = (BYTE)(tableLog + 1 - w);
|
||||
for (u = rankVal[w]; u < rankVal[w] + length; u++)
|
||||
dt[u] = D;
|
||||
rankVal[w] += length;
|
||||
}
|
||||
}
|
||||
|
||||
return iSize;
|
||||
}
|
||||
|
||||
static BYTE HUF_decodeSymbolX2(BIT_DStream_t *Dstream, const HUF_DEltX2 *dt, const U32 dtLog)
|
||||
{
|
||||
size_t const val = BIT_lookBitsFast(Dstream, dtLog); /* note : dtLog >= 1 */
|
||||
BYTE const c = dt[val].byte;
|
||||
BIT_skipBits(Dstream, dt[val].nbBits);
|
||||
return c;
|
||||
}
|
||||
|
||||
#define HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) *ptr++ = HUF_decodeSymbolX2(DStreamPtr, dt, dtLog)
|
||||
|
||||
#define HUF_DECODE_SYMBOLX2_1(ptr, DStreamPtr) \
|
||||
if (ZSTD_64bits() || (HUF_TABLELOG_MAX <= 12)) \
|
||||
HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr)
|
||||
|
||||
#define HUF_DECODE_SYMBOLX2_2(ptr, DStreamPtr) \
|
||||
if (ZSTD_64bits()) \
|
||||
HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr)
|
||||
|
||||
FORCE_INLINE size_t HUF_decodeStreamX2(BYTE *p, BIT_DStream_t *const bitDPtr, BYTE *const pEnd, const HUF_DEltX2 *const dt, const U32 dtLog)
|
||||
{
|
||||
BYTE *const pStart = p;
|
||||
|
||||
/* up to 4 symbols at a time */
|
||||
while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) && (p <= pEnd - 4)) {
|
||||
HUF_DECODE_SYMBOLX2_2(p, bitDPtr);
|
||||
HUF_DECODE_SYMBOLX2_1(p, bitDPtr);
|
||||
HUF_DECODE_SYMBOLX2_2(p, bitDPtr);
|
||||
HUF_DECODE_SYMBOLX2_0(p, bitDPtr);
|
||||
}
|
||||
|
||||
/* closer to the end */
|
||||
while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) && (p < pEnd))
|
||||
HUF_DECODE_SYMBOLX2_0(p, bitDPtr);
|
||||
|
||||
/* no more data to retrieve from bitstream, hence no need to reload */
|
||||
while (p < pEnd)
|
||||
HUF_DECODE_SYMBOLX2_0(p, bitDPtr);
|
||||
|
||||
return pEnd - pStart;
|
||||
}
|
||||
|
||||
static size_t HUF_decompress1X2_usingDTable_internal(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable)
|
||||
{
|
||||
BYTE *op = (BYTE *)dst;
|
||||
BYTE *const oend = op + dstSize;
|
||||
const void *dtPtr = DTable + 1;
|
||||
const HUF_DEltX2 *const dt = (const HUF_DEltX2 *)dtPtr;
|
||||
BIT_DStream_t bitD;
|
||||
DTableDesc const dtd = HUF_getDTableDesc(DTable);
|
||||
U32 const dtLog = dtd.tableLog;
|
||||
|
||||
{
|
||||
size_t const errorCode = BIT_initDStream(&bitD, cSrc, cSrcSize);
|
||||
if (HUF_isError(errorCode))
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
HUF_decodeStreamX2(op, &bitD, oend, dt, dtLog);
|
||||
|
||||
/* check */
|
||||
if (!BIT_endOfDStream(&bitD))
|
||||
return ERROR(corruption_detected);
|
||||
|
||||
return dstSize;
|
||||
}
|
||||
|
||||
size_t HUF_decompress1X2_usingDTable(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable)
|
||||
{
|
||||
DTableDesc dtd = HUF_getDTableDesc(DTable);
|
||||
if (dtd.tableType != 0)
|
||||
return ERROR(GENERIC);
|
||||
return HUF_decompress1X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable);
|
||||
}
|
||||
|
||||
size_t HUF_decompress1X2_DCtx_wksp(HUF_DTable *DCtx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize)
|
||||
{
|
||||
const BYTE *ip = (const BYTE *)cSrc;
|
||||
|
||||
size_t const hSize = HUF_readDTableX2_wksp(DCtx, cSrc, cSrcSize, workspace, workspaceSize);
|
||||
if (HUF_isError(hSize))
|
||||
return hSize;
|
||||
if (hSize >= cSrcSize)
|
||||
return ERROR(srcSize_wrong);
|
||||
ip += hSize;
|
||||
cSrcSize -= hSize;
|
||||
|
||||
return HUF_decompress1X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx);
|
||||
}
|
||||
|
||||
static size_t HUF_decompress4X2_usingDTable_internal(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable)
|
||||
{
|
||||
/* Check */
|
||||
if (cSrcSize < 10)
|
||||
return ERROR(corruption_detected); /* strict minimum : jump table + 1 byte per stream */
|
||||
|
||||
{
|
||||
const BYTE *const istart = (const BYTE *)cSrc;
|
||||
BYTE *const ostart = (BYTE *)dst;
|
||||
BYTE *const oend = ostart + dstSize;
|
||||
const void *const dtPtr = DTable + 1;
|
||||
const HUF_DEltX2 *const dt = (const HUF_DEltX2 *)dtPtr;
|
||||
|
||||
/* Init */
|
||||
BIT_DStream_t bitD1;
|
||||
BIT_DStream_t bitD2;
|
||||
BIT_DStream_t bitD3;
|
||||
BIT_DStream_t bitD4;
|
||||
size_t const length1 = ZSTD_readLE16(istart);
|
||||
size_t const length2 = ZSTD_readLE16(istart + 2);
|
||||
size_t const length3 = ZSTD_readLE16(istart + 4);
|
||||
size_t const length4 = cSrcSize - (length1 + length2 + length3 + 6);
|
||||
const BYTE *const istart1 = istart + 6; /* jumpTable */
|
||||
const BYTE *const istart2 = istart1 + length1;
|
||||
const BYTE *const istart3 = istart2 + length2;
|
||||
const BYTE *const istart4 = istart3 + length3;
|
||||
const size_t segmentSize = (dstSize + 3) / 4;
|
||||
BYTE *const opStart2 = ostart + segmentSize;
|
||||
BYTE *const opStart3 = opStart2 + segmentSize;
|
||||
BYTE *const opStart4 = opStart3 + segmentSize;
|
||||
BYTE *op1 = ostart;
|
||||
BYTE *op2 = opStart2;
|
||||
BYTE *op3 = opStart3;
|
||||
BYTE *op4 = opStart4;
|
||||
U32 endSignal;
|
||||
DTableDesc const dtd = HUF_getDTableDesc(DTable);
|
||||
U32 const dtLog = dtd.tableLog;
|
||||
|
||||
if (length4 > cSrcSize)
|
||||
return ERROR(corruption_detected); /* overflow */
|
||||
{
|
||||
size_t const errorCode = BIT_initDStream(&bitD1, istart1, length1);
|
||||
if (HUF_isError(errorCode))
|
||||
return errorCode;
|
||||
}
|
||||
{
|
||||
size_t const errorCode = BIT_initDStream(&bitD2, istart2, length2);
|
||||
if (HUF_isError(errorCode))
|
||||
return errorCode;
|
||||
}
|
||||
{
|
||||
size_t const errorCode = BIT_initDStream(&bitD3, istart3, length3);
|
||||
if (HUF_isError(errorCode))
|
||||
return errorCode;
|
||||
}
|
||||
{
|
||||
size_t const errorCode = BIT_initDStream(&bitD4, istart4, length4);
|
||||
if (HUF_isError(errorCode))
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
/* 16-32 symbols per loop (4-8 symbols per stream) */
|
||||
endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4);
|
||||
for (; (endSignal == BIT_DStream_unfinished) && (op4 < (oend - 7));) {
|
||||
HUF_DECODE_SYMBOLX2_2(op1, &bitD1);
|
||||
HUF_DECODE_SYMBOLX2_2(op2, &bitD2);
|
||||
HUF_DECODE_SYMBOLX2_2(op3, &bitD3);
|
||||
HUF_DECODE_SYMBOLX2_2(op4, &bitD4);
|
||||
HUF_DECODE_SYMBOLX2_1(op1, &bitD1);
|
||||
HUF_DECODE_SYMBOLX2_1(op2, &bitD2);
|
||||
HUF_DECODE_SYMBOLX2_1(op3, &bitD3);
|
||||
HUF_DECODE_SYMBOLX2_1(op4, &bitD4);
|
||||
HUF_DECODE_SYMBOLX2_2(op1, &bitD1);
|
||||
HUF_DECODE_SYMBOLX2_2(op2, &bitD2);
|
||||
HUF_DECODE_SYMBOLX2_2(op3, &bitD3);
|
||||
HUF_DECODE_SYMBOLX2_2(op4, &bitD4);
|
||||
HUF_DECODE_SYMBOLX2_0(op1, &bitD1);
|
||||
HUF_DECODE_SYMBOLX2_0(op2, &bitD2);
|
||||
HUF_DECODE_SYMBOLX2_0(op3, &bitD3);
|
||||
HUF_DECODE_SYMBOLX2_0(op4, &bitD4);
|
||||
endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4);
|
||||
}
|
||||
|
||||
/* check corruption */
|
||||
if (op1 > opStart2)
|
||||
return ERROR(corruption_detected);
|
||||
if (op2 > opStart3)
|
||||
return ERROR(corruption_detected);
|
||||
if (op3 > opStart4)
|
||||
return ERROR(corruption_detected);
|
||||
/* note : op4 supposed already verified within main loop */
|
||||
|
||||
/* finish bitStreams one by one */
|
||||
HUF_decodeStreamX2(op1, &bitD1, opStart2, dt, dtLog);
|
||||
HUF_decodeStreamX2(op2, &bitD2, opStart3, dt, dtLog);
|
||||
HUF_decodeStreamX2(op3, &bitD3, opStart4, dt, dtLog);
|
||||
HUF_decodeStreamX2(op4, &bitD4, oend, dt, dtLog);
|
||||
|
||||
/* check */
|
||||
endSignal = BIT_endOfDStream(&bitD1) & BIT_endOfDStream(&bitD2) & BIT_endOfDStream(&bitD3) & BIT_endOfDStream(&bitD4);
|
||||
if (!endSignal)
|
||||
return ERROR(corruption_detected);
|
||||
|
||||
/* decoded size */
|
||||
return dstSize;
|
||||
}
|
||||
}
|
||||
|
||||
size_t HUF_decompress4X2_usingDTable(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable)
|
||||
{
|
||||
DTableDesc dtd = HUF_getDTableDesc(DTable);
|
||||
if (dtd.tableType != 0)
|
||||
return ERROR(GENERIC);
|
||||
return HUF_decompress4X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable);
|
||||
}
|
||||
|
||||
size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize)
|
||||
{
|
||||
const BYTE *ip = (const BYTE *)cSrc;
|
||||
|
||||
size_t const hSize = HUF_readDTableX2_wksp(dctx, cSrc, cSrcSize, workspace, workspaceSize);
|
||||
if (HUF_isError(hSize))
|
||||
return hSize;
|
||||
if (hSize >= cSrcSize)
|
||||
return ERROR(srcSize_wrong);
|
||||
ip += hSize;
|
||||
cSrcSize -= hSize;
|
||||
|
||||
return HUF_decompress4X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx);
|
||||
}
|
||||
|
||||
/* *************************/
|
||||
/* double-symbols decoding */
|
||||
/* *************************/
|
||||
typedef struct {
|
||||
U16 sequence;
|
||||
BYTE nbBits;
|
||||
BYTE length;
|
||||
} HUF_DEltX4; /* double-symbols decoding */
|
||||
|
||||
typedef struct {
|
||||
BYTE symbol;
|
||||
BYTE weight;
|
||||
} sortedSymbol_t;
|
||||
|
||||
/* HUF_fillDTableX4Level2() :
|
||||
* `rankValOrigin` must be a table of at least (HUF_TABLELOG_MAX + 1) U32 */
|
||||
static void HUF_fillDTableX4Level2(HUF_DEltX4 *DTable, U32 sizeLog, const U32 consumed, const U32 *rankValOrigin, const int minWeight,
|
||||
const sortedSymbol_t *sortedSymbols, const U32 sortedListSize, U32 nbBitsBaseline, U16 baseSeq)
|
||||
{
|
||||
HUF_DEltX4 DElt;
|
||||
U32 rankVal[HUF_TABLELOG_MAX + 1];
|
||||
|
||||
/* get pre-calculated rankVal */
|
||||
memcpy(rankVal, rankValOrigin, sizeof(rankVal));
|
||||
|
||||
/* fill skipped values */
|
||||
if (minWeight > 1) {
|
||||
U32 i, skipSize = rankVal[minWeight];
|
||||
ZSTD_writeLE16(&(DElt.sequence), baseSeq);
|
||||
DElt.nbBits = (BYTE)(consumed);
|
||||
DElt.length = 1;
|
||||
for (i = 0; i < skipSize; i++)
|
||||
DTable[i] = DElt;
|
||||
}
|
||||
|
||||
/* fill DTable */
|
||||
{
|
||||
U32 s;
|
||||
for (s = 0; s < sortedListSize; s++) { /* note : sortedSymbols already skipped */
|
||||
const U32 symbol = sortedSymbols[s].symbol;
|
||||
const U32 weight = sortedSymbols[s].weight;
|
||||
const U32 nbBits = nbBitsBaseline - weight;
|
||||
const U32 length = 1 << (sizeLog - nbBits);
|
||||
const U32 start = rankVal[weight];
|
||||
U32 i = start;
|
||||
const U32 end = start + length;
|
||||
|
||||
ZSTD_writeLE16(&(DElt.sequence), (U16)(baseSeq + (symbol << 8)));
|
||||
DElt.nbBits = (BYTE)(nbBits + consumed);
|
||||
DElt.length = 2;
|
||||
do {
|
||||
DTable[i++] = DElt;
|
||||
} while (i < end); /* since length >= 1 */
|
||||
|
||||
rankVal[weight] += length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef U32 rankVal_t[HUF_TABLELOG_MAX][HUF_TABLELOG_MAX + 1];
|
||||
typedef U32 rankValCol_t[HUF_TABLELOG_MAX + 1];
|
||||
|
||||
static void HUF_fillDTableX4(HUF_DEltX4 *DTable, const U32 targetLog, const sortedSymbol_t *sortedList, const U32 sortedListSize, const U32 *rankStart,
|
||||
rankVal_t rankValOrigin, const U32 maxWeight, const U32 nbBitsBaseline)
|
||||
{
|
||||
U32 rankVal[HUF_TABLELOG_MAX + 1];
|
||||
const int scaleLog = nbBitsBaseline - targetLog; /* note : targetLog >= srcLog, hence scaleLog <= 1 */
|
||||
const U32 minBits = nbBitsBaseline - maxWeight;
|
||||
U32 s;
|
||||
|
||||
memcpy(rankVal, rankValOrigin, sizeof(rankVal));
|
||||
|
||||
/* fill DTable */
|
||||
for (s = 0; s < sortedListSize; s++) {
|
||||
const U16 symbol = sortedList[s].symbol;
|
||||
const U32 weight = sortedList[s].weight;
|
||||
const U32 nbBits = nbBitsBaseline - weight;
|
||||
const U32 start = rankVal[weight];
|
||||
const U32 length = 1 << (targetLog - nbBits);
|
||||
|
||||
if (targetLog - nbBits >= minBits) { /* enough room for a second symbol */
|
||||
U32 sortedRank;
|
||||
int minWeight = nbBits + scaleLog;
|
||||
if (minWeight < 1)
|
||||
minWeight = 1;
|
||||
sortedRank = rankStart[minWeight];
|
||||
HUF_fillDTableX4Level2(DTable + start, targetLog - nbBits, nbBits, rankValOrigin[nbBits], minWeight, sortedList + sortedRank,
|
||||
sortedListSize - sortedRank, nbBitsBaseline, symbol);
|
||||
} else {
|
||||
HUF_DEltX4 DElt;
|
||||
ZSTD_writeLE16(&(DElt.sequence), symbol);
|
||||
DElt.nbBits = (BYTE)(nbBits);
|
||||
DElt.length = 1;
|
||||
{
|
||||
U32 const end = start + length;
|
||||
U32 u;
|
||||
for (u = start; u < end; u++)
|
||||
DTable[u] = DElt;
|
||||
}
|
||||
}
|
||||
rankVal[weight] += length;
|
||||
}
|
||||
}
|
||||
|
||||
size_t HUF_readDTableX4_wksp(HUF_DTable *DTable, const void *src, size_t srcSize, void *workspace, size_t workspaceSize)
|
||||
{
|
||||
U32 tableLog, maxW, sizeOfSort, nbSymbols;
|
||||
DTableDesc dtd = HUF_getDTableDesc(DTable);
|
||||
U32 const maxTableLog = dtd.maxTableLog;
|
||||
size_t iSize;
|
||||
void *dtPtr = DTable + 1; /* force compiler to avoid strict-aliasing */
|
||||
HUF_DEltX4 *const dt = (HUF_DEltX4 *)dtPtr;
|
||||
U32 *rankStart;
|
||||
|
||||
rankValCol_t *rankVal;
|
||||
U32 *rankStats;
|
||||
U32 *rankStart0;
|
||||
sortedSymbol_t *sortedSymbol;
|
||||
BYTE *weightList;
|
||||
size_t spaceUsed32 = 0;
|
||||
|
||||
HUF_STATIC_ASSERT((sizeof(rankValCol_t) & 3) == 0);
|
||||
|
||||
rankVal = (rankValCol_t *)((U32 *)workspace + spaceUsed32);
|
||||
spaceUsed32 += (sizeof(rankValCol_t) * HUF_TABLELOG_MAX) >> 2;
|
||||
rankStats = (U32 *)workspace + spaceUsed32;
|
||||
spaceUsed32 += HUF_TABLELOG_MAX + 1;
|
||||
rankStart0 = (U32 *)workspace + spaceUsed32;
|
||||
spaceUsed32 += HUF_TABLELOG_MAX + 2;
|
||||
sortedSymbol = (sortedSymbol_t *)((U32 *)workspace + spaceUsed32);
|
||||
spaceUsed32 += ALIGN(sizeof(sortedSymbol_t) * (HUF_SYMBOLVALUE_MAX + 1), sizeof(U32)) >> 2;
|
||||
weightList = (BYTE *)((U32 *)workspace + spaceUsed32);
|
||||
spaceUsed32 += ALIGN(HUF_SYMBOLVALUE_MAX + 1, sizeof(U32)) >> 2;
|
||||
|
||||
if ((spaceUsed32 << 2) > workspaceSize)
|
||||
return ERROR(tableLog_tooLarge);
|
||||
workspace = (U32 *)workspace + spaceUsed32;
|
||||
workspaceSize -= (spaceUsed32 << 2);
|
||||
|
||||
rankStart = rankStart0 + 1;
|
||||
memset(rankStats, 0, sizeof(U32) * (2 * HUF_TABLELOG_MAX + 2 + 1));
|
||||
|
||||
HUF_STATIC_ASSERT(sizeof(HUF_DEltX4) == sizeof(HUF_DTable)); /* if compiler fails here, assertion is wrong */
|
||||
if (maxTableLog > HUF_TABLELOG_MAX)
|
||||
return ERROR(tableLog_tooLarge);
|
||||
/* memset(weightList, 0, sizeof(weightList)); */ /* is not necessary, even though some analyzer complain ... */
|
||||
|
||||
iSize = HUF_readStats_wksp(weightList, HUF_SYMBOLVALUE_MAX + 1, rankStats, &nbSymbols, &tableLog, src, srcSize, workspace, workspaceSize);
|
||||
if (HUF_isError(iSize))
|
||||
return iSize;
|
||||
|
||||
/* check result */
|
||||
if (tableLog > maxTableLog)
|
||||
return ERROR(tableLog_tooLarge); /* DTable can't fit code depth */
|
||||
|
||||
/* find maxWeight */
|
||||
for (maxW = tableLog; rankStats[maxW] == 0; maxW--) {
|
||||
} /* necessarily finds a solution before 0 */
|
||||
|
||||
/* Get start index of each weight */
|
||||
{
|
||||
U32 w, nextRankStart = 0;
|
||||
for (w = 1; w < maxW + 1; w++) {
|
||||
U32 curr = nextRankStart;
|
||||
nextRankStart += rankStats[w];
|
||||
rankStart[w] = curr;
|
||||
}
|
||||
rankStart[0] = nextRankStart; /* put all 0w symbols at the end of sorted list*/
|
||||
sizeOfSort = nextRankStart;
|
||||
}
|
||||
|
||||
/* sort symbols by weight */
|
||||
{
|
||||
U32 s;
|
||||
for (s = 0; s < nbSymbols; s++) {
|
||||
U32 const w = weightList[s];
|
||||
U32 const r = rankStart[w]++;
|
||||
sortedSymbol[r].symbol = (BYTE)s;
|
||||
sortedSymbol[r].weight = (BYTE)w;
|
||||
}
|
||||
rankStart[0] = 0; /* forget 0w symbols; this is beginning of weight(1) */
|
||||
}
|
||||
|
||||
/* Build rankVal */
|
||||
{
|
||||
U32 *const rankVal0 = rankVal[0];
|
||||
{
|
||||
int const rescale = (maxTableLog - tableLog) - 1; /* tableLog <= maxTableLog */
|
||||
U32 nextRankVal = 0;
|
||||
U32 w;
|
||||
for (w = 1; w < maxW + 1; w++) {
|
||||
U32 curr = nextRankVal;
|
||||
nextRankVal += rankStats[w] << (w + rescale);
|
||||
rankVal0[w] = curr;
|
||||
}
|
||||
}
|
||||
{
|
||||
U32 const minBits = tableLog + 1 - maxW;
|
||||
U32 consumed;
|
||||
for (consumed = minBits; consumed < maxTableLog - minBits + 1; consumed++) {
|
||||
U32 *const rankValPtr = rankVal[consumed];
|
||||
U32 w;
|
||||
for (w = 1; w < maxW + 1; w++) {
|
||||
rankValPtr[w] = rankVal0[w] >> consumed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HUF_fillDTableX4(dt, maxTableLog, sortedSymbol, sizeOfSort, rankStart0, rankVal, maxW, tableLog + 1);
|
||||
|
||||
dtd.tableLog = (BYTE)maxTableLog;
|
||||
dtd.tableType = 1;
|
||||
memcpy(DTable, &dtd, sizeof(dtd));
|
||||
return iSize;
|
||||
}
|
||||
|
||||
static U32 HUF_decodeSymbolX4(void *op, BIT_DStream_t *DStream, const HUF_DEltX4 *dt, const U32 dtLog)
|
||||
{
|
||||
size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */
|
||||
memcpy(op, dt + val, 2);
|
||||
BIT_skipBits(DStream, dt[val].nbBits);
|
||||
return dt[val].length;
|
||||
}
|
||||
|
||||
static U32 HUF_decodeLastSymbolX4(void *op, BIT_DStream_t *DStream, const HUF_DEltX4 *dt, const U32 dtLog)
|
||||
{
|
||||
size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */
|
||||
memcpy(op, dt + val, 1);
|
||||
if (dt[val].length == 1)
|
||||
BIT_skipBits(DStream, dt[val].nbBits);
|
||||
else {
|
||||
if (DStream->bitsConsumed < (sizeof(DStream->bitContainer) * 8)) {
|
||||
BIT_skipBits(DStream, dt[val].nbBits);
|
||||
if (DStream->bitsConsumed > (sizeof(DStream->bitContainer) * 8))
|
||||
/* ugly hack; works only because it's the last symbol. Note : can't easily extract nbBits from just this symbol */
|
||||
DStream->bitsConsumed = (sizeof(DStream->bitContainer) * 8);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define HUF_DECODE_SYMBOLX4_0(ptr, DStreamPtr) ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog)
|
||||
|
||||
#define HUF_DECODE_SYMBOLX4_1(ptr, DStreamPtr) \
|
||||
if (ZSTD_64bits() || (HUF_TABLELOG_MAX <= 12)) \
|
||||
ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog)
|
||||
|
||||
#define HUF_DECODE_SYMBOLX4_2(ptr, DStreamPtr) \
|
||||
if (ZSTD_64bits()) \
|
||||
ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog)
|
||||
|
||||
FORCE_INLINE size_t HUF_decodeStreamX4(BYTE *p, BIT_DStream_t *bitDPtr, BYTE *const pEnd, const HUF_DEltX4 *const dt, const U32 dtLog)
|
||||
{
|
||||
BYTE *const pStart = p;
|
||||
|
||||
/* up to 8 symbols at a time */
|
||||
while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd - (sizeof(bitDPtr->bitContainer) - 1))) {
|
||||
HUF_DECODE_SYMBOLX4_2(p, bitDPtr);
|
||||
HUF_DECODE_SYMBOLX4_1(p, bitDPtr);
|
||||
HUF_DECODE_SYMBOLX4_2(p, bitDPtr);
|
||||
HUF_DECODE_SYMBOLX4_0(p, bitDPtr);
|
||||
}
|
||||
|
||||
/* closer to end : up to 2 symbols at a time */
|
||||
while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p <= pEnd - 2))
|
||||
HUF_DECODE_SYMBOLX4_0(p, bitDPtr);
|
||||
|
||||
while (p <= pEnd - 2)
|
||||
HUF_DECODE_SYMBOLX4_0(p, bitDPtr); /* no need to reload : reached the end of DStream */
|
||||
|
||||
if (p < pEnd)
|
||||
p += HUF_decodeLastSymbolX4(p, bitDPtr, dt, dtLog);
|
||||
|
||||
return p - pStart;
|
||||
}
|
||||
|
||||
static size_t HUF_decompress1X4_usingDTable_internal(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable)
|
||||
{
|
||||
BIT_DStream_t bitD;
|
||||
|
||||
/* Init */
|
||||
{
|
||||
size_t const errorCode = BIT_initDStream(&bitD, cSrc, cSrcSize);
|
||||
if (HUF_isError(errorCode))
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
/* decode */
|
||||
{
|
||||
BYTE *const ostart = (BYTE *)dst;
|
||||
BYTE *const oend = ostart + dstSize;
|
||||
const void *const dtPtr = DTable + 1; /* force compiler to not use strict-aliasing */
|
||||
const HUF_DEltX4 *const dt = (const HUF_DEltX4 *)dtPtr;
|
||||
DTableDesc const dtd = HUF_getDTableDesc(DTable);
|
||||
HUF_decodeStreamX4(ostart, &bitD, oend, dt, dtd.tableLog);
|
||||
}
|
||||
|
||||
/* check */
|
||||
if (!BIT_endOfDStream(&bitD))
|
||||
return ERROR(corruption_detected);
|
||||
|
||||
/* decoded size */
|
||||
return dstSize;
|
||||
}
|
||||
|
||||
size_t HUF_decompress1X4_usingDTable(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable)
|
||||
{
|
||||
DTableDesc dtd = HUF_getDTableDesc(DTable);
|
||||
if (dtd.tableType != 1)
|
||||
return ERROR(GENERIC);
|
||||
return HUF_decompress1X4_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable);
|
||||
}
|
||||
|
||||
size_t HUF_decompress1X4_DCtx_wksp(HUF_DTable *DCtx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize)
|
||||
{
|
||||
const BYTE *ip = (const BYTE *)cSrc;
|
||||
|
||||
size_t const hSize = HUF_readDTableX4_wksp(DCtx, cSrc, cSrcSize, workspace, workspaceSize);
|
||||
if (HUF_isError(hSize))
|
||||
return hSize;
|
||||
if (hSize >= cSrcSize)
|
||||
return ERROR(srcSize_wrong);
|
||||
ip += hSize;
|
||||
cSrcSize -= hSize;
|
||||
|
||||
return HUF_decompress1X4_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx);
|
||||
}
|
||||
|
||||
static size_t HUF_decompress4X4_usingDTable_internal(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable)
|
||||
{
|
||||
if (cSrcSize < 10)
|
||||
return ERROR(corruption_detected); /* strict minimum : jump table + 1 byte per stream */
|
||||
|
||||
{
|
||||
const BYTE *const istart = (const BYTE *)cSrc;
|
||||
BYTE *const ostart = (BYTE *)dst;
|
||||
BYTE *const oend = ostart + dstSize;
|
||||
const void *const dtPtr = DTable + 1;
|
||||
const HUF_DEltX4 *const dt = (const HUF_DEltX4 *)dtPtr;
|
||||
|
||||
/* Init */
|
||||
BIT_DStream_t bitD1;
|
||||
BIT_DStream_t bitD2;
|
||||
BIT_DStream_t bitD3;
|
||||
BIT_DStream_t bitD4;
|
||||
size_t const length1 = ZSTD_readLE16(istart);
|
||||
size_t const length2 = ZSTD_readLE16(istart + 2);
|
||||
size_t const length3 = ZSTD_readLE16(istart + 4);
|
||||
size_t const length4 = cSrcSize - (length1 + length2 + length3 + 6);
|
||||
const BYTE *const istart1 = istart + 6; /* jumpTable */
|
||||
const BYTE *const istart2 = istart1 + length1;
|
||||
const BYTE *const istart3 = istart2 + length2;
|
||||
const BYTE *const istart4 = istart3 + length3;
|
||||
size_t const segmentSize = (dstSize + 3) / 4;
|
||||
BYTE *const opStart2 = ostart + segmentSize;
|
||||
BYTE *const opStart3 = opStart2 + segmentSize;
|
||||
BYTE *const opStart4 = opStart3 + segmentSize;
|
||||
BYTE *op1 = ostart;
|
||||
BYTE *op2 = opStart2;
|
||||
BYTE *op3 = opStart3;
|
||||
BYTE *op4 = opStart4;
|
||||
U32 endSignal;
|
||||
DTableDesc const dtd = HUF_getDTableDesc(DTable);
|
||||
U32 const dtLog = dtd.tableLog;
|
||||
|
||||
if (length4 > cSrcSize)
|
||||
return ERROR(corruption_detected); /* overflow */
|
||||
{
|
||||
size_t const errorCode = BIT_initDStream(&bitD1, istart1, length1);
|
||||
if (HUF_isError(errorCode))
|
||||
return errorCode;
|
||||
}
|
||||
{
|
||||
size_t const errorCode = BIT_initDStream(&bitD2, istart2, length2);
|
||||
if (HUF_isError(errorCode))
|
||||
return errorCode;
|
||||
}
|
||||
{
|
||||
size_t const errorCode = BIT_initDStream(&bitD3, istart3, length3);
|
||||
if (HUF_isError(errorCode))
|
||||
return errorCode;
|
||||
}
|
||||
{
|
||||
size_t const errorCode = BIT_initDStream(&bitD4, istart4, length4);
|
||||
if (HUF_isError(errorCode))
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
/* 16-32 symbols per loop (4-8 symbols per stream) */
|
||||
endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4);
|
||||
for (; (endSignal == BIT_DStream_unfinished) & (op4 < (oend - (sizeof(bitD4.bitContainer) - 1)));) {
|
||||
HUF_DECODE_SYMBOLX4_2(op1, &bitD1);
|
||||
HUF_DECODE_SYMBOLX4_2(op2, &bitD2);
|
||||
HUF_DECODE_SYMBOLX4_2(op3, &bitD3);
|
||||
HUF_DECODE_SYMBOLX4_2(op4, &bitD4);
|
||||
HUF_DECODE_SYMBOLX4_1(op1, &bitD1);
|
||||
HUF_DECODE_SYMBOLX4_1(op2, &bitD2);
|
||||
HUF_DECODE_SYMBOLX4_1(op3, &bitD3);
|
||||
HUF_DECODE_SYMBOLX4_1(op4, &bitD4);
|
||||
HUF_DECODE_SYMBOLX4_2(op1, &bitD1);
|
||||
HUF_DECODE_SYMBOLX4_2(op2, &bitD2);
|
||||
HUF_DECODE_SYMBOLX4_2(op3, &bitD3);
|
||||
HUF_DECODE_SYMBOLX4_2(op4, &bitD4);
|
||||
HUF_DECODE_SYMBOLX4_0(op1, &bitD1);
|
||||
HUF_DECODE_SYMBOLX4_0(op2, &bitD2);
|
||||
HUF_DECODE_SYMBOLX4_0(op3, &bitD3);
|
||||
HUF_DECODE_SYMBOLX4_0(op4, &bitD4);
|
||||
|
||||
endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4);
|
||||
}
|
||||
|
||||
/* check corruption */
|
||||
if (op1 > opStart2)
|
||||
return ERROR(corruption_detected);
|
||||
if (op2 > opStart3)
|
||||
return ERROR(corruption_detected);
|
||||
if (op3 > opStart4)
|
||||
return ERROR(corruption_detected);
|
||||
/* note : op4 already verified within main loop */
|
||||
|
||||
/* finish bitStreams one by one */
|
||||
HUF_decodeStreamX4(op1, &bitD1, opStart2, dt, dtLog);
|
||||
HUF_decodeStreamX4(op2, &bitD2, opStart3, dt, dtLog);
|
||||
HUF_decodeStreamX4(op3, &bitD3, opStart4, dt, dtLog);
|
||||
HUF_decodeStreamX4(op4, &bitD4, oend, dt, dtLog);
|
||||
|
||||
/* check */
|
||||
{
|
||||
U32 const endCheck = BIT_endOfDStream(&bitD1) & BIT_endOfDStream(&bitD2) & BIT_endOfDStream(&bitD3) & BIT_endOfDStream(&bitD4);
|
||||
if (!endCheck)
|
||||
return ERROR(corruption_detected);
|
||||
}
|
||||
|
||||
/* decoded size */
|
||||
return dstSize;
|
||||
}
|
||||
}
|
||||
|
||||
size_t HUF_decompress4X4_usingDTable(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable)
|
||||
{
|
||||
DTableDesc dtd = HUF_getDTableDesc(DTable);
|
||||
if (dtd.tableType != 1)
|
||||
return ERROR(GENERIC);
|
||||
return HUF_decompress4X4_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable);
|
||||
}
|
||||
|
||||
size_t HUF_decompress4X4_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize)
|
||||
{
|
||||
const BYTE *ip = (const BYTE *)cSrc;
|
||||
|
||||
size_t hSize = HUF_readDTableX4_wksp(dctx, cSrc, cSrcSize, workspace, workspaceSize);
|
||||
if (HUF_isError(hSize))
|
||||
return hSize;
|
||||
if (hSize >= cSrcSize)
|
||||
return ERROR(srcSize_wrong);
|
||||
ip += hSize;
|
||||
cSrcSize -= hSize;
|
||||
|
||||
return HUF_decompress4X4_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx);
|
||||
}
|
||||
|
||||
/* ********************************/
|
||||
/* Generic decompression selector */
|
||||
/* ********************************/
|
||||
|
||||
size_t HUF_decompress1X_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable)
|
||||
{
|
||||
DTableDesc const dtd = HUF_getDTableDesc(DTable);
|
||||
return dtd.tableType ? HUF_decompress1X4_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable)
|
||||
: HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable);
|
||||
}
|
||||
|
||||
size_t HUF_decompress4X_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable)
|
||||
{
|
||||
DTableDesc const dtd = HUF_getDTableDesc(DTable);
|
||||
return dtd.tableType ? HUF_decompress4X4_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable)
|
||||
: HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
U32 tableTime;
|
||||
U32 decode256Time;
|
||||
} algo_time_t;
|
||||
static const algo_time_t algoTime[16 /* Quantization */][3 /* single, double, quad */] = {
|
||||
/* single, double, quad */
|
||||
{{0, 0}, {1, 1}, {2, 2}}, /* Q==0 : impossible */
|
||||
{{0, 0}, {1, 1}, {2, 2}}, /* Q==1 : impossible */
|
||||
{{38, 130}, {1313, 74}, {2151, 38}}, /* Q == 2 : 12-18% */
|
||||
{{448, 128}, {1353, 74}, {2238, 41}}, /* Q == 3 : 18-25% */
|
||||
{{556, 128}, {1353, 74}, {2238, 47}}, /* Q == 4 : 25-32% */
|
||||
{{714, 128}, {1418, 74}, {2436, 53}}, /* Q == 5 : 32-38% */
|
||||
{{883, 128}, {1437, 74}, {2464, 61}}, /* Q == 6 : 38-44% */
|
||||
{{897, 128}, {1515, 75}, {2622, 68}}, /* Q == 7 : 44-50% */
|
||||
{{926, 128}, {1613, 75}, {2730, 75}}, /* Q == 8 : 50-56% */
|
||||
{{947, 128}, {1729, 77}, {3359, 77}}, /* Q == 9 : 56-62% */
|
||||
{{1107, 128}, {2083, 81}, {4006, 84}}, /* Q ==10 : 62-69% */
|
||||
{{1177, 128}, {2379, 87}, {4785, 88}}, /* Q ==11 : 69-75% */
|
||||
{{1242, 128}, {2415, 93}, {5155, 84}}, /* Q ==12 : 75-81% */
|
||||
{{1349, 128}, {2644, 106}, {5260, 106}}, /* Q ==13 : 81-87% */
|
||||
{{1455, 128}, {2422, 124}, {4174, 124}}, /* Q ==14 : 87-93% */
|
||||
{{722, 128}, {1891, 145}, {1936, 146}}, /* Q ==15 : 93-99% */
|
||||
};
|
||||
|
||||
/** HUF_selectDecoder() :
|
||||
* Tells which decoder is likely to decode faster,
|
||||
* based on a set of pre-determined metrics.
|
||||
* @return : 0==HUF_decompress4X2, 1==HUF_decompress4X4 .
|
||||
* Assumption : 0 < cSrcSize < dstSize <= 128 KB */
|
||||
U32 HUF_selectDecoder(size_t dstSize, size_t cSrcSize)
|
||||
{
|
||||
/* decoder timing evaluation */
|
||||
U32 const Q = (U32)(cSrcSize * 16 / dstSize); /* Q < 16 since dstSize > cSrcSize */
|
||||
U32 const D256 = (U32)(dstSize >> 8);
|
||||
U32 const DTime0 = algoTime[Q][0].tableTime + (algoTime[Q][0].decode256Time * D256);
|
||||
U32 DTime1 = algoTime[Q][1].tableTime + (algoTime[Q][1].decode256Time * D256);
|
||||
DTime1 += DTime1 >> 3; /* advantage to algorithm using less memory, for cache eviction */
|
||||
|
||||
return DTime1 < DTime0;
|
||||
}
|
||||
|
||||
typedef size_t (*decompressionAlgo)(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize);
|
||||
|
||||
size_t HUF_decompress4X_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize)
|
||||
{
|
||||
/* validation checks */
|
||||
if (dstSize == 0)
|
||||
return ERROR(dstSize_tooSmall);
|
||||
if (cSrcSize > dstSize)
|
||||
return ERROR(corruption_detected); /* invalid */
|
||||
if (cSrcSize == dstSize) {
|
||||
memcpy(dst, cSrc, dstSize);
|
||||
return dstSize;
|
||||
} /* not compressed */
|
||||
if (cSrcSize == 1) {
|
||||
memset(dst, *(const BYTE *)cSrc, dstSize);
|
||||
return dstSize;
|
||||
} /* RLE */
|
||||
|
||||
{
|
||||
U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize);
|
||||
return algoNb ? HUF_decompress4X4_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workspace, workspaceSize)
|
||||
: HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workspace, workspaceSize);
|
||||
}
|
||||
}
|
||||
|
||||
size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize)
|
||||
{
|
||||
/* validation checks */
|
||||
if (dstSize == 0)
|
||||
return ERROR(dstSize_tooSmall);
|
||||
if ((cSrcSize >= dstSize) || (cSrcSize <= 1))
|
||||
return ERROR(corruption_detected); /* invalid */
|
||||
|
||||
{
|
||||
U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize);
|
||||
return algoNb ? HUF_decompress4X4_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workspace, workspaceSize)
|
||||
: HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workspace, workspaceSize);
|
||||
}
|
||||
}
|
||||
|
||||
size_t HUF_decompress1X_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize)
|
||||
{
|
||||
/* validation checks */
|
||||
if (dstSize == 0)
|
||||
return ERROR(dstSize_tooSmall);
|
||||
if (cSrcSize > dstSize)
|
||||
return ERROR(corruption_detected); /* invalid */
|
||||
if (cSrcSize == dstSize) {
|
||||
memcpy(dst, cSrc, dstSize);
|
||||
return dstSize;
|
||||
} /* not compressed */
|
||||
if (cSrcSize == 1) {
|
||||
memset(dst, *(const BYTE *)cSrc, dstSize);
|
||||
return dstSize;
|
||||
} /* RLE */
|
||||
|
||||
{
|
||||
U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize);
|
||||
return algoNb ? HUF_decompress1X4_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workspace, workspaceSize)
|
||||
: HUF_decompress1X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workspace, workspaceSize);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of https://github.com/facebook/zstd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License version 2 as published by the
|
||||
* Free Software Foundation. This program is dual-licensed; you may select
|
||||
* either version 2 of the GNU General Public License ("GPL") or BSD license
|
||||
* ("BSD").
|
||||
*/
|
||||
|
||||
#ifndef MEM_H_MODULE
|
||||
#define MEM_H_MODULE
|
||||
|
||||
/*-****************************************
|
||||
* Dependencies
|
||||
******************************************/
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/string.h> /* memcpy */
|
||||
#include <linux/types.h> /* size_t, ptrdiff_t */
|
||||
|
||||
/*-****************************************
|
||||
* Compiler specifics
|
||||
******************************************/
|
||||
#define ZSTD_STATIC static __inline __attribute__((unused))
|
||||
|
||||
/*-**************************************************************
|
||||
* Basic Types
|
||||
*****************************************************************/
|
||||
typedef uint8_t BYTE;
|
||||
typedef uint16_t U16;
|
||||
typedef int16_t S16;
|
||||
typedef uint32_t U32;
|
||||
typedef int32_t S32;
|
||||
typedef uint64_t U64;
|
||||
typedef int64_t S64;
|
||||
typedef ptrdiff_t iPtrDiff;
|
||||
typedef uintptr_t uPtrDiff;
|
||||
|
||||
/*-**************************************************************
|
||||
* Memory I/O
|
||||
*****************************************************************/
|
||||
ZSTD_STATIC unsigned ZSTD_32bits(void) { return sizeof(size_t) == 4; }
|
||||
ZSTD_STATIC unsigned ZSTD_64bits(void) { return sizeof(size_t) == 8; }
|
||||
|
||||
#if defined(__LITTLE_ENDIAN)
|
||||
#define ZSTD_LITTLE_ENDIAN 1
|
||||
#else
|
||||
#define ZSTD_LITTLE_ENDIAN 0
|
||||
#endif
|
||||
|
||||
ZSTD_STATIC unsigned ZSTD_isLittleEndian(void) { return ZSTD_LITTLE_ENDIAN; }
|
||||
|
||||
ZSTD_STATIC U16 ZSTD_read16(const void *memPtr) { return get_unaligned((const U16 *)memPtr); }
|
||||
|
||||
ZSTD_STATIC U32 ZSTD_read32(const void *memPtr) { return get_unaligned((const U32 *)memPtr); }
|
||||
|
||||
ZSTD_STATIC U64 ZSTD_read64(const void *memPtr) { return get_unaligned((const U64 *)memPtr); }
|
||||
|
||||
ZSTD_STATIC size_t ZSTD_readST(const void *memPtr) { return get_unaligned((const size_t *)memPtr); }
|
||||
|
||||
ZSTD_STATIC void ZSTD_write16(void *memPtr, U16 value) { put_unaligned(value, (U16 *)memPtr); }
|
||||
|
||||
ZSTD_STATIC void ZSTD_write32(void *memPtr, U32 value) { put_unaligned(value, (U32 *)memPtr); }
|
||||
|
||||
ZSTD_STATIC void ZSTD_write64(void *memPtr, U64 value) { put_unaligned(value, (U64 *)memPtr); }
|
||||
|
||||
/*=== Little endian r/w ===*/
|
||||
|
||||
ZSTD_STATIC U16 ZSTD_readLE16(const void *memPtr) { return get_unaligned_le16(memPtr); }
|
||||
|
||||
ZSTD_STATIC void ZSTD_writeLE16(void *memPtr, U16 val) { put_unaligned_le16(val, memPtr); }
|
||||
|
||||
ZSTD_STATIC U32 ZSTD_readLE24(const void *memPtr) { return ZSTD_readLE16(memPtr) + (((const BYTE *)memPtr)[2] << 16); }
|
||||
|
||||
ZSTD_STATIC void ZSTD_writeLE24(void *memPtr, U32 val)
|
||||
{
|
||||
ZSTD_writeLE16(memPtr, (U16)val);
|
||||
((BYTE *)memPtr)[2] = (BYTE)(val >> 16);
|
||||
}
|
||||
|
||||
ZSTD_STATIC U32 ZSTD_readLE32(const void *memPtr) { return get_unaligned_le32(memPtr); }
|
||||
|
||||
ZSTD_STATIC void ZSTD_writeLE32(void *memPtr, U32 val32) { put_unaligned_le32(val32, memPtr); }
|
||||
|
||||
ZSTD_STATIC U64 ZSTD_readLE64(const void *memPtr) { return get_unaligned_le64(memPtr); }
|
||||
|
||||
ZSTD_STATIC void ZSTD_writeLE64(void *memPtr, U64 val64) { put_unaligned_le64(val64, memPtr); }
|
||||
|
||||
ZSTD_STATIC size_t ZSTD_readLEST(const void *memPtr)
|
||||
{
|
||||
if (ZSTD_32bits())
|
||||
return (size_t)ZSTD_readLE32(memPtr);
|
||||
else
|
||||
return (size_t)ZSTD_readLE64(memPtr);
|
||||
}
|
||||
|
||||
ZSTD_STATIC void ZSTD_writeLEST(void *memPtr, size_t val)
|
||||
{
|
||||
if (ZSTD_32bits())
|
||||
ZSTD_writeLE32(memPtr, (U32)val);
|
||||
else
|
||||
ZSTD_writeLE64(memPtr, (U64)val);
|
||||
}
|
||||
|
||||
/*=== Big endian r/w ===*/
|
||||
|
||||
ZSTD_STATIC U32 ZSTD_readBE32(const void *memPtr) { return get_unaligned_be32(memPtr); }
|
||||
|
||||
ZSTD_STATIC void ZSTD_writeBE32(void *memPtr, U32 val32) { put_unaligned_be32(val32, memPtr); }
|
||||
|
||||
ZSTD_STATIC U64 ZSTD_readBE64(const void *memPtr) { return get_unaligned_be64(memPtr); }
|
||||
|
||||
ZSTD_STATIC void ZSTD_writeBE64(void *memPtr, U64 val64) { put_unaligned_be64(val64, memPtr); }
|
||||
|
||||
ZSTD_STATIC size_t ZSTD_readBEST(const void *memPtr)
|
||||
{
|
||||
if (ZSTD_32bits())
|
||||
return (size_t)ZSTD_readBE32(memPtr);
|
||||
else
|
||||
return (size_t)ZSTD_readBE64(memPtr);
|
||||
}
|
||||
|
||||
ZSTD_STATIC void ZSTD_writeBEST(void *memPtr, size_t val)
|
||||
{
|
||||
if (ZSTD_32bits())
|
||||
ZSTD_writeBE32(memPtr, (U32)val);
|
||||
else
|
||||
ZSTD_writeBE64(memPtr, (U64)val);
|
||||
}
|
||||
|
||||
/* function safe only for comparisons */
|
||||
ZSTD_STATIC U32 ZSTD_readMINMATCH(const void *memPtr, U32 length)
|
||||
{
|
||||
switch (length) {
|
||||
default:
|
||||
case 4: return ZSTD_read32(memPtr);
|
||||
case 3:
|
||||
if (ZSTD_isLittleEndian())
|
||||
return ZSTD_read32(memPtr) << 8;
|
||||
else
|
||||
return ZSTD_read32(memPtr) >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* MEM_H_MODULE */
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of https://github.com/facebook/zstd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License version 2 as published by the
|
||||
* Free Software Foundation. This program is dual-licensed; you may select
|
||||
* either version 2 of the GNU General Public License ("GPL") or BSD license
|
||||
* ("BSD").
|
||||
*/
|
||||
|
||||
/*-*************************************
|
||||
* Dependencies
|
||||
***************************************/
|
||||
#include "error_private.h"
|
||||
#include "zstd_internal.h" /* declaration of ZSTD_isError, ZSTD_getErrorName, ZSTD_getErrorCode, ZSTD_getErrorString, ZSTD_versionNumber */
|
||||
#include <linux/kernel.h>
|
||||
|
||||
/*=**************************************************************
|
||||
* Custom allocator
|
||||
****************************************************************/
|
||||
|
||||
#define stack_push(stack, size) \
|
||||
({ \
|
||||
void *const ptr = ZSTD_PTR_ALIGN((stack)->ptr); \
|
||||
(stack)->ptr = (char *)ptr + (size); \
|
||||
(stack)->ptr <= (stack)->end ? ptr : NULL; \
|
||||
})
|
||||
|
||||
ZSTD_customMem ZSTD_initStack(void *workspace, size_t workspaceSize)
|
||||
{
|
||||
ZSTD_customMem stackMem = {ZSTD_stackAlloc, ZSTD_stackFree, workspace};
|
||||
ZSTD_stack *stack = (ZSTD_stack *)workspace;
|
||||
/* Verify preconditions */
|
||||
if (!workspace || workspaceSize < sizeof(ZSTD_stack) || workspace != ZSTD_PTR_ALIGN(workspace)) {
|
||||
ZSTD_customMem error = {NULL, NULL, NULL};
|
||||
return error;
|
||||
}
|
||||
/* Initialize the stack */
|
||||
stack->ptr = workspace;
|
||||
stack->end = (char *)workspace + workspaceSize;
|
||||
stack_push(stack, sizeof(ZSTD_stack));
|
||||
return stackMem;
|
||||
}
|
||||
|
||||
void *ZSTD_stackAllocAll(void *opaque, size_t *size)
|
||||
{
|
||||
ZSTD_stack *stack = (ZSTD_stack *)opaque;
|
||||
*size = (BYTE const *)stack->end - (BYTE *)ZSTD_PTR_ALIGN(stack->ptr);
|
||||
return stack_push(stack, *size);
|
||||
}
|
||||
|
||||
void *ZSTD_stackAlloc(void *opaque, size_t size)
|
||||
{
|
||||
ZSTD_stack *stack = (ZSTD_stack *)opaque;
|
||||
return stack_push(stack, size);
|
||||
}
|
||||
void ZSTD_stackFree(void *opaque, void *address)
|
||||
{
|
||||
(void)opaque;
|
||||
(void)address;
|
||||
}
|
||||
|
||||
void *ZSTD_malloc(size_t size, ZSTD_customMem customMem) { return customMem.customAlloc(customMem.opaque, size); }
|
||||
|
||||
void ZSTD_free(void *ptr, ZSTD_customMem customMem)
|
||||
{
|
||||
if (ptr != NULL)
|
||||
customMem.customFree(customMem.opaque, ptr);
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
/**
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of https://github.com/facebook/zstd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License version 2 as published by the
|
||||
* Free Software Foundation. This program is dual-licensed; you may select
|
||||
* either version 2 of the GNU General Public License ("GPL") or BSD license
|
||||
* ("BSD").
|
||||
*/
|
||||
|
||||
#ifndef ZSTD_CCOMMON_H_MODULE
|
||||
#define ZSTD_CCOMMON_H_MODULE
|
||||
|
||||
/*-*******************************************************
|
||||
* Compiler specifics
|
||||
*********************************************************/
|
||||
#define FORCE_INLINE static __always_inline
|
||||
#define FORCE_NOINLINE static noinline
|
||||
|
||||
/*-*************************************
|
||||
* Dependencies
|
||||
***************************************/
|
||||
#include "error_private.h"
|
||||
#include "mem.h"
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/xxhash.h>
|
||||
#include <linux/zstd.h>
|
||||
|
||||
/*-*************************************
|
||||
* shared macros
|
||||
***************************************/
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#define CHECK_F(f) \
|
||||
{ \
|
||||
size_t const errcod = f; \
|
||||
if (ERR_isError(errcod)) \
|
||||
return errcod; \
|
||||
} /* check and Forward error code */
|
||||
#define CHECK_E(f, e) \
|
||||
{ \
|
||||
size_t const errcod = f; \
|
||||
if (ERR_isError(errcod)) \
|
||||
return ERROR(e); \
|
||||
} /* check and send Error code */
|
||||
#define ZSTD_STATIC_ASSERT(c) \
|
||||
{ \
|
||||
enum { ZSTD_static_assert = 1 / (int)(!!(c)) }; \
|
||||
}
|
||||
|
||||
/*-*************************************
|
||||
* Common constants
|
||||
***************************************/
|
||||
#define ZSTD_OPT_NUM (1 << 12)
|
||||
#define ZSTD_DICT_MAGIC 0xEC30A437 /* v0.7+ */
|
||||
|
||||
#define ZSTD_REP_NUM 3 /* number of repcodes */
|
||||
#define ZSTD_REP_CHECK (ZSTD_REP_NUM) /* number of repcodes to check by the optimal parser */
|
||||
#define ZSTD_REP_MOVE (ZSTD_REP_NUM - 1)
|
||||
#define ZSTD_REP_MOVE_OPT (ZSTD_REP_NUM)
|
||||
static const U32 repStartValue[ZSTD_REP_NUM] = {1, 4, 8};
|
||||
|
||||
#define KB *(1 << 10)
|
||||
#define MB *(1 << 20)
|
||||
#define GB *(1U << 30)
|
||||
|
||||
#define BIT7 128
|
||||
#define BIT6 64
|
||||
#define BIT5 32
|
||||
#define BIT4 16
|
||||
#define BIT1 2
|
||||
#define BIT0 1
|
||||
|
||||
#define ZSTD_WINDOWLOG_ABSOLUTEMIN 10
|
||||
static const size_t ZSTD_fcs_fieldSize[4] = {0, 2, 4, 8};
|
||||
static const size_t ZSTD_did_fieldSize[4] = {0, 1, 2, 4};
|
||||
|
||||
#define ZSTD_BLOCKHEADERSIZE 3 /* C standard doesn't allow `static const` variable to be init using another `static const` variable */
|
||||
static const size_t ZSTD_blockHeaderSize = ZSTD_BLOCKHEADERSIZE;
|
||||
typedef enum { bt_raw, bt_rle, bt_compressed, bt_reserved } blockType_e;
|
||||
|
||||
#define MIN_SEQUENCES_SIZE 1 /* nbSeq==0 */
|
||||
#define MIN_CBLOCK_SIZE (1 /*litCSize*/ + 1 /* RLE or RAW */ + MIN_SEQUENCES_SIZE /* nbSeq==0 */) /* for a non-null block */
|
||||
|
||||
#define HufLog 12
|
||||
typedef enum { set_basic, set_rle, set_compressed, set_repeat } symbolEncodingType_e;
|
||||
|
||||
#define LONGNBSEQ 0x7F00
|
||||
|
||||
#define MINMATCH 3
|
||||
#define EQUAL_READ32 4
|
||||
|
||||
#define Litbits 8
|
||||
#define MaxLit ((1 << Litbits) - 1)
|
||||
#define MaxML 52
|
||||
#define MaxLL 35
|
||||
#define MaxOff 28
|
||||
#define MaxSeq MAX(MaxLL, MaxML) /* Assumption : MaxOff < MaxLL,MaxML */
|
||||
#define MLFSELog 9
|
||||
#define LLFSELog 9
|
||||
#define OffFSELog 8
|
||||
|
||||
static const U32 LL_bits[MaxLL + 1] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
|
||||
static const S16 LL_defaultNorm[MaxLL + 1] = {4, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 1, 1, 1, 1, 1, -1, -1, -1, -1};
|
||||
#define LL_DEFAULTNORMLOG 6 /* for static allocation */
|
||||
static const U32 LL_defaultNormLog = LL_DEFAULTNORMLOG;
|
||||
|
||||
static const U32 ML_bits[MaxML + 1] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
|
||||
static const S16 ML_defaultNorm[MaxML + 1] = {1, 4, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1};
|
||||
#define ML_DEFAULTNORMLOG 6 /* for static allocation */
|
||||
static const U32 ML_defaultNormLog = ML_DEFAULTNORMLOG;
|
||||
|
||||
static const S16 OF_defaultNorm[MaxOff + 1] = {1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1};
|
||||
#define OF_DEFAULTNORMLOG 5 /* for static allocation */
|
||||
static const U32 OF_defaultNormLog = OF_DEFAULTNORMLOG;
|
||||
|
||||
/*-*******************************************
|
||||
* Shared functions to include for inlining
|
||||
*********************************************/
|
||||
ZSTD_STATIC void ZSTD_copy8(void *dst, const void *src) {
|
||||
memcpy(dst, src, 8);
|
||||
}
|
||||
/*! ZSTD_wildcopy() :
|
||||
* custom version of memcpy(), can copy up to 7 bytes too many (8 bytes if length==0) */
|
||||
#define WILDCOPY_OVERLENGTH 8
|
||||
ZSTD_STATIC void ZSTD_wildcopy(void *dst, const void *src, ptrdiff_t length)
|
||||
{
|
||||
const BYTE* ip = (const BYTE*)src;
|
||||
BYTE* op = (BYTE*)dst;
|
||||
BYTE* const oend = op + length;
|
||||
/* Work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81388.
|
||||
* Avoid the bad case where the loop only runs once by handling the
|
||||
* special case separately. This doesn't trigger the bug because it
|
||||
* doesn't involve pointer/integer overflow.
|
||||
*/
|
||||
if (length <= 8)
|
||||
return ZSTD_copy8(dst, src);
|
||||
do {
|
||||
ZSTD_copy8(op, ip);
|
||||
op += 8;
|
||||
ip += 8;
|
||||
} while (op < oend);
|
||||
}
|
||||
|
||||
/*-*******************************************
|
||||
* Private interfaces
|
||||
*********************************************/
|
||||
typedef struct ZSTD_stats_s ZSTD_stats_t;
|
||||
|
||||
typedef struct {
|
||||
U32 off;
|
||||
U32 len;
|
||||
} ZSTD_match_t;
|
||||
|
||||
typedef struct {
|
||||
U32 price;
|
||||
U32 off;
|
||||
U32 mlen;
|
||||
U32 litlen;
|
||||
U32 rep[ZSTD_REP_NUM];
|
||||
} ZSTD_optimal_t;
|
||||
|
||||
typedef struct seqDef_s {
|
||||
U32 offset;
|
||||
U16 litLength;
|
||||
U16 matchLength;
|
||||
} seqDef;
|
||||
|
||||
typedef struct {
|
||||
seqDef *sequencesStart;
|
||||
seqDef *sequences;
|
||||
BYTE *litStart;
|
||||
BYTE *lit;
|
||||
BYTE *llCode;
|
||||
BYTE *mlCode;
|
||||
BYTE *ofCode;
|
||||
U32 longLengthID; /* 0 == no longLength; 1 == Lit.longLength; 2 == Match.longLength; */
|
||||
U32 longLengthPos;
|
||||
/* opt */
|
||||
ZSTD_optimal_t *priceTable;
|
||||
ZSTD_match_t *matchTable;
|
||||
U32 *matchLengthFreq;
|
||||
U32 *litLengthFreq;
|
||||
U32 *litFreq;
|
||||
U32 *offCodeFreq;
|
||||
U32 matchLengthSum;
|
||||
U32 matchSum;
|
||||
U32 litLengthSum;
|
||||
U32 litSum;
|
||||
U32 offCodeSum;
|
||||
U32 log2matchLengthSum;
|
||||
U32 log2matchSum;
|
||||
U32 log2litLengthSum;
|
||||
U32 log2litSum;
|
||||
U32 log2offCodeSum;
|
||||
U32 factor;
|
||||
U32 staticPrices;
|
||||
U32 cachedPrice;
|
||||
U32 cachedLitLength;
|
||||
const BYTE *cachedLiterals;
|
||||
} seqStore_t;
|
||||
|
||||
const seqStore_t *ZSTD_getSeqStore(const ZSTD_CCtx *ctx);
|
||||
void ZSTD_seqToCodes(const seqStore_t *seqStorePtr);
|
||||
int ZSTD_isSkipFrame(ZSTD_DCtx *dctx);
|
||||
|
||||
/*= Custom memory allocation functions */
|
||||
typedef void *(*ZSTD_allocFunction)(void *opaque, size_t size);
|
||||
typedef void (*ZSTD_freeFunction)(void *opaque, void *address);
|
||||
typedef struct {
|
||||
ZSTD_allocFunction customAlloc;
|
||||
ZSTD_freeFunction customFree;
|
||||
void *opaque;
|
||||
} ZSTD_customMem;
|
||||
|
||||
void *ZSTD_malloc(size_t size, ZSTD_customMem customMem);
|
||||
void ZSTD_free(void *ptr, ZSTD_customMem customMem);
|
||||
|
||||
/*====== stack allocation ======*/
|
||||
|
||||
typedef struct {
|
||||
void *ptr;
|
||||
const void *end;
|
||||
} ZSTD_stack;
|
||||
|
||||
#define ZSTD_ALIGN(x) ALIGN(x, sizeof(size_t))
|
||||
#define ZSTD_PTR_ALIGN(p) PTR_ALIGN(p, sizeof(size_t))
|
||||
|
||||
ZSTD_customMem ZSTD_initStack(void *workspace, size_t workspaceSize);
|
||||
|
||||
void *ZSTD_stackAllocAll(void *opaque, size_t *size);
|
||||
void *ZSTD_stackAlloc(void *opaque, size_t size);
|
||||
void ZSTD_stackFree(void *opaque, void *address);
|
||||
|
||||
/*====== common function ======*/
|
||||
|
||||
ZSTD_STATIC U32 ZSTD_highbit32(U32 val) { return 31 - __builtin_clz(val); }
|
||||
|
||||
/* hidden functions */
|
||||
|
||||
/* ZSTD_invalidateRepCodes() :
|
||||
* ensures next compression will not use repcodes from previous block.
|
||||
* Note : only works with regular variant;
|
||||
* do not use with extDict variant ! */
|
||||
void ZSTD_invalidateRepCodes(ZSTD_CCtx *cctx);
|
||||
|
||||
size_t ZSTD_freeCCtx(ZSTD_CCtx *cctx);
|
||||
size_t ZSTD_freeDCtx(ZSTD_DCtx *dctx);
|
||||
size_t ZSTD_freeCDict(ZSTD_CDict *cdict);
|
||||
size_t ZSTD_freeDDict(ZSTD_DDict *cdict);
|
||||
size_t ZSTD_freeCStream(ZSTD_CStream *zcs);
|
||||
size_t ZSTD_freeDStream(ZSTD_DStream *zds);
|
||||
|
||||
#endif /* ZSTD_CCOMMON_H_MODULE */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,37 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_ZSTD_COMPRESS) += zstd_compress.o
|
||||
obj-$(CONFIG_ZSTD_DECOMPRESS) += zstd_decompress.o
|
||||
|
||||
ccflags-y += -O3
|
||||
|
||||
zstd_compress-y := \
|
||||
zstd_compress_module.o \
|
||||
common/debug.o \
|
||||
common/entropy_common.o \
|
||||
common/error_private.o \
|
||||
common/fse_decompress.o \
|
||||
common/zstd_common.o \
|
||||
compress/fse_compress.o \
|
||||
compress/hist.o \
|
||||
compress/huf_compress.o \
|
||||
compress/zstd_compress.o \
|
||||
compress/zstd_compress_literals.o \
|
||||
compress/zstd_compress_sequences.o \
|
||||
compress/zstd_compress_superblock.o \
|
||||
compress/zstd_double_fast.o \
|
||||
compress/zstd_fast.o \
|
||||
compress/zstd_lazy.o \
|
||||
compress/zstd_ldm.o \
|
||||
compress/zstd_opt.o \
|
||||
|
||||
zstd_decompress-y := \
|
||||
zstd_decompress_module.o \
|
||||
common/debug.o \
|
||||
common/entropy_common.o \
|
||||
common/error_private.o \
|
||||
common/fse_decompress.o \
|
||||
common/zstd_common.o \
|
||||
decompress/huf_decompress.o \
|
||||
decompress/zstd_ddict.o \
|
||||
decompress/zstd_decompress.o \
|
||||
decompress/zstd_decompress_block.o \
|
||||
@@ -1,459 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of https://github.com/facebook/zstd.
|
||||
* An additional grant of patent rights can be found in the PATENTS file in the
|
||||
* same directory.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License version 2 as published by the
|
||||
* Free Software Foundation. This program is dual-licensed; you may select
|
||||
* either version 2 of the GNU General Public License ("GPL") or BSD license
|
||||
* ("BSD").
|
||||
*/
|
||||
|
||||
#ifndef LINUX_ZSTD_H
|
||||
#define LINUX_ZSTD_H
|
||||
|
||||
/**
|
||||
* This is a kernel-style API that wraps the upstream zstd API, which cannot be
|
||||
* used directly because the symbols aren't exported. It exposes the minimal
|
||||
* functionality which is currently required by users of zstd in the kernel.
|
||||
* Expose extra functions from lib/zstd/zstd.h as needed.
|
||||
*/
|
||||
|
||||
/* ====== Dependency ====== */
|
||||
#include <linux/types.h>
|
||||
|
||||
/* ====== Helper Functions ====== */
|
||||
/**
|
||||
* zstd_compress_bound() - maximum compressed size in worst case scenario
|
||||
* @src_size: The size of the data to compress.
|
||||
*
|
||||
* Return: The maximum compressed size in the worst case scenario.
|
||||
*/
|
||||
size_t zstd_compress_bound(size_t src_size);
|
||||
|
||||
/**
|
||||
* zstd_is_error() - tells if a size_t function result is an error code
|
||||
* @code: The function result to check for error.
|
||||
*
|
||||
* Return: Non-zero iff the code is an error.
|
||||
*/
|
||||
unsigned int zstd_is_error(size_t code);
|
||||
|
||||
/**
|
||||
* zstd_get_error_code() - translates an error function result to an error code
|
||||
* @code: The function result for which zstd_is_error(code) is true.
|
||||
*
|
||||
* Return: A unique error code for this error.
|
||||
*/
|
||||
int zstd_get_error_code(size_t code);
|
||||
|
||||
/**
|
||||
* zstd_get_error_name() - translates an error function result to a string
|
||||
* @code: The function result for which zstd_is_error(code) is true.
|
||||
*
|
||||
* Return: An error string corresponding to the error code.
|
||||
*/
|
||||
const char *zstd_get_error_name(size_t code);
|
||||
|
||||
/* ====== Parameter Selection ====== */
|
||||
|
||||
/**
|
||||
* enum zstd_strategy - zstd compression search strategy
|
||||
*
|
||||
* From faster to stronger.
|
||||
*/
|
||||
enum zstd_strategy {
|
||||
zstd_fast = 1,
|
||||
zstd_dfast = 2,
|
||||
zstd_greedy = 3,
|
||||
zstd_lazy = 4,
|
||||
zstd_lazy2 = 5,
|
||||
zstd_btlazy2 = 6,
|
||||
zstd_btopt = 7,
|
||||
zstd_btultra = 8,
|
||||
zstd_btultra2 = 9
|
||||
};
|
||||
|
||||
/**
|
||||
* struct zstd_compression_parameters - zstd compression parameters
|
||||
* @window_log: Log of the largest match distance. Larger means more
|
||||
* compression, and more memory needed during decompression.
|
||||
* @chain_log: Fully searched segment. Larger means more compression,
|
||||
* slower, and more memory (useless for fast).
|
||||
* @hash_log: Dispatch table. Larger means more compression,
|
||||
* slower, and more memory.
|
||||
* @search_log: Number of searches. Larger means more compression and slower.
|
||||
* @search_length: Match length searched. Larger means faster decompression,
|
||||
* sometimes less compression.
|
||||
* @target_length: Acceptable match size for optimal parser (only). Larger means
|
||||
* more compression, and slower.
|
||||
* @strategy: The zstd compression strategy.
|
||||
*/
|
||||
struct zstd_compression_parameters {
|
||||
unsigned int window_log;
|
||||
unsigned int chain_log;
|
||||
unsigned int hash_log;
|
||||
unsigned int search_log;
|
||||
unsigned int search_length;
|
||||
unsigned int target_length;
|
||||
enum zstd_strategy strategy;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct zstd_frame_parameters - zstd frame parameters
|
||||
* @content_size_flag: Controls whether content size will be present in the
|
||||
* frame header (when known).
|
||||
* @checksum_flag: Controls whether a 32-bit checksum is generated at the
|
||||
* end of the frame for error detection.
|
||||
* @no_dict_id_flag: Controls whether dictID will be saved into the frame
|
||||
* header when using dictionary compression.
|
||||
*
|
||||
* The default value is all fields set to 0.
|
||||
*/
|
||||
struct zstd_frame_parameters {
|
||||
unsigned int content_size_flag;
|
||||
unsigned int checksum_flag;
|
||||
unsigned int no_dict_id_flag;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct zstd_parameters - zstd parameters
|
||||
* @cparams: The compression parameters.
|
||||
* @fparams: The frame parameters.
|
||||
*/
|
||||
struct zstd_parameters {
|
||||
struct zstd_compression_parameters cparams;
|
||||
struct zstd_frame_parameters fparams;
|
||||
};
|
||||
|
||||
/**
|
||||
* zstd_get_params() - returns zstd_parameters for selected level
|
||||
* @level: The compression level
|
||||
* @estimated_src_size: The estimated source size to compress or 0
|
||||
* if unknown.
|
||||
*
|
||||
* Return: The selected zstd_parameters.
|
||||
*/
|
||||
struct zstd_parameters zstd_get_params(int level,
|
||||
unsigned long long estimated_src_size);
|
||||
|
||||
/* ====== Single-pass Compression ====== */
|
||||
|
||||
typedef struct ZSTD_CCtx_s zstd_cctx;
|
||||
|
||||
/**
|
||||
* zstd_cctx_workspace_bound() - max memory needed to initialize a zstd_cctx
|
||||
* @parameters: The compression parameters to be used.
|
||||
*
|
||||
* If multiple compression parameters might be used, the caller must call
|
||||
* zstd_cctx_workspace_bound() for each set of parameters and use the maximum
|
||||
* size.
|
||||
*
|
||||
* Return: A lower bound on the size of the workspace that is passed to
|
||||
* zstd_init_cctx().
|
||||
*/
|
||||
size_t zstd_cctx_workspace_bound(
|
||||
const struct zstd_compression_parameters *parameters);
|
||||
|
||||
/**
|
||||
* zstd_init_cctx() - initialize a zstd compression context
|
||||
* @workspace: The workspace to emplace the context into. It must outlive
|
||||
* the returned context.
|
||||
* @workspace_size: The size of workspace. Use zstd_cctx_workspace_bound() to
|
||||
* determine how large the workspace must be.
|
||||
*
|
||||
* Return: A zstd compression context or NULL on error.
|
||||
*/
|
||||
zstd_cctx *zstd_init_cctx(void *workspace, size_t workspace_size);
|
||||
|
||||
/**
|
||||
* zstd_compress_cctx() - compress src into dst with the initialized parameters
|
||||
* @cctx: The context. Must have been initialized with zstd_init_cctx().
|
||||
* @dst: The buffer to compress src into.
|
||||
* @dst_capacity: The size of the destination buffer. May be any size, but
|
||||
* ZSTD_compressBound(srcSize) is guaranteed to be large enough.
|
||||
* @src: The data to compress.
|
||||
* @src_size: The size of the data to compress.
|
||||
* @parameters: The compression parameters to be used.
|
||||
*
|
||||
* Return: The compressed size or an error, which can be checked using
|
||||
* zstd_is_error().
|
||||
*/
|
||||
size_t zstd_compress_cctx(zstd_cctx *cctx, void *dst, size_t dst_capacity,
|
||||
const void *src, size_t src_size, const struct zstd_parameters *parameters);
|
||||
|
||||
/* ====== Single-pass Decompression ====== */
|
||||
|
||||
typedef struct ZSTD_DCtx_s zstd_dctx;
|
||||
|
||||
/**
|
||||
* zstd_dctx_workspace_bound() - max memory needed to initialize a zstd_dctx
|
||||
*
|
||||
* Return: A lower bound on the size of the workspace that is passed to
|
||||
* zstd_init_dctx().
|
||||
*/
|
||||
size_t zstd_dctx_workspace_bound(void);
|
||||
|
||||
/**
|
||||
* zstd_init_dctx() - initialize a zstd decompression context
|
||||
* @workspace: The workspace to emplace the context into. It must outlive
|
||||
* the returned context.
|
||||
* @workspace_size: The size of workspace. Use zstd_dctx_workspace_bound() to
|
||||
* determine how large the workspace must be.
|
||||
*
|
||||
* Return: A zstd decompression context or NULL on error.
|
||||
*/
|
||||
zstd_dctx *zstd_init_dctx(void *workspace, size_t workspace_size);
|
||||
|
||||
/**
|
||||
* zstd_decompress_dctx() - decompress zstd compressed src into dst
|
||||
* @dctx: The decompression context.
|
||||
* @dst: The buffer to decompress src into.
|
||||
* @dst_capacity: The size of the destination buffer. Must be at least as large
|
||||
* as the decompressed size. If the caller cannot upper bound the
|
||||
* decompressed size, then it's better to use the streaming API.
|
||||
* @src: The zstd compressed data to decompress. Multiple concatenated
|
||||
* frames and skippable frames are allowed.
|
||||
* @src_size: The exact size of the data to decompress.
|
||||
*
|
||||
* Return: The decompressed size or an error, which can be checked using
|
||||
* zstd_is_error().
|
||||
*/
|
||||
size_t zstd_decompress_dctx(zstd_dctx *dctx, void *dst, size_t dst_capacity,
|
||||
const void *src, size_t src_size);
|
||||
|
||||
/* ====== Streaming Buffers ====== */
|
||||
|
||||
/**
|
||||
* struct zstd_in_buffer - input buffer for streaming
|
||||
* @src: Start of the input buffer.
|
||||
* @size: Size of the input buffer.
|
||||
* @pos: Position where reading stopped. Will be updated.
|
||||
* Necessarily 0 <= pos <= size.
|
||||
*/
|
||||
struct zstd_in_buffer {
|
||||
const void *src;
|
||||
size_t size;
|
||||
size_t pos;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct zstd_out_buffer - output buffer for streaming
|
||||
* @dst: Start of the output buffer.
|
||||
* @size: Size of the output buffer.
|
||||
* @pos: Position where writing stopped. Will be updated.
|
||||
* Necessarily 0 <= pos <= size.
|
||||
*/
|
||||
struct zstd_out_buffer {
|
||||
void *dst;
|
||||
size_t size;
|
||||
size_t pos;
|
||||
};
|
||||
|
||||
/* ====== Streaming Compression ====== */
|
||||
|
||||
typedef struct ZSTD_CCtx_s zstd_cstream;
|
||||
|
||||
/**
|
||||
* zstd_cstream_workspace_bound() - memory needed to initialize a zstd_cstream
|
||||
* @cparams: The compression parameters to be used for compression.
|
||||
*
|
||||
* Return: A lower bound on the size of the workspace that is passed to
|
||||
* zstd_init_cstream().
|
||||
*/
|
||||
size_t zstd_cstream_workspace_bound(
|
||||
const struct zstd_compression_parameters *cparams);
|
||||
|
||||
/**
|
||||
* zstd_init_cstream() - initialize a zstd streaming compression context
|
||||
* @parameters The zstd parameters to use for compression.
|
||||
* @pledged_src_size: If params.fParams.contentSizeFlag == 1 then the caller
|
||||
* must pass the source size (zero means empty source).
|
||||
* Otherwise, the caller may optionally pass the source
|
||||
* size, or zero if unknown.
|
||||
* @workspace: The workspace to emplace the context into. It must outlive
|
||||
* the returned context.
|
||||
* @workspace_size: The size of workspace.
|
||||
* Use zstd_cstream_workspace_bound(params->cparams) to
|
||||
* determine how large the workspace must be.
|
||||
*
|
||||
* Return: The zstd streaming compression context or NULL on error.
|
||||
*/
|
||||
zstd_cstream *zstd_init_cstream(const struct zstd_parameters *parameters,
|
||||
unsigned long long pledged_src_size, void *workspace, size_t workspace_size);
|
||||
|
||||
/**
|
||||
* zstd_reset_cstream() - reset the context using parameters from creation
|
||||
* @cstream: The zstd streaming compression context to reset.
|
||||
* @pledged_src_size: Optionally the source size, or zero if unknown.
|
||||
*
|
||||
* Resets the context using the parameters from creation. Skips dictionary
|
||||
* loading, since it can be reused. If `pledged_src_size` is non-zero the frame
|
||||
* content size is always written into the frame header.
|
||||
*
|
||||
* Return: Zero or an error, which can be checked using
|
||||
* zstd_is_error().
|
||||
*/
|
||||
size_t zstd_reset_cstream(zstd_cstream *cstream,
|
||||
unsigned long long pledged_src_size);
|
||||
|
||||
/**
|
||||
* zstd_compress_stream() - streaming compress some of input into output
|
||||
* @cstream: The zstd streaming compression context.
|
||||
* @output: Destination buffer. `output->pos` is updated to indicate how much
|
||||
* compressed data was written.
|
||||
* @input: Source buffer. `input->pos` is updated to indicate how much data
|
||||
* was read. Note that it may not consume the entire input, in which
|
||||
* case `input->pos < input->size`, and it's up to the caller to
|
||||
* present remaining data again.
|
||||
*
|
||||
* The `input` and `output` buffers may be any size. Guaranteed to make some
|
||||
* forward progress if `input` and `output` are not empty.
|
||||
*
|
||||
* Return: A hint for the number of bytes to use as the input for the next
|
||||
* function call or an error, which can be checked using
|
||||
* zstd_is_error().
|
||||
*/
|
||||
size_t zstd_compress_stream(zstd_cstream *cstream,
|
||||
struct zstd_out_buffer *output, struct zstd_in_buffer *input);
|
||||
|
||||
/**
|
||||
* zstd_flush_stream() - flush internal buffers into output
|
||||
* @cstream: The zstd streaming compression context.
|
||||
* @output: Destination buffer. `output->pos` is updated to indicate how much
|
||||
* compressed data was written.
|
||||
*
|
||||
* zstd_flush_stream() must be called until it returns 0, meaning all the data
|
||||
* has been flushed. Since zstd_flush_stream() causes a block to be ended,
|
||||
* calling it too often will degrade the compression ratio.
|
||||
*
|
||||
* Return: The number of bytes still present within internal buffers or an
|
||||
* error, which can be checked using zstd_is_error().
|
||||
*/
|
||||
size_t zstd_flush_stream(zstd_cstream *cstream, struct zstd_out_buffer *output);
|
||||
|
||||
/**
|
||||
* zstd_end_stream() - flush internal buffers into output and end the frame
|
||||
* @cstream: The zstd streaming compression context.
|
||||
* @output: Destination buffer. `output->pos` is updated to indicate how much
|
||||
* compressed data was written.
|
||||
*
|
||||
* zstd_end_stream() must be called until it returns 0, meaning all the data has
|
||||
* been flushed and the frame epilogue has been written.
|
||||
*
|
||||
* Return: The number of bytes still present within internal buffers or an
|
||||
* error, which can be checked using zstd_is_error().
|
||||
*/
|
||||
size_t zstd_end_stream(zstd_cstream *cstream, struct zstd_out_buffer *output);
|
||||
|
||||
/* ====== Streaming Decompression ====== */
|
||||
|
||||
typedef struct ZSTD_DCtx_s zstd_dstream;
|
||||
|
||||
/**
|
||||
* zstd_dstream_workspace_bound() - memory needed to initialize a zstd_dstream
|
||||
* @max_window_size: The maximum window size allowed for compressed frames.
|
||||
*
|
||||
* Return: A lower bound on the size of the workspace that is passed
|
||||
* to zstd_init_dstream().
|
||||
*/
|
||||
size_t zstd_dstream_workspace_bound(size_t max_window_size);
|
||||
|
||||
/**
|
||||
* zstd_init_dstream() - initialize a zstd streaming decompression context
|
||||
* @max_window_size: The maximum window size allowed for compressed frames.
|
||||
* @workspace: The workspace to emplace the context into. It must outlive
|
||||
* the returned context.
|
||||
* @workspaceSize: The size of workspace.
|
||||
* Use zstd_dstream_workspace_bound(max_window_size) to
|
||||
* determine how large the workspace must be.
|
||||
*
|
||||
* Return: The zstd streaming decompression context.
|
||||
*/
|
||||
zstd_dstream *zstd_init_dstream(size_t max_window_size, void *workspace,
|
||||
size_t workspace_size);
|
||||
|
||||
/**
|
||||
* zstd_reset_dstream() - reset the context using parameters from creation
|
||||
* @dstream: The zstd streaming decompression context to reset.
|
||||
*
|
||||
* Resets the context using the parameters from creation. Skips dictionary
|
||||
* loading, since it can be reused.
|
||||
*
|
||||
* Return: Zero or an error, which can be checked using zstd_is_error().
|
||||
*/
|
||||
size_t zstd_reset_dstream(zstd_dstream *dstream);
|
||||
|
||||
/**
|
||||
* zstd_decompress_stream() - streaming decompress some of input into output
|
||||
* @dstream: The zstd streaming decompression context.
|
||||
* @output: Destination buffer. `output.pos` is updated to indicate how much
|
||||
* decompressed data was written.
|
||||
* @input: Source buffer. `input.pos` is updated to indicate how much data was
|
||||
* read. Note that it may not consume the entire input, in which case
|
||||
* `input.pos < input.size`, and it's up to the caller to present
|
||||
* remaining data again.
|
||||
*
|
||||
* The `input` and `output` buffers may be any size. Guaranteed to make some
|
||||
* forward progress if `input` and `output` are not empty.
|
||||
* zstd_decompress_stream() will not consume the last byte of the frame until
|
||||
* the entire frame is flushed.
|
||||
*
|
||||
* Return: Returns 0 iff a frame is completely decoded and fully flushed.
|
||||
* Otherwise returns a hint for the number of bytes to use as the
|
||||
* input for the next function call or an error, which can be checked
|
||||
* using zstd_is_error(). The size hint will never load more than the
|
||||
* frame.
|
||||
*/
|
||||
size_t zstd_decompress_stream(zstd_dstream *dstream,
|
||||
struct zstd_out_buffer *output, struct zstd_in_buffer *input);
|
||||
|
||||
/* ====== Frame Inspection Functions ====== */
|
||||
|
||||
/**
|
||||
* zstd_find_frame_compressed_size() - returns the size of a compressed frame
|
||||
* @src: Source buffer. It should point to the start of a zstd encoded
|
||||
* frame or a skippable frame.
|
||||
* @src_size: The size of the source buffer. It must be at least as large as the
|
||||
* size of the frame.
|
||||
*
|
||||
* Return: The compressed size of the frame pointed to by `src` or an error,
|
||||
* which can be check with zstd_is_error().
|
||||
* Suitable to pass to ZSTD_decompress() or similar functions.
|
||||
*/
|
||||
size_t zstd_find_frame_compressed_size(const void *src, size_t src_size);
|
||||
|
||||
/**
|
||||
* struct zstd_frame_params - zstd frame parameters stored in the frame header
|
||||
* @frame_content_size: The frame content size, or 0 if not present.
|
||||
* @window_size: The window size, or 0 if the frame is a skippable frame.
|
||||
* @dict_id: The dictionary id, or 0 if not present.
|
||||
* @checksum_flag: Whether a checksum was used.
|
||||
*/
|
||||
struct zstd_frame_params {
|
||||
unsigned long long frame_content_size;
|
||||
unsigned int window_size;
|
||||
unsigned int dict_id;
|
||||
unsigned int checksum_flag;
|
||||
};
|
||||
|
||||
/**
|
||||
* zstd_get_frame_params() - extracts parameters from a zstd or skippable frame
|
||||
* @params: On success the frame parameters are written here.
|
||||
* @src: The source buffer. It must point to a zstd or skippable frame.
|
||||
* @src_size: The size of the source buffer.
|
||||
*
|
||||
* Return: 0 on success. If more data is required it returns how many bytes
|
||||
* must be provided to make forward progress. Otherwise it returns
|
||||
* an error, which can be checked using zstd_is_error().
|
||||
*/
|
||||
size_t zstd_get_frame_params(struct zstd_frame_params *params, const void *src,
|
||||
size_t src_size);
|
||||
|
||||
#endif /* LINUX_ZSTD_H */
|
||||
@@ -1,258 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2020, Yann Collet, Facebook, 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 MEM_H_MODULE
|
||||
#define MEM_H_MODULE
|
||||
|
||||
/*-****************************************
|
||||
* Dependencies
|
||||
******************************************/
|
||||
#include <asm/unaligned.h> /* get_unaligned, put_unaligned* */
|
||||
#include <linux/compiler.h> /* inline */
|
||||
#include <linux/swab.h> /* swab32, swab64 */
|
||||
#include <linux/types.h> /* size_t, ptrdiff_t */
|
||||
#include "debug.h" /* DEBUG_STATIC_ASSERT */
|
||||
|
||||
/*-****************************************
|
||||
* Compiler specifics
|
||||
******************************************/
|
||||
#define MEM_STATIC static inline
|
||||
|
||||
/*-**************************************************************
|
||||
* Basic Types
|
||||
*****************************************************************/
|
||||
typedef uint8_t BYTE;
|
||||
typedef uint16_t U16;
|
||||
typedef int16_t S16;
|
||||
typedef uint32_t U32;
|
||||
typedef int32_t S32;
|
||||
typedef uint64_t U64;
|
||||
typedef int64_t S64;
|
||||
|
||||
/*-**************************************************************
|
||||
* Memory I/O API
|
||||
*****************************************************************/
|
||||
/*=== Static platform detection ===*/
|
||||
MEM_STATIC unsigned MEM_32bits(void);
|
||||
MEM_STATIC unsigned MEM_64bits(void);
|
||||
MEM_STATIC unsigned MEM_isLittleEndian(void);
|
||||
|
||||
/*=== Native unaligned read/write ===*/
|
||||
MEM_STATIC U16 MEM_read16(const void* memPtr);
|
||||
MEM_STATIC U32 MEM_read32(const void* memPtr);
|
||||
MEM_STATIC U64 MEM_read64(const void* memPtr);
|
||||
MEM_STATIC size_t MEM_readST(const void* memPtr);
|
||||
|
||||
MEM_STATIC void MEM_write16(void* memPtr, U16 value);
|
||||
MEM_STATIC void MEM_write32(void* memPtr, U32 value);
|
||||
MEM_STATIC void MEM_write64(void* memPtr, U64 value);
|
||||
|
||||
/*=== Little endian unaligned read/write ===*/
|
||||
MEM_STATIC U16 MEM_readLE16(const void* memPtr);
|
||||
MEM_STATIC U32 MEM_readLE24(const void* memPtr);
|
||||
MEM_STATIC U32 MEM_readLE32(const void* memPtr);
|
||||
MEM_STATIC U64 MEM_readLE64(const void* memPtr);
|
||||
MEM_STATIC size_t MEM_readLEST(const void* memPtr);
|
||||
|
||||
MEM_STATIC void MEM_writeLE16(void* memPtr, U16 val);
|
||||
MEM_STATIC void MEM_writeLE24(void* memPtr, U32 val);
|
||||
MEM_STATIC void MEM_writeLE32(void* memPtr, U32 val32);
|
||||
MEM_STATIC void MEM_writeLE64(void* memPtr, U64 val64);
|
||||
MEM_STATIC void MEM_writeLEST(void* memPtr, size_t val);
|
||||
|
||||
/*=== Big endian unaligned read/write ===*/
|
||||
MEM_STATIC U32 MEM_readBE32(const void* memPtr);
|
||||
MEM_STATIC U64 MEM_readBE64(const void* memPtr);
|
||||
MEM_STATIC size_t MEM_readBEST(const void* memPtr);
|
||||
|
||||
MEM_STATIC void MEM_writeBE32(void* memPtr, U32 val32);
|
||||
MEM_STATIC void MEM_writeBE64(void* memPtr, U64 val64);
|
||||
MEM_STATIC void MEM_writeBEST(void* memPtr, size_t val);
|
||||
|
||||
/*=== Byteswap ===*/
|
||||
MEM_STATIC U32 MEM_swap32(U32 in);
|
||||
MEM_STATIC U64 MEM_swap64(U64 in);
|
||||
MEM_STATIC size_t MEM_swapST(size_t in);
|
||||
|
||||
/*-**************************************************************
|
||||
* Memory I/O Implementation
|
||||
*****************************************************************/
|
||||
MEM_STATIC unsigned MEM_32bits(void)
|
||||
{
|
||||
return sizeof(size_t) == 4;
|
||||
}
|
||||
|
||||
MEM_STATIC unsigned MEM_64bits(void)
|
||||
{
|
||||
return sizeof(size_t) == 8;
|
||||
}
|
||||
|
||||
#if defined(__LITTLE_ENDIAN)
|
||||
#define MEM_LITTLE_ENDIAN 1
|
||||
#else
|
||||
#define MEM_LITTLE_ENDIAN 0
|
||||
#endif
|
||||
|
||||
MEM_STATIC unsigned MEM_isLittleEndian(void)
|
||||
{
|
||||
return MEM_LITTLE_ENDIAN;
|
||||
}
|
||||
|
||||
MEM_STATIC U16 MEM_read16(const void *memPtr)
|
||||
{
|
||||
return get_unaligned((const U16 *)memPtr);
|
||||
}
|
||||
|
||||
MEM_STATIC U32 MEM_read32(const void *memPtr)
|
||||
{
|
||||
return get_unaligned((const U32 *)memPtr);
|
||||
}
|
||||
|
||||
MEM_STATIC U64 MEM_read64(const void *memPtr)
|
||||
{
|
||||
return get_unaligned((const U64 *)memPtr);
|
||||
}
|
||||
|
||||
MEM_STATIC size_t MEM_readST(const void *memPtr)
|
||||
{
|
||||
return get_unaligned((const size_t *)memPtr);
|
||||
}
|
||||
|
||||
MEM_STATIC void MEM_write16(void *memPtr, U16 value)
|
||||
{
|
||||
put_unaligned(value, (U16 *)memPtr);
|
||||
}
|
||||
|
||||
MEM_STATIC void MEM_write32(void *memPtr, U32 value)
|
||||
{
|
||||
put_unaligned(value, (U32 *)memPtr);
|
||||
}
|
||||
|
||||
MEM_STATIC void MEM_write64(void *memPtr, U64 value)
|
||||
{
|
||||
put_unaligned(value, (U64 *)memPtr);
|
||||
}
|
||||
|
||||
/*=== Little endian r/w ===*/
|
||||
|
||||
MEM_STATIC U16 MEM_readLE16(const void *memPtr)
|
||||
{
|
||||
return get_unaligned_le16(memPtr);
|
||||
}
|
||||
|
||||
MEM_STATIC void MEM_writeLE16(void *memPtr, U16 val)
|
||||
{
|
||||
put_unaligned_le16(val, memPtr);
|
||||
}
|
||||
|
||||
MEM_STATIC U32 MEM_readLE24(const void *memPtr)
|
||||
{
|
||||
return MEM_readLE16(memPtr) + (((const BYTE *)memPtr)[2] << 16);
|
||||
}
|
||||
|
||||
MEM_STATIC void MEM_writeLE24(void *memPtr, U32 val)
|
||||
{
|
||||
MEM_writeLE16(memPtr, (U16)val);
|
||||
((BYTE *)memPtr)[2] = (BYTE)(val >> 16);
|
||||
}
|
||||
|
||||
MEM_STATIC U32 MEM_readLE32(const void *memPtr)
|
||||
{
|
||||
return get_unaligned_le32(memPtr);
|
||||
}
|
||||
|
||||
MEM_STATIC void MEM_writeLE32(void *memPtr, U32 val32)
|
||||
{
|
||||
put_unaligned_le32(val32, memPtr);
|
||||
}
|
||||
|
||||
MEM_STATIC U64 MEM_readLE64(const void *memPtr)
|
||||
{
|
||||
return get_unaligned_le64(memPtr);
|
||||
}
|
||||
|
||||
MEM_STATIC void MEM_writeLE64(void *memPtr, U64 val64)
|
||||
{
|
||||
put_unaligned_le64(val64, memPtr);
|
||||
}
|
||||
|
||||
MEM_STATIC size_t MEM_readLEST(const void *memPtr)
|
||||
{
|
||||
if (MEM_32bits())
|
||||
return (size_t)MEM_readLE32(memPtr);
|
||||
else
|
||||
return (size_t)MEM_readLE64(memPtr);
|
||||
}
|
||||
|
||||
MEM_STATIC void MEM_writeLEST(void *memPtr, size_t val)
|
||||
{
|
||||
if (MEM_32bits())
|
||||
MEM_writeLE32(memPtr, (U32)val);
|
||||
else
|
||||
MEM_writeLE64(memPtr, (U64)val);
|
||||
}
|
||||
|
||||
/*=== Big endian r/w ===*/
|
||||
|
||||
MEM_STATIC U32 MEM_readBE32(const void *memPtr)
|
||||
{
|
||||
return get_unaligned_be32(memPtr);
|
||||
}
|
||||
|
||||
MEM_STATIC void MEM_writeBE32(void *memPtr, U32 val32)
|
||||
{
|
||||
put_unaligned_be32(val32, memPtr);
|
||||
}
|
||||
|
||||
MEM_STATIC U64 MEM_readBE64(const void *memPtr)
|
||||
{
|
||||
return get_unaligned_be64(memPtr);
|
||||
}
|
||||
|
||||
MEM_STATIC void MEM_writeBE64(void *memPtr, U64 val64)
|
||||
{
|
||||
put_unaligned_be64(val64, memPtr);
|
||||
}
|
||||
|
||||
MEM_STATIC size_t MEM_readBEST(const void *memPtr)
|
||||
{
|
||||
if (MEM_32bits())
|
||||
return (size_t)MEM_readBE32(memPtr);
|
||||
else
|
||||
return (size_t)MEM_readBE64(memPtr);
|
||||
}
|
||||
|
||||
MEM_STATIC void MEM_writeBEST(void *memPtr, size_t val)
|
||||
{
|
||||
if (MEM_32bits())
|
||||
MEM_writeBE32(memPtr, (U32)val);
|
||||
else
|
||||
MEM_writeBE64(memPtr, (U64)val);
|
||||
}
|
||||
|
||||
MEM_STATIC U32 MEM_swap32(U32 in)
|
||||
{
|
||||
return swab32(in);
|
||||
}
|
||||
|
||||
MEM_STATIC U64 MEM_swap64(U64 in)
|
||||
{
|
||||
return swab64(in);
|
||||
}
|
||||
|
||||
MEM_STATIC size_t MEM_swapST(size_t in)
|
||||
{
|
||||
if (MEM_32bits())
|
||||
return (size_t)MEM_swap32((U32)in);
|
||||
else
|
||||
return (size_t)MEM_swap64((U64)in);
|
||||
}
|
||||
|
||||
#endif /* MEM_H_MODULE */
|
||||
@@ -0,0 +1 @@
|
||||
*Test
|
||||
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, 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).
|
||||
*/
|
||||
|
||||
/*
|
||||
This program takes a file in input,
|
||||
performs a zstd round-trip test (compression - decompress)
|
||||
compares the result with original
|
||||
and generates a crash (double free) on corruption detection.
|
||||
*/
|
||||
|
||||
/*===========================================
|
||||
* Dependencies
|
||||
*==========================================*/
|
||||
#include <stddef.h> /* size_t */
|
||||
#include <stdlib.h> /* malloc, free, exit */
|
||||
#include <stdio.h> /* fprintf */
|
||||
#include <linux/zstd.h>
|
||||
|
||||
/*===========================================
|
||||
* Macros
|
||||
*==========================================*/
|
||||
#define MIN(a,b) ( (a) < (b) ? (a) : (b) )
|
||||
|
||||
static ZSTD_DCtx *dctx = NULL;
|
||||
void *dws = NULL;
|
||||
static void* rBuff = NULL;
|
||||
static size_t buffSize = 0;
|
||||
|
||||
static void crash(int errorCode){
|
||||
/* abort if AFL/libfuzzer, exit otherwise */
|
||||
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION /* could also use __AFL_COMPILER */
|
||||
abort();
|
||||
#else
|
||||
exit(errorCode);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void decompressCheck(const void* srcBuff, size_t srcBuffSize)
|
||||
{
|
||||
size_t const neededBuffSize = 20 * srcBuffSize;
|
||||
|
||||
/* Allocate all buffers and contexts if not already allocated */
|
||||
if (neededBuffSize > buffSize) {
|
||||
free(rBuff);
|
||||
buffSize = 0;
|
||||
|
||||
rBuff = malloc(neededBuffSize);
|
||||
if (!rBuff) {
|
||||
fprintf(stderr, "not enough memory ! \n");
|
||||
crash(1);
|
||||
}
|
||||
buffSize = neededBuffSize;
|
||||
}
|
||||
if (!dctx) {
|
||||
size_t const workspaceSize = ZSTD_DCtxWorkspaceBound();
|
||||
dws = malloc(workspaceSize);
|
||||
if (!dws) {
|
||||
fprintf(stderr, "not enough memory ! \n");
|
||||
crash(1);
|
||||
}
|
||||
dctx = ZSTD_initDCtx(dws, workspaceSize);
|
||||
if (!dctx) {
|
||||
fprintf(stderr, "not enough memory ! \n");
|
||||
crash(1);
|
||||
}
|
||||
}
|
||||
ZSTD_decompressDCtx(dctx, rBuff, buffSize, srcBuff, srcBuffSize);
|
||||
|
||||
#ifndef SKIP_FREE
|
||||
free(dws); dws = NULL; dctx = NULL;
|
||||
free(rBuff); rBuff = NULL;
|
||||
buffSize = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int LLVMFuzzerTestOneInput(const unsigned char *srcBuff, size_t srcBuffSize) {
|
||||
decompressCheck(srcBuff, srcBuffSize);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,34 +1,43 @@
|
||||
|
||||
LINUX := ../linux
|
||||
LINUX_ZSTDLIB := $(LINUX)/lib/zstd
|
||||
IFLAGS := -isystem include/ -I ../include/ -I ../lib/zstd/ -isystem googletest/googletest/include -isystem ../../../lib/common/
|
||||
|
||||
CPPFLAGS += -I$(LINUX)/include -I$(LINUX_ZSTDLIB) -Iinclude -DNDEBUG
|
||||
# Don't poison the workspace, it currently doesn't work with static allocation and workspace reuse
|
||||
CPPFLAGS += -DZSTD_ASAN_DONT_POISON_WORKSPACE
|
||||
SOURCES := $(wildcard ../lib/zstd/*.c)
|
||||
OBJECTS := $(patsubst %.c,%.o,$(SOURCES))
|
||||
|
||||
LINUX_ZSTD_MODULE := $(wildcard $(LINUX_ZSTDLIB)/*.c)
|
||||
LINUX_ZSTD_COMMON := $(wildcard $(LINUX_ZSTDLIB)/common/*.c)
|
||||
LINUX_ZSTD_COMPRESS := $(wildcard $(LINUX_ZSTDLIB)/compress/*.c)
|
||||
LINUX_ZSTD_DECOMPRESS := $(wildcard $(LINUX_ZSTDLIB)/decompress/*.c)
|
||||
LINUX_ZSTD_FILES := $(LINUX_ZSTD_MODULE) $(LINUX_ZSTD_COMMON) $(LINUX_ZSTD_COMPRESS) $(LINUX_ZSTD_DECOMPRESS)
|
||||
LINUX_ZSTD_OBJECTS := $(LINUX_ZSTD_FILES:.c=.o)
|
||||
ARFLAGS := rcs
|
||||
CXXFLAGS += -std=c++11 -g -O3 -Wcast-align
|
||||
CFLAGS += -g -O3 -Wframe-larger-than=400 -Wcast-align
|
||||
CPPFLAGS += $(IFLAGS)
|
||||
|
||||
liblinuxzstd.a: $(LINUX_ZSTD_OBJECTS)
|
||||
../lib/zstd/libzstd.a: $(OBJECTS)
|
||||
$(AR) $(ARFLAGS) $@ $^
|
||||
|
||||
test: test.c liblinuxzstd.a
|
||||
$(CC) $(LDFLAGS) $(CPPFLAGS) $(CFLAGS) $^ -o $@
|
||||
DecompressCrash: DecompressCrash.o $(OBJECTS) libFuzzer.a
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) $^ -o $@
|
||||
|
||||
static_test: static_test.c
|
||||
$(CC) $(LDFLAGS) $(CPPFLAGS) $(CFLAGS) $^ -o $@
|
||||
RoundTripCrash: RoundTripCrash.o $(OBJECTS) ../lib/xxhash.o libFuzzer.a
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) $^ -o $@
|
||||
|
||||
run-test: test static_test
|
||||
./macro-test.sh
|
||||
./test
|
||||
./static_test
|
||||
UserlandTest: UserlandTest.cpp ../lib/zstd/libzstd.a ../lib/xxhash.o
|
||||
$(CXX) $(CXXFLAGS) $(CPPFLAGS) $^ googletest/build/googlemock/gtest/libgtest.a googletest/build/googlemock/gtest/libgtest_main.a -o $@
|
||||
|
||||
XXHashUserlandTest: XXHashUserlandTest.cpp ../lib/xxhash.o ../../../lib/common/xxhash.o
|
||||
$(CXX) $(CXXFLAGS) $(CFLAGS) $(CPPFLAGS) $^ googletest/build/googlemock/gtest/libgtest.a googletest/build/googlemock/gtest/libgtest_main.a -o $@
|
||||
|
||||
# Install libfuzzer
|
||||
libFuzzer.a:
|
||||
@$(RM) -rf Fuzzer
|
||||
@git clone https://chromium.googlesource.com/chromium/llvm-project/llvm/lib/Fuzzer
|
||||
@./Fuzzer/build.sh
|
||||
|
||||
# Install googletest
|
||||
.PHONY: googletest
|
||||
googletest:
|
||||
@$(RM) -rf googletest
|
||||
@git clone https://github.com/google/googletest
|
||||
@mkdir -p googletest/build
|
||||
@cd googletest/build && cmake .. && $(MAKE)
|
||||
|
||||
.PHONY:
|
||||
clean:
|
||||
$(RM) -f $(LINUX_ZSTDLIB)/**/*.o
|
||||
$(RM) -f *.o *.a
|
||||
$(RM) -f test
|
||||
$(RM) -f *.{o,a} ../lib/zstd/*.{o,a} ../lib/*.o
|
||||
$(RM) -f DecompressCrash RoundTripCrash UserlandTest XXHashUserlandTest
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
/**
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, 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).
|
||||
*/
|
||||
|
||||
/*
|
||||
This program takes a file in input,
|
||||
performs a zstd round-trip test (compression - decompress)
|
||||
compares the result with original
|
||||
and generates a crash (double free) on corruption detection.
|
||||
*/
|
||||
|
||||
/*===========================================
|
||||
* Dependencies
|
||||
*==========================================*/
|
||||
#include <stddef.h> /* size_t */
|
||||
#include <stdlib.h> /* malloc, free, exit */
|
||||
#include <stdio.h> /* fprintf */
|
||||
#include <linux/xxhash.h>
|
||||
#include <linux/zstd.h>
|
||||
|
||||
/*===========================================
|
||||
* Macros
|
||||
*==========================================*/
|
||||
#define MIN(a,b) ( (a) < (b) ? (a) : (b) )
|
||||
|
||||
static const int kMaxClevel = 22;
|
||||
|
||||
static ZSTD_CCtx *cctx = NULL;
|
||||
void *cws = NULL;
|
||||
static ZSTD_DCtx *dctx = NULL;
|
||||
void *dws = NULL;
|
||||
static void* cBuff = NULL;
|
||||
static void* rBuff = NULL;
|
||||
static size_t buffSize = 0;
|
||||
|
||||
|
||||
/** roundTripTest() :
|
||||
* Compresses `srcBuff` into `compressedBuff`,
|
||||
* then decompresses `compressedBuff` into `resultBuff`.
|
||||
* Compression level used is derived from first content byte.
|
||||
* @return : result of decompression, which should be == `srcSize`
|
||||
* or an error code if either compression or decompression fails.
|
||||
* Note : `compressedBuffCapacity` should be `>= ZSTD_compressBound(srcSize)`
|
||||
* for compression to be guaranteed to work */
|
||||
static size_t roundTripTest(void* resultBuff, size_t resultBuffCapacity,
|
||||
void* compressedBuff, size_t compressedBuffCapacity,
|
||||
const void* srcBuff, size_t srcBuffSize)
|
||||
{
|
||||
size_t const hashLength = MIN(128, srcBuffSize);
|
||||
unsigned const h32 = xxh32(srcBuff, hashLength, 0);
|
||||
int const cLevel = h32 % kMaxClevel;
|
||||
ZSTD_parameters const params = ZSTD_getParams(cLevel, srcBuffSize, 0);
|
||||
size_t const cSize = ZSTD_compressCCtx(cctx, compressedBuff, compressedBuffCapacity, srcBuff, srcBuffSize, params);
|
||||
if (ZSTD_isError(cSize)) {
|
||||
fprintf(stderr, "Compression error : %u \n", ZSTD_getErrorCode(cSize));
|
||||
return cSize;
|
||||
}
|
||||
return ZSTD_decompressDCtx(dctx, resultBuff, resultBuffCapacity, compressedBuff, cSize);
|
||||
}
|
||||
|
||||
|
||||
static size_t checkBuffers(const void* buff1, const void* buff2, size_t buffSize)
|
||||
{
|
||||
const char* ip1 = (const char*)buff1;
|
||||
const char* ip2 = (const char*)buff2;
|
||||
size_t pos;
|
||||
|
||||
for (pos=0; pos<buffSize; pos++)
|
||||
if (ip1[pos]!=ip2[pos])
|
||||
break;
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
static void crash(int errorCode){
|
||||
/* abort if AFL/libfuzzer, exit otherwise */
|
||||
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION /* could also use __AFL_COMPILER */
|
||||
abort();
|
||||
#else
|
||||
exit(errorCode);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void roundTripCheck(const void* srcBuff, size_t srcBuffSize)
|
||||
{
|
||||
size_t const neededBuffSize = ZSTD_compressBound(srcBuffSize);
|
||||
|
||||
/* Allocate all buffers and contexts if not already allocated */
|
||||
if (neededBuffSize > buffSize) {
|
||||
free(cBuff);
|
||||
free(rBuff);
|
||||
buffSize = 0;
|
||||
|
||||
cBuff = malloc(neededBuffSize);
|
||||
rBuff = malloc(neededBuffSize);
|
||||
if (!cBuff || !rBuff) {
|
||||
fprintf(stderr, "not enough memory ! \n");
|
||||
crash(1);
|
||||
}
|
||||
buffSize = neededBuffSize;
|
||||
}
|
||||
if (!cctx) {
|
||||
ZSTD_compressionParameters const params = ZSTD_getCParams(kMaxClevel, 0, 0);
|
||||
size_t const workspaceSize = ZSTD_CCtxWorkspaceBound(params);
|
||||
cws = malloc(workspaceSize);
|
||||
if (!cws) {
|
||||
fprintf(stderr, "not enough memory ! \n");
|
||||
crash(1);
|
||||
}
|
||||
cctx = ZSTD_initCCtx(cws, workspaceSize);
|
||||
if (!cctx) {
|
||||
fprintf(stderr, "not enough memory ! \n");
|
||||
crash(1);
|
||||
}
|
||||
}
|
||||
if (!dctx) {
|
||||
size_t const workspaceSize = ZSTD_DCtxWorkspaceBound();
|
||||
dws = malloc(workspaceSize);
|
||||
if (!dws) {
|
||||
fprintf(stderr, "not enough memory ! \n");
|
||||
crash(1);
|
||||
}
|
||||
dctx = ZSTD_initDCtx(dws, workspaceSize);
|
||||
if (!dctx) {
|
||||
fprintf(stderr, "not enough memory ! \n");
|
||||
crash(1);
|
||||
}
|
||||
}
|
||||
|
||||
{ size_t const result = roundTripTest(rBuff, buffSize, cBuff, buffSize, srcBuff, srcBuffSize);
|
||||
if (ZSTD_isError(result)) {
|
||||
fprintf(stderr, "roundTripTest error : %u \n", ZSTD_getErrorCode(result));
|
||||
crash(1);
|
||||
}
|
||||
if (result != srcBuffSize) {
|
||||
fprintf(stderr, "Incorrect regenerated size : %u != %u\n", (unsigned)result, (unsigned)srcBuffSize);
|
||||
crash(1);
|
||||
}
|
||||
if (checkBuffers(srcBuff, rBuff, srcBuffSize) != srcBuffSize) {
|
||||
fprintf(stderr, "Silent decoding corruption !!!");
|
||||
crash(1);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef SKIP_FREE
|
||||
free(cws); cws = NULL; cctx = NULL;
|
||||
free(dws); dws = NULL; dctx = NULL;
|
||||
free(cBuff); cBuff = NULL;
|
||||
free(rBuff); rBuff = NULL;
|
||||
buffSize = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int LLVMFuzzerTestOneInput(const unsigned char *srcBuff, size_t srcBuffSize) {
|
||||
roundTripCheck(srcBuff, srcBuffSize);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,565 @@
|
||||
extern "C" {
|
||||
#include <linux/zstd.h>
|
||||
}
|
||||
#include <gtest/gtest.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace {
|
||||
struct WorkspaceDeleter {
|
||||
void *memory;
|
||||
|
||||
template <typename T> void operator()(T const *) { free(memory); }
|
||||
};
|
||||
|
||||
std::unique_ptr<ZSTD_CCtx, WorkspaceDeleter>
|
||||
createCCtx(ZSTD_compressionParameters cParams) {
|
||||
size_t const workspaceSize = ZSTD_CCtxWorkspaceBound(cParams);
|
||||
void *workspace = malloc(workspaceSize);
|
||||
std::unique_ptr<ZSTD_CCtx, WorkspaceDeleter> cctx{
|
||||
ZSTD_initCCtx(workspace, workspaceSize), WorkspaceDeleter{workspace}};
|
||||
if (!cctx) {
|
||||
throw std::runtime_error{"Bad cctx"};
|
||||
}
|
||||
return cctx;
|
||||
}
|
||||
|
||||
std::unique_ptr<ZSTD_CCtx, WorkspaceDeleter>
|
||||
createCCtx(int level, unsigned long long estimatedSrcSize = 0,
|
||||
size_t dictSize = 0) {
|
||||
auto const cParams = ZSTD_getCParams(level, estimatedSrcSize, dictSize);
|
||||
return createCCtx(cParams);
|
||||
}
|
||||
|
||||
std::unique_ptr<ZSTD_DCtx, WorkspaceDeleter>
|
||||
createDCtx() {
|
||||
size_t const workspaceSize = ZSTD_DCtxWorkspaceBound();
|
||||
void *workspace = malloc(workspaceSize);
|
||||
std::unique_ptr<ZSTD_DCtx, WorkspaceDeleter> dctx{
|
||||
ZSTD_initDCtx(workspace, workspaceSize), WorkspaceDeleter{workspace}};
|
||||
if (!dctx) {
|
||||
throw std::runtime_error{"Bad dctx"};
|
||||
}
|
||||
return dctx;
|
||||
}
|
||||
|
||||
std::unique_ptr<ZSTD_CDict, WorkspaceDeleter>
|
||||
createCDict(std::string const& dict, ZSTD_parameters params) {
|
||||
size_t const workspaceSize = ZSTD_CDictWorkspaceBound(params.cParams);
|
||||
void *workspace = malloc(workspaceSize);
|
||||
std::unique_ptr<ZSTD_CDict, WorkspaceDeleter> cdict{
|
||||
ZSTD_initCDict(dict.data(), dict.size(), params, workspace,
|
||||
workspaceSize),
|
||||
WorkspaceDeleter{workspace}};
|
||||
if (!cdict) {
|
||||
throw std::runtime_error{"Bad cdict"};
|
||||
}
|
||||
return cdict;
|
||||
}
|
||||
|
||||
std::unique_ptr<ZSTD_CDict, WorkspaceDeleter>
|
||||
createCDict(std::string const& dict, int level) {
|
||||
auto const params = ZSTD_getParams(level, 0, dict.size());
|
||||
return createCDict(dict, params);
|
||||
}
|
||||
|
||||
std::unique_ptr<ZSTD_DDict, WorkspaceDeleter>
|
||||
createDDict(std::string const& dict) {
|
||||
size_t const workspaceSize = ZSTD_DDictWorkspaceBound();
|
||||
void *workspace = malloc(workspaceSize);
|
||||
std::unique_ptr<ZSTD_DDict, WorkspaceDeleter> ddict{
|
||||
ZSTD_initDDict(dict.data(), dict.size(), workspace, workspaceSize),
|
||||
WorkspaceDeleter{workspace}};
|
||||
if (!ddict) {
|
||||
throw std::runtime_error{"Bad ddict"};
|
||||
}
|
||||
return ddict;
|
||||
}
|
||||
|
||||
std::unique_ptr<ZSTD_CStream, WorkspaceDeleter>
|
||||
createCStream(ZSTD_parameters params, unsigned long long pledgedSrcSize = 0) {
|
||||
size_t const workspaceSize = ZSTD_CStreamWorkspaceBound(params.cParams);
|
||||
void *workspace = malloc(workspaceSize);
|
||||
std::unique_ptr<ZSTD_CStream, WorkspaceDeleter> zcs{
|
||||
ZSTD_initCStream(params, pledgedSrcSize, workspace, workspaceSize)};
|
||||
if (!zcs) {
|
||||
throw std::runtime_error{"bad cstream"};
|
||||
}
|
||||
return zcs;
|
||||
}
|
||||
|
||||
std::unique_ptr<ZSTD_CStream, WorkspaceDeleter>
|
||||
createCStream(ZSTD_compressionParameters cParams, ZSTD_CDict const &cdict,
|
||||
unsigned long long pledgedSrcSize = 0) {
|
||||
size_t const workspaceSize = ZSTD_CStreamWorkspaceBound(cParams);
|
||||
void *workspace = malloc(workspaceSize);
|
||||
std::unique_ptr<ZSTD_CStream, WorkspaceDeleter> zcs{
|
||||
ZSTD_initCStream_usingCDict(&cdict, pledgedSrcSize, workspace,
|
||||
workspaceSize)};
|
||||
if (!zcs) {
|
||||
throw std::runtime_error{"bad cstream"};
|
||||
}
|
||||
return zcs;
|
||||
}
|
||||
|
||||
std::unique_ptr<ZSTD_CStream, WorkspaceDeleter>
|
||||
createCStream(int level, unsigned long long pledgedSrcSize = 0) {
|
||||
auto const params = ZSTD_getParams(level, pledgedSrcSize, 0);
|
||||
return createCStream(params, pledgedSrcSize);
|
||||
}
|
||||
|
||||
std::unique_ptr<ZSTD_DStream, WorkspaceDeleter>
|
||||
createDStream(size_t maxWindowSize = (1ULL << ZSTD_WINDOWLOG_MAX),
|
||||
ZSTD_DDict const *ddict = nullptr) {
|
||||
size_t const workspaceSize = ZSTD_DStreamWorkspaceBound(maxWindowSize);
|
||||
void *workspace = malloc(workspaceSize);
|
||||
std::unique_ptr<ZSTD_DStream, WorkspaceDeleter> zds{
|
||||
ddict == nullptr
|
||||
? ZSTD_initDStream(maxWindowSize, workspace, workspaceSize)
|
||||
: ZSTD_initDStream_usingDDict(maxWindowSize, ddict, workspace,
|
||||
workspaceSize)};
|
||||
if (!zds) {
|
||||
throw std::runtime_error{"bad dstream"};
|
||||
}
|
||||
return zds;
|
||||
}
|
||||
|
||||
std::string compress(ZSTD_CCtx &cctx, std::string const &data,
|
||||
ZSTD_parameters params, std::string const &dict = "") {
|
||||
std::string compressed;
|
||||
compressed.resize(ZSTD_compressBound(data.size()));
|
||||
size_t const rc =
|
||||
dict.empty()
|
||||
? ZSTD_compressCCtx(&cctx, &compressed[0], compressed.size(),
|
||||
data.data(), data.size(), params)
|
||||
: ZSTD_compress_usingDict(&cctx, &compressed[0], compressed.size(),
|
||||
data.data(), data.size(), dict.data(),
|
||||
dict.size(), params);
|
||||
if (ZSTD_isError(rc)) {
|
||||
throw std::runtime_error{"compression error"};
|
||||
}
|
||||
compressed.resize(rc);
|
||||
return compressed;
|
||||
}
|
||||
|
||||
std::string compress(ZSTD_CCtx& cctx, std::string const& data, int level, std::string const& dict = "") {
|
||||
auto const params = ZSTD_getParams(level, 0, dict.size());
|
||||
return compress(cctx, data, params, dict);
|
||||
}
|
||||
|
||||
std::string decompress(ZSTD_DCtx& dctx, std::string const& compressed, size_t decompressedSize, std::string const& dict = "") {
|
||||
std::string decompressed;
|
||||
decompressed.resize(decompressedSize);
|
||||
size_t const rc =
|
||||
dict.empty()
|
||||
? ZSTD_decompressDCtx(&dctx, &decompressed[0], decompressed.size(),
|
||||
compressed.data(), compressed.size())
|
||||
: ZSTD_decompress_usingDict(
|
||||
&dctx, &decompressed[0], decompressed.size(), compressed.data(),
|
||||
compressed.size(), dict.data(), dict.size());
|
||||
if (ZSTD_isError(rc)) {
|
||||
throw std::runtime_error{"decompression error"};
|
||||
}
|
||||
decompressed.resize(rc);
|
||||
return decompressed;
|
||||
}
|
||||
|
||||
std::string compress(ZSTD_CCtx& cctx, std::string const& data, ZSTD_CDict& cdict) {
|
||||
std::string compressed;
|
||||
compressed.resize(ZSTD_compressBound(data.size()));
|
||||
size_t const rc =
|
||||
ZSTD_compress_usingCDict(&cctx, &compressed[0], compressed.size(),
|
||||
data.data(), data.size(), &cdict);
|
||||
if (ZSTD_isError(rc)) {
|
||||
throw std::runtime_error{"compression error"};
|
||||
}
|
||||
compressed.resize(rc);
|
||||
return compressed;
|
||||
}
|
||||
|
||||
std::string decompress(ZSTD_DCtx& dctx, std::string const& compressed, size_t decompressedSize, ZSTD_DDict& ddict) {
|
||||
std::string decompressed;
|
||||
decompressed.resize(decompressedSize);
|
||||
size_t const rc =
|
||||
ZSTD_decompress_usingDDict(&dctx, &decompressed[0], decompressed.size(),
|
||||
compressed.data(), compressed.size(), &ddict);
|
||||
if (ZSTD_isError(rc)) {
|
||||
throw std::runtime_error{"decompression error"};
|
||||
}
|
||||
decompressed.resize(rc);
|
||||
return decompressed;
|
||||
}
|
||||
|
||||
std::string compress(ZSTD_CStream& zcs, std::string const& data) {
|
||||
std::string compressed;
|
||||
compressed.resize(ZSTD_compressBound(data.size()));
|
||||
ZSTD_inBuffer in = {data.data(), data.size(), 0};
|
||||
ZSTD_outBuffer out = {&compressed[0], compressed.size(), 0};
|
||||
while (in.pos != in.size) {
|
||||
size_t const rc = ZSTD_compressStream(&zcs, &out, &in);
|
||||
if (ZSTD_isError(rc)) {
|
||||
throw std::runtime_error{"compress stream failed"};
|
||||
}
|
||||
}
|
||||
size_t const rc = ZSTD_endStream(&zcs, &out);
|
||||
if (rc != 0) {
|
||||
throw std::runtime_error{"compress end failed"};
|
||||
}
|
||||
compressed.resize(out.pos);
|
||||
return compressed;
|
||||
}
|
||||
|
||||
std::string decompress(ZSTD_DStream &zds, std::string const &compressed,
|
||||
size_t decompressedSize) {
|
||||
std::string decompressed;
|
||||
decompressed.resize(decompressedSize);
|
||||
ZSTD_inBuffer in = {compressed.data(), compressed.size(), 0};
|
||||
ZSTD_outBuffer out = {&decompressed[0], decompressed.size(), 0};
|
||||
while (in.pos != in.size) {
|
||||
size_t const rc = ZSTD_decompressStream(&zds, &out, &in);
|
||||
if (ZSTD_isError(rc)) {
|
||||
throw std::runtime_error{"decompress stream failed"};
|
||||
}
|
||||
}
|
||||
decompressed.resize(out.pos);
|
||||
return decompressed;
|
||||
}
|
||||
|
||||
std::string makeData(size_t size) {
|
||||
std::string result;
|
||||
result.reserve(size + 20);
|
||||
while (result.size() < size) {
|
||||
result += "Hello world";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string const kData = "Hello world";
|
||||
std::string const kPlainDict = makeData(10000);
|
||||
std::string const kZstdDict{
|
||||
"\x37\xA4\x30\xEC\x99\x69\x58\x1C\x21\x10\xD8\x4A\x84\x01\xCC\xF3"
|
||||
"\x3C\xCF\x9B\x25\xBB\xC9\x6E\xB2\x9B\xEC\x26\xAD\xCF\xDF\x4E\xCD"
|
||||
"\xF3\x2C\x3A\x21\x84\x10\x42\x08\x21\x01\x33\xF1\x78\x3C\x1E\x8F"
|
||||
"\xC7\xE3\xF1\x78\x3C\xCF\xF3\xBC\xF7\xD4\x42\x41\x41\x41\x41\x41"
|
||||
"\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41"
|
||||
"\x41\x41\x41\x41\xA1\x50\x28\x14\x0A\x85\x42\xA1\x50\x28\x14\x0A"
|
||||
"\x85\xA2\x28\x8A\xA2\x28\x4A\x29\x7D\x74\xE1\xE1\xE1\xE1\xE1\xE1"
|
||||
"\xE1\xE1\xE1\xE1\xE1\xE1\xE1\xE1\xE1\xE1\xE1\xE1\xE1\xF1\x78\x3C"
|
||||
"\x1E\x8F\xC7\xE3\xF1\x78\x9E\xE7\x79\xEF\x01\x01\x00\x00\x00\x04"
|
||||
"\x00\x00\x00\x08\x00\x00\x00"
|
||||
"0123456789",
|
||||
161};
|
||||
}
|
||||
|
||||
TEST(Block, CCtx) {
|
||||
auto cctx = createCCtx(1);
|
||||
auto const compressed = compress(*cctx, kData, 1);
|
||||
auto dctx = createDCtx();
|
||||
auto const decompressed = decompress(*dctx, compressed, kData.size());
|
||||
EXPECT_EQ(kData, decompressed);
|
||||
}
|
||||
|
||||
TEST(Block, NoContentSize) {
|
||||
auto cctx = createCCtx(1);
|
||||
auto const c = compress(*cctx, kData, 1);
|
||||
auto const size = ZSTD_findDecompressedSize(c.data(), c.size());
|
||||
EXPECT_EQ(ZSTD_CONTENTSIZE_UNKNOWN, size);
|
||||
}
|
||||
|
||||
TEST(Block, ContentSize) {
|
||||
auto cctx = createCCtx(1);
|
||||
auto params = ZSTD_getParams(1, 0, 0);
|
||||
params.fParams.contentSizeFlag = 1;
|
||||
auto const c = compress(*cctx, kData, params);
|
||||
auto const size = ZSTD_findDecompressedSize(c.data(), c.size());
|
||||
EXPECT_EQ(kData.size(), size);
|
||||
}
|
||||
|
||||
TEST(Block, CCtxLevelIncrease) {
|
||||
std::string c;
|
||||
auto cctx = createCCtx(22);
|
||||
auto dctx = createDCtx();
|
||||
for (int level = 1; level <= 22; ++level) {
|
||||
auto compressed = compress(*cctx, kData, level);
|
||||
auto const decompressed = decompress(*dctx, compressed, kData.size());
|
||||
EXPECT_EQ(kData, decompressed);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Block, PlainDict) {
|
||||
auto cctx = createCCtx(1);
|
||||
auto const compressed = compress(*cctx, kData, 1, kPlainDict);
|
||||
auto dctx = createDCtx();
|
||||
EXPECT_ANY_THROW(decompress(*dctx, compressed, kData.size()));
|
||||
auto const decompressed =
|
||||
decompress(*dctx, compressed, kData.size(), kPlainDict);
|
||||
EXPECT_EQ(kData, decompressed);
|
||||
}
|
||||
|
||||
TEST(Block, ZstdDict) {
|
||||
auto cctx = createCCtx(1);
|
||||
auto const compressed = compress(*cctx, kData, 1, kZstdDict);
|
||||
auto dctx = createDCtx();
|
||||
EXPECT_ANY_THROW(decompress(*dctx, compressed, kData.size()));
|
||||
auto const decompressed =
|
||||
decompress(*dctx, compressed, kData.size(), kZstdDict);
|
||||
EXPECT_EQ(kData, decompressed);
|
||||
}
|
||||
|
||||
TEST(Block, PreprocessedPlainDict) {
|
||||
auto cctx = createCCtx(1);
|
||||
auto const cdict = createCDict(kPlainDict, 1);
|
||||
auto const compressed = compress(*cctx, kData, *cdict);
|
||||
auto dctx = createDCtx();
|
||||
auto const ddict = createDDict(kPlainDict);
|
||||
EXPECT_ANY_THROW(decompress(*dctx, compressed, kData.size()));
|
||||
auto const decompressed =
|
||||
decompress(*dctx, compressed, kData.size(), *ddict);
|
||||
EXPECT_EQ(kData, decompressed);
|
||||
}
|
||||
|
||||
TEST(Block, PreprocessedZstdDict) {
|
||||
auto cctx = createCCtx(1);
|
||||
auto const cdict = createCDict(kZstdDict, 1);
|
||||
auto const compressed = compress(*cctx, kData, *cdict);
|
||||
auto dctx = createDCtx();
|
||||
auto const ddict = createDDict(kZstdDict);
|
||||
EXPECT_ANY_THROW(decompress(*dctx, compressed, kData.size()));
|
||||
auto const decompressed =
|
||||
decompress(*dctx, compressed, kData.size(), *ddict);
|
||||
EXPECT_EQ(kData, decompressed);
|
||||
}
|
||||
|
||||
TEST(Block, ReinitializeCCtx) {
|
||||
auto cctx = createCCtx(1);
|
||||
{
|
||||
auto const compressed = compress(*cctx, kData, 1);
|
||||
auto dctx = createDCtx();
|
||||
auto const decompressed = decompress(*dctx, compressed, kData.size());
|
||||
EXPECT_EQ(kData, decompressed);
|
||||
}
|
||||
// Create the cctx with the same memory
|
||||
auto d = cctx.get_deleter();
|
||||
auto raw = cctx.release();
|
||||
auto params = ZSTD_getParams(1, 0, 0);
|
||||
cctx.reset(
|
||||
ZSTD_initCCtx(d.memory, ZSTD_CCtxWorkspaceBound(params.cParams)));
|
||||
// Repeat
|
||||
{
|
||||
auto const compressed = compress(*cctx, kData, 1);
|
||||
auto dctx = createDCtx();
|
||||
auto const decompressed = decompress(*dctx, compressed, kData.size());
|
||||
EXPECT_EQ(kData, decompressed);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Block, ReinitializeDCtx) {
|
||||
auto dctx = createDCtx();
|
||||
{
|
||||
auto cctx = createCCtx(1);
|
||||
auto const compressed = compress(*cctx, kData, 1);
|
||||
auto const decompressed = decompress(*dctx, compressed, kData.size());
|
||||
EXPECT_EQ(kData, decompressed);
|
||||
}
|
||||
// Create the cctx with the same memory
|
||||
auto d = dctx.get_deleter();
|
||||
auto raw = dctx.release();
|
||||
dctx.reset(ZSTD_initDCtx(d.memory, ZSTD_DCtxWorkspaceBound()));
|
||||
// Repeat
|
||||
{
|
||||
auto cctx = createCCtx(1);
|
||||
auto const compressed = compress(*cctx, kData, 1);
|
||||
auto dctx = createDCtx();
|
||||
auto const decompressed = decompress(*dctx, compressed, kData.size());
|
||||
EXPECT_EQ(kData, decompressed);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Stream, Basic) {
|
||||
auto zcs = createCStream(1);
|
||||
auto const compressed = compress(*zcs, kData);
|
||||
auto zds = createDStream();
|
||||
auto const decompressed = decompress(*zds, compressed, kData.size());
|
||||
EXPECT_EQ(kData, decompressed);
|
||||
}
|
||||
|
||||
TEST(Stream, PlainDict) {
|
||||
auto params = ZSTD_getParams(1, kData.size(), kPlainDict.size());
|
||||
params.cParams.windowLog = 17;
|
||||
auto cdict = createCDict(kPlainDict, params);
|
||||
auto zcs = createCStream(params.cParams, *cdict, kData.size());
|
||||
auto const compressed = compress(*zcs, kData);
|
||||
auto const contentSize =
|
||||
ZSTD_findDecompressedSize(compressed.data(), compressed.size());
|
||||
EXPECT_ANY_THROW(decompress(*createDStream(), compressed, kData.size()));
|
||||
auto ddict = createDDict(kPlainDict);
|
||||
auto zds = createDStream(1 << 17, ddict.get());
|
||||
auto const decompressed = decompress(*zds, compressed, kData.size());
|
||||
EXPECT_EQ(kData, decompressed);
|
||||
}
|
||||
|
||||
TEST(Stream, ZstdDict) {
|
||||
auto params = ZSTD_getParams(1, 0, 0);
|
||||
params.cParams.windowLog = 17;
|
||||
auto cdict = createCDict(kZstdDict, 1);
|
||||
auto zcs = createCStream(params.cParams, *cdict);
|
||||
auto const compressed = compress(*zcs, kData);
|
||||
EXPECT_ANY_THROW(decompress(*createDStream(), compressed, kData.size()));
|
||||
auto ddict = createDDict(kZstdDict);
|
||||
auto zds = createDStream(1 << 17, ddict.get());
|
||||
auto const decompressed = decompress(*zds, compressed, kData.size());
|
||||
EXPECT_EQ(kData, decompressed);
|
||||
}
|
||||
|
||||
TEST(Stream, ResetCStream) {
|
||||
auto zcs = createCStream(1);
|
||||
auto zds = createDStream();
|
||||
{
|
||||
auto const compressed = compress(*zcs, kData);
|
||||
auto const decompressed = decompress(*zds, compressed, kData.size());
|
||||
EXPECT_EQ(kData, decompressed);
|
||||
}
|
||||
{
|
||||
ZSTD_resetCStream(zcs.get(), 0);
|
||||
auto const compressed = compress(*zcs, kData);
|
||||
auto const decompressed = decompress(*zds, compressed, kData.size());
|
||||
EXPECT_EQ(kData, decompressed);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Stream, ResetDStream) {
|
||||
auto zcs = createCStream(1);
|
||||
auto zds = createDStream();
|
||||
auto const compressed = compress(*zcs, kData);
|
||||
EXPECT_ANY_THROW(decompress(*zds, kData, kData.size()));
|
||||
EXPECT_ANY_THROW(decompress(*zds, compressed, kData.size()));
|
||||
ZSTD_resetDStream(zds.get());
|
||||
auto const decompressed = decompress(*zds, compressed, kData.size());
|
||||
EXPECT_EQ(kData, decompressed);
|
||||
}
|
||||
|
||||
TEST(Stream, Flush) {
|
||||
auto zcs = createCStream(1);
|
||||
auto zds = createDStream();
|
||||
std::string compressed;
|
||||
{
|
||||
compressed.resize(ZSTD_compressBound(kData.size()));
|
||||
ZSTD_inBuffer in = {kData.data(), kData.size(), 0};
|
||||
ZSTD_outBuffer out = {&compressed[0], compressed.size(), 0};
|
||||
while (in.pos != in.size) {
|
||||
size_t const rc = ZSTD_compressStream(zcs.get(), &out, &in);
|
||||
if (ZSTD_isError(rc)) {
|
||||
throw std::runtime_error{"compress stream failed"};
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(0, out.pos);
|
||||
size_t const rc = ZSTD_flushStream(zcs.get(), &out);
|
||||
if (rc != 0) {
|
||||
throw std::runtime_error{"compress end failed"};
|
||||
}
|
||||
compressed.resize(out.pos);
|
||||
EXPECT_LT(0, out.pos);
|
||||
}
|
||||
std::string decompressed;
|
||||
{
|
||||
decompressed.resize(kData.size());
|
||||
ZSTD_inBuffer in = {compressed.data(), compressed.size(), 0};
|
||||
ZSTD_outBuffer out = {&decompressed[0], decompressed.size(), 0};
|
||||
while (in.pos != in.size) {
|
||||
size_t const rc = ZSTD_decompressStream(zds.get(), &out, &in);
|
||||
if (ZSTD_isError(rc)) {
|
||||
throw std::runtime_error{"decompress stream failed"};
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(kData, decompressed);
|
||||
}
|
||||
|
||||
TEST(Stream, DStreamLevelIncrease) {
|
||||
auto zds = createDStream();
|
||||
for (int level = 1; level <= 22; ++level) {
|
||||
auto zcs = createCStream(level);
|
||||
auto compressed = compress(*zcs, kData);
|
||||
ZSTD_resetDStream(zds.get());
|
||||
auto const decompressed = decompress(*zds, compressed, kData.size());
|
||||
EXPECT_EQ(kData, decompressed);
|
||||
}
|
||||
}
|
||||
|
||||
#define TEST_SYMBOL(symbol) \
|
||||
do { \
|
||||
extern void *__##symbol; \
|
||||
EXPECT_NE((void *)0, __##symbol); \
|
||||
} while (0)
|
||||
|
||||
TEST(API, Symbols) {
|
||||
TEST_SYMBOL(ZSTD_CCtxWorkspaceBound);
|
||||
TEST_SYMBOL(ZSTD_initCCtx);
|
||||
TEST_SYMBOL(ZSTD_compressCCtx);
|
||||
TEST_SYMBOL(ZSTD_compress_usingDict);
|
||||
TEST_SYMBOL(ZSTD_DCtxWorkspaceBound);
|
||||
TEST_SYMBOL(ZSTD_initDCtx);
|
||||
TEST_SYMBOL(ZSTD_decompressDCtx);
|
||||
TEST_SYMBOL(ZSTD_decompress_usingDict);
|
||||
|
||||
TEST_SYMBOL(ZSTD_CDictWorkspaceBound);
|
||||
TEST_SYMBOL(ZSTD_initCDict);
|
||||
TEST_SYMBOL(ZSTD_compress_usingCDict);
|
||||
TEST_SYMBOL(ZSTD_DDictWorkspaceBound);
|
||||
TEST_SYMBOL(ZSTD_initDDict);
|
||||
TEST_SYMBOL(ZSTD_decompress_usingDDict);
|
||||
|
||||
TEST_SYMBOL(ZSTD_CStreamWorkspaceBound);
|
||||
TEST_SYMBOL(ZSTD_initCStream);
|
||||
TEST_SYMBOL(ZSTD_initCStream_usingCDict);
|
||||
TEST_SYMBOL(ZSTD_resetCStream);
|
||||
TEST_SYMBOL(ZSTD_compressStream);
|
||||
TEST_SYMBOL(ZSTD_flushStream);
|
||||
TEST_SYMBOL(ZSTD_endStream);
|
||||
TEST_SYMBOL(ZSTD_CStreamInSize);
|
||||
TEST_SYMBOL(ZSTD_CStreamOutSize);
|
||||
TEST_SYMBOL(ZSTD_DStreamWorkspaceBound);
|
||||
TEST_SYMBOL(ZSTD_initDStream);
|
||||
TEST_SYMBOL(ZSTD_initDStream_usingDDict);
|
||||
TEST_SYMBOL(ZSTD_resetDStream);
|
||||
TEST_SYMBOL(ZSTD_decompressStream);
|
||||
TEST_SYMBOL(ZSTD_DStreamInSize);
|
||||
TEST_SYMBOL(ZSTD_DStreamOutSize);
|
||||
|
||||
TEST_SYMBOL(ZSTD_findFrameCompressedSize);
|
||||
TEST_SYMBOL(ZSTD_getFrameContentSize);
|
||||
TEST_SYMBOL(ZSTD_findDecompressedSize);
|
||||
|
||||
TEST_SYMBOL(ZSTD_getCParams);
|
||||
TEST_SYMBOL(ZSTD_getParams);
|
||||
TEST_SYMBOL(ZSTD_checkCParams);
|
||||
TEST_SYMBOL(ZSTD_adjustCParams);
|
||||
|
||||
TEST_SYMBOL(ZSTD_isFrame);
|
||||
TEST_SYMBOL(ZSTD_getDictID_fromDict);
|
||||
TEST_SYMBOL(ZSTD_getDictID_fromDDict);
|
||||
TEST_SYMBOL(ZSTD_getDictID_fromFrame);
|
||||
|
||||
TEST_SYMBOL(ZSTD_compressBegin);
|
||||
TEST_SYMBOL(ZSTD_compressBegin_usingDict);
|
||||
TEST_SYMBOL(ZSTD_compressBegin_advanced);
|
||||
TEST_SYMBOL(ZSTD_copyCCtx);
|
||||
TEST_SYMBOL(ZSTD_compressBegin_usingCDict);
|
||||
TEST_SYMBOL(ZSTD_compressContinue);
|
||||
TEST_SYMBOL(ZSTD_compressEnd);
|
||||
TEST_SYMBOL(ZSTD_getFrameParams);
|
||||
TEST_SYMBOL(ZSTD_decompressBegin);
|
||||
TEST_SYMBOL(ZSTD_decompressBegin_usingDict);
|
||||
TEST_SYMBOL(ZSTD_copyDCtx);
|
||||
TEST_SYMBOL(ZSTD_nextSrcSizeToDecompress);
|
||||
TEST_SYMBOL(ZSTD_decompressContinue);
|
||||
TEST_SYMBOL(ZSTD_nextInputType);
|
||||
|
||||
TEST_SYMBOL(ZSTD_getBlockSizeMax);
|
||||
TEST_SYMBOL(ZSTD_compressBlock);
|
||||
TEST_SYMBOL(ZSTD_decompressBlock);
|
||||
TEST_SYMBOL(ZSTD_insertBlock);
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
extern "C" {
|
||||
#include <linux/errno.h>
|
||||
#include <linux/xxhash.h>
|
||||
}
|
||||
#include <gtest/gtest.h>
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#define XXH_STATIC_LINKING_ONLY
|
||||
#include <xxhash.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace {
|
||||
const std::array<std::string, 11> kTestInputs = {
|
||||
"",
|
||||
"0",
|
||||
"01234",
|
||||
"0123456789abcde",
|
||||
"0123456789abcdef",
|
||||
"0123456789abcdef0",
|
||||
"0123456789abcdef0123",
|
||||
"0123456789abcdef0123456789abcde",
|
||||
"0123456789abcdef0123456789abcdef",
|
||||
"0123456789abcdef0123456789abcdef0",
|
||||
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
};
|
||||
|
||||
bool testXXH32(const void *input, const size_t length, uint32_t seed) {
|
||||
return XXH32(input, length, seed) == xxh32(input, length, seed);
|
||||
}
|
||||
|
||||
bool testXXH64(const void *input, const size_t length, uint32_t seed) {
|
||||
return XXH64(input, length, seed) == xxh64(input, length, seed);
|
||||
}
|
||||
|
||||
class XXH32State {
|
||||
struct xxh32_state kernelState;
|
||||
XXH32_state_t state;
|
||||
|
||||
public:
|
||||
explicit XXH32State(const uint32_t seed) { reset(seed); }
|
||||
XXH32State(XXH32State const& other) noexcept {
|
||||
xxh32_copy_state(&kernelState, &other.kernelState);
|
||||
XXH32_copyState(&state, &other.state);
|
||||
}
|
||||
XXH32State& operator=(XXH32State const& other) noexcept {
|
||||
xxh32_copy_state(&kernelState, &other.kernelState);
|
||||
XXH32_copyState(&state, &other.state);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void reset(const uint32_t seed) {
|
||||
xxh32_reset(&kernelState, seed);
|
||||
EXPECT_EQ(0, XXH32_reset(&state, seed));
|
||||
}
|
||||
|
||||
void update(const void *input, const size_t length) {
|
||||
EXPECT_EQ(0, xxh32_update(&kernelState, input, length));
|
||||
EXPECT_EQ(0, (int)XXH32_update(&state, input, length));
|
||||
}
|
||||
|
||||
bool testDigest() const {
|
||||
return xxh32_digest(&kernelState) == XXH32_digest(&state);
|
||||
}
|
||||
};
|
||||
|
||||
class XXH64State {
|
||||
struct xxh64_state kernelState;
|
||||
XXH64_state_t state;
|
||||
|
||||
public:
|
||||
explicit XXH64State(const uint64_t seed) { reset(seed); }
|
||||
XXH64State(XXH64State const& other) noexcept {
|
||||
xxh64_copy_state(&kernelState, &other.kernelState);
|
||||
XXH64_copyState(&state, &other.state);
|
||||
}
|
||||
XXH64State& operator=(XXH64State const& other) noexcept {
|
||||
xxh64_copy_state(&kernelState, &other.kernelState);
|
||||
XXH64_copyState(&state, &other.state);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void reset(const uint64_t seed) {
|
||||
xxh64_reset(&kernelState, seed);
|
||||
EXPECT_EQ(0, XXH64_reset(&state, seed));
|
||||
}
|
||||
|
||||
void update(const void *input, const size_t length) {
|
||||
EXPECT_EQ(0, xxh64_update(&kernelState, input, length));
|
||||
EXPECT_EQ(0, (int)XXH64_update(&state, input, length));
|
||||
}
|
||||
|
||||
bool testDigest() const {
|
||||
return xxh64_digest(&kernelState) == XXH64_digest(&state);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
TEST(Simple, Null) {
|
||||
EXPECT_TRUE(testXXH32(NULL, 0, 0));
|
||||
EXPECT_TRUE(testXXH64(NULL, 0, 0));
|
||||
}
|
||||
|
||||
TEST(Stream, Null) {
|
||||
struct xxh32_state state32;
|
||||
xxh32_reset(&state32, 0);
|
||||
EXPECT_EQ(-EINVAL, xxh32_update(&state32, NULL, 0));
|
||||
|
||||
struct xxh64_state state64;
|
||||
xxh64_reset(&state64, 0);
|
||||
EXPECT_EQ(-EINVAL, xxh64_update(&state64, NULL, 0));
|
||||
}
|
||||
|
||||
TEST(Simple, TestInputs) {
|
||||
for (uint32_t seed = 0; seed < 100000; seed = (seed + 1) * 3) {
|
||||
for (auto const input : kTestInputs) {
|
||||
EXPECT_TRUE(testXXH32(input.data(), input.size(), seed));
|
||||
EXPECT_TRUE(testXXH64(input.data(), input.size(), (uint64_t)seed));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Stream, TestInputs) {
|
||||
for (uint32_t seed = 0; seed < 100000; seed = (seed + 1) * 3) {
|
||||
for (auto const input : kTestInputs) {
|
||||
XXH32State s32(seed);
|
||||
XXH64State s64(seed);
|
||||
s32.update(input.data(), input.size());
|
||||
s64.update(input.data(), input.size());
|
||||
EXPECT_TRUE(s32.testDigest());
|
||||
EXPECT_TRUE(s64.testDigest());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Stream, MultipleTestInputs) {
|
||||
for (uint32_t seed = 0; seed < 100000; seed = (seed + 1) * 3) {
|
||||
XXH32State s32(seed);
|
||||
XXH64State s64(seed);
|
||||
for (auto const input : kTestInputs) {
|
||||
s32.update(input.data(), input.size());
|
||||
s64.update(input.data(), input.size());
|
||||
}
|
||||
EXPECT_TRUE(s32.testDigest());
|
||||
EXPECT_TRUE(s64.testDigest());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Stream, CopyState) {
|
||||
for (uint32_t seed = 0; seed < 100000; seed = (seed + 1) * 3) {
|
||||
XXH32State s32(seed);
|
||||
XXH64State s64(seed);
|
||||
for (auto const input : kTestInputs) {
|
||||
auto t32(s32);
|
||||
t32.update(input.data(), input.size());
|
||||
s32 = t32;
|
||||
auto t64(s64);
|
||||
t64.update(input.data(), input.size());
|
||||
s64 = t64;
|
||||
}
|
||||
EXPECT_TRUE(s32.testDigest());
|
||||
EXPECT_TRUE(s64.testDigest());
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
#define ASM_UNALIGNED_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define _LITTLE_ENDIAN 1
|
||||
@@ -32,7 +33,7 @@ static uint64_t _swap64(uint64_t in)
|
||||
static uint16_t get_unaligned_le16(const void* memPtr)
|
||||
{
|
||||
uint16_t val;
|
||||
__builtin_memcpy(&val, memPtr, sizeof(val));
|
||||
memcpy(&val, memPtr, sizeof(val));
|
||||
if (!_isLittleEndian()) _swap16(val);
|
||||
return val;
|
||||
}
|
||||
@@ -40,7 +41,7 @@ static uint16_t get_unaligned_le16(const void* memPtr)
|
||||
static uint32_t get_unaligned_le32(const void* memPtr)
|
||||
{
|
||||
uint32_t val;
|
||||
__builtin_memcpy(&val, memPtr, sizeof(val));
|
||||
memcpy(&val, memPtr, sizeof(val));
|
||||
if (!_isLittleEndian()) _swap32(val);
|
||||
return val;
|
||||
}
|
||||
@@ -48,7 +49,7 @@ static uint32_t get_unaligned_le32(const void* memPtr)
|
||||
static uint64_t get_unaligned_le64(const void* memPtr)
|
||||
{
|
||||
uint64_t val;
|
||||
__builtin_memcpy(&val, memPtr, sizeof(val));
|
||||
memcpy(&val, memPtr, sizeof(val));
|
||||
if (!_isLittleEndian()) _swap64(val);
|
||||
return val;
|
||||
}
|
||||
@@ -56,26 +57,26 @@ static uint64_t get_unaligned_le64(const void* memPtr)
|
||||
static void put_unaligned_le16(uint16_t value, void* memPtr)
|
||||
{
|
||||
if (!_isLittleEndian()) value = _swap16(value);
|
||||
__builtin_memcpy(memPtr, &value, sizeof(value));
|
||||
memcpy(memPtr, &value, sizeof(value));
|
||||
}
|
||||
|
||||
static void put_unaligned_le32(uint32_t value, void* memPtr)
|
||||
{
|
||||
if (!_isLittleEndian()) value = _swap32(value);
|
||||
__builtin_memcpy(memPtr, &value, sizeof(value));
|
||||
memcpy(memPtr, &value, sizeof(value));
|
||||
}
|
||||
|
||||
static void put_unaligned_le64(uint64_t value, void* memPtr)
|
||||
{
|
||||
if (!_isLittleEndian()) value = _swap64(value);
|
||||
__builtin_memcpy(memPtr, &value, sizeof(value));
|
||||
memcpy(memPtr, &value, sizeof(value));
|
||||
}
|
||||
|
||||
/* big endian */
|
||||
static uint32_t get_unaligned_be32(const void* memPtr)
|
||||
{
|
||||
uint32_t val;
|
||||
__builtin_memcpy(&val, memPtr, sizeof(val));
|
||||
memcpy(&val, memPtr, sizeof(val));
|
||||
if (_isLittleEndian()) _swap32(val);
|
||||
return val;
|
||||
}
|
||||
@@ -83,7 +84,7 @@ static uint32_t get_unaligned_be32(const void* memPtr)
|
||||
static uint64_t get_unaligned_be64(const void* memPtr)
|
||||
{
|
||||
uint64_t val;
|
||||
__builtin_memcpy(&val, memPtr, sizeof(val));
|
||||
memcpy(&val, memPtr, sizeof(val));
|
||||
if (_isLittleEndian()) _swap64(val);
|
||||
return val;
|
||||
}
|
||||
@@ -91,13 +92,13 @@ static uint64_t get_unaligned_be64(const void* memPtr)
|
||||
static void put_unaligned_be32(uint32_t value, void* memPtr)
|
||||
{
|
||||
if (_isLittleEndian()) value = _swap32(value);
|
||||
__builtin_memcpy(memPtr, &value, sizeof(value));
|
||||
memcpy(memPtr, &value, sizeof(value));
|
||||
}
|
||||
|
||||
static void put_unaligned_be64(uint64_t value, void* memPtr)
|
||||
{
|
||||
if (_isLittleEndian()) value = _swap64(value);
|
||||
__builtin_memcpy(memPtr, &value, sizeof(value));
|
||||
memcpy(memPtr, &value, sizeof(value));
|
||||
}
|
||||
|
||||
/* generic */
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2020, Facebook, 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 LINUX_COMPILER_H
|
||||
#define LINUX_COMPILER_H
|
||||
#ifndef LINUX_COMPILER_H_
|
||||
#define LINUX_COMPILER_H_
|
||||
|
||||
#ifndef inline
|
||||
#define inline __inline __attribute__((unused))
|
||||
#ifndef __always_inline
|
||||
# define __always_inline inline
|
||||
#endif
|
||||
|
||||
#ifndef noinline
|
||||
# define noinline __attribute__((__noinline__))
|
||||
#endif
|
||||
|
||||
#endif // LINUX_COMPILER_H_
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2020, Facebook, 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 LINUX_ERRNO_H
|
||||
#define LINUX_ERRNO_H
|
||||
#ifndef LINUX_ERRNO_H_
|
||||
#define LINUX_ERRNO_H_
|
||||
|
||||
#define EINVAL 22
|
||||
|
||||
#endif
|
||||
#endif // LINUX_ERRNO_H_
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2020, Facebook, 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 LINUX_KERNEL_H
|
||||
#define LINUX_KERNEL_H
|
||||
#ifndef LINUX_KERNEL_H_
|
||||
#define LINUX_KERNEL_H_
|
||||
|
||||
#define WARN_ON(x)
|
||||
#define ALIGN(x, a) ({ \
|
||||
typeof(x) const __xe = (x); \
|
||||
typeof(a) const __ae = (a); \
|
||||
typeof(a) const __m = __ae - 1; \
|
||||
typeof(x) const __r = __xe & __m; \
|
||||
__xe + (__r ? (__ae - __r) : 0); \
|
||||
})
|
||||
|
||||
#endif
|
||||
#define PTR_ALIGN(p, a) (typeof(p))ALIGN((unsigned long long)(p), (a))
|
||||
|
||||
#define current Something that doesn't compile :)
|
||||
|
||||
#endif // LINUX_KERNEL_H_
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2020, Facebook, 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 LINUX_LIMITS_H
|
||||
#define LINUX_LIMITS_H
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#endif
|
||||
@@ -1,15 +1,11 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2020, Facebook, 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 LINUX_MATH64_H
|
||||
#define LINUX_MATH64_H
|
||||
|
||||
#define div_u64(dividend, divisor) ((dividend) / (divisor))
|
||||
#include <stdint.h>
|
||||
|
||||
static uint64_t div_u64(uint64_t n, uint32_t d)
|
||||
{
|
||||
return n / d;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,18 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2020, Facebook, 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 LINUX_MODULE_H
|
||||
#define LINUX_MODULE_H
|
||||
#ifndef LINUX_MODULE_H_
|
||||
#define LINUX_MODULE_H_
|
||||
|
||||
#define EXPORT_SYMBOL(symbol) \
|
||||
void* __##symbol = symbol
|
||||
#define MODULE_LICENSE(license)
|
||||
#define MODULE_DESCRIPTION(description)
|
||||
#define MODULE_LICENSE(license) static char const *const LICENSE = license
|
||||
#define MODULE_DESCRIPTION(description) \
|
||||
static char const *const DESCRIPTION = description
|
||||
|
||||
#endif
|
||||
#endif // LINUX_MODULE_H_
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2020, Facebook, 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 LINUX_PRINTK_H
|
||||
#define LINUX_PRINTK_H
|
||||
|
||||
#define pr_debug(...)
|
||||
|
||||
#endif
|
||||
@@ -1,15 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2020, Facebook, 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 LINUX_STDDEF_H
|
||||
#define LINUX_STDDEF_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
#include <string.h>
|
||||
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2020, Facebook, 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 LINUX_SWAB_H
|
||||
#define LINUX_SWAB_H
|
||||
|
||||
#define swab32(x) __builtin_bswap32((x))
|
||||
#define swab64(x) __builtin_bswap64((x))
|
||||
|
||||
#endif
|
||||
@@ -1,16 +1,2 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2020, Facebook, 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 LINUX_TYPES_H
|
||||
#define LINUX_TYPES_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
||||
INCLUDE_DIR="$SCRIPT_DIR/../linux/include"
|
||||
LIB_DIR="$SCRIPT_DIR/../linux/lib"
|
||||
|
||||
|
||||
print() {
|
||||
printf '%b' "${*}"
|
||||
}
|
||||
|
||||
println() {
|
||||
printf '%b\n' "${*}"
|
||||
}
|
||||
|
||||
die() {
|
||||
println "$@" 1>&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
test_not_present() {
|
||||
print "Testing that '$1' is not present... "
|
||||
grep -r $1 "$INCLUDE_DIR" "$LIB_DIR" && die "Fail!"
|
||||
println "Okay"
|
||||
}
|
||||
|
||||
println "This test checks that the macro removal process worked as expected"
|
||||
println "If this test fails, then freestanding.py wasn't able to remove one of these"
|
||||
println "macros from the source code completely. You'll either need to rewrite the check"
|
||||
println "or improve freestanding.py."
|
||||
println ""
|
||||
|
||||
test_not_present "ZSTD_NO_INTRINSICS"
|
||||
test_not_present "ZSTD_NO_UNUSED_FUNCTIONS"
|
||||
test_not_present "ZSTD_LEGACY_SUPPORT"
|
||||
test_not_present "STATIC_BMI2"
|
||||
test_not_present "ZSTD_NO_INLINE"
|
||||
test_not_present "ZSTD_DLL_EXPORT"
|
||||
test_not_present "ZSTD_DLL_IMPORT"
|
||||
test_not_present "__ICCARM__"
|
||||
test_not_present "_MSC_VER"
|
||||
test_not_present "_WIN32"
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Facebook, 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 <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "decompress_sources.h"
|
||||
#include <linux/zstd.h>
|
||||
|
||||
#define CONTROL(x) \
|
||||
do { \
|
||||
if (!(x)) { \
|
||||
fprintf(stderr, "%s:%u: %s failed!\n", __FUNCTION__, __LINE__, #x); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
static const char kEmptyZstdFrame[] = {
|
||||
0x28, 0xb5, 0x2f, 0xfd, 0x24, 0x00, 0x01, 0x00, 0x00, 0x99, 0xe9, 0xd8, 0x51
|
||||
};
|
||||
|
||||
static void test_decompress_unzstd() {
|
||||
fprintf(stderr, "Testing decompress unzstd... ");
|
||||
{
|
||||
size_t const wkspSize = zstd_dctx_workspace_bound();
|
||||
void* wksp = malloc(wkspSize);
|
||||
CONTROL(wksp != NULL);
|
||||
ZSTD_DCtx* dctx = zstd_init_dctx(wksp, wkspSize);
|
||||
CONTROL(dctx != NULL);
|
||||
size_t const dSize = zstd_decompress_dctx(dctx, NULL, 0, kEmptyZstdFrame, sizeof(kEmptyZstdFrame));
|
||||
CONTROL(!zstd_is_error(dSize));
|
||||
CONTROL(dSize == 0);
|
||||
free(wksp);
|
||||
}
|
||||
fprintf(stderr, "Ok\n");
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
test_decompress_unzstd();
|
||||
return 0;
|
||||
}
|
||||
@@ -1,210 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 7-2020, Facebook, 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 <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <linux/zstd.h>
|
||||
|
||||
#define CONTROL(x) \
|
||||
do { \
|
||||
if (!(x)) { \
|
||||
fprintf(stderr, "%s:%u: %s failed!\n", __FUNCTION__, __LINE__, #x); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
typedef struct {
|
||||
char *data;
|
||||
char *data2;
|
||||
size_t dataSize;
|
||||
char *comp;
|
||||
size_t compSize;
|
||||
} test_data_t;
|
||||
|
||||
test_data_t create_test_data(void) {
|
||||
test_data_t data;
|
||||
data.dataSize = 128 * 1024;
|
||||
data.data = malloc(data.dataSize);
|
||||
CONTROL(data.data != NULL);
|
||||
data.data2 = malloc(data.dataSize);
|
||||
CONTROL(data.data2 != NULL);
|
||||
data.compSize = zstd_compress_bound(data.dataSize);
|
||||
data.comp = malloc(data.compSize);
|
||||
CONTROL(data.comp != NULL);
|
||||
memset(data.data, 0, data.dataSize);
|
||||
return data;
|
||||
}
|
||||
|
||||
static void free_test_data(test_data_t const *data) {
|
||||
free(data->data);
|
||||
free(data->data2);
|
||||
free(data->comp);
|
||||
}
|
||||
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
static void test_btrfs(test_data_t const *data) {
|
||||
fprintf(stderr, "testing btrfs use cases... ");
|
||||
size_t const size = MIN(data->dataSize, 128 * 1024);
|
||||
for (int level = -1; level < 16; ++level) {
|
||||
struct zstd_parameters params = zstd_get_params(level, size);
|
||||
CONTROL(params.cparams.window_log <= 17);
|
||||
size_t const workspaceSize =
|
||||
MAX(zstd_cstream_workspace_bound(¶ms.cparams),
|
||||
zstd_dstream_workspace_bound(size));
|
||||
void *workspace = malloc(workspaceSize);
|
||||
CONTROL(workspace != NULL);
|
||||
|
||||
char const *ip = data->data;
|
||||
char const *iend = ip + size;
|
||||
char *op = data->comp;
|
||||
char *oend = op + data->compSize;
|
||||
{
|
||||
zstd_cstream *cctx = zstd_init_cstream(¶ms, size, workspace, workspaceSize);
|
||||
CONTROL(cctx != NULL);
|
||||
struct zstd_out_buffer out = {NULL, 0, 0};
|
||||
struct zstd_in_buffer in = {NULL, 0, 0};
|
||||
for (;;) {
|
||||
if (in.pos == in.size) {
|
||||
in.src = ip;
|
||||
in.size = MIN(4096, iend - ip);
|
||||
in.pos = 0;
|
||||
ip += in.size;
|
||||
}
|
||||
|
||||
if (out.pos == out.size) {
|
||||
out.dst = op;
|
||||
out.size = MIN(4096, oend - op);
|
||||
out.pos = 0;
|
||||
op += out.size;
|
||||
}
|
||||
|
||||
if (ip != iend || in.pos < in.size) {
|
||||
CONTROL(!zstd_is_error(zstd_compress_stream(cctx, &out, &in)));
|
||||
} else {
|
||||
size_t const ret = zstd_end_stream(cctx, &out);
|
||||
CONTROL(!zstd_is_error(ret));
|
||||
if (ret == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
op += out.pos;
|
||||
}
|
||||
|
||||
ip = data->comp;
|
||||
iend = op;
|
||||
op = data->data2;
|
||||
oend = op + size;
|
||||
{
|
||||
zstd_dstream *dctx = zstd_init_dstream(1ULL << params.cparams.window_log, workspace, workspaceSize);
|
||||
CONTROL(dctx != NULL);
|
||||
struct zstd_out_buffer out = {NULL, 0, 0};
|
||||
struct zstd_in_buffer in = {NULL, 0, 0};
|
||||
for (;;) {
|
||||
if (in.pos == in.size) {
|
||||
in.src = ip;
|
||||
in.size = MIN(4096, iend - ip);
|
||||
in.pos = 0;
|
||||
ip += in.size;
|
||||
}
|
||||
|
||||
if (out.pos == out.size) {
|
||||
out.dst = op;
|
||||
out.size = MIN(4096, oend - op);
|
||||
out.pos = 0;
|
||||
op += out.size;
|
||||
}
|
||||
|
||||
size_t const ret = zstd_decompress_stream(dctx, &out, &in);
|
||||
CONTROL(!zstd_is_error(ret));
|
||||
if (ret == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
CONTROL(op - data->data2 == data->dataSize);
|
||||
CONTROL(!memcmp(data->data, data->data2, data->dataSize));
|
||||
free(workspace);
|
||||
}
|
||||
fprintf(stderr, "Ok\n");
|
||||
}
|
||||
|
||||
static void test_decompress_unzstd(test_data_t const *data) {
|
||||
fprintf(stderr, "Testing decompress unzstd... ");
|
||||
size_t cSize;
|
||||
{
|
||||
struct zstd_parameters params = zstd_get_params(19, 0);
|
||||
size_t const wkspSize = zstd_cctx_workspace_bound(¶ms.cparams);
|
||||
void* wksp = malloc(wkspSize);
|
||||
CONTROL(wksp != NULL);
|
||||
zstd_cctx* cctx = zstd_init_cctx(wksp, wkspSize);
|
||||
CONTROL(cctx != NULL);
|
||||
cSize = zstd_compress_cctx(cctx, data->comp, data->compSize, data->data, data->dataSize, ¶ms);
|
||||
CONTROL(!zstd_is_error(cSize));
|
||||
free(wksp);
|
||||
}
|
||||
{
|
||||
size_t const wkspSize = zstd_dctx_workspace_bound();
|
||||
void* wksp = malloc(wkspSize);
|
||||
CONTROL(wksp != NULL);
|
||||
zstd_dctx* dctx = zstd_init_dctx(wksp, wkspSize);
|
||||
CONTROL(dctx != NULL);
|
||||
size_t const dSize = zstd_decompress_dctx(dctx, data->data2, data->dataSize, data->comp, cSize);
|
||||
CONTROL(!zstd_is_error(dSize));
|
||||
CONTROL(dSize == data->dataSize);
|
||||
CONTROL(!memcmp(data->data, data->data2, data->dataSize));
|
||||
free(wksp);
|
||||
}
|
||||
fprintf(stderr, "Ok\n");
|
||||
}
|
||||
|
||||
static char *g_stack = NULL;
|
||||
|
||||
static void __attribute__((noinline)) use(void *x) {
|
||||
asm volatile("" : "+r"(x));
|
||||
}
|
||||
|
||||
static void __attribute__((noinline)) set_stack() {
|
||||
|
||||
char stack[8192];
|
||||
g_stack = stack;
|
||||
memset(g_stack, 0x33, 8192);
|
||||
use(g_stack);
|
||||
}
|
||||
|
||||
static void __attribute__((noinline)) check_stack() {
|
||||
size_t cleanStack = 0;
|
||||
while (cleanStack < 8192 && g_stack[cleanStack] == 0x33) {
|
||||
++cleanStack;
|
||||
}
|
||||
size_t const stackSize = 8192 - cleanStack;
|
||||
fprintf(stderr, "Maximum stack size: %zu\n", stackSize);
|
||||
CONTROL(stackSize <= 2048 + 512);
|
||||
}
|
||||
|
||||
static void test_stack_usage(test_data_t const *data) {
|
||||
set_stack();
|
||||
test_btrfs(data);
|
||||
test_decompress_unzstd(data);
|
||||
check_stack();
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
test_data_t data = create_test_data();
|
||||
test_btrfs(&data);
|
||||
test_decompress_unzstd(&data);
|
||||
test_stack_usage(&data);
|
||||
free_test_data(&data);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright (c) 2016-present, Facebook, 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).
|
||||
*/
|
||||
|
||||
/* DO_XXH should be 32 or 64 for xxh32 and xxh64 respectively */
|
||||
#define DO_XXH 0
|
||||
/* DO_CRC should be 0 or 1 */
|
||||
#define DO_CRC 0
|
||||
/* Buffer size */
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#if DO_XXH
|
||||
#include <linux/xxhash.h>
|
||||
#endif
|
||||
|
||||
#if DO_CRC
|
||||
#include <linux/crc32.h>
|
||||
#endif
|
||||
|
||||
/* Device name to pass to register_chrdev(). */
|
||||
#define DEVICE_NAME "xxhash_test"
|
||||
|
||||
/* Dynamically allocated device major number */
|
||||
static int device_major;
|
||||
|
||||
/*
|
||||
* We reuse the same hash state, and thus can hash only one
|
||||
* file at a time.
|
||||
*/
|
||||
static bool device_is_open;
|
||||
|
||||
static uint64_t total_length;
|
||||
|
||||
|
||||
#if (DO_XXH == 32)
|
||||
|
||||
#define xxh_state xxh32_state
|
||||
#define xxh_reset xxh32_reset
|
||||
#define xxh_update xxh32_update
|
||||
#define xxh_digest xxh32_digest
|
||||
#define XXH_FORMAT "XXH32 = 0x%x"
|
||||
|
||||
#elif (DO_XXH == 64)
|
||||
|
||||
#define xxh_state xxh64_state
|
||||
#define xxh_reset xxh64_reset
|
||||
#define xxh_update xxh64_update
|
||||
#define xxh_digest xxh64_digest
|
||||
#define XXH_FORMAT "XXH64 = 0x%llx"
|
||||
|
||||
#elif DO_XXH
|
||||
|
||||
#error "Invalid value of DO_XXH"
|
||||
|
||||
#endif
|
||||
|
||||
#if DO_XXH
|
||||
|
||||
/* XXH state */
|
||||
static struct xxh_state state;
|
||||
|
||||
#endif /* DO_XXH */
|
||||
|
||||
#if DO_CRC
|
||||
|
||||
static uint32_t crc;
|
||||
|
||||
#endif /* DO_CRC */
|
||||
|
||||
/*
|
||||
* Input buffer used to put data coming from userspace.
|
||||
*/
|
||||
static uint8_t buffer_in[BUFFER_SIZE];
|
||||
|
||||
static int xxhash_test_open(struct inode *i, struct file *f)
|
||||
{
|
||||
if (device_is_open)
|
||||
return -EBUSY;
|
||||
|
||||
device_is_open = true;
|
||||
|
||||
total_length = 0;
|
||||
#if DO_XXH
|
||||
xxh_reset(&state, 0);
|
||||
#endif
|
||||
#if DO_CRC
|
||||
crc = 0xFFFFFFFF;
|
||||
#endif
|
||||
|
||||
printk(KERN_INFO DEVICE_NAME ": opened\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xxhash_test_release(struct inode *i, struct file *f)
|
||||
{
|
||||
device_is_open = false;
|
||||
|
||||
printk(KERN_INFO DEVICE_NAME ": total_len = %llu\n", total_length);
|
||||
#if DO_XXH
|
||||
printk(KERN_INFO DEVICE_NAME ": " XXH_FORMAT "\n", xxh_digest(&state));
|
||||
#endif
|
||||
#if DO_CRC
|
||||
printk(KERN_INFO DEVICE_NAME ": CRC32 = 0x%08x\n", ~crc);
|
||||
#endif
|
||||
printk(KERN_INFO DEVICE_NAME ": closed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Hash the data given to us from userspace.
|
||||
*/
|
||||
static ssize_t xxhash_test_write(struct file *file, const char __user *buf,
|
||||
size_t size, loff_t *pos)
|
||||
{
|
||||
size_t remaining = size;
|
||||
|
||||
while (remaining > 0) {
|
||||
#if DO_XXH
|
||||
int ret;
|
||||
#endif
|
||||
size_t const copy_size = min(remaining, sizeof(buffer_in));
|
||||
|
||||
if (copy_from_user(buffer_in, buf, copy_size))
|
||||
return -EFAULT;
|
||||
buf += copy_size;
|
||||
remaining -= copy_size;
|
||||
total_length += copy_size;
|
||||
#if DO_XXH
|
||||
if ((ret = xxh_update(&state, buffer_in, copy_size))) {
|
||||
printk(KERN_INFO DEVICE_NAME ": xxh failure.");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
#if DO_CRC
|
||||
crc = crc32(crc, buffer_in, copy_size);
|
||||
#endif
|
||||
}
|
||||
return size;
|
||||
}
|
||||
/* register the character device. */
|
||||
static int __init xxhash_test_init(void)
|
||||
{
|
||||
static const struct file_operations fileops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = &xxhash_test_open,
|
||||
.release = &xxhash_test_release,
|
||||
.write = &xxhash_test_write
|
||||
};
|
||||
|
||||
device_major = register_chrdev(0, DEVICE_NAME, &fileops);
|
||||
if (device_major < 0) {
|
||||
return device_major;
|
||||
}
|
||||
|
||||
printk(KERN_INFO DEVICE_NAME ": module loaded\n");
|
||||
printk(KERN_INFO DEVICE_NAME ": Create a device node with "
|
||||
"'mknod " DEVICE_NAME " c %d 0' and write data "
|
||||
"to it.\n", device_major);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit xxhash_test_exit(void)
|
||||
{
|
||||
unregister_chrdev(device_major, DEVICE_NAME);
|
||||
printk(KERN_INFO DEVICE_NAME ": module unloaded\n");
|
||||
}
|
||||
|
||||
module_init(xxhash_test_init);
|
||||
module_exit(xxhash_test_exit);
|
||||
|
||||
MODULE_DESCRIPTION("XXHash tester");
|
||||
MODULE_VERSION("1.0");
|
||||
|
||||
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
@@ -1,206 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/zstd.h>
|
||||
|
||||
#include "zstd.h"
|
||||
#include "common/zstd_deps.h"
|
||||
#include "common/zstd_internal.h"
|
||||
|
||||
static void zstd_check_structs(void) {
|
||||
/* Check that the structs have the same size. */
|
||||
ZSTD_STATIC_ASSERT(sizeof(ZSTD_parameters) ==
|
||||
sizeof(struct zstd_parameters));
|
||||
ZSTD_STATIC_ASSERT(sizeof(ZSTD_compressionParameters) ==
|
||||
sizeof(struct zstd_compression_parameters));
|
||||
ZSTD_STATIC_ASSERT(sizeof(ZSTD_frameParameters) ==
|
||||
sizeof(struct zstd_frame_parameters));
|
||||
/* Zstd guarantees that the layout of the structs never change. Verify it. */
|
||||
ZSTD_STATIC_ASSERT(offsetof(ZSTD_parameters, cParams) ==
|
||||
offsetof(struct zstd_parameters, cparams));
|
||||
ZSTD_STATIC_ASSERT(offsetof(ZSTD_parameters, fParams) ==
|
||||
offsetof(struct zstd_parameters, fparams));
|
||||
ZSTD_STATIC_ASSERT(offsetof(ZSTD_compressionParameters, windowLog) ==
|
||||
offsetof(struct zstd_compression_parameters, window_log));
|
||||
ZSTD_STATIC_ASSERT(offsetof(ZSTD_compressionParameters, chainLog) ==
|
||||
offsetof(struct zstd_compression_parameters, chain_log));
|
||||
ZSTD_STATIC_ASSERT(offsetof(ZSTD_compressionParameters, hashLog) ==
|
||||
offsetof(struct zstd_compression_parameters, hash_log));
|
||||
ZSTD_STATIC_ASSERT(offsetof(ZSTD_compressionParameters, searchLog) ==
|
||||
offsetof(struct zstd_compression_parameters, search_log));
|
||||
ZSTD_STATIC_ASSERT(offsetof(ZSTD_compressionParameters, minMatch) ==
|
||||
offsetof(struct zstd_compression_parameters, search_length));
|
||||
ZSTD_STATIC_ASSERT(offsetof(ZSTD_compressionParameters, targetLength) ==
|
||||
offsetof(struct zstd_compression_parameters, target_length));
|
||||
ZSTD_STATIC_ASSERT(offsetof(ZSTD_compressionParameters, strategy) ==
|
||||
offsetof(struct zstd_compression_parameters, strategy));
|
||||
ZSTD_STATIC_ASSERT(offsetof(ZSTD_frameParameters, contentSizeFlag) ==
|
||||
offsetof(struct zstd_frame_parameters, content_size_flag));
|
||||
ZSTD_STATIC_ASSERT(offsetof(ZSTD_frameParameters, checksumFlag) ==
|
||||
offsetof(struct zstd_frame_parameters, checksum_flag));
|
||||
ZSTD_STATIC_ASSERT(offsetof(ZSTD_frameParameters, noDictIDFlag) ==
|
||||
offsetof(struct zstd_frame_parameters, no_dict_id_flag));
|
||||
/* Check that the strategies are the same. This can change. */
|
||||
ZSTD_STATIC_ASSERT((int)ZSTD_fast == (int)zstd_fast);
|
||||
ZSTD_STATIC_ASSERT((int)ZSTD_dfast == (int)zstd_dfast);
|
||||
ZSTD_STATIC_ASSERT((int)ZSTD_greedy == (int)zstd_greedy);
|
||||
ZSTD_STATIC_ASSERT((int)ZSTD_lazy == (int)zstd_lazy);
|
||||
ZSTD_STATIC_ASSERT((int)ZSTD_lazy2 == (int)zstd_lazy2);
|
||||
ZSTD_STATIC_ASSERT((int)ZSTD_btlazy2 == (int)zstd_btlazy2);
|
||||
ZSTD_STATIC_ASSERT((int)ZSTD_btopt == (int)zstd_btopt);
|
||||
ZSTD_STATIC_ASSERT((int)ZSTD_btultra == (int)zstd_btultra);
|
||||
ZSTD_STATIC_ASSERT((int)ZSTD_btultra2 == (int)zstd_btultra2);
|
||||
/* Check input buffer */
|
||||
ZSTD_STATIC_ASSERT(sizeof(ZSTD_inBuffer) == sizeof(struct zstd_in_buffer));
|
||||
ZSTD_STATIC_ASSERT(offsetof(ZSTD_inBuffer, src) ==
|
||||
offsetof(struct zstd_in_buffer, src));
|
||||
ZSTD_STATIC_ASSERT(offsetof(ZSTD_inBuffer, size) ==
|
||||
offsetof(struct zstd_in_buffer, size));
|
||||
ZSTD_STATIC_ASSERT(offsetof(ZSTD_inBuffer, pos) ==
|
||||
offsetof(struct zstd_in_buffer, pos));
|
||||
/* Check output buffer */
|
||||
ZSTD_STATIC_ASSERT(sizeof(ZSTD_outBuffer) ==
|
||||
sizeof(struct zstd_out_buffer));
|
||||
ZSTD_STATIC_ASSERT(offsetof(ZSTD_outBuffer, dst) ==
|
||||
offsetof(struct zstd_out_buffer, dst));
|
||||
ZSTD_STATIC_ASSERT(offsetof(ZSTD_outBuffer, size) ==
|
||||
offsetof(struct zstd_out_buffer, size));
|
||||
ZSTD_STATIC_ASSERT(offsetof(ZSTD_outBuffer, pos) ==
|
||||
offsetof(struct zstd_out_buffer, pos));
|
||||
}
|
||||
|
||||
size_t zstd_compress_bound(size_t src_size)
|
||||
{
|
||||
return ZSTD_compressBound(src_size);
|
||||
}
|
||||
EXPORT_SYMBOL(zstd_compress_bound);
|
||||
|
||||
struct zstd_parameters zstd_get_params(int level,
|
||||
unsigned long long estimated_src_size)
|
||||
{
|
||||
const ZSTD_parameters params = ZSTD_getParams(level, estimated_src_size, 0);
|
||||
struct zstd_parameters out;
|
||||
|
||||
/* no-op */
|
||||
zstd_check_structs();
|
||||
ZSTD_memcpy(&out, ¶ms, sizeof(out));
|
||||
return out;
|
||||
}
|
||||
EXPORT_SYMBOL(zstd_get_params);
|
||||
|
||||
size_t zstd_cctx_workspace_bound(
|
||||
const struct zstd_compression_parameters *cparams)
|
||||
{
|
||||
ZSTD_compressionParameters p;
|
||||
|
||||
ZSTD_memcpy(&p, cparams, sizeof(p));
|
||||
return ZSTD_estimateCCtxSize_usingCParams(p);
|
||||
}
|
||||
EXPORT_SYMBOL(zstd_cctx_workspace_bound);
|
||||
|
||||
zstd_cctx *zstd_init_cctx(void *workspace, size_t workspace_size)
|
||||
{
|
||||
if (workspace == NULL)
|
||||
return NULL;
|
||||
return ZSTD_initStaticCCtx(workspace, workspace_size);
|
||||
}
|
||||
EXPORT_SYMBOL(zstd_init_cctx);
|
||||
|
||||
size_t zstd_compress_cctx(zstd_cctx *cctx, void *dst, size_t dst_capacity,
|
||||
const void *src, size_t src_size, const struct zstd_parameters *parameters)
|
||||
{
|
||||
ZSTD_parameters p;
|
||||
|
||||
ZSTD_memcpy(&p, parameters, sizeof(p));
|
||||
return ZSTD_compress_advanced(cctx, dst, dst_capacity, src, src_size, NULL, 0, p);
|
||||
}
|
||||
EXPORT_SYMBOL(zstd_compress_cctx);
|
||||
|
||||
size_t zstd_cstream_workspace_bound(
|
||||
const struct zstd_compression_parameters *cparams)
|
||||
{
|
||||
ZSTD_compressionParameters p;
|
||||
|
||||
ZSTD_memcpy(&p, cparams, sizeof(p));
|
||||
return ZSTD_estimateCStreamSize_usingCParams(p);
|
||||
}
|
||||
EXPORT_SYMBOL(zstd_cstream_workspace_bound);
|
||||
|
||||
zstd_cstream *zstd_init_cstream(const struct zstd_parameters *parameters,
|
||||
unsigned long long pledged_src_size, void *workspace, size_t workspace_size)
|
||||
{
|
||||
ZSTD_parameters p;
|
||||
zstd_cstream *cstream;
|
||||
size_t ret;
|
||||
|
||||
if (workspace == NULL)
|
||||
return NULL;
|
||||
|
||||
cstream = ZSTD_initStaticCStream(workspace, workspace_size);
|
||||
if (cstream == NULL)
|
||||
return NULL;
|
||||
|
||||
/* 0 means unknown in linux zstd API but means 0 in new zstd API */
|
||||
if (pledged_src_size == 0)
|
||||
pledged_src_size = ZSTD_CONTENTSIZE_UNKNOWN;
|
||||
|
||||
ZSTD_memcpy(&p, parameters, sizeof(p));
|
||||
ret = ZSTD_initCStream_advanced(cstream, NULL, 0, p, pledged_src_size);
|
||||
if (ZSTD_isError(ret))
|
||||
return NULL;
|
||||
|
||||
return cstream;
|
||||
}
|
||||
EXPORT_SYMBOL(zstd_init_cstream);
|
||||
|
||||
size_t zstd_reset_cstream(zstd_cstream *cstream,
|
||||
unsigned long long pledged_src_size)
|
||||
{
|
||||
return ZSTD_resetCStream(cstream, pledged_src_size);
|
||||
}
|
||||
EXPORT_SYMBOL(zstd_reset_cstream);
|
||||
|
||||
size_t zstd_compress_stream(zstd_cstream *cstream,
|
||||
struct zstd_out_buffer *output, struct zstd_in_buffer *input)
|
||||
{
|
||||
ZSTD_outBuffer o;
|
||||
ZSTD_inBuffer i;
|
||||
size_t ret;
|
||||
|
||||
ZSTD_memcpy(&o, output, sizeof(o));
|
||||
ZSTD_memcpy(&i, input, sizeof(i));
|
||||
ret = ZSTD_compressStream(cstream, &o, &i);
|
||||
ZSTD_memcpy(output, &o, sizeof(o));
|
||||
ZSTD_memcpy(input, &i, sizeof(i));
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(zstd_compress_stream);
|
||||
|
||||
size_t zstd_flush_stream(zstd_cstream *cstream, struct zstd_out_buffer *output)
|
||||
{
|
||||
ZSTD_outBuffer o;
|
||||
size_t ret;
|
||||
|
||||
ZSTD_memcpy(&o, output, sizeof(o));
|
||||
ret = ZSTD_flushStream(cstream, &o);
|
||||
ZSTD_memcpy(output, &o, sizeof(o));
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(zstd_flush_stream);
|
||||
|
||||
size_t zstd_end_stream(zstd_cstream *cstream, struct zstd_out_buffer *output)
|
||||
{
|
||||
ZSTD_outBuffer o;
|
||||
size_t ret;
|
||||
|
||||
ZSTD_memcpy(&o, output, sizeof(o));
|
||||
ret = ZSTD_endStream(cstream, &o);
|
||||
ZSTD_memcpy(output, &o, sizeof(o));
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(zstd_end_stream);
|
||||
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_DESCRIPTION("Zstd Compressor");
|
||||
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* Copyright (c) 2016-present, Facebook, 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).
|
||||
*/
|
||||
|
||||
/* Compression level or 0 to disable */
|
||||
#define DO_ZLIB 9
|
||||
/* Compression level or 0 to disable */
|
||||
#define DO_ZSTD 0
|
||||
/* Buffer size */
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#if DO_ZSTD
|
||||
#include <linux/zstd.h>
|
||||
#endif
|
||||
|
||||
#if DO_ZLIB
|
||||
#include <linux/zlib.h>
|
||||
#endif
|
||||
|
||||
/* Device name to pass to register_chrdev(). */
|
||||
#define DEVICE_NAME "zstd_compress_test"
|
||||
|
||||
/* Dynamically allocated device major number */
|
||||
static int device_major;
|
||||
|
||||
/*
|
||||
* We reuse the same state, and thus can compress only one file at a time.
|
||||
*/
|
||||
static bool device_is_open;
|
||||
|
||||
|
||||
static void *workspace = NULL;
|
||||
|
||||
/*
|
||||
* Input buffer used to put data coming from userspace.
|
||||
*/
|
||||
static uint8_t buffer_in[BUFFER_SIZE];
|
||||
static uint8_t buffer_out[BUFFER_SIZE];
|
||||
|
||||
static uint64_t uncompressed_len;
|
||||
static uint64_t compressed_len;
|
||||
|
||||
#if DO_ZSTD
|
||||
|
||||
static ZSTD_CStream *state;
|
||||
|
||||
static ZSTD_inBuffer input = {
|
||||
.src = buffer_in,
|
||||
.size = sizeof(buffer_in),
|
||||
.pos = sizeof(buffer_in),
|
||||
};
|
||||
|
||||
static ZSTD_outBuffer output = {
|
||||
.dst = buffer_out,
|
||||
.size = sizeof(buffer_out),
|
||||
.pos = sizeof(buffer_out),
|
||||
};
|
||||
|
||||
#endif /* DO_ZSTD */
|
||||
|
||||
#if DO_ZLIB
|
||||
|
||||
static z_stream state = {
|
||||
.next_in = buffer_in,
|
||||
.avail_in = 0,
|
||||
.total_in = 0,
|
||||
|
||||
.next_out = buffer_out,
|
||||
.avail_out = sizeof(buffer_out),
|
||||
.total_out = 0,
|
||||
|
||||
.msg = NULL,
|
||||
.state = NULL,
|
||||
.workspace = NULL,
|
||||
};
|
||||
|
||||
#endif /* DO_ZLIB */
|
||||
|
||||
static int zstd_compress_test_open(struct inode *i, struct file *f)
|
||||
{
|
||||
if (device_is_open)
|
||||
return -EBUSY;
|
||||
|
||||
device_is_open = true;
|
||||
|
||||
uncompressed_len = compressed_len = 0;
|
||||
|
||||
#if DO_ZSTD
|
||||
if (ZSTD_isError(ZSTD_resetCStream(state, 0)))
|
||||
return -EIO;
|
||||
#endif
|
||||
|
||||
#if DO_ZLIB
|
||||
if (zlib_deflateReset(&state) != Z_OK)
|
||||
return -EIO;
|
||||
#endif
|
||||
|
||||
printk(KERN_INFO DEVICE_NAME ": opened\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zstd_compress_test_release(struct inode *i, struct file *f)
|
||||
{
|
||||
device_is_open = false;
|
||||
|
||||
#if DO_ZSTD
|
||||
do {
|
||||
size_t ret;
|
||||
|
||||
output.pos = 0;
|
||||
ret = ZSTD_endStream(state, &output);
|
||||
if (ZSTD_isError(ret)) {
|
||||
printk(KERN_INFO DEVICE_NAME ": zstd end error %u\n", ZSTD_getErrorCode(ret));
|
||||
return -EIO;
|
||||
}
|
||||
compressed_len += output.pos;
|
||||
} while (output.pos != output.size);
|
||||
#endif
|
||||
|
||||
#if DO_ZLIB
|
||||
for (;;) {
|
||||
int ret;
|
||||
|
||||
state.next_out = buffer_out;
|
||||
state.avail_out = sizeof(buffer_out);
|
||||
ret = zlib_deflate(&state, Z_FINISH);
|
||||
compressed_len += sizeof(buffer_out) - state.avail_out;
|
||||
if (ret == Z_STREAM_END)
|
||||
break;
|
||||
if (ret != Z_OK) {
|
||||
printk(KERN_INFO DEVICE_NAME ": zlib end error %d: %s\n", ret, state.msg);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
printk(KERN_INFO DEVICE_NAME ": uncompressed_len = %llu\n", uncompressed_len);
|
||||
printk(KERN_INFO DEVICE_NAME ": compressed_len = %llu\n", compressed_len);
|
||||
printk(KERN_INFO DEVICE_NAME ": closed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Hash the data given to us from userspace.
|
||||
*/
|
||||
static ssize_t zstd_compress_test_write(struct file *file,
|
||||
const char __user *buf, size_t size, loff_t *pos)
|
||||
{
|
||||
size_t remaining = size;
|
||||
|
||||
while (remaining > 0) {
|
||||
size_t const copy_size = min(remaining, sizeof(buffer_in));
|
||||
|
||||
if (copy_from_user(buffer_in, buf, copy_size))
|
||||
return -EFAULT;
|
||||
buf += copy_size;
|
||||
remaining -= copy_size;
|
||||
uncompressed_len += copy_size;
|
||||
|
||||
#if DO_ZSTD
|
||||
input.pos = 0;
|
||||
input.size = copy_size;
|
||||
while (input.pos != input.size) {
|
||||
size_t ret;
|
||||
|
||||
output.pos = 0;
|
||||
ret = ZSTD_compressStream(state, &output, &input);
|
||||
if (ZSTD_isError(ret)) {
|
||||
printk(KERN_INFO DEVICE_NAME ": zstd compress error %u\n", ZSTD_getErrorCode(ret));
|
||||
return -EIO;
|
||||
}
|
||||
compressed_len += output.pos;
|
||||
}
|
||||
#endif
|
||||
#if DO_ZLIB
|
||||
state.next_in = buffer_in;
|
||||
state.avail_in = copy_size;
|
||||
while (state.avail_in > 0) {
|
||||
int ret;
|
||||
|
||||
state.next_out = buffer_out;
|
||||
state.avail_out = sizeof(buffer_out);
|
||||
ret = zlib_deflate(&state, Z_NO_FLUSH);
|
||||
compressed_len += sizeof(buffer_out) - state.avail_out;
|
||||
if (ret != Z_OK) {
|
||||
printk(KERN_INFO DEVICE_NAME ": zlib end error %d: %s\n", ret, state.msg);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return size;
|
||||
}
|
||||
/* register the character device. */
|
||||
static int __init zstd_compress_test_init(void)
|
||||
{
|
||||
static const struct file_operations fileops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = &zstd_compress_test_open,
|
||||
.release = &zstd_compress_test_release,
|
||||
.write = &zstd_compress_test_write
|
||||
};
|
||||
size_t workspace_size = 0;
|
||||
#if DO_ZSTD
|
||||
ZSTD_parameters params;
|
||||
#endif
|
||||
|
||||
device_major = register_chrdev(0, DEVICE_NAME, &fileops);
|
||||
if (device_major < 0) {
|
||||
return device_major;
|
||||
}
|
||||
|
||||
#if DO_ZSTD
|
||||
params = ZSTD_getParams(DO_ZSTD, 0, 0);
|
||||
workspace_size = ZSTD_CStreamWorkspaceBound(params.cParams);
|
||||
|
||||
if (!(workspace = vmalloc(workspace_size)))
|
||||
goto fail;
|
||||
if (!(state = ZSTD_initCStream(params, 0, workspace, workspace_size)))
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
#if DO_ZLIB
|
||||
workspace_size = zlib_deflate_workspacesize(MAX_WBITS, MAX_MEM_LEVEL);
|
||||
|
||||
if (!(workspace = vmalloc(workspace_size)))
|
||||
goto fail;
|
||||
state.workspace = workspace;
|
||||
if (zlib_deflateInit(&state, DO_ZLIB) != Z_OK)
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
printk(KERN_INFO DEVICE_NAME ": module loaded\n");
|
||||
printk(KERN_INFO DEVICE_NAME ": compression requires %zu bytes of memory\n", workspace_size);
|
||||
printk(KERN_INFO DEVICE_NAME ": Create a device node with "
|
||||
"'mknod " DEVICE_NAME " c %d 0' and write data "
|
||||
"to it.\n", device_major);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
printk(KERN_INFO DEVICE_NAME ": failed to load module\n");
|
||||
if (workspace) {
|
||||
vfree(workspace);
|
||||
workspace = NULL;
|
||||
}
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void __exit zstd_compress_test_exit(void)
|
||||
{
|
||||
unregister_chrdev(device_major, DEVICE_NAME);
|
||||
#if DO_ZLIB
|
||||
zlib_deflateEnd(&state);
|
||||
#endif
|
||||
if (workspace) {
|
||||
vfree(workspace);
|
||||
workspace = NULL;
|
||||
}
|
||||
printk(KERN_INFO DEVICE_NAME ": module unloaded\n");
|
||||
}
|
||||
|
||||
module_init(zstd_compress_test_init);
|
||||
module_exit(zstd_compress_test_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Zstd compression tester");
|
||||
MODULE_VERSION("1.0");
|
||||
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
@@ -1,122 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/zstd.h>
|
||||
|
||||
#include "zstd.h"
|
||||
#include "common/zstd_deps.h"
|
||||
#include "common/zstd_errors.h"
|
||||
|
||||
/* Common symbols. zstd_compress must depend on zstd_decompress. */
|
||||
|
||||
unsigned int zstd_is_error(size_t code)
|
||||
{
|
||||
return ZSTD_isError(code);
|
||||
}
|
||||
EXPORT_SYMBOL(zstd_is_error);
|
||||
|
||||
int zstd_get_error_code(size_t code)
|
||||
{
|
||||
return ZSTD_getErrorCode(code);
|
||||
}
|
||||
EXPORT_SYMBOL(zstd_get_error_code);
|
||||
|
||||
const char *zstd_get_error_name(size_t code)
|
||||
{
|
||||
return ZSTD_getErrorName(code);
|
||||
}
|
||||
EXPORT_SYMBOL(zstd_get_error_name);
|
||||
|
||||
/* Decompression symbols. */
|
||||
|
||||
size_t zstd_dctx_workspace_bound(void)
|
||||
{
|
||||
return ZSTD_estimateDCtxSize();
|
||||
}
|
||||
EXPORT_SYMBOL(zstd_dctx_workspace_bound);
|
||||
|
||||
zstd_dctx *zstd_init_dctx(void *workspace, size_t workspace_size)
|
||||
{
|
||||
if (workspace == NULL)
|
||||
return NULL;
|
||||
return ZSTD_initStaticDCtx(workspace, workspace_size);
|
||||
}
|
||||
EXPORT_SYMBOL(zstd_init_dctx);
|
||||
|
||||
size_t zstd_decompress_dctx(zstd_dctx *dctx, void *dst, size_t dst_capacity,
|
||||
const void *src, size_t src_size)
|
||||
{
|
||||
return ZSTD_decompressDCtx(dctx, dst, dst_capacity, src, src_size);
|
||||
}
|
||||
EXPORT_SYMBOL(zstd_decompress_dctx);
|
||||
|
||||
size_t zstd_dstream_workspace_bound(size_t max_window_size)
|
||||
{
|
||||
return ZSTD_estimateDStreamSize(max_window_size);
|
||||
}
|
||||
EXPORT_SYMBOL(zstd_dstream_workspace_bound);
|
||||
|
||||
zstd_dstream *zstd_init_dstream(size_t max_window_size, void *workspace,
|
||||
size_t workspace_size)
|
||||
{
|
||||
if (workspace == NULL)
|
||||
return NULL;
|
||||
(void)max_window_size;
|
||||
return ZSTD_initStaticDStream(workspace, workspace_size);
|
||||
}
|
||||
EXPORT_SYMBOL(zstd_init_dstream);
|
||||
|
||||
size_t zstd_reset_dstream(zstd_dstream *dstream)
|
||||
{
|
||||
return ZSTD_resetDStream(dstream);
|
||||
}
|
||||
EXPORT_SYMBOL(zstd_reset_dstream);
|
||||
|
||||
size_t zstd_decompress_stream(zstd_dstream *dstream,
|
||||
struct zstd_out_buffer *output, struct zstd_in_buffer *input)
|
||||
{
|
||||
ZSTD_outBuffer o;
|
||||
ZSTD_inBuffer i;
|
||||
size_t ret;
|
||||
|
||||
ZSTD_memcpy(&o, output, sizeof(o));
|
||||
ZSTD_memcpy(&i, input, sizeof(i));
|
||||
ret = ZSTD_decompressStream(dstream, &o, &i);
|
||||
ZSTD_memcpy(output, &o, sizeof(o));
|
||||
ZSTD_memcpy(input, &i, sizeof(i));
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(zstd_decompress_stream);
|
||||
|
||||
size_t zstd_find_frame_compressed_size(const void *src, size_t src_size)
|
||||
{
|
||||
return ZSTD_findFrameCompressedSize(src, src_size);
|
||||
}
|
||||
EXPORT_SYMBOL(zstd_find_frame_compressed_size);
|
||||
|
||||
size_t zstd_get_frame_params(struct zstd_frame_params *params, const void *src,
|
||||
size_t src_size)
|
||||
{
|
||||
ZSTD_frameHeader h;
|
||||
const size_t ret = ZSTD_getFrameHeader(&h, src, src_size);
|
||||
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
if (h.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN)
|
||||
params->frame_content_size = h.frameContentSize;
|
||||
else
|
||||
params->frame_content_size = 0;
|
||||
|
||||
params->window_size = h.windowSize;
|
||||
params->dict_id = h.dictID;
|
||||
params->checksum_flag = h.checksumFlag;
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(zstd_get_frame_params);
|
||||
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_DESCRIPTION("Zstd Decompressor");
|
||||
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* Copyright (c) 2016-present, Facebook, 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).
|
||||
*/
|
||||
|
||||
/* Compression level or 0 to disable */
|
||||
#define DO_ZLIB 1
|
||||
/* Compression level or 0 to disable */
|
||||
#define DO_ZSTD 0
|
||||
/* Buffer size */
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#if DO_ZSTD
|
||||
#include <linux/zstd.h>
|
||||
#endif
|
||||
|
||||
#if DO_ZLIB
|
||||
#include <linux/zlib.h>
|
||||
#endif
|
||||
|
||||
/* Device name to pass to register_chrdev(). */
|
||||
#define DEVICE_NAME "zstd_decompress_test"
|
||||
|
||||
/* Dynamically allocated device major number */
|
||||
static int device_major;
|
||||
|
||||
/*
|
||||
* We reuse the same state, and thus can compress only one file at a time.
|
||||
*/
|
||||
static bool device_is_open;
|
||||
|
||||
|
||||
static void *workspace = NULL;
|
||||
|
||||
/*
|
||||
* Input buffer used to put data coming from userspace.
|
||||
*/
|
||||
static uint8_t buffer_in[BUFFER_SIZE];
|
||||
static uint8_t buffer_out[BUFFER_SIZE];
|
||||
|
||||
static uint64_t uncompressed_len;
|
||||
static uint64_t compressed_len;
|
||||
|
||||
#if DO_ZSTD
|
||||
|
||||
static ZSTD_DStream *state;
|
||||
|
||||
static ZSTD_inBuffer input = {
|
||||
.src = buffer_in,
|
||||
.size = sizeof(buffer_in),
|
||||
.pos = sizeof(buffer_in),
|
||||
};
|
||||
|
||||
static ZSTD_outBuffer output = {
|
||||
.dst = buffer_out,
|
||||
.size = sizeof(buffer_out),
|
||||
.pos = sizeof(buffer_out),
|
||||
};
|
||||
|
||||
#endif /* DO_ZSTD */
|
||||
|
||||
#if DO_ZLIB
|
||||
|
||||
static z_stream state = {
|
||||
.next_in = buffer_in,
|
||||
.avail_in = 0,
|
||||
.total_in = 0,
|
||||
|
||||
.next_out = buffer_out,
|
||||
.avail_out = sizeof(buffer_out),
|
||||
.total_out = 0,
|
||||
|
||||
.msg = NULL,
|
||||
.state = NULL,
|
||||
.workspace = NULL,
|
||||
};
|
||||
|
||||
#endif /* DO_ZLIB */
|
||||
|
||||
static int zstd_decompress_test_open(struct inode *i, struct file *f)
|
||||
{
|
||||
if (device_is_open)
|
||||
return -EBUSY;
|
||||
|
||||
device_is_open = true;
|
||||
|
||||
uncompressed_len = compressed_len = 0;
|
||||
|
||||
#if DO_ZSTD
|
||||
if (ZSTD_isError(ZSTD_resetDStream(state)))
|
||||
return -EIO;
|
||||
#endif
|
||||
|
||||
#if DO_ZLIB
|
||||
if (zlib_inflateReset(&state) != Z_OK)
|
||||
return -EIO;
|
||||
#endif
|
||||
|
||||
printk(KERN_INFO DEVICE_NAME ": opened\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zstd_decompress_test_release(struct inode *i, struct file *f)
|
||||
{
|
||||
device_is_open = false;
|
||||
|
||||
printk(KERN_INFO DEVICE_NAME ": uncompressed_len = %llu\n", uncompressed_len);
|
||||
printk(KERN_INFO DEVICE_NAME ": compressed_len = %llu\n", compressed_len);
|
||||
printk(KERN_INFO DEVICE_NAME ": closed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Hash the data given to us from userspace.
|
||||
*/
|
||||
static ssize_t zstd_decompress_test_write(struct file *file,
|
||||
const char __user *buf, size_t size, loff_t *pos)
|
||||
{
|
||||
size_t remaining = size;
|
||||
|
||||
while (remaining > 0) {
|
||||
size_t const copy_size = min(remaining, sizeof(buffer_in));
|
||||
|
||||
if (copy_from_user(buffer_in, buf, copy_size))
|
||||
return -EFAULT;
|
||||
buf += copy_size;
|
||||
remaining -= copy_size;
|
||||
compressed_len += copy_size;
|
||||
|
||||
#if DO_ZSTD
|
||||
input.pos = 0;
|
||||
input.size = copy_size;
|
||||
while (input.pos != input.size) {
|
||||
size_t ret;
|
||||
|
||||
output.pos = 0;
|
||||
ret = ZSTD_decompressStream(state, &output, &input);
|
||||
if (ZSTD_isError(ret)) {
|
||||
printk(KERN_INFO DEVICE_NAME ": zstd decompress error %u\n", ZSTD_getErrorCode(ret));
|
||||
return -EIO;
|
||||
}
|
||||
uncompressed_len += output.pos;
|
||||
}
|
||||
#endif
|
||||
#if DO_ZLIB
|
||||
state.next_in = buffer_in;
|
||||
state.avail_in = copy_size;
|
||||
while (state.avail_in > 0) {
|
||||
int ret;
|
||||
|
||||
state.next_out = buffer_out;
|
||||
state.avail_out = sizeof(buffer_out);
|
||||
ret = zlib_inflate(&state, Z_NO_FLUSH);
|
||||
uncompressed_len += sizeof(buffer_out) - state.avail_out;
|
||||
if (ret != Z_OK && ret != Z_STREAM_END) {
|
||||
printk(KERN_INFO DEVICE_NAME ": zlib decompress error %d: %s\n", ret, state.msg);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return size;
|
||||
}
|
||||
/* register the character device. */
|
||||
static int __init zstd_decompress_test_init(void)
|
||||
{
|
||||
static const struct file_operations fileops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = &zstd_decompress_test_open,
|
||||
.release = &zstd_decompress_test_release,
|
||||
.write = &zstd_decompress_test_write
|
||||
};
|
||||
size_t workspace_size = 0;
|
||||
#if DO_ZSTD
|
||||
ZSTD_parameters params;
|
||||
size_t max_window_size;
|
||||
#endif
|
||||
|
||||
device_major = register_chrdev(0, DEVICE_NAME, &fileops);
|
||||
if (device_major < 0) {
|
||||
return device_major;
|
||||
}
|
||||
|
||||
#if DO_ZSTD
|
||||
params = ZSTD_getParams(DO_ZSTD, 0, 0);
|
||||
max_window_size = (size_t)1 << params.cParams.windowLog;
|
||||
workspace_size = ZSTD_DStreamWorkspaceBound(max_window_size);
|
||||
|
||||
if (!(workspace = vmalloc(workspace_size)))
|
||||
goto fail;
|
||||
if (!(state = ZSTD_initDStream(max_window_size, workspace, workspace_size)))
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
#if DO_ZLIB
|
||||
workspace_size = zlib_inflate_workspacesize();
|
||||
|
||||
if (!(workspace = vmalloc(workspace_size)))
|
||||
goto fail;
|
||||
state.workspace = workspace;
|
||||
if (zlib_inflateInit(&state) != Z_OK)
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
printk(KERN_INFO DEVICE_NAME ": module loaded\n");
|
||||
printk(KERN_INFO DEVICE_NAME ": decompression requires %zu bytes of memory\n", workspace_size);
|
||||
printk(KERN_INFO DEVICE_NAME ": Create a device node with "
|
||||
"'mknod " DEVICE_NAME " c %d 0' and write data "
|
||||
"to it.\n", device_major);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
printk(KERN_INFO DEVICE_NAME ": failed to load module\n");
|
||||
if (workspace) {
|
||||
vfree(workspace);
|
||||
workspace = NULL;
|
||||
}
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void __exit zstd_decompress_test_exit(void)
|
||||
{
|
||||
unregister_chrdev(device_major, DEVICE_NAME);
|
||||
#if DO_ZLIB
|
||||
zlib_deflateEnd(&state);
|
||||
#endif
|
||||
if (workspace) {
|
||||
vfree(workspace);
|
||||
workspace = NULL;
|
||||
}
|
||||
printk(KERN_INFO DEVICE_NAME ": module unloaded\n");
|
||||
}
|
||||
|
||||
module_init(zstd_decompress_test_init);
|
||||
module_exit(zstd_decompress_test_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Zstd decompression tester");
|
||||
MODULE_VERSION("1.0");
|
||||
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2020, Facebook, 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file provides common libc dependencies that zstd requires.
|
||||
* The purpose is to allow replacing this file with a custom implementation
|
||||
* to compile zstd without libc support.
|
||||
*/
|
||||
|
||||
/* Need:
|
||||
* NULL
|
||||
* INT_MAX
|
||||
* UINT_MAX
|
||||
* ZSTD_memcpy()
|
||||
* ZSTD_memset()
|
||||
* ZSTD_memmove()
|
||||
*/
|
||||
#ifndef ZSTD_DEPS_COMMON
|
||||
#define ZSTD_DEPS_COMMON
|
||||
|
||||
#include <linux/limits.h>
|
||||
#include <linux/stddef.h>
|
||||
|
||||
#define ZSTD_memcpy(d,s,n) __builtin_memcpy((d),(s),(n))
|
||||
#define ZSTD_memmove(d,s,n) __builtin_memmove((d),(s),(n))
|
||||
#define ZSTD_memset(d,s,n) __builtin_memset((d),(s),(n))
|
||||
|
||||
#endif /* ZSTD_DEPS_COMMON */
|
||||
|
||||
/*
|
||||
* Define malloc as always failing. That means the user must
|
||||
* either use ZSTD_customMem or statically allocate memory.
|
||||
* Need:
|
||||
* ZSTD_malloc()
|
||||
* ZSTD_free()
|
||||
* ZSTD_calloc()
|
||||
*/
|
||||
#ifdef ZSTD_DEPS_NEED_MALLOC
|
||||
#ifndef ZSTD_DEPS_MALLOC
|
||||
#define ZSTD_DEPS_MALLOC
|
||||
|
||||
#define ZSTD_malloc(s) ({ (void)(s); NULL; })
|
||||
#define ZSTD_free(p) ((void)(p))
|
||||
#define ZSTD_calloc(n,s) ({ (void)(n); (void)(s); NULL; })
|
||||
|
||||
#endif /* ZSTD_DEPS_MALLOC */
|
||||
#endif /* ZSTD_DEPS_NEED_MALLOC */
|
||||
|
||||
/*
|
||||
* Provides 64-bit math support.
|
||||
* Need:
|
||||
* U64 ZSTD_div64(U64 dividend, U32 divisor)
|
||||
*/
|
||||
#ifdef ZSTD_DEPS_NEED_MATH64
|
||||
#ifndef ZSTD_DEPS_MATH64
|
||||
#define ZSTD_DEPS_MATH64
|
||||
|
||||
#include <linux/math64.h>
|
||||
|
||||
static uint64_t ZSTD_div64(uint64_t dividend, uint32_t divisor) {
|
||||
return div_u64(dividend, divisor);
|
||||
}
|
||||
|
||||
#endif /* ZSTD_DEPS_MATH64 */
|
||||
#endif /* ZSTD_DEPS_NEED_MATH64 */
|
||||
|
||||
/*
|
||||
* This is only requested when DEBUGLEVEL >= 1, meaning
|
||||
* it is disabled in production.
|
||||
* Need:
|
||||
* assert()
|
||||
*/
|
||||
#ifdef ZSTD_DEPS_NEED_ASSERT
|
||||
#ifndef ZSTD_DEPS_ASSERT
|
||||
#define ZSTD_DEPS_ASSERT
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#define assert(x) WARN_ON((x))
|
||||
|
||||
#endif /* ZSTD_DEPS_ASSERT */
|
||||
#endif /* ZSTD_DEPS_NEED_ASSERT */
|
||||
|
||||
/*
|
||||
* This is only requested when DEBUGLEVEL >= 2, meaning
|
||||
* it is disabled in production.
|
||||
* Need:
|
||||
* ZSTD_DEBUG_PRINT()
|
||||
*/
|
||||
#ifdef ZSTD_DEPS_NEED_IO
|
||||
#ifndef ZSTD_DEPS_IO
|
||||
#define ZSTD_DEPS_IO
|
||||
|
||||
#include <linux/printk.h>
|
||||
|
||||
#define ZSTD_DEBUG_PRINT(...) pr_debug(__VA_ARGS__)
|
||||
|
||||
#endif /* ZSTD_DEPS_IO */
|
||||
#endif /* ZSTD_DEPS_NEED_IO */
|
||||
|
||||
/*
|
||||
* Only requested when MSAN is enabled.
|
||||
* Need:
|
||||
* intptr_t
|
||||
*/
|
||||
#ifdef ZSTD_DEPS_NEED_STDINT
|
||||
#ifndef ZSTD_DEPS_STDINT
|
||||
#define ZSTD_DEPS_STDINT
|
||||
|
||||
/*
|
||||
* The Linux Kernel doesn't provide intptr_t, only uintptr_t, which
|
||||
* is an unsigned long.
|
||||
*/
|
||||
typedef long intptr_t;
|
||||
|
||||
#endif /* ZSTD_DEPS_STDINT */
|
||||
#endif /* ZSTD_DEPS_NEED_STDINT */
|
||||
@@ -1,42 +0,0 @@
|
||||
## Edit Distance Match Finder
|
||||
|
||||
```
|
||||
/* This match finder leverages techniques used in file comparison algorithms
|
||||
* to find matches between a dictionary and a source file.
|
||||
*
|
||||
* The original motivation for studying this approach was to try and optimize
|
||||
* Zstandard for the use case of patching: the most common scenario being
|
||||
* updating an existing software package with the next version. When patching,
|
||||
* the difference between the old version of the package and the new version
|
||||
* is generally tiny (most of the new file will be identical to
|
||||
* the old one). In more technical terms, the edit distance (the minimal number
|
||||
* of changes required to take one sequence of bytes to another) between the
|
||||
* files would be small relative to the size of the file.
|
||||
*
|
||||
* Various 'diffing' algorithms utilize this notion of edit distance and
|
||||
* the corrensponding concept of a minimal edit script between two
|
||||
* sequences to identify the regions within two files where they differ.
|
||||
* The core algorithm used in this match finder is described in:
|
||||
*
|
||||
* "An O(ND) Difference Algorithm and its Variations", Eugene W. Myers,
|
||||
* Algorithmica Vol. 1, 1986, pp. 251-266,
|
||||
* <https://doi.org/10.1007/BF01840446>.
|
||||
*
|
||||
* Additional algorithmic heuristics for speed improvement have also been included.
|
||||
* These we inspired from implementations of various regular and binary diffing
|
||||
* algorithms such as GNU diff, bsdiff, and Xdelta.
|
||||
*
|
||||
* Note: after some experimentation, this approach proved to not provide enough
|
||||
* utility to justify the additional CPU used in finding matches. The one area
|
||||
* where this approach consistenly outperforms Zstandard even on level 19 is
|
||||
* when compressing small files (<10 KB) using a equally small dictionary that
|
||||
* is very similar to the source file. For the use case that this was intended,
|
||||
* (large similar files) this approach by itself took 5-10X longer than zstd-19 and
|
||||
* generally resulted in 2-3X larger files. The core advantage that zstd-19 has
|
||||
* over this appraoch for match finding is the overlapping matches. This approach
|
||||
* cannot find any.
|
||||
*
|
||||
* I'm leaving this in the contrib section in case this ever becomes interesting
|
||||
* to explore again.
|
||||
* */
|
||||
```
|
||||
@@ -1,558 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, 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.
|
||||
*/
|
||||
|
||||
/*-*************************************
|
||||
* Dependencies
|
||||
***************************************/
|
||||
|
||||
/* Currently relies on qsort when combining contiguous matches. This can probabily
|
||||
* be avoided but would require changes to the algorithm. The qsort is far from
|
||||
* the bottleneck in this algorithm even for medium sized files so it's probably
|
||||
* not worth trying to address */
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "zstd_edist.h"
|
||||
#include "mem.h"
|
||||
|
||||
/*-*************************************
|
||||
* Constants
|
||||
***************************************/
|
||||
|
||||
/* Just a sential for the entires of the diagnomal matrix */
|
||||
#define ZSTD_EDIST_DIAG_MAX (S32)(1 << 30)
|
||||
|
||||
/* How large should a snake be to be considered a 'big' snake.
|
||||
* For an explanation of what a 'snake' is with respect to the
|
||||
* edit distance matrix, see the linked paper in zstd_edist.h */
|
||||
#define ZSTD_EDIST_SNAKE_THRESH 20
|
||||
|
||||
/* After how many iterations should we start to use the heuristic
|
||||
* based on 'big' snakes */
|
||||
#define ZSTD_EDIST_SNAKE_ITER_THRESH 200
|
||||
|
||||
/* After how many iterations should be just give up and take
|
||||
* the best availabe edit script for this round */
|
||||
#define ZSTD_EDIST_EXPENSIVE_THRESH 1024
|
||||
|
||||
/*-*************************************
|
||||
* Structures
|
||||
***************************************/
|
||||
|
||||
typedef struct {
|
||||
U32 dictIdx;
|
||||
U32 srcIdx;
|
||||
U32 matchLength;
|
||||
} ZSTD_eDist_match;
|
||||
|
||||
typedef struct {
|
||||
const BYTE* dict;
|
||||
const BYTE* src;
|
||||
size_t dictSize;
|
||||
size_t srcSize;
|
||||
S32* forwardDiag; /* Entires of the forward diagonal stored here */
|
||||
S32* backwardDiag; /* Entires of the backward diagonal stored here.
|
||||
* Note: this buffer and the 'forwardDiag' buffer
|
||||
* are contiguous. See the ZSTD_eDist_genSequences */
|
||||
ZSTD_eDist_match* matches; /* Accumulate matches of length 1 in this buffer.
|
||||
* In a subsequence post-processing step, we combine
|
||||
* contiguous matches. */
|
||||
U32 nbMatches;
|
||||
} ZSTD_eDist_state;
|
||||
|
||||
typedef struct {
|
||||
S32 dictMid; /* The mid diagonal for the dictionary */
|
||||
S32 srcMid; /* The mid diagonal for the source */
|
||||
int lowUseHeuristics; /* Should we use heuristics for the low part */
|
||||
int highUseHeuristics; /* Should we use heuristics for the high part */
|
||||
} ZSTD_eDist_partition;
|
||||
|
||||
/*-*************************************
|
||||
* Internal
|
||||
***************************************/
|
||||
|
||||
static void ZSTD_eDist_diag(ZSTD_eDist_state* state,
|
||||
ZSTD_eDist_partition* partition,
|
||||
S32 dictLow, S32 dictHigh, S32 srcLow,
|
||||
S32 srcHigh, int useHeuristics)
|
||||
{
|
||||
S32* const forwardDiag = state->forwardDiag;
|
||||
S32* const backwardDiag = state->backwardDiag;
|
||||
const BYTE* const dict = state->dict;
|
||||
const BYTE* const src = state->src;
|
||||
|
||||
S32 const diagMin = dictLow - srcHigh;
|
||||
S32 const diagMax = dictHigh - srcLow;
|
||||
S32 const forwardMid = dictLow - srcLow;
|
||||
S32 const backwardMid = dictHigh - srcHigh;
|
||||
|
||||
S32 forwardMin = forwardMid;
|
||||
S32 forwardMax = forwardMid;
|
||||
S32 backwardMin = backwardMid;
|
||||
S32 backwardMax = backwardMid;
|
||||
int odd = (forwardMid - backwardMid) & 1;
|
||||
U32 iterations;
|
||||
|
||||
forwardDiag[forwardMid] = dictLow;
|
||||
backwardDiag[backwardMid] = dictHigh;
|
||||
|
||||
/* Main loop for updating diag entries. Unless useHeuristics is
|
||||
* set to false, this loop will run until it finds the minimal
|
||||
* edit script */
|
||||
for (iterations = 1;;iterations++) {
|
||||
S32 diag;
|
||||
int bigSnake = 0;
|
||||
|
||||
if (forwardMin > diagMin) {
|
||||
forwardMin--;
|
||||
forwardDiag[forwardMin - 1] = -1;
|
||||
} else {
|
||||
forwardMin++;
|
||||
}
|
||||
|
||||
if (forwardMax < diagMax) {
|
||||
forwardMax++;
|
||||
forwardDiag[forwardMax + 1] = -1;
|
||||
} else {
|
||||
forwardMax--;
|
||||
}
|
||||
|
||||
for (diag = forwardMax; diag >= forwardMin; diag -= 2) {
|
||||
S32 dictIdx;
|
||||
S32 srcIdx;
|
||||
S32 low = forwardDiag[diag - 1];
|
||||
S32 high = forwardDiag[diag + 1];
|
||||
S32 dictIdx0 = low < high ? high : low + 1;
|
||||
|
||||
for (dictIdx = dictIdx0, srcIdx = dictIdx0 - diag;
|
||||
dictIdx < dictHigh && srcIdx < srcHigh && dict[dictIdx] == src[srcIdx];
|
||||
dictIdx++, srcIdx++) continue;
|
||||
|
||||
if (dictIdx - dictIdx0 > ZSTD_EDIST_SNAKE_THRESH)
|
||||
bigSnake = 1;
|
||||
|
||||
forwardDiag[diag] = dictIdx;
|
||||
|
||||
if (odd && backwardMin <= diag && diag <= backwardMax && backwardDiag[diag] <= dictIdx) {
|
||||
partition->dictMid = dictIdx;
|
||||
partition->srcMid = srcIdx;
|
||||
partition->lowUseHeuristics = 0;
|
||||
partition->highUseHeuristics = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (backwardMin > diagMin) {
|
||||
backwardMin--;
|
||||
backwardDiag[backwardMin - 1] = ZSTD_EDIST_DIAG_MAX;
|
||||
} else {
|
||||
backwardMin++;
|
||||
}
|
||||
|
||||
if (backwardMax < diagMax) {
|
||||
backwardMax++;
|
||||
backwardDiag[backwardMax + 1] = ZSTD_EDIST_DIAG_MAX;
|
||||
} else {
|
||||
backwardMax--;
|
||||
}
|
||||
|
||||
|
||||
for (diag = backwardMax; diag >= backwardMin; diag -= 2) {
|
||||
S32 dictIdx;
|
||||
S32 srcIdx;
|
||||
S32 low = backwardDiag[diag - 1];
|
||||
S32 high = backwardDiag[diag + 1];
|
||||
S32 dictIdx0 = low < high ? low : high - 1;
|
||||
|
||||
for (dictIdx = dictIdx0, srcIdx = dictIdx0 - diag;
|
||||
dictLow < dictIdx && srcLow < srcIdx && dict[dictIdx - 1] == src[srcIdx - 1];
|
||||
dictIdx--, srcIdx--) continue;
|
||||
|
||||
if (dictIdx0 - dictIdx > ZSTD_EDIST_SNAKE_THRESH)
|
||||
bigSnake = 1;
|
||||
|
||||
backwardDiag[diag] = dictIdx;
|
||||
|
||||
if (!odd && forwardMin <= diag && diag <= forwardMax && dictIdx <= forwardDiag[diag]) {
|
||||
partition->dictMid = dictIdx;
|
||||
partition->srcMid = srcIdx;
|
||||
partition->lowUseHeuristics = 0;
|
||||
partition->highUseHeuristics = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!useHeuristics)
|
||||
continue;
|
||||
|
||||
/* Everything under this point is a heuritic. Using these will
|
||||
* substantially speed up the match finding. In some cases, taking
|
||||
* the total match finding time from several minutes to seconds.
|
||||
* Of course, the caveat is that the edit script found may no longer
|
||||
* be optimal */
|
||||
|
||||
/* Big snake heuristic */
|
||||
if (iterations > ZSTD_EDIST_SNAKE_ITER_THRESH && bigSnake) {
|
||||
{
|
||||
S32 best = 0;
|
||||
|
||||
for (diag = forwardMax; diag >= forwardMin; diag -= 2) {
|
||||
S32 diagDiag = diag - forwardMid;
|
||||
S32 dictIdx = forwardDiag[diag];
|
||||
S32 srcIdx = dictIdx - diag;
|
||||
S32 v = (dictIdx - dictLow) * 2 - diagDiag;
|
||||
|
||||
if (v > 12 * (iterations + (diagDiag < 0 ? -diagDiag : diagDiag))) {
|
||||
if (v > best
|
||||
&& dictLow + ZSTD_EDIST_SNAKE_THRESH <= dictIdx && dictIdx <= dictHigh
|
||||
&& srcLow + ZSTD_EDIST_SNAKE_THRESH <= srcIdx && srcIdx <= srcHigh) {
|
||||
S32 k;
|
||||
for (k = 1; dict[dictIdx - k] == src[srcIdx - k]; k++) {
|
||||
if (k == ZSTD_EDIST_SNAKE_THRESH) {
|
||||
best = v;
|
||||
partition->dictMid = dictIdx;
|
||||
partition->srcMid = srcIdx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (best > 0) {
|
||||
partition->lowUseHeuristics = 0;
|
||||
partition->highUseHeuristics = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
S32 best = 0;
|
||||
|
||||
for (diag = backwardMax; diag >= backwardMin; diag -= 2) {
|
||||
S32 diagDiag = diag - backwardMid;
|
||||
S32 dictIdx = backwardDiag[diag];
|
||||
S32 srcIdx = dictIdx - diag;
|
||||
S32 v = (dictHigh - dictIdx) * 2 + diagDiag;
|
||||
|
||||
if (v > 12 * (iterations + (diagDiag < 0 ? -diagDiag : diagDiag))) {
|
||||
if (v > best
|
||||
&& dictLow < dictIdx && dictIdx <= dictHigh - ZSTD_EDIST_SNAKE_THRESH
|
||||
&& srcLow < srcIdx && srcIdx <= srcHigh - ZSTD_EDIST_SNAKE_THRESH) {
|
||||
int k;
|
||||
for (k = 0; dict[dictIdx + k] == src[srcIdx + k]; k++) {
|
||||
if (k == ZSTD_EDIST_SNAKE_THRESH - 1) {
|
||||
best = v;
|
||||
partition->dictMid = dictIdx;
|
||||
partition->srcMid = srcIdx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (best > 0) {
|
||||
partition->lowUseHeuristics = 1;
|
||||
partition->highUseHeuristics = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* More general 'too expensive' heuristic */
|
||||
if (iterations >= ZSTD_EDIST_EXPENSIVE_THRESH) {
|
||||
S32 forwardDictSrcBest;
|
||||
S32 forwardDictBest = 0;
|
||||
S32 backwardDictSrcBest;
|
||||
S32 backwardDictBest = 0;
|
||||
|
||||
forwardDictSrcBest = -1;
|
||||
for (diag = forwardMax; diag >= forwardMin; diag -= 2) {
|
||||
S32 dictIdx = MIN(forwardDiag[diag], dictHigh);
|
||||
S32 srcIdx = dictIdx - diag;
|
||||
|
||||
if (srcHigh < srcIdx) {
|
||||
dictIdx = srcHigh + diag;
|
||||
srcIdx = srcHigh;
|
||||
}
|
||||
|
||||
if (forwardDictSrcBest < dictIdx + srcIdx) {
|
||||
forwardDictSrcBest = dictIdx + srcIdx;
|
||||
forwardDictBest = dictIdx;
|
||||
}
|
||||
}
|
||||
|
||||
backwardDictSrcBest = ZSTD_EDIST_DIAG_MAX;
|
||||
for (diag = backwardMax; diag >= backwardMin; diag -= 2) {
|
||||
S32 dictIdx = MAX(dictLow, backwardDiag[diag]);
|
||||
S32 srcIdx = dictIdx - diag;
|
||||
|
||||
if (srcIdx < srcLow) {
|
||||
dictIdx = srcLow + diag;
|
||||
srcIdx = srcLow;
|
||||
}
|
||||
|
||||
if (dictIdx + srcIdx < backwardDictSrcBest) {
|
||||
backwardDictSrcBest = dictIdx + srcIdx;
|
||||
backwardDictBest = dictIdx;
|
||||
}
|
||||
}
|
||||
|
||||
if ((dictHigh + srcHigh) - backwardDictSrcBest < forwardDictSrcBest - (dictLow + srcLow)) {
|
||||
partition->dictMid = forwardDictBest;
|
||||
partition->srcMid = forwardDictSrcBest - forwardDictBest;
|
||||
partition->lowUseHeuristics = 0;
|
||||
partition->highUseHeuristics = 1;
|
||||
} else {
|
||||
partition->dictMid = backwardDictBest;
|
||||
partition->srcMid = backwardDictSrcBest - backwardDictBest;
|
||||
partition->lowUseHeuristics = 1;
|
||||
partition->highUseHeuristics = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ZSTD_eDist_insertMatch(ZSTD_eDist_state* state,
|
||||
S32 const dictIdx, S32 const srcIdx)
|
||||
{
|
||||
state->matches[state->nbMatches].dictIdx = dictIdx;
|
||||
state->matches[state->nbMatches].srcIdx = srcIdx;
|
||||
state->matches[state->nbMatches].matchLength = 1;
|
||||
state->nbMatches++;
|
||||
}
|
||||
|
||||
static int ZSTD_eDist_compare(ZSTD_eDist_state* state,
|
||||
S32 dictLow, S32 dictHigh, S32 srcLow,
|
||||
S32 srcHigh, int useHeuristics)
|
||||
{
|
||||
const BYTE* const dict = state->dict;
|
||||
const BYTE* const src = state->src;
|
||||
|
||||
/* Found matches while traversing from the low end */
|
||||
while (dictLow < dictHigh && srcLow < srcHigh && dict[dictLow] == src[srcLow]) {
|
||||
ZSTD_eDist_insertMatch(state, dictLow, srcLow);
|
||||
dictLow++;
|
||||
srcLow++;
|
||||
}
|
||||
|
||||
/* Found matches while traversing from the high end */
|
||||
while (dictLow < dictHigh && srcLow < srcHigh && dict[dictHigh - 1] == src[srcHigh - 1]) {
|
||||
ZSTD_eDist_insertMatch(state, dictHigh - 1, srcHigh - 1);
|
||||
dictHigh--;
|
||||
srcHigh--;
|
||||
}
|
||||
|
||||
/* If the low and high end end up touching. If we wanted to make
|
||||
* note of the differences like most diffing algorithms do, we would
|
||||
* do so here. In our case, we're only concerned with matches
|
||||
* Note: if you wanted to find the edit distance of the algorithm,
|
||||
* you could just accumulate the cost for an insertion/deletion
|
||||
* below. */
|
||||
if (dictLow == dictHigh) {
|
||||
while (srcLow < srcHigh) {
|
||||
/* Reaching this point means inserting src[srcLow] into
|
||||
* the current position of dict */
|
||||
srcLow++;
|
||||
}
|
||||
} else if (srcLow == srcHigh) {
|
||||
while (dictLow < dictHigh) {
|
||||
/* Reaching this point means deleteing dict[dictLow] from
|
||||
* the current positino of dict */
|
||||
dictLow++;
|
||||
}
|
||||
} else {
|
||||
ZSTD_eDist_partition partition;
|
||||
partition.dictMid = 0;
|
||||
partition.srcMid = 0;
|
||||
ZSTD_eDist_diag(state, &partition, dictLow, dictHigh,
|
||||
srcLow, srcHigh, useHeuristics);
|
||||
if (ZSTD_eDist_compare(state, dictLow, partition.dictMid,
|
||||
srcLow, partition.srcMid, partition.lowUseHeuristics))
|
||||
return 1;
|
||||
if (ZSTD_eDist_compare(state, partition.dictMid, dictHigh,
|
||||
partition.srcMid, srcHigh, partition.highUseHeuristics))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ZSTD_eDist_matchComp(const void* p, const void* q)
|
||||
{
|
||||
S32 const l = ((ZSTD_eDist_match*)p)->srcIdx;
|
||||
S32 const r = ((ZSTD_eDist_match*)q)->srcIdx;
|
||||
return (l - r);
|
||||
}
|
||||
|
||||
/* The matches from the approach above will all be of the form
|
||||
* (dictIdx, srcIdx, 1). this method combines contiguous matches
|
||||
* of length MINMATCH or greater. Matches less than MINMATCH
|
||||
* are discarded */
|
||||
static void ZSTD_eDist_combineMatches(ZSTD_eDist_state* state)
|
||||
{
|
||||
/* Create a new buffer to put the combined matches into
|
||||
* and memcpy to state->matches after */
|
||||
ZSTD_eDist_match* combinedMatches =
|
||||
ZSTD_malloc(state->nbMatches * sizeof(ZSTD_eDist_match),
|
||||
ZSTD_defaultCMem);
|
||||
|
||||
U32 nbCombinedMatches = 1;
|
||||
size_t i;
|
||||
|
||||
/* Make sure that the srcIdx and dictIdx are in sorted order.
|
||||
* The combination step won't work otherwise */
|
||||
qsort(state->matches, state->nbMatches, sizeof(ZSTD_eDist_match), ZSTD_eDist_matchComp);
|
||||
|
||||
memcpy(combinedMatches, state->matches, sizeof(ZSTD_eDist_match));
|
||||
for (i = 1; i < state->nbMatches; i++) {
|
||||
ZSTD_eDist_match const match = state->matches[i];
|
||||
ZSTD_eDist_match const combinedMatch =
|
||||
combinedMatches[nbCombinedMatches - 1];
|
||||
if (combinedMatch.srcIdx + combinedMatch.matchLength == match.srcIdx &&
|
||||
combinedMatch.dictIdx + combinedMatch.matchLength == match.dictIdx) {
|
||||
combinedMatches[nbCombinedMatches - 1].matchLength++;
|
||||
} else {
|
||||
/* Discard matches that are less than MINMATCH */
|
||||
if (combinedMatches[nbCombinedMatches - 1].matchLength < MINMATCH) {
|
||||
nbCombinedMatches--;
|
||||
}
|
||||
|
||||
memcpy(combinedMatches + nbCombinedMatches,
|
||||
state->matches + i, sizeof(ZSTD_eDist_match));
|
||||
nbCombinedMatches++;
|
||||
}
|
||||
}
|
||||
memcpy(state->matches, combinedMatches, nbCombinedMatches * sizeof(ZSTD_eDist_match));
|
||||
state->nbMatches = nbCombinedMatches;
|
||||
ZSTD_free(combinedMatches, ZSTD_defaultCMem);
|
||||
}
|
||||
|
||||
static size_t ZSTD_eDist_convertMatchesToSequences(ZSTD_Sequence* sequences,
|
||||
ZSTD_eDist_state* state)
|
||||
{
|
||||
const ZSTD_eDist_match* matches = state->matches;
|
||||
size_t const nbMatches = state->nbMatches;
|
||||
size_t const dictSize = state->dictSize;
|
||||
size_t nbSequences = 0;
|
||||
size_t i;
|
||||
for (i = 0; i < nbMatches; i++) {
|
||||
ZSTD_eDist_match const match = matches[i];
|
||||
U32 const litLength = !i ? match.srcIdx :
|
||||
match.srcIdx - (matches[i - 1].srcIdx + matches[i - 1].matchLength);
|
||||
U32 const offset = (match.srcIdx + dictSize) - match.dictIdx;
|
||||
U32 const matchLength = match.matchLength;
|
||||
sequences[nbSequences].offset = offset;
|
||||
sequences[nbSequences].litLength = litLength;
|
||||
sequences[nbSequences].matchLength = matchLength;
|
||||
nbSequences++;
|
||||
}
|
||||
return nbSequences;
|
||||
}
|
||||
|
||||
/*-*************************************
|
||||
* Interal utils
|
||||
***************************************/
|
||||
|
||||
static size_t ZSTD_eDist_hamingDist(const BYTE* const a,
|
||||
const BYTE* const b, size_t n)
|
||||
{
|
||||
size_t i;
|
||||
size_t dist = 0;
|
||||
for (i = 0; i < n; i++)
|
||||
dist += a[i] != b[i];
|
||||
return dist;
|
||||
}
|
||||
|
||||
/* This is a pretty naive recursive implementation that should only
|
||||
* be used for quick tests obviously. Don't try and run this on a
|
||||
* GB file or something. There are faster implementations. Use those
|
||||
* if you need to run it for large files. */
|
||||
static size_t ZSTD_eDist_levenshteinDist(const BYTE* const s,
|
||||
size_t const sn, const BYTE* const t,
|
||||
size_t const tn)
|
||||
{
|
||||
size_t a, b, c;
|
||||
|
||||
if (!sn)
|
||||
return tn;
|
||||
if (!tn)
|
||||
return sn;
|
||||
|
||||
if (s[sn - 1] == t[tn - 1])
|
||||
return ZSTD_eDist_levenshteinDist(
|
||||
s, sn - 1, t, tn - 1);
|
||||
|
||||
a = ZSTD_eDist_levenshteinDist(s, sn - 1, t, tn - 1);
|
||||
b = ZSTD_eDist_levenshteinDist(s, sn, t, tn - 1);
|
||||
c = ZSTD_eDist_levenshteinDist(s, sn - 1, t, tn);
|
||||
|
||||
if (a > b)
|
||||
a = b;
|
||||
if (a > c)
|
||||
a = c;
|
||||
|
||||
return a + 1;
|
||||
}
|
||||
|
||||
static void ZSTD_eDist_validateMatches(ZSTD_eDist_match* matches,
|
||||
size_t const nbMatches, const BYTE* const dict,
|
||||
size_t const dictSize, const BYTE* const src,
|
||||
size_t const srcSize)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < nbMatches; i++) {
|
||||
ZSTD_eDist_match match = matches[i];
|
||||
U32 const dictIdx = match.dictIdx;
|
||||
U32 const srcIdx = match.srcIdx;
|
||||
U32 const matchLength = match.matchLength;
|
||||
|
||||
assert(dictIdx + matchLength < dictSize);
|
||||
assert(srcIdx + matchLength < srcSize);
|
||||
assert(!memcmp(dict + dictIdx, src + srcIdx, matchLength));
|
||||
}
|
||||
}
|
||||
|
||||
/*-*************************************
|
||||
* API
|
||||
***************************************/
|
||||
|
||||
size_t ZSTD_eDist_genSequences(ZSTD_Sequence* sequences,
|
||||
const void* dict, size_t dictSize,
|
||||
const void* src, size_t srcSize,
|
||||
int useHeuristics)
|
||||
{
|
||||
size_t const nbDiags = dictSize + srcSize + 3;
|
||||
S32* buffer = ZSTD_malloc(nbDiags * 2 * sizeof(S32), ZSTD_defaultCMem);
|
||||
ZSTD_eDist_state state;
|
||||
size_t nbSequences = 0;
|
||||
|
||||
state.dict = (const BYTE*)dict;
|
||||
state.src = (const BYTE*)src;
|
||||
state.dictSize = dictSize;
|
||||
state.srcSize = srcSize;
|
||||
state.forwardDiag = buffer;
|
||||
state.backwardDiag = buffer + nbDiags;
|
||||
state.forwardDiag += srcSize + 1;
|
||||
state.backwardDiag += srcSize + 1;
|
||||
state.matches = ZSTD_malloc(srcSize * sizeof(ZSTD_eDist_match), ZSTD_defaultCMem);
|
||||
state.nbMatches = 0;
|
||||
|
||||
ZSTD_eDist_compare(&state, 0, dictSize, 0, srcSize, 1);
|
||||
ZSTD_eDist_combineMatches(&state);
|
||||
nbSequences = ZSTD_eDist_convertMatchesToSequences(sequences, &state);
|
||||
|
||||
ZSTD_free(buffer, ZSTD_defaultCMem);
|
||||
ZSTD_free(state.matches, ZSTD_defaultCMem);
|
||||
|
||||
return nbSequences;
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-present, Yann Collet, Facebook, 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.
|
||||
*/
|
||||
|
||||
/* This match finder leverages techniques used in file comparison algorithms
|
||||
* to find matches between a dictionary and a source file.
|
||||
*
|
||||
* The original motivation for studying this approach was to try and optimize
|
||||
* Zstandard for the use case of patching: the most common scenario being
|
||||
* updating an existing software package with the next version. When patching,
|
||||
* the difference between the old version of the package and the new version
|
||||
* is generally tiny (most of the new file will be identical to
|
||||
* the old one). In more technical terms, the edit distance (the minimal number
|
||||
* of changes required to take one sequence of bytes to another) between the
|
||||
* files would be small relative to the size of the file.
|
||||
*
|
||||
* Various 'diffing' algorithms utilize this notion of edit distance and
|
||||
* the corrensponding concept of a minimal edit script between two
|
||||
* sequences to identify the regions within two files where they differ.
|
||||
* The core algorithm used in this match finder is described in:
|
||||
*
|
||||
* "An O(ND) Difference Algorithm and its Variations", Eugene W. Myers,
|
||||
* Algorithmica Vol. 1, 1986, pp. 251-266,
|
||||
* <https://doi.org/10.1007/BF01840446>.
|
||||
*
|
||||
* Additional algorithmic heuristics for speed improvement have also been included.
|
||||
* These we inspired from implementations of various regular and binary diffing
|
||||
* algorithms such as GNU diff, bsdiff, and Xdelta.
|
||||
*
|
||||
* Note: after some experimentation, this approach proved to not provide enough
|
||||
* utility to justify the additional CPU used in finding matches. The one area
|
||||
* where this approach consistenly outperforms Zstandard even on level 19 is
|
||||
* when compressing small files (<10 KB) using a equally small dictionary that
|
||||
* is very similar to the source file. For the use case that this was intended,
|
||||
* (large similar files) this approach by itself took 5-10X longer than zstd-19 and
|
||||
* generally resulted in 2-3X larger files. The core advantage that zstd-19 has
|
||||
* over this appraoch for match finding is the overlapping matches. This approach
|
||||
* cannot find any.
|
||||
*
|
||||
* I'm leaving this in the contrib section in case this ever becomes interesting
|
||||
* to explore again.
|
||||
* */
|
||||
|
||||
#ifndef ZSTD_EDIST_H
|
||||
#define ZSTD_EDIST_H
|
||||
|
||||
/*-*************************************
|
||||
* Dependencies
|
||||
***************************************/
|
||||
|
||||
#include <stddef.h>
|
||||
#include "zstd_internal.h" /* ZSTD_Sequence */
|
||||
|
||||
/*! ZSTD_eDist_genSequences() :
|
||||
* Will populate the provided ZSTD_Sequence buffer with sequences
|
||||
* based on the optimal or near-optimal (depending on 'useHeuristics')
|
||||
* edit script between 'dict' and 'src.'
|
||||
* @return : the number of sequences found */
|
||||
size_t ZSTD_eDist_genSequences(ZSTD_Sequence* sequences,
|
||||
const void* dict, size_t dictSize,
|
||||
const void* src, size_t srcSize,
|
||||
int useHeuristics);
|
||||
|
||||
#endif
|
||||
@@ -13,10 +13,10 @@
|
||||
|
||||
namespace pzstd {
|
||||
|
||||
constexpr int kLogError = 1;
|
||||
constexpr int kLogInfo = 2;
|
||||
constexpr int kLogDebug = 3;
|
||||
constexpr int kLogVerbose = 4;
|
||||
constexpr int ERROR = 1;
|
||||
constexpr int INFO = 2;
|
||||
constexpr int DEBUG = 3;
|
||||
constexpr int VERBOSE = 4;
|
||||
|
||||
class Logger {
|
||||
std::mutex mutex_;
|
||||
|
||||
+11
-11
@@ -94,12 +94,12 @@ static std::uint64_t handleOneInput(const Options &options,
|
||||
if (!options.decompress) {
|
||||
double ratio = static_cast<double>(bytesWritten) /
|
||||
static_cast<double>(bytesRead + !bytesRead);
|
||||
state.log(kLogInfo, "%-20s :%6.2f%% (%6" PRIu64 " => %6" PRIu64
|
||||
state.log(INFO, "%-20s :%6.2f%% (%6" PRIu64 " => %6" PRIu64
|
||||
" bytes, %s)\n",
|
||||
inputFileName.c_str(), ratio * 100, bytesRead, bytesWritten,
|
||||
outputFileName.c_str());
|
||||
} else {
|
||||
state.log(kLogInfo, "%-20s: %" PRIu64 " bytes \n",
|
||||
state.log(INFO, "%-20s: %" PRIu64 " bytes \n",
|
||||
inputFileName.c_str(),bytesWritten);
|
||||
}
|
||||
}
|
||||
@@ -139,12 +139,12 @@ static FILE *openOutputFile(const Options &options,
|
||||
auto outputFd = std::fopen(outputFile.c_str(), "rb");
|
||||
if (outputFd != nullptr) {
|
||||
std::fclose(outputFd);
|
||||
if (!state.log.logsAt(kLogInfo)) {
|
||||
if (!state.log.logsAt(INFO)) {
|
||||
state.errorHolder.setError("Output file exists");
|
||||
return nullptr;
|
||||
}
|
||||
state.log(
|
||||
kLogInfo,
|
||||
INFO,
|
||||
"pzstd: %s already exists; do you wish to overwrite (y/n) ? ",
|
||||
outputFile.c_str());
|
||||
int c = getchar();
|
||||
@@ -170,7 +170,7 @@ int pzstdMain(const Options &options) {
|
||||
auto printErrorGuard = makeScopeGuard([&] {
|
||||
if (state.errorHolder.hasError()) {
|
||||
returnCode = 1;
|
||||
state.log(kLogError, "pzstd: %s: %s.\n", input.c_str(),
|
||||
state.log(ERROR, "pzstd: %s: %s.\n", input.c_str(),
|
||||
state.errorHolder.getError().c_str());
|
||||
}
|
||||
});
|
||||
@@ -388,7 +388,7 @@ std::uint64_t asyncCompressChunks(
|
||||
// Break the input up into chunks of size `step` and compress each chunk
|
||||
// independently.
|
||||
size_t step = calculateStep(size, numThreads, params);
|
||||
state.log(kLogDebug, "Chosen frame size: %zu\n", step);
|
||||
state.log(DEBUG, "Chosen frame size: %zu\n", step);
|
||||
auto status = FileStatus::Continue;
|
||||
while (status == FileStatus::Continue && !state.errorHolder.hasError()) {
|
||||
// Make a new input queue that we will put the chunk's input data into.
|
||||
@@ -403,7 +403,7 @@ std::uint64_t asyncCompressChunks(
|
||||
});
|
||||
// Pass the output queue to the writer thread.
|
||||
chunks.push(std::move(out));
|
||||
state.log(kLogVerbose, "%s\n", "Starting a new frame");
|
||||
state.log(VERBOSE, "%s\n", "Starting a new frame");
|
||||
// Fill the input queue for the compression job we just started
|
||||
status = readData(*in, ZSTD_CStreamInSize(), step, fd, &bytesRead);
|
||||
}
|
||||
@@ -540,14 +540,14 @@ std::uint64_t asyncDecompressFrames(
|
||||
if (frameSize == 0) {
|
||||
// We hit a non SkippableFrame ==> not compressed by pzstd or corrupted
|
||||
// Pass the rest of the source to this decompression task
|
||||
state.log(kLogVerbose, "%s\n",
|
||||
state.log(VERBOSE, "%s\n",
|
||||
"Input not in pzstd format, falling back to serial decompression");
|
||||
while (status == FileStatus::Continue && !state.errorHolder.hasError()) {
|
||||
status = readData(*in, chunkSize, chunkSize, fd, &totalBytesRead);
|
||||
}
|
||||
break;
|
||||
}
|
||||
state.log(kLogVerbose, "Decompressing a frame of size %zu", frameSize);
|
||||
state.log(VERBOSE, "Decompressing a frame of size %zu", frameSize);
|
||||
// Fill the input queue for the decompression job we just started
|
||||
status = readData(*in, chunkSize, frameSize, fd, &totalBytesRead);
|
||||
}
|
||||
@@ -573,7 +573,7 @@ std::uint64_t writeFile(
|
||||
bool decompress) {
|
||||
auto& errorHolder = state.errorHolder;
|
||||
auto lineClearGuard = makeScopeGuard([&state] {
|
||||
state.log.clear(kLogInfo);
|
||||
state.log.clear(INFO);
|
||||
});
|
||||
std::uint64_t bytesWritten = 0;
|
||||
std::shared_ptr<BufferWorkQueue> out;
|
||||
@@ -602,7 +602,7 @@ std::uint64_t writeFile(
|
||||
return bytesWritten;
|
||||
}
|
||||
bytesWritten += buffer.size();
|
||||
state.log.update(kLogInfo, "Written: %u MB ",
|
||||
state.log.update(INFO, "Written: %u MB ",
|
||||
static_cast<std::uint32_t>(bytesWritten >> 20));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ class SharedState {
|
||||
auto parameters = options.determineParameters();
|
||||
cStreamPool.reset(new ResourcePool<ZSTD_CStream>{
|
||||
[this, parameters]() -> ZSTD_CStream* {
|
||||
this->log(kLogVerbose, "%s\n", "Creating new ZSTD_CStream");
|
||||
this->log(VERBOSE, "%s\n", "Creating new ZSTD_CStream");
|
||||
auto zcs = ZSTD_createCStream();
|
||||
if (zcs) {
|
||||
auto err = ZSTD_initCStream_advanced(
|
||||
@@ -59,7 +59,7 @@ class SharedState {
|
||||
} else {
|
||||
dStreamPool.reset(new ResourcePool<ZSTD_DStream>{
|
||||
[this]() -> ZSTD_DStream* {
|
||||
this->log(kLogVerbose, "%s\n", "Creating new ZSTD_DStream");
|
||||
this->log(VERBOSE, "%s\n", "Creating new ZSTD_DStream");
|
||||
auto zds = ZSTD_createDStream();
|
||||
if (zds) {
|
||||
auto err = ZSTD_initDStream(zds);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user