libdeflate 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.gitmodules +3 -0
- data/.rspec +2 -0
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +9 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +52 -0
- data/Rakefile +15 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/ext/libdeflate/extconf.rb +14 -0
- data/ext/libdeflate/libdeflate/.gitignore +19 -0
- data/ext/libdeflate/libdeflate/COPYING +21 -0
- data/ext/libdeflate/libdeflate/Makefile +231 -0
- data/ext/libdeflate/libdeflate/Makefile.msc +64 -0
- data/ext/libdeflate/libdeflate/NEWS +57 -0
- data/ext/libdeflate/libdeflate/README.md +170 -0
- data/ext/libdeflate/libdeflate/common/common_defs.h +351 -0
- data/ext/libdeflate/libdeflate/common/compiler_gcc.h +134 -0
- data/ext/libdeflate/libdeflate/common/compiler_msc.h +95 -0
- data/ext/libdeflate/libdeflate/lib/adler32.c +213 -0
- data/ext/libdeflate/libdeflate/lib/adler32_impl.h +281 -0
- data/ext/libdeflate/libdeflate/lib/aligned_malloc.c +57 -0
- data/ext/libdeflate/libdeflate/lib/aligned_malloc.h +13 -0
- data/ext/libdeflate/libdeflate/lib/bt_matchfinder.h +357 -0
- data/ext/libdeflate/libdeflate/lib/crc32.c +368 -0
- data/ext/libdeflate/libdeflate/lib/crc32_impl.h +286 -0
- data/ext/libdeflate/libdeflate/lib/crc32_table.h +526 -0
- data/ext/libdeflate/libdeflate/lib/decompress_impl.h +404 -0
- data/ext/libdeflate/libdeflate/lib/deflate_compress.c +2817 -0
- data/ext/libdeflate/libdeflate/lib/deflate_compress.h +14 -0
- data/ext/libdeflate/libdeflate/lib/deflate_constants.h +66 -0
- data/ext/libdeflate/libdeflate/lib/deflate_decompress.c +889 -0
- data/ext/libdeflate/libdeflate/lib/gzip_compress.c +95 -0
- data/ext/libdeflate/libdeflate/lib/gzip_constants.h +45 -0
- data/ext/libdeflate/libdeflate/lib/gzip_decompress.c +130 -0
- data/ext/libdeflate/libdeflate/lib/hc_matchfinder.h +405 -0
- data/ext/libdeflate/libdeflate/lib/lib_common.h +35 -0
- data/ext/libdeflate/libdeflate/lib/matchfinder_avx2.h +53 -0
- data/ext/libdeflate/libdeflate/lib/matchfinder_common.h +205 -0
- data/ext/libdeflate/libdeflate/lib/matchfinder_neon.h +61 -0
- data/ext/libdeflate/libdeflate/lib/matchfinder_sse2.h +53 -0
- data/ext/libdeflate/libdeflate/lib/unaligned.h +202 -0
- data/ext/libdeflate/libdeflate/lib/x86_cpu_features.c +169 -0
- data/ext/libdeflate/libdeflate/lib/x86_cpu_features.h +48 -0
- data/ext/libdeflate/libdeflate/lib/zlib_compress.c +87 -0
- data/ext/libdeflate/libdeflate/lib/zlib_constants.h +21 -0
- data/ext/libdeflate/libdeflate/lib/zlib_decompress.c +91 -0
- data/ext/libdeflate/libdeflate/libdeflate.h +274 -0
- data/ext/libdeflate/libdeflate/programs/benchmark.c +558 -0
- data/ext/libdeflate/libdeflate/programs/checksum.c +197 -0
- data/ext/libdeflate/libdeflate/programs/detect.sh +62 -0
- data/ext/libdeflate/libdeflate/programs/gzip.c +603 -0
- data/ext/libdeflate/libdeflate/programs/prog_util.c +530 -0
- data/ext/libdeflate/libdeflate/programs/prog_util.h +162 -0
- data/ext/libdeflate/libdeflate/programs/test_checksums.c +135 -0
- data/ext/libdeflate/libdeflate/programs/tgetopt.c +118 -0
- data/ext/libdeflate/libdeflate/tools/afl-fuzz/Makefile +12 -0
- data/ext/libdeflate/libdeflate/tools/afl-fuzz/deflate_compress/fuzz.c +40 -0
- data/ext/libdeflate/libdeflate/tools/afl-fuzz/deflate_compress/inputs/0 +0 -0
- data/ext/libdeflate/libdeflate/tools/afl-fuzz/deflate_decompress/fuzz.c +28 -0
- data/ext/libdeflate/libdeflate/tools/afl-fuzz/deflate_decompress/inputs/0 +3 -0
- data/ext/libdeflate/libdeflate/tools/afl-fuzz/gzip_decompress/fuzz.c +28 -0
- data/ext/libdeflate/libdeflate/tools/afl-fuzz/gzip_decompress/inputs/0 +0 -0
- data/ext/libdeflate/libdeflate/tools/afl-fuzz/prepare_for_fuzz.sh +14 -0
- data/ext/libdeflate/libdeflate/tools/afl-fuzz/zlib_decompress/fuzz.c +28 -0
- data/ext/libdeflate/libdeflate/tools/afl-fuzz/zlib_decompress/inputs/0 +3 -0
- data/ext/libdeflate/libdeflate/tools/android_build.sh +104 -0
- data/ext/libdeflate/libdeflate/tools/checksum_benchmarks.sh +76 -0
- data/ext/libdeflate/libdeflate/tools/exec_tests.sh +30 -0
- data/ext/libdeflate/libdeflate/tools/gen_crc32_multipliers.c +108 -0
- data/ext/libdeflate/libdeflate/tools/gen_crc32_table.c +100 -0
- data/ext/libdeflate/libdeflate/tools/gzip_tests.sh +412 -0
- data/ext/libdeflate/libdeflate/tools/make-windows-releases +21 -0
- data/ext/libdeflate/libdeflate/tools/mips_build.sh +9 -0
- data/ext/libdeflate/libdeflate/tools/msc_test.bat +3 -0
- data/ext/libdeflate/libdeflate/tools/pgo_build.sh +23 -0
- data/ext/libdeflate/libdeflate/tools/produce_gzip_benchmark_table.sh +37 -0
- data/ext/libdeflate/libdeflate/tools/run_tests.sh +305 -0
- data/ext/libdeflate/libdeflate/tools/windows_build.sh +10 -0
- data/ext/libdeflate/libdeflate_ext.c +389 -0
- data/ext/libdeflate/libdeflate_ext.h +8 -0
- data/lib/libdeflate.rb +2 -0
- data/lib/libdeflate/version.rb +3 -0
- data/libdeflate.gemspec +33 -0
- metadata +230 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
# Try gcc profile-guided optimizations
|
4
|
+
|
5
|
+
set -eu
|
6
|
+
|
7
|
+
MAKE="make -j$(grep -c processor /proc/cpuinfo)"
|
8
|
+
DATAFILE="$HOME/data/silesia"
|
9
|
+
|
10
|
+
$MAKE benchmark > /dev/null
|
11
|
+
echo "====================="
|
12
|
+
echo "Original performance:"
|
13
|
+
echo "---------------------"
|
14
|
+
./benchmark "$@" "$DATAFILE"
|
15
|
+
|
16
|
+
$MAKE CFLAGS=-fprofile-generate LDFLAGS=-fprofile-generate benchmark > /dev/null
|
17
|
+
./benchmark "$@" "$DATAFILE" > /dev/null
|
18
|
+
$MAKE CFLAGS=-fprofile-use benchmark > /dev/null
|
19
|
+
rm -f {lib,programs}/*.gcda
|
20
|
+
echo "=========================="
|
21
|
+
echo "PGO-optimized performance:"
|
22
|
+
echo "--------------------------"
|
23
|
+
./benchmark "$@" "$DATAFILE"
|
@@ -0,0 +1,37 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -e
|
4
|
+
|
5
|
+
do_benchmark() {
|
6
|
+
usize=$(stat -c %s "$file")
|
7
|
+
"$HOME/proj/libdeflate/benchmark" -g -s $usize "$@" "$file" \
|
8
|
+
| grep Compressed | cut -f 4 -d ' '
|
9
|
+
}
|
10
|
+
|
11
|
+
echo "File | zlib -6 | zlib -9 | libdeflate -6 | libdeflate -9 | libdeflate -12"
|
12
|
+
echo "-----|---------|---------|---------------|---------------|---------------"
|
13
|
+
|
14
|
+
for file in "$@"; do
|
15
|
+
echo -n "$(basename "$file")"
|
16
|
+
results=()
|
17
|
+
results+=($(do_benchmark -Y -6))
|
18
|
+
results+=($(do_benchmark -Y -9))
|
19
|
+
results+=($(do_benchmark -6))
|
20
|
+
results+=($(do_benchmark -9))
|
21
|
+
results+=($(do_benchmark -12))
|
22
|
+
best=2000000000
|
23
|
+
for result in "${results[@]}"; do
|
24
|
+
if (( result < best)); then
|
25
|
+
best=$result
|
26
|
+
fi
|
27
|
+
done
|
28
|
+
for result in "${results[@]}"; do
|
29
|
+
if (( result == best )); then
|
30
|
+
em="**"
|
31
|
+
else
|
32
|
+
em=""
|
33
|
+
fi
|
34
|
+
echo -n " | ${em}${result}${em}"
|
35
|
+
done
|
36
|
+
echo
|
37
|
+
done
|
@@ -0,0 +1,305 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
#
|
3
|
+
# Test script for libdeflate
|
4
|
+
#
|
5
|
+
# Usage: ./tools/run_tests.sh [TESTGROUP]... [-TESTGROUP]...
|
6
|
+
#
|
7
|
+
# By default all tests are run, but it is possible to explicitly include or
|
8
|
+
# exclude specific test groups.
|
9
|
+
#
|
10
|
+
|
11
|
+
set -eu -o pipefail
|
12
|
+
cd "$(dirname "$0")/.."
|
13
|
+
|
14
|
+
TESTGROUPS=(all)
|
15
|
+
|
16
|
+
set_test_groups() {
|
17
|
+
TESTGROUPS=("$@")
|
18
|
+
local have_exclusion=0
|
19
|
+
local have_all=0
|
20
|
+
for group in "${TESTGROUPS[@]}"; do
|
21
|
+
if [[ $group == -* ]]; then
|
22
|
+
have_exclusion=1
|
23
|
+
elif [[ $group == all ]]; then
|
24
|
+
have_all=1
|
25
|
+
fi
|
26
|
+
done
|
27
|
+
if (( have_exclusion && !have_all )); then
|
28
|
+
TESTGROUPS=(all "${TESTGROUPS[@]}")
|
29
|
+
fi
|
30
|
+
}
|
31
|
+
|
32
|
+
if [ $# -gt 0 ]; then
|
33
|
+
set_test_groups "$@"
|
34
|
+
fi
|
35
|
+
|
36
|
+
SMOKEDATA="${SMOKEDATA:=$HOME/data/smokedata}"
|
37
|
+
if [ ! -e "$SMOKEDATA" ]; then
|
38
|
+
echo "SMOKEDATA (value: $SMOKEDATA) does not exist. Set the" \
|
39
|
+
"environmental variable SMOKEDATA to a file to use in" \
|
40
|
+
"compression/decompression tests." 1>&2
|
41
|
+
exit 1
|
42
|
+
fi
|
43
|
+
|
44
|
+
NDKDIR="${NDKDIR:=/opt/android-ndk}"
|
45
|
+
|
46
|
+
FILES=("$SMOKEDATA" ./tools/exec_tests.sh benchmark test_checksums)
|
47
|
+
EXEC_TESTS_CMD="WRAPPER= SMOKEDATA=\"$(basename $SMOKEDATA)\" sh exec_tests.sh"
|
48
|
+
NPROC=$(grep -c processor /proc/cpuinfo)
|
49
|
+
VALGRIND="valgrind --quiet --error-exitcode=100 --leak-check=full --errors-for-leak-kinds=all"
|
50
|
+
SANITIZE_CFLAGS="-fsanitize=undefined -fno-sanitize-recover=undefined,integer"
|
51
|
+
|
52
|
+
TMPFILE="$(mktemp)"
|
53
|
+
trap "rm -f \"$TMPFILE\"" EXIT
|
54
|
+
|
55
|
+
###############################################################################
|
56
|
+
|
57
|
+
rm -f run_tests.log
|
58
|
+
exec > >(tee -ia run_tests.log)
|
59
|
+
exec 2> >(tee -ia run_tests.log >&2)
|
60
|
+
|
61
|
+
TESTS_SKIPPED=0
|
62
|
+
log_skip() {
|
63
|
+
log "[WARNING, TEST SKIPPED]: $@"
|
64
|
+
TESTS_SKIPPED=1
|
65
|
+
}
|
66
|
+
|
67
|
+
log() {
|
68
|
+
echo "[$(date)] $@"
|
69
|
+
}
|
70
|
+
|
71
|
+
run_cmd() {
|
72
|
+
log "$@"
|
73
|
+
"$@" > /dev/null
|
74
|
+
}
|
75
|
+
|
76
|
+
test_group_included() {
|
77
|
+
local included=0 group
|
78
|
+
for group in "${TESTGROUPS[@]}"; do
|
79
|
+
if [ "$group" = "$1" ]; then
|
80
|
+
included=1 # explicitly included
|
81
|
+
break
|
82
|
+
fi
|
83
|
+
if [ "$group" = "-$1" ]; then
|
84
|
+
included=0 # explicitly excluded
|
85
|
+
break
|
86
|
+
fi
|
87
|
+
if [ "$group" = "all" ]; then # implicitly included
|
88
|
+
included=1
|
89
|
+
fi
|
90
|
+
done
|
91
|
+
if (( included )); then
|
92
|
+
log "Starting test group: $1"
|
93
|
+
fi
|
94
|
+
(( included ))
|
95
|
+
}
|
96
|
+
|
97
|
+
###############################################################################
|
98
|
+
|
99
|
+
native_build_and_test() {
|
100
|
+
make "$@" -j$NPROC all test_programs > /dev/null
|
101
|
+
WRAPPER="$WRAPPER" SMOKEDATA="$SMOKEDATA" sh ./tools/exec_tests.sh \
|
102
|
+
> /dev/null
|
103
|
+
}
|
104
|
+
|
105
|
+
native_tests() {
|
106
|
+
test_group_included native || return 0
|
107
|
+
local compiler cflags compilers=(gcc)
|
108
|
+
shopt -s nullglob
|
109
|
+
compilers+=(/usr/bin/gcc-[0-9]*)
|
110
|
+
compilers+=(/usr/bin/clang-[0-9]*)
|
111
|
+
compilers+=(/opt/gcc*/bin/gcc)
|
112
|
+
compilers+=(/opt/clang*/bin/clang)
|
113
|
+
shopt -u nullglob
|
114
|
+
for compiler in ${compilers[@]}; do
|
115
|
+
for cflags in "" "-march=native" "-m32"; do
|
116
|
+
if [ "$compiler" = "/usr/bin/gcc-4.8" -a \
|
117
|
+
"$cflags" = "-m32" ]; then
|
118
|
+
continue
|
119
|
+
fi
|
120
|
+
log "Running tests with CC=$compiler," \
|
121
|
+
"CFLAGS=$cflags"
|
122
|
+
WRAPPER= native_build_and_test \
|
123
|
+
CC=$compiler CFLAGS="$cflags -Werror"
|
124
|
+
done
|
125
|
+
done
|
126
|
+
|
127
|
+
log "Running tests with Valgrind"
|
128
|
+
WRAPPER="$VALGRIND" native_build_and_test
|
129
|
+
|
130
|
+
log "Running tests with undefined behavior sanitizer"
|
131
|
+
WRAPPER= native_build_and_test CC=clang CFLAGS="$SANITIZE_CFLAGS"
|
132
|
+
}
|
133
|
+
|
134
|
+
###############################################################################
|
135
|
+
|
136
|
+
android_build() {
|
137
|
+
run_cmd ./tools/android_build.sh --ndkdir="$NDKDIR" "$@"
|
138
|
+
}
|
139
|
+
|
140
|
+
android_build_and_test() {
|
141
|
+
android_build "$@"
|
142
|
+
run_cmd adb push "${FILES[@]}" /data/local/tmp/
|
143
|
+
|
144
|
+
# Note: adb shell always returns 0, even if the shell command fails...
|
145
|
+
log "adb shell \"cd /data/local/tmp && $EXEC_TESTS_CMD\""
|
146
|
+
adb shell "cd /data/local/tmp && $EXEC_TESTS_CMD" > "$TMPFILE"
|
147
|
+
if ! grep -q "exec_tests finished successfully" "$TMPFILE"; then
|
148
|
+
log "Android test failure! adb shell output:"
|
149
|
+
cat "$TMPFILE"
|
150
|
+
return 1
|
151
|
+
fi
|
152
|
+
}
|
153
|
+
|
154
|
+
android_tests() {
|
155
|
+
local compiler
|
156
|
+
|
157
|
+
test_group_included android || return 0
|
158
|
+
if [ ! -e $NDKDIR ]; then
|
159
|
+
log_skip "Android NDK was not found in NDKDIR=$NDKDIR!" \
|
160
|
+
"If you want to run the Android tests, set the" \
|
161
|
+
"environmental variable NDKDIR to the location of" \
|
162
|
+
"your Android NDK installation"
|
163
|
+
return 0
|
164
|
+
fi
|
165
|
+
|
166
|
+
if ! type -P adb > /dev/null; then
|
167
|
+
log_skip "adb (android-tools) is not installed"
|
168
|
+
return 0
|
169
|
+
fi
|
170
|
+
|
171
|
+
if ! adb devices | grep -q 'device$'; then
|
172
|
+
log_skip "No Android device is currently attached"
|
173
|
+
return 0
|
174
|
+
fi
|
175
|
+
|
176
|
+
for compiler in gcc clang; do
|
177
|
+
android_build_and_test --arch=arm --compiler=$compiler
|
178
|
+
|
179
|
+
android_build_and_test --arch=arm --compiler=$compiler \
|
180
|
+
--disable-neon
|
181
|
+
|
182
|
+
# arm64: currently compiled but not run
|
183
|
+
android_build --arch=arm64 --compiler=$compiler
|
184
|
+
done
|
185
|
+
}
|
186
|
+
|
187
|
+
###############################################################################
|
188
|
+
|
189
|
+
mips_tests() {
|
190
|
+
test_group_included mips || return 0
|
191
|
+
if ! ping -c 1 dd-wrt > /dev/null; then
|
192
|
+
log_skip "Can't run MIPS tests: dd-wrt system not available"
|
193
|
+
return 0
|
194
|
+
fi
|
195
|
+
run_cmd ./tools/mips_build.sh
|
196
|
+
run_cmd scp "${FILES[@]}" root@dd-wrt:
|
197
|
+
run_cmd ssh root@dd-wrt "$EXEC_TESTS_CMD"
|
198
|
+
}
|
199
|
+
|
200
|
+
###############################################################################
|
201
|
+
|
202
|
+
windows_tests() {
|
203
|
+
local arch
|
204
|
+
|
205
|
+
test_group_included windows || return 0
|
206
|
+
|
207
|
+
# Windows: currently compiled but not run
|
208
|
+
for arch in i686 x86_64; do
|
209
|
+
local compiler=${arch}-w64-mingw32-gcc
|
210
|
+
if ! type -P $compiler > /dev/null; then
|
211
|
+
log_skip "$compiler not found"
|
212
|
+
continue
|
213
|
+
fi
|
214
|
+
run_cmd make CC=$compiler CFLAGS=-Werror -j$NPROC \
|
215
|
+
all test_programs
|
216
|
+
done
|
217
|
+
}
|
218
|
+
|
219
|
+
###############################################################################
|
220
|
+
|
221
|
+
static_analysis_tests() {
|
222
|
+
test_group_included static_analysis || return 0
|
223
|
+
if ! type -P scan-build > /dev/null; then
|
224
|
+
log_skip "clang static analyzer (scan-build) not found"
|
225
|
+
return 0
|
226
|
+
fi
|
227
|
+
run_cmd scan-build --status-bugs make -j$NPROC all test_programs
|
228
|
+
}
|
229
|
+
|
230
|
+
###############################################################################
|
231
|
+
|
232
|
+
gzip_tests() {
|
233
|
+
test_group_included gzip || return 0
|
234
|
+
|
235
|
+
local gzip gunzip
|
236
|
+
run_cmd make -j$NPROC gzip gunzip
|
237
|
+
for gzip in "$PWD/gzip" /usr/bin/gzip; do
|
238
|
+
for gunzip in "$PWD/gunzip" /usr/bin/gunzip; do
|
239
|
+
log "Running gzip program tests with GZIP=$gzip," \
|
240
|
+
"GUNZIP=$gunzip"
|
241
|
+
GZIP="$gzip" GUNZIP="$gunzip" SMOKEDATA="$SMOKEDATA" \
|
242
|
+
./tools/gzip_tests.sh
|
243
|
+
done
|
244
|
+
done
|
245
|
+
|
246
|
+
log "Running gzip program tests with Valgrind"
|
247
|
+
GZIP="$VALGRIND $PWD/gzip" GUNZIP="$VALGRIND $PWD/gunzip" \
|
248
|
+
SMOKEDATA="$SMOKEDATA" ./tools/gzip_tests.sh
|
249
|
+
|
250
|
+
log "Running gzip program tests with undefined behavior sanitizer"
|
251
|
+
run_cmd make -j$NPROC CC=clang CFLAGS="$SANITIZE_CFLAGS" gzip gunzip
|
252
|
+
GZIP="$PWD/gzip" GUNZIP="$PWD/gunzip" \
|
253
|
+
SMOKEDATA="$SMOKEDATA" ./tools/gzip_tests.sh
|
254
|
+
}
|
255
|
+
|
256
|
+
###############################################################################
|
257
|
+
|
258
|
+
edge_case_tests() {
|
259
|
+
test_group_included edge_case || return 0
|
260
|
+
|
261
|
+
# Regression test for "deflate_compress: fix corruption with long
|
262
|
+
# literal run". Try to compress a file longer than 65535 bytes where no
|
263
|
+
# 2-byte sequence (3 would be sufficient) is repeated <= 32768 bytes
|
264
|
+
# apart, and the distribution of bytes remains constant throughout, and
|
265
|
+
# yet not all bytes are used so the data is still slightly compressible.
|
266
|
+
# There will be no matches in this data, but the compressor should still
|
267
|
+
# output a compressed block, and this block should contain more than
|
268
|
+
# 65535 consecutive literals, which triggered the bug.
|
269
|
+
#
|
270
|
+
# Note: on random data, this situation is extremely unlikely if the
|
271
|
+
# compressor uses all matches it finds, since random data will on
|
272
|
+
# average have a 3-byte match every (256**3)/32768 = 512 bytes.
|
273
|
+
python3 > "$TMPFILE" << EOF
|
274
|
+
import sys
|
275
|
+
for i in range(2):
|
276
|
+
for stride in range(1,251):
|
277
|
+
b = bytes(stride*multiple % 251 for multiple in range(251))
|
278
|
+
sys.stdout.buffer.write(b)
|
279
|
+
EOF
|
280
|
+
run_cmd make -j$NPROC benchmark
|
281
|
+
run_cmd ./benchmark -3 "$TMPFILE"
|
282
|
+
run_cmd ./benchmark -6 "$TMPFILE"
|
283
|
+
run_cmd ./benchmark -12 "$TMPFILE"
|
284
|
+
}
|
285
|
+
|
286
|
+
###############################################################################
|
287
|
+
|
288
|
+
log "Starting libdeflate tests"
|
289
|
+
log " TESTGROUPS=(${TESTGROUPS[@]})"
|
290
|
+
log " SMOKEDATA=$SMOKEDATA"
|
291
|
+
log " NDKDIR=$NDKDIR"
|
292
|
+
|
293
|
+
native_tests
|
294
|
+
android_tests
|
295
|
+
mips_tests
|
296
|
+
windows_tests
|
297
|
+
static_analysis_tests
|
298
|
+
gzip_tests
|
299
|
+
edge_case_tests
|
300
|
+
|
301
|
+
if (( TESTS_SKIPPED )); then
|
302
|
+
log "No tests failed, but some tests were skipped. See above."
|
303
|
+
else
|
304
|
+
log "All tests passed!"
|
305
|
+
fi
|
@@ -0,0 +1,389 @@
|
|
1
|
+
#include "libdeflate_ext.h"
|
2
|
+
|
3
|
+
#define DEFAULT_COMPRESSION 6
|
4
|
+
|
5
|
+
#define FORMAT_DEFLATE 0
|
6
|
+
#define FORMAT_ZLIB 1
|
7
|
+
#define FORMAT_GZIP 2
|
8
|
+
|
9
|
+
VALUE rb_eLibdefalteError, rb_eBadDataError;
|
10
|
+
|
11
|
+
/*
|
12
|
+
* call-seq:
|
13
|
+
* Libdeflate.adler32(str = nil, adler = nil) -> integer
|
14
|
+
*
|
15
|
+
* Updates an Adler-32 checksum with <i>str</i>. If <i>str</i> is omitted, it
|
16
|
+
* returns the initial value of Adler-32 checksum. If <i>adler</i> is omitted,
|
17
|
+
* it assumes that the initial value of Adler-32 checksum is given.
|
18
|
+
*
|
19
|
+
* Libdeflate.adler32 #=> 1
|
20
|
+
* Libdeflate.adler32('foo') #=> 42074437
|
21
|
+
* Libdeflate.adler32('oo', Libdeflate.adler32('f')) #=> 42074437
|
22
|
+
*/
|
23
|
+
static VALUE
|
24
|
+
rb_libdeflate_adler32(int argc, VALUE *argv, VALUE self)
|
25
|
+
{
|
26
|
+
VALUE str, adler;
|
27
|
+
unsigned long checksum;
|
28
|
+
|
29
|
+
rb_scan_args(argc, argv, "02", &str, &adler);
|
30
|
+
|
31
|
+
if (!NIL_P(adler)) {
|
32
|
+
checksum = NUM2ULONG(adler);
|
33
|
+
} else if (!NIL_P(str)) {
|
34
|
+
checksum = libdeflate_adler32(0, NULL, 0);
|
35
|
+
} else {
|
36
|
+
checksum = 0;
|
37
|
+
}
|
38
|
+
|
39
|
+
if (NIL_P(str)) {
|
40
|
+
checksum = libdeflate_adler32(checksum, NULL, 0);
|
41
|
+
} else {
|
42
|
+
StringValue(str);
|
43
|
+
checksum = libdeflate_adler32(checksum, RSTRING_PTR(str), RSTRING_LEN(str));
|
44
|
+
}
|
45
|
+
|
46
|
+
return ULONG2NUM(checksum);
|
47
|
+
}
|
48
|
+
|
49
|
+
/*
|
50
|
+
* call-seq:
|
51
|
+
* Libdeflate.crc32(str = nil, crc = nil) -> integer
|
52
|
+
*
|
53
|
+
* Updates a CRC-32 checksum with <i>str</i>. If <i>str</i> is omitted, it
|
54
|
+
* returns the initial value of CRC-32 checksum. If <i>crc</i> is omitted, it
|
55
|
+
* assumes that the initial value of CRC-32 checksum is given.
|
56
|
+
*
|
57
|
+
* Libdeflate.crc32 #=> 0
|
58
|
+
* Libdeflate.crc32('foo') #=> 2356372769
|
59
|
+
* Libdeflate.crc32('oo', Libdeflate.crc32('f')) #=> 2356372769
|
60
|
+
*/
|
61
|
+
static VALUE
|
62
|
+
rb_libdeflate_crc32(int argc, VALUE *argv, VALUE self)
|
63
|
+
{
|
64
|
+
VALUE str, crc;
|
65
|
+
unsigned long checksum;
|
66
|
+
|
67
|
+
rb_scan_args(argc, argv, "02", &str, &crc);
|
68
|
+
|
69
|
+
if (!NIL_P(crc)) {
|
70
|
+
checksum = NUM2ULONG(crc);
|
71
|
+
} else if (!NIL_P(str)) {
|
72
|
+
checksum = libdeflate_crc32(0, NULL, 0);
|
73
|
+
} else {
|
74
|
+
checksum = 0;
|
75
|
+
}
|
76
|
+
|
77
|
+
if (NIL_P(str)) {
|
78
|
+
checksum = libdeflate_crc32(checksum, NULL, 0);
|
79
|
+
} else {
|
80
|
+
StringValue(str);
|
81
|
+
checksum = libdeflate_crc32(checksum, RSTRING_PTR(str), RSTRING_LEN(str));
|
82
|
+
}
|
83
|
+
|
84
|
+
return ULONG2NUM(checksum);
|
85
|
+
}
|
86
|
+
|
87
|
+
static void
|
88
|
+
compressor_free(void *ptr)
|
89
|
+
{
|
90
|
+
libdeflate_free_compressor((struct libdeflate_compressor *)ptr);
|
91
|
+
}
|
92
|
+
|
93
|
+
static const rb_data_type_t compressor_data_type = {
|
94
|
+
"compressor",
|
95
|
+
{ NULL, compressor_free, NULL, },
|
96
|
+
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
97
|
+
};
|
98
|
+
|
99
|
+
static VALUE
|
100
|
+
rb_compressor_s_allocate(VALUE klass)
|
101
|
+
{
|
102
|
+
return TypedData_Wrap_Struct(klass, &compressor_data_type, 0);
|
103
|
+
}
|
104
|
+
|
105
|
+
/*
|
106
|
+
* call-seq:
|
107
|
+
* initialize(level = DEFAULT_COMPRESSION) -> compressor
|
108
|
+
*
|
109
|
+
* Returns a new Libdeflate::Compressor object. <i>level</i> must be in range
|
110
|
+
* from 1 to 12, and defaults to DEFAULT_COMPRESSION.
|
111
|
+
*
|
112
|
+
* Libdeflate::Compressor.new #=> #<Libdeflate::Compressor:0x007fad31b672c8>
|
113
|
+
*/
|
114
|
+
static VALUE
|
115
|
+
rb_compressor_initialize(int argc, VALUE *argv, VALUE self)
|
116
|
+
{
|
117
|
+
VALUE level;
|
118
|
+
int compression_level;
|
119
|
+
struct libdeflate_compressor *c;
|
120
|
+
|
121
|
+
rb_scan_args(argc, argv, "01", &level);
|
122
|
+
|
123
|
+
compression_level = NIL_P(level) ? DEFAULT_COMPRESSION : FIX2INT(level);
|
124
|
+
|
125
|
+
c = libdeflate_alloc_compressor(compression_level);
|
126
|
+
if (c == NULL) {
|
127
|
+
rb_raise(rb_eLibdefalteError, "libdeflate_alloc_compressor: compression_level=%d", compression_level);
|
128
|
+
}
|
129
|
+
|
130
|
+
DATA_PTR(self) = c;
|
131
|
+
|
132
|
+
return self;
|
133
|
+
}
|
134
|
+
|
135
|
+
static inline struct libdeflate_compressor *
|
136
|
+
check_compressor(VALUE self)
|
137
|
+
{
|
138
|
+
return rb_check_typeddata(self, &compressor_data_type);
|
139
|
+
}
|
140
|
+
|
141
|
+
/*
|
142
|
+
* call-seq:
|
143
|
+
* compressor.compress(str, format = DEFLATE, outbuf = nil) -> string
|
144
|
+
*
|
145
|
+
* Compresses the given string into <i>format</i>. Valid values of <i>format</i>
|
146
|
+
* are DEFLATE (default), ZLIB and GZIP. If <i>outbuf</i> is given, the
|
147
|
+
* resulting compressed data will be written to it.
|
148
|
+
*
|
149
|
+
* compressor.compress('foo') #=> "\x01\x03\x00\xFC\xFFfoo"
|
150
|
+
* compressor.compress('foo', Libdeflate::ZLIB) #=> "x\x9C\x01\x03\x00\xFC\xFFfoo\x02\x82\x01E"
|
151
|
+
*
|
152
|
+
* outbuf = 'bar'
|
153
|
+
* compressor.compress('foo', nil, outbuf) #=> "\x01\x03\x00\xFC\xFFfoo"
|
154
|
+
* outbuf #=> "\x01\x03\x00\xFC\xFFfoo"
|
155
|
+
*/
|
156
|
+
static VALUE
|
157
|
+
rb_compressor_compress(int argc, VALUE *argv, VALUE self)
|
158
|
+
{
|
159
|
+
struct libdeflate_compressor *c = check_compressor(self);
|
160
|
+
VALUE str, format, outbuf;
|
161
|
+
size_t (*compress_func)(struct libdeflate_compressor *, const void *, size_t, void *, size_t);
|
162
|
+
size_t (*compress_bound_func)(struct libdeflate_compressor *, size_t);
|
163
|
+
size_t out_nbytes, max_out_nbytes;
|
164
|
+
|
165
|
+
rb_scan_args(argc, argv, "12", &str, &format, &outbuf);
|
166
|
+
|
167
|
+
StringValue(str);
|
168
|
+
|
169
|
+
switch (NIL_P(format) ? FORMAT_DEFLATE : FIX2INT(format)) {
|
170
|
+
case FORMAT_DEFLATE:
|
171
|
+
compress_func = &libdeflate_deflate_compress;
|
172
|
+
compress_bound_func = &libdeflate_deflate_compress_bound;
|
173
|
+
break;
|
174
|
+
case FORMAT_ZLIB:
|
175
|
+
compress_func = &libdeflate_zlib_compress;
|
176
|
+
compress_bound_func = &libdeflate_zlib_compress_bound;
|
177
|
+
break;
|
178
|
+
case FORMAT_GZIP:
|
179
|
+
compress_func = &libdeflate_gzip_compress;
|
180
|
+
compress_bound_func = &libdeflate_gzip_compress_bound;
|
181
|
+
break;
|
182
|
+
default:
|
183
|
+
rb_raise(rb_eLibdefalteError, "unknown compressed data format: %d", FIX2INT(format));
|
184
|
+
}
|
185
|
+
|
186
|
+
if (NIL_P(outbuf)) {
|
187
|
+
outbuf = rb_str_buf_new(compress_bound_func(c, RSTRING_LEN(str)));
|
188
|
+
} else {
|
189
|
+
StringValue(outbuf);
|
190
|
+
rb_str_modify(outbuf);
|
191
|
+
}
|
192
|
+
|
193
|
+
out_nbytes = compress_func(c,
|
194
|
+
RSTRING_PTR(str),
|
195
|
+
RSTRING_LEN(str),
|
196
|
+
RSTRING_PTR(outbuf),
|
197
|
+
rb_str_capacity(outbuf));
|
198
|
+
|
199
|
+
if (out_nbytes > 0) {
|
200
|
+
rb_str_set_len(outbuf, out_nbytes);
|
201
|
+
OBJ_INFECT(outbuf, str);
|
202
|
+
return outbuf;
|
203
|
+
}
|
204
|
+
|
205
|
+
max_out_nbytes = compress_bound_func(c, RSTRING_LEN(str));
|
206
|
+
if (rb_str_capacity(outbuf) >= max_out_nbytes) {
|
207
|
+
rb_raise(rb_eLibdefalteError, "failed to compress data");
|
208
|
+
}
|
209
|
+
|
210
|
+
rb_str_modify_expand(outbuf, max_out_nbytes - RSTRING_LEN(outbuf));
|
211
|
+
|
212
|
+
out_nbytes = compress_func(c,
|
213
|
+
RSTRING_PTR(str),
|
214
|
+
RSTRING_LEN(str),
|
215
|
+
RSTRING_PTR(outbuf),
|
216
|
+
rb_str_capacity(outbuf));
|
217
|
+
|
218
|
+
if (out_nbytes == 0) {
|
219
|
+
rb_raise(rb_eLibdefalteError, "failed to compress data");
|
220
|
+
}
|
221
|
+
|
222
|
+
rb_str_set_len(outbuf, out_nbytes);
|
223
|
+
OBJ_INFECT(outbuf, str);
|
224
|
+
|
225
|
+
return outbuf;
|
226
|
+
}
|
227
|
+
|
228
|
+
static void
|
229
|
+
decompressor_free(void *ptr)
|
230
|
+
{
|
231
|
+
libdeflate_free_decompressor((struct libdeflate_decompressor *)ptr);
|
232
|
+
}
|
233
|
+
|
234
|
+
static const rb_data_type_t decompressor_data_type = {
|
235
|
+
"decompressor",
|
236
|
+
{ NULL, decompressor_free, NULL, },
|
237
|
+
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
238
|
+
};
|
239
|
+
|
240
|
+
static VALUE
|
241
|
+
rb_decompressor_s_allocate(VALUE klass)
|
242
|
+
{
|
243
|
+
return TypedData_Wrap_Struct(klass, &decompressor_data_type, 0);
|
244
|
+
}
|
245
|
+
|
246
|
+
/*
|
247
|
+
* call-seq:
|
248
|
+
* initialize -> decompressor
|
249
|
+
*
|
250
|
+
* Returns a new Libdeflate::Decompressor object.
|
251
|
+
*
|
252
|
+
* Libdeflate::Decompressor.new #=> #<Libdeflate::Decompressor:0x007fde591a6500>
|
253
|
+
*/
|
254
|
+
static VALUE
|
255
|
+
rb_decompressor_initialize(VALUE self)
|
256
|
+
{
|
257
|
+
struct libdeflate_decompressor *d = libdeflate_alloc_decompressor();
|
258
|
+
if (d == NULL) {
|
259
|
+
rb_raise(rb_eLibdefalteError, "libdeflate_alloc_decompressor");
|
260
|
+
}
|
261
|
+
|
262
|
+
DATA_PTR(self) = d;
|
263
|
+
|
264
|
+
return self;
|
265
|
+
}
|
266
|
+
|
267
|
+
static inline struct libdeflate_decompressor *
|
268
|
+
check_decompressor(VALUE self)
|
269
|
+
{
|
270
|
+
return rb_check_typeddata(self, &decompressor_data_type);
|
271
|
+
}
|
272
|
+
|
273
|
+
static long
|
274
|
+
next_power_of_two(long n) {
|
275
|
+
n--;
|
276
|
+
n |= n >> 1;
|
277
|
+
n |= n >> 2;
|
278
|
+
n |= n >> 4;
|
279
|
+
n |= n >> 8;
|
280
|
+
n |= n >> 16;
|
281
|
+
#if LONG_MAX > UINT32_MAX
|
282
|
+
n |= n >> 32;
|
283
|
+
#endif
|
284
|
+
n++;
|
285
|
+
return n;
|
286
|
+
}
|
287
|
+
|
288
|
+
/*
|
289
|
+
* call-seq:
|
290
|
+
* decompressor.decompress(str, format = nil, outbuf = nil) -> string
|
291
|
+
*
|
292
|
+
* Decompresses the given string compressed in <i>format</i>. Valid values of
|
293
|
+
* <i>format</i> are DEFLATE (default), ZLIB and GZIP. If <i>outbuf</i> is
|
294
|
+
* given, the resulting uncompressed data will be written to it.
|
295
|
+
*
|
296
|
+
* decompressor.decompress("\x01\x03\x00\xFC\xFFfoo") #=> "foo"
|
297
|
+
* decompressor.decompress("x\x9C\x01\x03\x00\xFC\xFFfoo\x02\x82\x01E", Libdeflate::ZLIB) #=> "foo"
|
298
|
+
*
|
299
|
+
* outbuf = 'bar'
|
300
|
+
* decompressor.decompress("\x01\x03\x00\xFC\xFFfoo", nil, outbuf) #=> "foo"
|
301
|
+
* outbuf #=> "foo"
|
302
|
+
*/
|
303
|
+
static VALUE
|
304
|
+
rb_compressor_decompress(int argc, VALUE *argv, VALUE self)
|
305
|
+
{
|
306
|
+
struct libdeflate_decompressor *d = check_decompressor(self);
|
307
|
+
VALUE str, format, outbuf;
|
308
|
+
enum libdeflate_result (*decompress_func)(struct libdeflate_decompressor *, const void *, size_t, void *, size_t, size_t *);
|
309
|
+
size_t actual_out_nbytes_ret;
|
310
|
+
enum libdeflate_result decompress_result;
|
311
|
+
|
312
|
+
rb_scan_args(argc, argv, "12", &str, &format, &outbuf);
|
313
|
+
|
314
|
+
StringValue(str);
|
315
|
+
|
316
|
+
switch (NIL_P(format) ? FORMAT_DEFLATE : FIX2INT(format)) {
|
317
|
+
case FORMAT_DEFLATE:
|
318
|
+
decompress_func = &libdeflate_deflate_decompress;
|
319
|
+
break;
|
320
|
+
case FORMAT_ZLIB:
|
321
|
+
decompress_func = &libdeflate_zlib_decompress;
|
322
|
+
break;
|
323
|
+
case FORMAT_GZIP:
|
324
|
+
decompress_func = &libdeflate_gzip_decompress;
|
325
|
+
break;
|
326
|
+
default:
|
327
|
+
rb_raise(rb_eLibdefalteError, "unknown compressed data format: %d", FIX2INT(format));
|
328
|
+
}
|
329
|
+
|
330
|
+
if (NIL_P(outbuf)) {
|
331
|
+
outbuf = rb_str_buf_new(next_power_of_two(RSTRING_LEN(str)) << 4);
|
332
|
+
} else {
|
333
|
+
StringValue(outbuf);
|
334
|
+
rb_str_modify(outbuf);
|
335
|
+
}
|
336
|
+
|
337
|
+
for (;;) {
|
338
|
+
decompress_result = decompress_func(d,
|
339
|
+
RSTRING_PTR(str),
|
340
|
+
RSTRING_LEN(str),
|
341
|
+
RSTRING_PTR(outbuf),
|
342
|
+
rb_str_capacity(outbuf),
|
343
|
+
&actual_out_nbytes_ret);
|
344
|
+
|
345
|
+
if (decompress_result != LIBDEFLATE_INSUFFICIENT_SPACE) {
|
346
|
+
break;
|
347
|
+
}
|
348
|
+
|
349
|
+
rb_str_modify_expand(outbuf, (rb_str_capacity(outbuf) << 1) - RSTRING_LEN(outbuf));
|
350
|
+
}
|
351
|
+
|
352
|
+
if (decompress_result == LIBDEFLATE_BAD_DATA) {
|
353
|
+
rb_raise(rb_eBadDataError, "failed to decompress data");
|
354
|
+
} else if (decompress_result != LIBDEFLATE_SUCCESS) {
|
355
|
+
rb_raise(rb_eLibdefalteError, "failed to decompress data");
|
356
|
+
}
|
357
|
+
|
358
|
+
rb_str_set_len(outbuf, actual_out_nbytes_ret);
|
359
|
+
OBJ_INFECT(outbuf, str);
|
360
|
+
|
361
|
+
return outbuf;
|
362
|
+
}
|
363
|
+
|
364
|
+
void
|
365
|
+
Init_libdeflate_ext(void)
|
366
|
+
{
|
367
|
+
VALUE rb_mLibdeflate, rb_cCompressor, rb_cDecompressor;
|
368
|
+
|
369
|
+
rb_mLibdeflate = rb_define_module("Libdeflate");
|
370
|
+
rb_eLibdefalteError = rb_define_class_under(rb_mLibdeflate, "Error", rb_eStandardError);
|
371
|
+
rb_eBadDataError = rb_define_class_under(rb_mLibdeflate, "BadDataError", rb_eLibdefalteError);
|
372
|
+
|
373
|
+
rb_define_const(rb_mLibdeflate, "DEFLATE", INT2FIX(FORMAT_DEFLATE));
|
374
|
+
rb_define_const(rb_mLibdeflate, "ZLIB", INT2FIX(FORMAT_ZLIB));
|
375
|
+
rb_define_const(rb_mLibdeflate, "GZIP", INT2FIX(FORMAT_GZIP));
|
376
|
+
|
377
|
+
rb_define_module_function(rb_mLibdeflate, "adler32", rb_libdeflate_adler32, -1);
|
378
|
+
rb_define_module_function(rb_mLibdeflate, "crc32", rb_libdeflate_crc32, -1);
|
379
|
+
|
380
|
+
rb_cCompressor = rb_define_class_under(rb_mLibdeflate, "Compressor", rb_cObject);
|
381
|
+
rb_define_alloc_func(rb_cCompressor, rb_compressor_s_allocate);
|
382
|
+
rb_define_method(rb_cCompressor, "initialize", rb_compressor_initialize, -1);
|
383
|
+
rb_define_method(rb_cCompressor, "compress", rb_compressor_compress, -1);
|
384
|
+
|
385
|
+
rb_cDecompressor = rb_define_class_under(rb_mLibdeflate, "Decompressor", rb_cObject);
|
386
|
+
rb_define_alloc_func(rb_cDecompressor, rb_decompressor_s_allocate);
|
387
|
+
rb_define_method(rb_cDecompressor, "initialize", rb_decompressor_initialize, 0);
|
388
|
+
rb_define_method(rb_cDecompressor, "decompress", rb_compressor_decompress, -1);
|
389
|
+
}
|