zopfli 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/main.yml +35 -0
  3. data/.github/workflows/publish.yml +34 -0
  4. data/.gitmodules +1 -1
  5. data/Gemfile +4 -0
  6. data/README.md +6 -1
  7. data/Rakefile +14 -10
  8. data/ext/extconf.rb +36 -32
  9. data/ext/zopfli.c +52 -20
  10. data/lib/zopfli/version.rb +1 -1
  11. data/smoke.sh +9 -0
  12. data/test/test_helper.rb +7 -0
  13. data/test/zopfli_test.rb +63 -0
  14. data/vendor/zopfli/src/zopfli/blocksplitter.c +41 -53
  15. data/vendor/zopfli/src/zopfli/blocksplitter.h +2 -6
  16. data/vendor/zopfli/src/zopfli/cache.c +6 -0
  17. data/vendor/zopfli/src/zopfli/deflate.c +613 -381
  18. data/vendor/zopfli/src/zopfli/deflate.h +8 -2
  19. data/vendor/zopfli/src/zopfli/gzip_container.c +54 -47
  20. data/vendor/zopfli/src/zopfli/hash.c +18 -10
  21. data/vendor/zopfli/src/zopfli/hash.h +10 -7
  22. data/vendor/zopfli/src/zopfli/katajainen.c +73 -62
  23. data/vendor/zopfli/src/zopfli/katajainen.h +1 -1
  24. data/vendor/zopfli/src/zopfli/lz77.c +190 -42
  25. data/vendor/zopfli/src/zopfli/lz77.h +39 -23
  26. data/vendor/zopfli/src/zopfli/squeeze.c +75 -61
  27. data/vendor/zopfli/src/zopfli/squeeze.h +1 -0
  28. data/vendor/zopfli/src/zopfli/symbols.h +239 -0
  29. data/vendor/zopfli/src/zopfli/util.c +0 -178
  30. data/vendor/zopfli/src/zopfli/util.h +6 -23
  31. data/vendor/zopfli/src/zopfli/zlib_container.c +1 -1
  32. data/vendor/zopfli/src/zopfli/zopfli.h +1 -4
  33. data/vendor/zopfli/src/zopfli/zopfli_bin.c +31 -15
  34. data/zopfli.gemspec +12 -32
  35. metadata +20 -68
  36. data/test/fixtures/alice29.txt +0 -3609
  37. data/test/test_zopfli_deflate.rb +0 -47
  38. data/vendor/zopfli/CONTRIBUTORS +0 -7
  39. data/vendor/zopfli/README +0 -32
  40. data/vendor/zopfli/README.zopflipng +0 -35
  41. data/vendor/zopfli/makefile +0 -37
  42. data/vendor/zopfli/src/zopflipng/lodepng/lodepng.cpp +0 -6253
  43. data/vendor/zopfli/src/zopflipng/lodepng/lodepng.h +0 -1705
  44. data/vendor/zopfli/src/zopflipng/lodepng/lodepng_util.cpp +0 -656
  45. data/vendor/zopfli/src/zopflipng/lodepng/lodepng_util.h +0 -151
  46. data/vendor/zopfli/src/zopflipng/zopflipng_bin.cc +0 -407
  47. data/vendor/zopfli/src/zopflipng/zopflipng_lib.cc +0 -376
  48. data/vendor/zopfli/src/zopflipng/zopflipng_lib.h +0 -79
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 116f5697342e3dbe9f08bcb519c661c51c263c63
4
- data.tar.gz: 8148085bad781a3dcb70465ef45ea29f6a24b918
2
+ SHA256:
3
+ metadata.gz: ea7e06b8390382908a91c402f02754fa8f2a325699d424731f3903595cbe2d73
4
+ data.tar.gz: 138f867202c10b77cca5cd0c06545add1a630dd8721e5d67dc667d5257c55915
5
5
  SHA512:
6
- metadata.gz: de7fcf5546c2ec184ffa701127f8398af60474a7b830cf6d2317d2cc68860479083d4daaa21cf168ac77be70fd2e5073be2197ee05890bf528698a4bc24328f8
7
- data.tar.gz: 5c92c334266fdc7c8d5155918e556f071aabc07c3b2db316c87afa8e21d46173cb9bc14d0fa73108131462e9703814212dd75abbfbe8ce557280d7f37866a6cd
6
+ metadata.gz: b6bbf82746d164940dd0781b512623fc22101c12240dc32b8745007f8ce04079d4fd07e6d1c25dea78f96bfaed8202c85ed18fb766041b4050a4dd3339bf46a1
7
+ data.tar.gz: f5415a40dee71d25d4cfd8279ccf5ec6ce3269551055be2dfddc8c375635481e39fbfdebc9f7547928631762a67dcada8e99c8da33cb669734bd2a218ff809e7
@@ -0,0 +1,35 @@
1
+ name: Ruby
2
+
3
+ on: [pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ strategy:
8
+ fail-fast: false
9
+ matrix:
10
+ ruby: [2.5, 2.6, 2.7, 3.0]
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v2
14
+ with:
15
+ submodules: true
16
+ - name: Set up Ruby
17
+ uses: ruby/setup-ruby@v1
18
+ with:
19
+ ruby-version: ${{ matrix.ruby }}
20
+ bundler-cache: true
21
+ - name: Run the default task
22
+ run: |
23
+ bundle exec rake clobber test build
24
+ gem install --no-document "$(ls pkg/zopfli-*.gem)"
25
+ cat <<EOF | ruby
26
+ require "zopfli"
27
+ require "zlib"
28
+ if Zlib::Inflate.inflate(Zopfli.deflate(File.read("README.md"))) == File.read("README.md")
29
+ puts "OK"
30
+ exit 0
31
+ else
32
+ puts "NG"
33
+ exit 0
34
+ end
35
+ EOF
@@ -0,0 +1,34 @@
1
+ name: Publish Gem
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+
8
+ jobs:
9
+ build:
10
+ strategy:
11
+ fail-fast: false
12
+ matrix:
13
+ ruby: [3.0]
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v2
17
+ with:
18
+ submodules: true
19
+ - name: Set up Ruby
20
+ uses: ruby/setup-ruby@v1
21
+ with:
22
+ ruby-version: ${{ matrix.ruby }}
23
+ bundler-cache: true
24
+ - name: Run release task
25
+ run: |
26
+ mkdir -p ~/.gem
27
+ cat << EOF > ~/.gem/credentials
28
+ ---
29
+ :github: Bearer ${{secrets.GITHUB_TOKEN}}
30
+ :rubygems_api_key: ${{secrets.RUBYGEMS_API_KEY}}
31
+ EOF
32
+ chmod 600 ~/.gem/credentials
33
+ bundle exec rake release[remote]
34
+ rm -f ~/.gem/credentials
@@ -1,3 +1,3 @@
1
1
  [submodule "vendor/zopfli"]
2
2
  path = vendor/zopfli
3
- url = https://code.google.com/p/zopfli/
3
+ url = https://github.com/google/zopfli.git
data/Gemfile CHANGED
@@ -2,3 +2,7 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in zopfli.gemspec
4
4
  gemspec
5
+
6
+ gem "rake", "~> 13.0"
7
+ gem "test-unit", "~> 3.0"
8
+ gem "test-unit-rr"
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Zopfli
2
2
 
3
- see https://code.google.com/p/zopfli/
3
+ see https://github.com/google/zopfli
4
4
 
5
5
  ## Installation
6
6
 
@@ -30,6 +30,11 @@ compressed_data = Zopfli.deflate string
30
30
  uncompressed_data = Zlib::Inflate.inflate compressed_data
31
31
  uncompressed_data == string
32
32
  # => true
33
+
34
+ Zlib.deflate(File.read('LICENSE.txt'), Zlib::BEST_COMPRESSION).bytesize
35
+ # => 628
36
+ Zopfli.deflate(File.read('LICENSE.txt')).bytesize
37
+ # => 601
33
38
  ```
34
39
 
35
40
  ## Contributing
data/Rakefile CHANGED
@@ -1,25 +1,29 @@
1
+ require "bundler/setup"
1
2
  require "bundler/gem_tasks"
3
+ require "rake/clean"
2
4
  require "rake/testtask"
3
5
  require "rbconfig"
4
6
 
5
7
  DLEXT = RbConfig::CONFIG["DLEXT"]
6
8
 
7
- file "ext/zopfli.#{DLEXT}" => Dir.glob("ext/*{.rb,.c}") do
9
+ file "ext/zopfli.#{DLEXT}" => Dir.glob("ext/*{.rb,.c,.h}") do
8
10
  Dir.chdir("ext") do
9
- ruby "extconf.rb"
10
- sh "make"
11
+ ruby "extconf.rb"
12
+ sh "make"
11
13
  end
12
14
  cp "ext/zopfli.#{DLEXT}", "lib"
13
15
  end
14
16
 
15
- task :clean do
16
- files = Dir["ext/*"] - ["ext/extconf.rb", "ext/zopfli.c"]
17
- files+= ["ext/zopfli.#{DLEXT}", "lib/zopfli.#{DLEXT}"]
18
- rm_rf(files) unless files.empty?
19
- end
20
-
21
- Rake::TestTask.new do |t|
17
+ Rake::TestTask.new(:test) do |t|
18
+ t.libs << "test"
19
+ t.test_files = FileList["test/**/*_test.rb"]
22
20
  t.warning = true
23
21
  t.verbose = true
24
22
  end
23
+
24
+ CLEAN.include "ext/zopfli.#{DLEXT}", "lib/zopfli.#{DLEXT}"
25
+ CLEAN.include "ext/*"
26
+ CLEAN.exclude "ext/extconf.rb", "ext/zopfli.c"
27
+
25
28
  task :test => "ext/zopfli.#{DLEXT}"
29
+ task :default => :test
@@ -8,36 +8,40 @@
8
8
  # the ZopfliOptions first.
9
9
  require "mkmf"
10
10
 
11
- dst = File.dirname File.expand_path __FILE__
12
- src = File.join dst, "..", "vendor", "zopfli", "src", "zopfli"
13
-
14
- %w(
15
- blocksplitter.c
16
- blocksplitter.h
17
- cache.c
18
- cache.h
19
- deflate.c
20
- deflate.h
21
- gzip_container.c
22
- gzip_container.h
23
- hash.c
24
- hash.h
25
- katajainen.c
26
- katajainen.h
27
- lz77.c
28
- lz77.h
29
- squeeze.c
30
- squeeze.h
31
- tree.c
32
- tree.h
33
- util.c
34
- util.h
35
- zlib_container.c
36
- zlib_container.h
37
- zopfli.h
38
- zopfli_lib.c
39
- ).each do |file|
40
- FileUtils.copy File.join(src, file), File.join(dst, file) if FileTest.exist? File.join(src, file)
11
+ dir_config("zopfli")
12
+ if have_header("zopfli/zopfli.h") && have_library("zopfli", "ZopfliCompress", "zopfli/zopfli.h")
13
+ create_makefile "zopfli"
14
+ else
15
+ dst = File.dirname File.expand_path __FILE__
16
+ src = File.join dst, "..", "vendor", "zopfli", "src", "zopfli"
17
+ %w[
18
+ blocksplitter.c
19
+ blocksplitter.h
20
+ cache.c
21
+ cache.h
22
+ deflate.c
23
+ deflate.h
24
+ gzip_container.c
25
+ gzip_container.h
26
+ hash.c
27
+ hash.h
28
+ katajainen.c
29
+ katajainen.h
30
+ lz77.c
31
+ lz77.h
32
+ squeeze.c
33
+ squeeze.h
34
+ symbols.h
35
+ tree.c
36
+ tree.h
37
+ util.c
38
+ util.h
39
+ zlib_container.c
40
+ zlib_container.h
41
+ zopfli.h
42
+ zopfli_lib.c
43
+ ].each do |file|
44
+ FileUtils.copy File.join(src, file), File.join(dst, file) if FileTest.exist? File.join(src, file)
45
+ end
46
+ create_makefile "zopfli"
41
47
  end
42
-
43
- create_makefile "zopfli"
@@ -1,12 +1,17 @@
1
1
  #include "ruby.h"
2
+ #ifdef HAVE_RUBY_THREAD_H
3
+ #include "ruby/thread.h"
4
+ #endif
5
+ #ifdef HAVE_ZOPFLI_ZOPFLI_H
6
+ #include "zopfli/zopfli.h"
7
+ #else
2
8
  #include "zopfli.h"
9
+ #endif
3
10
 
4
11
  #define CSTR2SYM(x) ID2SYM(rb_intern(x))
5
12
  #define DEFAULT_FORMAT ZOPFLI_FORMAT_ZLIB
6
13
 
7
- static VALUE rb_mZopfli;
8
-
9
- ZopfliFormat
14
+ static ZopfliFormat
10
15
  zopfli_deflate_parse_options(ZopfliOptions *options, VALUE opts)
11
16
  {
12
17
  ZopfliFormat format;
@@ -50,35 +55,60 @@ zopfli_deflate_parse_options(ZopfliOptions *options, VALUE opts)
50
55
  return format;
51
56
  }
52
57
 
53
- VALUE
54
- zopfli_deflate(int argc, VALUE *argv, VALUE self)
55
- {
56
- VALUE in, out, opts;
58
+ typedef struct {
57
59
  ZopfliOptions options;
58
60
  ZopfliFormat format;
59
- unsigned char *tmp = NULL;
60
- size_t tmpsize = 0;
61
+ unsigned char *in;
62
+ size_t insize;
63
+ unsigned char *out;
64
+ size_t outsize;
65
+ } zopfli_deflate_args_t;
66
+
67
+ static void*
68
+ zopfli_deflate_no_gvl(void* arg)
69
+ {
70
+ zopfli_deflate_args_t *args = (zopfli_deflate_args_t*)arg;
61
71
 
62
- ZopfliInitOptions(&options);
72
+ ZopfliCompress(&args->options, args->format,
73
+ args->in, args->insize,
74
+ &args->out, &args->outsize);
75
+
76
+ return arg;
77
+ }
78
+
79
+ static VALUE
80
+ zopfli_deflate(int argc, VALUE *argv, VALUE self)
81
+ {
82
+ zopfli_deflate_args_t args;
83
+ VALUE in, out, opts;
84
+
85
+ ZopfliInitOptions(&args.options);
63
86
 
64
87
  rb_scan_args(argc, argv, "11", &in, &opts);
65
88
 
66
89
  if (!NIL_P(opts)) {
67
- format = zopfli_deflate_parse_options(&options, opts);
90
+ args.format = zopfli_deflate_parse_options(&args.options, opts);
68
91
  } else {
69
- format = DEFAULT_FORMAT;
92
+ args.format = DEFAULT_FORMAT;
70
93
  }
71
94
 
72
95
  StringValue(in);
73
96
 
74
- ZopfliCompress(&options,
75
- format,
76
- RSTRING_PTR(in), RSTRING_LEN(in),
77
- &tmp, &tmpsize);
97
+ args.in = (unsigned char*)RSTRING_PTR(in);
98
+ args.insize = RSTRING_LEN(in);
99
+
100
+ args.out = NULL;
101
+ args.outsize = 0;
102
+
103
+ #ifdef HAVE_RUBY_THREAD_H
104
+ rb_thread_call_without_gvl(zopfli_deflate_no_gvl, (void *)&args, NULL, NULL);
105
+ #else
106
+ zopfli_deflate_no_gvl((void *)&args);
107
+ #endif
78
108
 
79
- out = rb_str_new(tmp, tmpsize);
109
+ out = rb_str_new((const char*)args.out, args.outsize);
80
110
 
81
- free(tmp);
111
+ free(args.out);
82
112
 
83
113
  return out;
84
114
  }
@@ -86,7 +116,9 @@ zopfli_deflate(int argc, VALUE *argv, VALUE self)
86
116
  void
87
117
  Init_zopfli()
88
118
  {
89
- rb_mZopfli = rb_define_module("Zopfli");
119
+ #if HAVE_RB_EXT_RACTOR_SAFE
120
+ rb_ext_ractor_safe(true);
121
+ #endif
122
+ VALUE rb_mZopfli = rb_define_module("Zopfli");
90
123
  rb_define_singleton_method(rb_mZopfli, "deflate", zopfli_deflate, -1);
91
- rb_require("zopfli/version");
92
124
  }
@@ -1,3 +1,3 @@
1
1
  module Zopfli
2
- VERSION = "0.0.3"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -0,0 +1,9 @@
1
+ #!/bin/bash
2
+ gem list | grep zopfli && gem uninstall --force zopfli
3
+ bundle exec rake clean build
4
+ gem install --force --local --no-document "$(ls pkg/zopfli-*.gem)"
5
+ cat <<EOF | ruby
6
+ require 'zopfli'
7
+ require 'zlib'
8
+ abort if Zlib::Inflate.inflate(Zopfli.deflate(File.read('smoke.sh'))) != File.read('smoke.sh')
9
+ EOF
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
4
+ require "zopfli"
5
+
6
+ require "test-unit"
7
+ require "test/unit/rr"
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+ require "zlib"
5
+ require "stringio"
6
+
7
+ class ZopfliTest < Test::Unit::TestCase
8
+ T = [*"a".."z", *"A".."Z", *"0".."9"].freeze
9
+
10
+ def random_data(length = 1024)
11
+ Array.new(length) { T.sample }.join
12
+ end
13
+
14
+ test "VERSION" do
15
+ assert do
16
+ ::Zopfli.const_defined?(:VERSION)
17
+ end
18
+ end
19
+
20
+ test "well done" do
21
+ s = random_data
22
+ assert_equal s, Zlib::Inflate.inflate(Zopfli.deflate(s, format: :zlib))
23
+ end
24
+
25
+ test "well done(default format is zlib)" do
26
+ s = random_data
27
+ assert_equal Zopfli.deflate(s, format: :zlib), Zopfli.deflate(s)
28
+ end
29
+
30
+ test "well done(gzip format)" do
31
+ s = random_data
32
+ assert_equal s, Zlib::GzipReader.wrap(StringIO.new(Zopfli.deflate(s, format: :gzip)), &:read)
33
+ end
34
+
35
+ test "well done(deflate)" do
36
+ s = random_data
37
+ assert_nothing_raised do
38
+ Zopfli.deflate(s, format: :deflate)
39
+ end
40
+ end
41
+
42
+ test "raise error when pass invalid format" do
43
+ s = random_data
44
+ assert_raise ArgumentError do
45
+ Zopfli.deflate(s, format: :lzma)
46
+ end
47
+ end
48
+
49
+ sub_test_case "Ractor safe" do
50
+ test "able to invoke non-main ractor" do
51
+ unless defined? ::Ractor
52
+ notify "Ractor not defined"
53
+ omit
54
+ end
55
+ a = Array.new(2) do
56
+ Ractor.new(random_data) do |s|
57
+ Zlib::Inflate.inflate(Zopfli.deflate(s)) == s
58
+ end
59
+ end
60
+ assert_equal [true, true], a.map(&:take)
61
+ end
62
+ end
63
+ end
@@ -24,7 +24,6 @@ Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala)
24
24
  #include <stdlib.h>
25
25
 
26
26
  #include "deflate.h"
27
- #include "lz77.h"
28
27
  #include "squeeze.h"
29
28
  #include "tree.h"
30
29
  #include "util.h"
@@ -39,9 +38,10 @@ typedef double FindMinimumFun(size_t i, void* context);
39
38
  /*
40
39
  Finds minimum of function f(i) where is is of type size_t, f(i) is of type
41
40
  double, i is in range start-end (excluding end).
41
+ Outputs the minimum value in *smallest and returns the index of this value.
42
42
  */
43
43
  static size_t FindMinimum(FindMinimumFun f, void* context,
44
- size_t start, size_t end) {
44
+ size_t start, size_t end, double* smallest) {
45
45
  if (end - start < 1024) {
46
46
  double best = ZOPFLI_LARGE_FLOAT;
47
47
  size_t result = start;
@@ -53,6 +53,7 @@ static size_t FindMinimum(FindMinimumFun f, void* context,
53
53
  result = i;
54
54
  }
55
55
  }
56
+ *smallest = best;
56
57
  return result;
57
58
  } else {
58
59
  /* Try to find minimum faster by recursively checking multiple points. */
@@ -88,6 +89,7 @@ static size_t FindMinimum(FindMinimumFun f, void* context,
88
89
  pos = p[besti];
89
90
  lastbest = best;
90
91
  }
92
+ *smallest = lastbest;
91
93
  return pos;
92
94
  #undef NUM
93
95
  }
@@ -103,16 +105,13 @@ dists: ll77 distances
103
105
  lstart: start of block
104
106
  lend: end of block (not inclusive)
105
107
  */
106
- static double EstimateCost(const unsigned short* litlens,
107
- const unsigned short* dists,
108
+ static double EstimateCost(const ZopfliLZ77Store* lz77,
108
109
  size_t lstart, size_t lend) {
109
- return ZopfliCalculateBlockSize(litlens, dists, lstart, lend, 2);
110
+ return ZopfliCalculateBlockSizeAutoType(lz77, lstart, lend);
110
111
  }
111
112
 
112
113
  typedef struct SplitCostContext {
113
- const unsigned short* litlens;
114
- const unsigned short* dists;
115
- size_t llsize;
114
+ const ZopfliLZ77Store* lz77;
116
115
  size_t start;
117
116
  size_t end;
118
117
  } SplitCostContext;
@@ -125,23 +124,20 @@ type: FindMinimumFun
125
124
  */
126
125
  static double SplitCost(size_t i, void* context) {
127
126
  SplitCostContext* c = (SplitCostContext*)context;
128
- return EstimateCost(c->litlens, c->dists, c->start, i) +
129
- EstimateCost(c->litlens, c->dists, i, c->end);
127
+ return EstimateCost(c->lz77, c->start, i) + EstimateCost(c->lz77, i, c->end);
130
128
  }
131
129
 
132
130
  static void AddSorted(size_t value, size_t** out, size_t* outsize) {
133
131
  size_t i;
134
132
  ZOPFLI_APPEND_DATA(value, out, outsize);
135
- if (*outsize > 0) {
136
- for (i = 0; i < *outsize - 1; i++) {
137
- if ((*out)[i] > value) {
138
- size_t j;
139
- for (j = *outsize - 1; j > i; j--) {
140
- (*out)[j] = (*out)[j - 1];
141
- }
142
- (*out)[i] = value;
143
- break;
133
+ for (i = 0; i + 1 < *outsize; i++) {
134
+ if ((*out)[i] > value) {
135
+ size_t j;
136
+ for (j = *outsize - 1; j > i; j--) {
137
+ (*out)[j] = (*out)[j - 1];
144
138
  }
139
+ (*out)[i] = value;
140
+ break;
145
141
  }
146
142
  }
147
143
  }
@@ -149,9 +145,8 @@ static void AddSorted(size_t value, size_t** out, size_t* outsize) {
149
145
  /*
150
146
  Prints the block split points as decimal and hex values in the terminal.
151
147
  */
152
- static void PrintBlockSplitPoints(const unsigned short* litlens,
153
- const unsigned short* dists,
154
- size_t llsize, const size_t* lz77splitpoints,
148
+ static void PrintBlockSplitPoints(const ZopfliLZ77Store* lz77,
149
+ const size_t* lz77splitpoints,
155
150
  size_t nlz77points) {
156
151
  size_t* splitpoints = 0;
157
152
  size_t npoints = 0;
@@ -160,8 +155,8 @@ static void PrintBlockSplitPoints(const unsigned short* litlens,
160
155
  index values. */
161
156
  size_t pos = 0;
162
157
  if (nlz77points > 0) {
163
- for (i = 0; i < llsize; i++) {
164
- size_t length = dists[i] == 0 ? 1 : litlens[i];
158
+ for (i = 0; i < lz77->size; i++) {
159
+ size_t length = lz77->dists[i] == 0 ? 1 : lz77->litlens[i];
165
160
  if (lz77splitpoints[npoints] == i) {
166
161
  ZOPFLI_APPEND_DATA(pos, &splitpoints, &npoints);
167
162
  if (npoints == nlz77points) break;
@@ -188,7 +183,7 @@ static void PrintBlockSplitPoints(const unsigned short* litlens,
188
183
  Finds next block to try to split, the largest of the available ones.
189
184
  The largest is chosen to make sure that if only a limited amount of blocks is
190
185
  requested, their sizes are spread evenly.
191
- llsize: the size of the LL77 data, which is the size of the done array here.
186
+ lz77size: the size of the LL77 data, which is the size of the done array here.
192
187
  done: array indicating which blocks starting at that position are no longer
193
188
  splittable (splitting them increases rather than decreases cost).
194
189
  splitpoints: the splitpoints found so far.
@@ -198,7 +193,7 @@ lend: output variable, giving end of block.
198
193
  returns 1 if a block was found, 0 if no block found (all are done).
199
194
  */
200
195
  static int FindLargestSplittableBlock(
201
- size_t llsize, const unsigned char* done,
196
+ size_t lz77size, const unsigned char* done,
202
197
  const size_t* splitpoints, size_t npoints,
203
198
  size_t* lstart, size_t* lend) {
204
199
  size_t longest = 0;
@@ -206,7 +201,7 @@ static int FindLargestSplittableBlock(
206
201
  size_t i;
207
202
  for (i = 0; i <= npoints; i++) {
208
203
  size_t start = i == 0 ? 0 : splitpoints[i - 1];
209
- size_t end = i == npoints ? llsize - 1 : splitpoints[i];
204
+ size_t end = i == npoints ? lz77size - 1 : splitpoints[i];
210
205
  if (!done[start] && end - start > longest) {
211
206
  *lstart = start;
212
207
  *lend = end;
@@ -218,9 +213,7 @@ static int FindLargestSplittableBlock(
218
213
  }
219
214
 
220
215
  void ZopfliBlockSplitLZ77(const ZopfliOptions* options,
221
- const unsigned short* litlens,
222
- const unsigned short* dists,
223
- size_t llsize, size_t maxblocks,
216
+ const ZopfliLZ77Store* lz77, size_t maxblocks,
224
217
  size_t** splitpoints, size_t* npoints) {
225
218
  size_t lstart, lend;
226
219
  size_t i;
@@ -229,14 +222,14 @@ void ZopfliBlockSplitLZ77(const ZopfliOptions* options,
229
222
  unsigned char* done;
230
223
  double splitcost, origcost;
231
224
 
232
- if (llsize < 10) return; /* This code fails on tiny files. */
225
+ if (lz77->size < 10) return; /* This code fails on tiny files. */
233
226
 
234
- done = (unsigned char*)malloc(llsize);
227
+ done = (unsigned char*)malloc(lz77->size);
235
228
  if (!done) exit(-1); /* Allocation failed. */
236
- for (i = 0; i < llsize; i++) done[i] = 0;
229
+ for (i = 0; i < lz77->size; i++) done[i] = 0;
237
230
 
238
231
  lstart = 0;
239
- lend = llsize;
232
+ lend = lz77->size;
240
233
  for (;;) {
241
234
  SplitCostContext c;
242
235
 
@@ -244,20 +237,16 @@ void ZopfliBlockSplitLZ77(const ZopfliOptions* options,
244
237
  break;
245
238
  }
246
239
 
247
- c.litlens = litlens;
248
- c.dists = dists;
249
- c.llsize = llsize;
240
+ c.lz77 = lz77;
250
241
  c.start = lstart;
251
242
  c.end = lend;
252
243
  assert(lstart < lend);
253
- llpos = FindMinimum(SplitCost, &c, lstart + 1, lend);
244
+ llpos = FindMinimum(SplitCost, &c, lstart + 1, lend, &splitcost);
254
245
 
255
246
  assert(llpos > lstart);
256
247
  assert(llpos < lend);
257
248
 
258
- splitcost = EstimateCost(litlens, dists, lstart, llpos) +
259
- EstimateCost(litlens, dists, llpos, lend);
260
- origcost = EstimateCost(litlens, dists, lstart, lend);
249
+ origcost = EstimateCost(lz77, lstart, lend);
261
250
 
262
251
  if (splitcost > origcost || llpos == lstart + 1 || llpos == lend) {
263
252
  done[lstart] = 1;
@@ -267,7 +256,7 @@ void ZopfliBlockSplitLZ77(const ZopfliOptions* options,
267
256
  }
268
257
 
269
258
  if (!FindLargestSplittableBlock(
270
- llsize, done, *splitpoints, *npoints, &lstart, &lend)) {
259
+ lz77->size, done, *splitpoints, *npoints, &lstart, &lend)) {
271
260
  break; /* No further split will probably reduce compression. */
272
261
  }
273
262
 
@@ -277,7 +266,7 @@ void ZopfliBlockSplitLZ77(const ZopfliOptions* options,
277
266
  }
278
267
 
279
268
  if (options->verbose) {
280
- PrintBlockSplitPoints(litlens, dists, llsize, *splitpoints, *npoints);
269
+ PrintBlockSplitPoints(lz77, *splitpoints, *npoints);
281
270
  }
282
271
 
283
272
  free(done);
@@ -292,25 +281,22 @@ void ZopfliBlockSplit(const ZopfliOptions* options,
292
281
  size_t* lz77splitpoints = 0;
293
282
  size_t nlz77points = 0;
294
283
  ZopfliLZ77Store store;
284
+ ZopfliHash hash;
285
+ ZopfliHash* h = &hash;
295
286
 
296
- ZopfliInitLZ77Store(&store);
297
-
298
- s.options = options;
299
- s.blockstart = instart;
300
- s.blockend = inend;
301
- #ifdef ZOPFLI_LONGEST_MATCH_CACHE
302
- s.lmc = 0;
303
- #endif
287
+ ZopfliInitLZ77Store(in, &store);
288
+ ZopfliInitBlockState(options, instart, inend, 0, &s);
289
+ ZopfliAllocHash(ZOPFLI_WINDOW_SIZE, h);
304
290
 
305
291
  *npoints = 0;
306
292
  *splitpoints = 0;
307
293
 
308
294
  /* Unintuitively, Using a simple LZ77 method here instead of ZopfliLZ77Optimal
309
295
  results in better blocks. */
310
- ZopfliLZ77Greedy(&s, in, instart, inend, &store);
296
+ ZopfliLZ77Greedy(&s, in, instart, inend, &store, h);
311
297
 
312
298
  ZopfliBlockSplitLZ77(options,
313
- store.litlens, store.dists, store.size, maxblocks,
299
+ &store, maxblocks,
314
300
  &lz77splitpoints, &nlz77points);
315
301
 
316
302
  /* Convert LZ77 positions to positions in the uncompressed input. */
@@ -328,7 +314,9 @@ void ZopfliBlockSplit(const ZopfliOptions* options,
328
314
  assert(*npoints == nlz77points);
329
315
 
330
316
  free(lz77splitpoints);
317
+ ZopfliCleanBlockState(&s);
331
318
  ZopfliCleanLZ77Store(&store);
319
+ ZopfliCleanHash(h);
332
320
  }
333
321
 
334
322
  void ZopfliBlockSplitSimple(const unsigned char* in,