libdeflate 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.gitmodules +3 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +1 -0
  6. data/.rubocop_todo.yml +9 -0
  7. data/.travis.yml +5 -0
  8. data/Gemfile +4 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +52 -0
  11. data/Rakefile +15 -0
  12. data/bin/console +14 -0
  13. data/bin/setup +8 -0
  14. data/ext/libdeflate/extconf.rb +14 -0
  15. data/ext/libdeflate/libdeflate/.gitignore +19 -0
  16. data/ext/libdeflate/libdeflate/COPYING +21 -0
  17. data/ext/libdeflate/libdeflate/Makefile +231 -0
  18. data/ext/libdeflate/libdeflate/Makefile.msc +64 -0
  19. data/ext/libdeflate/libdeflate/NEWS +57 -0
  20. data/ext/libdeflate/libdeflate/README.md +170 -0
  21. data/ext/libdeflate/libdeflate/common/common_defs.h +351 -0
  22. data/ext/libdeflate/libdeflate/common/compiler_gcc.h +134 -0
  23. data/ext/libdeflate/libdeflate/common/compiler_msc.h +95 -0
  24. data/ext/libdeflate/libdeflate/lib/adler32.c +213 -0
  25. data/ext/libdeflate/libdeflate/lib/adler32_impl.h +281 -0
  26. data/ext/libdeflate/libdeflate/lib/aligned_malloc.c +57 -0
  27. data/ext/libdeflate/libdeflate/lib/aligned_malloc.h +13 -0
  28. data/ext/libdeflate/libdeflate/lib/bt_matchfinder.h +357 -0
  29. data/ext/libdeflate/libdeflate/lib/crc32.c +368 -0
  30. data/ext/libdeflate/libdeflate/lib/crc32_impl.h +286 -0
  31. data/ext/libdeflate/libdeflate/lib/crc32_table.h +526 -0
  32. data/ext/libdeflate/libdeflate/lib/decompress_impl.h +404 -0
  33. data/ext/libdeflate/libdeflate/lib/deflate_compress.c +2817 -0
  34. data/ext/libdeflate/libdeflate/lib/deflate_compress.h +14 -0
  35. data/ext/libdeflate/libdeflate/lib/deflate_constants.h +66 -0
  36. data/ext/libdeflate/libdeflate/lib/deflate_decompress.c +889 -0
  37. data/ext/libdeflate/libdeflate/lib/gzip_compress.c +95 -0
  38. data/ext/libdeflate/libdeflate/lib/gzip_constants.h +45 -0
  39. data/ext/libdeflate/libdeflate/lib/gzip_decompress.c +130 -0
  40. data/ext/libdeflate/libdeflate/lib/hc_matchfinder.h +405 -0
  41. data/ext/libdeflate/libdeflate/lib/lib_common.h +35 -0
  42. data/ext/libdeflate/libdeflate/lib/matchfinder_avx2.h +53 -0
  43. data/ext/libdeflate/libdeflate/lib/matchfinder_common.h +205 -0
  44. data/ext/libdeflate/libdeflate/lib/matchfinder_neon.h +61 -0
  45. data/ext/libdeflate/libdeflate/lib/matchfinder_sse2.h +53 -0
  46. data/ext/libdeflate/libdeflate/lib/unaligned.h +202 -0
  47. data/ext/libdeflate/libdeflate/lib/x86_cpu_features.c +169 -0
  48. data/ext/libdeflate/libdeflate/lib/x86_cpu_features.h +48 -0
  49. data/ext/libdeflate/libdeflate/lib/zlib_compress.c +87 -0
  50. data/ext/libdeflate/libdeflate/lib/zlib_constants.h +21 -0
  51. data/ext/libdeflate/libdeflate/lib/zlib_decompress.c +91 -0
  52. data/ext/libdeflate/libdeflate/libdeflate.h +274 -0
  53. data/ext/libdeflate/libdeflate/programs/benchmark.c +558 -0
  54. data/ext/libdeflate/libdeflate/programs/checksum.c +197 -0
  55. data/ext/libdeflate/libdeflate/programs/detect.sh +62 -0
  56. data/ext/libdeflate/libdeflate/programs/gzip.c +603 -0
  57. data/ext/libdeflate/libdeflate/programs/prog_util.c +530 -0
  58. data/ext/libdeflate/libdeflate/programs/prog_util.h +162 -0
  59. data/ext/libdeflate/libdeflate/programs/test_checksums.c +135 -0
  60. data/ext/libdeflate/libdeflate/programs/tgetopt.c +118 -0
  61. data/ext/libdeflate/libdeflate/tools/afl-fuzz/Makefile +12 -0
  62. data/ext/libdeflate/libdeflate/tools/afl-fuzz/deflate_compress/fuzz.c +40 -0
  63. data/ext/libdeflate/libdeflate/tools/afl-fuzz/deflate_compress/inputs/0 +0 -0
  64. data/ext/libdeflate/libdeflate/tools/afl-fuzz/deflate_decompress/fuzz.c +28 -0
  65. data/ext/libdeflate/libdeflate/tools/afl-fuzz/deflate_decompress/inputs/0 +3 -0
  66. data/ext/libdeflate/libdeflate/tools/afl-fuzz/gzip_decompress/fuzz.c +28 -0
  67. data/ext/libdeflate/libdeflate/tools/afl-fuzz/gzip_decompress/inputs/0 +0 -0
  68. data/ext/libdeflate/libdeflate/tools/afl-fuzz/prepare_for_fuzz.sh +14 -0
  69. data/ext/libdeflate/libdeflate/tools/afl-fuzz/zlib_decompress/fuzz.c +28 -0
  70. data/ext/libdeflate/libdeflate/tools/afl-fuzz/zlib_decompress/inputs/0 +3 -0
  71. data/ext/libdeflate/libdeflate/tools/android_build.sh +104 -0
  72. data/ext/libdeflate/libdeflate/tools/checksum_benchmarks.sh +76 -0
  73. data/ext/libdeflate/libdeflate/tools/exec_tests.sh +30 -0
  74. data/ext/libdeflate/libdeflate/tools/gen_crc32_multipliers.c +108 -0
  75. data/ext/libdeflate/libdeflate/tools/gen_crc32_table.c +100 -0
  76. data/ext/libdeflate/libdeflate/tools/gzip_tests.sh +412 -0
  77. data/ext/libdeflate/libdeflate/tools/make-windows-releases +21 -0
  78. data/ext/libdeflate/libdeflate/tools/mips_build.sh +9 -0
  79. data/ext/libdeflate/libdeflate/tools/msc_test.bat +3 -0
  80. data/ext/libdeflate/libdeflate/tools/pgo_build.sh +23 -0
  81. data/ext/libdeflate/libdeflate/tools/produce_gzip_benchmark_table.sh +37 -0
  82. data/ext/libdeflate/libdeflate/tools/run_tests.sh +305 -0
  83. data/ext/libdeflate/libdeflate/tools/windows_build.sh +10 -0
  84. data/ext/libdeflate/libdeflate_ext.c +389 -0
  85. data/ext/libdeflate/libdeflate_ext.h +8 -0
  86. data/lib/libdeflate.rb +2 -0
  87. data/lib/libdeflate/version.rb +3 -0
  88. data/libdeflate.gemspec +33 -0
  89. 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,10 @@
1
+ #!/bin/bash
2
+
3
+ set -eu
4
+
5
+ make -j CC=i686-w64-mingw32-gcc all test_programs
6
+ cp -vf *.exe /j/exe/
7
+ make -j CC=x86_64-w64-mingw32-gcc all test_programs
8
+ cp -vf *.exe /j/exe64/
9
+
10
+ sudo $HOME/bin/sudo/restart-smbd
@@ -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
+ }